cc-codeconductor 1.4.1 → 1.5.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 +61 -13
- package/dist/core/verification/rdd-receipt.d.ts +39 -0
- package/dist/core/verification/verification-runner.d.ts +8 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1257 -330
- package/dist/library.js +267 -26
- package/dist/validation/schemas.d.ts +521 -26
- package/docs/generated/cli.md +20 -0
- package/package.json +1 -1
- package/presets/agy/AGENTS.md +6 -0
- package/presets/agy/README.md +1 -1
- package/presets/agy/hooks.json +1 -1
- package/presets/agy/scripts/invoke-hook.cjs +20 -5
- package/presets/agy/settings.json +1 -1
- package/presets/agy/skills/api-versioning/SKILL.md +394 -0
- package/presets/agy/skills/astro/SKILL.md +318 -0
- package/presets/agy/skills/auth-token-inspector/SKILL.md +30 -0
- package/presets/agy/skills/cc-pagespeed/SKILL.md +2 -3
- package/presets/agy/skills/code-review/SKILL.md +207 -0
- package/presets/agy/skills/django-orm/SKILL.md +460 -0
- package/presets/agy/skills/django-uv/SKILL.md +405 -0
- package/presets/agy/skills/drizzle-schema-architect/SKILL.md +50 -0
- package/presets/agy/skills/fastapi-pydantic-strict/SKILL.md +43 -0
- package/presets/agy/skills/jpa-nplusone-detector/SKILL.md +45 -0
- package/presets/agy/skills/jpa-postgres/SKILL.md +623 -0
- package/presets/agy/skills/livewire-alpine-bridge/SKILL.md +35 -0
- package/presets/agy/skills/nextjs-typescript/SKILL.md +390 -0
- package/presets/agy/skills/python/SKILL.md +611 -0
- package/presets/agy/skills/seo-analytics-injector/SKILL.md +43 -0
- package/presets/agy/skills/spring-auth-auditor/SKILL.md +29 -0
- package/presets/agy/skills/spring-boot-feature/SKILL.md +563 -0
- package/presets/agy/skills/spring-boot-testing-strategy/SKILL.md +475 -0
- package/presets/agy/skills/tailwind-responsive-auditor/SKILL.md +29 -0
- package/presets/agy/skills/tdd-mutation-tester/SKILL.md +27 -0
- package/presets/agy/workflows/cc-handoff.md +2 -1
- package/presets/agy/workflows/cc-odd.md +15 -0
- package/presets/agy/workflows/cc-pagespeed.md +2 -3
- package/presets/agy/workflows/cc-review.md +31 -0
- package/presets/agy/workflows/cc-security.md +1 -1
- package/presets/claude/CLAUDE.md +6 -0
- package/presets/claude/commands/cc/handoff.md +2 -1
- package/presets/claude/commands/cc/odd.md +15 -0
- package/presets/claude/commands/cc/review.md +33 -0
- package/presets/claude/settings.json +2 -2
- package/presets/claude/skills/android/SKILL.md +1 -1
- package/presets/claude/skills/api-versioning/SKILL.md +1 -1
- package/presets/claude/skills/astro/SKILL.md +318 -0
- package/presets/claude/skills/auth-token-inspector/SKILL.md +30 -0
- package/presets/claude/skills/code-review/SKILL.md +207 -0
- package/presets/claude/skills/django-orm/SKILL.md +1 -1
- package/presets/claude/skills/django-testing/SKILL.md +1 -1
- package/presets/claude/skills/django-uv/SKILL.md +405 -0
- package/presets/claude/skills/drizzle-schema-architect/SKILL.md +50 -0
- package/presets/claude/skills/fastapi-pydantic-strict/SKILL.md +43 -0
- package/presets/claude/skills/jpa-nplusone-detector/SKILL.md +45 -0
- package/presets/claude/skills/jpa-postgres/SKILL.md +1 -1
- package/presets/claude/skills/livewire-alpine-bridge/SKILL.md +35 -0
- package/presets/claude/skills/nextjs-typescript/SKILL.md +390 -0
- package/presets/claude/skills/pagespeed-perf/SKILL.md +1 -1
- package/presets/claude/skills/python/SKILL.md +1 -1
- package/presets/claude/skills/python-django-stack/SKILL.md +1 -1
- package/presets/claude/skills/python-fastapi-stack/SKILL.md +1 -1
- package/presets/claude/skills/security/SKILL.md +1 -1
- package/presets/claude/skills/seo-analytics-injector/SKILL.md +43 -0
- package/presets/claude/skills/spring-auth-auditor/SKILL.md +29 -0
- package/presets/claude/skills/spring-boot-feature/SKILL.md +1 -1
- package/presets/claude/skills/spring-boot-kotlin/SKILL.md +1 -1
- package/presets/claude/skills/spring-boot-testing-strategy/SKILL.md +475 -0
- package/presets/claude/skills/sqlalchemy/SKILL.md +1 -1
- package/presets/claude/skills/tailwind-responsive-auditor/SKILL.md +29 -0
- package/presets/claude/skills/tdd-mutation-tester/SKILL.md +27 -0
- package/presets/claude/skills/testing-strategy/SKILL.md +1 -1
- package/presets/codex/AGENTS.md +6 -0
- package/presets/codex/skills/android/SKILL.md +1 -1
- package/presets/codex/skills/api-versioning/SKILL.md +1 -1
- package/presets/codex/skills/astro/SKILL.md +318 -0
- package/presets/codex/skills/auth-token-inspector/SKILL.md +30 -0
- package/presets/codex/skills/cc-handoff/SKILL.md +3 -1
- package/presets/codex/skills/cc-odd/SKILL.md +25 -0
- package/presets/codex/skills/cc-openspec/SKILL.md +5 -1
- package/presets/codex/skills/cc-pagespeed/SKILL.md +2 -3
- package/presets/codex/skills/cc-review/SKILL.md +31 -0
- package/presets/codex/skills/cc-security/SKILL.md +1 -1
- package/presets/codex/skills/cc-spec-mutation/SKILL.md +5 -0
- package/presets/codex/skills/cc-tdd-cycle/SKILL.md +8 -0
- package/presets/codex/skills/code-review/SKILL.md +207 -0
- package/presets/codex/skills/django-orm/SKILL.md +1 -1
- package/presets/codex/skills/django-testing/SKILL.md +1 -1
- package/presets/codex/skills/django-uv/SKILL.md +405 -0
- package/presets/codex/skills/drizzle-schema-architect/SKILL.md +50 -0
- package/presets/codex/skills/fastapi-pydantic-strict/SKILL.md +43 -0
- package/presets/codex/skills/jpa-nplusone-detector/SKILL.md +45 -0
- package/presets/codex/skills/jpa-postgres/SKILL.md +1 -1
- package/presets/codex/skills/livewire-alpine-bridge/SKILL.md +35 -0
- package/presets/codex/skills/nextjs-typescript/SKILL.md +390 -0
- package/presets/codex/skills/pagespeed-perf/SKILL.md +1 -1
- package/presets/codex/skills/python/SKILL.md +1 -1
- package/presets/codex/skills/python-django-stack/SKILL.md +1 -1
- package/presets/codex/skills/python-fastapi-stack/SKILL.md +1 -1
- package/presets/codex/skills/security-ai-llm/SKILL.md +43 -0
- package/presets/codex/skills/security-blue-team/SKILL.md +43 -0
- package/presets/codex/skills/security-cloud/SKILL.md +43 -0
- package/presets/codex/skills/security-crypto/SKILL.md +43 -0
- package/presets/codex/skills/security-exploit-dev/SKILL.md +45 -0
- package/presets/codex/skills/security-grc/SKILL.md +43 -0
- package/presets/codex/skills/security-incident-response/SKILL.md +45 -0
- package/presets/codex/skills/security-log-analysis/SKILL.md +43 -0
- package/presets/codex/skills/security-malware-analysis/SKILL.md +44 -0
- package/presets/codex/skills/security-mobile/SKILL.md +43 -0
- package/presets/codex/skills/security-network/SKILL.md +43 -0
- package/presets/codex/skills/security-ot-ics/SKILL.md +43 -0
- package/presets/codex/skills/security-recon/SKILL.md +45 -0
- package/presets/codex/skills/security-red-team/SKILL.md +44 -0
- package/presets/codex/skills/security-reverse-engineering/SKILL.md +44 -0
- package/presets/codex/skills/security-soc-automation/SKILL.md +43 -0
- package/presets/codex/skills/security-threat-hunting/SKILL.md +43 -0
- package/presets/codex/skills/security-vuln-assessment/SKILL.md +45 -0
- package/presets/codex/skills/security-web/SKILL.md +44 -0
- package/presets/codex/skills/seo-analytics-injector/SKILL.md +43 -0
- package/presets/codex/skills/spring-auth-auditor/SKILL.md +29 -0
- package/presets/codex/skills/spring-boot-feature/SKILL.md +2 -2
- package/presets/codex/skills/spring-boot-kotlin/SKILL.md +1 -1
- package/presets/codex/skills/spring-boot-testing-strategy/SKILL.md +475 -0
- package/presets/codex/skills/sqlalchemy/SKILL.md +1 -1
- package/presets/codex/skills/tailwind-responsive-auditor/SKILL.md +29 -0
- package/presets/codex/skills/tdd-mutation-tester/SKILL.md +27 -0
- package/presets/codex/skills/testing-strategy/SKILL.md +1 -1
- package/presets/cursor/AGENTS.md +6 -0
- package/presets/cursor/commands/cc/handoff.md +3 -1
- package/presets/cursor/commands/cc/odd.md +20 -0
- package/presets/cursor/commands/cc/openspec.md +5 -1
- package/presets/cursor/commands/cc/pagespeed.md +2 -3
- package/presets/cursor/commands/cc/review.md +31 -0
- package/presets/cursor/commands/cc/security.md +1 -1
- package/presets/cursor/commands/cc/spec-mutation.md +5 -0
- package/presets/cursor/commands/cc/tdd-cycle.md +8 -0
- package/presets/cursor/skills/android/SKILL.md +1 -1
- package/presets/cursor/skills/api-versioning/SKILL.md +2 -1
- package/presets/cursor/skills/astro/SKILL.md +1 -1
- package/presets/cursor/skills/auth-token-inspector/SKILL.md +1 -1
- package/presets/cursor/skills/code-review/SKILL.md +1 -1
- package/presets/cursor/skills/django-orm/SKILL.md +3 -5
- package/presets/cursor/skills/django-testing/SKILL.md +1 -1
- package/presets/cursor/skills/django-uv/SKILL.md +1 -1
- package/presets/cursor/skills/drizzle-schema-architect/SKILL.md +1 -1
- package/presets/cursor/skills/fastapi-pydantic-strict/SKILL.md +1 -1
- package/presets/cursor/skills/jpa-nplusone-detector/SKILL.md +1 -1
- package/presets/cursor/skills/jpa-postgres/SKILL.md +2 -4
- package/presets/cursor/skills/livewire-alpine-bridge/SKILL.md +1 -1
- package/presets/cursor/skills/nextjs-typescript/SKILL.md +1 -1
- package/presets/cursor/skills/pagespeed-perf/SKILL.md +1 -1
- package/presets/cursor/skills/python/SKILL.md +6 -7
- package/presets/cursor/skills/python-django-stack/SKILL.md +1 -1
- package/presets/cursor/skills/python-fastapi-stack/SKILL.md +1 -1
- package/presets/cursor/skills/security/SKILL.md +1 -1
- package/presets/cursor/skills/seo-analytics-injector/SKILL.md +1 -1
- package/presets/cursor/skills/spring-auth-auditor/SKILL.md +1 -1
- package/presets/cursor/skills/spring-boot-feature/SKILL.md +2 -4
- package/presets/cursor/skills/spring-boot-kotlin/SKILL.md +1 -1
- package/presets/cursor/skills/spring-boot-testing-strategy/SKILL.md +1 -1
- package/presets/cursor/skills/sqlalchemy/SKILL.md +1 -1
- package/presets/cursor/skills/tailwind-responsive-auditor/SKILL.md +1 -1
- package/presets/cursor/skills/tdd-mutation-tester/SKILL.md +1 -1
- package/presets/gemini/GEMINI.md +6 -0
- package/presets/gemini/commands/cc/handoff.toml +3 -1
- package/presets/gemini/commands/cc/odd.toml +20 -0
- package/presets/gemini/commands/cc/openspec.toml +5 -1
- package/presets/gemini/commands/cc/pagespeed.toml +2 -3
- package/presets/gemini/commands/cc/review.toml +31 -0
- package/presets/gemini/commands/cc/security.toml +1 -1
- package/presets/gemini/commands/cc/spec-mutation.toml +5 -0
- package/presets/gemini/commands/cc/tdd-cycle.toml +8 -0
- package/presets/opencode/README.md +45 -52
- package/presets/opencode/agents/implementer.md +2 -0
- package/presets/opencode/agents/reviewer.md +2 -0
- package/presets/opencode/agents/tester.md +2 -0
- package/presets/opencode/commands/cc-handoff.md +2 -1
- package/presets/opencode/commands/cc-odd.md +15 -0
- package/presets/opencode/commands/cc-pagespeed.md +2 -3
- package/presets/opencode/commands/cc-review.md +31 -0
- package/presets/opencode/commands/cc-security.md +1 -1
- package/presets/opencode/opencode.jsonc +1 -1
- package/presets/opencode/skills/android/SKILL.md +1 -1
- package/presets/opencode/skills/api-versioning/SKILL.md +2 -1
- package/presets/opencode/skills/astro/SKILL.md +1 -1
- package/presets/opencode/skills/auth-token-inspector/SKILL.md +1 -1
- package/presets/opencode/skills/code-review/SKILL.md +1 -1
- package/presets/opencode/skills/django-orm/SKILL.md +3 -3
- package/presets/opencode/skills/django-testing/SKILL.md +1 -1
- package/presets/opencode/skills/django-uv/SKILL.md +1 -1
- package/presets/opencode/skills/drizzle-schema-architect/SKILL.md +1 -1
- package/presets/opencode/skills/fastapi-pydantic-strict/SKILL.md +1 -1
- package/presets/opencode/skills/jpa-nplusone-detector/SKILL.md +1 -1
- package/presets/opencode/skills/jpa-postgres/SKILL.md +2 -1
- package/presets/opencode/skills/livewire-alpine-bridge/SKILL.md +1 -1
- package/presets/opencode/skills/nextjs-typescript/SKILL.md +1 -1
- package/presets/opencode/skills/pagespeed-perf/SKILL.md +1 -1
- package/presets/opencode/skills/python/SKILL.md +6 -5
- package/presets/opencode/skills/python-django-stack/SKILL.md +1 -1
- package/presets/opencode/skills/python-fastapi-stack/SKILL.md +1 -1
- package/presets/opencode/skills/security/SKILL.md +1 -1
- package/presets/opencode/skills/seo-analytics-injector/SKILL.md +1 -1
- package/presets/opencode/skills/spring-auth-auditor/SKILL.md +1 -1
- package/presets/opencode/skills/spring-boot-feature/SKILL.md +2 -1
- package/presets/opencode/skills/spring-boot-kotlin/SKILL.md +1 -1
- package/presets/opencode/skills/spring-boot-testing-strategy/SKILL.md +1 -1
- package/presets/opencode/skills/sqlalchemy/SKILL.md +1 -1
- package/presets/opencode/skills/tailwind-responsive-auditor/SKILL.md +1 -1
- package/presets/opencode/skills/tdd-mutation-tester/SKILL.md +1 -1
- package/presets/pi/AGENTS.md +6 -0
- package/presets/seo-hotel/skills/astro-seo/SKILL.md +1 -1
- package/presets/seo-hotel/skills/geo-readiness/SKILL.md +1 -1
- package/presets/seo-hotel/skills/off-page/SKILL.md +1 -1
- package/presets/seo-hotel/skills/schema-validator/SKILL.md +1 -1
- package/presets/seo-hotel/skills/seo-audit/SKILL.md +1 -1
- package/presets/shared/invoke-hook.cjs +20 -5
- package/src/presets/models/roles.yml +28 -28
- package/src/presets/shared-skills.yml +58 -22
- package/src/presets/targets/pi.yml +1 -0
package/dist/index.js
CHANGED
|
@@ -72,7 +72,7 @@ var package_default;
|
|
|
72
72
|
var init_package = __esm(() => {
|
|
73
73
|
package_default = {
|
|
74
74
|
name: "cc-codeconductor",
|
|
75
|
-
version: "1.
|
|
75
|
+
version: "1.5.0",
|
|
76
76
|
description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
|
|
77
77
|
keywords: [
|
|
78
78
|
"ai",
|
|
@@ -11206,6 +11206,11 @@ __export(exports_schemas, {
|
|
|
11206
11206
|
CouncilVerdictInputSchema: () => CouncilVerdictInputSchema,
|
|
11207
11207
|
CouncilVerdictSchema: () => CouncilVerdictSchema,
|
|
11208
11208
|
DecisionSchema: () => DecisionSchema,
|
|
11209
|
+
DeliveryLedgerRequestSchema: () => DeliveryLedgerRequestSchema,
|
|
11210
|
+
DeliveryLedgerSchema: () => DeliveryLedgerSchema,
|
|
11211
|
+
DeliveryLedgerTaskSchema: () => DeliveryLedgerTaskSchema,
|
|
11212
|
+
DeliveryRouteSchema: () => DeliveryRouteSchema,
|
|
11213
|
+
DeliveryTelemetrySchema: () => DeliveryTelemetrySchema,
|
|
11209
11214
|
EvaluationIndexSchema: () => EvaluationIndexSchema,
|
|
11210
11215
|
EvidenceSchema: () => EvidenceSchema,
|
|
11211
11216
|
ExecutionContextSchema: () => ExecutionContextSchema,
|
|
@@ -11270,6 +11275,7 @@ __export(exports_schemas, {
|
|
|
11270
11275
|
TaskOutcomeSourceSchema: () => TaskOutcomeSourceSchema,
|
|
11271
11276
|
TaskOutcomeStatusSchema: () => TaskOutcomeStatusSchema,
|
|
11272
11277
|
TechnicalPlanOutputSchema: () => TechnicalPlanOutputSchema,
|
|
11278
|
+
TokenUsageSchema: () => TokenUsageSchema,
|
|
11273
11279
|
ToolProviderNamesSchema: () => ToolProviderNamesSchema,
|
|
11274
11280
|
VerificationReportSchema: () => VerificationReportSchema,
|
|
11275
11281
|
WorkflowCommandSchema: () => WorkflowCommandSchema,
|
|
@@ -11433,7 +11439,7 @@ function validateProductEvent(data) {
|
|
|
11433
11439
|
function validateSkillsRegistry(data) {
|
|
11434
11440
|
return SkillsRegistrySchema.parse(data);
|
|
11435
11441
|
}
|
|
11436
|
-
var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, SkillFrontmatterSchema, CommandFrontmatterSchema, TargetCapabilitiesSchema, ContractTargetSchema, ContractFormatSchema, AgentContractSchema, CouncilFindingSchema, CouncilVerdictInputSchema, ConsensusConfigSchema, CouncilVerdictSchema, ClaudeAgentFileSchema, OpenCodeAgentFileSchema, SentryStackFrameSchema, SentryWebhookSchema, MemoryPointerSchema, MemoryIndexSchema, GoalTaskSchema, GoalGraphSchema, ConfidenceLevelSchema, ProductNodeTypeSchema, GraphRelationSchema, KnowledgeEntitySchema, DecisionSchema, EvidenceSchema, CanonicalTaskCardStatusSchema, CanonicalTaskCardSchema, ProductGraphNodeSchema, ProductGraphEdgeSchema, ProductGraphSchema, ProductMetaSchema, ProductEventTypeSchema, ProductEventSchema, OperationalStateSchema, StrategicMemorySchema, ImpactReportSchema, BusinessReviewOutputSchema, VerificationReportSchema, BacklogStatusSchema, BacklogTypeSchema, BacklogPrioritySchema, BacklogGlobalSchema, BacklogItemSchema, BacklogDocumentSchema, OpenspecTaskCardPhaseSchema, OpenspecTaskCardStatusSchema, OpenspecTaskCardSchema, OpenspecStateSchema, SpecAnalyzeFindingSchema, SpecAnalyzeReportSchema, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, TaskOutcomeSchema, HarnessComponentIdSchema, HarnessComponentLayerSchema, HarnessToggleKindSchema, HarnessCatalogEntrySchema, HarnessCatalogSchema, HarnessOverlaySchema, HarnessExperimentSchema, HarnessSuiteTaskSchema, HarnessSuiteSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema, SkillsRegistrySchema;
|
|
11442
|
+
var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, SkillFrontmatterSchema, CommandFrontmatterSchema, TargetCapabilitiesSchema, ContractTargetSchema, ContractFormatSchema, AgentContractSchema, CouncilFindingSchema, CouncilVerdictInputSchema, ConsensusConfigSchema, CouncilVerdictSchema, ClaudeAgentFileSchema, OpenCodeAgentFileSchema, SentryStackFrameSchema, SentryWebhookSchema, MemoryPointerSchema, MemoryIndexSchema, GoalTaskSchema, GoalGraphSchema, ConfidenceLevelSchema, ProductNodeTypeSchema, GraphRelationSchema, KnowledgeEntitySchema, DecisionSchema, EvidenceSchema, CanonicalTaskCardStatusSchema, CanonicalTaskCardSchema, ProductGraphNodeSchema, ProductGraphEdgeSchema, ProductGraphSchema, ProductMetaSchema, ProductEventTypeSchema, ProductEventSchema, OperationalStateSchema, StrategicMemorySchema, ImpactReportSchema, BusinessReviewOutputSchema, VerificationReportSchema, BacklogStatusSchema, BacklogTypeSchema, BacklogPrioritySchema, BacklogGlobalSchema, BacklogItemSchema, BacklogDocumentSchema, OpenspecTaskCardPhaseSchema, OpenspecTaskCardStatusSchema, OpenspecTaskCardSchema, OpenspecStateSchema, SpecAnalyzeFindingSchema, SpecAnalyzeReportSchema, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, DeliveryRouteSchema, TokenUsageSchema, DeliveryTelemetrySchema, DeliveryLedgerTaskSchema, DeliveryLedgerSchema, DeliveryLedgerRequestSchema, TaskOutcomeSchema, HarnessComponentIdSchema, HarnessComponentLayerSchema, HarnessToggleKindSchema, HarnessCatalogEntrySchema, HarnessCatalogSchema, HarnessOverlaySchema, HarnessExperimentSchema, HarnessSuiteTaskSchema, HarnessSuiteSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema, SkillsRegistrySchema;
|
|
11437
11443
|
var init_schemas = __esm(() => {
|
|
11438
11444
|
init_zod();
|
|
11439
11445
|
init_runner_target();
|
|
@@ -11598,7 +11604,8 @@ var init_schemas = __esm(() => {
|
|
|
11598
11604
|
complianceVeto: exports_external.boolean().optional(),
|
|
11599
11605
|
confidence: exports_external.number().min(0).max(1),
|
|
11600
11606
|
findings: exports_external.array(CouncilFindingSchema),
|
|
11601
|
-
summary: exports_external.string()
|
|
11607
|
+
summary: exports_external.string(),
|
|
11608
|
+
candidateHash: exports_external.string().min(1).optional()
|
|
11602
11609
|
});
|
|
11603
11610
|
ConsensusConfigSchema = exports_external.object({
|
|
11604
11611
|
algorithm: exports_external.enum(["majority", "unanimous"]),
|
|
@@ -11606,7 +11613,8 @@ var init_schemas = __esm(() => {
|
|
|
11606
11613
|
allowComplianceVeto: exports_external.boolean().optional(),
|
|
11607
11614
|
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(),
|
|
11608
11615
|
quorum: exports_external.number().int().positive().optional(),
|
|
11609
|
-
criticalFindingsPolicy: exports_external.enum(["escalate", "reject", "ignore"]).optional()
|
|
11616
|
+
criticalFindingsPolicy: exports_external.enum(["escalate", "reject", "ignore"]).optional(),
|
|
11617
|
+
candidateHash: exports_external.string().min(1).optional()
|
|
11610
11618
|
}).refine((config) => config.algorithm !== "unanimous" || config.expectedAgentIds !== undefined, {
|
|
11611
11619
|
message: "unanimous consensus requires a non-empty expectedAgentIds roster",
|
|
11612
11620
|
path: ["expectedAgentIds"]
|
|
@@ -11983,7 +11991,8 @@ var init_schemas = __esm(() => {
|
|
|
11983
11991
|
"prototype",
|
|
11984
11992
|
"handoff",
|
|
11985
11993
|
"clarify",
|
|
11986
|
-
"security"
|
|
11994
|
+
"security",
|
|
11995
|
+
"odd"
|
|
11987
11996
|
]);
|
|
11988
11997
|
CcepOutputFormatSchema = exports_external.enum(["taskcard", "plan", "verdict"]);
|
|
11989
11998
|
CommandEnvelopeSchema = exports_external.object({
|
|
@@ -12199,6 +12208,46 @@ var init_schemas = __esm(() => {
|
|
|
12199
12208
|
"revise",
|
|
12200
12209
|
"reject"
|
|
12201
12210
|
]);
|
|
12211
|
+
DeliveryRouteSchema = exports_external.enum(["read-only", "small", "tracked", "openspec"]);
|
|
12212
|
+
TokenUsageSchema = exports_external.union([
|
|
12213
|
+
exports_external.object({
|
|
12214
|
+
status: exports_external.literal("known"),
|
|
12215
|
+
total: exports_external.number().nonnegative()
|
|
12216
|
+
}),
|
|
12217
|
+
exports_external.object({ status: exports_external.literal("unknown") })
|
|
12218
|
+
]);
|
|
12219
|
+
DeliveryTelemetrySchema = exports_external.object({
|
|
12220
|
+
route: DeliveryRouteSchema,
|
|
12221
|
+
contextBytes: exports_external.number().int().nonnegative().optional(),
|
|
12222
|
+
handoffs: exports_external.number().int().nonnegative().optional(),
|
|
12223
|
+
checks: exports_external.array(exports_external.string().min(1)).optional(),
|
|
12224
|
+
tokenUsage: TokenUsageSchema
|
|
12225
|
+
});
|
|
12226
|
+
DeliveryLedgerTaskSchema = exports_external.object({
|
|
12227
|
+
id: exports_external.string().min(1),
|
|
12228
|
+
title: exports_external.string().min(1),
|
|
12229
|
+
status: exports_external.enum(["pending", "done", "blocked"])
|
|
12230
|
+
});
|
|
12231
|
+
DeliveryLedgerSchema = exports_external.object({
|
|
12232
|
+
version: exports_external.literal(1),
|
|
12233
|
+
id: exports_external.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/),
|
|
12234
|
+
taskCard: CanonicalTaskCardSchema,
|
|
12235
|
+
tasks: exports_external.array(DeliveryLedgerTaskSchema).min(1),
|
|
12236
|
+
evidence: exports_external.array(exports_external.string().min(1)),
|
|
12237
|
+
nextStep: exports_external.string().min(1),
|
|
12238
|
+
memoryTopicKey: exports_external.string().min(1),
|
|
12239
|
+
workspace: exports_external.record(exports_external.string(), exports_external.string()),
|
|
12240
|
+
createdAt: exports_external.string().datetime(),
|
|
12241
|
+
updatedAt: exports_external.string().datetime()
|
|
12242
|
+
});
|
|
12243
|
+
DeliveryLedgerRequestSchema = exports_external.object({
|
|
12244
|
+
id: exports_external.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/),
|
|
12245
|
+
authorized: exports_external.boolean(),
|
|
12246
|
+
route: DeliveryRouteSchema,
|
|
12247
|
+
taskCard: CanonicalTaskCardSchema,
|
|
12248
|
+
tasks: exports_external.array(DeliveryLedgerTaskSchema).min(1),
|
|
12249
|
+
nextStep: exports_external.string().min(1)
|
|
12250
|
+
});
|
|
12202
12251
|
TaskOutcomeSchema = exports_external.object({
|
|
12203
12252
|
id: exports_external.string(),
|
|
12204
12253
|
taskId: exports_external.string(),
|
|
@@ -12222,7 +12271,8 @@ var init_schemas = __esm(() => {
|
|
|
12222
12271
|
variantId: exports_external.string().optional(),
|
|
12223
12272
|
suiteTaskId: exports_external.string().optional(),
|
|
12224
12273
|
harnessFingerprint: exports_external.string().optional(),
|
|
12225
|
-
disabledComponents: exports_external.array(exports_external.string()).optional()
|
|
12274
|
+
disabledComponents: exports_external.array(exports_external.string()).optional(),
|
|
12275
|
+
delivery: DeliveryTelemetrySchema.optional()
|
|
12226
12276
|
});
|
|
12227
12277
|
HarnessComponentIdSchema = exports_external.enum([
|
|
12228
12278
|
"wayfinding",
|
|
@@ -12939,18 +12989,19 @@ var init_path_containment = __esm(() => {
|
|
|
12939
12989
|
});
|
|
12940
12990
|
|
|
12941
12991
|
// src/core/presets/complementary-detector.ts
|
|
12942
|
-
import {
|
|
12992
|
+
import { execFile } from "node:child_process";
|
|
12943
12993
|
import { existsSync, readFileSync } from "node:fs";
|
|
12944
12994
|
import { join as join3 } from "node:path";
|
|
12945
12995
|
import { homedir } from "node:os";
|
|
12946
|
-
|
|
12996
|
+
import { promisify } from "node:util";
|
|
12997
|
+
async function detectComplementaryTools() {
|
|
12947
12998
|
if (cachedStatus) {
|
|
12948
12999
|
return cachedStatus;
|
|
12949
13000
|
}
|
|
12950
|
-
const isCmdAvailable = (cmd) => {
|
|
13001
|
+
const isCmdAvailable = async (cmd) => {
|
|
12951
13002
|
try {
|
|
12952
13003
|
const binary = process.platform === "win32" ? "where" : "which";
|
|
12953
|
-
|
|
13004
|
+
await execFileAsync(binary, [cmd], { timeout: 1000 });
|
|
12954
13005
|
return true;
|
|
12955
13006
|
} catch {
|
|
12956
13007
|
return false;
|
|
@@ -12979,18 +13030,28 @@ function detectComplementaryTools() {
|
|
|
12979
13030
|
} catch {}
|
|
12980
13031
|
return false;
|
|
12981
13032
|
};
|
|
13033
|
+
const [rtk, codeReviewGraph, tokenSavior, tokenSaviorRecall, engram, gentleAi] = await Promise.all([
|
|
13034
|
+
isCmdAvailable("rtk"),
|
|
13035
|
+
isCmdAvailable("code-review-graph"),
|
|
13036
|
+
isCmdAvailable("token-savior"),
|
|
13037
|
+
isCmdAvailable("token-savior-recall"),
|
|
13038
|
+
isCmdAvailable("engram"),
|
|
13039
|
+
isCmdAvailable("gentle-ai")
|
|
13040
|
+
]);
|
|
12982
13041
|
cachedStatus = {
|
|
12983
|
-
rtk
|
|
12984
|
-
codeReviewGraph
|
|
12985
|
-
tokenSavior:
|
|
13042
|
+
rtk,
|
|
13043
|
+
codeReviewGraph,
|
|
13044
|
+
tokenSavior: tokenSavior || tokenSaviorRecall,
|
|
12986
13045
|
caveman: hasCaveman(),
|
|
12987
|
-
engram
|
|
12988
|
-
gentleAi
|
|
13046
|
+
engram,
|
|
13047
|
+
gentleAi
|
|
12989
13048
|
};
|
|
12990
13049
|
return cachedStatus;
|
|
12991
13050
|
}
|
|
12992
|
-
var cachedStatus = null;
|
|
12993
|
-
var init_complementary_detector = () => {
|
|
13051
|
+
var execFileAsync, cachedStatus = null;
|
|
13052
|
+
var init_complementary_detector = __esm(() => {
|
|
13053
|
+
execFileAsync = promisify(execFile);
|
|
13054
|
+
});
|
|
12994
13055
|
|
|
12995
13056
|
// src/core/presets/file-copier.ts
|
|
12996
13057
|
import { lstat as lstat2, readdir, readFile as readFile4, stat } from "node:fs/promises";
|
|
@@ -13083,9 +13144,9 @@ function extractAgentRole(filePath) {
|
|
|
13083
13144
|
];
|
|
13084
13145
|
return knownRoles.includes(name) ? name : null;
|
|
13085
13146
|
}
|
|
13086
|
-
function renderTemplate(content, modelConfig, filePath, locale = "en") {
|
|
13147
|
+
async function renderTemplate(content, modelConfig, filePath, locale = "en") {
|
|
13087
13148
|
const cleanLocale = locale === "es" || locale === "en" ? locale : "en";
|
|
13088
|
-
const tools = detectComplementaryTools();
|
|
13149
|
+
const tools = await detectComplementaryTools();
|
|
13089
13150
|
let complementaryRules = "";
|
|
13090
13151
|
if (cleanLocale === "es") {
|
|
13091
13152
|
if (tools.rtk) {
|
|
@@ -13208,7 +13269,7 @@ function substituteToolNames(content, modelConfig) {
|
|
|
13208
13269
|
const eol = fmMatch[1];
|
|
13209
13270
|
const frontmatter = fmMatch[2];
|
|
13210
13271
|
if (target === "opencode" && modelConfig.permissions) {
|
|
13211
|
-
const updatedFrontmatter2 = frontmatter.replace(/^tools:\s*(.+)\r?\n?/m, "");
|
|
13272
|
+
const updatedFrontmatter2 = frontmatter.replace(/^tools:\s*(.+)\r?\n?/m, "").replace(/^(?:temperature|effort):[^\r\n]*\r?\n?/gm, "");
|
|
13212
13273
|
return content.replace(fmMatch[0], `---${eol}${updatedFrontmatter2}${eol}---`);
|
|
13213
13274
|
}
|
|
13214
13275
|
if (!modelConfig.tools)
|
|
@@ -13226,7 +13287,7 @@ function substituteToolNames(content, modelConfig) {
|
|
|
13226
13287
|
});
|
|
13227
13288
|
return content.replace(fmMatch[0], `---${eol}${updatedFrontmatter}${eol}---`);
|
|
13228
13289
|
}
|
|
13229
|
-
function injectMcpServers(jsonString, filePath) {
|
|
13290
|
+
async function injectMcpServers(jsonString, filePath) {
|
|
13230
13291
|
const isAgyMcpConfig = filePath.endsWith("mcp_config.json");
|
|
13231
13292
|
const isClaudeSettings = filePath.endsWith("settings.json");
|
|
13232
13293
|
const isOpencodeJsonc = filePath.endsWith("opencode.jsonc") || filePath.endsWith("opencode.json");
|
|
@@ -13239,7 +13300,7 @@ function injectMcpServers(jsonString, filePath) {
|
|
|
13239
13300
|
cleanJson = jsonString.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "$1");
|
|
13240
13301
|
}
|
|
13241
13302
|
const obj = JSON.parse(cleanJson);
|
|
13242
|
-
const tools = detectComplementaryTools();
|
|
13303
|
+
const tools = await detectComplementaryTools();
|
|
13243
13304
|
const mcpServers = {};
|
|
13244
13305
|
if (tools.tokenSavior) {
|
|
13245
13306
|
mcpServers["token-savior-recall"] = {
|
|
@@ -13311,8 +13372,8 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
|
|
|
13311
13372
|
} catch (e) {
|
|
13312
13373
|
return { src: srcPath, dest: destPath, action: "error", error: `Cannot read source: ${e}` };
|
|
13313
13374
|
}
|
|
13314
|
-
let incomingContent = isTemplate && modelConfig ? renderTemplate(content, modelConfig, srcPath, locale) : content;
|
|
13315
|
-
incomingContent = injectMcpServers(incomingContent, destPath);
|
|
13375
|
+
let incomingContent = isTemplate && modelConfig ? await renderTemplate(content, modelConfig, srcPath, locale) : content;
|
|
13376
|
+
incomingContent = await injectMcpServers(incomingContent, destPath);
|
|
13316
13377
|
let finalContent = incomingContent;
|
|
13317
13378
|
let action = "written";
|
|
13318
13379
|
if (strategy === "append") {
|
|
@@ -14579,7 +14640,7 @@ async function checkUpdates(basePath, isGlobal, includeConflicts = false) {
|
|
|
14579
14640
|
let expectedContent;
|
|
14580
14641
|
try {
|
|
14581
14642
|
const srcContent = await readFile7(src, "utf-8");
|
|
14582
|
-
expectedContent = isTemplate && modelConfig ? renderTemplate(srcContent, modelConfig, src, locale) : srcContent;
|
|
14643
|
+
expectedContent = isTemplate && modelConfig ? await renderTemplate(srcContent, modelConfig, src, locale) : srcContent;
|
|
14583
14644
|
} catch {
|
|
14584
14645
|
continue;
|
|
14585
14646
|
}
|
|
@@ -15041,7 +15102,7 @@ async function doctorCommand(options) {
|
|
|
15041
15102
|
message: "No product graph yet. Run `codeconductor ingest` to build product memory."
|
|
15042
15103
|
});
|
|
15043
15104
|
}
|
|
15044
|
-
const compTools = detectComplementaryTools();
|
|
15105
|
+
const compTools = await detectComplementaryTools();
|
|
15045
15106
|
const toolDetails = [
|
|
15046
15107
|
{ name: "rtk", key: "rtk", label: "RTK", desc: "Run `brew install rtk` to compress command outputs" },
|
|
15047
15108
|
{ name: "code-review-graph", key: "codeReviewGraph", label: "code-review-graph", desc: "Run `pipx install code-review-graph` to navigate symbol graph" },
|
|
@@ -15419,6 +15480,25 @@ var init_profiles = __esm(() => {
|
|
|
15419
15480
|
routing: { default: ["validate-backlog", "discover", "design", "analyze", "test", "implement", "review"] },
|
|
15420
15481
|
confirmationGate: baseGate
|
|
15421
15482
|
},
|
|
15483
|
+
odd: {
|
|
15484
|
+
id: "odd",
|
|
15485
|
+
version: 1,
|
|
15486
|
+
command: "odd",
|
|
15487
|
+
taskCard: {
|
|
15488
|
+
type: "feature",
|
|
15489
|
+
requiredFields: ["title", "type", "risk", "scope", "context", "acceptanceCriteria"]
|
|
15490
|
+
},
|
|
15491
|
+
phases: [
|
|
15492
|
+
{ id: "wayfinding", agent: "repo-explorer", outputSchema: "agent-output" },
|
|
15493
|
+
{ id: "intake", agent: "task-coach", outputSchema: "planner-output", stopGate: "confirmation" },
|
|
15494
|
+
{ id: "ledger", agent: "orchestrator", type: "cli-gate" },
|
|
15495
|
+
{ id: "test", agent: "tester", dependsOn: ["intake"] },
|
|
15496
|
+
{ id: "implement", agent: "implementer", dependsOn: ["test"] },
|
|
15497
|
+
{ id: "review", agent: "reviewer" }
|
|
15498
|
+
],
|
|
15499
|
+
routing: { default: ["wayfinding", "intake", "ledger", "test", "implement", "review"] },
|
|
15500
|
+
confirmationGate: baseGate
|
|
15501
|
+
},
|
|
15422
15502
|
backlog: {
|
|
15423
15503
|
id: "backlog",
|
|
15424
15504
|
version: 1,
|
|
@@ -18304,13 +18384,13 @@ var init_binary_downloader = __esm(() => {
|
|
|
18304
18384
|
});
|
|
18305
18385
|
|
|
18306
18386
|
// src/core/lsp/lsp-installer.ts
|
|
18307
|
-
import { execFile } from "node:child_process";
|
|
18387
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
18308
18388
|
import { access as access6, mkdir as mkdir6, mkdtemp, rename as rename2, rm, writeFile as writeFile5 } from "node:fs/promises";
|
|
18309
18389
|
import { homedir as homedir8, tmpdir } from "node:os";
|
|
18310
18390
|
import { join as join9 } from "node:path";
|
|
18311
|
-
import { promisify } from "node:util";
|
|
18391
|
+
import { promisify as promisify2 } from "node:util";
|
|
18312
18392
|
async function assertTarArchiveSafe(archivePath, destRoot) {
|
|
18313
|
-
const { stdout } = await
|
|
18393
|
+
const { stdout } = await execFileAsync2("tar", ["-tzf", archivePath], {
|
|
18314
18394
|
timeout: 60000,
|
|
18315
18395
|
maxBuffer: 10 * 1024 * 1024
|
|
18316
18396
|
});
|
|
@@ -18322,7 +18402,7 @@ async function assertTarArchiveSafe(archivePath, destRoot) {
|
|
|
18322
18402
|
}
|
|
18323
18403
|
async function extractHardenedTar(archivePath, destRoot) {
|
|
18324
18404
|
await assertTarArchiveSafe(archivePath, destRoot);
|
|
18325
|
-
await
|
|
18405
|
+
await execFileAsync2("tar", ["-xzf", archivePath, "-C", destRoot, ...TAR_EXTRACT_HARDENING_FLAGS], { timeout: 120000 });
|
|
18326
18406
|
}
|
|
18327
18407
|
|
|
18328
18408
|
class LspInstaller {
|
|
@@ -18332,7 +18412,7 @@ class LspInstaller {
|
|
|
18332
18412
|
}
|
|
18333
18413
|
async checkInstalled(def) {
|
|
18334
18414
|
try {
|
|
18335
|
-
const { stdout } = await
|
|
18415
|
+
const { stdout } = await execFileAsync2("which", [def.binaryName], { timeout: 5000 });
|
|
18336
18416
|
const path = stdout.trim();
|
|
18337
18417
|
if (path) {
|
|
18338
18418
|
const version = await this.getVersion(def);
|
|
@@ -18341,7 +18421,7 @@ class LspInstaller {
|
|
|
18341
18421
|
} catch {}
|
|
18342
18422
|
if (def.packageManager === "npm" && def.npmDetect) {
|
|
18343
18423
|
try {
|
|
18344
|
-
const { stdout } = await
|
|
18424
|
+
const { stdout } = await execFileAsync2("npm", ["list", "-g", def.npmDetect, "--depth=0"], { timeout: 1e4 });
|
|
18345
18425
|
if (stdout.includes(def.npmDetect)) {
|
|
18346
18426
|
const version = this.parseVersionFromNpmList(stdout, def.npmDetect);
|
|
18347
18427
|
return { installed: true, version };
|
|
@@ -18350,7 +18430,7 @@ class LspInstaller {
|
|
|
18350
18430
|
}
|
|
18351
18431
|
if (def.packageManager === "pip" && def.pipDetect) {
|
|
18352
18432
|
try {
|
|
18353
|
-
const { stdout } = await
|
|
18433
|
+
const { stdout } = await execFileAsync2("pip", ["show", def.pipDetect], { timeout: 1e4 });
|
|
18354
18434
|
if (stdout.includes("Version:")) {
|
|
18355
18435
|
const version = this.parseVersionFromPipShow(stdout);
|
|
18356
18436
|
return { installed: true, version };
|
|
@@ -18414,7 +18494,7 @@ class LspInstaller {
|
|
|
18414
18494
|
}
|
|
18415
18495
|
async getVersion(def) {
|
|
18416
18496
|
try {
|
|
18417
|
-
const { stdout } = await
|
|
18497
|
+
const { stdout } = await execFileAsync2(def.binaryName, [def.versionFlag], { timeout: 5000 });
|
|
18418
18498
|
return stdout.trim().split(`
|
|
18419
18499
|
`)[0];
|
|
18420
18500
|
} catch {
|
|
@@ -18432,7 +18512,7 @@ class LspInstaller {
|
|
|
18432
18512
|
async installNpm(def) {
|
|
18433
18513
|
assertPinnedPackage(def);
|
|
18434
18514
|
try {
|
|
18435
|
-
await
|
|
18515
|
+
await execFileAsync2("npm", ["install", "-g", def.package], { timeout: 120000 });
|
|
18436
18516
|
} catch (error) {
|
|
18437
18517
|
throw new Error(`Failed to install ${def.serverName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
18438
18518
|
}
|
|
@@ -18440,7 +18520,7 @@ class LspInstaller {
|
|
|
18440
18520
|
async installPip(def) {
|
|
18441
18521
|
assertPinnedPackage(def);
|
|
18442
18522
|
try {
|
|
18443
|
-
await
|
|
18523
|
+
await execFileAsync2("pip", ["install", "--user", def.package], { timeout: 120000 });
|
|
18444
18524
|
} catch (error) {
|
|
18445
18525
|
throw new Error(`Failed to install ${def.serverName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
18446
18526
|
}
|
|
@@ -18482,7 +18562,7 @@ class LspInstaller {
|
|
|
18482
18562
|
await access6(candidate);
|
|
18483
18563
|
await rename2(candidate, destPath);
|
|
18484
18564
|
} catch {
|
|
18485
|
-
const { stdout: found } = await
|
|
18565
|
+
const { stdout: found } = await execFileAsync2("find", [workDir, "-type", "f", "-name", def.binaryName], { timeout: 30000 });
|
|
18486
18566
|
const first = found.split(`
|
|
18487
18567
|
`).map((l) => l.trim()).find((l) => l.length > 0);
|
|
18488
18568
|
if (!first) {
|
|
@@ -18492,7 +18572,7 @@ class LspInstaller {
|
|
|
18492
18572
|
await rename2(first, destPath);
|
|
18493
18573
|
}
|
|
18494
18574
|
if (process.platform !== "win32") {
|
|
18495
|
-
await
|
|
18575
|
+
await execFileAsync2("chmod", ["+x", destPath], { timeout: 5000 });
|
|
18496
18576
|
}
|
|
18497
18577
|
} finally {
|
|
18498
18578
|
await rm(workDir, { recursive: true, force: true });
|
|
@@ -18502,11 +18582,11 @@ class LspInstaller {
|
|
|
18502
18582
|
function createLspInstaller() {
|
|
18503
18583
|
return new LspInstaller;
|
|
18504
18584
|
}
|
|
18505
|
-
var
|
|
18585
|
+
var execFileAsync2, TAR_EXTRACT_HARDENING_FLAGS;
|
|
18506
18586
|
var init_lsp_installer = __esm(() => {
|
|
18507
18587
|
init_binary_integrity();
|
|
18508
18588
|
init_binary_downloader();
|
|
18509
|
-
|
|
18589
|
+
execFileAsync2 = promisify2(execFile2);
|
|
18510
18590
|
TAR_EXTRACT_HARDENING_FLAGS = [
|
|
18511
18591
|
"--no-same-owner",
|
|
18512
18592
|
"--no-same-permissions"
|
|
@@ -20771,13 +20851,13 @@ function slugify(text) {
|
|
|
20771
20851
|
|
|
20772
20852
|
// src/core/knowledge/source-parsers.ts
|
|
20773
20853
|
import { readFile as readFile19 } from "node:fs/promises";
|
|
20774
|
-
import { join as join15 } from "node:path";
|
|
20854
|
+
import { basename as basename6, join as join15, relative as relative7, sep as sep5 } from "node:path";
|
|
20775
20855
|
async function parseAdrFile(filePath) {
|
|
20776
20856
|
try {
|
|
20777
20857
|
const content = await readFile19(filePath, "utf-8");
|
|
20778
|
-
const
|
|
20779
|
-
const idMatch =
|
|
20780
|
-
const id = idMatch ? `adr-${idMatch[1]}` : slugify(
|
|
20858
|
+
const base = basename6(filePath);
|
|
20859
|
+
const idMatch = base.match(/adr-(\d+)/i);
|
|
20860
|
+
const id = idMatch ? `adr-${idMatch[1]}` : slugify(base);
|
|
20781
20861
|
const titleMatch = content.match(/^#\s+ADR-\d+:\s*(.+)$/m);
|
|
20782
20862
|
const statusMatch = content.match(/\*\*Status:\*\*\s*(\w+)/i);
|
|
20783
20863
|
const contextSection = extractSection(content, "Context");
|
|
@@ -20787,7 +20867,7 @@ async function parseAdrFile(filePath) {
|
|
|
20787
20867
|
`).filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*")).map((l) => l.replace(/^[\s*-]+/, "").trim()).filter(Boolean);
|
|
20788
20868
|
return {
|
|
20789
20869
|
id,
|
|
20790
|
-
title: titleMatch?.[1]?.trim() ??
|
|
20870
|
+
title: titleMatch?.[1]?.trim() ?? base,
|
|
20791
20871
|
status: statusMatch?.[1]?.toLowerCase() ?? "unknown",
|
|
20792
20872
|
context: contextSection.trim(),
|
|
20793
20873
|
decision: decisionSection.trim(),
|
|
@@ -20887,7 +20967,7 @@ async function scanSrcComponents(projectRoot) {
|
|
|
20887
20967
|
source: "src/",
|
|
20888
20968
|
confidence: "medium",
|
|
20889
20969
|
relations: [],
|
|
20890
|
-
data: { path: full.
|
|
20970
|
+
data: { path: relative7(projectRoot, full).split(sep5).join("/") }
|
|
20891
20971
|
});
|
|
20892
20972
|
await walk(full, prefix ? `${prefix}-${domainSlug}` : domainSlug);
|
|
20893
20973
|
}
|
|
@@ -20948,7 +21028,7 @@ var init_episodic_store = __esm(() => {
|
|
|
20948
21028
|
// src/core/knowledge/ingest-pipeline.ts
|
|
20949
21029
|
import { createHash as createHash3 } from "node:crypto";
|
|
20950
21030
|
import { readFile as readFile21 } from "node:fs/promises";
|
|
20951
|
-
import { join as join16 } from "node:path";
|
|
21031
|
+
import { join as join16, relative as relative8, sep as sep6 } from "node:path";
|
|
20952
21032
|
import { mkdir as mkdir13, writeFile as writeFile12 } from "node:fs/promises";
|
|
20953
21033
|
async function mapLimit(items, limit, fn) {
|
|
20954
21034
|
if (items.length === 0)
|
|
@@ -20997,7 +21077,7 @@ async function collectDeferRisks(projectRoot) {
|
|
|
20997
21077
|
const perFile = await mapLimit(files, WALK_READ_CONCURRENCY, async (full) => {
|
|
20998
21078
|
const entities = [];
|
|
20999
21079
|
const content = await readFile21(full, "utf-8");
|
|
21000
|
-
const rel = full.
|
|
21080
|
+
const rel = relative8(projectRoot, full).split(sep6).join("/");
|
|
21001
21081
|
const deferRe = new RegExp(DEFER_REGEX_SOURCE, "gm");
|
|
21002
21082
|
let match;
|
|
21003
21083
|
while ((match = deferRe.exec(content)) !== null) {
|
|
@@ -21487,7 +21567,10 @@ async function listScorecards(projectRoot, filter = {}) {
|
|
|
21487
21567
|
}
|
|
21488
21568
|
async function hasPassingScorecard(projectRoot, backlogId) {
|
|
21489
21569
|
const listed = await listScorecards(projectRoot, { backlogId, verdict: "PASS" });
|
|
21490
|
-
|
|
21570
|
+
if (listed.success && listed.data.length > 0)
|
|
21571
|
+
return true;
|
|
21572
|
+
const outcomes = await listOutcomes(projectRoot, { backlogId, source: "review" });
|
|
21573
|
+
return outcomes.success && outcomes.data.some((outcome) => outcome.verdict === "PASS");
|
|
21491
21574
|
}
|
|
21492
21575
|
async function loadScorecard(projectRoot, id) {
|
|
21493
21576
|
try {
|
|
@@ -21519,7 +21602,7 @@ function aggregateOutcomes(outcomes) {
|
|
|
21519
21602
|
agentScoreCounts[o.agent] = (agentScoreCounts[o.agent] ?? 0) + 1;
|
|
21520
21603
|
}
|
|
21521
21604
|
if (!byModel[o.model])
|
|
21522
|
-
byModel[o.model] = { count: 0, avgScore: 0 };
|
|
21605
|
+
byModel[o.model] = { count: 0, avgScore: 0, knownTokenMeasurements: 0 };
|
|
21523
21606
|
byModel[o.model].count++;
|
|
21524
21607
|
if (o.weightedScore !== undefined) {
|
|
21525
21608
|
byModel[o.model].avgScore += o.weightedScore;
|
|
@@ -21529,10 +21612,11 @@ function aggregateOutcomes(outcomes) {
|
|
|
21529
21612
|
byModel[o.model].avgCost = (byModel[o.model].avgCost ?? 0) + o.costUsd;
|
|
21530
21613
|
modelCostCounts[o.model] = (modelCostCounts[o.model] ?? 0) + 1;
|
|
21531
21614
|
}
|
|
21532
|
-
|
|
21533
|
-
|
|
21534
|
-
byModel[o.model].avgTokens = (byModel[o.model].avgTokens ?? 0) +
|
|
21615
|
+
const reportedTokens = o.delivery?.tokenUsage.status === "known" ? o.delivery.tokenUsage.total : o.tokensIn !== undefined || o.tokensOut !== undefined ? (o.tokensIn ?? 0) + (o.tokensOut ?? 0) : undefined;
|
|
21616
|
+
if (reportedTokens !== undefined) {
|
|
21617
|
+
byModel[o.model].avgTokens = (byModel[o.model].avgTokens ?? 0) + reportedTokens;
|
|
21535
21618
|
modelTokenCounts[o.model] = (modelTokenCounts[o.model] ?? 0) + 1;
|
|
21619
|
+
byModel[o.model].knownTokenMeasurements++;
|
|
21536
21620
|
}
|
|
21537
21621
|
if (o.variantId) {
|
|
21538
21622
|
if (!byVariant[o.variantId])
|
|
@@ -22182,11 +22266,140 @@ var init_compile_checker = __esm(() => {
|
|
|
22182
22266
|
];
|
|
22183
22267
|
});
|
|
22184
22268
|
|
|
22269
|
+
// src/core/verification/rdd-receipt.ts
|
|
22270
|
+
var exports_rdd_receipt = {};
|
|
22271
|
+
__export(exports_rdd_receipt, {
|
|
22272
|
+
captureReceipt: () => captureReceipt,
|
|
22273
|
+
collectReceiptPaths: () => collectReceiptPaths,
|
|
22274
|
+
isRddReceipt: () => isRddReceipt,
|
|
22275
|
+
verifyReceipt: () => verifyReceipt
|
|
22276
|
+
});
|
|
22277
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
22278
|
+
import { readdir as readdir7, readFile as readFile27 } from "node:fs/promises";
|
|
22279
|
+
import { join as join18, relative as relative9, resolve as resolve26, sep as sep7 } from "node:path";
|
|
22280
|
+
function sha2562(content) {
|
|
22281
|
+
return createHash4("sha256").update(content).digest("hex");
|
|
22282
|
+
}
|
|
22283
|
+
function canonicalPath(projectRoot, input) {
|
|
22284
|
+
const root = resolve26(projectRoot);
|
|
22285
|
+
const absolute = resolve26(root, input);
|
|
22286
|
+
if (absolute !== root && !absolute.startsWith(`${root}${sep7}`)) {
|
|
22287
|
+
throw new Error(`RDD receipt path "${input}" is outside the project root`);
|
|
22288
|
+
}
|
|
22289
|
+
const normalized = relative9(root, absolute);
|
|
22290
|
+
if (!normalized || normalized === ".") {
|
|
22291
|
+
throw new Error("RDD receipts require file paths, not the project root");
|
|
22292
|
+
}
|
|
22293
|
+
return { absolute, relative: normalized.split(sep7).join("/") };
|
|
22294
|
+
}
|
|
22295
|
+
async function fingerprint(projectRoot, paths) {
|
|
22296
|
+
const deduplicated = new Map;
|
|
22297
|
+
for (const path of paths) {
|
|
22298
|
+
const canonical = canonicalPath(projectRoot, path);
|
|
22299
|
+
deduplicated.set(canonical.relative, canonical.absolute);
|
|
22300
|
+
}
|
|
22301
|
+
const result = [];
|
|
22302
|
+
for (const [path, absolute] of [...deduplicated.entries()].sort(([left], [right]) => left.localeCompare(right))) {
|
|
22303
|
+
result.push({ path, hash: sha2562(await readFile27(absolute)) });
|
|
22304
|
+
}
|
|
22305
|
+
return result;
|
|
22306
|
+
}
|
|
22307
|
+
function manifestHash(paths) {
|
|
22308
|
+
return sha2562(paths.map((entry) => `${entry.path}\x00${entry.hash}`).join(`
|
|
22309
|
+
`));
|
|
22310
|
+
}
|
|
22311
|
+
function isRddReceipt(value) {
|
|
22312
|
+
if (!value || typeof value !== "object")
|
|
22313
|
+
return false;
|
|
22314
|
+
const receipt = value;
|
|
22315
|
+
return receipt.version === 1 && typeof receipt.taskId === "string" && typeof receipt.phase === "string" && Array.isArray(receipt.paths) && receipt.paths.every((entry) => Boolean(entry) && typeof entry.path === "string" && typeof entry.hash === "string") && (receipt.coverage === "paths" || receipt.coverage === "project") && typeof receipt.manifestHash === "string" && (receipt.outcome === "passed" || receipt.outcome === "failed") && typeof receipt.capturedAt === "string";
|
|
22316
|
+
}
|
|
22317
|
+
async function captureReceipt(projectRoot, input) {
|
|
22318
|
+
if (!input.taskId.trim())
|
|
22319
|
+
throw new Error("RDD receipts require a task id");
|
|
22320
|
+
if (input.paths.length === 0 && input.coverage !== "project") {
|
|
22321
|
+
throw new Error("RDD receipts require at least one protected path");
|
|
22322
|
+
}
|
|
22323
|
+
const paths = await fingerprint(projectRoot, input.paths);
|
|
22324
|
+
return {
|
|
22325
|
+
version: 1,
|
|
22326
|
+
taskId: input.taskId,
|
|
22327
|
+
phase: input.phase,
|
|
22328
|
+
paths,
|
|
22329
|
+
coverage: input.coverage ?? "paths",
|
|
22330
|
+
manifestHash: manifestHash(paths),
|
|
22331
|
+
command: input.command,
|
|
22332
|
+
outcome: input.outcome,
|
|
22333
|
+
capturedAt: new Date().toISOString()
|
|
22334
|
+
};
|
|
22335
|
+
}
|
|
22336
|
+
async function collectReceiptPaths(projectRoot) {
|
|
22337
|
+
const root = resolve26(projectRoot);
|
|
22338
|
+
const paths = [];
|
|
22339
|
+
async function visit2(current) {
|
|
22340
|
+
const entries = await readdir7(current, { withFileTypes: true });
|
|
22341
|
+
for (const entry of entries) {
|
|
22342
|
+
if (entry.isDirectory()) {
|
|
22343
|
+
if (!EXCLUDED_DIRECTORIES.has(entry.name))
|
|
22344
|
+
await visit2(join18(current, entry.name));
|
|
22345
|
+
continue;
|
|
22346
|
+
}
|
|
22347
|
+
if (entry.isFile())
|
|
22348
|
+
paths.push(relative9(root, join18(current, entry.name)).split(sep7).join("/"));
|
|
22349
|
+
}
|
|
22350
|
+
}
|
|
22351
|
+
await visit2(root);
|
|
22352
|
+
return paths.sort();
|
|
22353
|
+
}
|
|
22354
|
+
async function verifyReceipt(projectRoot, receipt) {
|
|
22355
|
+
const expected = new Map(receipt.paths.map((entry) => [entry.path, entry.hash]));
|
|
22356
|
+
const changedPaths = [];
|
|
22357
|
+
const current = [];
|
|
22358
|
+
const pathsToCheck = receipt.coverage === "project" ? await collectReceiptPaths(projectRoot) : receipt.paths.map((entry) => entry.path);
|
|
22359
|
+
const currentByPath = new Map;
|
|
22360
|
+
for (const path of pathsToCheck) {
|
|
22361
|
+
try {
|
|
22362
|
+
const canonical = canonicalPath(projectRoot, path);
|
|
22363
|
+
const hash = sha2562(await readFile27(canonical.absolute));
|
|
22364
|
+
const currentEntry = { path: canonical.relative, hash };
|
|
22365
|
+
current.push(currentEntry);
|
|
22366
|
+
currentByPath.set(currentEntry.path, currentEntry);
|
|
22367
|
+
if (hash !== expected.get(currentEntry.path))
|
|
22368
|
+
changedPaths.push(currentEntry.path);
|
|
22369
|
+
} catch {
|
|
22370
|
+
changedPaths.push(path);
|
|
22371
|
+
}
|
|
22372
|
+
}
|
|
22373
|
+
for (const path of expected.keys()) {
|
|
22374
|
+
if (!currentByPath.has(path) && !changedPaths.includes(path))
|
|
22375
|
+
changedPaths.push(path);
|
|
22376
|
+
}
|
|
22377
|
+
changedPaths.sort();
|
|
22378
|
+
const actualHash = manifestHash(current.sort((left, right) => left.path.localeCompare(right.path)));
|
|
22379
|
+
return {
|
|
22380
|
+
valid: changedPaths.length === 0 && actualHash === receipt.manifestHash,
|
|
22381
|
+
expectedHash: receipt.manifestHash,
|
|
22382
|
+
actualHash,
|
|
22383
|
+
changedPaths
|
|
22384
|
+
};
|
|
22385
|
+
}
|
|
22386
|
+
var EXCLUDED_DIRECTORIES;
|
|
22387
|
+
var init_rdd_receipt = __esm(() => {
|
|
22388
|
+
EXCLUDED_DIRECTORIES = new Set([
|
|
22389
|
+
".git",
|
|
22390
|
+
".codeconductor",
|
|
22391
|
+
"node_modules",
|
|
22392
|
+
"dist",
|
|
22393
|
+
"coverage",
|
|
22394
|
+
"graphify-out"
|
|
22395
|
+
]);
|
|
22396
|
+
});
|
|
22397
|
+
|
|
22185
22398
|
// src/core/verification/verification-runner.ts
|
|
22186
22399
|
import { existsSync as existsSync3 } from "node:fs";
|
|
22187
|
-
import { readFile as
|
|
22188
|
-
import { dirname as dirname10, join as
|
|
22189
|
-
import { mkdir as mkdir18, readdir as
|
|
22400
|
+
import { readFile as readFile28 } from "node:fs/promises";
|
|
22401
|
+
import { dirname as dirname10, join as join19, resolve as resolve27 } from "node:path";
|
|
22402
|
+
import { mkdir as mkdir18, readdir as readdir8, writeFile as writeFile18 } from "node:fs/promises";
|
|
22190
22403
|
function isPassingEvidence(ev) {
|
|
22191
22404
|
switch (ev.type) {
|
|
22192
22405
|
case "verification":
|
|
@@ -22203,9 +22416,17 @@ function isPassingEvidence(ev) {
|
|
|
22203
22416
|
return false;
|
|
22204
22417
|
}
|
|
22205
22418
|
}
|
|
22419
|
+
async function isCurrentPassingEvidence(projectRoot, ev) {
|
|
22420
|
+
if (!isPassingEvidence(ev))
|
|
22421
|
+
return false;
|
|
22422
|
+
const receipt = ev.data?.rddReceipt;
|
|
22423
|
+
if (!isRddReceipt(receipt))
|
|
22424
|
+
return false;
|
|
22425
|
+
return (await verifyReceipt(projectRoot, receipt)).valid;
|
|
22426
|
+
}
|
|
22206
22427
|
function evidenceFilePath(evDir, id) {
|
|
22207
|
-
const path =
|
|
22208
|
-
if (dirname10(path) !==
|
|
22428
|
+
const path = resolve27(evDir, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
22429
|
+
if (dirname10(path) !== resolve27(evDir)) {
|
|
22209
22430
|
return err(new Error(`Refusing to write evidence for "${id}" outside ${evDir}`));
|
|
22210
22431
|
}
|
|
22211
22432
|
return ok(path);
|
|
@@ -22231,7 +22452,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
|
|
|
22231
22452
|
return ok({ records, invalid });
|
|
22232
22453
|
let files;
|
|
22233
22454
|
try {
|
|
22234
|
-
files = await
|
|
22455
|
+
files = await readdir8(evDir);
|
|
22235
22456
|
} catch (e) {
|
|
22236
22457
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
22237
22458
|
}
|
|
@@ -22239,7 +22460,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
|
|
|
22239
22460
|
if (!file.endsWith(".json"))
|
|
22240
22461
|
continue;
|
|
22241
22462
|
try {
|
|
22242
|
-
const raw = await
|
|
22463
|
+
const raw = await readFile28(join19(evDir, file), "utf-8");
|
|
22243
22464
|
const ev = EvidenceSchema.parse(JSON.parse(raw));
|
|
22244
22465
|
if (ev.relatedTask === taskId)
|
|
22245
22466
|
records.push(ev);
|
|
@@ -22260,7 +22481,7 @@ async function validateEvidenceIds(projectRoot, taskId, evidenceIds) {
|
|
|
22260
22481
|
if (!record) {
|
|
22261
22482
|
return err(new Error(`Evidence "${id}" is not a record of task ${taskId}`));
|
|
22262
22483
|
}
|
|
22263
|
-
if (!
|
|
22484
|
+
if (!await isCurrentPassingEvidence(projectRoot, record)) {
|
|
22264
22485
|
return err(new Error(`Evidence "${id}" does not record a passing result`));
|
|
22265
22486
|
}
|
|
22266
22487
|
if (record.type === "verification")
|
|
@@ -22341,7 +22562,12 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
22341
22562
|
passed: collected.data.invalid === 0,
|
|
22342
22563
|
message: collected.data.invalid === 0 ? "All evidence records are readable" : `${collected.data.invalid} unreadable evidence record(s)`
|
|
22343
22564
|
});
|
|
22344
|
-
const supporting =
|
|
22565
|
+
const supporting = [];
|
|
22566
|
+
for (const candidate of collected.data.records) {
|
|
22567
|
+
if (candidate.type !== "verification" && await isCurrentPassingEvidence(projectRoot, candidate)) {
|
|
22568
|
+
supporting.push(candidate);
|
|
22569
|
+
}
|
|
22570
|
+
}
|
|
22345
22571
|
evidenceIds.push(...supporting.map((ev) => ev.id));
|
|
22346
22572
|
checks.push({
|
|
22347
22573
|
name: "evidence_present",
|
|
@@ -22349,6 +22575,13 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
22349
22575
|
message: supporting.length > 0 ? `${supporting.length} passing evidence record(s) for task` : task.risk === "low" ? "Low risk task — evidence optional" : "No passing evidence records found for task"
|
|
22350
22576
|
});
|
|
22351
22577
|
const passed = checks.every((c) => c.passed);
|
|
22578
|
+
const receipt = await captureReceipt(projectRoot, {
|
|
22579
|
+
taskId,
|
|
22580
|
+
phase: "verification",
|
|
22581
|
+
paths: await collectReceiptPaths(projectRoot),
|
|
22582
|
+
outcome: passed ? "passed" : "failed",
|
|
22583
|
+
coverage: "project"
|
|
22584
|
+
});
|
|
22352
22585
|
const evidence = {
|
|
22353
22586
|
id: `ev-verify-${taskId}-${Date.now()}`,
|
|
22354
22587
|
source: "cc verify",
|
|
@@ -22357,7 +22590,7 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
22357
22590
|
relatedTask: taskId,
|
|
22358
22591
|
confidence: passed ? 0.9 : 0.3,
|
|
22359
22592
|
summary: passed ? "Verification passed" : "Verification failed",
|
|
22360
|
-
data: { passed, checks }
|
|
22593
|
+
data: { passed, checks, rddReceipt: receipt }
|
|
22361
22594
|
};
|
|
22362
22595
|
const persisted = await persistEvidence(projectRoot, evidence);
|
|
22363
22596
|
if (!persisted.success)
|
|
@@ -22380,19 +22613,25 @@ async function gateTaskCompletion(projectRoot, taskId, evidenceRequired, evidenc
|
|
|
22380
22613
|
if (collected.data.invalid > 0)
|
|
22381
22614
|
return ok(false);
|
|
22382
22615
|
const cited = evidenceIds ? new Set(evidenceIds) : undefined;
|
|
22383
|
-
const hasPassing = (type) =>
|
|
22616
|
+
const hasPassing = async (type) => {
|
|
22617
|
+
for (const evidence of collected.data.records) {
|
|
22618
|
+
if ((!cited || cited.has(evidence.id)) && evidence.type === type && await isCurrentPassingEvidence(projectRoot, evidence))
|
|
22619
|
+
return true;
|
|
22620
|
+
}
|
|
22621
|
+
return false;
|
|
22622
|
+
};
|
|
22384
22623
|
for (const req of evidenceRequired) {
|
|
22385
22624
|
switch (req) {
|
|
22386
22625
|
case "acceptance_criteria_met":
|
|
22387
|
-
if (!hasPassing("verification"))
|
|
22626
|
+
if (!await hasPassing("verification"))
|
|
22388
22627
|
return ok(false);
|
|
22389
22628
|
break;
|
|
22390
22629
|
case "tests_passed":
|
|
22391
|
-
if (!hasPassing("test"))
|
|
22630
|
+
if (!await hasPassing("test"))
|
|
22392
22631
|
return ok(false);
|
|
22393
22632
|
break;
|
|
22394
22633
|
case "review_approved":
|
|
22395
|
-
if (!hasPassing("review"))
|
|
22634
|
+
if (!await hasPassing("review"))
|
|
22396
22635
|
return ok(false);
|
|
22397
22636
|
break;
|
|
22398
22637
|
default:
|
|
@@ -22401,11 +22640,66 @@ async function gateTaskCompletion(projectRoot, taskId, evidenceRequired, evidenc
|
|
|
22401
22640
|
}
|
|
22402
22641
|
return ok(true);
|
|
22403
22642
|
}
|
|
22404
|
-
async function
|
|
22643
|
+
async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
|
|
22644
|
+
const evDir = evidenceDir(projectRoot);
|
|
22645
|
+
const path = evidenceFilePath(evDir, evidenceId);
|
|
22646
|
+
if (!path.success)
|
|
22647
|
+
return path;
|
|
22648
|
+
let raw;
|
|
22649
|
+
try {
|
|
22650
|
+
raw = await readFile28(path.data, "utf-8");
|
|
22651
|
+
} catch (e) {
|
|
22652
|
+
return err(e instanceof Error ? e : new Error(String(e)));
|
|
22653
|
+
}
|
|
22654
|
+
let ev;
|
|
22655
|
+
try {
|
|
22656
|
+
ev = EvidenceSchema.parse(JSON.parse(raw));
|
|
22657
|
+
} catch {
|
|
22658
|
+
return err(new Error(`Evidence "${evidenceId}" is not valid TDD evidence`));
|
|
22659
|
+
}
|
|
22660
|
+
if (ev.relatedTask !== taskId) {
|
|
22661
|
+
return err(new Error(`Evidence "${evidenceId}" is not a record of task ${taskId}`));
|
|
22662
|
+
}
|
|
22663
|
+
if (ev.type !== "tdd" || ev.source !== TDD_EVIDENCE_SOURCE) {
|
|
22664
|
+
return err(new Error(`Evidence "${evidenceId}" was not captured by the verification runner`));
|
|
22665
|
+
}
|
|
22666
|
+
const capturedBy = ev.data?.capturedBy;
|
|
22667
|
+
const suiteFailed = ev.data?.suiteFailed;
|
|
22668
|
+
const suitePassed = ev.data?.suitePassed;
|
|
22669
|
+
if (capturedBy !== TDD_CAPTURED_BY || typeof suiteFailed !== "boolean" || typeof suitePassed !== "boolean") {
|
|
22670
|
+
return err(new Error(`Evidence "${evidenceId}" was not captured by the verification runner`));
|
|
22671
|
+
}
|
|
22672
|
+
const receipt = ev.data?.rddReceipt;
|
|
22673
|
+
if (!isRddReceipt(receipt)) {
|
|
22674
|
+
return err(new Error(`Evidence "${evidenceId}" has no valid RDD receipt`));
|
|
22675
|
+
}
|
|
22676
|
+
if (receipt.taskId !== taskId) {
|
|
22677
|
+
return err(new Error(`Evidence "${evidenceId}" receipt belongs to another task`));
|
|
22678
|
+
}
|
|
22679
|
+
const verified = await verifyReceipt(projectRoot, receipt);
|
|
22680
|
+
if (!verified.valid) {
|
|
22681
|
+
return err(new Error(`Evidence "${evidenceId}" RDD receipt is stale: ${verified.changedPaths.join(", ")}`));
|
|
22682
|
+
}
|
|
22683
|
+
return ok({ capturedBy, suiteFailed, suitePassed, receipt });
|
|
22684
|
+
}
|
|
22685
|
+
async function hasTddRunnerEvidence(projectRoot, taskId, expectedPhase) {
|
|
22405
22686
|
const collected = await collectTaskEvidence(projectRoot, taskId);
|
|
22406
22687
|
if (!collected.success)
|
|
22407
22688
|
return false;
|
|
22408
|
-
|
|
22689
|
+
for (const ev of collected.data.records) {
|
|
22690
|
+
if (ev.type !== "tdd" || ev.source !== TDD_EVIDENCE_SOURCE || ev.data?.capturedBy !== TDD_CAPTURED_BY) {
|
|
22691
|
+
continue;
|
|
22692
|
+
}
|
|
22693
|
+
const loaded = await loadTddSuiteEvidence(projectRoot, taskId, ev.id);
|
|
22694
|
+
if (!loaded.success)
|
|
22695
|
+
continue;
|
|
22696
|
+
if (expectedPhase === "red" && (!loaded.data.suiteFailed || loaded.data.suitePassed))
|
|
22697
|
+
continue;
|
|
22698
|
+
if (expectedPhase === "green" && (!loaded.data.suitePassed || loaded.data.suiteFailed))
|
|
22699
|
+
continue;
|
|
22700
|
+
return true;
|
|
22701
|
+
}
|
|
22702
|
+
return false;
|
|
22409
22703
|
}
|
|
22410
22704
|
var TDD_EVIDENCE_SOURCE = "cc verify", TDD_CAPTURED_BY = "verification-runner";
|
|
22411
22705
|
var init_verification_runner = __esm(() => {
|
|
@@ -22414,6 +22708,7 @@ var init_verification_runner = __esm(() => {
|
|
|
22414
22708
|
init_compile_checker();
|
|
22415
22709
|
init_paths();
|
|
22416
22710
|
init_episodic_store();
|
|
22711
|
+
init_rdd_receipt();
|
|
22417
22712
|
});
|
|
22418
22713
|
|
|
22419
22714
|
// src/core/orchestrator/runtime-orchestrator.ts
|
|
@@ -22753,8 +23048,8 @@ var init_loop_state = __esm(() => {
|
|
|
22753
23048
|
});
|
|
22754
23049
|
|
|
22755
23050
|
// src/core/loop/git-stats.ts
|
|
22756
|
-
import { execFile as
|
|
22757
|
-
import { promisify as
|
|
23051
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
23052
|
+
import { promisify as promisify3 } from "node:util";
|
|
22758
23053
|
function countPorcelain(output) {
|
|
22759
23054
|
return output.split(`
|
|
22760
23055
|
`).filter((line) => line.trim().length > 0).length;
|
|
@@ -22778,12 +23073,12 @@ function countNumstat(output) {
|
|
|
22778
23073
|
async function fetchGitChangeStats(cwd) {
|
|
22779
23074
|
try {
|
|
22780
23075
|
const [status, diff] = await Promise.all([
|
|
22781
|
-
|
|
23076
|
+
execFileAsync3("git", ["status", "--porcelain"], {
|
|
22782
23077
|
cwd,
|
|
22783
23078
|
encoding: "utf-8",
|
|
22784
23079
|
timeout: 15000
|
|
22785
23080
|
}),
|
|
22786
|
-
|
|
23081
|
+
execFileAsync3("git", ["diff", "--numstat"], {
|
|
22787
23082
|
cwd,
|
|
22788
23083
|
encoding: "utf-8",
|
|
22789
23084
|
timeout: 15000
|
|
@@ -22807,9 +23102,9 @@ async function readGitChangeStats(cwd, options) {
|
|
|
22807
23102
|
cache = { cwd, at: now, stats };
|
|
22808
23103
|
return stats;
|
|
22809
23104
|
}
|
|
22810
|
-
var
|
|
23105
|
+
var execFileAsync3, GIT_STATS_CACHE_TTL_MS = 3000, cache;
|
|
22811
23106
|
var init_git_stats = __esm(() => {
|
|
22812
|
-
|
|
23107
|
+
execFileAsync3 = promisify3(execFile3);
|
|
22813
23108
|
});
|
|
22814
23109
|
|
|
22815
23110
|
// src/core/loop/loop-engine.ts
|
|
@@ -23536,6 +23831,163 @@ var init_verify_command = __esm(() => {
|
|
|
23536
23831
|
init_verification_runner();
|
|
23537
23832
|
});
|
|
23538
23833
|
|
|
23834
|
+
// src/commands/rdd.command.ts
|
|
23835
|
+
var exports_rdd_command = {};
|
|
23836
|
+
__export(exports_rdd_command, {
|
|
23837
|
+
rddCommand: () => rddCommand,
|
|
23838
|
+
rddStatus: () => rddStatus
|
|
23839
|
+
});
|
|
23840
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
23841
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
23842
|
+
import { mkdir as mkdir19, readFile as readFile29, readdir as readdir9, rename as rename3, writeFile as writeFile19, chmod } from "node:fs/promises";
|
|
23843
|
+
import { dirname as dirname11, resolve as resolve28 } from "node:path";
|
|
23844
|
+
import { promisify as promisify4 } from "node:util";
|
|
23845
|
+
function receiptPath(projectRoot, id) {
|
|
23846
|
+
const directory = resolve28(evidenceDir(projectRoot));
|
|
23847
|
+
const path = resolve28(directory, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
23848
|
+
if (dirname11(path) !== directory)
|
|
23849
|
+
throw new Error(`Invalid RDD receipt id: ${id}`);
|
|
23850
|
+
return path;
|
|
23851
|
+
}
|
|
23852
|
+
async function loadReceipt(projectRoot, id) {
|
|
23853
|
+
const raw = await readFile29(receiptPath(projectRoot, id), "utf-8");
|
|
23854
|
+
const evidence = EvidenceSchema.parse(JSON.parse(raw));
|
|
23855
|
+
if (evidence.type !== "rdd")
|
|
23856
|
+
throw new Error(`Evidence "${id}" is not an RDD receipt`);
|
|
23857
|
+
return { evidence, receipt: assertReceipt(evidence.data?.rddReceipt) };
|
|
23858
|
+
}
|
|
23859
|
+
function assertReceipt(value) {
|
|
23860
|
+
if (!isRddReceipt(value))
|
|
23861
|
+
throw new Error("RDD receipt payload is invalid");
|
|
23862
|
+
return value;
|
|
23863
|
+
}
|
|
23864
|
+
async function rddStatus(projectRoot, taskId) {
|
|
23865
|
+
const directory = evidenceDir(projectRoot);
|
|
23866
|
+
if (!existsSync4(directory))
|
|
23867
|
+
return [];
|
|
23868
|
+
const receipts = [];
|
|
23869
|
+
for (const file of await readdir9(directory)) {
|
|
23870
|
+
if (!file.endsWith(".json"))
|
|
23871
|
+
continue;
|
|
23872
|
+
try {
|
|
23873
|
+
const id = file.slice(0, -".json".length);
|
|
23874
|
+
const { evidence, receipt } = await loadReceipt(projectRoot, id);
|
|
23875
|
+
if (taskId && evidence.relatedTask !== taskId)
|
|
23876
|
+
continue;
|
|
23877
|
+
const verification = await verifyReceipt(projectRoot, receipt);
|
|
23878
|
+
receipts.push({
|
|
23879
|
+
id: evidence.id,
|
|
23880
|
+
taskId: evidence.relatedTask,
|
|
23881
|
+
phase: receipt.phase,
|
|
23882
|
+
timestamp: evidence.timestamp,
|
|
23883
|
+
valid: verification.valid,
|
|
23884
|
+
changedPaths: verification.changedPaths
|
|
23885
|
+
});
|
|
23886
|
+
} catch {}
|
|
23887
|
+
}
|
|
23888
|
+
return receipts.sort((left, right) => right.timestamp.localeCompare(left.timestamp));
|
|
23889
|
+
}
|
|
23890
|
+
async function installHooks(projectRoot) {
|
|
23891
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", "--git-path", "hooks"], {
|
|
23892
|
+
cwd: projectRoot,
|
|
23893
|
+
encoding: "utf8"
|
|
23894
|
+
});
|
|
23895
|
+
const hooksDir = resolve28(projectRoot, stdout.trim());
|
|
23896
|
+
await mkdir19(hooksDir, { recursive: true });
|
|
23897
|
+
const installed = [];
|
|
23898
|
+
const skipped = [];
|
|
23899
|
+
for (const hook of ["pre-commit", "pre-push"]) {
|
|
23900
|
+
const path = resolve28(hooksDir, hook);
|
|
23901
|
+
const backup = resolve28(hooksDir, `${hook}.rdd-original`);
|
|
23902
|
+
const marker = "# codeconductor-rdd-hook";
|
|
23903
|
+
if (existsSync4(path)) {
|
|
23904
|
+
const existing = await readFile29(path, "utf8");
|
|
23905
|
+
if (existing.includes(marker)) {
|
|
23906
|
+
skipped.push(hook);
|
|
23907
|
+
continue;
|
|
23908
|
+
}
|
|
23909
|
+
if (existsSync4(backup)) {
|
|
23910
|
+
throw new Error(`Cannot preserve ${hook}: ${backup} already exists`);
|
|
23911
|
+
}
|
|
23912
|
+
await rename3(path, backup);
|
|
23913
|
+
}
|
|
23914
|
+
const script = `#!/bin/sh
|
|
23915
|
+
${marker}
|
|
23916
|
+
set -e
|
|
23917
|
+
HOOK_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
|
23918
|
+
if [ -x "$HOOK_DIR/${hook}.rdd-original" ]; then
|
|
23919
|
+
"$HOOK_DIR/${hook}.rdd-original" "$@"
|
|
23920
|
+
fi
|
|
23921
|
+
cc-codeconductor rdd git-check
|
|
23922
|
+
`;
|
|
23923
|
+
await writeFile19(path, script, "utf8");
|
|
23924
|
+
await chmod(path, 493);
|
|
23925
|
+
installed.push(hook);
|
|
23926
|
+
}
|
|
23927
|
+
return { installed, skipped };
|
|
23928
|
+
}
|
|
23929
|
+
async function rddCommand(options) {
|
|
23930
|
+
try {
|
|
23931
|
+
if (options.subcommand === "capture") {
|
|
23932
|
+
if (!options.taskId)
|
|
23933
|
+
throw new Error("Missing task id. Use --task <id>.");
|
|
23934
|
+
const protectedPaths = options.paths?.length ? options.paths : await collectReceiptPaths(options.projectRoot);
|
|
23935
|
+
const receipt = await captureReceipt(options.projectRoot, {
|
|
23936
|
+
taskId: options.taskId,
|
|
23937
|
+
phase: options.phase ?? "verification",
|
|
23938
|
+
paths: protectedPaths,
|
|
23939
|
+
outcome: options.outcome ?? "passed",
|
|
23940
|
+
command: options.command,
|
|
23941
|
+
coverage: options.paths?.length ? "paths" : "project"
|
|
23942
|
+
});
|
|
23943
|
+
const id = `ev-rdd-${options.taskId}-${Date.now()}`;
|
|
23944
|
+
const evidence = {
|
|
23945
|
+
id,
|
|
23946
|
+
source: "cc rdd",
|
|
23947
|
+
type: "rdd",
|
|
23948
|
+
timestamp: new Date().toISOString(),
|
|
23949
|
+
relatedTask: options.taskId,
|
|
23950
|
+
confidence: 0.9,
|
|
23951
|
+
summary: `RDD ${receipt.phase} receipt captured`,
|
|
23952
|
+
data: { rddReceipt: receipt }
|
|
23953
|
+
};
|
|
23954
|
+
const directory = evidenceDir(options.projectRoot);
|
|
23955
|
+
await mkdir19(directory, { recursive: true });
|
|
23956
|
+
await writeFile19(receiptPath(options.projectRoot, id), JSON.stringify(evidence, null, 2), "utf-8");
|
|
23957
|
+
return { code: 0, data: { success: true, receiptId: id, receipt } };
|
|
23958
|
+
}
|
|
23959
|
+
if (options.subcommand === "verify") {
|
|
23960
|
+
if (!options.receiptId)
|
|
23961
|
+
throw new Error("Missing receipt id. Use --receipt <id>.");
|
|
23962
|
+
const { receipt } = await loadReceipt(options.projectRoot, options.receiptId);
|
|
23963
|
+
const verification = await verifyReceipt(options.projectRoot, receipt);
|
|
23964
|
+
return {
|
|
23965
|
+
code: verification.valid ? 0 : 1,
|
|
23966
|
+
data: { success: verification.valid, receiptId: options.receiptId, ...verification }
|
|
23967
|
+
};
|
|
23968
|
+
}
|
|
23969
|
+
if (options.subcommand === "status") {
|
|
23970
|
+
return { code: 0, data: { success: true, receipts: await rddStatus(options.projectRoot, options.taskId) } };
|
|
23971
|
+
}
|
|
23972
|
+
if (options.subcommand === "git-check") {
|
|
23973
|
+
const current = (await rddStatus(options.projectRoot))[0];
|
|
23974
|
+
if (!current)
|
|
23975
|
+
return { code: 1, data: { success: false, errors: ["No RDD receipt exists for the Git candidate."] } };
|
|
23976
|
+
return current.valid ? { code: 0, data: { success: true, receiptId: current.id } } : { code: 1, data: { success: false, receiptId: current.id, changedPaths: current.changedPaths } };
|
|
23977
|
+
}
|
|
23978
|
+
return { code: 0, data: { success: true, ...await installHooks(options.projectRoot) } };
|
|
23979
|
+
} catch (error) {
|
|
23980
|
+
return { code: 1, data: { success: false, errors: [error instanceof Error ? error.message : String(error)] } };
|
|
23981
|
+
}
|
|
23982
|
+
}
|
|
23983
|
+
var execFileAsync4;
|
|
23984
|
+
var init_rdd_command = __esm(() => {
|
|
23985
|
+
init_schemas();
|
|
23986
|
+
init_paths();
|
|
23987
|
+
init_rdd_receipt();
|
|
23988
|
+
execFileAsync4 = promisify4(execFile4);
|
|
23989
|
+
});
|
|
23990
|
+
|
|
23539
23991
|
// src/core/ccep/confirmation-gate.ts
|
|
23540
23992
|
function matchesRiskThreshold(severity, threshold) {
|
|
23541
23993
|
const order = { low: 0, medium: 1, high: 2 };
|
|
@@ -23841,11 +24293,11 @@ var init_output_validator = __esm(() => {
|
|
|
23841
24293
|
});
|
|
23842
24294
|
|
|
23843
24295
|
// src/core/ccep/command-parser.ts
|
|
23844
|
-
import { existsSync as
|
|
23845
|
-
import { join as
|
|
24296
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
24297
|
+
import { basename as basename7, join as join20 } from "node:path";
|
|
23846
24298
|
function readProjectId(projectRoot) {
|
|
23847
|
-
const packageJsonPath =
|
|
23848
|
-
if (
|
|
24299
|
+
const packageJsonPath = join20(projectRoot, "package.json");
|
|
24300
|
+
if (existsSync5(packageJsonPath)) {
|
|
23849
24301
|
try {
|
|
23850
24302
|
const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
23851
24303
|
if (pkg.name) {
|
|
@@ -23853,7 +24305,7 @@ function readProjectId(projectRoot) {
|
|
|
23853
24305
|
}
|
|
23854
24306
|
} catch {}
|
|
23855
24307
|
}
|
|
23856
|
-
return projectRoot
|
|
24308
|
+
return basename7(projectRoot) || "unknown";
|
|
23857
24309
|
}
|
|
23858
24310
|
function parseCommand(command, userRequest, projectRoot) {
|
|
23859
24311
|
const validatedCommand = WorkflowCommandSchema.parse(command);
|
|
@@ -23874,8 +24326,8 @@ function parseCommand(command, userRequest, projectRoot) {
|
|
|
23874
24326
|
maxVariance: "low"
|
|
23875
24327
|
}
|
|
23876
24328
|
};
|
|
23877
|
-
const packageJsonPath =
|
|
23878
|
-
if (
|
|
24329
|
+
const packageJsonPath = join20(projectRoot, "package.json");
|
|
24330
|
+
if (existsSync5(packageJsonPath)) {
|
|
23879
24331
|
try {
|
|
23880
24332
|
const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
23881
24333
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
@@ -23919,7 +24371,8 @@ var init_command_parser = __esm(() => {
|
|
|
23919
24371
|
prototype: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" },
|
|
23920
24372
|
handoff: { outputFormat: "plan", needConfirmation: false, riskThreshold: "low" },
|
|
23921
24373
|
clarify: { outputFormat: "plan", needConfirmation: false, riskThreshold: "low" },
|
|
23922
|
-
security: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "high" }
|
|
24374
|
+
security: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "high" },
|
|
24375
|
+
odd: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" }
|
|
23923
24376
|
};
|
|
23924
24377
|
});
|
|
23925
24378
|
|
|
@@ -23946,10 +24399,10 @@ var init_workflow_profile_loader = __esm(() => {
|
|
|
23946
24399
|
});
|
|
23947
24400
|
|
|
23948
24401
|
// src/core/evaluation/harness-catalog.ts
|
|
23949
|
-
import { createHash as
|
|
23950
|
-
import { existsSync as
|
|
23951
|
-
import { copyFile as copyFile2, mkdir as
|
|
23952
|
-
import { dirname as
|
|
24402
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
24403
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
|
|
24404
|
+
import { copyFile as copyFile2, mkdir as mkdir20, readFile as readFile30, readdir as readdir10, rm as rm4, writeFile as writeFile20 } from "node:fs/promises";
|
|
24405
|
+
import { dirname as dirname12, join as join21, resolve as resolve29 } from "node:path";
|
|
23953
24406
|
function catalogIds(catalog = DEFAULT_HARNESS_CATALOG) {
|
|
23954
24407
|
return catalog.components.map((c) => c.id);
|
|
23955
24408
|
}
|
|
@@ -23999,23 +24452,23 @@ function harnessFingerprint(disabled, contractVersion, ccepProfile = "feature",
|
|
|
23999
24452
|
contractVersion,
|
|
24000
24453
|
ccepProfile
|
|
24001
24454
|
};
|
|
24002
|
-
return
|
|
24455
|
+
return createHash5("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
|
|
24003
24456
|
}
|
|
24004
24457
|
async function loadHarnessCatalog(projectRoot) {
|
|
24005
|
-
const overridePath =
|
|
24458
|
+
const overridePath = resolve29(projectRoot, EVAL_DIR2, CATALOG_FILE);
|
|
24006
24459
|
try {
|
|
24007
|
-
const raw = await
|
|
24460
|
+
const raw = await readFile30(overridePath, "utf-8");
|
|
24008
24461
|
return HarnessCatalogSchema.parse($parse(raw));
|
|
24009
24462
|
} catch {
|
|
24010
24463
|
return DEFAULT_HARNESS_CATALOG;
|
|
24011
24464
|
}
|
|
24012
24465
|
}
|
|
24013
24466
|
function activeOverlayPath(projectRoot) {
|
|
24014
|
-
return
|
|
24467
|
+
return resolve29(projectRoot, EVAL_DIR2, ACTIVE_OVERLAY_FILE);
|
|
24015
24468
|
}
|
|
24016
24469
|
async function readActiveOverlay(projectRoot) {
|
|
24017
24470
|
try {
|
|
24018
|
-
const raw = await
|
|
24471
|
+
const raw = await readFile30(activeOverlayPath(projectRoot), "utf-8");
|
|
24019
24472
|
return HarnessOverlaySchema.parse($parse(raw));
|
|
24020
24473
|
} catch {
|
|
24021
24474
|
return null;
|
|
@@ -24024,7 +24477,7 @@ async function readActiveOverlay(projectRoot) {
|
|
|
24024
24477
|
function isProductGraphDisabled(projectRoot) {
|
|
24025
24478
|
try {
|
|
24026
24479
|
const path = activeOverlayPath(projectRoot);
|
|
24027
|
-
if (!
|
|
24480
|
+
if (!existsSync6(path))
|
|
24028
24481
|
return false;
|
|
24029
24482
|
const overlay = HarnessOverlaySchema.parse($parse(readFileSync4(path, "utf-8")));
|
|
24030
24483
|
return overlay.disableProductGraph === true || overlay.disabledComponents.includes("product_graph");
|
|
@@ -24069,55 +24522,55 @@ function applyCatalogToProfile(profile, disabled) {
|
|
|
24069
24522
|
return next;
|
|
24070
24523
|
}
|
|
24071
24524
|
async function restoreOverlayBackup(projectRoot) {
|
|
24072
|
-
const backupRoot =
|
|
24073
|
-
if (!
|
|
24525
|
+
const backupRoot = resolve29(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
|
|
24526
|
+
if (!existsSync6(backupRoot))
|
|
24074
24527
|
return;
|
|
24075
|
-
const configBackup =
|
|
24076
|
-
const configDest =
|
|
24077
|
-
if (
|
|
24078
|
-
await
|
|
24528
|
+
const configBackup = join21(backupRoot, "config.yml");
|
|
24529
|
+
const configDest = resolve29(projectRoot, ".codeconductor", "config.yml");
|
|
24530
|
+
if (existsSync6(configBackup)) {
|
|
24531
|
+
await mkdir20(dirname12(configDest), { recursive: true });
|
|
24079
24532
|
await copyFile2(configBackup, configDest);
|
|
24080
24533
|
}
|
|
24081
|
-
const workflowsBackup =
|
|
24082
|
-
const workflowsDest =
|
|
24083
|
-
if (
|
|
24084
|
-
await
|
|
24085
|
-
const files = await
|
|
24534
|
+
const workflowsBackup = join21(backupRoot, "workflows");
|
|
24535
|
+
const workflowsDest = resolve29(projectRoot, ".codeconductor", "workflows");
|
|
24536
|
+
if (existsSync6(workflowsBackup)) {
|
|
24537
|
+
await mkdir20(workflowsDest, { recursive: true });
|
|
24538
|
+
const files = await readdir10(workflowsBackup);
|
|
24086
24539
|
for (const file of files) {
|
|
24087
|
-
await copyFile2(
|
|
24540
|
+
await copyFile2(join21(workflowsBackup, file), join21(workflowsDest, file));
|
|
24088
24541
|
}
|
|
24089
24542
|
}
|
|
24090
|
-
const overlayBackup =
|
|
24543
|
+
const overlayBackup = join21(backupRoot, ACTIVE_OVERLAY_FILE);
|
|
24091
24544
|
const overlayDest = activeOverlayPath(projectRoot);
|
|
24092
|
-
if (
|
|
24545
|
+
if (existsSync6(overlayBackup)) {
|
|
24093
24546
|
await copyFile2(overlayBackup, overlayDest);
|
|
24094
|
-
} else if (
|
|
24547
|
+
} else if (existsSync6(overlayDest)) {
|
|
24095
24548
|
await rm4(overlayDest, { force: true });
|
|
24096
24549
|
}
|
|
24097
24550
|
}
|
|
24098
24551
|
async function snapshotForBackup(projectRoot) {
|
|
24099
|
-
const backupRoot =
|
|
24100
|
-
if (
|
|
24552
|
+
const backupRoot = resolve29(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
|
|
24553
|
+
if (existsSync6(backupRoot))
|
|
24101
24554
|
return;
|
|
24102
|
-
await
|
|
24103
|
-
const configPath =
|
|
24104
|
-
if (
|
|
24105
|
-
await copyFile2(configPath,
|
|
24106
|
-
}
|
|
24107
|
-
const workflowsDir =
|
|
24108
|
-
if (
|
|
24109
|
-
const dest =
|
|
24110
|
-
await
|
|
24111
|
-
const files = await
|
|
24555
|
+
await mkdir20(backupRoot, { recursive: true });
|
|
24556
|
+
const configPath = resolve29(projectRoot, ".codeconductor", "config.yml");
|
|
24557
|
+
if (existsSync6(configPath)) {
|
|
24558
|
+
await copyFile2(configPath, join21(backupRoot, "config.yml"));
|
|
24559
|
+
}
|
|
24560
|
+
const workflowsDir = resolve29(projectRoot, ".codeconductor", "workflows");
|
|
24561
|
+
if (existsSync6(workflowsDir)) {
|
|
24562
|
+
const dest = join21(backupRoot, "workflows");
|
|
24563
|
+
await mkdir20(dest, { recursive: true });
|
|
24564
|
+
const files = await readdir10(workflowsDir);
|
|
24112
24565
|
for (const file of files) {
|
|
24113
24566
|
if (file.endsWith(".yml")) {
|
|
24114
|
-
await copyFile2(
|
|
24567
|
+
await copyFile2(join21(workflowsDir, file), join21(dest, file));
|
|
24115
24568
|
}
|
|
24116
24569
|
}
|
|
24117
24570
|
}
|
|
24118
24571
|
const overlay = activeOverlayPath(projectRoot);
|
|
24119
|
-
if (
|
|
24120
|
-
await copyFile2(overlay,
|
|
24572
|
+
if (existsSync6(overlay)) {
|
|
24573
|
+
await copyFile2(overlay, join21(backupRoot, ACTIVE_OVERLAY_FILE));
|
|
24121
24574
|
}
|
|
24122
24575
|
}
|
|
24123
24576
|
async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
|
|
@@ -24126,13 +24579,13 @@ async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
|
|
|
24126
24579
|
await restoreOverlayBackup(projectRoot);
|
|
24127
24580
|
await snapshotForBackup(projectRoot);
|
|
24128
24581
|
}
|
|
24129
|
-
const workflowsDir =
|
|
24130
|
-
await
|
|
24131
|
-
await
|
|
24582
|
+
const workflowsDir = resolve29(projectRoot, ".codeconductor", "workflows");
|
|
24583
|
+
await mkdir20(workflowsDir, { recursive: true });
|
|
24584
|
+
await mkdir20(resolve29(projectRoot, EVAL_DIR2), { recursive: true });
|
|
24132
24585
|
for (const command of WORKFLOW_OVERLAY_COMMANDS) {
|
|
24133
24586
|
const base = loadWorkflowProfile(command);
|
|
24134
24587
|
const next = applyCatalogToProfile(base, disabled);
|
|
24135
|
-
await
|
|
24588
|
+
await writeFile20(join21(workflowsDir, `${command}.yml`), $stringify(next), "utf-8");
|
|
24136
24589
|
}
|
|
24137
24590
|
const needsConfig = disabled.includes("compile_loop") || disabled.includes("council");
|
|
24138
24591
|
if (needsConfig) {
|
|
@@ -24160,7 +24613,7 @@ async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
|
|
|
24160
24613
|
contractVersion: options.contractVersion,
|
|
24161
24614
|
ccepProfile: options.ccepProfile ?? "feature"
|
|
24162
24615
|
});
|
|
24163
|
-
await
|
|
24616
|
+
await writeFile20(activeOverlayPath(projectRoot), $stringify(overlay), "utf-8");
|
|
24164
24617
|
return ok(overlay);
|
|
24165
24618
|
} catch (e) {
|
|
24166
24619
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -24271,9 +24724,9 @@ var init_harness_catalog = __esm(() => {
|
|
|
24271
24724
|
});
|
|
24272
24725
|
|
|
24273
24726
|
// src/core/openspec/openspec-state.ts
|
|
24274
|
-
import { createHash as
|
|
24275
|
-
import { mkdir as
|
|
24276
|
-
import { resolve as
|
|
24727
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
24728
|
+
import { mkdir as mkdir21, readFile as readFile31, rename as rename4, writeFile as writeFile21 } from "node:fs/promises";
|
|
24729
|
+
import { resolve as resolve30 } from "node:path";
|
|
24277
24730
|
function canTransition(from, to) {
|
|
24278
24731
|
return ALLOWED_TRANSITIONS[from]?.includes(to) ?? false;
|
|
24279
24732
|
}
|
|
@@ -24294,13 +24747,13 @@ function applyBacklogTransition(content, itemId, from, to, progress) {
|
|
|
24294
24747
|
}
|
|
24295
24748
|
async function writeFileAtomic(path, content) {
|
|
24296
24749
|
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
24297
|
-
await
|
|
24298
|
-
await
|
|
24750
|
+
await writeFile21(tmp, content, "utf-8");
|
|
24751
|
+
await rename4(tmp, path);
|
|
24299
24752
|
}
|
|
24300
24753
|
async function loadOpenspecState(projectRoot) {
|
|
24301
24754
|
try {
|
|
24302
|
-
const filePath =
|
|
24303
|
-
const content = await
|
|
24755
|
+
const filePath = resolve30(projectRoot, STATE_FILE);
|
|
24756
|
+
const content = await readFile31(filePath, "utf-8");
|
|
24304
24757
|
const data = JSON.parse(content);
|
|
24305
24758
|
return ok(OpenspecStateSchema.parse(data));
|
|
24306
24759
|
} catch (e) {
|
|
@@ -24317,10 +24770,10 @@ async function loadOpenspecState(projectRoot) {
|
|
|
24317
24770
|
}
|
|
24318
24771
|
async function writeOpenspecState(projectRoot, state) {
|
|
24319
24772
|
try {
|
|
24320
|
-
const dir =
|
|
24321
|
-
await
|
|
24773
|
+
const dir = resolve30(projectRoot, ".codeconductor");
|
|
24774
|
+
await mkdir21(dir, { recursive: true });
|
|
24322
24775
|
const validated = OpenspecStateSchema.parse(state);
|
|
24323
|
-
await writeFileAtomic(
|
|
24776
|
+
await writeFileAtomic(resolve30(dir, "openspec-state.json"), JSON.stringify(validated, null, 2));
|
|
24324
24777
|
return ok(undefined);
|
|
24325
24778
|
} catch (e) {
|
|
24326
24779
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -24342,7 +24795,7 @@ function getNextTaskCard(state) {
|
|
|
24342
24795
|
return null;
|
|
24343
24796
|
}
|
|
24344
24797
|
function hashContent(content) {
|
|
24345
|
-
return
|
|
24798
|
+
return createHash6("sha256").update(content).digest("hex").slice(0, 16);
|
|
24346
24799
|
}
|
|
24347
24800
|
function serializeItemSnapshot(item) {
|
|
24348
24801
|
return JSON.stringify({
|
|
@@ -24424,19 +24877,19 @@ var init_openspec_state = __esm(() => {
|
|
|
24424
24877
|
});
|
|
24425
24878
|
|
|
24426
24879
|
// src/core/ccep/context-resolver.ts
|
|
24427
|
-
import { existsSync as
|
|
24428
|
-
import { readFile as
|
|
24429
|
-
import { join as
|
|
24880
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
24881
|
+
import { readFile as readFile32 } from "node:fs/promises";
|
|
24882
|
+
import { join as join22 } from "node:path";
|
|
24430
24883
|
async function loadProductKnowledge(projectRoot) {
|
|
24431
24884
|
if (isProductGraphDisabled(projectRoot)) {
|
|
24432
24885
|
return {};
|
|
24433
24886
|
}
|
|
24434
24887
|
const graphPath = productGraphPath(projectRoot);
|
|
24435
|
-
if (!
|
|
24888
|
+
if (!existsSync7(graphPath)) {
|
|
24436
24889
|
return {};
|
|
24437
24890
|
}
|
|
24438
24891
|
try {
|
|
24439
|
-
const raw = await
|
|
24892
|
+
const raw = await readFile32(graphPath, "utf-8");
|
|
24440
24893
|
const graph = ProductGraphSchema.parse(JSON.parse(raw));
|
|
24441
24894
|
return {
|
|
24442
24895
|
productName: graph.productName,
|
|
@@ -24452,18 +24905,18 @@ async function loadProductKnowledge(projectRoot) {
|
|
|
24452
24905
|
}
|
|
24453
24906
|
async function resolveAstSource(projectRoot) {
|
|
24454
24907
|
if (isProductGraphDisabled(projectRoot)) {
|
|
24455
|
-
const graphifyPath2 =
|
|
24456
|
-
if (
|
|
24908
|
+
const graphifyPath2 = join22(projectRoot, "graphify-out", "graph.json");
|
|
24909
|
+
if (existsSync7(graphifyPath2)) {
|
|
24457
24910
|
return { source: "graphify", confidence: "medium" };
|
|
24458
24911
|
}
|
|
24459
24912
|
return { source: "manual", confidence: "low" };
|
|
24460
24913
|
}
|
|
24461
24914
|
const graphPath = productGraphPath(projectRoot);
|
|
24462
|
-
if (
|
|
24915
|
+
if (existsSync7(graphPath)) {
|
|
24463
24916
|
return { source: "product-graph", confidence: "high" };
|
|
24464
24917
|
}
|
|
24465
|
-
const graphifyPath =
|
|
24466
|
-
if (
|
|
24918
|
+
const graphifyPath = join22(projectRoot, "graphify-out", "graph.json");
|
|
24919
|
+
if (existsSync7(graphifyPath)) {
|
|
24467
24920
|
return { source: "graphify", confidence: "medium" };
|
|
24468
24921
|
}
|
|
24469
24922
|
try {
|
|
@@ -24704,6 +25157,12 @@ function councilConsensus(verdicts, config = DEFAULT_CONFIG2) {
|
|
|
24704
25157
|
individualVerdicts: verdicts
|
|
24705
25158
|
};
|
|
24706
25159
|
}
|
|
25160
|
+
if (config.candidateHash !== undefined) {
|
|
25161
|
+
const mismatched = verdicts.filter((verdict) => verdict.candidateHash !== config.candidateHash);
|
|
25162
|
+
if (mismatched.length > 0) {
|
|
25163
|
+
return escalated(verdicts, `Candidate receipt mismatch for ${mismatched.length} ballot(s) — escalated for human review.`, counts);
|
|
25164
|
+
}
|
|
25165
|
+
}
|
|
24707
25166
|
const critical = allFindings.filter((f) => f.severity === "critical");
|
|
24708
25167
|
if (critical.length > 0 && policy === "reject") {
|
|
24709
25168
|
return {
|
|
@@ -25265,12 +25724,26 @@ async function ccepCommand(options) {
|
|
|
25265
25724
|
}
|
|
25266
25725
|
mergedConfig = { ...mergedConfig, ...parsedConfig.data };
|
|
25267
25726
|
}
|
|
25727
|
+
let currentCandidateHash;
|
|
25728
|
+
if (mergedConfig?.candidateHash !== undefined) {
|
|
25729
|
+
const { captureReceipt: captureReceipt2, collectReceiptPaths: collectReceiptPaths2 } = await Promise.resolve().then(() => (init_rdd_receipt(), exports_rdd_receipt));
|
|
25730
|
+
const receipt = await captureReceipt2(projectRoot, {
|
|
25731
|
+
taskId: "council",
|
|
25732
|
+
phase: "review",
|
|
25733
|
+
paths: await collectReceiptPaths2(projectRoot),
|
|
25734
|
+
outcome: "passed",
|
|
25735
|
+
coverage: "project"
|
|
25736
|
+
});
|
|
25737
|
+
currentCandidateHash = receipt.manifestHash;
|
|
25738
|
+
mergedConfig = { ...mergedConfig, candidateHash: currentCandidateHash };
|
|
25739
|
+
}
|
|
25268
25740
|
const verdict = councilConsensus(parsedBallots.verdicts, mergedConfig);
|
|
25269
25741
|
return {
|
|
25270
25742
|
code: consensusExitCode(verdict.status),
|
|
25271
25743
|
data: {
|
|
25272
25744
|
success: verdict.status === "APPROVED",
|
|
25273
25745
|
command: "ccep consensus",
|
|
25746
|
+
candidateHash: currentCandidateHash,
|
|
25274
25747
|
verdict
|
|
25275
25748
|
}
|
|
25276
25749
|
};
|
|
@@ -25507,9 +25980,9 @@ var init_backlog_validator = __esm(() => {
|
|
|
25507
25980
|
});
|
|
25508
25981
|
|
|
25509
25982
|
// src/core/openspec/backlog-scanner.ts
|
|
25510
|
-
import { execFileSync
|
|
25511
|
-
import { readFile as
|
|
25512
|
-
import { resolve as
|
|
25983
|
+
import { execFileSync } from "node:child_process";
|
|
25984
|
+
import { readFile as readFile33 } from "node:fs/promises";
|
|
25985
|
+
import { resolve as resolve31 } from "node:path";
|
|
25513
25986
|
function itemSnapshot(items) {
|
|
25514
25987
|
const snap = {};
|
|
25515
25988
|
for (const item of items) {
|
|
@@ -25519,12 +25992,12 @@ function itemSnapshot(items) {
|
|
|
25519
25992
|
}
|
|
25520
25993
|
async function scanBacklog(projectRoot, previousSnapshot = {}) {
|
|
25521
25994
|
try {
|
|
25522
|
-
const filePath =
|
|
25523
|
-
const content = await
|
|
25995
|
+
const filePath = resolve31(projectRoot, BACKLOG_FILENAME);
|
|
25996
|
+
const content = await readFile33(filePath, "utf-8");
|
|
25524
25997
|
const hash = hashContent(content);
|
|
25525
25998
|
let fileChanged = true;
|
|
25526
25999
|
try {
|
|
25527
|
-
const diff =
|
|
26000
|
+
const diff = execFileSync("git", ["diff", "--name-only", "--", BACKLOG_FILENAME], {
|
|
25528
26001
|
cwd: projectRoot,
|
|
25529
26002
|
encoding: "utf-8",
|
|
25530
26003
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -25757,8 +26230,8 @@ var init_backlog_planner = __esm(() => {
|
|
|
25757
26230
|
});
|
|
25758
26231
|
|
|
25759
26232
|
// src/core/openspec/openspec-generator.ts
|
|
25760
|
-
import { mkdir as
|
|
25761
|
-
import { basename as
|
|
26233
|
+
import { mkdir as mkdir22, rename as rename5, writeFile as writeFile22 } from "node:fs/promises";
|
|
26234
|
+
import { basename as basename8, resolve as resolve32 } from "node:path";
|
|
25762
26235
|
function pad3(n) {
|
|
25763
26236
|
return String(n).padStart(3, "0");
|
|
25764
26237
|
}
|
|
@@ -25823,7 +26296,7 @@ The system MUST ${criterion}.
|
|
|
25823
26296
|
|
|
25824
26297
|
#### Scenario: SC-${n} ${criterion}
|
|
25825
26298
|
|
|
25826
|
-
- GIVEN the
|
|
26299
|
+
- GIVEN the relevant scoped behavior
|
|
25827
26300
|
- WHEN this change is applied
|
|
25828
26301
|
- THEN ${criterion}
|
|
25829
26302
|
`;
|
|
@@ -25837,8 +26310,8 @@ ${blocks.join(`
|
|
|
25837
26310
|
`;
|
|
25838
26311
|
}
|
|
25839
26312
|
async function writeTasksMarkdown(projectRoot, changePath, cards, options = {}) {
|
|
25840
|
-
await
|
|
25841
|
-
await
|
|
26313
|
+
await mkdir22(resolve32(projectRoot, changePath), { recursive: true });
|
|
26314
|
+
await writeFile22(resolve32(projectRoot, changePath, "tasks.md"), tasksContent(cards, options), "utf-8");
|
|
25842
26315
|
}
|
|
25843
26316
|
function tasksContent(cards, options = {}) {
|
|
25844
26317
|
const criteria = options.acceptanceCriteria ?? [];
|
|
@@ -25883,19 +26356,19 @@ function tasksContent(cards, options = {}) {
|
|
|
25883
26356
|
}
|
|
25884
26357
|
async function generateOpenspecChange(projectRoot, item, taskCards, options = {}) {
|
|
25885
26358
|
const slug = buildChangeSlug(item);
|
|
25886
|
-
const changeDir =
|
|
25887
|
-
await
|
|
26359
|
+
const changeDir = resolve32(projectRoot, "openspec", "changes", slug);
|
|
26360
|
+
await mkdir22(resolve32(changeDir, "specs"), { recursive: true });
|
|
25888
26361
|
const taskOpts = {
|
|
25889
26362
|
tddRequired: options.tddRequired,
|
|
25890
26363
|
acceptanceCriteria: options.acceptanceCriteria ?? item.acceptanceCriteria
|
|
25891
26364
|
};
|
|
25892
|
-
await
|
|
25893
|
-
await
|
|
25894
|
-
await
|
|
26365
|
+
await writeFile22(resolve32(changeDir, "proposal.md"), proposalContent(item), "utf-8");
|
|
26366
|
+
await writeFile22(resolve32(changeDir, "design.md"), designContent(item), "utf-8");
|
|
26367
|
+
await writeFile22(resolve32(changeDir, "tasks.md"), tasksContent(taskCards, taskOpts), "utf-8");
|
|
25895
26368
|
const capability = capabilitySlug(item.title);
|
|
25896
|
-
await
|
|
25897
|
-
await
|
|
25898
|
-
await
|
|
26369
|
+
await mkdir22(resolve32(changeDir, "specs", capability), { recursive: true });
|
|
26370
|
+
await writeFile22(resolve32(changeDir, "specs", capability, "spec.md"), specDelta(item), "utf-8");
|
|
26371
|
+
await writeFile22(resolve32(changeDir, "change.yaml"), `version: 1
|
|
25899
26372
|
backlogId: ${item.id}
|
|
25900
26373
|
profile: standard
|
|
25901
26374
|
capabilities:
|
|
@@ -25904,25 +26377,25 @@ capabilities:
|
|
|
25904
26377
|
return `openspec/changes/${slug}`;
|
|
25905
26378
|
}
|
|
25906
26379
|
async function archiveChangeFolder(projectRoot, changePath) {
|
|
25907
|
-
const src =
|
|
25908
|
-
const slug =
|
|
25909
|
-
const destDir =
|
|
25910
|
-
await
|
|
25911
|
-
const dest =
|
|
25912
|
-
await
|
|
26380
|
+
const src = resolve32(projectRoot, changePath);
|
|
26381
|
+
const slug = basename8(changePath);
|
|
26382
|
+
const destDir = resolve32(projectRoot, "openspec", "changes", "archive");
|
|
26383
|
+
await mkdir22(destDir, { recursive: true });
|
|
26384
|
+
const dest = resolve32(destDir, slug);
|
|
26385
|
+
await rename5(src, dest);
|
|
25913
26386
|
return `openspec/changes/archive/${slug}`;
|
|
25914
26387
|
}
|
|
25915
26388
|
async function ensureOpenspecConfig(projectRoot) {
|
|
25916
|
-
const configPath =
|
|
26389
|
+
const configPath = resolve32(projectRoot, "openspec", "config.yaml");
|
|
25917
26390
|
try {
|
|
25918
26391
|
const { access: access7 } = await import("node:fs/promises");
|
|
25919
26392
|
await access7(configPath);
|
|
25920
26393
|
} catch {
|
|
25921
|
-
await
|
|
26394
|
+
await mkdir22(resolve32(projectRoot, "openspec"), { recursive: true });
|
|
25922
26395
|
const stub = `# OpenSpec project config (CodeConductor native compatible)
|
|
25923
26396
|
schema: spec-driven
|
|
25924
26397
|
`;
|
|
25925
|
-
await
|
|
26398
|
+
await writeFile22(configPath, stub, "utf-8");
|
|
25926
26399
|
}
|
|
25927
26400
|
}
|
|
25928
26401
|
var init_openspec_generator = __esm(() => {
|
|
@@ -25930,8 +26403,8 @@ var init_openspec_generator = __esm(() => {
|
|
|
25930
26403
|
});
|
|
25931
26404
|
|
|
25932
26405
|
// src/core/openspec/spec-quality.ts
|
|
25933
|
-
import { readdir as
|
|
25934
|
-
import { join as
|
|
26406
|
+
import { readdir as readdir11, readFile as readFile34 } from "node:fs/promises";
|
|
26407
|
+
import { join as join23, relative as relative10, resolve as resolve33 } from "node:path";
|
|
25935
26408
|
function uniqueIds(matches) {
|
|
25936
26409
|
return [...new Set(matches ?? [])];
|
|
25937
26410
|
}
|
|
@@ -26048,12 +26521,12 @@ async function walkMarkdown(dir) {
|
|
|
26048
26521
|
const out = [];
|
|
26049
26522
|
let entries;
|
|
26050
26523
|
try {
|
|
26051
|
-
entries = await
|
|
26524
|
+
entries = await readdir11(dir, { withFileTypes: true });
|
|
26052
26525
|
} catch {
|
|
26053
26526
|
return out;
|
|
26054
26527
|
}
|
|
26055
26528
|
for (const entry of entries) {
|
|
26056
|
-
const full =
|
|
26529
|
+
const full = join23(dir, entry.name);
|
|
26057
26530
|
if (entry.isDirectory()) {
|
|
26058
26531
|
out.push(...await walkMarkdown(full));
|
|
26059
26532
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -26063,8 +26536,8 @@ async function walkMarkdown(dir) {
|
|
|
26063
26536
|
return out;
|
|
26064
26537
|
}
|
|
26065
26538
|
async function assessChangeFolder(projectRoot, changePath) {
|
|
26066
|
-
const root =
|
|
26067
|
-
const changeRoot =
|
|
26539
|
+
const root = resolve33(projectRoot);
|
|
26540
|
+
const changeRoot = resolve33(root, changePath);
|
|
26068
26541
|
if (!changeRoot.startsWith(root)) {
|
|
26069
26542
|
return {
|
|
26070
26543
|
valid: false,
|
|
@@ -26082,22 +26555,22 @@ async function assessChangeFolder(projectRoot, changePath) {
|
|
|
26082
26555
|
successCriteria: []
|
|
26083
26556
|
};
|
|
26084
26557
|
}
|
|
26085
|
-
const specDir =
|
|
26558
|
+
const specDir = join23(changeRoot, "specs");
|
|
26086
26559
|
const specFiles = await walkMarkdown(specDir);
|
|
26087
|
-
const extra = ["proposal.md", "design.md"].map((name) =>
|
|
26560
|
+
const extra = ["proposal.md", "design.md"].map((name) => join23(changeRoot, name));
|
|
26088
26561
|
const reports = [];
|
|
26089
26562
|
for (const file of specFiles) {
|
|
26090
|
-
const content = await
|
|
26091
|
-
reports.push(assessSpecMarkdown(content,
|
|
26563
|
+
const content = await readFile34(file, "utf-8");
|
|
26564
|
+
reports.push(assessSpecMarkdown(content, relative10(root, file).replace(/\\/g, "/")));
|
|
26092
26565
|
}
|
|
26093
26566
|
for (const file of extra) {
|
|
26094
26567
|
let content;
|
|
26095
26568
|
try {
|
|
26096
|
-
content = await
|
|
26569
|
+
content = await readFile34(file, "utf-8");
|
|
26097
26570
|
} catch {
|
|
26098
26571
|
continue;
|
|
26099
26572
|
}
|
|
26100
|
-
const rel =
|
|
26573
|
+
const rel = relative10(root, file).replace(/\\/g, "/");
|
|
26101
26574
|
if (PLACEHOLDER.test(content)) {
|
|
26102
26575
|
reports.push({
|
|
26103
26576
|
valid: false,
|
|
@@ -26141,15 +26614,15 @@ var init_spec_quality = __esm(() => {
|
|
|
26141
26614
|
FR_ID = /\bFR-\d{3}\b/g;
|
|
26142
26615
|
SC_ID = /\bSC-\d{3}\b/g;
|
|
26143
26616
|
US_AC = /\b(?:US|AC)#?\d+\b/g;
|
|
26144
|
-
GWT = /\bGIVEN\b[\s\S]
|
|
26617
|
+
GWT = /\bGIVEN\b[\s\S]*?\bWHEN\b[\s\S]*?\bTHEN\b/i;
|
|
26145
26618
|
NEEDS_CLARIFICATION = /\[NEEDS CLARIFICATION:[^\]]*\]/gi;
|
|
26146
26619
|
PLACEHOLDER = /\(To be completed/i;
|
|
26147
26620
|
HOW_LEAK = /\b(TypeScript|PostgreSQL|Django|React|Spring Boot|package\.json|implements class)\b/i;
|
|
26148
26621
|
});
|
|
26149
26622
|
|
|
26150
26623
|
// src/core/openspec/spec-analyzer.ts
|
|
26151
|
-
import { readFile as
|
|
26152
|
-
import { join as
|
|
26624
|
+
import { readFile as readFile35, readdir as readdir12 } from "node:fs/promises";
|
|
26625
|
+
import { join as join24, resolve as resolve34 } from "node:path";
|
|
26153
26626
|
function unique(ids) {
|
|
26154
26627
|
return [...new Set(ids)];
|
|
26155
26628
|
}
|
|
@@ -26176,17 +26649,17 @@ function testSection(tasksMarkdown) {
|
|
|
26176
26649
|
async function readDeltaSpecs(specDir) {
|
|
26177
26650
|
let entries;
|
|
26178
26651
|
try {
|
|
26179
|
-
entries = await
|
|
26652
|
+
entries = await readdir12(specDir, { withFileTypes: true });
|
|
26180
26653
|
} catch {
|
|
26181
26654
|
return "";
|
|
26182
26655
|
}
|
|
26183
26656
|
const contents = [];
|
|
26184
26657
|
for (const entry of entries) {
|
|
26185
|
-
const path =
|
|
26658
|
+
const path = join24(specDir, entry.name);
|
|
26186
26659
|
if (entry.isDirectory()) {
|
|
26187
26660
|
contents.push(await readDeltaSpecs(path));
|
|
26188
26661
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
26189
|
-
contents.push(await
|
|
26662
|
+
contents.push(await readFile35(path, "utf-8"));
|
|
26190
26663
|
}
|
|
26191
26664
|
}
|
|
26192
26665
|
return contents.filter(Boolean).join(`
|
|
@@ -26264,17 +26737,17 @@ function analyzeSpecArtifacts(input) {
|
|
|
26264
26737
|
};
|
|
26265
26738
|
}
|
|
26266
26739
|
async function analyzeChangeFolder(projectRoot, changePath, options) {
|
|
26267
|
-
const changeRoot =
|
|
26268
|
-
const specMarkdown = await readDeltaSpecs(
|
|
26740
|
+
const changeRoot = resolve34(projectRoot, changePath);
|
|
26741
|
+
const specMarkdown = await readDeltaSpecs(join24(changeRoot, "specs"));
|
|
26269
26742
|
let tasksMarkdown = "";
|
|
26270
26743
|
try {
|
|
26271
|
-
tasksMarkdown = await
|
|
26744
|
+
tasksMarkdown = await readFile35(join24(changeRoot, "tasks.md"), "utf-8");
|
|
26272
26745
|
} catch {
|
|
26273
26746
|
tasksMarkdown = "";
|
|
26274
26747
|
}
|
|
26275
26748
|
let designMarkdown;
|
|
26276
26749
|
try {
|
|
26277
|
-
designMarkdown = await
|
|
26750
|
+
designMarkdown = await readFile35(join24(changeRoot, "design.md"), "utf-8");
|
|
26278
26751
|
} catch {
|
|
26279
26752
|
designMarkdown = undefined;
|
|
26280
26753
|
}
|
|
@@ -26293,18 +26766,18 @@ var init_spec_analyzer = __esm(() => {
|
|
|
26293
26766
|
});
|
|
26294
26767
|
|
|
26295
26768
|
// src/core/openspec/spec-sync.ts
|
|
26296
|
-
import { mkdir as
|
|
26297
|
-
import { join as
|
|
26769
|
+
import { mkdir as mkdir23, readFile as readFile36, readdir as readdir13 } from "node:fs/promises";
|
|
26770
|
+
import { join as join25, relative as relative11, resolve as resolve35 } from "node:path";
|
|
26298
26771
|
async function markdownFiles(dir) {
|
|
26299
26772
|
let entries;
|
|
26300
26773
|
try {
|
|
26301
|
-
entries = await
|
|
26774
|
+
entries = await readdir13(dir, { withFileTypes: true });
|
|
26302
26775
|
} catch {
|
|
26303
26776
|
return [];
|
|
26304
26777
|
}
|
|
26305
26778
|
const files = [];
|
|
26306
26779
|
for (const entry of entries) {
|
|
26307
|
-
const path =
|
|
26780
|
+
const path = join25(dir, entry.name);
|
|
26308
26781
|
if (entry.isDirectory())
|
|
26309
26782
|
files.push(...await markdownFiles(path));
|
|
26310
26783
|
else if (entry.isFile() && entry.name.endsWith(".md"))
|
|
@@ -26380,9 +26853,9 @@ ${delta.markdown}`;
|
|
|
26380
26853
|
` };
|
|
26381
26854
|
}
|
|
26382
26855
|
async function syncChangeSpecs(projectRoot, changePath) {
|
|
26383
|
-
const root =
|
|
26384
|
-
const changeRoot =
|
|
26385
|
-
const changeSpecs =
|
|
26856
|
+
const root = resolve35(projectRoot);
|
|
26857
|
+
const changeRoot = resolve35(root, changePath);
|
|
26858
|
+
const changeSpecs = join25(changeRoot, "specs");
|
|
26386
26859
|
if (changeRoot !== root && !changeRoot.startsWith(`${root}/`)) {
|
|
26387
26860
|
return { success: false, syncedPaths: [], errors: ["Change path escapes project root"] };
|
|
26388
26861
|
}
|
|
@@ -26392,34 +26865,34 @@ async function syncChangeSpecs(projectRoot, changePath) {
|
|
|
26392
26865
|
}
|
|
26393
26866
|
const writes = [];
|
|
26394
26867
|
for (const file of files) {
|
|
26395
|
-
const delta = await
|
|
26868
|
+
const delta = await readFile36(file, "utf-8");
|
|
26396
26869
|
const requirements = deltaRequirements(delta);
|
|
26397
26870
|
if (requirements.length === 0) {
|
|
26398
26871
|
return {
|
|
26399
26872
|
success: false,
|
|
26400
26873
|
syncedPaths: [],
|
|
26401
|
-
errors: [`${
|
|
26874
|
+
errors: [`${relative11(root, file)}: no delta requirements found`]
|
|
26402
26875
|
};
|
|
26403
26876
|
}
|
|
26404
|
-
const relativeSpec =
|
|
26405
|
-
const target =
|
|
26877
|
+
const relativeSpec = relative11(changeSpecs, file);
|
|
26878
|
+
const target = join25(root, "openspec", "specs", relativeSpec);
|
|
26406
26879
|
let existing = `# ${relativeSpec.replace(/\\/g, "/")}
|
|
26407
26880
|
`;
|
|
26408
26881
|
try {
|
|
26409
|
-
existing = await
|
|
26882
|
+
existing = await readFile36(target, "utf-8");
|
|
26410
26883
|
} catch {}
|
|
26411
|
-
const merged = upsertRequirements(existing, requirements,
|
|
26884
|
+
const merged = upsertRequirements(existing, requirements, relative11(root, file));
|
|
26412
26885
|
if (!merged.ok)
|
|
26413
26886
|
return { success: false, syncedPaths: [], errors: [merged.error] };
|
|
26414
26887
|
writes.push({ path: target, content: merged.content });
|
|
26415
26888
|
}
|
|
26416
26889
|
for (const write of writes) {
|
|
26417
|
-
await
|
|
26890
|
+
await mkdir23(resolve35(write.path, ".."), { recursive: true });
|
|
26418
26891
|
await writeFileAtomic(write.path, write.content);
|
|
26419
26892
|
}
|
|
26420
26893
|
return {
|
|
26421
26894
|
success: true,
|
|
26422
|
-
syncedPaths: writes.map((write) =>
|
|
26895
|
+
syncedPaths: writes.map((write) => relative11(root, write.path).replace(/\\/g, "/")),
|
|
26423
26896
|
errors: []
|
|
26424
26897
|
};
|
|
26425
26898
|
}
|
|
@@ -26435,8 +26908,8 @@ var exports_openspec_command = {};
|
|
|
26435
26908
|
__export(exports_openspec_command, {
|
|
26436
26909
|
openspecCommand: () => openspecCommand
|
|
26437
26910
|
});
|
|
26438
|
-
import { readFile as
|
|
26439
|
-
import { resolve as
|
|
26911
|
+
import { readFile as readFile37 } from "node:fs/promises";
|
|
26912
|
+
import { resolve as resolve36 } from "node:path";
|
|
26440
26913
|
async function openspecCommand(options) {
|
|
26441
26914
|
const { subcommand, itemId, projectRoot, output, reason } = options;
|
|
26442
26915
|
switch (subcommand) {
|
|
@@ -26458,6 +26931,8 @@ async function openspecCommand(options) {
|
|
|
26458
26931
|
return handleDone(projectRoot, itemId);
|
|
26459
26932
|
case "block":
|
|
26460
26933
|
return handleBlock(projectRoot, itemId, reason);
|
|
26934
|
+
case "unblock":
|
|
26935
|
+
return handleUnblock(projectRoot, itemId);
|
|
26461
26936
|
case "archive":
|
|
26462
26937
|
return handleArchive(projectRoot, itemId);
|
|
26463
26938
|
default:
|
|
@@ -26480,7 +26955,7 @@ function fail(command, errors3, extra = {}) {
|
|
|
26480
26955
|
};
|
|
26481
26956
|
}
|
|
26482
26957
|
async function persistBacklog(projectRoot, content) {
|
|
26483
|
-
await writeFileAtomic(
|
|
26958
|
+
await writeFileAtomic(resolve36(projectRoot, BACKLOG_FILENAME), content);
|
|
26484
26959
|
}
|
|
26485
26960
|
async function persistState(projectRoot, state) {
|
|
26486
26961
|
const written = await writeOpenspecState(projectRoot, state);
|
|
@@ -26490,7 +26965,7 @@ async function persistState(projectRoot, state) {
|
|
|
26490
26965
|
return { ok: true };
|
|
26491
26966
|
}
|
|
26492
26967
|
async function readBacklogMarkdown(projectRoot) {
|
|
26493
|
-
return
|
|
26968
|
+
return readFile37(resolve36(projectRoot, BACKLOG_FILENAME), "utf-8");
|
|
26494
26969
|
}
|
|
26495
26970
|
async function transitionItem(projectRoot, item, to, progress) {
|
|
26496
26971
|
const content = await readBacklogMarkdown(projectRoot);
|
|
@@ -26688,17 +27163,18 @@ async function handleAnalyze(projectRoot, itemId) {
|
|
|
26688
27163
|
const policyParts = [];
|
|
26689
27164
|
for (const name of ["AGENTS.md", "BACKLOG.md"]) {
|
|
26690
27165
|
try {
|
|
26691
|
-
policyParts.push(await
|
|
27166
|
+
policyParts.push(await readFile37(resolve36(projectRoot, name), "utf-8"));
|
|
26692
27167
|
} catch {}
|
|
26693
27168
|
}
|
|
26694
27169
|
const cards = itemCards(stateResult.data, targetId);
|
|
26695
27170
|
const tddCards = cards.filter((c) => c.phase === "test" || c.phase === "implement");
|
|
26696
27171
|
let hasTddEvidence;
|
|
26697
27172
|
if (tddCards.length > 0) {
|
|
26698
|
-
hasTddEvidence =
|
|
27173
|
+
hasTddEvidence = true;
|
|
26699
27174
|
for (const card of tddCards) {
|
|
26700
|
-
|
|
26701
|
-
|
|
27175
|
+
const expectedPhase = card.phase === "test" ? "red" : "green";
|
|
27176
|
+
if (!await hasTddRunnerEvidence(projectRoot, card.id, expectedPhase)) {
|
|
27177
|
+
hasTddEvidence = false;
|
|
26702
27178
|
break;
|
|
26703
27179
|
}
|
|
26704
27180
|
}
|
|
@@ -26883,10 +27359,11 @@ async function handleDone(projectRoot, cardId) {
|
|
|
26883
27359
|
const backlog = await loadBacklog(projectRoot);
|
|
26884
27360
|
const tddRequired = backlog.success ? backlog.data.global.tddRequired : false;
|
|
26885
27361
|
if (tddRequired && (card.phase === "test" || card.phase === "implement")) {
|
|
26886
|
-
const
|
|
27362
|
+
const expectedPhase = card.phase === "test" ? "red" : "green";
|
|
27363
|
+
const evidenced = await hasTddRunnerEvidence(projectRoot, cardId, expectedPhase);
|
|
26887
27364
|
if (!evidenced) {
|
|
26888
27365
|
return fail(command, [
|
|
26889
|
-
`Card ${cardId} (${card.phase}) requires verification-runner TDD evidence. Run captureTddSuiteEvidence before openspec done.`
|
|
27366
|
+
`Card ${cardId} (${card.phase}) requires current ${expectedPhase.toUpperCase()} verification-runner TDD evidence. Run captureTddSuiteEvidence before openspec done.`
|
|
26890
27367
|
]);
|
|
26891
27368
|
}
|
|
26892
27369
|
}
|
|
@@ -26961,6 +27438,57 @@ async function handleBlock(projectRoot, cardId, reason) {
|
|
|
26961
27438
|
}
|
|
26962
27439
|
};
|
|
26963
27440
|
}
|
|
27441
|
+
async function handleUnblock(projectRoot, cardId) {
|
|
27442
|
+
const command = "openspec unblock";
|
|
27443
|
+
if (!cardId) {
|
|
27444
|
+
return fail(command, ["Missing card id. Usage: openspec unblock <cardId>"]);
|
|
27445
|
+
}
|
|
27446
|
+
const loaded = await loadStateOrFail(projectRoot, command);
|
|
27447
|
+
if (!loaded.ok)
|
|
27448
|
+
return loaded.response;
|
|
27449
|
+
const card = findCard(loaded.state, cardId);
|
|
27450
|
+
if (!card) {
|
|
27451
|
+
return fail(command, [`Task card ${cardId} not found`]);
|
|
27452
|
+
}
|
|
27453
|
+
if (card.status !== "blocked") {
|
|
27454
|
+
return fail(command, [`Card ${cardId} must be blocked before unblock (got ${card.status})`]);
|
|
27455
|
+
}
|
|
27456
|
+
const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
|
|
27457
|
+
if (!itemLoaded.ok)
|
|
27458
|
+
return itemLoaded.response;
|
|
27459
|
+
if (itemLoaded.item.status !== "BLOCKED") {
|
|
27460
|
+
return fail(command, [
|
|
27461
|
+
`Backlog item ${itemLoaded.item.id} must be BLOCKED before unblock (got ${itemLoaded.item.status})`
|
|
27462
|
+
]);
|
|
27463
|
+
}
|
|
27464
|
+
const state = setTaskCardStatus(loaded.state, cardId, "pending");
|
|
27465
|
+
const written = await persistState(projectRoot, state);
|
|
27466
|
+
if (!written.ok)
|
|
27467
|
+
return fail(command, [written.error]);
|
|
27468
|
+
const changePath = state.changePaths[card.backlogId];
|
|
27469
|
+
if (changePath) {
|
|
27470
|
+
const backlog = await loadBacklog(projectRoot);
|
|
27471
|
+
await writeTasksMarkdown(projectRoot, changePath, itemCards(state, card.backlogId), {
|
|
27472
|
+
tddRequired: backlog.success ? backlog.data.global.tddRequired : false,
|
|
27473
|
+
acceptanceCriteria: itemLoaded.item.acceptanceCriteria
|
|
27474
|
+
});
|
|
27475
|
+
}
|
|
27476
|
+
const transition = await transitionItem(projectRoot, itemLoaded.item, "READY");
|
|
27477
|
+
if (!transition.ok)
|
|
27478
|
+
return fail(command, [transition.error]);
|
|
27479
|
+
return {
|
|
27480
|
+
code: 0,
|
|
27481
|
+
data: {
|
|
27482
|
+
success: true,
|
|
27483
|
+
command,
|
|
27484
|
+
cardId,
|
|
27485
|
+
cardStatus: "pending",
|
|
27486
|
+
itemId: itemLoaded.item.id,
|
|
27487
|
+
itemStatus: "READY",
|
|
27488
|
+
nextStep: `Run openspec plan ${itemLoaded.item.id}.`
|
|
27489
|
+
}
|
|
27490
|
+
};
|
|
27491
|
+
}
|
|
26964
27492
|
async function handleArchive(projectRoot, itemId) {
|
|
26965
27493
|
const command = "openspec archive";
|
|
26966
27494
|
if (!itemId) {
|
|
@@ -27037,7 +27565,7 @@ async function handleArchive(projectRoot, itemId) {
|
|
|
27037
27565
|
}
|
|
27038
27566
|
};
|
|
27039
27567
|
}
|
|
27040
|
-
var KNOWN_SUBCOMMANDS = "validate, scan, plan, analyze, status, next, start, done, block, archive";
|
|
27568
|
+
var KNOWN_SUBCOMMANDS = "validate, scan, plan, analyze, status, next, start, done, block, unblock, archive";
|
|
27041
27569
|
var init_openspec_command = __esm(() => {
|
|
27042
27570
|
init_backlog_parser();
|
|
27043
27571
|
init_backlog_validator();
|
|
@@ -27055,12 +27583,194 @@ var init_openspec_command = __esm(() => {
|
|
|
27055
27583
|
init_loop_engine();
|
|
27056
27584
|
});
|
|
27057
27585
|
|
|
27586
|
+
// src/core/ccep/context-assembly.ts
|
|
27587
|
+
function resumeDecision(changedPaths) {
|
|
27588
|
+
if (changedPaths.length === 0)
|
|
27589
|
+
return { status: "ready" };
|
|
27590
|
+
return {
|
|
27591
|
+
status: "needs_decision",
|
|
27592
|
+
changedPaths,
|
|
27593
|
+
question: `Workspace changed in ${changedPaths.join(", ")}. Continue with the current workspace?`
|
|
27594
|
+
};
|
|
27595
|
+
}
|
|
27596
|
+
var init_context_assembly = () => {};
|
|
27597
|
+
|
|
27598
|
+
// src/core/delivery/delivery-ledger.ts
|
|
27599
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
27600
|
+
import { mkdir as mkdir24, readFile as readFile38, writeFile as writeFile23 } from "node:fs/promises";
|
|
27601
|
+
import { resolve as resolve37, sep as sep8 } from "node:path";
|
|
27602
|
+
function ledgerPath(projectRoot, id) {
|
|
27603
|
+
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(id))
|
|
27604
|
+
throw new Error("Invalid delivery ledger id");
|
|
27605
|
+
return resolve37(projectRoot, DELIVERY_DIR, `${id}.md`);
|
|
27606
|
+
}
|
|
27607
|
+
async function fingerprintWorkspace(projectRoot, paths) {
|
|
27608
|
+
const root = resolve37(projectRoot);
|
|
27609
|
+
const fingerprints = {};
|
|
27610
|
+
for (const path of paths) {
|
|
27611
|
+
const candidate = resolve37(root, path);
|
|
27612
|
+
if (candidate !== root && !candidate.startsWith(`${root}${sep8}`)) {
|
|
27613
|
+
fingerprints[path] = "outside-root";
|
|
27614
|
+
continue;
|
|
27615
|
+
}
|
|
27616
|
+
try {
|
|
27617
|
+
fingerprints[path] = createHash7("sha256").update(await readFile38(candidate)).digest("hex");
|
|
27618
|
+
} catch {
|
|
27619
|
+
fingerprints[path] = "missing";
|
|
27620
|
+
}
|
|
27621
|
+
}
|
|
27622
|
+
return fingerprints;
|
|
27623
|
+
}
|
|
27624
|
+
function render(ledger) {
|
|
27625
|
+
return `# Delivery Ledger: ${ledger.id}
|
|
27626
|
+
|
|
27627
|
+
${START}
|
|
27628
|
+
\`\`\`yaml
|
|
27629
|
+
${$stringify(ledger)}\`\`\`
|
|
27630
|
+
${END}
|
|
27631
|
+
`;
|
|
27632
|
+
}
|
|
27633
|
+
function parseLedger(content) {
|
|
27634
|
+
const match = content.match(/<!-- delivery-ledger-start -->\n```yaml\n([\s\S]*?)```\n<!-- delivery-ledger-end -->/);
|
|
27635
|
+
if (!match)
|
|
27636
|
+
throw new Error("Invalid delivery ledger format");
|
|
27637
|
+
return DeliveryLedgerSchema.parse($parse(match[1]));
|
|
27638
|
+
}
|
|
27639
|
+
async function loadDeliveryLedger(projectRoot, id) {
|
|
27640
|
+
return parseLedger(await readFile38(ledgerPath(projectRoot, id), "utf-8"));
|
|
27641
|
+
}
|
|
27642
|
+
async function createDeliveryLedger(projectRoot, input) {
|
|
27643
|
+
const request = DeliveryLedgerRequestSchema.parse(input);
|
|
27644
|
+
const path = ledgerPath(projectRoot, request.id);
|
|
27645
|
+
try {
|
|
27646
|
+
return { created: false, ledger: await loadDeliveryLedger(projectRoot, request.id) };
|
|
27647
|
+
} catch (error) {
|
|
27648
|
+
if (!(error instanceof Error) || !error.message.includes("ENOENT"))
|
|
27649
|
+
throw error;
|
|
27650
|
+
}
|
|
27651
|
+
const now = new Date().toISOString();
|
|
27652
|
+
const ledger = DeliveryLedgerSchema.parse({
|
|
27653
|
+
version: 1,
|
|
27654
|
+
id: request.id,
|
|
27655
|
+
taskCard: request.taskCard,
|
|
27656
|
+
tasks: request.tasks,
|
|
27657
|
+
evidence: request.taskCard.evidenceRequired,
|
|
27658
|
+
nextStep: request.nextStep,
|
|
27659
|
+
memoryTopicKey: `delivery:${request.id}`,
|
|
27660
|
+
workspace: await fingerprintWorkspace(projectRoot, request.taskCard.targetFiles),
|
|
27661
|
+
createdAt: now,
|
|
27662
|
+
updatedAt: now
|
|
27663
|
+
});
|
|
27664
|
+
await mkdir24(resolve37(projectRoot, DELIVERY_DIR), { recursive: true });
|
|
27665
|
+
try {
|
|
27666
|
+
await writeFile23(path, render(ledger), { encoding: "utf-8", flag: "wx" });
|
|
27667
|
+
return { created: true, ledger };
|
|
27668
|
+
} catch (error) {
|
|
27669
|
+
if (error instanceof Error && error.message.includes("EEXIST")) {
|
|
27670
|
+
return { created: false, ledger: await loadDeliveryLedger(projectRoot, request.id) };
|
|
27671
|
+
}
|
|
27672
|
+
throw error;
|
|
27673
|
+
}
|
|
27674
|
+
}
|
|
27675
|
+
async function reconcileDeliveryLedger(projectRoot, id) {
|
|
27676
|
+
const ledger = await loadDeliveryLedger(projectRoot, id);
|
|
27677
|
+
const current = await fingerprintWorkspace(projectRoot, Object.keys(ledger.workspace));
|
|
27678
|
+
const changedPaths = Object.keys(ledger.workspace).filter((path) => ledger.workspace[path] !== current[path]);
|
|
27679
|
+
return { ledger, changedPaths };
|
|
27680
|
+
}
|
|
27681
|
+
var DELIVERY_DIR = ".codeconductor/delivery", START = "<!-- delivery-ledger-start -->", END = "<!-- delivery-ledger-end -->";
|
|
27682
|
+
var init_delivery_ledger = __esm(() => {
|
|
27683
|
+
init_dist();
|
|
27684
|
+
init_schemas();
|
|
27685
|
+
});
|
|
27686
|
+
|
|
27687
|
+
// src/commands/odd.command.ts
|
|
27688
|
+
var exports_odd_command = {};
|
|
27689
|
+
__export(exports_odd_command, {
|
|
27690
|
+
oddCommand: () => oddCommand
|
|
27691
|
+
});
|
|
27692
|
+
async function oddCommand(options) {
|
|
27693
|
+
if (options.subcommand === "create") {
|
|
27694
|
+
let input = options.input;
|
|
27695
|
+
if (typeof input === "string") {
|
|
27696
|
+
try {
|
|
27697
|
+
input = JSON.parse(input);
|
|
27698
|
+
} catch {
|
|
27699
|
+
return { code: 1, data: { success: false, errors: ["Invalid JSON input"] } };
|
|
27700
|
+
}
|
|
27701
|
+
}
|
|
27702
|
+
const request = DeliveryLedgerRequestSchema.safeParse(input);
|
|
27703
|
+
if (!request.success)
|
|
27704
|
+
return { code: 1, data: { success: false, errors: request.error.issues } };
|
|
27705
|
+
if (!request.data.authorized || request.data.route !== "tracked") {
|
|
27706
|
+
return { code: 0, data: { success: true, created: false, reason: "route_not_tracked" } };
|
|
27707
|
+
}
|
|
27708
|
+
const issues = validateTaskCardForProfile(loadWorkflowProfile("odd", options.projectRoot), request.data.taskCard);
|
|
27709
|
+
if (issues.length > 0)
|
|
27710
|
+
return { code: 1, data: { success: false, errors: issues } };
|
|
27711
|
+
try {
|
|
27712
|
+
const result = await createDeliveryLedger(options.projectRoot, request.data);
|
|
27713
|
+
return { code: 0, data: { success: true, ...result, ...result.created ? {} : { existing: true } } };
|
|
27714
|
+
} catch (error) {
|
|
27715
|
+
return { code: 1, data: { success: false, errors: [String(error)] } };
|
|
27716
|
+
}
|
|
27717
|
+
}
|
|
27718
|
+
if (!options.id)
|
|
27719
|
+
return { code: 1, data: { success: false, errors: ["Missing ledger id"] } };
|
|
27720
|
+
try {
|
|
27721
|
+
if (options.subcommand === "read") {
|
|
27722
|
+
return { code: 0, data: { success: true, ledger: await loadDeliveryLedger(options.projectRoot, options.id) } };
|
|
27723
|
+
}
|
|
27724
|
+
if (options.subcommand === "reconcile") {
|
|
27725
|
+
const result = await reconcileDeliveryLedger(options.projectRoot, options.id);
|
|
27726
|
+
const memory = await loadMemoryIndex(options.projectRoot);
|
|
27727
|
+
const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
|
|
27728
|
+
const rdd = await rddCommand2({
|
|
27729
|
+
subcommand: "status",
|
|
27730
|
+
projectRoot: options.projectRoot,
|
|
27731
|
+
taskId: result.ledger.taskCard.id
|
|
27732
|
+
});
|
|
27733
|
+
const receipts = rdd.data.receipts ?? [];
|
|
27734
|
+
const staleReceipt = receipts.some((receipt) => receipt.valid === false);
|
|
27735
|
+
const changedPaths = staleReceipt ? [...result.changedPaths, "RDD receipt"].sort() : result.changedPaths;
|
|
27736
|
+
return {
|
|
27737
|
+
code: 0,
|
|
27738
|
+
data: {
|
|
27739
|
+
success: true,
|
|
27740
|
+
status: changedPaths.length === 0 ? "ready" : "conflict",
|
|
27741
|
+
resume: resumeDecision(changedPaths),
|
|
27742
|
+
rdd,
|
|
27743
|
+
memory: memory.success ? "available" : "unavailable",
|
|
27744
|
+
memoryPointers: memory.success ? memory.data.pointers.filter((pointer) => pointer.topic_key === result.ledger.memoryTopicKey) : [],
|
|
27745
|
+
...result,
|
|
27746
|
+
changedPaths
|
|
27747
|
+
}
|
|
27748
|
+
};
|
|
27749
|
+
}
|
|
27750
|
+
return { code: 1, data: { success: false, errors: [`Unknown odd subcommand: ${options.subcommand}`] } };
|
|
27751
|
+
} catch (error) {
|
|
27752
|
+
return { code: 1, data: { success: false, errors: [String(error)] } };
|
|
27753
|
+
}
|
|
27754
|
+
}
|
|
27755
|
+
var init_odd_command = __esm(() => {
|
|
27756
|
+
init_memory_index();
|
|
27757
|
+
init_context_assembly();
|
|
27758
|
+
init_task_card_validator();
|
|
27759
|
+
init_workflow_profile_loader();
|
|
27760
|
+
init_delivery_ledger();
|
|
27761
|
+
init_schemas();
|
|
27762
|
+
});
|
|
27763
|
+
|
|
27058
27764
|
// src/core/hooks/hook-runner.ts
|
|
27059
27765
|
function normalizeCommand(raw) {
|
|
27060
27766
|
return raw.replace(/\s+/g, " ").trim();
|
|
27061
27767
|
}
|
|
27062
27768
|
function stripGitBinary(command) {
|
|
27063
|
-
|
|
27769
|
+
const match = command.match(/^(?:&\s*)?("[^"]+"|'[^']+'|[^\s]+)([\s\S]*)$/);
|
|
27770
|
+
if (!match)
|
|
27771
|
+
return command;
|
|
27772
|
+
const binary = match[1].replace(/^["']|["']$/g, "").replace(/\\/g, "/").split("/").pop();
|
|
27773
|
+
return /^git(?:\.exe)?$/i.test(binary ?? "") ? `git${match[2]}` : command;
|
|
27064
27774
|
}
|
|
27065
27775
|
function evaluatePath(filePath) {
|
|
27066
27776
|
if (SENSITIVE_PATH.test(filePath.replace(/\//g, "\\")) || SENSITIVE_PATH.test(filePath)) {
|
|
@@ -27069,9 +27779,44 @@ function evaluatePath(filePath) {
|
|
|
27069
27779
|
return allow();
|
|
27070
27780
|
}
|
|
27071
27781
|
function evaluateCommand(rawCommand) {
|
|
27072
|
-
|
|
27782
|
+
if (/(?:curl|wget)\b.+\|\s*(sh|bash|zsh|cmd)\b/i.test(normalizeCommand(rawCommand)))
|
|
27783
|
+
return deny(MSG_POLICY);
|
|
27784
|
+
const tokens = rawCommand.match(/"[^"]*"|'[^']*'|[^"';&|\r\n]+|[;&|\r\n]+/g) ?? [];
|
|
27785
|
+
const commands = [];
|
|
27786
|
+
let current = "";
|
|
27787
|
+
for (const token of tokens) {
|
|
27788
|
+
if (/^[;&|\r\n]+$/.test(token) && !(token === "&" && !current.trim())) {
|
|
27789
|
+
commands.push(current);
|
|
27790
|
+
current = "";
|
|
27791
|
+
} else {
|
|
27792
|
+
current += token;
|
|
27793
|
+
}
|
|
27794
|
+
}
|
|
27795
|
+
commands.push(current);
|
|
27796
|
+
let verdict = allow();
|
|
27797
|
+
for (const part of commands) {
|
|
27798
|
+
const result = evaluateSingleCommand(part);
|
|
27799
|
+
if (result.action === "deny")
|
|
27800
|
+
return result;
|
|
27801
|
+
if (result.action === "ask")
|
|
27802
|
+
verdict = result;
|
|
27803
|
+
}
|
|
27804
|
+
return verdict;
|
|
27805
|
+
}
|
|
27806
|
+
function evaluateSingleCommand(rawCommand) {
|
|
27807
|
+
let command = stripGitBinary(normalizeCommand(rawCommand));
|
|
27073
27808
|
if (!command)
|
|
27074
27809
|
return allow();
|
|
27810
|
+
if (/^git\s/i.test(command)) {
|
|
27811
|
+
const words = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
27812
|
+
let index = 1;
|
|
27813
|
+
while (words[index]?.startsWith("-")) {
|
|
27814
|
+
const option = words[index++];
|
|
27815
|
+
if (/^(?:-C|-c|--git-dir|--work-tree|--namespace)$/.test(option))
|
|
27816
|
+
index++;
|
|
27817
|
+
}
|
|
27818
|
+
command = ["git", ...words.slice(index)].join(" ");
|
|
27819
|
+
}
|
|
27075
27820
|
if (SENSITIVE_PATH.test(command) && READ_LIKE.test(command)) {
|
|
27076
27821
|
return deny(MSG_SECRET);
|
|
27077
27822
|
}
|
|
@@ -27094,9 +27839,12 @@ function evaluateCommand(rawCommand) {
|
|
|
27094
27839
|
}
|
|
27095
27840
|
if (/^rm\s+-rf\s+(\*|[\\/])/i.test(command))
|
|
27096
27841
|
return deny(MSG_POLICY);
|
|
27097
|
-
|
|
27842
|
+
const windowsTarget = /(?:^|\s)["']?(?:[a-z]:[\\/]|\\|\*)/i.test(command);
|
|
27843
|
+
if (windowsTarget && /^Remove-Item\b/i.test(command) && /\s-(?:Recurse|r)\b/i.test(command) && /\s-(?:Force|f)\b/i.test(command))
|
|
27098
27844
|
return deny(MSG_POLICY);
|
|
27099
|
-
if (
|
|
27845
|
+
if (windowsTarget && /^(?:rd|rmdir|del|erase)\b/i.test(command) && /\s\/s\b/i.test(command))
|
|
27846
|
+
return deny(MSG_POLICY);
|
|
27847
|
+
if (/^sudo\s+/i.test(command))
|
|
27100
27848
|
return deny(MSG_POLICY);
|
|
27101
27849
|
if (/^chmod\s+777\b/i.test(command))
|
|
27102
27850
|
return deny(MSG_POLICY);
|
|
@@ -27123,13 +27871,16 @@ function evaluatePreTool(input) {
|
|
|
27123
27871
|
function formatHookOutput(verdict, format) {
|
|
27124
27872
|
if (format === "agy") {
|
|
27125
27873
|
if (verdict.action === "deny") {
|
|
27126
|
-
return JSON.stringify({ decision: "deny",
|
|
27874
|
+
return JSON.stringify({ decision: "deny", reason: verdict.message });
|
|
27127
27875
|
}
|
|
27128
27876
|
if (verdict.action === "ask") {
|
|
27129
27877
|
return JSON.stringify({ decision: "ask" });
|
|
27130
27878
|
}
|
|
27131
27879
|
return JSON.stringify({ decision: "allow" });
|
|
27132
27880
|
}
|
|
27881
|
+
if (verdict.action === "ask") {
|
|
27882
|
+
return JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "ask" } });
|
|
27883
|
+
}
|
|
27133
27884
|
return "";
|
|
27134
27885
|
}
|
|
27135
27886
|
function claudeExitCode(verdict) {
|
|
@@ -27140,11 +27891,24 @@ function parseAgyPayload(raw) {
|
|
|
27140
27891
|
return {};
|
|
27141
27892
|
try {
|
|
27142
27893
|
const parsed = JSON.parse(raw);
|
|
27143
|
-
const args = parsed.arguments ?? {};
|
|
27894
|
+
const args = parsed.toolCall?.args ?? parsed.arguments ?? {};
|
|
27144
27895
|
return {
|
|
27145
|
-
toolName: parsed.toolName,
|
|
27146
|
-
command: args.CommandLine,
|
|
27147
|
-
filePath: args.TargetFile
|
|
27896
|
+
toolName: typeof (parsed.toolCall?.name ?? parsed.toolName) === "string" ? parsed.toolCall?.name ?? parsed.toolName : undefined,
|
|
27897
|
+
command: typeof args.CommandLine === "string" ? args.CommandLine : undefined,
|
|
27898
|
+
filePath: typeof args.TargetFile === "string" ? args.TargetFile : typeof args.AbsolutePath === "string" ? args.AbsolutePath : undefined
|
|
27899
|
+
};
|
|
27900
|
+
} catch {
|
|
27901
|
+
return {};
|
|
27902
|
+
}
|
|
27903
|
+
}
|
|
27904
|
+
function parseClaudePayload(raw) {
|
|
27905
|
+
try {
|
|
27906
|
+
const parsed = JSON.parse(raw);
|
|
27907
|
+
const input = parsed.tool_input ?? {};
|
|
27908
|
+
return {
|
|
27909
|
+
toolName: typeof parsed.tool_name === "string" ? parsed.tool_name : undefined,
|
|
27910
|
+
command: typeof input.command === "string" ? input.command : undefined,
|
|
27911
|
+
filePath: typeof input.file_path === "string" ? input.file_path : undefined
|
|
27148
27912
|
};
|
|
27149
27913
|
} catch {
|
|
27150
27914
|
return {};
|
|
@@ -27206,14 +27970,17 @@ __export(exports_hook_command, {
|
|
|
27206
27970
|
readStdinText: () => readStdinText
|
|
27207
27971
|
});
|
|
27208
27972
|
import { spawnSync } from "node:child_process";
|
|
27209
|
-
import { existsSync as
|
|
27210
|
-
import {
|
|
27973
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "node:fs";
|
|
27974
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
27975
|
+
import { dirname as dirname13, resolve as resolve38 } from "node:path";
|
|
27211
27976
|
function detectFormat(explicit, stdinText) {
|
|
27212
27977
|
if (explicit)
|
|
27213
27978
|
return explicit;
|
|
27214
|
-
|
|
27215
|
-
|
|
27216
|
-
|
|
27979
|
+
try {
|
|
27980
|
+
const payload = JSON.parse(stdinText);
|
|
27981
|
+
if (payload && (("toolCall" in payload) || ("toolName" in payload) || ("workspacePaths" in payload)))
|
|
27982
|
+
return "agy";
|
|
27983
|
+
} catch {}
|
|
27217
27984
|
return "claude";
|
|
27218
27985
|
}
|
|
27219
27986
|
async function readStdinText() {
|
|
@@ -27225,22 +27992,22 @@ async function readStdinText() {
|
|
|
27225
27992
|
}
|
|
27226
27993
|
return Buffer.concat(chunks).toString("utf8");
|
|
27227
27994
|
}
|
|
27228
|
-
function resolvePreToolInput(options, stdinText) {
|
|
27229
|
-
const fromStdin = parseAgyPayload(stdinText);
|
|
27995
|
+
function resolvePreToolInput(options, stdinText, format) {
|
|
27996
|
+
const fromStdin = format === "agy" ? parseAgyPayload(stdinText) : parseClaudePayload(stdinText);
|
|
27230
27997
|
return {
|
|
27231
|
-
command: options.command ?? process.env.CLAUDE_TOOL_INPUT_COMMAND ?? fromStdin.command,
|
|
27232
|
-
filePath: options.filePath ?? process.env.CLAUDE_TOOL_INPUT_FILE_PATH ?? fromStdin.filePath,
|
|
27998
|
+
command: options.command ?? (process.env.CLAUDE_TOOL_INPUT_COMMAND || undefined) ?? fromStdin.command,
|
|
27999
|
+
filePath: options.filePath ?? (process.env.CLAUDE_TOOL_INPUT_FILE_PATH || undefined) ?? fromStdin.filePath,
|
|
27233
28000
|
toolName: fromStdin.toolName
|
|
27234
28001
|
};
|
|
27235
28002
|
}
|
|
27236
28003
|
async function sessionLines(projectRoot) {
|
|
27237
|
-
const backlogPath =
|
|
27238
|
-
const profilePath =
|
|
28004
|
+
const backlogPath = resolve38(projectRoot, "BACKLOG.md");
|
|
28005
|
+
const profilePath = resolve38(projectRoot, ".codeconductor", "evaluation", "execution-profile.yml");
|
|
27239
28006
|
return [
|
|
27240
28007
|
"[CodeConductor] Session start",
|
|
27241
28008
|
"Skills: openspec, backlog, evaluation, testing-tdd — invoke via /cc-* then CLI.",
|
|
27242
|
-
|
|
27243
|
-
|
|
28009
|
+
existsSync8(backlogPath) ? "BACKLOG.md present — run: bun run dev openspec status" : "No BACKLOG.md — author with /cc-backlog if you need an item queue.",
|
|
28010
|
+
existsSync8(profilePath) ? "Scorecard profile on disk — run: bun run dev scorecard models" : "Scorecard: bun run dev scorecard create --task <id> --from-diff",
|
|
27244
28011
|
"Hooks: bun run dev hook pre-tool | post-tool | session-start",
|
|
27245
28012
|
"Eval suites: bun run dev scorecard suite-run --suite workflow-gates"
|
|
27246
28013
|
];
|
|
@@ -27256,15 +28023,15 @@ async function hookCommand(options) {
|
|
|
27256
28023
|
return { code: 0, data: { success: true, command: "hook session-start" } };
|
|
27257
28024
|
}
|
|
27258
28025
|
if (event === "post-tool") {
|
|
27259
|
-
const filePath = options
|
|
27260
|
-
formatWrittenFile(filePath);
|
|
28026
|
+
const filePath = resolvePreToolInput(options, stdinText, format).filePath;
|
|
28027
|
+
formatWrittenFile(filePath, options.projectRoot);
|
|
27261
28028
|
if (format === "agy") {
|
|
27262
28029
|
process.stdout.write(`{}
|
|
27263
28030
|
`);
|
|
27264
28031
|
}
|
|
27265
28032
|
return { code: 0, data: { success: true, command: "hook post-tool" } };
|
|
27266
28033
|
}
|
|
27267
|
-
const input = resolvePreToolInput(options, stdinText);
|
|
28034
|
+
const input = resolvePreToolInput(options, stdinText, format);
|
|
27268
28035
|
const verdict = evaluatePreTool(input);
|
|
27269
28036
|
if (format === "agy") {
|
|
27270
28037
|
process.stdout.write(`${formatHookOutput(verdict, "agy")}
|
|
@@ -27278,23 +28045,40 @@ async function hookCommand(options) {
|
|
|
27278
28045
|
process.stderr.write(`${verdict.message}
|
|
27279
28046
|
`);
|
|
27280
28047
|
}
|
|
28048
|
+
const output = formatHookOutput(verdict, "claude");
|
|
28049
|
+
if (output)
|
|
28050
|
+
process.stdout.write(`${output}
|
|
28051
|
+
`);
|
|
27281
28052
|
return {
|
|
27282
28053
|
code: claudeExitCode(verdict),
|
|
27283
28054
|
data: { success: verdict.action !== "deny", command: "hook pre-tool", verdict }
|
|
27284
28055
|
};
|
|
27285
28056
|
}
|
|
27286
|
-
function formatWrittenFile(filePath) {
|
|
27287
|
-
if (!filePath
|
|
28057
|
+
function formatWrittenFile(filePath, projectRoot) {
|
|
28058
|
+
if (!filePath)
|
|
28059
|
+
return;
|
|
28060
|
+
filePath = resolve38(projectRoot, filePath);
|
|
28061
|
+
if (!existsSync8(filePath))
|
|
27288
28062
|
return;
|
|
27289
28063
|
const prettier = /\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|md|mdx|css|scss|html|astro|ya?ml)$/i;
|
|
27290
28064
|
const eslint = /\.(ts|tsx|js|jsx|mjs|cjs)$/i;
|
|
27291
28065
|
const python = /\.py$/i;
|
|
27292
|
-
const opts = { stdio: "ignore", windowsHide: true };
|
|
28066
|
+
const opts = { cwd: projectRoot, stdio: "ignore", windowsHide: true, timeout: 5000 };
|
|
28067
|
+
const require2 = createRequire2(resolve38(projectRoot, "package.json"));
|
|
28068
|
+
function runNodeTool(name, args) {
|
|
28069
|
+
try {
|
|
28070
|
+
const manifestPath = require2.resolve(`${name}/package.json`);
|
|
28071
|
+
const manifest = JSON.parse(readFileSync5(manifestPath, "utf8"));
|
|
28072
|
+
const bin = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.[name];
|
|
28073
|
+
if (typeof bin === "string")
|
|
28074
|
+
spawnSync(process.execPath, [resolve38(dirname13(manifestPath), bin), ...args], opts);
|
|
28075
|
+
} catch {}
|
|
28076
|
+
}
|
|
27293
28077
|
if (prettier.test(filePath)) {
|
|
27294
|
-
|
|
28078
|
+
runNodeTool("prettier", ["--write", filePath]);
|
|
27295
28079
|
}
|
|
27296
28080
|
if (eslint.test(filePath)) {
|
|
27297
|
-
|
|
28081
|
+
runNodeTool("eslint", ["--fix", filePath]);
|
|
27298
28082
|
}
|
|
27299
28083
|
if (python.test(filePath)) {
|
|
27300
28084
|
spawnSync("ruff", ["format", filePath], opts);
|
|
@@ -27701,7 +28485,13 @@ function ccGainToScorecardImpact(result) {
|
|
|
27701
28485
|
}
|
|
27702
28486
|
|
|
27703
28487
|
// src/core/evaluation/scorecard-signals.ts
|
|
27704
|
-
import { execFileSync as
|
|
28488
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
28489
|
+
function parseScopeFiles(scope) {
|
|
28490
|
+
return scope.split(/[,;]/).map((entry) => entry.trim().replace(/[.;:]$/, "").replace(/^`|`$/g, "")).filter(Boolean);
|
|
28491
|
+
}
|
|
28492
|
+
function isGeneratedBuildCache(path) {
|
|
28493
|
+
return /(^|\/)\.gradle\//.test(path);
|
|
28494
|
+
}
|
|
27705
28495
|
function collectScorecardSignals(projectRoot, scopeFiles, extra) {
|
|
27706
28496
|
const findings = [];
|
|
27707
28497
|
const overrides = {};
|
|
@@ -27725,7 +28515,7 @@ function collectScorecardSignals(projectRoot, scopeFiles, extra) {
|
|
|
27725
28515
|
}
|
|
27726
28516
|
let diff = "";
|
|
27727
28517
|
try {
|
|
27728
|
-
diff =
|
|
28518
|
+
diff = execFileSync2("git", ["diff", "HEAD"], {
|
|
27729
28519
|
cwd: projectRoot,
|
|
27730
28520
|
encoding: "utf-8",
|
|
27731
28521
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -27748,7 +28538,7 @@ function collectScorecardSignals(projectRoot, scopeFiles, extra) {
|
|
|
27748
28538
|
changedFiles.add(m[1]);
|
|
27749
28539
|
}
|
|
27750
28540
|
if (scopeFiles && scopeFiles.length > 0) {
|
|
27751
|
-
const outOfScope = [...changedFiles].filter((f) => !scopeFiles.some((s) => f.includes(s.replace(/\*\*/g, ""))));
|
|
28541
|
+
const outOfScope = [...changedFiles].filter((file) => !isGeneratedBuildCache(file)).filter((f) => !scopeFiles.some((s) => f.includes(s.replace(/\*\*/g, ""))));
|
|
27752
28542
|
if (outOfScope.length > 0) {
|
|
27753
28543
|
overrides.minimal_diff = {
|
|
27754
28544
|
score: 0,
|
|
@@ -27819,25 +28609,25 @@ var init_scorecard_signals = __esm(() => {
|
|
|
27819
28609
|
});
|
|
27820
28610
|
|
|
27821
28611
|
// src/core/evaluation/harness-experiment.ts
|
|
27822
|
-
import { existsSync as
|
|
27823
|
-
import { cp, mkdir as
|
|
27824
|
-
import { dirname as
|
|
28612
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
28613
|
+
import { cp, mkdir as mkdir25, readFile as readFile39, readdir as readdir14, writeFile as writeFile24 } from "node:fs/promises";
|
|
28614
|
+
import { dirname as dirname14, join as join26, resolve as resolve39 } from "node:path";
|
|
27825
28615
|
function experimentDir(projectRoot, experimentId) {
|
|
27826
|
-
return
|
|
28616
|
+
return resolve39(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR, experimentId);
|
|
27827
28617
|
}
|
|
27828
28618
|
function experimentYamlPath(projectRoot, experimentId) {
|
|
27829
|
-
return
|
|
28619
|
+
return join26(experimentDir(projectRoot, experimentId), "experiment.yml");
|
|
27830
28620
|
}
|
|
27831
28621
|
async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
|
|
27832
28622
|
const candidates = suitePath ? [suitePath] : [
|
|
27833
|
-
|
|
27834
|
-
|
|
28623
|
+
resolve39(projectRoot, "eval", "suites", suiteId, "suite.yml"),
|
|
28624
|
+
resolve39(projectRoot, EVAL_DIR2, "suites", suiteId, "suite.yml")
|
|
27835
28625
|
];
|
|
27836
28626
|
for (const path of candidates) {
|
|
27837
|
-
if (!
|
|
28627
|
+
if (!existsSync9(path))
|
|
27838
28628
|
continue;
|
|
27839
28629
|
try {
|
|
27840
|
-
const suite = HarnessSuiteSchema.parse($parse(await
|
|
28630
|
+
const suite = HarnessSuiteSchema.parse($parse(await readFile39(path, "utf-8")));
|
|
27841
28631
|
return ok({ ...suite, path });
|
|
27842
28632
|
} catch (e) {
|
|
27843
28633
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -27847,7 +28637,7 @@ async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
|
|
|
27847
28637
|
}
|
|
27848
28638
|
async function loadExperiment(projectRoot, experimentId) {
|
|
27849
28639
|
try {
|
|
27850
|
-
const raw = await
|
|
28640
|
+
const raw = await readFile39(experimentYamlPath(projectRoot, experimentId), "utf-8");
|
|
27851
28641
|
return ok(HarnessExperimentSchema.parse($parse(raw)));
|
|
27852
28642
|
} catch (e) {
|
|
27853
28643
|
if (e.code === "ENOENT") {
|
|
@@ -27857,10 +28647,10 @@ async function loadExperiment(projectRoot, experimentId) {
|
|
|
27857
28647
|
}
|
|
27858
28648
|
}
|
|
27859
28649
|
async function listExperiments(projectRoot) {
|
|
27860
|
-
const root =
|
|
27861
|
-
if (!
|
|
28650
|
+
const root = resolve39(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR);
|
|
28651
|
+
if (!existsSync9(root))
|
|
27862
28652
|
return [];
|
|
27863
|
-
const ids = await
|
|
28653
|
+
const ids = await readdir14(root);
|
|
27864
28654
|
const experiments = [];
|
|
27865
28655
|
for (const id of ids) {
|
|
27866
28656
|
const loaded = await loadExperiment(projectRoot, id);
|
|
@@ -27936,9 +28726,9 @@ function generateExperimentId(suiteId, now = new Date) {
|
|
|
27936
28726
|
return `abl-${date}-${suiteId}-${rand}`;
|
|
27937
28727
|
}
|
|
27938
28728
|
async function copyFixture(suitePath, fixtureDir, dest) {
|
|
27939
|
-
const source = fixtureDir ?
|
|
27940
|
-
await
|
|
27941
|
-
if (
|
|
28729
|
+
const source = fixtureDir ? resolve39(dirname14(suitePath), fixtureDir) : join26(dirname14(suitePath), "fixture");
|
|
28730
|
+
await mkdir25(dest, { recursive: true });
|
|
28731
|
+
if (existsSync9(source)) {
|
|
27942
28732
|
await cp(source, dest, { recursive: true });
|
|
27943
28733
|
}
|
|
27944
28734
|
}
|
|
@@ -27959,12 +28749,12 @@ async function startHarnessExperiment(projectRoot, options) {
|
|
|
27959
28749
|
suitePath: suite.data.path
|
|
27960
28750
|
};
|
|
27961
28751
|
const root = experimentDir(projectRoot, experimentId);
|
|
27962
|
-
await
|
|
27963
|
-
await
|
|
27964
|
-
await
|
|
28752
|
+
await mkdir25(root, { recursive: true });
|
|
28753
|
+
await writeFile24(experimentYamlPath(projectRoot, experimentId), $stringify(experiment), "utf-8");
|
|
28754
|
+
await writeFile24(join26(root, "EXPERIMENT.md"), formatExperimentMarkdown(experiment), "utf-8");
|
|
27965
28755
|
for (const task of suite.data.tasks) {
|
|
27966
28756
|
for (const variantId of variants) {
|
|
27967
|
-
const runRoot =
|
|
28757
|
+
const runRoot = join26(root, "runs", task.id, variantDirName(variantId));
|
|
27968
28758
|
await copyFixture(suite.data.path, suite.data.fixtureDir, runRoot);
|
|
27969
28759
|
await writeConfig(runRoot, structuredClone(DEFAULT_CONFIG), true);
|
|
27970
28760
|
const disabled = variantId === "baseline" ? [] : options.components.filter((id) => variantIdFor(id) === variantId);
|
|
@@ -27976,7 +28766,7 @@ async function startHarnessExperiment(projectRoot, options) {
|
|
|
27976
28766
|
});
|
|
27977
28767
|
if (!overlay.success)
|
|
27978
28768
|
return overlay;
|
|
27979
|
-
await
|
|
28769
|
+
await writeFile24(join26(runRoot, "TASK.md"), formatTaskCard(task, experimentId, variantId), "utf-8");
|
|
27980
28770
|
}
|
|
27981
28771
|
}
|
|
27982
28772
|
return ok(experiment);
|
|
@@ -28010,8 +28800,8 @@ var init_harness_experiment = __esm(() => {
|
|
|
28010
28800
|
|
|
28011
28801
|
// src/core/evaluation/suite-run.ts
|
|
28012
28802
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
28013
|
-
import { existsSync as
|
|
28014
|
-
import { resolve as
|
|
28803
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
28804
|
+
import { resolve as resolve40 } from "node:path";
|
|
28015
28805
|
async function runHarnessSuiteTasks(projectRoot, suiteId, suitePath) {
|
|
28016
28806
|
const suite = await loadHarnessSuite(projectRoot, suiteId, suitePath);
|
|
28017
28807
|
if (!suite.success) {
|
|
@@ -28071,7 +28861,7 @@ function runTaskCommand(projectRoot, task) {
|
|
|
28071
28861
|
if (parts.length === 0) {
|
|
28072
28862
|
return { ok: false, detail: "Empty testCommand" };
|
|
28073
28863
|
}
|
|
28074
|
-
const bin = parts[0] === "bun" &&
|
|
28864
|
+
const bin = parts[0] === "bun" && existsSync10(resolve40(projectRoot, "src/cli/main.ts")) ? "bun" : parts[0];
|
|
28075
28865
|
const args = parts[0] === "bun" ? parts.slice(1) : parts.slice(1);
|
|
28076
28866
|
const result = spawnSync2(bin, args, {
|
|
28077
28867
|
cwd: projectRoot,
|
|
@@ -28122,11 +28912,11 @@ var init_suite_run = __esm(() => {
|
|
|
28122
28912
|
});
|
|
28123
28913
|
|
|
28124
28914
|
// src/core/evaluation/execution-profile.ts
|
|
28125
|
-
import { readFile as
|
|
28126
|
-
import { resolve as
|
|
28915
|
+
import { readFile as readFile40 } from "node:fs/promises";
|
|
28916
|
+
import { resolve as resolve41 } from "node:path";
|
|
28127
28917
|
async function loadExecutionProfile(projectRoot, defaultTarget = "opencode") {
|
|
28128
28918
|
try {
|
|
28129
|
-
const content = await
|
|
28919
|
+
const content = await readFile40(resolve41(projectRoot, PROFILE_FILE), "utf-8");
|
|
28130
28920
|
const data = $parse(content);
|
|
28131
28921
|
return ExecutionProfileSchema.parse(data);
|
|
28132
28922
|
} catch {
|
|
@@ -28206,14 +28996,14 @@ var init_execution_profile = __esm(() => {
|
|
|
28206
28996
|
});
|
|
28207
28997
|
|
|
28208
28998
|
// src/core/evaluation/prompt-diff.ts
|
|
28209
|
-
import { readFile as
|
|
28210
|
-
import { join as
|
|
28999
|
+
import { readFile as readFile41, readdir as readdir15 } from "node:fs/promises";
|
|
29000
|
+
import { join as join27, resolve as resolve42 } from "node:path";
|
|
28211
29001
|
function normalizeVersion(v) {
|
|
28212
29002
|
return v.startsWith("v") ? v : `v${v}`;
|
|
28213
29003
|
}
|
|
28214
29004
|
async function listPromptFiles(dir) {
|
|
28215
29005
|
try {
|
|
28216
|
-
const entries = await
|
|
29006
|
+
const entries = await readdir15(dir, { withFileTypes: true });
|
|
28217
29007
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name);
|
|
28218
29008
|
} catch {
|
|
28219
29009
|
return [];
|
|
@@ -28256,30 +29046,30 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
|
|
|
28256
29046
|
const fromV = normalizeVersion(fromVersion);
|
|
28257
29047
|
const toV = normalizeVersion(toVersion);
|
|
28258
29048
|
const searchRoots = [
|
|
28259
|
-
|
|
28260
|
-
|
|
28261
|
-
|
|
29049
|
+
resolve42(options.projectRoot ?? PACKAGE_ROOT, ".agents", "prompts"),
|
|
29050
|
+
join27(ROOT_PRESETS_DIR, target, "prompts"),
|
|
29051
|
+
join27(ROOT_PRESETS_DIR, "opencode", "prompts")
|
|
28262
29052
|
];
|
|
28263
29053
|
let fromDir = "";
|
|
28264
29054
|
let toDir = "";
|
|
28265
29055
|
for (const root of searchRoots) {
|
|
28266
|
-
const f =
|
|
28267
|
-
const t =
|
|
29056
|
+
const f = join27(root, fromV);
|
|
29057
|
+
const t = join27(root, toV);
|
|
28268
29058
|
try {
|
|
28269
|
-
await
|
|
29059
|
+
await readFile41(join27(f, "orchestrator.md"), "utf-8");
|
|
28270
29060
|
fromDir = f;
|
|
28271
29061
|
toDir = t;
|
|
28272
29062
|
break;
|
|
28273
29063
|
} catch {}
|
|
28274
29064
|
}
|
|
28275
29065
|
if (!fromDir) {
|
|
28276
|
-
fromDir =
|
|
28277
|
-
toDir =
|
|
29066
|
+
fromDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", fromV);
|
|
29067
|
+
toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
|
|
28278
29068
|
} else {
|
|
28279
29069
|
try {
|
|
28280
|
-
await
|
|
29070
|
+
await readFile41(join27(toDir, "orchestrator.md"), "utf-8");
|
|
28281
29071
|
} catch {
|
|
28282
|
-
toDir =
|
|
29072
|
+
toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
|
|
28283
29073
|
}
|
|
28284
29074
|
}
|
|
28285
29075
|
const fromFiles = await listPromptFiles(fromDir);
|
|
@@ -28297,12 +29087,12 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
|
|
|
28297
29087
|
let oldText = "";
|
|
28298
29088
|
let newText = "";
|
|
28299
29089
|
try {
|
|
28300
|
-
oldText = await
|
|
29090
|
+
oldText = await readFile41(join27(fromDir, name), "utf-8");
|
|
28301
29091
|
} catch {
|
|
28302
29092
|
oldText = "";
|
|
28303
29093
|
}
|
|
28304
29094
|
try {
|
|
28305
|
-
newText = await
|
|
29095
|
+
newText = await readFile41(join27(toDir, name), "utf-8");
|
|
28306
29096
|
} catch {
|
|
28307
29097
|
newText = "";
|
|
28308
29098
|
}
|
|
@@ -28341,13 +29131,13 @@ var init_prompt_diff = __esm(() => {
|
|
|
28341
29131
|
});
|
|
28342
29132
|
|
|
28343
29133
|
// src/core/evaluation/regression-checklist.ts
|
|
28344
|
-
import { execFileSync as
|
|
28345
|
-
import { mkdir as
|
|
28346
|
-
import { dirname as
|
|
29134
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
29135
|
+
import { mkdir as mkdir26, readFile as readFile42, writeFile as writeFile25 } from "node:fs/promises";
|
|
29136
|
+
import { dirname as dirname15, resolve as resolve43 } from "node:path";
|
|
28347
29137
|
async function loadRegressionChecklist() {
|
|
28348
29138
|
try {
|
|
28349
|
-
const path =
|
|
28350
|
-
const content = await
|
|
29139
|
+
const path = resolve43(ROOT_PRESETS_DIR, "templates", "regression-checklist.yml");
|
|
29140
|
+
const content = await readFile42(path, "utf-8");
|
|
28351
29141
|
const data = $parse(content);
|
|
28352
29142
|
return data.checks ?? [];
|
|
28353
29143
|
} catch {
|
|
@@ -28361,7 +29151,7 @@ function runCheckCommand(cmd, cwd) {
|
|
|
28361
29151
|
if (!file)
|
|
28362
29152
|
return { passed: false, message: "Empty command" };
|
|
28363
29153
|
try {
|
|
28364
|
-
|
|
29154
|
+
execFileSync3(file, args, {
|
|
28365
29155
|
cwd,
|
|
28366
29156
|
encoding: "utf-8",
|
|
28367
29157
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -28402,9 +29192,9 @@ async function runRegressionChecklist(projectRoot, options = {}) {
|
|
|
28402
29192
|
checks,
|
|
28403
29193
|
timestamp: new Date().toISOString()
|
|
28404
29194
|
};
|
|
28405
|
-
const outPath =
|
|
28406
|
-
await
|
|
28407
|
-
await
|
|
29195
|
+
const outPath = resolve43(projectRoot, ".codeconductor/evaluation/regression-last.json");
|
|
29196
|
+
await mkdir26(dirname15(outPath), { recursive: true });
|
|
29197
|
+
await writeFile25(outPath, JSON.stringify(report, null, 2), "utf-8");
|
|
28408
29198
|
return ok(report);
|
|
28409
29199
|
} catch (e) {
|
|
28410
29200
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -28446,8 +29236,8 @@ var init_model_comparison = __esm(() => {
|
|
|
28446
29236
|
});
|
|
28447
29237
|
|
|
28448
29238
|
// src/core/evaluation/cost-quality-matrix.ts
|
|
28449
|
-
import { writeFile as
|
|
28450
|
-
import { resolve as
|
|
29239
|
+
import { writeFile as writeFile26 } from "node:fs/promises";
|
|
29240
|
+
import { resolve as resolve44 } from "node:path";
|
|
28451
29241
|
async function buildCostQualityMatrix(projectRoot, outcomes) {
|
|
28452
29242
|
const agg = aggregateOutcomes(outcomes);
|
|
28453
29243
|
const rows = Object.entries(agg.byModel).map(([model, entry]) => {
|
|
@@ -28467,7 +29257,7 @@ async function buildCostQualityMatrix(projectRoot, outcomes) {
|
|
|
28467
29257
|
rows
|
|
28468
29258
|
};
|
|
28469
29259
|
const dir = await ensureEvaluationDir(projectRoot);
|
|
28470
|
-
await
|
|
29260
|
+
await writeFile26(resolve44(dir, "matrix.yml"), $stringify(matrix), "utf-8");
|
|
28471
29261
|
return matrix;
|
|
28472
29262
|
}
|
|
28473
29263
|
function formatMatrixMarkdown(matrix) {
|
|
@@ -28491,8 +29281,8 @@ var init_cost_quality_matrix = __esm(() => {
|
|
|
28491
29281
|
});
|
|
28492
29282
|
|
|
28493
29283
|
// src/core/evaluation/ablation-report.ts
|
|
28494
|
-
import { mkdir as
|
|
28495
|
-
import { resolve as
|
|
29284
|
+
import { mkdir as mkdir27, writeFile as writeFile27 } from "node:fs/promises";
|
|
29285
|
+
import { resolve as resolve45 } from "node:path";
|
|
28496
29286
|
function isPass(o) {
|
|
28497
29287
|
return o.verdict === "PASS" || o.status === "pass";
|
|
28498
29288
|
}
|
|
@@ -28660,10 +29450,10 @@ function formatAblationMarkdown(report) {
|
|
|
28660
29450
|
`);
|
|
28661
29451
|
}
|
|
28662
29452
|
async function persistAblationReport(projectRoot, markdown) {
|
|
28663
|
-
const dir =
|
|
28664
|
-
await
|
|
28665
|
-
const path =
|
|
28666
|
-
await
|
|
29453
|
+
const dir = resolve45(projectRoot, EVAL_DIR2);
|
|
29454
|
+
await mkdir27(dir, { recursive: true });
|
|
29455
|
+
const path = resolve45(dir, "ablation-last.md");
|
|
29456
|
+
await writeFile27(path, markdown, "utf-8");
|
|
28667
29457
|
return path;
|
|
28668
29458
|
}
|
|
28669
29459
|
var SCORE_THRESHOLD = 0.1, PASS_RATE_THRESHOLD = 0.05;
|
|
@@ -28677,8 +29467,8 @@ var exports_scorecard_command = {};
|
|
|
28677
29467
|
__export(exports_scorecard_command, {
|
|
28678
29468
|
scorecardCommand: () => scorecardCommand
|
|
28679
29469
|
});
|
|
28680
|
-
import { readFile as
|
|
28681
|
-
import { resolve as
|
|
29470
|
+
import { readFile as readFile43 } from "node:fs/promises";
|
|
29471
|
+
import { resolve as resolve46 } from "node:path";
|
|
28682
29472
|
function contractVersion() {
|
|
28683
29473
|
return package_default.version;
|
|
28684
29474
|
}
|
|
@@ -28739,7 +29529,7 @@ async function handleCreate(projectRoot, options) {
|
|
|
28739
29529
|
if (backlog.success && options.taskId) {
|
|
28740
29530
|
const item = backlog.data.items.find((i) => i.id === options.taskId);
|
|
28741
29531
|
if (item) {
|
|
28742
|
-
scopeFiles = item.scope
|
|
29532
|
+
scopeFiles = parseScopeFiles(item.scope);
|
|
28743
29533
|
}
|
|
28744
29534
|
}
|
|
28745
29535
|
let hints = collectScorecardSignals(projectRoot, scopeFiles);
|
|
@@ -28823,7 +29613,7 @@ async function handleRecord(projectRoot, options) {
|
|
|
28823
29613
|
const variantId = options.variantId ?? overlay?.variantId;
|
|
28824
29614
|
const suiteTaskId = options.suiteTaskId;
|
|
28825
29615
|
const disabled = overlay?.disabledComponents ?? (variantId ? parseVariantId(variantId) : undefined);
|
|
28826
|
-
const
|
|
29616
|
+
const fingerprint2 = experimentId || variantId ? harnessFingerprint(disabled ?? [], overlay?.contractVersion ?? contractVersion(), overlay?.ccepProfile ?? "feature") : undefined;
|
|
28827
29617
|
const outcome = {
|
|
28828
29618
|
id: generateEvalId("out"),
|
|
28829
29619
|
taskId,
|
|
@@ -28838,10 +29628,17 @@ async function handleRecord(projectRoot, options) {
|
|
|
28838
29628
|
costUsd: options.costUsd,
|
|
28839
29629
|
tokensIn: options.tokens,
|
|
28840
29630
|
durationMs: options.durationMs,
|
|
29631
|
+
delivery: options.deliveryRoute ? {
|
|
29632
|
+
route: options.deliveryRoute,
|
|
29633
|
+
contextBytes: options.contextBytes,
|
|
29634
|
+
handoffs: options.handoffs,
|
|
29635
|
+
checks: options.checks,
|
|
29636
|
+
tokenUsage: options.tokens !== undefined ? { status: "known", total: options.tokens } : { status: "unknown" }
|
|
29637
|
+
} : undefined,
|
|
28841
29638
|
experimentId,
|
|
28842
29639
|
variantId,
|
|
28843
29640
|
suiteTaskId,
|
|
28844
|
-
harnessFingerprint:
|
|
29641
|
+
harnessFingerprint: fingerprint2,
|
|
28845
29642
|
disabledComponents: disabled && disabled.length > 0 ? [...disabled] : disabled ? [] : undefined,
|
|
28846
29643
|
status: options.verdict === "PASS" ? "pass" : options.verdict === "REJECT" ? "reject" : undefined
|
|
28847
29644
|
};
|
|
@@ -28959,13 +29756,13 @@ async function handleCompareModels(projectRoot, options) {
|
|
|
28959
29756
|
outcomes = outcomes.filter((o) => options.modelsFilter.includes(o.model));
|
|
28960
29757
|
}
|
|
28961
29758
|
const markdown = generateModelComparisonMarkdown(outcomes, options.modelsFilter);
|
|
28962
|
-
const templatePath =
|
|
29759
|
+
const templatePath = resolve46(projectRoot, ".codeconductor/evaluation/model-comparison-last.md");
|
|
28963
29760
|
try {
|
|
28964
|
-
await
|
|
29761
|
+
await readFile43(templatePath);
|
|
28965
29762
|
} catch {}
|
|
28966
|
-
const { writeFile:
|
|
28967
|
-
await
|
|
28968
|
-
await
|
|
29763
|
+
const { writeFile: writeFile28, mkdir: mkdir28 } = await import("node:fs/promises");
|
|
29764
|
+
await mkdir28(resolve46(projectRoot, ".codeconductor/evaluation"), { recursive: true });
|
|
29765
|
+
await writeFile28(templatePath, markdown, "utf-8");
|
|
28969
29766
|
return {
|
|
28970
29767
|
code: 0,
|
|
28971
29768
|
data: {
|
|
@@ -30434,7 +31231,7 @@ var exports_seo_audit_command = {};
|
|
|
30434
31231
|
__export(exports_seo_audit_command, {
|
|
30435
31232
|
seoAuditCommand: () => seoAuditCommand
|
|
30436
31233
|
});
|
|
30437
|
-
import { join as
|
|
31234
|
+
import { join as join28 } from "node:path";
|
|
30438
31235
|
async function seoAuditCommand(options) {
|
|
30439
31236
|
const { url, sitemap, format, failOn, delay: delay2, output, followRedirects, force } = options;
|
|
30440
31237
|
if (!url && !sitemap) {
|
|
@@ -30447,7 +31244,7 @@ async function seoAuditCommand(options) {
|
|
|
30447
31244
|
}
|
|
30448
31245
|
};
|
|
30449
31246
|
}
|
|
30450
|
-
const defaultMarkdownPath = output === undefined && format === "markdown" ?
|
|
31247
|
+
const defaultMarkdownPath = output === undefined && format === "markdown" ? join28("seo-reports", `audit-report-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19)}.md`) : undefined;
|
|
30451
31248
|
const outputPathToWrite = output ?? defaultMarkdownPath;
|
|
30452
31249
|
if (outputPathToWrite !== undefined) {
|
|
30453
31250
|
const preflightError = await preflightContainedOutput(options.projectRoot, outputPathToWrite, { force });
|
|
@@ -30808,6 +31605,7 @@ var COMMANDS = [
|
|
|
30808
31605
|
{ name: "setup", group: "getting-started", summary: "Configure and install CodeConductor.", usage: ["cc-codeconductor setup [--target <target>] [--locale en|es] [--yes] [--dry-run]"] },
|
|
30809
31606
|
{ name: "detect", group: "getting-started", summary: "Inspect the project stack and recommended presets.", usage: ["cc-codeconductor detect"] },
|
|
30810
31607
|
{ name: "init", group: "getting-started", summary: "Initialize low-level CodeConductor configuration.", usage: ["cc-codeconductor init [--locale en|es]"] },
|
|
31608
|
+
{ name: "usage", group: "getting-started", summary: "Show installation examples for presets, council, and LSP.", usage: ["cc-codeconductor usage"] },
|
|
30811
31609
|
{ name: "install", group: "getting-started", summary: "Install harness components.", usage: ["cc-codeconductor install preset --target <target>"], options: ["--target opencode|claude|codex|gemini|cursor|agy|pi|all"], subcommands: [
|
|
30812
31610
|
{ name: "preset", group: "getting-started", summary: "Install agents, prompts, skills, and commands.", usage: ["cc-codeconductor install preset --target <target> [--locale en|es]"] },
|
|
30813
31611
|
{ name: "council", group: "getting-started", summary: "Install generated council files.", usage: ["cc-codeconductor install council --target <target>"] },
|
|
@@ -30832,6 +31630,7 @@ var COMMANDS = [
|
|
|
30832
31630
|
{ name: "orchestrate", group: "workflow", summary: "Run goal execution orchestration.", usage: ["cc-codeconductor orchestrate <subcommand>"] },
|
|
30833
31631
|
{ name: "impact", group: "workflow", summary: "Analyze change impact.", usage: ["cc-codeconductor impact [--files <paths>]"] },
|
|
30834
31632
|
{ name: "verify", group: "workflow", summary: "Verify task completion with evidence.", usage: ["cc-codeconductor verify --task <id>"] },
|
|
31633
|
+
{ name: "rdd", group: "workflow", summary: "Capture and validate Receipt-Driven Development evidence.", usage: ["cc-codeconductor rdd capture --task <id> [--phase red|green|review]", "cc-codeconductor rdd verify --receipt <id>", "cc-codeconductor rdd status [--task <id>]", "cc-codeconductor rdd git-check", "cc-codeconductor rdd install-hooks"] },
|
|
30835
31634
|
{ name: "seo", group: "tool", summary: "Audit SEO and generate llms.txt.", usage: ["cc-codeconductor seo audit --url <url>"] },
|
|
30836
31635
|
{ name: "debt-harvest", aliases: ["harvest"], group: "tool", summary: "Scan source files for deferred debt.", usage: ["cc-codeconductor debt-harvest"] }
|
|
30837
31636
|
];
|
|
@@ -30920,6 +31719,96 @@ _arguments '1:command:(${names})' '--target[target]:target:(${targets})'`;
|
|
|
30920
31719
|
return null;
|
|
30921
31720
|
}
|
|
30922
31721
|
|
|
31722
|
+
// src/cli/usage-guide.ts
|
|
31723
|
+
init_cli_bin();
|
|
31724
|
+
var RECOMMENDED_FLOW = [
|
|
31725
|
+
"cc-codeconductor setup --target claude",
|
|
31726
|
+
"cc-codeconductor setup --target claude --locale es --yes",
|
|
31727
|
+
"cc-codeconductor setup --target claude --dry-run"
|
|
31728
|
+
];
|
|
31729
|
+
var PRIMITIVES = [
|
|
31730
|
+
"cc-codeconductor detect",
|
|
31731
|
+
"cc-codeconductor init --locale es",
|
|
31732
|
+
"cc-codeconductor install preset --target claude",
|
|
31733
|
+
"cc-codeconductor install council --target claude",
|
|
31734
|
+
"cc-codeconductor install lsp --target claude"
|
|
31735
|
+
];
|
|
31736
|
+
var PRESET_EXAMPLES = [
|
|
31737
|
+
"cc-codeconductor install preset --target claude",
|
|
31738
|
+
"cc-codeconductor install preset --target claude --global",
|
|
31739
|
+
"cc-codeconductor install preset --target claude --force",
|
|
31740
|
+
"cc-codeconductor install preset --target claude --dry-run",
|
|
31741
|
+
"cc-codeconductor install preset --target all",
|
|
31742
|
+
"cc-codeconductor install preset --target claude --locale es",
|
|
31743
|
+
"cc-codeconductor install preset --target claude --global --force"
|
|
31744
|
+
];
|
|
31745
|
+
var COUNCIL_EXAMPLES = [
|
|
31746
|
+
"cc-codeconductor install council --target claude",
|
|
31747
|
+
"cc-codeconductor install council --target claude --global",
|
|
31748
|
+
"cc-codeconductor install council --target claude --force",
|
|
31749
|
+
"cc-codeconductor install council --target claude --dry-run",
|
|
31750
|
+
"cc-codeconductor install council --target all",
|
|
31751
|
+
"cc-codeconductor install council --target claude --global --force"
|
|
31752
|
+
];
|
|
31753
|
+
var LSP_EXAMPLES = [
|
|
31754
|
+
"cc-codeconductor install lsp --target claude --lang typescript,python",
|
|
31755
|
+
"cc-codeconductor install lsp --target all --lang typescript,python",
|
|
31756
|
+
"cc-codeconductor install lsp --target claude --global",
|
|
31757
|
+
"cc-codeconductor install lsp --target claude --dry-run"
|
|
31758
|
+
];
|
|
31759
|
+
var AFTER_INSTALL_EXAMPLES = [
|
|
31760
|
+
["cc-codeconductor doctor", "validates the installation"],
|
|
31761
|
+
["cc-codeconductor status", "shows what is installed"],
|
|
31762
|
+
["cc-codeconductor update --dry-run", "previews reconciliation of managed files after upgrading"],
|
|
31763
|
+
["cc-codeconductor update --force", "applies reconciliation of managed files after upgrading"],
|
|
31764
|
+
["cc-codeconductor migrate --dry-run", "previews repair of orphaned artifacts a reinstall cannot fix"]
|
|
31765
|
+
];
|
|
31766
|
+
function renderExamples(lines, bin) {
|
|
31767
|
+
return lines.map((line) => ` ${formatUsage(line, bin)}`);
|
|
31768
|
+
}
|
|
31769
|
+
function renderAnnotatedExamples(entries, bin) {
|
|
31770
|
+
return entries.map(([line, description]) => ` ${formatUsage(line, bin)} — ${description}`);
|
|
31771
|
+
}
|
|
31772
|
+
function renderUsageGuide(bin = resolveCliBin()) {
|
|
31773
|
+
return [
|
|
31774
|
+
"CodeConductor — installation usage guide",
|
|
31775
|
+
"",
|
|
31776
|
+
"Recommended flow (one command, installs and configures everything):",
|
|
31777
|
+
...renderExamples(RECOMMENDED_FLOW, bin),
|
|
31778
|
+
"",
|
|
31779
|
+
"Fine-grained primitives (for scripting or partial installs):",
|
|
31780
|
+
...renderExamples(PRIMITIVES, bin),
|
|
31781
|
+
"",
|
|
31782
|
+
"Shared flags:",
|
|
31783
|
+
" --target opencode|claude|codex|gemini|cursor|agy|pi|all (default: opencode)",
|
|
31784
|
+
" --global Install to the home directory (~/.claude, ~/.opencode, etc.) instead of the project",
|
|
31785
|
+
" --force Allow overwriting existing files",
|
|
31786
|
+
" --dry-run Show what would happen without writing files",
|
|
31787
|
+
" --locale Instruction language for agent files: en (default) | es — install preset only",
|
|
31788
|
+
" --lang Comma-separated languages to install LSP servers for — install lsp only",
|
|
31789
|
+
" --output Output mode: human (default) | json",
|
|
31790
|
+
"",
|
|
31791
|
+
"install preset — agents, prompts, skills, and commands:",
|
|
31792
|
+
...renderExamples(PRESET_EXAMPLES, bin),
|
|
31793
|
+
"",
|
|
31794
|
+
"install council — generated council spec files:",
|
|
31795
|
+
...renderExamples(COUNCIL_EXAMPLES, bin),
|
|
31796
|
+
"",
|
|
31797
|
+
"install lsp — language servers:",
|
|
31798
|
+
...renderExamples(LSP_EXAMPLES, bin),
|
|
31799
|
+
" Dedicated integration config is generated for: opencode, claude, codex, gemini, cursor, agy.",
|
|
31800
|
+
" pi installs the language servers but does not get a dedicated integration config file.",
|
|
31801
|
+
"",
|
|
31802
|
+
"After installing, verify and keep in sync:",
|
|
31803
|
+
...renderAnnotatedExamples(AFTER_INSTALL_EXAMPLES, bin),
|
|
31804
|
+
"",
|
|
31805
|
+
"More detail (GitHub only, not bundled in the npm package):",
|
|
31806
|
+
" docs/usage-cli.md Detailed CLI reference, incl. per-target generated files",
|
|
31807
|
+
" docs/getting-started/ Installation, first project, and update walkthroughs"
|
|
31808
|
+
].join(`
|
|
31809
|
+
`);
|
|
31810
|
+
}
|
|
31811
|
+
|
|
30923
31812
|
// src/cli/router.ts
|
|
30924
31813
|
var OutputModeSchema = exports_external.enum(["json", "human"]);
|
|
30925
31814
|
var SeoFormatSchema = exports_external.enum(["cli", "json", "markdown"]);
|
|
@@ -31200,6 +32089,8 @@ async function routeCommand(args, projectRoot) {
|
|
|
31200
32089
|
case "docs":
|
|
31201
32090
|
case "man":
|
|
31202
32091
|
return { code: 0, data: { success: true, command, output: renderDocs(subcommand) } };
|
|
32092
|
+
case "usage":
|
|
32093
|
+
return { code: 0, data: { success: true, command: "usage", output: renderUsageGuide() } };
|
|
31203
32094
|
case "completion": {
|
|
31204
32095
|
const output = renderCompletion(subcommand ?? "");
|
|
31205
32096
|
return output ? { code: 0, data: { success: true, command: "completion", output } } : cliContractError("completion", ["Expected shell: bash, zsh, fish, or powershell."]);
|
|
@@ -31310,6 +32201,24 @@ async function routeCommand(args, projectRoot) {
|
|
|
31310
32201
|
allowCompileCheck: options["allow-compile-check"] === true || options["allow-compile-check"] === "true"
|
|
31311
32202
|
});
|
|
31312
32203
|
}
|
|
32204
|
+
case "rdd": {
|
|
32205
|
+
const validSubs = ["capture", "verify", "status", "git-check", "install-hooks"];
|
|
32206
|
+
if (!subcommand || !validSubs.includes(subcommand)) {
|
|
32207
|
+
return unknownSubcommand("rdd", subcommand ?? "", [...validSubs]);
|
|
32208
|
+
}
|
|
32209
|
+
const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
|
|
32210
|
+
const paths = typeof options.paths === "string" ? options.paths.split(",").map((path) => path.trim()).filter(Boolean) : undefined;
|
|
32211
|
+
return rddCommand2({
|
|
32212
|
+
subcommand,
|
|
32213
|
+
projectRoot,
|
|
32214
|
+
taskId: options.task ?? args.rest?.[0],
|
|
32215
|
+
receiptId: options.receipt,
|
|
32216
|
+
phase: options.phase,
|
|
32217
|
+
paths,
|
|
32218
|
+
outcome: options.outcome,
|
|
32219
|
+
command: options.command
|
|
32220
|
+
});
|
|
32221
|
+
}
|
|
31313
32222
|
case "ccep": {
|
|
31314
32223
|
const validSubs = ["parse", "profile", "resolve", "compile", "validate", "evaluate", "consensus", "taskcard"];
|
|
31315
32224
|
if (subcommand && !validSubs.includes(subcommand)) {
|
|
@@ -31348,6 +32257,7 @@ async function routeCommand(args, projectRoot) {
|
|
|
31348
32257
|
"start",
|
|
31349
32258
|
"done",
|
|
31350
32259
|
"block",
|
|
32260
|
+
"unblock",
|
|
31351
32261
|
"archive"
|
|
31352
32262
|
];
|
|
31353
32263
|
let openspecSub = "validate";
|
|
@@ -31370,6 +32280,19 @@ async function routeCommand(args, projectRoot) {
|
|
|
31370
32280
|
output: flags.output
|
|
31371
32281
|
});
|
|
31372
32282
|
}
|
|
32283
|
+
case "odd": {
|
|
32284
|
+
const validSubs = ["create", "read", "reconcile"];
|
|
32285
|
+
if (!subcommand || !validSubs.includes(subcommand)) {
|
|
32286
|
+
return unknownSubcommand(command, subcommand ?? "", validSubs);
|
|
32287
|
+
}
|
|
32288
|
+
const { oddCommand: oddCommand2 } = await Promise.resolve().then(() => (init_odd_command(), exports_odd_command));
|
|
32289
|
+
return oddCommand2({
|
|
32290
|
+
subcommand,
|
|
32291
|
+
projectRoot,
|
|
32292
|
+
input: options.input,
|
|
32293
|
+
id: options.id ?? args.rest?.[0]
|
|
32294
|
+
});
|
|
32295
|
+
}
|
|
31373
32296
|
case "hook": {
|
|
31374
32297
|
const validHookEvents = ["pre-tool", "post-tool", "session-start"];
|
|
31375
32298
|
if (subcommand && !validHookEvents.includes(subcommand)) {
|
|
@@ -31433,6 +32356,10 @@ async function routeCommand(args, projectRoot) {
|
|
|
31433
32356
|
costUsd: options.cost ? parseFloat(String(options.cost)) : undefined,
|
|
31434
32357
|
tokens: options.tokens ? parseInt(String(options.tokens), 10) : undefined,
|
|
31435
32358
|
durationMs: options.duration ? parseInt(String(options.duration), 10) : undefined,
|
|
32359
|
+
deliveryRoute: options.route,
|
|
32360
|
+
contextBytes: options["context-bytes"] ? parseInt(String(options["context-bytes"]), 10) : undefined,
|
|
32361
|
+
handoffs: options.handoffs ? parseInt(String(options.handoffs), 10) : undefined,
|
|
32362
|
+
checks: typeof options.checks === "string" ? options.checks.split(",").map((check) => check.trim()).filter(Boolean) : undefined,
|
|
31436
32363
|
verdict: options.verdict,
|
|
31437
32364
|
weightedScore: options.score ? parseFloat(String(options.score)) : undefined,
|
|
31438
32365
|
source: options.source,
|