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.
- package/README.md +24 -11
- package/dist/core/runner/runner-target.d.ts +4 -4
- package/dist/domain/product/entities.d.ts +1 -1
- package/dist/index.js +1714 -795
- package/dist/library.js +62 -7
- package/dist/validation/schemas.d.ts +210 -43
- package/docs/generated/cli.md +8 -0
- package/package.json +1 -1
- package/presets/agy/skills/openspec/SKILL.md +39 -11
- package/presets/agy/workflows/cc-api-contract.md +4 -5
- package/presets/agy/workflows/cc-backlog.md +4 -5
- package/presets/agy/workflows/cc-clarify.md +4 -5
- package/presets/agy/workflows/cc-council.md +6 -7
- package/presets/agy/workflows/cc-db-migration.md +4 -5
- package/presets/agy/workflows/cc-explore.md +4 -5
- package/presets/agy/workflows/cc-feature.md +4 -5
- package/presets/agy/workflows/cc-fix.md +4 -5
- package/presets/agy/workflows/cc-handoff.md +8 -5
- package/presets/agy/workflows/cc-iterative.md +4 -5
- package/presets/agy/workflows/cc-odd.md +4 -3
- package/presets/agy/workflows/cc-openspec.md +5 -6
- package/presets/agy/workflows/cc-pagespeed.md +4 -5
- package/presets/agy/workflows/cc-prototype.md +4 -5
- package/presets/agy/workflows/cc-refactor.md +4 -5
- package/presets/agy/workflows/cc-review.md +4 -5
- package/presets/agy/workflows/cc-scorecard.md +4 -5
- package/presets/agy/workflows/cc-security.md +4 -5
- package/presets/agy/workflows/cc-spec-mutation.md +4 -5
- package/presets/agy/workflows/cc-tdd-cycle.md +4 -5
- package/presets/agy/workflows/cc-test-plan.md +4 -5
- package/presets/agy/workflows/cc-triage.md +4 -5
- package/presets/claude/CLAUDE.md +6 -5
- package/presets/claude/commands/cc/api-contract.md +4 -5
- package/presets/claude/commands/cc/backlog.md +4 -5
- package/presets/claude/commands/cc/clarify.md +4 -5
- package/presets/claude/commands/cc/council.md +6 -7
- package/presets/claude/commands/cc/db-migration.md +4 -5
- package/presets/claude/commands/cc/explore.md +4 -5
- package/presets/claude/commands/cc/feature.md +4 -5
- package/presets/claude/commands/cc/fix.md +4 -5
- package/presets/claude/commands/cc/handoff.md +8 -5
- package/presets/claude/commands/cc/iterative.md +4 -5
- package/presets/claude/commands/cc/odd.md +4 -3
- package/presets/claude/commands/cc/openspec.md +43 -8
- package/presets/claude/commands/cc/pagespeed.md +4 -5
- package/presets/claude/commands/cc/prototype.md +4 -5
- package/presets/claude/commands/cc/refactor.md +4 -5
- package/presets/claude/commands/cc/review.md +4 -5
- package/presets/claude/commands/cc/scorecard.md +4 -5
- package/presets/claude/commands/cc/security.md +4 -5
- package/presets/claude/commands/cc/spec-mutation.md +4 -5
- package/presets/claude/commands/cc/tdd-cycle.md +4 -5
- package/presets/claude/commands/cc/test-plan.md +4 -5
- package/presets/claude/commands/cc/triage.md +4 -5
- package/presets/claude/settings.json +32 -78
- package/presets/claude/skills/openspec/SKILL.md +39 -11
- package/presets/codex/config.toml +2 -0
- package/presets/codex/skills/cc-api-contract/SKILL.md +4 -5
- package/presets/codex/skills/cc-backlog/SKILL.md +4 -5
- package/presets/codex/skills/cc-clarify/SKILL.md +4 -5
- package/presets/codex/skills/cc-council/SKILL.md +12 -7
- package/presets/codex/skills/cc-db-migration/SKILL.md +4 -5
- package/presets/codex/skills/cc-explore/SKILL.md +4 -5
- package/presets/codex/skills/cc-feature/SKILL.md +4 -5
- package/presets/codex/skills/cc-fix/SKILL.md +4 -5
- package/presets/codex/skills/cc-handoff/SKILL.md +8 -5
- package/presets/codex/skills/cc-iterative/SKILL.md +4 -5
- package/presets/codex/skills/cc-odd/SKILL.md +4 -3
- package/presets/codex/skills/cc-openspec/SKILL.md +31 -7
- package/presets/codex/skills/cc-pagespeed/SKILL.md +4 -5
- package/presets/codex/skills/cc-prototype/SKILL.md +4 -5
- package/presets/codex/skills/cc-refactor/SKILL.md +4 -5
- package/presets/codex/skills/cc-review/SKILL.md +4 -5
- package/presets/codex/skills/cc-scorecard/SKILL.md +4 -5
- package/presets/codex/skills/cc-security/SKILL.md +4 -5
- package/presets/codex/skills/cc-spec-mutation/SKILL.md +4 -5
- package/presets/codex/skills/cc-tdd-cycle/SKILL.md +4 -5
- package/presets/codex/skills/cc-test-plan/SKILL.md +4 -5
- package/presets/codex/skills/cc-triage/SKILL.md +4 -5
- package/presets/codex/skills/openspec/SKILL.md +39 -11
- package/presets/cursor/commands/cc/api-contract.md +4 -5
- package/presets/cursor/commands/cc/backlog.md +4 -5
- package/presets/cursor/commands/cc/clarify.md +4 -5
- package/presets/cursor/commands/cc/council.md +6 -7
- package/presets/cursor/commands/cc/db-migration.md +4 -5
- package/presets/cursor/commands/cc/explore.md +4 -5
- package/presets/cursor/commands/cc/feature.md +4 -5
- package/presets/cursor/commands/cc/fix.md +4 -5
- package/presets/cursor/commands/cc/handoff.md +8 -5
- package/presets/cursor/commands/cc/iterative.md +4 -5
- package/presets/cursor/commands/cc/odd.md +4 -3
- package/presets/cursor/commands/cc/openspec.md +25 -7
- package/presets/cursor/commands/cc/pagespeed.md +4 -5
- package/presets/cursor/commands/cc/prototype.md +4 -5
- package/presets/cursor/commands/cc/refactor.md +4 -5
- package/presets/cursor/commands/cc/review.md +4 -5
- package/presets/cursor/commands/cc/scorecard.md +4 -5
- package/presets/cursor/commands/cc/security.md +4 -5
- package/presets/cursor/commands/cc/spec-mutation.md +4 -5
- package/presets/cursor/commands/cc/tdd-cycle.md +4 -5
- package/presets/cursor/commands/cc/test-plan.md +4 -5
- package/presets/cursor/commands/cc/triage.md +4 -5
- package/presets/cursor/skills/openspec/SKILL.md +39 -11
- package/presets/gemini/commands/cc/api-contract.toml +4 -5
- package/presets/gemini/commands/cc/backlog.toml +4 -5
- package/presets/gemini/commands/cc/clarify.toml +4 -5
- package/presets/gemini/commands/cc/council.toml +6 -7
- package/presets/gemini/commands/cc/db-migration.toml +4 -5
- package/presets/gemini/commands/cc/explore.toml +4 -5
- package/presets/gemini/commands/cc/feature.toml +4 -5
- package/presets/gemini/commands/cc/fix.toml +4 -5
- package/presets/gemini/commands/cc/handoff.toml +8 -5
- package/presets/gemini/commands/cc/iterative.toml +4 -5
- package/presets/gemini/commands/cc/odd.toml +4 -3
- package/presets/gemini/commands/cc/openspec.toml +25 -7
- package/presets/gemini/commands/cc/pagespeed.toml +4 -5
- package/presets/gemini/commands/cc/prototype.toml +4 -5
- package/presets/gemini/commands/cc/refactor.toml +4 -5
- package/presets/gemini/commands/cc/review.toml +4 -5
- package/presets/gemini/commands/cc/scorecard.toml +4 -5
- package/presets/gemini/commands/cc/security.toml +4 -5
- package/presets/gemini/commands/cc/spec-mutation.toml +4 -5
- package/presets/gemini/commands/cc/tdd-cycle.toml +4 -5
- package/presets/gemini/commands/cc/test-plan.toml +4 -5
- package/presets/gemini/commands/cc/triage.toml +4 -5
- package/presets/muse/AGENTS.md +46 -0
- package/presets/muse/hooks.json +30 -0
- package/presets/opencode/commands/cc-api-contract.md +4 -5
- package/presets/opencode/commands/cc-backlog.md +4 -5
- package/presets/opencode/commands/cc-clarify.md +4 -5
- package/presets/opencode/commands/cc-council.md +6 -7
- package/presets/opencode/commands/cc-db-migration.md +4 -5
- package/presets/opencode/commands/cc-explore.md +4 -5
- package/presets/opencode/commands/cc-feature.md +4 -5
- package/presets/opencode/commands/cc-fix.md +4 -5
- package/presets/opencode/commands/cc-handoff.md +8 -5
- package/presets/opencode/commands/cc-iterative.md +4 -5
- package/presets/opencode/commands/cc-odd.md +4 -3
- package/presets/opencode/commands/cc-openspec.md +10 -9
- package/presets/opencode/commands/cc-pagespeed.md +4 -5
- package/presets/opencode/commands/cc-prototype.md +4 -5
- package/presets/opencode/commands/cc-refactor.md +4 -5
- package/presets/opencode/commands/cc-review.md +4 -5
- package/presets/opencode/commands/cc-scorecard.md +4 -5
- package/presets/opencode/commands/cc-security.md +4 -5
- package/presets/opencode/commands/cc-spec-mutation.md +4 -5
- package/presets/opencode/commands/cc-tdd-cycle.md +4 -5
- package/presets/opencode/commands/cc-test-plan.md +4 -5
- package/presets/opencode/commands/cc-triage.md +4 -5
- package/presets/opencode/skills/openspec/SKILL.md +39 -11
- package/src/presets/manifests/codex.yml +3 -0
- package/src/presets/manifests/muse.yml +18 -0
- package/src/presets/models/muse.yml +4 -0
- package/src/presets/models/roles.yml +40 -28
- 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.
|
|
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:
|
|
11524
|
-
dest:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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}`) && !
|
|
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}`) ||
|
|
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 (
|
|
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 === "" ||
|
|
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 === "" ||
|
|
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 && !
|
|
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:
|
|
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 (
|
|
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
|
|
13370
|
-
|
|
13371
|
-
|
|
13372
|
-
}
|
|
13373
|
-
|
|
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
|
|
14138
|
-
const
|
|
14139
|
-
|
|
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
|
-
|
|
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 `
|
|
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
|
-
|
|
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
|
-
|
|
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),
|
|
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
|
|
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
|
-
|
|
17133
|
-
|
|
17134
|
-
|
|
17135
|
-
|
|
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
|
|
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 === "" ||
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
18874
|
-
await writeFile6(
|
|
19020
|
+
await mkdir7(dirname7(gated), { recursive: true });
|
|
19021
|
+
await writeFile6(gated, snapshot.content);
|
|
18875
19022
|
} else {
|
|
18876
|
-
await unlink(
|
|
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 {
|
|
18897
|
-
import {
|
|
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
|
|
19007
|
-
await
|
|
19008
|
-
|
|
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
|
|
19018
|
-
await
|
|
19019
|
-
|
|
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
|
|
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
|
-
|
|
19249
|
+
lockRel = join11(".agents", "skills-lock.json");
|
|
19105
19250
|
}
|
|
19106
19251
|
}
|
|
19107
19252
|
} catch {}
|
|
19108
19253
|
try {
|
|
19109
|
-
await
|
|
19110
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
19672
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
19754
|
-
await
|
|
19755
|
-
const ledgerPath =
|
|
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
|
|
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
|
|
20335
|
+
import { join as join15 } from "node:path";
|
|
20191
20336
|
function productGraphPath(projectRoot) {
|
|
20192
|
-
return
|
|
20337
|
+
return join15(projectRoot, PRODUCT_DIR, PRODUCT_GRAPH_FILE);
|
|
20193
20338
|
}
|
|
20194
20339
|
function productMetaPath(projectRoot) {
|
|
20195
|
-
return
|
|
20340
|
+
return join15(projectRoot, PRODUCT_DIR, PRODUCT_META_FILE);
|
|
20196
20341
|
}
|
|
20197
20342
|
function eventsPath(projectRoot) {
|
|
20198
|
-
return
|
|
20343
|
+
return join15(projectRoot, PRODUCT_DIR, EVENTS_FILE);
|
|
20199
20344
|
}
|
|
20200
20345
|
function operationalStatePath(projectRoot) {
|
|
20201
|
-
return
|
|
20346
|
+
return join15(projectRoot, PRODUCT_DIR, OPERATIONAL_STATE_FILE);
|
|
20202
20347
|
}
|
|
20203
20348
|
function strategicPath(projectRoot) {
|
|
20204
|
-
return
|
|
20349
|
+
return join15(projectRoot, PRODUCT_DIR, STRATEGIC_FILE);
|
|
20205
20350
|
}
|
|
20206
20351
|
function productReportPath(projectRoot) {
|
|
20207
|
-
return
|
|
20352
|
+
return join15(projectRoot, PRODUCT_DIR, PRODUCT_REPORT_FILE);
|
|
20208
20353
|
}
|
|
20209
20354
|
function evidenceDir(projectRoot) {
|
|
20210
|
-
return
|
|
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
|
|
20228
|
-
import { dirname as
|
|
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
|
|
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
|
|
20245
|
-
await
|
|
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
|
|
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
|
|
20603
|
+
await mkdir10(dir, { recursive: true });
|
|
20459
20604
|
const yaml = $stringify(graph);
|
|
20460
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
20854
|
-
import { basename as basename6, join as
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
21031
|
-
import { join as
|
|
21032
|
-
import { mkdir as
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
21080
|
-
const rel = relative8(projectRoot, full).split(
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
21247
|
-
await
|
|
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
|
|
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
|
|
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
|
|
21355
|
-
await
|
|
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
|
|
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
|
|
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
|
|
21415
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
21470
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
21671
|
-
import { join as
|
|
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
|
|
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
|
|
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
|
|
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
|
|
22279
|
-
import { join as
|
|
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}${
|
|
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(
|
|
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
|
|
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(
|
|
22489
|
+
await visit2(join19(current, entry.name));
|
|
22345
22490
|
continue;
|
|
22346
22491
|
}
|
|
22347
22492
|
if (entry.isFile())
|
|
22348
|
-
paths.push(relative9(root,
|
|
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
|
|
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
|
|
22401
|
-
import { dirname as
|
|
22402
|
-
import { mkdir as
|
|
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 (
|
|
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
|
|
22441
|
-
await
|
|
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
|
|
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
|
|
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
|
|
23843
|
-
import { dirname as
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
23956
|
-
await
|
|
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
|
-
|
|
24341
|
+
instruction = `You are the Planner / ${label} of CodeConductor.
|
|
24085
24342
|
Convert product intent into a structured plan without writing code.`;
|
|
24086
|
-
}
|
|
24087
|
-
|
|
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
|
-
|
|
24096
|
-
}
|
|
24097
|
-
|
|
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
|
|
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":
|
|
24197
|
-
|
|
24198
|
-
|
|
24199
|
-
|
|
24200
|
-
|
|
24201
|
-
|
|
24202
|
-
|
|
24203
|
-
|
|
24204
|
-
|
|
24205
|
-
|
|
24206
|
-
|
|
24207
|
-
|
|
24208
|
-
|
|
24209
|
-
|
|
24210
|
-
|
|
24211
|
-
|
|
24212
|
-
|
|
24213
|
-
|
|
24214
|
-
|
|
24215
|
-
|
|
24216
|
-
|
|
24217
|
-
|
|
24218
|
-
|
|
24219
|
-
|
|
24220
|
-
|
|
24221
|
-
|
|
24222
|
-
|
|
24223
|
-
|
|
24224
|
-
|
|
24225
|
-
|
|
24226
|
-
|
|
24227
|
-
|
|
24228
|
-
|
|
24229
|
-
|
|
24230
|
-
|
|
24231
|
-
|
|
24232
|
-
|
|
24233
|
-
|
|
24234
|
-
|
|
24235
|
-
|
|
24236
|
-
|
|
24237
|
-
|
|
24238
|
-
|
|
24239
|
-
|
|
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
|
|
24515
|
+
import { basename as basename7, join as join21 } from "node:path";
|
|
24298
24516
|
function readProjectId(projectRoot) {
|
|
24299
|
-
const packageJsonPath =
|
|
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 =
|
|
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
|
|
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
|
|
24405
|
-
import { dirname as
|
|
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
|
|
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
|
|
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 =
|
|
24746
|
+
const configBackup = join22(backupRoot, "config.yml");
|
|
24529
24747
|
const configDest = resolve29(projectRoot, ".codeconductor", "config.yml");
|
|
24530
24748
|
if (existsSync6(configBackup)) {
|
|
24531
|
-
await
|
|
24749
|
+
await mkdir19(dirname11(configDest), { recursive: true });
|
|
24532
24750
|
await copyFile2(configBackup, configDest);
|
|
24533
24751
|
}
|
|
24534
|
-
const workflowsBackup =
|
|
24752
|
+
const workflowsBackup = join22(backupRoot, "workflows");
|
|
24535
24753
|
const workflowsDest = resolve29(projectRoot, ".codeconductor", "workflows");
|
|
24536
24754
|
if (existsSync6(workflowsBackup)) {
|
|
24537
|
-
await
|
|
24755
|
+
await mkdir19(workflowsDest, { recursive: true });
|
|
24538
24756
|
const files = await readdir10(workflowsBackup);
|
|
24539
24757
|
for (const file of files) {
|
|
24540
|
-
await copyFile2(
|
|
24758
|
+
await copyFile2(join22(workflowsBackup, file), join22(workflowsDest, file));
|
|
24541
24759
|
}
|
|
24542
24760
|
}
|
|
24543
|
-
const overlayBackup =
|
|
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
|
|
24773
|
+
await mkdir19(backupRoot, { recursive: true });
|
|
24556
24774
|
const configPath = resolve29(projectRoot, ".codeconductor", "config.yml");
|
|
24557
24775
|
if (existsSync6(configPath)) {
|
|
24558
|
-
await copyFile2(configPath,
|
|
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 =
|
|
24563
|
-
await
|
|
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(
|
|
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,
|
|
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
|
|
24584
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
24882
|
-
import { join as
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
25436
|
-
|
|
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:
|
|
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
|
-
|
|
26056
|
+
phaseRoles: resolvedPhase.roles,
|
|
26057
|
+
outputSchema: compiled.outputSchema,
|
|
25576
26058
|
promptVersion,
|
|
25577
|
-
|
|
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
|
-
|
|
26501
|
+
function checkBacklogFileChanged(projectRoot) {
|
|
25994
26502
|
try {
|
|
25995
|
-
const
|
|
25996
|
-
|
|
25997
|
-
|
|
25998
|
-
|
|
25999
|
-
|
|
26000
|
-
|
|
26001
|
-
|
|
26002
|
-
|
|
26003
|
-
|
|
26004
|
-
|
|
26005
|
-
|
|
26006
|
-
|
|
26007
|
-
|
|
26008
|
-
|
|
26009
|
-
|
|
26010
|
-
|
|
26011
|
-
|
|
26012
|
-
|
|
26013
|
-
|
|
26014
|
-
|
|
26015
|
-
|
|
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
|
-
|
|
26029
|
-
|
|
26030
|
-
|
|
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
|
-
|
|
26033
|
-
|
|
26034
|
-
|
|
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
|
-
|
|
26039
|
-
|
|
26040
|
-
|
|
26041
|
-
|
|
26042
|
-
|
|
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
|
-
|
|
26049
|
-
|
|
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:
|
|
26089
|
-
|
|
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
|
|
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:**
|
|
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:
|
|
26813
|
+
${item.risks ? `Risks:
|
|
26814
|
+
${markUntrusted("risks", item.risks)}` : "See design.md for technical impact."}
|
|
26262
26815
|
|
|
26263
|
-
**Out of scope:**
|
|
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
|
|
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) =>
|
|
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
|
|
26314
|
-
await
|
|
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
|
|
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
|
|
26366
|
-
await
|
|
26367
|
-
await
|
|
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
|
|
26370
|
-
await
|
|
26371
|
-
await
|
|
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
|
|
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:
|
|
26392
|
-
await
|
|
26958
|
+
const { access: access8 } = await import("node:fs/promises");
|
|
26959
|
+
await access8(configPath);
|
|
26393
26960
|
} catch {
|
|
26394
|
-
await
|
|
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
|
|
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 {
|
|
26407
|
-
import { join as
|
|
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
|
|
27064
|
+
const text = block.markdown;
|
|
27065
|
+
const heading = text.split(`
|
|
26458
27066
|
`, 1)[0] ?? "";
|
|
26459
|
-
if (!RFC2119.test(
|
|
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(
|
|
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 =
|
|
26559
|
-
const specFiles = await
|
|
26560
|
-
const extra = ["proposal.md", "design.md"].map((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
|
|
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
|
|
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
|
|
26625
|
-
import { join as
|
|
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
|
-
|
|
26651
|
-
|
|
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(
|
|
27318
|
+
const specMarkdown = await readDeltaSpecs(join26(changeRoot, "specs"));
|
|
26742
27319
|
let tasksMarkdown = "";
|
|
26743
27320
|
try {
|
|
26744
|
-
tasksMarkdown = await
|
|
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
|
|
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
|
|
26770
|
-
import { join as
|
|
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
|
|
26805
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
27430
|
+
const target = join27(root, "openspec", "specs", relativeSpec);
|
|
26879
27431
|
let existing = `# ${relativeSpec.replace(/\\/g, "/")}
|
|
26880
27432
|
`;
|
|
26881
27433
|
try {
|
|
26882
|
-
existing = await
|
|
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
|
|
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
|
|
27451
|
+
var SECTION_HEADING;
|
|
26900
27452
|
var init_spec_sync = __esm(() => {
|
|
26901
27453
|
init_openspec_state();
|
|
26902
|
-
|
|
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
|
|
26912
|
-
import { resolve as
|
|
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(
|
|
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
|
|
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
|
-
|
|
26986
|
-
const
|
|
26987
|
-
if (!
|
|
27621
|
+
function resolveActiveChange(state, command, itemId) {
|
|
27622
|
+
const targetId = itemId ?? state.activeItemId;
|
|
27623
|
+
if (!targetId) {
|
|
26988
27624
|
return {
|
|
26989
|
-
|
|
26990
|
-
|
|
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
|
|
27001
|
-
|
|
27002
|
-
|
|
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
|
|
27005
|
-
const
|
|
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
|
-
|
|
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:
|
|
27027
|
-
archiveCount:
|
|
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
|
|
27033
|
-
|
|
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: [
|
|
27767
|
+
errors: [sessionResult.error.message]
|
|
27042
27768
|
}
|
|
27043
27769
|
};
|
|
27044
27770
|
}
|
|
27045
|
-
const
|
|
27046
|
-
|
|
27047
|
-
|
|
27048
|
-
|
|
27049
|
-
|
|
27050
|
-
|
|
27051
|
-
|
|
27052
|
-
|
|
27053
|
-
|
|
27054
|
-
|
|
27055
|
-
|
|
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
|
-
...
|
|
27788
|
+
...scan
|
|
27065
27789
|
}
|
|
27066
27790
|
};
|
|
27067
27791
|
}
|
|
27068
27792
|
async function handlePlan(projectRoot, itemId) {
|
|
27069
|
-
const
|
|
27793
|
+
const sessionResult = await loadBacklogSession(projectRoot);
|
|
27794
|
+
const validateFirst = await buildValidationResponse(projectRoot, sessionResult);
|
|
27070
27795
|
if (validateFirst.code !== 0)
|
|
27071
27796
|
return validateFirst;
|
|
27072
|
-
|
|
27073
|
-
|
|
27074
|
-
|
|
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
|
|
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
|
|
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
|
|
27150
|
-
if (!
|
|
27151
|
-
return fail(command, [
|
|
27152
|
-
|
|
27153
|
-
const
|
|
27154
|
-
if (!
|
|
27155
|
-
return
|
|
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
|
|
27213
|
-
|
|
27214
|
-
|
|
27215
|
-
|
|
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
|
-
|
|
27281
|
-
const
|
|
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:
|
|
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
|
|
27301
|
-
if (!
|
|
27302
|
-
return
|
|
27303
|
-
const
|
|
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 =
|
|
28129
|
+
const itemLoaded = findItemOrFail(session, card.backlogId, command);
|
|
27311
28130
|
if (!itemLoaded.ok)
|
|
27312
28131
|
return itemLoaded.response;
|
|
27313
|
-
let 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
|
|
27341
|
-
if (!
|
|
27342
|
-
return
|
|
27343
|
-
const
|
|
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 =
|
|
28176
|
+
const itemLoaded = findItemOrFail(session, card.backlogId, command);
|
|
27357
28177
|
if (!itemLoaded.ok)
|
|
27358
28178
|
return itemLoaded.response;
|
|
27359
|
-
const
|
|
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(
|
|
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
|
|
27412
|
-
if (!
|
|
27413
|
-
return
|
|
27414
|
-
const
|
|
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 =
|
|
28238
|
+
const itemLoaded = findItemOrFail(session, card.backlogId, command);
|
|
27419
28239
|
if (!itemLoaded.ok)
|
|
27420
28240
|
return itemLoaded.response;
|
|
27421
|
-
const state = setTaskCardStatus(
|
|
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
|
|
27447
|
-
if (!
|
|
27448
|
-
return
|
|
27449
|
-
const
|
|
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 =
|
|
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(
|
|
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:
|
|
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
|
|
27498
|
-
if (!
|
|
27499
|
-
return
|
|
27500
|
-
const
|
|
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(
|
|
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 =
|
|
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
|
-
...
|
|
27552
|
-
activeItemId:
|
|
27553
|
-
changePaths: archivedPath ? { ...
|
|
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
|
-
|
|
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
|
|
27601
|
-
import { resolve as
|
|
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
|
|
28481
|
+
return resolve38(projectRoot, DELIVERY_DIR, `${id}.md`);
|
|
27606
28482
|
}
|
|
27607
28483
|
async function fingerprintWorkspace(projectRoot, paths) {
|
|
27608
|
-
const root =
|
|
28484
|
+
const root = resolve38(projectRoot);
|
|
27609
28485
|
const fingerprints = {};
|
|
27610
28486
|
for (const path of paths) {
|
|
27611
|
-
const candidate =
|
|
27612
|
-
if (candidate !== root && !candidate.startsWith(`${root}${
|
|
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
|
|
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
|
|
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
|
|
28541
|
+
await mkdir23(resolve38(projectRoot, DELIVERY_DIR), { recursive: true });
|
|
27665
28542
|
try {
|
|
27666
|
-
await
|
|
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
|
|
27726
|
-
|
|
27727
|
-
|
|
27728
|
-
|
|
27729
|
-
|
|
27730
|
-
|
|
27731
|
-
|
|
27732
|
-
}
|
|
27733
|
-
|
|
27734
|
-
|
|
27735
|
-
const
|
|
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:
|
|
27741
|
-
|
|
27742
|
-
|
|
27743
|
-
|
|
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
|
|
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 =
|
|
28005
|
-
const profilePath =
|
|
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 =
|
|
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(
|
|
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, [
|
|
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
|
|
28614
|
-
import { dirname as
|
|
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
|
|
29520
|
+
return resolve40(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR, experimentId);
|
|
28617
29521
|
}
|
|
28618
29522
|
function experimentYamlPath(projectRoot, experimentId) {
|
|
28619
|
-
return
|
|
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
|
-
|
|
28624
|
-
|
|
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
|
|
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
|
|
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 =
|
|
29554
|
+
const root = resolve40(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR);
|
|
28651
29555
|
if (!existsSync9(root))
|
|
28652
29556
|
return [];
|
|
28653
|
-
const ids = await
|
|
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 ?
|
|
28730
|
-
await
|
|
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
|
|
28753
|
-
await
|
|
28754
|
-
await
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
28916
|
-
import { resolve as
|
|
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
|
|
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
|
|
29000
|
-
import { join as
|
|
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
|
|
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
|
-
|
|
29050
|
-
|
|
29051
|
-
|
|
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 =
|
|
29057
|
-
const t =
|
|
29960
|
+
const f = join30(root, fromV);
|
|
29961
|
+
const t = join30(root, toV);
|
|
29058
29962
|
try {
|
|
29059
|
-
await
|
|
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 =
|
|
29067
|
-
toDir =
|
|
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
|
|
29974
|
+
await readFile44(join30(toDir, "orchestrator.md"), "utf-8");
|
|
29071
29975
|
} catch {
|
|
29072
|
-
toDir =
|
|
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
|
|
29994
|
+
oldText = await readFile44(join30(fromDir, name), "utf-8");
|
|
29091
29995
|
} catch {
|
|
29092
29996
|
oldText = "";
|
|
29093
29997
|
}
|
|
29094
29998
|
try {
|
|
29095
|
-
newText = await
|
|
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
|
|
29136
|
-
import { dirname as
|
|
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 =
|
|
29140
|
-
const content = await
|
|
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 =
|
|
29196
|
-
await
|
|
29197
|
-
await
|
|
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
|
|
29240
|
-
import { resolve as
|
|
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
|
|
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
|
|
29285
|
-
import { resolve as
|
|
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 =
|
|
29454
|
-
await
|
|
29455
|
-
const path =
|
|
29456
|
-
await
|
|
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
|
|
29471
|
-
import { resolve as
|
|
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 =
|
|
30663
|
+
const templatePath = resolve47(projectRoot, ".codeconductor/evaluation/model-comparison-last.md");
|
|
29760
30664
|
try {
|
|
29761
|
-
await
|
|
30665
|
+
await readFile46(templatePath);
|
|
29762
30666
|
} catch {}
|
|
29763
|
-
const { writeFile:
|
|
29764
|
-
await
|
|
29765
|
-
await
|
|
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
|
|
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" ?
|
|
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
|
|
32303
|
-
const format =
|
|
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,
|