@warpgogol/forge 5.2.4 → 5.3.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/AGENTS.md +32 -2
- package/README.md +1 -1
- package/README.uk.md +8 -8
- package/bin/cli.ts +1 -0
- package/dist/bin/cli.js +1 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/os/adr/adr.module.d.ts.map +1 -1
- package/dist/os/adr/adr.module.js +8 -3
- package/dist/os/adr/adr.module.js.map +1 -1
- package/dist/os/adr/frontmatter-io.js +5 -5
- package/dist/os/adr/frontmatter-io.js.map +1 -1
- package/dist/os/adr/handlers/archive.d.ts.map +1 -1
- package/dist/os/adr/handlers/archive.js +10 -4
- package/dist/os/adr/handlers/archive.js.map +1 -1
- package/dist/os/adr/handlers/implement-stamp.d.ts.map +1 -1
- package/dist/os/adr/handlers/implement-stamp.js +10 -9
- package/dist/os/adr/handlers/implement-stamp.js.map +1 -1
- package/dist/os/adr/handlers/list-create.js +5 -5
- package/dist/os/adr/handlers/list-create.js.map +1 -1
- package/dist/os/adr/handlers/validate.js +1 -1
- package/dist/os/adr/handlers/validate.js.map +1 -1
- package/dist/os/audit/audit.module.js +3 -3
- package/dist/os/audit/audit.module.js.map +1 -1
- package/dist/os/audit/frontmatter-io.js +5 -5
- package/dist/os/audit/frontmatter-io.js.map +1 -1
- package/dist/os/audit/handlers/archive.d.ts.map +1 -1
- package/dist/os/audit/handlers/archive.js +10 -4
- package/dist/os/audit/handlers/archive.js.map +1 -1
- package/dist/os/compass/compass.module.d.ts.map +1 -1
- package/dist/os/compass/compass.module.js +17 -1
- package/dist/os/compass/compass.module.js.map +1 -1
- package/dist/os/compass/handlers/compass-audit-handler.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-audit-handler.js +12 -5
- package/dist/os/compass/handlers/compass-audit-handler.js.map +1 -1
- package/dist/os/compass/handlers/compass-change-summary-handler.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-change-summary-handler.js +3 -2
- package/dist/os/compass/handlers/compass-change-summary-handler.js.map +1 -1
- package/dist/os/compass/handlers/compass-inventory-handler.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-inventory-handler.js +6 -4
- package/dist/os/compass/handlers/compass-inventory-handler.js.map +1 -1
- package/dist/os/compass/handlers/compass-inventory.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-inventory.js +10 -8
- package/dist/os/compass/handlers/compass-inventory.js.map +1 -1
- package/dist/os/compass/handlers/compass-migrate-handler.js +1 -1
- package/dist/os/compass/handlers/compass-migrate-handler.js.map +1 -1
- package/dist/os/compass/handlers/compass-migrate.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-migrate.js +6 -3
- package/dist/os/compass/handlers/compass-migrate.js.map +1 -1
- package/dist/os/compass/handlers/git-revision.d.ts.map +1 -1
- package/dist/os/compass/handlers/git-revision.js +9 -6
- package/dist/os/compass/handlers/git-revision.js.map +1 -1
- package/dist/os/compass/handlers/resolve-scan-root.js +1 -1
- package/dist/os/compass/handlers/resolve-scan-root.js.map +1 -1
- package/dist/os/compass/handlers/summary-record.d.ts.map +1 -1
- package/dist/os/compass/handlers/summary-record.js +8 -4
- package/dist/os/compass/handlers/summary-record.js.map +1 -1
- package/dist/os/core/core.module.d.ts.map +1 -1
- package/dist/os/core/core.module.js +43 -4
- package/dist/os/core/core.module.js.map +1 -1
- package/dist/os/core/handlers/assets-helpers.d.ts.map +1 -1
- package/dist/os/core/handlers/assets-helpers.js +6 -3
- package/dist/os/core/handlers/assets-helpers.js.map +1 -1
- package/dist/os/core/handlers/build.js +1 -1
- package/dist/os/core/handlers/build.js.map +1 -1
- package/dist/os/core/handlers/determinism-check.d.ts.map +1 -1
- package/dist/os/core/handlers/determinism-check.js +10 -6
- package/dist/os/core/handlers/determinism-check.js.map +1 -1
- package/dist/os/core/handlers/dev.js +1 -1
- package/dist/os/core/handlers/dev.js.map +1 -1
- package/dist/os/core/handlers/file-size-lint.d.ts.map +1 -1
- package/dist/os/core/handlers/file-size-lint.js +11 -7
- package/dist/os/core/handlers/file-size-lint.js.map +1 -1
- package/dist/os/core/handlers/forge-autonomy-validate.d.ts.map +1 -1
- package/dist/os/core/handlers/forge-autonomy-validate.js +9 -5
- package/dist/os/core/handlers/forge-autonomy-validate.js.map +1 -1
- package/dist/os/core/handlers/knowledge-compact.js +1 -1
- package/dist/os/core/handlers/knowledge-compact.js.map +1 -1
- package/dist/os/core/handlers/package-health.js +1 -1
- package/dist/os/core/handlers/package-health.js.map +1 -1
- package/dist/os/core/handlers/pinned-check.js +2 -2
- package/dist/os/core/handlers/pinned-check.js.map +1 -1
- package/dist/os/core/handlers/pinned-init.d.ts.map +1 -1
- package/dist/os/core/handlers/pinned-init.js +9 -8
- package/dist/os/core/handlers/pinned-init.js.map +1 -1
- package/dist/os/core/handlers/pinned-validate.js +3 -3
- package/dist/os/core/handlers/pinned-validate.js.map +1 -1
- package/dist/os/core/handlers/profile-resolve.js +1 -1
- package/dist/os/core/handlers/profile-resolve.js.map +1 -1
- package/dist/os/core/handlers/public-surface.js +1 -1
- package/dist/os/core/handlers/public-surface.js.map +1 -1
- package/dist/os/core/handlers/release-prepare.d.ts.map +1 -1
- package/dist/os/core/handlers/release-prepare.js +10 -6
- package/dist/os/core/handlers/release-prepare.js.map +1 -1
- package/dist/os/core/handlers/release-publish.js +6 -6
- package/dist/os/core/handlers/release-publish.js.map +1 -1
- package/dist/os/core/handlers/validate.js +1 -1
- package/dist/os/core/handlers/validate.js.map +1 -1
- package/dist/os/exploration/exploration.module.d.ts.map +1 -1
- package/dist/os/exploration/exploration.module.js +1 -0
- package/dist/os/exploration/exploration.module.js.map +1 -1
- package/dist/os/exploration/frontmatter-io.js +4 -4
- package/dist/os/exploration/frontmatter-io.js.map +1 -1
- package/dist/os/exploration/handlers/archive.js +3 -3
- package/dist/os/exploration/handlers/archive.js.map +1 -1
- package/dist/os/mission/handlers/archive.d.ts.map +1 -1
- package/dist/os/mission/handlers/archive.js +22 -19
- package/dist/os/mission/handlers/archive.js.map +1 -1
- package/dist/os/mission/mission.module.js +3 -3
- package/dist/os/mission/mission.module.js.map +1 -1
- package/dist/os/naming/naming-convention.d.ts.map +1 -1
- package/dist/os/naming/naming-convention.js +7 -3
- package/dist/os/naming/naming-convention.js.map +1 -1
- package/dist/os/naming/naming.module.d.ts.map +1 -1
- package/dist/os/naming/naming.module.js +4 -3
- package/dist/os/naming/naming.module.js.map +1 -1
- package/dist/os/notes/notes.module.d.ts.map +1 -1
- package/dist/os/notes/notes.module.js +6 -3
- package/dist/os/notes/notes.module.js.map +1 -1
- package/dist/os/plan/frontmatter-io.js +5 -5
- package/dist/os/plan/frontmatter-io.js.map +1 -1
- package/dist/os/plan/handlers/archive.d.ts.map +1 -1
- package/dist/os/plan/handlers/archive.js +10 -4
- package/dist/os/plan/handlers/archive.js.map +1 -1
- package/dist/os/plan/plan.module.js +3 -3
- package/dist/os/plan/plan.module.js.map +1 -1
- package/dist/os/plugin/plugin.module.d.ts.map +1 -1
- package/dist/os/plugin/plugin.module.js +6 -3
- package/dist/os/plugin/plugin.module.js.map +1 -1
- package/dist/os/program/discovery.js +2 -2
- package/dist/os/program/discovery.js.map +1 -1
- package/dist/os/program/handlers/complete.js +1 -1
- package/dist/os/program/handlers/complete.js.map +1 -1
- package/dist/os/program/handlers/lease.js +1 -1
- package/dist/os/program/handlers/lease.js.map +1 -1
- package/dist/os/program/handlers/seal.js +1 -1
- package/dist/os/program/handlers/seal.js.map +1 -1
- package/dist/os/program/lease.js +1 -1
- package/dist/os/program/lease.js.map +1 -1
- package/dist/os/program/program.module.d.ts.map +1 -1
- package/dist/os/program/program.module.js +9 -3
- package/dist/os/program/program.module.js.map +1 -1
- package/dist/os/queue/index.js +1 -1
- package/dist/os/queue/manifest.d.ts.map +1 -1
- package/dist/os/queue/manifest.js +4 -8
- package/dist/os/queue/manifest.js.map +1 -1
- package/dist/os/rfc/acceptance.d.ts.map +1 -1
- package/dist/os/rfc/acceptance.js +10 -6
- package/dist/os/rfc/acceptance.js.map +1 -1
- package/dist/os/rfc/decision-log.d.ts.map +1 -1
- package/dist/os/rfc/decision-log.js +4 -3
- package/dist/os/rfc/decision-log.js.map +1 -1
- package/dist/os/rfc/dna-trace.d.ts.map +1 -1
- package/dist/os/rfc/dna-trace.js +7 -2
- package/dist/os/rfc/dna-trace.js.map +1 -1
- package/dist/os/rfc/frontmatter-io.js +5 -5
- package/dist/os/rfc/frontmatter-io.js.map +1 -1
- package/dist/os/rfc/handlers/archive.d.ts.map +1 -1
- package/dist/os/rfc/handlers/archive.js +11 -5
- package/dist/os/rfc/handlers/archive.js.map +1 -1
- package/dist/os/rfc/handlers/check.d.ts.map +1 -1
- package/dist/os/rfc/handlers/check.js +6 -3
- package/dist/os/rfc/handlers/check.js.map +1 -1
- package/dist/os/rfc/handlers/implement-stamp.d.ts.map +1 -1
- package/dist/os/rfc/handlers/implement-stamp.js +11 -10
- package/dist/os/rfc/handlers/implement-stamp.js.map +1 -1
- package/dist/os/rfc/handlers/index-graph.js +3 -3
- package/dist/os/rfc/handlers/index-graph.js.map +1 -1
- package/dist/os/rfc/handlers/lifecycle.d.ts.map +1 -1
- package/dist/os/rfc/handlers/lifecycle.js +6 -2
- package/dist/os/rfc/handlers/lifecycle.js.map +1 -1
- package/dist/os/rfc/handlers/list-create.d.ts.map +1 -1
- package/dist/os/rfc/handlers/list-create.js +7 -6
- package/dist/os/rfc/handlers/list-create.js.map +1 -1
- package/dist/os/rfc/handlers/shared.js +3 -3
- package/dist/os/rfc/handlers/shared.js.map +1 -1
- package/dist/os/rfc/handlers/supersede-propose.d.ts.map +1 -1
- package/dist/os/rfc/handlers/supersede-propose.js +7 -3
- package/dist/os/rfc/handlers/supersede-propose.js.map +1 -1
- package/dist/os/rfc/handlers/validate-rules.d.ts.map +1 -1
- package/dist/os/rfc/handlers/validate-rules.js +8 -5
- package/dist/os/rfc/handlers/validate-rules.js.map +1 -1
- package/dist/os/rfc/rfc.module.d.ts.map +1 -1
- package/dist/os/rfc/rfc.module.js +44 -4
- package/dist/os/rfc/rfc.module.js.map +1 -1
- package/dist/os/rfc/verification-evidence.d.ts.map +1 -1
- package/dist/os/rfc/verification-evidence.js +9 -5
- package/dist/os/rfc/verification-evidence.js.map +1 -1
- package/dist/os/rfc/verification-refresh.d.ts.map +1 -1
- package/dist/os/rfc/verification-refresh.js +4 -3
- package/dist/os/rfc/verification-refresh.js.map +1 -1
- package/dist/os/session/frontmatter-io.js +12 -12
- package/dist/os/session/frontmatter-io.js.map +1 -1
- package/dist/os/session/handlers/archive.d.ts.map +1 -1
- package/dist/os/session/handlers/archive.js +10 -4
- package/dist/os/session/handlers/archive.js.map +1 -1
- package/dist/os/session/handlers/metrics-aggregate.d.ts.map +1 -1
- package/dist/os/session/handlers/metrics-aggregate.js +9 -5
- package/dist/os/session/handlers/metrics-aggregate.js.map +1 -1
- package/dist/os/session/handlers/metrics-rfc.d.ts.map +1 -1
- package/dist/os/session/handlers/metrics-rfc.js +11 -8
- package/dist/os/session/handlers/metrics-rfc.js.map +1 -1
- package/dist/os/session/handlers/metrics-session.d.ts.map +1 -1
- package/dist/os/session/handlers/metrics-session.js +6 -3
- package/dist/os/session/handlers/metrics-session.js.map +1 -1
- package/dist/os/session/handlers/save.d.ts.map +1 -1
- package/dist/os/session/handlers/save.js +14 -8
- package/dist/os/session/handlers/save.js.map +1 -1
- package/dist/os/session/session.module.d.ts.map +1 -1
- package/dist/os/session/session.module.js +6 -0
- package/dist/os/session/session.module.js.map +1 -1
- package/dist/os/spec/live-spec-list.js +4 -4
- package/dist/os/spec/live-spec-list.js.map +1 -1
- package/dist/os/spec/live-spec-merge.js +5 -5
- package/dist/os/spec/live-spec-merge.js.map +1 -1
- package/dist/os/spec/live-spec-show.js +3 -3
- package/dist/os/spec/live-spec-show.js.map +1 -1
- package/dist/os/spec/live-spec-validate.js +4 -4
- package/dist/os/spec/live-spec-validate.js.map +1 -1
- package/dist/os/spec/spec-materialize.js +9 -9
- package/dist/os/spec/spec-materialize.js.map +1 -1
- package/dist/os/spec/spec-status.js +9 -9
- package/dist/os/spec/spec-status.js.map +1 -1
- package/dist/os/spec/spec-validate.js +7 -7
- package/dist/os/spec/spec-validate.js.map +1 -1
- package/dist/os/spec/spec.module.d.ts.map +1 -1
- package/dist/os/spec/spec.module.js +8 -0
- package/dist/os/spec/spec.module.js.map +1 -1
- package/dist/os/werkstatt/handlers/lock.js +16 -16
- package/dist/os/werkstatt/handlers/lock.js.map +1 -1
- package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js +3 -3
- package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js.map +1 -1
- package/dist/os/werkstatt/handlers/werkstatt-operation-validate.d.ts.map +1 -1
- package/dist/os/werkstatt/handlers/werkstatt-operation-validate.js +3 -2
- package/dist/os/werkstatt/handlers/werkstatt-operation-validate.js.map +1 -1
- package/dist/os/werkstatt/werkstatt.module.d.ts.map +1 -1
- package/dist/os/werkstatt/werkstatt.module.js +6 -2
- package/dist/os/werkstatt/werkstatt.module.js.map +1 -1
- package/dist/os/workflow/handlers.d.ts.map +1 -1
- package/dist/os/workflow/handlers.js +8 -5
- package/dist/os/workflow/handlers.js.map +1 -1
- package/dist/os/workflow/workflow.module.d.ts.map +1 -1
- package/dist/os/workflow/workflow.module.js +6 -3
- package/dist/os/workflow/workflow.module.js.map +1 -1
- package/dist/src/compass/contract-registry.js +1 -1
- package/dist/src/compass/contract-registry.js.map +1 -1
- package/dist/src/config/forge-config.d.ts +1 -1
- package/dist/src/config/forge-config.js +1 -1
- package/dist/src/config/forge-config.js.map +1 -1
- package/dist/src/knowledge/budgets.js +1 -1
- package/dist/src/knowledge/budgets.js.map +1 -1
- package/dist/src/knowledge/compact.js +1 -1
- package/dist/src/knowledge/compact.js.map +1 -1
- package/dist/src/knowledge/parse.js +1 -1
- package/dist/src/knowledge/parse.js.map +1 -1
- package/dist/src/knowledge/sync.d.ts.map +1 -1
- package/dist/src/knowledge/sync.js +3 -5
- package/dist/src/knowledge/sync.js.map +1 -1
- package/dist/src/migration-adapters/git-utils.js +2 -2
- package/dist/src/migration-adapters/git-utils.js.map +1 -1
- package/dist/src/migration-adapters/ignored-files.js +2 -2
- package/dist/src/migration-adapters/ignored-files.js.map +1 -1
- package/dist/src/migration-adapters/node-typescript-pnpm/index.js +1 -1
- package/dist/src/migration-adapters/node-typescript-pnpm/index.js.map +1 -1
- package/dist/src/migration-adapters/phaser-pnpm/index.js +1 -1
- package/dist/src/migration-adapters/phaser-pnpm/index.js.map +1 -1
- package/dist/src/onboarding/agents-generate.d.ts.map +1 -1
- package/dist/src/onboarding/agents-generate.js +60 -31
- package/dist/src/onboarding/agents-generate.js.map +1 -1
- package/dist/src/onboarding/create.d.ts.map +1 -1
- package/dist/src/onboarding/create.js +10 -10
- package/dist/src/onboarding/create.js.map +1 -1
- package/dist/src/onboarding/doctor.d.ts.map +1 -1
- package/dist/src/onboarding/doctor.js +68 -59
- package/dist/src/onboarding/doctor.js.map +1 -1
- package/dist/src/onboarding/init.d.ts.map +1 -1
- package/dist/src/onboarding/init.js +30 -1
- package/dist/src/onboarding/init.js.map +1 -1
- package/dist/src/onboarding/invariant-engine.js +1 -1
- package/dist/src/onboarding/invariant-engine.js.map +1 -1
- package/dist/src/onboarding/memory-compact.d.ts.map +1 -1
- package/dist/src/onboarding/memory-compact.js +5 -4
- package/dist/src/onboarding/memory-compact.js.map +1 -1
- package/dist/src/onboarding/memory-scaffold.js +1 -1
- package/dist/src/onboarding/memory-scaffold.js.map +1 -1
- package/dist/src/onboarding/nested-agents-generate.d.ts +4 -1
- package/dist/src/onboarding/nested-agents-generate.d.ts.map +1 -1
- package/dist/src/onboarding/nested-agents-generate.js +34 -5
- package/dist/src/onboarding/nested-agents-generate.js.map +1 -1
- package/dist/src/onboarding/nested-agents-templates.d.ts.map +1 -1
- package/dist/src/onboarding/nested-agents-templates.js +8 -12
- package/dist/src/onboarding/nested-agents-templates.js.map +1 -1
- package/dist/src/onboarding/npm-token-check.js +2 -2
- package/dist/src/onboarding/npm-token-check.js.map +1 -1
- package/dist/src/onboarding/prettierignore.d.ts +29 -0
- package/dist/src/onboarding/prettierignore.d.ts.map +1 -0
- package/dist/src/onboarding/prettierignore.js +231 -0
- package/dist/src/onboarding/prettierignore.js.map +1 -0
- package/dist/src/onboarding/profile-validate.d.ts.map +1 -1
- package/dist/src/onboarding/profile-validate.js +5 -4
- package/dist/src/onboarding/profile-validate.js.map +1 -1
- package/dist/src/onboarding/scaffold-project.d.ts.map +1 -1
- package/dist/src/onboarding/scaffold-project.js +35 -27
- package/dist/src/onboarding/scaffold-project.js.map +1 -1
- package/dist/src/onboarding/scaffold.js +1 -1
- package/dist/src/onboarding/scaffold.js.map +1 -1
- package/dist/src/onboarding/skill-markers.d.ts +27 -0
- package/dist/src/onboarding/skill-markers.d.ts.map +1 -0
- package/dist/src/onboarding/skill-markers.js +131 -0
- package/dist/src/onboarding/skill-markers.js.map +1 -0
- package/dist/src/onboarding/upgrade.d.ts +10 -0
- package/dist/src/onboarding/upgrade.d.ts.map +1 -1
- package/dist/src/onboarding/upgrade.js +215 -74
- package/dist/src/onboarding/upgrade.js.map +1 -1
- package/dist/src/onboarding/workspace-discovery.js +1 -1
- package/dist/src/onboarding/workspace-discovery.js.map +1 -1
- package/dist/src/pipeline-status.d.ts +6 -5
- package/dist/src/pipeline-status.d.ts.map +1 -1
- package/dist/src/pipeline-status.js +24 -19
- package/dist/src/pipeline-status.js.map +1 -1
- package/dist/src/profiles/stack-profile.js +1 -1
- package/dist/src/profiles/stack-profile.js.map +1 -1
- package/dist/src/registry.d.ts +1 -1
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +3 -3
- package/dist/src/registry.js.map +1 -1
- package/dist/src/skill-schema.d.ts +1 -1
- package/dist/src/skill-schema.d.ts.map +1 -1
- package/dist/src/skill-schema.js +4 -1
- package/dist/src/skill-schema.js.map +1 -1
- package/dist/src/types.d.ts +116 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/editable-region.d.ts +46 -0
- package/dist/src/utils/editable-region.d.ts.map +1 -0
- package/dist/src/utils/editable-region.js +125 -0
- package/dist/src/utils/editable-region.js.map +1 -0
- package/dist/src/utils/fs-idempotent.d.ts +2 -1
- package/dist/src/utils/fs-idempotent.d.ts.map +1 -1
- package/dist/src/utils/fs-idempotent.js +11 -7
- package/dist/src/utils/fs-idempotent.js.map +1 -1
- package/dist/src/utils/generated-marker.d.ts +17 -0
- package/dist/src/utils/generated-marker.d.ts.map +1 -1
- package/dist/src/utils/generated-marker.js +29 -1
- package/dist/src/utils/generated-marker.js.map +1 -1
- package/dist/src/utils/index.d.ts +3 -1
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +3 -1
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/io.d.ts +9 -0
- package/dist/src/utils/io.d.ts.map +1 -0
- package/dist/src/utils/io.js +130 -0
- package/dist/src/utils/io.js.map +1 -0
- package/dist/src/utils/markdown-table.d.ts +21 -0
- package/dist/src/utils/markdown-table.d.ts.map +1 -0
- package/dist/src/utils/markdown-table.js +116 -0
- package/dist/src/utils/markdown-table.js.map +1 -0
- package/dist/src/utils/sync-fs.d.ts +5 -0
- package/dist/src/utils/sync-fs.d.ts.map +1 -0
- package/dist/src/utils/sync-fs.js +26 -0
- package/dist/src/utils/sync-fs.js.map +1 -0
- package/dist/src/validators/note-frontmatter-validate.js +1 -1
- package/dist/src/validators/note-frontmatter-validate.js.map +1 -1
- package/dist/src/validators/note-link-validate.js +1 -1
- package/dist/src/validators/note-link-validate.js.map +1 -1
- package/dist/src/validators/note-orphan-detect.js +1 -1
- package/dist/src/validators/note-orphan-detect.js.map +1 -1
- package/dist/src/validators/port-validate.js +1 -1
- package/dist/src/validators/port-validate.js.map +1 -1
- package/dist/src/validators/skill-validate.d.ts.map +1 -1
- package/dist/src/validators/skill-validate.js +34 -13
- package/dist/src/validators/skill-validate.js.map +1 -1
- package/os/adr/adr.module.ts +136 -132
- package/os/adr/frontmatter-io.ts +5 -5
- package/os/adr/handlers/archive.ts +8 -4
- package/os/adr/handlers/implement-stamp.ts +14 -9
- package/os/adr/handlers/list-create.ts +5 -5
- package/os/adr/handlers/validate.ts +1 -1
- package/os/audit/audit.module.ts +33 -34
- package/os/audit/frontmatter-io.ts +5 -5
- package/os/audit/handlers/archive.ts +8 -4
- package/os/compass/compass.module.ts +17 -1
- package/os/compass/handlers/compass-audit-handler.ts +13 -5
- package/os/compass/handlers/compass-change-summary-handler.ts +4 -2
- package/os/compass/handlers/compass-inventory-handler.ts +7 -4
- package/os/compass/handlers/compass-inventory.ts +11 -8
- package/os/compass/handlers/compass-migrate-handler.ts +1 -1
- package/os/compass/handlers/compass-migrate.ts +7 -3
- package/os/compass/handlers/git-revision.ts +11 -6
- package/os/compass/handlers/resolve-scan-root.ts +1 -1
- package/os/compass/handlers/summary-record.ts +10 -4
- package/os/compass/handlers/tests/compass-audit-plan.test.ts +2 -1
- package/os/core/core.module.ts +43 -4
- package/os/core/handlers/assets-helpers.ts +7 -3
- package/os/core/handlers/build.ts +1 -1
- package/os/core/handlers/determinism-check.ts +12 -6
- package/os/core/handlers/dev.ts +1 -1
- package/os/core/handlers/file-size-lint.ts +16 -9
- package/os/core/handlers/forge-autonomy-validate.ts +10 -5
- package/os/core/handlers/knowledge-compact.ts +1 -1
- package/os/core/handlers/package-health.ts +1 -1
- package/os/core/handlers/pinned-check.ts +2 -2
- package/os/core/handlers/pinned-init.ts +9 -8
- package/os/core/handlers/pinned-validate.ts +3 -3
- package/os/core/handlers/profile-resolve.ts +1 -1
- package/os/core/handlers/public-surface.ts +1 -1
- package/os/core/handlers/release-prepare.ts +11 -6
- package/os/core/handlers/release-publish.ts +6 -6
- package/os/core/handlers/validate.ts +1 -1
- package/os/exploration/exploration.module.ts +1 -0
- package/os/exploration/frontmatter-io.ts +4 -4
- package/os/exploration/handlers/archive.ts +3 -3
- package/os/mission/handlers/archive.ts +27 -19
- package/os/mission/mission.module.ts +38 -39
- package/os/naming/naming-convention.ts +9 -3
- package/os/naming/naming.module.ts +25 -25
- package/os/notes/notes.module.ts +87 -85
- package/os/plan/frontmatter-io.ts +5 -5
- package/os/plan/handlers/archive.ts +8 -4
- package/os/plan/plan.module.ts +33 -34
- package/os/plugin/plugin.module.ts +8 -6
- package/os/program/discovery.ts +2 -2
- package/os/program/handlers/complete.ts +1 -1
- package/os/program/handlers/lease.ts +1 -1
- package/os/program/handlers/seal.ts +1 -1
- package/os/program/lease.ts +1 -1
- package/os/program/program.module.ts +198 -193
- package/os/queue/index.ts +1 -1
- package/os/queue/manifest.ts +10 -9
- package/os/queue/queue-validate.test.ts +6 -15
- package/os/rfc/acceptance.ts +12 -6
- package/os/rfc/decision-log.ts +4 -3
- package/os/rfc/dna-trace.ts +8 -2
- package/os/rfc/frontmatter-io.ts +5 -5
- package/os/rfc/handlers/archive.ts +9 -5
- package/os/rfc/handlers/check.ts +5 -3
- package/os/rfc/handlers/implement-stamp.ts +15 -10
- package/os/rfc/handlers/index-graph.ts +3 -3
- package/os/rfc/handlers/lifecycle.ts +7 -2
- package/os/rfc/handlers/list-create.ts +7 -6
- package/os/rfc/handlers/shared.ts +3 -3
- package/os/rfc/handlers/supersede-propose.ts +8 -3
- package/os/rfc/handlers/validate-rules-rfc1006.test.ts +18 -6
- package/os/rfc/handlers/validate-rules.ts +10 -5
- package/os/rfc/rfc-create-concurrent.test.ts +11 -3
- package/os/rfc/rfc.module.ts +413 -374
- package/os/rfc/verification-evidence.ts +11 -5
- package/os/rfc/verification-refresh.ts +4 -3
- package/os/session/frontmatter-io.ts +14 -14
- package/os/session/handlers/archive.ts +8 -4
- package/os/session/handlers/metrics-aggregate.ts +34 -13
- package/os/session/handlers/metrics-rfc.ts +13 -8
- package/os/session/handlers/metrics-session.ts +7 -3
- package/os/session/handlers/save.ts +12 -8
- package/os/session/session.module.ts +6 -0
- package/os/spec/live-spec-list.ts +4 -4
- package/os/spec/live-spec-merge.ts +5 -5
- package/os/spec/live-spec-show.ts +3 -3
- package/os/spec/live-spec-validate.ts +4 -4
- package/os/spec/spec-materialize.ts +9 -9
- package/os/spec/spec-status.ts +9 -9
- package/os/spec/spec-validate.ts +7 -7
- package/os/spec/spec.module.ts +8 -0
- package/os/werkstatt/handlers/lock.ts +16 -16
- package/os/werkstatt/handlers/werkstatt-lock-recover.ts +3 -3
- package/os/werkstatt/handlers/werkstatt-operation-validate.ts +4 -2
- package/os/werkstatt/werkstatt.module.ts +8 -5
- package/os/workflow/handlers.ts +7 -5
- package/os/workflow/workflow.module.ts +45 -44
- package/package.json +1 -1
- package/skills/fo/fo-add-tests/SKILL.md +2 -2
- package/skills/fo/fo-architecture/SKILL.md +2 -2
- package/skills/fo/fo-compass-annotate/SKILL.md +2 -2
- package/skills/fo/fo-design-summit/SKILL.md +2 -2
- package/skills/fo/fo-doc-audit/SKILL.md +2 -2
- package/skills/fo/fo-explore/SKILL.md +2 -2
- package/skills/fo/fo-extract-dna/SKILL.md +2 -2
- package/skills/fo/fo-fix/SKILL.md +2 -2
- package/skills/fo/fo-handoff/SKILL.md +2 -2
- package/skills/fo/fo-harvest/SKILL.md +2 -2
- package/skills/fo/fo-idea/SKILL.md +2 -2
- package/skills/fo/fo-idea-audit/SKILL.md +2 -2
- package/skills/fo/fo-idea-create-adr/SKILL.md +3 -3
- package/skills/fo/fo-idea-create-rfc/SKILL.md +3 -3
- package/skills/fo/fo-idea-enhance/SKILL.md +2 -2
- package/skills/fo/fo-idea-i-just-want-to-see-the-plan/SKILL.md +2 -2
- package/skills/fo/fo-idea-i-just-want-to-see-the-result/SKILL.md +1 -1
- package/skills/fo/fo-idea-implement/SKILL.md +2 -2
- package/skills/fo/fo-idea-plan/SKILL.md +2 -2
- package/skills/fo/fo-idea-status/SKILL.md +2 -2
- package/skills/fo/fo-knowledge-distill/SKILL.md +2 -2
- package/skills/fo/fo-memory-sync/SKILL.md +2 -2
- package/skills/fo/fo-qa/SKILL.md +2 -2
- package/skills/fo/fo-review/SKILL.md +2 -2
- package/skills/fo/fo-session-retro/SKILL.md +2 -2
- package/skills/fo/fo-session-save/SKILL.md +2 -2
- package/skills/fo/fo-spec-ingest/SKILL.md +2 -2
- package/skills/fo/fo-step-commit/SKILL.md +1 -1
- package/skills/fo/fo-triage/SKILL.md +2 -2
- package/skills/meta/forge-bootstrap/SKILL.md +1 -1
- package/skills/meta/port-to-forge/SKILL.md +1 -1
- package/skills/meta/skill-create/SKILL.md +1 -1
- package/skills/shared/grilling/SKILL.md +1 -1
- package/skills/shared/grilling/learned-principles.archive.md +17 -0
- package/skills/shared/grilling/learned-principles.md +0 -1
- package/skills/shared/knowledge/learned-principles.md +6 -0
- package/skills/shared/my-preferences/SKILL.md +1 -1
- package/skills/shared/windows-ai-tooling/SKILL.md +1 -1
- package/skills/shared/writing-great-skills/SKILL.md +1 -1
- package/src/compass/contract-registry.ts +1 -1
- package/src/config/forge-config.ts +1 -1
- package/src/knowledge/__tests__/sync.test.ts +2 -7
- package/src/knowledge/budgets.ts +1 -1
- package/src/knowledge/compact.ts +1 -1
- package/src/knowledge/parse.ts +1 -1
- package/src/knowledge/sync.ts +3 -5
- package/src/migration-adapters/git-utils.ts +2 -2
- package/src/migration-adapters/ignored-files.ts +2 -2
- package/src/migration-adapters/node-typescript-pnpm/index.ts +1 -1
- package/src/migration-adapters/phaser-pnpm/index.ts +1 -1
- package/src/onboarding/agents-generate.ts +74 -33
- package/src/onboarding/create.ts +10 -10
- package/src/onboarding/doctor.ts +70 -55
- package/src/onboarding/init.ts +38 -1
- package/src/onboarding/invariant-engine.ts +1 -1
- package/src/onboarding/memory-compact.ts +5 -4
- package/src/onboarding/memory-scaffold.ts +1 -1
- package/src/onboarding/nested-agents-generate.ts +45 -4
- package/src/onboarding/nested-agents-templates.ts +19 -13
- package/src/onboarding/npm-token-check.ts +2 -2
- package/src/onboarding/prettierignore.ts +263 -0
- package/src/onboarding/profile-validate.ts +5 -4
- package/src/onboarding/scaffold-project.ts +38 -27
- package/src/onboarding/scaffold.ts +1 -1
- package/src/onboarding/skill-markers.ts +162 -0
- package/src/onboarding/templates/behavioral-layer-core.md +2 -4
- package/src/onboarding/upgrade.ts +285 -89
- package/src/onboarding/workspace-discovery.ts +1 -1
- package/src/pipeline-status.test.ts +1 -4
- package/src/pipeline-status.ts +30 -13
- package/src/profiles/stack-profile.ts +1 -1
- package/src/registry.ts +4 -4
- package/src/skill-schema.ts +4 -1
- package/src/tests/acceptance-probe-kinds.test.ts +5 -15
- package/src/tests/agents-generate.test.ts +6 -4
- package/src/tests/contract-registry.test.ts +12 -6
- package/src/tests/doctor-fix.test.ts +1 -4
- package/src/tests/doctor-nested-agents.test.ts +53 -1
- package/src/tests/editable-generated-region.test.ts +231 -0
- package/src/tests/file-size-lint.test.ts +8 -12
- package/src/tests/fixtures/agents-generate-business-before.txt +54 -54
- package/src/tests/markdown-table.test.ts +107 -0
- package/src/tests/prettierignore-manage.test.ts +134 -0
- package/src/tests/skill-agents-sync.test.ts +168 -0
- package/src/tests/skill-sync-marker.test.ts +134 -0
- package/src/tests/skill-validate.test.ts +12 -5
- package/src/tests/upgrade.test.ts +87 -41
- package/src/types.ts +123 -0
- package/src/utils/editable-region.ts +156 -0
- package/src/utils/fs-idempotent.ts +12 -6
- package/src/utils/generated-marker.ts +36 -2
- package/src/utils/index.ts +17 -0
- package/src/utils/io.ts +143 -0
- package/src/utils/markdown-table.ts +121 -0
- package/src/utils/sync-fs.ts +43 -0
- package/src/validators/note-frontmatter-validate.ts +1 -1
- package/src/validators/note-link-validate.ts +1 -1
- package/src/validators/note-orphan-detect.ts +1 -1
- package/src/validators/port-validate.ts +1 -1
- package/src/validators/skill-validate.ts +38 -13
|
@@ -15,7 +15,7 @@ workspace type (app, service, package) by content markers.</purpose>
|
|
|
15
15
|
</CHANGE_SUMMARY>
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import fs from "
|
|
18
|
+
import * as fs from "../utils/sync-fs.ts";
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
import { hasGeneratedMarker } from "../utils/index.ts";
|
|
21
21
|
import type { ProfileWorkspaceType } from "../profiles/profile-schema.ts";
|
|
@@ -116,10 +116,7 @@ describe("deriveQueueItemStatus — derivation matrix (RFC-1140)", () => {
|
|
|
116
116
|
|
|
117
117
|
test("audit file only → in-progress/enhance", async () => {
|
|
118
118
|
await writeRfc("RFC-1107", "status: draft\n");
|
|
119
|
-
await fs.writeFile(
|
|
120
|
-
path.join(tmpDir, "docs/audits/audit-rfc-1107-test.md"),
|
|
121
|
-
"# audit\n",
|
|
122
|
-
);
|
|
119
|
+
await fs.writeFile(path.join(tmpDir, "docs/audits/audit-rfc-1107-test.md"), "# audit\n");
|
|
123
120
|
const r = await deriveQueueItemStatus(tmpDir, "RFC-1107");
|
|
124
121
|
expect(r?.status).toBe("in-progress");
|
|
125
122
|
expect(r?.pipelineStep).toBe("enhance");
|
package/src/pipeline-status.ts
CHANGED
|
@@ -25,8 +25,9 @@ Add pipeline-status.test.ts (derivation matrix, order, next selection, ADR, skip
|
|
|
25
25
|
</CHANGE_SUMMARY>
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import fs from "node:fs/promises";
|
|
29
28
|
import path from "node:path";
|
|
29
|
+
import type { WorkspaceIO } from "./types.ts";
|
|
30
|
+
import { resolveIo } from "./utils/io.ts";
|
|
30
31
|
import YAML from "yaml";
|
|
31
32
|
|
|
32
33
|
// ---------------------------------------------------------------------------
|
|
@@ -75,10 +76,12 @@ export function parseFrontmatter(source: string): ParsedDocument {
|
|
|
75
76
|
export async function findAuditFile(
|
|
76
77
|
workspaceRoot: string,
|
|
77
78
|
docId: string,
|
|
79
|
+
io?: WorkspaceIO,
|
|
78
80
|
): Promise<string | undefined> {
|
|
81
|
+
const fio = resolveIo(io);
|
|
79
82
|
const auditsDir = path.join(workspaceRoot, "docs/audits");
|
|
80
83
|
try {
|
|
81
|
-
const entries = await
|
|
84
|
+
const entries = (await fio.readdir(auditsDir)).map((e) => e.name);
|
|
82
85
|
const prefix = `audit-${docId.toLowerCase()}`;
|
|
83
86
|
const match = entries.find((e) => e.toLowerCase().startsWith(prefix) && e.endsWith(".md"));
|
|
84
87
|
return match ? path.join("docs/audits", match) : undefined;
|
|
@@ -90,10 +93,12 @@ export async function findAuditFile(
|
|
|
90
93
|
export async function findPlanFile(
|
|
91
94
|
workspaceRoot: string,
|
|
92
95
|
docId: string,
|
|
96
|
+
io?: WorkspaceIO,
|
|
93
97
|
): Promise<string | undefined> {
|
|
98
|
+
const fio = resolveIo(io);
|
|
94
99
|
const plansDir = path.join(workspaceRoot, "docs/plans");
|
|
95
100
|
try {
|
|
96
|
-
const entries = await
|
|
101
|
+
const entries = (await fio.readdir(plansDir)).map((e) => e.name);
|
|
97
102
|
const prefix = `plan-${docId.toLowerCase()}`;
|
|
98
103
|
const match = entries.find((e) => e.toLowerCase().startsWith(prefix) && e.endsWith(".md"));
|
|
99
104
|
return match ? path.join("docs/plans", match) : undefined;
|
|
@@ -122,20 +127,26 @@ async function scanForDocument(
|
|
|
122
127
|
dirPath: string,
|
|
123
128
|
relativePrefix: string,
|
|
124
129
|
idLower: string,
|
|
130
|
+
io: WorkspaceIO,
|
|
125
131
|
): Promise<string | undefined> {
|
|
126
132
|
let entries;
|
|
127
133
|
try {
|
|
128
|
-
entries = await
|
|
134
|
+
entries = await io.readdir(dirPath);
|
|
129
135
|
} catch {
|
|
130
136
|
return undefined;
|
|
131
137
|
}
|
|
132
138
|
for (const entry of entries) {
|
|
133
139
|
const relativePath = relativePrefix ? `${relativePrefix}/${entry.name}` : entry.name;
|
|
134
|
-
if (entry.isDirectory
|
|
135
|
-
const found = await scanForDocument(
|
|
140
|
+
if (entry.isDirectory) {
|
|
141
|
+
const found = await scanForDocument(
|
|
142
|
+
path.join(dirPath, entry.name),
|
|
143
|
+
relativePath,
|
|
144
|
+
idLower,
|
|
145
|
+
io,
|
|
146
|
+
);
|
|
136
147
|
if (found) return found;
|
|
137
148
|
} else if (
|
|
138
|
-
entry.isFile
|
|
149
|
+
entry.isFile &&
|
|
139
150
|
entry.name.endsWith(".md") &&
|
|
140
151
|
entry.name.toLowerCase().startsWith(idLower)
|
|
141
152
|
) {
|
|
@@ -153,19 +164,21 @@ async function scanForDocument(
|
|
|
153
164
|
export async function resolveDocument(
|
|
154
165
|
workspaceRoot: string,
|
|
155
166
|
docId: string,
|
|
167
|
+
io?: WorkspaceIO,
|
|
156
168
|
): Promise<ResolvedDocument | undefined> {
|
|
169
|
+
const fio = resolveIo(io);
|
|
157
170
|
const idMatch = DOC_ID_PATTERN.exec(docId.trim());
|
|
158
171
|
if (!idMatch) return undefined;
|
|
159
172
|
const kind: QueueDocumentKind = idMatch[1]!.toUpperCase() === "ADR" ? "adr" : "rfc";
|
|
160
173
|
const baseDir = kind === "adr" ? "docs/adrs" : "docs/rfcs";
|
|
161
174
|
const idLower = docId.toLowerCase();
|
|
162
175
|
|
|
163
|
-
const found = await scanForDocument(path.join(workspaceRoot, baseDir), "", idLower);
|
|
176
|
+
const found = await scanForDocument(path.join(workspaceRoot, baseDir), "", idLower, fio);
|
|
164
177
|
if (!found) return undefined;
|
|
165
178
|
|
|
166
179
|
let source: string;
|
|
167
180
|
try {
|
|
168
|
-
source = await
|
|
181
|
+
source = await fio.readFile(path.join(workspaceRoot, baseDir, found));
|
|
169
182
|
} catch {
|
|
170
183
|
return undefined;
|
|
171
184
|
}
|
|
@@ -242,8 +255,10 @@ export interface QueueItemReport {
|
|
|
242
255
|
export async function deriveQueueItemStatus(
|
|
243
256
|
workspaceRoot: string,
|
|
244
257
|
docId: string,
|
|
258
|
+
io?: WorkspaceIO,
|
|
245
259
|
): Promise<QueueItemReport | undefined> {
|
|
246
|
-
const
|
|
260
|
+
const fio = resolveIo(io);
|
|
261
|
+
const doc = await resolveDocument(workspaceRoot, docId, fio);
|
|
247
262
|
if (!doc) return undefined;
|
|
248
263
|
|
|
249
264
|
const fm = doc.frontmatter;
|
|
@@ -268,8 +283,8 @@ export async function deriveQueueItemStatus(
|
|
|
268
283
|
}
|
|
269
284
|
|
|
270
285
|
const enhancedAt = fm["enhancedAt"] ? String(fm["enhancedAt"]) : undefined;
|
|
271
|
-
const auditFile = await findAuditFile(workspaceRoot, doc.id);
|
|
272
|
-
const planFile = await findPlanFile(workspaceRoot, doc.id);
|
|
286
|
+
const auditFile = await findAuditFile(workspaceRoot, doc.id, fio);
|
|
287
|
+
const planFile = await findPlanFile(workspaceRoot, doc.id, fio);
|
|
273
288
|
|
|
274
289
|
const stages = computeRfcPipelineStages({
|
|
275
290
|
status,
|
|
@@ -306,10 +321,12 @@ export interface QueueReport {
|
|
|
306
321
|
export async function deriveQueueReport(
|
|
307
322
|
workspaceRoot: string,
|
|
308
323
|
itemIds: string[],
|
|
324
|
+
io?: WorkspaceIO,
|
|
309
325
|
): Promise<QueueReport> {
|
|
326
|
+
const fio = resolveIo(io);
|
|
310
327
|
const items: QueueItemReport[] = [];
|
|
311
328
|
for (const id of itemIds) {
|
|
312
|
-
const report = await deriveQueueItemStatus(workspaceRoot, id);
|
|
329
|
+
const report = await deriveQueueItemStatus(workspaceRoot, id, fio);
|
|
313
330
|
// Unresolvable ids are reported by the manifest validator; the resolver
|
|
314
331
|
// skips them here so a partial report stays usable.
|
|
315
332
|
if (report) items.push(report);
|
|
@@ -20,7 +20,7 @@ Sweep batch 2: real KEY_DECISIONS on 10 files, expanded purposes (CONTRACT-02/PU
|
|
|
20
20
|
</CHANGE_SUMMARY>
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import fs from "
|
|
23
|
+
import * as fs from "../utils/sync-fs.ts";
|
|
24
24
|
import path from "node:path";
|
|
25
25
|
import { z } from "zod";
|
|
26
26
|
import { parse as parseYaml } from "yaml";
|
package/src/registry.ts
CHANGED
|
@@ -19,7 +19,7 @@ Sweep batch 2: real KEY_DECISIONS on 10 files, expanded purposes (CONTRACT-02/PU
|
|
|
19
19
|
</CHANGE_SUMMARY>
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import fs from "
|
|
22
|
+
import * as fs from "./utils/sync-fs.ts";
|
|
23
23
|
import path from "node:path";
|
|
24
24
|
import { parse as parseYaml } from "yaml";
|
|
25
25
|
import type { ForgeConfig } from "./config/forge-config.ts";
|
|
@@ -38,7 +38,7 @@ export interface ForgeSkillEntry {
|
|
|
38
38
|
dependsOn: string[];
|
|
39
39
|
path: string;
|
|
40
40
|
knowledge?: string[];
|
|
41
|
-
|
|
41
|
+
triggerPhrases?: string[];
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -191,9 +191,9 @@ function discoverForgeSkills(skillsRoot: string): ForgeSkillEntry[] {
|
|
|
191
191
|
),
|
|
192
192
|
}
|
|
193
193
|
: {}),
|
|
194
|
-
...(Array.isArray(fm["
|
|
194
|
+
...(Array.isArray(fm["triggerPhrases"])
|
|
195
195
|
? {
|
|
196
|
-
|
|
196
|
+
triggerPhrases: (fm["triggerPhrases"] as unknown[]).filter(
|
|
197
197
|
(v): v is string => typeof v === "string",
|
|
198
198
|
),
|
|
199
199
|
}
|
package/src/skill-schema.ts
CHANGED
|
@@ -31,7 +31,10 @@ export const skillFrontmatterSchema = z.object({
|
|
|
31
31
|
})
|
|
32
32
|
.optional(),
|
|
33
33
|
knowledge: z.array(z.string()).optional(),
|
|
34
|
-
triggers
|
|
34
|
+
// Named triggerPhrases, not triggers — agent-IDE skill loaders (Devin CLI,
|
|
35
|
+
// Windsurf) reserve `triggers` for invocation modes (user/model); a list of
|
|
36
|
+
// phrases parses to zero valid modes and the skill is dropped entirely.
|
|
37
|
+
triggerPhrases: z.array(z.string().min(5).max(100)).max(5).optional(),
|
|
35
38
|
});
|
|
36
39
|
|
|
37
40
|
export type SkillFrontmatter = z.infer<typeof skillFrontmatterSchema>;
|
|
@@ -36,17 +36,13 @@ describe("validateAcceptanceShape — test probe", () => {
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
it("rejects a test probe missing file", () => {
|
|
39
|
-
const issues = validateAcceptanceShape([
|
|
40
|
-
{ probe: "test", expect: { exitCode: 0 } },
|
|
41
|
-
]);
|
|
39
|
+
const issues = validateAcceptanceShape([{ probe: "test", expect: { exitCode: 0 } }]);
|
|
42
40
|
expect(issues).toHaveLength(1);
|
|
43
41
|
expect(issues[0].message).toContain('requires a string "file"');
|
|
44
42
|
});
|
|
45
43
|
|
|
46
44
|
it("rejects a test probe missing expect.exitCode", () => {
|
|
47
|
-
const issues = validateAcceptanceShape([
|
|
48
|
-
{ probe: "test", file: "src/test.ts" },
|
|
49
|
-
]);
|
|
45
|
+
const issues = validateAcceptanceShape([{ probe: "test", file: "src/test.ts" }]);
|
|
50
46
|
expect(issues).toHaveLength(1);
|
|
51
47
|
expect(issues[0].message).toContain("requires expect: { exitCode");
|
|
52
48
|
});
|
|
@@ -108,9 +104,7 @@ describe("validateAcceptanceShape — existing probe kinds (regression)", () =>
|
|
|
108
104
|
});
|
|
109
105
|
|
|
110
106
|
it("accepts well-formed file-exists probe", () => {
|
|
111
|
-
const issues = validateAcceptanceShape([
|
|
112
|
-
{ probe: "file-exists", path: "src/index.ts" },
|
|
113
|
-
]);
|
|
107
|
+
const issues = validateAcceptanceShape([{ probe: "file-exists", path: "src/index.ts" }]);
|
|
114
108
|
expect(issues).toEqual([]);
|
|
115
109
|
});
|
|
116
110
|
|
|
@@ -122,16 +116,12 @@ describe("validateAcceptanceShape — existing probe kinds (regression)", () =>
|
|
|
122
116
|
});
|
|
123
117
|
|
|
124
118
|
it("accepts well-formed command-registered probe", () => {
|
|
125
|
-
const issues = validateAcceptanceShape([
|
|
126
|
-
{ probe: "command-registered", name: "rfc.validate" },
|
|
127
|
-
]);
|
|
119
|
+
const issues = validateAcceptanceShape([{ probe: "command-registered", name: "rfc.validate" }]);
|
|
128
120
|
expect(issues).toEqual([]);
|
|
129
121
|
});
|
|
130
122
|
|
|
131
123
|
it("accepts well-formed page probe", () => {
|
|
132
|
-
const issues = validateAcceptanceShape([
|
|
133
|
-
{ probe: "page", path: "/about" },
|
|
134
|
-
]);
|
|
124
|
+
const issues = validateAcceptanceShape([{ probe: "page", path: "/about" }]);
|
|
135
125
|
expect(issues).toEqual([]);
|
|
136
126
|
});
|
|
137
127
|
});
|
|
@@ -102,7 +102,8 @@ test("agents-generate includes intent-to-skill routing table", async () => {
|
|
|
102
102
|
|
|
103
103
|
const agentsMd = await readFile(join(tempDir, "AGENTS.md"), "utf8");
|
|
104
104
|
expect(agentsMd).toContain("### Intent-to-skill routing");
|
|
105
|
-
|
|
105
|
+
// RFC-1154: header cells are padded to column width — match via regex
|
|
106
|
+
expect(agentsMd).toMatch(/\| Operator says something like +\| Skill +\|/);
|
|
106
107
|
// Should contain at least one skill name from the registry
|
|
107
108
|
expect(agentsMd).toContain("fo-");
|
|
108
109
|
});
|
|
@@ -427,9 +428,10 @@ test("agents-generate nested renders entry points from exports map", async () =>
|
|
|
427
428
|
|
|
428
429
|
const nested = await readFile(join(tempDir, "packages", "my-pkg", "AGENTS.md"), "utf8");
|
|
429
430
|
expect(nested).toContain("## Entry points");
|
|
430
|
-
|
|
431
|
-
expect(nested).
|
|
432
|
-
expect(nested).
|
|
431
|
+
// RFC-1154: cells are padded to column width — match via regex
|
|
432
|
+
expect(nested).toMatch(/\| `@test\/my-pkg` +\| `\.\/src\/index\.ts` +\|/);
|
|
433
|
+
expect(nested).toMatch(/\| `@test\/my-pkg\/port` +\| `\.\/src\/port\.ts` +\|/);
|
|
434
|
+
expect(nested).toMatch(/\| `@test\/my-pkg\/client` +\| `\.\/src\/client\.ts` +\|/);
|
|
433
435
|
});
|
|
434
436
|
|
|
435
437
|
test("agents-generate nested separates workspace and external dependencies", async () => {
|
|
@@ -41,11 +41,15 @@ describe("contract-registry (RFC-0943)", () => {
|
|
|
41
41
|
|
|
42
42
|
describe("fileMatchesGlobs", () => {
|
|
43
43
|
test("matches a simple glob pattern", () => {
|
|
44
|
-
expect(fileMatchesGlobs("packages/my-pack/src/index.ts", ["packages/my-pack/**/*.ts"])).toBe(
|
|
44
|
+
expect(fileMatchesGlobs("packages/my-pack/src/index.ts", ["packages/my-pack/**/*.ts"])).toBe(
|
|
45
|
+
true,
|
|
46
|
+
);
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
test("does not match when glob does not match", () => {
|
|
48
|
-
expect(
|
|
50
|
+
expect(
|
|
51
|
+
fileMatchesGlobs("packages/other-pack/src/index.ts", ["packages/my-pack/**/*.ts"]),
|
|
52
|
+
).toBe(false);
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
test("matches any of multiple globs", () => {
|
|
@@ -58,7 +62,9 @@ describe("contract-registry (RFC-0943)", () => {
|
|
|
58
62
|
});
|
|
59
63
|
|
|
60
64
|
test("handles dot files with dot option", () => {
|
|
61
|
-
expect(fileMatchesGlobs("packages/my-pack/.env.example", ["packages/**/*.example"])).toBe(
|
|
65
|
+
expect(fileMatchesGlobs("packages/my-pack/.env.example", ["packages/**/*.example"])).toBe(
|
|
66
|
+
true,
|
|
67
|
+
);
|
|
62
68
|
});
|
|
63
69
|
});
|
|
64
70
|
|
|
@@ -86,7 +92,7 @@ describe("contract-registry (RFC-0943)", () => {
|
|
|
86
92
|
const packDir = join(ws, "my-pack");
|
|
87
93
|
writeManifest(
|
|
88
94
|
packDir,
|
|
89
|
-
|
|
95
|
+
'id: my-pack\nversion: 1.0.0\nextensionPoints:\n compass:\n contract:\n blocks:\n - blockId: api-contract\n requiredFor:\n - "packages/my-pack/**/*.ts"\n requiredTags:\n - name: endpoints\n minWords: 5\n',
|
|
90
96
|
);
|
|
91
97
|
|
|
92
98
|
const config: ForgeConfig = {
|
|
@@ -111,11 +117,11 @@ describe("contract-registry (RFC-0943)", () => {
|
|
|
111
117
|
try {
|
|
112
118
|
writeManifest(
|
|
113
119
|
join(ws, "pack-a"),
|
|
114
|
-
|
|
120
|
+
'id: pack-a\nversion: 1.0.0\nextensionPoints:\n compass:\n contract:\n blocks:\n - blockId: shared-block\n requiredFor:\n - "packages/**/*.ts"\n',
|
|
115
121
|
);
|
|
116
122
|
writeManifest(
|
|
117
123
|
join(ws, "pack-b"),
|
|
118
|
-
|
|
124
|
+
'id: pack-b\nversion: 1.0.0\nextensionPoints:\n compass:\n contract:\n blocks:\n - blockId: shared-block\n requiredFor:\n - "services/**/*.ts"\n',
|
|
119
125
|
);
|
|
120
126
|
|
|
121
127
|
const config: ForgeConfig = {
|
|
@@ -83,10 +83,7 @@ test("doctor --fix regenerates stale nested AGENTS.md and subsequent run passes"
|
|
|
83
83
|
expect(fixEntry).toBeDefined();
|
|
84
84
|
expect(fixEntry!.action).toBe("fixed");
|
|
85
85
|
|
|
86
|
-
const regenerated = await readFile(
|
|
87
|
-
join(tempDir, "packages", "my-pkg", "AGENTS.md"),
|
|
88
|
-
"utf8",
|
|
89
|
-
);
|
|
86
|
+
const regenerated = await readFile(join(tempDir, "packages", "my-pkg", "AGENTS.md"), "utf8");
|
|
90
87
|
expect(regenerated).toContain("`@test/my-pkg` — Agent Guide");
|
|
91
88
|
expect(regenerated).not.toContain("Old content");
|
|
92
89
|
|
|
@@ -9,10 +9,13 @@ workspace discovery exclusions via bindings.workspaces.skipDirs (RFC-1150).</pur
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { test, expect, beforeEach, afterEach } from "vitest";
|
|
12
|
-
import { mkdtemp, rm, mkdir, writeFile } from "node:fs/promises";
|
|
12
|
+
import { mkdtemp, rm, mkdir, writeFile, appendFile } from "node:fs/promises";
|
|
13
13
|
import { tmpdir } from "node:os";
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
import { runDoctor } from "../onboarding/doctor.ts";
|
|
16
|
+
import { generateNestedAgentsMd } from "../onboarding/nested-agents-generate.ts";
|
|
17
|
+
import { buildGeneratedHeader } from "../utils/index.ts";
|
|
18
|
+
import { loadForgeConfig } from "../config/forge-config.ts";
|
|
16
19
|
import type { ForgeCommandInput, ForgeRuntimeContext } from "../types.ts";
|
|
17
20
|
|
|
18
21
|
let tempDir: string;
|
|
@@ -106,3 +109,52 @@ test("nested-AGENTS.md check reports artifact dir as missing without skipDirs",
|
|
|
106
109
|
expect(check).toBeDefined();
|
|
107
110
|
expect(check!.message).toContain("builds");
|
|
108
111
|
});
|
|
112
|
+
|
|
113
|
+
// RFC-1153: boundary-aware staleness -----------------------------------------
|
|
114
|
+
|
|
115
|
+
test("AC-8: custom content below the forge:custom boundary reports in-sync, not stale", async () => {
|
|
116
|
+
await writeFile(join(tempDir, "forge.yaml"), forgeYaml([]), "utf8");
|
|
117
|
+
await mkdir(join(tempDir, "packages", "pkg"), { recursive: true });
|
|
118
|
+
await writeFile(join(tempDir, "packages", "pkg", "package.json"), '{"name":"pkg"}', "utf8");
|
|
119
|
+
|
|
120
|
+
const config = loadForgeConfig(tempDir);
|
|
121
|
+
await generateNestedAgentsMd(tempDir, config, false);
|
|
122
|
+
|
|
123
|
+
// Operator appends a custom section below the marker
|
|
124
|
+
await appendFile(
|
|
125
|
+
join(tempDir, "packages", "pkg", "AGENTS.md"),
|
|
126
|
+
"\n## Custom rules\n\n- workspace-specific note\n",
|
|
127
|
+
"utf8",
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const input: ForgeCommandInput = { argv: [], flags: {} };
|
|
131
|
+
const result = await runDoctor(input, makeContext());
|
|
132
|
+
|
|
133
|
+
const check = result.data!.checks.find((c) => c.name === "nested-AGENTS.md");
|
|
134
|
+
expect(check).toBeDefined();
|
|
135
|
+
expect(
|
|
136
|
+
check!.message,
|
|
137
|
+
"Below-boundary divergence must not count as stale — merge(expected, actual) compares only the head",
|
|
138
|
+
).not.toContain("stale");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("AC-6: divergent file without a resolvable boundary reports unmapped-customization", async () => {
|
|
142
|
+
await writeFile(join(tempDir, "forge.yaml"), forgeYaml([]), "utf8");
|
|
143
|
+
await mkdir(join(tempDir, "packages", "legacy"), { recursive: true });
|
|
144
|
+
await writeFile(join(tempDir, "packages", "legacy", "package.json"), '{"name":"legacy"}', "utf8");
|
|
145
|
+
// Marker-bearing (isGenerated) file that diverges and lacks footer/forge:custom
|
|
146
|
+
await writeFile(
|
|
147
|
+
join(tempDir, "packages", "legacy", "AGENTS.md"),
|
|
148
|
+
`${buildGeneratedHeader({ filePath: "AGENTS.md", ownerCommand: "forge.agents.generate", editable: true })}\n# Hand-maintained\n\ndivergent body\n`,
|
|
149
|
+
"utf8",
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const input: ForgeCommandInput = { argv: [], flags: {} };
|
|
153
|
+
const result = await runDoctor(input, makeContext());
|
|
154
|
+
|
|
155
|
+
const check = result.data!.checks.find((c) => c.name === "nested-AGENTS.md");
|
|
156
|
+
expect(check).toBeDefined();
|
|
157
|
+
expect(check!.status).toBe("warn");
|
|
158
|
+
expect(check!.message).toContain("unmapped-customization");
|
|
159
|
+
expect(check!.message).not.toContain("stale");
|
|
160
|
+
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Unit tests for the RFC-1153 preservation boundary — splitEditableGenerated
|
|
4
|
+
and mergeEditableGenerated (forge:custom marker + canonical-footer fallback),
|
|
5
|
+
plus an integration pass through generateNestedAgentsMd on a temp workspace.</purpose>
|
|
6
|
+
</MODULE_CONTRACT>
|
|
7
|
+
<CHANGE_SUMMARY>
|
|
8
|
+
<item>RFC-1153: initial boundary merge tests — marker, footer fallback, fail-closed, comment styles, CRLF, integration.</item>
|
|
9
|
+
</CHANGE_SUMMARY>
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { test, expect, beforeEach, afterEach } from "vitest";
|
|
13
|
+
import { mkdtemp, mkdir, rm, writeFile, readFile, appendFile } from "node:fs/promises";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import {
|
|
17
|
+
splitEditableGenerated,
|
|
18
|
+
mergeEditableGenerated,
|
|
19
|
+
canonicalFooterOf,
|
|
20
|
+
} from "../utils/editable-region.ts";
|
|
21
|
+
import {
|
|
22
|
+
CUSTOM_BOUNDARY_HINTS,
|
|
23
|
+
CUSTOM_BOUNDARY_MARKERS,
|
|
24
|
+
buildGeneratedHeader,
|
|
25
|
+
} from "../utils/generated-marker.ts";
|
|
26
|
+
import { generateNestedAgentsMd } from "../onboarding/nested-agents-generate.ts";
|
|
27
|
+
import { loadForgeConfig } from "../config/forge-config.ts";
|
|
28
|
+
|
|
29
|
+
const MD = "AGENTS.md";
|
|
30
|
+
const MD_MARKER = CUSTOM_BOUNDARY_MARKERS["block-html"]; // <!-- forge:custom -->
|
|
31
|
+
const MD_HINT = CUSTOM_BOUNDARY_HINTS["block-html"];
|
|
32
|
+
|
|
33
|
+
const FOOTER = "See the root `AGENTS.md` for project-wide rules, skills, and capabilities.";
|
|
34
|
+
|
|
35
|
+
function render(body: string): string {
|
|
36
|
+
return `${buildGeneratedHeader({ filePath: MD, ownerCommand: "forge.agents.generate", editable: true })}\n${body}\n\n${FOOTER}\n`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test("AC-1: content below the forge:custom marker is preserved byte-identical", () => {
|
|
40
|
+
const tail = "\n## Workspace rules\n\n- always run pnpm test first\n- custom note\n";
|
|
41
|
+
const existing = `${render("old body v1")}${MD_MARKER}\n${MD_HINT}\n${tail}`;
|
|
42
|
+
const merged = mergeEditableGenerated(render("fresh body v2"), existing, MD);
|
|
43
|
+
expect(merged).not.toBeNull();
|
|
44
|
+
// Fresh head regenerated, tail below the marker verbatim
|
|
45
|
+
expect(merged).toContain("fresh body v2");
|
|
46
|
+
expect(merged).not.toContain("old body v1");
|
|
47
|
+
expect(
|
|
48
|
+
merged!.endsWith(tail.trimStart() === tail ? tail : `\n${tail}`) || merged!.includes(tail),
|
|
49
|
+
).toBe(true);
|
|
50
|
+
expect(merged).toContain(MD_MARKER);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("marker line itself belongs to the generated head — operator-placed marker also merges", () => {
|
|
54
|
+
const tail = "everything below survives\n";
|
|
55
|
+
const existing = `hand-tuned intro\n${MD_MARKER}\n${tail}`;
|
|
56
|
+
const merged = mergeEditableGenerated(render("body"), existing, MD);
|
|
57
|
+
expect(merged).toBe(`${render("body")}${MD_MARKER}\n${MD_HINT}\n${tail}`);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("AC-2: footer fallback preserves text below the canonical template footer", () => {
|
|
61
|
+
const tail = "\noperator-appended rules\n";
|
|
62
|
+
const existing = `${render("stale body")}${tail}`; // marker-less, ends with footer + tail
|
|
63
|
+
const merged = mergeEditableGenerated(render("new body"), existing, MD);
|
|
64
|
+
expect(merged).not.toBeNull();
|
|
65
|
+
expect(merged!.endsWith(tail)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("AC-3: footer-fallback merge introduces the forge:custom marker as the new boundary", () => {
|
|
69
|
+
const existing = `${render("body")}\ncustom tail\n`;
|
|
70
|
+
const merged = mergeEditableGenerated(render("body"), existing, MD);
|
|
71
|
+
expect(merged).toContain(MD_MARKER);
|
|
72
|
+
expect(merged).toContain(MD_HINT);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("AC-5: footer-less marker-less divergent content returns null — caller must skip", () => {
|
|
76
|
+
const existing = `${buildGeneratedHeader({ filePath: MD, ownerCommand: "forge.agents.generate", editable: true })}\n# Custom\n\ntotally divergent, no footer\n`;
|
|
77
|
+
expect(mergeEditableGenerated(render("body"), existing, MD)).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("identical content is a clean regeneration — marker appended for future runs", () => {
|
|
81
|
+
const r = render("body");
|
|
82
|
+
const merged = mergeEditableGenerated(r, r, MD);
|
|
83
|
+
expect(merged).toBe(`${r}${MD_MARKER}\n${MD_HINT}\n`);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("new file (existing null) gains the boundary marker and hint", () => {
|
|
87
|
+
const merged = mergeEditableGenerated(render("body"), null, MD);
|
|
88
|
+
expect(merged).toBe(`${render("body")}${MD_MARKER}\n${MD_HINT}\n`);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("AC-4: boundary marker is emitted in the comment style of the target file", () => {
|
|
92
|
+
for (const [file, style] of [
|
|
93
|
+
["AGENTS.md", "block-html"],
|
|
94
|
+
["config.ts", "line-slash"],
|
|
95
|
+
["data.yaml", "line-hash"],
|
|
96
|
+
["styles.css", "block-css"],
|
|
97
|
+
] as const) {
|
|
98
|
+
const merged = mergeEditableGenerated("content\n", null, file);
|
|
99
|
+
expect(merged, `marker for ${file} must use ${style} comment syntax`).toContain(
|
|
100
|
+
CUSTOM_BOUNDARY_MARKERS[style],
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("CRLF input normalizes to LF and still resolves the marker boundary", () => {
|
|
106
|
+
const tail = "preserved tail\n";
|
|
107
|
+
const existing = `a\r\nb\r\n${MD_MARKER}\r\n${tail}`;
|
|
108
|
+
const merged = mergeEditableGenerated("fresh\n", existing, MD);
|
|
109
|
+
expect(merged).toBe(`fresh\n${MD_MARKER}\n${MD_HINT}\n${tail}`);
|
|
110
|
+
expect(merged).not.toContain("\r\n");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("mid-file marker freezes everything below the first occurrence", () => {
|
|
114
|
+
const tail = "mid doc\nfrozen rules\n";
|
|
115
|
+
const existing = `intro\n${MD_MARKER}\n${tail}`;
|
|
116
|
+
const merged = mergeEditableGenerated(render("body"), existing, MD);
|
|
117
|
+
expect(merged!.endsWith(tail)).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("forge:custom token in prose is treated as a boundary (first occurrence wins)", () => {
|
|
121
|
+
const existing = `notes on the forge:custom feature\nkept anyway\n`;
|
|
122
|
+
const split = splitEditableGenerated(existing, MD);
|
|
123
|
+
expect(split.boundary).toBe("marker");
|
|
124
|
+
expect(split.customTail).toBe("kept anyway\n");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("boundary none: split returns whole content as head", () => {
|
|
128
|
+
const split = splitEditableGenerated("plain\nfile\n", MD, FOOTER);
|
|
129
|
+
expect(split.boundary).toBe("none");
|
|
130
|
+
expect(split.customTail).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("canonicalFooterOf returns the last non-empty line", () => {
|
|
134
|
+
expect(canonicalFooterOf("a\nb\n\n\n")).toBe("b");
|
|
135
|
+
expect(canonicalFooterOf("x\n")).toBe("x");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// --- Integration through the real write path -----------------------------
|
|
139
|
+
|
|
140
|
+
let tempDir: string;
|
|
141
|
+
|
|
142
|
+
beforeEach(async () => {
|
|
143
|
+
tempDir = await mkdtemp(join(tmpdir(), "editable-region-it-"));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
afterEach(async () => {
|
|
147
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const FORGE_YAML = `schema: "forge/config@1"
|
|
151
|
+
project:
|
|
152
|
+
name: test-project
|
|
153
|
+
stack: []
|
|
154
|
+
packageManager: pnpm
|
|
155
|
+
paths:
|
|
156
|
+
rfcsDir: docs/rfcs
|
|
157
|
+
adrsDir: docs/adrs
|
|
158
|
+
plansDir: docs/plans
|
|
159
|
+
auditsDir: docs/audits
|
|
160
|
+
specsDir: docs/specs
|
|
161
|
+
skillsDir: .agents/skills
|
|
162
|
+
sessionsDir: docs/sessions
|
|
163
|
+
bindings:
|
|
164
|
+
schema: forge/bindings@1
|
|
165
|
+
commands:
|
|
166
|
+
validateRfc: null
|
|
167
|
+
validateAdr: null
|
|
168
|
+
implementStamp: null
|
|
169
|
+
typecheck: null
|
|
170
|
+
test: null
|
|
171
|
+
scopedBuild: null
|
|
172
|
+
specValidate: null
|
|
173
|
+
sessionSave: null
|
|
174
|
+
paths:
|
|
175
|
+
invariantsFile: null
|
|
176
|
+
compassDocs: []
|
|
177
|
+
reviewsDir: null
|
|
178
|
+
handoffsDir: null
|
|
179
|
+
sessionsDir: null
|
|
180
|
+
`;
|
|
181
|
+
|
|
182
|
+
test("generateNestedAgentsMd regenerates the head and preserves the custom tail", async () => {
|
|
183
|
+
await writeFile(join(tempDir, "forge.yaml"), FORGE_YAML, "utf8");
|
|
184
|
+
await mkdir(join(tempDir, "packages", "pkg"), { recursive: true });
|
|
185
|
+
await writeFile(join(tempDir, "packages", "pkg", "package.json"), '{"name":"pkg"}', "utf8");
|
|
186
|
+
|
|
187
|
+
const config = loadForgeConfig(tempDir);
|
|
188
|
+
|
|
189
|
+
// First run — fresh file gains the marker + hint
|
|
190
|
+
const first = await generateNestedAgentsMd(tempDir, config, false);
|
|
191
|
+
expect(first.generated).toContain(join("packages", "pkg", "AGENTS.md"));
|
|
192
|
+
const agentsPath = join(tempDir, "packages", "pkg", "AGENTS.md");
|
|
193
|
+
const initial = await readFile(agentsPath, "utf8");
|
|
194
|
+
expect(initial).toContain(MD_MARKER);
|
|
195
|
+
expect(initial).toContain(MD_HINT);
|
|
196
|
+
|
|
197
|
+
// Operator appends a custom section below the boundary
|
|
198
|
+
await appendFile(agentsPath, "\n## Workspace rules\n\n- never touch dist/\n", "utf8");
|
|
199
|
+
|
|
200
|
+
// Second run — head regenerated, tail preserved verbatim
|
|
201
|
+
const second = await generateNestedAgentsMd(tempDir, config, false);
|
|
202
|
+
expect(second.preserved).toContain(join("packages", "pkg", "AGENTS.md"));
|
|
203
|
+
const merged = await readFile(agentsPath, "utf8");
|
|
204
|
+
expect(merged).toContain("## Workspace rules");
|
|
205
|
+
expect(merged).toContain("- never touch dist/");
|
|
206
|
+
// Boundary sits between generated head and custom tail
|
|
207
|
+
expect(merged.indexOf(MD_MARKER)).toBeLessThan(merged.indexOf("## Workspace rules"));
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("generateNestedAgentsMd skips a divergent file without a resolvable boundary", async () => {
|
|
211
|
+
await writeFile(join(tempDir, "forge.yaml"), FORGE_YAML, "utf8");
|
|
212
|
+
await mkdir(join(tempDir, "packages", "legacy"), { recursive: true });
|
|
213
|
+
await writeFile(join(tempDir, "packages", "legacy", "package.json"), '{"name":"legacy"}', "utf8");
|
|
214
|
+
// Marker-bearing (isGenerated) file whose body diverges and lacks the footer
|
|
215
|
+
await writeFile(
|
|
216
|
+
join(tempDir, "packages", "legacy", "AGENTS.md"),
|
|
217
|
+
`${buildGeneratedHeader({ filePath: "AGENTS.md", ownerCommand: "forge.agents.generate", editable: true })}\n# Hand-maintained\n\ndivergent body\n`,
|
|
218
|
+
"utf8",
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const config = loadForgeConfig(tempDir);
|
|
222
|
+
const result = await generateNestedAgentsMd(tempDir, config, false);
|
|
223
|
+
const skippedEntry = result.skipped.find((s) => s.includes("legacy"));
|
|
224
|
+
expect(
|
|
225
|
+
skippedEntry,
|
|
226
|
+
"Divergent file without forge:custom/footer boundary must be skipped as unmapped-customization",
|
|
227
|
+
).toContain("unmapped-customization");
|
|
228
|
+
// File untouched
|
|
229
|
+
const content = await readFile(join(tempDir, "packages", "legacy", "AGENTS.md"), "utf8");
|
|
230
|
+
expect(content).toContain("divergent body");
|
|
231
|
+
});
|