Skip to content

Add preset devflow init bootstrap#65

Merged
Sungblab merged 1 commit into
mainfrom
codex/devflow-init-mcp-bootstrap
Jun 16, 2026
Merged

Add preset devflow init bootstrap#65
Sungblab merged 1 commit into
mainfrom
codex/devflow-init-mcp-bootstrap

Conversation

@Sungblab

Copy link
Copy Markdown
Owner

Summary

  • Add preset-aware devflow init bootstrap for solo-product, research, and content-site.
  • Generate .devflow/config.json, AGENTS guidance, inferred gates, GitHub CI, and optional Codex/Claude harness files through shared core contracts.
  • Expose devflow.init through MCP with dry-run and confirmed-write paths, plus docs and tests.

Verification

  • npm test
  • npm run docs:check
  • node packages/cli/src/index.js health --json
  • node packages/cli/src/index.js harness health --targets codex,claude --json
  • npm run pack:check
  • git diff --check

Review

  • Devflow review recorded for init-mcp-bootstrap: passed.

@Sungblab Sungblab merged commit 79d97f7 into main Jun 16, 2026
2 checks passed
@Sungblab Sungblab deleted the codex/devflow-init-mcp-bootstrap branch June 16, 2026 03:17

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a preset-aware bootstrap and initialization path for Devflow Native via the devflow init command and a new devflow.init MCP tool. It supports configuring presets (solo-product, research, content-site), targets, CI providers, and review policies, while automatically inferring verification gates from package.json scripts and safely augmenting existing AGENTS.md files. Feedback on the changes suggests improving the robustness of input normalization helpers by using loose equality checks to handle explicit null values, and refining the AGENTS.md appending logic to prevent unnecessary leading newlines in empty files.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +619 to +626
function normalizeInitPreset(input = {}) {
const rawPreset = input.preset ?? input.profile ?? "standard";
const preset = String(rawPreset || "standard").trim() || "standard";
if (input.preset !== undefined && !INIT_PRESETS.has(preset)) {
throw new Error(`Unsupported devflow init preset: ${preset}`);
}
return preset;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using loose equality check != null is safer and more robust than strict inequality !== undefined when normalizing input presets, as it correctly handles both null and undefined values that might be passed programmatically (e.g., via MCP clients).

Suggested change
function normalizeInitPreset(input = {}) {
const rawPreset = input.preset ?? input.profile ?? "standard";
const preset = String(rawPreset || "standard").trim() || "standard";
if (input.preset !== undefined && !INIT_PRESETS.has(preset)) {
throw new Error(`Unsupported devflow init preset: ${preset}`);
}
return preset;
}
function normalizeInitPreset(input = {}) {
const rawPreset = input.preset ?? input.profile ?? "standard";
const preset = String(rawPreset || "standard").trim() || "standard";
if (input.preset != null && !INIT_PRESETS.has(preset)) {
throw new Error("Unsupported devflow init preset: " + preset);
}
return preset;
}

Comment on lines +628 to +637
function normalizeInitTargets(targets) {
if (targets === undefined) {
return [];
}
if (Array.isArray(targets) && targets.length === 0) {
return [];
}
return normalizeHarnessTargets(Array.isArray(targets) ? targets : [targets])
.filter((target) => target === "codex" || target === "claude");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using targets == null instead of targets === undefined ensures that explicit null values passed programmatically are safely handled and default to an empty array, preventing downstream type errors or unexpected behavior.

Suggested change
function normalizeInitTargets(targets) {
if (targets === undefined) {
return [];
}
if (Array.isArray(targets) && targets.length === 0) {
return [];
}
return normalizeHarnessTargets(Array.isArray(targets) ? targets : [targets])
.filter((target) => target === "codex" || target === "claude");
}
function normalizeInitTargets(targets) {
if (targets == null) {
return [];
}
if (Array.isArray(targets) && targets.length === 0) {
return [];
}
return normalizeHarnessTargets(Array.isArray(targets) ? targets : [targets])
.filter((target) => target === "codex" || target === "claude");
}

Comment on lines +639 to +651
function normalizeInitCi(ci) {
if (ci === undefined || ci === false) {
return "none";
}
const normalized = String(ci).trim().toLowerCase();
if (normalized === "none") {
return "none";
}
if (normalized !== "github") {
throw new Error(`Unsupported devflow init ci provider: ${ci}`);
}
return normalized;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Checking ci == null instead of ci === undefined ensures that explicit null values are safely normalized to "none" instead of throwing an unsupported provider error due to string coercion of null.

Suggested change
function normalizeInitCi(ci) {
if (ci === undefined || ci === false) {
return "none";
}
const normalized = String(ci).trim().toLowerCase();
if (normalized === "none") {
return "none";
}
if (normalized !== "github") {
throw new Error(`Unsupported devflow init ci provider: ${ci}`);
}
return normalized;
}
function normalizeInitCi(ci) {
if (ci == null || ci === false) {
return "none";
}
const normalized = String(ci).trim().toLowerCase();
if (normalized === "none") {
return "none";
}
if (normalized !== "github") {
throw new Error("Unsupported devflow init ci provider: " + ci);
}
return normalized;
}

Comment on lines +653 to +671
function normalizeInitReviewRequired(review, preset) {
if (review === undefined) {
return preset === "standard" || preset === "solo-product";
}
if (review === true) {
return true;
}
if (review === false) {
return false;
}
const normalized = String(review).trim().toLowerCase();
if (normalized === "required") {
return true;
}
if (normalized === "optional" || normalized === "none") {
return false;
}
throw new Error(`Unsupported devflow init review mode: ${review}`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using review == null instead of review === undefined ensures that explicit null values are safely handled and default to the preset-based policy, avoiding unexpected errors during string coercion of null.

Suggested change
function normalizeInitReviewRequired(review, preset) {
if (review === undefined) {
return preset === "standard" || preset === "solo-product";
}
if (review === true) {
return true;
}
if (review === false) {
return false;
}
const normalized = String(review).trim().toLowerCase();
if (normalized === "required") {
return true;
}
if (normalized === "optional" || normalized === "none") {
return false;
}
throw new Error(`Unsupported devflow init review mode: ${review}`);
}
function normalizeInitReviewRequired(review, preset) {
if (review == null) {
return preset === "standard" || preset === "solo-product";
}
if (review === true) {
return true;
}
if (review === false) {
return false;
}
const normalized = String(review).trim().toLowerCase();
if (normalized === "required") {
return true;
}
if (normalized === "optional" || normalized === "none") {
return false;
}
throw new Error("Unsupported devflow init review mode: " + review);
}

Comment on lines +1650 to +1659
function appendDevflowAgentGuide(existing, generated) {
if (existing.includes("## Devflow Native")) {
return existing;
}
const markerIndex = generated.indexOf("## Devflow Native");
const section = markerIndex >= 0 ? generated.slice(markerIndex).trim() : generated.trim();
const normalized = existing.length > 0 && !existing.endsWith("\n") ? `${existing}\n` : existing;
const spacer = normalized.endsWith("\n\n") ? "" : "\n";
return `${normalized}${spacer}${section}\n`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When appending the Devflow section to an empty or whitespace-only AGENTS.md file, the current logic introduces an unnecessary leading newline. Checking if normalized.length === 0 prevents this and ensures a clean file format.

Suggested change
function appendDevflowAgentGuide(existing, generated) {
if (existing.includes("## Devflow Native")) {
return existing;
}
const markerIndex = generated.indexOf("## Devflow Native");
const section = markerIndex >= 0 ? generated.slice(markerIndex).trim() : generated.trim();
const normalized = existing.length > 0 && !existing.endsWith("\n") ? `${existing}\n` : existing;
const spacer = normalized.endsWith("\n\n") ? "" : "\n";
return `${normalized}${spacer}${section}\n`;
}
function appendDevflowAgentGuide(existing, generated) {
if (existing.includes("## Devflow Native")) {
return existing;
}
const markerIndex = generated.indexOf("## Devflow Native");
const section = markerIndex >= 0 ? generated.slice(markerIndex).trim() : generated.trim();
const normalized = existing.length > 0 && !existing.endsWith("\n") ? `${existing}\n` : existing;
const spacer = normalized.length === 0 || normalized.endsWith("\n\n") ? "" : "\n";
return normalized + spacer + section + "\n";
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant