diff --git a/src/core/project-config.ts b/src/core/project-config.ts index 6c1ea04a5..3d74a48b0 100644 --- a/src/core/project-config.ts +++ b/src/core/project-config.ts @@ -38,6 +38,13 @@ export const ProjectConfigSchema = z.object({ ) .optional() .describe('Per-artifact rules, keyed by artifact ID'), + + // Optional: whether the project is a monorepo (affects front matter sync behavior) + // Set to false to skip monorepo-related prompts (e.g., app label in spec front matter) + monorepo: z + .boolean() + .optional() + .describe('Set to false to opt out of monorepo front matter conventions'), }); export type ProjectConfig = z.infer; @@ -152,6 +159,17 @@ export function readProjectConfig(projectRoot: string): ProjectConfig | null { } } + // Parse monorepo field using Zod + if (raw.monorepo !== undefined) { + const monorepoField = z.boolean(); + const monorepoResult = monorepoField.safeParse(raw.monorepo); + if (monorepoResult.success) { + config.monorepo = monorepoResult.data; + } else { + console.warn(`Invalid 'monorepo' field in config (must be boolean)`); + } + } + // Return partial config even if some fields failed return Object.keys(config).length > 0 ? (config as ProjectConfig) : null; } catch (error) { diff --git a/src/core/templates/workflows/archive-change.ts b/src/core/templates/workflows/archive-change.ts index 1c37ffde0..0399fbc2d 100644 --- a/src/core/templates/workflows/archive-change.ts +++ b/src/core/templates/workflows/archive-change.ts @@ -66,6 +66,9 @@ export function getArchiveChangeSkillTemplate(): SkillTemplate { If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change ''. Delta spec analysis: "). Proceed to archive regardless of choice. + **Front matter consistency (after sync):** + Ensure newly synced specs follow existing front matter conventions. See sync-specs step 3e for full logic (monorepo detection, opt-out via \`monorepo: false\` in config). + 5. **Perform the archive** Create the archive directory if it doesn't exist: @@ -181,6 +184,9 @@ export function getOpsxArchiveCommandTemplate(): CommandTemplate { If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change ''. Delta spec analysis: "). Proceed to archive regardless of choice. + **Front matter consistency (after sync):** + Ensure newly synced specs follow existing front matter conventions. See sync-specs step 3e for full logic (monorepo detection, opt-out via \`monorepo: false\` in config). + 5. **Perform the archive** Create the archive directory if it doesn't exist: diff --git a/src/core/templates/workflows/bulk-archive-change.ts b/src/core/templates/workflows/bulk-archive-change.ts index d57db8aa0..fec9693ec 100644 --- a/src/core/templates/workflows/bulk-archive-change.ts +++ b/src/core/templates/workflows/bulk-archive-change.ts @@ -125,6 +125,7 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig - Use the openspec-sync-specs approach (agent-driven intelligent merge) - For conflicts, apply in resolved order - Track if sync was done + - Ensure front matter consistency on newly synced specs (see sync-specs step 3e) b. **Perform the archive**: \`\`\`bash @@ -372,6 +373,7 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig - Use the openspec-sync-specs approach (agent-driven intelligent merge) - For conflicts, apply in resolved order - Track if sync was done + - Ensure front matter consistency on newly synced specs (see sync-specs step 3e) b. **Perform the archive**: \`\`\`bash diff --git a/src/core/templates/workflows/sync-specs.ts b/src/core/templates/workflows/sync-specs.ts index 34da4276e..cdc137327 100644 --- a/src/core/templates/workflows/sync-specs.ts +++ b/src/core/templates/workflows/sync-specs.ts @@ -71,6 +71,17 @@ This is an **agent-driven** operation - you will read delta specs and directly e - Add Purpose section (can be brief, mark as TBD) - Add Requirements section with the ADDED requirements + e. **Ensure front matter consistency** on newly synced specs (common in monorepos where specs need to indicate which app/package they belong to): + - If the project config (\`openspec/config.yaml\` or \`openspec/config.yml\`) contains \`monorepo: false\` → skip + - Sample existing main specs in \`openspec/specs/\` for YAML front matter convention + - If existing specs use front matter → apply same pattern to newly created/updated specs + - If no existing specs have front matter AND project is single-app → skip + - If no existing specs have front matter AND project is a monorepo (multiple apps/packages detected): + + **Prompt options:** + - "Establish front matter convention (recommended)" — suggest \`app: \` based on project structure, apply to newly synced specs + - "Skip and don't ask again" — add \`monorepo: false\` to the existing OpenSpec config file without overwriting other keys + 4. **Show summary** After applying all changes, summarize: @@ -210,6 +221,17 @@ This is an **agent-driven** operation - you will read delta specs and directly e - Add Purpose section (can be brief, mark as TBD) - Add Requirements section with the ADDED requirements + e. **Ensure front matter consistency** on newly synced specs (common in monorepos where specs need to indicate which app/package they belong to): + - If the project config (\`openspec/config.yaml\` or \`openspec/config.yml\`) contains \`monorepo: false\` → skip + - Sample existing main specs in \`openspec/specs/\` for YAML front matter convention + - If existing specs use front matter → apply same pattern to newly created/updated specs + - If no existing specs have front matter AND project is single-app → skip + - If no existing specs have front matter AND project is a monorepo (multiple apps/packages detected): + + **Prompt options:** + - "Establish front matter convention (recommended)" — suggest \`app: \` based on project structure, apply to newly synced specs + - "Skip and don't ask again" — add \`monorepo: false\` to the existing OpenSpec config file without overwriting other keys + 4. **Show summary** After applying all changes, summarize: