@warpgogol/forge 5.2.4 → 5.3.1
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 +1 -1
- package/README.uk.md +8 -8
- package/bin/cli.ts +25 -33
- package/dist/bin/cli.js +21 -32
- 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 -10
- 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 +44 -23
- 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/handlers/validate.d.ts.map +1 -1
- package/dist/os/rfc/handlers/validate.js +6 -1
- package/dist/os/rfc/handlers/validate.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/handlers/workspace-deps.d.ts.map +1 -1
- package/dist/os/werkstatt/handlers/workspace-deps.js +0 -1
- package/dist/os/werkstatt/handlers/workspace-deps.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/cli-flags.d.ts +28 -0
- package/dist/src/cli-flags.d.ts.map +1 -0
- package/dist/src/cli-flags.js +232 -0
- package/dist/src/cli-flags.js.map +1 -0
- 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 +76 -34
- 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 -10
- 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.test.ts +38 -1
- package/os/mission/handlers/archive.ts +54 -23
- 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/handlers/validate.test.ts +71 -0
- package/os/rfc/handlers/validate.ts +6 -1
- 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/handlers/workspace-deps.ts +0 -1
- 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/profiles/forge-shell.yaml +1 -1
- package/profiles/godot-game.yaml +1 -1
- package/profiles/knowledge.yaml +1 -1
- package/profiles/phaser-game.yaml +1 -1
- package/profiles/site-workshop.yaml +1 -1
- package/profiles/typescript.yaml +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/cli-flags.ts +285 -0
- 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 +92 -36
- 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 +28 -5
- package/src/tests/cli-flags.test.ts +169 -0
- 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 +85 -56
- 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
- package/AGENTS.md +0 -502
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Unit tests for the RFC-1154 managed .prettierignore block:
|
|
4
|
+
root-only prettier detection, delimited-block reconcile preserving operator
|
|
5
|
+
content, marker-driven skill entries, and the skipped contract for
|
|
6
|
+
prettier-free consumers.</purpose>
|
|
7
|
+
</MODULE_CONTRACT>
|
|
8
|
+
<CHANGE_SUMMARY>
|
|
9
|
+
<item>RFC-1154: initial prettierignore plan/apply tests.</item>
|
|
10
|
+
</CHANGE_SUMMARY>
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { test, expect, beforeEach, afterEach } from "vitest";
|
|
14
|
+
import { mkdtemp, rm, writeFile, mkdir, readFile } from "node:fs/promises";
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import {
|
|
19
|
+
planPrettierignore,
|
|
20
|
+
applyPrettierignore,
|
|
21
|
+
reconcilePrettierignoreContent,
|
|
22
|
+
PRETTIERIGNORE_BLOCK_BEGIN,
|
|
23
|
+
PRETTIERIGNORE_BLOCK_END,
|
|
24
|
+
} from "../onboarding/prettierignore.ts";
|
|
25
|
+
import { SKILL_MARKER_FILE } from "../onboarding/skill-markers.ts";
|
|
26
|
+
|
|
27
|
+
let tmpDir: string;
|
|
28
|
+
|
|
29
|
+
beforeEach(async () => {
|
|
30
|
+
tmpDir = await mkdtemp(join(tmpdir(), "prettierignore-"));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
async function withPrettier(root: string): Promise<void> {
|
|
38
|
+
await writeFile(join(root, ".prettierrc"), "{}\n");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
test("detects prettier via root .prettierrc", async () => {
|
|
42
|
+
await withPrettier(tmpDir);
|
|
43
|
+
const plan = await planPrettierignore(tmpDir);
|
|
44
|
+
expect(plan.applies).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("detects prettier via package.json prettier key", async () => {
|
|
48
|
+
await writeFile(join(tmpDir, "package.json"), JSON.stringify({ prettier: {} }));
|
|
49
|
+
const plan = await planPrettierignore(tmpDir);
|
|
50
|
+
expect(plan.applies).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("detects prettier via prettier devDependency", async () => {
|
|
54
|
+
await writeFile(
|
|
55
|
+
join(tmpDir, "package.json"),
|
|
56
|
+
JSON.stringify({ devDependencies: { prettier: "^3" } }),
|
|
57
|
+
);
|
|
58
|
+
const plan = await planPrettierignore(tmpDir);
|
|
59
|
+
expect(plan.applies).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("detects prettier via existing .prettierignore alone", async () => {
|
|
63
|
+
await writeFile(join(tmpDir, ".prettierignore"), "dist/\n");
|
|
64
|
+
const plan = await planPrettierignore(tmpDir);
|
|
65
|
+
expect(plan.applies).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("AC-4: no prettier markers — plan does not apply, apply writes nothing", async () => {
|
|
69
|
+
const plan = await planPrettierignore(tmpDir);
|
|
70
|
+
expect(plan.applies).toBe(false);
|
|
71
|
+
const result = await applyPrettierignore(tmpDir, plan);
|
|
72
|
+
expect(result).toBe("skipped");
|
|
73
|
+
expect(existsSync(join(tmpDir, ".prettierignore"))).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("AC-2: block contains generated.yaml glob, sessions, metrics, queues entries", async () => {
|
|
77
|
+
await withPrettier(tmpDir);
|
|
78
|
+
const plan = await planPrettierignore(tmpDir);
|
|
79
|
+
const result = await applyPrettierignore(tmpDir, plan);
|
|
80
|
+
expect(result).toBe("written");
|
|
81
|
+
const content = await readFile(join(tmpDir, ".prettierignore"), "utf8");
|
|
82
|
+
expect(content).toContain(PRETTIERIGNORE_BLOCK_BEGIN);
|
|
83
|
+
expect(content).toContain("**/*.generated.yaml");
|
|
84
|
+
expect(content).toContain("docs/sessions/");
|
|
85
|
+
expect(content).toContain("docs/metrics/");
|
|
86
|
+
expect(content).toContain("docs/queues/session-*.yaml");
|
|
87
|
+
expect(content).toContain(PRETTIERIGNORE_BLOCK_END);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("skill entries enumerate only marker-carrying dirs (AC-9)", async () => {
|
|
91
|
+
await withPrettier(tmpDir);
|
|
92
|
+
const skillsDir = join(tmpDir, ".agents", "skills");
|
|
93
|
+
await mkdir(join(skillsDir, "fo-idea"), { recursive: true });
|
|
94
|
+
await writeFile(join(skillsDir, "fo-idea", SKILL_MARKER_FILE), '{"files":["SKILL.md"]}');
|
|
95
|
+
await mkdir(join(skillsDir, "my-own-skill"), { recursive: true }); // unmarked — consumer-owned
|
|
96
|
+
|
|
97
|
+
const plan = await planPrettierignore(tmpDir);
|
|
98
|
+
expect(plan.entries).toContain(".agents/skills/fo-idea/");
|
|
99
|
+
expect(plan.entries).not.toContain(".agents/skills/my-own-skill/");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("reconcile preserves operator content outside the block", async () => {
|
|
103
|
+
const existing = `dist/\nnode_modules/\n${PRETTIERIGNORE_BLOCK_BEGIN}\nold-entry/\n${PRETTIERIGNORE_BLOCK_END}\ncustom/\n`;
|
|
104
|
+
const { next, changed } = reconcilePrettierignoreContent(existing, ["a/", "b/"]);
|
|
105
|
+
expect(changed).toBe("updated");
|
|
106
|
+
expect(next).toContain("dist/\nnode_modules/\n");
|
|
107
|
+
expect(next).toContain("custom/\n");
|
|
108
|
+
expect(next).not.toContain("old-entry/");
|
|
109
|
+
expect(next).toContain("a/\nb/");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("unbalanced markers treat the file as operator-owned and append a fresh block", async () => {
|
|
113
|
+
const existing = `dist/\n${PRETTIERIGNORE_BLOCK_BEGIN}\nstray/\n`; // no END marker
|
|
114
|
+
const { next, changed } = reconcilePrettierignoreContent(existing, ["x/"]);
|
|
115
|
+
expect(changed).toBe("updated");
|
|
116
|
+
expect(next).toContain("dist/\n");
|
|
117
|
+
// Original content preserved verbatim — forge does not merge into a
|
|
118
|
+
// corrupted region; a fresh block is appended.
|
|
119
|
+
expect(next.indexOf(PRETTIERIGNORE_BLOCK_BEGIN)).toBeLessThan(
|
|
120
|
+
next.lastIndexOf(PRETTIERIGNORE_BLOCK_BEGIN),
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("repeated apply is idempotent", async () => {
|
|
125
|
+
await withPrettier(tmpDir);
|
|
126
|
+
await writeFile(join(tmpDir, ".prettierignore"), "dist/\n");
|
|
127
|
+
const plan = await planPrettierignore(tmpDir);
|
|
128
|
+
const first = await applyPrettierignore(tmpDir, plan);
|
|
129
|
+
expect(first).toBe("updated");
|
|
130
|
+
const second = await applyPrettierignore(tmpDir, await planPrettierignore(tmpDir));
|
|
131
|
+
expect(second, "second apply must be unchanged — check reconcile block boundaries").toBe(
|
|
132
|
+
"unchanged",
|
|
133
|
+
);
|
|
134
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>CI gate: every forge/pack skill source must be fully propagated to
|
|
4
|
+
`.agents/skills/` (IDE discovery copy written by forge.init/forge.upgrade).
|
|
5
|
+
SKILL.md syncs verbatim; declared knowledge files follow the append-only
|
|
6
|
+
syncKnowledgeFile contract — in the canonical repo a resync must be a no-op,
|
|
7
|
+
otherwise source and copy have drifted apart.</purpose>
|
|
8
|
+
<non-goals>
|
|
9
|
+
<item>Do not validate skill content/schema — forge.skill.validate owns that.</item>
|
|
10
|
+
<item>Do not flag unmarked dirs — a missing .forge-managed marker means
|
|
11
|
+
consumer-owned (RFC-1154), e.g. `_shared/`.</item>
|
|
12
|
+
</non-goals>
|
|
13
|
+
<KEY_DECISIONS>
|
|
14
|
+
<item>planKnowledgeSync is reused so the gate encodes the exact merge
|
|
15
|
+
semantics of the syncer — only "unchanged" is a pass; "merged" (source has
|
|
16
|
+
entries the copy lacks), "copied" (copy missing), and "skipped"
|
|
17
|
+
(non-mergeable divergence = hand-edited copy) all fail.</item>
|
|
18
|
+
<item>Runs on the real repo — this is a drift gate, not a fixture test.</item>
|
|
19
|
+
</KEY_DECISIONS>
|
|
20
|
+
<CHANGE_SUMMARY>
|
|
21
|
+
<item>Initial version — covers FORGE_SKILLS, declared pack skills
|
|
22
|
+
(discoverPackSkills), and the shared-knowledge layer.</item>
|
|
23
|
+
</CHANGE_SUMMARY>
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import fs from "node:fs";
|
|
27
|
+
import path from "node:path";
|
|
28
|
+
import { describe, expect, it } from "vitest";
|
|
29
|
+
|
|
30
|
+
import { loadForgeConfig } from "../config/forge-config.ts";
|
|
31
|
+
import { planKnowledgeSync } from "../knowledge/index.ts";
|
|
32
|
+
import { FORGE_SKILLS, discoverPackSkills } from "../registry.ts";
|
|
33
|
+
|
|
34
|
+
const forgeRoot = path.resolve(import.meta.dirname, "../..");
|
|
35
|
+
const workspaceRoot = path.resolve(import.meta.dirname, "../../../..");
|
|
36
|
+
|
|
37
|
+
interface SkillSource {
|
|
38
|
+
name: string;
|
|
39
|
+
srcDir: string;
|
|
40
|
+
knowledge: string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function collectSkillSources(): SkillSource[] {
|
|
44
|
+
const sources: SkillSource[] = [];
|
|
45
|
+
for (const skill of FORGE_SKILLS) {
|
|
46
|
+
sources.push({
|
|
47
|
+
name: skill.name,
|
|
48
|
+
srcDir: path.dirname(path.join(forgeRoot, skill.path)),
|
|
49
|
+
knowledge: skill.knowledge ?? [],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const config = loadForgeConfig(workspaceRoot);
|
|
53
|
+
for (const skill of discoverPackSkills(workspaceRoot, config)) {
|
|
54
|
+
sources.push({
|
|
55
|
+
name: skill.name,
|
|
56
|
+
srcDir: path.dirname(path.join(workspaceRoot, skill.dir, skill.path)),
|
|
57
|
+
knowledge: skill.knowledge ?? [],
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return sources;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Skipped in the standalone package repo — the gate needs a forge-managed
|
|
64
|
+
// workspace root (forge.yaml + synced skillsDir), which only exists for the
|
|
65
|
+
// monorepo and consumer projects, not for the published forge source tree.
|
|
66
|
+
const hasForgeWorkspace = fs.existsSync(path.join(workspaceRoot, "forge.yaml"));
|
|
67
|
+
describe.runIf(hasForgeWorkspace)(
|
|
68
|
+
".agents/skills sync gate (forge.init/forge.upgrade parity)",
|
|
69
|
+
() => {
|
|
70
|
+
// describe callbacks still run during collection even when runIf is false,
|
|
71
|
+
// so initializers must tolerate the absent forge.yaml themselves.
|
|
72
|
+
const config = hasForgeWorkspace ? loadForgeConfig(workspaceRoot) : null;
|
|
73
|
+
const skillsDir = config ? path.join(workspaceRoot, config.paths.skillsDir) : "";
|
|
74
|
+
const sources = config ? collectSkillSources() : [];
|
|
75
|
+
|
|
76
|
+
it("every forge + pack skill has a synced copy in .agents/skills/", () => {
|
|
77
|
+
const missing = sources
|
|
78
|
+
.filter((s) => !fs.existsSync(path.join(skillsDir, s.name, "SKILL.md")))
|
|
79
|
+
.map((s) => `${s.name} (src ${path.relative(workspaceRoot, s.srcDir)})`);
|
|
80
|
+
expect(
|
|
81
|
+
missing,
|
|
82
|
+
`skills never synced — run \`forge init\`/\`forge upgrade\` to populate ${path.relative(workspaceRoot, skillsDir)}`,
|
|
83
|
+
).toEqual([]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("SKILL.md copies are byte-identical to their sources", () => {
|
|
87
|
+
const drifted: string[] = [];
|
|
88
|
+
for (const s of sources) {
|
|
89
|
+
const src = path.join(s.srcDir, "SKILL.md");
|
|
90
|
+
const dest = path.join(skillsDir, s.name, "SKILL.md");
|
|
91
|
+
if (!fs.existsSync(dest)) continue; // reported by the presence test
|
|
92
|
+
if (fs.readFileSync(src, "utf8") !== fs.readFileSync(dest, "utf8")) {
|
|
93
|
+
drifted.push(s.name);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
expect(
|
|
97
|
+
drifted,
|
|
98
|
+
"SKILL.md drift — edit the pack source and re-sync, never edit .agents/skills/ directly",
|
|
99
|
+
).toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("declared knowledge files are in sync (append-only merge would be a no-op)", () => {
|
|
103
|
+
const stale: string[] = [];
|
|
104
|
+
for (const s of sources) {
|
|
105
|
+
for (const kf of s.knowledge) {
|
|
106
|
+
const src = path.join(s.srcDir, kf);
|
|
107
|
+
const dest = path.join(skillsDir, s.name, kf);
|
|
108
|
+
if (!fs.existsSync(src)) {
|
|
109
|
+
stale.push(`${s.name}/${kf} — declared but missing in source`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!fs.existsSync(dest)) {
|
|
113
|
+
stale.push(`${s.name}/${kf} — missing in .agents/skills (run forge upgrade)`);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const plan = planKnowledgeSync(src, dest);
|
|
117
|
+
if (plan.action === "merged") {
|
|
118
|
+
stale.push(`${s.name}/${kf} — source has entries the copy lacks`);
|
|
119
|
+
} else if (plan.action === "skipped") {
|
|
120
|
+
stale.push(`${s.name}/${kf} — copy diverged non-mergeably (hand-edited .agents copy)`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
expect(stale).toEqual([]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("undeclared aux files in the copy still match their sources", () => {
|
|
128
|
+
// The syncer writes SKILL.md + declared knowledge files only. Aux files
|
|
129
|
+
// (references, templates, placeholder dirs) that exist in BOTH trees are
|
|
130
|
+
// hand-copied and silently diverge — a same-named pair must stay identical,
|
|
131
|
+
// and a copy-only orphan can never resync.
|
|
132
|
+
const flagged: string[] = [];
|
|
133
|
+
for (const s of sources) {
|
|
134
|
+
const destDir = path.join(skillsDir, s.name);
|
|
135
|
+
if (!fs.existsSync(destDir)) continue;
|
|
136
|
+
const declared = new Set(["SKILL.md", ".forge-managed", ...s.knowledge]);
|
|
137
|
+
for (const entry of fs.readdirSync(destDir)) {
|
|
138
|
+
if (declared.has(entry)) continue;
|
|
139
|
+
const srcEntry = path.join(s.srcDir, entry);
|
|
140
|
+
const destEntry = path.join(destDir, entry);
|
|
141
|
+
if (fs.statSync(destEntry).isDirectory()) {
|
|
142
|
+
if (!fs.existsSync(srcEntry))
|
|
143
|
+
flagged.push(`${s.name}/${entry}/ — dir absent in source`);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (!fs.existsSync(srcEntry)) {
|
|
147
|
+
flagged.push(`${s.name}/${entry} — absent in source, copy can never resync`);
|
|
148
|
+
} else if (fs.readFileSync(srcEntry, "utf8") !== fs.readFileSync(destEntry, "utf8")) {
|
|
149
|
+
flagged.push(`${s.name}/${entry} — diverged from source`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
expect(
|
|
154
|
+
flagged,
|
|
155
|
+
"aux-file drift — declare aux files via `knowledge:` in SKILL.md or keep copies identical to source",
|
|
156
|
+
).toEqual([]);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("shared-knowledge layer is synced", () => {
|
|
160
|
+
const src = path.join(forgeRoot, "skills", "shared", "knowledge", "learned-principles.md");
|
|
161
|
+
const dest = path.join(skillsDir, "shared-knowledge", "learned-principles.md");
|
|
162
|
+
if (!fs.existsSync(src)) return; // layer absent — nothing to gate
|
|
163
|
+
expect(fs.existsSync(dest), "shared-knowledge/learned-principles.md not synced").toBe(true);
|
|
164
|
+
const plan = planKnowledgeSync(src, dest);
|
|
165
|
+
expect(plan.action, `shared-knowledge drift (sync action: ${plan.action})`).toBe("unchanged");
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
);
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Unit tests for RFC-1154 skill markers: .forge-managed manifests
|
|
4
|
+
recorded on sync, and the stale-dir prune under the subset-of-manifest rule
|
|
5
|
+
(keeps dirs with consumer files or corrupt markers, never touches unmarked
|
|
6
|
+
dirs).</purpose>
|
|
7
|
+
</MODULE_CONTRACT>
|
|
8
|
+
<CHANGE_SUMMARY>
|
|
9
|
+
<item>RFC-1154: initial marker + prune tests.</item>
|
|
10
|
+
</CHANGE_SUMMARY>
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { test, expect, beforeEach, afterEach } from "vitest";
|
|
14
|
+
import { mkdtemp, rm, writeFile, mkdir, readFile } from "node:fs/promises";
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import {
|
|
19
|
+
SKILL_MARKER_FILE,
|
|
20
|
+
writeSkillMarker,
|
|
21
|
+
writeSkillMarkerSync,
|
|
22
|
+
pruneStaleSkillDirs,
|
|
23
|
+
} from "../onboarding/skill-markers.ts";
|
|
24
|
+
|
|
25
|
+
let tmpDir: string;
|
|
26
|
+
let skillsDir: string;
|
|
27
|
+
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
tmpDir = await mkdtemp(join(tmpdir(), "skill-marker-"));
|
|
30
|
+
skillsDir = join(tmpDir, ".agents", "skills");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
async function makeSkillDir(name: string, files: string[]): Promise<void> {
|
|
38
|
+
const dir = join(skillsDir, name);
|
|
39
|
+
await mkdir(dir, { recursive: true });
|
|
40
|
+
for (const f of files) {
|
|
41
|
+
const filePath = join(dir, f);
|
|
42
|
+
await mkdir(join(filePath, ".."), { recursive: true });
|
|
43
|
+
await writeFile(filePath, `content of ${f}\n`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
test("AC-7: writeSkillMarker records the exact file list", async () => {
|
|
48
|
+
const dir = join(skillsDir, "fo-idea");
|
|
49
|
+
await mkdir(dir, { recursive: true });
|
|
50
|
+
await writeSkillMarker(dir, ["SKILL.md", "qa-log.md"]);
|
|
51
|
+
const raw = await readFile(join(dir, SKILL_MARKER_FILE), "utf8");
|
|
52
|
+
expect(JSON.parse(raw)).toEqual({ files: ["SKILL.md", "qa-log.md"] });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("marker write is idempotent (identical content on repeat)", async () => {
|
|
56
|
+
const dir = join(skillsDir, "fo-idea");
|
|
57
|
+
await mkdir(dir, { recursive: true });
|
|
58
|
+
writeSkillMarkerSync(dir, ["b.md", "a.md"]);
|
|
59
|
+
const first = await readFile(join(dir, SKILL_MARKER_FILE), "utf8");
|
|
60
|
+
writeSkillMarkerSync(dir, ["b.md", "a.md"]);
|
|
61
|
+
expect(await readFile(join(dir, SKILL_MARKER_FILE), "utf8")).toBe(first);
|
|
62
|
+
expect(JSON.parse(first).files).toEqual(["a.md", "b.md"]); // sorted, deterministic
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("AC-8: prunes a stale marked dir whose contents match the manifest", async () => {
|
|
66
|
+
const dir = join(skillsDir, "old-skill");
|
|
67
|
+
await makeSkillDir("old-skill", ["SKILL.md"]);
|
|
68
|
+
await writeSkillMarker(dir, ["SKILL.md"]);
|
|
69
|
+
|
|
70
|
+
const result = await pruneStaleSkillDirs(skillsDir, new Set(["other-skill"]));
|
|
71
|
+
expect(result.pruned).toEqual(["old-skill"]);
|
|
72
|
+
expect(result.keptWithConsumerFiles).toEqual([]);
|
|
73
|
+
expect(existsSync(dir)).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("AC-8: keeps a stale marked dir containing consumer files", async () => {
|
|
77
|
+
const dir = join(skillsDir, "old-skill");
|
|
78
|
+
await makeSkillDir("old-skill", ["SKILL.md", "my-notes.md"]);
|
|
79
|
+
await writeSkillMarker(dir, ["SKILL.md"]);
|
|
80
|
+
|
|
81
|
+
const result = await pruneStaleSkillDirs(skillsDir, new Set([]));
|
|
82
|
+
expect(result.pruned).toEqual([]);
|
|
83
|
+
expect(result.keptWithConsumerFiles).toHaveLength(1);
|
|
84
|
+
expect(result.keptWithConsumerFiles[0]).toContain("old-skill");
|
|
85
|
+
expect(result.keptWithConsumerFiles[0]).toContain("my-notes.md");
|
|
86
|
+
expect(existsSync(dir)).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("AC-9: unmarked dirs are never pruned", async () => {
|
|
90
|
+
const dir = join(skillsDir, "consumer-skill");
|
|
91
|
+
await makeSkillDir("consumer-skill", ["SKILL.md"]);
|
|
92
|
+
|
|
93
|
+
const result = await pruneStaleSkillDirs(skillsDir, new Set([]));
|
|
94
|
+
expect(result.pruned).toEqual([]);
|
|
95
|
+
expect(result.keptWithConsumerFiles).toEqual([]);
|
|
96
|
+
expect(existsSync(dir)).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("dirs still in the sync set are kept regardless of marker", async () => {
|
|
100
|
+
const dir = join(skillsDir, "current-skill");
|
|
101
|
+
await makeSkillDir("current-skill", ["SKILL.md"]);
|
|
102
|
+
await writeSkillMarker(dir, ["SKILL.md"]);
|
|
103
|
+
|
|
104
|
+
const result = await pruneStaleSkillDirs(skillsDir, new Set(["current-skill"]));
|
|
105
|
+
expect(result.pruned).toEqual([]);
|
|
106
|
+
expect(existsSync(dir)).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("corrupt marker disables prune for that dir (fail-safe keep)", async () => {
|
|
110
|
+
const dir = join(skillsDir, "broken-skill");
|
|
111
|
+
await makeSkillDir("broken-skill", ["SKILL.md"]);
|
|
112
|
+
await writeFile(join(dir, SKILL_MARKER_FILE), "not json {{{");
|
|
113
|
+
|
|
114
|
+
const result = await pruneStaleSkillDirs(skillsDir, new Set([]));
|
|
115
|
+
expect(result.pruned).toEqual([]);
|
|
116
|
+
expect(result.keptWithConsumerFiles).toHaveLength(1);
|
|
117
|
+
expect(result.keptWithConsumerFiles[0]).toContain("corrupt marker");
|
|
118
|
+
expect(existsSync(dir)).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("nested manifest files are matched recursively", async () => {
|
|
122
|
+
const dir = join(skillsDir, "nested-skill");
|
|
123
|
+
await makeSkillDir("nested-skill", ["SKILL.md", "knowledge/notes.md"]);
|
|
124
|
+
await writeSkillMarker(dir, ["SKILL.md", "knowledge/notes.md"]);
|
|
125
|
+
|
|
126
|
+
const result = await pruneStaleSkillDirs(skillsDir, new Set([]));
|
|
127
|
+
expect(result.pruned).toEqual(["nested-skill"]);
|
|
128
|
+
expect(existsSync(dir)).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("missing skills dir returns empty result", async () => {
|
|
132
|
+
const result = await pruneStaleSkillDirs(join(tmpDir, "nonexistent"), new Set());
|
|
133
|
+
expect(result).toEqual({ pruned: [], keptWithConsumerFiles: [] });
|
|
134
|
+
});
|
|
@@ -78,14 +78,14 @@ describe("RFC-0539: Pack skill validation (SKILL-14, SKILL-15)", () => {
|
|
|
78
78
|
});
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
describe("RFC-0548: SKILL-16
|
|
82
|
-
test("SKILL-16: real workspace fo-skills have valid
|
|
81
|
+
describe("RFC-0548: SKILL-16 triggerPhrases field validation", () => {
|
|
82
|
+
test("SKILL-16: real workspace fo-skills have valid triggerPhrases (no violations)", () => {
|
|
83
83
|
const result = runSkillValidate({}, { workspaceRoot: process.cwd() });
|
|
84
84
|
const skill16Violations = result.violations.filter((v) => v.rule === "SKILL-16");
|
|
85
85
|
expect(skill16Violations).toEqual([]);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
test("SKILL-16:
|
|
88
|
+
test("SKILL-16: triggerPhrases are only on fo-category skills", () => {
|
|
89
89
|
const result = runSkillValidate({}, { workspaceRoot: process.cwd() });
|
|
90
90
|
const skill16Violations = result.violations.filter(
|
|
91
91
|
(v) => v.rule === "SKILL-16" && v.message.includes("only allowed on fo-category"),
|
|
@@ -93,13 +93,20 @@ describe("RFC-0548: SKILL-16 triggers field validation", () => {
|
|
|
93
93
|
expect(skill16Violations).toEqual([]);
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
test("SKILL-16: no pack skills declare
|
|
96
|
+
test("SKILL-16: no pack skills declare triggerPhrases", () => {
|
|
97
97
|
const result = runSkillValidate({}, { workspaceRoot: process.cwd() });
|
|
98
98
|
const packTriggerViolations = result.violations.filter(
|
|
99
|
-
(v) =>
|
|
99
|
+
(v) =>
|
|
100
|
+
v.rule === "SKILL-16" && v.message.includes("pack skills may not declare triggerPhrases"),
|
|
100
101
|
);
|
|
101
102
|
expect(packTriggerViolations).toEqual([]);
|
|
102
103
|
});
|
|
104
|
+
|
|
105
|
+
test("SKILL-22: no skill declares the reserved `triggers` key", () => {
|
|
106
|
+
const result = runSkillValidate({}, { workspaceRoot: process.cwd() });
|
|
107
|
+
const skill22Violations = result.violations.filter((v) => v.rule === "SKILL-22");
|
|
108
|
+
expect(skill22Violations).toEqual([]);
|
|
109
|
+
});
|
|
103
110
|
});
|
|
104
111
|
|
|
105
112
|
describe("RFC-0553: SKILL-17 platform reference prohibition", () => {
|
|
@@ -9,11 +9,15 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { test, expect, beforeEach, afterEach, vi } from "vitest";
|
|
12
|
-
import { mkdtemp, mkdir, writeFile, rm, readFile } from "node:fs/promises";
|
|
12
|
+
import { mkdtemp, mkdir, writeFile, rm, readFile, appendFile } from "node:fs/promises";
|
|
13
13
|
import { existsSync } from "node:fs";
|
|
14
14
|
import { tmpdir } from "node:os";
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { runUpgrade } from "../onboarding/upgrade.ts";
|
|
17
|
+
import { generateNestedAgentsMd } from "../onboarding/nested-agents-generate.ts";
|
|
18
|
+
import { ambientIo } from "../utils/io.ts";
|
|
19
|
+
import { buildGeneratedHeader } from "../utils/index.ts";
|
|
20
|
+
import type { WorkspaceIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io";
|
|
17
21
|
import { runInit } from "../onboarding/init.ts";
|
|
18
22
|
import {
|
|
19
23
|
loadForgeConfig,
|
|
@@ -163,7 +167,9 @@ test("forge.upgrade with syncedVersion matching installed version returns noop",
|
|
|
163
167
|
expect(result.data?.status).toBe("noop");
|
|
164
168
|
expect(result.data?.fromVersion).toBe("0.2.0");
|
|
165
169
|
expect(result.data?.toVersion).toBe("0.2.0");
|
|
166
|
-
|
|
170
|
+
// RFC-1154: the reconcile pass still runs on a same-version noop — skills it
|
|
171
|
+
// re-synced are reported in skillsUpdated.
|
|
172
|
+
expect(result.data?.skillsUpdated).toContain("fo-idea");
|
|
167
173
|
expect(result.exitCode).toBe(0);
|
|
168
174
|
});
|
|
169
175
|
|
|
@@ -358,66 +364,40 @@ test("forge.upgrade --update-npm attempts npm install for npm consumer", async (
|
|
|
358
364
|
await setupNpmConsumerForge(tempDir, "0.2.0");
|
|
359
365
|
await setupConsumerForgeYaml(tempDir, null);
|
|
360
366
|
|
|
361
|
-
//
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
exec: vi.fn(),
|
|
367
|
-
execFile: vi.fn(),
|
|
368
|
-
spawn: vi.fn(),
|
|
369
|
-
fork: vi.fn(),
|
|
370
|
-
}));
|
|
371
|
-
|
|
372
|
-
// Re-import with mock
|
|
373
|
-
const { runUpgrade: runUpgradeMocked } = await import("../onboarding/upgrade.ts");
|
|
374
|
-
|
|
375
|
-
const result = await runUpgradeMocked(
|
|
367
|
+
// RFC-1152: inject a fake WorkspaceIO — exec is the port seam; real fs stays.
|
|
368
|
+
const execMock = vi.fn(async () => ({ exitCode: 0, stdout: "", stderr: "" }));
|
|
369
|
+
const io = { ...ambientIo, exec: execMock } as WorkspaceIO;
|
|
370
|
+
|
|
371
|
+
const result = await runUpgrade(
|
|
376
372
|
{ argv: [], flags: { "update-npm": true } },
|
|
377
|
-
makeContext(tempDir, false),
|
|
373
|
+
{ ...makeContext(tempDir, false), io },
|
|
378
374
|
);
|
|
379
375
|
|
|
380
376
|
expect(result.data?.status).toBe("pass");
|
|
381
|
-
expect(
|
|
377
|
+
expect(execMock).toHaveBeenCalled();
|
|
382
378
|
expect(result.data?.npmUpdated).toBe(true);
|
|
383
379
|
expect(result.data?.npmUpdateSkipped).toBe(null);
|
|
384
|
-
|
|
385
|
-
vi.doUnmock("node:child_process");
|
|
386
|
-
vi.resetModules();
|
|
387
380
|
});
|
|
388
381
|
|
|
389
382
|
test("forge.upgrade --update-npm reports failure when npm install throws", async () => {
|
|
390
383
|
await setupNpmConsumerForge(tempDir, "0.2.0");
|
|
391
384
|
await setupConsumerForgeYaml(tempDir, null);
|
|
392
385
|
|
|
393
|
-
//
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
});
|
|
397
|
-
vi.resetModules();
|
|
398
|
-
vi.doMock("node:child_process", () => ({
|
|
399
|
-
execSync: execSyncMock,
|
|
400
|
-
exec: vi.fn(),
|
|
401
|
-
execFile: vi.fn(),
|
|
402
|
-
spawn: vi.fn(),
|
|
403
|
-
fork: vi.fn(),
|
|
404
|
-
}));
|
|
405
|
-
|
|
406
|
-
const { runUpgrade: runUpgradeMocked } = await import("../onboarding/upgrade.ts");
|
|
386
|
+
// RFC-1152: fake io.exec fails — simulates network error / registry unavailable
|
|
387
|
+
const execMock = vi.fn(async () => ({ exitCode: 1, stdout: "", stderr: "network error" }));
|
|
388
|
+
const io = { ...ambientIo, exec: execMock } as WorkspaceIO;
|
|
407
389
|
|
|
408
|
-
const result = await
|
|
390
|
+
const result = await runUpgrade(
|
|
409
391
|
{ argv: [], flags: { "update-npm": true } },
|
|
410
|
-
makeContext(tempDir, false),
|
|
392
|
+
{ ...makeContext(tempDir, false), io },
|
|
411
393
|
);
|
|
412
394
|
|
|
413
395
|
expect(result.data?.status).toBe("pass");
|
|
414
|
-
expect(
|
|
396
|
+
expect(execMock).toHaveBeenCalled();
|
|
415
397
|
expect(result.data?.npmUpdated).toBe(false);
|
|
416
398
|
expect(result.data?.npmUpdateSkipped).toBe(
|
|
417
399
|
"install failed (network error or registry unavailable)",
|
|
418
400
|
);
|
|
419
|
-
|
|
420
|
-
vi.doUnmock("node:child_process");
|
|
421
401
|
vi.resetModules();
|
|
422
402
|
});
|
|
423
403
|
|
|
@@ -432,6 +412,72 @@ test("forge.upgrade without --update-npm does not attempt npm install", async ()
|
|
|
432
412
|
expect(result.data?.npmUpdateSkipped).toBe(null);
|
|
433
413
|
});
|
|
434
414
|
|
|
415
|
+
// RFC-1153: dry-run preview of the nested-AGENTS.md merge --------------------
|
|
416
|
+
|
|
417
|
+
test("AC-7: forge.upgrade --dry-run reports nestedAgentsPlanned and nestedAgentsSkipped", async () => {
|
|
418
|
+
await setupForgeSource(tempDir, "0.2.0");
|
|
419
|
+
await setupConsumerForgeYaml(tempDir, null);
|
|
420
|
+
|
|
421
|
+
// Workspace with no AGENTS.md — merged output would be written → planned
|
|
422
|
+
await mkdir(join(tempDir, "packages", "newpkg"), { recursive: true });
|
|
423
|
+
await writeFile(join(tempDir, "packages", "newpkg", "package.json"), '{"name":"newpkg"}', "utf8");
|
|
424
|
+
|
|
425
|
+
// Marker-bearing file that diverges with no resolvable boundary → skipped
|
|
426
|
+
await mkdir(join(tempDir, "packages", "unmapped"), { recursive: true });
|
|
427
|
+
await writeFile(
|
|
428
|
+
join(tempDir, "packages", "unmapped", "package.json"),
|
|
429
|
+
'{"name":"unmapped"}',
|
|
430
|
+
"utf8",
|
|
431
|
+
);
|
|
432
|
+
await writeFile(
|
|
433
|
+
join(tempDir, "packages", "unmapped", "AGENTS.md"),
|
|
434
|
+
`${buildGeneratedHeader({ filePath: "AGENTS.md", ownerCommand: "forge.agents.generate", editable: true })}\n# Hand-maintained\n\ndivergent body\n`,
|
|
435
|
+
"utf8",
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
const result = await runUpgrade(
|
|
439
|
+
{ argv: [], flags: { "dry-run": true } },
|
|
440
|
+
makeContext(tempDir, true),
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
expect(result.data?.status).toBe("pass");
|
|
444
|
+
const planned = result.data?.nestedAgentsPlanned ?? [];
|
|
445
|
+
const skipped = result.data?.nestedAgentsSkipped ?? [];
|
|
446
|
+
|
|
447
|
+
expect(
|
|
448
|
+
planned,
|
|
449
|
+
"planned must contain exactly the guides whose merged output differs from disk",
|
|
450
|
+
).toContain(join("packages", "newpkg", "AGENTS.md"));
|
|
451
|
+
expect(skipped.some((s) => s.includes("unmapped") && s.includes("unmapped-customization"))).toBe(
|
|
452
|
+
true,
|
|
453
|
+
);
|
|
454
|
+
// No files written under dry-run
|
|
455
|
+
expect(existsSync(join(tempDir, "packages", "newpkg", "AGENTS.md"))).toBe(false);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
test("forge.upgrade executing path reports nestedAgentsPreserved for carried tails", async () => {
|
|
459
|
+
await setupForgeSource(tempDir, "0.2.0");
|
|
460
|
+
await setupConsumerForgeYaml(tempDir, null);
|
|
461
|
+
|
|
462
|
+
// First run materializes the nested guide with the boundary marker
|
|
463
|
+
await mkdir(join(tempDir, "packages", "pkg"), { recursive: true });
|
|
464
|
+
await writeFile(join(tempDir, "packages", "pkg", "package.json"), '{"name":"pkg"}', "utf8");
|
|
465
|
+
const config = loadForgeConfig(tempDir);
|
|
466
|
+
await generateNestedAgentsMd(tempDir, config, false);
|
|
467
|
+
await appendFile(
|
|
468
|
+
join(tempDir, "packages", "pkg", "AGENTS.md"),
|
|
469
|
+
"\n## Custom rules\n\n- keep me\n",
|
|
470
|
+
"utf8",
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
const result = await runUpgrade({ argv: [], flags: {} }, makeContext(tempDir, false));
|
|
474
|
+
|
|
475
|
+
expect(result.data?.status).toBe("pass");
|
|
476
|
+
expect(result.data?.nestedAgentsPreserved).toContain(join("packages", "pkg", "AGENTS.md"));
|
|
477
|
+
const merged = await readFile(join(tempDir, "packages", "pkg", "AGENTS.md"), "utf8");
|
|
478
|
+
expect(merged).toContain("- keep me");
|
|
479
|
+
});
|
|
480
|
+
|
|
435
481
|
function qaLog(entries: { id: string; title: string; body: string }[]): string {
|
|
436
482
|
const blocks = entries.map(
|
|
437
483
|
(e) =>
|