cc-codeconductor 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/README.md +24 -11
  2. package/dist/core/runner/runner-target.d.ts +4 -4
  3. package/dist/domain/product/entities.d.ts +1 -1
  4. package/dist/index.js +1714 -795
  5. package/dist/library.js +62 -7
  6. package/dist/validation/schemas.d.ts +210 -43
  7. package/docs/generated/cli.md +8 -0
  8. package/package.json +1 -1
  9. package/presets/agy/skills/openspec/SKILL.md +39 -11
  10. package/presets/agy/workflows/cc-api-contract.md +4 -5
  11. package/presets/agy/workflows/cc-backlog.md +4 -5
  12. package/presets/agy/workflows/cc-clarify.md +4 -5
  13. package/presets/agy/workflows/cc-council.md +6 -7
  14. package/presets/agy/workflows/cc-db-migration.md +4 -5
  15. package/presets/agy/workflows/cc-explore.md +4 -5
  16. package/presets/agy/workflows/cc-feature.md +4 -5
  17. package/presets/agy/workflows/cc-fix.md +4 -5
  18. package/presets/agy/workflows/cc-handoff.md +8 -5
  19. package/presets/agy/workflows/cc-iterative.md +4 -5
  20. package/presets/agy/workflows/cc-odd.md +4 -3
  21. package/presets/agy/workflows/cc-openspec.md +5 -6
  22. package/presets/agy/workflows/cc-pagespeed.md +4 -5
  23. package/presets/agy/workflows/cc-prototype.md +4 -5
  24. package/presets/agy/workflows/cc-refactor.md +4 -5
  25. package/presets/agy/workflows/cc-review.md +4 -5
  26. package/presets/agy/workflows/cc-scorecard.md +4 -5
  27. package/presets/agy/workflows/cc-security.md +4 -5
  28. package/presets/agy/workflows/cc-spec-mutation.md +4 -5
  29. package/presets/agy/workflows/cc-tdd-cycle.md +4 -5
  30. package/presets/agy/workflows/cc-test-plan.md +4 -5
  31. package/presets/agy/workflows/cc-triage.md +4 -5
  32. package/presets/claude/CLAUDE.md +6 -5
  33. package/presets/claude/commands/cc/api-contract.md +4 -5
  34. package/presets/claude/commands/cc/backlog.md +4 -5
  35. package/presets/claude/commands/cc/clarify.md +4 -5
  36. package/presets/claude/commands/cc/council.md +6 -7
  37. package/presets/claude/commands/cc/db-migration.md +4 -5
  38. package/presets/claude/commands/cc/explore.md +4 -5
  39. package/presets/claude/commands/cc/feature.md +4 -5
  40. package/presets/claude/commands/cc/fix.md +4 -5
  41. package/presets/claude/commands/cc/handoff.md +8 -5
  42. package/presets/claude/commands/cc/iterative.md +4 -5
  43. package/presets/claude/commands/cc/odd.md +4 -3
  44. package/presets/claude/commands/cc/openspec.md +43 -8
  45. package/presets/claude/commands/cc/pagespeed.md +4 -5
  46. package/presets/claude/commands/cc/prototype.md +4 -5
  47. package/presets/claude/commands/cc/refactor.md +4 -5
  48. package/presets/claude/commands/cc/review.md +4 -5
  49. package/presets/claude/commands/cc/scorecard.md +4 -5
  50. package/presets/claude/commands/cc/security.md +4 -5
  51. package/presets/claude/commands/cc/spec-mutation.md +4 -5
  52. package/presets/claude/commands/cc/tdd-cycle.md +4 -5
  53. package/presets/claude/commands/cc/test-plan.md +4 -5
  54. package/presets/claude/commands/cc/triage.md +4 -5
  55. package/presets/claude/settings.json +32 -78
  56. package/presets/claude/skills/openspec/SKILL.md +39 -11
  57. package/presets/codex/config.toml +2 -0
  58. package/presets/codex/skills/cc-api-contract/SKILL.md +4 -5
  59. package/presets/codex/skills/cc-backlog/SKILL.md +4 -5
  60. package/presets/codex/skills/cc-clarify/SKILL.md +4 -5
  61. package/presets/codex/skills/cc-council/SKILL.md +12 -7
  62. package/presets/codex/skills/cc-db-migration/SKILL.md +4 -5
  63. package/presets/codex/skills/cc-explore/SKILL.md +4 -5
  64. package/presets/codex/skills/cc-feature/SKILL.md +4 -5
  65. package/presets/codex/skills/cc-fix/SKILL.md +4 -5
  66. package/presets/codex/skills/cc-handoff/SKILL.md +8 -5
  67. package/presets/codex/skills/cc-iterative/SKILL.md +4 -5
  68. package/presets/codex/skills/cc-odd/SKILL.md +4 -3
  69. package/presets/codex/skills/cc-openspec/SKILL.md +31 -7
  70. package/presets/codex/skills/cc-pagespeed/SKILL.md +4 -5
  71. package/presets/codex/skills/cc-prototype/SKILL.md +4 -5
  72. package/presets/codex/skills/cc-refactor/SKILL.md +4 -5
  73. package/presets/codex/skills/cc-review/SKILL.md +4 -5
  74. package/presets/codex/skills/cc-scorecard/SKILL.md +4 -5
  75. package/presets/codex/skills/cc-security/SKILL.md +4 -5
  76. package/presets/codex/skills/cc-spec-mutation/SKILL.md +4 -5
  77. package/presets/codex/skills/cc-tdd-cycle/SKILL.md +4 -5
  78. package/presets/codex/skills/cc-test-plan/SKILL.md +4 -5
  79. package/presets/codex/skills/cc-triage/SKILL.md +4 -5
  80. package/presets/codex/skills/openspec/SKILL.md +39 -11
  81. package/presets/cursor/commands/cc/api-contract.md +4 -5
  82. package/presets/cursor/commands/cc/backlog.md +4 -5
  83. package/presets/cursor/commands/cc/clarify.md +4 -5
  84. package/presets/cursor/commands/cc/council.md +6 -7
  85. package/presets/cursor/commands/cc/db-migration.md +4 -5
  86. package/presets/cursor/commands/cc/explore.md +4 -5
  87. package/presets/cursor/commands/cc/feature.md +4 -5
  88. package/presets/cursor/commands/cc/fix.md +4 -5
  89. package/presets/cursor/commands/cc/handoff.md +8 -5
  90. package/presets/cursor/commands/cc/iterative.md +4 -5
  91. package/presets/cursor/commands/cc/odd.md +4 -3
  92. package/presets/cursor/commands/cc/openspec.md +25 -7
  93. package/presets/cursor/commands/cc/pagespeed.md +4 -5
  94. package/presets/cursor/commands/cc/prototype.md +4 -5
  95. package/presets/cursor/commands/cc/refactor.md +4 -5
  96. package/presets/cursor/commands/cc/review.md +4 -5
  97. package/presets/cursor/commands/cc/scorecard.md +4 -5
  98. package/presets/cursor/commands/cc/security.md +4 -5
  99. package/presets/cursor/commands/cc/spec-mutation.md +4 -5
  100. package/presets/cursor/commands/cc/tdd-cycle.md +4 -5
  101. package/presets/cursor/commands/cc/test-plan.md +4 -5
  102. package/presets/cursor/commands/cc/triage.md +4 -5
  103. package/presets/cursor/skills/openspec/SKILL.md +39 -11
  104. package/presets/gemini/commands/cc/api-contract.toml +4 -5
  105. package/presets/gemini/commands/cc/backlog.toml +4 -5
  106. package/presets/gemini/commands/cc/clarify.toml +4 -5
  107. package/presets/gemini/commands/cc/council.toml +6 -7
  108. package/presets/gemini/commands/cc/db-migration.toml +4 -5
  109. package/presets/gemini/commands/cc/explore.toml +4 -5
  110. package/presets/gemini/commands/cc/feature.toml +4 -5
  111. package/presets/gemini/commands/cc/fix.toml +4 -5
  112. package/presets/gemini/commands/cc/handoff.toml +8 -5
  113. package/presets/gemini/commands/cc/iterative.toml +4 -5
  114. package/presets/gemini/commands/cc/odd.toml +4 -3
  115. package/presets/gemini/commands/cc/openspec.toml +25 -7
  116. package/presets/gemini/commands/cc/pagespeed.toml +4 -5
  117. package/presets/gemini/commands/cc/prototype.toml +4 -5
  118. package/presets/gemini/commands/cc/refactor.toml +4 -5
  119. package/presets/gemini/commands/cc/review.toml +4 -5
  120. package/presets/gemini/commands/cc/scorecard.toml +4 -5
  121. package/presets/gemini/commands/cc/security.toml +4 -5
  122. package/presets/gemini/commands/cc/spec-mutation.toml +4 -5
  123. package/presets/gemini/commands/cc/tdd-cycle.toml +4 -5
  124. package/presets/gemini/commands/cc/test-plan.toml +4 -5
  125. package/presets/gemini/commands/cc/triage.toml +4 -5
  126. package/presets/muse/AGENTS.md +46 -0
  127. package/presets/muse/hooks.json +30 -0
  128. package/presets/opencode/commands/cc-api-contract.md +4 -5
  129. package/presets/opencode/commands/cc-backlog.md +4 -5
  130. package/presets/opencode/commands/cc-clarify.md +4 -5
  131. package/presets/opencode/commands/cc-council.md +6 -7
  132. package/presets/opencode/commands/cc-db-migration.md +4 -5
  133. package/presets/opencode/commands/cc-explore.md +4 -5
  134. package/presets/opencode/commands/cc-feature.md +4 -5
  135. package/presets/opencode/commands/cc-fix.md +4 -5
  136. package/presets/opencode/commands/cc-handoff.md +8 -5
  137. package/presets/opencode/commands/cc-iterative.md +4 -5
  138. package/presets/opencode/commands/cc-odd.md +4 -3
  139. package/presets/opencode/commands/cc-openspec.md +10 -9
  140. package/presets/opencode/commands/cc-pagespeed.md +4 -5
  141. package/presets/opencode/commands/cc-prototype.md +4 -5
  142. package/presets/opencode/commands/cc-refactor.md +4 -5
  143. package/presets/opencode/commands/cc-review.md +4 -5
  144. package/presets/opencode/commands/cc-scorecard.md +4 -5
  145. package/presets/opencode/commands/cc-security.md +4 -5
  146. package/presets/opencode/commands/cc-spec-mutation.md +4 -5
  147. package/presets/opencode/commands/cc-tdd-cycle.md +4 -5
  148. package/presets/opencode/commands/cc-test-plan.md +4 -5
  149. package/presets/opencode/commands/cc-triage.md +4 -5
  150. package/presets/opencode/skills/openspec/SKILL.md +39 -11
  151. package/src/presets/manifests/codex.yml +3 -0
  152. package/src/presets/manifests/muse.yml +18 -0
  153. package/src/presets/models/muse.yml +4 -0
  154. package/src/presets/models/roles.yml +40 -28
  155. package/src/presets/targets/muse.yml +12 -0
package/dist/index.js CHANGED
@@ -72,7 +72,7 @@ var package_default;
72
72
  var init_package = __esm(() => {
73
73
  package_default = {
74
74
  name: "cc-codeconductor",
75
- version: "1.5.0",
75
+ version: "1.6.0",
76
76
  description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
77
77
  keywords: [
78
78
  "ai",
@@ -4140,9 +4140,10 @@ var init_runner_target = __esm(() => {
4140
4140
  "cursor",
4141
4141
  "agy",
4142
4142
  "pi",
4143
+ "muse",
4143
4144
  "all"
4144
4145
  ];
4145
- INDIVIDUAL_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "agy", "pi"];
4146
+ INDIVIDUAL_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "agy", "pi", "muse"];
4146
4147
  });
4147
4148
 
4148
4149
  // src/utils/cli-bin.ts
@@ -11216,9 +11217,11 @@ __export(exports_schemas, {
11216
11217
  ExecutionContextSchema: () => ExecutionContextSchema,
11217
11218
  ExecutionProfileNameSchema: () => ExecutionProfileNameSchema,
11218
11219
  ExecutionProfileSchema: () => ExecutionProfileSchema,
11220
+ FixIntakeOutputSchema: () => FixIntakeOutputSchema,
11219
11221
  GoalGraphSchema: () => GoalGraphSchema,
11220
11222
  GoalTaskSchema: () => GoalTaskSchema,
11221
11223
  GraphRelationSchema: () => GraphRelationSchema,
11224
+ HandoffEnvelopeSchema: () => HandoffEnvelopeSchema,
11222
11225
  HarnessCatalogEntrySchema: () => HarnessCatalogEntrySchema,
11223
11226
  HarnessCatalogSchema: () => HarnessCatalogSchema,
11224
11227
  HarnessComponentIdSchema: () => HarnessComponentIdSchema,
@@ -11322,6 +11325,12 @@ __export(exports_schemas, {
11322
11325
  validateTaskOutcome: () => validateTaskOutcome,
11323
11326
  validateWorkflowProfile: () => validateWorkflowProfile
11324
11327
  });
11328
+ import { isAbsolute } from "node:path";
11329
+ function isContainedRelativePath(value) {
11330
+ if (value === "" || isAbsolute(value))
11331
+ return false;
11332
+ return !value.replace(/\\/g, "/").split("/").includes("..");
11333
+ }
11325
11334
  function validateCouncilSpec(data) {
11326
11335
  return CouncilSpecSchema.parse(data);
11327
11336
  }
@@ -11439,7 +11448,7 @@ function validateProductEvent(data) {
11439
11448
  function validateSkillsRegistry(data) {
11440
11449
  return SkillsRegistrySchema.parse(data);
11441
11450
  }
11442
- var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, SkillFrontmatterSchema, CommandFrontmatterSchema, TargetCapabilitiesSchema, ContractTargetSchema, ContractFormatSchema, AgentContractSchema, CouncilFindingSchema, CouncilVerdictInputSchema, ConsensusConfigSchema, CouncilVerdictSchema, ClaudeAgentFileSchema, OpenCodeAgentFileSchema, SentryStackFrameSchema, SentryWebhookSchema, MemoryPointerSchema, MemoryIndexSchema, GoalTaskSchema, GoalGraphSchema, ConfidenceLevelSchema, ProductNodeTypeSchema, GraphRelationSchema, KnowledgeEntitySchema, DecisionSchema, EvidenceSchema, CanonicalTaskCardStatusSchema, CanonicalTaskCardSchema, ProductGraphNodeSchema, ProductGraphEdgeSchema, ProductGraphSchema, ProductMetaSchema, ProductEventTypeSchema, ProductEventSchema, OperationalStateSchema, StrategicMemorySchema, ImpactReportSchema, BusinessReviewOutputSchema, VerificationReportSchema, BacklogStatusSchema, BacklogTypeSchema, BacklogPrioritySchema, BacklogGlobalSchema, BacklogItemSchema, BacklogDocumentSchema, OpenspecTaskCardPhaseSchema, OpenspecTaskCardStatusSchema, OpenspecTaskCardSchema, OpenspecStateSchema, SpecAnalyzeFindingSchema, SpecAnalyzeReportSchema, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, DeliveryRouteSchema, TokenUsageSchema, DeliveryTelemetrySchema, DeliveryLedgerTaskSchema, DeliveryLedgerSchema, DeliveryLedgerRequestSchema, TaskOutcomeSchema, HarnessComponentIdSchema, HarnessComponentLayerSchema, HarnessToggleKindSchema, HarnessCatalogEntrySchema, HarnessCatalogSchema, HarnessOverlaySchema, HarnessExperimentSchema, HarnessSuiteTaskSchema, HarnessSuiteSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema, SkillsRegistrySchema;
11451
+ var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ContainedRelativePathSchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, SkillFrontmatterSchema, CommandFrontmatterSchema, TargetCapabilitiesSchema, ContractTargetSchema, ContractFormatSchema, AgentContractSchema, CouncilFindingSchema, CouncilVerdictInputSchema, ConsensusConfigSchema, CouncilVerdictSchema, ClaudeAgentFileSchema, OpenCodeAgentFileSchema, SentryStackFrameSchema, SentryWebhookSchema, MemoryPointerSchema, MemoryIndexSchema, GoalTaskSchema, GoalGraphSchema, ConfidenceLevelSchema, ProductNodeTypeSchema, GraphRelationSchema, KnowledgeEntitySchema, DecisionSchema, EvidenceSchema, CanonicalTaskCardStatusSchema, CanonicalTaskCardSchema, ProductGraphNodeSchema, ProductGraphEdgeSchema, ProductGraphSchema, ProductMetaSchema, ProductEventTypeSchema, ProductEventSchema, OperationalStateSchema, StrategicMemorySchema, ImpactReportSchema, BusinessReviewOutputSchema, VerificationReportSchema, BacklogStatusSchema, BacklogTypeSchema, BacklogPrioritySchema, BacklogGlobalSchema, BacklogItemSchema, BacklogDocumentSchema, OpenspecTaskCardPhaseSchema, OpenspecTaskCardStatusSchema, OpenspecTaskCardSchema, OpenspecStateSchema, SpecAnalyzeFindingSchema, SpecAnalyzeReportSchema, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, FixIntakeOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, DeliveryRouteSchema, TokenUsageSchema, DeliveryTelemetrySchema, DeliveryLedgerTaskSchema, DeliveryLedgerSchema, HandoffEnvelopeSchema, RepoRelativePathSchema, DeliveryLedgerRequestSchema, TaskOutcomeSchema, HarnessComponentIdSchema, HarnessComponentLayerSchema, HarnessToggleKindSchema, HarnessCatalogEntrySchema, HarnessCatalogSchema, HarnessOverlaySchema, HarnessExperimentSchema, HarnessSuiteTaskSchema, HarnessSuiteSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema, SkillsRegistrySchema;
11443
11452
  var init_schemas = __esm(() => {
11444
11453
  init_zod();
11445
11454
  init_runner_target();
@@ -11517,11 +11526,15 @@ var init_schemas = __esm(() => {
11517
11526
  "append",
11518
11527
  "merge-json",
11519
11528
  "merge-managed",
11529
+ "create-only",
11520
11530
  "skip"
11521
11531
  ]);
11532
+ ContainedRelativePathSchema = exports_external.string().refine(isContainedRelativePath, (value) => ({
11533
+ message: `Path must be relative without ".." segments: ${value}`
11534
+ }));
11522
11535
  ManifestEntrySchema = exports_external.object({
11523
- src: exports_external.string(),
11524
- dest: exports_external.string(),
11536
+ src: ContainedRelativePathSchema,
11537
+ dest: ContainedRelativePathSchema,
11525
11538
  strategy: InstallStrategySchema,
11526
11539
  globalStrategy: InstallStrategySchema.optional(),
11527
11540
  template: exports_external.boolean().optional()
@@ -11542,7 +11555,8 @@ var init_schemas = __esm(() => {
11542
11555
  cursor: exports_external.string().optional(),
11543
11556
  agy: exports_external.string().optional(),
11544
11557
  pi: exports_external.string().optional(),
11545
- grok: exports_external.string().optional()
11558
+ grok: exports_external.string().optional(),
11559
+ muse: exports_external.string().optional()
11546
11560
  })),
11547
11561
  tools: exports_external.record(exports_external.string(), ToolProviderNamesSchema).optional(),
11548
11562
  permissions: PermissionProviderNamesSchema.optional()
@@ -11821,7 +11835,8 @@ var init_schemas = __esm(() => {
11821
11835
  "goal.updated",
11822
11836
  "blocker.detected",
11823
11837
  "verification.completed",
11824
- "feedback.processed"
11838
+ "feedback.processed",
11839
+ "context.compiled"
11825
11840
  ]);
11826
11841
  ProductEventSchema = exports_external.object({
11827
11842
  id: exports_external.string(),
@@ -12166,6 +12181,11 @@ var init_schemas = __esm(() => {
12166
12181
  risks: exports_external.array(exports_external.string()).default([]),
12167
12182
  openQuestions: exports_external.array(exports_external.string()).optional()
12168
12183
  });
12184
+ FixIntakeOutputSchema = exports_external.object({
12185
+ actualBehavior: exports_external.string(),
12186
+ expectedBehavior: exports_external.string(),
12187
+ reproductionSteps: exports_external.array(exports_external.string())
12188
+ });
12169
12189
  ScorecardVerdictSchema = exports_external.enum(["PASS", "REVISE", "REJECT"]);
12170
12190
  ScorecardCriterionIdSchema = exports_external.enum([
12171
12191
  "acceptance",
@@ -12237,16 +12257,51 @@ var init_schemas = __esm(() => {
12237
12257
  nextStep: exports_external.string().min(1),
12238
12258
  memoryTopicKey: exports_external.string().min(1),
12239
12259
  workspace: exports_external.record(exports_external.string(), exports_external.string()),
12260
+ technicalPlanPath: exports_external.string().min(1).optional(),
12240
12261
  createdAt: exports_external.string().datetime(),
12241
12262
  updatedAt: exports_external.string().datetime()
12242
12263
  });
12264
+ HandoffEnvelopeSchema = exports_external.object({
12265
+ version: exports_external.literal(1),
12266
+ task: exports_external.object({
12267
+ id: exports_external.string().min(1),
12268
+ objective: exports_external.string().min(1),
12269
+ acceptanceCriteria: exports_external.array(exports_external.string().min(1)),
12270
+ status: CanonicalTaskCardStatusSchema
12271
+ }),
12272
+ decisions: exports_external.object({
12273
+ constraints: exports_external.array(exports_external.string()),
12274
+ unresolved: exports_external.array(exports_external.string())
12275
+ }),
12276
+ scope: exports_external.object({
12277
+ relevantFiles: exports_external.array(exports_external.string()),
12278
+ boundaries: exports_external.array(exports_external.string())
12279
+ }),
12280
+ verification: exports_external.object({
12281
+ evidence: exports_external.array(exports_external.string())
12282
+ }),
12283
+ change: exports_external.object({
12284
+ touchedFiles: exports_external.array(exports_external.string())
12285
+ }),
12286
+ next: exports_external.object({
12287
+ role: exports_external.string().min(1),
12288
+ objective: exports_external.string().min(1)
12289
+ }),
12290
+ sources: exports_external.object({
12291
+ deliveryLedger: exports_external.string().min(1),
12292
+ memoryTopicKey: exports_external.string().min(1),
12293
+ technicalPlanPath: exports_external.string().min(1).optional()
12294
+ })
12295
+ });
12296
+ RepoRelativePathSchema = exports_external.string().min(1).refine((path) => !/^([/\\]|[A-Za-z]:)/.test(path) && !path.split(/[/\\]/).includes(".."), 'Path must be relative to the repository and must not contain ".." segments');
12243
12297
  DeliveryLedgerRequestSchema = exports_external.object({
12244
12298
  id: exports_external.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/),
12245
12299
  authorized: exports_external.boolean(),
12246
12300
  route: DeliveryRouteSchema,
12247
12301
  taskCard: CanonicalTaskCardSchema,
12248
12302
  tasks: exports_external.array(DeliveryLedgerTaskSchema).min(1),
12249
- nextStep: exports_external.string().min(1)
12303
+ nextStep: exports_external.string().min(1),
12304
+ technicalPlanPath: RepoRelativePathSchema.optional()
12250
12305
  });
12251
12306
  TaskOutcomeSchema = exports_external.object({
12252
12307
  id: exports_external.string(),
@@ -12470,30 +12525,45 @@ var init_target_compatibility = __esm(() => {
12470
12525
  import { readFile as readFile3 } from "node:fs/promises";
12471
12526
  import { join } from "node:path";
12472
12527
  async function loadManifest(target) {
12528
+ const cached = manifestCache.get(target);
12529
+ if (cached)
12530
+ return cached;
12473
12531
  const manifestPath = join(MANIFESTS_DIR, `${target}.yml`);
12474
12532
  const content = await readFile3(manifestPath, "utf-8");
12475
12533
  const data = $parse(content);
12476
- return InstallManifestSchema.parse(data);
12534
+ const manifest = InstallManifestSchema.parse(data);
12535
+ manifestCache.set(target, manifest);
12536
+ return manifest;
12477
12537
  }
12478
12538
  async function loadModelConfig(target) {
12539
+ const cached = modelConfigCache.get(target);
12540
+ if (cached)
12541
+ return cached;
12479
12542
  const [rolesRaw, targetRaw] = await Promise.all([
12480
12543
  readFile3(join(MODELS_DIR, "roles.yml"), "utf-8"),
12481
12544
  readFile3(join(MODELS_DIR, `${target}.yml`), "utf-8")
12482
12545
  ]);
12483
12546
  const roles = $parse(rolesRaw);
12484
12547
  const targetData = $parse(targetRaw);
12485
- return ModelConfigSchema.parse({
12548
+ const config = ModelConfigSchema.parse({
12486
12549
  target: targetData.target,
12487
12550
  agents: roles.agents,
12488
12551
  tools: targetData.permissions ? undefined : roles.tools,
12489
12552
  permissions: targetData.permissions
12490
12553
  });
12554
+ modelConfigCache.set(target, config);
12555
+ return config;
12491
12556
  }
12492
12557
  async function loadTargetCapabilities(target) {
12558
+ const cached = capabilitiesCache.get(target);
12559
+ if (cached)
12560
+ return cached;
12493
12561
  const content = await readFile3(join(TARGETS_DIR, `${target}.yml`), "utf-8");
12494
- return TargetCapabilitiesSchema.parse($parse(content));
12562
+ const capabilities = TargetCapabilitiesSchema.parse($parse(content));
12563
+ capabilitiesCache.set(target, capabilities);
12564
+ return capabilities;
12495
12565
  }
12496
- var MANIFESTS_DIR, MODELS_DIR, TARGETS_DIR, PRESETS_DIR;
12566
+ var MANIFESTS_DIR, MODELS_DIR, TARGETS_DIR, PRESETS_DIR, manifestCache, modelConfigCache, capabilitiesCache;
12497
12567
  var init_manifest_loader = __esm(() => {
12498
12568
  init_dist();
12499
12569
  init_schemas();
@@ -12502,6 +12572,9 @@ var init_manifest_loader = __esm(() => {
12502
12572
  MODELS_DIR = join(SRC_PRESETS_DIR, "models");
12503
12573
  TARGETS_DIR = join(SRC_PRESETS_DIR, "targets");
12504
12574
  PRESETS_DIR = ROOT_PRESETS_DIR;
12575
+ manifestCache = new Map;
12576
+ modelConfigCache = new Map;
12577
+ capabilitiesCache = new Map;
12505
12578
  });
12506
12579
 
12507
12580
  // src/core/i18n/language-instructions.ts
@@ -12752,23 +12825,23 @@ var init_safety = __esm(() => {
12752
12825
  // src/core/filesystem/path-containment.ts
12753
12826
  import { constants as constants2 } from "node:fs";
12754
12827
  import { lstat, mkdir, open, realpath } from "node:fs/promises";
12755
- import { basename as basename2, dirname as dirname2, isAbsolute, join as join2, relative, resolve as resolve3, sep as sep2 } from "node:path";
12828
+ import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative, resolve as resolve3, sep as sep2 } from "node:path";
12756
12829
  function isInside(root, target) {
12757
12830
  const rel = relative(root, target);
12758
- return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute(rel);
12831
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute2(rel);
12759
12832
  }
12760
12833
  function isProtectedUnderRoot(rootReal, absolutePath) {
12761
12834
  if (absolutePath === rootReal) {
12762
12835
  return false;
12763
12836
  }
12764
12837
  const relFromRoot = relative(rootReal, absolutePath);
12765
- if (relFromRoot === "" || relFromRoot === ".." || relFromRoot.startsWith(`..${sep2}`) || isAbsolute(relFromRoot)) {
12838
+ if (relFromRoot === "" || relFromRoot === ".." || relFromRoot.startsWith(`..${sep2}`) || isAbsolute2(relFromRoot)) {
12766
12839
  return true;
12767
12840
  }
12768
12841
  return !validateWritePath(relFromRoot);
12769
12842
  }
12770
12843
  async function resolveWithinRoot(projectRoot, relPath) {
12771
- if (isAbsolute(relPath)) {
12844
+ if (isAbsolute2(relPath)) {
12772
12845
  return;
12773
12846
  }
12774
12847
  const rootAbs = resolve3(projectRoot);
@@ -12833,7 +12906,7 @@ async function canonicalizeExistingPrefix(target) {
12833
12906
  }
12834
12907
  }
12835
12908
  async function resolveOutputWithinRoot(projectRoot, relPath) {
12836
- if (relPath === "" || isAbsolute(relPath)) {
12909
+ if (relPath === "" || isAbsolute2(relPath)) {
12837
12910
  return;
12838
12911
  }
12839
12912
  const rootAbs = resolve3(projectRoot);
@@ -12952,7 +13025,7 @@ async function writeContainedFile(projectRoot, relPath, content, options) {
12952
13025
  }
12953
13026
  }
12954
13027
  async function preflightContainedOutput(projectRoot, relPath, options) {
12955
- if (relPath === "" || isAbsolute(relPath)) {
13028
+ if (relPath === "" || isAbsolute2(relPath)) {
12956
13029
  return `Invalid --output path: ${relPath}. It must be relative to the project root.`;
12957
13030
  }
12958
13031
  if (!validateWritePath(relPath)) {
@@ -13289,9 +13362,8 @@ function substituteToolNames(content, modelConfig) {
13289
13362
  }
13290
13363
  async function injectMcpServers(jsonString, filePath) {
13291
13364
  const isAgyMcpConfig = filePath.endsWith("mcp_config.json");
13292
- const isClaudeSettings = filePath.endsWith("settings.json");
13293
13365
  const isOpencodeJsonc = filePath.endsWith("opencode.jsonc") || filePath.endsWith("opencode.json");
13294
- if (!isAgyMcpConfig && !isClaudeSettings && !isOpencodeJsonc) {
13366
+ if (!isAgyMcpConfig && !isOpencodeJsonc) {
13295
13367
  return jsonString;
13296
13368
  }
13297
13369
  try {
@@ -13306,7 +13378,7 @@ async function injectMcpServers(jsonString, filePath) {
13306
13378
  mcpServers["token-savior-recall"] = {
13307
13379
  command: "token-savior",
13308
13380
  env: {
13309
- TOKEN_SAVIOR_CLIENT: isClaudeSettings ? "claude-code" : isOpencodeJsonc ? "opencode" : "agy"
13381
+ TOKEN_SAVIOR_CLIENT: isOpencodeJsonc ? "opencode" : "agy"
13310
13382
  }
13311
13383
  };
13312
13384
  }
@@ -13319,7 +13391,7 @@ async function injectMcpServers(jsonString, filePath) {
13319
13391
  if (Object.keys(mcpServers).length === 0) {
13320
13392
  return jsonString;
13321
13393
  }
13322
- if (isClaudeSettings || isAgyMcpConfig) {
13394
+ if (isAgyMcpConfig) {
13323
13395
  obj.mcpServers = mergeDeep(obj.mcpServers || {}, mcpServers);
13324
13396
  } else if (isOpencodeJsonc) {
13325
13397
  const opencodeMcp = {};
@@ -13342,7 +13414,7 @@ async function injectMcpServers(jsonString, filePath) {
13342
13414
  return jsonString;
13343
13415
  }
13344
13416
  }
13345
- async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTemplate, modelConfig, locale, baseDir) {
13417
+ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTemplate, modelConfig, locale, baseDir, preRendered) {
13346
13418
  if (strategy === "skip") {
13347
13419
  return { src: srcPath, dest: destPath, action: "skipped", dryRun };
13348
13420
  }
@@ -13363,16 +13435,21 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
13363
13435
  return { src: srcPath, dest: destPath, action: "error", error: String(error) };
13364
13436
  }
13365
13437
  }
13366
- if (strategy === "overwrite" && !force && await fileExists2(destPath)) {
13438
+ if ((strategy === "overwrite" && !force || strategy === "create-only") && await fileExists2(destPath)) {
13367
13439
  return { src: srcPath, dest: destPath, action: "skipped", dryRun };
13368
13440
  }
13369
- let content;
13370
- try {
13371
- content = await readFile4(srcPath, "utf-8");
13372
- } catch (e) {
13373
- return { src: srcPath, dest: destPath, action: "error", error: `Cannot read source: ${e}` };
13441
+ let incomingContent;
13442
+ if (preRendered !== undefined) {
13443
+ incomingContent = preRendered;
13444
+ } else {
13445
+ let content;
13446
+ try {
13447
+ content = await readFile4(srcPath, "utf-8");
13448
+ } catch (e) {
13449
+ return { src: srcPath, dest: destPath, action: "error", error: `Cannot read source: ${e}` };
13450
+ }
13451
+ incomingContent = isTemplate && modelConfig ? await renderTemplate(content, modelConfig, srcPath, locale) : content;
13374
13452
  }
13375
- let incomingContent = isTemplate && modelConfig ? await renderTemplate(content, modelConfig, srcPath, locale) : content;
13376
13453
  incomingContent = await injectMcpServers(incomingContent, destPath);
13377
13454
  let finalContent = incomingContent;
13378
13455
  let action = "written";
@@ -13419,7 +13496,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
13419
13496
  }
13420
13497
  }
13421
13498
  if (dryRun) {
13422
- return { src: srcPath, dest: destPath, action, dryRun: true };
13499
+ return { src: srcPath, dest: destPath, action, dryRun: true, renderedContent: finalContent };
13423
13500
  }
13424
13501
  if (baseDir === undefined) {
13425
13502
  return {
@@ -13433,12 +13510,12 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
13433
13510
  await writeContainedFile(baseDir, relative2(baseDir, destPath), finalContent, {
13434
13511
  force: true
13435
13512
  });
13436
- return { src: srcPath, dest: destPath, action };
13513
+ return { src: srcPath, dest: destPath, action, renderedContent: finalContent };
13437
13514
  } catch (e) {
13438
13515
  return { src: srcPath, dest: destPath, action: "error", error: String(e) };
13439
13516
  }
13440
13517
  }
13441
- async function copyFromManifest(manifest, presetsDir, baseDir, isGlobal, dryRun, force, modelConfig = null, locale = "en") {
13518
+ async function copyFromManifest(manifest, presetsDir, baseDir, isGlobal, dryRun, force, modelConfig = null, locale = "en", preRendered) {
13442
13519
  const results = [];
13443
13520
  for (const entry of manifest.entries) {
13444
13521
  const strategy = isGlobal && entry.globalStrategy ? entry.globalStrategy : entry.strategy;
@@ -13452,7 +13529,7 @@ async function copyFromManifest(manifest, presetsDir, baseDir, isGlobal, dryRun,
13452
13529
  const files = await resolveEntryFiles(resolvedEntry, presetsDir, resolvedBaseDir);
13453
13530
  const isTemplate = entry.template === true;
13454
13531
  for (const { src, dest } of files) {
13455
- results.push(await applySingleFile(src, dest, strategy, force, dryRun, isTemplate, modelConfig, locale, resolvedBaseDir));
13532
+ results.push(await applySingleFile(src, dest, strategy, force, dryRun, isTemplate, modelConfig, locale, resolvedBaseDir, preRendered?.[dest]));
13456
13533
  }
13457
13534
  }
13458
13535
  return results;
@@ -13589,6 +13666,9 @@ __export(exports_preset_loader, {
13589
13666
  import { readFile as readFile6 } from "node:fs/promises";
13590
13667
  import { resolve as resolve6 } from "node:path";
13591
13668
  async function loadPreset(name, projectRoot = process.cwd()) {
13669
+ if (!PRESET_NAME_RE.test(name)) {
13670
+ return err(new ValidationError(`Invalid preset name: ${name}`));
13671
+ }
13592
13672
  const candidates = [
13593
13673
  resolve6(projectRoot, ".codeconductor", "presets", `${name}.yml`),
13594
13674
  resolve6(SRC_PRESETS_DIR, name, `${name}.yml`)
@@ -13616,11 +13696,13 @@ async function loadPreset(name, projectRoot = process.cwd()) {
13616
13696
  async function loadCouncilPreset(projectRoot) {
13617
13697
  return loadPreset("council", projectRoot);
13618
13698
  }
13699
+ var PRESET_NAME_RE;
13619
13700
  var init_preset_loader = __esm(() => {
13620
13701
  init_dist();
13621
13702
  init_errors();
13622
13703
  init_schemas();
13623
13704
  init_package_paths();
13705
+ PRESET_NAME_RE = /^[a-z0-9-]+$/;
13624
13706
  });
13625
13707
 
13626
13708
  // src/domain/council/council-agent.ts
@@ -14004,6 +14086,11 @@ ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).joi
14004
14086
 
14005
14087
  ## Usage
14006
14088
  Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
14089
+ Use Sonnet 5.5 for the routine council pass. Share one concise evidence summary and
14090
+ the relevant diff with the necessary roles. Invoke council-architect with
14091
+ \`model: opus\` only for
14092
+ one unresolved business-logic question; return its short decision to Sonnet for
14093
+ the verdict. Do not run every council role on Opus or repeat full context.
14007
14094
 
14008
14095
  ## Context
14009
14096
  ${spec.outputContract}
@@ -14118,7 +14205,8 @@ function generateCodexFiles(spec) {
14118
14205
  files.push({
14119
14206
  path: ".codex/config.toml",
14120
14207
  content: generateCodexConfig(spec),
14121
- overwrite: false
14208
+ overwrite: false,
14209
+ mergeExisting: (existing) => mergeCodexCouncilConfig(existing, spec)
14122
14210
  });
14123
14211
  for (const agent of spec.agents) {
14124
14212
  files.push({
@@ -14134,17 +14222,32 @@ function generateCodexFiles(spec) {
14134
14222
  });
14135
14223
  return files;
14136
14224
  }
14137
- function generateCodexConfig(spec) {
14138
- const agentTables = spec.agents.map((agent) => {
14139
- const focusAreas = agent.focus.join(", ");
14140
- return `
14225
+ function agentTable(agent) {
14226
+ const focusAreas = agent.focus.join(", ");
14227
+ return `
14141
14228
  [agents.${agent.id}]
14142
14229
  description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
14230
+ config_file = "agents/council_${agent.id}.toml"
14143
14231
  nickname_candidates = ["${agent.role}", "Council ${agent.role}"]`;
14144
- }).join(`
14232
+ }
14233
+ function mergeCodexCouncilConfig(existing, spec) {
14234
+ const missing = spec.agents.filter((agent) => !existing.split(`
14235
+ `).some((line) => line.trim() === `[agents.${agent.id}]`));
14236
+ if (missing.length === 0)
14237
+ return existing;
14238
+ return `${existing.trimEnd()}
14239
+ ${missing.map(agentTable).join(`
14240
+ `)}
14241
+ `;
14242
+ }
14243
+ function generateCodexConfig(spec) {
14244
+ const agentTables = spec.agents.map(agentTable).join(`
14145
14245
  `);
14146
14246
  return `# Codex Council Configuration
14147
14247
 
14248
+ model = "gpt-6.1-sol"
14249
+ model_reasoning_effort = "medium"
14250
+
14148
14251
  [project]
14149
14252
  name = "council"
14150
14253
  version = "${spec.version}"
@@ -14154,7 +14257,9 @@ ${agentTables}
14154
14257
  function generateCodexAgent(agent) {
14155
14258
  const focusAreas = agent.focus.join(", ");
14156
14259
  const checklist = checklistFor(agent.id).join("; ");
14157
- return `name = "${agent.role}"
14260
+ return `model = "gpt-6.1-sol"
14261
+ model_reasoning_effort = "medium"
14262
+ name = "${agent.role}"
14158
14263
  description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
14159
14264
  nickname_candidates = ["${agent.role}", "Council ${agent.role}"]
14160
14265
  developer_instructions = "You are the ${agent.role} council agent. Your focus areas are: ${focusAreas}. Context: ${agent.context}. Apply ${agent.modelHint} reasoning to your analysis. Review checklist: ${checklist}. Categorize findings as CRITICAL (blocks the verdict), WARNING, or SUGGESTION."
@@ -14173,6 +14278,9 @@ ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).joi
14173
14278
 
14174
14279
  ## Usage
14175
14280
  Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
14281
+ Keep GPT-6.1 Sol at medium reasoning effort. Share a concise evidence summary
14282
+ and relevant diff with only the necessary council roles. Reuse findings in the
14283
+ final verdict instead of replaying the full transcript or repeating analysis.
14176
14284
  `;
14177
14285
  }
14178
14286
  var init_codex_council_generator = __esm(() => {
@@ -14381,6 +14489,9 @@ function getTargetInstallationPath(target, basePath, isGlobal) {
14381
14489
  if (target === "agy") {
14382
14490
  return isGlobal ? resolve7(basePath, ".gemini", "config") : resolve7(basePath, ".agents");
14383
14491
  }
14492
+ if (target === "muse" && isGlobal) {
14493
+ return resolve7(basePath, ".agents");
14494
+ }
14384
14495
  return resolve7(basePath, `.${target}`);
14385
14496
  }
14386
14497
  async function isTargetInstalled(target, basePath, isGlobal) {
@@ -14607,6 +14718,7 @@ async function checkUpdates(basePath, isGlobal, includeConflicts = false) {
14607
14718
  locale = configResult.data.defaults.locale ?? "en";
14608
14719
  }
14609
14720
  } catch {}
14721
+ const rendered = {};
14610
14722
  const checkTarget = async (target) => {
14611
14723
  const isInstalled = await isTargetInstalled(target, basePath, isGlobal);
14612
14724
  if (!isInstalled) {
@@ -14641,6 +14753,7 @@ async function checkUpdates(basePath, isGlobal, includeConflicts = false) {
14641
14753
  try {
14642
14754
  const srcContent = await readFile7(src, "utf-8");
14643
14755
  expectedContent = isTemplate && modelConfig ? await renderTemplate(srcContent, modelConfig, src, locale) : srcContent;
14756
+ rendered[dest] = expectedContent;
14644
14757
  } catch {
14645
14758
  continue;
14646
14759
  }
@@ -14725,7 +14838,8 @@ async function checkUpdates(basePath, isGlobal, includeConflicts = false) {
14725
14838
  let destContent;
14726
14839
  try {
14727
14840
  destContent = await readFile7(dest, "utf-8");
14728
- if (destContent.trim() !== f.content.trim()) {
14841
+ const expected = f.mergeExisting ? f.mergeExisting(destContent) : f.content;
14842
+ if (destContent.trim() !== expected.trim()) {
14729
14843
  changedFiles.push(dest);
14730
14844
  }
14731
14845
  } catch {
@@ -14778,7 +14892,8 @@ async function checkUpdates(basePath, isGlobal, includeConflicts = false) {
14778
14892
  council: councilHasUpdate,
14779
14893
  policy: policyHasUpdate,
14780
14894
  targets: targetResults,
14781
- skills: skillResults
14895
+ skills: skillResults,
14896
+ rendered
14782
14897
  };
14783
14898
  }
14784
14899
  var cachedRegistry, registryLoadAttempted = false;
@@ -15547,6 +15662,7 @@ var init_profiles = __esm(() => {
15547
15662
  { id: "implement", agent: "implementer" },
15548
15663
  {
15549
15664
  id: "council-review",
15665
+ agents: ["architect", "product", "delivery", "data-ops", "security-reviewer", "devil"],
15550
15666
  skill: "council",
15551
15667
  outputSchema: "council-verdict"
15552
15668
  }
@@ -16202,6 +16318,8 @@ function resolveAssets(target) {
16202
16318
  return ["AGENTS.md", "rules", "workflows", "skills", "prompts/v1.0.0"];
16203
16319
  case "pi":
16204
16320
  return ["AGENTS.md", "settings.json", "prompts", "skills", "agents", "prompts/v1.0.0"];
16321
+ case "muse":
16322
+ return ["AGENTS.md", "skills", "hooks.json"];
16205
16323
  }
16206
16324
  }
16207
16325
  var CURRENT_PRESET_VERSION = "v1.0.0";
@@ -16910,7 +17028,7 @@ __export(exports_file_writer, {
16910
17028
  writeGeneratedFiles: () => writeGeneratedFiles,
16911
17029
  writeSingleFile: () => writeSingleFile
16912
17030
  });
16913
- import { access as access5 } from "node:fs/promises";
17031
+ import { access as access5, readFile as readFile11 } from "node:fs/promises";
16914
17032
  import { relative as relative4 } from "node:path";
16915
17033
  async function writeGeneratedFiles(files, options) {
16916
17034
  const results = [];
@@ -16937,7 +17055,15 @@ async function writeGeneratedFiles(files, options) {
16937
17055
  });
16938
17056
  continue;
16939
17057
  }
16940
- if (!options.force) {
17058
+ let content = file.content;
17059
+ let merged = false;
17060
+ if (file.mergeExisting) {
17061
+ try {
17062
+ content = file.mergeExisting(await readFile11(file.path, "utf-8"));
17063
+ merged = true;
17064
+ } catch {}
17065
+ }
17066
+ if (!options.force && !merged) {
16941
17067
  try {
16942
17068
  await access5(file.path);
16943
17069
  results.push({
@@ -16957,7 +17083,7 @@ async function writeGeneratedFiles(files, options) {
16957
17083
  continue;
16958
17084
  }
16959
17085
  try {
16960
- await writeContainedFile(options.projectRoot, relative4(options.projectRoot, file.path), file.content, { force: true });
17086
+ await writeContainedFile(options.projectRoot, relative4(options.projectRoot, file.path), content, { force: true });
16961
17087
  results.push({
16962
17088
  path: file.path,
16963
17089
  success: true
@@ -16997,7 +17123,7 @@ __export(exports_install_command, {
16997
17123
  installPresetCommand: () => installPresetCommand,
16998
17124
  verifyRenderedFiles: () => verifyRenderedFiles
16999
17125
  });
17000
- import { readFile as readFile11 } from "node:fs/promises";
17126
+ import { readFile as readFile12 } from "node:fs/promises";
17001
17127
  import { homedir as homedir7 } from "node:os";
17002
17128
  import { basename as basename5, dirname as dirname6, resolve as resolve12, sep as sep3 } from "node:path";
17003
17129
  async function installCommand(options) {
@@ -17128,11 +17254,13 @@ async function verifyRenderedFiles(manifest, baseDir, results) {
17128
17254
  const isTemplateFile = templatePrefixes.some((p) => r.dest === p || r.dest.startsWith(p + sep3));
17129
17255
  if (!isTemplateFile)
17130
17256
  continue;
17131
- let content;
17132
- try {
17133
- content = await readFile11(r.dest, "utf-8");
17134
- } catch {
17135
- continue;
17257
+ let content = r.renderedContent;
17258
+ if (content === undefined) {
17259
+ try {
17260
+ content = await readFile12(r.dest, "utf-8");
17261
+ } catch {
17262
+ continue;
17263
+ }
17136
17264
  }
17137
17265
  const leaked = [...new Set(content.match(/\{\{[A-Z_]+\}\}/g) ?? [])];
17138
17266
  if (leaked.length > 0) {
@@ -17756,7 +17884,7 @@ var init_opencode_lsp_generator = __esm(() => {
17756
17884
 
17757
17885
  // src/core/lsp/binary-integrity.ts
17758
17886
  import { createHash as createHash2 } from "node:crypto";
17759
- import { isAbsolute as isAbsolute2, resolve as resolve14, sep as sep4 } from "node:path";
17887
+ import { isAbsolute as isAbsolute3, resolve as resolve14, sep as sep4 } from "node:path";
17760
17888
  function assertPinnedBinaryUrl(url) {
17761
17889
  let parsed;
17762
17890
  try {
@@ -17789,7 +17917,7 @@ function assertSha256Hex(content, expectedHex) {
17789
17917
  }
17790
17918
  function resolveSafeArchiveEntry(destRoot, entry) {
17791
17919
  const normalized = entry.replace(/\\/g, "/");
17792
- if (normalized === "" || isAbsolute2(normalized) || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized) || normalized.split("/").includes("..")) {
17920
+ if (normalized === "" || isAbsolute3(normalized) || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized) || normalized.split("/").includes("..")) {
17793
17921
  throw new Error(`Unsafe archive entry rejected: ${entry}`);
17794
17922
  }
17795
17923
  const root = resolve14(destRoot);
@@ -18835,26 +18963,40 @@ var init_install_lsp_command = __esm(() => {
18835
18963
  });
18836
18964
 
18837
18965
  // src/core/install/update-transaction.ts
18838
- import { mkdir as mkdir7, readFile as readFile12, rm as rm2, unlink, writeFile as writeFile6 } from "node:fs/promises";
18839
- import { dirname as dirname7, join as join10, relative as relative5, resolve as resolve16 } from "node:path";
18966
+ import { mkdir as mkdir7, readFile as readFile13, rm as rm2, unlink, writeFile as writeFile6 } from "node:fs/promises";
18967
+ import { dirname as dirname7, isAbsolute as isAbsolute4, join as join10, relative as relative5, resolve as resolve16, sep as sep5 } from "node:path";
18968
+ function assertInsideBase(basePath, destination) {
18969
+ const base = resolve16(basePath);
18970
+ const absolute = resolve16(base, destination);
18971
+ const rel = relative5(base, absolute);
18972
+ if (rel === "" || rel === ".." || rel.startsWith(`..${sep5}`) || isAbsolute4(rel)) {
18973
+ throw new Error(`Update destination escapes the base path: ${destination}`);
18974
+ }
18975
+ return rel;
18976
+ }
18840
18977
 
18841
18978
  class UpdateTransaction {
18842
18979
  snapshots;
18843
18980
  backupDir;
18844
- constructor(snapshots, backupDir) {
18981
+ basePath;
18982
+ constructor(snapshots, backupDir, basePath) {
18845
18983
  this.snapshots = snapshots;
18846
18984
  this.backupDir = backupDir;
18985
+ this.basePath = basePath;
18847
18986
  }
18848
18987
  static async begin(basePath, destinations) {
18849
18988
  const unique = [...new Set(destinations)];
18989
+ for (const destination of unique) {
18990
+ assertInsideBase(basePath, destination);
18991
+ }
18850
18992
  const backupDir = resolve16(basePath, ".codeconductor", "backups", `update-${Date.now()}`);
18851
18993
  await mkdir7(backupDir, { recursive: true });
18852
18994
  const snapshots = [];
18853
18995
  for (const destination of unique) {
18854
18996
  try {
18855
- const content = await readFile12(destination);
18997
+ const content = await readFile13(destination);
18856
18998
  snapshots.push({ path: destination, existed: true, content });
18857
- const backupPath = join10(backupDir, relative5(basePath, destination));
18999
+ const backupPath = join10(backupDir, relative5(resolve16(basePath), resolve16(destination)));
18858
19000
  await mkdir7(dirname7(backupPath), { recursive: true });
18859
19001
  await writeFile6(backupPath, content);
18860
19002
  } catch (error) {
@@ -18865,15 +19007,20 @@ class UpdateTransaction {
18865
19007
  }
18866
19008
  }
18867
19009
  }
18868
- return new UpdateTransaction(snapshots, backupDir);
19010
+ return new UpdateTransaction(snapshots, backupDir, basePath);
18869
19011
  }
18870
19012
  async rollback() {
18871
19013
  for (const snapshot of this.snapshots) {
19014
+ const rel = assertInsideBase(this.basePath, snapshot.path);
19015
+ const gated = await resolveOutputWithinRoot(this.basePath, rel);
19016
+ if (gated === undefined) {
19017
+ throw new Error(`Rollback destination is no longer contained: ${snapshot.path}`);
19018
+ }
18872
19019
  if (snapshot.existed) {
18873
- await mkdir7(dirname7(snapshot.path), { recursive: true });
18874
- await writeFile6(snapshot.path, snapshot.content);
19020
+ await mkdir7(dirname7(gated), { recursive: true });
19021
+ await writeFile6(gated, snapshot.content);
18875
19022
  } else {
18876
- await unlink(snapshot.path).catch((error) => {
19023
+ await unlink(gated).catch((error) => {
18877
19024
  if (error.code !== "ENOENT")
18878
19025
  throw error;
18879
19026
  });
@@ -18885,7 +19032,9 @@ class UpdateTransaction {
18885
19032
  await rm2(this.backupDir, { recursive: true, force: true });
18886
19033
  }
18887
19034
  }
18888
- var init_update_transaction = () => {};
19035
+ var init_update_transaction = __esm(() => {
19036
+ init_path_containment();
19037
+ });
18889
19038
 
18890
19039
  // src/commands/update.command.ts
18891
19040
  var exports_update_command = {};
@@ -18893,8 +19042,8 @@ __export(exports_update_command, {
18893
19042
  updateCommand: () => updateCommand
18894
19043
  });
18895
19044
  import { homedir as homedir10 } from "node:os";
18896
- import { resolve as resolve17, dirname as dirname8 } from "node:path";
18897
- import { mkdir as mkdir8, readFile as readFile13, writeFile as writeFile7, stat as stat3 } from "node:fs/promises";
19045
+ import { join as join11, resolve as resolve17 } from "node:path";
19046
+ import { readFile as readFile14, stat as stat3 } from "node:fs/promises";
18898
19047
  async function updateCommand(options) {
18899
19048
  const { dryRun, force, global: isGlobal, output, projectRoot, check = false } = options;
18900
19049
  const basePath = isGlobal ? homedir10() : projectRoot;
@@ -19000,24 +19149,20 @@ async function updateCommand(options) {
19000
19149
  ];
19001
19150
  transaction = await UpdateTransaction.begin(basePath, plannedDestinations);
19002
19151
  if (updateResults.council) {
19003
- const localCouncil = resolve17(basePath, ".codeconductor", "presets", "council.yml");
19004
19152
  const bundledCouncil = resolve17(SRC_PRESETS_DIR, "council", "council.yml");
19005
19153
  try {
19006
- const content = await readFile13(bundledCouncil, "utf-8");
19007
- await mkdir8(dirname8(localCouncil), { recursive: true });
19008
- await writeFile7(localCouncil, content, "utf-8");
19009
- updated.push(localCouncil);
19154
+ const content = await readFile14(bundledCouncil, "utf-8");
19155
+ const written = await writeContainedFile(basePath, join11(".codeconductor", "presets", "council.yml"), content, { force: true });
19156
+ updated.push(written);
19010
19157
  } catch (e) {
19011
19158
  throw new Error(`Failed to update council.yml: ${e}`);
19012
19159
  }
19013
19160
  }
19014
19161
  if (updateResults.policy) {
19015
- const localPolicy = resolve17(basePath, ".codeconductor", "presets", "policy.yml");
19016
19162
  try {
19017
- const content = await readFile13(POLICY_PATH, "utf-8");
19018
- await mkdir8(dirname8(localPolicy), { recursive: true });
19019
- await writeFile7(localPolicy, content, "utf-8");
19020
- updated.push(localPolicy);
19163
+ const content = await readFile14(POLICY_PATH, "utf-8");
19164
+ const written = await writeContainedFile(basePath, join11(".codeconductor", "presets", "policy.yml"), content, { force: true });
19165
+ updated.push(written);
19021
19166
  } catch (e) {
19022
19167
  throw new Error(`Failed to update policy.yml: ${e}`);
19023
19168
  }
@@ -19028,7 +19173,7 @@ async function updateCommand(options) {
19028
19173
  const targetName = t.target;
19029
19174
  const manifest = await loadManifest(targetName);
19030
19175
  const modelConfig = await loadModelConfig(targetName);
19031
- const results = await copyFromManifest(manifest, PRESETS_DIR, basePath, isGlobal, false, force, modelConfig, locale);
19176
+ const results = await copyFromManifest(manifest, PRESETS_DIR, basePath, isGlobal, false, force, modelConfig, locale, updateResults.rendered);
19032
19177
  for (const r of results) {
19033
19178
  if (r.action !== "skipped" && r.action !== "error") {
19034
19179
  updated.push(r.dest);
@@ -19095,20 +19240,19 @@ async function updateCommand(options) {
19095
19240
  for (const s of updateResults.skills) {
19096
19241
  newSkillsLock[s.id] = s.latestVersion;
19097
19242
  }
19098
- let lockDest = resolve17(basePath, ".codeconductor", "skills-lock.json");
19243
+ let lockRel = join11(".codeconductor", "skills-lock.json");
19099
19244
  try {
19100
19245
  const statAgents = await stat3(resolve17(basePath, ".agents"));
19101
19246
  if (statAgents.isDirectory()) {
19102
19247
  const statCodeConductor = await stat3(resolve17(basePath, ".codeconductor")).catch(() => null);
19103
19248
  if (!statCodeConductor) {
19104
- lockDest = resolve17(basePath, ".agents", "skills-lock.json");
19249
+ lockRel = join11(".agents", "skills-lock.json");
19105
19250
  }
19106
19251
  }
19107
19252
  } catch {}
19108
19253
  try {
19109
- await mkdir8(dirname8(lockDest), { recursive: true });
19110
- await writeFile7(lockDest, JSON.stringify(newSkillsLock, null, 2), "utf-8");
19111
- updated.push(lockDest);
19254
+ const written = await writeContainedFile(basePath, lockRel, JSON.stringify(newSkillsLock, null, 2), { force: true });
19255
+ updated.push(written);
19112
19256
  } catch (e) {
19113
19257
  throw new Error(`Failed to write skills-lock.json: ${e}`);
19114
19258
  }
@@ -19155,6 +19299,7 @@ async function updateCommand(options) {
19155
19299
  }
19156
19300
  }
19157
19301
  var init_update_command = __esm(() => {
19302
+ init_path_containment();
19158
19303
  init_config_loader();
19159
19304
  init_update_checker();
19160
19305
  init_file_copier();
@@ -19172,9 +19317,9 @@ __export(exports_migrate_command, {
19172
19317
  migrateCommand: () => migrateCommand,
19173
19318
  migratePermissionRules: () => migratePermissionRules
19174
19319
  });
19175
- import { readFile as readFile14, readdir as readdir3, rm as rm3, writeFile as writeFile8 } from "node:fs/promises";
19320
+ import { readFile as readFile15, readdir as readdir3, rm as rm3, writeFile as writeFile7 } from "node:fs/promises";
19176
19321
  import { homedir as homedir11 } from "node:os";
19177
- import { join as join11, resolve as resolve18 } from "node:path";
19322
+ import { join as join12, resolve as resolve18 } from "node:path";
19178
19323
  function migratePermissionRules(settings) {
19179
19324
  const permissions = settings.permissions;
19180
19325
  if (typeof permissions !== "object" || permissions === null) {
@@ -19221,7 +19366,7 @@ function resolveSettingsPath(options) {
19221
19366
  if (options.file)
19222
19367
  return resolve18(options.projectRoot, options.file);
19223
19368
  const base = options.global ? homedir11() : options.projectRoot;
19224
- return join11(base, ".claude", "settings.json");
19369
+ return join12(base, ".claude", "settings.json");
19225
19370
  }
19226
19371
  async function findOrphanedPromptVersions(basePath, isGlobal) {
19227
19372
  const found = [];
@@ -19231,7 +19376,7 @@ async function findOrphanedPromptVersions(basePath, isGlobal) {
19231
19376
  if (!installed)
19232
19377
  continue;
19233
19378
  const installPath = getTargetInstallationPath(target, basePath, isGlobal);
19234
- const promptsDir = join11(installPath, "prompts");
19379
+ const promptsDir = join12(installPath, "prompts");
19235
19380
  if (seenDirs.has(promptsDir))
19236
19381
  continue;
19237
19382
  seenDirs.add(promptsDir);
@@ -19246,7 +19391,7 @@ async function findOrphanedPromptVersions(basePath, isGlobal) {
19246
19391
  continue;
19247
19392
  if (entry.name === CURRENT_PRESET_VERSION)
19248
19393
  continue;
19249
- found.push({ target, path: join11(promptsDir, entry.name), version: entry.name });
19394
+ found.push({ target, path: join12(promptsDir, entry.name), version: entry.name });
19250
19395
  }
19251
19396
  }
19252
19397
  return found;
@@ -19255,7 +19400,7 @@ async function migrateCommand(options) {
19255
19400
  const settingsPath = resolveSettingsPath(options);
19256
19401
  let raw = null;
19257
19402
  try {
19258
- raw = await readFile14(settingsPath, "utf-8");
19403
+ raw = await readFile15(settingsPath, "utf-8");
19259
19404
  } catch (error) {
19260
19405
  if (error.code !== "ENOENT") {
19261
19406
  return {
@@ -19290,7 +19435,7 @@ async function migrateCommand(options) {
19290
19435
  duplicatesRemoved = migration.duplicatesRemoved;
19291
19436
  settingsChanged = migration.changes.length > 0 || migration.duplicatesRemoved.length > 0;
19292
19437
  if (settingsChanged && !options.dryRun) {
19293
- await writeFile8(settingsPath, `${JSON.stringify(migration.settings, null, 2)}
19438
+ await writeFile7(settingsPath, `${JSON.stringify(migration.settings, null, 2)}
19294
19439
  `, "utf-8");
19295
19440
  }
19296
19441
  }
@@ -19528,7 +19673,7 @@ __export(exports_help_command, {
19528
19673
  helpCommand: () => helpCommand
19529
19674
  });
19530
19675
  import { readdir as readdir4 } from "node:fs/promises";
19531
- import { join as join12 } from "node:path";
19676
+ import { join as join13 } from "node:path";
19532
19677
  async function scanPresetDir(projectRoot, target) {
19533
19678
  const inventory = {
19534
19679
  target,
@@ -19537,12 +19682,12 @@ async function scanPresetDir(projectRoot, target) {
19537
19682
  agents: [],
19538
19683
  workflows: []
19539
19684
  };
19540
- const presetDir = join12(projectRoot, "presets", target);
19685
+ const presetDir = join13(projectRoot, "presets", target);
19541
19686
  try {
19542
19687
  const entries = await readdir4(presetDir, { withFileTypes: true });
19543
19688
  for (const entry of entries) {
19544
19689
  if (entry.isDirectory()) {
19545
- const subDir = join12(presetDir, entry.name);
19690
+ const subDir = join13(presetDir, entry.name);
19546
19691
  const subEntries = await readdir4(subDir, { withFileTypes: true });
19547
19692
  for (const sub of subEntries) {
19548
19693
  if (sub.isFile()) {
@@ -19668,14 +19813,14 @@ var exports_debt_harvest_command = {};
19668
19813
  __export(exports_debt_harvest_command, {
19669
19814
  debtHarvestCommand: () => debtHarvestCommand
19670
19815
  });
19671
- import { readdir as readdir5, readFile as readFile15, writeFile as writeFile9, mkdir as mkdir9 } from "node:fs/promises";
19672
- import { join as join13, relative as relative6, extname } from "node:path";
19816
+ import { readdir as readdir5, readFile as readFile16, writeFile as writeFile8, mkdir as mkdir8 } from "node:fs/promises";
19817
+ import { join as join14, relative as relative6, extname } from "node:path";
19673
19818
  async function walkDir(dir, extensions) {
19674
19819
  const results = [];
19675
19820
  try {
19676
19821
  const entries = await readdir5(dir, { withFileTypes: true });
19677
19822
  for (const entry of entries) {
19678
- const fullPath = join13(dir, entry.name);
19823
+ const fullPath = join14(dir, entry.name);
19679
19824
  if (entry.isDirectory()) {
19680
19825
  if (entry.name.startsWith(".") || entry.name === "node_modules")
19681
19826
  continue;
@@ -19742,19 +19887,19 @@ function renderLedger(entries) {
19742
19887
  async function debtHarvestCommand(options) {
19743
19888
  const { projectRoot, dir = "src", output } = options;
19744
19889
  try {
19745
- const targetDir = join13(projectRoot, dir);
19890
+ const targetDir = join14(projectRoot, dir);
19746
19891
  const files = await walkDir(targetDir, SOURCE_EXTENSIONS);
19747
19892
  const allEntries = [];
19748
19893
  for (const file of files) {
19749
- const content = await readFile15(file, "utf-8");
19894
+ const content = await readFile16(file, "utf-8");
19750
19895
  const entries = extractDefers(content, relative6(projectRoot, file));
19751
19896
  allEntries.push(...entries);
19752
19897
  }
19753
- const ledgerDir = join13(projectRoot, ".codeconductor");
19754
- await mkdir9(ledgerDir, { recursive: true });
19755
- const ledgerPath = join13(ledgerDir, "debt-ledger.md");
19898
+ const ledgerDir = join14(projectRoot, ".codeconductor");
19899
+ await mkdir8(ledgerDir, { recursive: true });
19900
+ const ledgerPath = join14(ledgerDir, "debt-ledger.md");
19756
19901
  const ledgerContent = renderLedger(allEntries);
19757
- await writeFile9(ledgerPath, ledgerContent, "utf-8");
19902
+ await writeFile8(ledgerPath, ledgerContent, "utf-8");
19758
19903
  if (output === "json") {
19759
19904
  return {
19760
19905
  code: 0,
@@ -20187,27 +20332,27 @@ var init_goal_planner = __esm(() => {
20187
20332
  });
20188
20333
 
20189
20334
  // src/core/product-graph/paths.ts
20190
- import { join as join14 } from "node:path";
20335
+ import { join as join15 } from "node:path";
20191
20336
  function productGraphPath(projectRoot) {
20192
- return join14(projectRoot, PRODUCT_DIR, PRODUCT_GRAPH_FILE);
20337
+ return join15(projectRoot, PRODUCT_DIR, PRODUCT_GRAPH_FILE);
20193
20338
  }
20194
20339
  function productMetaPath(projectRoot) {
20195
- return join14(projectRoot, PRODUCT_DIR, PRODUCT_META_FILE);
20340
+ return join15(projectRoot, PRODUCT_DIR, PRODUCT_META_FILE);
20196
20341
  }
20197
20342
  function eventsPath(projectRoot) {
20198
- return join14(projectRoot, PRODUCT_DIR, EVENTS_FILE);
20343
+ return join15(projectRoot, PRODUCT_DIR, EVENTS_FILE);
20199
20344
  }
20200
20345
  function operationalStatePath(projectRoot) {
20201
- return join14(projectRoot, PRODUCT_DIR, OPERATIONAL_STATE_FILE);
20346
+ return join15(projectRoot, PRODUCT_DIR, OPERATIONAL_STATE_FILE);
20202
20347
  }
20203
20348
  function strategicPath(projectRoot) {
20204
- return join14(projectRoot, PRODUCT_DIR, STRATEGIC_FILE);
20349
+ return join15(projectRoot, PRODUCT_DIR, STRATEGIC_FILE);
20205
20350
  }
20206
20351
  function productReportPath(projectRoot) {
20207
- return join14(projectRoot, PRODUCT_DIR, PRODUCT_REPORT_FILE);
20352
+ return join15(projectRoot, PRODUCT_DIR, PRODUCT_REPORT_FILE);
20208
20353
  }
20209
20354
  function evidenceDir(projectRoot) {
20210
- return join14(projectRoot, PRODUCT_DIR, EVIDENCE_DIR);
20355
+ return join15(projectRoot, PRODUCT_DIR, EVIDENCE_DIR);
20211
20356
  }
20212
20357
  var PRODUCT_DIR = ".codeconductor", PRODUCT_GRAPH_FILE = "product-graph.json", PRODUCT_META_FILE = "product-meta.json", EVENTS_FILE = "events.jsonl", OPERATIONAL_STATE_FILE = "operational-state.json", STRATEGIC_FILE = "strategic.json", PRODUCT_REPORT_FILE = "product-report.md", EVIDENCE_DIR = "evidence";
20213
20358
  var init_paths = () => {};
@@ -20224,11 +20369,11 @@ __export(exports_graph_store, {
20224
20369
  saveGraph: () => saveGraph,
20225
20370
  upsertNode: () => upsertNode
20226
20371
  });
20227
- import { mkdir as mkdir10, readFile as readFile16, writeFile as writeFile10 } from "node:fs/promises";
20228
- import { dirname as dirname9 } from "node:path";
20372
+ import { mkdir as mkdir9, readFile as readFile17, writeFile as writeFile9 } from "node:fs/promises";
20373
+ import { dirname as dirname8 } from "node:path";
20229
20374
  async function loadGraph(projectRoot) {
20230
20375
  try {
20231
- const raw = await readFile16(productGraphPath(projectRoot), "utf-8");
20376
+ const raw = await readFile17(productGraphPath(projectRoot), "utf-8");
20232
20377
  return ok(ProductGraphSchema.parse(JSON.parse(raw)));
20233
20378
  } catch (e) {
20234
20379
  if (e.code === "ENOENT") {
@@ -20241,8 +20386,8 @@ async function saveGraph(projectRoot, graph) {
20241
20386
  try {
20242
20387
  const validated = ProductGraphSchema.parse(graph);
20243
20388
  const path = productGraphPath(projectRoot);
20244
- await mkdir10(dirname9(path), { recursive: true });
20245
- await writeFile10(path, JSON.stringify(validated, null, 2), "utf-8");
20389
+ await mkdir9(dirname8(path), { recursive: true });
20390
+ await writeFile9(path, JSON.stringify(validated, null, 2), "utf-8");
20246
20391
  return ok(undefined);
20247
20392
  } catch (e) {
20248
20393
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -20410,7 +20555,7 @@ __export(exports_goal_state, {
20410
20555
  markTaskBlocked: () => markTaskBlocked,
20411
20556
  writeGoal: () => writeGoal
20412
20557
  });
20413
- import { mkdir as mkdir11, readFile as readFile17, writeFile as writeFile11 } from "node:fs/promises";
20558
+ import { mkdir as mkdir10, readFile as readFile18, writeFile as writeFile10 } from "node:fs/promises";
20414
20559
  import { resolve as resolve19 } from "node:path";
20415
20560
  function validateGoalGraph2(graph) {
20416
20561
  const ids = new Set(graph.tasks.map((t) => t.id));
@@ -20455,15 +20600,15 @@ async function writeGoal(projectRoot, graph) {
20455
20600
  if (!validation.success)
20456
20601
  return validation;
20457
20602
  const dir = resolve19(projectRoot, ".codeconductor");
20458
- await mkdir11(dir, { recursive: true });
20603
+ await mkdir10(dir, { recursive: true });
20459
20604
  const yaml = $stringify(graph);
20460
- await writeFile11(resolve19(dir, "current-goal.yml"), yaml, "utf-8");
20605
+ await writeFile10(resolve19(dir, "current-goal.yml"), yaml, "utf-8");
20461
20606
  return ok(undefined);
20462
20607
  }
20463
20608
  async function loadGoal(projectRoot) {
20464
20609
  try {
20465
20610
  const filePath = resolve19(projectRoot, GOAL_FILE);
20466
- const content = await readFile17(filePath, "utf-8");
20611
+ const content = await readFile18(filePath, "utf-8");
20467
20612
  const data = $parse(content);
20468
20613
  const validated = GoalGraphSchema.parse(data);
20469
20614
  return validateGoalGraph2(validated);
@@ -20625,7 +20770,7 @@ var init_goal_command = __esm(() => {
20625
20770
  });
20626
20771
 
20627
20772
  // src/core/openspec/backlog-parser.ts
20628
- import { readFile as readFile18 } from "node:fs/promises";
20773
+ import { readFile as readFile19 } from "node:fs/promises";
20629
20774
  import { resolve as resolve20 } from "node:path";
20630
20775
  function parseBool(value) {
20631
20776
  const v = value.trim().toLowerCase();
@@ -20801,7 +20946,7 @@ function parseBacklogMarkdown(content) {
20801
20946
  async function loadBacklog(projectRoot) {
20802
20947
  try {
20803
20948
  const filePath = resolve20(projectRoot, BACKLOG_FILE);
20804
- const content = await readFile18(filePath, "utf-8");
20949
+ const content = await readFile19(filePath, "utf-8");
20805
20950
  return parseBacklogMarkdown(content);
20806
20951
  } catch (e) {
20807
20952
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -20850,11 +20995,11 @@ function slugify(text) {
20850
20995
  }
20851
20996
 
20852
20997
  // src/core/knowledge/source-parsers.ts
20853
- import { readFile as readFile19 } from "node:fs/promises";
20854
- import { basename as basename6, join as join15, relative as relative7, sep as sep5 } from "node:path";
20998
+ import { readFile as readFile20 } from "node:fs/promises";
20999
+ import { basename as basename6, join as join16, relative as relative7, sep as sep6 } from "node:path";
20855
21000
  async function parseAdrFile(filePath) {
20856
21001
  try {
20857
- const content = await readFile19(filePath, "utf-8");
21002
+ const content = await readFile20(filePath, "utf-8");
20858
21003
  const base = basename6(filePath);
20859
21004
  const idMatch = base.match(/adr-(\d+)/i);
20860
21005
  const id = idMatch ? `adr-${idMatch[1]}` : slugify(base);
@@ -20903,7 +21048,7 @@ function adrToEntities(adr, source) {
20903
21048
  }
20904
21049
  async function parseReadmeEntities(projectRoot, productName) {
20905
21050
  try {
20906
- const content = await readFile19(join15(projectRoot, "README.md"), "utf-8");
21051
+ const content = await readFile20(join16(projectRoot, "README.md"), "utf-8");
20907
21052
  const firstPara = content.split(`
20908
21053
 
20909
21054
  `).find((p) => p.trim() && !p.startsWith("#") && !p.startsWith(">")) ?? "";
@@ -20924,7 +21069,7 @@ async function parseReadmeEntities(projectRoot, productName) {
20924
21069
  }
20925
21070
  async function parseGraphifyEntities(projectRoot) {
20926
21071
  try {
20927
- const raw = await readFile19(join15(projectRoot, "graphify-out", "graph.json"), "utf-8");
21072
+ const raw = await readFile20(join16(projectRoot, "graphify-out", "graph.json"), "utf-8");
20928
21073
  const graph = JSON.parse(raw);
20929
21074
  const entities = [];
20930
21075
  for (const node2 of graph.nodes ?? []) {
@@ -20948,14 +21093,14 @@ async function parseGraphifyEntities(projectRoot) {
20948
21093
  async function scanSrcComponents(projectRoot) {
20949
21094
  const { readdir: readdir6 } = await import("node:fs/promises");
20950
21095
  const entities = [];
20951
- const srcDir = join15(projectRoot, "src");
21096
+ const srcDir = join16(projectRoot, "src");
20952
21097
  async function walk(dir, prefix) {
20953
21098
  try {
20954
21099
  const entries = await readdir6(dir, { withFileTypes: true });
20955
21100
  for (const entry of entries) {
20956
21101
  if (entry.name.startsWith("."))
20957
21102
  continue;
20958
- const full = join15(dir, entry.name);
21103
+ const full = join16(dir, entry.name);
20959
21104
  if (entry.isDirectory()) {
20960
21105
  if (entry.name === "node_modules")
20961
21106
  continue;
@@ -20967,7 +21112,7 @@ async function scanSrcComponents(projectRoot) {
20967
21112
  source: "src/",
20968
21113
  confidence: "medium",
20969
21114
  relations: [],
20970
- data: { path: relative7(projectRoot, full).split(sep5).join("/") }
21115
+ data: { path: relative7(projectRoot, full).split(sep6).join("/") }
20971
21116
  });
20972
21117
  await walk(full, prefix ? `${prefix}-${domainSlug}` : domainSlug);
20973
21118
  }
@@ -20980,7 +21125,7 @@ async function scanSrcComponents(projectRoot) {
20980
21125
  var init_source_parsers = () => {};
20981
21126
 
20982
21127
  // src/core/memory/episodic-store.ts
20983
- import { appendFile, mkdir as mkdir12, readFile as readFile20 } from "node:fs/promises";
21128
+ import { appendFile, mkdir as mkdir11, readFile as readFile21 } from "node:fs/promises";
20984
21129
  import { resolve as resolve21 } from "node:path";
20985
21130
  async function appendEvent(projectRoot, event) {
20986
21131
  try {
@@ -20989,7 +21134,7 @@ async function appendEvent(projectRoot, event) {
20989
21134
  id: event.id ?? `evt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
20990
21135
  });
20991
21136
  const path = eventsPath(projectRoot);
20992
- await mkdir12(resolve21(projectRoot, ".codeconductor"), { recursive: true });
21137
+ await mkdir11(resolve21(projectRoot, ".codeconductor"), { recursive: true });
20993
21138
  await appendFile(path, JSON.stringify(full) + `
20994
21139
  `, "utf-8");
20995
21140
  return ok(full);
@@ -20999,7 +21144,7 @@ async function appendEvent(projectRoot, event) {
20999
21144
  }
21000
21145
  async function listEvents(projectRoot, since, type) {
21001
21146
  try {
21002
- const content = await readFile20(eventsPath(projectRoot), "utf-8");
21147
+ const content = await readFile21(eventsPath(projectRoot), "utf-8");
21003
21148
  const events = [];
21004
21149
  for (const line of content.split(`
21005
21150
  `)) {
@@ -21027,9 +21172,9 @@ var init_episodic_store = __esm(() => {
21027
21172
 
21028
21173
  // src/core/knowledge/ingest-pipeline.ts
21029
21174
  import { createHash as createHash3 } from "node:crypto";
21030
- import { readFile as readFile21 } from "node:fs/promises";
21031
- import { join as join16, relative as relative8, sep as sep6 } from "node:path";
21032
- import { mkdir as mkdir13, writeFile as writeFile12 } from "node:fs/promises";
21175
+ import { readFile as readFile22 } from "node:fs/promises";
21176
+ import { join as join17, relative as relative8, sep as sep7 } from "node:path";
21177
+ import { mkdir as mkdir12, writeFile as writeFile11 } from "node:fs/promises";
21033
21178
  async function mapLimit(items, limit, fn) {
21034
21179
  if (items.length === 0)
21035
21180
  return [];
@@ -21049,7 +21194,7 @@ async function mapLimit(items, limit, fn) {
21049
21194
  }
21050
21195
  async function hashFile(path) {
21051
21196
  try {
21052
- const content = await readFile21(path, "utf-8");
21197
+ const content = await readFile22(path, "utf-8");
21053
21198
  return createHash3("sha256").update(content).digest("hex").slice(0, 16);
21054
21199
  } catch {
21055
21200
  return null;
@@ -21064,7 +21209,7 @@ async function collectDeferRisks(projectRoot) {
21064
21209
  for (const entry of entries) {
21065
21210
  if (entry.name.startsWith(".") || entry.name === "node_modules")
21066
21211
  continue;
21067
- const full = join16(dir, entry.name);
21212
+ const full = join17(dir, entry.name);
21068
21213
  if (entry.isDirectory()) {
21069
21214
  await walk(full);
21070
21215
  } else if (entry.name.endsWith(".ts")) {
@@ -21073,11 +21218,11 @@ async function collectDeferRisks(projectRoot) {
21073
21218
  }
21074
21219
  } catch {}
21075
21220
  }
21076
- await walk(join16(projectRoot, "src"));
21221
+ await walk(join17(projectRoot, "src"));
21077
21222
  const perFile = await mapLimit(files, WALK_READ_CONCURRENCY, async (full) => {
21078
21223
  const entities = [];
21079
- const content = await readFile21(full, "utf-8");
21080
- const rel = relative8(projectRoot, full).split(sep6).join("/");
21224
+ const content = await readFile22(full, "utf-8");
21225
+ const rel = relative8(projectRoot, full).split(sep7).join("/");
21081
21226
  const deferRe = new RegExp(DEFER_REGEX_SOURCE, "gm");
21082
21227
  let match;
21083
21228
  while ((match = deferRe.exec(content)) !== null) {
@@ -21136,12 +21281,12 @@ async function parseBacklogEntities(projectRoot) {
21136
21281
  }
21137
21282
  async function parseAdrEntities(projectRoot) {
21138
21283
  const { readdir: readdir6 } = await import("node:fs/promises");
21139
- const adrDir = join16(projectRoot, "docs", "adr");
21284
+ const adrDir = join17(projectRoot, "docs", "adr");
21140
21285
  const entities = [];
21141
21286
  try {
21142
21287
  const files = (await readdir6(adrDir)).filter((file) => file.endsWith(".md"));
21143
21288
  const parsed = await Promise.all(files.map(async (file) => {
21144
- const adr = await parseAdrFile(join16(adrDir, file));
21289
+ const adr = await parseAdrFile(join17(adrDir, file));
21145
21290
  return adr ? adrToEntities(adr, `docs/adr/${file}`) : [];
21146
21291
  }));
21147
21292
  entities.push(...parsed.flat());
@@ -21233,7 +21378,7 @@ async function runIngest(projectRoot, productName) {
21233
21378
  await saveGraph2(projectRoot, graph);
21234
21379
  const sourceHashes = {};
21235
21380
  for (const src of ["README.md", "BACKLOG.md", "ROADMAP.md"]) {
21236
- const h = await hashFile(join16(projectRoot, src));
21381
+ const h = await hashFile(join17(projectRoot, src));
21237
21382
  if (h)
21238
21383
  sourceHashes[src] = h;
21239
21384
  }
@@ -21243,8 +21388,8 @@ async function runIngest(projectRoot, productName) {
21243
21388
  lastIngestAt: new Date().toISOString(),
21244
21389
  sourceHashes
21245
21390
  };
21246
- await mkdir13(join16(projectRoot, ".codeconductor"), { recursive: true });
21247
- await writeFile12(productMetaPath(projectRoot), JSON.stringify(ProductMetaSchema.parse(meta), null, 2), "utf-8");
21391
+ await mkdir12(join17(projectRoot, ".codeconductor"), { recursive: true });
21392
+ await writeFile11(productMetaPath(projectRoot), JSON.stringify(ProductMetaSchema.parse(meta), null, 2), "utf-8");
21248
21393
  await appendEvent(projectRoot, {
21249
21394
  type: "ingest.completed",
21250
21395
  timestamp: new Date().toISOString(),
@@ -21325,11 +21470,11 @@ var init_ingest_command = __esm(() => {
21325
21470
  });
21326
21471
 
21327
21472
  // src/core/memory/operational-state.ts
21328
- import { mkdir as mkdir14, readFile as readFile22, writeFile as writeFile13 } from "node:fs/promises";
21473
+ import { mkdir as mkdir13, readFile as readFile23, writeFile as writeFile12 } from "node:fs/promises";
21329
21474
  import { resolve as resolve22 } from "node:path";
21330
21475
  async function loadOperationalState(projectRoot) {
21331
21476
  try {
21332
- const raw = await readFile22(operationalStatePath(projectRoot), "utf-8");
21477
+ const raw = await readFile23(operationalStatePath(projectRoot), "utf-8");
21333
21478
  return ok(OperationalStateSchema.parse(JSON.parse(raw)));
21334
21479
  } catch (e) {
21335
21480
  if (e.code === "ENOENT") {
@@ -21351,8 +21496,8 @@ async function saveOperationalState(projectRoot, state) {
21351
21496
  updatedAt: new Date().toISOString()
21352
21497
  });
21353
21498
  const path = operationalStatePath(projectRoot);
21354
- await mkdir14(resolve22(projectRoot, ".codeconductor"), { recursive: true });
21355
- await writeFile13(path, JSON.stringify(validated, null, 2), "utf-8");
21499
+ await mkdir13(resolve22(projectRoot, ".codeconductor"), { recursive: true });
21500
+ await writeFile12(path, JSON.stringify(validated, null, 2), "utf-8");
21356
21501
  return ok(undefined);
21357
21502
  } catch (e) {
21358
21503
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -21386,11 +21531,11 @@ var init_operational_state = __esm(() => {
21386
21531
  });
21387
21532
 
21388
21533
  // src/core/memory/strategic-memory.ts
21389
- import { mkdir as mkdir15, readFile as readFile23, writeFile as writeFile14 } from "node:fs/promises";
21534
+ import { mkdir as mkdir14, readFile as readFile24, writeFile as writeFile13 } from "node:fs/promises";
21390
21535
  import { resolve as resolve23 } from "node:path";
21391
21536
  async function loadStrategicMemory(projectRoot) {
21392
21537
  try {
21393
- const raw = await readFile23(strategicPath(projectRoot), "utf-8");
21538
+ const raw = await readFile24(strategicPath(projectRoot), "utf-8");
21394
21539
  return ok(StrategicMemorySchema.parse(JSON.parse(raw)));
21395
21540
  } catch (e) {
21396
21541
  if (e.code === "ENOENT") {
@@ -21411,8 +21556,8 @@ async function saveStrategicMemory(projectRoot, memory) {
21411
21556
  updatedAt: new Date().toISOString()
21412
21557
  });
21413
21558
  const path = strategicPath(projectRoot);
21414
- await mkdir15(resolve23(projectRoot, ".codeconductor"), { recursive: true });
21415
- await writeFile14(path, JSON.stringify(validated, null, 2), "utf-8");
21559
+ await mkdir14(resolve23(projectRoot, ".codeconductor"), { recursive: true });
21560
+ await writeFile13(path, JSON.stringify(validated, null, 2), "utf-8");
21416
21561
  return ok(undefined);
21417
21562
  } catch (e) {
21418
21563
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -21424,7 +21569,7 @@ var init_strategic_memory = __esm(() => {
21424
21569
  });
21425
21570
 
21426
21571
  // src/core/memory/memory-index.ts
21427
- import { mkdir as mkdir16, readFile as readFile24, writeFile as writeFile15 } from "node:fs/promises";
21572
+ import { mkdir as mkdir15, readFile as readFile25, writeFile as writeFile14 } from "node:fs/promises";
21428
21573
  import { resolve as resolve24 } from "node:path";
21429
21574
  function parseMarkdown(content) {
21430
21575
  const startIdx = content.indexOf(SENTINEL_START);
@@ -21445,7 +21590,7 @@ function parseMarkdown(content) {
21445
21590
  async function loadMemoryIndex(projectRoot) {
21446
21591
  try {
21447
21592
  const filePath = resolve24(projectRoot, MEMORY_FILE);
21448
- const content = await readFile24(filePath, "utf-8");
21593
+ const content = await readFile25(filePath, "utf-8");
21449
21594
  const parsed = parseMarkdown(content);
21450
21595
  if (!parsed) {
21451
21596
  return err(new Error("Failed to parse memory index from memory.md"));
@@ -21462,12 +21607,12 @@ var init_memory_index = __esm(() => {
21462
21607
  });
21463
21608
 
21464
21609
  // src/core/evaluation/outcome-store.ts
21465
- import { appendFile as appendFile2, mkdir as mkdir17, readFile as readFile25, readdir as readdir6, writeFile as writeFile16 } from "node:fs/promises";
21610
+ import { appendFile as appendFile2, mkdir as mkdir16, readFile as readFile26, readdir as readdir6, writeFile as writeFile15 } from "node:fs/promises";
21466
21611
  import { resolve as resolve25 } from "node:path";
21467
21612
  async function ensureEvaluationDir(projectRoot) {
21468
21613
  const dir = resolve25(projectRoot, EVAL_DIR);
21469
- await mkdir17(dir, { recursive: true });
21470
- await mkdir17(resolve25(dir, SCORECARDS_DIR), { recursive: true });
21614
+ await mkdir16(dir, { recursive: true });
21615
+ await mkdir16(resolve25(dir, SCORECARDS_DIR), { recursive: true });
21471
21616
  return dir;
21472
21617
  }
21473
21618
  async function appendOutcome(projectRoot, outcome) {
@@ -21479,11 +21624,11 @@ async function appendOutcome(projectRoot, outcome) {
21479
21624
  const indexPath = resolve25(dir, INDEX_FILE);
21480
21625
  let index = { version: 1, lastOutcomeId: validated.id };
21481
21626
  try {
21482
- const raw = await readFile25(indexPath, "utf-8");
21627
+ const raw = await readFile26(indexPath, "utf-8");
21483
21628
  index = EvaluationIndexSchema.parse(JSON.parse(raw));
21484
21629
  index.lastOutcomeId = validated.id;
21485
21630
  } catch {}
21486
- await writeFile16(indexPath, JSON.stringify(index, null, 2), "utf-8");
21631
+ await writeFile15(indexPath, JSON.stringify(index, null, 2), "utf-8");
21487
21632
  return ok(undefined);
21488
21633
  } catch (e) {
21489
21634
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -21492,7 +21637,7 @@ async function appendOutcome(projectRoot, outcome) {
21492
21637
  async function listOutcomes(projectRoot, filter = {}) {
21493
21638
  try {
21494
21639
  const path = resolve25(projectRoot, EVAL_DIR, OUTCOMES_FILE);
21495
- const content = await readFile25(path, "utf-8");
21640
+ const content = await readFile26(path, "utf-8");
21496
21641
  const lines = content.split(`
21497
21642
  `).filter((l) => l.trim().length > 0);
21498
21643
  const outcomes = [];
@@ -21531,7 +21676,7 @@ async function saveScorecard(projectRoot, record) {
21531
21676
  const dir = await ensureEvaluationDir(projectRoot);
21532
21677
  const validated = ScorecardRecordSchema.parse(record);
21533
21678
  const filePath = resolve25(dir, SCORECARDS_DIR, `${validated.id}.json`);
21534
- await writeFile16(filePath, JSON.stringify(validated, null, 2), "utf-8");
21679
+ await writeFile15(filePath, JSON.stringify(validated, null, 2), "utf-8");
21535
21680
  return ok(filePath);
21536
21681
  } catch (e) {
21537
21682
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -21552,7 +21697,7 @@ async function listScorecards(projectRoot, filter = {}) {
21552
21697
  for (const file of files) {
21553
21698
  if (!file.endsWith(".json"))
21554
21699
  continue;
21555
- const parsed = ScorecardRecordSchema.parse(JSON.parse(await readFile25(resolve25(dir, file), "utf-8")));
21700
+ const parsed = ScorecardRecordSchema.parse(JSON.parse(await readFile26(resolve25(dir, file), "utf-8")));
21556
21701
  if (filter.backlogId && parsed.backlogId !== filter.backlogId)
21557
21702
  continue;
21558
21703
  if (filter.verdict && parsed.verdict !== filter.verdict)
@@ -21575,7 +21720,7 @@ async function hasPassingScorecard(projectRoot, backlogId) {
21575
21720
  async function loadScorecard(projectRoot, id) {
21576
21721
  try {
21577
21722
  const filePath = resolve25(projectRoot, EVAL_DIR, SCORECARDS_DIR, `${id}.json`);
21578
- const content = await readFile25(filePath, "utf-8");
21723
+ const content = await readFile26(filePath, "utf-8");
21579
21724
  return ok(ScorecardRecordSchema.parse(JSON.parse(content)));
21580
21725
  } catch (e) {
21581
21726
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -21667,11 +21812,11 @@ var init_outcome_store = __esm(() => {
21667
21812
  });
21668
21813
 
21669
21814
  // src/core/feedback/feedback-ingestor.ts
21670
- import { readFile as readFile26 } from "node:fs/promises";
21671
- import { join as join17 } from "node:path";
21815
+ import { readFile as readFile27 } from "node:fs/promises";
21816
+ import { join as join18 } from "node:path";
21672
21817
  async function parseChangelogLearnings(projectRoot) {
21673
21818
  try {
21674
- const content = await readFile26(join17(projectRoot, "CHANGELOG.md"), "utf-8");
21819
+ const content = await readFile27(join18(projectRoot, "CHANGELOG.md"), "utf-8");
21675
21820
  const unreleased = content.match(/## \[Unreleased\]([\s\S]*?)(?=## \[|\n## )/i);
21676
21821
  if (!unreleased)
21677
21822
  return [];
@@ -21791,7 +21936,7 @@ var exports_product_command = {};
21791
21936
  __export(exports_product_command, {
21792
21937
  productCommand: () => productCommand
21793
21938
  });
21794
- import { writeFile as writeFile17 } from "node:fs/promises";
21939
+ import { writeFile as writeFile16 } from "node:fs/promises";
21795
21940
  async function productCommand(options) {
21796
21941
  const { subcommand, projectRoot, output, query, from, to, since, format } = options;
21797
21942
  try {
@@ -21994,7 +22139,7 @@ async function handleExport(projectRoot, format, output) {
21994
22139
  ...queryNodes(graph.data, "risk").map((n) => `- ${n.name}`)
21995
22140
  ].join(`
21996
22141
  `);
21997
- await writeFile17(productReportPath(projectRoot), md, "utf-8");
22142
+ await writeFile16(productReportPath(projectRoot), md, "utf-8");
21998
22143
  return {
21999
22144
  code: 0,
22000
22145
  data: {
@@ -22275,22 +22420,22 @@ __export(exports_rdd_receipt, {
22275
22420
  verifyReceipt: () => verifyReceipt
22276
22421
  });
22277
22422
  import { createHash as createHash4 } from "node:crypto";
22278
- import { readdir as readdir7, readFile as readFile27 } from "node:fs/promises";
22279
- import { join as join18, relative as relative9, resolve as resolve26, sep as sep7 } from "node:path";
22423
+ import { readdir as readdir7, readFile as readFile28 } from "node:fs/promises";
22424
+ import { join as join19, relative as relative9, resolve as resolve26, sep as sep8 } from "node:path";
22280
22425
  function sha2562(content) {
22281
22426
  return createHash4("sha256").update(content).digest("hex");
22282
22427
  }
22283
22428
  function canonicalPath(projectRoot, input) {
22284
22429
  const root = resolve26(projectRoot);
22285
22430
  const absolute = resolve26(root, input);
22286
- if (absolute !== root && !absolute.startsWith(`${root}${sep7}`)) {
22431
+ if (absolute !== root && !absolute.startsWith(`${root}${sep8}`)) {
22287
22432
  throw new Error(`RDD receipt path "${input}" is outside the project root`);
22288
22433
  }
22289
22434
  const normalized = relative9(root, absolute);
22290
22435
  if (!normalized || normalized === ".") {
22291
22436
  throw new Error("RDD receipts require file paths, not the project root");
22292
22437
  }
22293
- return { absolute, relative: normalized.split(sep7).join("/") };
22438
+ return { absolute, relative: normalized.split(sep8).join("/") };
22294
22439
  }
22295
22440
  async function fingerprint(projectRoot, paths) {
22296
22441
  const deduplicated = new Map;
@@ -22300,7 +22445,7 @@ async function fingerprint(projectRoot, paths) {
22300
22445
  }
22301
22446
  const result = [];
22302
22447
  for (const [path, absolute] of [...deduplicated.entries()].sort(([left], [right]) => left.localeCompare(right))) {
22303
- result.push({ path, hash: sha2562(await readFile27(absolute)) });
22448
+ result.push({ path, hash: sha2562(await readFile28(absolute)) });
22304
22449
  }
22305
22450
  return result;
22306
22451
  }
@@ -22341,11 +22486,11 @@ async function collectReceiptPaths(projectRoot) {
22341
22486
  for (const entry of entries) {
22342
22487
  if (entry.isDirectory()) {
22343
22488
  if (!EXCLUDED_DIRECTORIES.has(entry.name))
22344
- await visit2(join18(current, entry.name));
22489
+ await visit2(join19(current, entry.name));
22345
22490
  continue;
22346
22491
  }
22347
22492
  if (entry.isFile())
22348
- paths.push(relative9(root, join18(current, entry.name)).split(sep7).join("/"));
22493
+ paths.push(relative9(root, join19(current, entry.name)).split(sep8).join("/"));
22349
22494
  }
22350
22495
  }
22351
22496
  await visit2(root);
@@ -22360,7 +22505,7 @@ async function verifyReceipt(projectRoot, receipt) {
22360
22505
  for (const path of pathsToCheck) {
22361
22506
  try {
22362
22507
  const canonical = canonicalPath(projectRoot, path);
22363
- const hash = sha2562(await readFile27(canonical.absolute));
22508
+ const hash = sha2562(await readFile28(canonical.absolute));
22364
22509
  const currentEntry = { path: canonical.relative, hash };
22365
22510
  current.push(currentEntry);
22366
22511
  currentByPath.set(currentEntry.path, currentEntry);
@@ -22397,9 +22542,9 @@ var init_rdd_receipt = __esm(() => {
22397
22542
 
22398
22543
  // src/core/verification/verification-runner.ts
22399
22544
  import { existsSync as existsSync3 } from "node:fs";
22400
- import { readFile as readFile28 } from "node:fs/promises";
22401
- import { dirname as dirname10, join as join19, resolve as resolve27 } from "node:path";
22402
- import { mkdir as mkdir18, readdir as readdir8, writeFile as writeFile18 } from "node:fs/promises";
22545
+ import { readFile as readFile29 } from "node:fs/promises";
22546
+ import { dirname as dirname9, join as join20, resolve as resolve27 } from "node:path";
22547
+ import { mkdir as mkdir17, readdir as readdir8, writeFile as writeFile17 } from "node:fs/promises";
22403
22548
  function isPassingEvidence(ev) {
22404
22549
  switch (ev.type) {
22405
22550
  case "verification":
@@ -22426,7 +22571,7 @@ async function isCurrentPassingEvidence(projectRoot, ev) {
22426
22571
  }
22427
22572
  function evidenceFilePath(evDir, id) {
22428
22573
  const path = resolve27(evDir, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
22429
- if (dirname10(path) !== resolve27(evDir)) {
22574
+ if (dirname9(path) !== resolve27(evDir)) {
22430
22575
  return err(new Error(`Refusing to write evidence for "${id}" outside ${evDir}`));
22431
22576
  }
22432
22577
  return ok(path);
@@ -22437,8 +22582,8 @@ async function persistEvidence(projectRoot, evidence) {
22437
22582
  if (!evidencePath.success)
22438
22583
  return evidencePath;
22439
22584
  try {
22440
- await mkdir18(evDir, { recursive: true });
22441
- await writeFile18(evidencePath.data, JSON.stringify(evidence, null, 2), "utf-8");
22585
+ await mkdir17(evDir, { recursive: true });
22586
+ await writeFile17(evidencePath.data, JSON.stringify(evidence, null, 2), "utf-8");
22442
22587
  } catch (e) {
22443
22588
  return err(e instanceof Error ? e : new Error(String(e)));
22444
22589
  }
@@ -22460,7 +22605,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
22460
22605
  if (!file.endsWith(".json"))
22461
22606
  continue;
22462
22607
  try {
22463
- const raw = await readFile28(join19(evDir, file), "utf-8");
22608
+ const raw = await readFile29(join20(evDir, file), "utf-8");
22464
22609
  const ev = EvidenceSchema.parse(JSON.parse(raw));
22465
22610
  if (ev.relatedTask === taskId)
22466
22611
  records.push(ev);
@@ -22647,7 +22792,7 @@ async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
22647
22792
  return path;
22648
22793
  let raw;
22649
22794
  try {
22650
- raw = await readFile28(path.data, "utf-8");
22795
+ raw = await readFile29(path.data, "utf-8");
22651
22796
  } catch (e) {
22652
22797
  return err(e instanceof Error ? e : new Error(String(e)));
22653
22798
  }
@@ -23839,18 +23984,18 @@ __export(exports_rdd_command, {
23839
23984
  });
23840
23985
  import { execFile as execFile4 } from "node:child_process";
23841
23986
  import { existsSync as existsSync4 } from "node:fs";
23842
- import { mkdir as mkdir19, readFile as readFile29, readdir as readdir9, rename as rename3, writeFile as writeFile19, chmod } from "node:fs/promises";
23843
- import { dirname as dirname11, resolve as resolve28 } from "node:path";
23987
+ import { mkdir as mkdir18, readFile as readFile30, readdir as readdir9, rename as rename3, writeFile as writeFile18, chmod } from "node:fs/promises";
23988
+ import { dirname as dirname10, resolve as resolve28 } from "node:path";
23844
23989
  import { promisify as promisify4 } from "node:util";
23845
23990
  function receiptPath(projectRoot, id) {
23846
23991
  const directory = resolve28(evidenceDir(projectRoot));
23847
23992
  const path = resolve28(directory, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
23848
- if (dirname11(path) !== directory)
23993
+ if (dirname10(path) !== directory)
23849
23994
  throw new Error(`Invalid RDD receipt id: ${id}`);
23850
23995
  return path;
23851
23996
  }
23852
23997
  async function loadReceipt(projectRoot, id) {
23853
- const raw = await readFile29(receiptPath(projectRoot, id), "utf-8");
23998
+ const raw = await readFile30(receiptPath(projectRoot, id), "utf-8");
23854
23999
  const evidence = EvidenceSchema.parse(JSON.parse(raw));
23855
24000
  if (evidence.type !== "rdd")
23856
24001
  throw new Error(`Evidence "${id}" is not an RDD receipt`);
@@ -23893,7 +24038,7 @@ async function installHooks(projectRoot) {
23893
24038
  encoding: "utf8"
23894
24039
  });
23895
24040
  const hooksDir = resolve28(projectRoot, stdout.trim());
23896
- await mkdir19(hooksDir, { recursive: true });
24041
+ await mkdir18(hooksDir, { recursive: true });
23897
24042
  const installed = [];
23898
24043
  const skipped = [];
23899
24044
  for (const hook of ["pre-commit", "pre-push"]) {
@@ -23901,7 +24046,7 @@ async function installHooks(projectRoot) {
23901
24046
  const backup = resolve28(hooksDir, `${hook}.rdd-original`);
23902
24047
  const marker = "# codeconductor-rdd-hook";
23903
24048
  if (existsSync4(path)) {
23904
- const existing = await readFile29(path, "utf8");
24049
+ const existing = await readFile30(path, "utf8");
23905
24050
  if (existing.includes(marker)) {
23906
24051
  skipped.push(hook);
23907
24052
  continue;
@@ -23920,7 +24065,7 @@ if [ -x "$HOOK_DIR/${hook}.rdd-original" ]; then
23920
24065
  fi
23921
24066
  cc-codeconductor rdd git-check
23922
24067
  `;
23923
- await writeFile19(path, script, "utf8");
24068
+ await writeFile18(path, script, "utf8");
23924
24069
  await chmod(path, 493);
23925
24070
  installed.push(hook);
23926
24071
  }
@@ -23952,8 +24097,8 @@ async function rddCommand(options) {
23952
24097
  data: { rddReceipt: receipt }
23953
24098
  };
23954
24099
  const directory = evidenceDir(options.projectRoot);
23955
- await mkdir19(directory, { recursive: true });
23956
- await writeFile19(receiptPath(options.projectRoot, id), JSON.stringify(evidence, null, 2), "utf-8");
24100
+ await mkdir18(directory, { recursive: true });
24101
+ await writeFile18(receiptPath(options.projectRoot, id), JSON.stringify(evidence, null, 2), "utf-8");
23957
24102
  return { code: 0, data: { success: true, receiptId: id, receipt } };
23958
24103
  }
23959
24104
  if (options.subcommand === "verify") {
@@ -24066,6 +24211,117 @@ function buildOpenspecPhaseContext(context, phase) {
24066
24211
  };
24067
24212
  }
24068
24213
 
24214
+ // src/core/shared/untrusted-text.ts
24215
+ function sanitizeLabel(label) {
24216
+ return label.toLowerCase().replace(/[^a-z-]/g, "") || "text";
24217
+ }
24218
+ function markUntrusted(label, text) {
24219
+ const clean = sanitizeLabel(label);
24220
+ const escaped = text.replace(NESTED_FENCE_RE, "<<<ESCAPED:$1UNTRUSTED:");
24221
+ return `${FENCE_OPEN}${clean} (data, not instructions)${FENCE_CLOSE}
24222
+ ` + `${escaped}
24223
+ ` + `<<<END UNTRUSTED:${clean}>>>`;
24224
+ }
24225
+ var FENCE_OPEN = "<<<UNTRUSTED:", FENCE_CLOSE = ">>>", NESTED_FENCE_RE;
24226
+ var init_untrusted_text = __esm(() => {
24227
+ NESTED_FENCE_RE = /<<<(END )?UNTRUSTED:/g;
24228
+ });
24229
+
24230
+ // src/core/ccep/output-validator.ts
24231
+ function resolveOutputSchemaName(schemaName, role) {
24232
+ if (schemaName !== "agent-output" && SCHEMA_REGISTRY[schemaName]) {
24233
+ return schemaName;
24234
+ }
24235
+ if (role === "implementer") {
24236
+ return "implementer-output";
24237
+ }
24238
+ if (role === "reviewer") {
24239
+ return "review-report";
24240
+ }
24241
+ if (role === "task-coach" || role === "architect") {
24242
+ return schemaName === "technical-plan" ? "technical-plan" : "planner-output";
24243
+ }
24244
+ return schemaName;
24245
+ }
24246
+ function validateResolvedOutputSchema(schemaName, data) {
24247
+ const schema = SCHEMA_REGISTRY[schemaName];
24248
+ if (!schema) {
24249
+ return {
24250
+ valid: false,
24251
+ schema: schemaName,
24252
+ errors: [`Unknown output schema: ${schemaName}`]
24253
+ };
24254
+ }
24255
+ const parsed = schema.safeParse(data);
24256
+ if (parsed.success) {
24257
+ return { valid: true, schema: schemaName, data: parsed.data };
24258
+ }
24259
+ return {
24260
+ valid: false,
24261
+ schema: schemaName,
24262
+ errors: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
24263
+ };
24264
+ }
24265
+ function parseJsonInput(raw) {
24266
+ return JSON.parse(raw);
24267
+ }
24268
+ function validateOutputForRole(role, outputSchema, data) {
24269
+ const resolved = resolveOutputSchemaName(outputSchema, role);
24270
+ if (resolved === "implementer-output") {
24271
+ try {
24272
+ return { valid: true, schema: "implementer-output", data: validateImplementerOutput(data) };
24273
+ } catch (err3) {
24274
+ return {
24275
+ valid: false,
24276
+ schema: "implementer-output",
24277
+ errors: [String(err3)]
24278
+ };
24279
+ }
24280
+ }
24281
+ if (resolved === "review-report") {
24282
+ try {
24283
+ return { valid: true, schema: "review-report", data: validateReviewerOutput(data) };
24284
+ } catch (err3) {
24285
+ return {
24286
+ valid: false,
24287
+ schema: "review-report",
24288
+ errors: [String(err3)]
24289
+ };
24290
+ }
24291
+ }
24292
+ if (resolved === "planner-output") {
24293
+ try {
24294
+ return { valid: true, schema: "planner-output", data: validatePlannerOutput(data) };
24295
+ } catch (err3) {
24296
+ return {
24297
+ valid: false,
24298
+ schema: "planner-output",
24299
+ errors: [String(err3)]
24300
+ };
24301
+ }
24302
+ }
24303
+ return validateResolvedOutputSchema(resolved, data);
24304
+ }
24305
+ var SCHEMA_REGISTRY;
24306
+ var init_output_validator = __esm(() => {
24307
+ init_schemas();
24308
+ SCHEMA_REGISTRY = {
24309
+ "planner-output": PlannerOutputSchema,
24310
+ "implementer-output": ImplementerOutputSchema,
24311
+ "review-report": ReviewerOutputSchema,
24312
+ "technical-plan": TechnicalPlanOutputSchema,
24313
+ "fix-intake-output": FixIntakeOutputSchema,
24314
+ "council-verdict": CouncilVerdictSchema,
24315
+ "agent-output": AgentOutputSchema,
24316
+ taskcard: CanonicalTaskCardSchema,
24317
+ "test-plan": AgentOutputSchema,
24318
+ "complexity-audit": AgentOutputSchema,
24319
+ "api-contract": AgentOutputSchema,
24320
+ "pagespeed-report": AgentOutputSchema,
24321
+ "scorecard-record": ScorecardRecordSchema
24322
+ };
24323
+ });
24324
+
24069
24325
  // src/core/ccep/prompt-compiler.ts
24070
24326
  function buildSystemLayer() {
24071
24327
  return [
@@ -24078,46 +24334,54 @@ function buildSystemLayer() {
24078
24334
  ].join(`
24079
24335
  `);
24080
24336
  }
24081
- function buildAgentLayer(role, phase) {
24337
+ function buildAgentLayer(role, phase, outputSchema) {
24082
24338
  const label = ROLE_LABELS[role] ?? role;
24339
+ let instruction;
24083
24340
  if (role === "task-coach" && phase === "intake") {
24084
- return `You are the Planner / ${label} of CodeConductor.
24341
+ instruction = `You are the Planner / ${label} of CodeConductor.
24085
24342
  Convert product intent into a structured plan without writing code.`;
24086
- }
24087
- if (role === "implementer") {
24088
- return `You are the ${label}.
24089
- Execute only your assigned phase: ${phase}.
24090
- Write the minimal diff. Return implementer-output JSON only.`;
24091
- }
24092
- if (role === "reviewer") {
24093
- return `You are the ${label}.
24343
+ } else if (role === "implementer") {
24344
+ instruction = `You are the ${label}.
24094
24345
  Execute only your assigned phase: ${phase}.
24095
- Return review-report JSON only.`;
24096
- }
24097
- return `You are the ${label}.
24346
+ Write the minimal diff.`;
24347
+ } else {
24348
+ instruction = `You are the ${label}.
24098
24349
  Execute only your assigned phase: ${phase}.`;
24350
+ }
24351
+ return `${instruction}
24352
+ Return ${outputSchema} JSON only.`;
24099
24353
  }
24100
24354
  function buildTaskLayer(context, phase) {
24355
+ const goal = markUntrusted("user-request", context.intent.goal);
24101
24356
  if (context.envelope.command === "openspec") {
24102
- return JSON.stringify(buildOpenspecPhaseContext(context, phase).task, null, 2);
24357
+ return JSON.stringify({ ...buildOpenspecPhaseContext(context, phase).task, goal }, null, 2);
24103
24358
  }
24104
24359
  return JSON.stringify({
24105
24360
  command: context.envelope.command,
24106
- intent: context.intent,
24361
+ intent: { ...context.intent, goal },
24107
24362
  phase,
24108
- userRequest: context.envelope.userRequest,
24109
24363
  project: context.project.name
24110
24364
  }, null, 2);
24111
24365
  }
24366
+ function scopeKnowledgeForRole(knowledge, role) {
24367
+ const keys = KNOWLEDGE_KEYS_BY_ROLE[role];
24368
+ if (!keys)
24369
+ return knowledge;
24370
+ if (Object.keys(knowledge).some((key) => !PRODUCT_GRAPH_KNOWLEDGE_KEYS.has(key))) {
24371
+ return knowledge;
24372
+ }
24373
+ return Object.fromEntries(keys.filter((key) => knowledge[key] !== undefined).map((key) => [key, knowledge[key]]));
24374
+ }
24112
24375
  function compilePrompt(options) {
24113
24376
  const { role, phase, context, promptVersion } = options;
24114
24377
  const phaseDef = context.profile.phases.find((p) => p.id === phase);
24115
- const schemaName = phaseDef?.outputSchema ?? context.outputSchema ?? "agent-output";
24378
+ const configuredSchemaName = phaseDef?.outputSchema ?? context.outputSchema ?? "agent-output";
24379
+ const schemaName = resolveOutputSchemaName(configuredSchemaName, role);
24116
24380
  const schemaBody = OUTPUT_SCHEMAS[schemaName] ?? OUTPUT_SCHEMAS["agent-output"];
24117
- const knowledge = context.envelope.command === "openspec" ? buildOpenspecPhaseContext(context, phase).knowledge : context.knowledge;
24381
+ const knowledge = context.envelope.command === "openspec" ? buildOpenspecPhaseContext(context, phase).knowledge : scopeKnowledgeForRole(context.knowledge, role);
24118
24382
  const layers = [
24119
24383
  { name: "system", content: buildSystemLayer() },
24120
- { name: "agent", content: buildAgentLayer(role, phase) },
24384
+ { name: "agent", content: buildAgentLayer(role, phase, schemaName) },
24121
24385
  {
24122
24386
  name: "policies",
24123
24387
  content: JSON.stringify({ ...context.policies, promptVersion }, null, 2)
@@ -24135,10 +24399,12 @@ ${schemaBody}`
24135
24399
  ${layer.content}`).join(`
24136
24400
 
24137
24401
  `);
24138
- return { layers, prompt };
24402
+ return { layers, prompt, outputSchema: schemaName };
24139
24403
  }
24140
- var ROLE_LABELS, OUTPUT_SCHEMAS;
24404
+ var ROLE_LABELS, OUTPUT_SCHEMAS, KNOWLEDGE_KEYS_BY_ROLE, PRODUCT_GRAPH_KNOWLEDGE_KEYS;
24141
24405
  var init_prompt_compiler = __esm(() => {
24406
+ init_untrusted_text();
24407
+ init_output_validator();
24142
24408
  ROLE_LABELS = {
24143
24409
  "task-coach": "Task Coach",
24144
24410
  architect: "Architect",
@@ -24149,7 +24415,12 @@ var init_prompt_compiler = __esm(() => {
24149
24415
  "contract-builder": "Contract Builder",
24150
24416
  "complexity-auditor": "Complexity Auditor",
24151
24417
  orchestrator: "Orchestrator",
24152
- "repo-explorer": "Repo Explorer"
24418
+ "repo-explorer": "Repo Explorer",
24419
+ product: "Product",
24420
+ delivery: "Delivery",
24421
+ "data-ops": "DataOps",
24422
+ "security-reviewer": "Security Reviewer",
24423
+ devil: "Devil"
24153
24424
  };
24154
24425
  OUTPUT_SCHEMAS = {
24155
24426
  "planner-output": `{
@@ -24193,110 +24464,57 @@ var init_prompt_compiler = __esm(() => {
24193
24464
  "filesChanged": [{ "path": "", "summary": "" }],
24194
24465
  "tests": { "runner": "", "result": "passed" | "failed" }
24195
24466
  }`,
24196
- "agent-output": '{ "status": "success", "artifacts": [], "next_actions": [] }'
24197
- };
24198
- });
24199
-
24200
- // src/core/ccep/output-validator.ts
24201
- function resolveOutputSchemaName(schemaName, role) {
24202
- if (schemaName !== "agent-output" && SCHEMA_REGISTRY[schemaName]) {
24203
- return schemaName;
24204
- }
24205
- if (role === "implementer") {
24206
- return "implementer-output";
24207
- }
24208
- if (role === "reviewer") {
24209
- return "review-report";
24210
- }
24211
- if (role === "task-coach" || role === "architect") {
24212
- return schemaName === "technical-plan" ? "technical-plan" : "planner-output";
24213
- }
24214
- return schemaName;
24215
- }
24216
- function validateAgentOutputBySchema(schemaName, data, role) {
24217
- const resolved = resolveOutputSchemaName(schemaName, role);
24218
- const schema = SCHEMA_REGISTRY[resolved];
24219
- if (!schema) {
24220
- return {
24221
- valid: false,
24222
- schema: resolved,
24223
- errors: [`Unknown output schema: ${resolved}`]
24224
- };
24225
- }
24226
- const parsed = schema.safeParse(data);
24227
- if (parsed.success) {
24228
- return { valid: true, schema: resolved, data: parsed.data };
24229
- }
24230
- return {
24231
- valid: false,
24232
- schema: resolved,
24233
- errors: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
24234
- };
24235
- }
24236
- function parseJsonInput(raw) {
24237
- return JSON.parse(raw);
24238
- }
24239
- function validateOutputForRole(role, outputSchema, data) {
24240
- if (role === "implementer") {
24241
- try {
24242
- return { valid: true, schema: "implementer-output", data: validateImplementerOutput(data) };
24243
- } catch (err3) {
24244
- return {
24245
- valid: false,
24246
- schema: "implementer-output",
24247
- errors: [String(err3)]
24248
- };
24249
- }
24250
- }
24251
- if (role === "reviewer") {
24252
- try {
24253
- return { valid: true, schema: "review-report", data: validateReviewerOutput(data) };
24254
- } catch (err3) {
24255
- return {
24256
- valid: false,
24257
- schema: "review-report",
24258
- errors: [String(err3)]
24259
- };
24260
- }
24261
- }
24262
- if (outputSchema === "planner-output" || role === "task-coach") {
24263
- try {
24264
- return { valid: true, schema: "planner-output", data: validatePlannerOutput(data) };
24265
- } catch (err3) {
24266
- return {
24267
- valid: false,
24268
- schema: "planner-output",
24269
- errors: [String(err3)]
24270
- };
24271
- }
24272
- }
24273
- return validateAgentOutputBySchema(outputSchema, data, role);
24274
- }
24275
- var SCHEMA_REGISTRY;
24276
- var init_output_validator = __esm(() => {
24277
- init_schemas();
24278
- SCHEMA_REGISTRY = {
24279
- "planner-output": PlannerOutputSchema,
24280
- "implementer-output": ImplementerOutputSchema,
24281
- "review-report": ReviewerOutputSchema,
24282
- "technical-plan": TechnicalPlanOutputSchema,
24283
- "fix-intake-output": AgentOutputSchema,
24284
- "council-verdict": CouncilVerdictSchema,
24285
- "agent-output": AgentOutputSchema,
24286
- taskcard: CanonicalTaskCardSchema,
24287
- "test-plan": AgentOutputSchema,
24288
- "complexity-audit": AgentOutputSchema,
24289
- "api-contract": AgentOutputSchema,
24290
- "pagespeed-report": AgentOutputSchema,
24291
- "scorecard-record": ScorecardRecordSchema
24292
- };
24467
+ "agent-output": `{
24468
+ "status": "success" | "failure" | "blocked" | "needs_clarification",
24469
+ "confidence": 0.0,
24470
+ "warnings": [],
24471
+ "artifacts": [],
24472
+ "next_actions": []
24473
+ }`,
24474
+ "scorecard-record": `{
24475
+ "id": "",
24476
+ "taskId": "",
24477
+ "agent": "",
24478
+ "contractVersion": "",
24479
+ "criteria": [],
24480
+ "weightedScore": 0.0,
24481
+ "verdict": "PASS" | "REVISE" | "REJECT",
24482
+ "findings": [],
24483
+ "createdAt": ""
24484
+ }`
24485
+ };
24486
+ KNOWLEDGE_KEYS_BY_ROLE = {
24487
+ "task-coach": ["productName", "domains", "requirements", "risks"],
24488
+ architect: ["productName", "domains", "decisions", "requirements", "risks"],
24489
+ tester: ["productName", "requirements", "risks"],
24490
+ implementer: ["productName", "decisions", "requirements"],
24491
+ reviewer: ["productName", "decisions", "requirements", "risks"],
24492
+ docs: ["productName", "decisions", "requirements"],
24493
+ "contract-builder": ["productName", "domains", "decisions", "requirements"],
24494
+ "complexity-auditor": ["productName", "decisions", "requirements", "risks"],
24495
+ orchestrator: ["productName", "domains", "decisions", "requirements", "risks"],
24496
+ "repo-explorer": ["productName", "domains", "requirements"],
24497
+ product: ["productName", "requirements"],
24498
+ delivery: ["productName", "requirements", "risks"],
24499
+ "data-ops": ["productName", "domains", "requirements"],
24500
+ "security-reviewer": ["productName", "decisions", "requirements", "risks"],
24501
+ devil: ["decisions", "requirements", "risks"]
24502
+ };
24503
+ PRODUCT_GRAPH_KNOWLEDGE_KEYS = new Set([
24504
+ "productName",
24505
+ "domains",
24506
+ "decisions",
24507
+ "risks",
24508
+ "requirements",
24509
+ "nodeCount"
24510
+ ]);
24293
24511
  });
24294
24512
 
24295
24513
  // src/core/ccep/command-parser.ts
24296
24514
  import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
24297
- import { basename as basename7, join as join20 } from "node:path";
24515
+ import { basename as basename7, join as join21 } from "node:path";
24298
24516
  function readProjectId(projectRoot) {
24299
- const packageJsonPath = join20(projectRoot, "package.json");
24517
+ const packageJsonPath = join21(projectRoot, "package.json");
24300
24518
  if (existsSync5(packageJsonPath)) {
24301
24519
  try {
24302
24520
  const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
@@ -24326,7 +24544,7 @@ function parseCommand(command, userRequest, projectRoot) {
24326
24544
  maxVariance: "low"
24327
24545
  }
24328
24546
  };
24329
- const packageJsonPath = join20(projectRoot, "package.json");
24547
+ const packageJsonPath = join21(projectRoot, "package.json");
24330
24548
  if (existsSync5(packageJsonPath)) {
24331
24549
  try {
24332
24550
  const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
@@ -24382,9 +24600,9 @@ function resolveWorkflowPhase(profile, phaseId) {
24382
24600
  if (!phase) {
24383
24601
  return null;
24384
24602
  }
24385
- const role = phase.agent ?? phase.agents?.[0] ?? "orchestrator";
24603
+ const roles = phase.agent !== undefined ? [phase.agent] : phase.agents !== undefined && phase.agents.length > 0 ? [...phase.agents] : ["orchestrator"];
24386
24604
  const outputSchema = phase.outputSchema ?? profile.intakeSchema ?? "agent-output";
24387
- return { id: phase.id, role, outputSchema };
24605
+ return { id: phase.id, role: roles[0], roles, outputSchema };
24388
24606
  }
24389
24607
  function loadWorkflowProfile(command, projectRoot) {
24390
24608
  const fromYaml = loadWorkflowProfileFromYaml(command, projectRoot);
@@ -24401,8 +24619,8 @@ var init_workflow_profile_loader = __esm(() => {
24401
24619
  // src/core/evaluation/harness-catalog.ts
24402
24620
  import { createHash as createHash5 } from "node:crypto";
24403
24621
  import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
24404
- import { copyFile as copyFile2, mkdir as mkdir20, readFile as readFile30, readdir as readdir10, rm as rm4, writeFile as writeFile20 } from "node:fs/promises";
24405
- import { dirname as dirname12, join as join21, resolve as resolve29 } from "node:path";
24622
+ import { copyFile as copyFile2, mkdir as mkdir19, readFile as readFile31, readdir as readdir10, rm as rm4, writeFile as writeFile19 } from "node:fs/promises";
24623
+ import { dirname as dirname11, join as join22, resolve as resolve29 } from "node:path";
24406
24624
  function catalogIds(catalog = DEFAULT_HARNESS_CATALOG) {
24407
24625
  return catalog.components.map((c) => c.id);
24408
24626
  }
@@ -24457,7 +24675,7 @@ function harnessFingerprint(disabled, contractVersion, ccepProfile = "feature",
24457
24675
  async function loadHarnessCatalog(projectRoot) {
24458
24676
  const overridePath = resolve29(projectRoot, EVAL_DIR2, CATALOG_FILE);
24459
24677
  try {
24460
- const raw = await readFile30(overridePath, "utf-8");
24678
+ const raw = await readFile31(overridePath, "utf-8");
24461
24679
  return HarnessCatalogSchema.parse($parse(raw));
24462
24680
  } catch {
24463
24681
  return DEFAULT_HARNESS_CATALOG;
@@ -24468,7 +24686,7 @@ function activeOverlayPath(projectRoot) {
24468
24686
  }
24469
24687
  async function readActiveOverlay(projectRoot) {
24470
24688
  try {
24471
- const raw = await readFile30(activeOverlayPath(projectRoot), "utf-8");
24689
+ const raw = await readFile31(activeOverlayPath(projectRoot), "utf-8");
24472
24690
  return HarnessOverlaySchema.parse($parse(raw));
24473
24691
  } catch {
24474
24692
  return null;
@@ -24525,22 +24743,22 @@ async function restoreOverlayBackup(projectRoot) {
24525
24743
  const backupRoot = resolve29(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
24526
24744
  if (!existsSync6(backupRoot))
24527
24745
  return;
24528
- const configBackup = join21(backupRoot, "config.yml");
24746
+ const configBackup = join22(backupRoot, "config.yml");
24529
24747
  const configDest = resolve29(projectRoot, ".codeconductor", "config.yml");
24530
24748
  if (existsSync6(configBackup)) {
24531
- await mkdir20(dirname12(configDest), { recursive: true });
24749
+ await mkdir19(dirname11(configDest), { recursive: true });
24532
24750
  await copyFile2(configBackup, configDest);
24533
24751
  }
24534
- const workflowsBackup = join21(backupRoot, "workflows");
24752
+ const workflowsBackup = join22(backupRoot, "workflows");
24535
24753
  const workflowsDest = resolve29(projectRoot, ".codeconductor", "workflows");
24536
24754
  if (existsSync6(workflowsBackup)) {
24537
- await mkdir20(workflowsDest, { recursive: true });
24755
+ await mkdir19(workflowsDest, { recursive: true });
24538
24756
  const files = await readdir10(workflowsBackup);
24539
24757
  for (const file of files) {
24540
- await copyFile2(join21(workflowsBackup, file), join21(workflowsDest, file));
24758
+ await copyFile2(join22(workflowsBackup, file), join22(workflowsDest, file));
24541
24759
  }
24542
24760
  }
24543
- const overlayBackup = join21(backupRoot, ACTIVE_OVERLAY_FILE);
24761
+ const overlayBackup = join22(backupRoot, ACTIVE_OVERLAY_FILE);
24544
24762
  const overlayDest = activeOverlayPath(projectRoot);
24545
24763
  if (existsSync6(overlayBackup)) {
24546
24764
  await copyFile2(overlayBackup, overlayDest);
@@ -24552,25 +24770,25 @@ async function snapshotForBackup(projectRoot) {
24552
24770
  const backupRoot = resolve29(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
24553
24771
  if (existsSync6(backupRoot))
24554
24772
  return;
24555
- await mkdir20(backupRoot, { recursive: true });
24773
+ await mkdir19(backupRoot, { recursive: true });
24556
24774
  const configPath = resolve29(projectRoot, ".codeconductor", "config.yml");
24557
24775
  if (existsSync6(configPath)) {
24558
- await copyFile2(configPath, join21(backupRoot, "config.yml"));
24776
+ await copyFile2(configPath, join22(backupRoot, "config.yml"));
24559
24777
  }
24560
24778
  const workflowsDir = resolve29(projectRoot, ".codeconductor", "workflows");
24561
24779
  if (existsSync6(workflowsDir)) {
24562
- const dest = join21(backupRoot, "workflows");
24563
- await mkdir20(dest, { recursive: true });
24780
+ const dest = join22(backupRoot, "workflows");
24781
+ await mkdir19(dest, { recursive: true });
24564
24782
  const files = await readdir10(workflowsDir);
24565
24783
  for (const file of files) {
24566
24784
  if (file.endsWith(".yml")) {
24567
- await copyFile2(join21(workflowsDir, file), join21(dest, file));
24785
+ await copyFile2(join22(workflowsDir, file), join22(dest, file));
24568
24786
  }
24569
24787
  }
24570
24788
  }
24571
24789
  const overlay = activeOverlayPath(projectRoot);
24572
24790
  if (existsSync6(overlay)) {
24573
- await copyFile2(overlay, join21(backupRoot, ACTIVE_OVERLAY_FILE));
24791
+ await copyFile2(overlay, join22(backupRoot, ACTIVE_OVERLAY_FILE));
24574
24792
  }
24575
24793
  }
24576
24794
  async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
@@ -24580,12 +24798,12 @@ async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
24580
24798
  await snapshotForBackup(projectRoot);
24581
24799
  }
24582
24800
  const workflowsDir = resolve29(projectRoot, ".codeconductor", "workflows");
24583
- await mkdir20(workflowsDir, { recursive: true });
24584
- await mkdir20(resolve29(projectRoot, EVAL_DIR2), { recursive: true });
24801
+ await mkdir19(workflowsDir, { recursive: true });
24802
+ await mkdir19(resolve29(projectRoot, EVAL_DIR2), { recursive: true });
24585
24803
  for (const command of WORKFLOW_OVERLAY_COMMANDS) {
24586
24804
  const base = loadWorkflowProfile(command);
24587
24805
  const next = applyCatalogToProfile(base, disabled);
24588
- await writeFile20(join21(workflowsDir, `${command}.yml`), $stringify(next), "utf-8");
24806
+ await writeFile19(join22(workflowsDir, `${command}.yml`), $stringify(next), "utf-8");
24589
24807
  }
24590
24808
  const needsConfig = disabled.includes("compile_loop") || disabled.includes("council");
24591
24809
  if (needsConfig) {
@@ -24613,7 +24831,7 @@ async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
24613
24831
  contractVersion: options.contractVersion,
24614
24832
  ccepProfile: options.ccepProfile ?? "feature"
24615
24833
  });
24616
- await writeFile20(activeOverlayPath(projectRoot), $stringify(overlay), "utf-8");
24834
+ await writeFile19(activeOverlayPath(projectRoot), $stringify(overlay), "utf-8");
24617
24835
  return ok(overlay);
24618
24836
  } catch (e) {
24619
24837
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -24725,7 +24943,7 @@ var init_harness_catalog = __esm(() => {
24725
24943
 
24726
24944
  // src/core/openspec/openspec-state.ts
24727
24945
  import { createHash as createHash6 } from "node:crypto";
24728
- import { mkdir as mkdir21, readFile as readFile31, rename as rename4, writeFile as writeFile21 } from "node:fs/promises";
24946
+ import { mkdir as mkdir20, readFile as readFile32, rename as rename4, writeFile as writeFile20 } from "node:fs/promises";
24729
24947
  import { resolve as resolve30 } from "node:path";
24730
24948
  function canTransition(from, to) {
24731
24949
  return ALLOWED_TRANSITIONS[from]?.includes(to) ?? false;
@@ -24747,13 +24965,13 @@ function applyBacklogTransition(content, itemId, from, to, progress) {
24747
24965
  }
24748
24966
  async function writeFileAtomic(path, content) {
24749
24967
  const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
24750
- await writeFile21(tmp, content, "utf-8");
24968
+ await writeFile20(tmp, content, "utf-8");
24751
24969
  await rename4(tmp, path);
24752
24970
  }
24753
24971
  async function loadOpenspecState(projectRoot) {
24754
24972
  try {
24755
24973
  const filePath = resolve30(projectRoot, STATE_FILE);
24756
- const content = await readFile31(filePath, "utf-8");
24974
+ const content = await readFile32(filePath, "utf-8");
24757
24975
  const data = JSON.parse(content);
24758
24976
  return ok(OpenspecStateSchema.parse(data));
24759
24977
  } catch (e) {
@@ -24771,7 +24989,7 @@ async function loadOpenspecState(projectRoot) {
24771
24989
  async function writeOpenspecState(projectRoot, state) {
24772
24990
  try {
24773
24991
  const dir = resolve30(projectRoot, ".codeconductor");
24774
- await mkdir21(dir, { recursive: true });
24992
+ await mkdir20(dir, { recursive: true });
24775
24993
  const validated = OpenspecStateSchema.parse(state);
24776
24994
  await writeFileAtomic(resolve30(dir, "openspec-state.json"), JSON.stringify(validated, null, 2));
24777
24995
  return ok(undefined);
@@ -24878,8 +25096,8 @@ var init_openspec_state = __esm(() => {
24878
25096
 
24879
25097
  // src/core/ccep/context-resolver.ts
24880
25098
  import { existsSync as existsSync7 } from "node:fs";
24881
- import { readFile as readFile32 } from "node:fs/promises";
24882
- import { join as join22 } from "node:path";
25099
+ import { readFile as readFile33 } from "node:fs/promises";
25100
+ import { join as join23 } from "node:path";
24883
25101
  async function loadProductKnowledge(projectRoot) {
24884
25102
  if (isProductGraphDisabled(projectRoot)) {
24885
25103
  return {};
@@ -24889,7 +25107,7 @@ async function loadProductKnowledge(projectRoot) {
24889
25107
  return {};
24890
25108
  }
24891
25109
  try {
24892
- const raw = await readFile32(graphPath, "utf-8");
25110
+ const raw = await readFile33(graphPath, "utf-8");
24893
25111
  const graph = ProductGraphSchema.parse(JSON.parse(raw));
24894
25112
  return {
24895
25113
  productName: graph.productName,
@@ -24905,7 +25123,7 @@ async function loadProductKnowledge(projectRoot) {
24905
25123
  }
24906
25124
  async function resolveAstSource(projectRoot) {
24907
25125
  if (isProductGraphDisabled(projectRoot)) {
24908
- const graphifyPath2 = join22(projectRoot, "graphify-out", "graph.json");
25126
+ const graphifyPath2 = join23(projectRoot, "graphify-out", "graph.json");
24909
25127
  if (existsSync7(graphifyPath2)) {
24910
25128
  return { source: "graphify", confidence: "medium" };
24911
25129
  }
@@ -24915,7 +25133,7 @@ async function resolveAstSource(projectRoot) {
24915
25133
  if (existsSync7(graphPath)) {
24916
25134
  return { source: "product-graph", confidence: "high" };
24917
25135
  }
24918
- const graphifyPath = join22(projectRoot, "graphify-out", "graph.json");
25136
+ const graphifyPath = join23(projectRoot, "graphify-out", "graph.json");
24919
25137
  if (existsSync7(graphifyPath)) {
24920
25138
  return { source: "graphify", confidence: "medium" };
24921
25139
  }
@@ -24991,6 +25209,9 @@ function canonicalAgentId(agentId) {
24991
25209
  function isSecurityCategory(category) {
24992
25210
  return SECURITY_CATEGORIES.has(category.trim().toLowerCase());
24993
25211
  }
25212
+ function isSecurityEmitter(agentId) {
25213
+ return canonicalAgentId(agentId).includes("security");
25214
+ }
24994
25215
  function resolveQuorum(config, ballotCount) {
24995
25216
  if (typeof config.quorum === "number") {
24996
25217
  const required = Math.max(1, config.quorum);
@@ -25115,7 +25336,7 @@ function councilConsensus(verdicts, config = DEFAULT_CONFIG2) {
25115
25336
  if (confidence < 0.6) {
25116
25337
  hasLowConfidence = true;
25117
25338
  }
25118
- if (config.allowSecurityVeto && v.securityVeto && v.status === "REJECTED") {
25339
+ if (config.allowSecurityVeto && v.securityVeto && v.status === "REJECTED" && isSecurityEmitter(v.agentId)) {
25119
25340
  vetoApplied = true;
25120
25341
  vetoByAgentId = v.agentId;
25121
25342
  }
@@ -25125,7 +25346,7 @@ function councilConsensus(verdicts, config = DEFAULT_CONFIG2) {
25125
25346
  }
25126
25347
  for (const finding of v.findings) {
25127
25348
  allFindings.push(finding);
25128
- if (config.allowSecurityVeto && finding.severity === "critical" && isSecurityCategory(finding.category)) {
25349
+ if (config.allowSecurityVeto && finding.severity === "critical" && isSecurityCategory(finding.category) && isSecurityEmitter(v.agentId)) {
25129
25350
  vetoApplied = true;
25130
25351
  vetoByAgentId = v.agentId;
25131
25352
  }
@@ -25264,6 +25485,148 @@ var init_council_consensus = __esm(() => {
25264
25485
  ]);
25265
25486
  });
25266
25487
 
25488
+ // src/domain/council/council-spec.ts
25489
+ function defaultCouncilSpec() {
25490
+ return {
25491
+ name: "council",
25492
+ version: "0.1.0",
25493
+ description: "Multi-agent council",
25494
+ outputContract: "v1",
25495
+ agents: DEFAULT_COUNCIL_AGENTS
25496
+ };
25497
+ }
25498
+ function hasSecurityFocusedAgent(spec) {
25499
+ return spec.agents.some((agent) => {
25500
+ const haystack = [agent.id, agent.role, ...agent.focus].join(" ").toLowerCase();
25501
+ return SECURITY_HINTS.some((hint) => haystack.includes(hint));
25502
+ });
25503
+ }
25504
+ function deriveConsensusConfig(spec, overrides = {}) {
25505
+ const expectedAgentIds = overrides.expectedAgentIds ?? spec.agents.map((agent) => agent.id);
25506
+ const allowSecurityVeto = overrides.allowSecurityVeto ?? true;
25507
+ if (allowSecurityVeto && !hasSecurityFocusedAgent(spec)) {
25508
+ throw new Error("allowSecurityVeto requires at least one security-focused agent in the council roster");
25509
+ }
25510
+ return {
25511
+ algorithm: overrides.algorithm ?? "majority",
25512
+ allowSecurityVeto,
25513
+ allowComplianceVeto: overrides.allowComplianceVeto ?? true,
25514
+ expectedAgentIds,
25515
+ quorum: overrides.quorum ?? Math.ceil(expectedAgentIds.length / 2),
25516
+ criticalFindingsPolicy: overrides.criticalFindingsPolicy ?? "escalate",
25517
+ candidateHash: overrides.candidateHash
25518
+ };
25519
+ }
25520
+ function selectCouncilPanel(spec, request) {
25521
+ const type = request.type.trim().toLowerCase();
25522
+ const scope = request.scope.join(" ").toLowerCase();
25523
+ const selected = new Set(["delivery", "security-reviewer", "devil"]);
25524
+ const isFeature = type === "feature" || type === "api";
25525
+ const isStructural = ["feature", "api", "refactor", "migration", "database"].includes(type);
25526
+ if (isFeature || request.risk !== "low")
25527
+ selected.add("architect");
25528
+ if (isFeature || request.risk === "high")
25529
+ selected.add("product");
25530
+ if (DATA_HINTS.some((hint) => scope.includes(hint)) || ["migration", "database"].includes(type)) {
25531
+ selected.add("data-ops");
25532
+ }
25533
+ if (request.risk === "high" || SECURITY_HINTS.some((hint) => scope.includes(hint))) {
25534
+ selected.add("security-reviewer");
25535
+ }
25536
+ if (isStructural)
25537
+ selected.add("architect");
25538
+ const expectedAgentIds = spec.agents.map((agent) => agent.id).filter((id) => selected.has(id));
25539
+ return deriveConsensusConfig(spec, {
25540
+ expectedAgentIds,
25541
+ quorum: Math.ceil(expectedAgentIds.length / 2)
25542
+ });
25543
+ }
25544
+ var SECURITY_HINTS, DEFAULT_COUNCIL_AGENTS, DATA_HINTS;
25545
+ var init_council_spec = __esm(() => {
25546
+ SECURITY_HINTS = ["security", "auth", "injection", "credentials", "supply-chain"];
25547
+ DEFAULT_COUNCIL_AGENTS = [
25548
+ {
25549
+ id: "architect",
25550
+ role: "Architect",
25551
+ context: "repo-readonly",
25552
+ modelHint: "strong-reasoning",
25553
+ focus: ["architecture", "design-patterns", "code-structure"]
25554
+ },
25555
+ {
25556
+ id: "product",
25557
+ role: "Product",
25558
+ context: "prompt-only",
25559
+ modelHint: "balanced",
25560
+ focus: ["requirements", "ux", "business-value"]
25561
+ },
25562
+ {
25563
+ id: "delivery",
25564
+ role: "Delivery",
25565
+ context: "repo-readonly",
25566
+ modelHint: "practical-coding",
25567
+ focus: ["delivery", "testing", "deployment"]
25568
+ },
25569
+ {
25570
+ id: "data-ops",
25571
+ role: "DataOps",
25572
+ context: "repo-readonly",
25573
+ modelHint: "analytical",
25574
+ focus: ["data", "pipelines", "analytics"]
25575
+ },
25576
+ {
25577
+ id: "security-reviewer",
25578
+ role: "Security Reviewer",
25579
+ context: "repo-readonly",
25580
+ modelHint: "security-reasoning",
25581
+ focus: [
25582
+ "security",
25583
+ "vulnerabilities",
25584
+ "compliance",
25585
+ "credentials",
25586
+ "injection",
25587
+ "auth",
25588
+ "supply-chain"
25589
+ ]
25590
+ },
25591
+ {
25592
+ id: "devil",
25593
+ role: "Devil",
25594
+ context: "repo-readonly",
25595
+ modelHint: "adversarial",
25596
+ focus: ["review", "edge-cases", "failure-modes"]
25597
+ }
25598
+ ];
25599
+ DATA_HINTS = ["data", "database", "migration", "analytics", "pipeline", "sql"];
25600
+ });
25601
+
25602
+ // src/core/ccep/risk-classifier.ts
25603
+ function classifyRisk(input) {
25604
+ const type = (input.type ?? "").toLowerCase();
25605
+ const blob = [type, ...input.targetFiles ?? [], ...input.signals ?? []].join(" ");
25606
+ if (HIGH_RE.test(blob) || INFRA_RE.test(blob) || type === "db-migration" || type === "api-contract") {
25607
+ return "high";
25608
+ }
25609
+ if (type === "docs" || type === "review") {
25610
+ return "low";
25611
+ }
25612
+ if (type === "test") {
25613
+ return "low";
25614
+ }
25615
+ if (type === "refactor" && (input.signals ?? []).some((s) => /full[-_ ]?test[-_ ]?coverage/i.test(s))) {
25616
+ return "low";
25617
+ }
25618
+ if (type === "fix") {
25619
+ const isolated = (input.signals ?? []).some((s) => /isolated/i.test(s));
25620
+ return isolated ? "low" : "medium";
25621
+ }
25622
+ return "medium";
25623
+ }
25624
+ var HIGH_RE, INFRA_RE;
25625
+ var init_risk_classifier = __esm(() => {
25626
+ HIGH_RE = /(migrat|schema|auth(?:n|z|entication|orization)?|oauth|payment|billing|credential|password|secret|openapi|public[-_ ]?api|api[-_ ]?contract)/i;
25627
+ INFRA_RE = /(alembic|\.github[/\\]workflows|dockerfile|terraform|\.tf\b|package-lock\.json|go\.sum|cargo\.lock)/i;
25628
+ });
25629
+
25267
25630
  // src/core/shared/vague-criterion.ts
25268
25631
  function isVagueCriterion(text) {
25269
25632
  const t = text.trim();
@@ -25424,16 +25787,34 @@ function consensusExitCode(status) {
25424
25787
  return 1;
25425
25788
  return 2;
25426
25789
  }
25790
+ function parseConsensusBallots(verdicts) {
25791
+ if (!Array.isArray(verdicts)) {
25792
+ return { ok: false, error: "Consensus input must be a verdicts array or { verdicts, config? }" };
25793
+ }
25794
+ const parsed = [];
25795
+ for (let i = 0;i < verdicts.length; i++) {
25796
+ const ballot = CouncilVerdictInputSchema.safeParse(verdicts[i]);
25797
+ if (!ballot.success) {
25798
+ return {
25799
+ ok: false,
25800
+ error: ballot.error.issues.map((issue) => `verdicts[${i}].${issue.path.join(".")}: ${issue.message}`).join("; ")
25801
+ };
25802
+ }
25803
+ parsed.push(ballot.data);
25804
+ }
25805
+ return { ok: true, verdicts: parsed };
25806
+ }
25427
25807
  function parseConsensusPayload(data) {
25428
25808
  if (Array.isArray(data)) {
25429
- return { ok: true, verdicts: data };
25809
+ return parseConsensusBallots(data);
25430
25810
  }
25431
25811
  if (typeof data !== "object" || data === null) {
25432
25812
  return { ok: false, error: "Consensus input must be a verdicts array or { verdicts, config? }" };
25433
25813
  }
25434
25814
  const record = data;
25435
- if (!Array.isArray(record.verdicts)) {
25436
- return { ok: false, error: "Consensus input must be a verdicts array or { verdicts, config? }" };
25815
+ const ballots = parseConsensusBallots(record.verdicts);
25816
+ if (!ballots.ok) {
25817
+ return ballots;
25437
25818
  }
25438
25819
  let config;
25439
25820
  if (record.config !== undefined) {
@@ -25446,7 +25827,19 @@ function parseConsensusPayload(data) {
25446
25827
  }
25447
25828
  config = parsed.data;
25448
25829
  }
25449
- return { ok: true, verdicts: record.verdicts, config };
25830
+ return { ok: true, verdicts: ballots.verdicts, config };
25831
+ }
25832
+ function resolvePanelRisk(panelRisk, type, scope) {
25833
+ if (panelRisk === undefined || panelRisk === "") {
25834
+ return { ok: true, risk: classifyRisk({ type, signals: scope }) };
25835
+ }
25836
+ if (panelRisk !== "low" && panelRisk !== "medium" && panelRisk !== "high") {
25837
+ return {
25838
+ ok: false,
25839
+ error: `Invalid --risk: ${panelRisk}. Expected one of: low, medium, high`
25840
+ };
25841
+ }
25842
+ return { ok: true, risk: panelRisk };
25450
25843
  }
25451
25844
  async function ccepCommand(options) {
25452
25845
  const {
@@ -25459,8 +25852,17 @@ async function ccepCommand(options) {
25459
25852
  input,
25460
25853
  contextPath,
25461
25854
  promptVersion = "v1.0.0",
25855
+ view = "full",
25856
+ recordTelemetry = false,
25857
+ executionId,
25858
+ taskId,
25859
+ contextStrategy,
25462
25860
  config,
25463
- rest
25861
+ rest,
25862
+ panel,
25863
+ panelType,
25864
+ panelScope,
25865
+ panelRisk
25464
25866
  } = options;
25465
25867
  switch (subcommand) {
25466
25868
  case "parse": {
@@ -25509,6 +25911,28 @@ async function ccepCommand(options) {
25509
25911
  };
25510
25912
  }
25511
25913
  case "compile": {
25914
+ if (!["prompt", "layers", "full"].includes(view)) {
25915
+ return {
25916
+ code: 1,
25917
+ data: {
25918
+ success: false,
25919
+ command: "ccep compile",
25920
+ errors: [`Unknown compile view: ${view}`]
25921
+ }
25922
+ };
25923
+ }
25924
+ if (contextStrategy !== undefined && !CONTEXT_STRATEGIES.includes(contextStrategy)) {
25925
+ return {
25926
+ code: 1,
25927
+ data: {
25928
+ success: false,
25929
+ command: "ccep compile",
25930
+ errors: [
25931
+ `Invalid context strategy: ${contextStrategy}. Expected one of: ${CONTEXT_STRATEGIES.join(", ")}`
25932
+ ]
25933
+ }
25934
+ };
25935
+ }
25512
25936
  const parsed = parseWorkflowCommand(command);
25513
25937
  if (!parsed.ok) {
25514
25938
  return {
@@ -25559,12 +25983,69 @@ async function ccepCommand(options) {
25559
25983
  outputSchema: resolvedPhase.outputSchema
25560
25984
  };
25561
25985
  }
25986
+ const compileStartedAt = Date.now();
25562
25987
  const compiled = compilePrompt({
25563
25988
  role: agentRole,
25564
25989
  phase: phaseId,
25565
25990
  context,
25566
25991
  promptVersion
25567
25992
  });
25993
+ if (recordTelemetry) {
25994
+ const event = await appendEvent(projectRoot, {
25995
+ type: "context.compiled",
25996
+ timestamp: new Date().toISOString(),
25997
+ payload: {
25998
+ workflow: parsed.command,
25999
+ phase: phaseId,
26000
+ agent: agentRole,
26001
+ executionId: executionId ?? "unknown",
26002
+ taskId: taskId ?? "unknown",
26003
+ contextStrategy: contextStrategy ?? "unknown",
26004
+ promptVersion,
26005
+ view,
26006
+ source: "compiler",
26007
+ success: true,
26008
+ provider: "unknown",
26009
+ model: "unknown",
26010
+ effort: "unknown",
26011
+ sessionId: "unknown",
26012
+ parentSessionId: "unknown",
26013
+ requestedModel: "unknown",
26014
+ effectiveModel: "unknown",
26015
+ inputTokens: "unknown",
26016
+ outputTokens: "unknown",
26017
+ reasoningTokens: "unknown",
26018
+ cacheReadTokens: "unknown",
26019
+ cacheWriteTokens: "unknown",
26020
+ cachedTokens: "unknown",
26021
+ contextTokensEstimate: "unknown",
26022
+ filesRead: "unknown",
26023
+ uniqueFilesRead: "unknown",
26024
+ repeatedFileReads: "unknown",
26025
+ toolCalls: "unknown",
26026
+ retries: "unknown",
26027
+ compactions: "unknown",
26028
+ modelSwitches: "unknown",
26029
+ promptBytes: Buffer.byteLength(compiled.prompt, "utf-8"),
26030
+ layerBytes: Object.fromEntries(compiled.layers.map((layer) => [
26031
+ layer.name,
26032
+ Buffer.byteLength(layer.content, "utf-8")
26033
+ ])),
26034
+ durationMs: Date.now() - compileStartedAt
26035
+ }
26036
+ });
26037
+ if (!event.success) {
26038
+ return {
26039
+ code: 1,
26040
+ data: {
26041
+ success: false,
26042
+ command: "ccep compile",
26043
+ errors: [`Could not record compile telemetry: ${event.error.message}`]
26044
+ }
26045
+ };
26046
+ }
26047
+ }
26048
+ const representation = view === "prompt" ? { prompt: compiled.prompt } : view === "layers" ? { layers: compiled.layers } : { layers: compiled.layers, prompt: compiled.prompt };
25568
26049
  return {
25569
26050
  code: 0,
25570
26051
  data: {
@@ -25572,10 +26053,10 @@ async function ccepCommand(options) {
25572
26053
  command: "ccep compile",
25573
26054
  phase: phaseId,
25574
26055
  role: agentRole,
25575
- outputSchema: resolvedPhase.outputSchema,
26056
+ phaseRoles: resolvedPhase.roles,
26057
+ outputSchema: compiled.outputSchema,
25576
26058
  promptVersion,
25577
- layers: compiled.layers,
25578
- prompt: compiled.prompt
26059
+ ...representation
25579
26060
  }
25580
26061
  };
25581
26062
  }
@@ -25684,6 +26165,30 @@ async function ccepCommand(options) {
25684
26165
  };
25685
26166
  }
25686
26167
  case "consensus": {
26168
+ let panelConfig;
26169
+ if (panel) {
26170
+ const type = panelType ?? "";
26171
+ const scope = (panelScope ?? "").split(",").map((s) => s.trim()).filter((s) => s.length > 0);
26172
+ const risk = resolvePanelRisk(panelRisk, type, scope);
26173
+ if (!risk.ok) {
26174
+ return {
26175
+ code: 1,
26176
+ data: { success: false, command: "ccep consensus", errors: [risk.error] }
26177
+ };
26178
+ }
26179
+ panelConfig = selectCouncilPanel(defaultCouncilSpec(), { type, risk: risk.risk, scope });
26180
+ const hasBallots = (input ?? rest?.join(" ").trim() ?? "") !== "";
26181
+ if (!hasBallots) {
26182
+ return {
26183
+ code: 0,
26184
+ data: {
26185
+ success: true,
26186
+ command: "ccep consensus",
26187
+ panel: panelConfig
26188
+ }
26189
+ };
26190
+ }
26191
+ }
25687
26192
  const payload = await readInputPayload(projectRoot, input, rest);
25688
26193
  if (!payload.ok) {
25689
26194
  return {
@@ -25698,7 +26203,7 @@ async function ccepCommand(options) {
25698
26203
  data: { success: false, command: "ccep consensus", errors: [parsedBallots.error] }
25699
26204
  };
25700
26205
  }
25701
- let mergedConfig = parsedBallots.config;
26206
+ let mergedConfig = panelConfig ?? parsedBallots.config;
25702
26207
  if (config) {
25703
26208
  const configPayload = await readInputPayload(projectRoot, config);
25704
26209
  if (!configPayload.ok) {
@@ -25829,16 +26334,21 @@ async function ccepCommand(options) {
25829
26334
  };
25830
26335
  }
25831
26336
  }
26337
+ var CONTEXT_STRATEGIES;
25832
26338
  var init_ccep_command = __esm(() => {
25833
26339
  init_path_containment();
25834
26340
  init_prompt_compiler();
26341
+ init_episodic_store();
25835
26342
  init_output_validator();
25836
26343
  init_command_parser();
25837
26344
  init_context_resolver();
25838
26345
  init_workflow_profile_loader();
25839
26346
  init_council_consensus();
26347
+ init_council_spec();
26348
+ init_risk_classifier();
25840
26349
  init_task_card_validator();
25841
26350
  init_schemas();
26351
+ CONTEXT_STRATEGIES = ["sticky", "isolated", "artifact", "compact", "fork", "full"];
25842
26352
  });
25843
26353
 
25844
26354
  // src/core/openspec/backlog-validator.ts
@@ -25981,8 +26491,6 @@ var init_backlog_validator = __esm(() => {
25981
26491
 
25982
26492
  // src/core/openspec/backlog-scanner.ts
25983
26493
  import { execFileSync } from "node:child_process";
25984
- import { readFile as readFile33 } from "node:fs/promises";
25985
- import { resolve as resolve31 } from "node:path";
25986
26494
  function itemSnapshot(items) {
25987
26495
  const snap = {};
25988
26496
  for (const item of items) {
@@ -25990,67 +26498,106 @@ function itemSnapshot(items) {
25990
26498
  }
25991
26499
  return snap;
25992
26500
  }
25993
- async function scanBacklog(projectRoot, previousSnapshot = {}) {
26501
+ function checkBacklogFileChanged(projectRoot) {
25994
26502
  try {
25995
- const filePath = resolve31(projectRoot, BACKLOG_FILENAME);
25996
- const content = await readFile33(filePath, "utf-8");
25997
- const hash = hashContent(content);
25998
- let fileChanged = true;
25999
- try {
26000
- const diff = execFileSync("git", ["diff", "--name-only", "--", BACKLOG_FILENAME], {
26001
- cwd: projectRoot,
26002
- encoding: "utf-8",
26003
- stdio: ["ignore", "pipe", "ignore"]
26004
- });
26005
- fileChanged = diff.trim().length > 0;
26006
- } catch {
26007
- fileChanged = true;
26008
- }
26009
- const parseResult = parseBacklogMarkdown(content);
26010
- if (!parseResult.success)
26011
- return parseResult;
26012
- const doc = parseResult.data;
26013
- const current = itemSnapshot([...doc.items, ...doc.archive]);
26014
- const newItems = [];
26015
- const modifiedItems = [];
26016
- const closedItems = [];
26017
- for (const [id, snap] of Object.entries(current)) {
26018
- if (!previousSnapshot[id]) {
26019
- newItems.push(id);
26020
- } else if (previousSnapshot[id] !== snap) {
26021
- modifiedItems.push(id);
26022
- }
26023
- }
26024
- for (const id of Object.keys(previousSnapshot)) {
26025
- if (!current[id])
26026
- closedItems.push(id);
26503
+ const diff = execFileSync("git", ["diff", "--name-only", "--", BACKLOG_FILENAME], {
26504
+ cwd: projectRoot,
26505
+ encoding: "utf-8",
26506
+ stdio: ["ignore", "pipe", "ignore"]
26507
+ });
26508
+ return diff.trim().length > 0;
26509
+ } catch {
26510
+ return true;
26511
+ }
26512
+ }
26513
+ function computeScanDiff(content, doc, previousSnapshot = {}, fileChanged = true) {
26514
+ const hash = hashContent(content);
26515
+ const current = itemSnapshot([...doc.items, ...doc.archive]);
26516
+ const newItems = [];
26517
+ const modifiedItems = [];
26518
+ const closedItems = [];
26519
+ for (const [id, snap] of Object.entries(current)) {
26520
+ if (!previousSnapshot[id]) {
26521
+ newItems.push(id);
26522
+ } else if (previousSnapshot[id] !== snap) {
26523
+ modifiedItems.push(id);
26027
26524
  }
26028
- for (const item of doc.archive) {
26029
- if (!closedItems.includes(item.id))
26030
- closedItems.push(item.id);
26525
+ }
26526
+ for (const id of Object.keys(previousSnapshot)) {
26527
+ if (!current[id])
26528
+ closedItems.push(id);
26529
+ }
26530
+ for (const item of doc.archive) {
26531
+ if (!closedItems.includes(item.id))
26532
+ closedItems.push(item.id);
26533
+ }
26534
+ for (const item of doc.items) {
26535
+ if (item.status === "DONE" && !closedItems.includes(item.id)) {
26536
+ closedItems.push(item.id);
26031
26537
  }
26032
- for (const item of doc.items) {
26033
- if (item.status === "DONE" && !closedItems.includes(item.id)) {
26034
- closedItems.push(item.id);
26035
- }
26538
+ }
26539
+ return {
26540
+ fileChanged,
26541
+ contentHash: hash,
26542
+ newItems,
26543
+ modifiedItems,
26544
+ closedItems
26545
+ };
26546
+ }
26547
+ function buildItemSnapshot(items, archive) {
26548
+ return itemSnapshot([...items, ...archive]);
26549
+ }
26550
+ var init_backlog_scanner = __esm(() => {
26551
+ init_backlog_parser();
26552
+ init_openspec_state();
26553
+ });
26554
+
26555
+ // src/core/openspec/backlog-session.ts
26556
+ import { readFile as readFile34 } from "node:fs/promises";
26557
+ import { resolve as resolve31 } from "node:path";
26558
+ async function loadBacklogSession(projectRoot) {
26559
+ try {
26560
+ const raw = await readFile34(resolve31(projectRoot, BACKLOG_FILENAME), "utf-8");
26561
+ const parsed = parseBacklogMarkdown(raw);
26562
+ if (!parsed.success)
26563
+ return parsed;
26564
+ const stateResult = await loadOpenspecState(projectRoot);
26565
+ if (!stateResult.success)
26566
+ return stateResult;
26567
+ const state = stateResult.data;
26568
+ const report = validateBacklog(parsed.data);
26569
+ if (!report.valid) {
26570
+ return err(new BacklogSessionError(parsed.data, report, state));
26036
26571
  }
26037
26572
  return ok({
26038
- fileChanged,
26039
- contentHash: hash,
26040
- newItems,
26041
- modifiedItems,
26042
- closedItems
26573
+ raw,
26574
+ doc: parsed.data,
26575
+ state,
26576
+ scan: computeScanDiff(raw, parsed.data, state.itemSnapshots ?? {}, checkBacklogFileChanged(projectRoot)),
26577
+ report
26043
26578
  });
26044
26579
  } catch (e) {
26045
26580
  return err(e instanceof Error ? e : new Error(String(e)));
26046
26581
  }
26047
26582
  }
26048
- function buildItemSnapshot(items, archive) {
26049
- return itemSnapshot([...items, ...archive]);
26050
- }
26051
- var init_backlog_scanner = __esm(() => {
26583
+ var BacklogSessionError;
26584
+ var init_backlog_session = __esm(() => {
26052
26585
  init_backlog_parser();
26586
+ init_backlog_validator();
26587
+ init_backlog_scanner();
26053
26588
  init_openspec_state();
26589
+ BacklogSessionError = class BacklogSessionError extends Error {
26590
+ doc;
26591
+ report;
26592
+ state;
26593
+ constructor(doc, report, state) {
26594
+ super(report.errors.map((e) => e.message).join("; ") || "Invalid BACKLOG.md");
26595
+ this.name = "BacklogSessionError";
26596
+ this.doc = doc;
26597
+ this.report = report;
26598
+ this.state = state;
26599
+ }
26600
+ };
26054
26601
  });
26055
26602
 
26056
26603
  // src/core/openspec/agent-router.ts
@@ -26085,8 +26632,11 @@ function phaseTitle(phase, item) {
26085
26632
  function phasePrompt(phase, item) {
26086
26633
  const base = `Backlog item ${item.id} — ${item.title}
26087
26634
 
26088
- Description: ${item.description}
26089
- Scope: ${item.scope}`;
26635
+ ` + `Description:
26636
+ ${markUntrusted("description", item.description)}
26637
+
26638
+ ` + `Scope:
26639
+ ${markUntrusted("scope", item.scope)}`;
26090
26640
  switch (phase) {
26091
26641
  case "discover":
26092
26642
  return `${base}
@@ -26213,6 +26763,7 @@ var DEFAULT_PHASE_ORDER, TDD_PHASE_ORDER;
26213
26763
  var init_backlog_planner = __esm(() => {
26214
26764
  init_agent_router();
26215
26765
  init_openspec_state();
26766
+ init_untrusted_text();
26216
26767
  DEFAULT_PHASE_ORDER = [
26217
26768
  "discover",
26218
26769
  "design",
@@ -26230,7 +26781,7 @@ var init_backlog_planner = __esm(() => {
26230
26781
  });
26231
26782
 
26232
26783
  // src/core/openspec/openspec-generator.ts
26233
- import { mkdir as mkdir22, rename as rename5, writeFile as writeFile22 } from "node:fs/promises";
26784
+ import { access as access7, mkdir as mkdir21, rename as rename5, writeFile as writeFile21 } from "node:fs/promises";
26234
26785
  import { basename as basename8, resolve as resolve32 } from "node:path";
26235
26786
  function pad3(n) {
26236
26787
  return String(n).padStart(3, "0");
@@ -26243,13 +26794,14 @@ function proposalContent(item) {
26243
26794
 
26244
26795
  ## Why
26245
26796
 
26246
- ${item.description}
26797
+ ${markUntrusted("description", item.description)}
26247
26798
 
26248
- ${item.businessValue ? `**Business value:** ${item.businessValue}` : ""}
26799
+ ${item.businessValue ? `**Business value:**
26800
+ ${markUntrusted("business-value", item.businessValue)}` : ""}
26249
26801
 
26250
26802
  ## What Changes
26251
26803
 
26252
- - ${item.scope}
26804
+ - ${markUntrusted("scope", item.scope)}
26253
26805
 
26254
26806
  ## Capabilities
26255
26807
 
@@ -26258,9 +26810,11 @@ ${item.businessValue ? `**Business value:** ${item.businessValue}` : ""}
26258
26810
 
26259
26811
  ## Impact
26260
26812
 
26261
- ${item.risks ? `Risks: ${item.risks}` : "See design.md for technical impact."}
26813
+ ${item.risks ? `Risks:
26814
+ ${markUntrusted("risks", item.risks)}` : "See design.md for technical impact."}
26262
26815
 
26263
- **Out of scope:** ${item.outOfScope || "None specified."}
26816
+ **Out of scope:**
26817
+ ${markUntrusted("out-of-scope", item.outOfScope || "None specified.")}
26264
26818
  `;
26265
26819
  }
26266
26820
  function designContent(item) {
@@ -26268,15 +26822,20 @@ function designContent(item) {
26268
26822
 
26269
26823
  ## Approach
26270
26824
 
26271
- Deliver "${item.title}" inside ${item.scope}. The architect MUST refine files and risks before implementation.
26825
+ Deliver "${item.title}" inside:
26826
+
26827
+ ${markUntrusted("scope", item.scope)}
26828
+
26829
+ The architect MUST refine files and risks before implementation.
26272
26830
 
26273
26831
  ## Files Affected
26274
26832
 
26275
- ${item.scope}
26833
+ ${markUntrusted("scope", item.scope)}
26276
26834
 
26277
26835
  ## Acceptance Criteria
26278
26836
 
26279
- ${item.acceptanceCriteria.map((c) => `- ${c}`).join(`
26837
+ ${item.acceptanceCriteria.map((c) => markUntrusted("acceptance-criterion", c)).join(`
26838
+
26280
26839
  `)}
26281
26840
 
26282
26841
  ## Complexity Tracking
@@ -26310,8 +26869,8 @@ ${blocks.join(`
26310
26869
  `;
26311
26870
  }
26312
26871
  async function writeTasksMarkdown(projectRoot, changePath, cards, options = {}) {
26313
- await mkdir22(resolve32(projectRoot, changePath), { recursive: true });
26314
- await writeFile22(resolve32(projectRoot, changePath, "tasks.md"), tasksContent(cards, options), "utf-8");
26872
+ await mkdir21(resolve32(projectRoot, changePath), { recursive: true });
26873
+ await writeFile21(resolve32(projectRoot, changePath, "tasks.md"), tasksContent(cards, options), "utf-8");
26315
26874
  }
26316
26875
  function tasksContent(cards, options = {}) {
26317
26876
  const criteria = options.acceptanceCriteria ?? [];
@@ -26357,18 +26916,18 @@ function tasksContent(cards, options = {}) {
26357
26916
  async function generateOpenspecChange(projectRoot, item, taskCards, options = {}) {
26358
26917
  const slug = buildChangeSlug(item);
26359
26918
  const changeDir = resolve32(projectRoot, "openspec", "changes", slug);
26360
- await mkdir22(resolve32(changeDir, "specs"), { recursive: true });
26919
+ await mkdir21(resolve32(changeDir, "specs"), { recursive: true });
26361
26920
  const taskOpts = {
26362
26921
  tddRequired: options.tddRequired,
26363
26922
  acceptanceCriteria: options.acceptanceCriteria ?? item.acceptanceCriteria
26364
26923
  };
26365
- await writeFile22(resolve32(changeDir, "proposal.md"), proposalContent(item), "utf-8");
26366
- await writeFile22(resolve32(changeDir, "design.md"), designContent(item), "utf-8");
26367
- await writeFile22(resolve32(changeDir, "tasks.md"), tasksContent(taskCards, taskOpts), "utf-8");
26924
+ await writeFile21(resolve32(changeDir, "proposal.md"), proposalContent(item), "utf-8");
26925
+ await writeFile21(resolve32(changeDir, "design.md"), designContent(item), "utf-8");
26926
+ await writeFile21(resolve32(changeDir, "tasks.md"), tasksContent(taskCards, taskOpts), "utf-8");
26368
26927
  const capability = capabilitySlug(item.title);
26369
- await mkdir22(resolve32(changeDir, "specs", capability), { recursive: true });
26370
- await writeFile22(resolve32(changeDir, "specs", capability, "spec.md"), specDelta(item), "utf-8");
26371
- await writeFile22(resolve32(changeDir, "change.yaml"), `version: 1
26928
+ await mkdir21(resolve32(changeDir, "specs", capability), { recursive: true });
26929
+ await writeFile21(resolve32(changeDir, "specs", capability, "spec.md"), specDelta(item), "utf-8");
26930
+ await writeFile21(resolve32(changeDir, "change.yaml"), `version: 1
26372
26931
  backlogId: ${item.id}
26373
26932
  profile: standard
26374
26933
  capabilities:
@@ -26380,38 +26939,86 @@ async function archiveChangeFolder(projectRoot, changePath) {
26380
26939
  const src = resolve32(projectRoot, changePath);
26381
26940
  const slug = basename8(changePath);
26382
26941
  const destDir = resolve32(projectRoot, "openspec", "changes", "archive");
26383
- await mkdir22(destDir, { recursive: true });
26942
+ await mkdir21(destDir, { recursive: true });
26384
26943
  const dest = resolve32(destDir, slug);
26944
+ try {
26945
+ await access7(dest);
26946
+ throw new Error(`Archive destination already exists: openspec/changes/archive/${slug}`);
26947
+ } catch (error) {
26948
+ if (error instanceof Error && error.message.startsWith("Archive destination already exists")) {
26949
+ throw error;
26950
+ }
26951
+ }
26385
26952
  await rename5(src, dest);
26386
26953
  return `openspec/changes/archive/${slug}`;
26387
26954
  }
26388
26955
  async function ensureOpenspecConfig(projectRoot) {
26389
26956
  const configPath = resolve32(projectRoot, "openspec", "config.yaml");
26390
26957
  try {
26391
- const { access: access7 } = await import("node:fs/promises");
26392
- await access7(configPath);
26958
+ const { access: access8 } = await import("node:fs/promises");
26959
+ await access8(configPath);
26393
26960
  } catch {
26394
- await mkdir22(resolve32(projectRoot, "openspec"), { recursive: true });
26961
+ await mkdir21(resolve32(projectRoot, "openspec"), { recursive: true });
26395
26962
  const stub = `# OpenSpec project config (CodeConductor native compatible)
26396
26963
  schema: spec-driven
26397
26964
  `;
26398
- await writeFile22(configPath, stub, "utf-8");
26965
+ await writeFile21(configPath, stub, "utf-8");
26399
26966
  }
26400
26967
  }
26401
26968
  var init_openspec_generator = __esm(() => {
26402
26969
  init_backlog_planner();
26970
+ init_untrusted_text();
26971
+ });
26972
+
26973
+ // src/core/openspec/spec-files.ts
26974
+ import { readFile as readFile35, readdir as readdir11 } from "node:fs/promises";
26975
+ import { join as join24 } from "node:path";
26976
+ async function walkMarkdownFiles(dir) {
26977
+ let entries;
26978
+ try {
26979
+ entries = await readdir11(dir, { withFileTypes: true });
26980
+ } catch {
26981
+ return [];
26982
+ }
26983
+ const files = [];
26984
+ for (const entry of entries) {
26985
+ const path = join24(dir, entry.name);
26986
+ if (entry.isDirectory()) {
26987
+ files.push(...await walkMarkdownFiles(path));
26988
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
26989
+ files.push(path);
26990
+ }
26991
+ }
26992
+ return files;
26993
+ }
26994
+ async function readMarkdownFiles(dir) {
26995
+ const files = await walkMarkdownFiles(dir);
26996
+ const out = [];
26997
+ for (const path of files) {
26998
+ out.push({ path, content: await readFile35(path, "utf-8") });
26999
+ }
27000
+ return out;
27001
+ }
27002
+ function requirementBlocks(markdown) {
27003
+ const starts = [...markdown.matchAll(/^### Requirement:.*$/gm)];
27004
+ return starts.map((match, index) => {
27005
+ const start = match.index ?? 0;
27006
+ const end = starts[index + 1]?.index ?? markdown.length;
27007
+ const block = markdown.slice(start, end).trim();
27008
+ return { id: block.match(REQUIREMENT_HEADING)?.[1] ?? null, markdown: block };
27009
+ });
27010
+ }
27011
+ var REQUIREMENT_HEADING;
27012
+ var init_spec_files = __esm(() => {
27013
+ REQUIREMENT_HEADING = /^### Requirement:.*?\b(FR-\d{3})\b.*$/m;
26403
27014
  });
26404
27015
 
26405
27016
  // src/core/openspec/spec-quality.ts
26406
- import { readdir as readdir11, readFile as readFile34 } from "node:fs/promises";
26407
- import { join as join23, relative as relative10, resolve as resolve33 } from "node:path";
27017
+ import { readFile as readFile36 } from "node:fs/promises";
27018
+ import { join as join25, relative as relative10, resolve as resolve33 } from "node:path";
26408
27019
  function uniqueIds(matches) {
26409
27020
  return [...new Set(matches ?? [])];
26410
27021
  }
26411
- function requirementBlocks(content) {
26412
- const parts = content.split(/^### Requirement:/m);
26413
- return parts.slice(1).map((block) => `### Requirement:${block}`);
26414
- }
26415
27022
  function assessSpecMarkdown(content, path) {
26416
27023
  const issues = [];
26417
27024
  const clarifications = content.match(NEEDS_CLARIFICATION) ?? [];
@@ -26454,9 +27061,10 @@ function assessSpecMarkdown(content, path) {
26454
27061
  });
26455
27062
  }
26456
27063
  for (const block of blocks) {
26457
- const heading = block.split(`
27064
+ const text = block.markdown;
27065
+ const heading = text.split(`
26458
27066
  `, 1)[0] ?? "";
26459
- if (!RFC2119.test(block)) {
27067
+ if (!RFC2119.test(text)) {
26460
27068
  issues.push({
26461
27069
  code: "MISSING_RFC2119",
26462
27070
  message: `Requirement "${heading}" MUST use RFC 2119 keywords (MUST/SHALL/SHOULD/MAY)`,
@@ -26464,7 +27072,7 @@ function assessSpecMarkdown(content, path) {
26464
27072
  severity: "error"
26465
27073
  });
26466
27074
  }
26467
- if (!GWT.test(block)) {
27075
+ if (!GWT.test(text)) {
26468
27076
  issues.push({
26469
27077
  code: "MISSING_GWT",
26470
27078
  message: `Requirement "${heading}" MUST include a Given/When/Then scenario`,
@@ -26517,24 +27125,6 @@ function mergeSpecQualityReports(reports) {
26517
27125
  successCriteria
26518
27126
  };
26519
27127
  }
26520
- async function walkMarkdown(dir) {
26521
- const out = [];
26522
- let entries;
26523
- try {
26524
- entries = await readdir11(dir, { withFileTypes: true });
26525
- } catch {
26526
- return out;
26527
- }
26528
- for (const entry of entries) {
26529
- const full = join23(dir, entry.name);
26530
- if (entry.isDirectory()) {
26531
- out.push(...await walkMarkdown(full));
26532
- } else if (entry.isFile() && entry.name.endsWith(".md")) {
26533
- out.push(full);
26534
- }
26535
- }
26536
- return out;
26537
- }
26538
27128
  async function assessChangeFolder(projectRoot, changePath) {
26539
27129
  const root = resolve33(projectRoot);
26540
27130
  const changeRoot = resolve33(root, changePath);
@@ -26555,18 +27145,18 @@ async function assessChangeFolder(projectRoot, changePath) {
26555
27145
  successCriteria: []
26556
27146
  };
26557
27147
  }
26558
- const specDir = join23(changeRoot, "specs");
26559
- const specFiles = await walkMarkdown(specDir);
26560
- const extra = ["proposal.md", "design.md"].map((name) => join23(changeRoot, name));
27148
+ const specDir = join25(changeRoot, "specs");
27149
+ const specFiles = await walkMarkdownFiles(specDir);
27150
+ const extra = ["proposal.md", "design.md"].map((name) => join25(changeRoot, name));
26561
27151
  const reports = [];
26562
27152
  for (const file of specFiles) {
26563
- const content = await readFile34(file, "utf-8");
27153
+ const content = await readFile36(file, "utf-8");
26564
27154
  reports.push(assessSpecMarkdown(content, relative10(root, file).replace(/\\/g, "/")));
26565
27155
  }
26566
27156
  for (const file of extra) {
26567
27157
  let content;
26568
27158
  try {
26569
- content = await readFile34(file, "utf-8");
27159
+ content = await readFile36(file, "utf-8");
26570
27160
  } catch {
26571
27161
  continue;
26572
27162
  }
@@ -26610,6 +27200,7 @@ async function assessChangeFolder(projectRoot, changePath) {
26610
27200
  }
26611
27201
  var RFC2119, FR_ID, SC_ID, US_AC, GWT, NEEDS_CLARIFICATION, PLACEHOLDER, HOW_LEAK, MAX_CLARIFICATIONS = 3;
26612
27202
  var init_spec_quality = __esm(() => {
27203
+ init_spec_files();
26613
27204
  RFC2119 = /\b(MUST(?: NOT)?|SHALL(?: NOT)?|SHOULD(?: NOT)?|MAY)\b/;
26614
27205
  FR_ID = /\bFR-\d{3}\b/g;
26615
27206
  SC_ID = /\bSC-\d{3}\b/g;
@@ -26621,8 +27212,8 @@ var init_spec_quality = __esm(() => {
26621
27212
  });
26622
27213
 
26623
27214
  // src/core/openspec/spec-analyzer.ts
26624
- import { readFile as readFile35, readdir as readdir12 } from "node:fs/promises";
26625
- import { join as join24, resolve as resolve34 } from "node:path";
27215
+ import { readFile as readFile37 } from "node:fs/promises";
27216
+ import { join as join26, resolve as resolve34 } from "node:path";
26626
27217
  function unique(ids) {
26627
27218
  return [...new Set(ids)];
26628
27219
  }
@@ -26647,22 +27238,8 @@ function testSection(tasksMarkdown) {
26647
27238
  `);
26648
27239
  }
26649
27240
  async function readDeltaSpecs(specDir) {
26650
- let entries;
26651
- try {
26652
- entries = await readdir12(specDir, { withFileTypes: true });
26653
- } catch {
26654
- return "";
26655
- }
26656
- const contents = [];
26657
- for (const entry of entries) {
26658
- const path = join24(specDir, entry.name);
26659
- if (entry.isDirectory()) {
26660
- contents.push(await readDeltaSpecs(path));
26661
- } else if (entry.isFile() && entry.name.endsWith(".md")) {
26662
- contents.push(await readFile35(path, "utf-8"));
26663
- }
26664
- }
26665
- return contents.filter(Boolean).join(`
27241
+ const files = await readMarkdownFiles(specDir);
27242
+ return files.map((f) => f.content).filter(Boolean).join(`
26666
27243
 
26667
27244
  `);
26668
27245
  }
@@ -26738,16 +27315,16 @@ function analyzeSpecArtifacts(input) {
26738
27315
  }
26739
27316
  async function analyzeChangeFolder(projectRoot, changePath, options) {
26740
27317
  const changeRoot = resolve34(projectRoot, changePath);
26741
- const specMarkdown = await readDeltaSpecs(join24(changeRoot, "specs"));
27318
+ const specMarkdown = await readDeltaSpecs(join26(changeRoot, "specs"));
26742
27319
  let tasksMarkdown = "";
26743
27320
  try {
26744
- tasksMarkdown = await readFile35(join24(changeRoot, "tasks.md"), "utf-8");
27321
+ tasksMarkdown = await readFile37(join26(changeRoot, "tasks.md"), "utf-8");
26745
27322
  } catch {
26746
27323
  tasksMarkdown = "";
26747
27324
  }
26748
27325
  let designMarkdown;
26749
27326
  try {
26750
- designMarkdown = await readFile35(join24(changeRoot, "design.md"), "utf-8");
27327
+ designMarkdown = await readFile37(join26(changeRoot, "design.md"), "utf-8");
26751
27328
  } catch {
26752
27329
  designMarkdown = undefined;
26753
27330
  }
@@ -26762,53 +27339,28 @@ async function analyzeChangeFolder(projectRoot, changePath, options) {
26762
27339
  });
26763
27340
  }
26764
27341
  var init_spec_analyzer = __esm(() => {
27342
+ init_spec_files();
26765
27343
  init_spec_quality();
26766
27344
  });
26767
27345
 
26768
27346
  // src/core/openspec/spec-sync.ts
26769
- import { mkdir as mkdir23, readFile as readFile36, readdir as readdir13 } from "node:fs/promises";
26770
- import { join as join25, relative as relative11, resolve as resolve35 } from "node:path";
26771
- async function markdownFiles(dir) {
26772
- let entries;
26773
- try {
26774
- entries = await readdir13(dir, { withFileTypes: true });
26775
- } catch {
26776
- return [];
26777
- }
26778
- const files = [];
26779
- for (const entry of entries) {
26780
- const path = join25(dir, entry.name);
26781
- if (entry.isDirectory())
26782
- files.push(...await markdownFiles(path));
26783
- else if (entry.isFile() && entry.name.endsWith(".md"))
26784
- files.push(path);
26785
- }
26786
- return files;
26787
- }
26788
- function requirementBlocks2(markdown) {
26789
- const starts = [...markdown.matchAll(/^### Requirement:.*$/gm)];
26790
- return starts.flatMap((match, index) => {
26791
- const start = match.index ?? 0;
26792
- const end = starts[index + 1]?.index ?? markdown.length;
26793
- const block = markdown.slice(start, end).trim();
26794
- const id = block.match(REQUIREMENT_HEADING)?.[1];
26795
- return id ? [{ id, markdown: block }] : [];
26796
- });
26797
- }
27347
+ import { mkdir as mkdir22, readFile as readFile38 } from "node:fs/promises";
27348
+ import { join as join27, relative as relative11, resolve as resolve35 } from "node:path";
26798
27349
  function deltaRequirements(markdown) {
26799
27350
  const sections = [...markdown.matchAll(SECTION_HEADING)];
26800
27351
  return sections.flatMap((section, index) => {
26801
27352
  const start = (section.index ?? 0) + section[0].length;
26802
27353
  const end = sections[index + 1]?.index ?? markdown.length;
26803
27354
  const operation = section[1];
26804
- return requirementBlocks2(markdown.slice(start, end)).map((requirement) => ({
26805
- ...requirement,
27355
+ return requirementBlocks(markdown.slice(start, end)).filter((requirement) => requirement.id !== null).map((requirement) => ({
27356
+ id: requirement.id,
27357
+ markdown: requirement.markdown,
26806
27358
  operation
26807
27359
  }));
26808
27360
  });
26809
27361
  }
26810
27362
  function upsertRequirements(existing, deltas, source) {
26811
- const requirements = requirementBlocks2(existing);
27363
+ const requirements = requirementBlocks(existing).filter((requirement) => requirement.id !== null);
26812
27364
  const byId = new Map(requirements.map((requirement) => [requirement.id, requirement]));
26813
27365
  if (byId.size !== requirements.length) {
26814
27366
  return { ok: false, error: `${source}: durable spec has duplicate requirement IDs` };
@@ -26840,7 +27392,7 @@ ${delta.markdown}`;
26840
27392
  if (!current)
26841
27393
  continue;
26842
27394
  if (delta.operation === "MODIFIED") {
26843
- next = next.replace(current.markdown, delta.markdown);
27395
+ next = next.replace(current.markdown, () => delta.markdown);
26844
27396
  byId.set(delta.id, { id: delta.id, markdown: delta.markdown });
26845
27397
  } else {
26846
27398
  next = next.replace(current.markdown, "").replace(/\n{3,}/g, `
@@ -26855,17 +27407,17 @@ ${delta.markdown}`;
26855
27407
  async function syncChangeSpecs(projectRoot, changePath) {
26856
27408
  const root = resolve35(projectRoot);
26857
27409
  const changeRoot = resolve35(root, changePath);
26858
- const changeSpecs = join25(changeRoot, "specs");
27410
+ const changeSpecs = join27(changeRoot, "specs");
26859
27411
  if (changeRoot !== root && !changeRoot.startsWith(`${root}/`)) {
26860
27412
  return { success: false, syncedPaths: [], errors: ["Change path escapes project root"] };
26861
27413
  }
26862
- const files = await markdownFiles(changeSpecs);
27414
+ const files = await walkMarkdownFiles(changeSpecs);
26863
27415
  if (files.length === 0) {
26864
27416
  return { success: false, syncedPaths: [], errors: ["Change folder has no delta specs"] };
26865
27417
  }
26866
27418
  const writes = [];
26867
27419
  for (const file of files) {
26868
- const delta = await readFile36(file, "utf-8");
27420
+ const delta = await readFile38(file, "utf-8");
26869
27421
  const requirements = deltaRequirements(delta);
26870
27422
  if (requirements.length === 0) {
26871
27423
  return {
@@ -26875,11 +27427,11 @@ async function syncChangeSpecs(projectRoot, changePath) {
26875
27427
  };
26876
27428
  }
26877
27429
  const relativeSpec = relative11(changeSpecs, file);
26878
- const target = join25(root, "openspec", "specs", relativeSpec);
27430
+ const target = join27(root, "openspec", "specs", relativeSpec);
26879
27431
  let existing = `# ${relativeSpec.replace(/\\/g, "/")}
26880
27432
  `;
26881
27433
  try {
26882
- existing = await readFile36(target, "utf-8");
27434
+ existing = await readFile38(target, "utf-8");
26883
27435
  } catch {}
26884
27436
  const merged = upsertRequirements(existing, requirements, relative11(root, file));
26885
27437
  if (!merged.ok)
@@ -26887,7 +27439,7 @@ async function syncChangeSpecs(projectRoot, changePath) {
26887
27439
  writes.push({ path: target, content: merged.content });
26888
27440
  }
26889
27441
  for (const write of writes) {
26890
- await mkdir23(resolve35(write.path, ".."), { recursive: true });
27442
+ await mkdir22(resolve35(write.path, ".."), { recursive: true });
26891
27443
  await writeFileAtomic(write.path, write.content);
26892
27444
  }
26893
27445
  return {
@@ -26896,20 +27448,104 @@ async function syncChangeSpecs(projectRoot, changePath) {
26896
27448
  errors: []
26897
27449
  };
26898
27450
  }
26899
- var REQUIREMENT_HEADING, SECTION_HEADING;
27451
+ var SECTION_HEADING;
26900
27452
  var init_spec_sync = __esm(() => {
26901
27453
  init_openspec_state();
26902
- REQUIREMENT_HEADING = /^### Requirement:.*?\b(FR-\d{3})\b.*$/m;
27454
+ init_spec_files();
26903
27455
  SECTION_HEADING = /^## (ADDED|MODIFIED|REMOVED) Requirements\s*$/gm;
26904
27456
  });
26905
27457
 
27458
+ // src/core/openspec/artifact-progress.ts
27459
+ import { access as access8, readdir as readdir12, readFile as readFile39 } from "node:fs/promises";
27460
+ import { join as join28, resolve as resolve36 } from "node:path";
27461
+ function countCheckboxes(markdown) {
27462
+ let total = 0;
27463
+ let complete = 0;
27464
+ for (const line of markdown.split(`
27465
+ `)) {
27466
+ const marker = line.match(CHECKBOX)?.[1];
27467
+ if (marker === undefined)
27468
+ continue;
27469
+ total += 1;
27470
+ if (marker === "x" || marker === "X")
27471
+ complete += 1;
27472
+ }
27473
+ return { total, complete, remaining: total - complete };
27474
+ }
27475
+ async function exists(path) {
27476
+ try {
27477
+ await access8(path);
27478
+ return true;
27479
+ } catch {
27480
+ return false;
27481
+ }
27482
+ }
27483
+ async function hasMarkdown(dir) {
27484
+ let entries;
27485
+ try {
27486
+ entries = await readdir12(dir, { withFileTypes: true });
27487
+ } catch {
27488
+ return false;
27489
+ }
27490
+ for (const entry of entries) {
27491
+ const full = join28(dir, entry.name);
27492
+ if (entry.isFile() && entry.name.endsWith(".md"))
27493
+ return true;
27494
+ if (entry.isDirectory() && await hasMarkdown(full))
27495
+ return true;
27496
+ }
27497
+ return false;
27498
+ }
27499
+ async function readArtifactProgress(projectRoot, changePath) {
27500
+ const root = resolve36(projectRoot);
27501
+ const changeRoot = resolve36(root, changePath);
27502
+ if (changeRoot !== root && !changeRoot.startsWith(`${root}/`)) {
27503
+ return ABSENT;
27504
+ }
27505
+ const [proposal, design, tasks, specs] = await Promise.all([
27506
+ exists(join28(changeRoot, "proposal.md")),
27507
+ exists(join28(changeRoot, "design.md")),
27508
+ exists(join28(changeRoot, "tasks.md")),
27509
+ hasMarkdown(join28(changeRoot, "specs"))
27510
+ ]);
27511
+ let checkboxes = { total: 0, complete: 0, remaining: 0 };
27512
+ if (tasks) {
27513
+ try {
27514
+ checkboxes = countCheckboxes(await readFile39(join28(changeRoot, "tasks.md"), "utf-8"));
27515
+ } catch {
27516
+ checkboxes = { total: 0, complete: 0, remaining: 0 };
27517
+ }
27518
+ }
27519
+ return { artifacts: { proposal, design, tasks, specs }, checkboxes };
27520
+ }
27521
+ function missingArtifacts(presence) {
27522
+ const missing = [];
27523
+ if (!presence.proposal)
27524
+ missing.push("proposal.md");
27525
+ if (!presence.design)
27526
+ missing.push("design.md");
27527
+ if (!presence.tasks)
27528
+ missing.push("tasks.md");
27529
+ if (!presence.specs)
27530
+ missing.push("specs/*.md");
27531
+ return missing;
27532
+ }
27533
+ var CHECKBOX, ABSENT;
27534
+ var init_artifact_progress = __esm(() => {
27535
+ CHECKBOX = /^\s*-\s*\[([^\]])\]/;
27536
+ ABSENT = {
27537
+ artifacts: { proposal: false, design: false, tasks: false, specs: false },
27538
+ checkboxes: { total: 0, complete: 0, remaining: 0 }
27539
+ };
27540
+ });
27541
+
26906
27542
  // src/commands/openspec.command.ts
26907
27543
  var exports_openspec_command = {};
26908
27544
  __export(exports_openspec_command, {
26909
27545
  openspecCommand: () => openspecCommand
26910
27546
  });
26911
- import { readFile as readFile37 } from "node:fs/promises";
26912
- import { resolve as resolve36 } from "node:path";
27547
+ import { readFile as readFile40 } from "node:fs/promises";
27548
+ import { resolve as resolve37 } from "node:path";
26913
27549
  async function openspecCommand(options) {
26914
27550
  const { subcommand, itemId, projectRoot, output, reason } = options;
26915
27551
  switch (subcommand) {
@@ -26935,6 +27571,10 @@ async function openspecCommand(options) {
26935
27571
  return handleUnblock(projectRoot, itemId);
26936
27572
  case "archive":
26937
27573
  return handleArchive(projectRoot, itemId);
27574
+ case "sync":
27575
+ return handleSync(projectRoot, itemId);
27576
+ case "verify":
27577
+ return handleVerify(projectRoot, itemId);
26938
27578
  default:
26939
27579
  return {
26940
27580
  code: 1,
@@ -26955,7 +27595,7 @@ function fail(command, errors3, extra = {}) {
26955
27595
  };
26956
27596
  }
26957
27597
  async function persistBacklog(projectRoot, content) {
26958
- await writeFileAtomic(resolve36(projectRoot, BACKLOG_FILENAME), content);
27598
+ await writeFileAtomic(resolve37(projectRoot, BACKLOG_FILENAME), content);
26959
27599
  }
26960
27600
  async function persistState(projectRoot, state) {
26961
27601
  const written = await writeOpenspecState(projectRoot, state);
@@ -26964,17 +27604,13 @@ async function persistState(projectRoot, state) {
26964
27604
  }
26965
27605
  return { ok: true };
26966
27606
  }
26967
- async function readBacklogMarkdown(projectRoot) {
26968
- return readFile37(resolve36(projectRoot, BACKLOG_FILENAME), "utf-8");
26969
- }
26970
- async function transitionItem(projectRoot, item, to, progress) {
26971
- const content = await readBacklogMarkdown(projectRoot);
27607
+ async function transitionItem(projectRoot, item, to, progress, content) {
26972
27608
  const next = applyBacklogTransition(content, item.id, item.status, to, progress);
26973
27609
  if (!next.success) {
26974
27610
  return { ok: false, error: next.error.message };
26975
27611
  }
26976
27612
  await persistBacklog(projectRoot, next.data);
26977
- return { ok: true };
27613
+ return { ok: true, content: next.data };
26978
27614
  }
26979
27615
  function findCard(state, cardId) {
26980
27616
  return state.taskCards.find((c) => c.id === cardId);
@@ -26982,28 +27618,92 @@ function findCard(state, cardId) {
26982
27618
  function itemCards(state, backlogId) {
26983
27619
  return state.taskCards.filter((c) => c.backlogId === backlogId);
26984
27620
  }
26985
- async function handleValidate(projectRoot) {
26986
- const loadResult = await loadBacklog(projectRoot);
26987
- if (!loadResult.success) {
27621
+ function resolveActiveChange(state, command, itemId) {
27622
+ const targetId = itemId ?? state.activeItemId;
27623
+ if (!targetId) {
26988
27624
  return {
26989
- code: 1,
26990
- data: {
26991
- success: false,
26992
- command: "openspec validate",
26993
- errors: [loadResult.error.message],
26994
- recommendations: [
26995
- "Create BACKLOG.md at project root using the CodeConductor template."
26996
- ]
26997
- }
27625
+ ok: false,
27626
+ response: fail(command, ["No active change. Run openspec plan or pass an item id."])
26998
27627
  };
26999
27628
  }
27000
- const report = validateBacklog(loadResult.data);
27001
- const errors3 = report.errors.map((e) => e.message);
27002
- const recommendations = [...report.recommendations];
27629
+ const changePath = state.changePaths[targetId];
27630
+ if (!changePath || changePath.includes("/archive/")) {
27631
+ return { ok: false, response: fail(command, [`No active change folder for ${targetId}`]) };
27632
+ }
27633
+ return { ok: true, targetId, changePath };
27634
+ }
27635
+ async function collectAnalyzeReport(projectRoot, session, targetId, changePath) {
27636
+ const tddRequired = session.doc.global.tddRequired;
27637
+ const policyParts = [];
27638
+ try {
27639
+ policyParts.push(await readFile40(resolve37(projectRoot, "AGENTS.md"), "utf-8"));
27640
+ } catch {}
27641
+ policyParts.push(session.raw);
27642
+ const cards = itemCards(session.state, targetId);
27643
+ const tddCards = cards.filter((c) => c.phase === "test" || c.phase === "implement");
27644
+ let hasTddEvidence;
27645
+ if (tddCards.length > 0) {
27646
+ hasTddEvidence = true;
27647
+ for (const card of tddCards) {
27648
+ const expectedPhase = card.phase === "test" ? "red" : "green";
27649
+ if (!await hasTddRunnerEvidence(projectRoot, card.id, expectedPhase)) {
27650
+ hasTddEvidence = false;
27651
+ break;
27652
+ }
27653
+ }
27654
+ }
27655
+ const report = await analyzeChangeFolder(projectRoot, changePath, {
27656
+ tddRequired,
27657
+ policyText: policyParts.join(`
27658
+ `),
27659
+ hasTddEvidence
27660
+ });
27661
+ return { tddRequired, report };
27662
+ }
27663
+ function buildNextSteps(input) {
27664
+ const { state, nextItemId, progress } = input;
27665
+ if (!state || !state.activeItemId) {
27666
+ return [
27667
+ nextItemId ? `Run openspec plan ${nextItemId} to start the next READY item.` : "No active change and no READY item. Author one with /cc-backlog, then run openspec plan <BC-id>."
27668
+ ];
27669
+ }
27670
+ const itemId = state.activeItemId;
27671
+ const steps = [];
27672
+ if (progress) {
27673
+ const missing = missingArtifacts(progress.artifacts);
27674
+ if (missing.length > 0) {
27675
+ steps.push(`Change folder is missing ${missing.join(", ")} — re-run openspec plan ${itemId} or restore them.`);
27676
+ }
27677
+ }
27678
+ const cards = itemCards(state, itemId);
27679
+ const blocked = cards.find((c) => c.status === "blocked");
27680
+ if (blocked) {
27681
+ steps.push(`Run openspec unblock ${blocked.id} to resume the blocked card.`);
27682
+ return steps;
27683
+ }
27684
+ const next = getNextTaskCard(state);
27685
+ if (next && next.backlogId === itemId) {
27686
+ steps.push(`Run openspec start ${next.id} then delegate to ${next.agent}.`);
27687
+ return steps;
27688
+ }
27689
+ const doing = cards.find((c) => c.status === "doing");
27690
+ if (doing) {
27691
+ steps.push(`Run openspec done ${doing.id} when the ${doing.phase} phase completes.`);
27692
+ return steps;
27693
+ }
27694
+ if (cards.length > 0 && cards.every((c) => c.status === "done")) {
27695
+ steps.push(`Run openspec verify ${itemId}, record a scorecard verdict, then run openspec archive ${itemId}.`);
27696
+ return steps;
27697
+ }
27698
+ steps.push(`Run openspec next to get the next TaskCard for ${itemId}.`);
27699
+ return steps;
27700
+ }
27701
+ async function assessActiveChangeSpecs(projectRoot, state) {
27702
+ const errors3 = [];
27703
+ const recommendations = [];
27003
27704
  let specValid = true;
27004
- const stateResult = await loadOpenspecState(projectRoot);
27005
- const activeId = stateResult.success ? stateResult.data.activeItemId : undefined;
27006
- const changePath = activeId && stateResult.success ? stateResult.data.changePaths[activeId] : undefined;
27705
+ const activeId = state.activeItemId;
27706
+ const changePath = activeId ? state.changePaths[activeId] : undefined;
27007
27707
  if (changePath && !changePath.includes("/archive/")) {
27008
27708
  const specReport = await assessChangeFolder(projectRoot, changePath);
27009
27709
  specValid = specReport.valid;
@@ -27014,7 +27714,32 @@ async function handleValidate(projectRoot) {
27014
27714
  recommendations.push("Spec has [NEEDS CLARIFICATION] markers — run ccep evaluate and wait for human input.");
27015
27715
  }
27016
27716
  }
27017
- const valid = report.valid && specValid;
27717
+ return { specValid, errors: errors3, recommendations };
27718
+ }
27719
+ function sessionParts(session) {
27720
+ return { doc: session.doc, state: session.state, report: session.report };
27721
+ }
27722
+ async function buildValidationResponse(projectRoot, sessionResult) {
27723
+ if (!sessionResult.success && !(sessionResult.error instanceof BacklogSessionError)) {
27724
+ return {
27725
+ code: 1,
27726
+ data: {
27727
+ success: false,
27728
+ command: "openspec validate",
27729
+ errors: [sessionResult.error.message],
27730
+ recommendations: [
27731
+ "Create BACKLOG.md at project root using the CodeConductor template."
27732
+ ]
27733
+ }
27734
+ };
27735
+ }
27736
+ const { doc, state, report } = sessionParts(sessionResult.success ? sessionResult.data : sessionResult.error);
27737
+ const errors3 = report.errors.map((e) => e.message);
27738
+ const recommendations = [...report.recommendations];
27739
+ const spec = await assessActiveChangeSpecs(projectRoot, state);
27740
+ errors3.push(...spec.errors);
27741
+ recommendations.push(...spec.recommendations);
27742
+ const valid = report.valid && spec.specValid;
27018
27743
  return {
27019
27744
  code: valid ? 0 : 1,
27020
27745
  data: {
@@ -27023,57 +27748,56 @@ async function handleValidate(projectRoot) {
27023
27748
  valid,
27024
27749
  errors: errors3,
27025
27750
  recommendations,
27026
- itemCount: loadResult.data.items.length,
27027
- archiveCount: loadResult.data.archive.length
27751
+ itemCount: doc.items.length,
27752
+ archiveCount: doc.archive.length
27028
27753
  }
27029
27754
  };
27030
27755
  }
27756
+ async function handleValidate(projectRoot) {
27757
+ return buildValidationResponse(projectRoot, await loadBacklogSession(projectRoot));
27758
+ }
27031
27759
  async function handleScan(projectRoot) {
27032
- const stateResult = await loadOpenspecState(projectRoot);
27033
- const prevSnap = stateResult.success ? stateResult.data.itemSnapshots ?? {} : {};
27034
- const scanResult = await scanBacklog(projectRoot, prevSnap);
27035
- if (!scanResult.success) {
27760
+ const sessionResult = await loadBacklogSession(projectRoot);
27761
+ if (!sessionResult.success) {
27036
27762
  return {
27037
27763
  code: 1,
27038
27764
  data: {
27039
27765
  success: false,
27040
27766
  command: "openspec scan",
27041
- errors: [scanResult.error.message]
27767
+ errors: [sessionResult.error.message]
27042
27768
  }
27043
27769
  };
27044
27770
  }
27045
- const loadResult = await loadBacklog(projectRoot);
27046
- if (loadResult.success && stateResult.success) {
27047
- const snapshots = buildItemSnapshot(loadResult.data.items, loadResult.data.archive);
27048
- const state = {
27049
- ...stateResult.data,
27050
- lastScanHash: scanResult.data.contentHash,
27051
- lastScanAt: new Date().toISOString(),
27052
- itemSnapshots: snapshots
27053
- };
27054
- const written = await persistState(projectRoot, state);
27055
- if (!written.ok) {
27056
- return fail("openspec scan", [written.error]);
27057
- }
27771
+ const { doc, state: loadedState, scan } = sessionResult.data;
27772
+ const snapshots = buildItemSnapshot(doc.items, doc.archive);
27773
+ const state = {
27774
+ ...loadedState,
27775
+ lastScanHash: scan.contentHash,
27776
+ lastScanAt: new Date().toISOString(),
27777
+ itemSnapshots: snapshots
27778
+ };
27779
+ const written = await persistState(projectRoot, state);
27780
+ if (!written.ok) {
27781
+ return fail("openspec scan", [written.error]);
27058
27782
  }
27059
27783
  return {
27060
27784
  code: 0,
27061
27785
  data: {
27062
27786
  success: true,
27063
27787
  command: "openspec scan",
27064
- ...scanResult.data
27788
+ ...scan
27065
27789
  }
27066
27790
  };
27067
27791
  }
27068
27792
  async function handlePlan(projectRoot, itemId) {
27069
- const validateFirst = await handleValidate(projectRoot);
27793
+ const sessionResult = await loadBacklogSession(projectRoot);
27794
+ const validateFirst = await buildValidationResponse(projectRoot, sessionResult);
27070
27795
  if (validateFirst.code !== 0)
27071
27796
  return validateFirst;
27072
- const loadResult = await loadBacklog(projectRoot);
27073
- if (!loadResult.success) {
27074
- return { code: 1, data: { success: false, errors: [loadResult.error.message] } };
27075
- }
27076
- const doc = loadResult.data;
27797
+ if (!sessionResult.success)
27798
+ return validateFirst;
27799
+ const session = sessionResult.data;
27800
+ const doc = session.doc;
27077
27801
  const item = selectNextItem(doc, itemId);
27078
27802
  if (!item) {
27079
27803
  return {
@@ -27087,13 +27811,7 @@ async function handlePlan(projectRoot, itemId) {
27087
27811
  }
27088
27812
  };
27089
27813
  }
27090
- const stateResult = await loadOpenspecState(projectRoot);
27091
- const existingState = stateResult.success ? stateResult.data : {
27092
- version: 1,
27093
- taskCards: [],
27094
- changePaths: {},
27095
- itemSnapshots: {}
27096
- };
27814
+ const existingState = session.state;
27097
27815
  if (item.status !== "PLANNED" && !canTransition(item.status, "PLANNED")) {
27098
27816
  return fail("openspec plan", [
27099
27817
  `Illegal backlog transition for ${item.id}: ${item.status} → PLANNED.`
@@ -27124,8 +27842,7 @@ async function handlePlan(projectRoot, itemId) {
27124
27842
  if (!written.ok) {
27125
27843
  return fail("openspec plan", [written.error]);
27126
27844
  }
27127
- const content = await readBacklogMarkdown(projectRoot);
27128
- const transition = applyBacklogTransition(content, item.id, item.status, "PLANNED", item.progress);
27845
+ const transition = applyBacklogTransition(session.raw, item.id, item.status, "PLANNED", item.progress);
27129
27846
  if (!transition.success) {
27130
27847
  return fail("openspec plan", [transition.error.message]);
27131
27848
  }
@@ -27146,45 +27863,14 @@ async function handlePlan(projectRoot, itemId) {
27146
27863
  }
27147
27864
  async function handleAnalyze(projectRoot, itemId) {
27148
27865
  const command = "openspec analyze";
27149
- const stateResult = await loadOpenspecState(projectRoot);
27150
- if (!stateResult.success) {
27151
- return fail(command, [stateResult.error.message]);
27152
- }
27153
- const targetId = itemId ?? stateResult.data.activeItemId;
27154
- if (!targetId) {
27155
- return fail(command, ["No active change. Run openspec plan or pass an item id."]);
27156
- }
27157
- const changePath = stateResult.data.changePaths[targetId];
27158
- if (!changePath || changePath.includes("/archive/")) {
27159
- return fail(command, [`No active change folder for ${targetId}`]);
27160
- }
27161
- const backlog = await loadBacklog(projectRoot);
27162
- const tddRequired = backlog.success ? backlog.data.global.tddRequired : false;
27163
- const policyParts = [];
27164
- for (const name of ["AGENTS.md", "BACKLOG.md"]) {
27165
- try {
27166
- policyParts.push(await readFile37(resolve36(projectRoot, name), "utf-8"));
27167
- } catch {}
27168
- }
27169
- const cards = itemCards(stateResult.data, targetId);
27170
- const tddCards = cards.filter((c) => c.phase === "test" || c.phase === "implement");
27171
- let hasTddEvidence;
27172
- if (tddCards.length > 0) {
27173
- hasTddEvidence = true;
27174
- for (const card of tddCards) {
27175
- const expectedPhase = card.phase === "test" ? "red" : "green";
27176
- if (!await hasTddRunnerEvidence(projectRoot, card.id, expectedPhase)) {
27177
- hasTddEvidence = false;
27178
- break;
27179
- }
27180
- }
27181
- }
27182
- const report = await analyzeChangeFolder(projectRoot, changePath, {
27183
- tddRequired,
27184
- policyText: policyParts.join(`
27185
- `),
27186
- hasTddEvidence
27187
- });
27866
+ const sessionResult = await loadBacklogSession(projectRoot);
27867
+ if (!sessionResult.success)
27868
+ return fail(command, [sessionResult.error.message]);
27869
+ const session = sessionResult.data;
27870
+ const resolved = resolveActiveChange(session.state, command, itemId);
27871
+ if (!resolved.ok)
27872
+ return resolved.response;
27873
+ const { report } = await collectAnalyzeReport(projectRoot, session, resolved.targetId, resolved.changePath);
27188
27874
  const data = SpecAnalyzeReportSchema.parse({
27189
27875
  changePath: report.changePath,
27190
27876
  frIds: report.frIds,
@@ -27208,13 +27894,146 @@ async function handleAnalyze(projectRoot, itemId) {
27208
27894
  }
27209
27895
  };
27210
27896
  }
27897
+ async function handleSync(projectRoot, itemId) {
27898
+ const command = "openspec sync";
27899
+ const loaded = await loadStateOrFail(projectRoot, command);
27900
+ if (!loaded.ok)
27901
+ return loaded.response;
27902
+ const resolved = resolveActiveChange(loaded.state, command, itemId);
27903
+ if (!resolved.ok)
27904
+ return resolved.response;
27905
+ const synced = await syncChangeSpecs(projectRoot, resolved.changePath);
27906
+ if (!synced.success) {
27907
+ return fail(command, [
27908
+ `Spec sync failed for ${resolved.targetId}: ${synced.errors.join("; ")}`
27909
+ ]);
27910
+ }
27911
+ return {
27912
+ code: 0,
27913
+ data: {
27914
+ success: true,
27915
+ command,
27916
+ itemId: resolved.targetId,
27917
+ changePath: resolved.changePath,
27918
+ syncedPaths: synced.syncedPaths
27919
+ }
27920
+ };
27921
+ }
27922
+ function analyzeSeverityToVerify(severity) {
27923
+ if (severity === "CRITICAL")
27924
+ return "CRITICAL";
27925
+ if (severity === "HIGH")
27926
+ return "WARNING";
27927
+ return "SUGGESTION";
27928
+ }
27929
+ async function handleVerify(projectRoot, itemId) {
27930
+ const command = "openspec verify";
27931
+ const sessionResult = await loadBacklogSession(projectRoot);
27932
+ if (!sessionResult.success)
27933
+ return fail(command, [sessionResult.error.message]);
27934
+ const session = sessionResult.data;
27935
+ const resolved = resolveActiveChange(session.state, command, itemId);
27936
+ if (!resolved.ok)
27937
+ return resolved.response;
27938
+ const { targetId, changePath } = resolved;
27939
+ const itemLoaded = findItemOrFail(session, targetId, command);
27940
+ if (!itemLoaded.ok)
27941
+ return itemLoaded.response;
27942
+ const { report } = await collectAnalyzeReport(projectRoot, session, targetId, changePath);
27943
+ const progress = await readArtifactProgress(projectRoot, changePath);
27944
+ const cards = itemCards(session.state, targetId);
27945
+ const doneCount = cards.filter((c) => c.status === "done").length;
27946
+ const issues = [];
27947
+ for (const finding of report.findings) {
27948
+ const dimension = finding.code === "POLICY_CONFLICT" || finding.code === "NEEDS_CLARIFICATION" ? "Coherence" : "Correctness";
27949
+ issues.push({
27950
+ severity: analyzeSeverityToVerify(finding.severity),
27951
+ code: finding.code,
27952
+ message: finding.message,
27953
+ dimension
27954
+ });
27955
+ }
27956
+ if (cards.length === 0) {
27957
+ issues.push({
27958
+ severity: "WARNING",
27959
+ code: "NO_CARDS",
27960
+ message: `No task cards for ${targetId}. Run openspec plan ${targetId}.`,
27961
+ dimension: "Completeness"
27962
+ });
27963
+ } else if (doneCount < cards.length) {
27964
+ const pending = cards.filter((c) => c.status !== "done").map((c) => c.id);
27965
+ issues.push({
27966
+ severity: "WARNING",
27967
+ code: "CARDS_PENDING",
27968
+ message: `${cards.length - doneCount} card(s) not done (${pending.join(", ")}).`,
27969
+ dimension: "Completeness"
27970
+ });
27971
+ }
27972
+ for (const name of missingArtifacts(progress.artifacts)) {
27973
+ issues.push({
27974
+ severity: "WARNING",
27975
+ code: "ARTIFACT_MISSING",
27976
+ message: `Change folder is missing ${name}.`,
27977
+ dimension: "Completeness"
27978
+ });
27979
+ }
27980
+ if (progress.checkboxes.remaining > 0) {
27981
+ issues.push({
27982
+ severity: "WARNING",
27983
+ code: "CHECKBOXES_REMAINING",
27984
+ message: `${progress.checkboxes.remaining} of ${progress.checkboxes.total} tasks.md boxes are unchecked. ` + "Tick them as FRs complete (only [x]/[X] counts).",
27985
+ dimension: "Completeness"
27986
+ });
27987
+ }
27988
+ const scorecardOk = !itemLoaded.reviewRequired || await hasPassingScorecard(projectRoot, targetId);
27989
+ if (!scorecardOk) {
27990
+ issues.push({
27991
+ severity: "WARNING",
27992
+ code: "SCORECARD_PENDING",
27993
+ message: `Global review is required but no PASS scorecard exists for ${targetId}.`,
27994
+ dimension: "Coherence"
27995
+ });
27996
+ }
27997
+ const archiveReady = !issues.some((i) => i.severity === "CRITICAL") && cards.length > 0 && doneCount === cards.length && missingArtifacts(progress.artifacts).length === 0 && scorecardOk;
27998
+ return {
27999
+ code: 0,
28000
+ data: {
28001
+ success: true,
28002
+ command,
28003
+ advisory: true,
28004
+ itemId: targetId,
28005
+ changePath,
28006
+ archiveReady,
28007
+ artifacts: progress.artifacts,
28008
+ checkboxProgress: progress.checkboxes,
28009
+ cardsTotal: cards.length,
28010
+ cardsDone: doneCount,
28011
+ frCoveragePct: report.frCoveragePct,
28012
+ scCoveragePct: report.scCoveragePct,
28013
+ testCoveragePct: report.testCoveragePct,
28014
+ issues
28015
+ }
28016
+ };
28017
+ }
27211
28018
  async function handleStatus2(projectRoot) {
27212
- const loadResult = await loadBacklog(projectRoot);
27213
- const stateResult = await loadOpenspecState(projectRoot);
27214
- const nextItem = loadResult.success ? selectNextItem(loadResult.data) : null;
27215
- const state = stateResult.success ? stateResult.data : null;
28019
+ const sessionResult = await loadBacklogSession(projectRoot);
28020
+ let doc = null;
28021
+ let state = null;
28022
+ if (sessionResult.success) {
28023
+ doc = sessionResult.data.doc;
28024
+ state = sessionResult.data.state;
28025
+ } else if (sessionResult.error instanceof BacklogSessionError) {
28026
+ doc = sessionResult.error.doc;
28027
+ state = sessionResult.error.state;
28028
+ } else {
28029
+ const fallback = await loadOpenspecState(projectRoot);
28030
+ state = fallback.success ? fallback.data : null;
28031
+ }
28032
+ const nextItem = doc ? selectNextItem(doc) : null;
27216
28033
  const pendingCards = state?.taskCards.filter((c) => c.status === "pending").length ?? 0;
27217
28034
  const doneCards = state?.taskCards.filter((c) => c.status === "done").length ?? 0;
28035
+ const changePath = state?.activeItemId ? state.changePaths[state.activeItemId] : undefined;
28036
+ const progress = changePath && !changePath.includes("/archive/") ? await readArtifactProgress(projectRoot, changePath) : undefined;
27218
28037
  return {
27219
28038
  code: 0,
27220
28039
  data: {
@@ -27225,7 +28044,10 @@ async function handleStatus2(projectRoot) {
27225
28044
  nextItemTitle: nextItem?.title,
27226
28045
  taskCardsPending: pendingCards,
27227
28046
  taskCardsDone: doneCards,
27228
- changePaths: state?.changePaths ?? {}
28047
+ changePaths: state?.changePaths ?? {},
28048
+ artifacts: progress?.artifacts,
28049
+ checkboxProgress: progress?.checkboxes,
28050
+ nextSteps: buildNextSteps({ state, nextItemId: nextItem?.id, progress })
27229
28051
  }
27230
28052
  };
27231
28053
  }
@@ -27277,19 +28099,15 @@ async function loadStateOrFail(projectRoot, command) {
27277
28099
  }
27278
28100
  return { ok: true, state: stateResult.data };
27279
28101
  }
27280
- async function loadItemOrFail(projectRoot, itemId, command) {
27281
- const loadResult = await loadBacklog(projectRoot);
27282
- if (!loadResult.success) {
27283
- return { ok: false, response: fail(command, [loadResult.error.message]) };
27284
- }
27285
- const item = loadResult.data.items.find((i) => i.id === itemId) ?? loadResult.data.archive.find((i) => i.id === itemId);
28102
+ function findItemOrFail(session, itemId, command) {
28103
+ const item = session.doc.items.find((i) => i.id === itemId) ?? session.doc.archive.find((i) => i.id === itemId);
27286
28104
  if (!item) {
27287
28105
  return { ok: false, response: fail(command, [`Backlog item ${itemId} not found`]) };
27288
28106
  }
27289
28107
  return {
27290
28108
  ok: true,
27291
28109
  item,
27292
- reviewRequired: loadResult.data.global.reviewRequired
28110
+ reviewRequired: session.doc.global.reviewRequired
27293
28111
  };
27294
28112
  }
27295
28113
  async function handleStart(projectRoot, cardId) {
@@ -27297,27 +28115,28 @@ async function handleStart(projectRoot, cardId) {
27297
28115
  if (!cardId) {
27298
28116
  return fail(command, ["Missing card id. Usage: openspec start <cardId>"]);
27299
28117
  }
27300
- const loaded = await loadStateOrFail(projectRoot, command);
27301
- if (!loaded.ok)
27302
- return loaded.response;
27303
- const card = findCard(loaded.state, cardId);
28118
+ const sessionResult = await loadBacklogSession(projectRoot);
28119
+ if (!sessionResult.success)
28120
+ return fail(command, [sessionResult.error.message]);
28121
+ const session = sessionResult.data;
28122
+ const card = findCard(session.state, cardId);
27304
28123
  if (!card) {
27305
28124
  return fail(command, [`Task card ${cardId} not found`]);
27306
28125
  }
27307
28126
  if (card.status !== "pending" && card.status !== "doing") {
27308
28127
  return fail(command, [`Card ${cardId} cannot start from status ${card.status}`]);
27309
28128
  }
27310
- const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
28129
+ const itemLoaded = findItemOrFail(session, card.backlogId, command);
27311
28130
  if (!itemLoaded.ok)
27312
28131
  return itemLoaded.response;
27313
- let state = loaded.state;
28132
+ let state = session.state;
27314
28133
  if (card.status === "pending") {
27315
28134
  state = setTaskCardStatus(state, cardId, "doing");
27316
28135
  const written = await persistState(projectRoot, state);
27317
28136
  if (!written.ok)
27318
28137
  return fail(command, [written.error]);
27319
28138
  }
27320
- const transition = await transitionItem(projectRoot, itemLoaded.item, "IN_PROGRESS", itemLoaded.item.progress);
28139
+ const transition = await transitionItem(projectRoot, itemLoaded.item, "IN_PROGRESS", itemLoaded.item.progress, session.raw);
27321
28140
  if (!transition.ok)
27322
28141
  return fail(command, [transition.error]);
27323
28142
  return {
@@ -27337,10 +28156,11 @@ async function handleDone(projectRoot, cardId) {
27337
28156
  if (!cardId) {
27338
28157
  return fail(command, ["Missing card id. Usage: openspec done <cardId>"]);
27339
28158
  }
27340
- const loaded = await loadStateOrFail(projectRoot, command);
27341
- if (!loaded.ok)
27342
- return loaded.response;
27343
- const card = findCard(loaded.state, cardId);
28159
+ const sessionResult = await loadBacklogSession(projectRoot);
28160
+ if (!sessionResult.success)
28161
+ return fail(command, [sessionResult.error.message]);
28162
+ const session = sessionResult.data;
28163
+ const card = findCard(session.state, cardId);
27344
28164
  if (!card) {
27345
28165
  return fail(command, [`Task card ${cardId} not found`]);
27346
28166
  }
@@ -27353,11 +28173,10 @@ async function handleDone(projectRoot, cardId) {
27353
28173
  if (card.status !== "doing") {
27354
28174
  return fail(command, [`Card ${cardId} must be doing before done (got ${card.status})`]);
27355
28175
  }
27356
- const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
28176
+ const itemLoaded = findItemOrFail(session, card.backlogId, command);
27357
28177
  if (!itemLoaded.ok)
27358
28178
  return itemLoaded.response;
27359
- const backlog = await loadBacklog(projectRoot);
27360
- const tddRequired = backlog.success ? backlog.data.global.tddRequired : false;
28179
+ const tddRequired = session.doc.global.tddRequired;
27361
28180
  if (tddRequired && (card.phase === "test" || card.phase === "implement")) {
27362
28181
  const expectedPhase = card.phase === "test" ? "red" : "green";
27363
28182
  const evidenced = await hasTddRunnerEvidence(projectRoot, cardId, expectedPhase);
@@ -27367,7 +28186,7 @@ async function handleDone(projectRoot, cardId) {
27367
28186
  ]);
27368
28187
  }
27369
28188
  }
27370
- const state = setTaskCardStatus(loaded.state, cardId, "done");
28189
+ const state = setTaskCardStatus(session.state, cardId, "done");
27371
28190
  const cards = itemCards(state, card.backlogId);
27372
28191
  const doneCount = cards.filter((c) => c.status === "done").length;
27373
28192
  const progress = cards.length === 0 ? 0 : Math.round(doneCount / cards.length * 100);
@@ -27383,7 +28202,7 @@ async function handleDone(projectRoot, cardId) {
27383
28202
  });
27384
28203
  }
27385
28204
  const nextStatus = allDone ? "REVIEW" : "IN_PROGRESS";
27386
- const transition = await transitionItem(projectRoot, itemLoaded.item, nextStatus, progress);
28205
+ const transition = await transitionItem(projectRoot, itemLoaded.item, nextStatus, progress, session.raw);
27387
28206
  if (!transition.ok)
27388
28207
  return fail(command, [transition.error]);
27389
28208
  return {
@@ -27408,21 +28227,22 @@ async function handleBlock(projectRoot, cardId, reason) {
27408
28227
  if (!reason || !reason.trim()) {
27409
28228
  return fail(command, ["--reason is required"]);
27410
28229
  }
27411
- const loaded = await loadStateOrFail(projectRoot, command);
27412
- if (!loaded.ok)
27413
- return loaded.response;
27414
- const card = findCard(loaded.state, cardId);
28230
+ const sessionResult = await loadBacklogSession(projectRoot);
28231
+ if (!sessionResult.success)
28232
+ return fail(command, [sessionResult.error.message]);
28233
+ const session = sessionResult.data;
28234
+ const card = findCard(session.state, cardId);
27415
28235
  if (!card) {
27416
28236
  return fail(command, [`Task card ${cardId} not found`]);
27417
28237
  }
27418
- const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
28238
+ const itemLoaded = findItemOrFail(session, card.backlogId, command);
27419
28239
  if (!itemLoaded.ok)
27420
28240
  return itemLoaded.response;
27421
- const state = setTaskCardStatus(loaded.state, cardId, "blocked");
28241
+ const state = setTaskCardStatus(session.state, cardId, "blocked");
27422
28242
  const written = await persistState(projectRoot, state);
27423
28243
  if (!written.ok)
27424
28244
  return fail(command, [written.error]);
27425
- const transition = await transitionItem(projectRoot, itemLoaded.item, "BLOCKED");
28245
+ const transition = await transitionItem(projectRoot, itemLoaded.item, "BLOCKED", undefined, session.raw);
27426
28246
  if (!transition.ok)
27427
28247
  return fail(command, [transition.error]);
27428
28248
  return {
@@ -27443,17 +28263,18 @@ async function handleUnblock(projectRoot, cardId) {
27443
28263
  if (!cardId) {
27444
28264
  return fail(command, ["Missing card id. Usage: openspec unblock <cardId>"]);
27445
28265
  }
27446
- const loaded = await loadStateOrFail(projectRoot, command);
27447
- if (!loaded.ok)
27448
- return loaded.response;
27449
- const card = findCard(loaded.state, cardId);
28266
+ const sessionResult = await loadBacklogSession(projectRoot);
28267
+ if (!sessionResult.success)
28268
+ return fail(command, [sessionResult.error.message]);
28269
+ const session = sessionResult.data;
28270
+ const card = findCard(session.state, cardId);
27450
28271
  if (!card) {
27451
28272
  return fail(command, [`Task card ${cardId} not found`]);
27452
28273
  }
27453
28274
  if (card.status !== "blocked") {
27454
28275
  return fail(command, [`Card ${cardId} must be blocked before unblock (got ${card.status})`]);
27455
28276
  }
27456
- const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
28277
+ const itemLoaded = findItemOrFail(session, card.backlogId, command);
27457
28278
  if (!itemLoaded.ok)
27458
28279
  return itemLoaded.response;
27459
28280
  if (itemLoaded.item.status !== "BLOCKED") {
@@ -27461,19 +28282,18 @@ async function handleUnblock(projectRoot, cardId) {
27461
28282
  `Backlog item ${itemLoaded.item.id} must be BLOCKED before unblock (got ${itemLoaded.item.status})`
27462
28283
  ]);
27463
28284
  }
27464
- const state = setTaskCardStatus(loaded.state, cardId, "pending");
28285
+ const state = setTaskCardStatus(session.state, cardId, "pending");
27465
28286
  const written = await persistState(projectRoot, state);
27466
28287
  if (!written.ok)
27467
28288
  return fail(command, [written.error]);
27468
28289
  const changePath = state.changePaths[card.backlogId];
27469
28290
  if (changePath) {
27470
- const backlog = await loadBacklog(projectRoot);
27471
28291
  await writeTasksMarkdown(projectRoot, changePath, itemCards(state, card.backlogId), {
27472
- tddRequired: backlog.success ? backlog.data.global.tddRequired : false,
28292
+ tddRequired: session.doc.global.tddRequired,
27473
28293
  acceptanceCriteria: itemLoaded.item.acceptanceCriteria
27474
28294
  });
27475
28295
  }
27476
- const transition = await transitionItem(projectRoot, itemLoaded.item, "READY");
28296
+ const transition = await transitionItem(projectRoot, itemLoaded.item, "READY", undefined, session.raw);
27477
28297
  if (!transition.ok)
27478
28298
  return fail(command, [transition.error]);
27479
28299
  return {
@@ -27494,13 +28314,14 @@ async function handleArchive(projectRoot, itemId) {
27494
28314
  if (!itemId) {
27495
28315
  return fail(command, ["Missing item id. Usage: openspec archive <itemId>"]);
27496
28316
  }
27497
- const loaded = await loadStateOrFail(projectRoot, command);
27498
- if (!loaded.ok)
27499
- return loaded.response;
27500
- const itemLoaded = await loadItemOrFail(projectRoot, itemId, command);
28317
+ const sessionResult = await loadBacklogSession(projectRoot);
28318
+ if (!sessionResult.success)
28319
+ return fail(command, [sessionResult.error.message]);
28320
+ const session = sessionResult.data;
28321
+ const itemLoaded = findItemOrFail(session, itemId, command);
27501
28322
  if (!itemLoaded.ok)
27502
28323
  return itemLoaded.response;
27503
- const cards = itemCards(loaded.state, itemId);
28324
+ const cards = itemCards(session.state, itemId);
27504
28325
  if (cards.length === 0) {
27505
28326
  return fail(command, [`No task cards found for ${itemId}. Run openspec plan first.`]);
27506
28327
  }
@@ -27525,8 +28346,26 @@ async function handleArchive(projectRoot, itemId) {
27525
28346
  }
27526
28347
  }
27527
28348
  let archivedPath;
27528
- const changePath = loaded.state.changePaths[itemId];
28349
+ const changePath = session.state.changePaths[itemId];
28350
+ const warnings = [];
27529
28351
  if (changePath && !changePath.includes("/archive/")) {
28352
+ const progress = await readArtifactProgress(projectRoot, changePath);
28353
+ const missing = missingArtifacts(progress.artifacts);
28354
+ if (missing.length > 0) {
28355
+ return fail(command, [
28356
+ `Cannot archive ${itemId}: change folder is missing ${missing.join(", ")}. Re-run openspec plan ${itemId} or restore them.`
28357
+ ]);
28358
+ }
28359
+ const { report } = await collectAnalyzeReport(projectRoot, session, itemId, changePath);
28360
+ const critical = report.findings.filter((f) => f.severity === "CRITICAL");
28361
+ if (critical.length > 0) {
28362
+ return fail(command, [
28363
+ `Cannot archive ${itemId}: analyze reports ${critical.length} CRITICAL finding(s): ${critical.map((f) => f.code).join(", ")}. Resolve them first.`
28364
+ ]);
28365
+ }
28366
+ if (progress.checkboxes.remaining > 0) {
28367
+ warnings.push(`${progress.checkboxes.remaining} of ${progress.checkboxes.total} tasks.md checkboxes are unchecked; tick them as FRs complete (only [x]/[X] counts as done).`);
28368
+ }
27530
28369
  const synced = await syncChangeSpecs(projectRoot, changePath);
27531
28370
  if (!synced.success) {
27532
28371
  return fail(command, [`Cannot archive ${itemId}: spec sync failed: ${synced.errors.join("; ")}`]);
@@ -27539,18 +28378,19 @@ async function handleArchive(projectRoot, itemId) {
27539
28378
  ]);
27540
28379
  }
27541
28380
  }
28381
+ let content = session.raw;
27542
28382
  if (itemLoaded.item.status !== "DONE") {
27543
- const toDone = await transitionItem(projectRoot, itemLoaded.item, "DONE", 100);
28383
+ const toDone = await transitionItem(projectRoot, itemLoaded.item, "DONE", 100, content);
27544
28384
  if (!toDone.ok)
27545
28385
  return fail(command, [toDone.error]);
28386
+ content = toDone.content;
27546
28387
  }
27547
- const content = await readBacklogMarkdown(projectRoot);
27548
28388
  const archived = archiveItemInMarkdown(content, itemId);
27549
28389
  await persistBacklog(projectRoot, archived);
27550
28390
  const nextState = {
27551
- ...loaded.state,
27552
- activeItemId: loaded.state.activeItemId === itemId ? undefined : loaded.state.activeItemId,
27553
- changePaths: archivedPath ? { ...loaded.state.changePaths, [itemId]: archivedPath } : loaded.state.changePaths
28391
+ ...session.state,
28392
+ activeItemId: session.state.activeItemId === itemId ? undefined : session.state.activeItemId,
28393
+ changePaths: archivedPath ? { ...session.state.changePaths, [itemId]: archivedPath } : session.state.changePaths
27554
28394
  };
27555
28395
  const written = await persistState(projectRoot, nextState);
27556
28396
  if (!written.ok)
@@ -27561,20 +28401,21 @@ async function handleArchive(projectRoot, itemId) {
27561
28401
  success: true,
27562
28402
  command,
27563
28403
  itemId,
27564
- archivedPath: archivedPath ?? changePath
28404
+ archivedPath: archivedPath ?? changePath,
28405
+ warnings
27565
28406
  }
27566
28407
  };
27567
28408
  }
27568
- var KNOWN_SUBCOMMANDS = "validate, scan, plan, analyze, status, next, start, done, block, unblock, archive";
28409
+ var KNOWN_SUBCOMMANDS = "validate, scan, plan, analyze, status, next, start, done, block, unblock, archive, sync, verify";
27569
28410
  var init_openspec_command = __esm(() => {
27570
- init_backlog_parser();
27571
- init_backlog_validator();
28411
+ init_backlog_session();
27572
28412
  init_backlog_scanner();
27573
28413
  init_backlog_planner();
27574
28414
  init_openspec_generator();
27575
28415
  init_spec_quality();
27576
28416
  init_spec_analyzer();
27577
28417
  init_spec_sync();
28418
+ init_artifact_progress();
27578
28419
  init_schemas();
27579
28420
  init_verification_runner();
27580
28421
  init_outcome_store();
@@ -27595,26 +28436,61 @@ function resumeDecision(changedPaths) {
27595
28436
  }
27596
28437
  var init_context_assembly = () => {};
27597
28438
 
28439
+ // src/core/ccep/handoff-envelope.ts
28440
+ function buildHandoffEnvelope(ledger, changedPaths) {
28441
+ return HandoffEnvelopeSchema.parse({
28442
+ version: 1,
28443
+ task: {
28444
+ id: ledger.taskCard.id,
28445
+ objective: ledger.taskCard.objective,
28446
+ acceptanceCriteria: ledger.taskCard.acceptanceCriteria,
28447
+ status: ledger.taskCard.status
28448
+ },
28449
+ decisions: {
28450
+ constraints: ledger.taskCard.constraints,
28451
+ unresolved: ledger.tasks.filter((task) => task.status === "blocked").map((task) => task.title)
28452
+ },
28453
+ scope: {
28454
+ relevantFiles: ledger.taskCard.targetFiles,
28455
+ boundaries: ledger.taskCard.boundaries
28456
+ },
28457
+ verification: { evidence: ledger.evidence },
28458
+ change: { touchedFiles: [...new Set(changedPaths)].sort() },
28459
+ next: {
28460
+ role: ledger.taskCard.agentType,
28461
+ objective: ledger.nextStep
28462
+ },
28463
+ sources: {
28464
+ deliveryLedger: ledger.id,
28465
+ memoryTopicKey: ledger.memoryTopicKey,
28466
+ technicalPlanPath: ledger.technicalPlanPath
28467
+ }
28468
+ });
28469
+ }
28470
+ var init_handoff_envelope = __esm(() => {
28471
+ init_schemas();
28472
+ });
28473
+
27598
28474
  // src/core/delivery/delivery-ledger.ts
27599
28475
  import { createHash as createHash7 } from "node:crypto";
27600
- import { mkdir as mkdir24, readFile as readFile38, writeFile as writeFile23 } from "node:fs/promises";
27601
- import { resolve as resolve37, sep as sep8 } from "node:path";
28476
+ import { mkdir as mkdir23, readFile as readFile41, writeFile as writeFile22 } from "node:fs/promises";
28477
+ import { resolve as resolve38, sep as sep9 } from "node:path";
27602
28478
  function ledgerPath(projectRoot, id) {
27603
28479
  if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(id))
27604
28480
  throw new Error("Invalid delivery ledger id");
27605
- return resolve37(projectRoot, DELIVERY_DIR, `${id}.md`);
28481
+ return resolve38(projectRoot, DELIVERY_DIR, `${id}.md`);
27606
28482
  }
27607
28483
  async function fingerprintWorkspace(projectRoot, paths) {
27608
- const root = resolve37(projectRoot);
28484
+ const root = resolve38(projectRoot);
27609
28485
  const fingerprints = {};
27610
28486
  for (const path of paths) {
27611
- const candidate = resolve37(root, path);
27612
- if (candidate !== root && !candidate.startsWith(`${root}${sep8}`)) {
28487
+ const candidate = resolve38(root, path);
28488
+ if (candidate !== root && !candidate.startsWith(`${root}${sep9}`)) {
27613
28489
  fingerprints[path] = "outside-root";
27614
28490
  continue;
27615
28491
  }
27616
28492
  try {
27617
- fingerprints[path] = createHash7("sha256").update(await readFile38(candidate)).digest("hex");
28493
+ fingerprints[path] = createHash7("sha256").update(await readFile41(candidate)).digest("hex");
27618
28494
  } catch {
27619
28495
  fingerprints[path] = "missing";
27620
28496
  }
@@ -27637,7 +28513,7 @@ function parseLedger(content) {
27637
28513
  return DeliveryLedgerSchema.parse($parse(match[1]));
27638
28514
  }
27639
28515
  async function loadDeliveryLedger(projectRoot, id) {
27640
- return parseLedger(await readFile38(ledgerPath(projectRoot, id), "utf-8"));
28516
+ return parseLedger(await readFile41(ledgerPath(projectRoot, id), "utf-8"));
27641
28517
  }
27642
28518
  async function createDeliveryLedger(projectRoot, input) {
27643
28519
  const request = DeliveryLedgerRequestSchema.parse(input);
@@ -27658,12 +28534,13 @@ async function createDeliveryLedger(projectRoot, input) {
27658
28534
  nextStep: request.nextStep,
27659
28535
  memoryTopicKey: `delivery:${request.id}`,
27660
28536
  workspace: await fingerprintWorkspace(projectRoot, request.taskCard.targetFiles),
28537
+ technicalPlanPath: request.technicalPlanPath,
27661
28538
  createdAt: now,
27662
28539
  updatedAt: now
27663
28540
  });
27664
- await mkdir24(resolve37(projectRoot, DELIVERY_DIR), { recursive: true });
28541
+ await mkdir23(resolve38(projectRoot, DELIVERY_DIR), { recursive: true });
27665
28542
  try {
27666
- await writeFile23(path, render(ledger), { encoding: "utf-8", flag: "wx" });
28543
+ await writeFile22(path, render(ledger), { encoding: "utf-8", flag: "wx" });
27667
28544
  return { created: true, ledger };
27668
28545
  } catch (error) {
27669
28546
  if (error instanceof Error && error.message.includes("EEXIST")) {
@@ -27689,6 +28566,28 @@ var exports_odd_command = {};
27689
28566
  __export(exports_odd_command, {
27690
28567
  oddCommand: () => oddCommand
27691
28568
  });
28569
+ async function reconcileForResume(projectRoot, id) {
28570
+ const result = await reconcileDeliveryLedger(projectRoot, id);
28571
+ const memory = await loadMemoryIndex(projectRoot);
28572
+ const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
28573
+ const rdd = await rddCommand2({
28574
+ subcommand: "status",
28575
+ projectRoot,
28576
+ taskId: result.ledger.taskCard.id
28577
+ });
28578
+ const receipts = rdd.data.receipts ?? [];
28579
+ const staleReceipt = receipts.some((receipt) => receipt.valid === false);
28580
+ const changedPaths = staleReceipt ? [...result.changedPaths, "RDD receipt"].sort() : result.changedPaths;
28581
+ return {
28582
+ ...result,
28583
+ changedPaths,
28584
+ resume: resumeDecision(changedPaths),
28585
+ status: changedPaths.length === 0 ? "ready" : "conflict",
28586
+ rdd,
28587
+ memory: memory.success ? "available" : "unavailable",
28588
+ memoryPointers: memory.success ? memory.data.pointers.filter((pointer) => pointer.topic_key === result.ledger.memoryTopicKey) : []
28589
+ };
28590
+ }
27692
28591
  async function oddCommand(options) {
27693
28592
  if (options.subcommand === "create") {
27694
28593
  let input = options.input;
@@ -27722,28 +28621,25 @@ async function oddCommand(options) {
27722
28621
  return { code: 0, data: { success: true, ledger: await loadDeliveryLedger(options.projectRoot, options.id) } };
27723
28622
  }
27724
28623
  if (options.subcommand === "reconcile") {
27725
- const result = await reconcileDeliveryLedger(options.projectRoot, options.id);
27726
- const memory = await loadMemoryIndex(options.projectRoot);
27727
- const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
27728
- const rdd = await rddCommand2({
27729
- subcommand: "status",
27730
- projectRoot: options.projectRoot,
27731
- taskId: result.ledger.taskCard.id
27732
- });
27733
- const receipts = rdd.data.receipts ?? [];
27734
- const staleReceipt = receipts.some((receipt) => receipt.valid === false);
27735
- const changedPaths = staleReceipt ? [...result.changedPaths, "RDD receipt"].sort() : result.changedPaths;
28624
+ const result = await reconcileForResume(options.projectRoot, options.id);
28625
+ return {
28626
+ code: 0,
28627
+ data: {
28628
+ success: true,
28629
+ ...result
28630
+ }
28631
+ };
28632
+ }
28633
+ if (options.subcommand === "handoff") {
28634
+ const result = await reconcileForResume(options.projectRoot, options.id);
27736
28635
  return {
27737
28636
  code: 0,
27738
28637
  data: {
27739
28638
  success: true,
27740
- status: changedPaths.length === 0 ? "ready" : "conflict",
27741
- resume: resumeDecision(changedPaths),
27742
- rdd,
27743
- memory: memory.success ? "available" : "unavailable",
27744
- memoryPointers: memory.success ? memory.data.pointers.filter((pointer) => pointer.topic_key === result.ledger.memoryTopicKey) : [],
27745
- ...result,
27746
- changedPaths
28639
+ status: result.status,
28640
+ changedPaths: result.changedPaths,
28641
+ resume: result.resume,
28642
+ handoff: buildHandoffEnvelope(result.ledger, result.changedPaths)
27747
28643
  }
27748
28644
  };
27749
28645
  }
@@ -27755,6 +28651,7 @@ async function oddCommand(options) {
27755
28651
  var init_odd_command = __esm(() => {
27756
28652
  init_memory_index();
27757
28653
  init_context_assembly();
28654
+ init_handoff_envelope();
27758
28655
  init_task_card_validator();
27759
28656
  init_workflow_profile_loader();
27760
28657
  init_delivery_ledger();
@@ -27886,6 +28783,9 @@ function formatHookOutput(verdict, format) {
27886
28783
  function claudeExitCode(verdict) {
27887
28784
  return verdict.action === "deny" ? 2 : 0;
27888
28785
  }
28786
+ function parseMusePayload(stdinText) {
28787
+ return parseClaudePayload(stdinText);
28788
+ }
27889
28789
  function parseAgyPayload(raw) {
27890
28790
  if (!raw.trim())
27891
28791
  return {};
@@ -27959,7 +28859,7 @@ function isDestructiveRestore(command) {
27959
28859
  }
27960
28860
  var SENSITIVE_PATH, READ_LIKE, MSG_SECRET = "Bloqueado: intento de leer archivos sensibles", MSG_AUTHORITY = "Bloqueado: El agente no tiene autoridad sobre este comando.", MSG_POLICY = "Bloqueado: Command violated security policy";
27961
28861
  var init_hook_runner = __esm(() => {
27962
- SENSITIVE_PATH = /(?:\.env\b|secrets[/\\]|id_rsa|\.pem\b|\.key\b|(?:^|[/\\])\.ssh(?:[/\\]|$)|(?:^|[/\\])\.aws(?:[/\\]|$)|(?:^|[/\\])\.kube(?:[/\\]|$))/i;
28862
+ SENSITIVE_PATH = /(?:\.env(?!\.example(?:$|[/\\]))\b|secrets[/\\]|id_rsa|\.pem\b|\.key\b|(?:^|[/\\])\.ssh(?:[/\\]|$)|(?:^|[/\\])\.aws(?:[/\\]|$)|(?:^|[/\\])\.kube(?:[/\\]|$))/i;
27963
28863
  READ_LIKE = /(?:^|[\s;&|])(cat|less|more|head|tail|type|Get-Content|cp|copy|mv|move|scp)\b/i;
27964
28864
  });
27965
28865
 
@@ -27967,12 +28867,16 @@ var init_hook_runner = __esm(() => {
27967
28867
  var exports_hook_command = {};
27968
28868
  __export(exports_hook_command, {
27969
28869
  hookCommand: () => hookCommand,
28870
+ parseHookFormat: () => parseHookFormat,
27970
28871
  readStdinText: () => readStdinText
27971
28872
  });
27972
28873
  import { spawnSync } from "node:child_process";
27973
28874
  import { existsSync as existsSync8, readFileSync as readFileSync5 } from "node:fs";
27974
28875
  import { createRequire as createRequire2 } from "node:module";
27975
- import { dirname as dirname13, resolve as resolve38 } from "node:path";
28876
+ import { dirname as dirname12, resolve as resolve39 } from "node:path";
28877
+ function parseHookFormat(raw) {
28878
+ return raw === "agy" || raw === "claude" || raw === "muse" ? raw : undefined;
28879
+ }
27976
28880
  function detectFormat(explicit, stdinText) {
27977
28881
  if (explicit)
27978
28882
  return explicit;
@@ -27993,7 +28897,7 @@ async function readStdinText() {
27993
28897
  return Buffer.concat(chunks).toString("utf8");
27994
28898
  }
27995
28899
  function resolvePreToolInput(options, stdinText, format) {
27996
- const fromStdin = format === "agy" ? parseAgyPayload(stdinText) : parseClaudePayload(stdinText);
28900
+ const fromStdin = format === "agy" ? parseAgyPayload(stdinText) : format === "muse" ? parseMusePayload(stdinText) : parseClaudePayload(stdinText);
27997
28901
  return {
27998
28902
  command: options.command ?? (process.env.CLAUDE_TOOL_INPUT_COMMAND || undefined) ?? fromStdin.command,
27999
28903
  filePath: options.filePath ?? (process.env.CLAUDE_TOOL_INPUT_FILE_PATH || undefined) ?? fromStdin.filePath,
@@ -28001,8 +28905,8 @@ function resolvePreToolInput(options, stdinText, format) {
28001
28905
  };
28002
28906
  }
28003
28907
  async function sessionLines(projectRoot) {
28004
- const backlogPath = resolve38(projectRoot, "BACKLOG.md");
28005
- const profilePath = resolve38(projectRoot, ".codeconductor", "evaluation", "execution-profile.yml");
28908
+ const backlogPath = resolve39(projectRoot, "BACKLOG.md");
28909
+ const profilePath = resolve39(projectRoot, ".codeconductor", "evaluation", "execution-profile.yml");
28006
28910
  return [
28007
28911
  "[CodeConductor] Session start",
28008
28912
  "Skills: openspec, backlog, evaluation, testing-tdd — invoke via /cc-* then CLI.",
@@ -28057,21 +28961,21 @@ async function hookCommand(options) {
28057
28961
  function formatWrittenFile(filePath, projectRoot) {
28058
28962
  if (!filePath)
28059
28963
  return;
28060
- filePath = resolve38(projectRoot, filePath);
28964
+ filePath = resolve39(projectRoot, filePath);
28061
28965
  if (!existsSync8(filePath))
28062
28966
  return;
28063
28967
  const prettier = /\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|md|mdx|css|scss|html|astro|ya?ml)$/i;
28064
28968
  const eslint = /\.(ts|tsx|js|jsx|mjs|cjs)$/i;
28065
28969
  const python = /\.py$/i;
28066
28970
  const opts = { cwd: projectRoot, stdio: "ignore", windowsHide: true, timeout: 5000 };
28067
- const require2 = createRequire2(resolve38(projectRoot, "package.json"));
28971
+ const require2 = createRequire2(resolve39(projectRoot, "package.json"));
28068
28972
  function runNodeTool(name, args) {
28069
28973
  try {
28070
28974
  const manifestPath = require2.resolve(`${name}/package.json`);
28071
28975
  const manifest = JSON.parse(readFileSync5(manifestPath, "utf8"));
28072
28976
  const bin = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.[name];
28073
28977
  if (typeof bin === "string")
28074
- spawnSync(process.execPath, [resolve38(dirname13(manifestPath), bin), ...args], opts);
28978
+ spawnSync(process.execPath, [resolve39(dirname12(manifestPath), bin), ...args], opts);
28075
28979
  } catch {}
28076
28980
  }
28077
28981
  if (prettier.test(filePath)) {
@@ -28610,24 +29514,24 @@ var init_scorecard_signals = __esm(() => {
28610
29514
 
28611
29515
  // src/core/evaluation/harness-experiment.ts
28612
29516
  import { existsSync as existsSync9 } from "node:fs";
28613
- import { cp, mkdir as mkdir25, readFile as readFile39, readdir as readdir14, writeFile as writeFile24 } from "node:fs/promises";
28614
- import { dirname as dirname14, join as join26, resolve as resolve39 } from "node:path";
29517
+ import { cp, mkdir as mkdir24, readFile as readFile42, readdir as readdir13, writeFile as writeFile23 } from "node:fs/promises";
29518
+ import { dirname as dirname13, join as join29, resolve as resolve40 } from "node:path";
28615
29519
  function experimentDir(projectRoot, experimentId) {
28616
- return resolve39(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR, experimentId);
29520
+ return resolve40(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR, experimentId);
28617
29521
  }
28618
29522
  function experimentYamlPath(projectRoot, experimentId) {
28619
- return join26(experimentDir(projectRoot, experimentId), "experiment.yml");
29523
+ return join29(experimentDir(projectRoot, experimentId), "experiment.yml");
28620
29524
  }
28621
29525
  async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
28622
29526
  const candidates = suitePath ? [suitePath] : [
28623
- resolve39(projectRoot, "eval", "suites", suiteId, "suite.yml"),
28624
- resolve39(projectRoot, EVAL_DIR2, "suites", suiteId, "suite.yml")
29527
+ resolve40(projectRoot, "eval", "suites", suiteId, "suite.yml"),
29528
+ resolve40(projectRoot, EVAL_DIR2, "suites", suiteId, "suite.yml")
28625
29529
  ];
28626
29530
  for (const path of candidates) {
28627
29531
  if (!existsSync9(path))
28628
29532
  continue;
28629
29533
  try {
28630
- const suite = HarnessSuiteSchema.parse($parse(await readFile39(path, "utf-8")));
29534
+ const suite = HarnessSuiteSchema.parse($parse(await readFile42(path, "utf-8")));
28631
29535
  return ok({ ...suite, path });
28632
29536
  } catch (e) {
28633
29537
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -28637,7 +29541,7 @@ async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
28637
29541
  }
28638
29542
  async function loadExperiment(projectRoot, experimentId) {
28639
29543
  try {
28640
- const raw = await readFile39(experimentYamlPath(projectRoot, experimentId), "utf-8");
29544
+ const raw = await readFile42(experimentYamlPath(projectRoot, experimentId), "utf-8");
28641
29545
  return ok(HarnessExperimentSchema.parse($parse(raw)));
28642
29546
  } catch (e) {
28643
29547
  if (e.code === "ENOENT") {
@@ -28647,10 +29551,10 @@ async function loadExperiment(projectRoot, experimentId) {
28647
29551
  }
28648
29552
  }
28649
29553
  async function listExperiments(projectRoot) {
28650
- const root = resolve39(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR);
29554
+ const root = resolve40(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR);
28651
29555
  if (!existsSync9(root))
28652
29556
  return [];
28653
- const ids = await readdir14(root);
29557
+ const ids = await readdir13(root);
28654
29558
  const experiments = [];
28655
29559
  for (const id of ids) {
28656
29560
  const loaded = await loadExperiment(projectRoot, id);
@@ -28726,8 +29630,8 @@ function generateExperimentId(suiteId, now = new Date) {
28726
29630
  return `abl-${date}-${suiteId}-${rand}`;
28727
29631
  }
28728
29632
  async function copyFixture(suitePath, fixtureDir, dest) {
28729
- const source = fixtureDir ? resolve39(dirname14(suitePath), fixtureDir) : join26(dirname14(suitePath), "fixture");
28730
- await mkdir25(dest, { recursive: true });
29633
+ const source = fixtureDir ? resolve40(dirname13(suitePath), fixtureDir) : join29(dirname13(suitePath), "fixture");
29634
+ await mkdir24(dest, { recursive: true });
28731
29635
  if (existsSync9(source)) {
28732
29636
  await cp(source, dest, { recursive: true });
28733
29637
  }
@@ -28749,12 +29653,12 @@ async function startHarnessExperiment(projectRoot, options) {
28749
29653
  suitePath: suite.data.path
28750
29654
  };
28751
29655
  const root = experimentDir(projectRoot, experimentId);
28752
- await mkdir25(root, { recursive: true });
28753
- await writeFile24(experimentYamlPath(projectRoot, experimentId), $stringify(experiment), "utf-8");
28754
- await writeFile24(join26(root, "EXPERIMENT.md"), formatExperimentMarkdown(experiment), "utf-8");
29656
+ await mkdir24(root, { recursive: true });
29657
+ await writeFile23(experimentYamlPath(projectRoot, experimentId), $stringify(experiment), "utf-8");
29658
+ await writeFile23(join29(root, "EXPERIMENT.md"), formatExperimentMarkdown(experiment), "utf-8");
28755
29659
  for (const task of suite.data.tasks) {
28756
29660
  for (const variantId of variants) {
28757
- const runRoot = join26(root, "runs", task.id, variantDirName(variantId));
29661
+ const runRoot = join29(root, "runs", task.id, variantDirName(variantId));
28758
29662
  await copyFixture(suite.data.path, suite.data.fixtureDir, runRoot);
28759
29663
  await writeConfig(runRoot, structuredClone(DEFAULT_CONFIG), true);
28760
29664
  const disabled = variantId === "baseline" ? [] : options.components.filter((id) => variantIdFor(id) === variantId);
@@ -28766,7 +29670,7 @@ async function startHarnessExperiment(projectRoot, options) {
28766
29670
  });
28767
29671
  if (!overlay.success)
28768
29672
  return overlay;
28769
- await writeFile24(join26(runRoot, "TASK.md"), formatTaskCard(task, experimentId, variantId), "utf-8");
29673
+ await writeFile23(join29(runRoot, "TASK.md"), formatTaskCard(task, experimentId, variantId), "utf-8");
28770
29674
  }
28771
29675
  }
28772
29676
  return ok(experiment);
@@ -28801,7 +29705,7 @@ var init_harness_experiment = __esm(() => {
28801
29705
  // src/core/evaluation/suite-run.ts
28802
29706
  import { spawnSync as spawnSync2 } from "node:child_process";
28803
29707
  import { existsSync as existsSync10 } from "node:fs";
28804
- import { resolve as resolve40 } from "node:path";
29708
+ import { resolve as resolve41 } from "node:path";
28805
29709
  async function runHarnessSuiteTasks(projectRoot, suiteId, suitePath) {
28806
29710
  const suite = await loadHarnessSuite(projectRoot, suiteId, suitePath);
28807
29711
  if (!suite.success) {
@@ -28861,7 +29765,7 @@ function runTaskCommand(projectRoot, task) {
28861
29765
  if (parts.length === 0) {
28862
29766
  return { ok: false, detail: "Empty testCommand" };
28863
29767
  }
28864
- const bin = parts[0] === "bun" && existsSync10(resolve40(projectRoot, "src/cli/main.ts")) ? "bun" : parts[0];
29768
+ const bin = parts[0] === "bun" && existsSync10(resolve41(projectRoot, "src/cli/main.ts")) ? "bun" : parts[0];
28865
29769
  const args = parts[0] === "bun" ? parts.slice(1) : parts.slice(1);
28866
29770
  const result = spawnSync2(bin, args, {
28867
29771
  cwd: projectRoot,
@@ -28912,11 +29816,11 @@ var init_suite_run = __esm(() => {
28912
29816
  });
28913
29817
 
28914
29818
  // src/core/evaluation/execution-profile.ts
28915
- import { readFile as readFile40 } from "node:fs/promises";
28916
- import { resolve as resolve41 } from "node:path";
29819
+ import { readFile as readFile43 } from "node:fs/promises";
29820
+ import { resolve as resolve42 } from "node:path";
28917
29821
  async function loadExecutionProfile(projectRoot, defaultTarget = "opencode") {
28918
29822
  try {
28919
- const content = await readFile40(resolve41(projectRoot, PROFILE_FILE), "utf-8");
29823
+ const content = await readFile43(resolve42(projectRoot, PROFILE_FILE), "utf-8");
28920
29824
  const data = $parse(content);
28921
29825
  return ExecutionProfileSchema.parse(data);
28922
29826
  } catch {
@@ -28996,14 +29900,14 @@ var init_execution_profile = __esm(() => {
28996
29900
  });
28997
29901
 
28998
29902
  // src/core/evaluation/prompt-diff.ts
28999
- import { readFile as readFile41, readdir as readdir15 } from "node:fs/promises";
29000
- import { join as join27, resolve as resolve42 } from "node:path";
29903
+ import { readFile as readFile44, readdir as readdir14 } from "node:fs/promises";
29904
+ import { join as join30, resolve as resolve43 } from "node:path";
29001
29905
  function normalizeVersion(v) {
29002
29906
  return v.startsWith("v") ? v : `v${v}`;
29003
29907
  }
29004
29908
  async function listPromptFiles(dir) {
29005
29909
  try {
29006
- const entries = await readdir15(dir, { withFileTypes: true });
29910
+ const entries = await readdir14(dir, { withFileTypes: true });
29007
29911
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name);
29008
29912
  } catch {
29009
29913
  return [];
@@ -29046,30 +29950,30 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
29046
29950
  const fromV = normalizeVersion(fromVersion);
29047
29951
  const toV = normalizeVersion(toVersion);
29048
29952
  const searchRoots = [
29049
- resolve42(options.projectRoot ?? PACKAGE_ROOT, ".agents", "prompts"),
29050
- join27(ROOT_PRESETS_DIR, target, "prompts"),
29051
- join27(ROOT_PRESETS_DIR, "opencode", "prompts")
29953
+ resolve43(options.projectRoot ?? PACKAGE_ROOT, ".agents", "prompts"),
29954
+ join30(ROOT_PRESETS_DIR, target, "prompts"),
29955
+ join30(ROOT_PRESETS_DIR, "opencode", "prompts")
29052
29956
  ];
29053
29957
  let fromDir = "";
29054
29958
  let toDir = "";
29055
29959
  for (const root of searchRoots) {
29056
- const f = join27(root, fromV);
29057
- const t = join27(root, toV);
29960
+ const f = join30(root, fromV);
29961
+ const t = join30(root, toV);
29058
29962
  try {
29059
- await readFile41(join27(f, "orchestrator.md"), "utf-8");
29963
+ await readFile44(join30(f, "orchestrator.md"), "utf-8");
29060
29964
  fromDir = f;
29061
29965
  toDir = t;
29062
29966
  break;
29063
29967
  } catch {}
29064
29968
  }
29065
29969
  if (!fromDir) {
29066
- fromDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", fromV);
29067
- toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
29970
+ fromDir = join30(ROOT_PRESETS_DIR, "opencode", "prompts", fromV);
29971
+ toDir = join30(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
29068
29972
  } else {
29069
29973
  try {
29070
- await readFile41(join27(toDir, "orchestrator.md"), "utf-8");
29974
+ await readFile44(join30(toDir, "orchestrator.md"), "utf-8");
29071
29975
  } catch {
29072
- toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
29976
+ toDir = join30(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
29073
29977
  }
29074
29978
  }
29075
29979
  const fromFiles = await listPromptFiles(fromDir);
@@ -29087,12 +29991,12 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
29087
29991
  let oldText = "";
29088
29992
  let newText = "";
29089
29993
  try {
29090
- oldText = await readFile41(join27(fromDir, name), "utf-8");
29994
+ oldText = await readFile44(join30(fromDir, name), "utf-8");
29091
29995
  } catch {
29092
29996
  oldText = "";
29093
29997
  }
29094
29998
  try {
29095
- newText = await readFile41(join27(toDir, name), "utf-8");
29999
+ newText = await readFile44(join30(toDir, name), "utf-8");
29096
30000
  } catch {
29097
30001
  newText = "";
29098
30002
  }
@@ -29132,12 +30036,12 @@ var init_prompt_diff = __esm(() => {
29132
30036
 
29133
30037
  // src/core/evaluation/regression-checklist.ts
29134
30038
  import { execFileSync as execFileSync3 } from "node:child_process";
29135
- import { mkdir as mkdir26, readFile as readFile42, writeFile as writeFile25 } from "node:fs/promises";
29136
- import { dirname as dirname15, resolve as resolve43 } from "node:path";
30039
+ import { mkdir as mkdir25, readFile as readFile45, writeFile as writeFile24 } from "node:fs/promises";
30040
+ import { dirname as dirname14, resolve as resolve44 } from "node:path";
29137
30041
  async function loadRegressionChecklist() {
29138
30042
  try {
29139
- const path = resolve43(ROOT_PRESETS_DIR, "templates", "regression-checklist.yml");
29140
- const content = await readFile42(path, "utf-8");
30043
+ const path = resolve44(ROOT_PRESETS_DIR, "templates", "regression-checklist.yml");
30044
+ const content = await readFile45(path, "utf-8");
29141
30045
  const data = $parse(content);
29142
30046
  return data.checks ?? [];
29143
30047
  } catch {
@@ -29192,9 +30096,9 @@ async function runRegressionChecklist(projectRoot, options = {}) {
29192
30096
  checks,
29193
30097
  timestamp: new Date().toISOString()
29194
30098
  };
29195
- const outPath = resolve43(projectRoot, ".codeconductor/evaluation/regression-last.json");
29196
- await mkdir26(dirname15(outPath), { recursive: true });
29197
- await writeFile25(outPath, JSON.stringify(report, null, 2), "utf-8");
30099
+ const outPath = resolve44(projectRoot, ".codeconductor/evaluation/regression-last.json");
30100
+ await mkdir25(dirname14(outPath), { recursive: true });
30101
+ await writeFile24(outPath, JSON.stringify(report, null, 2), "utf-8");
29198
30102
  return ok(report);
29199
30103
  } catch (e) {
29200
30104
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -29236,8 +30140,8 @@ var init_model_comparison = __esm(() => {
29236
30140
  });
29237
30141
 
29238
30142
  // src/core/evaluation/cost-quality-matrix.ts
29239
- import { writeFile as writeFile26 } from "node:fs/promises";
29240
- import { resolve as resolve44 } from "node:path";
30143
+ import { writeFile as writeFile25 } from "node:fs/promises";
30144
+ import { resolve as resolve45 } from "node:path";
29241
30145
  async function buildCostQualityMatrix(projectRoot, outcomes) {
29242
30146
  const agg = aggregateOutcomes(outcomes);
29243
30147
  const rows = Object.entries(agg.byModel).map(([model, entry]) => {
@@ -29257,7 +30161,7 @@ async function buildCostQualityMatrix(projectRoot, outcomes) {
29257
30161
  rows
29258
30162
  };
29259
30163
  const dir = await ensureEvaluationDir(projectRoot);
29260
- await writeFile26(resolve44(dir, "matrix.yml"), $stringify(matrix), "utf-8");
30164
+ await writeFile25(resolve45(dir, "matrix.yml"), $stringify(matrix), "utf-8");
29261
30165
  return matrix;
29262
30166
  }
29263
30167
  function formatMatrixMarkdown(matrix) {
@@ -29281,8 +30185,8 @@ var init_cost_quality_matrix = __esm(() => {
29281
30185
  });
29282
30186
 
29283
30187
  // src/core/evaluation/ablation-report.ts
29284
- import { mkdir as mkdir27, writeFile as writeFile27 } from "node:fs/promises";
29285
- import { resolve as resolve45 } from "node:path";
30188
+ import { mkdir as mkdir26, writeFile as writeFile26 } from "node:fs/promises";
30189
+ import { resolve as resolve46 } from "node:path";
29286
30190
  function isPass(o) {
29287
30191
  return o.verdict === "PASS" || o.status === "pass";
29288
30192
  }
@@ -29450,10 +30354,10 @@ function formatAblationMarkdown(report) {
29450
30354
  `);
29451
30355
  }
29452
30356
  async function persistAblationReport(projectRoot, markdown) {
29453
- const dir = resolve45(projectRoot, EVAL_DIR2);
29454
- await mkdir27(dir, { recursive: true });
29455
- const path = resolve45(dir, "ablation-last.md");
29456
- await writeFile27(path, markdown, "utf-8");
30357
+ const dir = resolve46(projectRoot, EVAL_DIR2);
30358
+ await mkdir26(dir, { recursive: true });
30359
+ const path = resolve46(dir, "ablation-last.md");
30360
+ await writeFile26(path, markdown, "utf-8");
29457
30361
  return path;
29458
30362
  }
29459
30363
  var SCORE_THRESHOLD = 0.1, PASS_RATE_THRESHOLD = 0.05;
@@ -29467,8 +30371,8 @@ var exports_scorecard_command = {};
29467
30371
  __export(exports_scorecard_command, {
29468
30372
  scorecardCommand: () => scorecardCommand
29469
30373
  });
29470
- import { readFile as readFile43 } from "node:fs/promises";
29471
- import { resolve as resolve46 } from "node:path";
30374
+ import { readFile as readFile46 } from "node:fs/promises";
30375
+ import { resolve as resolve47 } from "node:path";
29472
30376
  function contractVersion() {
29473
30377
  return package_default.version;
29474
30378
  }
@@ -29756,13 +30660,13 @@ async function handleCompareModels(projectRoot, options) {
29756
30660
  outcomes = outcomes.filter((o) => options.modelsFilter.includes(o.model));
29757
30661
  }
29758
30662
  const markdown = generateModelComparisonMarkdown(outcomes, options.modelsFilter);
29759
- const templatePath = resolve46(projectRoot, ".codeconductor/evaluation/model-comparison-last.md");
30663
+ const templatePath = resolve47(projectRoot, ".codeconductor/evaluation/model-comparison-last.md");
29760
30664
  try {
29761
- await readFile43(templatePath);
30665
+ await readFile46(templatePath);
29762
30666
  } catch {}
29763
- const { writeFile: writeFile28, mkdir: mkdir28 } = await import("node:fs/promises");
29764
- await mkdir28(resolve46(projectRoot, ".codeconductor/evaluation"), { recursive: true });
29765
- await writeFile28(templatePath, markdown, "utf-8");
30667
+ const { writeFile: writeFile27, mkdir: mkdir27 } = await import("node:fs/promises");
30668
+ await mkdir27(resolve47(projectRoot, ".codeconductor/evaluation"), { recursive: true });
30669
+ await writeFile27(templatePath, markdown, "utf-8");
29766
30670
  return {
29767
30671
  code: 0,
29768
30672
  data: {
@@ -31231,7 +32135,7 @@ var exports_seo_audit_command = {};
31231
32135
  __export(exports_seo_audit_command, {
31232
32136
  seoAuditCommand: () => seoAuditCommand
31233
32137
  });
31234
- import { join as join28 } from "node:path";
32138
+ import { join as join31 } from "node:path";
31235
32139
  async function seoAuditCommand(options) {
31236
32140
  const { url, sitemap, format, failOn, delay: delay2, output, followRedirects, force } = options;
31237
32141
  if (!url && !sitemap) {
@@ -31244,7 +32148,7 @@ async function seoAuditCommand(options) {
31244
32148
  }
31245
32149
  };
31246
32150
  }
31247
- const defaultMarkdownPath = output === undefined && format === "markdown" ? join28("seo-reports", `audit-report-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19)}.md`) : undefined;
32151
+ const defaultMarkdownPath = output === undefined && format === "markdown" ? join31("seo-reports", `audit-report-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19)}.md`) : undefined;
31248
32152
  const outputPathToWrite = output ?? defaultMarkdownPath;
31249
32153
  if (outputPathToWrite !== undefined) {
31250
32154
  const preflightError = await preflightContainedOutput(options.projectRoot, outputPathToWrite, { force });
@@ -31606,7 +32510,7 @@ var COMMANDS = [
31606
32510
  { name: "detect", group: "getting-started", summary: "Inspect the project stack and recommended presets.", usage: ["cc-codeconductor detect"] },
31607
32511
  { name: "init", group: "getting-started", summary: "Initialize low-level CodeConductor configuration.", usage: ["cc-codeconductor init [--locale en|es]"] },
31608
32512
  { name: "usage", group: "getting-started", summary: "Show installation examples for presets, council, and LSP.", usage: ["cc-codeconductor usage"] },
31609
- { name: "install", group: "getting-started", summary: "Install harness components.", usage: ["cc-codeconductor install preset --target <target>"], options: ["--target opencode|claude|codex|gemini|cursor|agy|pi|all"], subcommands: [
32513
+ { name: "install", group: "getting-started", summary: "Install harness components.", usage: ["cc-codeconductor install preset --target <target>"], options: ["--target opencode|claude|codex|gemini|cursor|agy|pi|muse|all"], subcommands: [
31610
32514
  { name: "preset", group: "getting-started", summary: "Install agents, prompts, skills, and commands.", usage: ["cc-codeconductor install preset --target <target> [--locale en|es]"] },
31611
32515
  { name: "council", group: "getting-started", summary: "Install generated council files.", usage: ["cc-codeconductor install council --target <target>"] },
31612
32516
  { name: "lsp", group: "getting-started", summary: "Install supported language servers.", usage: ["cc-codeconductor install lsp --target <target> [--lang typescript,python]"] }
@@ -31625,6 +32529,7 @@ var COMMANDS = [
31625
32529
  { name: "ingest", group: "workflow", summary: "Ingest repository knowledge into the product graph.", usage: ["cc-codeconductor ingest"] },
31626
32530
  { name: "product", group: "workflow", summary: "Explore the product graph and memory.", usage: ["cc-codeconductor product <subcommand>"] },
31627
32531
  { name: "ccep", group: "workflow", summary: "Run CCEP contract workflows.", usage: ["cc-codeconductor ccep <subcommand>"] },
32532
+ { name: "odd", group: "workflow", summary: "Create, resume, and hand off tracked delivery work.", usage: ["cc-codeconductor odd <create|read|reconcile|handoff>"] },
31628
32533
  { name: "openspec", group: "workflow", summary: "Run the OpenSpec delivery loop.", usage: ["cc-codeconductor openspec <subcommand>"] },
31629
32534
  { name: "scorecard", group: "workflow", summary: "Record and aggregate outcomes.", usage: ["cc-codeconductor scorecard <subcommand>"] },
31630
32535
  { name: "orchestrate", group: "workflow", summary: "Run goal execution orchestration.", usage: ["cc-codeconductor orchestrate <subcommand>"] },
@@ -31699,7 +32604,7 @@ function renderDocs(command, bin = "cc-codeconductor") {
31699
32604
  }
31700
32605
  function renderCompletion(shell) {
31701
32606
  const names = COMMANDS.flatMap((item) => [item.name, ...item.aliases ?? []]).join(" ");
31702
- const targets = "opencode claude codex gemini cursor agy pi all";
32607
+ const targets = "opencode claude codex gemini cursor agy pi muse all";
31703
32608
  if (shell === "bash")
31704
32609
  return `_cc(){ local cur="${"${COMP_WORDS[COMP_CWORD]}"}"; local prev="${"${COMP_WORDS[COMP_CWORD-1]}"}"; if [[ "$prev" == "--target" ]]; then COMPREPLY=( $(compgen -W "${targets}" -- "$cur") ); else COMPREPLY=( $(compgen -W "${names}" -- "$cur") ); fi; }
31705
32610
  complete -F _cc cc-codeconductor codeconductor cc`;
@@ -31814,6 +32719,7 @@ var OutputModeSchema = exports_external.enum(["json", "human"]);
31814
32719
  var SeoFormatSchema = exports_external.enum(["cli", "json", "markdown"]);
31815
32720
  var SeoFailOnSchema = exports_external.enum(["error", "warning", "never"]);
31816
32721
  var NonNegativeIntegerSchema = exports_external.coerce.number().int().nonnegative();
32722
+ var BOOLEAN_OPTIONS = new Set(["record-telemetry"]);
31817
32723
  function parseArgs(args) {
31818
32724
  const commandAcceptsOutputPath = args[0] === "seo";
31819
32725
  const flags = {
@@ -31884,6 +32790,9 @@ function parseArgs(args) {
31884
32790
  } else if (value !== undefined) {
31885
32791
  options[key] = value;
31886
32792
  consumed.add(i);
32793
+ } else if (BOOLEAN_OPTIONS.has(key)) {
32794
+ options[key] = true;
32795
+ consumed.add(i);
31887
32796
  } else if (remaining[i + 1] && !remaining[i + 1].startsWith("-")) {
31888
32797
  options[key] = remaining[++i];
31889
32798
  consumed.add(i - 1);
@@ -32241,8 +33150,17 @@ async function routeCommand(args, projectRoot) {
32241
33150
  input: options.input,
32242
33151
  contextPath: options.context || options.contextPath,
32243
33152
  promptVersion: options["prompt-version"] || options.promptVersion,
33153
+ view: options.view,
33154
+ recordTelemetry: options["record-telemetry"] === true || options["record-telemetry"] === "true",
33155
+ executionId: options["execution-id"],
33156
+ taskId: options["task-id"],
33157
+ contextStrategy: options["context-strategy"],
32244
33158
  config: options.config,
32245
- rest: validateRest
33159
+ rest: validateRest,
33160
+ panel: options.panel === true || options.panel === "true",
33161
+ panelType: options.type,
33162
+ panelScope: options.scope,
33163
+ panelRisk: options.risk
32246
33164
  });
32247
33165
  }
32248
33166
  case "openspec":
@@ -32258,7 +33176,9 @@ async function routeCommand(args, projectRoot) {
32258
33176
  "done",
32259
33177
  "block",
32260
33178
  "unblock",
32261
- "archive"
33179
+ "archive",
33180
+ "sync",
33181
+ "verify"
32262
33182
  ];
32263
33183
  let openspecSub = "validate";
32264
33184
  let itemId = options.item || undefined;
@@ -32281,7 +33201,7 @@ async function routeCommand(args, projectRoot) {
32281
33201
  });
32282
33202
  }
32283
33203
  case "odd": {
32284
- const validSubs = ["create", "read", "reconcile"];
33204
+ const validSubs = ["create", "read", "reconcile", "handoff"];
32285
33205
  if (!subcommand || !validSubs.includes(subcommand)) {
32286
33206
  return unknownSubcommand(command, subcommand ?? "", validSubs);
32287
33207
  }
@@ -32299,9 +33219,8 @@ async function routeCommand(args, projectRoot) {
32299
33219
  return unknownSubcommand(command, subcommand, [...validHookEvents]);
32300
33220
  }
32301
33221
  const event = validHookEvents.includes(subcommand) ? subcommand : "pre-tool";
32302
- const formatRaw = options.format;
32303
- const format = formatRaw === "agy" || formatRaw === "claude" ? formatRaw : undefined;
32304
- const { hookCommand: hookCommand2, readStdinText: readStdinText2 } = await Promise.resolve().then(() => (init_hook_command(), exports_hook_command));
33222
+ const { hookCommand: hookCommand2, parseHookFormat: parseHookFormat2, readStdinText: readStdinText2 } = await Promise.resolve().then(() => (init_hook_command(), exports_hook_command));
33223
+ const format = parseHookFormat2(options.format);
32305
33224
  const stdinText = await readStdinText2();
32306
33225
  return hookCommand2({
32307
33226
  event,