cc-codeconductor 1.0.0 → 1.1.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 +324 -87
- package/dist/core/compilation/compile-checker.d.ts +2 -0
- package/dist/core/loop/loop-engine.d.ts +3 -0
- package/dist/index.js +2701 -577
- package/dist/library.js +167 -18
- package/dist/validation/schemas.d.ts +390 -44
- package/package.json +4 -1
- package/presets/agy/AGENTS.md +3 -0
- package/presets/agy/gates/pre-commit/GATE.md +5 -4
- package/presets/agy/skills/backlog/SKILL.md +91 -0
- package/presets/agy/skills/openspec/SKILL.md +5 -1
- package/presets/agy/skills/security-ai-llm/SKILL.md +43 -0
- package/presets/agy/skills/security-blue-team/SKILL.md +43 -0
- package/presets/agy/skills/security-cloud/SKILL.md +43 -0
- package/presets/agy/skills/security-crypto/SKILL.md +43 -0
- package/presets/agy/skills/security-exploit-dev/SKILL.md +45 -0
- package/presets/agy/skills/security-grc/SKILL.md +43 -0
- package/presets/agy/skills/security-incident-response/SKILL.md +45 -0
- package/presets/agy/skills/security-log-analysis/SKILL.md +43 -0
- package/presets/agy/skills/security-malware-analysis/SKILL.md +44 -0
- package/presets/agy/skills/security-mobile/SKILL.md +43 -0
- package/presets/agy/skills/security-network/SKILL.md +43 -0
- package/presets/agy/skills/security-ot-ics/SKILL.md +43 -0
- package/presets/agy/skills/security-recon/SKILL.md +45 -0
- package/presets/agy/skills/security-red-team/SKILL.md +44 -0
- package/presets/agy/skills/security-reverse-engineering/SKILL.md +44 -0
- package/presets/agy/skills/security-soc-automation/SKILL.md +43 -0
- package/presets/agy/skills/security-threat-hunting/SKILL.md +43 -0
- package/presets/agy/skills/security-vuln-assessment/SKILL.md +45 -0
- package/presets/agy/skills/security-web/SKILL.md +44 -0
- package/presets/agy/workflows/cc-ask.md +2 -1
- package/presets/agy/workflows/cc-backlog.md +69 -0
- package/presets/agy/workflows/cc-explore.md +1 -1
- package/presets/agy/workflows/cc-openspec.md +3 -1
- package/presets/agy/workflows/cc-security.md +180 -0
- package/presets/claude/CLAUDE.md +4 -0
- package/presets/claude/commands/cc/ask.md +2 -1
- package/presets/claude/commands/cc/backlog.md +104 -0
- package/presets/claude/commands/cc/explore.md +1 -1
- package/presets/claude/commands/cc/openspec.md +16 -1
- package/presets/claude/commands/cc/security.md +179 -0
- package/presets/claude/gates/pre-commit/GATE.md +5 -4
- package/presets/claude/skills/backlog/SKILL.md +91 -0
- package/presets/claude/skills/openspec/SKILL.md +5 -1
- package/presets/claude/skills/security/SKILL.md +382 -0
- package/presets/claude/skills/security-ai-llm/SKILL.md +43 -0
- package/presets/claude/skills/security-blue-team/SKILL.md +43 -0
- package/presets/claude/skills/security-cloud/SKILL.md +43 -0
- package/presets/claude/skills/security-crypto/SKILL.md +43 -0
- package/presets/claude/skills/security-exploit-dev/SKILL.md +45 -0
- package/presets/claude/skills/security-grc/SKILL.md +43 -0
- package/presets/claude/skills/security-incident-response/SKILL.md +45 -0
- package/presets/claude/skills/security-log-analysis/SKILL.md +43 -0
- package/presets/claude/skills/security-malware-analysis/SKILL.md +44 -0
- package/presets/claude/skills/security-mobile/SKILL.md +43 -0
- package/presets/claude/skills/security-network/SKILL.md +43 -0
- package/presets/claude/skills/security-ot-ics/SKILL.md +43 -0
- package/presets/claude/skills/security-recon/SKILL.md +45 -0
- package/presets/claude/skills/security-red-team/SKILL.md +44 -0
- package/presets/claude/skills/security-reverse-engineering/SKILL.md +44 -0
- package/presets/claude/skills/security-soc-automation/SKILL.md +43 -0
- package/presets/claude/skills/security-threat-hunting/SKILL.md +43 -0
- package/presets/claude/skills/security-vuln-assessment/SKILL.md +45 -0
- package/presets/claude/skills/security-web/SKILL.md +44 -0
- package/presets/codex/commands/cc-ask.md +2 -1
- package/presets/codex/gates/pre-commit/GATE.md +5 -4
- package/presets/cursor/AGENTS.md +2 -2
- package/presets/cursor/commands/cc/ask.md +2 -1
- package/presets/cursor/commands/cc/backlog.md +105 -0
- package/presets/cursor/commands/cc/explore.md +1 -1
- package/presets/cursor/commands/cc/openspec.md +16 -1
- package/presets/cursor/commands/cc/security.md +179 -0
- package/presets/cursor/gates/pre-commit/GATE.md +5 -4
- package/presets/cursor/skills/backlog/SKILL.md +91 -0
- package/presets/cursor/skills/openspec/SKILL.md +5 -1
- package/presets/cursor/skills/security-ai-llm/SKILL.md +43 -0
- package/presets/cursor/skills/security-blue-team/SKILL.md +43 -0
- package/presets/cursor/skills/security-cloud/SKILL.md +43 -0
- package/presets/cursor/skills/security-crypto/SKILL.md +43 -0
- package/presets/cursor/skills/security-exploit-dev/SKILL.md +45 -0
- package/presets/cursor/skills/security-grc/SKILL.md +43 -0
- package/presets/cursor/skills/security-incident-response/SKILL.md +45 -0
- package/presets/cursor/skills/security-log-analysis/SKILL.md +43 -0
- package/presets/cursor/skills/security-malware-analysis/SKILL.md +44 -0
- package/presets/cursor/skills/security-mobile/SKILL.md +43 -0
- package/presets/cursor/skills/security-network/SKILL.md +43 -0
- package/presets/cursor/skills/security-ot-ics/SKILL.md +43 -0
- package/presets/cursor/skills/security-recon/SKILL.md +45 -0
- package/presets/cursor/skills/security-red-team/SKILL.md +44 -0
- package/presets/cursor/skills/security-reverse-engineering/SKILL.md +44 -0
- package/presets/cursor/skills/security-soc-automation/SKILL.md +43 -0
- package/presets/cursor/skills/security-threat-hunting/SKILL.md +43 -0
- package/presets/cursor/skills/security-vuln-assessment/SKILL.md +45 -0
- package/presets/cursor/skills/security-web/SKILL.md +44 -0
- package/presets/opencode/commands/cc-ask.md +2 -1
- package/presets/opencode/commands/cc-backlog.md +68 -0
- package/presets/opencode/commands/cc-explore.md +1 -1
- package/presets/opencode/commands/cc-openspec.md +3 -1
- package/presets/opencode/commands/cc-security.md +179 -0
- package/presets/opencode/gates/pre-commit/GATE.md +5 -4
- package/presets/opencode/skills/backlog/SKILL.md +91 -0
- package/presets/opencode/skills/openspec/SKILL.md +5 -1
- package/presets/opencode/skills/security-ai-llm/SKILL.md +43 -0
- package/presets/opencode/skills/security-blue-team/SKILL.md +43 -0
- package/presets/opencode/skills/security-cloud/SKILL.md +43 -0
- package/presets/opencode/skills/security-crypto/SKILL.md +43 -0
- package/presets/opencode/skills/security-exploit-dev/SKILL.md +45 -0
- package/presets/opencode/skills/security-grc/SKILL.md +43 -0
- package/presets/opencode/skills/security-incident-response/SKILL.md +45 -0
- package/presets/opencode/skills/security-log-analysis/SKILL.md +43 -0
- package/presets/opencode/skills/security-malware-analysis/SKILL.md +44 -0
- package/presets/opencode/skills/security-mobile/SKILL.md +43 -0
- package/presets/opencode/skills/security-network/SKILL.md +43 -0
- package/presets/opencode/skills/security-ot-ics/SKILL.md +43 -0
- package/presets/opencode/skills/security-recon/SKILL.md +45 -0
- package/presets/opencode/skills/security-red-team/SKILL.md +44 -0
- package/presets/opencode/skills/security-reverse-engineering/SKILL.md +44 -0
- package/presets/opencode/skills/security-soc-automation/SKILL.md +43 -0
- package/presets/opencode/skills/security-threat-hunting/SKILL.md +43 -0
- package/presets/opencode/skills/security-vuln-assessment/SKILL.md +45 -0
- package/presets/opencode/skills/security-web/SKILL.md +44 -0
- package/src/presets/council/council.yml +12 -0
package/dist/index.js
CHANGED
|
@@ -83,7 +83,7 @@ var package_default;
|
|
|
83
83
|
var init_package = __esm(() => {
|
|
84
84
|
package_default = {
|
|
85
85
|
name: "cc-codeconductor",
|
|
86
|
-
version: "1.
|
|
86
|
+
version: "1.1.0",
|
|
87
87
|
description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
|
|
88
88
|
keywords: [
|
|
89
89
|
"ai",
|
|
@@ -132,7 +132,10 @@ var init_package = __esm(() => {
|
|
|
132
132
|
"build:lib": "bun build src/index.ts --target=node --outfile=dist/library.js --packages=external",
|
|
133
133
|
"build:types": "tsc -p tsconfig.lib.json --emitDeclarationOnly",
|
|
134
134
|
test: "bun test",
|
|
135
|
+
"test:fast": "bun run scripts/test-fast.ts",
|
|
135
136
|
typecheck: "tsc --noEmit",
|
|
137
|
+
lint: "bun run scripts/lint.ts",
|
|
138
|
+
"check:coverage": "bun run scripts/check-coverage.ts",
|
|
136
139
|
"check:prompt-changelog": "bun run scripts/check-prompt-changelog.ts",
|
|
137
140
|
"release:patch": "bash release.sh patch",
|
|
138
141
|
"release:minor": "bash release.sh minor",
|
|
@@ -11274,6 +11277,10 @@ __export(exports_schemas, {
|
|
|
11274
11277
|
validateMemoryIndex: () => validateMemoryIndex,
|
|
11275
11278
|
validateKnowledgeEntity: () => validateKnowledgeEntity,
|
|
11276
11279
|
validateImplementerOutput: () => validateImplementerOutput,
|
|
11280
|
+
validateHarnessSuite: () => validateHarnessSuite,
|
|
11281
|
+
validateHarnessOverlay: () => validateHarnessOverlay,
|
|
11282
|
+
validateHarnessExperiment: () => validateHarnessExperiment,
|
|
11283
|
+
validateHarnessCatalog: () => validateHarnessCatalog,
|
|
11277
11284
|
validateGoalGraph: () => validateGoalGraph,
|
|
11278
11285
|
validateExecutionContext: () => validateExecutionContext,
|
|
11279
11286
|
validateEvidence: () => validateEvidence,
|
|
@@ -11337,6 +11344,15 @@ __export(exports_schemas, {
|
|
|
11337
11344
|
ImplementerTestsSchema: () => ImplementerTestsSchema,
|
|
11338
11345
|
ImplementerOutputSchema: () => ImplementerOutputSchema,
|
|
11339
11346
|
ImpactReportSchema: () => ImpactReportSchema,
|
|
11347
|
+
HarnessToggleKindSchema: () => HarnessToggleKindSchema,
|
|
11348
|
+
HarnessSuiteTaskSchema: () => HarnessSuiteTaskSchema,
|
|
11349
|
+
HarnessSuiteSchema: () => HarnessSuiteSchema,
|
|
11350
|
+
HarnessOverlaySchema: () => HarnessOverlaySchema,
|
|
11351
|
+
HarnessExperimentSchema: () => HarnessExperimentSchema,
|
|
11352
|
+
HarnessComponentLayerSchema: () => HarnessComponentLayerSchema,
|
|
11353
|
+
HarnessComponentIdSchema: () => HarnessComponentIdSchema,
|
|
11354
|
+
HarnessCatalogSchema: () => HarnessCatalogSchema,
|
|
11355
|
+
HarnessCatalogEntrySchema: () => HarnessCatalogEntrySchema,
|
|
11340
11356
|
GraphRelationSchema: () => GraphRelationSchema,
|
|
11341
11357
|
GoalTaskSchema: () => GoalTaskSchema,
|
|
11342
11358
|
GoalGraphSchema: () => GoalGraphSchema,
|
|
@@ -11427,6 +11443,18 @@ function validateScorecardRecord(data) {
|
|
|
11427
11443
|
function validateTaskOutcome(data) {
|
|
11428
11444
|
return TaskOutcomeSchema.parse(data);
|
|
11429
11445
|
}
|
|
11446
|
+
function validateHarnessCatalog(data) {
|
|
11447
|
+
return HarnessCatalogSchema.parse(data);
|
|
11448
|
+
}
|
|
11449
|
+
function validateHarnessOverlay(data) {
|
|
11450
|
+
return HarnessOverlaySchema.parse(data);
|
|
11451
|
+
}
|
|
11452
|
+
function validateHarnessExperiment(data) {
|
|
11453
|
+
return HarnessExperimentSchema.parse(data);
|
|
11454
|
+
}
|
|
11455
|
+
function validateHarnessSuite(data) {
|
|
11456
|
+
return HarnessSuiteSchema.parse(data);
|
|
11457
|
+
}
|
|
11430
11458
|
function validateCommandEnvelope(data) {
|
|
11431
11459
|
return CommandEnvelopeSchema.parse(data);
|
|
11432
11460
|
}
|
|
@@ -11466,7 +11494,7 @@ function validateCanonicalTaskCard(data) {
|
|
|
11466
11494
|
function validateProductEvent(data) {
|
|
11467
11495
|
return ProductEventSchema.parse(data);
|
|
11468
11496
|
}
|
|
11469
|
-
var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, 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, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, TaskOutcomeSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema;
|
|
11497
|
+
var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, 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, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, TaskOutcomeSchema, HarnessComponentIdSchema, HarnessComponentLayerSchema, HarnessToggleKindSchema, HarnessCatalogEntrySchema, HarnessCatalogSchema, HarnessOverlaySchema, HarnessExperimentSchema, HarnessSuiteTaskSchema, HarnessSuiteSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema;
|
|
11470
11498
|
var init_schemas = __esm(() => {
|
|
11471
11499
|
init_zod();
|
|
11472
11500
|
CouncilAgentSpecSchema = exports_external.object({
|
|
@@ -11488,7 +11516,7 @@ var init_schemas = __esm(() => {
|
|
|
11488
11516
|
version: exports_external.string(),
|
|
11489
11517
|
description: exports_external.string(),
|
|
11490
11518
|
outputContract: exports_external.string(),
|
|
11491
|
-
agents: exports_external.array(CouncilAgentSpecSchema)
|
|
11519
|
+
agents: exports_external.array(CouncilAgentSpecSchema).min(1).refine((agents) => new Set(agents.map((a) => a.id.trim().toLowerCase())).size === agents.length, { message: "council agent ids must be unique" })
|
|
11492
11520
|
});
|
|
11493
11521
|
ProjectProfileSchema = exports_external.object({
|
|
11494
11522
|
rootDir: exports_external.string(),
|
|
@@ -11610,7 +11638,7 @@ var init_schemas = __esm(() => {
|
|
|
11610
11638
|
status: exports_external.enum(["APPROVED", "REJECTED", "ABSTAIN"]),
|
|
11611
11639
|
securityVeto: exports_external.boolean(),
|
|
11612
11640
|
complianceVeto: exports_external.boolean().optional(),
|
|
11613
|
-
confidence: exports_external.number().min(0).max(1)
|
|
11641
|
+
confidence: exports_external.number().min(0).max(1),
|
|
11614
11642
|
findings: exports_external.array(CouncilFindingSchema),
|
|
11615
11643
|
summary: exports_external.string()
|
|
11616
11644
|
});
|
|
@@ -11618,10 +11646,15 @@ var init_schemas = __esm(() => {
|
|
|
11618
11646
|
algorithm: exports_external.enum(["majority", "unanimous"]),
|
|
11619
11647
|
allowSecurityVeto: exports_external.boolean(),
|
|
11620
11648
|
allowComplianceVeto: exports_external.boolean().optional(),
|
|
11621
|
-
expectedAgentIds: exports_external.array(exports_external.string().min(1).refine((id) => id.trim().length > 0)).min(1).refine((ids) => new Set(ids.map((id) => id.trim().toLowerCase())).size === ids.length).optional()
|
|
11649
|
+
expectedAgentIds: exports_external.array(exports_external.string().min(1).refine((id) => id.trim().length > 0)).min(1).refine((ids) => new Set(ids.map((id) => id.trim().toLowerCase())).size === ids.length).optional(),
|
|
11650
|
+
quorum: exports_external.number().int().positive().optional(),
|
|
11651
|
+
criticalFindingsPolicy: exports_external.enum(["escalate", "reject", "ignore"]).optional()
|
|
11622
11652
|
}).refine((config) => config.algorithm !== "unanimous" || config.expectedAgentIds !== undefined, {
|
|
11623
11653
|
message: "unanimous consensus requires a non-empty expectedAgentIds roster",
|
|
11624
11654
|
path: ["expectedAgentIds"]
|
|
11655
|
+
}).refine((config) => !config.allowSecurityVeto || config.expectedAgentIds === undefined || config.expectedAgentIds.some((id) => id.toLowerCase().includes("security")), {
|
|
11656
|
+
message: "allowSecurityVeto requires at least one security agent in expectedAgentIds",
|
|
11657
|
+
path: ["expectedAgentIds"]
|
|
11625
11658
|
});
|
|
11626
11659
|
CouncilVerdictSchema = exports_external.object({
|
|
11627
11660
|
status: exports_external.enum(["APPROVED", "REJECTED", "ESCALATED"]),
|
|
@@ -11773,8 +11806,17 @@ var init_schemas = __esm(() => {
|
|
|
11773
11806
|
agentType: exports_external.string(),
|
|
11774
11807
|
evidenceRequired: exports_external.array(exports_external.string()).default([]),
|
|
11775
11808
|
status: CanonicalTaskCardStatusSchema,
|
|
11776
|
-
type: exports_external.enum(["feature", "fix", "refactor", "review", "docs", "test"])
|
|
11777
|
-
linkedCapabilities: exports_external.array(exports_external.string()).default([])
|
|
11809
|
+
type: exports_external.enum(["feature", "fix", "refactor", "review", "docs", "test"]),
|
|
11810
|
+
linkedCapabilities: exports_external.array(exports_external.string()).default([]),
|
|
11811
|
+
boundaries: exports_external.array(exports_external.string()).default([]),
|
|
11812
|
+
requiresHumanReview: exports_external.boolean().optional(),
|
|
11813
|
+
requiresTests: exports_external.boolean().optional(),
|
|
11814
|
+
contextScope: exports_external.enum(["isolated", "continuation", "full"]).optional(),
|
|
11815
|
+
actualBehavior: exports_external.string().optional(),
|
|
11816
|
+
expectedBehavior: exports_external.string().optional(),
|
|
11817
|
+
reproductionSteps: exports_external.array(exports_external.string()).optional(),
|
|
11818
|
+
domain: exports_external.string().optional(),
|
|
11819
|
+
authorization: exports_external.string().optional()
|
|
11778
11820
|
});
|
|
11779
11821
|
ProductGraphNodeSchema = exports_external.object({
|
|
11780
11822
|
id: exports_external.string(),
|
|
@@ -11932,7 +11974,8 @@ var init_schemas = __esm(() => {
|
|
|
11932
11974
|
modelHint: exports_external.string().optional(),
|
|
11933
11975
|
dependsOn: exports_external.array(exports_external.string()).default([]),
|
|
11934
11976
|
acceptanceCriteria: exports_external.array(exports_external.string()),
|
|
11935
|
-
status: OpenspecTaskCardStatusSchema
|
|
11977
|
+
status: OpenspecTaskCardStatusSchema,
|
|
11978
|
+
itemHash: exports_external.string().optional()
|
|
11936
11979
|
});
|
|
11937
11980
|
OpenspecStateSchema = exports_external.object({
|
|
11938
11981
|
version: exports_external.literal(1),
|
|
@@ -11954,6 +11997,7 @@ var init_schemas = __esm(() => {
|
|
|
11954
11997
|
"db-migration",
|
|
11955
11998
|
"pagespeed",
|
|
11956
11999
|
"openspec",
|
|
12000
|
+
"backlog",
|
|
11957
12001
|
"scorecard",
|
|
11958
12002
|
"council",
|
|
11959
12003
|
"iterative",
|
|
@@ -11961,7 +12005,8 @@ var init_schemas = __esm(() => {
|
|
|
11961
12005
|
"triage",
|
|
11962
12006
|
"prototype",
|
|
11963
12007
|
"handoff",
|
|
11964
|
-
"clarify"
|
|
12008
|
+
"clarify",
|
|
12009
|
+
"security"
|
|
11965
12010
|
]);
|
|
11966
12011
|
CcepOutputFormatSchema = exports_external.enum(["taskcard", "plan", "verdict"]);
|
|
11967
12012
|
CommandEnvelopeSchema = exports_external.object({
|
|
@@ -12195,7 +12240,86 @@ var init_schemas = __esm(() => {
|
|
|
12195
12240
|
tokensIn: exports_external.number().optional(),
|
|
12196
12241
|
tokensOut: exports_external.number().optional(),
|
|
12197
12242
|
costUsd: exports_external.number().optional(),
|
|
12198
|
-
durationMs: exports_external.number().optional()
|
|
12243
|
+
durationMs: exports_external.number().optional(),
|
|
12244
|
+
experimentId: exports_external.string().optional(),
|
|
12245
|
+
variantId: exports_external.string().optional(),
|
|
12246
|
+
suiteTaskId: exports_external.string().optional(),
|
|
12247
|
+
harnessFingerprint: exports_external.string().optional(),
|
|
12248
|
+
disabledComponents: exports_external.array(exports_external.string()).optional()
|
|
12249
|
+
});
|
|
12250
|
+
HarnessComponentIdSchema = exports_external.enum([
|
|
12251
|
+
"wayfinding",
|
|
12252
|
+
"intake",
|
|
12253
|
+
"design",
|
|
12254
|
+
"test_first",
|
|
12255
|
+
"review",
|
|
12256
|
+
"docs",
|
|
12257
|
+
"confirmation_gates",
|
|
12258
|
+
"compile_loop",
|
|
12259
|
+
"council",
|
|
12260
|
+
"product_graph"
|
|
12261
|
+
]);
|
|
12262
|
+
HarnessComponentLayerSchema = exports_external.enum([
|
|
12263
|
+
"phase",
|
|
12264
|
+
"gate",
|
|
12265
|
+
"loop",
|
|
12266
|
+
"knowledge",
|
|
12267
|
+
"council"
|
|
12268
|
+
]);
|
|
12269
|
+
HarnessToggleKindSchema = exports_external.enum([
|
|
12270
|
+
"ccep_phase",
|
|
12271
|
+
"ccep_gates",
|
|
12272
|
+
"config_loop",
|
|
12273
|
+
"config_council",
|
|
12274
|
+
"product_graph"
|
|
12275
|
+
]);
|
|
12276
|
+
HarnessCatalogEntrySchema = exports_external.object({
|
|
12277
|
+
id: HarnessComponentIdSchema,
|
|
12278
|
+
layer: HarnessComponentLayerSchema,
|
|
12279
|
+
label: exports_external.string(),
|
|
12280
|
+
defaultOn: exports_external.boolean().default(true),
|
|
12281
|
+
toggle: exports_external.object({
|
|
12282
|
+
kind: HarnessToggleKindSchema,
|
|
12283
|
+
phaseId: exports_external.string().optional()
|
|
12284
|
+
})
|
|
12285
|
+
});
|
|
12286
|
+
HarnessCatalogSchema = exports_external.object({
|
|
12287
|
+
version: exports_external.literal(1),
|
|
12288
|
+
components: exports_external.array(HarnessCatalogEntrySchema).min(1)
|
|
12289
|
+
});
|
|
12290
|
+
HarnessOverlaySchema = exports_external.object({
|
|
12291
|
+
experimentId: exports_external.string().optional(),
|
|
12292
|
+
variantId: exports_external.string(),
|
|
12293
|
+
disabledComponents: exports_external.array(HarnessComponentIdSchema),
|
|
12294
|
+
disableProductGraph: exports_external.boolean().optional().default(false),
|
|
12295
|
+
contractVersion: exports_external.string().optional(),
|
|
12296
|
+
ccepProfile: exports_external.string().optional().default("feature")
|
|
12297
|
+
});
|
|
12298
|
+
HarnessExperimentSchema = exports_external.object({
|
|
12299
|
+
id: exports_external.string(),
|
|
12300
|
+
suiteId: exports_external.string(),
|
|
12301
|
+
createdAt: exports_external.string(),
|
|
12302
|
+
contractVersion: exports_external.string(),
|
|
12303
|
+
components: exports_external.array(HarnessComponentIdSchema),
|
|
12304
|
+
variants: exports_external.array(exports_external.string()).min(1),
|
|
12305
|
+
suiteTaskIds: exports_external.array(exports_external.string()),
|
|
12306
|
+
suitePath: exports_external.string().optional()
|
|
12307
|
+
});
|
|
12308
|
+
HarnessSuiteTaskSchema = exports_external.object({
|
|
12309
|
+
id: exports_external.string(),
|
|
12310
|
+
title: exports_external.string(),
|
|
12311
|
+
type: exports_external.enum(["feature", "fix", "refactor"]),
|
|
12312
|
+
risk: exports_external.enum(["low", "medium", "high"]).default("low"),
|
|
12313
|
+
prompt: exports_external.string(),
|
|
12314
|
+
scope: exports_external.string(),
|
|
12315
|
+
acceptanceCriteria: exports_external.array(exports_external.string()).min(1),
|
|
12316
|
+
testCommand: exports_external.string().optional()
|
|
12317
|
+
});
|
|
12318
|
+
HarnessSuiteSchema = exports_external.object({
|
|
12319
|
+
id: exports_external.string(),
|
|
12320
|
+
version: exports_external.number().int().positive(),
|
|
12321
|
+
fixtureDir: exports_external.string().optional(),
|
|
12322
|
+
tasks: exports_external.array(HarnessSuiteTaskSchema).min(1)
|
|
12199
12323
|
});
|
|
12200
12324
|
EvaluationIndexSchema = exports_external.object({
|
|
12201
12325
|
version: exports_external.literal(1),
|
|
@@ -12377,6 +12501,24 @@ var init_profiles = __esm(() => {
|
|
|
12377
12501
|
routing: { default: ["validate-backlog", "discover", "design", "test", "implement", "review"] },
|
|
12378
12502
|
confirmationGate: baseGate
|
|
12379
12503
|
},
|
|
12504
|
+
backlog: {
|
|
12505
|
+
id: "backlog",
|
|
12506
|
+
version: 1,
|
|
12507
|
+
command: "backlog",
|
|
12508
|
+
taskCard: {
|
|
12509
|
+
type: "feature",
|
|
12510
|
+
requiredFields: ["title", "type", "risk", "scope", "context", "acceptanceCriteria"]
|
|
12511
|
+
},
|
|
12512
|
+
phases: [
|
|
12513
|
+
{ id: "wayfinding", agent: "repo-explorer", outputSchema: "agent-output" },
|
|
12514
|
+
{ id: "intake", agent: "task-coach", outputSchema: "planner-output", stopGate: "confirmation" },
|
|
12515
|
+
{ id: "write-backlog", agent: "docs" },
|
|
12516
|
+
{ id: "validate-backlog", agent: "orchestrator", type: "cli-gate" },
|
|
12517
|
+
{ id: "plan-changes", agent: "orchestrator", type: "cli-gate" }
|
|
12518
|
+
],
|
|
12519
|
+
routing: { default: ["wayfinding", "intake", "write-backlog", "validate-backlog", "plan-changes"] },
|
|
12520
|
+
confirmationGate: baseGate
|
|
12521
|
+
},
|
|
12380
12522
|
scorecard: {
|
|
12381
12523
|
id: "scorecard",
|
|
12382
12524
|
version: 1,
|
|
@@ -12507,13 +12649,42 @@ var init_profiles = __esm(() => {
|
|
|
12507
12649
|
],
|
|
12508
12650
|
routing: { default: ["re-explain"] },
|
|
12509
12651
|
confirmationGate: { stopOnHighRisk: false, stopOnQuestions: true }
|
|
12652
|
+
},
|
|
12653
|
+
security: {
|
|
12654
|
+
id: "security",
|
|
12655
|
+
version: 1,
|
|
12656
|
+
command: "security",
|
|
12657
|
+
taskCard: {
|
|
12658
|
+
type: "fix",
|
|
12659
|
+
requiredFields: ["domain", "authorization", "risk", "scope"]
|
|
12660
|
+
},
|
|
12661
|
+
phases: [
|
|
12662
|
+
{ id: "wayfinding", agent: "repo-explorer", outputSchema: "agent-output" },
|
|
12663
|
+
{ id: "intake", agent: "task-coach", outputSchema: "planner-output", stopGate: "confirmation" },
|
|
12664
|
+
{ id: "design", agent: "architect", stopGate: "approval" },
|
|
12665
|
+
{ id: "test", agent: "tester" },
|
|
12666
|
+
{ id: "implement", agent: "implementer", dependsOn: ["test"] },
|
|
12667
|
+
{ id: "review", agent: "reviewer" }
|
|
12668
|
+
],
|
|
12669
|
+
routing: {
|
|
12670
|
+
default: ["wayfinding", "intake", "test", "implement"],
|
|
12671
|
+
riskRules: [
|
|
12672
|
+
{ when: { risk: "low" }, then: ["wayfinding", "intake", "test", "implement"] },
|
|
12673
|
+
{
|
|
12674
|
+
when: { risk: ["medium", "high"] },
|
|
12675
|
+
then: ["wayfinding", "intake", "design", "test", "implement", "review"]
|
|
12676
|
+
}
|
|
12677
|
+
]
|
|
12678
|
+
},
|
|
12679
|
+
confirmationGate: baseGate
|
|
12510
12680
|
}
|
|
12511
12681
|
};
|
|
12512
12682
|
});
|
|
12513
12683
|
|
|
12514
12684
|
// src/core/ccep/profile-yaml.ts
|
|
12515
12685
|
import { existsSync, readFileSync } from "node:fs";
|
|
12516
|
-
import { join } from "node:path";
|
|
12686
|
+
import { dirname as dirname2, join } from "node:path";
|
|
12687
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12517
12688
|
function workflowYamlPath(command, projectRoot) {
|
|
12518
12689
|
if (projectRoot) {
|
|
12519
12690
|
const projectPath = join(projectRoot, ".codeconductor", "workflows", `${command}.yml`);
|
|
@@ -12551,7 +12722,7 @@ var init_profile_yaml = __esm(() => {
|
|
|
12551
12722
|
init_dist();
|
|
12552
12723
|
init_schemas();
|
|
12553
12724
|
init_profiles();
|
|
12554
|
-
BUNDLED_WORKFLOWS_DIR = join(import.meta.
|
|
12725
|
+
BUNDLED_WORKFLOWS_DIR = join(dirname2(fileURLToPath2(import.meta.url)), "workflows");
|
|
12555
12726
|
});
|
|
12556
12727
|
|
|
12557
12728
|
// src/core/ccep/workflow-init.ts
|
|
@@ -13130,17 +13301,65 @@ function resolveAssets(target) {
|
|
|
13130
13301
|
}
|
|
13131
13302
|
var CURRENT_PRESET_VERSION = "v1.0.0";
|
|
13132
13303
|
|
|
13304
|
+
// src/core/openspec/openspec-gitignore.ts
|
|
13305
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
13306
|
+
import { homedir } from "node:os";
|
|
13307
|
+
import { resolve as resolve4 } from "node:path";
|
|
13308
|
+
function gitignoreHasEntry(content, entry) {
|
|
13309
|
+
return content.split(/\r?\n/).some((line) => line.trim() === entry);
|
|
13310
|
+
}
|
|
13311
|
+
async function ensureOpenspecGitignore(projectRoot) {
|
|
13312
|
+
if (projectRoot === homedir())
|
|
13313
|
+
return null;
|
|
13314
|
+
const gitignorePath = resolve4(projectRoot, ".gitignore");
|
|
13315
|
+
let existing = "";
|
|
13316
|
+
try {
|
|
13317
|
+
existing = await readFile2(gitignorePath, "utf-8");
|
|
13318
|
+
} catch {
|
|
13319
|
+
existing = "";
|
|
13320
|
+
}
|
|
13321
|
+
const missing = OPENSPEC_GITIGNORE_ENTRIES.filter((entry) => !gitignoreHasEntry(existing, entry));
|
|
13322
|
+
if (missing.length === 0)
|
|
13323
|
+
return null;
|
|
13324
|
+
const chunks = [];
|
|
13325
|
+
if (existing.length > 0 && !existing.endsWith(`
|
|
13326
|
+
`)) {
|
|
13327
|
+
chunks.push(`
|
|
13328
|
+
`);
|
|
13329
|
+
}
|
|
13330
|
+
if (!existing.includes(OPENSPEC_GITIGNORE_HEADER)) {
|
|
13331
|
+
if (existing.length > 0)
|
|
13332
|
+
chunks.push(`
|
|
13333
|
+
`);
|
|
13334
|
+
chunks.push(OPENSPEC_GITIGNORE_HEADER, `
|
|
13335
|
+
`);
|
|
13336
|
+
}
|
|
13337
|
+
chunks.push(missing.join(`
|
|
13338
|
+
`), `
|
|
13339
|
+
`);
|
|
13340
|
+
await writeFile2(gitignorePath, existing + chunks.join(""), "utf-8");
|
|
13341
|
+
return ".gitignore";
|
|
13342
|
+
}
|
|
13343
|
+
var OPENSPEC_GITIGNORE_HEADER = "# CodeConductor OpenSpec (local operational artifacts)", OPENSPEC_GITIGNORE_ENTRIES;
|
|
13344
|
+
var init_openspec_gitignore = __esm(() => {
|
|
13345
|
+
OPENSPEC_GITIGNORE_ENTRIES = [
|
|
13346
|
+
"BACKLOG.md",
|
|
13347
|
+
"openspec/",
|
|
13348
|
+
".codeconductor/openspec-state.json"
|
|
13349
|
+
];
|
|
13350
|
+
});
|
|
13351
|
+
|
|
13133
13352
|
// src/commands/init.command.ts
|
|
13134
13353
|
var exports_init_command = {};
|
|
13135
13354
|
__export(exports_init_command, {
|
|
13136
13355
|
initCommand: () => initCommand
|
|
13137
13356
|
});
|
|
13138
|
-
import { access as access4, mkdir as mkdir3, readFile as
|
|
13139
|
-
import { homedir } from "node:os";
|
|
13140
|
-
import { basename, resolve as
|
|
13357
|
+
import { access as access4, mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
13358
|
+
import { homedir as homedir2 } from "node:os";
|
|
13359
|
+
import { basename, resolve as resolve5 } from "node:path";
|
|
13141
13360
|
async function initCommand(options) {
|
|
13142
13361
|
const { projectRoot, dryRun, force, global: isGlobal, output, locale } = options;
|
|
13143
|
-
const baseDir = isGlobal ?
|
|
13362
|
+
const baseDir = isGlobal ? homedir2() : projectRoot;
|
|
13144
13363
|
try {
|
|
13145
13364
|
let profile = null;
|
|
13146
13365
|
if (!isGlobal) {
|
|
@@ -13217,6 +13436,7 @@ async function initCommand(options) {
|
|
|
13217
13436
|
}
|
|
13218
13437
|
const copiedPresets = await copyPresets(baseDir, presetsToCopy, force);
|
|
13219
13438
|
const openspecCreated = await initOpenspecArtifacts(baseDir, force);
|
|
13439
|
+
const gitignoreCreated = await ensureOpenspecGitignore(baseDir);
|
|
13220
13440
|
const evalCreated = await initEvaluationArtifacts(baseDir, force);
|
|
13221
13441
|
const workflowsCreated = await initWorkflowArtifacts(baseDir, force);
|
|
13222
13442
|
return {
|
|
@@ -13228,6 +13448,7 @@ async function initCommand(options) {
|
|
|
13228
13448
|
".codeconductor/config.yml",
|
|
13229
13449
|
...copiedPresets,
|
|
13230
13450
|
...openspecCreated,
|
|
13451
|
+
...gitignoreCreated ? [gitignoreCreated] : [],
|
|
13231
13452
|
...evalCreated,
|
|
13232
13453
|
...workflowsCreated
|
|
13233
13454
|
],
|
|
@@ -13256,7 +13477,7 @@ async function initCommand(options) {
|
|
|
13256
13477
|
}
|
|
13257
13478
|
async function resolvePresetsToCopy() {
|
|
13258
13479
|
const sources = [];
|
|
13259
|
-
const bundledCouncil =
|
|
13480
|
+
const bundledCouncil = resolve5(SRC_PRESETS_DIR, "council", "council.yml");
|
|
13260
13481
|
if (await fileExists3(bundledCouncil)) {
|
|
13261
13482
|
sources.push({ name: "council.yml", sourcePath: bundledCouncil });
|
|
13262
13483
|
}
|
|
@@ -13267,17 +13488,17 @@ async function resolvePresetsToCopy() {
|
|
|
13267
13488
|
return sources;
|
|
13268
13489
|
}
|
|
13269
13490
|
async function copyPresets(baseDir, presets, force) {
|
|
13270
|
-
const presetsDir =
|
|
13491
|
+
const presetsDir = resolve5(baseDir, ".codeconductor", "presets");
|
|
13271
13492
|
await mkdir3(presetsDir, { recursive: true });
|
|
13272
13493
|
const copied = [];
|
|
13273
13494
|
for (const preset of presets) {
|
|
13274
|
-
const destPath =
|
|
13495
|
+
const destPath = resolve5(presetsDir, preset.name);
|
|
13275
13496
|
if (!force && await fileExists3(destPath)) {
|
|
13276
13497
|
continue;
|
|
13277
13498
|
}
|
|
13278
13499
|
try {
|
|
13279
|
-
const content = await
|
|
13280
|
-
await
|
|
13500
|
+
const content = await readFile3(preset.sourcePath, "utf-8");
|
|
13501
|
+
await writeFile3(destPath, content, "utf-8");
|
|
13281
13502
|
copied.push(`.codeconductor/presets/${preset.name}`);
|
|
13282
13503
|
} catch (err2) {
|
|
13283
13504
|
const code = err2.code;
|
|
@@ -13298,21 +13519,21 @@ async function fileExists3(path) {
|
|
|
13298
13519
|
}
|
|
13299
13520
|
}
|
|
13300
13521
|
async function initOpenspecArtifacts(baseDir, force) {
|
|
13301
|
-
if (baseDir ===
|
|
13522
|
+
if (baseDir === homedir2())
|
|
13302
13523
|
return [];
|
|
13303
13524
|
const created = [];
|
|
13304
|
-
const backlogDest =
|
|
13305
|
-
const backlogTemplate =
|
|
13525
|
+
const backlogDest = resolve5(baseDir, "BACKLOG.md");
|
|
13526
|
+
const backlogTemplate = resolve5(ROOT_PRESETS_DIR, "templates", "BACKLOG.md");
|
|
13306
13527
|
if (force || !await fileExists3(backlogDest)) {
|
|
13307
13528
|
if (await fileExists3(backlogTemplate)) {
|
|
13308
|
-
const content = await
|
|
13309
|
-
await
|
|
13529
|
+
const content = await readFile3(backlogTemplate, "utf-8");
|
|
13530
|
+
await writeFile3(backlogDest, content, "utf-8");
|
|
13310
13531
|
created.push("BACKLOG.md");
|
|
13311
13532
|
}
|
|
13312
13533
|
}
|
|
13313
|
-
const stateDir =
|
|
13534
|
+
const stateDir = resolve5(baseDir, ".codeconductor");
|
|
13314
13535
|
await mkdir3(stateDir, { recursive: true });
|
|
13315
|
-
const stateDest =
|
|
13536
|
+
const stateDest = resolve5(stateDir, "openspec-state.json");
|
|
13316
13537
|
if (force || !await fileExists3(stateDest)) {
|
|
13317
13538
|
const initialState = JSON.stringify({
|
|
13318
13539
|
version: 1,
|
|
@@ -13320,34 +13541,34 @@ async function initOpenspecArtifacts(baseDir, force) {
|
|
|
13320
13541
|
changePaths: {},
|
|
13321
13542
|
itemSnapshots: {}
|
|
13322
13543
|
}, null, 2);
|
|
13323
|
-
await
|
|
13544
|
+
await writeFile3(stateDest, initialState, "utf-8");
|
|
13324
13545
|
created.push(".codeconductor/openspec-state.json");
|
|
13325
13546
|
}
|
|
13326
13547
|
return created;
|
|
13327
13548
|
}
|
|
13328
13549
|
async function initEvaluationArtifacts(baseDir, force) {
|
|
13329
|
-
if (baseDir ===
|
|
13550
|
+
if (baseDir === homedir2())
|
|
13330
13551
|
return [];
|
|
13331
13552
|
const created = [];
|
|
13332
|
-
const evalDir =
|
|
13553
|
+
const evalDir = resolve5(baseDir, ".codeconductor", "evaluation");
|
|
13333
13554
|
await mkdir3(evalDir, { recursive: true });
|
|
13334
|
-
await mkdir3(
|
|
13335
|
-
const indexPath =
|
|
13555
|
+
await mkdir3(resolve5(evalDir, "scorecards"), { recursive: true });
|
|
13556
|
+
const indexPath = resolve5(evalDir, "index.json");
|
|
13336
13557
|
if (force || !await fileExists3(indexPath)) {
|
|
13337
|
-
await
|
|
13558
|
+
await writeFile3(indexPath, JSON.stringify({ version: 1 }, null, 2), "utf-8");
|
|
13338
13559
|
created.push(".codeconductor/evaluation/index.json");
|
|
13339
13560
|
}
|
|
13340
|
-
const outcomesPath =
|
|
13561
|
+
const outcomesPath = resolve5(evalDir, "outcomes.jsonl");
|
|
13341
13562
|
if (force || !await fileExists3(outcomesPath)) {
|
|
13342
|
-
await
|
|
13563
|
+
await writeFile3(outcomesPath, "", "utf-8");
|
|
13343
13564
|
created.push(".codeconductor/evaluation/outcomes.jsonl");
|
|
13344
13565
|
}
|
|
13345
|
-
const profileDest =
|
|
13346
|
-
const profileTemplate =
|
|
13566
|
+
const profileDest = resolve5(evalDir, "execution-profile.yml");
|
|
13567
|
+
const profileTemplate = resolve5(ROOT_PRESETS_DIR, "templates", "execution-profile.yml");
|
|
13347
13568
|
if (force || !await fileExists3(profileDest)) {
|
|
13348
13569
|
if (await fileExists3(profileTemplate)) {
|
|
13349
|
-
const content = await
|
|
13350
|
-
await
|
|
13570
|
+
const content = await readFile3(profileTemplate, "utf-8");
|
|
13571
|
+
await writeFile3(profileDest, content, "utf-8");
|
|
13351
13572
|
created.push(".codeconductor/evaluation/execution-profile.yml");
|
|
13352
13573
|
}
|
|
13353
13574
|
}
|
|
@@ -13358,6 +13579,7 @@ var init_init_command = __esm(() => {
|
|
|
13358
13579
|
init_workflow_init();
|
|
13359
13580
|
init_project_detector();
|
|
13360
13581
|
init_package_paths();
|
|
13582
|
+
init_openspec_gitignore();
|
|
13361
13583
|
});
|
|
13362
13584
|
|
|
13363
13585
|
// src/commands/detect.command.ts
|
|
@@ -13670,12 +13892,12 @@ var init_opencode_lsp_generator = __esm(() => {
|
|
|
13670
13892
|
});
|
|
13671
13893
|
|
|
13672
13894
|
// src/core/config/config-loader.ts
|
|
13673
|
-
import { readFile as
|
|
13674
|
-
import { resolve as
|
|
13895
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
13896
|
+
import { resolve as resolve6 } from "node:path";
|
|
13675
13897
|
async function loadConfig(projectRoot) {
|
|
13676
13898
|
try {
|
|
13677
|
-
const configPath =
|
|
13678
|
-
const content = await
|
|
13899
|
+
const configPath = resolve6(projectRoot, CONFIG_FILE2);
|
|
13900
|
+
const content = await readFile4(configPath, "utf-8");
|
|
13679
13901
|
const data = $parse(content);
|
|
13680
13902
|
if (!data) {
|
|
13681
13903
|
return err(new ValidationError("Empty config file"));
|
|
@@ -13694,8 +13916,8 @@ async function loadConfig(projectRoot) {
|
|
|
13694
13916
|
}
|
|
13695
13917
|
async function configExists(projectRoot) {
|
|
13696
13918
|
try {
|
|
13697
|
-
const configPath =
|
|
13698
|
-
await
|
|
13919
|
+
const configPath = resolve6(projectRoot, CONFIG_FILE2);
|
|
13920
|
+
await readFile4(configPath, "utf-8");
|
|
13699
13921
|
return true;
|
|
13700
13922
|
} catch {
|
|
13701
13923
|
return false;
|
|
@@ -13711,7 +13933,7 @@ var init_config_loader = __esm(() => {
|
|
|
13711
13933
|
// src/core/filesystem/path-containment.ts
|
|
13712
13934
|
import { constants as constants2 } from "node:fs";
|
|
13713
13935
|
import { lstat, mkdir as mkdir4, open, realpath } from "node:fs/promises";
|
|
13714
|
-
import { basename as basename2, dirname as
|
|
13936
|
+
import { basename as basename2, dirname as dirname3, isAbsolute, join as join3, relative, resolve as resolve7, sep as sep2 } from "node:path";
|
|
13715
13937
|
function isInside(root, target) {
|
|
13716
13938
|
const rel = relative(root, target);
|
|
13717
13939
|
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute(rel);
|
|
@@ -13730,8 +13952,8 @@ async function resolveWithinRoot(projectRoot, relPath) {
|
|
|
13730
13952
|
if (isAbsolute(relPath)) {
|
|
13731
13953
|
return;
|
|
13732
13954
|
}
|
|
13733
|
-
const rootAbs =
|
|
13734
|
-
const candidate =
|
|
13955
|
+
const rootAbs = resolve7(projectRoot);
|
|
13956
|
+
const candidate = resolve7(rootAbs, relPath);
|
|
13735
13957
|
if (!isInside(rootAbs, candidate)) {
|
|
13736
13958
|
return;
|
|
13737
13959
|
}
|
|
@@ -13747,7 +13969,7 @@ async function readFileWithinRoot(projectRoot, relPath) {
|
|
|
13747
13969
|
if (candidateReal === undefined) {
|
|
13748
13970
|
return;
|
|
13749
13971
|
}
|
|
13750
|
-
const rootReal = await realpath(
|
|
13972
|
+
const rootReal = await realpath(resolve7(projectRoot));
|
|
13751
13973
|
let handle;
|
|
13752
13974
|
try {
|
|
13753
13975
|
handle = await open(candidateReal, constants2.O_RDONLY | O_NOFOLLOW);
|
|
@@ -13782,7 +14004,7 @@ async function canonicalizeExistingPrefix(target) {
|
|
|
13782
14004
|
if (error.code !== "ENOENT") {
|
|
13783
14005
|
throw error;
|
|
13784
14006
|
}
|
|
13785
|
-
const parent =
|
|
14007
|
+
const parent = dirname3(current);
|
|
13786
14008
|
if (parent === current) {
|
|
13787
14009
|
throw error;
|
|
13788
14010
|
}
|
|
@@ -13795,8 +14017,8 @@ async function resolveOutputWithinRoot(projectRoot, relPath) {
|
|
|
13795
14017
|
if (relPath === "" || isAbsolute(relPath)) {
|
|
13796
14018
|
return;
|
|
13797
14019
|
}
|
|
13798
|
-
const rootAbs =
|
|
13799
|
-
const candidate =
|
|
14020
|
+
const rootAbs = resolve7(projectRoot);
|
|
14021
|
+
const candidate = resolve7(rootAbs, relPath);
|
|
13800
14022
|
if (!isInside(rootAbs, candidate)) {
|
|
13801
14023
|
return;
|
|
13802
14024
|
}
|
|
@@ -13879,12 +14101,12 @@ async function writeContainedFile(projectRoot, relPath, content, options) {
|
|
|
13879
14101
|
if (resolved === undefined) {
|
|
13880
14102
|
throw new OutputPathError(`Output path is not an allowed destination: ${relPath}`);
|
|
13881
14103
|
}
|
|
13882
|
-
await mkdir4(
|
|
14104
|
+
await mkdir4(dirname3(resolved), { recursive: true });
|
|
13883
14105
|
const outputPath = await resolveOutputWithinRoot(projectRoot, relPath);
|
|
13884
14106
|
if (outputPath === undefined) {
|
|
13885
14107
|
throw new OutputPathError(`Output path is not an allowed destination: ${relPath}`);
|
|
13886
14108
|
}
|
|
13887
|
-
const rootReal = await realpath(
|
|
14109
|
+
const rootReal = await realpath(resolve7(projectRoot));
|
|
13888
14110
|
let handle;
|
|
13889
14111
|
try {
|
|
13890
14112
|
handle = await openOutputHandle(outputPath, force);
|
|
@@ -14035,7 +14257,7 @@ var init_file_writer = __esm(() => {
|
|
|
14035
14257
|
|
|
14036
14258
|
// src/core/lsp/binary-integrity.ts
|
|
14037
14259
|
import { createHash } from "node:crypto";
|
|
14038
|
-
import { isAbsolute as isAbsolute2, resolve as
|
|
14260
|
+
import { isAbsolute as isAbsolute2, resolve as resolve8, sep as sep3 } from "node:path";
|
|
14039
14261
|
function assertPinnedBinaryUrl(url) {
|
|
14040
14262
|
let parsed;
|
|
14041
14263
|
try {
|
|
@@ -14071,8 +14293,8 @@ function resolveSafeArchiveEntry(destRoot, entry) {
|
|
|
14071
14293
|
if (normalized === "" || isAbsolute2(normalized) || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized) || normalized.split("/").includes("..")) {
|
|
14072
14294
|
throw new Error(`Unsafe archive entry rejected: ${entry}`);
|
|
14073
14295
|
}
|
|
14074
|
-
const root =
|
|
14075
|
-
const target =
|
|
14296
|
+
const root = resolve8(destRoot);
|
|
14297
|
+
const target = resolve8(root, normalized);
|
|
14076
14298
|
const rel = target.slice(root.length);
|
|
14077
14299
|
if (target !== root && (rel === "" || !rel.startsWith(sep3))) {
|
|
14078
14300
|
throw new Error(`Unsafe archive entry rejected: ${entry}`);
|
|
@@ -14322,7 +14544,7 @@ function parseContentLength(raw, url) {
|
|
|
14322
14544
|
return declared;
|
|
14323
14545
|
}
|
|
14324
14546
|
function performRequest(target, remainingMs, originalUrl, totalTimeout, followRedirects) {
|
|
14325
|
-
return new Promise((
|
|
14547
|
+
return new Promise((resolve9, reject) => {
|
|
14326
14548
|
const isHttps = target.url.protocol === "https:";
|
|
14327
14549
|
const port = target.url.port !== "" ? Number(target.url.port) : isHttps ? 443 : 80;
|
|
14328
14550
|
let timer;
|
|
@@ -14341,7 +14563,7 @@ function performRequest(target, remainingMs, originalUrl, totalTimeout, followRe
|
|
|
14341
14563
|
return;
|
|
14342
14564
|
settled = true;
|
|
14343
14565
|
clearTimeout(timer);
|
|
14344
|
-
|
|
14566
|
+
resolve9(response);
|
|
14345
14567
|
};
|
|
14346
14568
|
timer = setTimeout(() => {
|
|
14347
14569
|
fail(new SafeFetchError(`Request timed out after ${totalTimeout}ms: ${originalUrl}`));
|
|
@@ -14445,7 +14667,7 @@ async function safeFetchWithInternals(urlString, options = {}, internals = {}) {
|
|
|
14445
14667
|
}
|
|
14446
14668
|
}
|
|
14447
14669
|
async function delay(ms) {
|
|
14448
|
-
return new Promise((
|
|
14670
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
14449
14671
|
}
|
|
14450
14672
|
var MAX_BODY_BYTES, MAX_REDIRECTS = 5, USER_AGENT = "CodeConductor-SEO/0.3.0", ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", SafeFetchError, BLOCKED_IPV4_CIDRS, BLOCKED_IPV4;
|
|
14451
14673
|
var init_safe_fetch_internal = __esm(() => {
|
|
@@ -14664,8 +14886,8 @@ var init_binary_downloader = __esm(() => {
|
|
|
14664
14886
|
|
|
14665
14887
|
// src/core/lsp/lsp-installer.ts
|
|
14666
14888
|
import { execFile } from "node:child_process";
|
|
14667
|
-
import { access as access6, mkdir as mkdir5, mkdtemp, rename, rm, writeFile as
|
|
14668
|
-
import { homedir as
|
|
14889
|
+
import { access as access6, mkdir as mkdir5, mkdtemp, rename, rm, writeFile as writeFile4 } from "node:fs/promises";
|
|
14890
|
+
import { homedir as homedir3, tmpdir } from "node:os";
|
|
14669
14891
|
import { join as join4 } from "node:path";
|
|
14670
14892
|
import { promisify } from "node:util";
|
|
14671
14893
|
async function assertTarArchiveSafe(archivePath, destRoot) {
|
|
@@ -14687,7 +14909,7 @@ async function extractHardenedTar(archivePath, destRoot) {
|
|
|
14687
14909
|
class LspInstaller {
|
|
14688
14910
|
lspBinDir;
|
|
14689
14911
|
constructor() {
|
|
14690
|
-
this.lspBinDir = join4(
|
|
14912
|
+
this.lspBinDir = join4(homedir3(), ".codeconductor", "lsp", "bin");
|
|
14691
14913
|
}
|
|
14692
14914
|
async checkInstalled(def) {
|
|
14693
14915
|
try {
|
|
@@ -14757,11 +14979,9 @@ class LspInstaller {
|
|
|
14757
14979
|
const results = [];
|
|
14758
14980
|
for (const lsp of lsps) {
|
|
14759
14981
|
if (options.dryRun) {
|
|
14760
|
-
const status = await this.checkInstalled(lsp);
|
|
14761
14982
|
results.push({
|
|
14762
14983
|
lspId: lsp.id,
|
|
14763
|
-
status:
|
|
14764
|
-
version: status.version
|
|
14984
|
+
status: "installed"
|
|
14765
14985
|
});
|
|
14766
14986
|
} else {
|
|
14767
14987
|
const result = await this.installLsp(lsp);
|
|
@@ -14833,7 +15053,7 @@ class LspInstaller {
|
|
|
14833
15053
|
try {
|
|
14834
15054
|
const archiveName = binary.url.endsWith(".zip") ? "server.zip" : "server.tar.gz";
|
|
14835
15055
|
const archivePath = join4(workDir, archiveName);
|
|
14836
|
-
await
|
|
15056
|
+
await writeFile4(archivePath, bytes);
|
|
14837
15057
|
if (archiveName.endsWith(".zip")) {
|
|
14838
15058
|
throw new Error(`Zip binary installs are not supported yet for ${def.serverName}; use a .tar.gz artifact`);
|
|
14839
15059
|
}
|
|
@@ -14976,11 +15196,11 @@ var exports_install_lsp_command = {};
|
|
|
14976
15196
|
__export(exports_install_lsp_command, {
|
|
14977
15197
|
installLspCommand: () => installLspCommand
|
|
14978
15198
|
});
|
|
14979
|
-
import { homedir as
|
|
14980
|
-
import { resolve as
|
|
15199
|
+
import { homedir as homedir4 } from "node:os";
|
|
15200
|
+
import { resolve as resolve9 } from "node:path";
|
|
14981
15201
|
async function installLspCommand(options) {
|
|
14982
15202
|
const { target, lang, dryRun, force, global: isGlobal, output, projectRoot } = options;
|
|
14983
|
-
const baseDir = isGlobal ?
|
|
15203
|
+
const baseDir = isGlobal ? homedir4() : projectRoot;
|
|
14984
15204
|
try {
|
|
14985
15205
|
const runnerTarget = parseRunnerTarget(target);
|
|
14986
15206
|
const targets = getIndividualTargets(runnerTarget);
|
|
@@ -15028,7 +15248,7 @@ async function installLspCommand(options) {
|
|
|
15028
15248
|
const generatedFiles = generator.generate(installReport.results);
|
|
15029
15249
|
const resolvedFiles = generatedFiles.map((f) => ({
|
|
15030
15250
|
...f,
|
|
15031
|
-
path:
|
|
15251
|
+
path: resolve9(baseDir, f.path)
|
|
15032
15252
|
}));
|
|
15033
15253
|
const results = await writeGeneratedFiles(resolvedFiles, {
|
|
15034
15254
|
...writeOptions,
|
|
@@ -15919,18 +16139,18 @@ function countOccurrences(content, needle) {
|
|
|
15919
16139
|
var MANAGED_BEGIN_MARKER = "<!-- CODECONDUCTOR:BEGIN managed -->", MANAGED_END_MARKER = "<!-- CODECONDUCTOR:END managed -->";
|
|
15920
16140
|
|
|
15921
16141
|
// src/core/presets/complementary-detector.ts
|
|
15922
|
-
import {
|
|
16142
|
+
import { execFileSync } from "node:child_process";
|
|
15923
16143
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
15924
16144
|
import { join as join5 } from "node:path";
|
|
15925
|
-
import { homedir as
|
|
16145
|
+
import { homedir as homedir5 } from "node:os";
|
|
15926
16146
|
function detectComplementaryTools() {
|
|
15927
16147
|
if (cachedStatus) {
|
|
15928
16148
|
return cachedStatus;
|
|
15929
16149
|
}
|
|
15930
16150
|
const isCmdAvailable = (cmd) => {
|
|
15931
16151
|
try {
|
|
15932
|
-
const
|
|
15933
|
-
|
|
16152
|
+
const binary = process.platform === "win32" ? "where" : "which";
|
|
16153
|
+
execFileSync(binary, [cmd], { stdio: "ignore", timeout: 1000 });
|
|
15934
16154
|
return true;
|
|
15935
16155
|
} catch {
|
|
15936
16156
|
return false;
|
|
@@ -15938,7 +16158,7 @@ function detectComplementaryTools() {
|
|
|
15938
16158
|
};
|
|
15939
16159
|
const hasCaveman = () => {
|
|
15940
16160
|
try {
|
|
15941
|
-
const globalSettingsPath = join5(
|
|
16161
|
+
const globalSettingsPath = join5(homedir5(), ".claude", "settings.json");
|
|
15942
16162
|
if (existsSync2(globalSettingsPath)) {
|
|
15943
16163
|
const content = readFileSync2(globalSettingsPath, "utf8");
|
|
15944
16164
|
const settings = JSON.parse(content);
|
|
@@ -15973,9 +16193,9 @@ var cachedStatus = null;
|
|
|
15973
16193
|
var init_complementary_detector = () => {};
|
|
15974
16194
|
|
|
15975
16195
|
// src/core/presets/file-copier.ts
|
|
15976
|
-
import { lstat as lstat2, readdir as readdir2, readFile as
|
|
15977
|
-
import { homedir as
|
|
15978
|
-
import { join as join6, relative as relative3, resolve as
|
|
16196
|
+
import { lstat as lstat2, readdir as readdir2, readFile as readFile5, stat } from "node:fs/promises";
|
|
16197
|
+
import { homedir as homedir6 } from "node:os";
|
|
16198
|
+
import { join as join6, relative as relative3, resolve as resolve10 } from "node:path";
|
|
15979
16199
|
function isMaintainerReservedDest(baseDir, destPath) {
|
|
15980
16200
|
const rel = relative3(baseDir, destPath).replace(/\\/g, "/");
|
|
15981
16201
|
if (MAINTAINER_RESERVED_EXACT.has(rel))
|
|
@@ -16004,17 +16224,17 @@ async function fileExists4(path) {
|
|
|
16004
16224
|
}
|
|
16005
16225
|
}
|
|
16006
16226
|
async function resolveEntryFiles(entry, presetsDir, baseDir) {
|
|
16007
|
-
const srcAbsolute =
|
|
16227
|
+
const srcAbsolute = resolve10(presetsDir, entry.src);
|
|
16008
16228
|
try {
|
|
16009
16229
|
const s = await stat(srcAbsolute);
|
|
16010
16230
|
if (s.isDirectory()) {
|
|
16011
16231
|
const files = await listFilesRecursive(srcAbsolute);
|
|
16012
16232
|
return files.map((f) => ({
|
|
16013
16233
|
src: join6(srcAbsolute, f),
|
|
16014
|
-
dest:
|
|
16234
|
+
dest: resolve10(baseDir, entry.dest, f)
|
|
16015
16235
|
}));
|
|
16016
16236
|
}
|
|
16017
|
-
return [{ src: srcAbsolute, dest:
|
|
16237
|
+
return [{ src: srcAbsolute, dest: resolve10(baseDir, entry.dest) }];
|
|
16018
16238
|
} catch {
|
|
16019
16239
|
return [];
|
|
16020
16240
|
}
|
|
@@ -16282,7 +16502,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
|
|
|
16282
16502
|
}
|
|
16283
16503
|
let content;
|
|
16284
16504
|
try {
|
|
16285
|
-
content = await
|
|
16505
|
+
content = await readFile5(srcPath, "utf-8");
|
|
16286
16506
|
} catch (e) {
|
|
16287
16507
|
return { src: srcPath, dest: destPath, action: "error", error: `Cannot read source: ${e}` };
|
|
16288
16508
|
}
|
|
@@ -16293,7 +16513,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
|
|
|
16293
16513
|
if (strategy === "append") {
|
|
16294
16514
|
let existing = "";
|
|
16295
16515
|
try {
|
|
16296
|
-
existing = await
|
|
16516
|
+
existing = await readFile5(destPath, "utf-8");
|
|
16297
16517
|
} catch {}
|
|
16298
16518
|
if (existing) {
|
|
16299
16519
|
finalContent = existing + `
|
|
@@ -16306,7 +16526,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
|
|
|
16306
16526
|
} else if (strategy === "merge-json") {
|
|
16307
16527
|
let existing = {};
|
|
16308
16528
|
try {
|
|
16309
|
-
existing = JSON.parse(await
|
|
16529
|
+
existing = JSON.parse(await readFile5(destPath, "utf-8"));
|
|
16310
16530
|
} catch {}
|
|
16311
16531
|
try {
|
|
16312
16532
|
const incoming = JSON.parse(incomingContent);
|
|
@@ -16318,7 +16538,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
|
|
|
16318
16538
|
} else if (strategy === "merge-managed") {
|
|
16319
16539
|
let existing = null;
|
|
16320
16540
|
try {
|
|
16321
|
-
existing = await
|
|
16541
|
+
existing = await readFile5(destPath, "utf-8");
|
|
16322
16542
|
} catch {}
|
|
16323
16543
|
const markerless = existing !== null && !existing.includes(MANAGED_BEGIN_MARKER) && !existing.includes(MANAGED_END_MARKER);
|
|
16324
16544
|
if (markerless && !force) {
|
|
@@ -16359,7 +16579,7 @@ async function copyFromManifest(manifest, presetsDir, baseDir, isGlobal, dryRun,
|
|
|
16359
16579
|
let resolvedBaseDir = baseDir;
|
|
16360
16580
|
let resolvedEntry = entry;
|
|
16361
16581
|
if (manifest.target === "agy" && isGlobal) {
|
|
16362
|
-
resolvedBaseDir = join6(
|
|
16582
|
+
resolvedBaseDir = join6(homedir6(), ".gemini", "config");
|
|
16363
16583
|
resolvedEntry = {
|
|
16364
16584
|
...entry,
|
|
16365
16585
|
dest: entry.dest.replace(/^\.agents\/?/, "")
|
|
@@ -16391,17 +16611,17 @@ var init_file_copier = __esm(() => {
|
|
|
16391
16611
|
});
|
|
16392
16612
|
|
|
16393
16613
|
// src/core/presets/manifest-loader.ts
|
|
16394
|
-
import { readFile as
|
|
16614
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
16395
16615
|
import { join as join7 } from "node:path";
|
|
16396
16616
|
async function loadManifest(target) {
|
|
16397
16617
|
const manifestPath = join7(MANIFESTS_DIR, `${target}.yml`);
|
|
16398
|
-
const content = await
|
|
16618
|
+
const content = await readFile6(manifestPath, "utf-8");
|
|
16399
16619
|
const data = $parse(content);
|
|
16400
16620
|
return InstallManifestSchema.parse(data);
|
|
16401
16621
|
}
|
|
16402
16622
|
async function loadModelConfig(target) {
|
|
16403
16623
|
const modelPath = join7(MODELS_DIR, `${target}.yml`);
|
|
16404
|
-
const content = await
|
|
16624
|
+
const content = await readFile6(modelPath, "utf-8");
|
|
16405
16625
|
const data = $parse(content);
|
|
16406
16626
|
return ModelConfigSchema.parse(data);
|
|
16407
16627
|
}
|
|
@@ -16421,16 +16641,16 @@ __export(exports_preset_loader, {
|
|
|
16421
16641
|
loadPreset: () => loadPreset,
|
|
16422
16642
|
loadCouncilPreset: () => loadCouncilPreset
|
|
16423
16643
|
});
|
|
16424
|
-
import { readFile as
|
|
16425
|
-
import { resolve as
|
|
16644
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
16645
|
+
import { resolve as resolve11 } from "node:path";
|
|
16426
16646
|
async function loadPreset(name, projectRoot = process.cwd()) {
|
|
16427
16647
|
const candidates = [
|
|
16428
|
-
|
|
16429
|
-
|
|
16648
|
+
resolve11(projectRoot, ".codeconductor", "presets", `${name}.yml`),
|
|
16649
|
+
resolve11(SRC_PRESETS_DIR, name, `${name}.yml`)
|
|
16430
16650
|
];
|
|
16431
16651
|
for (const presetPath of candidates) {
|
|
16432
16652
|
try {
|
|
16433
|
-
const content = await
|
|
16653
|
+
const content = await readFile7(presetPath, "utf-8");
|
|
16434
16654
|
const data = $parse(content);
|
|
16435
16655
|
if (!data)
|
|
16436
16656
|
continue;
|
|
@@ -16464,11 +16684,11 @@ __export(exports_install_command, {
|
|
|
16464
16684
|
installPresetCommand: () => installPresetCommand,
|
|
16465
16685
|
installCommand: () => installCommand
|
|
16466
16686
|
});
|
|
16467
|
-
import { homedir as
|
|
16468
|
-
import { resolve as
|
|
16687
|
+
import { homedir as homedir7 } from "node:os";
|
|
16688
|
+
import { resolve as resolve12 } from "node:path";
|
|
16469
16689
|
async function installCommand(options) {
|
|
16470
16690
|
const { target, dryRun, force, global: isGlobal, output, projectRoot } = options;
|
|
16471
|
-
const baseDir = isGlobal ?
|
|
16691
|
+
const baseDir = isGlobal ? homedir7() : projectRoot;
|
|
16472
16692
|
try {
|
|
16473
16693
|
const runnerTarget = parseRunnerTarget(target);
|
|
16474
16694
|
const targets = getIndividualTargets(runnerTarget);
|
|
@@ -16509,10 +16729,10 @@ async function installCommand(options) {
|
|
|
16509
16729
|
}
|
|
16510
16730
|
const generatedFiles = await installer.generate();
|
|
16511
16731
|
const isAgyGlobal = (t === "agy" || t === "gemini") && isGlobal;
|
|
16512
|
-
const targetBase = isAgyGlobal ?
|
|
16732
|
+
const targetBase = isAgyGlobal ? resolve12(homedir7(), ".gemini", "config") : baseDir;
|
|
16513
16733
|
const resolvedFiles = generatedFiles.map((f) => ({
|
|
16514
16734
|
...f,
|
|
16515
|
-
path:
|
|
16735
|
+
path: resolve12(targetBase, isAgyGlobal ? f.path.replace(/^\.agents\/?/, "") : f.path)
|
|
16516
16736
|
}));
|
|
16517
16737
|
const results = await writeGeneratedFiles(resolvedFiles, {
|
|
16518
16738
|
...writeOptions,
|
|
@@ -16574,7 +16794,7 @@ async function installCommand(options) {
|
|
|
16574
16794
|
}
|
|
16575
16795
|
async function installPresetCommand(options) {
|
|
16576
16796
|
const { target, dryRun, force, global: isGlobal, projectRoot } = options;
|
|
16577
|
-
const baseDir = isGlobal ?
|
|
16797
|
+
const baseDir = isGlobal ? homedir7() : projectRoot;
|
|
16578
16798
|
try {
|
|
16579
16799
|
const runnerTarget = parseRunnerTarget(target);
|
|
16580
16800
|
const targets = getIndividualTargets(runnerTarget);
|
|
@@ -16637,9 +16857,9 @@ var init_install_command = __esm(() => {
|
|
|
16637
16857
|
});
|
|
16638
16858
|
|
|
16639
16859
|
// src/core/security/target-compatibility.ts
|
|
16640
|
-
import { readFile as
|
|
16860
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
16641
16861
|
async function loadTargetSecurityCompatibility() {
|
|
16642
|
-
const policy = $parse(await
|
|
16862
|
+
const policy = $parse(await readFile8(POLICY_PATH, "utf-8"));
|
|
16643
16863
|
const targets = [
|
|
16644
16864
|
"opencode",
|
|
16645
16865
|
"claude",
|
|
@@ -16673,14 +16893,14 @@ var init_target_compatibility = __esm(() => {
|
|
|
16673
16893
|
});
|
|
16674
16894
|
|
|
16675
16895
|
// src/core/presets/update-checker.ts
|
|
16676
|
-
import { stat as stat2, readFile as
|
|
16677
|
-
import { resolve as
|
|
16678
|
-
import { homedir as
|
|
16896
|
+
import { stat as stat2, readFile as readFile9 } from "node:fs/promises";
|
|
16897
|
+
import { resolve as resolve13, join as join8 } from "node:path";
|
|
16898
|
+
import { homedir as homedir8 } from "node:os";
|
|
16679
16899
|
function getTargetInstallationPath(target, basePath, isGlobal) {
|
|
16680
16900
|
if (target === "agy") {
|
|
16681
|
-
return isGlobal ?
|
|
16901
|
+
return isGlobal ? resolve13(basePath, ".gemini", "config") : resolve13(basePath, ".agents");
|
|
16682
16902
|
}
|
|
16683
|
-
return
|
|
16903
|
+
return resolve13(basePath, `.${target}`);
|
|
16684
16904
|
}
|
|
16685
16905
|
async function isTargetInstalled(target, basePath, isGlobal) {
|
|
16686
16906
|
const path = getTargetInstallationPath(target, basePath, isGlobal);
|
|
@@ -16693,12 +16913,12 @@ async function isTargetInstalled(target, basePath, isGlobal) {
|
|
|
16693
16913
|
}
|
|
16694
16914
|
async function validateAgentFileSizes(basePath, isGlobal) {
|
|
16695
16915
|
const filesToCheck = [];
|
|
16696
|
-
filesToCheck.push(
|
|
16697
|
-
filesToCheck.push(
|
|
16916
|
+
filesToCheck.push(resolve13(basePath, ".claude", "CLAUDE.md"));
|
|
16917
|
+
filesToCheck.push(resolve13(basePath, ".codex", "AGENTS.md"));
|
|
16698
16918
|
if (isGlobal) {
|
|
16699
|
-
filesToCheck.push(
|
|
16919
|
+
filesToCheck.push(resolve13(basePath, ".gemini", "config", "AGENTS.md"));
|
|
16700
16920
|
} else {
|
|
16701
|
-
filesToCheck.push(
|
|
16921
|
+
filesToCheck.push(resolve13(basePath, ".agents", "AGENTS.md"));
|
|
16702
16922
|
}
|
|
16703
16923
|
const largeFiles = [];
|
|
16704
16924
|
for (const filePath of filesToCheck) {
|
|
@@ -16740,7 +16960,7 @@ async function validateAgentMarkers(basePath, isGlobal) {
|
|
|
16740
16960
|
let resolvedEntry = entry;
|
|
16741
16961
|
let targetBaseDir = basePath;
|
|
16742
16962
|
if (target === "agy" && isGlobal) {
|
|
16743
|
-
targetBaseDir = join8(
|
|
16963
|
+
targetBaseDir = join8(homedir8(), ".gemini", "config");
|
|
16744
16964
|
resolvedEntry = {
|
|
16745
16965
|
...entry,
|
|
16746
16966
|
dest: entry.dest.replace(/^\.agents\/?/, "")
|
|
@@ -16749,7 +16969,7 @@ async function validateAgentMarkers(basePath, isGlobal) {
|
|
|
16749
16969
|
const files = await resolveEntryFiles(resolvedEntry, PRESETS_DIR, targetBaseDir);
|
|
16750
16970
|
for (const { dest } of files) {
|
|
16751
16971
|
try {
|
|
16752
|
-
const content = await
|
|
16972
|
+
const content = await readFile9(dest, "utf-8");
|
|
16753
16973
|
const beginCount = content.split(MANAGED_BEGIN_MARKER).length - 1;
|
|
16754
16974
|
const endCount = content.split(MANAGED_END_MARKER).length - 1;
|
|
16755
16975
|
if (beginCount === 0 && endCount === 0) {
|
|
@@ -16773,8 +16993,8 @@ async function validateAgentMarkers(basePath, isGlobal) {
|
|
|
16773
16993
|
}
|
|
16774
16994
|
async function fileContentDiffers(pathA, pathB) {
|
|
16775
16995
|
try {
|
|
16776
|
-
const contentA = await
|
|
16777
|
-
const contentB = await
|
|
16996
|
+
const contentA = await readFile9(pathA, "utf-8");
|
|
16997
|
+
const contentB = await readFile9(pathB, "utf-8");
|
|
16778
16998
|
return contentA.trim() !== contentB.trim();
|
|
16779
16999
|
} catch {
|
|
16780
17000
|
try {
|
|
@@ -16787,12 +17007,12 @@ async function fileContentDiffers(pathA, pathB) {
|
|
|
16787
17007
|
}
|
|
16788
17008
|
async function loadSkillsLock(basePath) {
|
|
16789
17009
|
const paths = [
|
|
16790
|
-
|
|
16791
|
-
|
|
17010
|
+
resolve13(basePath, ".codeconductor", "skills-lock.json"),
|
|
17011
|
+
resolve13(basePath, ".agents", "skills-lock.json")
|
|
16792
17012
|
];
|
|
16793
17013
|
for (const p of paths) {
|
|
16794
17014
|
try {
|
|
16795
|
-
const content = await
|
|
17015
|
+
const content = await readFile9(p, "utf-8");
|
|
16796
17016
|
return JSON.parse(content);
|
|
16797
17017
|
} catch {}
|
|
16798
17018
|
}
|
|
@@ -16808,9 +17028,9 @@ async function getLatestSkillVersion(skillId) {
|
|
|
16808
17028
|
"cursor"
|
|
16809
17029
|
];
|
|
16810
17030
|
for (const target of targets) {
|
|
16811
|
-
const skillPath =
|
|
17031
|
+
const skillPath = resolve13(ROOT_PRESETS_DIR, target, "skills", skillId, "SKILL.md");
|
|
16812
17032
|
try {
|
|
16813
|
-
const content = await
|
|
17033
|
+
const content = await readFile9(skillPath, "utf-8");
|
|
16814
17034
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
16815
17035
|
if (fmMatch) {
|
|
16816
17036
|
const parsed = $parse(fmMatch[1]);
|
|
@@ -16823,10 +17043,10 @@ async function getLatestSkillVersion(skillId) {
|
|
|
16823
17043
|
return null;
|
|
16824
17044
|
}
|
|
16825
17045
|
async function checkUpdates(basePath, isGlobal) {
|
|
16826
|
-
const localCouncil =
|
|
16827
|
-
const bundledCouncil =
|
|
17046
|
+
const localCouncil = resolve13(basePath, ".codeconductor", "presets", "council.yml");
|
|
17047
|
+
const bundledCouncil = resolve13(SRC_PRESETS_DIR, "council", "council.yml");
|
|
16828
17048
|
const councilHasUpdate = await fileContentDiffers(localCouncil, bundledCouncil);
|
|
16829
|
-
const localPolicy =
|
|
17049
|
+
const localPolicy = resolve13(basePath, ".codeconductor", "presets", "policy.yml");
|
|
16830
17050
|
const policyHasUpdate = await fileContentDiffers(localPolicy, POLICY_PATH);
|
|
16831
17051
|
const targetsToCheck = [
|
|
16832
17052
|
"opencode",
|
|
@@ -16865,7 +17085,7 @@ async function checkUpdates(basePath, isGlobal) {
|
|
|
16865
17085
|
let resolvedEntry = entry;
|
|
16866
17086
|
let targetBaseDir = basePath;
|
|
16867
17087
|
if (target === "agy" && isGlobal) {
|
|
16868
|
-
targetBaseDir = join8(
|
|
17088
|
+
targetBaseDir = join8(homedir8(), ".gemini", "config");
|
|
16869
17089
|
resolvedEntry = {
|
|
16870
17090
|
...entry,
|
|
16871
17091
|
dest: entry.dest.replace(/^\.agents\/?/, "")
|
|
@@ -16876,14 +17096,14 @@ async function checkUpdates(basePath, isGlobal) {
|
|
|
16876
17096
|
for (const { src, dest } of files) {
|
|
16877
17097
|
let expectedContent;
|
|
16878
17098
|
try {
|
|
16879
|
-
const srcContent = await
|
|
17099
|
+
const srcContent = await readFile9(src, "utf-8");
|
|
16880
17100
|
expectedContent = isTemplate && modelConfig ? renderTemplate(srcContent, modelConfig, src, locale) : srcContent;
|
|
16881
17101
|
} catch {
|
|
16882
17102
|
continue;
|
|
16883
17103
|
}
|
|
16884
17104
|
let destContent;
|
|
16885
17105
|
try {
|
|
16886
|
-
destContent = await
|
|
17106
|
+
destContent = await readFile9(dest, "utf-8");
|
|
16887
17107
|
} catch {
|
|
16888
17108
|
changedFiles.push(dest);
|
|
16889
17109
|
continue;
|
|
@@ -16955,13 +17175,13 @@ async function checkUpdates(basePath, isGlobal) {
|
|
|
16955
17175
|
let targetPath = f.path;
|
|
16956
17176
|
let targetBase = basePath;
|
|
16957
17177
|
if ((target === "agy" || target === "gemini") && isGlobal) {
|
|
16958
|
-
targetBase = join8(
|
|
17178
|
+
targetBase = join8(homedir8(), ".gemini", "config");
|
|
16959
17179
|
targetPath = targetPath.replace(/^\.agents\/?/, "");
|
|
16960
17180
|
}
|
|
16961
|
-
const dest =
|
|
17181
|
+
const dest = resolve13(targetBase, targetPath);
|
|
16962
17182
|
let destContent;
|
|
16963
17183
|
try {
|
|
16964
|
-
destContent = await
|
|
17184
|
+
destContent = await readFile9(dest, "utf-8");
|
|
16965
17185
|
if (destContent.trim() !== f.content.trim()) {
|
|
16966
17186
|
changedFiles.push(dest);
|
|
16967
17187
|
}
|
|
@@ -17032,7 +17252,7 @@ __export(exports_doctor_command, {
|
|
|
17032
17252
|
doctorCommand: () => doctorCommand
|
|
17033
17253
|
});
|
|
17034
17254
|
import { join as join9 } from "node:path";
|
|
17035
|
-
import { homedir as
|
|
17255
|
+
import { homedir as homedir9 } from "node:os";
|
|
17036
17256
|
async function doctorCommand(options) {
|
|
17037
17257
|
const { projectRoot, output } = options;
|
|
17038
17258
|
const checks = [];
|
|
@@ -17108,7 +17328,7 @@ async function doctorCommand(options) {
|
|
|
17108
17328
|
});
|
|
17109
17329
|
}
|
|
17110
17330
|
const localUpdates = await checkUpdates(projectRoot, false);
|
|
17111
|
-
const globalUpdates = await checkUpdates(
|
|
17331
|
+
const globalUpdates = await checkUpdates(homedir9(), true);
|
|
17112
17332
|
const updateDetails = [];
|
|
17113
17333
|
if (localUpdates.hasUpdates) {
|
|
17114
17334
|
if (localUpdates.council)
|
|
@@ -17152,7 +17372,7 @@ async function doctorCommand(options) {
|
|
|
17152
17372
|
});
|
|
17153
17373
|
}
|
|
17154
17374
|
const largeFilesLocal = await validateAgentFileSizes(projectRoot, false);
|
|
17155
|
-
const largeFilesGlobal = await validateAgentFileSizes(
|
|
17375
|
+
const largeFilesGlobal = await validateAgentFileSizes(homedir9(), true);
|
|
17156
17376
|
const allLargeFiles = [...largeFilesLocal, ...largeFilesGlobal];
|
|
17157
17377
|
if (allLargeFiles.length > 0) {
|
|
17158
17378
|
checks.push({
|
|
@@ -17168,7 +17388,7 @@ async function doctorCommand(options) {
|
|
|
17168
17388
|
});
|
|
17169
17389
|
}
|
|
17170
17390
|
const missingMarkersLocal = await validateAgentMarkers(projectRoot, false);
|
|
17171
|
-
const missingMarkersGlobal = await validateAgentMarkers(
|
|
17391
|
+
const missingMarkersGlobal = await validateAgentMarkers(homedir9(), true);
|
|
17172
17392
|
const allMissingMarkers = [...missingMarkersLocal, ...missingMarkersGlobal];
|
|
17173
17393
|
if (allMissingMarkers.length > 0) {
|
|
17174
17394
|
checks.push({
|
|
@@ -17196,8 +17416,8 @@ async function doctorCommand(options) {
|
|
|
17196
17416
|
const graphPath = join9(projectRoot, ".codeconductor", "product-graph.json");
|
|
17197
17417
|
if (existsSync3(graphPath)) {
|
|
17198
17418
|
try {
|
|
17199
|
-
const { readFile:
|
|
17200
|
-
const raw = await
|
|
17419
|
+
const { readFile: readFile10 } = await import("node:fs/promises");
|
|
17420
|
+
const raw = await readFile10(graphPath, "utf-8");
|
|
17201
17421
|
const graph = ProductGraphSchema2.parse(JSON.parse(raw));
|
|
17202
17422
|
checks.push({
|
|
17203
17423
|
name: "product-graph",
|
|
@@ -17284,12 +17504,12 @@ var exports_update_command = {};
|
|
|
17284
17504
|
__export(exports_update_command, {
|
|
17285
17505
|
updateCommand: () => updateCommand
|
|
17286
17506
|
});
|
|
17287
|
-
import { homedir as
|
|
17288
|
-
import { resolve as
|
|
17289
|
-
import { mkdir as mkdir6, readFile as
|
|
17507
|
+
import { homedir as homedir10 } from "node:os";
|
|
17508
|
+
import { resolve as resolve14, dirname as dirname4 } from "node:path";
|
|
17509
|
+
import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile5, stat as stat3 } from "node:fs/promises";
|
|
17290
17510
|
async function updateCommand(options) {
|
|
17291
17511
|
const { dryRun, force, global: isGlobal, output, projectRoot } = options;
|
|
17292
|
-
const basePath = isGlobal ?
|
|
17512
|
+
const basePath = isGlobal ? homedir10() : projectRoot;
|
|
17293
17513
|
try {
|
|
17294
17514
|
const configResult = await loadConfig(basePath);
|
|
17295
17515
|
if (!configResult.success) {
|
|
@@ -17351,23 +17571,23 @@ async function updateCommand(options) {
|
|
|
17351
17571
|
}
|
|
17352
17572
|
const updated = [];
|
|
17353
17573
|
if (updateResults.council) {
|
|
17354
|
-
const localCouncil =
|
|
17355
|
-
const bundledCouncil =
|
|
17574
|
+
const localCouncil = resolve14(basePath, ".codeconductor", "presets", "council.yml");
|
|
17575
|
+
const bundledCouncil = resolve14(SRC_PRESETS_DIR, "council", "council.yml");
|
|
17356
17576
|
try {
|
|
17357
|
-
const content = await
|
|
17358
|
-
await mkdir6(
|
|
17359
|
-
await
|
|
17577
|
+
const content = await readFile10(bundledCouncil, "utf-8");
|
|
17578
|
+
await mkdir6(dirname4(localCouncil), { recursive: true });
|
|
17579
|
+
await writeFile5(localCouncil, content, "utf-8");
|
|
17360
17580
|
updated.push(localCouncil);
|
|
17361
17581
|
} catch (e) {
|
|
17362
17582
|
throw new Error(`Failed to update council.yml: ${e}`);
|
|
17363
17583
|
}
|
|
17364
17584
|
}
|
|
17365
17585
|
if (updateResults.policy) {
|
|
17366
|
-
const localPolicy =
|
|
17586
|
+
const localPolicy = resolve14(basePath, ".codeconductor", "presets", "policy.yml");
|
|
17367
17587
|
try {
|
|
17368
|
-
const content = await
|
|
17369
|
-
await mkdir6(
|
|
17370
|
-
await
|
|
17588
|
+
const content = await readFile10(POLICY_PATH, "utf-8");
|
|
17589
|
+
await mkdir6(dirname4(localPolicy), { recursive: true });
|
|
17590
|
+
await writeFile5(localPolicy, content, "utf-8");
|
|
17371
17591
|
updated.push(localPolicy);
|
|
17372
17592
|
} catch (e) {
|
|
17373
17593
|
throw new Error(`Failed to update policy.yml: ${e}`);
|
|
@@ -17416,10 +17636,10 @@ async function updateCommand(options) {
|
|
|
17416
17636
|
if (installer) {
|
|
17417
17637
|
const generatedFiles = await installer.generate();
|
|
17418
17638
|
const isAgyGlobal = (targetName === "agy" || targetName === "gemini") && isGlobal;
|
|
17419
|
-
const targetBase = isAgyGlobal ?
|
|
17639
|
+
const targetBase = isAgyGlobal ? resolve14(homedir10(), ".gemini", "config") : basePath;
|
|
17420
17640
|
const resolvedFiles = generatedFiles.map((f) => ({
|
|
17421
17641
|
...f,
|
|
17422
|
-
path:
|
|
17642
|
+
path: resolve14(targetBase, isAgyGlobal ? f.path.replace(/^\.agents\/?/, "") : f.path)
|
|
17423
17643
|
}));
|
|
17424
17644
|
const filesToWrite = resolvedFiles.filter((f) => t.files.includes(f.path));
|
|
17425
17645
|
if (filesToWrite.length > 0) {
|
|
@@ -17446,19 +17666,19 @@ async function updateCommand(options) {
|
|
|
17446
17666
|
for (const s of updateResults.skills) {
|
|
17447
17667
|
newSkillsLock[s.id] = s.latestVersion;
|
|
17448
17668
|
}
|
|
17449
|
-
let lockDest =
|
|
17669
|
+
let lockDest = resolve14(basePath, ".codeconductor", "skills-lock.json");
|
|
17450
17670
|
try {
|
|
17451
|
-
const statAgents = await stat3(
|
|
17671
|
+
const statAgents = await stat3(resolve14(basePath, ".agents"));
|
|
17452
17672
|
if (statAgents.isDirectory()) {
|
|
17453
|
-
const statCodeConductor = await stat3(
|
|
17673
|
+
const statCodeConductor = await stat3(resolve14(basePath, ".codeconductor")).catch(() => null);
|
|
17454
17674
|
if (!statCodeConductor) {
|
|
17455
|
-
lockDest =
|
|
17675
|
+
lockDest = resolve14(basePath, ".agents", "skills-lock.json");
|
|
17456
17676
|
}
|
|
17457
17677
|
}
|
|
17458
17678
|
} catch {}
|
|
17459
17679
|
try {
|
|
17460
|
-
await mkdir6(
|
|
17461
|
-
await
|
|
17680
|
+
await mkdir6(dirname4(lockDest), { recursive: true });
|
|
17681
|
+
await writeFile5(lockDest, JSON.stringify(newSkillsLock, null, 2), "utf-8");
|
|
17462
17682
|
updated.push(lockDest);
|
|
17463
17683
|
} catch (e) {
|
|
17464
17684
|
throw new Error(`Failed to write skills-lock.json: ${e}`);
|
|
@@ -17504,8 +17724,38 @@ function recommendAskFlow(problem) {
|
|
|
17504
17724
|
const flow = catalog.get(command);
|
|
17505
17725
|
return { command: flow.command, slash: flow.slash, reason };
|
|
17506
17726
|
};
|
|
17507
|
-
if (includesAny(text, ["openspec", "
|
|
17508
|
-
return pick("openspec", "The request names OpenSpec,
|
|
17727
|
+
if (includesAny(text, ["bc-", "tracer bullet", "openspec plan", "openspec next"]) || text.includes("deliver") && includesAny(text, ["backlog", "openspec", "bc-"])) {
|
|
17728
|
+
return pick("openspec", "The request names OpenSpec delivery, a tracer bullet, or a BC-xxx item, so /cc:openspec is the delivery loop.");
|
|
17729
|
+
}
|
|
17730
|
+
if (includesAny(text, [
|
|
17731
|
+
"create backlog",
|
|
17732
|
+
"write backlog",
|
|
17733
|
+
"add to backlog",
|
|
17734
|
+
"append to backlog",
|
|
17735
|
+
"new backlog",
|
|
17736
|
+
"author backlog",
|
|
17737
|
+
"add a backlog",
|
|
17738
|
+
"add backlog"
|
|
17739
|
+
]) || text.includes("backlog") && includesAny(text, ["create", "write", "append", "objetivo", "objective", "objectives"])) {
|
|
17740
|
+
return pick("backlog", "The request is to create or append BACKLOG.md, so /cc:backlog is the authoring loop.");
|
|
17741
|
+
}
|
|
17742
|
+
if (includesAny(text, ["openspec", "backlog"])) {
|
|
17743
|
+
return pick("openspec", "The request names OpenSpec or BACKLOG without authoring verbs, so /cc:openspec is the delivery loop.");
|
|
17744
|
+
}
|
|
17745
|
+
if (includesAny(text, [
|
|
17746
|
+
"/cc-security",
|
|
17747
|
+
"/cc:security",
|
|
17748
|
+
"authorized security",
|
|
17749
|
+
"vulnerability assessment",
|
|
17750
|
+
"vuln assessment",
|
|
17751
|
+
"threat hunting",
|
|
17752
|
+
"incident response",
|
|
17753
|
+
"penetration test",
|
|
17754
|
+
"pentest",
|
|
17755
|
+
"purple team",
|
|
17756
|
+
"security hardening"
|
|
17757
|
+
])) {
|
|
17758
|
+
return pick("security", "The request is authorized defensive security work, so /cc:security is the fit.");
|
|
17509
17759
|
}
|
|
17510
17760
|
if (includesAny(text, [
|
|
17511
17761
|
"tdd",
|
|
@@ -17576,10 +17826,20 @@ var init_recommend_flow = __esm(() => {
|
|
|
17576
17826
|
slash: "/cc:tdd-cycle",
|
|
17577
17827
|
blurb: "one red-green-refactor cycle starting from a failing test"
|
|
17578
17828
|
},
|
|
17829
|
+
{
|
|
17830
|
+
command: "backlog",
|
|
17831
|
+
slash: "/cc:backlog",
|
|
17832
|
+
blurb: "create or append BACKLOG.md items and OpenSpec change folders"
|
|
17833
|
+
},
|
|
17579
17834
|
{
|
|
17580
17835
|
command: "openspec",
|
|
17581
17836
|
slash: "/cc:openspec",
|
|
17582
17837
|
blurb: "deliver a BACKLOG.md / OpenSpec tracer bullet"
|
|
17838
|
+
},
|
|
17839
|
+
{
|
|
17840
|
+
command: "security",
|
|
17841
|
+
slash: "/cc:security",
|
|
17842
|
+
blurb: "authorized defensive security work with an authorization gate"
|
|
17583
17843
|
}
|
|
17584
17844
|
];
|
|
17585
17845
|
});
|
|
@@ -17766,7 +18026,7 @@ var exports_debt_harvest_command = {};
|
|
|
17766
18026
|
__export(exports_debt_harvest_command, {
|
|
17767
18027
|
debtHarvestCommand: () => debtHarvestCommand
|
|
17768
18028
|
});
|
|
17769
|
-
import { readdir as readdir4, readFile as
|
|
18029
|
+
import { readdir as readdir4, readFile as readFile11, writeFile as writeFile6, mkdir as mkdir7 } from "node:fs/promises";
|
|
17770
18030
|
import { join as join11, relative as relative4, extname } from "node:path";
|
|
17771
18031
|
async function walkDir(dir, extensions) {
|
|
17772
18032
|
const results = [];
|
|
@@ -17844,7 +18104,7 @@ async function debtHarvestCommand(options) {
|
|
|
17844
18104
|
const files = await walkDir(targetDir, SOURCE_EXTENSIONS);
|
|
17845
18105
|
const allEntries = [];
|
|
17846
18106
|
for (const file of files) {
|
|
17847
|
-
const content = await
|
|
18107
|
+
const content = await readFile11(file, "utf-8");
|
|
17848
18108
|
const entries = extractDefers(content, relative4(projectRoot, file));
|
|
17849
18109
|
allEntries.push(...entries);
|
|
17850
18110
|
}
|
|
@@ -17852,7 +18112,7 @@ async function debtHarvestCommand(options) {
|
|
|
17852
18112
|
await mkdir7(ledgerDir, { recursive: true });
|
|
17853
18113
|
const ledgerPath = join11(ledgerDir, "debt-ledger.md");
|
|
17854
18114
|
const ledgerContent = renderLedger(allEntries);
|
|
17855
|
-
await
|
|
18115
|
+
await writeFile6(ledgerPath, ledgerContent, "utf-8");
|
|
17856
18116
|
if (output === "json") {
|
|
17857
18117
|
return {
|
|
17858
18118
|
code: 0,
|
|
@@ -18322,11 +18582,11 @@ __export(exports_graph_store, {
|
|
|
18322
18582
|
findPath: () => findPath,
|
|
18323
18583
|
countByType: () => countByType
|
|
18324
18584
|
});
|
|
18325
|
-
import { mkdir as mkdir8, readFile as
|
|
18326
|
-
import { dirname as
|
|
18585
|
+
import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile7 } from "node:fs/promises";
|
|
18586
|
+
import { dirname as dirname5 } from "node:path";
|
|
18327
18587
|
async function loadGraph(projectRoot) {
|
|
18328
18588
|
try {
|
|
18329
|
-
const raw = await
|
|
18589
|
+
const raw = await readFile12(productGraphPath(projectRoot), "utf-8");
|
|
18330
18590
|
return ok(ProductGraphSchema.parse(JSON.parse(raw)));
|
|
18331
18591
|
} catch (e) {
|
|
18332
18592
|
if (e.code === "ENOENT") {
|
|
@@ -18339,8 +18599,8 @@ async function saveGraph(projectRoot, graph) {
|
|
|
18339
18599
|
try {
|
|
18340
18600
|
const validated = ProductGraphSchema.parse(graph);
|
|
18341
18601
|
const path = productGraphPath(projectRoot);
|
|
18342
|
-
await mkdir8(
|
|
18343
|
-
await
|
|
18602
|
+
await mkdir8(dirname5(path), { recursive: true });
|
|
18603
|
+
await writeFile7(path, JSON.stringify(validated, null, 2), "utf-8");
|
|
18344
18604
|
return ok(undefined);
|
|
18345
18605
|
} catch (e) {
|
|
18346
18606
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -18507,8 +18767,8 @@ __export(exports_goal_state, {
|
|
|
18507
18767
|
markTaskBlocked: () => markTaskBlocked,
|
|
18508
18768
|
loadGoal: () => loadGoal
|
|
18509
18769
|
});
|
|
18510
|
-
import { mkdir as mkdir9, readFile as
|
|
18511
|
-
import { resolve as
|
|
18770
|
+
import { mkdir as mkdir9, readFile as readFile13, writeFile as writeFile8 } from "node:fs/promises";
|
|
18771
|
+
import { resolve as resolve15 } from "node:path";
|
|
18512
18772
|
function validateGoalGraph2(graph) {
|
|
18513
18773
|
const ids = new Set(graph.tasks.map((t) => t.id));
|
|
18514
18774
|
if (ids.size !== graph.tasks.length) {
|
|
@@ -18551,16 +18811,16 @@ async function writeGoal(projectRoot, graph) {
|
|
|
18551
18811
|
const validation = validateGoalGraph2(graph);
|
|
18552
18812
|
if (!validation.success)
|
|
18553
18813
|
return validation;
|
|
18554
|
-
const dir =
|
|
18814
|
+
const dir = resolve15(projectRoot, ".codeconductor");
|
|
18555
18815
|
await mkdir9(dir, { recursive: true });
|
|
18556
18816
|
const yaml = $stringify(graph);
|
|
18557
|
-
await
|
|
18817
|
+
await writeFile8(resolve15(dir, "current-goal.yml"), yaml, "utf-8");
|
|
18558
18818
|
return ok(undefined);
|
|
18559
18819
|
}
|
|
18560
18820
|
async function loadGoal(projectRoot) {
|
|
18561
18821
|
try {
|
|
18562
|
-
const filePath =
|
|
18563
|
-
const content = await
|
|
18822
|
+
const filePath = resolve15(projectRoot, GOAL_FILE);
|
|
18823
|
+
const content = await readFile13(filePath, "utf-8");
|
|
18564
18824
|
const data = $parse(content);
|
|
18565
18825
|
const validated = GoalGraphSchema.parse(data);
|
|
18566
18826
|
return validateGoalGraph2(validated);
|
|
@@ -18722,8 +18982,8 @@ var init_goal_command = __esm(() => {
|
|
|
18722
18982
|
});
|
|
18723
18983
|
|
|
18724
18984
|
// src/core/openspec/backlog-parser.ts
|
|
18725
|
-
import { readFile as
|
|
18726
|
-
import { resolve as
|
|
18985
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
18986
|
+
import { resolve as resolve16 } from "node:path";
|
|
18727
18987
|
function parseBool(value) {
|
|
18728
18988
|
const v = value.trim().toLowerCase();
|
|
18729
18989
|
return v === "yes" || v === "true";
|
|
@@ -18897,8 +19157,8 @@ function parseBacklogMarkdown(content) {
|
|
|
18897
19157
|
}
|
|
18898
19158
|
async function loadBacklog(projectRoot) {
|
|
18899
19159
|
try {
|
|
18900
|
-
const filePath =
|
|
18901
|
-
const content = await
|
|
19160
|
+
const filePath = resolve16(projectRoot, BACKLOG_FILE);
|
|
19161
|
+
const content = await readFile14(filePath, "utf-8");
|
|
18902
19162
|
return parseBacklogMarkdown(content);
|
|
18903
19163
|
} catch (e) {
|
|
18904
19164
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -18947,11 +19207,11 @@ function slugify(text) {
|
|
|
18947
19207
|
}
|
|
18948
19208
|
|
|
18949
19209
|
// src/core/knowledge/source-parsers.ts
|
|
18950
|
-
import { readFile as
|
|
19210
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
18951
19211
|
import { join as join13 } from "node:path";
|
|
18952
19212
|
async function parseAdrFile(filePath) {
|
|
18953
19213
|
try {
|
|
18954
|
-
const content = await
|
|
19214
|
+
const content = await readFile15(filePath, "utf-8");
|
|
18955
19215
|
const basename3 = filePath.split("/").pop() ?? "";
|
|
18956
19216
|
const idMatch = basename3.match(/adr-(\d+)/i);
|
|
18957
19217
|
const id = idMatch ? `adr-${idMatch[1]}` : slugify(basename3);
|
|
@@ -19000,7 +19260,7 @@ function adrToEntities(adr, source) {
|
|
|
19000
19260
|
}
|
|
19001
19261
|
async function parseReadmeEntities(projectRoot, productName) {
|
|
19002
19262
|
try {
|
|
19003
|
-
const content = await
|
|
19263
|
+
const content = await readFile15(join13(projectRoot, "README.md"), "utf-8");
|
|
19004
19264
|
const firstPara = content.split(`
|
|
19005
19265
|
|
|
19006
19266
|
`).find((p) => p.trim() && !p.startsWith("#") && !p.startsWith(">")) ?? "";
|
|
@@ -19021,7 +19281,7 @@ async function parseReadmeEntities(projectRoot, productName) {
|
|
|
19021
19281
|
}
|
|
19022
19282
|
async function parseGraphifyEntities(projectRoot) {
|
|
19023
19283
|
try {
|
|
19024
|
-
const raw = await
|
|
19284
|
+
const raw = await readFile15(join13(projectRoot, "graphify-out", "graph.json"), "utf-8");
|
|
19025
19285
|
const graph = JSON.parse(raw);
|
|
19026
19286
|
const entities = [];
|
|
19027
19287
|
for (const node2 of graph.nodes ?? []) {
|
|
@@ -19077,8 +19337,8 @@ async function scanSrcComponents(projectRoot) {
|
|
|
19077
19337
|
var init_source_parsers = () => {};
|
|
19078
19338
|
|
|
19079
19339
|
// src/core/memory/episodic-store.ts
|
|
19080
|
-
import { appendFile, mkdir as mkdir10, readFile as
|
|
19081
|
-
import { resolve as
|
|
19340
|
+
import { appendFile, mkdir as mkdir10, readFile as readFile16 } from "node:fs/promises";
|
|
19341
|
+
import { resolve as resolve17 } from "node:path";
|
|
19082
19342
|
async function appendEvent(projectRoot, event) {
|
|
19083
19343
|
try {
|
|
19084
19344
|
const full = ProductEventSchema.parse({
|
|
@@ -19086,7 +19346,7 @@ async function appendEvent(projectRoot, event) {
|
|
|
19086
19346
|
id: event.id ?? `evt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
19087
19347
|
});
|
|
19088
19348
|
const path = eventsPath(projectRoot);
|
|
19089
|
-
await mkdir10(
|
|
19349
|
+
await mkdir10(resolve17(projectRoot, ".codeconductor"), { recursive: true });
|
|
19090
19350
|
await appendFile(path, JSON.stringify(full) + `
|
|
19091
19351
|
`, "utf-8");
|
|
19092
19352
|
return ok(full);
|
|
@@ -19096,7 +19356,7 @@ async function appendEvent(projectRoot, event) {
|
|
|
19096
19356
|
}
|
|
19097
19357
|
async function listEvents(projectRoot, since, type) {
|
|
19098
19358
|
try {
|
|
19099
|
-
const content = await
|
|
19359
|
+
const content = await readFile16(eventsPath(projectRoot), "utf-8");
|
|
19100
19360
|
const events = [];
|
|
19101
19361
|
for (const line of content.split(`
|
|
19102
19362
|
`)) {
|
|
@@ -19124,9 +19384,9 @@ var init_episodic_store = __esm(() => {
|
|
|
19124
19384
|
|
|
19125
19385
|
// src/core/knowledge/ingest-pipeline.ts
|
|
19126
19386
|
import { createHash as createHash2 } from "node:crypto";
|
|
19127
|
-
import { readFile as
|
|
19387
|
+
import { readFile as readFile17 } from "node:fs/promises";
|
|
19128
19388
|
import { join as join14 } from "node:path";
|
|
19129
|
-
import { mkdir as mkdir11, writeFile as
|
|
19389
|
+
import { mkdir as mkdir11, writeFile as writeFile9 } from "node:fs/promises";
|
|
19130
19390
|
async function mapLimit(items, limit, fn) {
|
|
19131
19391
|
if (items.length === 0)
|
|
19132
19392
|
return [];
|
|
@@ -19146,7 +19406,7 @@ async function mapLimit(items, limit, fn) {
|
|
|
19146
19406
|
}
|
|
19147
19407
|
async function hashFile(path) {
|
|
19148
19408
|
try {
|
|
19149
|
-
const content = await
|
|
19409
|
+
const content = await readFile17(path, "utf-8");
|
|
19150
19410
|
return createHash2("sha256").update(content).digest("hex").slice(0, 16);
|
|
19151
19411
|
} catch {
|
|
19152
19412
|
return null;
|
|
@@ -19173,7 +19433,7 @@ async function collectDeferRisks(projectRoot) {
|
|
|
19173
19433
|
await walk(join14(projectRoot, "src"));
|
|
19174
19434
|
const perFile = await mapLimit(files, WALK_READ_CONCURRENCY, async (full) => {
|
|
19175
19435
|
const entities = [];
|
|
19176
|
-
const content = await
|
|
19436
|
+
const content = await readFile17(full, "utf-8");
|
|
19177
19437
|
const rel = full.replace(projectRoot + "/", "");
|
|
19178
19438
|
const deferRe = new RegExp(DEFER_REGEX_SOURCE, "gm");
|
|
19179
19439
|
let match;
|
|
@@ -19341,7 +19601,7 @@ async function runIngest(projectRoot, productName) {
|
|
|
19341
19601
|
sourceHashes
|
|
19342
19602
|
};
|
|
19343
19603
|
await mkdir11(join14(projectRoot, ".codeconductor"), { recursive: true });
|
|
19344
|
-
await
|
|
19604
|
+
await writeFile9(productMetaPath(projectRoot), JSON.stringify(ProductMetaSchema.parse(meta), null, 2), "utf-8");
|
|
19345
19605
|
await appendEvent(projectRoot, {
|
|
19346
19606
|
type: "ingest.completed",
|
|
19347
19607
|
timestamp: new Date().toISOString(),
|
|
@@ -19422,11 +19682,11 @@ var init_ingest_command = __esm(() => {
|
|
|
19422
19682
|
});
|
|
19423
19683
|
|
|
19424
19684
|
// src/core/memory/operational-state.ts
|
|
19425
|
-
import { mkdir as mkdir12, readFile as
|
|
19426
|
-
import { resolve as
|
|
19685
|
+
import { mkdir as mkdir12, readFile as readFile18, writeFile as writeFile10 } from "node:fs/promises";
|
|
19686
|
+
import { resolve as resolve18 } from "node:path";
|
|
19427
19687
|
async function loadOperationalState(projectRoot) {
|
|
19428
19688
|
try {
|
|
19429
|
-
const raw = await
|
|
19689
|
+
const raw = await readFile18(operationalStatePath(projectRoot), "utf-8");
|
|
19430
19690
|
return ok(OperationalStateSchema.parse(JSON.parse(raw)));
|
|
19431
19691
|
} catch (e) {
|
|
19432
19692
|
if (e.code === "ENOENT") {
|
|
@@ -19448,8 +19708,8 @@ async function saveOperationalState(projectRoot, state) {
|
|
|
19448
19708
|
updatedAt: new Date().toISOString()
|
|
19449
19709
|
});
|
|
19450
19710
|
const path = operationalStatePath(projectRoot);
|
|
19451
|
-
await mkdir12(
|
|
19452
|
-
await
|
|
19711
|
+
await mkdir12(resolve18(projectRoot, ".codeconductor"), { recursive: true });
|
|
19712
|
+
await writeFile10(path, JSON.stringify(validated, null, 2), "utf-8");
|
|
19453
19713
|
return ok(undefined);
|
|
19454
19714
|
} catch (e) {
|
|
19455
19715
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -19483,11 +19743,11 @@ var init_operational_state = __esm(() => {
|
|
|
19483
19743
|
});
|
|
19484
19744
|
|
|
19485
19745
|
// src/core/memory/strategic-memory.ts
|
|
19486
|
-
import { mkdir as mkdir13, readFile as
|
|
19487
|
-
import { resolve as
|
|
19746
|
+
import { mkdir as mkdir13, readFile as readFile19, writeFile as writeFile11 } from "node:fs/promises";
|
|
19747
|
+
import { resolve as resolve19 } from "node:path";
|
|
19488
19748
|
async function loadStrategicMemory(projectRoot) {
|
|
19489
19749
|
try {
|
|
19490
|
-
const raw = await
|
|
19750
|
+
const raw = await readFile19(strategicPath(projectRoot), "utf-8");
|
|
19491
19751
|
return ok(StrategicMemorySchema.parse(JSON.parse(raw)));
|
|
19492
19752
|
} catch (e) {
|
|
19493
19753
|
if (e.code === "ENOENT") {
|
|
@@ -19508,8 +19768,8 @@ async function saveStrategicMemory(projectRoot, memory) {
|
|
|
19508
19768
|
updatedAt: new Date().toISOString()
|
|
19509
19769
|
});
|
|
19510
19770
|
const path = strategicPath(projectRoot);
|
|
19511
|
-
await mkdir13(
|
|
19512
|
-
await
|
|
19771
|
+
await mkdir13(resolve19(projectRoot, ".codeconductor"), { recursive: true });
|
|
19772
|
+
await writeFile11(path, JSON.stringify(validated, null, 2), "utf-8");
|
|
19513
19773
|
return ok(undefined);
|
|
19514
19774
|
} catch (e) {
|
|
19515
19775
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -19521,8 +19781,8 @@ var init_strategic_memory = __esm(() => {
|
|
|
19521
19781
|
});
|
|
19522
19782
|
|
|
19523
19783
|
// src/core/memory/memory-index.ts
|
|
19524
|
-
import { mkdir as mkdir14, readFile as
|
|
19525
|
-
import { resolve as
|
|
19784
|
+
import { mkdir as mkdir14, readFile as readFile20, writeFile as writeFile12 } from "node:fs/promises";
|
|
19785
|
+
import { resolve as resolve20 } from "node:path";
|
|
19526
19786
|
function parseMarkdown(content) {
|
|
19527
19787
|
const startIdx = content.indexOf(SENTINEL_START);
|
|
19528
19788
|
const endIdx = content.indexOf(SENTINEL_END);
|
|
@@ -19541,8 +19801,8 @@ function parseMarkdown(content) {
|
|
|
19541
19801
|
}
|
|
19542
19802
|
async function loadMemoryIndex(projectRoot) {
|
|
19543
19803
|
try {
|
|
19544
|
-
const filePath =
|
|
19545
|
-
const content = await
|
|
19804
|
+
const filePath = resolve20(projectRoot, MEMORY_FILE);
|
|
19805
|
+
const content = await readFile20(filePath, "utf-8");
|
|
19546
19806
|
const parsed = parseMarkdown(content);
|
|
19547
19807
|
if (!parsed) {
|
|
19548
19808
|
return err(new Error("Failed to parse memory index from memory.md"));
|
|
@@ -19559,28 +19819,28 @@ var init_memory_index = __esm(() => {
|
|
|
19559
19819
|
});
|
|
19560
19820
|
|
|
19561
19821
|
// src/core/evaluation/outcome-store.ts
|
|
19562
|
-
import { appendFile as appendFile2, mkdir as mkdir15, readFile as
|
|
19563
|
-
import { resolve as
|
|
19822
|
+
import { appendFile as appendFile2, mkdir as mkdir15, readFile as readFile21, writeFile as writeFile13 } from "node:fs/promises";
|
|
19823
|
+
import { resolve as resolve21 } from "node:path";
|
|
19564
19824
|
async function ensureEvaluationDir(projectRoot) {
|
|
19565
|
-
const dir =
|
|
19825
|
+
const dir = resolve21(projectRoot, EVAL_DIR);
|
|
19566
19826
|
await mkdir15(dir, { recursive: true });
|
|
19567
|
-
await mkdir15(
|
|
19827
|
+
await mkdir15(resolve21(dir, SCORECARDS_DIR), { recursive: true });
|
|
19568
19828
|
return dir;
|
|
19569
19829
|
}
|
|
19570
19830
|
async function appendOutcome(projectRoot, outcome) {
|
|
19571
19831
|
try {
|
|
19572
19832
|
const dir = await ensureEvaluationDir(projectRoot);
|
|
19573
19833
|
const validated = TaskOutcomeSchema.parse(outcome);
|
|
19574
|
-
await appendFile2(
|
|
19834
|
+
await appendFile2(resolve21(dir, OUTCOMES_FILE), JSON.stringify(validated) + `
|
|
19575
19835
|
`, "utf-8");
|
|
19576
|
-
const indexPath =
|
|
19836
|
+
const indexPath = resolve21(dir, INDEX_FILE);
|
|
19577
19837
|
let index = { version: 1, lastOutcomeId: validated.id };
|
|
19578
19838
|
try {
|
|
19579
|
-
const raw = await
|
|
19839
|
+
const raw = await readFile21(indexPath, "utf-8");
|
|
19580
19840
|
index = EvaluationIndexSchema.parse(JSON.parse(raw));
|
|
19581
19841
|
index.lastOutcomeId = validated.id;
|
|
19582
19842
|
} catch {}
|
|
19583
|
-
await
|
|
19843
|
+
await writeFile13(indexPath, JSON.stringify(index, null, 2), "utf-8");
|
|
19584
19844
|
return ok(undefined);
|
|
19585
19845
|
} catch (e) {
|
|
19586
19846
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -19588,8 +19848,8 @@ async function appendOutcome(projectRoot, outcome) {
|
|
|
19588
19848
|
}
|
|
19589
19849
|
async function listOutcomes(projectRoot, filter = {}) {
|
|
19590
19850
|
try {
|
|
19591
|
-
const path =
|
|
19592
|
-
const content = await
|
|
19851
|
+
const path = resolve21(projectRoot, EVAL_DIR, OUTCOMES_FILE);
|
|
19852
|
+
const content = await readFile21(path, "utf-8");
|
|
19593
19853
|
const lines = content.split(`
|
|
19594
19854
|
`).filter((l) => l.trim().length > 0);
|
|
19595
19855
|
const outcomes = [];
|
|
@@ -19607,6 +19867,12 @@ async function listOutcomes(projectRoot, filter = {}) {
|
|
|
19607
19867
|
continue;
|
|
19608
19868
|
if (filter.since && parsed.timestamp < filter.since)
|
|
19609
19869
|
continue;
|
|
19870
|
+
if (filter.experimentId && parsed.experimentId !== filter.experimentId)
|
|
19871
|
+
continue;
|
|
19872
|
+
if (filter.variantId && parsed.variantId !== filter.variantId)
|
|
19873
|
+
continue;
|
|
19874
|
+
if (filter.suiteTaskId && parsed.suiteTaskId !== filter.suiteTaskId)
|
|
19875
|
+
continue;
|
|
19610
19876
|
outcomes.push(parsed);
|
|
19611
19877
|
}
|
|
19612
19878
|
return ok(outcomes);
|
|
@@ -19621,8 +19887,8 @@ async function saveScorecard(projectRoot, record) {
|
|
|
19621
19887
|
try {
|
|
19622
19888
|
const dir = await ensureEvaluationDir(projectRoot);
|
|
19623
19889
|
const validated = ScorecardRecordSchema.parse(record);
|
|
19624
|
-
const filePath =
|
|
19625
|
-
await
|
|
19890
|
+
const filePath = resolve21(dir, SCORECARDS_DIR, `${validated.id}.json`);
|
|
19891
|
+
await writeFile13(filePath, JSON.stringify(validated, null, 2), "utf-8");
|
|
19626
19892
|
return ok(filePath);
|
|
19627
19893
|
} catch (e) {
|
|
19628
19894
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -19630,8 +19896,8 @@ async function saveScorecard(projectRoot, record) {
|
|
|
19630
19896
|
}
|
|
19631
19897
|
async function loadScorecard(projectRoot, id) {
|
|
19632
19898
|
try {
|
|
19633
|
-
const filePath =
|
|
19634
|
-
const content = await
|
|
19899
|
+
const filePath = resolve21(projectRoot, EVAL_DIR, SCORECARDS_DIR, `${id}.json`);
|
|
19900
|
+
const content = await readFile21(filePath, "utf-8");
|
|
19635
19901
|
return ok(ScorecardRecordSchema.parse(JSON.parse(content)));
|
|
19636
19902
|
} catch (e) {
|
|
19637
19903
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -19643,10 +19909,12 @@ function aggregateOutcomes(outcomes) {
|
|
|
19643
19909
|
const avgWeightedScore = withScore.length > 0 ? withScore.reduce((s, o) => s + (o.weightedScore ?? 0), 0) / withScore.length : 0;
|
|
19644
19910
|
const byAgent = {};
|
|
19645
19911
|
const byModel = {};
|
|
19912
|
+
const byVariant = {};
|
|
19646
19913
|
const agentScoreCounts = {};
|
|
19647
19914
|
const modelScoreCounts = {};
|
|
19648
19915
|
const modelCostCounts = {};
|
|
19649
19916
|
const modelTokenCounts = {};
|
|
19917
|
+
const variantScoreCounts = {};
|
|
19650
19918
|
for (const o of outcomes) {
|
|
19651
19919
|
if (!byAgent[o.agent])
|
|
19652
19920
|
byAgent[o.agent] = { count: 0, avgScore: 0 };
|
|
@@ -19671,6 +19939,15 @@ function aggregateOutcomes(outcomes) {
|
|
|
19671
19939
|
byModel[o.model].avgTokens = (byModel[o.model].avgTokens ?? 0) + tokens;
|
|
19672
19940
|
modelTokenCounts[o.model] = (modelTokenCounts[o.model] ?? 0) + 1;
|
|
19673
19941
|
}
|
|
19942
|
+
if (o.variantId) {
|
|
19943
|
+
if (!byVariant[o.variantId])
|
|
19944
|
+
byVariant[o.variantId] = { count: 0, avgScore: 0 };
|
|
19945
|
+
byVariant[o.variantId].count++;
|
|
19946
|
+
if (o.weightedScore !== undefined) {
|
|
19947
|
+
byVariant[o.variantId].avgScore += o.weightedScore;
|
|
19948
|
+
variantScoreCounts[o.variantId] = (variantScoreCounts[o.variantId] ?? 0) + 1;
|
|
19949
|
+
}
|
|
19950
|
+
}
|
|
19674
19951
|
}
|
|
19675
19952
|
for (const agent of Object.keys(byAgent)) {
|
|
19676
19953
|
const entry = byAgent[agent];
|
|
@@ -19686,12 +19963,18 @@ function aggregateOutcomes(outcomes) {
|
|
|
19686
19963
|
if (entry.avgTokens !== undefined)
|
|
19687
19964
|
entry.avgTokens /= modelTokenCounts[model];
|
|
19688
19965
|
}
|
|
19966
|
+
for (const variant of Object.keys(byVariant)) {
|
|
19967
|
+
const entry = byVariant[variant];
|
|
19968
|
+
const scoreCount = variantScoreCounts[variant] ?? 0;
|
|
19969
|
+
entry.avgScore = scoreCount > 0 ? entry.avgScore / scoreCount : 0;
|
|
19970
|
+
}
|
|
19689
19971
|
return {
|
|
19690
19972
|
total: outcomes.length,
|
|
19691
19973
|
passRate: outcomes.length > 0 ? passed.length / outcomes.length : 0,
|
|
19692
19974
|
avgWeightedScore: Math.round(avgWeightedScore * 1000) / 1000,
|
|
19693
19975
|
byAgent,
|
|
19694
|
-
byModel
|
|
19976
|
+
byModel,
|
|
19977
|
+
byVariant
|
|
19695
19978
|
};
|
|
19696
19979
|
}
|
|
19697
19980
|
function generateEvalId(prefix) {
|
|
@@ -19705,11 +19988,11 @@ var init_outcome_store = __esm(() => {
|
|
|
19705
19988
|
});
|
|
19706
19989
|
|
|
19707
19990
|
// src/core/feedback/feedback-ingestor.ts
|
|
19708
|
-
import { readFile as
|
|
19991
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
19709
19992
|
import { join as join15 } from "node:path";
|
|
19710
19993
|
async function parseChangelogLearnings(projectRoot) {
|
|
19711
19994
|
try {
|
|
19712
|
-
const content = await
|
|
19995
|
+
const content = await readFile22(join15(projectRoot, "CHANGELOG.md"), "utf-8");
|
|
19713
19996
|
const unreleased = content.match(/## \[Unreleased\]([\s\S]*?)(?=## \[|\n## )/i);
|
|
19714
19997
|
if (!unreleased)
|
|
19715
19998
|
return [];
|
|
@@ -19743,6 +20026,15 @@ async function generateInsights(projectRoot) {
|
|
|
19743
20026
|
});
|
|
19744
20027
|
}
|
|
19745
20028
|
}
|
|
20029
|
+
const tagged = outcomes.data.filter((o) => o.experimentId && o.variantId);
|
|
20030
|
+
if (tagged.length > 0) {
|
|
20031
|
+
const experiments = new Set(tagged.map((o) => o.experimentId));
|
|
20032
|
+
insights.push({
|
|
20033
|
+
type: "harness_ablation",
|
|
20034
|
+
message: `${experiments.size} harness experiment(s), ${tagged.length} tagged outcomes`,
|
|
20035
|
+
confidence: "medium"
|
|
20036
|
+
});
|
|
20037
|
+
}
|
|
19746
20038
|
}
|
|
19747
20039
|
const learnings = await parseChangelogLearnings(projectRoot);
|
|
19748
20040
|
for (const learning of learnings.slice(0, 5)) {
|
|
@@ -19820,7 +20112,7 @@ var exports_product_command = {};
|
|
|
19820
20112
|
__export(exports_product_command, {
|
|
19821
20113
|
productCommand: () => productCommand
|
|
19822
20114
|
});
|
|
19823
|
-
import { writeFile as
|
|
20115
|
+
import { writeFile as writeFile14 } from "node:fs/promises";
|
|
19824
20116
|
async function productCommand(options) {
|
|
19825
20117
|
const { subcommand, projectRoot, output, query, from, to, since, format } = options;
|
|
19826
20118
|
try {
|
|
@@ -20023,7 +20315,7 @@ async function handleExport(projectRoot, format, output) {
|
|
|
20023
20315
|
...queryNodes(graph.data, "risk").map((n) => `- ${n.name}`)
|
|
20024
20316
|
].join(`
|
|
20025
20317
|
`);
|
|
20026
|
-
await
|
|
20318
|
+
await writeFile14(productReportPath(projectRoot), md, "utf-8");
|
|
20027
20319
|
return {
|
|
20028
20320
|
code: 0,
|
|
20029
20321
|
data: {
|
|
@@ -20226,8 +20518,8 @@ async function runCompileCheck(options) {
|
|
|
20226
20518
|
})();
|
|
20227
20519
|
collectOutput.catch(() => {});
|
|
20228
20520
|
let timeoutId;
|
|
20229
|
-
const timeout = new Promise((
|
|
20230
|
-
timeoutId = setTimeout(() =>
|
|
20521
|
+
const timeout = new Promise((resolve22) => {
|
|
20522
|
+
timeoutId = setTimeout(() => resolve22("timeout"), timeoutMs);
|
|
20231
20523
|
});
|
|
20232
20524
|
try {
|
|
20233
20525
|
const outcome = await Promise.race([collectOutput, timeout]);
|
|
@@ -20297,9 +20589,9 @@ var init_compile_checker = __esm(() => {
|
|
|
20297
20589
|
|
|
20298
20590
|
// src/core/verification/verification-runner.ts
|
|
20299
20591
|
import { existsSync as existsSync3 } from "node:fs";
|
|
20300
|
-
import { readFile as
|
|
20301
|
-
import { dirname as
|
|
20302
|
-
import { mkdir as mkdir16, readdir as readdir5, writeFile as
|
|
20592
|
+
import { readFile as readFile23 } from "node:fs/promises";
|
|
20593
|
+
import { dirname as dirname6, join as join16, resolve as resolve22 } from "node:path";
|
|
20594
|
+
import { mkdir as mkdir16, readdir as readdir5, writeFile as writeFile15 } from "node:fs/promises";
|
|
20303
20595
|
function isPassingEvidence(ev) {
|
|
20304
20596
|
switch (ev.type) {
|
|
20305
20597
|
case "verification":
|
|
@@ -20317,8 +20609,8 @@ function isPassingEvidence(ev) {
|
|
|
20317
20609
|
}
|
|
20318
20610
|
}
|
|
20319
20611
|
function evidenceFilePath(evDir, id) {
|
|
20320
|
-
const path =
|
|
20321
|
-
if (
|
|
20612
|
+
const path = resolve22(evDir, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
20613
|
+
if (dirname6(path) !== resolve22(evDir)) {
|
|
20322
20614
|
return err(new Error(`Refusing to write evidence for "${id}" outside ${evDir}`));
|
|
20323
20615
|
}
|
|
20324
20616
|
return ok(path);
|
|
@@ -20330,7 +20622,7 @@ async function persistEvidence(projectRoot, evidence) {
|
|
|
20330
20622
|
return evidencePath;
|
|
20331
20623
|
try {
|
|
20332
20624
|
await mkdir16(evDir, { recursive: true });
|
|
20333
|
-
await
|
|
20625
|
+
await writeFile15(evidencePath.data, JSON.stringify(evidence, null, 2), "utf-8");
|
|
20334
20626
|
} catch (e) {
|
|
20335
20627
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
20336
20628
|
}
|
|
@@ -20352,7 +20644,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
|
|
|
20352
20644
|
if (!file.endsWith(".json"))
|
|
20353
20645
|
continue;
|
|
20354
20646
|
try {
|
|
20355
|
-
const raw = await
|
|
20647
|
+
const raw = await readFile23(join16(evDir, file), "utf-8");
|
|
20356
20648
|
const ev = EvidenceSchema.parse(JSON.parse(raw));
|
|
20357
20649
|
if (ev.relatedTask === taskId)
|
|
20358
20650
|
records.push(ev);
|
|
@@ -20556,7 +20848,8 @@ function goalTaskToCanonicalCard(task, objective) {
|
|
|
20556
20848
|
evidenceRequired: task.evidenceRequired ?? ["acceptance_criteria_met"],
|
|
20557
20849
|
status: task.status === "in-progress" ? "in-progress" : task.status === "done" ? "done" : task.status === "blocked" ? "blocked" : "ready",
|
|
20558
20850
|
type: task.type,
|
|
20559
|
-
linkedCapabilities: []
|
|
20851
|
+
linkedCapabilities: [],
|
|
20852
|
+
boundaries: []
|
|
20560
20853
|
};
|
|
20561
20854
|
}
|
|
20562
20855
|
function buildTaskEnvelope(card, projectRoot, projectName) {
|
|
@@ -20987,6 +21280,8 @@ async function runLoop(generateFn, compileCheckFn, config = {}, taskTitle = "com
|
|
|
20987
21280
|
let state = createInitialState({ maxIterations, maxTokenBudget });
|
|
20988
21281
|
let totalErrors = 0;
|
|
20989
21282
|
let feedbackText;
|
|
21283
|
+
let compileCheck;
|
|
21284
|
+
let compileCheckSkipReason;
|
|
20990
21285
|
logTransition(state.phase, "INIT", state.iteration);
|
|
20991
21286
|
const startResult = loopStateMachine(state, { type: "START" });
|
|
20992
21287
|
state = startResult.state;
|
|
@@ -21022,6 +21317,12 @@ async function runLoop(generateFn, compileCheckFn, config = {}, taskTitle = "com
|
|
|
21022
21317
|
logTransition(state.phase, "COMPILE_CHECK", state.iteration);
|
|
21023
21318
|
const compileResult = await compileCheckFn();
|
|
21024
21319
|
totalErrors += compileResult.errors.length;
|
|
21320
|
+
if (compileResult.skipped) {
|
|
21321
|
+
compileCheck = "skipped";
|
|
21322
|
+
compileCheckSkipReason = compileResult.skipReason;
|
|
21323
|
+
} else {
|
|
21324
|
+
compileCheck = "ran";
|
|
21325
|
+
}
|
|
21025
21326
|
const checkResult = loopStateMachine(state, {
|
|
21026
21327
|
type: "COMPILE_CHECK_COMPLETED",
|
|
21027
21328
|
errors: compileResult.errors
|
|
@@ -21045,7 +21346,9 @@ async function runLoop(generateFn, compileCheckFn, config = {}, taskTitle = "com
|
|
|
21045
21346
|
iterations: state.iteration,
|
|
21046
21347
|
totalErrors,
|
|
21047
21348
|
finalPhase: "DONE",
|
|
21048
|
-
errorHistory: state.errorHistory
|
|
21349
|
+
errorHistory: state.errorHistory,
|
|
21350
|
+
compileCheck,
|
|
21351
|
+
compileCheckSkipReason
|
|
21049
21352
|
};
|
|
21050
21353
|
}
|
|
21051
21354
|
case "ESCALATED": {
|
|
@@ -21057,7 +21360,9 @@ async function runLoop(generateFn, compileCheckFn, config = {}, taskTitle = "com
|
|
|
21057
21360
|
totalErrors,
|
|
21058
21361
|
finalPhase: "ESCALATED",
|
|
21059
21362
|
escalationReport: report,
|
|
21060
|
-
errorHistory: state.errorHistory
|
|
21363
|
+
errorHistory: state.errorHistory,
|
|
21364
|
+
compileCheck,
|
|
21365
|
+
compileCheckSkipReason
|
|
21061
21366
|
};
|
|
21062
21367
|
}
|
|
21063
21368
|
case "FAILED": {
|
|
@@ -21067,7 +21372,9 @@ async function runLoop(generateFn, compileCheckFn, config = {}, taskTitle = "com
|
|
|
21067
21372
|
iterations: state.iteration,
|
|
21068
21373
|
totalErrors,
|
|
21069
21374
|
finalPhase: "FAILED",
|
|
21070
|
-
errorHistory: state.errorHistory
|
|
21375
|
+
errorHistory: state.errorHistory,
|
|
21376
|
+
compileCheck,
|
|
21377
|
+
compileCheckSkipReason
|
|
21071
21378
|
};
|
|
21072
21379
|
}
|
|
21073
21380
|
default: {
|
|
@@ -21077,7 +21384,9 @@ async function runLoop(generateFn, compileCheckFn, config = {}, taskTitle = "com
|
|
|
21077
21384
|
iterations: state.iteration,
|
|
21078
21385
|
totalErrors,
|
|
21079
21386
|
finalPhase: state.phase,
|
|
21080
|
-
errorHistory: state.errorHistory
|
|
21387
|
+
errorHistory: state.errorHistory,
|
|
21388
|
+
compileCheck,
|
|
21389
|
+
compileCheckSkipReason
|
|
21081
21390
|
};
|
|
21082
21391
|
}
|
|
21083
21392
|
}
|
|
@@ -21134,6 +21443,15 @@ function buildEscalationReport(taskTitle, state, originalTask) {
|
|
|
21134
21443
|
recommendedAction
|
|
21135
21444
|
};
|
|
21136
21445
|
}
|
|
21446
|
+
function skippedCompileResult(reason) {
|
|
21447
|
+
return {
|
|
21448
|
+
...CLEAN_COMPILE,
|
|
21449
|
+
skipped: true,
|
|
21450
|
+
skipReason: reason,
|
|
21451
|
+
stderr: `compileCheck: skipped
|
|
21452
|
+
${reason}`
|
|
21453
|
+
};
|
|
21454
|
+
}
|
|
21137
21455
|
function shouldRunAgentLoop(type, agentType, phase) {
|
|
21138
21456
|
if (phase === "implement" || phase === "test")
|
|
21139
21457
|
return true;
|
|
@@ -21146,11 +21464,13 @@ async function runLoopForProject(projectRoot, options) {
|
|
|
21146
21464
|
const loopCfg = configResult.success ? configResult.data.loop : undefined;
|
|
21147
21465
|
const compileCheck = configResult.success ? configResult.data.safety.compileCheck : undefined;
|
|
21148
21466
|
const compileCheckFn = async () => {
|
|
21149
|
-
if (!compileCheck?.enabled || !compileCheck.command)
|
|
21150
|
-
return
|
|
21467
|
+
if (!compileCheck?.enabled || !compileCheck.command) {
|
|
21468
|
+
return skippedCompileResult(!compileCheck?.enabled ? "Compile check is not enabled in config." : "Compile check is enabled but no command is configured.");
|
|
21469
|
+
}
|
|
21151
21470
|
const allowed = options.allowCompileCheck === true || isAllowlistedCompileCommand(compileCheck.command);
|
|
21152
|
-
if (!allowed)
|
|
21153
|
-
return
|
|
21471
|
+
if (!allowed) {
|
|
21472
|
+
return skippedCompileResult("Compile check not executed: the command is not on the compile allowlist. " + "Re-run with --allow-compile-check to trust it.");
|
|
21473
|
+
}
|
|
21154
21474
|
const result = await runCompileCheck({
|
|
21155
21475
|
command: compileCheck.command,
|
|
21156
21476
|
cwd: projectRoot,
|
|
@@ -21737,10 +22057,15 @@ var init_prompt_compiler = __esm(() => {
|
|
|
21737
22057
|
"needsConfirmation": true
|
|
21738
22058
|
}`,
|
|
21739
22059
|
"council-verdict": `{
|
|
21740
|
-
"status": "APPROVED" | "
|
|
21741
|
-
"
|
|
22060
|
+
"status": "APPROVED" | "REJECTED" | "ESCALATED",
|
|
22061
|
+
"totalAgents": 0,
|
|
22062
|
+
"approvedCount": 0,
|
|
22063
|
+
"rejectedCount": 0,
|
|
22064
|
+
"abstainedCount": 0,
|
|
22065
|
+
"vetoApplied": false,
|
|
21742
22066
|
"findings": [],
|
|
21743
|
-
"
|
|
22067
|
+
"summary": "",
|
|
22068
|
+
"individualVerdicts": []
|
|
21744
22069
|
}`,
|
|
21745
22070
|
"technical-plan": '{ "approach": "", "filesAffected": [], "risks": [] }',
|
|
21746
22071
|
"fix-intake-output": '{ "actualBehavior": "", "expectedBehavior": "", "reproductionSteps": [] }',
|
|
@@ -21784,7 +22109,14 @@ function resolveOutputSchemaName(schemaName, role) {
|
|
|
21784
22109
|
}
|
|
21785
22110
|
function validateAgentOutputBySchema(schemaName, data, role) {
|
|
21786
22111
|
const resolved = resolveOutputSchemaName(schemaName, role);
|
|
21787
|
-
const schema = SCHEMA_REGISTRY[resolved]
|
|
22112
|
+
const schema = SCHEMA_REGISTRY[resolved];
|
|
22113
|
+
if (!schema) {
|
|
22114
|
+
return {
|
|
22115
|
+
valid: false,
|
|
22116
|
+
schema: resolved,
|
|
22117
|
+
errors: [`Unknown output schema: ${resolved}`]
|
|
22118
|
+
};
|
|
22119
|
+
}
|
|
21788
22120
|
const parsed = schema.safeParse(data);
|
|
21789
22121
|
if (parsed.success) {
|
|
21790
22122
|
return { valid: true, schema: resolved, data: parsed.data };
|
|
@@ -21843,8 +22175,14 @@ var init_output_validator = __esm(() => {
|
|
|
21843
22175
|
"review-report": ReviewerOutputSchema,
|
|
21844
22176
|
"technical-plan": TechnicalPlanOutputSchema,
|
|
21845
22177
|
"fix-intake-output": AgentOutputSchema,
|
|
21846
|
-
"council-verdict":
|
|
21847
|
-
"agent-output": AgentOutputSchema
|
|
22178
|
+
"council-verdict": CouncilVerdictSchema,
|
|
22179
|
+
"agent-output": AgentOutputSchema,
|
|
22180
|
+
taskcard: CanonicalTaskCardSchema,
|
|
22181
|
+
"test-plan": AgentOutputSchema,
|
|
22182
|
+
"complexity-audit": AgentOutputSchema,
|
|
22183
|
+
"api-contract": AgentOutputSchema,
|
|
22184
|
+
"pagespeed-report": AgentOutputSchema,
|
|
22185
|
+
"scorecard-record": ScorecardRecordSchema
|
|
21848
22186
|
};
|
|
21849
22187
|
});
|
|
21850
22188
|
|
|
@@ -21917,6 +22255,7 @@ var init_command_parser = __esm(() => {
|
|
|
21917
22255
|
"db-migration": { outputFormat: "plan", needConfirmation: true, riskThreshold: "high" },
|
|
21918
22256
|
pagespeed: { outputFormat: "verdict", needConfirmation: false, riskThreshold: "low" },
|
|
21919
22257
|
openspec: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" },
|
|
22258
|
+
backlog: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" },
|
|
21920
22259
|
scorecard: { outputFormat: "verdict", needConfirmation: false, riskThreshold: "low" },
|
|
21921
22260
|
council: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" },
|
|
21922
22261
|
iterative: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" },
|
|
@@ -21924,96 +22263,8 @@ var init_command_parser = __esm(() => {
|
|
|
21924
22263
|
triage: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" },
|
|
21925
22264
|
prototype: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" },
|
|
21926
22265
|
handoff: { outputFormat: "plan", needConfirmation: false, riskThreshold: "low" },
|
|
21927
|
-
clarify: { outputFormat: "plan", needConfirmation: false, riskThreshold: "low" }
|
|
21928
|
-
|
|
21929
|
-
});
|
|
21930
|
-
|
|
21931
|
-
// src/core/ccep/context-resolver.ts
|
|
21932
|
-
import { existsSync as existsSync5 } from "node:fs";
|
|
21933
|
-
import { readFile as readFile23 } from "node:fs/promises";
|
|
21934
|
-
import { join as join18 } from "node:path";
|
|
21935
|
-
async function loadProductKnowledge(projectRoot) {
|
|
21936
|
-
const graphPath = productGraphPath(projectRoot);
|
|
21937
|
-
if (!existsSync5(graphPath)) {
|
|
21938
|
-
return {};
|
|
21939
|
-
}
|
|
21940
|
-
try {
|
|
21941
|
-
const raw = await readFile23(graphPath, "utf-8");
|
|
21942
|
-
const graph = ProductGraphSchema.parse(JSON.parse(raw));
|
|
21943
|
-
return {
|
|
21944
|
-
productName: graph.productName,
|
|
21945
|
-
domains: queryNodes(graph, "domain").map((n) => n.name),
|
|
21946
|
-
decisions: queryNodes(graph, "decision").slice(0, 10).map((n) => ({ id: n.id, name: n.name, data: n.data })),
|
|
21947
|
-
risks: queryNodes(graph, "risk").slice(0, 10).map((n) => ({ id: n.id, name: n.name })),
|
|
21948
|
-
requirements: queryNodes(graph, "requirement").slice(0, 10).map((n) => ({ id: n.id, name: n.name, status: n.data.status })),
|
|
21949
|
-
nodeCount: graph.nodes.length
|
|
21950
|
-
};
|
|
21951
|
-
} catch {
|
|
21952
|
-
return {};
|
|
21953
|
-
}
|
|
21954
|
-
}
|
|
21955
|
-
async function resolveAstSource(projectRoot) {
|
|
21956
|
-
const graphPath = productGraphPath(projectRoot);
|
|
21957
|
-
if (existsSync5(graphPath)) {
|
|
21958
|
-
return { source: "product-graph", confidence: "high" };
|
|
21959
|
-
}
|
|
21960
|
-
const graphifyPath = join18(projectRoot, "graphify-out", "graph.json");
|
|
21961
|
-
if (existsSync5(graphifyPath)) {
|
|
21962
|
-
return { source: "graphify", confidence: "medium" };
|
|
21963
|
-
}
|
|
21964
|
-
try {
|
|
21965
|
-
const profile = await detectProject(projectRoot);
|
|
21966
|
-
if (profile.signals.length > 0) {
|
|
21967
|
-
return { source: "detect", confidence: profile.confidence };
|
|
21968
|
-
}
|
|
21969
|
-
} catch {}
|
|
21970
|
-
return { source: "manual", confidence: "low" };
|
|
21971
|
-
}
|
|
21972
|
-
async function resolveContext(envelope, profile, projectRoot) {
|
|
21973
|
-
const ast = await resolveAstSource(projectRoot);
|
|
21974
|
-
const knowledge = await loadProductKnowledge(projectRoot);
|
|
21975
|
-
const firstPhase = profile.phases[0];
|
|
21976
|
-
let stack = envelope.repoContext.stack;
|
|
21977
|
-
if (stack.length === 0 || stack.length === 1 && stack[0] === "unknown") {
|
|
21978
|
-
try {
|
|
21979
|
-
const detected = await detectProject(projectRoot);
|
|
21980
|
-
stack = [...new Set([...detected.languages, ...detected.frameworks])];
|
|
21981
|
-
} catch {
|
|
21982
|
-
stack = envelope.repoContext.stack;
|
|
21983
|
-
}
|
|
21984
|
-
}
|
|
21985
|
-
const context = {
|
|
21986
|
-
envelope,
|
|
21987
|
-
profile,
|
|
21988
|
-
intent: {
|
|
21989
|
-
type: envelope.command,
|
|
21990
|
-
goal: envelope.userRequest,
|
|
21991
|
-
domain: envelope.repoContext.domain
|
|
21992
|
-
},
|
|
21993
|
-
project: {
|
|
21994
|
-
name: envelope.projectId,
|
|
21995
|
-
rootDir: projectRoot,
|
|
21996
|
-
stack
|
|
21997
|
-
},
|
|
21998
|
-
knowledge,
|
|
21999
|
-
ast,
|
|
22000
|
-
policies: DEFAULT_POLICIES,
|
|
22001
|
-
outputSchema: firstPhase?.outputSchema ?? profile.intakeSchema ?? "agent-output"
|
|
22002
|
-
};
|
|
22003
|
-
return validateExecutionContext(context);
|
|
22004
|
-
}
|
|
22005
|
-
var DEFAULT_POLICIES;
|
|
22006
|
-
var init_context_resolver = __esm(() => {
|
|
22007
|
-
init_project_detector();
|
|
22008
|
-
init_schemas();
|
|
22009
|
-
init_command_parser();
|
|
22010
|
-
init_paths();
|
|
22011
|
-
init_graph_store();
|
|
22012
|
-
DEFAULT_POLICIES = {
|
|
22013
|
-
architecture: "modular",
|
|
22014
|
-
testing: "required",
|
|
22015
|
-
documentation: "required",
|
|
22016
|
-
breakingChanges: "approval"
|
|
22266
|
+
clarify: { outputFormat: "plan", needConfirmation: false, riskThreshold: "low" },
|
|
22267
|
+
security: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "high" }
|
|
22017
22268
|
};
|
|
22018
22269
|
});
|
|
22019
22270
|
|
|
@@ -22039,25 +22290,821 @@ var init_workflow_profile_loader = __esm(() => {
|
|
|
22039
22290
|
init_profiles();
|
|
22040
22291
|
});
|
|
22041
22292
|
|
|
22042
|
-
// src/
|
|
22043
|
-
|
|
22044
|
-
|
|
22045
|
-
|
|
22046
|
-
}
|
|
22047
|
-
function
|
|
22048
|
-
|
|
22049
|
-
|
|
22293
|
+
// src/core/evaluation/harness-catalog.ts
|
|
22294
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
22295
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
|
|
22296
|
+
import { copyFile as copyFile2, mkdir as mkdir17, readFile as readFile24, readdir as readdir6, rm as rm2, writeFile as writeFile16 } from "node:fs/promises";
|
|
22297
|
+
import { dirname as dirname7, join as join18, resolve as resolve23 } from "node:path";
|
|
22298
|
+
function catalogIds(catalog = DEFAULT_HARNESS_CATALOG) {
|
|
22299
|
+
return catalog.components.map((c) => c.id);
|
|
22300
|
+
}
|
|
22301
|
+
function isHarnessComponentId(value) {
|
|
22302
|
+
return catalogIds().includes(value);
|
|
22303
|
+
}
|
|
22304
|
+
function parseComponentsFlag(raw, catalog = DEFAULT_HARNESS_CATALOG) {
|
|
22305
|
+
if (!raw || raw.trim() === "" || raw.trim() === "all") {
|
|
22306
|
+
return ok(catalogIds(catalog));
|
|
22050
22307
|
}
|
|
22051
|
-
const
|
|
22052
|
-
|
|
22053
|
-
|
|
22308
|
+
const ids = [];
|
|
22309
|
+
for (const part of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
22310
|
+
if (!isHarnessComponentId(part)) {
|
|
22311
|
+
return err(new Error(`Unknown harness component: ${part}`));
|
|
22312
|
+
}
|
|
22313
|
+
ids.push(part);
|
|
22054
22314
|
}
|
|
22055
|
-
return
|
|
22315
|
+
return ok(ids);
|
|
22056
22316
|
}
|
|
22057
|
-
function
|
|
22058
|
-
return
|
|
22317
|
+
function variantIdFor(component) {
|
|
22318
|
+
return component ? `minus:${component}` : "baseline";
|
|
22059
22319
|
}
|
|
22060
|
-
|
|
22320
|
+
function parseVariantId(variantId) {
|
|
22321
|
+
if (!variantId || variantId === "baseline")
|
|
22322
|
+
return [];
|
|
22323
|
+
if (variantId.startsWith("minus:")) {
|
|
22324
|
+
const id = variantId.slice("minus:".length);
|
|
22325
|
+
return isHarnessComponentId(id) ? [id] : [];
|
|
22326
|
+
}
|
|
22327
|
+
return [];
|
|
22328
|
+
}
|
|
22329
|
+
function variantDirName(variantId) {
|
|
22330
|
+
return variantId.replaceAll(":", "-");
|
|
22331
|
+
}
|
|
22332
|
+
function componentStates(disabled, catalog = DEFAULT_HARNESS_CATALOG) {
|
|
22333
|
+
const off = new Set(disabled);
|
|
22334
|
+
const states = {};
|
|
22335
|
+
for (const entry of catalog.components) {
|
|
22336
|
+
states[entry.id] = entry.defaultOn && !off.has(entry.id);
|
|
22337
|
+
}
|
|
22338
|
+
return states;
|
|
22339
|
+
}
|
|
22340
|
+
function harnessFingerprint(disabled, contractVersion, ccepProfile = "feature", catalog = DEFAULT_HARNESS_CATALOG) {
|
|
22341
|
+
const states = componentStates(disabled, catalog);
|
|
22342
|
+
const payload = {
|
|
22343
|
+
components: Object.fromEntries(Object.entries(states).sort(([a], [b]) => a.localeCompare(b))),
|
|
22344
|
+
contractVersion,
|
|
22345
|
+
ccepProfile
|
|
22346
|
+
};
|
|
22347
|
+
return createHash3("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
|
|
22348
|
+
}
|
|
22349
|
+
async function loadHarnessCatalog(projectRoot) {
|
|
22350
|
+
const overridePath = resolve23(projectRoot, EVAL_DIR2, CATALOG_FILE);
|
|
22351
|
+
try {
|
|
22352
|
+
const raw = await readFile24(overridePath, "utf-8");
|
|
22353
|
+
return HarnessCatalogSchema.parse($parse(raw));
|
|
22354
|
+
} catch {
|
|
22355
|
+
return DEFAULT_HARNESS_CATALOG;
|
|
22356
|
+
}
|
|
22357
|
+
}
|
|
22358
|
+
function activeOverlayPath(projectRoot) {
|
|
22359
|
+
return resolve23(projectRoot, EVAL_DIR2, ACTIVE_OVERLAY_FILE);
|
|
22360
|
+
}
|
|
22361
|
+
async function readActiveOverlay(projectRoot) {
|
|
22362
|
+
try {
|
|
22363
|
+
const raw = await readFile24(activeOverlayPath(projectRoot), "utf-8");
|
|
22364
|
+
return HarnessOverlaySchema.parse($parse(raw));
|
|
22365
|
+
} catch {
|
|
22366
|
+
return null;
|
|
22367
|
+
}
|
|
22368
|
+
}
|
|
22369
|
+
function isProductGraphDisabled(projectRoot) {
|
|
22370
|
+
try {
|
|
22371
|
+
const path = activeOverlayPath(projectRoot);
|
|
22372
|
+
if (!existsSync5(path))
|
|
22373
|
+
return false;
|
|
22374
|
+
const overlay = HarnessOverlaySchema.parse($parse(readFileSync4(path, "utf-8")));
|
|
22375
|
+
return overlay.disableProductGraph === true || overlay.disabledComponents.includes("product_graph");
|
|
22376
|
+
} catch {
|
|
22377
|
+
return false;
|
|
22378
|
+
}
|
|
22379
|
+
}
|
|
22380
|
+
function cloneProfile(profile) {
|
|
22381
|
+
return structuredClone(profile);
|
|
22382
|
+
}
|
|
22383
|
+
function dropPhase(profile, phaseId) {
|
|
22384
|
+
profile.routing.default = profile.routing.default.filter((id) => id !== phaseId);
|
|
22385
|
+
if (profile.routing.riskRules) {
|
|
22386
|
+
for (const rule of profile.routing.riskRules) {
|
|
22387
|
+
rule.then = rule.then.filter((id) => id !== phaseId);
|
|
22388
|
+
}
|
|
22389
|
+
}
|
|
22390
|
+
}
|
|
22391
|
+
function disableGates(profile) {
|
|
22392
|
+
profile.confirmationGate.stopOnHighRisk = false;
|
|
22393
|
+
profile.confirmationGate.stopOnQuestions = false;
|
|
22394
|
+
for (const phase of profile.phases) {
|
|
22395
|
+
delete phase.stopGate;
|
|
22396
|
+
}
|
|
22397
|
+
}
|
|
22398
|
+
function disableCouncilPhases(profile) {
|
|
22399
|
+
for (const phaseId of ["deliberation", "council-review"]) {
|
|
22400
|
+
dropPhase(profile, phaseId);
|
|
22401
|
+
}
|
|
22402
|
+
}
|
|
22403
|
+
function applyCatalogToProfile(profile, disabled) {
|
|
22404
|
+
const next = cloneProfile(profile);
|
|
22405
|
+
for (const id of disabled) {
|
|
22406
|
+
const phaseId = PHASE_BY_COMPONENT[id];
|
|
22407
|
+
if (phaseId)
|
|
22408
|
+
dropPhase(next, phaseId);
|
|
22409
|
+
if (id === "confirmation_gates")
|
|
22410
|
+
disableGates(next);
|
|
22411
|
+
if (id === "council")
|
|
22412
|
+
disableCouncilPhases(next);
|
|
22413
|
+
}
|
|
22414
|
+
return next;
|
|
22415
|
+
}
|
|
22416
|
+
async function restoreOverlayBackup(projectRoot) {
|
|
22417
|
+
const backupRoot = resolve23(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
|
|
22418
|
+
if (!existsSync5(backupRoot))
|
|
22419
|
+
return;
|
|
22420
|
+
const configBackup = join18(backupRoot, "config.yml");
|
|
22421
|
+
const configDest = resolve23(projectRoot, ".codeconductor", "config.yml");
|
|
22422
|
+
if (existsSync5(configBackup)) {
|
|
22423
|
+
await mkdir17(dirname7(configDest), { recursive: true });
|
|
22424
|
+
await copyFile2(configBackup, configDest);
|
|
22425
|
+
}
|
|
22426
|
+
const workflowsBackup = join18(backupRoot, "workflows");
|
|
22427
|
+
const workflowsDest = resolve23(projectRoot, ".codeconductor", "workflows");
|
|
22428
|
+
if (existsSync5(workflowsBackup)) {
|
|
22429
|
+
await mkdir17(workflowsDest, { recursive: true });
|
|
22430
|
+
const files = await readdir6(workflowsBackup);
|
|
22431
|
+
for (const file of files) {
|
|
22432
|
+
await copyFile2(join18(workflowsBackup, file), join18(workflowsDest, file));
|
|
22433
|
+
}
|
|
22434
|
+
}
|
|
22435
|
+
const overlayBackup = join18(backupRoot, ACTIVE_OVERLAY_FILE);
|
|
22436
|
+
const overlayDest = activeOverlayPath(projectRoot);
|
|
22437
|
+
if (existsSync5(overlayBackup)) {
|
|
22438
|
+
await copyFile2(overlayBackup, overlayDest);
|
|
22439
|
+
} else if (existsSync5(overlayDest)) {
|
|
22440
|
+
await rm2(overlayDest, { force: true });
|
|
22441
|
+
}
|
|
22442
|
+
}
|
|
22443
|
+
async function snapshotForBackup(projectRoot) {
|
|
22444
|
+
const backupRoot = resolve23(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
|
|
22445
|
+
if (existsSync5(backupRoot))
|
|
22446
|
+
return;
|
|
22447
|
+
await mkdir17(backupRoot, { recursive: true });
|
|
22448
|
+
const configPath = resolve23(projectRoot, ".codeconductor", "config.yml");
|
|
22449
|
+
if (existsSync5(configPath)) {
|
|
22450
|
+
await copyFile2(configPath, join18(backupRoot, "config.yml"));
|
|
22451
|
+
}
|
|
22452
|
+
const workflowsDir = resolve23(projectRoot, ".codeconductor", "workflows");
|
|
22453
|
+
if (existsSync5(workflowsDir)) {
|
|
22454
|
+
const dest = join18(backupRoot, "workflows");
|
|
22455
|
+
await mkdir17(dest, { recursive: true });
|
|
22456
|
+
const files = await readdir6(workflowsDir);
|
|
22457
|
+
for (const file of files) {
|
|
22458
|
+
if (file.endsWith(".yml")) {
|
|
22459
|
+
await copyFile2(join18(workflowsDir, file), join18(dest, file));
|
|
22460
|
+
}
|
|
22461
|
+
}
|
|
22462
|
+
}
|
|
22463
|
+
const overlay = activeOverlayPath(projectRoot);
|
|
22464
|
+
if (existsSync5(overlay)) {
|
|
22465
|
+
await copyFile2(overlay, join18(backupRoot, ACTIVE_OVERLAY_FILE));
|
|
22466
|
+
}
|
|
22467
|
+
}
|
|
22468
|
+
async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
|
|
22469
|
+
try {
|
|
22470
|
+
if (options.backup) {
|
|
22471
|
+
await restoreOverlayBackup(projectRoot);
|
|
22472
|
+
await snapshotForBackup(projectRoot);
|
|
22473
|
+
}
|
|
22474
|
+
const workflowsDir = resolve23(projectRoot, ".codeconductor", "workflows");
|
|
22475
|
+
await mkdir17(workflowsDir, { recursive: true });
|
|
22476
|
+
await mkdir17(resolve23(projectRoot, EVAL_DIR2), { recursive: true });
|
|
22477
|
+
for (const command of WORKFLOW_OVERLAY_COMMANDS) {
|
|
22478
|
+
const base = loadWorkflowProfile(command);
|
|
22479
|
+
const next = applyCatalogToProfile(base, disabled);
|
|
22480
|
+
await writeFile16(join18(workflowsDir, `${command}.yml`), $stringify(next), "utf-8");
|
|
22481
|
+
}
|
|
22482
|
+
const needsConfig = disabled.includes("compile_loop") || disabled.includes("council");
|
|
22483
|
+
if (needsConfig) {
|
|
22484
|
+
const loaded = await loadConfig(projectRoot);
|
|
22485
|
+
const current = loaded.success ? loaded.data : structuredClone(DEFAULT_CONFIG);
|
|
22486
|
+
if (disabled.includes("compile_loop")) {
|
|
22487
|
+
current.loop = { ...current.loop, enabled: false };
|
|
22488
|
+
}
|
|
22489
|
+
if (disabled.includes("council")) {
|
|
22490
|
+
current.presets = {
|
|
22491
|
+
...current.presets,
|
|
22492
|
+
council: { ...current.presets.council, enabled: false }
|
|
22493
|
+
};
|
|
22494
|
+
}
|
|
22495
|
+
const write = await writeConfig(projectRoot, current, true);
|
|
22496
|
+
if (!write.success) {
|
|
22497
|
+
return err(write.error);
|
|
22498
|
+
}
|
|
22499
|
+
}
|
|
22500
|
+
const overlay = HarnessOverlaySchema.parse({
|
|
22501
|
+
experimentId: options.experimentId,
|
|
22502
|
+
variantId: options.variantId ?? (disabled[0] ? variantIdFor(disabled[0]) : "baseline"),
|
|
22503
|
+
disabledComponents: [...disabled],
|
|
22504
|
+
disableProductGraph: disabled.includes("product_graph"),
|
|
22505
|
+
contractVersion: options.contractVersion,
|
|
22506
|
+
ccepProfile: options.ccepProfile ?? "feature"
|
|
22507
|
+
});
|
|
22508
|
+
await writeFile16(activeOverlayPath(projectRoot), $stringify(overlay), "utf-8");
|
|
22509
|
+
return ok(overlay);
|
|
22510
|
+
} catch (e) {
|
|
22511
|
+
return err(e instanceof Error ? e : new Error(String(e)));
|
|
22512
|
+
}
|
|
22513
|
+
}
|
|
22514
|
+
async function currentFingerprint(projectRoot, contractVersion) {
|
|
22515
|
+
const catalog = await loadHarnessCatalog(projectRoot);
|
|
22516
|
+
const overlay = await readActiveOverlay(projectRoot);
|
|
22517
|
+
const disabled = overlay?.disabledComponents ?? [];
|
|
22518
|
+
return {
|
|
22519
|
+
fingerprint: harnessFingerprint(disabled, overlay?.contractVersion ?? contractVersion, overlay?.ccepProfile ?? "feature", catalog),
|
|
22520
|
+
overlay,
|
|
22521
|
+
states: componentStates(disabled, catalog)
|
|
22522
|
+
};
|
|
22523
|
+
}
|
|
22524
|
+
var EVAL_DIR2 = ".codeconductor/evaluation", ACTIVE_OVERLAY_FILE = "active-overlay.yml", CATALOG_FILE = "harness-catalog.yml", OVERLAY_BACKUP_DIR = "overlay-backup", WORKFLOW_OVERLAY_COMMANDS, PHASE_BY_COMPONENT, DEFAULT_HARNESS_CATALOG;
|
|
22525
|
+
var init_harness_catalog = __esm(() => {
|
|
22526
|
+
init_dist();
|
|
22527
|
+
init_schemas();
|
|
22528
|
+
init_codeconductor_config();
|
|
22529
|
+
init_config_loader();
|
|
22530
|
+
init_config_writer();
|
|
22531
|
+
init_workflow_profile_loader();
|
|
22532
|
+
WORKFLOW_OVERLAY_COMMANDS = ["feature", "fix", "refactor", "council"];
|
|
22533
|
+
PHASE_BY_COMPONENT = {
|
|
22534
|
+
wayfinding: "wayfinding",
|
|
22535
|
+
intake: "intake",
|
|
22536
|
+
design: "design",
|
|
22537
|
+
test_first: "test",
|
|
22538
|
+
review: "review",
|
|
22539
|
+
docs: "docs"
|
|
22540
|
+
};
|
|
22541
|
+
DEFAULT_HARNESS_CATALOG = {
|
|
22542
|
+
version: 1,
|
|
22543
|
+
components: [
|
|
22544
|
+
{
|
|
22545
|
+
id: "wayfinding",
|
|
22546
|
+
layer: "phase",
|
|
22547
|
+
label: "Wayfinding (repo-explorer)",
|
|
22548
|
+
defaultOn: true,
|
|
22549
|
+
toggle: { kind: "ccep_phase", phaseId: "wayfinding" }
|
|
22550
|
+
},
|
|
22551
|
+
{
|
|
22552
|
+
id: "intake",
|
|
22553
|
+
layer: "phase",
|
|
22554
|
+
label: "Intake (task-coach)",
|
|
22555
|
+
defaultOn: true,
|
|
22556
|
+
toggle: { kind: "ccep_phase", phaseId: "intake" }
|
|
22557
|
+
},
|
|
22558
|
+
{
|
|
22559
|
+
id: "design",
|
|
22560
|
+
layer: "phase",
|
|
22561
|
+
label: "Design (architect)",
|
|
22562
|
+
defaultOn: true,
|
|
22563
|
+
toggle: { kind: "ccep_phase", phaseId: "design" }
|
|
22564
|
+
},
|
|
22565
|
+
{
|
|
22566
|
+
id: "test_first",
|
|
22567
|
+
layer: "phase",
|
|
22568
|
+
label: "Test-first (tester before implement)",
|
|
22569
|
+
defaultOn: true,
|
|
22570
|
+
toggle: { kind: "ccep_phase", phaseId: "test" }
|
|
22571
|
+
},
|
|
22572
|
+
{
|
|
22573
|
+
id: "review",
|
|
22574
|
+
layer: "phase",
|
|
22575
|
+
label: "Review (reviewer)",
|
|
22576
|
+
defaultOn: true,
|
|
22577
|
+
toggle: { kind: "ccep_phase", phaseId: "review" }
|
|
22578
|
+
},
|
|
22579
|
+
{
|
|
22580
|
+
id: "docs",
|
|
22581
|
+
layer: "phase",
|
|
22582
|
+
label: "Docs",
|
|
22583
|
+
defaultOn: true,
|
|
22584
|
+
toggle: { kind: "ccep_phase", phaseId: "docs" }
|
|
22585
|
+
},
|
|
22586
|
+
{
|
|
22587
|
+
id: "confirmation_gates",
|
|
22588
|
+
layer: "gate",
|
|
22589
|
+
label: "Confirmation / stop gates",
|
|
22590
|
+
defaultOn: true,
|
|
22591
|
+
toggle: { kind: "ccep_gates" }
|
|
22592
|
+
},
|
|
22593
|
+
{
|
|
22594
|
+
id: "compile_loop",
|
|
22595
|
+
layer: "loop",
|
|
22596
|
+
label: "Compile-fix loop",
|
|
22597
|
+
defaultOn: true,
|
|
22598
|
+
toggle: { kind: "config_loop" }
|
|
22599
|
+
},
|
|
22600
|
+
{
|
|
22601
|
+
id: "council",
|
|
22602
|
+
layer: "council",
|
|
22603
|
+
label: "Council deliberation and review",
|
|
22604
|
+
defaultOn: true,
|
|
22605
|
+
toggle: { kind: "config_council" }
|
|
22606
|
+
},
|
|
22607
|
+
{
|
|
22608
|
+
id: "product_graph",
|
|
22609
|
+
layer: "knowledge",
|
|
22610
|
+
label: "Product-graph knowledge layer",
|
|
22611
|
+
defaultOn: true,
|
|
22612
|
+
toggle: { kind: "product_graph" }
|
|
22613
|
+
}
|
|
22614
|
+
]
|
|
22615
|
+
};
|
|
22616
|
+
});
|
|
22617
|
+
|
|
22618
|
+
// src/core/ccep/context-resolver.ts
|
|
22619
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
22620
|
+
import { readFile as readFile25 } from "node:fs/promises";
|
|
22621
|
+
import { join as join19 } from "node:path";
|
|
22622
|
+
async function loadProductKnowledge(projectRoot) {
|
|
22623
|
+
if (isProductGraphDisabled(projectRoot)) {
|
|
22624
|
+
return {};
|
|
22625
|
+
}
|
|
22626
|
+
const graphPath = productGraphPath(projectRoot);
|
|
22627
|
+
if (!existsSync6(graphPath)) {
|
|
22628
|
+
return {};
|
|
22629
|
+
}
|
|
22630
|
+
try {
|
|
22631
|
+
const raw = await readFile25(graphPath, "utf-8");
|
|
22632
|
+
const graph = ProductGraphSchema.parse(JSON.parse(raw));
|
|
22633
|
+
return {
|
|
22634
|
+
productName: graph.productName,
|
|
22635
|
+
domains: queryNodes(graph, "domain").map((n) => n.name),
|
|
22636
|
+
decisions: queryNodes(graph, "decision").slice(0, 10).map((n) => ({ id: n.id, name: n.name, data: n.data })),
|
|
22637
|
+
risks: queryNodes(graph, "risk").slice(0, 10).map((n) => ({ id: n.id, name: n.name })),
|
|
22638
|
+
requirements: queryNodes(graph, "requirement").slice(0, 10).map((n) => ({ id: n.id, name: n.name, status: n.data.status })),
|
|
22639
|
+
nodeCount: graph.nodes.length
|
|
22640
|
+
};
|
|
22641
|
+
} catch {
|
|
22642
|
+
return {};
|
|
22643
|
+
}
|
|
22644
|
+
}
|
|
22645
|
+
async function resolveAstSource(projectRoot) {
|
|
22646
|
+
if (isProductGraphDisabled(projectRoot)) {
|
|
22647
|
+
const graphifyPath2 = join19(projectRoot, "graphify-out", "graph.json");
|
|
22648
|
+
if (existsSync6(graphifyPath2)) {
|
|
22649
|
+
return { source: "graphify", confidence: "medium" };
|
|
22650
|
+
}
|
|
22651
|
+
return { source: "manual", confidence: "low" };
|
|
22652
|
+
}
|
|
22653
|
+
const graphPath = productGraphPath(projectRoot);
|
|
22654
|
+
if (existsSync6(graphPath)) {
|
|
22655
|
+
return { source: "product-graph", confidence: "high" };
|
|
22656
|
+
}
|
|
22657
|
+
const graphifyPath = join19(projectRoot, "graphify-out", "graph.json");
|
|
22658
|
+
if (existsSync6(graphifyPath)) {
|
|
22659
|
+
return { source: "graphify", confidence: "medium" };
|
|
22660
|
+
}
|
|
22661
|
+
try {
|
|
22662
|
+
const profile = await detectProject(projectRoot);
|
|
22663
|
+
if (profile.signals.length > 0) {
|
|
22664
|
+
return { source: "detect", confidence: profile.confidence };
|
|
22665
|
+
}
|
|
22666
|
+
} catch {}
|
|
22667
|
+
return { source: "manual", confidence: "low" };
|
|
22668
|
+
}
|
|
22669
|
+
async function resolveContext(envelope, profile, projectRoot) {
|
|
22670
|
+
const ast = await resolveAstSource(projectRoot);
|
|
22671
|
+
const knowledge = await loadProductKnowledge(projectRoot);
|
|
22672
|
+
const firstPhase = profile.phases[0];
|
|
22673
|
+
let stack = envelope.repoContext.stack;
|
|
22674
|
+
if (stack.length === 0 || stack.length === 1 && stack[0] === "unknown") {
|
|
22675
|
+
try {
|
|
22676
|
+
const detected = await detectProject(projectRoot);
|
|
22677
|
+
stack = [...new Set([...detected.languages, ...detected.frameworks])];
|
|
22678
|
+
} catch {
|
|
22679
|
+
stack = envelope.repoContext.stack;
|
|
22680
|
+
}
|
|
22681
|
+
}
|
|
22682
|
+
const context = {
|
|
22683
|
+
envelope,
|
|
22684
|
+
profile,
|
|
22685
|
+
intent: {
|
|
22686
|
+
type: envelope.command,
|
|
22687
|
+
goal: envelope.userRequest,
|
|
22688
|
+
domain: envelope.repoContext.domain
|
|
22689
|
+
},
|
|
22690
|
+
project: {
|
|
22691
|
+
name: envelope.projectId,
|
|
22692
|
+
rootDir: projectRoot,
|
|
22693
|
+
stack
|
|
22694
|
+
},
|
|
22695
|
+
knowledge,
|
|
22696
|
+
ast,
|
|
22697
|
+
policies: DEFAULT_POLICIES,
|
|
22698
|
+
outputSchema: firstPhase?.outputSchema ?? profile.intakeSchema ?? "agent-output"
|
|
22699
|
+
};
|
|
22700
|
+
return validateExecutionContext(context);
|
|
22701
|
+
}
|
|
22702
|
+
var DEFAULT_POLICIES;
|
|
22703
|
+
var init_context_resolver = __esm(() => {
|
|
22704
|
+
init_project_detector();
|
|
22705
|
+
init_schemas();
|
|
22706
|
+
init_command_parser();
|
|
22707
|
+
init_paths();
|
|
22708
|
+
init_graph_store();
|
|
22709
|
+
init_harness_catalog();
|
|
22710
|
+
DEFAULT_POLICIES = {
|
|
22711
|
+
architecture: "modular",
|
|
22712
|
+
testing: "required",
|
|
22713
|
+
documentation: "required",
|
|
22714
|
+
breakingChanges: "approval"
|
|
22715
|
+
};
|
|
22716
|
+
});
|
|
22717
|
+
|
|
22718
|
+
// src/domain/council/council-consensus.ts
|
|
22719
|
+
function canonicalAgentId(agentId) {
|
|
22720
|
+
return agentId.trim().toLowerCase();
|
|
22721
|
+
}
|
|
22722
|
+
function isSecurityCategory(category) {
|
|
22723
|
+
return SECURITY_CATEGORIES.has(category.trim().toLowerCase());
|
|
22724
|
+
}
|
|
22725
|
+
function resolveQuorum(config, ballotCount) {
|
|
22726
|
+
if (typeof config.quorum === "number") {
|
|
22727
|
+
const required = Math.max(1, config.quorum);
|
|
22728
|
+
return { required, met: ballotCount >= required };
|
|
22729
|
+
}
|
|
22730
|
+
if (config.expectedAgentIds && config.expectedAgentIds.length > 0) {
|
|
22731
|
+
const required = Math.ceil(config.expectedAgentIds.length / 2);
|
|
22732
|
+
return { required, met: ballotCount >= required };
|
|
22733
|
+
}
|
|
22734
|
+
if (config.algorithm === "majority") {
|
|
22735
|
+
return { required: 3, met: ballotCount >= 3 };
|
|
22736
|
+
}
|
|
22737
|
+
return { required: 1, met: ballotCount >= 1 };
|
|
22738
|
+
}
|
|
22739
|
+
function validateBallotBox(verdicts, expectedAgentIds) {
|
|
22740
|
+
if (expectedAgentIds !== undefined) {
|
|
22741
|
+
if (expectedAgentIds.length === 0 || expectedAgentIds.some((id) => typeof id !== "string" || canonicalAgentId(id) === "")) {
|
|
22742
|
+
return "invalid expected agent roster";
|
|
22743
|
+
}
|
|
22744
|
+
const canonicalExpected = expectedAgentIds.map(canonicalAgentId);
|
|
22745
|
+
if (new Set(canonicalExpected).size !== canonicalExpected.length) {
|
|
22746
|
+
return "duplicate agent IDs in expected roster";
|
|
22747
|
+
}
|
|
22748
|
+
}
|
|
22749
|
+
const invalidCount = verdicts.filter((v) => typeof v.agentId !== "string" || v.agentId.trim() === "" || !VALID_STATUSES.has(v.status)).length;
|
|
22750
|
+
if (invalidCount > 0) {
|
|
22751
|
+
return `${invalidCount} invalid verdict(s) received`;
|
|
22752
|
+
}
|
|
22753
|
+
const missingConfidence = verdicts.filter((v) => typeof v.confidence !== "number").length;
|
|
22754
|
+
if (missingConfidence > 0) {
|
|
22755
|
+
return `${missingConfidence} verdict(s) missing confidence`;
|
|
22756
|
+
}
|
|
22757
|
+
const seen = new Set;
|
|
22758
|
+
const duplicates = new Set;
|
|
22759
|
+
for (const v of verdicts) {
|
|
22760
|
+
const canonicalId = canonicalAgentId(v.agentId);
|
|
22761
|
+
if (seen.has(canonicalId)) {
|
|
22762
|
+
duplicates.add(v.agentId);
|
|
22763
|
+
}
|
|
22764
|
+
seen.add(canonicalId);
|
|
22765
|
+
}
|
|
22766
|
+
if (duplicates.size > 0) {
|
|
22767
|
+
return `duplicate verdicts from ${[...duplicates].join(", ")}`;
|
|
22768
|
+
}
|
|
22769
|
+
return;
|
|
22770
|
+
}
|
|
22771
|
+
function unanimousFailure(verdicts, expectedAgentIds) {
|
|
22772
|
+
const box = validateBallotBox(verdicts, expectedAgentIds);
|
|
22773
|
+
if (box)
|
|
22774
|
+
return box;
|
|
22775
|
+
if (expectedAgentIds === undefined) {
|
|
22776
|
+
return "missing expected agent roster";
|
|
22777
|
+
}
|
|
22778
|
+
const seen = new Set(verdicts.map((v) => canonicalAgentId(v.agentId)));
|
|
22779
|
+
const missing = expectedAgentIds.filter((id) => !seen.has(canonicalAgentId(id)));
|
|
22780
|
+
if (missing.length > 0) {
|
|
22781
|
+
return `missing verdicts from ${missing.join(", ")}`;
|
|
22782
|
+
}
|
|
22783
|
+
const expected = new Set(expectedAgentIds.map(canonicalAgentId));
|
|
22784
|
+
const unexpected = verdicts.filter((v) => !expected.has(canonicalAgentId(v.agentId))).map((v) => v.agentId);
|
|
22785
|
+
if (unexpected.length > 0) {
|
|
22786
|
+
return `unexpected verdicts from ${unexpected.join(", ")}`;
|
|
22787
|
+
}
|
|
22788
|
+
const nonApproving = verdicts.filter((v) => v.status !== "APPROVED").length;
|
|
22789
|
+
if (nonApproving > 0) {
|
|
22790
|
+
return `${nonApproving} agent(s) did not approve`;
|
|
22791
|
+
}
|
|
22792
|
+
return;
|
|
22793
|
+
}
|
|
22794
|
+
function escalated(verdicts, summary, counts) {
|
|
22795
|
+
return {
|
|
22796
|
+
status: "ESCALATED",
|
|
22797
|
+
totalAgents: verdicts.length,
|
|
22798
|
+
approvedCount: counts?.approvedCount ?? 0,
|
|
22799
|
+
rejectedCount: counts?.rejectedCount ?? 0,
|
|
22800
|
+
abstainedCount: counts?.abstainedCount ?? 0,
|
|
22801
|
+
vetoApplied: false,
|
|
22802
|
+
complianceVetoApplied: false,
|
|
22803
|
+
averageConfidence: counts?.averageConfidence,
|
|
22804
|
+
findings: counts?.findings ? [...counts.findings] : [],
|
|
22805
|
+
summary,
|
|
22806
|
+
individualVerdicts: verdicts
|
|
22807
|
+
};
|
|
22808
|
+
}
|
|
22809
|
+
function councilConsensus(verdicts, config = DEFAULT_CONFIG2) {
|
|
22810
|
+
const totalAgents = verdicts.length;
|
|
22811
|
+
if (totalAgents === 0) {
|
|
22812
|
+
return escalated(verdicts, "No verdicts submitted — escalated for human review.");
|
|
22813
|
+
}
|
|
22814
|
+
const ballotFailure = validateBallotBox(verdicts, config.expectedAgentIds);
|
|
22815
|
+
if (ballotFailure) {
|
|
22816
|
+
return escalated(verdicts, `Ballot box invalid: ${ballotFailure} — escalated for human review.`);
|
|
22817
|
+
}
|
|
22818
|
+
let approvedCount = 0;
|
|
22819
|
+
let rejectedCount = 0;
|
|
22820
|
+
let abstainedCount = 0;
|
|
22821
|
+
let vetoApplied = false;
|
|
22822
|
+
let complianceVetoApplied = false;
|
|
22823
|
+
let vetoByAgentId;
|
|
22824
|
+
let totalConfidence = 0;
|
|
22825
|
+
let hasLowConfidence = false;
|
|
22826
|
+
const allFindings = [];
|
|
22827
|
+
const allowCompliance = typeof config.allowComplianceVeto === "boolean" ? config.allowComplianceVeto : true;
|
|
22828
|
+
const policy = config.criticalFindingsPolicy ?? "escalate";
|
|
22829
|
+
for (const v of verdicts) {
|
|
22830
|
+
switch (v.status) {
|
|
22831
|
+
case "APPROVED":
|
|
22832
|
+
approvedCount++;
|
|
22833
|
+
break;
|
|
22834
|
+
case "REJECTED":
|
|
22835
|
+
rejectedCount++;
|
|
22836
|
+
break;
|
|
22837
|
+
case "ABSTAIN":
|
|
22838
|
+
abstainedCount++;
|
|
22839
|
+
break;
|
|
22840
|
+
}
|
|
22841
|
+
if (typeof v.confidence !== "number") {
|
|
22842
|
+
return escalated(verdicts, "Ballot box invalid: verdict(s) missing confidence — escalated for human review.");
|
|
22843
|
+
}
|
|
22844
|
+
const confidence = v.confidence;
|
|
22845
|
+
totalConfidence += confidence;
|
|
22846
|
+
if (confidence < 0.6) {
|
|
22847
|
+
hasLowConfidence = true;
|
|
22848
|
+
}
|
|
22849
|
+
if (config.allowSecurityVeto && v.securityVeto && v.status === "REJECTED") {
|
|
22850
|
+
vetoApplied = true;
|
|
22851
|
+
vetoByAgentId = v.agentId;
|
|
22852
|
+
}
|
|
22853
|
+
if (allowCompliance && v.complianceVeto && v.status === "REJECTED") {
|
|
22854
|
+
complianceVetoApplied = true;
|
|
22855
|
+
vetoByAgentId = v.agentId;
|
|
22856
|
+
}
|
|
22857
|
+
for (const finding of v.findings) {
|
|
22858
|
+
allFindings.push(finding);
|
|
22859
|
+
if (config.allowSecurityVeto && finding.severity === "critical" && isSecurityCategory(finding.category)) {
|
|
22860
|
+
vetoApplied = true;
|
|
22861
|
+
vetoByAgentId = v.agentId;
|
|
22862
|
+
}
|
|
22863
|
+
}
|
|
22864
|
+
}
|
|
22865
|
+
const averageConfidence = totalConfidence / totalAgents;
|
|
22866
|
+
const counts = {
|
|
22867
|
+
approvedCount,
|
|
22868
|
+
rejectedCount,
|
|
22869
|
+
abstainedCount,
|
|
22870
|
+
averageConfidence,
|
|
22871
|
+
findings: allFindings
|
|
22872
|
+
};
|
|
22873
|
+
if (vetoApplied || complianceVetoApplied) {
|
|
22874
|
+
const vetoAgent = verdicts.find((v) => v.agentId === vetoByAgentId);
|
|
22875
|
+
const vetoType = vetoApplied ? "Security" : "Compliance";
|
|
22876
|
+
return {
|
|
22877
|
+
status: "REJECTED",
|
|
22878
|
+
totalAgents,
|
|
22879
|
+
approvedCount,
|
|
22880
|
+
rejectedCount,
|
|
22881
|
+
abstainedCount,
|
|
22882
|
+
vetoApplied,
|
|
22883
|
+
complianceVetoApplied,
|
|
22884
|
+
vetoByAgentId,
|
|
22885
|
+
averageConfidence,
|
|
22886
|
+
findings: allFindings,
|
|
22887
|
+
summary: `${vetoType} veto applied by ${vetoAgent?.agentRole ?? vetoByAgentId} — rejected regardless of majority.`,
|
|
22888
|
+
individualVerdicts: verdicts
|
|
22889
|
+
};
|
|
22890
|
+
}
|
|
22891
|
+
const critical = allFindings.filter((f) => f.severity === "critical");
|
|
22892
|
+
if (critical.length > 0 && policy === "reject") {
|
|
22893
|
+
return {
|
|
22894
|
+
status: "REJECTED",
|
|
22895
|
+
totalAgents,
|
|
22896
|
+
approvedCount,
|
|
22897
|
+
rejectedCount,
|
|
22898
|
+
abstainedCount,
|
|
22899
|
+
vetoApplied: false,
|
|
22900
|
+
complianceVetoApplied: false,
|
|
22901
|
+
averageConfidence,
|
|
22902
|
+
findings: allFindings,
|
|
22903
|
+
summary: `${critical.length} critical finding(s) — rejected by criticalFindingsPolicy.`,
|
|
22904
|
+
individualVerdicts: verdicts
|
|
22905
|
+
};
|
|
22906
|
+
}
|
|
22907
|
+
if (critical.length > 0 && policy === "escalate") {
|
|
22908
|
+
return escalated(verdicts, `${critical.length} critical finding(s) — escalated by criticalFindingsPolicy.`, counts);
|
|
22909
|
+
}
|
|
22910
|
+
const quorum = resolveQuorum(config, totalAgents);
|
|
22911
|
+
if (!quorum.met) {
|
|
22912
|
+
return escalated(verdicts, `Quorum not reached (${totalAgents}/${quorum.required}) — escalated for human review.`, counts);
|
|
22913
|
+
}
|
|
22914
|
+
if (hasLowConfidence || averageConfidence < 0.7) {
|
|
22915
|
+
const reason = hasLowConfidence ? "one or more agents reported confidence below 0.6" : `average confidence (${averageConfidence.toFixed(2)}) is below 0.7`;
|
|
22916
|
+
return escalated(verdicts, `Confidence threshold breach: ${reason} — escalated for human review.`, {
|
|
22917
|
+
approvedCount,
|
|
22918
|
+
rejectedCount,
|
|
22919
|
+
abstainedCount,
|
|
22920
|
+
averageConfidence,
|
|
22921
|
+
findings: allFindings
|
|
22922
|
+
});
|
|
22923
|
+
}
|
|
22924
|
+
if (config.algorithm === "majority") {
|
|
22925
|
+
if (approvedCount > rejectedCount) {
|
|
22926
|
+
return {
|
|
22927
|
+
status: "APPROVED",
|
|
22928
|
+
totalAgents,
|
|
22929
|
+
approvedCount,
|
|
22930
|
+
rejectedCount,
|
|
22931
|
+
abstainedCount,
|
|
22932
|
+
vetoApplied: false,
|
|
22933
|
+
complianceVetoApplied: false,
|
|
22934
|
+
averageConfidence,
|
|
22935
|
+
findings: allFindings,
|
|
22936
|
+
summary: `Approved by majority (${approvedCount}/${totalAgents}).`,
|
|
22937
|
+
individualVerdicts: verdicts
|
|
22938
|
+
};
|
|
22939
|
+
}
|
|
22940
|
+
}
|
|
22941
|
+
if (config.algorithm === "unanimous") {
|
|
22942
|
+
const failure = unanimousFailure(verdicts, config.expectedAgentIds);
|
|
22943
|
+
if (!failure) {
|
|
22944
|
+
return {
|
|
22945
|
+
status: "APPROVED",
|
|
22946
|
+
totalAgents,
|
|
22947
|
+
approvedCount,
|
|
22948
|
+
rejectedCount,
|
|
22949
|
+
abstainedCount,
|
|
22950
|
+
vetoApplied: false,
|
|
22951
|
+
complianceVetoApplied: false,
|
|
22952
|
+
averageConfidence,
|
|
22953
|
+
findings: allFindings,
|
|
22954
|
+
summary: `Approved unanimously (${approvedCount}/${totalAgents}).`,
|
|
22955
|
+
individualVerdicts: verdicts
|
|
22956
|
+
};
|
|
22957
|
+
}
|
|
22958
|
+
return escalated(verdicts, `Unanimity not reached: ${failure} — escalated for human review.`, {
|
|
22959
|
+
approvedCount,
|
|
22960
|
+
rejectedCount,
|
|
22961
|
+
abstainedCount,
|
|
22962
|
+
averageConfidence,
|
|
22963
|
+
findings: allFindings
|
|
22964
|
+
});
|
|
22965
|
+
}
|
|
22966
|
+
return escalated(verdicts, `No clear consensus (${approvedCount} approved, ${rejectedCount} rejected, ${abstainedCount} abstained) — escalated for human review.`, {
|
|
22967
|
+
approvedCount,
|
|
22968
|
+
rejectedCount,
|
|
22969
|
+
abstainedCount,
|
|
22970
|
+
averageConfidence,
|
|
22971
|
+
findings: allFindings
|
|
22972
|
+
});
|
|
22973
|
+
}
|
|
22974
|
+
var DEFAULT_CONFIG2, VALID_STATUSES, SECURITY_CATEGORIES;
|
|
22975
|
+
var init_council_consensus = __esm(() => {
|
|
22976
|
+
DEFAULT_CONFIG2 = {
|
|
22977
|
+
algorithm: "majority",
|
|
22978
|
+
allowSecurityVeto: true,
|
|
22979
|
+
allowComplianceVeto: true,
|
|
22980
|
+
criticalFindingsPolicy: "escalate"
|
|
22981
|
+
};
|
|
22982
|
+
VALID_STATUSES = new Set(["APPROVED", "REJECTED", "ABSTAIN"]);
|
|
22983
|
+
SECURITY_CATEGORIES = new Set([
|
|
22984
|
+
"security",
|
|
22985
|
+
"auth",
|
|
22986
|
+
"injection",
|
|
22987
|
+
"credentials",
|
|
22988
|
+
"supply-chain"
|
|
22989
|
+
]);
|
|
22990
|
+
});
|
|
22991
|
+
|
|
22992
|
+
// src/core/shared/vague-criterion.ts
|
|
22993
|
+
function isVagueCriterion(text) {
|
|
22994
|
+
const t = text.trim();
|
|
22995
|
+
if (t.length < 8)
|
|
22996
|
+
return true;
|
|
22997
|
+
return VAGUE_PATTERNS.some((p) => p.test(t));
|
|
22998
|
+
}
|
|
22999
|
+
var VAGUE_PATTERNS;
|
|
23000
|
+
var init_vague_criterion = __esm(() => {
|
|
23001
|
+
VAGUE_PATTERNS = [
|
|
23002
|
+
/^mejorar\s/i,
|
|
23003
|
+
/^fix\s+bugs?$/i,
|
|
23004
|
+
/^refactor$/i,
|
|
23005
|
+
/^hacer\s+refactor/i,
|
|
23006
|
+
/^arreglar\s+bugs?/i,
|
|
23007
|
+
/^improve\s+ux$/i,
|
|
23008
|
+
/^better\s+ux$/i,
|
|
23009
|
+
/^cleanup$/i,
|
|
23010
|
+
/^misc$/i
|
|
23011
|
+
];
|
|
23012
|
+
});
|
|
23013
|
+
|
|
23014
|
+
// src/core/ccep/task-card-validator.ts
|
|
23015
|
+
function isNonEmpty(value) {
|
|
23016
|
+
if (value === undefined || value === null)
|
|
23017
|
+
return false;
|
|
23018
|
+
if (typeof value === "string")
|
|
23019
|
+
return value.trim().length > 0;
|
|
23020
|
+
if (Array.isArray(value)) {
|
|
23021
|
+
return value.length > 0 && value.every((entry) => isNonEmpty(entry));
|
|
23022
|
+
}
|
|
23023
|
+
return true;
|
|
23024
|
+
}
|
|
23025
|
+
function fieldValue(card, field) {
|
|
23026
|
+
if (field === "scope") {
|
|
23027
|
+
return card.targetFiles;
|
|
23028
|
+
}
|
|
23029
|
+
return card[field];
|
|
23030
|
+
}
|
|
23031
|
+
function validateTaskCardForProfile(profile, card) {
|
|
23032
|
+
const issues = [];
|
|
23033
|
+
const parsed = CanonicalTaskCardSchema.safeParse(card);
|
|
23034
|
+
if (!parsed.success) {
|
|
23035
|
+
issues.push({
|
|
23036
|
+
code: "SCHEMA",
|
|
23037
|
+
message: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
|
|
23038
|
+
});
|
|
23039
|
+
return issues;
|
|
23040
|
+
}
|
|
23041
|
+
const data = parsed.data;
|
|
23042
|
+
const required = profile.taskCard?.requiredFields ?? [];
|
|
23043
|
+
for (const field of required) {
|
|
23044
|
+
if (!isNonEmpty(fieldValue(data, field))) {
|
|
23045
|
+
issues.push({
|
|
23046
|
+
code: "MISSING_FIELD",
|
|
23047
|
+
message: `Required field "${field}" is missing or empty`,
|
|
23048
|
+
itemId: data.id
|
|
23049
|
+
});
|
|
23050
|
+
}
|
|
23051
|
+
}
|
|
23052
|
+
if (data.status === "draft") {
|
|
23053
|
+
issues.push({
|
|
23054
|
+
code: "NOT_ROUTABLE",
|
|
23055
|
+
message: 'TaskCard status "draft" is not routable',
|
|
23056
|
+
itemId: data.id
|
|
23057
|
+
});
|
|
23058
|
+
}
|
|
23059
|
+
if (data.risk === "high" && data.requiresHumanReview !== true) {
|
|
23060
|
+
issues.push({
|
|
23061
|
+
code: "HIGH_RISK_REVIEW",
|
|
23062
|
+
message: "risk: high requires requiresHumanReview: true",
|
|
23063
|
+
itemId: data.id
|
|
23064
|
+
});
|
|
23065
|
+
}
|
|
23066
|
+
if (data.type !== "docs" && data.type !== "review" && data.requiresTests !== true) {
|
|
23067
|
+
issues.push({
|
|
23068
|
+
code: "TESTS_REQUIRED",
|
|
23069
|
+
message: `type "${data.type}" requires requiresTests: true`,
|
|
23070
|
+
itemId: data.id
|
|
23071
|
+
});
|
|
23072
|
+
}
|
|
23073
|
+
for (const criterion of data.acceptanceCriteria) {
|
|
23074
|
+
if (isVagueCriterion(criterion)) {
|
|
23075
|
+
issues.push({
|
|
23076
|
+
code: "VAGUE_ACCEPTANCE",
|
|
23077
|
+
message: `Vague acceptance criterion: "${criterion}"`,
|
|
23078
|
+
itemId: data.id
|
|
23079
|
+
});
|
|
23080
|
+
}
|
|
23081
|
+
}
|
|
23082
|
+
return issues;
|
|
23083
|
+
}
|
|
23084
|
+
var init_task_card_validator = __esm(() => {
|
|
23085
|
+
init_schemas();
|
|
23086
|
+
init_vague_criterion();
|
|
23087
|
+
});
|
|
23088
|
+
|
|
23089
|
+
// src/commands/ccep.command.ts
|
|
23090
|
+
var exports_ccep_command = {};
|
|
23091
|
+
__export(exports_ccep_command, {
|
|
23092
|
+
ccepCommand: () => ccepCommand
|
|
23093
|
+
});
|
|
23094
|
+
function parseWorkflowCommand(command) {
|
|
23095
|
+
if (!command) {
|
|
23096
|
+
return { ok: false, error: "Missing required --command flag" };
|
|
23097
|
+
}
|
|
23098
|
+
const result = WorkflowCommandSchema.safeParse(command);
|
|
23099
|
+
if (!result.success) {
|
|
23100
|
+
return { ok: false, error: `Unknown workflow command: ${command}` };
|
|
23101
|
+
}
|
|
23102
|
+
return { ok: true, command: result.data };
|
|
23103
|
+
}
|
|
23104
|
+
function containedContextError(contextPath) {
|
|
23105
|
+
return `Could not read --context file: ${contextPath}. ` + "It must be a regular file at a relative path inside the project root.";
|
|
23106
|
+
}
|
|
23107
|
+
async function readContainedContext(projectRoot, contextPath) {
|
|
22061
23108
|
try {
|
|
22062
23109
|
return await readFileWithinRoot(projectRoot, contextPath);
|
|
22063
23110
|
} catch {
|
|
@@ -22095,6 +23142,37 @@ async function readInputPayload(projectRoot, input, rest) {
|
|
|
22095
23142
|
return { ok: false, error: `Invalid JSON input: ${String(err3)}` };
|
|
22096
23143
|
}
|
|
22097
23144
|
}
|
|
23145
|
+
function consensusExitCode(status) {
|
|
23146
|
+
if (status === "APPROVED")
|
|
23147
|
+
return 0;
|
|
23148
|
+
if (status === "REJECTED")
|
|
23149
|
+
return 1;
|
|
23150
|
+
return 2;
|
|
23151
|
+
}
|
|
23152
|
+
function parseConsensusPayload(data) {
|
|
23153
|
+
if (Array.isArray(data)) {
|
|
23154
|
+
return { ok: true, verdicts: data };
|
|
23155
|
+
}
|
|
23156
|
+
if (typeof data !== "object" || data === null) {
|
|
23157
|
+
return { ok: false, error: "Consensus input must be a verdicts array or { verdicts, config? }" };
|
|
23158
|
+
}
|
|
23159
|
+
const record = data;
|
|
23160
|
+
if (!Array.isArray(record.verdicts)) {
|
|
23161
|
+
return { ok: false, error: "Consensus input must be a verdicts array or { verdicts, config? }" };
|
|
23162
|
+
}
|
|
23163
|
+
let config;
|
|
23164
|
+
if (record.config !== undefined) {
|
|
23165
|
+
const parsed = ConsensusConfigSchema.safeParse(record.config);
|
|
23166
|
+
if (!parsed.success) {
|
|
23167
|
+
return {
|
|
23168
|
+
ok: false,
|
|
23169
|
+
error: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
|
|
23170
|
+
};
|
|
23171
|
+
}
|
|
23172
|
+
config = parsed.data;
|
|
23173
|
+
}
|
|
23174
|
+
return { ok: true, verdicts: record.verdicts, config };
|
|
23175
|
+
}
|
|
22098
23176
|
async function ccepCommand(options) {
|
|
22099
23177
|
const {
|
|
22100
23178
|
subcommand,
|
|
@@ -22106,6 +23184,7 @@ async function ccepCommand(options) {
|
|
|
22106
23184
|
input,
|
|
22107
23185
|
contextPath,
|
|
22108
23186
|
promptVersion = "v1.0.0",
|
|
23187
|
+
config,
|
|
22109
23188
|
rest
|
|
22110
23189
|
} = options;
|
|
22111
23190
|
switch (subcommand) {
|
|
@@ -22260,6 +23339,22 @@ async function ccepCommand(options) {
|
|
|
22260
23339
|
data: { success: false, command: "ccep validate", errors: [payload.error] }
|
|
22261
23340
|
};
|
|
22262
23341
|
}
|
|
23342
|
+
if (profile.taskCard && CanonicalTaskCardSchema.safeParse(payload.data).success) {
|
|
23343
|
+
const issues = validateTaskCardForProfile(profile, payload.data);
|
|
23344
|
+
return {
|
|
23345
|
+
code: issues.length === 0 ? 0 : 1,
|
|
23346
|
+
data: {
|
|
23347
|
+
success: issues.length === 0,
|
|
23348
|
+
command: "ccep validate",
|
|
23349
|
+
phase: phaseId,
|
|
23350
|
+
role: agentRole,
|
|
23351
|
+
schema: "taskcard",
|
|
23352
|
+
valid: issues.length === 0,
|
|
23353
|
+
issues,
|
|
23354
|
+
errors: issues.map((issue) => issue.message)
|
|
23355
|
+
}
|
|
23356
|
+
};
|
|
23357
|
+
}
|
|
22263
23358
|
const result = validateOutputForRole(agentRole, resolvedPhase.outputSchema, payload.data);
|
|
22264
23359
|
return {
|
|
22265
23360
|
code: result.valid ? 0 : 1,
|
|
@@ -22275,6 +23370,95 @@ async function ccepCommand(options) {
|
|
|
22275
23370
|
}
|
|
22276
23371
|
};
|
|
22277
23372
|
}
|
|
23373
|
+
case "taskcard": {
|
|
23374
|
+
const parsed = parseWorkflowCommand(command);
|
|
23375
|
+
if (!parsed.ok) {
|
|
23376
|
+
return {
|
|
23377
|
+
code: 1,
|
|
23378
|
+
data: { success: false, command: "ccep taskcard", errors: [parsed.error] }
|
|
23379
|
+
};
|
|
23380
|
+
}
|
|
23381
|
+
const profile = loadWorkflowProfile(parsed.command, projectRoot);
|
|
23382
|
+
if (!profile.taskCard) {
|
|
23383
|
+
return {
|
|
23384
|
+
code: 1,
|
|
23385
|
+
data: {
|
|
23386
|
+
success: false,
|
|
23387
|
+
command: "ccep taskcard",
|
|
23388
|
+
errors: [`Workflow "${parsed.command}" does not declare a taskCard contract`]
|
|
23389
|
+
}
|
|
23390
|
+
};
|
|
23391
|
+
}
|
|
23392
|
+
const payload = await readInputPayload(projectRoot, input, rest);
|
|
23393
|
+
if (!payload.ok) {
|
|
23394
|
+
return {
|
|
23395
|
+
code: 1,
|
|
23396
|
+
data: { success: false, command: "ccep taskcard", errors: [payload.error] }
|
|
23397
|
+
};
|
|
23398
|
+
}
|
|
23399
|
+
const issues = validateTaskCardForProfile(profile, payload.data);
|
|
23400
|
+
return {
|
|
23401
|
+
code: issues.length === 0 ? 0 : 1,
|
|
23402
|
+
data: {
|
|
23403
|
+
success: issues.length === 0,
|
|
23404
|
+
command: "ccep taskcard",
|
|
23405
|
+
workflow: parsed.command,
|
|
23406
|
+
issues,
|
|
23407
|
+
errors: issues.map((issue) => issue.message)
|
|
23408
|
+
}
|
|
23409
|
+
};
|
|
23410
|
+
}
|
|
23411
|
+
case "consensus": {
|
|
23412
|
+
const payload = await readInputPayload(projectRoot, input, rest);
|
|
23413
|
+
if (!payload.ok) {
|
|
23414
|
+
return {
|
|
23415
|
+
code: 1,
|
|
23416
|
+
data: { success: false, command: "ccep consensus", errors: [payload.error] }
|
|
23417
|
+
};
|
|
23418
|
+
}
|
|
23419
|
+
const parsedBallots = parseConsensusPayload(payload.data);
|
|
23420
|
+
if (!parsedBallots.ok) {
|
|
23421
|
+
return {
|
|
23422
|
+
code: 1,
|
|
23423
|
+
data: { success: false, command: "ccep consensus", errors: [parsedBallots.error] }
|
|
23424
|
+
};
|
|
23425
|
+
}
|
|
23426
|
+
let mergedConfig = parsedBallots.config;
|
|
23427
|
+
if (config) {
|
|
23428
|
+
const configPayload = await readInputPayload(projectRoot, config);
|
|
23429
|
+
if (!configPayload.ok) {
|
|
23430
|
+
return {
|
|
23431
|
+
code: 1,
|
|
23432
|
+
data: {
|
|
23433
|
+
success: false,
|
|
23434
|
+
command: "ccep consensus",
|
|
23435
|
+
errors: [`Invalid --config: ${configPayload.error}`]
|
|
23436
|
+
}
|
|
23437
|
+
};
|
|
23438
|
+
}
|
|
23439
|
+
const parsedConfig = ConsensusConfigSchema.safeParse(configPayload.data);
|
|
23440
|
+
if (!parsedConfig.success) {
|
|
23441
|
+
return {
|
|
23442
|
+
code: 1,
|
|
23443
|
+
data: {
|
|
23444
|
+
success: false,
|
|
23445
|
+
command: "ccep consensus",
|
|
23446
|
+
errors: parsedConfig.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
|
23447
|
+
}
|
|
23448
|
+
};
|
|
23449
|
+
}
|
|
23450
|
+
mergedConfig = { ...mergedConfig, ...parsedConfig.data };
|
|
23451
|
+
}
|
|
23452
|
+
const verdict = councilConsensus(parsedBallots.verdicts, mergedConfig);
|
|
23453
|
+
return {
|
|
23454
|
+
code: consensusExitCode(verdict.status),
|
|
23455
|
+
data: {
|
|
23456
|
+
success: verdict.status === "APPROVED",
|
|
23457
|
+
command: "ccep consensus",
|
|
23458
|
+
verdict
|
|
23459
|
+
}
|
|
23460
|
+
};
|
|
23461
|
+
}
|
|
22278
23462
|
case "evaluate": {
|
|
22279
23463
|
const parsed = parseWorkflowCommand(command);
|
|
22280
23464
|
if (!parsed.ok) {
|
|
@@ -22350,7 +23534,7 @@ async function ccepCommand(options) {
|
|
|
22350
23534
|
success: false,
|
|
22351
23535
|
command: "ccep",
|
|
22352
23536
|
errors: [
|
|
22353
|
-
`Unknown subcommand: ${subcommand}. Use: parse, profile, resolve, compile, validate, evaluate`
|
|
23537
|
+
`Unknown subcommand: ${subcommand}. Use: parse, profile, resolve, compile, validate, evaluate, consensus, taskcard`
|
|
22354
23538
|
]
|
|
22355
23539
|
}
|
|
22356
23540
|
};
|
|
@@ -22363,6 +23547,8 @@ var init_ccep_command = __esm(() => {
|
|
|
22363
23547
|
init_command_parser();
|
|
22364
23548
|
init_context_resolver();
|
|
22365
23549
|
init_workflow_profile_loader();
|
|
23550
|
+
init_council_consensus();
|
|
23551
|
+
init_task_card_validator();
|
|
22366
23552
|
init_schemas();
|
|
22367
23553
|
});
|
|
22368
23554
|
|
|
@@ -22398,12 +23584,6 @@ function detectCycle(items) {
|
|
|
22398
23584
|
}
|
|
22399
23585
|
return null;
|
|
22400
23586
|
}
|
|
22401
|
-
function isVagueCriterion(text) {
|
|
22402
|
-
const t = text.trim();
|
|
22403
|
-
if (t.length < 8)
|
|
22404
|
-
return true;
|
|
22405
|
-
return VAGUE_PATTERNS.some((p) => p.test(t));
|
|
22406
|
-
}
|
|
22407
23587
|
function validateBacklog(doc) {
|
|
22408
23588
|
const errors3 = [];
|
|
22409
23589
|
const recommendations = [];
|
|
@@ -22505,30 +23685,42 @@ function buildRecommendations(errors3, extra = []) {
|
|
|
22505
23685
|
}
|
|
22506
23686
|
return recs;
|
|
22507
23687
|
}
|
|
22508
|
-
var VAGUE_PATTERNS;
|
|
22509
23688
|
var init_backlog_validator = __esm(() => {
|
|
22510
23689
|
init_schemas();
|
|
22511
|
-
|
|
22512
|
-
/^mejorar\s/i,
|
|
22513
|
-
/^fix\s+bugs?$/i,
|
|
22514
|
-
/^refactor$/i,
|
|
22515
|
-
/^hacer\s+refactor/i,
|
|
22516
|
-
/^arreglar\s+bugs?/i,
|
|
22517
|
-
/^improve\s+ux$/i,
|
|
22518
|
-
/^better\s+ux$/i,
|
|
22519
|
-
/^cleanup$/i,
|
|
22520
|
-
/^misc$/i
|
|
22521
|
-
];
|
|
23690
|
+
init_vague_criterion();
|
|
22522
23691
|
});
|
|
22523
23692
|
|
|
22524
23693
|
// src/core/openspec/openspec-state.ts
|
|
22525
|
-
import { createHash as
|
|
22526
|
-
import { mkdir as
|
|
22527
|
-
import { resolve as
|
|
23694
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
23695
|
+
import { mkdir as mkdir18, readFile as readFile26, rename as rename2, writeFile as writeFile17 } from "node:fs/promises";
|
|
23696
|
+
import { resolve as resolve24 } from "node:path";
|
|
23697
|
+
function canTransition(from, to) {
|
|
23698
|
+
return ALLOWED_TRANSITIONS[from]?.includes(to) ?? false;
|
|
23699
|
+
}
|
|
23700
|
+
function escapeRegExp(value) {
|
|
23701
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
23702
|
+
}
|
|
23703
|
+
function applyBacklogTransition(content, itemId, from, to, progress) {
|
|
23704
|
+
if (from === to) {
|
|
23705
|
+
if (progress === undefined)
|
|
23706
|
+
return ok(content);
|
|
23707
|
+
return ok(updateBacklogItemInMarkdown(content, itemId, { progress }));
|
|
23708
|
+
}
|
|
23709
|
+
if (!canTransition(from, to)) {
|
|
23710
|
+
const allowed = ALLOWED_TRANSITIONS[from] ?? [];
|
|
23711
|
+
return err(new Error(`Illegal backlog transition for ${itemId}: ${from} → ${to}. ` + `Allowed: ${allowed.length > 0 ? allowed.join(", ") : "none"}.`));
|
|
23712
|
+
}
|
|
23713
|
+
return ok(updateBacklogItemInMarkdown(content, itemId, { status: to, progress }));
|
|
23714
|
+
}
|
|
23715
|
+
async function writeFileAtomic(path, content) {
|
|
23716
|
+
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
23717
|
+
await writeFile17(tmp, content, "utf-8");
|
|
23718
|
+
await rename2(tmp, path);
|
|
23719
|
+
}
|
|
22528
23720
|
async function loadOpenspecState(projectRoot) {
|
|
22529
23721
|
try {
|
|
22530
|
-
const filePath =
|
|
22531
|
-
const content = await
|
|
23722
|
+
const filePath = resolve24(projectRoot, STATE_FILE);
|
|
23723
|
+
const content = await readFile26(filePath, "utf-8");
|
|
22532
23724
|
const data = JSON.parse(content);
|
|
22533
23725
|
return ok(OpenspecStateSchema.parse(data));
|
|
22534
23726
|
} catch (e) {
|
|
@@ -22545,15 +23737,19 @@ async function loadOpenspecState(projectRoot) {
|
|
|
22545
23737
|
}
|
|
22546
23738
|
async function writeOpenspecState(projectRoot, state) {
|
|
22547
23739
|
try {
|
|
22548
|
-
const dir =
|
|
22549
|
-
await
|
|
23740
|
+
const dir = resolve24(projectRoot, ".codeconductor");
|
|
23741
|
+
await mkdir18(dir, { recursive: true });
|
|
22550
23742
|
const validated = OpenspecStateSchema.parse(state);
|
|
22551
|
-
await
|
|
23743
|
+
await writeFileAtomic(resolve24(dir, "openspec-state.json"), JSON.stringify(validated, null, 2));
|
|
22552
23744
|
return ok(undefined);
|
|
22553
23745
|
} catch (e) {
|
|
22554
23746
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
22555
23747
|
}
|
|
22556
23748
|
}
|
|
23749
|
+
function setTaskCardStatus(state, cardId, status) {
|
|
23750
|
+
const taskCards = state.taskCards.map((c) => c.id === cardId ? { ...c, status } : c);
|
|
23751
|
+
return { ...state, taskCards };
|
|
23752
|
+
}
|
|
22557
23753
|
function getNextTaskCard(state) {
|
|
22558
23754
|
const done = new Set(state.taskCards.filter((c) => c.status === "done").map((c) => c.id));
|
|
22559
23755
|
for (const card of state.taskCards) {
|
|
@@ -22566,13 +23762,25 @@ function getNextTaskCard(state) {
|
|
|
22566
23762
|
return null;
|
|
22567
23763
|
}
|
|
22568
23764
|
function hashContent(content) {
|
|
22569
|
-
return
|
|
23765
|
+
return createHash4("sha256").update(content).digest("hex").slice(0, 16);
|
|
23766
|
+
}
|
|
23767
|
+
function serializeItemSnapshot(item) {
|
|
23768
|
+
return JSON.stringify({
|
|
23769
|
+
status: item.status,
|
|
23770
|
+
progress: item.progress,
|
|
23771
|
+
title: item.title,
|
|
23772
|
+
description: item.description,
|
|
23773
|
+
scope: item.scope,
|
|
23774
|
+
type: item.type,
|
|
23775
|
+
priority: item.priority,
|
|
23776
|
+
acceptanceCriteria: item.acceptanceCriteria
|
|
23777
|
+
});
|
|
22570
23778
|
}
|
|
22571
23779
|
function updateBacklogItemInMarkdown(content, itemId, updates) {
|
|
22572
23780
|
const lines = content.split(`
|
|
22573
23781
|
`);
|
|
22574
23782
|
let inTargetItem = false;
|
|
22575
|
-
const idPattern = new RegExp(`^###\\s+.*${itemId}\\b`, "i");
|
|
23783
|
+
const idPattern = new RegExp(`^###\\s+.*${escapeRegExp(itemId)}\\b`, "i");
|
|
22576
23784
|
for (let i = 0;i < lines.length; i++) {
|
|
22577
23785
|
const line = lines[i];
|
|
22578
23786
|
if (line.match(/^###\s+/)) {
|
|
@@ -22591,34 +23799,69 @@ function updateBacklogItemInMarkdown(content, itemId, updates) {
|
|
|
22591
23799
|
return lines.join(`
|
|
22592
23800
|
`);
|
|
22593
23801
|
}
|
|
22594
|
-
|
|
23802
|
+
function archiveItemInMarkdown(content, itemId) {
|
|
23803
|
+
const lines = content.split(`
|
|
23804
|
+
`);
|
|
23805
|
+
const itemStart = lines.findIndex((l) => l.match(new RegExp(`^###\\s+.*${escapeRegExp(itemId)}\\b`, "i")));
|
|
23806
|
+
if (itemStart < 0)
|
|
23807
|
+
return content;
|
|
23808
|
+
let itemEnd = lines.length;
|
|
23809
|
+
for (let i = itemStart + 1;i < lines.length; i++) {
|
|
23810
|
+
if (lines[i].match(/^###\s+/) || lines[i].match(/^##\s+Archive/i)) {
|
|
23811
|
+
itemEnd = i;
|
|
23812
|
+
break;
|
|
23813
|
+
}
|
|
23814
|
+
}
|
|
23815
|
+
const block = lines.slice(itemStart, itemEnd);
|
|
23816
|
+
const statusLine = block.findIndex((l) => l.match(/^-\s+Status:/i));
|
|
23817
|
+
if (statusLine >= 0)
|
|
23818
|
+
block[statusLine] = "- Status: DONE";
|
|
23819
|
+
const progressLine = block.findIndex((l) => l.match(/^-\s+Progress:/i));
|
|
23820
|
+
if (progressLine >= 0)
|
|
23821
|
+
block[progressLine] = "- Progress: 100%";
|
|
23822
|
+
const withoutItem = [...lines.slice(0, itemStart), ...lines.slice(itemEnd)];
|
|
23823
|
+
const archiveIdx = withoutItem.findIndex((l) => /^##\s+Archive/i.test(l));
|
|
23824
|
+
if (archiveIdx < 0) {
|
|
23825
|
+
withoutItem.push("", "## Archive", ...block);
|
|
23826
|
+
} else {
|
|
23827
|
+
withoutItem.splice(archiveIdx + 1, 0, ...block);
|
|
23828
|
+
}
|
|
23829
|
+
return withoutItem.join(`
|
|
23830
|
+
`);
|
|
23831
|
+
}
|
|
23832
|
+
var STATE_FILE = ".codeconductor/openspec-state.json", ALLOWED_TRANSITIONS;
|
|
22595
23833
|
var init_openspec_state = __esm(() => {
|
|
22596
23834
|
init_schemas();
|
|
23835
|
+
ALLOWED_TRANSITIONS = {
|
|
23836
|
+
TODO: ["READY"],
|
|
23837
|
+
READY: ["PLANNED", "BLOCKED"],
|
|
23838
|
+
PLANNED: ["IN_PROGRESS", "BLOCKED"],
|
|
23839
|
+
IN_PROGRESS: ["REVIEW", "BLOCKED"],
|
|
23840
|
+
BLOCKED: ["READY"],
|
|
23841
|
+
REVIEW: ["DONE", "IN_PROGRESS"],
|
|
23842
|
+
DONE: []
|
|
23843
|
+
};
|
|
22597
23844
|
});
|
|
22598
23845
|
|
|
22599
23846
|
// src/core/openspec/backlog-scanner.ts
|
|
22600
|
-
import {
|
|
22601
|
-
import { readFile as
|
|
22602
|
-
import { resolve as
|
|
23847
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
23848
|
+
import { readFile as readFile27 } from "node:fs/promises";
|
|
23849
|
+
import { resolve as resolve25 } from "node:path";
|
|
22603
23850
|
function itemSnapshot(items) {
|
|
22604
23851
|
const snap = {};
|
|
22605
23852
|
for (const item of items) {
|
|
22606
|
-
snap[item.id] =
|
|
22607
|
-
status: item.status,
|
|
22608
|
-
progress: item.progress,
|
|
22609
|
-
title: item.title
|
|
22610
|
-
});
|
|
23853
|
+
snap[item.id] = serializeItemSnapshot(item);
|
|
22611
23854
|
}
|
|
22612
23855
|
return snap;
|
|
22613
23856
|
}
|
|
22614
23857
|
async function scanBacklog(projectRoot, previousSnapshot = {}) {
|
|
22615
23858
|
try {
|
|
22616
|
-
const filePath =
|
|
22617
|
-
const content = await
|
|
23859
|
+
const filePath = resolve25(projectRoot, BACKLOG_FILENAME);
|
|
23860
|
+
const content = await readFile27(filePath, "utf-8");
|
|
22618
23861
|
const hash = hashContent(content);
|
|
22619
23862
|
let fileChanged = true;
|
|
22620
23863
|
try {
|
|
22621
|
-
const diff =
|
|
23864
|
+
const diff = execFileSync2("git", ["diff", "--name-only", "--", BACKLOG_FILENAME], {
|
|
22622
23865
|
cwd: projectRoot,
|
|
22623
23866
|
encoding: "utf-8",
|
|
22624
23867
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -22752,8 +23995,17 @@ function phaseAcceptance(phase, item) {
|
|
|
22752
23995
|
function getPhaseOrder(doc) {
|
|
22753
23996
|
return doc.global.tddRequired ? [...TDD_PHASE_ORDER] : [...DEFAULT_PHASE_ORDER];
|
|
22754
23997
|
}
|
|
22755
|
-
function
|
|
23998
|
+
function hashItemSnapshot(item) {
|
|
23999
|
+
return hashContent(serializeItemSnapshot(item));
|
|
24000
|
+
}
|
|
24001
|
+
function planTaskCardsForItem(item, doc, existingCards = [], previousSnapshot) {
|
|
22756
24002
|
const phases = getPhaseOrder(doc);
|
|
24003
|
+
const currentHash = hashItemSnapshot(item);
|
|
24004
|
+
const snapshotDrifted = Boolean(previousSnapshot && hashContent(previousSnapshot) !== currentHash);
|
|
24005
|
+
const existingForItem = existingCards.filter((c) => c.backlogId === item.id);
|
|
24006
|
+
const previousOrder = existingForItem.map((c) => c.phase);
|
|
24007
|
+
const orderChanged = previousOrder.length > 0 && previousOrder.join(",") !== phases.join(",");
|
|
24008
|
+
const invalidatedCards = [];
|
|
22757
24009
|
const cards = [];
|
|
22758
24010
|
let prevId;
|
|
22759
24011
|
for (const phase of phases) {
|
|
@@ -22761,6 +24013,11 @@ function planTaskCardsForItem(item, doc, existingCards = []) {
|
|
|
22761
24013
|
const existing = existingCards.find((c) => c.id === id);
|
|
22762
24014
|
const route = routePhaseToAgent(phase);
|
|
22763
24015
|
const dependsOn = prevId ? [prevId] : [];
|
|
24016
|
+
const drifted = snapshotDrifted || Boolean(existing?.itemHash && existing.itemHash !== currentHash);
|
|
24017
|
+
const reset = drifted || orderChanged;
|
|
24018
|
+
if (existing && reset) {
|
|
24019
|
+
invalidatedCards.push(existing.id);
|
|
24020
|
+
}
|
|
22764
24021
|
cards.push({
|
|
22765
24022
|
id,
|
|
22766
24023
|
backlogId: item.id,
|
|
@@ -22771,11 +24028,22 @@ function planTaskCardsForItem(item, doc, existingCards = []) {
|
|
|
22771
24028
|
modelHint: route.modelKey,
|
|
22772
24029
|
dependsOn,
|
|
22773
24030
|
acceptanceCriteria: phaseAcceptance(phase, item),
|
|
22774
|
-
status: existing?.status ?? "pending"
|
|
24031
|
+
status: reset ? "pending" : existing?.status ?? "pending",
|
|
24032
|
+
itemHash: currentHash
|
|
22775
24033
|
});
|
|
22776
24034
|
prevId = id;
|
|
22777
24035
|
}
|
|
22778
|
-
|
|
24036
|
+
const leftover = existingForItem.filter((c) => !phases.includes(c.phase));
|
|
24037
|
+
for (const card of leftover) {
|
|
24038
|
+
invalidatedCards.push(card.id);
|
|
24039
|
+
}
|
|
24040
|
+
const uniqueInvalidated = [...new Set(invalidatedCards)];
|
|
24041
|
+
const tddImpact = orderChanged ? {
|
|
24042
|
+
previousOrder,
|
|
24043
|
+
nextOrder: phases,
|
|
24044
|
+
resetCardIds: uniqueInvalidated
|
|
24045
|
+
} : undefined;
|
|
24046
|
+
return { cards, invalidatedCards: uniqueInvalidated, tddImpact };
|
|
22779
24047
|
}
|
|
22780
24048
|
function comparePriority(a, b) {
|
|
22781
24049
|
const order = { P0: 0, P1: 1, P2: 2, P3: 3 };
|
|
@@ -22808,6 +24076,7 @@ function buildChangeSlug(item) {
|
|
|
22808
24076
|
var DEFAULT_PHASE_ORDER, TDD_PHASE_ORDER;
|
|
22809
24077
|
var init_backlog_planner = __esm(() => {
|
|
22810
24078
|
init_agent_router();
|
|
24079
|
+
init_openspec_state();
|
|
22811
24080
|
DEFAULT_PHASE_ORDER = [
|
|
22812
24081
|
"discover",
|
|
22813
24082
|
"design",
|
|
@@ -22825,8 +24094,8 @@ var init_backlog_planner = __esm(() => {
|
|
|
22825
24094
|
});
|
|
22826
24095
|
|
|
22827
24096
|
// src/core/openspec/openspec-generator.ts
|
|
22828
|
-
import { mkdir as
|
|
22829
|
-
import { resolve as
|
|
24097
|
+
import { mkdir as mkdir19, rename as rename3, writeFile as writeFile18 } from "node:fs/promises";
|
|
24098
|
+
import { basename as basename3, resolve as resolve26 } from "node:path";
|
|
22830
24099
|
function proposalContent(item) {
|
|
22831
24100
|
return `# Proposal: ${item.title}
|
|
22832
24101
|
|
|
@@ -22869,6 +24138,10 @@ ${item.acceptanceCriteria.map((c) => `- ${c}`).join(`
|
|
|
22869
24138
|
`)}
|
|
22870
24139
|
`;
|
|
22871
24140
|
}
|
|
24141
|
+
async function writeTasksMarkdown(projectRoot, changePath, cards) {
|
|
24142
|
+
await mkdir19(resolve26(projectRoot, changePath), { recursive: true });
|
|
24143
|
+
await writeFile18(resolve26(projectRoot, changePath, "tasks.md"), tasksContent(cards), "utf-8");
|
|
24144
|
+
}
|
|
22872
24145
|
function tasksContent(cards) {
|
|
22873
24146
|
const implCards = cards.filter((c) => c.phase === "implement" || c.phase === "test");
|
|
22874
24147
|
const lines = ["# Implementation Tasks", ""];
|
|
@@ -22884,11 +24157,11 @@ function tasksContent(cards) {
|
|
|
22884
24157
|
}
|
|
22885
24158
|
async function generateOpenspecChange(projectRoot, item, taskCards) {
|
|
22886
24159
|
const slug = buildChangeSlug(item);
|
|
22887
|
-
const changeDir =
|
|
22888
|
-
await
|
|
22889
|
-
await
|
|
22890
|
-
await
|
|
22891
|
-
await
|
|
24160
|
+
const changeDir = resolve26(projectRoot, "openspec", "changes", slug);
|
|
24161
|
+
await mkdir19(resolve26(changeDir, "specs"), { recursive: true });
|
|
24162
|
+
await writeFile18(resolve26(changeDir, "proposal.md"), proposalContent(item), "utf-8");
|
|
24163
|
+
await writeFile18(resolve26(changeDir, "design.md"), designPlaceholder(item), "utf-8");
|
|
24164
|
+
await writeFile18(resolve26(changeDir, "tasks.md"), tasksContent(taskCards), "utf-8");
|
|
22892
24165
|
const specStub = `# Delta Spec: ${item.title}
|
|
22893
24166
|
|
|
22894
24167
|
## ADDED Requirements
|
|
@@ -22896,20 +24169,29 @@ async function generateOpenspecChange(projectRoot, item, taskCards) {
|
|
|
22896
24169
|
${item.acceptanceCriteria.map((c) => `- ${c}`).join(`
|
|
22897
24170
|
`)}
|
|
22898
24171
|
`;
|
|
22899
|
-
await
|
|
24172
|
+
await writeFile18(resolve26(changeDir, "specs", "delta.md"), specStub, "utf-8");
|
|
22900
24173
|
return `openspec/changes/${slug}`;
|
|
22901
24174
|
}
|
|
24175
|
+
async function archiveChangeFolder(projectRoot, changePath) {
|
|
24176
|
+
const src = resolve26(projectRoot, changePath);
|
|
24177
|
+
const slug = basename3(changePath);
|
|
24178
|
+
const destDir = resolve26(projectRoot, "openspec", "changes", "archive");
|
|
24179
|
+
await mkdir19(destDir, { recursive: true });
|
|
24180
|
+
const dest = resolve26(destDir, slug);
|
|
24181
|
+
await rename3(src, dest);
|
|
24182
|
+
return `openspec/changes/archive/${slug}`;
|
|
24183
|
+
}
|
|
22902
24184
|
async function ensureOpenspecConfig(projectRoot) {
|
|
22903
|
-
const configPath =
|
|
24185
|
+
const configPath = resolve26(projectRoot, "openspec", "config.yaml");
|
|
22904
24186
|
try {
|
|
22905
24187
|
const { access: access7 } = await import("node:fs/promises");
|
|
22906
24188
|
await access7(configPath);
|
|
22907
24189
|
} catch {
|
|
22908
|
-
await
|
|
24190
|
+
await mkdir19(resolve26(projectRoot, "openspec"), { recursive: true });
|
|
22909
24191
|
const stub = `# OpenSpec project config (CodeConductor native compatible)
|
|
22910
24192
|
schema: spec-driven
|
|
22911
24193
|
`;
|
|
22912
|
-
await
|
|
24194
|
+
await writeFile18(configPath, stub, "utf-8");
|
|
22913
24195
|
}
|
|
22914
24196
|
}
|
|
22915
24197
|
var init_openspec_generator = __esm(() => {
|
|
@@ -22921,10 +24203,10 @@ var exports_openspec_command = {};
|
|
|
22921
24203
|
__export(exports_openspec_command, {
|
|
22922
24204
|
openspecCommand: () => openspecCommand
|
|
22923
24205
|
});
|
|
22924
|
-
import { readFile as
|
|
22925
|
-
import { resolve as
|
|
24206
|
+
import { readFile as readFile28 } from "node:fs/promises";
|
|
24207
|
+
import { resolve as resolve27 } from "node:path";
|
|
22926
24208
|
async function openspecCommand(options) {
|
|
22927
|
-
const { subcommand, itemId, projectRoot, output } = options;
|
|
24209
|
+
const { subcommand, itemId, projectRoot, output, reason } = options;
|
|
22928
24210
|
switch (subcommand) {
|
|
22929
24211
|
case "validate":
|
|
22930
24212
|
return handleValidate(projectRoot);
|
|
@@ -22936,6 +24218,14 @@ async function openspecCommand(options) {
|
|
|
22936
24218
|
return handleStatus2(projectRoot);
|
|
22937
24219
|
case "next":
|
|
22938
24220
|
return handleNext2(projectRoot);
|
|
24221
|
+
case "start":
|
|
24222
|
+
return handleStart(projectRoot, itemId);
|
|
24223
|
+
case "done":
|
|
24224
|
+
return handleDone(projectRoot, itemId);
|
|
24225
|
+
case "block":
|
|
24226
|
+
return handleBlock(projectRoot, itemId, reason);
|
|
24227
|
+
case "archive":
|
|
24228
|
+
return handleArchive(projectRoot, itemId);
|
|
22939
24229
|
default:
|
|
22940
24230
|
return {
|
|
22941
24231
|
code: 1,
|
|
@@ -22943,12 +24233,46 @@ async function openspecCommand(options) {
|
|
|
22943
24233
|
success: false,
|
|
22944
24234
|
command: "openspec",
|
|
22945
24235
|
errors: [
|
|
22946
|
-
`Unknown subcommand: ${subcommand}. Use:
|
|
24236
|
+
`Unknown subcommand: ${subcommand}. Use: ${KNOWN_SUBCOMMANDS}`
|
|
22947
24237
|
]
|
|
22948
24238
|
}
|
|
22949
24239
|
};
|
|
22950
24240
|
}
|
|
22951
24241
|
}
|
|
24242
|
+
function fail(command, errors3, extra = {}) {
|
|
24243
|
+
return {
|
|
24244
|
+
code: 1,
|
|
24245
|
+
data: { success: false, command, errors: errors3, ...extra }
|
|
24246
|
+
};
|
|
24247
|
+
}
|
|
24248
|
+
async function persistBacklog(projectRoot, content) {
|
|
24249
|
+
await writeFileAtomic(resolve27(projectRoot, BACKLOG_FILENAME), content);
|
|
24250
|
+
}
|
|
24251
|
+
async function persistState(projectRoot, state) {
|
|
24252
|
+
const written = await writeOpenspecState(projectRoot, state);
|
|
24253
|
+
if (!written.success) {
|
|
24254
|
+
return { ok: false, error: written.error.message };
|
|
24255
|
+
}
|
|
24256
|
+
return { ok: true };
|
|
24257
|
+
}
|
|
24258
|
+
async function readBacklogMarkdown(projectRoot) {
|
|
24259
|
+
return readFile28(resolve27(projectRoot, BACKLOG_FILENAME), "utf-8");
|
|
24260
|
+
}
|
|
24261
|
+
async function transitionItem(projectRoot, item, to, progress) {
|
|
24262
|
+
const content = await readBacklogMarkdown(projectRoot);
|
|
24263
|
+
const next = applyBacklogTransition(content, item.id, item.status, to, progress);
|
|
24264
|
+
if (!next.success) {
|
|
24265
|
+
return { ok: false, error: next.error.message };
|
|
24266
|
+
}
|
|
24267
|
+
await persistBacklog(projectRoot, next.data);
|
|
24268
|
+
return { ok: true };
|
|
24269
|
+
}
|
|
24270
|
+
function findCard(state, cardId) {
|
|
24271
|
+
return state.taskCards.find((c) => c.id === cardId);
|
|
24272
|
+
}
|
|
24273
|
+
function itemCards(state, backlogId) {
|
|
24274
|
+
return state.taskCards.filter((c) => c.backlogId === backlogId);
|
|
24275
|
+
}
|
|
22952
24276
|
async function handleValidate(projectRoot) {
|
|
22953
24277
|
const loadResult = await loadBacklog(projectRoot);
|
|
22954
24278
|
if (!loadResult.success) {
|
|
@@ -23001,7 +24325,10 @@ async function handleScan(projectRoot) {
|
|
|
23001
24325
|
lastScanAt: new Date().toISOString(),
|
|
23002
24326
|
itemSnapshots: snapshots
|
|
23003
24327
|
};
|
|
23004
|
-
await
|
|
24328
|
+
const written = await persistState(projectRoot, state);
|
|
24329
|
+
if (!written.ok) {
|
|
24330
|
+
return fail("openspec scan", [written.error]);
|
|
24331
|
+
}
|
|
23005
24332
|
}
|
|
23006
24333
|
return {
|
|
23007
24334
|
code: 0,
|
|
@@ -23041,7 +24368,13 @@ async function handlePlan(projectRoot, itemId) {
|
|
|
23041
24368
|
changePaths: {},
|
|
23042
24369
|
itemSnapshots: {}
|
|
23043
24370
|
};
|
|
23044
|
-
|
|
24371
|
+
if (item.status !== "PLANNED" && !canTransition(item.status, "PLANNED")) {
|
|
24372
|
+
return fail("openspec plan", [
|
|
24373
|
+
`Illegal backlog transition for ${item.id}: ${item.status} → PLANNED.`
|
|
24374
|
+
]);
|
|
24375
|
+
}
|
|
24376
|
+
const planned = planTaskCardsForItem(item, doc, existingState.taskCards, existingState.itemSnapshots?.[item.id]);
|
|
24377
|
+
const taskCards = planned.cards;
|
|
23045
24378
|
await ensureOpenspecConfig(projectRoot);
|
|
23046
24379
|
const changePath = await generateOpenspecChange(projectRoot, item, taskCards);
|
|
23047
24380
|
const newState = {
|
|
@@ -23052,16 +24385,22 @@ async function handlePlan(projectRoot, itemId) {
|
|
|
23052
24385
|
changePaths: {
|
|
23053
24386
|
...existingState.changePaths,
|
|
23054
24387
|
[item.id]: changePath
|
|
24388
|
+
},
|
|
24389
|
+
itemSnapshots: {
|
|
24390
|
+
...existingState.itemSnapshots,
|
|
24391
|
+
[item.id]: serializeItemSnapshot(item)
|
|
23055
24392
|
}
|
|
23056
24393
|
};
|
|
23057
|
-
await
|
|
23058
|
-
|
|
23059
|
-
|
|
23060
|
-
|
|
23061
|
-
|
|
23062
|
-
|
|
23063
|
-
|
|
23064
|
-
|
|
24394
|
+
const written = await persistState(projectRoot, newState);
|
|
24395
|
+
if (!written.ok) {
|
|
24396
|
+
return fail("openspec plan", [written.error]);
|
|
24397
|
+
}
|
|
24398
|
+
const content = await readBacklogMarkdown(projectRoot);
|
|
24399
|
+
const transition = applyBacklogTransition(content, item.id, item.status, "PLANNED", item.progress);
|
|
24400
|
+
if (!transition.success) {
|
|
24401
|
+
return fail("openspec plan", [transition.error.message]);
|
|
24402
|
+
}
|
|
24403
|
+
await persistBacklog(projectRoot, transition.data);
|
|
23065
24404
|
return {
|
|
23066
24405
|
code: 0,
|
|
23067
24406
|
data: {
|
|
@@ -23070,7 +24409,9 @@ async function handlePlan(projectRoot, itemId) {
|
|
|
23070
24409
|
itemId: item.id,
|
|
23071
24410
|
title: item.title,
|
|
23072
24411
|
changePath,
|
|
23073
|
-
taskCards
|
|
24412
|
+
taskCards,
|
|
24413
|
+
invalidatedCards: planned.invalidatedCards,
|
|
24414
|
+
tddImpact: planned.tddImpact
|
|
23074
24415
|
}
|
|
23075
24416
|
};
|
|
23076
24417
|
}
|
|
@@ -23119,23 +24460,244 @@ async function handleNext2(projectRoot) {
|
|
|
23119
24460
|
}
|
|
23120
24461
|
};
|
|
23121
24462
|
}
|
|
23122
|
-
let loop = undefined;
|
|
23123
|
-
if (shouldRunAgentLoop(undefined, next.agent, next.phase)) {
|
|
23124
|
-
loop = await runLoopForProject(projectRoot, {
|
|
23125
|
-
taskTitle: next.title,
|
|
23126
|
-
originalTask: next.prompt
|
|
23127
|
-
});
|
|
24463
|
+
let loop = undefined;
|
|
24464
|
+
if (shouldRunAgentLoop(undefined, next.agent, next.phase)) {
|
|
24465
|
+
loop = await runLoopForProject(projectRoot, {
|
|
24466
|
+
taskTitle: next.title,
|
|
24467
|
+
originalTask: next.prompt
|
|
24468
|
+
});
|
|
24469
|
+
}
|
|
24470
|
+
return {
|
|
24471
|
+
code: loop && !loop.success ? 1 : 0,
|
|
24472
|
+
data: {
|
|
24473
|
+
success: !(loop && !loop.success),
|
|
24474
|
+
command: "openspec next",
|
|
24475
|
+
taskCard: next,
|
|
24476
|
+
...loop ? { loop } : {}
|
|
24477
|
+
}
|
|
24478
|
+
};
|
|
24479
|
+
}
|
|
24480
|
+
async function loadStateOrFail(projectRoot, command) {
|
|
24481
|
+
const stateResult = await loadOpenspecState(projectRoot);
|
|
24482
|
+
if (!stateResult.success) {
|
|
24483
|
+
return { ok: false, response: fail(command, [stateResult.error.message]) };
|
|
24484
|
+
}
|
|
24485
|
+
return { ok: true, state: stateResult.data };
|
|
24486
|
+
}
|
|
24487
|
+
async function loadItemOrFail(projectRoot, itemId, command) {
|
|
24488
|
+
const loadResult = await loadBacklog(projectRoot);
|
|
24489
|
+
if (!loadResult.success) {
|
|
24490
|
+
return { ok: false, response: fail(command, [loadResult.error.message]) };
|
|
24491
|
+
}
|
|
24492
|
+
const item = loadResult.data.items.find((i) => i.id === itemId) ?? loadResult.data.archive.find((i) => i.id === itemId);
|
|
24493
|
+
if (!item) {
|
|
24494
|
+
return { ok: false, response: fail(command, [`Backlog item ${itemId} not found`]) };
|
|
24495
|
+
}
|
|
24496
|
+
return {
|
|
24497
|
+
ok: true,
|
|
24498
|
+
item,
|
|
24499
|
+
reviewRequired: loadResult.data.global.reviewRequired
|
|
24500
|
+
};
|
|
24501
|
+
}
|
|
24502
|
+
async function handleStart(projectRoot, cardId) {
|
|
24503
|
+
const command = "openspec start";
|
|
24504
|
+
if (!cardId) {
|
|
24505
|
+
return fail(command, ["Missing card id. Usage: openspec start <cardId>"]);
|
|
24506
|
+
}
|
|
24507
|
+
const loaded = await loadStateOrFail(projectRoot, command);
|
|
24508
|
+
if (!loaded.ok)
|
|
24509
|
+
return loaded.response;
|
|
24510
|
+
const card = findCard(loaded.state, cardId);
|
|
24511
|
+
if (!card) {
|
|
24512
|
+
return fail(command, [`Task card ${cardId} not found`]);
|
|
24513
|
+
}
|
|
24514
|
+
if (card.status !== "pending" && card.status !== "doing") {
|
|
24515
|
+
return fail(command, [`Card ${cardId} cannot start from status ${card.status}`]);
|
|
24516
|
+
}
|
|
24517
|
+
const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
|
|
24518
|
+
if (!itemLoaded.ok)
|
|
24519
|
+
return itemLoaded.response;
|
|
24520
|
+
let state = loaded.state;
|
|
24521
|
+
if (card.status === "pending") {
|
|
24522
|
+
state = setTaskCardStatus(state, cardId, "doing");
|
|
24523
|
+
const written = await persistState(projectRoot, state);
|
|
24524
|
+
if (!written.ok)
|
|
24525
|
+
return fail(command, [written.error]);
|
|
24526
|
+
}
|
|
24527
|
+
const transition = await transitionItem(projectRoot, itemLoaded.item, "IN_PROGRESS", itemLoaded.item.progress);
|
|
24528
|
+
if (!transition.ok)
|
|
24529
|
+
return fail(command, [transition.error]);
|
|
24530
|
+
return {
|
|
24531
|
+
code: 0,
|
|
24532
|
+
data: {
|
|
24533
|
+
success: true,
|
|
24534
|
+
command,
|
|
24535
|
+
cardId,
|
|
24536
|
+
cardStatus: "doing",
|
|
24537
|
+
itemId: itemLoaded.item.id,
|
|
24538
|
+
itemStatus: "IN_PROGRESS"
|
|
24539
|
+
}
|
|
24540
|
+
};
|
|
24541
|
+
}
|
|
24542
|
+
async function handleDone(projectRoot, cardId) {
|
|
24543
|
+
const command = "openspec done";
|
|
24544
|
+
if (!cardId) {
|
|
24545
|
+
return fail(command, ["Missing card id. Usage: openspec done <cardId>"]);
|
|
24546
|
+
}
|
|
24547
|
+
const loaded = await loadStateOrFail(projectRoot, command);
|
|
24548
|
+
if (!loaded.ok)
|
|
24549
|
+
return loaded.response;
|
|
24550
|
+
const card = findCard(loaded.state, cardId);
|
|
24551
|
+
if (!card) {
|
|
24552
|
+
return fail(command, [`Task card ${cardId} not found`]);
|
|
24553
|
+
}
|
|
24554
|
+
if (card.status === "done") {
|
|
24555
|
+
return {
|
|
24556
|
+
code: 0,
|
|
24557
|
+
data: { success: true, command, cardId, cardStatus: "done", alreadyDone: true }
|
|
24558
|
+
};
|
|
24559
|
+
}
|
|
24560
|
+
if (card.status !== "doing") {
|
|
24561
|
+
return fail(command, [`Card ${cardId} must be doing before done (got ${card.status})`]);
|
|
24562
|
+
}
|
|
24563
|
+
const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
|
|
24564
|
+
if (!itemLoaded.ok)
|
|
24565
|
+
return itemLoaded.response;
|
|
24566
|
+
const state = setTaskCardStatus(loaded.state, cardId, "done");
|
|
24567
|
+
const cards = itemCards(state, card.backlogId);
|
|
24568
|
+
const doneCount = cards.filter((c) => c.status === "done").length;
|
|
24569
|
+
const progress = cards.length === 0 ? 0 : Math.round(doneCount / cards.length * 100);
|
|
24570
|
+
const allDone = cards.length > 0 && doneCount === cards.length;
|
|
24571
|
+
const written = await persistState(projectRoot, state);
|
|
24572
|
+
if (!written.ok)
|
|
24573
|
+
return fail(command, [written.error]);
|
|
24574
|
+
const changePath = state.changePaths[card.backlogId];
|
|
24575
|
+
if (changePath) {
|
|
24576
|
+
await writeTasksMarkdown(projectRoot, changePath, cards);
|
|
24577
|
+
}
|
|
24578
|
+
const nextStatus = allDone ? "REVIEW" : "IN_PROGRESS";
|
|
24579
|
+
const transition = await transitionItem(projectRoot, itemLoaded.item, nextStatus, progress);
|
|
24580
|
+
if (!transition.ok)
|
|
24581
|
+
return fail(command, [transition.error]);
|
|
24582
|
+
return {
|
|
24583
|
+
code: 0,
|
|
24584
|
+
data: {
|
|
24585
|
+
success: true,
|
|
24586
|
+
command,
|
|
24587
|
+
cardId,
|
|
24588
|
+
cardStatus: "done",
|
|
24589
|
+
itemId: itemLoaded.item.id,
|
|
24590
|
+
progress,
|
|
24591
|
+
itemStatus: nextStatus,
|
|
24592
|
+
allCardsDone: allDone
|
|
24593
|
+
}
|
|
24594
|
+
};
|
|
24595
|
+
}
|
|
24596
|
+
async function handleBlock(projectRoot, cardId, reason) {
|
|
24597
|
+
const command = "openspec block";
|
|
24598
|
+
if (!cardId) {
|
|
24599
|
+
return fail(command, ["Missing card id. Usage: openspec block <cardId> --reason <text>"]);
|
|
24600
|
+
}
|
|
24601
|
+
if (!reason || !reason.trim()) {
|
|
24602
|
+
return fail(command, ["--reason is required"]);
|
|
24603
|
+
}
|
|
24604
|
+
const loaded = await loadStateOrFail(projectRoot, command);
|
|
24605
|
+
if (!loaded.ok)
|
|
24606
|
+
return loaded.response;
|
|
24607
|
+
const card = findCard(loaded.state, cardId);
|
|
24608
|
+
if (!card) {
|
|
24609
|
+
return fail(command, [`Task card ${cardId} not found`]);
|
|
24610
|
+
}
|
|
24611
|
+
const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
|
|
24612
|
+
if (!itemLoaded.ok)
|
|
24613
|
+
return itemLoaded.response;
|
|
24614
|
+
const state = setTaskCardStatus(loaded.state, cardId, "blocked");
|
|
24615
|
+
const written = await persistState(projectRoot, state);
|
|
24616
|
+
if (!written.ok)
|
|
24617
|
+
return fail(command, [written.error]);
|
|
24618
|
+
const transition = await transitionItem(projectRoot, itemLoaded.item, "BLOCKED");
|
|
24619
|
+
if (!transition.ok)
|
|
24620
|
+
return fail(command, [transition.error]);
|
|
24621
|
+
return {
|
|
24622
|
+
code: 0,
|
|
24623
|
+
data: {
|
|
24624
|
+
success: true,
|
|
24625
|
+
command,
|
|
24626
|
+
cardId,
|
|
24627
|
+
cardStatus: "blocked",
|
|
24628
|
+
itemId: itemLoaded.item.id,
|
|
24629
|
+
itemStatus: "BLOCKED",
|
|
24630
|
+
reason: reason.trim()
|
|
24631
|
+
}
|
|
24632
|
+
};
|
|
24633
|
+
}
|
|
24634
|
+
async function handleArchive(projectRoot, itemId) {
|
|
24635
|
+
const command = "openspec archive";
|
|
24636
|
+
if (!itemId) {
|
|
24637
|
+
return fail(command, ["Missing item id. Usage: openspec archive <itemId>"]);
|
|
24638
|
+
}
|
|
24639
|
+
const loaded = await loadStateOrFail(projectRoot, command);
|
|
24640
|
+
if (!loaded.ok)
|
|
24641
|
+
return loaded.response;
|
|
24642
|
+
const itemLoaded = await loadItemOrFail(projectRoot, itemId, command);
|
|
24643
|
+
if (!itemLoaded.ok)
|
|
24644
|
+
return itemLoaded.response;
|
|
24645
|
+
const cards = itemCards(loaded.state, itemId);
|
|
24646
|
+
if (cards.length === 0) {
|
|
24647
|
+
return fail(command, [`No task cards found for ${itemId}. Run openspec plan first.`]);
|
|
24648
|
+
}
|
|
24649
|
+
const unfinished = cards.filter((c) => c.status !== "done");
|
|
24650
|
+
if (unfinished.length > 0) {
|
|
24651
|
+
return fail(command, [
|
|
24652
|
+
`Cannot archive ${itemId}: ${unfinished.length} card(s) are not done (${unfinished.map((c) => c.id).join(", ")})`
|
|
24653
|
+
]);
|
|
24654
|
+
}
|
|
24655
|
+
if (itemLoaded.reviewRequired) {
|
|
24656
|
+
const reviewCard = cards.find((c) => c.phase === "review");
|
|
24657
|
+
if (!reviewCard || reviewCard.status !== "done") {
|
|
24658
|
+
return fail(command, [
|
|
24659
|
+
`Cannot archive ${itemId}: global.reviewRequired needs the review phase card done`
|
|
24660
|
+
]);
|
|
24661
|
+
}
|
|
24662
|
+
}
|
|
24663
|
+
if (itemLoaded.item.status !== "DONE") {
|
|
24664
|
+
const toDone = await transitionItem(projectRoot, itemLoaded.item, "DONE", 100);
|
|
24665
|
+
if (!toDone.ok)
|
|
24666
|
+
return fail(command, [toDone.error]);
|
|
24667
|
+
}
|
|
24668
|
+
const content = await readBacklogMarkdown(projectRoot);
|
|
24669
|
+
const archived = archiveItemInMarkdown(content, itemId);
|
|
24670
|
+
await persistBacklog(projectRoot, archived);
|
|
24671
|
+
let archivedPath;
|
|
24672
|
+
const changePath = loaded.state.changePaths[itemId];
|
|
24673
|
+
if (changePath && !changePath.includes("/archive/")) {
|
|
24674
|
+
try {
|
|
24675
|
+
archivedPath = await archiveChangeFolder(projectRoot, changePath);
|
|
24676
|
+
} catch (e) {
|
|
24677
|
+
return fail(command, [
|
|
24678
|
+
`Backlog archived but change folder move failed: ${e instanceof Error ? e.message : String(e)}`
|
|
24679
|
+
]);
|
|
24680
|
+
}
|
|
23128
24681
|
}
|
|
24682
|
+
const nextState = {
|
|
24683
|
+
...loaded.state,
|
|
24684
|
+
activeItemId: loaded.state.activeItemId === itemId ? undefined : loaded.state.activeItemId,
|
|
24685
|
+
changePaths: archivedPath ? { ...loaded.state.changePaths, [itemId]: archivedPath } : loaded.state.changePaths
|
|
24686
|
+
};
|
|
24687
|
+
const written = await persistState(projectRoot, nextState);
|
|
24688
|
+
if (!written.ok)
|
|
24689
|
+
return fail(command, [written.error]);
|
|
23129
24690
|
return {
|
|
23130
|
-
code:
|
|
24691
|
+
code: 0,
|
|
23131
24692
|
data: {
|
|
23132
|
-
success:
|
|
23133
|
-
command
|
|
23134
|
-
|
|
23135
|
-
|
|
24693
|
+
success: true,
|
|
24694
|
+
command,
|
|
24695
|
+
itemId,
|
|
24696
|
+
archivedPath: archivedPath ?? changePath
|
|
23136
24697
|
}
|
|
23137
24698
|
};
|
|
23138
24699
|
}
|
|
24700
|
+
var KNOWN_SUBCOMMANDS = "validate, scan, plan, status, next, start, done, block, archive";
|
|
23139
24701
|
var init_openspec_command = __esm(() => {
|
|
23140
24702
|
init_backlog_parser();
|
|
23141
24703
|
init_backlog_validator();
|
|
@@ -23543,13 +25105,13 @@ function ccGainToScorecardImpact(result) {
|
|
|
23543
25105
|
}
|
|
23544
25106
|
|
|
23545
25107
|
// src/core/evaluation/scorecard-signals.ts
|
|
23546
|
-
import {
|
|
25108
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
23547
25109
|
function collectScorecardSignals(projectRoot, scopeFiles) {
|
|
23548
25110
|
const findings = [];
|
|
23549
25111
|
const overrides = {};
|
|
23550
25112
|
let diff = "";
|
|
23551
25113
|
try {
|
|
23552
|
-
diff =
|
|
25114
|
+
diff = execFileSync3("git", ["diff", "HEAD"], {
|
|
23553
25115
|
cwd: projectRoot,
|
|
23554
25116
|
encoding: "utf-8",
|
|
23555
25117
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -23608,11 +25170,11 @@ var init_scorecard_signals = __esm(() => {
|
|
|
23608
25170
|
});
|
|
23609
25171
|
|
|
23610
25172
|
// src/core/evaluation/execution-profile.ts
|
|
23611
|
-
import { readFile as
|
|
23612
|
-
import { resolve as
|
|
25173
|
+
import { readFile as readFile29 } from "node:fs/promises";
|
|
25174
|
+
import { resolve as resolve28 } from "node:path";
|
|
23613
25175
|
async function loadExecutionProfile(projectRoot, defaultTarget = "opencode") {
|
|
23614
25176
|
try {
|
|
23615
|
-
const content = await
|
|
25177
|
+
const content = await readFile29(resolve28(projectRoot, PROFILE_FILE), "utf-8");
|
|
23616
25178
|
const data = $parse(content);
|
|
23617
25179
|
return ExecutionProfileSchema.parse(data);
|
|
23618
25180
|
} catch {
|
|
@@ -23692,14 +25254,14 @@ var init_execution_profile = __esm(() => {
|
|
|
23692
25254
|
});
|
|
23693
25255
|
|
|
23694
25256
|
// src/core/evaluation/prompt-diff.ts
|
|
23695
|
-
import { readFile as
|
|
23696
|
-
import { join as
|
|
25257
|
+
import { readFile as readFile30, readdir as readdir7 } from "node:fs/promises";
|
|
25258
|
+
import { join as join20, resolve as resolve29 } from "node:path";
|
|
23697
25259
|
function normalizeVersion(v) {
|
|
23698
25260
|
return v.startsWith("v") ? v : `v${v}`;
|
|
23699
25261
|
}
|
|
23700
25262
|
async function listPromptFiles(dir) {
|
|
23701
25263
|
try {
|
|
23702
|
-
const entries = await
|
|
25264
|
+
const entries = await readdir7(dir, { withFileTypes: true });
|
|
23703
25265
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name);
|
|
23704
25266
|
} catch {
|
|
23705
25267
|
return [];
|
|
@@ -23742,30 +25304,30 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
|
|
|
23742
25304
|
const fromV = normalizeVersion(fromVersion);
|
|
23743
25305
|
const toV = normalizeVersion(toVersion);
|
|
23744
25306
|
const searchRoots = [
|
|
23745
|
-
|
|
23746
|
-
|
|
23747
|
-
|
|
25307
|
+
resolve29(options.projectRoot ?? PACKAGE_ROOT, ".agents", "prompts"),
|
|
25308
|
+
join20(ROOT_PRESETS_DIR, target, "prompts"),
|
|
25309
|
+
join20(ROOT_PRESETS_DIR, "opencode", "prompts")
|
|
23748
25310
|
];
|
|
23749
25311
|
let fromDir = "";
|
|
23750
25312
|
let toDir = "";
|
|
23751
25313
|
for (const root of searchRoots) {
|
|
23752
|
-
const f =
|
|
23753
|
-
const t =
|
|
25314
|
+
const f = join20(root, fromV);
|
|
25315
|
+
const t = join20(root, toV);
|
|
23754
25316
|
try {
|
|
23755
|
-
await
|
|
25317
|
+
await readFile30(join20(f, "orchestrator.md"), "utf-8");
|
|
23756
25318
|
fromDir = f;
|
|
23757
25319
|
toDir = t;
|
|
23758
25320
|
break;
|
|
23759
25321
|
} catch {}
|
|
23760
25322
|
}
|
|
23761
25323
|
if (!fromDir) {
|
|
23762
|
-
fromDir =
|
|
23763
|
-
toDir =
|
|
25324
|
+
fromDir = join20(ROOT_PRESETS_DIR, "opencode", "prompts", fromV);
|
|
25325
|
+
toDir = join20(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
|
|
23764
25326
|
} else {
|
|
23765
25327
|
try {
|
|
23766
|
-
await
|
|
25328
|
+
await readFile30(join20(toDir, "orchestrator.md"), "utf-8");
|
|
23767
25329
|
} catch {
|
|
23768
|
-
toDir =
|
|
25330
|
+
toDir = join20(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
|
|
23769
25331
|
}
|
|
23770
25332
|
}
|
|
23771
25333
|
const fromFiles = await listPromptFiles(fromDir);
|
|
@@ -23783,12 +25345,12 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
|
|
|
23783
25345
|
let oldText = "";
|
|
23784
25346
|
let newText = "";
|
|
23785
25347
|
try {
|
|
23786
|
-
oldText = await
|
|
25348
|
+
oldText = await readFile30(join20(fromDir, name), "utf-8");
|
|
23787
25349
|
} catch {
|
|
23788
25350
|
oldText = "";
|
|
23789
25351
|
}
|
|
23790
25352
|
try {
|
|
23791
|
-
newText = await
|
|
25353
|
+
newText = await readFile30(join20(toDir, name), "utf-8");
|
|
23792
25354
|
} catch {
|
|
23793
25355
|
newText = "";
|
|
23794
25356
|
}
|
|
@@ -23827,13 +25389,13 @@ var init_prompt_diff = __esm(() => {
|
|
|
23827
25389
|
});
|
|
23828
25390
|
|
|
23829
25391
|
// src/core/evaluation/regression-checklist.ts
|
|
23830
|
-
import { execFileSync } from "node:child_process";
|
|
23831
|
-
import { mkdir as
|
|
23832
|
-
import { dirname as
|
|
25392
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
25393
|
+
import { mkdir as mkdir20, readFile as readFile31, writeFile as writeFile19 } from "node:fs/promises";
|
|
25394
|
+
import { dirname as dirname8, resolve as resolve30 } from "node:path";
|
|
23833
25395
|
async function loadRegressionChecklist() {
|
|
23834
25396
|
try {
|
|
23835
|
-
const path =
|
|
23836
|
-
const content = await
|
|
25397
|
+
const path = resolve30(ROOT_PRESETS_DIR, "templates", "regression-checklist.yml");
|
|
25398
|
+
const content = await readFile31(path, "utf-8");
|
|
23837
25399
|
const data = $parse(content);
|
|
23838
25400
|
return data.checks ?? [];
|
|
23839
25401
|
} catch {
|
|
@@ -23847,7 +25409,7 @@ function runCheckCommand(cmd, cwd) {
|
|
|
23847
25409
|
if (!file)
|
|
23848
25410
|
return { passed: false, message: "Empty command" };
|
|
23849
25411
|
try {
|
|
23850
|
-
|
|
25412
|
+
execFileSync4(file, args, {
|
|
23851
25413
|
cwd,
|
|
23852
25414
|
encoding: "utf-8",
|
|
23853
25415
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -23888,9 +25450,9 @@ async function runRegressionChecklist(projectRoot, options = {}) {
|
|
|
23888
25450
|
checks,
|
|
23889
25451
|
timestamp: new Date().toISOString()
|
|
23890
25452
|
};
|
|
23891
|
-
const outPath =
|
|
23892
|
-
await
|
|
23893
|
-
await
|
|
25453
|
+
const outPath = resolve30(projectRoot, ".codeconductor/evaluation/regression-last.json");
|
|
25454
|
+
await mkdir20(dirname8(outPath), { recursive: true });
|
|
25455
|
+
await writeFile19(outPath, JSON.stringify(report, null, 2), "utf-8");
|
|
23894
25456
|
return ok(report);
|
|
23895
25457
|
} catch (e) {
|
|
23896
25458
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -23932,8 +25494,8 @@ var init_model_comparison = __esm(() => {
|
|
|
23932
25494
|
});
|
|
23933
25495
|
|
|
23934
25496
|
// src/core/evaluation/cost-quality-matrix.ts
|
|
23935
|
-
import { writeFile as
|
|
23936
|
-
import { resolve as
|
|
25497
|
+
import { writeFile as writeFile20 } from "node:fs/promises";
|
|
25498
|
+
import { resolve as resolve31 } from "node:path";
|
|
23937
25499
|
async function buildCostQualityMatrix(projectRoot, outcomes) {
|
|
23938
25500
|
const agg = aggregateOutcomes(outcomes);
|
|
23939
25501
|
const rows = Object.entries(agg.byModel).map(([model, entry]) => {
|
|
@@ -23953,7 +25515,7 @@ async function buildCostQualityMatrix(projectRoot, outcomes) {
|
|
|
23953
25515
|
rows
|
|
23954
25516
|
};
|
|
23955
25517
|
const dir = await ensureEvaluationDir(projectRoot);
|
|
23956
|
-
await
|
|
25518
|
+
await writeFile20(resolve31(dir, "matrix.yml"), $stringify(matrix), "utf-8");
|
|
23957
25519
|
return matrix;
|
|
23958
25520
|
}
|
|
23959
25521
|
function formatMatrixMarkdown(matrix) {
|
|
@@ -23976,13 +25538,385 @@ var init_cost_quality_matrix = __esm(() => {
|
|
|
23976
25538
|
init_outcome_store();
|
|
23977
25539
|
});
|
|
23978
25540
|
|
|
25541
|
+
// src/core/evaluation/harness-experiment.ts
|
|
25542
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
25543
|
+
import { cp, mkdir as mkdir21, readFile as readFile32, readdir as readdir8, writeFile as writeFile21 } from "node:fs/promises";
|
|
25544
|
+
import { dirname as dirname9, join as join21, resolve as resolve32 } from "node:path";
|
|
25545
|
+
function experimentDir(projectRoot, experimentId) {
|
|
25546
|
+
return resolve32(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR, experimentId);
|
|
25547
|
+
}
|
|
25548
|
+
function experimentYamlPath(projectRoot, experimentId) {
|
|
25549
|
+
return join21(experimentDir(projectRoot, experimentId), "experiment.yml");
|
|
25550
|
+
}
|
|
25551
|
+
async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
|
|
25552
|
+
const candidates = suitePath ? [suitePath] : [
|
|
25553
|
+
resolve32(projectRoot, "eval", "suites", suiteId, "suite.yml"),
|
|
25554
|
+
resolve32(projectRoot, EVAL_DIR2, "suites", suiteId, "suite.yml")
|
|
25555
|
+
];
|
|
25556
|
+
for (const path of candidates) {
|
|
25557
|
+
if (!existsSync7(path))
|
|
25558
|
+
continue;
|
|
25559
|
+
try {
|
|
25560
|
+
const suite = HarnessSuiteSchema.parse($parse(await readFile32(path, "utf-8")));
|
|
25561
|
+
return ok({ ...suite, path });
|
|
25562
|
+
} catch (e) {
|
|
25563
|
+
return err(e instanceof Error ? e : new Error(String(e)));
|
|
25564
|
+
}
|
|
25565
|
+
}
|
|
25566
|
+
return err(new Error(`Harness suite not found: ${suiteId}`));
|
|
25567
|
+
}
|
|
25568
|
+
async function loadExperiment(projectRoot, experimentId) {
|
|
25569
|
+
try {
|
|
25570
|
+
const raw = await readFile32(experimentYamlPath(projectRoot, experimentId), "utf-8");
|
|
25571
|
+
return ok(HarnessExperimentSchema.parse($parse(raw)));
|
|
25572
|
+
} catch (e) {
|
|
25573
|
+
if (e.code === "ENOENT") {
|
|
25574
|
+
return err(new Error(`Experiment not found: ${experimentId}`));
|
|
25575
|
+
}
|
|
25576
|
+
return err(e instanceof Error ? e : new Error(String(e)));
|
|
25577
|
+
}
|
|
25578
|
+
}
|
|
25579
|
+
async function listExperiments(projectRoot) {
|
|
25580
|
+
const root = resolve32(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR);
|
|
25581
|
+
if (!existsSync7(root))
|
|
25582
|
+
return [];
|
|
25583
|
+
const ids = await readdir8(root);
|
|
25584
|
+
const experiments = [];
|
|
25585
|
+
for (const id of ids) {
|
|
25586
|
+
const loaded = await loadExperiment(projectRoot, id);
|
|
25587
|
+
if (loaded.success)
|
|
25588
|
+
experiments.push(loaded.data);
|
|
25589
|
+
}
|
|
25590
|
+
return experiments;
|
|
25591
|
+
}
|
|
25592
|
+
function formatTaskCard(task, experimentId, variantId) {
|
|
25593
|
+
return [
|
|
25594
|
+
"## Task Card",
|
|
25595
|
+
"",
|
|
25596
|
+
`**Title:** ${task.title}`,
|
|
25597
|
+
`**Type:** ${task.type}`,
|
|
25598
|
+
`**Risk:** ${task.risk}`,
|
|
25599
|
+
`**Scope:** ${task.scope}`,
|
|
25600
|
+
`**suiteTaskId:** ${task.id}`,
|
|
25601
|
+
`**experimentId:** ${experimentId}`,
|
|
25602
|
+
`**variantId:** ${variantId}`,
|
|
25603
|
+
"",
|
|
25604
|
+
"### Context",
|
|
25605
|
+
"",
|
|
25606
|
+
task.prompt,
|
|
25607
|
+
"",
|
|
25608
|
+
"### Acceptance Criteria",
|
|
25609
|
+
"",
|
|
25610
|
+
...task.acceptanceCriteria.map((c) => `- [ ] ${c}`),
|
|
25611
|
+
"",
|
|
25612
|
+
"### Constraints",
|
|
25613
|
+
"",
|
|
25614
|
+
"- Stay inside the declared scope.",
|
|
25615
|
+
"- Record the outcome with experiment and variant tags.",
|
|
25616
|
+
"",
|
|
25617
|
+
"### Scoring",
|
|
25618
|
+
"",
|
|
25619
|
+
"When done:",
|
|
25620
|
+
"",
|
|
25621
|
+
"```",
|
|
25622
|
+
`bun run dev scorecard create --task ${task.id} --from-diff`,
|
|
25623
|
+
`bun run dev scorecard record --task ${task.id} --verdict PASS|REVISE|REJECT --score <n> --experiment ${experimentId} --variant ${variantId} --suite-task ${task.id}`,
|
|
25624
|
+
"```",
|
|
25625
|
+
task.testCommand ? `
|
|
25626
|
+
Fixture test command: \`${task.testCommand}\`
|
|
25627
|
+
` : ""
|
|
25628
|
+
].join(`
|
|
25629
|
+
`);
|
|
25630
|
+
}
|
|
25631
|
+
function formatExperimentMarkdown(experiment) {
|
|
25632
|
+
return [
|
|
25633
|
+
`# Harness experiment ${experiment.id}`,
|
|
25634
|
+
"",
|
|
25635
|
+
`- Suite: ${experiment.suiteId}`,
|
|
25636
|
+
`- Contract: ${experiment.contractVersion}`,
|
|
25637
|
+
`- Created: ${experiment.createdAt}`,
|
|
25638
|
+
`- Variants: ${experiment.variants.join(", ")}`,
|
|
25639
|
+
`- Tasks: ${experiment.suiteTaskIds.join(", ")}`,
|
|
25640
|
+
"",
|
|
25641
|
+
"This runner does not invoke a model. Execute each run directory with the host",
|
|
25642
|
+
"agent, then `scorecard record` with `--experiment` and `--variant`.",
|
|
25643
|
+
"",
|
|
25644
|
+
"Compare with:",
|
|
25645
|
+
"",
|
|
25646
|
+
"```",
|
|
25647
|
+
`bun run dev scorecard ablation --experiment ${experiment.id}`,
|
|
25648
|
+
"```",
|
|
25649
|
+
""
|
|
25650
|
+
].join(`
|
|
25651
|
+
`);
|
|
25652
|
+
}
|
|
25653
|
+
function generateExperimentId(suiteId, now = new Date) {
|
|
25654
|
+
const date = now.toISOString().slice(0, 10).replaceAll("-", "");
|
|
25655
|
+
const rand = Math.random().toString(36).slice(2, 6);
|
|
25656
|
+
return `abl-${date}-${suiteId}-${rand}`;
|
|
25657
|
+
}
|
|
25658
|
+
async function copyFixture(suitePath, fixtureDir, dest) {
|
|
25659
|
+
const source = fixtureDir ? resolve32(dirname9(suitePath), fixtureDir) : join21(dirname9(suitePath), "fixture");
|
|
25660
|
+
await mkdir21(dest, { recursive: true });
|
|
25661
|
+
if (existsSync7(source)) {
|
|
25662
|
+
await cp(source, dest, { recursive: true });
|
|
25663
|
+
}
|
|
25664
|
+
}
|
|
25665
|
+
async function startHarnessExperiment(projectRoot, options) {
|
|
25666
|
+
const suite = await loadHarnessSuite(projectRoot, options.suiteId, options.suitePath);
|
|
25667
|
+
if (!suite.success)
|
|
25668
|
+
return suite;
|
|
25669
|
+
const experimentId = options.experimentId ?? generateExperimentId(suite.data.id);
|
|
25670
|
+
const variants = ["baseline", ...options.components.map((id) => variantIdFor(id))];
|
|
25671
|
+
const experiment = {
|
|
25672
|
+
id: experimentId,
|
|
25673
|
+
suiteId: suite.data.id,
|
|
25674
|
+
createdAt: new Date().toISOString(),
|
|
25675
|
+
contractVersion: options.contractVersion,
|
|
25676
|
+
components: options.components,
|
|
25677
|
+
variants,
|
|
25678
|
+
suiteTaskIds: suite.data.tasks.map((t) => t.id),
|
|
25679
|
+
suitePath: suite.data.path
|
|
25680
|
+
};
|
|
25681
|
+
const root = experimentDir(projectRoot, experimentId);
|
|
25682
|
+
await mkdir21(root, { recursive: true });
|
|
25683
|
+
await writeFile21(experimentYamlPath(projectRoot, experimentId), $stringify(experiment), "utf-8");
|
|
25684
|
+
await writeFile21(join21(root, "EXPERIMENT.md"), formatExperimentMarkdown(experiment), "utf-8");
|
|
25685
|
+
for (const task of suite.data.tasks) {
|
|
25686
|
+
for (const variantId of variants) {
|
|
25687
|
+
const runRoot = join21(root, "runs", task.id, variantDirName(variantId));
|
|
25688
|
+
await copyFixture(suite.data.path, suite.data.fixtureDir, runRoot);
|
|
25689
|
+
await writeConfig(runRoot, structuredClone(DEFAULT_CONFIG), true);
|
|
25690
|
+
const disabled = variantId === "baseline" ? [] : options.components.filter((id) => variantIdFor(id) === variantId);
|
|
25691
|
+
const overlay = await applyHarnessOverlay(runRoot, disabled, {
|
|
25692
|
+
experimentId,
|
|
25693
|
+
variantId,
|
|
25694
|
+
contractVersion: options.contractVersion,
|
|
25695
|
+
backup: false
|
|
25696
|
+
});
|
|
25697
|
+
if (!overlay.success)
|
|
25698
|
+
return overlay;
|
|
25699
|
+
await writeFile21(join21(runRoot, "TASK.md"), formatTaskCard(task, experimentId, variantId), "utf-8");
|
|
25700
|
+
}
|
|
25701
|
+
}
|
|
25702
|
+
return ok(experiment);
|
|
25703
|
+
}
|
|
25704
|
+
async function applyExperimentVariant(projectRoot, experimentId, variantId, contractVersion) {
|
|
25705
|
+
const loaded = await loadExperiment(projectRoot, experimentId);
|
|
25706
|
+
if (!loaded.success)
|
|
25707
|
+
return loaded;
|
|
25708
|
+
if (!loaded.data.variants.includes(variantId)) {
|
|
25709
|
+
return err(new Error(`Unknown variant ${variantId} for ${experimentId}`));
|
|
25710
|
+
}
|
|
25711
|
+
const disabled = variantId === "baseline" ? [] : loaded.data.components.filter((id) => variantIdFor(id) === variantId);
|
|
25712
|
+
const overlay = await applyHarnessOverlay(projectRoot, disabled, {
|
|
25713
|
+
experimentId,
|
|
25714
|
+
variantId,
|
|
25715
|
+
contractVersion,
|
|
25716
|
+
backup: true
|
|
25717
|
+
});
|
|
25718
|
+
if (!overlay.success)
|
|
25719
|
+
return overlay;
|
|
25720
|
+
return ok(loaded.data);
|
|
25721
|
+
}
|
|
25722
|
+
var EXPERIMENTS_DIR = "experiments";
|
|
25723
|
+
var init_harness_experiment = __esm(() => {
|
|
25724
|
+
init_dist();
|
|
25725
|
+
init_schemas();
|
|
25726
|
+
init_codeconductor_config();
|
|
25727
|
+
init_config_writer();
|
|
25728
|
+
init_harness_catalog();
|
|
25729
|
+
});
|
|
25730
|
+
|
|
25731
|
+
// src/core/evaluation/ablation-report.ts
|
|
25732
|
+
import { mkdir as mkdir22, writeFile as writeFile22 } from "node:fs/promises";
|
|
25733
|
+
import { resolve as resolve33 } from "node:path";
|
|
25734
|
+
function isPass(o) {
|
|
25735
|
+
return o.verdict === "PASS" || o.status === "pass";
|
|
25736
|
+
}
|
|
25737
|
+
function mean(values) {
|
|
25738
|
+
if (values.length === 0)
|
|
25739
|
+
return;
|
|
25740
|
+
return values.reduce((s, v) => s + v, 0) / values.length;
|
|
25741
|
+
}
|
|
25742
|
+
function metricsFrom(outcomes, criteria) {
|
|
25743
|
+
const scores = outcomes.map((o) => o.weightedScore).filter((n) => n !== undefined);
|
|
25744
|
+
const costs = outcomes.map((o) => o.costUsd).filter((n) => n !== undefined);
|
|
25745
|
+
const durations = outcomes.map((o) => o.durationMs).filter((n) => n !== undefined);
|
|
25746
|
+
return {
|
|
25747
|
+
count: outcomes.length,
|
|
25748
|
+
avgScore: mean(scores) ?? 0,
|
|
25749
|
+
passRate: outcomes.length > 0 ? outcomes.filter(isPass).length / outcomes.length : 0,
|
|
25750
|
+
avgCost: mean(costs),
|
|
25751
|
+
avgDurationMs: mean(durations),
|
|
25752
|
+
criteria
|
|
25753
|
+
};
|
|
25754
|
+
}
|
|
25755
|
+
function classifyAblation(deltaScore, deltaPassRate) {
|
|
25756
|
+
const scoreNeutral = Math.abs(deltaScore) < SCORE_THRESHOLD;
|
|
25757
|
+
const passNeutral = Math.abs(deltaPassRate) < PASS_RATE_THRESHOLD;
|
|
25758
|
+
if (scoreNeutral && passNeutral)
|
|
25759
|
+
return "no_change";
|
|
25760
|
+
if (deltaScore <= -SCORE_THRESHOLD)
|
|
25761
|
+
return "degrades";
|
|
25762
|
+
if (deltaScore >= SCORE_THRESHOLD)
|
|
25763
|
+
return "improves";
|
|
25764
|
+
if (deltaPassRate <= -PASS_RATE_THRESHOLD)
|
|
25765
|
+
return "degrades";
|
|
25766
|
+
if (deltaPassRate >= PASS_RATE_THRESHOLD)
|
|
25767
|
+
return "improves";
|
|
25768
|
+
return "no_change";
|
|
25769
|
+
}
|
|
25770
|
+
async function averageCriteria(projectRoot, outcomes) {
|
|
25771
|
+
const buckets = {};
|
|
25772
|
+
for (const o of outcomes) {
|
|
25773
|
+
if (!o.scorecardId)
|
|
25774
|
+
continue;
|
|
25775
|
+
const loaded = await loadScorecard(projectRoot, o.scorecardId);
|
|
25776
|
+
if (!loaded.success)
|
|
25777
|
+
continue;
|
|
25778
|
+
for (const c of loaded.data.criteria) {
|
|
25779
|
+
if (!buckets[c.id])
|
|
25780
|
+
buckets[c.id] = [];
|
|
25781
|
+
buckets[c.id].push(c.score);
|
|
25782
|
+
}
|
|
25783
|
+
}
|
|
25784
|
+
const ids = Object.keys(buckets);
|
|
25785
|
+
if (ids.length === 0)
|
|
25786
|
+
return;
|
|
25787
|
+
const avg = {};
|
|
25788
|
+
for (const id of ids) {
|
|
25789
|
+
avg[id] = mean(buckets[id]) ?? 0;
|
|
25790
|
+
}
|
|
25791
|
+
return avg;
|
|
25792
|
+
}
|
|
25793
|
+
async function buildAblationReport(projectRoot, outcomes, experimentId) {
|
|
25794
|
+
const scoped = experimentId ? outcomes.filter((o) => o.experimentId === experimentId) : outcomes.filter((o) => o.variantId);
|
|
25795
|
+
const byComponent = new Map;
|
|
25796
|
+
const keyOf = (o) => o.suiteTaskId ?? o.taskId;
|
|
25797
|
+
const treatmentIds = (o) => {
|
|
25798
|
+
const fromField = o.disabledComponents?.filter((id) => catalogIds().includes(id));
|
|
25799
|
+
if (fromField && fromField.length > 0)
|
|
25800
|
+
return fromField;
|
|
25801
|
+
return parseVariantId(o.variantId ?? "");
|
|
25802
|
+
};
|
|
25803
|
+
const baselines = scoped.filter((o) => o.variantId === "baseline");
|
|
25804
|
+
const treatments = scoped.filter((o) => o.variantId && o.variantId !== "baseline");
|
|
25805
|
+
const baselineKeys = new Set(baselines.map(keyOf));
|
|
25806
|
+
for (const o of treatments) {
|
|
25807
|
+
const ids = treatmentIds(o);
|
|
25808
|
+
const hasPair = baselineKeys.has(keyOf(o)) || baselines.length > 0;
|
|
25809
|
+
if (!hasPair)
|
|
25810
|
+
continue;
|
|
25811
|
+
for (const id of ids) {
|
|
25812
|
+
if (!byComponent.has(id))
|
|
25813
|
+
byComponent.set(id, { baseline: [], treatment: [] });
|
|
25814
|
+
byComponent.get(id).treatment.push(o);
|
|
25815
|
+
}
|
|
25816
|
+
}
|
|
25817
|
+
for (const [id, pair] of byComponent) {
|
|
25818
|
+
const treatmentKeys = new Set(pair.treatment.map(keyOf));
|
|
25819
|
+
const pairedBaselines = baselines.filter((o) => treatmentKeys.has(keyOf(o)));
|
|
25820
|
+
pair.baseline = pairedBaselines.length > 0 ? pairedBaselines : baselines;
|
|
25821
|
+
}
|
|
25822
|
+
const rows = [];
|
|
25823
|
+
for (const id of catalogIds()) {
|
|
25824
|
+
const pair = byComponent.get(id);
|
|
25825
|
+
if (!pair || pair.treatment.length === 0 || pair.baseline.length === 0)
|
|
25826
|
+
continue;
|
|
25827
|
+
const baselineCriteria = await averageCriteria(projectRoot, pair.baseline);
|
|
25828
|
+
const treatmentCriteria = await averageCriteria(projectRoot, pair.treatment);
|
|
25829
|
+
const baseline = metricsFrom(pair.baseline, baselineCriteria);
|
|
25830
|
+
const treatment = metricsFrom(pair.treatment, treatmentCriteria);
|
|
25831
|
+
const deltaScore = treatment.avgScore - baseline.avgScore;
|
|
25832
|
+
const deltaPassRate = treatment.passRate - baseline.passRate;
|
|
25833
|
+
const deltaCriteria = {};
|
|
25834
|
+
const criterionIds = new Set([
|
|
25835
|
+
...Object.keys(baseline.criteria ?? {}),
|
|
25836
|
+
...Object.keys(treatment.criteria ?? {})
|
|
25837
|
+
]);
|
|
25838
|
+
for (const cid of criterionIds) {
|
|
25839
|
+
deltaCriteria[cid] = (treatment.criteria?.[cid] ?? 0) - (baseline.criteria?.[cid] ?? 0);
|
|
25840
|
+
}
|
|
25841
|
+
rows.push({
|
|
25842
|
+
component: id,
|
|
25843
|
+
variantId: `minus:${id}`,
|
|
25844
|
+
baseline,
|
|
25845
|
+
treatment,
|
|
25846
|
+
deltaScore: Math.round(deltaScore * 1000) / 1000,
|
|
25847
|
+
deltaPassRate: Math.round(deltaPassRate * 1000) / 1000,
|
|
25848
|
+
deltaCost: baseline.avgCost !== undefined && treatment.avgCost !== undefined ? Math.round((treatment.avgCost - baseline.avgCost) * 1e4) / 1e4 : undefined,
|
|
25849
|
+
deltaDurationMs: baseline.avgDurationMs !== undefined && treatment.avgDurationMs !== undefined ? Math.round(treatment.avgDurationMs - baseline.avgDurationMs) : undefined,
|
|
25850
|
+
deltaCriteria,
|
|
25851
|
+
verdict: classifyAblation(deltaScore, deltaPassRate)
|
|
25852
|
+
});
|
|
25853
|
+
}
|
|
25854
|
+
return {
|
|
25855
|
+
generatedAt: new Date().toISOString(),
|
|
25856
|
+
experimentId,
|
|
25857
|
+
outcomeCount: scoped.length,
|
|
25858
|
+
pairedCount: rows.reduce((s, r) => s + Math.min(r.baseline.count, r.treatment.count), 0),
|
|
25859
|
+
rows
|
|
25860
|
+
};
|
|
25861
|
+
}
|
|
25862
|
+
function formatAblationMarkdown(report) {
|
|
25863
|
+
const lines = [
|
|
25864
|
+
"# Harness Ablation Report",
|
|
25865
|
+
"",
|
|
25866
|
+
`Generated: ${report.generatedAt}`,
|
|
25867
|
+
report.experimentId ? `Experiment: ${report.experimentId}` : "Experiment: (all tagged outcomes)",
|
|
25868
|
+
`Outcomes: ${report.outcomeCount}`,
|
|
25869
|
+
"",
|
|
25870
|
+
"| Component | Verdict | Δ score | Δ pass | Δ cost | Δ duration | Baseline n | Treatment n |",
|
|
25871
|
+
"|-----------|---------|---------|--------|--------|------------|------------|-------------|"
|
|
25872
|
+
];
|
|
25873
|
+
for (const row of report.rows) {
|
|
25874
|
+
lines.push(`| ${row.component} | ${row.verdict} | ${row.deltaScore.toFixed(2)} | ${(row.deltaPassRate * 100).toFixed(0)}pp | ${row.deltaCost?.toFixed(4) ?? "—"} | ${row.deltaDurationMs ?? "—"} | ${row.baseline.count} | ${row.treatment.count} |`);
|
|
25875
|
+
}
|
|
25876
|
+
if (report.rows.length === 0) {
|
|
25877
|
+
lines.push("| — | — | — | — | — | — | — | — |");
|
|
25878
|
+
lines.push("", "No paired baseline/treatment outcomes yet.");
|
|
25879
|
+
} else {
|
|
25880
|
+
lines.push("", "## Criterion deltas", "");
|
|
25881
|
+
for (const row of report.rows) {
|
|
25882
|
+
const keys = Object.keys(row.deltaCriteria);
|
|
25883
|
+
if (keys.length === 0)
|
|
25884
|
+
continue;
|
|
25885
|
+
lines.push(`### minus:${row.component}`);
|
|
25886
|
+
for (const key of keys) {
|
|
25887
|
+
const delta = row.deltaCriteria[key] ?? 0;
|
|
25888
|
+
lines.push(`- ${key}: ${delta >= 0 ? "+" : ""}${delta.toFixed(2)}`);
|
|
25889
|
+
}
|
|
25890
|
+
lines.push("");
|
|
25891
|
+
}
|
|
25892
|
+
lines.push("## How to read", "");
|
|
25893
|
+
lines.push(`- \`improves\`: removing the component raised score by ≥ ${SCORE_THRESHOLD} or pass rate by ≥ ${PASS_RATE_THRESHOLD * 100}pp.`);
|
|
25894
|
+
lines.push(`- \`degrades\`: removing the component lowered score by ≥ ${SCORE_THRESHOLD} or pass rate by ≥ ${PASS_RATE_THRESHOLD * 100}pp.`);
|
|
25895
|
+
lines.push(`- \`no_change\`: both deltas stay inside those thresholds.`);
|
|
25896
|
+
}
|
|
25897
|
+
return lines.join(`
|
|
25898
|
+
`);
|
|
25899
|
+
}
|
|
25900
|
+
async function persistAblationReport(projectRoot, markdown) {
|
|
25901
|
+
const dir = resolve33(projectRoot, EVAL_DIR2);
|
|
25902
|
+
await mkdir22(dir, { recursive: true });
|
|
25903
|
+
const path = resolve33(dir, "ablation-last.md");
|
|
25904
|
+
await writeFile22(path, markdown, "utf-8");
|
|
25905
|
+
return path;
|
|
25906
|
+
}
|
|
25907
|
+
var SCORE_THRESHOLD = 0.1, PASS_RATE_THRESHOLD = 0.05;
|
|
25908
|
+
var init_ablation_report = __esm(() => {
|
|
25909
|
+
init_harness_catalog();
|
|
25910
|
+
init_outcome_store();
|
|
25911
|
+
});
|
|
25912
|
+
|
|
23979
25913
|
// src/commands/scorecard.command.ts
|
|
23980
25914
|
var exports_scorecard_command = {};
|
|
23981
25915
|
__export(exports_scorecard_command, {
|
|
23982
25916
|
scorecardCommand: () => scorecardCommand
|
|
23983
25917
|
});
|
|
23984
|
-
import { readFile as
|
|
23985
|
-
import { resolve as
|
|
25918
|
+
import { readFile as readFile33 } from "node:fs/promises";
|
|
25919
|
+
import { resolve as resolve34 } from "node:path";
|
|
23986
25920
|
function contractVersion() {
|
|
23987
25921
|
return package_default.version;
|
|
23988
25922
|
}
|
|
@@ -24009,6 +25943,14 @@ async function scorecardCommand(options) {
|
|
|
24009
25943
|
return handleMatrix(projectRoot);
|
|
24010
25944
|
case "compare-models":
|
|
24011
25945
|
return handleCompareModels(projectRoot, options);
|
|
25946
|
+
case "catalog":
|
|
25947
|
+
return handleCatalog(projectRoot);
|
|
25948
|
+
case "fingerprint":
|
|
25949
|
+
return handleFingerprint(projectRoot);
|
|
25950
|
+
case "experiment":
|
|
25951
|
+
return handleExperiment(projectRoot, options);
|
|
25952
|
+
case "ablation":
|
|
25953
|
+
return handleAblation(projectRoot, options);
|
|
24012
25954
|
default:
|
|
24013
25955
|
return {
|
|
24014
25956
|
code: 1,
|
|
@@ -24016,7 +25958,7 @@ async function scorecardCommand(options) {
|
|
|
24016
25958
|
success: false,
|
|
24017
25959
|
command: "scorecard",
|
|
24018
25960
|
errors: [
|
|
24019
|
-
`Unknown subcommand: ${subcommand}. Use: create, show, record, list, aggregate, models, prompt-diff, regression, matrix, compare-models`
|
|
25961
|
+
`Unknown subcommand: ${subcommand}. Use: create, show, record, list, aggregate, models, prompt-diff, regression, matrix, compare-models, catalog, fingerprint, experiment, ablation`
|
|
24020
25962
|
]
|
|
24021
25963
|
}
|
|
24022
25964
|
};
|
|
@@ -24085,6 +26027,12 @@ async function handleShow(projectRoot, id) {
|
|
|
24085
26027
|
}
|
|
24086
26028
|
async function handleRecord(projectRoot, options) {
|
|
24087
26029
|
const taskId = options.taskId ?? "unknown-task";
|
|
26030
|
+
const overlay = await readActiveOverlay(projectRoot);
|
|
26031
|
+
const experimentId = options.experimentId ?? overlay?.experimentId;
|
|
26032
|
+
const variantId = options.variantId ?? overlay?.variantId;
|
|
26033
|
+
const suiteTaskId = options.suiteTaskId;
|
|
26034
|
+
const disabled = overlay?.disabledComponents ?? (variantId ? parseVariantId(variantId) : undefined);
|
|
26035
|
+
const fingerprint = experimentId || variantId ? harnessFingerprint(disabled ?? [], overlay?.contractVersion ?? contractVersion(), overlay?.ccepProfile ?? "feature") : undefined;
|
|
24088
26036
|
const outcome = {
|
|
24089
26037
|
id: generateEvalId("out"),
|
|
24090
26038
|
taskId,
|
|
@@ -24099,6 +26047,11 @@ async function handleRecord(projectRoot, options) {
|
|
|
24099
26047
|
costUsd: options.costUsd,
|
|
24100
26048
|
tokensIn: options.tokens,
|
|
24101
26049
|
durationMs: options.durationMs,
|
|
26050
|
+
experimentId,
|
|
26051
|
+
variantId,
|
|
26052
|
+
suiteTaskId,
|
|
26053
|
+
harnessFingerprint: fingerprint,
|
|
26054
|
+
disabledComponents: disabled && disabled.length > 0 ? [...disabled] : disabled ? [] : undefined,
|
|
24102
26055
|
status: options.verdict === "PASS" ? "pass" : options.verdict === "REJECT" ? "reject" : undefined
|
|
24103
26056
|
};
|
|
24104
26057
|
const result = await appendOutcome(projectRoot, outcome);
|
|
@@ -24113,7 +26066,10 @@ async function handleList(projectRoot, options) {
|
|
|
24113
26066
|
model: options.model,
|
|
24114
26067
|
taskId: options.taskId,
|
|
24115
26068
|
since: options.since,
|
|
24116
|
-
source: options.source
|
|
26069
|
+
source: options.source,
|
|
26070
|
+
experimentId: options.experimentId,
|
|
26071
|
+
variantId: options.variantId,
|
|
26072
|
+
suiteTaskId: options.suiteTaskId
|
|
24117
26073
|
};
|
|
24118
26074
|
const result = await listOutcomes(projectRoot, filter);
|
|
24119
26075
|
if (!result.success) {
|
|
@@ -24125,7 +26081,13 @@ async function handleList(projectRoot, options) {
|
|
|
24125
26081
|
};
|
|
24126
26082
|
}
|
|
24127
26083
|
async function handleAggregate(projectRoot, options) {
|
|
24128
|
-
const filter = {
|
|
26084
|
+
const filter = {
|
|
26085
|
+
since: options.since,
|
|
26086
|
+
agent: options.agent,
|
|
26087
|
+
model: options.model,
|
|
26088
|
+
experimentId: options.experimentId,
|
|
26089
|
+
variantId: options.variantId
|
|
26090
|
+
};
|
|
24129
26091
|
const list = await listOutcomes(projectRoot, filter);
|
|
24130
26092
|
if (!list.success) {
|
|
24131
26093
|
return { code: 1, data: { success: false, errors: [list.error.message] } };
|
|
@@ -24206,13 +26168,13 @@ async function handleCompareModels(projectRoot, options) {
|
|
|
24206
26168
|
outcomes = outcomes.filter((o) => options.modelsFilter.includes(o.model));
|
|
24207
26169
|
}
|
|
24208
26170
|
const markdown = generateModelComparisonMarkdown(outcomes, options.modelsFilter);
|
|
24209
|
-
const templatePath =
|
|
26171
|
+
const templatePath = resolve34(projectRoot, ".codeconductor/evaluation/model-comparison-last.md");
|
|
24210
26172
|
try {
|
|
24211
|
-
await
|
|
26173
|
+
await readFile33(templatePath);
|
|
24212
26174
|
} catch {}
|
|
24213
|
-
const { writeFile:
|
|
24214
|
-
await
|
|
24215
|
-
await
|
|
26175
|
+
const { writeFile: writeFile23, mkdir: mkdir23 } = await import("node:fs/promises");
|
|
26176
|
+
await mkdir23(resolve34(projectRoot, ".codeconductor/evaluation"), { recursive: true });
|
|
26177
|
+
await writeFile23(templatePath, markdown, "utf-8");
|
|
24216
26178
|
return {
|
|
24217
26179
|
code: 0,
|
|
24218
26180
|
data: {
|
|
@@ -24224,6 +26186,119 @@ async function handleCompareModels(projectRoot, options) {
|
|
|
24224
26186
|
}
|
|
24225
26187
|
};
|
|
24226
26188
|
}
|
|
26189
|
+
async function handleCatalog(projectRoot) {
|
|
26190
|
+
const catalog = await loadHarnessCatalog(projectRoot);
|
|
26191
|
+
return {
|
|
26192
|
+
code: 0,
|
|
26193
|
+
data: { success: true, command: "scorecard catalog", catalog }
|
|
26194
|
+
};
|
|
26195
|
+
}
|
|
26196
|
+
async function handleFingerprint(projectRoot) {
|
|
26197
|
+
const snap = await currentFingerprint(projectRoot, contractVersion());
|
|
26198
|
+
return {
|
|
26199
|
+
code: 0,
|
|
26200
|
+
data: {
|
|
26201
|
+
success: true,
|
|
26202
|
+
command: "scorecard fingerprint",
|
|
26203
|
+
fingerprint: snap.fingerprint,
|
|
26204
|
+
overlay: snap.overlay,
|
|
26205
|
+
components: snap.states
|
|
26206
|
+
}
|
|
26207
|
+
};
|
|
26208
|
+
}
|
|
26209
|
+
async function handleExperiment(projectRoot, options) {
|
|
26210
|
+
const action = options.experimentAction ?? "list";
|
|
26211
|
+
if (action === "list") {
|
|
26212
|
+
const experiments = await listExperiments(projectRoot);
|
|
26213
|
+
return {
|
|
26214
|
+
code: 0,
|
|
26215
|
+
data: { success: true, command: "scorecard experiment list", experiments, count: experiments.length }
|
|
26216
|
+
};
|
|
26217
|
+
}
|
|
26218
|
+
if (action === "show") {
|
|
26219
|
+
if (!options.experimentId && !options.id) {
|
|
26220
|
+
return { code: 1, data: { success: false, errors: ["Usage: scorecard experiment show <id>"] } };
|
|
26221
|
+
}
|
|
26222
|
+
const loaded = await loadExperiment(projectRoot, options.experimentId ?? options.id);
|
|
26223
|
+
if (!loaded.success) {
|
|
26224
|
+
return { code: 1, data: { success: false, errors: [loaded.error.message] } };
|
|
26225
|
+
}
|
|
26226
|
+
return { code: 0, data: { success: true, command: "scorecard experiment show", experiment: loaded.data } };
|
|
26227
|
+
}
|
|
26228
|
+
if (action === "start") {
|
|
26229
|
+
const parsed = parseComponentsFlag(options.components);
|
|
26230
|
+
if (!parsed.success) {
|
|
26231
|
+
return { code: 1, data: { success: false, errors: [parsed.error.message] } };
|
|
26232
|
+
}
|
|
26233
|
+
const started = await startHarnessExperiment(projectRoot, {
|
|
26234
|
+
suiteId: options.suiteId ?? "harness-v1",
|
|
26235
|
+
suitePath: options.suitePath,
|
|
26236
|
+
components: parsed.data,
|
|
26237
|
+
contractVersion: contractVersion(),
|
|
26238
|
+
experimentId: options.experimentId ?? options.id
|
|
26239
|
+
});
|
|
26240
|
+
if (!started.success) {
|
|
26241
|
+
return { code: 1, data: { success: false, errors: [started.error.message] } };
|
|
26242
|
+
}
|
|
26243
|
+
return {
|
|
26244
|
+
code: 0,
|
|
26245
|
+
data: { success: true, command: "scorecard experiment start", experiment: started.data }
|
|
26246
|
+
};
|
|
26247
|
+
}
|
|
26248
|
+
if (action === "apply") {
|
|
26249
|
+
const experimentId = options.experimentId ?? options.id;
|
|
26250
|
+
const variantId = options.variantId;
|
|
26251
|
+
if (!experimentId || !variantId) {
|
|
26252
|
+
return {
|
|
26253
|
+
code: 1,
|
|
26254
|
+
data: { success: false, errors: ["Usage: scorecard experiment apply --id <exp> --variant baseline|minus:<id>"] }
|
|
26255
|
+
};
|
|
26256
|
+
}
|
|
26257
|
+
const applied = await applyExperimentVariant(projectRoot, experimentId, variantId, contractVersion());
|
|
26258
|
+
if (!applied.success) {
|
|
26259
|
+
return { code: 1, data: { success: false, errors: [applied.error.message] } };
|
|
26260
|
+
}
|
|
26261
|
+
return {
|
|
26262
|
+
code: 0,
|
|
26263
|
+
data: {
|
|
26264
|
+
success: true,
|
|
26265
|
+
command: "scorecard experiment apply",
|
|
26266
|
+
experiment: applied.data,
|
|
26267
|
+
variantId
|
|
26268
|
+
}
|
|
26269
|
+
};
|
|
26270
|
+
}
|
|
26271
|
+
return {
|
|
26272
|
+
code: 1,
|
|
26273
|
+
data: {
|
|
26274
|
+
success: false,
|
|
26275
|
+
errors: ["Usage: scorecard experiment start|apply|list|show"]
|
|
26276
|
+
}
|
|
26277
|
+
};
|
|
26278
|
+
}
|
|
26279
|
+
async function handleAblation(projectRoot, options) {
|
|
26280
|
+
const filter = {
|
|
26281
|
+
since: options.since,
|
|
26282
|
+
experimentId: options.experimentId ?? options.id
|
|
26283
|
+
};
|
|
26284
|
+
const list = await listOutcomes(projectRoot, filter);
|
|
26285
|
+
if (!list.success) {
|
|
26286
|
+
return { code: 1, data: { success: false, errors: [list.error.message] } };
|
|
26287
|
+
}
|
|
26288
|
+
const report = await buildAblationReport(projectRoot, list.data, filter.experimentId);
|
|
26289
|
+
const markdown = formatAblationMarkdown(report);
|
|
26290
|
+
const path = await persistAblationReport(projectRoot, markdown);
|
|
26291
|
+
return {
|
|
26292
|
+
code: 0,
|
|
26293
|
+
data: {
|
|
26294
|
+
success: true,
|
|
26295
|
+
command: "scorecard ablation",
|
|
26296
|
+
report,
|
|
26297
|
+
markdown,
|
|
26298
|
+
path
|
|
26299
|
+
}
|
|
26300
|
+
};
|
|
26301
|
+
}
|
|
24227
26302
|
var init_scorecard_command = __esm(() => {
|
|
24228
26303
|
init_package();
|
|
24229
26304
|
init_config_loader();
|
|
@@ -24235,6 +26310,9 @@ var init_scorecard_command = __esm(() => {
|
|
|
24235
26310
|
init_regression_checklist();
|
|
24236
26311
|
init_model_comparison();
|
|
24237
26312
|
init_cost_quality_matrix();
|
|
26313
|
+
init_harness_catalog();
|
|
26314
|
+
init_harness_experiment();
|
|
26315
|
+
init_ablation_report();
|
|
24238
26316
|
init_backlog_parser();
|
|
24239
26317
|
});
|
|
24240
26318
|
|
|
@@ -25538,7 +27616,7 @@ var exports_seo_audit_command = {};
|
|
|
25538
27616
|
__export(exports_seo_audit_command, {
|
|
25539
27617
|
seoAuditCommand: () => seoAuditCommand
|
|
25540
27618
|
});
|
|
25541
|
-
import { join as
|
|
27619
|
+
import { join as join22 } from "node:path";
|
|
25542
27620
|
async function seoAuditCommand(options) {
|
|
25543
27621
|
const { url, sitemap, format, failOn, delay: delay2, output, followRedirects, force } = options;
|
|
25544
27622
|
if (!url && !sitemap) {
|
|
@@ -25551,7 +27629,7 @@ async function seoAuditCommand(options) {
|
|
|
25551
27629
|
}
|
|
25552
27630
|
};
|
|
25553
27631
|
}
|
|
25554
|
-
const defaultMarkdownPath = output === undefined && format === "markdown" ?
|
|
27632
|
+
const defaultMarkdownPath = output === undefined && format === "markdown" ? join22("seo-reports", `audit-report-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19)}.md`) : undefined;
|
|
25555
27633
|
const outputPathToWrite = output ?? defaultMarkdownPath;
|
|
25556
27634
|
if (outputPathToWrite !== undefined) {
|
|
25557
27635
|
const preflightError = await preflightContainedOutput(options.projectRoot, outputPathToWrite, { force });
|
|
@@ -25814,7 +27892,7 @@ var init_seo_llms_command = __esm(() => {
|
|
|
25814
27892
|
init_llms_generator();
|
|
25815
27893
|
});
|
|
25816
27894
|
|
|
25817
|
-
// src/cli/
|
|
27895
|
+
// src/cli/execute.ts
|
|
25818
27896
|
init_errors();
|
|
25819
27897
|
|
|
25820
27898
|
// src/cli/router.ts
|
|
@@ -25940,9 +28018,10 @@ Published commands (package ${package_default.version}):
|
|
|
25940
28018
|
ask Recommend a /cc: slash command from a natural-language problem
|
|
25941
28019
|
cc-help Show preset inventory (skills, subagents, commands)
|
|
25942
28020
|
debt-harvest / harvest Scan source files for deferred debt items
|
|
25943
|
-
ccep
|
|
25944
|
-
openspec OpenSpec loop:
|
|
28021
|
+
ccep CCEP contracts: parse/profile/validate/evaluate/consensus/taskcard
|
|
28022
|
+
openspec OpenSpec loop: validate/scan/plan/status/next/start/done/block/archive
|
|
25945
28023
|
scorecard Record and aggregate evaluation outcomes
|
|
28024
|
+
(catalog / fingerprint / experiment / ablation)
|
|
25946
28025
|
|
|
25947
28026
|
v1.0.0 (in this repo, not in published ${package_default.version}):
|
|
25948
28027
|
goal / cc-goal Plan goal into task graph with dependencies
|
|
@@ -25978,7 +28057,7 @@ Orchestration loops:
|
|
|
25978
28057
|
src/core/ccep/). Prefer /cc-iterative, /cc-triage, and /cc-handoff;
|
|
25979
28058
|
other CCEP commands are supporting profiles.
|
|
25980
28059
|
OpenSpec is a delivery loop (CLI openspec + /cc-openspec) on top of
|
|
25981
|
-
BACKLOG.md — not only a scanner.
|
|
28060
|
+
BACKLOG.md — not only a scanner. Author BACKLOG.md with /cc-backlog.
|
|
25982
28061
|
runWorkflowPipeline() in src/core/pipeline/workflow-loop.ts is an
|
|
25983
28062
|
experimental library-only 8-phase loop — not a shipped CLI runtime.
|
|
25984
28063
|
|
|
@@ -26020,14 +28099,23 @@ Examples:
|
|
|
26020
28099
|
npx cc-codeconductor ccep profile council --output json
|
|
26021
28100
|
npx cc-codeconductor ccep resolve --command feature "Add CRUD"
|
|
26022
28101
|
npx cc-codeconductor ccep evaluate --command feature --input @planner.json
|
|
28102
|
+
npx cc-codeconductor ccep consensus --input @verdicts.json
|
|
28103
|
+
npx cc-codeconductor ccep taskcard --command feature --input @card.json
|
|
26023
28104
|
npx cc-codeconductor openspec validate
|
|
26024
28105
|
npx cc-codeconductor openspec scan
|
|
26025
28106
|
npx cc-codeconductor openspec plan BC-001
|
|
26026
28107
|
npx cc-codeconductor openspec status
|
|
26027
28108
|
npx cc-codeconductor openspec next
|
|
28109
|
+
npx cc-codeconductor openspec start BC-001-discover
|
|
28110
|
+
npx cc-codeconductor openspec done BC-001-discover
|
|
28111
|
+
npx cc-codeconductor openspec block BC-001-implement --reason "waiting on design"
|
|
28112
|
+
npx cc-codeconductor openspec archive BC-001
|
|
26028
28113
|
npx cc-codeconductor scorecard create --task BC-001 --from-diff
|
|
26029
28114
|
npx cc-codeconductor scorecard models
|
|
26030
28115
|
npx cc-codeconductor scorecard aggregate
|
|
28116
|
+
npx cc-codeconductor scorecard catalog
|
|
28117
|
+
npx cc-codeconductor scorecard experiment start --suite harness-v1 --components review
|
|
28118
|
+
npx cc-codeconductor scorecard ablation --experiment <id>
|
|
26031
28119
|
npx cc-codeconductor help
|
|
26032
28120
|
npx cc-codeconductor ask "login fails with 500"
|
|
26033
28121
|
npx cc-codeconductor cc-help --target opencode
|
|
@@ -26288,7 +28376,7 @@ async function routeCommand(args, projectRoot) {
|
|
|
26288
28376
|
});
|
|
26289
28377
|
}
|
|
26290
28378
|
case "ccep": {
|
|
26291
|
-
const validSubs = ["parse", "profile", "resolve", "compile", "validate", "evaluate"];
|
|
28379
|
+
const validSubs = ["parse", "profile", "resolve", "compile", "validate", "evaluate", "consensus", "taskcard"];
|
|
26292
28380
|
if (subcommand && !validSubs.includes(subcommand)) {
|
|
26293
28381
|
return unknownSubcommand("ccep", subcommand, validSubs);
|
|
26294
28382
|
}
|
|
@@ -26296,7 +28384,7 @@ async function routeCommand(args, projectRoot) {
|
|
|
26296
28384
|
const workflowCommand = options.command || undefined;
|
|
26297
28385
|
const requestParts = args.rest ?? [];
|
|
26298
28386
|
const userRequest = ccepSub === "profile" && !workflowCommand ? requestParts[0] : ["compile", "resolve", "parse", "evaluate"].includes(ccepSub) ? requestParts.join(" ").trim() : requestParts.join(" ").trim();
|
|
26299
|
-
const validateRest = (ccepSub === "validate" || ccepSub === "evaluate") && !options.input ? requestParts : undefined;
|
|
28387
|
+
const validateRest = (ccepSub === "validate" || ccepSub === "evaluate" || ccepSub === "consensus" || ccepSub === "taskcard") && !options.input ? requestParts : undefined;
|
|
26300
28388
|
const { ccepCommand: ccepCommand2 } = await Promise.resolve().then(() => (init_ccep_command(), exports_ccep_command));
|
|
26301
28389
|
return ccepCommand2({
|
|
26302
28390
|
subcommand: ccepSub,
|
|
@@ -26309,12 +28397,23 @@ async function routeCommand(args, projectRoot) {
|
|
|
26309
28397
|
input: options.input,
|
|
26310
28398
|
contextPath: options.context || options.contextPath,
|
|
26311
28399
|
promptVersion: options["prompt-version"] || options.promptVersion,
|
|
28400
|
+
config: options.config,
|
|
26312
28401
|
rest: validateRest
|
|
26313
28402
|
});
|
|
26314
28403
|
}
|
|
26315
28404
|
case "openspec":
|
|
26316
28405
|
case "cc-openspec": {
|
|
26317
|
-
const validSubs = [
|
|
28406
|
+
const validSubs = [
|
|
28407
|
+
"validate",
|
|
28408
|
+
"scan",
|
|
28409
|
+
"plan",
|
|
28410
|
+
"status",
|
|
28411
|
+
"next",
|
|
28412
|
+
"start",
|
|
28413
|
+
"done",
|
|
28414
|
+
"block",
|
|
28415
|
+
"archive"
|
|
28416
|
+
];
|
|
26318
28417
|
let openspecSub = "validate";
|
|
26319
28418
|
let itemId = options.item || undefined;
|
|
26320
28419
|
if (subcommand && validSubs.includes(subcommand)) {
|
|
@@ -26330,6 +28429,7 @@ async function routeCommand(args, projectRoot) {
|
|
|
26330
28429
|
return openspecCommand2({
|
|
26331
28430
|
subcommand: openspecSub,
|
|
26332
28431
|
itemId,
|
|
28432
|
+
reason: typeof options.reason === "string" ? options.reason : undefined,
|
|
26333
28433
|
projectRoot,
|
|
26334
28434
|
output: flags.output
|
|
26335
28435
|
});
|
|
@@ -26346,7 +28446,11 @@ async function routeCommand(args, projectRoot) {
|
|
|
26346
28446
|
"prompt-diff",
|
|
26347
28447
|
"regression",
|
|
26348
28448
|
"matrix",
|
|
26349
|
-
"compare-models"
|
|
28449
|
+
"compare-models",
|
|
28450
|
+
"catalog",
|
|
28451
|
+
"fingerprint",
|
|
28452
|
+
"experiment",
|
|
28453
|
+
"ablation"
|
|
26350
28454
|
];
|
|
26351
28455
|
if (subcommand && !validSubs.includes(subcommand)) {
|
|
26352
28456
|
return unknownSubcommand(command, subcommand, validSubs);
|
|
@@ -26361,7 +28465,7 @@ async function routeCommand(args, projectRoot) {
|
|
|
26361
28465
|
subcommand: scorecardSub,
|
|
26362
28466
|
projectRoot,
|
|
26363
28467
|
output: flags.output,
|
|
26364
|
-
id: scorecardSub === "show" ? args.rest?.[0] ?? options.id : options.id,
|
|
28468
|
+
id: scorecardSub === "show" || scorecardSub === "ablation" ? args.rest?.[0] ?? options.id : options.id,
|
|
26365
28469
|
taskId: options.task ?? options.item,
|
|
26366
28470
|
agent: options.agent,
|
|
26367
28471
|
model: options.model,
|
|
@@ -26376,7 +28480,14 @@ async function routeCommand(args, projectRoot) {
|
|
|
26376
28480
|
weightedScore: options.score ? parseFloat(String(options.score)) : undefined,
|
|
26377
28481
|
source: options.source,
|
|
26378
28482
|
since: options.since,
|
|
26379
|
-
modelsFilter
|
|
28483
|
+
modelsFilter,
|
|
28484
|
+
experimentId: options.experiment ?? (scorecardSub === "experiment" && args.rest?.[1] ? args.rest[1] : undefined) ?? (scorecardSub === "ablation" ? args.rest?.[0] : undefined),
|
|
28485
|
+
variantId: options.variant,
|
|
28486
|
+
suiteTaskId: options["suite-task"] ?? options.suiteTask,
|
|
28487
|
+
suiteId: options.suite,
|
|
28488
|
+
suitePath: options["suite-path"] ?? options.suitePath,
|
|
28489
|
+
components: options.components,
|
|
28490
|
+
experimentAction: scorecardSub === "experiment" ? args.rest?.[0] : undefined
|
|
26380
28491
|
});
|
|
26381
28492
|
}
|
|
26382
28493
|
case "seo": {
|
|
@@ -26430,107 +28541,120 @@ async function routeCommand(args, projectRoot) {
|
|
|
26430
28541
|
}
|
|
26431
28542
|
}
|
|
26432
28543
|
|
|
26433
|
-
// src/cli/
|
|
26434
|
-
async function
|
|
26435
|
-
const parsed = parseArgs(args
|
|
28544
|
+
// src/cli/execute.ts
|
|
28545
|
+
async function executeCli(args, projectRoot) {
|
|
28546
|
+
const parsed = parseArgs(args);
|
|
26436
28547
|
if (parsed.flags.version) {
|
|
26437
28548
|
console.log(getVersion());
|
|
26438
|
-
|
|
28549
|
+
return 0;
|
|
26439
28550
|
}
|
|
26440
28551
|
if (parsed.flags.help || !parsed.command || parsed.command === "help") {
|
|
26441
28552
|
console.log(getHelp());
|
|
26442
|
-
|
|
28553
|
+
return 0;
|
|
26443
28554
|
}
|
|
26444
|
-
const projectRoot = process.cwd();
|
|
26445
28555
|
try {
|
|
26446
28556
|
const result = await routeCommand(parsed, projectRoot);
|
|
26447
|
-
|
|
26448
|
-
|
|
26449
|
-
|
|
26450
|
-
|
|
26451
|
-
|
|
26452
|
-
|
|
26453
|
-
|
|
26454
|
-
|
|
26455
|
-
|
|
26456
|
-
|
|
26457
|
-
|
|
26458
|
-
|
|
26459
|
-
|
|
26460
|
-
|
|
26461
|
-
|
|
26462
|
-
|
|
26463
|
-
|
|
26464
|
-
|
|
26465
|
-
|
|
26466
|
-
|
|
26467
|
-
|
|
26468
|
-
|
|
26469
|
-
|
|
26470
|
-
|
|
26471
|
-
|
|
26472
|
-
|
|
26473
|
-
|
|
26474
|
-
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
|
|
26478
|
-
|
|
26479
|
-
|
|
26480
|
-
|
|
26481
|
-
|
|
26482
|
-
|
|
26483
|
-
|
|
26484
|
-
|
|
26485
|
-
|
|
26486
|
-
|
|
26487
|
-
|
|
28557
|
+
emitCliResult(parsed, result);
|
|
28558
|
+
return result.code;
|
|
28559
|
+
} catch (error) {
|
|
28560
|
+
emitCliError(parsed, error);
|
|
28561
|
+
return getExitCode(error);
|
|
28562
|
+
}
|
|
28563
|
+
}
|
|
28564
|
+
function emitCliResult(parsed, result) {
|
|
28565
|
+
if (parsed.flags.output === "json") {
|
|
28566
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
28567
|
+
return;
|
|
28568
|
+
}
|
|
28569
|
+
if (!result.data) {
|
|
28570
|
+
return;
|
|
28571
|
+
}
|
|
28572
|
+
const data = result.data;
|
|
28573
|
+
if ("help" in data) {
|
|
28574
|
+
console.log(data.help);
|
|
28575
|
+
return;
|
|
28576
|
+
}
|
|
28577
|
+
if ("message" in data && typeof data.message === "string") {
|
|
28578
|
+
console.log(data.message);
|
|
28579
|
+
return;
|
|
28580
|
+
}
|
|
28581
|
+
if ("errors" in data && Array.isArray(data.errors) && data.errors.length > 0) {
|
|
28582
|
+
data.errors.forEach((e) => console.error(e));
|
|
28583
|
+
}
|
|
28584
|
+
if ("checks" in data) {
|
|
28585
|
+
const checks = data.checks;
|
|
28586
|
+
checks.forEach((c) => {
|
|
28587
|
+
const icon = c.status === "pass" ? "✓" : c.status === "warn" ? "⚠" : "✗";
|
|
28588
|
+
console.log(`${icon} ${c.name}: ${c.message}`);
|
|
28589
|
+
});
|
|
28590
|
+
} else if ("fileResults" in data) {
|
|
28591
|
+
const fileResults = data.fileResults;
|
|
28592
|
+
const actionIcon = {
|
|
28593
|
+
written: "✓",
|
|
28594
|
+
appended: "→",
|
|
28595
|
+
merged: "~",
|
|
28596
|
+
skipped: "∅",
|
|
28597
|
+
error: "✗"
|
|
28598
|
+
};
|
|
28599
|
+
fileResults.forEach((r) => {
|
|
28600
|
+
if (r.action === "skipped")
|
|
28601
|
+
return;
|
|
28602
|
+
const icon = actionIcon[r.action] ?? "?";
|
|
28603
|
+
const prefix = r.dryRun ? "[dry-run] " : "";
|
|
28604
|
+
const suffix = r.error ? `: ${r.error}` : "";
|
|
28605
|
+
console.log(`${icon} ${prefix}${r.dest}${suffix}`);
|
|
28606
|
+
});
|
|
28607
|
+
const acted = fileResults.filter((r) => r.action !== "skipped");
|
|
28608
|
+
const errCount = acted.filter((r) => r.action === "error").length;
|
|
28609
|
+
const count = acted.length - errCount;
|
|
28610
|
+
const note = data.dryRun ? " (dry-run)" : "";
|
|
28611
|
+
console.log(`
|
|
26488
28612
|
${count} files processed${errCount > 0 ? `, ${errCount} errors` : ""}${note}`);
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26496
|
-
|
|
26497
|
-
|
|
26498
|
-
|
|
26499
|
-
|
|
26500
|
-
|
|
26501
|
-
|
|
26502
|
-
|
|
26503
|
-
|
|
26504
|
-
|
|
26505
|
-
|
|
26506
|
-
|
|
26507
|
-
|
|
26508
|
-
|
|
26509
|
-
|
|
26510
|
-
|
|
26511
|
-
|
|
26512
|
-
}
|
|
26513
|
-
});
|
|
26514
|
-
} else if ("output" in data && typeof data.output === "string") {
|
|
26515
|
-
console.log(data.output);
|
|
26516
|
-
}
|
|
26517
|
-
}
|
|
28613
|
+
} else if ("written" in data) {
|
|
28614
|
+
const written = data.written;
|
|
28615
|
+
if (written.length > 0) {
|
|
28616
|
+
if ("targets" in data && Array.isArray(data.targets) && data.targets.length > 0) {
|
|
28617
|
+
console.log(`Installed to: ${data.targets.join(", ")} (${written.length} files)`);
|
|
28618
|
+
} else {
|
|
28619
|
+
console.log(`Written ${written.length} files`);
|
|
28620
|
+
}
|
|
28621
|
+
}
|
|
28622
|
+
} else if ("wouldCreate" in data) {
|
|
28623
|
+
const items = data.wouldCreate;
|
|
28624
|
+
console.log(`Would create ${items.length} files:`);
|
|
28625
|
+
items.forEach((f) => console.log(` + ${f}`));
|
|
28626
|
+
} else if ("created" in data) {
|
|
28627
|
+
console.log(`Created ${data.created.length} files`);
|
|
28628
|
+
} else if ("detected" in data) {
|
|
28629
|
+
console.log("Detected:");
|
|
28630
|
+
const detected = data.detected;
|
|
28631
|
+
Object.entries(detected).forEach(([key, value]) => {
|
|
28632
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
28633
|
+
console.log(` - ${key}: ${value.join(", ")}`);
|
|
28634
|
+
} else if (typeof value === "string" && value.length > 0) {
|
|
28635
|
+
console.log(` - ${key}: ${value}`);
|
|
26518
28636
|
}
|
|
26519
|
-
}
|
|
26520
|
-
|
|
26521
|
-
|
|
26522
|
-
const exitCode = getExitCode(error);
|
|
26523
|
-
if (parsed.flags.output === "json") {
|
|
26524
|
-
console.log(JSON.stringify({
|
|
26525
|
-
success: false,
|
|
26526
|
-
errors: [String(error)]
|
|
26527
|
-
}, null, 2));
|
|
26528
|
-
} else {
|
|
26529
|
-
console.error(String(error));
|
|
26530
|
-
}
|
|
26531
|
-
process.exit(exitCode);
|
|
28637
|
+
});
|
|
28638
|
+
} else if ("output" in data && typeof data.output === "string") {
|
|
28639
|
+
console.log(data.output);
|
|
26532
28640
|
}
|
|
26533
28641
|
}
|
|
28642
|
+
function emitCliError(parsed, error) {
|
|
28643
|
+
if (parsed.flags.output === "json") {
|
|
28644
|
+
console.log(JSON.stringify({
|
|
28645
|
+
success: false,
|
|
28646
|
+
errors: [String(error)]
|
|
28647
|
+
}, null, 2));
|
|
28648
|
+
return;
|
|
28649
|
+
}
|
|
28650
|
+
console.error(String(error));
|
|
28651
|
+
}
|
|
28652
|
+
|
|
28653
|
+
// src/cli/main.ts
|
|
28654
|
+
async function runCli(args) {
|
|
28655
|
+
const code = await executeCli(args.slice(2), process.cwd());
|
|
28656
|
+
process.exit(code);
|
|
28657
|
+
}
|
|
26534
28658
|
runCli(process.argv);
|
|
26535
28659
|
export {
|
|
26536
28660
|
runCli
|