project-tiny-context-harness 0.2.85 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -428
- package/assets/README.md +283 -455
- package/assets/README.zh-CN.md +199 -123
- package/assets/agents/AGENTS_CORE.md +54 -67
- package/assets/context_templates/context.toml +3 -0
- package/assets/context_templates/global.md +6 -6
- package/assets/github/harness.yml +2 -2
- package/assets/skills/context_development_engineer/SKILL.md +91 -107
- package/assets/skills/context_product_plan/SKILL.md +18 -28
- package/assets/skills/context_surface_contract/SKILL.md +35 -38
- package/assets/skills/context_uiux_design/SKILL.md +31 -30
- package/assets/skills/long-task-workflow/SKILL.md +76 -0
- package/assets/skills/long-task-workflow/agents/openai.yaml +4 -0
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +41 -0
- package/assets/skills/long-task-workflow/references/contract-authoring.md +62 -0
- package/assets/skills/long-task-workflow/references/evidence-design.md +45 -0
- package/assets/skills/normal-long-task/SKILL.md +12 -489
- package/assets/skills/source-plan-authoring/SKILL.md +379 -0
- package/assets/tools/validate_context.py +293 -127
- package/dist/cli.js +3 -1
- package/dist/commands/check-modularity.js +33 -10
- package/dist/commands/composite-campaign.d.ts +1 -0
- package/dist/commands/composite-campaign.js +8 -0
- package/dist/commands/composite-long-task.d.ts +1 -6
- package/dist/commands/composite-long-task.js +7 -163
- package/dist/commands/delivery-set.d.ts +1 -0
- package/dist/commands/delivery-set.js +8 -0
- package/dist/commands/disable.d.ts +1 -0
- package/dist/commands/disable.js +15 -0
- package/dist/commands/enable.d.ts +1 -0
- package/dist/commands/enable.js +14 -0
- package/dist/commands/export-context-args.js +13 -4
- package/dist/commands/export-context.js +18 -7
- package/dist/commands/index.js +42 -30
- package/dist/commands/init.js +12 -6
- package/dist/commands/long-task-authoring.d.ts +3 -0
- package/dist/commands/long-task-authoring.js +92 -0
- package/dist/commands/long-task-explain.d.ts +1 -0
- package/dist/commands/long-task-explain.js +52 -0
- package/dist/commands/long-task.d.ts +1 -0
- package/dist/commands/long-task.js +226 -0
- package/dist/commands/package-source.js +2 -2
- package/dist/commands/upgrade.js +10 -10
- package/dist/index.d.ts +11 -1
- package/dist/index.js +8 -0
- package/dist/lib/config.js +34 -8
- package/dist/lib/context-default-footprint.d.ts +16 -0
- package/dist/lib/context-default-footprint.js +107 -0
- package/dist/lib/context-export.js +105 -41
- package/dist/lib/context-graph-snapshot.d.ts +18 -0
- package/dist/lib/context-graph-snapshot.js +171 -0
- package/dist/lib/context-manifest-schema.d.ts +28 -0
- package/dist/lib/context-manifest-schema.js +150 -0
- package/dist/lib/context-manifest.js +28 -20
- package/dist/lib/context-templates.js +5 -5
- package/dist/lib/design-md.js +1 -1
- package/dist/lib/doctor.js +54 -1
- package/dist/lib/harness-root.js +25 -8
- package/dist/lib/init.js +6 -3
- package/dist/lib/legacy-managed-scan.js +23 -7
- package/dist/lib/legacy-sdlc-migration.js +21 -11
- package/dist/lib/long-task-acceptance-reference.d.ts +22 -0
- package/dist/lib/long-task-acceptance-reference.js +46 -0
- package/dist/lib/long-task-acceptance-shape.d.ts +5 -0
- package/dist/lib/long-task-acceptance-shape.js +131 -0
- package/dist/lib/long-task-activation-validation.d.ts +21 -0
- package/dist/lib/long-task-activation-validation.js +136 -0
- package/dist/lib/long-task-artifacts.d.ts +5 -0
- package/dist/lib/long-task-artifacts.js +34 -0
- package/dist/lib/long-task-assertions-v2.d.ts +22 -0
- package/dist/lib/long-task-assertions-v2.js +212 -0
- package/dist/lib/long-task-authoring-authority-preview.d.ts +5 -0
- package/dist/lib/long-task-authoring-authority-preview.js +85 -0
- package/dist/lib/long-task-authoring-preflight-diagnostics.d.ts +8 -0
- package/dist/lib/long-task-authoring-preflight-diagnostics.js +179 -0
- package/dist/lib/long-task-authoring-preflight-repair-order.d.ts +8 -0
- package/dist/lib/long-task-authoring-preflight-repair-order.js +95 -0
- package/dist/lib/long-task-authoring-preflight-types.d.ts +46 -0
- package/dist/lib/long-task-authoring-preflight-types.js +38 -0
- package/dist/lib/long-task-authoring-preflight.d.ts +3 -0
- package/dist/lib/long-task-authoring-preflight.js +44 -0
- package/dist/lib/long-task-authority-material-diff.d.ts +17 -0
- package/dist/lib/long-task-authority-material-diff.js +152 -0
- package/dist/lib/long-task-authority-materials.d.ts +7 -0
- package/dist/lib/long-task-authority-materials.js +83 -0
- package/dist/lib/long-task-authority-policy.d.ts +168 -0
- package/dist/lib/long-task-authority-policy.js +181 -0
- package/dist/lib/long-task-authority-revision-details.d.ts +19 -0
- package/dist/lib/long-task-authority-revision-details.js +192 -0
- package/dist/lib/long-task-authority-revision-enforcement.d.ts +3 -0
- package/dist/lib/long-task-authority-revision-enforcement.js +41 -0
- package/dist/lib/long-task-authority-revision-types.d.ts +50 -0
- package/dist/lib/long-task-authority-revision-types.js +1 -0
- package/dist/lib/long-task-authority-revision.d.ts +4 -0
- package/dist/lib/long-task-authority-revision.js +188 -0
- package/dist/lib/long-task-authority-transition-policy.d.ts +62 -0
- package/dist/lib/long-task-authority-transition-policy.js +62 -0
- package/dist/lib/long-task-authority-types.d.ts +96 -0
- package/dist/lib/long-task-authority-types.js +1 -0
- package/dist/lib/long-task-authority.d.ts +7 -0
- package/dist/lib/long-task-authority.js +142 -0
- package/dist/lib/long-task-boundary-check.d.ts +15 -0
- package/dist/lib/long-task-boundary-check.js +9 -0
- package/dist/lib/long-task-check-evidence-decoder.d.ts +3 -0
- package/dist/lib/long-task-check-evidence-decoder.js +74 -0
- package/dist/lib/long-task-check-execution-policy.d.ts +18 -0
- package/dist/lib/long-task-check-execution-policy.js +36 -0
- package/dist/lib/long-task-check-runner.d.ts +2 -0
- package/dist/lib/long-task-check-runner.js +181 -0
- package/dist/lib/long-task-check-shape.d.ts +2 -0
- package/dist/lib/long-task-check-shape.js +136 -0
- package/dist/lib/long-task-claim-definitions.d.ts +7 -0
- package/dist/lib/long-task-claim-definitions.js +78 -0
- package/dist/lib/long-task-claim-proof-policy.d.ts +4 -0
- package/dist/lib/long-task-claim-proof-policy.js +57 -0
- package/dist/lib/long-task-claims.d.ts +10 -0
- package/dist/lib/long-task-claims.js +187 -0
- package/dist/lib/long-task-context-authority-topology.d.ts +7 -0
- package/dist/lib/long-task-context-authority-topology.js +37 -0
- package/dist/lib/long-task-context-authority.d.ts +14 -0
- package/dist/lib/long-task-context-authority.js +66 -0
- package/dist/lib/long-task-contract-types.d.ts +203 -0
- package/dist/lib/long-task-contract-types.js +1 -0
- package/dist/lib/long-task-counterfactual-claim-policy.d.ts +10 -0
- package/dist/lib/long-task-counterfactual-claim-policy.js +57 -0
- package/dist/lib/long-task-counterfactual-sandbox.d.ts +6 -0
- package/dist/lib/long-task-counterfactual-sandbox.js +87 -0
- package/dist/lib/long-task-counterfactual-types.d.ts +24 -0
- package/dist/lib/long-task-counterfactual-types.js +1 -0
- package/dist/lib/long-task-delivery-compiler.d.ts +10 -0
- package/dist/lib/long-task-delivery-compiler.js +129 -0
- package/dist/lib/long-task-delivery-parser.d.ts +13 -0
- package/dist/lib/long-task-delivery-parser.js +90 -0
- package/dist/lib/long-task-delivery-preflight.d.ts +5 -0
- package/dist/lib/long-task-delivery-preflight.js +151 -0
- package/dist/lib/long-task-delivery-shape.d.ts +5 -0
- package/dist/lib/long-task-delivery-shape.js +5 -0
- package/dist/lib/long-task-delivery-types.d.ts +8 -0
- package/dist/lib/long-task-delivery-types.js +8 -0
- package/dist/lib/long-task-delivery-validation.d.ts +3 -0
- package/dist/lib/long-task-delivery-validation.js +175 -0
- package/dist/lib/long-task-evidence-adapter-policy.d.ts +4 -0
- package/dist/lib/long-task-evidence-adapter-policy.js +15 -0
- package/dist/lib/long-task-evidence-adapter-types.d.ts +1 -0
- package/dist/lib/long-task-evidence-adapter-types.js +1 -0
- package/dist/lib/long-task-evidence-findings.d.ts +5 -0
- package/dist/lib/long-task-evidence-findings.js +113 -0
- package/dist/lib/long-task-evidence-sensitivity-policy.d.ts +4 -0
- package/dist/lib/long-task-evidence-sensitivity-policy.js +98 -0
- package/dist/lib/long-task-evidence-v2.d.ts +5 -0
- package/dist/lib/long-task-evidence-v2.js +226 -0
- package/dist/lib/long-task-explain-acceptance-link.d.ts +46 -0
- package/dist/lib/long-task-explain-acceptance-link.js +96 -0
- package/dist/lib/long-task-explain-claim-links.d.ts +45 -0
- package/dist/lib/long-task-explain-claim-links.js +88 -0
- package/dist/lib/long-task-explain-source-links.d.ts +93 -0
- package/dist/lib/long-task-explain-source-links.js +51 -0
- package/dist/lib/long-task-final-v2.d.ts +2 -0
- package/dist/lib/long-task-final-v2.js +124 -0
- package/dist/lib/long-task-finding-context.d.ts +3 -0
- package/dist/lib/long-task-finding-context.js +63 -0
- package/dist/lib/long-task-freshness.d.ts +3 -0
- package/dist/lib/long-task-freshness.js +80 -0
- package/dist/lib/long-task-hook-install.d.ts +9 -0
- package/dist/lib/long-task-hook-install.js +184 -0
- package/dist/lib/long-task-hook-preflight.d.ts +7 -0
- package/dist/lib/long-task-hook-preflight.js +58 -0
- package/dist/lib/long-task-observation-ownership.d.ts +2 -0
- package/dist/lib/long-task-observation-ownership.js +22 -0
- package/dist/lib/long-task-outcome-parser.d.ts +4 -0
- package/dist/lib/long-task-outcome-parser.js +100 -0
- package/dist/lib/long-task-paths.d.ts +49 -0
- package/dist/lib/long-task-paths.js +418 -0
- package/dist/lib/long-task-playwright-counterfactual-policy.d.ts +7 -0
- package/dist/lib/long-task-playwright-counterfactual-policy.js +125 -0
- package/dist/lib/long-task-playwright-evidence.d.ts +6 -0
- package/dist/lib/long-task-playwright-evidence.js +244 -0
- package/dist/lib/long-task-product-shape.d.ts +6 -0
- package/dist/lib/long-task-product-shape.js +73 -0
- package/dist/lib/long-task-progress.d.ts +4 -0
- package/dist/lib/long-task-progress.js +114 -0
- package/dist/lib/long-task-protected-files.d.ts +1 -0
- package/dist/lib/long-task-protected-files.js +30 -0
- package/dist/lib/long-task-required-proof-surfaces.d.ts +2 -0
- package/dist/lib/long-task-required-proof-surfaces.js +13 -0
- package/dist/lib/long-task-requirement-shape.d.ts +2 -0
- package/dist/lib/long-task-requirement-shape.js +18 -0
- package/dist/lib/long-task-risk-surfaces.d.ts +2 -0
- package/dist/lib/long-task-risk-surfaces.js +60 -0
- package/dist/lib/long-task-risk-types.d.ts +5 -0
- package/dist/lib/long-task-risk-types.js +12 -0
- package/dist/lib/long-task-risk.d.ts +9 -0
- package/dist/lib/long-task-risk.js +132 -0
- package/dist/lib/long-task-root-shape.d.ts +4 -0
- package/dist/lib/long-task-root-shape.js +86 -0
- package/dist/lib/long-task-runner-environment.d.ts +7 -0
- package/dist/lib/long-task-runner-environment.js +57 -0
- package/dist/lib/long-task-runner-files.d.ts +3 -0
- package/dist/lib/long-task-runner-files.js +39 -0
- package/dist/lib/long-task-runner-freeze.d.ts +2 -0
- package/dist/lib/long-task-runner-freeze.js +168 -0
- package/dist/lib/long-task-runtime-types.d.ts +214 -0
- package/dist/lib/long-task-runtime-types.js +1 -0
- package/dist/lib/long-task-shape-primitives.d.ts +17 -0
- package/dist/lib/long-task-shape-primitives.js +81 -0
- package/dist/lib/long-task-source-authority-types.d.ts +13 -0
- package/dist/lib/long-task-source-authority-types.js +1 -0
- package/dist/lib/long-task-source-claim-validation.d.ts +5 -0
- package/dist/lib/long-task-source-claim-validation.js +96 -0
- package/dist/lib/long-task-source-continuity.d.ts +4 -0
- package/dist/lib/long-task-source-continuity.js +57 -0
- package/dist/lib/long-task-source-inventory.d.ts +2 -0
- package/dist/lib/long-task-source-inventory.js +21 -0
- package/dist/lib/long-task-source-item-parser.d.ts +3 -0
- package/dist/lib/long-task-source-item-parser.js +112 -0
- package/dist/lib/long-task-source-shape.d.ts +2 -0
- package/dist/lib/long-task-source-shape.js +56 -0
- package/dist/lib/long-task-source-target-continuity.d.ts +4 -0
- package/dist/lib/long-task-source-target-continuity.js +80 -0
- package/dist/lib/long-task-source-target-index.d.ts +12 -0
- package/dist/lib/long-task-source-target-index.js +76 -0
- package/dist/lib/long-task-source-validation.d.ts +3 -0
- package/dist/lib/long-task-source-validation.js +46 -0
- package/dist/lib/long-task-state.d.ts +91 -0
- package/dist/lib/long-task-state.js +611 -0
- package/dist/lib/long-task-status-projection.d.ts +21 -0
- package/dist/lib/long-task-status-projection.js +116 -0
- package/dist/lib/long-task-status-v2.d.ts +37 -0
- package/dist/lib/long-task-status-v2.js +306 -0
- package/dist/lib/long-task-technical-shape.d.ts +3 -0
- package/dist/lib/long-task-technical-shape.js +44 -0
- package/dist/lib/long-task-verifier-authority.d.ts +12 -0
- package/dist/lib/long-task-verifier-authority.js +47 -0
- package/dist/lib/long-task-verifier-identity.d.ts +2 -0
- package/dist/lib/long-task-verifier-identity.js +55 -0
- package/dist/lib/long-task-verifier-v2.d.ts +13 -0
- package/dist/lib/long-task-verifier-v2.js +266 -0
- package/dist/lib/long-task-workspace-runtime-types.d.ts +23 -0
- package/dist/lib/long-task-workspace-runtime-types.js +1 -0
- package/dist/lib/long-task-workspace.d.ts +25 -0
- package/dist/lib/long-task-workspace.js +366 -0
- package/dist/lib/managed-file.js +20 -5
- package/dist/lib/migrations.js +156 -32
- package/dist/lib/modularity.d.ts +27 -1
- package/dist/lib/modularity.js +499 -23
- package/dist/lib/package-json-config.js +14 -5
- package/dist/lib/package-source.js +4 -2
- package/dist/lib/profiles.d.ts +13 -0
- package/dist/lib/profiles.js +66 -0
- package/dist/lib/schema-guard.js +4 -2
- package/dist/lib/source-files.js +14 -7
- package/dist/lib/source-pack-classify.js +91 -19
- package/dist/lib/source-pack-config.js +25 -8
- package/dist/lib/source-pack-export.js +84 -25
- package/dist/lib/source-pack-manifest.js +18 -6
- package/dist/lib/source-pack-records.js +35 -11
- package/dist/lib/source-pack-render.js +47 -18
- package/dist/lib/stable-json.d.ts +2 -0
- package/dist/lib/stable-json.js +21 -0
- package/dist/lib/strict-codec.d.ts +4 -0
- package/dist/lib/strict-codec.js +65 -0
- package/dist/lib/sync-engine.js +76 -24
- package/dist/lib/types.d.ts +8 -1
- package/dist/lib/upgrade.js +4 -3
- package/dist/lib/validators.js +218 -191
- package/dist/long-task-hook.d.ts +2 -0
- package/dist/long-task-hook.js +55 -0
- package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +340 -0
- package/dist/schemas/long-task-delivery-v2/long-task-outcomes-v2.schema.json +18 -0
- package/migrations/README.md +8 -3
- package/package.json +17 -6
- package/source-mappings.yaml +0 -3
- package/assets/protected-harness-baseline.json +0 -20
- package/assets/skills/composite-long-task-workflow/SKILL.md +0 -245
- package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +0 -62
- package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +0 -17
- package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +0 -682
- package/dist/commands/superpowers.d.ts +0 -1
- package/dist/commands/superpowers.js +0 -8
- package/dist/lib/composite-long-task-renderer.d.ts +0 -12
- package/dist/lib/composite-long-task-renderer.js +0 -109
- package/dist/lib/plan-acceptance-artifacts.d.ts +0 -1
- package/dist/lib/plan-acceptance-artifacts.js +0 -220
- package/dist/lib/plan-acceptance-evidence.d.ts +0 -2
- package/dist/lib/plan-acceptance-evidence.js +0 -108
- package/dist/lib/plan-acceptance-json.d.ts +0 -15
- package/dist/lib/plan-acceptance-json.js +0 -133
- package/dist/lib/plan-acceptance-validator.d.ts +0 -2
- package/dist/lib/plan-acceptance-validator.js +0 -209
- package/dist/lib/plan-contract-validator.d.ts +0 -2
- package/dist/lib/plan-contract-validator.js +0 -127
- package/dist/lib/plan-validator-common.d.ts +0 -24
- package/dist/lib/plan-validator-common.js +0 -196
- package/dist/lib/superpowers-task-ac010.d.ts +0 -6
- package/dist/lib/superpowers-task-ac010.js +0 -26
- package/dist/lib/superpowers-task-assertion-normalizers.d.ts +0 -3
- package/dist/lib/superpowers-task-assertion-normalizers.js +0 -74
- package/dist/lib/superpowers-task-assertions.d.ts +0 -20
- package/dist/lib/superpowers-task-assertions.js +0 -257
- package/dist/lib/superpowers-task-attempt.d.ts +0 -4
- package/dist/lib/superpowers-task-attempt.js +0 -102
- package/dist/lib/superpowers-task-command-run-correlation.d.ts +0 -8
- package/dist/lib/superpowers-task-command-run-correlation.js +0 -103
- package/dist/lib/superpowers-task-command-specs.d.ts +0 -3
- package/dist/lib/superpowers-task-command-specs.js +0 -52
- package/dist/lib/superpowers-task-compile-diagnostics.d.ts +0 -5
- package/dist/lib/superpowers-task-compile-diagnostics.js +0 -20
- package/dist/lib/superpowers-task-compile-guards.d.ts +0 -2
- package/dist/lib/superpowers-task-compile-guards.js +0 -66
- package/dist/lib/superpowers-task-compile.d.ts +0 -6
- package/dist/lib/superpowers-task-compile.js +0 -133
- package/dist/lib/superpowers-task-completion-output.d.ts +0 -91
- package/dist/lib/superpowers-task-completion-output.js +0 -374
- package/dist/lib/superpowers-task-conformance.d.ts +0 -2
- package/dist/lib/superpowers-task-conformance.js +0 -24
- package/dist/lib/superpowers-task-contradictions.d.ts +0 -6
- package/dist/lib/superpowers-task-contradictions.js +0 -126
- package/dist/lib/superpowers-task-current-evidence.d.ts +0 -3
- package/dist/lib/superpowers-task-current-evidence.js +0 -176
- package/dist/lib/superpowers-task-delivery.d.ts +0 -4
- package/dist/lib/superpowers-task-delivery.js +0 -96
- package/dist/lib/superpowers-task-derive.d.ts +0 -14
- package/dist/lib/superpowers-task-derive.js +0 -393
- package/dist/lib/superpowers-task-events.d.ts +0 -1
- package/dist/lib/superpowers-task-events.js +0 -13
- package/dist/lib/superpowers-task-evidence-kernel.d.ts +0 -19
- package/dist/lib/superpowers-task-evidence-kernel.js +0 -347
- package/dist/lib/superpowers-task-evidence-records.d.ts +0 -2
- package/dist/lib/superpowers-task-evidence-records.js +0 -55
- package/dist/lib/superpowers-task-evidence.d.ts +0 -10
- package/dist/lib/superpowers-task-evidence.js +0 -147
- package/dist/lib/superpowers-task-fields.d.ts +0 -22
- package/dist/lib/superpowers-task-fields.js +0 -276
- package/dist/lib/superpowers-task-final-card.d.ts +0 -3
- package/dist/lib/superpowers-task-final-card.js +0 -35
- package/dist/lib/superpowers-task-gates.d.ts +0 -12
- package/dist/lib/superpowers-task-gates.js +0 -267
- package/dist/lib/superpowers-task-harness-drift.d.ts +0 -11
- package/dist/lib/superpowers-task-harness-drift.js +0 -90
- package/dist/lib/superpowers-task-next-slices.d.ts +0 -1
- package/dist/lib/superpowers-task-next-slices.js +0 -12
- package/dist/lib/superpowers-task-protected-baseline.d.ts +0 -10
- package/dist/lib/superpowers-task-protected-baseline.js +0 -66
- package/dist/lib/superpowers-task-source-compile.d.ts +0 -5
- package/dist/lib/superpowers-task-source-compile.js +0 -226
- package/dist/lib/superpowers-task-source-parser.d.ts +0 -23
- package/dist/lib/superpowers-task-source-parser.js +0 -218
- package/dist/lib/superpowers-task-state-schema.d.ts +0 -460
- package/dist/lib/superpowers-task-state-schema.js +0 -66
- package/dist/lib/superpowers-task-state-shape.d.ts +0 -3
- package/dist/lib/superpowers-task-state-shape.js +0 -50
- package/dist/lib/superpowers-task-state.d.ts +0 -16
- package/dist/lib/superpowers-task-state.js +0 -246
- package/dist/lib/superpowers-task-status.d.ts +0 -2
- package/dist/lib/superpowers-task-status.js +0 -24
- package/dist/lib/superpowers-task-under-specified.d.ts +0 -7
- package/dist/lib/superpowers-task-under-specified.js +0 -61
- package/dist/lib/superpowers-task-unregistered-evidence.d.ts +0 -11
- package/dist/lib/superpowers-task-unregistered-evidence.js +0 -72
- package/dist/lib/superpowers-task-validator.d.ts +0 -5
- package/dist/lib/superpowers-task-validator.js +0 -293
|
@@ -3,9 +3,9 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { promises as fs } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
|
-
import { ensureDir, listFiles, pathExists, readText, writeTextIfChanged } from "./fs.js";
|
|
6
|
+
import { ensureDir, listFiles, pathExists, readText, writeTextIfChanged, } from "./fs.js";
|
|
7
7
|
import { harnessRoot } from "./harness-root.js";
|
|
8
|
-
import { SAFE_EXAMPLE_FILE_NAMES, shouldExcludeRelativePath, shouldIncludeCodeFile, toPosix } from "./source-files.js";
|
|
8
|
+
import { SAFE_EXAMPLE_FILE_NAMES, shouldExcludeRelativePath, shouldIncludeCodeFile, toPosix, } from "./source-files.js";
|
|
9
9
|
const execFileAsync = promisify(execFile);
|
|
10
10
|
const EXPORT_HEADER = "Export artifact. Do not reference from project_context/context.toml.";
|
|
11
11
|
const DEFAULT_EXPORT_DIR = "tmp/ty-context/context-exports";
|
|
@@ -30,8 +30,11 @@ async function runFullContextExport(projectRoot, options) {
|
|
|
30
30
|
const outputRelativePath = repoRelative(projectRoot, outputPath);
|
|
31
31
|
const warnings = [];
|
|
32
32
|
const sourceFiles = await collectSourceFiles(projectRoot, warnings);
|
|
33
|
-
const sourceRelativeFiles = sourceFiles
|
|
34
|
-
|
|
33
|
+
const sourceRelativeFiles = sourceFiles
|
|
34
|
+
.map((file) => repoRelative(projectRoot, file))
|
|
35
|
+
.sort();
|
|
36
|
+
const sourceContextCount = sourceRelativeFiles.filter((file) => file === "project_context/context.toml" ||
|
|
37
|
+
file.startsWith("project_context/")).length;
|
|
35
38
|
const sourceSections = [];
|
|
36
39
|
const contextEntrySections = [];
|
|
37
40
|
for (const file of sourceFiles) {
|
|
@@ -66,7 +69,9 @@ async function runFullContextExport(projectRoot, options) {
|
|
|
66
69
|
"- source_file_list:",
|
|
67
70
|
...sourceRelativeFiles.map((file) => ` - ${file}`),
|
|
68
71
|
"- warnings:",
|
|
69
|
-
...(warnings.length > 0
|
|
72
|
+
...(warnings.length > 0
|
|
73
|
+
? warnings.map((warning) => ` - ${warning}`)
|
|
74
|
+
: [" - none"]),
|
|
70
75
|
"",
|
|
71
76
|
"## Directory Tree Summary",
|
|
72
77
|
"",
|
|
@@ -78,12 +83,14 @@ async function runFullContextExport(projectRoot, options) {
|
|
|
78
83
|
"",
|
|
79
84
|
"## Context Code Entry Point Index",
|
|
80
85
|
"",
|
|
81
|
-
contextEntrySections.length > 0
|
|
86
|
+
contextEntrySections.length > 0
|
|
87
|
+
? contextEntrySections.join("\n\n")
|
|
88
|
+
: "- No Code Entry Points sections found in exported Context Markdown.",
|
|
82
89
|
"",
|
|
83
90
|
"## Source Files",
|
|
84
91
|
"",
|
|
85
92
|
sourceSections.join("\n\n"),
|
|
86
|
-
""
|
|
93
|
+
"",
|
|
87
94
|
].join("\n");
|
|
88
95
|
if (!options.check) {
|
|
89
96
|
await ensureDir(path.dirname(outputPath));
|
|
@@ -96,7 +103,7 @@ async function runFullContextExport(projectRoot, options) {
|
|
|
96
103
|
sourceFiles: sourceRelativeFiles,
|
|
97
104
|
sourceContextCount,
|
|
98
105
|
warnings,
|
|
99
|
-
wrote: !options.check
|
|
106
|
+
wrote: !options.check,
|
|
100
107
|
};
|
|
101
108
|
}
|
|
102
109
|
async function runCodeImplementationExport(projectRoot, options) {
|
|
@@ -121,7 +128,7 @@ async function runCodeImplementationExport(projectRoot, options) {
|
|
|
121
128
|
characters: content.length,
|
|
122
129
|
sha256: sha256(content),
|
|
123
130
|
summary: summarizeCodeFile(relative, content, language),
|
|
124
|
-
content
|
|
131
|
+
content,
|
|
125
132
|
});
|
|
126
133
|
}
|
|
127
134
|
records.sort((left, right) => left.relative.localeCompare(right.relative));
|
|
@@ -146,7 +153,9 @@ async function runCodeImplementationExport(projectRoot, options) {
|
|
|
146
153
|
`- total_lines: ${totalLines}`,
|
|
147
154
|
`- total_characters: ${totalCharacters}`,
|
|
148
155
|
"- warnings:",
|
|
149
|
-
...(warnings.length > 0
|
|
156
|
+
...(warnings.length > 0
|
|
157
|
+
? warnings.map((warning) => ` - ${warning}`)
|
|
158
|
+
: [" - none"]),
|
|
150
159
|
"",
|
|
151
160
|
"## Implementation Guide",
|
|
152
161
|
"",
|
|
@@ -159,7 +168,7 @@ async function runCodeImplementationExport(projectRoot, options) {
|
|
|
159
168
|
"## Source Files",
|
|
160
169
|
"",
|
|
161
170
|
records.map(renderCodeFileSection).join("\n\n"),
|
|
162
|
-
""
|
|
171
|
+
"",
|
|
163
172
|
].join("\n");
|
|
164
173
|
if (!options.check) {
|
|
165
174
|
await ensureDir(path.dirname(outputPath));
|
|
@@ -175,7 +184,7 @@ async function runCodeImplementationExport(projectRoot, options) {
|
|
|
175
184
|
totalLines,
|
|
176
185
|
totalCharacters,
|
|
177
186
|
warnings,
|
|
178
|
-
wrote: !options.check
|
|
187
|
+
wrote: !options.check,
|
|
179
188
|
};
|
|
180
189
|
}
|
|
181
190
|
function resolveOutputPath(projectRoot, requestedOutput, now, mode) {
|
|
@@ -190,7 +199,8 @@ function resolveOutputPath(projectRoot, requestedOutput, now, mode) {
|
|
|
190
199
|
throw new Error("export-context --output must stay inside the workspace; use tmp/ty-context/context-exports/<name>.md");
|
|
191
200
|
}
|
|
192
201
|
const normalized = toPosix(relative);
|
|
193
|
-
if (normalized === "project_context" ||
|
|
202
|
+
if (normalized === "project_context" ||
|
|
203
|
+
normalized.startsWith("project_context/")) {
|
|
194
204
|
throw new Error("export-context output is a temporary artifact; use tmp/ty-context/context-exports/** instead of project_context/**");
|
|
195
205
|
}
|
|
196
206
|
if (!normalized.startsWith(`${DEFAULT_EXPORT_DIR}/`)) {
|
|
@@ -212,7 +222,8 @@ async function collectSourceFiles(projectRoot, warnings) {
|
|
|
212
222
|
const contextRoot = path.join(projectRoot, "project_context");
|
|
213
223
|
for (const file of await listFiles(contextRoot)) {
|
|
214
224
|
const relative = repoRelative(projectRoot, file);
|
|
215
|
-
if ((relative.endsWith(".md") || relative.endsWith(".toml")) &&
|
|
225
|
+
if ((relative.endsWith(".md") || relative.endsWith(".toml")) &&
|
|
226
|
+
!shouldExcludeRelativePath(relative)) {
|
|
216
227
|
files.add(file);
|
|
217
228
|
}
|
|
218
229
|
}
|
|
@@ -220,14 +231,16 @@ async function collectSourceFiles(projectRoot, warnings) {
|
|
|
220
231
|
const skillRoot = path.join(projectRoot, root, "skills");
|
|
221
232
|
for (const file of await listFiles(skillRoot)) {
|
|
222
233
|
const relative = repoRelative(projectRoot, file);
|
|
223
|
-
if (relative.endsWith("/SKILL.md") &&
|
|
234
|
+
if (relative.endsWith("/SKILL.md") &&
|
|
235
|
+
!shouldExcludeRelativePath(relative)) {
|
|
224
236
|
files.add(file);
|
|
225
237
|
}
|
|
226
238
|
}
|
|
227
239
|
for (const file of await listCandidateFiles(projectRoot)) {
|
|
228
240
|
const relative = repoRelative(projectRoot, file);
|
|
229
241
|
const base = path.basename(file);
|
|
230
|
-
if (["README.md", "AGENTS.md", "DESIGN.md"].includes(base) &&
|
|
242
|
+
if (["README.md", "AGENTS.md", "DESIGN.md"].includes(base) &&
|
|
243
|
+
!shouldExcludeRelativePath(relative)) {
|
|
231
244
|
files.add(file);
|
|
232
245
|
}
|
|
233
246
|
}
|
|
@@ -247,9 +260,17 @@ async function collectCodeSourceFiles(projectRoot) {
|
|
|
247
260
|
}
|
|
248
261
|
async function listGitCandidateFiles(projectRoot) {
|
|
249
262
|
try {
|
|
250
|
-
const result = await execFileAsync("git", [
|
|
263
|
+
const result = await execFileAsync("git", [
|
|
264
|
+
"-C",
|
|
265
|
+
projectRoot,
|
|
266
|
+
"ls-files",
|
|
267
|
+
"--cached",
|
|
268
|
+
"--others",
|
|
269
|
+
"--exclude-standard",
|
|
270
|
+
"-z",
|
|
271
|
+
], {
|
|
251
272
|
encoding: "utf8",
|
|
252
|
-
maxBuffer: GIT_LS_MAX_BUFFER
|
|
273
|
+
maxBuffer: GIT_LS_MAX_BUFFER,
|
|
253
274
|
});
|
|
254
275
|
const stdout = result.stdout;
|
|
255
276
|
return stdout
|
|
@@ -322,7 +343,8 @@ async function buildDirectoryTree(projectRoot, warnings) {
|
|
|
322
343
|
}
|
|
323
344
|
const visibleEntries = entries
|
|
324
345
|
.filter((entry) => !shouldExcludeRelativePath(repoRelative(projectRoot, path.join(current, entry.name))))
|
|
325
|
-
.sort((left, right) => Number(right.isDirectory()) - Number(left.isDirectory()) ||
|
|
346
|
+
.sort((left, right) => Number(right.isDirectory()) - Number(left.isDirectory()) ||
|
|
347
|
+
left.name.localeCompare(right.name));
|
|
326
348
|
for (const entry of visibleEntries) {
|
|
327
349
|
if (count >= MAX_TREE_ENTRIES) {
|
|
328
350
|
truncated = true;
|
|
@@ -362,7 +384,9 @@ async function buildMakefileVerificationSummary(projectRoot) {
|
|
|
362
384
|
summary.push(` ${line.trim()}`);
|
|
363
385
|
}
|
|
364
386
|
}
|
|
365
|
-
return summary.length > 0
|
|
387
|
+
return summary.length > 0
|
|
388
|
+
? summary.join("\n")
|
|
389
|
+
: "- No obvious verification targets found in root Makefile.";
|
|
366
390
|
}
|
|
367
391
|
function redactSensitiveAssignments(content) {
|
|
368
392
|
let count = 0;
|
|
@@ -379,7 +403,13 @@ function redactSensitiveAssignments(content) {
|
|
|
379
403
|
function renderSourceSection(relative, content) {
|
|
380
404
|
const fence = fenceFor(content);
|
|
381
405
|
const language = languageFor(relative);
|
|
382
|
-
return [
|
|
406
|
+
return [
|
|
407
|
+
`### ${relative}`,
|
|
408
|
+
"",
|
|
409
|
+
`${fence}${language}`,
|
|
410
|
+
content.trimEnd(),
|
|
411
|
+
fence,
|
|
412
|
+
].join("\n");
|
|
383
413
|
}
|
|
384
414
|
function renderCodeFileSection(record) {
|
|
385
415
|
const fence = fenceFor(record.content);
|
|
@@ -396,7 +426,7 @@ function renderCodeFileSection(record) {
|
|
|
396
426
|
"",
|
|
397
427
|
`${fence}${record.language}`,
|
|
398
428
|
record.content.trimEnd(),
|
|
399
|
-
fence
|
|
429
|
+
fence,
|
|
400
430
|
].join("\n");
|
|
401
431
|
}
|
|
402
432
|
function renderCodeFileIndex(records) {
|
|
@@ -406,7 +436,7 @@ function renderCodeFileIndex(records) {
|
|
|
406
436
|
return [
|
|
407
437
|
"| Path | Type | Lines | Characters | SHA256 | Summary |",
|
|
408
438
|
"|---|---:|---:|---:|---|---|",
|
|
409
|
-
...records.map((record) => `| ${escapeTableCell(record.relative)} | ${escapeTableCell(record.language)} | ${record.lines} | ${record.characters} | ${record.sha256.slice(0, 12)} | ${escapeTableCell(record.summary)} |`)
|
|
439
|
+
...records.map((record) => `| ${escapeTableCell(record.relative)} | ${escapeTableCell(record.language)} | ${record.lines} | ${record.characters} | ${record.sha256.slice(0, 12)} | ${escapeTableCell(record.summary)} |`),
|
|
410
440
|
].join("\n");
|
|
411
441
|
}
|
|
412
442
|
function buildImplementationGuide(records) {
|
|
@@ -432,7 +462,7 @@ function buildImplementationGuide(records) {
|
|
|
432
462
|
`- Main modules: ${modules || "no obvious modules identified"}.`,
|
|
433
463
|
`- Key entry points: ${entrypoints.length > 0 ? entrypoints.join(", ") : "no obvious entry points identified"}.`,
|
|
434
464
|
"- Each file block keeps the relative path, heuristic summary, line count, character count, SHA256 and redacted source body.",
|
|
435
|
-
"- This file is a temporary implementation snapshot, not durable Context; durable project facts still belong in project_context/**."
|
|
465
|
+
"- This file is a temporary implementation snapshot, not durable Context; durable project facts still belong in project_context/**.",
|
|
436
466
|
].join("\n");
|
|
437
467
|
}
|
|
438
468
|
function summarizeCodeFile(relative, content, language) {
|
|
@@ -448,17 +478,25 @@ function summarizeCodeFile(relative, content, language) {
|
|
|
448
478
|
if (base.toLowerCase() === "makefile") {
|
|
449
479
|
return summarizeMakefile(content);
|
|
450
480
|
}
|
|
451
|
-
if (base.toLowerCase().startsWith("dockerfile") ||
|
|
481
|
+
if (base.toLowerCase().startsWith("dockerfile") ||
|
|
482
|
+
lower.endsWith(".dockerfile")) {
|
|
452
483
|
const image = /^FROM\s+(.+)$/im.exec(content)?.[1]?.trim();
|
|
453
|
-
return image
|
|
484
|
+
return image
|
|
485
|
+
? `Defines a Docker image build starting from ${image}.`
|
|
486
|
+
: "Defines a Docker image build.";
|
|
454
487
|
}
|
|
455
|
-
if (lower.endsWith("docker-compose.yml") ||
|
|
488
|
+
if (lower.endsWith("docker-compose.yml") ||
|
|
489
|
+
lower.endsWith("docker-compose.yaml") ||
|
|
490
|
+
base.toLowerCase().startsWith("compose.")) {
|
|
456
491
|
const services = extractYamlTopLevelMapKeys(content, "services").slice(0, 6);
|
|
457
492
|
return services.length > 0
|
|
458
493
|
? `Defines Docker Compose services ${services.join(", ")}.`
|
|
459
494
|
: "Defines Docker Compose services and local runtime wiring.";
|
|
460
495
|
}
|
|
461
|
-
if (language === "yaml" ||
|
|
496
|
+
if (language === "yaml" ||
|
|
497
|
+
language === "toml" ||
|
|
498
|
+
language === "json" ||
|
|
499
|
+
language === "jsonc") {
|
|
462
500
|
return symbols.length > 0
|
|
463
501
|
? `Defines project configuration around ${symbols.slice(0, 6).join(", ")}.`
|
|
464
502
|
: "Defines project configuration used by the implementation or tooling.";
|
|
@@ -471,19 +509,30 @@ function summarizeCodeFile(relative, content, language) {
|
|
|
471
509
|
function extractSymbolSummary(content, language) {
|
|
472
510
|
const symbols = new Set();
|
|
473
511
|
const patterns = language === "python"
|
|
474
|
-
? [
|
|
512
|
+
? [
|
|
513
|
+
/^(?:async\s+)?def\s+([A-Za-z_][\w]*)/gm,
|
|
514
|
+
/^class\s+([A-Za-z_][\w]*)/gm,
|
|
515
|
+
]
|
|
475
516
|
: language === "go"
|
|
476
|
-
? [
|
|
517
|
+
? [
|
|
518
|
+
/^func\s+(?:\([^)]+\)\s*)?([A-Za-z_][\w]*)/gm,
|
|
519
|
+
/^type\s+([A-Za-z_][\w]*)/gm,
|
|
520
|
+
]
|
|
477
521
|
: language === "sql"
|
|
478
|
-
? [
|
|
479
|
-
|
|
522
|
+
? [
|
|
523
|
+
/\bCREATE\s+(?:OR\s+REPLACE\s+)?(?:TABLE|VIEW|FUNCTION|PROCEDURE|INDEX|TRIGGER)\s+([A-Za-z0-9_."]+)/gim,
|
|
524
|
+
]
|
|
525
|
+
: language === "yaml" ||
|
|
526
|
+
language === "toml" ||
|
|
527
|
+
language === "json" ||
|
|
528
|
+
language === "jsonc"
|
|
480
529
|
? [/^["']?([A-Za-z0-9_.-]+)["']?\s*[:=]/gm]
|
|
481
530
|
: [
|
|
482
531
|
/(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/g,
|
|
483
532
|
/(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/g,
|
|
484
533
|
/(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)/g,
|
|
485
534
|
/(?:export\s+)?type\s+([A-Za-z_$][\w$]*)/g,
|
|
486
|
-
/(?:export\s+)?const\s+([A-Za-z_$][\w$]*)/g
|
|
535
|
+
/(?:export\s+)?const\s+([A-Za-z_$][\w$]*)/g,
|
|
487
536
|
];
|
|
488
537
|
for (const pattern of patterns) {
|
|
489
538
|
let match;
|
|
@@ -505,7 +554,7 @@ function extractRouteSummary(content) {
|
|
|
505
554
|
const routePatterns = [
|
|
506
555
|
/\.(get|post|put|patch|delete|head|options)\s*\(\s*["'`]([^"'`]+)["'`]/gi,
|
|
507
556
|
/\bHandleFunc\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
508
|
-
/\bHandle\s*\(\s*["'`]([^"'`]+)["'`]/g
|
|
557
|
+
/\bHandle\s*\(\s*["'`]([^"'`]+)["'`]/g,
|
|
509
558
|
];
|
|
510
559
|
for (const pattern of routePatterns) {
|
|
511
560
|
let match;
|
|
@@ -521,7 +570,9 @@ function extractRouteSummary(content) {
|
|
|
521
570
|
return [...routes];
|
|
522
571
|
}
|
|
523
572
|
function summarizeMakefile(content) {
|
|
524
|
-
const targets = [...content.matchAll(/^([A-Za-z0-9_.-]+):/gm)]
|
|
573
|
+
const targets = [...content.matchAll(/^([A-Za-z0-9_.-]+):/gm)]
|
|
574
|
+
.map((match) => match[1])
|
|
575
|
+
.slice(0, 8);
|
|
525
576
|
return targets.length > 0
|
|
526
577
|
? `Defines Make targets ${targets.join(", ")} for local build, validation or automation.`
|
|
527
578
|
: "Defines Make targets for local build, validation or automation.";
|
|
@@ -551,10 +602,14 @@ function extractJsonString(content, key) {
|
|
|
551
602
|
}
|
|
552
603
|
function describeFilePurpose(relative, language) {
|
|
553
604
|
const lower = relative.toLowerCase();
|
|
554
|
-
if (lower.includes("/test") ||
|
|
605
|
+
if (lower.includes("/test") ||
|
|
606
|
+
lower.includes(".test.") ||
|
|
607
|
+
lower.includes(".spec.")) {
|
|
555
608
|
return `Contains ${language} tests for ${path.posix.basename(relative)}`;
|
|
556
609
|
}
|
|
557
|
-
if (lower.includes("/cli") ||
|
|
610
|
+
if (lower.includes("/cli") ||
|
|
611
|
+
lower.endsWith("/cli.ts") ||
|
|
612
|
+
lower.endsWith("/cli.js")) {
|
|
558
613
|
return `Implements ${language} CLI behavior for ${path.posix.basename(relative)}`;
|
|
559
614
|
}
|
|
560
615
|
if (lower.includes("/commands/")) {
|
|
@@ -563,7 +618,9 @@ function describeFilePurpose(relative, language) {
|
|
|
563
618
|
if (lower.includes("/lib/") || lower.includes("/internal/")) {
|
|
564
619
|
return `Implements ${language} library behavior for ${path.posix.basename(relative)}`;
|
|
565
620
|
}
|
|
566
|
-
if (lower.includes("/components/") ||
|
|
621
|
+
if (lower.includes("/components/") ||
|
|
622
|
+
lower.includes("/pages/") ||
|
|
623
|
+
lower.includes("/app/")) {
|
|
567
624
|
return `Implements ${language} UI or application behavior for ${path.posix.basename(relative)}`;
|
|
568
625
|
}
|
|
569
626
|
return `Contains ${language} implementation for ${path.posix.basename(relative)}`;
|
|
@@ -624,7 +681,9 @@ function languageFor(relative) {
|
|
|
624
681
|
if (base === "makefile") {
|
|
625
682
|
return "make";
|
|
626
683
|
}
|
|
627
|
-
if (base === "dockerfile" ||
|
|
684
|
+
if (base === "dockerfile" ||
|
|
685
|
+
base.startsWith("dockerfile.") ||
|
|
686
|
+
lower.endsWith(".dockerfile")) {
|
|
628
687
|
return "dockerfile";
|
|
629
688
|
}
|
|
630
689
|
if (SAFE_EXAMPLE_FILE_NAMES.has(base)) {
|
|
@@ -645,7 +704,9 @@ function languageFor(relative) {
|
|
|
645
704
|
if (lower.endsWith(".jsx")) {
|
|
646
705
|
return "jsx";
|
|
647
706
|
}
|
|
648
|
-
if (lower.endsWith(".js") ||
|
|
707
|
+
if (lower.endsWith(".js") ||
|
|
708
|
+
lower.endsWith(".mjs") ||
|
|
709
|
+
lower.endsWith(".cjs")) {
|
|
649
710
|
return "javascript";
|
|
650
711
|
}
|
|
651
712
|
if (lower.endsWith(".py")) {
|
|
@@ -699,7 +760,10 @@ function extractSection(content, heading) {
|
|
|
699
760
|
return body || undefined;
|
|
700
761
|
}
|
|
701
762
|
function timestampForFile(now) {
|
|
702
|
-
return now
|
|
763
|
+
return now
|
|
764
|
+
.toISOString()
|
|
765
|
+
.replace(/[-:]/g, "")
|
|
766
|
+
.replace(/\.\d{3}Z$/, "Z");
|
|
703
767
|
}
|
|
704
768
|
function repoRelative(root, file) {
|
|
705
769
|
return toPosix(path.relative(root, file));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ContextGraphSnapshot {
|
|
2
|
+
mode: "referenced" | "full";
|
|
3
|
+
topology_sha256: string;
|
|
4
|
+
files: string[];
|
|
5
|
+
sha256: Record<string, string>;
|
|
6
|
+
controlling_files: string[];
|
|
7
|
+
supporting_files: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface CampaignContextBaseline {
|
|
10
|
+
graph_sha256: string;
|
|
11
|
+
files: Record<string, string>;
|
|
12
|
+
baseline_sha256: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function captureContextGraphSnapshot(repositoryRoot: string, contextRefs: Iterable<string>, mode?: "referenced" | "full"): Promise<ContextGraphSnapshot>;
|
|
15
|
+
export declare function captureCampaignContextBaseline(repositoryRoot: string, contextRefs: Iterable<string>): Promise<CampaignContextBaseline>;
|
|
16
|
+
export declare function assertCampaignContextBaselineFresh(repositoryRoot: string, expected: CampaignContextBaseline, contextRefs: Iterable<string>): Promise<void>;
|
|
17
|
+
export declare function currentContextTopologySha256(repositoryRoot: string): Promise<string>;
|
|
18
|
+
export declare function listContextFiles(repositoryRoot: string): Promise<string[]>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { contextAuthorityTopologyHash } from "./long-task-context-authority-topology.js";
|
|
4
|
+
import { canonicalJson, sha256Hex } from "./strict-codec.js";
|
|
5
|
+
import { parseContextManifest, } from "./context-manifest-schema.js";
|
|
6
|
+
import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
|
|
7
|
+
const CONTEXT_MANIFEST_PATH = "project_context/context.toml";
|
|
8
|
+
const SUPPORTING_CONTEXT_ROLES = new Set(["implementation-index", "archive"]);
|
|
9
|
+
export async function captureContextGraphSnapshot(repositoryRoot, contextRefs, mode = "referenced") {
|
|
10
|
+
const root = path.resolve(repositoryRoot);
|
|
11
|
+
const manifest = await readContextManifest(root);
|
|
12
|
+
const allFiles = await listContextFiles(root);
|
|
13
|
+
const available = new Set(allFiles);
|
|
14
|
+
const selected = new Set();
|
|
15
|
+
const explicitlySelected = new Set();
|
|
16
|
+
if (mode === "full") {
|
|
17
|
+
allFiles.forEach((file) => selected.add(file));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
for (const required of [
|
|
21
|
+
CONTEXT_MANIFEST_PATH,
|
|
22
|
+
"project_context/global.md",
|
|
23
|
+
"project_context/architecture.md",
|
|
24
|
+
]) {
|
|
25
|
+
if (!available.has(required)) {
|
|
26
|
+
throw new Error(`context_snapshot_required_file_missing:${required}`);
|
|
27
|
+
}
|
|
28
|
+
selected.add(required);
|
|
29
|
+
explicitlySelected.add(required);
|
|
30
|
+
}
|
|
31
|
+
for (const contextRef of contextRefs) {
|
|
32
|
+
const normalized = normalizeRepoPath(contextRef);
|
|
33
|
+
if (!available.has(normalized)) {
|
|
34
|
+
throw new Error(`context_ref_invalid:${contextRef}`);
|
|
35
|
+
}
|
|
36
|
+
selected.add(normalized);
|
|
37
|
+
explicitlySelected.add(normalized);
|
|
38
|
+
}
|
|
39
|
+
addTransitiveChildren(manifest, selected, available);
|
|
40
|
+
}
|
|
41
|
+
const files = [...selected].sort();
|
|
42
|
+
const authorityTopologySha256 = contextAuthorityTopologyHash(manifest, files);
|
|
43
|
+
const hashes = Object.fromEntries(await Promise.all(files.map(async (file) => [
|
|
44
|
+
file,
|
|
45
|
+
file === CONTEXT_MANIFEST_PATH
|
|
46
|
+
? authorityTopologySha256
|
|
47
|
+
: await hashFile(root, path.join(root, file)),
|
|
48
|
+
])));
|
|
49
|
+
const classification = classifyContextFiles(manifest, files, explicitlySelected, mode);
|
|
50
|
+
return {
|
|
51
|
+
mode,
|
|
52
|
+
topology_sha256: authorityTopologySha256,
|
|
53
|
+
files,
|
|
54
|
+
sha256: sortRecord(hashes),
|
|
55
|
+
controlling_files: classification.controlling,
|
|
56
|
+
supporting_files: classification.supporting,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export async function captureCampaignContextBaseline(repositoryRoot, contextRefs) {
|
|
60
|
+
const snapshot = await captureContextGraphSnapshot(repositoryRoot, contextRefs, "referenced");
|
|
61
|
+
const files = sortRecord(snapshot.sha256);
|
|
62
|
+
return {
|
|
63
|
+
graph_sha256: snapshot.topology_sha256,
|
|
64
|
+
files,
|
|
65
|
+
baseline_sha256: sha256Hex(canonicalJson({ graph_sha256: snapshot.topology_sha256, files })),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export async function assertCampaignContextBaselineFresh(repositoryRoot, expected, contextRefs) {
|
|
69
|
+
const current = await captureCampaignContextBaseline(repositoryRoot, contextRefs);
|
|
70
|
+
if (current.graph_sha256 !== expected.graph_sha256) {
|
|
71
|
+
throw new Error("campaign_context_changed:graph");
|
|
72
|
+
}
|
|
73
|
+
if (current.baseline_sha256 !== expected.baseline_sha256) {
|
|
74
|
+
const changed = [
|
|
75
|
+
...new Set([
|
|
76
|
+
...Object.keys(expected.files),
|
|
77
|
+
...Object.keys(current.files),
|
|
78
|
+
]),
|
|
79
|
+
]
|
|
80
|
+
.filter((file) => expected.files[file] !== current.files[file])
|
|
81
|
+
.sort();
|
|
82
|
+
throw new Error(`campaign_context_changed:files:${changed.join(",") || "set"}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export async function currentContextTopologySha256(repositoryRoot) {
|
|
86
|
+
const root = path.resolve(repositoryRoot);
|
|
87
|
+
return contextAuthorityTopologyHash(await readContextManifest(root), await listContextFiles(root));
|
|
88
|
+
}
|
|
89
|
+
export async function listContextFiles(repositoryRoot) {
|
|
90
|
+
const root = path.resolve(repositoryRoot);
|
|
91
|
+
const contextRoot = path.join(root, "project_context");
|
|
92
|
+
const result = [];
|
|
93
|
+
async function visit(directory) {
|
|
94
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
95
|
+
const absolute = path.join(directory, entry.name);
|
|
96
|
+
if (entry.isSymbolicLink()) {
|
|
97
|
+
throw new Error(`protected_input_symlink_not_allowed:context_authority:${absolute.replace(/\\/gu, "/")}`);
|
|
98
|
+
}
|
|
99
|
+
if (entry.isDirectory()) {
|
|
100
|
+
await visit(absolute);
|
|
101
|
+
}
|
|
102
|
+
else if (entry.isFile()) {
|
|
103
|
+
await assertProtectedRepositoryFile(root, absolute, "context_authority");
|
|
104
|
+
result.push(repoRelative(root, absolute));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
await visit(contextRoot);
|
|
109
|
+
return result.sort();
|
|
110
|
+
}
|
|
111
|
+
async function readContextManifest(root) {
|
|
112
|
+
const manifestFile = await assertProtectedRepositoryFile(root, path.join(root, "project_context", "context.toml"), "context_manifest");
|
|
113
|
+
const parsed = parseContextManifest(await readFile(manifestFile, "utf8"));
|
|
114
|
+
if (!parsed.manifest || parsed.errors.length > 0) {
|
|
115
|
+
throw new Error(`context_manifest_invalid:${parsed.errors.join("|")}`);
|
|
116
|
+
}
|
|
117
|
+
return parsed.manifest;
|
|
118
|
+
}
|
|
119
|
+
function addTransitiveChildren(manifest, selected, available) {
|
|
120
|
+
const children = new Map(manifest.contexts.map((entry) => [
|
|
121
|
+
normalizeRepoPath(entry.path),
|
|
122
|
+
entry.default_children.map(normalizeRepoPath),
|
|
123
|
+
]));
|
|
124
|
+
const queue = [...selected];
|
|
125
|
+
while (queue.length > 0) {
|
|
126
|
+
const current = queue.shift();
|
|
127
|
+
for (const child of children.get(current) ?? []) {
|
|
128
|
+
if (!available.has(child)) {
|
|
129
|
+
throw new Error(`context_graph_child_missing:${current}:${child}`);
|
|
130
|
+
}
|
|
131
|
+
if (!selected.has(child)) {
|
|
132
|
+
selected.add(child);
|
|
133
|
+
queue.push(child);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function classifyContextFiles(manifest, files, explicitlySelected, mode) {
|
|
139
|
+
if (mode === "full")
|
|
140
|
+
return { controlling: [...files], supporting: [] };
|
|
141
|
+
const roles = new Map(manifest.contexts.map((entry) => [normalizeRepoPath(entry.path), entry.role]));
|
|
142
|
+
const controlling = [];
|
|
143
|
+
const supporting = [];
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
const role = roles.get(file);
|
|
146
|
+
if (explicitlySelected.has(file) ||
|
|
147
|
+
role === undefined ||
|
|
148
|
+
!SUPPORTING_CONTEXT_ROLES.has(role))
|
|
149
|
+
controlling.push(file);
|
|
150
|
+
else
|
|
151
|
+
supporting.push(file);
|
|
152
|
+
}
|
|
153
|
+
return { controlling, supporting };
|
|
154
|
+
}
|
|
155
|
+
async function hashFile(root, file) {
|
|
156
|
+
const protectedFile = await assertProtectedRepositoryFile(root, file, "context_authority");
|
|
157
|
+
return sha256Hex(await readFile(protectedFile));
|
|
158
|
+
}
|
|
159
|
+
function repoRelative(root, file) {
|
|
160
|
+
const value = path.relative(root, file).replace(/\\/g, "/");
|
|
161
|
+
if (value.startsWith("../") || path.isAbsolute(value)) {
|
|
162
|
+
throw new Error(`Path is outside repository: ${file}`);
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
function normalizeRepoPath(value) {
|
|
167
|
+
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
168
|
+
}
|
|
169
|
+
function sortRecord(value) {
|
|
170
|
+
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)));
|
|
171
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type ContextRole = "global" | "architecture" | "area" | "domain" | "subdomain" | "foundation" | "archive" | "contract" | "verification" | "deployment" | "implementation-index" | "decision-rationale";
|
|
2
|
+
export interface ContextAreaEntry {
|
|
3
|
+
line: number;
|
|
4
|
+
id: string;
|
|
5
|
+
root: string;
|
|
6
|
+
context: string;
|
|
7
|
+
kind?: string;
|
|
8
|
+
default: boolean;
|
|
9
|
+
forbidden_runtime_dependencies: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface ContextNodeEntry {
|
|
12
|
+
line: number;
|
|
13
|
+
path: string;
|
|
14
|
+
role: string;
|
|
15
|
+
read_when?: string;
|
|
16
|
+
read_policy?: string;
|
|
17
|
+
triggers: string[];
|
|
18
|
+
default_children: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface ContextManifest {
|
|
21
|
+
areas: ContextAreaEntry[];
|
|
22
|
+
contexts: ContextNodeEntry[];
|
|
23
|
+
}
|
|
24
|
+
export interface ContextManifestParseResult {
|
|
25
|
+
manifest?: ContextManifest;
|
|
26
|
+
errors: string[];
|
|
27
|
+
}
|
|
28
|
+
export declare function parseContextManifest(content: string, file?: string): ContextManifestParseResult;
|