mandrel 1.59.0 → 1.61.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/README.md +86 -44
- package/.agents/docs/SDLC.md +135 -141
- package/.agents/docs/configuration.md +77 -20
- package/.agents/docs/quality-gates.md +796 -0
- package/.agents/docs/workflows.md +6 -9
- package/.agents/instructions.md +12 -11
- package/.agents/personas/architect.md +1 -1
- package/.agents/personas/product.md +1 -1
- package/.agents/personas/project-manager.md +14 -14
- package/.agents/personas/technical-writer.md +1 -1
- package/.agents/rules/changelog-style.md +5 -5
- package/.agents/rules/git-conventions.md +3 -3
- package/.agents/runtime-deps.json +2 -2
- package/.agents/schemas/agentrc.schema.json +3 -3
- package/.agents/schemas/dispatch-manifest.json +4 -4
- package/.agents/schemas/epic-spec.schema.json +15 -45
- package/.agents/schemas/lifecycle/README.md +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/README.md +2 -2
- package/.agents/scripts/acceptance-eval.js +1 -1
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/agents-bootstrap-github.js +23 -119
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/check-doc-links.js +2 -3
- package/.agents/scripts/diagnose-friction.js +1 -1
- package/.agents/scripts/dispatcher.js +2 -2
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/epic-audit-prepare.js +3 -3
- package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
- package/.agents/scripts/epic-deliver-preflight.js +6 -6
- package/.agents/scripts/epic-deliver-prepare.js +1 -1
- package/.agents/scripts/epic-execute-record-wave.js +4 -4
- package/.agents/scripts/epic-plan-healthcheck.js +6 -10
- package/.agents/scripts/epic-plan-spec-validate.js +1 -1
- package/.agents/scripts/epic-reconcile.js +11 -29
- package/.agents/scripts/evidence-gate.js +1 -1
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +7 -11
- package/.agents/scripts/lib/ITicketingProvider.js +1 -1
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
- package/.agents/scripts/lib/baseline-snapshot.js +7 -7
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
- package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +47 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/bootstrap/gh-preflight.js +7 -9
- package/.agents/scripts/lib/bootstrap/manifest.js +21 -1
- package/.agents/scripts/lib/bootstrap/merge-methods.js +31 -16
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +32 -11
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/sync-agentrc.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
- package/.agents/scripts/lib/config-settings-schema.js +3 -3
- package/.agents/scripts/lib/detect-package-manager.js +72 -0
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- package/.agents/scripts/lib/errors/index.js +4 -4
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
- package/.agents/scripts/lib/findings/classify-finding.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +10 -10
- package/.agents/scripts/lib/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +5 -10
- package/.agents/scripts/lib/onboard/detect-stack.js +10 -10
- package/.agents/scripts/lib/onboard/init-tail.js +218 -0
- package/.agents/scripts/lib/onboard/scaffold-docs.js +18 -3
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
- package/.agents/scripts/lib/orchestration/code-review.js +5 -5
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
- package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +9 -25
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +8 -8
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +26 -13
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +6 -21
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +8 -8
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
- package/.agents/scripts/lib/orchestration/file-assumptions.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -3
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/preflight-cache.js +1 -1
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
- package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
- package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +2 -2
- package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
- package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +2 -2
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
- package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +5 -12
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +8 -8
- package/.agents/scripts/lib/orchestration/ticketing/state.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +2 -2
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +1 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
- package/.agents/scripts/lib/preflight-runner.js +1 -1
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
- package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
- package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
- package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
- package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
- package/.agents/scripts/lib/runtime-deps/preflight.js +6 -6
- package/.agents/scripts/lib/signals/schema.js +1 -1
- package/.agents/scripts/lib/spec/index.js +1 -1
- package/.agents/scripts/lib/spec/loader.js +2 -2
- package/.agents/scripts/lib/spec/state.js +7 -16
- package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
- package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
- package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
- package/.agents/scripts/lib/story-lifecycle.js +8 -8
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/wave-runner/tick.js +1 -1
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +5 -2
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
- package/.agents/scripts/lifecycle-emit.js +1 -1
- package/.agents/scripts/providers/github/board-add.js +1 -1
- package/.agents/scripts/providers/github/errors.js +1 -1
- package/.agents/scripts/providers/github/mappers.js +2 -2
- package/.agents/scripts/providers/github/tickets.js +4 -4
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +1 -1
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +5 -5
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +13 -16
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +1 -1
- package/.agents/scripts/validate-docs-freshness.js +1 -1
- package/.agents/scripts/wave-tick.js +1 -1
- package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
- package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
- package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
- package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
- package/.agents/skills/core/scope-triage/SKILL.md +9 -10
- package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
- package/.agents/skills/skills.index.json +7 -7
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +16 -31
- package/.agents/workflows/audit-architecture.md +2 -2
- package/.agents/workflows/audit-clean-code.md +2 -2
- package/.agents/workflows/audit-dependencies.md +1 -1
- package/.agents/workflows/audit-devops.md +1 -1
- package/.agents/workflows/audit-documentation.md +2 -2
- package/.agents/workflows/audit-lighthouse.md +1 -1
- package/.agents/workflows/audit-performance.md +2 -2
- package/.agents/workflows/audit-privacy.md +1 -1
- package/.agents/workflows/audit-quality.md +2 -2
- package/.agents/workflows/audit-security.md +2 -2
- package/.agents/workflows/audit-seo.md +1 -1
- package/.agents/workflows/audit-sre.md +1 -1
- package/.agents/workflows/audit-to-stories.md +10 -10
- package/.agents/workflows/audit-ux-ui.md +1 -1
- package/.agents/workflows/deliver.md +85 -0
- package/.agents/workflows/explain.md +3 -3
- package/.agents/workflows/git-merge-pr.md +1 -1
- package/.agents/workflows/git-pr-all.md +13 -10
- package/.agents/workflows/git-push.md +6 -3
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
- package/.agents/workflows/helpers/agents-sync-config.md +3 -2
- package/.agents/workflows/helpers/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +43 -43
- package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
- package/.agents/workflows/helpers/diagnose.md +1 -1
- package/.agents/workflows/helpers/epic-audit.md +6 -6
- package/.agents/workflows/helpers/epic-deliver-story.md +13 -13
- package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
- package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
- package/.agents/workflows/helpers/epic-testing.md +3 -3
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
- package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
- package/.agents/workflows/helpers/signals.md +1 -1
- package/.agents/workflows/helpers/single-story-deliver.md +11 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/plan.md +131 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +19 -39
- package/bin/mandrel.js +235 -16
- package/docs/CHANGELOG.md +1173 -0
- package/lib/cli/doctor.js +45 -3
- package/lib/cli/init.js +97 -36
- package/lib/cli/registry.js +41 -145
- package/lib/cli/sync.js +122 -23
- package/lib/cli/uninstall.js +42 -7
- package/lib/cli/update.js +524 -210
- package/lib/cli/version-helpers.js +59 -0
- package/package.json +7 -6
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/workflows/onboard.md +0 -208
- package/lib/cli/__tests__/migrate.test.js +0 -268
- package/lib/cli/__tests__/sync-local-zone.test.js +0 -247
- package/lib/cli/__tests__/sync.test.js +0 -372
- package/lib/cli/__tests__/update-major.test.js +0 -217
- package/lib/cli/__tests__/update.test.js +0 -696
- package/lib/cli/__tests__/version-check.test.js +0 -398
- package/lib/migrations/__tests__/index.test.js +0 -216
package/lib/cli/sync.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* `./.agents/` directory by **plain file copy** — never a symlink — so
|
|
8
8
|
* Windows and POSIX behave identically (Tech Spec #3459, Feature #3461).
|
|
9
9
|
*
|
|
10
|
-
* Design contract (per Story #3467 AC
|
|
10
|
+
* Design contract (per Story #3467 AC, Tech Spec #3459 "API Changes", and
|
|
11
|
+
* Story #4046 sync-prune):
|
|
11
12
|
* - Copy, not symlink. The materialized tree is plain regular files.
|
|
12
13
|
* - Idempotent. A second run overwrites in place and leaves ./.agents/
|
|
13
14
|
* byte-identical to the package payload.
|
|
@@ -17,12 +18,14 @@
|
|
|
17
18
|
* - Exits non-zero with an actionable message when `mandrel` is
|
|
18
19
|
* not resolvable in node_modules.
|
|
19
20
|
* - `--dry-run` reports the planned copies and writes nothing.
|
|
20
|
-
* - `--force` is accepted; the copy is overwrite-in-place either way, so
|
|
21
|
-
* it exists for explicitness/forward-compat and changes no behaviour.
|
|
22
21
|
* - Local-additions zone. The `.agents/local/` subtree is never copied
|
|
23
22
|
* into nor pruned from the destination (Story #3498). It is the
|
|
24
23
|
* consumer-owned space for hand-authored additions that must survive
|
|
25
24
|
* every re-materialization.
|
|
25
|
+
* - Sync prune (Story #4046). After the copy pass, any file inside the
|
|
26
|
+
* managed `.agents/` zone (everything except `.agents/local/`) that has
|
|
27
|
+
* no counterpart in the package payload is deleted. Consumer additions
|
|
28
|
+
* under `.agents/local/` are never touched.
|
|
26
29
|
*
|
|
27
30
|
* Security (Tech Spec #3459 "Postinstall safety"):
|
|
28
31
|
* - Does nothing beyond a local file copy: no network, no shell, no writes
|
|
@@ -42,7 +45,7 @@ import nodeFs from 'node:fs';
|
|
|
42
45
|
import { createRequire } from 'node:module';
|
|
43
46
|
import path from 'node:path';
|
|
44
47
|
|
|
45
|
-
const PACKAGE_NAME = 'mandrel';
|
|
48
|
+
export const PACKAGE_NAME = 'mandrel';
|
|
46
49
|
|
|
47
50
|
/**
|
|
48
51
|
* Top-level directory name (relative to `.agents/`) reserved as the
|
|
@@ -56,7 +59,16 @@ const PACKAGE_NAME = 'mandrel';
|
|
|
56
59
|
* which keeps both the dry-run plan and the real copy free of any
|
|
57
60
|
* `.agents/local/**` path even if a future payload were to ship one.
|
|
58
61
|
*/
|
|
59
|
-
const LOCAL_ZONE_DIR = 'local';
|
|
62
|
+
export const LOCAL_ZONE_DIR = 'local';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Basename pattern for consumer local-override files (`*.local.<ext>`,
|
|
66
|
+
* e.g. `instructions.local.md`, `foo.local.json`). Matches the gitignore
|
|
67
|
+
* convention (`.agents/*.local.md`, `.agentrc.local.json`) and the override
|
|
68
|
+
* mechanism documented in `.agents/instructions.md` § 1.E. These files never
|
|
69
|
+
* ship in the payload and are exempt from the sync prune pass.
|
|
70
|
+
*/
|
|
71
|
+
export const LOCAL_OVERRIDE_RE = /\.local\.[^.]+$/;
|
|
60
72
|
|
|
61
73
|
/**
|
|
62
74
|
* Default resolver: locate the installed `mandrel` package root by
|
|
@@ -68,7 +80,7 @@ const LOCAL_ZONE_DIR = 'local';
|
|
|
68
80
|
* @param {string} fromDir - Directory to resolve from (the consumer project).
|
|
69
81
|
* @returns {string} Absolute path to the package root.
|
|
70
82
|
*/
|
|
71
|
-
function defaultResolvePackageRoot(fromDir) {
|
|
83
|
+
export function defaultResolvePackageRoot(fromDir) {
|
|
72
84
|
// Resolve relative to the consumer project so we find *their* install,
|
|
73
85
|
// not a copy hoisted next to this CLI module.
|
|
74
86
|
const requireFrom = createRequire(path.join(fromDir, 'noop.js'));
|
|
@@ -88,13 +100,13 @@ function defaultResolvePackageRoot(fromDir) {
|
|
|
88
100
|
* from the package payload (Story #3498). See {@link LOCAL_ZONE_DIR}.
|
|
89
101
|
*
|
|
90
102
|
* @param {string} dir - Absolute directory to walk.
|
|
91
|
-
* @param {typeof nodeFs}
|
|
103
|
+
* @param {typeof nodeFs} fsImpl
|
|
92
104
|
* @param {string} [prefix] - Accumulated relative prefix (internal).
|
|
93
105
|
* @returns {string[]} Relative file paths.
|
|
94
106
|
*/
|
|
95
|
-
function listFiles(dir,
|
|
107
|
+
export function listFiles(dir, fsImpl, prefix = '') {
|
|
96
108
|
const out = [];
|
|
97
|
-
for (const ent of
|
|
109
|
+
for (const ent of fsImpl.readdirSync(dir, { withFileTypes: true })) {
|
|
98
110
|
// Never enumerate the sync-exempt local-additions zone. Matching on the
|
|
99
111
|
// empty prefix scopes the skip to the top-level `.agents/local/` only,
|
|
100
112
|
// leaving any deeper directory that happens to be named `local` intact.
|
|
@@ -104,7 +116,55 @@ function listFiles(dir, fs, prefix = '') {
|
|
|
104
116
|
const rel = prefix ? path.join(prefix, ent.name) : ent.name;
|
|
105
117
|
const abs = path.join(dir, ent.name);
|
|
106
118
|
if (ent.isDirectory()) {
|
|
107
|
-
out.push(...listFiles(abs,
|
|
119
|
+
out.push(...listFiles(abs, fsImpl, rel));
|
|
120
|
+
} else {
|
|
121
|
+
out.push(rel);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Recursively enumerate every regular file under `dir`, returning paths
|
|
129
|
+
* relative to `dir` using OS separators. The top-level `local/` subtree is
|
|
130
|
+
* skipped so consumer additions inside `.agents/local/` are never pruned
|
|
131
|
+
* (Story #3498). Files following the `*.local.*` naming convention (e.g.
|
|
132
|
+
* `.agents/instructions.local.md` — the documented consumer override
|
|
133
|
+
* mechanism, instructions.md § 1.E) are also skipped: they never ship in
|
|
134
|
+
* the payload and must survive every sync.
|
|
135
|
+
*
|
|
136
|
+
* Mirrors `listFiles` but operates on the destination tree so we can
|
|
137
|
+
* identify stale files that have no payload counterpart (Story #4046 A3).
|
|
138
|
+
*
|
|
139
|
+
* @param {string} dir - Absolute directory to walk.
|
|
140
|
+
* @param {typeof nodeFs} fsImpl
|
|
141
|
+
* @param {string} [prefix] - Accumulated relative prefix (internal).
|
|
142
|
+
* @returns {string[]} Relative file paths.
|
|
143
|
+
*/
|
|
144
|
+
function listDestFiles(dir, fsImpl, prefix = '') {
|
|
145
|
+
const out = [];
|
|
146
|
+
let entries;
|
|
147
|
+
try {
|
|
148
|
+
entries = fsImpl.readdirSync(dir, { withFileTypes: true });
|
|
149
|
+
} catch {
|
|
150
|
+
// Directory absent — nothing to prune.
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
for (const ent of entries) {
|
|
154
|
+
// The local-additions zone is never pruned; skip it at the top level.
|
|
155
|
+
if (prefix === '' && ent.name === LOCAL_ZONE_DIR && ent.isDirectory()) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
// Consumer local-override files (`*.local.*`, e.g. instructions.local.md,
|
|
159
|
+
// foo.local.json) are gitignored, never shipped in the payload, and a
|
|
160
|
+
// documented override mechanism (instructions.md § 1.E) — never prune.
|
|
161
|
+
if (!ent.isDirectory() && LOCAL_OVERRIDE_RE.test(ent.name)) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const rel = prefix ? path.join(prefix, ent.name) : ent.name;
|
|
165
|
+
const abs = path.join(dir, ent.name);
|
|
166
|
+
if (ent.isDirectory()) {
|
|
167
|
+
out.push(...listDestFiles(abs, fsImpl, rel));
|
|
108
168
|
} else {
|
|
109
169
|
out.push(rel);
|
|
110
170
|
}
|
|
@@ -115,6 +175,11 @@ function listFiles(dir, fs, prefix = '') {
|
|
|
115
175
|
/**
|
|
116
176
|
* Materialize the package's `.agents/` tree into `./.agents/`.
|
|
117
177
|
*
|
|
178
|
+
* After the copy pass, any file inside the managed zone of the destination
|
|
179
|
+
* `.agents/` tree (everything outside `.agents/local/`) that has no
|
|
180
|
+
* counterpart in the package payload is deleted (sync-prune, Story #4046 A3).
|
|
181
|
+
* Consumer additions placed under `.agents/local/` are never touched.
|
|
182
|
+
*
|
|
118
183
|
* @param {{
|
|
119
184
|
* argv?: string[],
|
|
120
185
|
* resolvePackageRoot?: (fromDir: string) => string,
|
|
@@ -124,8 +189,8 @@ function listFiles(dir, fs, prefix = '') {
|
|
|
124
189
|
* writeErr?: (s: string) => void,
|
|
125
190
|
* exit?: (code: number) => void,
|
|
126
191
|
* }} [opts]
|
|
127
|
-
* @returns {{ copied: number, planned: number, dryRun: boolean }}
|
|
128
|
-
* (also returned in dry-run / error paths for testability).
|
|
192
|
+
* @returns {{ copied: number, planned: number, pruned: number, dryRun: boolean }}
|
|
193
|
+
* Summary (also returned in dry-run / error paths for testability).
|
|
129
194
|
*/
|
|
130
195
|
export function runSync({
|
|
131
196
|
argv = [],
|
|
@@ -137,8 +202,6 @@ export function runSync({
|
|
|
137
202
|
exit = (code) => process.exit(code),
|
|
138
203
|
} = {}) {
|
|
139
204
|
const dryRun = argv.includes('--dry-run');
|
|
140
|
-
// `--force` overwrites local edits. The copy is overwrite-in-place
|
|
141
|
-
// regardless, so the flag is accepted but does not branch behaviour.
|
|
142
205
|
const projectRoot = cwd();
|
|
143
206
|
|
|
144
207
|
let packageRoot;
|
|
@@ -150,7 +213,7 @@ export function runSync({
|
|
|
150
213
|
` → Install it first: npm install ${PACKAGE_NAME}\n`,
|
|
151
214
|
);
|
|
152
215
|
exit(1);
|
|
153
|
-
return { copied: 0, planned: 0, dryRun };
|
|
216
|
+
return { copied: 0, planned: 0, pruned: 0, dryRun };
|
|
154
217
|
}
|
|
155
218
|
|
|
156
219
|
const sourceRoot = path.join(packageRoot, '.agents');
|
|
@@ -160,23 +223,36 @@ export function runSync({
|
|
|
160
223
|
` → Reinstall the package: npm install ${PACKAGE_NAME}\n`,
|
|
161
224
|
);
|
|
162
225
|
exit(1);
|
|
163
|
-
return { copied: 0, planned: 0, dryRun };
|
|
226
|
+
return { copied: 0, planned: 0, pruned: 0, dryRun };
|
|
164
227
|
}
|
|
165
228
|
|
|
166
229
|
const destRoot = path.join(projectRoot, '.agents');
|
|
167
|
-
const
|
|
230
|
+
const payloadFiles = listFiles(sourceRoot, fs);
|
|
168
231
|
|
|
169
232
|
if (dryRun) {
|
|
170
|
-
for (const rel of
|
|
233
|
+
for (const rel of payloadFiles) {
|
|
171
234
|
write(`would copy ${path.join('.agents', rel)}\n`);
|
|
172
235
|
}
|
|
236
|
+
// Compute stale files (managed-zone destination files with no payload
|
|
237
|
+
// counterpart) for the dry-run plan so operators can preview pruning.
|
|
238
|
+
const payloadSet = new Set(payloadFiles);
|
|
239
|
+
const destFiles = listDestFiles(destRoot, fs);
|
|
240
|
+
const stale = destFiles.filter((f) => !payloadSet.has(f));
|
|
241
|
+
for (const rel of stale) {
|
|
242
|
+
write(`would prune ${path.join('.agents', rel)}\n`);
|
|
243
|
+
}
|
|
173
244
|
write(
|
|
174
|
-
`✅ Dry run: ${
|
|
245
|
+
`✅ Dry run: ${payloadFiles.length} file(s) would be materialized, ${stale.length} stale file(s) would be pruned from ./.agents/\n`,
|
|
175
246
|
);
|
|
176
|
-
return {
|
|
247
|
+
return {
|
|
248
|
+
copied: 0,
|
|
249
|
+
planned: payloadFiles.length,
|
|
250
|
+
pruned: 0,
|
|
251
|
+
dryRun: true,
|
|
252
|
+
};
|
|
177
253
|
}
|
|
178
254
|
|
|
179
|
-
for (const rel of
|
|
255
|
+
for (const rel of payloadFiles) {
|
|
180
256
|
const src = path.join(sourceRoot, rel);
|
|
181
257
|
const dest = path.join(destRoot, rel);
|
|
182
258
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
@@ -185,8 +261,31 @@ export function runSync({
|
|
|
185
261
|
fs.copyFileSync(src, dest);
|
|
186
262
|
}
|
|
187
263
|
|
|
188
|
-
|
|
189
|
-
|
|
264
|
+
// Prune pass (Story #4046 A3): remove managed-zone destination files that
|
|
265
|
+
// have no counterpart in the payload. The local-additions zone
|
|
266
|
+
// (.agents/local/) is never enumerated by listDestFiles and is therefore
|
|
267
|
+
// never pruned — consumer additions there are sanctioned, not stale.
|
|
268
|
+
const payloadSet = new Set(payloadFiles);
|
|
269
|
+
const destFiles = listDestFiles(destRoot, fs);
|
|
270
|
+
const staleFiles = destFiles.filter((f) => !payloadSet.has(f));
|
|
271
|
+
for (const rel of staleFiles) {
|
|
272
|
+
const dest = path.join(destRoot, rel);
|
|
273
|
+
fs.unlinkSync(dest);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (staleFiles.length > 0) {
|
|
277
|
+
write(
|
|
278
|
+
`✅ Materialized ${payloadFiles.length} file(s) into ./.agents/ (pruned ${staleFiles.length} stale file(s))\n`,
|
|
279
|
+
);
|
|
280
|
+
} else {
|
|
281
|
+
write(`✅ Materialized ${payloadFiles.length} file(s) into ./.agents/\n`);
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
copied: payloadFiles.length,
|
|
285
|
+
planned: payloadFiles.length,
|
|
286
|
+
pruned: staleFiles.length,
|
|
287
|
+
dryRun: false,
|
|
288
|
+
};
|
|
190
289
|
}
|
|
191
290
|
|
|
192
291
|
/**
|
package/lib/cli/uninstall.js
CHANGED
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
|
|
55
55
|
import fs from 'node:fs';
|
|
56
56
|
import path from 'node:path';
|
|
57
|
-
import { fileURLToPath } from 'node:url';
|
|
58
57
|
|
|
59
58
|
import {
|
|
60
59
|
LEDGER_SCHEMA_VERSION,
|
|
@@ -640,6 +639,7 @@ function formatOutcome(outcome) {
|
|
|
640
639
|
* write?: (s: string) => void,
|
|
641
640
|
* exit?: (code: number) => void,
|
|
642
641
|
* includeGithub?: boolean,
|
|
642
|
+
* dryRun?: boolean,
|
|
643
643
|
* }} [opts]
|
|
644
644
|
* @returns {{ revertedCount: number, manualCount: number, ledgerFound: boolean, parseErrorCount: number }}
|
|
645
645
|
*/
|
|
@@ -650,6 +650,7 @@ export function runUninstall({
|
|
|
650
650
|
write = (s) => process.stdout.write(s),
|
|
651
651
|
exit = (code) => process.exit(code),
|
|
652
652
|
includeGithub = false,
|
|
653
|
+
dryRun = false,
|
|
653
654
|
} = {}) {
|
|
654
655
|
const root = projectRoot ?? resolveProjectRoot(cwd);
|
|
655
656
|
|
|
@@ -695,6 +696,39 @@ export function runUninstall({
|
|
|
695
696
|
|
|
696
697
|
const { fileTargets, manual, executedActionByTarget } = planUninstall(ledger);
|
|
697
698
|
|
|
699
|
+
// --- Dry run — show what would be reversed without touching anything. ---
|
|
700
|
+
if (dryRun) {
|
|
701
|
+
write('mandrel uninstall — planned reversal (dry run)\n');
|
|
702
|
+
for (const target of fileTargets) {
|
|
703
|
+
write(
|
|
704
|
+
formatOutcome({
|
|
705
|
+
kind: 'reverted',
|
|
706
|
+
target,
|
|
707
|
+
detail: '(would be reverted)',
|
|
708
|
+
}),
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
for (const entry of manual) {
|
|
712
|
+
const note = includeGithub
|
|
713
|
+
? `${entry.detail} (acknowledged — reverse manually via the GitHub UI/API)`
|
|
714
|
+
: `${entry.detail} (left untouched — pass --include-github to acknowledge)`;
|
|
715
|
+
write(
|
|
716
|
+
formatOutcome({ kind: 'manual', target: entry.target, detail: note }),
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
write(
|
|
720
|
+
`Dry run: ${fileTargets.length} file target(s) would be reverted, ` +
|
|
721
|
+
`${manual.length} manual follow-up(s).\n`,
|
|
722
|
+
);
|
|
723
|
+
exit(0);
|
|
724
|
+
return {
|
|
725
|
+
revertedCount: 0,
|
|
726
|
+
manualCount: manual.length,
|
|
727
|
+
ledgerFound: true,
|
|
728
|
+
parseErrorCount: 0,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
|
|
698
732
|
let revertedCount = 0;
|
|
699
733
|
let parseErrorCount = 0;
|
|
700
734
|
for (const target of fileTargets) {
|
|
@@ -782,14 +816,15 @@ export function runUninstall({
|
|
|
782
816
|
/**
|
|
783
817
|
* Default export consumed by `bin/mandrel.js`.
|
|
784
818
|
*
|
|
785
|
-
*
|
|
819
|
+
* Supported flags:
|
|
820
|
+
* --include-github Acknowledge GitHub-side manual follow-ups.
|
|
821
|
+
* --dry-run Show what would be reversed without touching anything.
|
|
822
|
+
*
|
|
823
|
+
* @param {string[]} argv
|
|
786
824
|
* @returns {Promise<void>}
|
|
787
825
|
*/
|
|
788
826
|
export default async function run(argv = []) {
|
|
789
827
|
const includeGithub = argv.includes('--include-github');
|
|
790
|
-
|
|
828
|
+
const dryRun = argv.includes('--dry-run');
|
|
829
|
+
runUninstall({ includeGithub, dryRun });
|
|
791
830
|
}
|
|
792
|
-
|
|
793
|
-
// Re-export so tests and callers can reference the resolved module path
|
|
794
|
-
// without re-deriving it.
|
|
795
|
-
export const __filenameForTests = fileURLToPath(import.meta.url);
|