devflow-kit 3.8.0 → 3.10.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.
Files changed (211) hide show
  1. package/CHANGELOG.md +231 -0
  2. package/README.md +6 -6
  3. package/dist/agents/code.md +10 -10
  4. package/dist/agents/fix.md +7 -7
  5. package/dist/agents/git.md +2 -2
  6. package/dist/agents/harden.md +7 -6
  7. package/dist/agents/refactor.md +6 -6
  8. package/dist/agents/skim.md +1 -1
  9. package/dist/agents/triage.md +7 -0
  10. package/dist/cli/commands/ambient.js +10 -2
  11. package/dist/cli/commands/compliance-prompts.js +9 -4
  12. package/dist/cli/commands/compliance.js +171 -24
  13. package/dist/cli/commands/curation.js +79 -23
  14. package/dist/cli/commands/gate-level-prompts.js +39 -23
  15. package/dist/cli/commands/gates.js +6 -7
  16. package/dist/cli/commands/init.js +150 -145
  17. package/dist/cli/commands/install-report.js +34 -0
  18. package/dist/cli/commands/legacy-sweep.js +64 -0
  19. package/dist/cli/commands/memory.js +59 -12
  20. package/dist/cli/commands/proxy.js +6 -1
  21. package/dist/cli/commands/rules.js +29 -12
  22. package/dist/cli/commands/skills.js +42 -9
  23. package/dist/cli/commands/tracker.js +9 -3
  24. package/dist/cli/commands/uninstall.js +354 -60
  25. package/dist/commands/bug-analysis.md +71 -122
  26. package/dist/commands/code-review.md +11 -11
  27. package/dist/commands/debug.md +2 -2
  28. package/dist/commands/dynamic-build.md +25 -25
  29. package/dist/commands/dynamic-plan.md +2 -2
  30. package/dist/commands/dynamic-profile.md +2 -2
  31. package/dist/commands/dynamic-tickets.md +2 -2
  32. package/dist/commands/explore.md +2 -2
  33. package/dist/commands/implement.md +48 -34
  34. package/dist/commands/plan.md +2 -2
  35. package/dist/commands/release.md +3 -3
  36. package/dist/commands/research.md +2 -2
  37. package/dist/commands/resolve.md +50 -46
  38. package/dist/commands/self-review.md +10 -9
  39. package/dist/compliance-off/agents/git.md +120 -0
  40. package/dist/compliance-off/agents/triage.md +180 -0
  41. package/dist/compliance-off/commands/bug-analysis.md +398 -0
  42. package/dist/compliance-off/commands/code-review.md +520 -0
  43. package/dist/compliance-off/commands/dynamic-build.md +1251 -0
  44. package/dist/compliance-off/commands/dynamic-tickets.md +676 -0
  45. package/dist/compliance-off/commands/implement.md +736 -0
  46. package/dist/compliance-off/commands/plan.md +671 -0
  47. package/dist/compliance-off/commands/release.md +309 -0
  48. package/dist/compliance-off/commands/resolve.md +742 -0
  49. package/dist/compliance-off/skills/git/references/decision-markers.md +17 -0
  50. package/dist/compliance-off/skills/git/references/learn-conventions.md +80 -0
  51. package/dist/compliance-off/skills/git/references/pr/check-ci-status.md +33 -0
  52. package/dist/compliance-off/skills/git/references/pr/check-merge-readiness.md +49 -0
  53. package/dist/compliance-off/skills/git/references/pr/fetch-review-threads.md +52 -0
  54. package/dist/compliance-off/skills/git/references/pr/validate-branch.md +47 -0
  55. package/dist/compliance-off/skills/git/references/tracker/_contract.md +33 -0
  56. package/dist/compliance-off/skills/git/references/tracker/_mcp.md +153 -0
  57. package/dist/compliance-off/skills/git/references/tracker/github/create-release.md +35 -0
  58. package/dist/compliance-off/skills/git/references/tracker/github/ensure-pr-ready.md +100 -0
  59. package/dist/compliance-off/skills/git/references/tracker/github/ensure-traceable-issue.md +74 -0
  60. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issue.md +69 -0
  61. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issues-batch.md +65 -0
  62. package/dist/compliance-off/skills/git/references/tracker/github/gather-release-evidence.md +48 -0
  63. package/dist/compliance-off/skills/git/references/tracker/github/setup-task.md +77 -0
  64. package/dist/compliance-off/skills/git/references/tracker/jira/create-release.md +35 -0
  65. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-pr-ready.md +100 -0
  66. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-traceable-issue.md +61 -0
  67. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issue.md +51 -0
  68. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issues-batch.md +63 -0
  69. package/dist/compliance-off/skills/git/references/tracker/jira/gather-release-evidence.md +47 -0
  70. package/dist/compliance-off/skills/git/references/tracker/jira/setup-task.md +82 -0
  71. package/dist/compliance-off/skills/git/references/tracker/linear/create-release.md +35 -0
  72. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-pr-ready.md +100 -0
  73. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-traceable-issue.md +61 -0
  74. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issue.md +51 -0
  75. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issues-batch.md +63 -0
  76. package/dist/compliance-off/skills/git/references/tracker/linear/gather-release-evidence.md +47 -0
  77. package/dist/compliance-off/skills/git/references/tracker/linear/setup-task.md +83 -0
  78. package/dist/compliance-off/skills/git/references/trust-rule.md +7 -0
  79. package/dist/core/agent-models.js +19 -4
  80. package/dist/core/area-pause-notice.js +44 -0
  81. package/dist/core/assets.js +126 -17
  82. package/dist/core/compliance-off-files.js +27 -0
  83. package/dist/core/curation-move-skip.js +4 -5
  84. package/dist/core/curation-queue-cleanup.js +6 -5
  85. package/dist/core/curation-store.js +1 -0
  86. package/dist/core/curation-variants.js +460 -112
  87. package/dist/core/evidence-policy.js +10 -8
  88. package/dist/core/flags.js +2 -2
  89. package/dist/core/installed-variants.js +63 -0
  90. package/dist/core/manifest.js +48 -5
  91. package/dist/core/mds-variants.js +700 -3
  92. package/dist/core/orphan-sweep.js +11 -2
  93. package/dist/core/plugins.js +69 -12
  94. package/dist/core/tracked-areas.js +138 -14
  95. package/dist/curation-compliance-off/agents/triage.md +176 -0
  96. package/dist/curation-compliance-off/commands/bug-analysis.md +349 -0
  97. package/dist/curation-compliance-off/commands/code-review.md +481 -0
  98. package/dist/curation-compliance-off/commands/dynamic-build.md +1211 -0
  99. package/dist/curation-compliance-off/commands/dynamic-tickets.md +634 -0
  100. package/dist/curation-compliance-off/commands/implement.md +689 -0
  101. package/dist/curation-compliance-off/commands/plan.md +631 -0
  102. package/dist/curation-compliance-off/commands/release.md +279 -0
  103. package/dist/curation-compliance-off/commands/resolve.md +692 -0
  104. package/dist/curation-off/agents/code.md +10 -10
  105. package/dist/curation-off/agents/fix.md +7 -7
  106. package/dist/curation-off/agents/harden.md +7 -6
  107. package/dist/curation-off/agents/refactor.md +6 -6
  108. package/dist/curation-off/agents/triage.md +7 -0
  109. package/dist/curation-off/commands/bug-analysis.md +69 -119
  110. package/dist/curation-off/commands/code-review.md +11 -11
  111. package/dist/curation-off/commands/debug.md +2 -2
  112. package/dist/curation-off/commands/dynamic-build.md +25 -25
  113. package/dist/curation-off/commands/explore.md +2 -2
  114. package/dist/curation-off/commands/implement.md +48 -34
  115. package/dist/curation-off/commands/plan.md +2 -2
  116. package/dist/curation-off/commands/release.md +1 -1
  117. package/dist/curation-off/commands/resolve.md +50 -46
  118. package/dist/curation-off/commands/self-review.md +10 -9
  119. package/dist/hud/components/curation-counts.js +27 -0
  120. package/dist/skills/git/references/pr/post-resolution-summary.md +12 -11
  121. package/dist/skills/git/references/pr/post-review-summary.md +12 -11
  122. package/dist/skills/git/references/pr/resolve-review-threads.md +1 -1
  123. package/dist/skills/git/references/pr/update-pr-evidence.md +2 -2
  124. package/dist/skills/git/references/publication-gate.md +6 -7
  125. package/dist/skills/git/references/tracker/github/manage-debt.md +6 -2
  126. package/dist/skills/git/references/tracker/jira/manage-debt.md +6 -2
  127. package/dist/skills/git/references/tracker/linear/manage-debt.md +6 -2
  128. package/dist/targets/claude-code/behavior-install.js +23 -9
  129. package/dist/targets/claude-code/charter-render.js +374 -0
  130. package/dist/targets/claude-code/compliance-install.js +32 -12
  131. package/dist/targets/claude-code/compliance-off-files.js +245 -0
  132. package/dist/targets/claude-code/curation-install.js +570 -100
  133. package/dist/targets/claude-code/installer.js +467 -62
  134. package/dist/targets/claude-code/language-stamp.js +6 -0
  135. package/dist/targets/claude-code/linked-folder.js +182 -0
  136. package/dist/targets/claude-code/reference-converge.js +118 -0
  137. package/dist/targets/claude-code/tracker-install.js +21 -8
  138. package/dist/targets/claude-code/tracker-servers.js +20 -6
  139. package/dist/targets/claude-code/workflow-folder-install.js +231 -0
  140. package/dist/variant-axes.json +109 -0
  141. package/package.json +1 -1
  142. package/src/assets/agents/_partials/_code.mds +6 -6
  143. package/src/assets/agents/code.mds +4 -4
  144. package/src/assets/agents/evaluate.md +7 -13
  145. package/src/assets/agents/fix.mds +1 -1
  146. package/src/assets/agents/git.mds +34 -2
  147. package/src/assets/agents/harden.mds +7 -6
  148. package/src/assets/agents/skim.mds +1 -1
  149. package/src/assets/agents/test.md +21 -12
  150. package/src/assets/agents/triage.mds +9 -0
  151. package/src/assets/agents/validate.md +27 -34
  152. package/src/assets/commands/_partials/_compliance.mds +5 -3
  153. package/src/assets/commands/_partials/_decisions.mds +10 -8
  154. package/src/assets/commands/_partials/_engine.mds +9 -8
  155. package/src/assets/commands/_partials/_publication.mds +10 -1
  156. package/src/assets/commands/_partials/_settings.mds +2 -2
  157. package/src/assets/commands/bug-analysis.mds +80 -126
  158. package/src/assets/commands/code-review.mds +52 -9
  159. package/src/assets/commands/dynamic-build.mds +109 -15
  160. package/src/assets/commands/dynamic-tickets.mds +13 -0
  161. package/src/assets/commands/implement.mds +145 -33
  162. package/src/assets/commands/plan.mds +19 -0
  163. package/src/assets/commands/release.mds +50 -1
  164. package/src/assets/commands/resolve.mds +229 -44
  165. package/src/assets/commands/self-review.mds +8 -7
  166. package/src/assets/mds/git/_ops.mds +29 -1
  167. package/src/assets/mds/git/_ops_pr.mds +20 -10
  168. package/src/assets/mds/git/_ops_steps.mds +27 -0
  169. package/src/assets/mds/git/_pr.mds +15 -15
  170. package/src/assets/mds/git/_references.mds +12 -7
  171. package/src/assets/mds/tracker/_common.mds +4 -0
  172. package/src/assets/mds/tracker/_github.mds +43 -4
  173. package/src/assets/mds/tracker/_jira.mds +44 -4
  174. package/src/assets/mds/tracker/_linear.mds +44 -4
  175. package/src/assets/rules/quality.md +2 -0
  176. package/src/assets/scripts/ci-wait.cjs +493 -118
  177. package/src/assets/scripts/hooks/assets/implement-installed +1 -0
  178. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +13 -11
  179. package/src/assets/scripts/hooks/background-memory-update +79 -1
  180. package/src/assets/scripts/hooks/json-helper.cjs +44 -2
  181. package/src/assets/scripts/hooks/lib/curation-store.cjs +82 -20
  182. package/src/assets/scripts/hooks/lib/render-decisions.cjs +9 -1
  183. package/src/assets/scripts/hooks/lib/tracked-areas.cjs +226 -33
  184. package/src/assets/scripts/hooks/memory-permission +10 -2
  185. package/src/assets/scripts/hooks/memory-worker +11 -0
  186. package/src/assets/scripts/hooks/pre-compact-memory +11 -0
  187. package/src/assets/scripts/hooks/preamble +18 -8
  188. package/src/assets/scripts/hooks/queue-append +218 -1
  189. package/src/assets/scripts/hooks/session-start-context +89 -37
  190. package/src/assets/scripts/hooks/session-start-memory +39 -0
  191. package/src/assets/scripts/hooks/session-start-orchestrator +4 -2
  192. package/src/assets/scripts/pr-evidence.cjs +46 -4
  193. package/src/assets/scripts/resolve-settings.cjs +244 -30
  194. package/src/assets/scripts/verify-evidence.cjs +708 -124
  195. package/src/assets/skills/agent-references/SKILL.md +7 -1
  196. package/src/assets/skills/agent-references/references/commands/implement-levels.md +6 -4
  197. package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +3 -3
  198. package/src/assets/skills/agent-references/references/commands/plan-levels.md +1 -1
  199. package/src/assets/skills/agent-references/references/commands/resolve-levels.md +3 -2
  200. package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +6 -2
  201. package/src/assets/skills/agent-references/references/synthesize/review.md +4 -4
  202. package/src/assets/skills/agent-references/references/triage/severity-tiers.md +43 -0
  203. package/src/assets/skills/agent-references/references/validate/passed-alone.md +14 -0
  204. package/src/assets/skills/docs-framework/SKILL.md +5 -2
  205. package/src/assets/skills/git/SKILL.md +3 -3
  206. package/src/assets/skills/git/references/github-api.md +2 -139
  207. package/src/assets/skills/git/references/github-posting.md +158 -0
  208. package/src/assets/skills/review-methodology/SKILL.md +1 -1
  209. package/src/assets/skills/review-methodology/references/patterns.md +1 -1
  210. package/src/assets/skills/review-methodology/references/report-template.md +1 -2
  211. package/src/assets/skills/testing/SKILL.md +2 -4
@@ -14,16 +14,18 @@ import { loadCurationMove } from '../../core/curation-move.js';
14
14
  import { describeMoveText, describeOutcome, runInitCurationMove, shownSkipAt } from './curation-move-step.js';
15
15
  import { realGlobalTuningMoveIO, realProjectLedgerMoveIO } from './curation-move-io.js';
16
16
  import { describeEchoedValue } from '../../core/echo-value.js';
17
- import { installViaFileCopy, composeScripts } from '../../targets/claude-code/installer.js';
18
- import { formatOverlaySummary, formatSkillScopeSummary, formatTrackerAssetSummary, isPluginListUnchanged } from './install-report.js';
17
+ import { installViaFileCopy, installScripts } from '../../targets/claude-code/installer.js';
18
+ import { formatLinksSummary, formatOverlaySummary, formatSkillScopeSummary, formatTrackerAssetSummary, isPluginListUnchanged } from './install-report.js';
19
+ import { mergeLinkReports, newLinkReport } from '../../targets/claude-code/linked-folder.js';
20
+ import { sweepLegacyDevflowFiles } from './legacy-sweep.js';
19
21
  import { convergeTrackerArtifacts } from '../../targets/claude-code/tracker-install.js';
20
22
  import { installSettings, installManagedSettings, installClaudeignore, hasClaudeignore, discoverProjectGitRoots, ensureDevflowGitignore, applyUserSecurityDenyList, detectDenyState, resolveSecurityAction, assertHistoricalDenySuperset, loadTemplateDenyEntries, stripUserSecurityDenyList, } from '../../targets/claude-code/post-install.js';
21
- import { DEVFLOW_PLUGINS, LEGACY_COMMAND_NAMES, LEGACY_RULE_NAMES, buildAssetMaps, buildScopedSkillsMap, buildRulesMap, partitionSelectablePlugins, WORKFLOW_ORDER, parsePluginSelection, resolveFeatureRedirect } from '../../core/plugins.js';
23
+ import { DEVFLOW_PLUGINS, registeredPluginNames, buildAssetMaps, buildScopedSkillsMap, buildRulesMap, partitionSelectablePlugins, WORKFLOW_ORDER, parsePluginSelection, resolveFeatureRedirect } from '../../core/plugins.js';
22
24
  import { LEGACY_SKILL_NAMES } from '../../targets/claude-code/legacy.js';
23
25
  import { detectPlatform, detectShell, getProfilePath, getSafeDeleteInfo, hasSafeDelete } from '../../core/safe-delete.js';
24
26
  import { generateSafeDeleteBlock, installToProfile, removeFromProfile, getInstalledVersion, SAFE_DELETE_BLOCK_VERSION } from '../../core/safe-delete-install.js';
25
27
  import { convergeAmbientHooks } from './ambient.js';
26
- import { convergeMemoryHooks, drainMemoryQueue, workingMemoryNote } from './memory.js';
28
+ import { convergeMemoryHooks, drainMemoryQueue, formatMemoryNotDrained, workingMemoryNote } from './memory.js';
27
29
  import { addCaptureHooks, removeCaptureHooks } from './capture.js';
28
30
  import { removeDreamHook } from './legacy-hooks.js';
29
31
  import { addProxyHooks, removeProxyHooks, applyProxyEnv, stripProxyEnv, runProxyPreflight, buildRealPreflightDeps } from './proxy.js';
@@ -39,7 +41,6 @@ import { addContextHook, removeContextHook, hasContextHook } from './context.js'
39
41
  import { writeSettingsFileAtomic } from '../../core/fs-atomic.js';
40
42
  import { writeManagedConfig, readConfigIfPresent, DEFAULT_CONFIG } from '../../core/feature-config.js';
41
43
  import { drainDisabledCurationQueue, formatCurationNotDrained } from '../../core/curation-queue-cleanup.js';
42
- import { formatRefusedDrain } from '../../core/queue-drain.js';
43
44
  import { removeManagedDenyList, describeManagedDenyRemoval } from './security.js';
44
45
  import { resolveInitSeed, applyCliToggles, resolveResetGatedInputs, resolvePluginsToInstall } from './init-seed.js';
45
46
  import { parseFrameworkList, normalizeFrameworks } from '../../core/compliance.js';
@@ -50,8 +51,8 @@ import { applyTrackerSentinel, effectiveTrackerServers, parseTrackerId, rearmTra
50
51
  import { formatTrackerSummary, shouldRunTrackerStep, runTrackerStep, buildClackTrackerPrompts, } from './tracker-prompts.js';
51
52
  import { buildClackGateLevelPrompts, carriedGateLevel, formatGateLevelRow, formatGateLevelSummary, readStoredGateLevel, resolveGateLevelForInit, resolveGateLevelInitState, } from './gate-level-prompts.js';
52
53
  import { shouldRunAttributionStep, runAttributionStep, buildClackAttributionPrompts, applyAttributionAnswer, attributionSeedFrom, } from './attribution-prompts.js';
53
- import { convergeFromManifest } from '../../targets/claude-code/compliance-install.js';
54
- import { convergeCurationVariants, installedVersionMatches, readRunningVersion } from '../../targets/claude-code/curation-install.js';
54
+ import { convergeVariantAxes, describePostingLeft, installedVersionMatches, readRunningVersion } from '../../targets/claude-code/curation-install.js';
55
+ import { POSTING_OFF_NOTICE, installRecordFor, postingNoticeDue, readInstalledVariants } from '../../core/installed-variants.js';
55
56
  import { auditAndRecordClaudeMd, formatClaudeMdAuditNote, formatClaudeMdAuditUnavailable, } from '../../core/claude-md-audit.js';
56
57
  import * as os from 'os';
57
58
  // Re-export pure functions for tests (canonical source is post-install.ts)
@@ -146,6 +147,43 @@ function logSummaryLines(lines) {
146
147
  }
147
148
  }
148
149
  }
150
+ /**
151
+ * Converge the installed agents onto the saved model and effort assignments, after the file copy
152
+ * wrote the shipped defaults. Per-item failures are non-fatal.
153
+ *
154
+ * Init-only optimization: with an empty mapping and the proxy off, every agent already holds its
155
+ * shipped default, so the walk would read the agent files and write none; it is skipped. The
156
+ * disable and revert paths call `reapplyAgentMapping` directly and always need the full walk.
157
+ * `hasAgentMappingEntries` counts agent entries only: an `agents.memory`-only mapping names no
158
+ * installed file, so it keeps this gate closed (D-WORKER-AGENTS).
159
+ *
160
+ * The links this walk met are returned, not printed: the caller prints them once, through the same
161
+ * summary as the install step. `verbose` prints the other warnings of the walk.
162
+ */
163
+ export async function reapplyAgentMappingForInit(opts) {
164
+ const { proxyEnabled, claudeDir, devflowDir, verbose } = opts;
165
+ const preCheckMapping = await readAgentMapping(devflowDir);
166
+ const hasMappingEntries = preCheckMapping.ok && hasAgentMappingEntries(preCheckMapping.value);
167
+ if (!hasMappingEntries && !proxyEnabled)
168
+ return newLinkReport();
169
+ const result = await reapplyAgentMapping({
170
+ proxyEnabled,
171
+ installDir: path.join(claudeDir, 'agents', 'devflow'),
172
+ devflowDir,
173
+ });
174
+ const links = { replacedLinks: result.replacedLinks, refusedFolders: result.refusedFolders };
175
+ if (verbose) {
176
+ const linkLines = new Set(formatLinksSummary(links).map(line => line.message));
177
+ for (const warning of result.warnings) {
178
+ if (!linkLines.has(warning))
179
+ p.log.warn(warning);
180
+ }
181
+ if (result.updated.length > 0) {
182
+ p.log.info(`Agent model mapping reapplied: ${result.updated.length} agent(s) updated`);
183
+ }
184
+ }
185
+ return links;
186
+ }
149
187
  /**
150
188
  * Classify the safe-delete installation state based on the installed version
151
189
  * in the user's shell profile.
@@ -517,11 +555,13 @@ export async function lookupDrainLedgerRoot(opts, lookup = lookupLedgerRoot) {
517
555
  *
518
556
  * Returns one warning for each drain refused because a folder on the way to its
519
557
  * queue is a symbolic link (D-CLI-NO-SYMLINK), one for a curation drain that
520
- * could not list the backups (the live queue is drained then), and one for a curation
558
+ * could not list the backups (the live queue is drained then), one for a curation
521
559
  * drain that left the project alone because git tracks a data folder or did not answer
522
- * (the fixed notice of a skipped move); the caller prints them. When the move step has
523
- * printed that notice for the ledger root (`shownSkip`, its reason), the drain's line
524
- * does not repeat it.
560
+ * (the fixed notice of a paused curation area), and one for a memory drain that left the project
561
+ * alone because git tracks the memory folder or did not answer (the fixed notice of a
562
+ * paused memory area); the caller prints them. When the move step has printed its own
563
+ * notice of a skipped move for the ledger root (`shownSkip`, its reason), the curation
564
+ * drain's line is only the short sentence that the queue was not drained.
525
565
  */
526
566
  export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue, drainCurationQueue: drainDisabledCurationQueue }) {
527
567
  if (!opts.manifestWritten)
@@ -530,7 +570,7 @@ export async function drainDisabledFeatureQueues(opts, io = { drainMemoryQueue,
530
570
  if (!opts.memoryEnabled && opts.gitRoot !== null) {
531
571
  const memory = await io.drainMemoryQueue(opts.gitRoot);
532
572
  if (!memory.drained)
533
- refused.push(formatRefusedDrain('memory', memory.linkedFolder));
573
+ refused.push(formatMemoryNotDrained(memory));
534
574
  }
535
575
  if (!opts.curationEnabled && opts.ledgerRoot !== null) {
536
576
  const curation = await io.drainCurationQueue(opts.ledgerRoot);
@@ -590,6 +630,20 @@ export function buildHudOnlyManifest(existing, version, now, refusedGateLevel) {
590
630
  updatedAt: now,
591
631
  };
592
632
  }
633
+ /**
634
+ * The plugin names `init --hud-only` renders the orchestrator charter for. Pure.
635
+ *
636
+ * D-CHARTER-INSTALL-RENDER: --hud-only reinstalls no plugin, so the installed charter must keep describing
637
+ * what the manifest says is installed: the recorded plugins, with a deleted name pruned and a legacy name
638
+ * mapped as every other partial run does (`resolvePluginList`), and only names this registry has kept
639
+ * ({@link registeredPluginNames}). With no manifest, or one `readManifest()` refused, nothing is known to be installed, so the set is EMPTY: the
640
+ * render then names no workflow, and no sentinel is written. The registry fallback that uninstall takes
641
+ * would hand a machine with no record every workflow's routing line, which is the failure the render exists
642
+ * to prevent.
643
+ */
644
+ export function hudOnlyCharterPlugins(manifest) {
645
+ return registeredPluginNames(resolvePluginList([], manifest, true));
646
+ }
593
647
  /**
594
648
  * The line `init --hud-only` prints when the version the manifest records is not the
595
649
  * running version, or null when there is nothing to say. Pure.
@@ -703,8 +757,8 @@ export const initCommand = new Command('init')
703
757
  .option('--no-proxy', 'Disable external model routing')
704
758
  .option('--behavior', 'Enable the behavior rule (agents reply in ASD-STE100 Simplified Technical English; off by default)')
705
759
  .option('--no-behavior', 'Disable the behavior rule')
706
- .option('--compliance <list>', 'Enable compliance with comma-separated framework IDs (e.g., gdpr,hipaa)')
707
- .option('--no-compliance', 'Disable compliance (removes the rule; the skill and framework references stay installed; frameworks remembered for re-enable)')
760
+ .option('--compliance <list>', 'Enable compliance with comma-separated framework IDs (e.g., gdpr,hipaa); installs the steps that post evidence and summaries to GitHub or a tracker')
761
+ .option('--no-compliance', 'Disable compliance (removes the rule and every step that posts evidence or summaries to GitHub or a tracker; the skill and framework references stay installed; frameworks remembered for re-enable)')
708
762
  .option('--tracker <id>', 'The machine\'s default issue tracker provider: github, jira, or linear')
709
763
  .option('--gate-level <full|standard|lean>', 'The machine\'s default gate level: full, standard, or lean (skips the gate level prompt)')
710
764
  .option('--security <mode>', 'Security deny list location: user, managed, or none', /^(user|managed|none)$/i)
@@ -780,22 +834,28 @@ export const initCommand = new Command('init')
780
834
  p.log.error(`Failed to update settings: ${error instanceof Error ? error.message : error}`);
781
835
  process.exit(1);
782
836
  }
783
- // Install HUD scripts to devflow dir
784
- const scriptsTarget = path.join(devflowDir, 'scripts');
785
- try {
786
- await composeScripts(scriptsTarget);
787
- }
788
- catch (error) {
789
- p.log.error(`Failed to install HUD scripts: ${error instanceof Error ? error.message : error}`);
790
- process.exit(1);
791
- }
792
837
  // Read the existing manifest: over a prior install, --hud-only touches only
793
- // the HUD (D-HUD-ONLY-PRESERVE in buildHudOnlyManifest).
838
+ // the HUD (D-HUD-ONLY-PRESERVE in buildHudOnlyManifest). Read before the scripts are
839
+ // installed, so the charter render works from the record as the run found it.
794
840
  let existingHudManifest = null;
795
841
  try {
796
842
  existingHudManifest = await readManifest(devflowDir);
797
843
  }
798
844
  catch { /* absent on fresh install — existingHudManifest stays null */ }
845
+ // Install HUD scripts to devflow dir. The orchestrator charter and the implement sentinel
846
+ // render for the plugins the manifest records, and for the empty set when it records none
847
+ // (D-CHARTER-INSTALL-RENDER). A render failure is a warning.
848
+ try {
849
+ await installScripts({
850
+ scriptsTarget: path.join(devflowDir, 'scripts'),
851
+ plugins: hudOnlyCharterPlugins(existingHudManifest),
852
+ warn: (msg) => p.log.warn(msg),
853
+ });
854
+ }
855
+ catch (error) {
856
+ p.log.error(`Failed to install HUD scripts: ${error instanceof Error ? error.message : error}`);
857
+ process.exit(1);
858
+ }
799
859
  // A manifest readManifest() refuses reads as none, but its machine gate level is still
800
860
  // stored there: --hud-only keeps it and says so, as the full init does. --reset does not
801
861
  // apply here, so nothing asks to forget it.
@@ -1311,9 +1371,9 @@ export const initCommand = new Command('init')
1311
1371
  }
1312
1372
  else {
1313
1373
  p.note('Puts every session (git repos only) in orchestrator posture:\n' +
1314
- 'a ~535-token charter at session start plus a per-prompt reminder\n' +
1374
+ 'a charter of up to ~850 tokens at session start plus a per-prompt reminder\n' +
1315
1375
  'steer the main model to delegate work to agents and devflow workflows\n' +
1316
- 'instead of doing it mainline. Plan-mode handoffs auto-run devflow:implement.', 'Ambient Mode');
1376
+ 'instead of doing it mainline. Plan-mode handoffs run devflow:implement when it is installed.', 'Ambient Mode');
1317
1377
  const ambientChoice = await p.select({
1318
1378
  message: 'Enable ambient mode?',
1319
1379
  options: [
@@ -1809,6 +1869,10 @@ export const initCommand = new Command('init')
1809
1869
  // their say (D-LEARNING-VARIANT-INSTALL): the files installed here and the
1810
1870
  // manifest written at the end of init cannot disagree about it.
1811
1871
  curation: curationEnabled,
1872
+ // The SETTLED compliance switch, never a raw seed or a repository's narrowing: the
1873
+ // variant follows the machine switch alone, and with it off the install refuses
1874
+ // before it writes a file when the variant files are not all there.
1875
+ compliance: complianceEnabled,
1812
1876
  trackerServers,
1813
1877
  // Non-fatal install notices with no other channel (skipped symlinks in the
1814
1878
  // generated reference tree, mode-normalisation failures) reach the user rather
@@ -1821,55 +1885,44 @@ export const initCommand = new Command('init')
1821
1885
  p.log.error(`${error}`);
1822
1886
  process.exit(1);
1823
1887
  }
1824
- // D-LEARNING-VARIANT-INSTALL: converge the curation variants. The file copy above
1825
- // installed the variant of everything IT installed; on a `--plugin` install the
1826
- // other plugins' files, already on disk, may still be the other variant (this run
1827
- // may have flipped the switch), and the apply-decisions skill is removed here when
1828
- // the copy skipped it on a partial install. Rewrites only what is installed.
1829
- // Warn-not-abort: either mismatch is safe (an on variant is still gated at run
1830
- // time, an off variant loads no decisions). The agent mapping is reapplied below.
1831
- try {
1832
- const curationConverge = await convergeCurationVariants({
1833
- claudeDir,
1834
- devflowDir,
1835
- curation: curationEnabled,
1836
- plugins: effectivePlugins,
1837
- warn: (msg) => installWarnings.push(msg),
1838
- });
1839
- if (verbose && curationConverge.converged) {
1840
- p.log.info(`Curation ${curationEnabled ? 'on' : 'off'} variants: ` +
1841
- `${curationConverge.commandsRewritten.length} command(s) and ${curationConverge.agentsRewritten.length} agent(s) ` +
1842
- `rewritten, ${curationConverge.unchanged} already current`);
1843
- }
1844
- }
1845
- catch (err) {
1846
- installWarnings.push(`Curation variant convergence failed — ${err instanceof Error ? err.message : String(err)}`);
1847
- }
1848
- // Converge compliance artifacts (always converge, never short-circuit).
1849
- // Called unconditionally so that enabling/disabling compliance during init
1850
- // is reflected in the installed artifacts without a separate devflow compliance run.
1851
- // Wrapped in its own try/catch (warn-not-abort).
1852
- let convergeResult = null;
1853
- try {
1854
- convergeResult = await convergeFromManifest({
1855
- claudeDir,
1856
- devflowDir,
1857
- manifest: { features: { compliance: { enabled: complianceEnabled, frameworks: complianceFrameworks }, rules: rulesEnabled } },
1858
- warn: (msg) => p.log.warn(msg),
1859
- });
1860
- // I41: emit legacy-upgrade notice when compliance is disabled AND a pre-existing rule
1861
- // was found. The skill is no signal — converge installs it on every machine
1862
- // (D-COMPLIANCE-INSTALL-ALWAYS) — so removedPreexisting reports the rule alone, on a
1863
- // partial install; hadComplianceRule covers full installs, where installViaFileCopy
1864
- // wipes the rules dir before converge probes it.
1865
- if (!complianceEnabled && (convergeResult.removedPreexisting || hadComplianceRule)) {
1866
- p.log.info('Compliance rule removed — if you previously had devflow-compliance installed, ' +
1867
- 'run `devflow compliance --enable` to re-enable with your framework selection.');
1868
- }
1869
- }
1870
- catch (err) {
1871
- p.log.warn(`Compliance artifact convergence failed — install succeeded but compliance artifacts may be stale: ` +
1872
- `${err instanceof Error ? err.message : String(err)}`);
1888
+ // The one converge for both switches (D-LEARNING-VARIANT-INSTALL, AC-27): the file copy
1889
+ // above installed the variant of everything IT installed; on a `--plugin` install the
1890
+ // other plugins' files, already on disk, may still be another variant (this run may
1891
+ // have flipped a switch), and the apply-decisions skill is removed here when the copy
1892
+ // skipped it on a partial install. It also converges the compliance skill and rule, so
1893
+ // enabling or disabling compliance during init needs no separate devflow compliance run.
1894
+ // Rewrites only what is installed. Warn-not-abort: a failure is a warning here and the
1895
+ // install record below is withheld, so the status line says to run init again. The agent
1896
+ // mapping is reapplied below.
1897
+ const variantConverge = await convergeVariantAxes({
1898
+ claudeDir,
1899
+ devflowDir,
1900
+ manifest: {
1901
+ features: {
1902
+ curation: curationEnabled,
1903
+ compliance: { enabled: complianceEnabled, frameworks: complianceFrameworks },
1904
+ rules: rulesEnabled,
1905
+ },
1906
+ },
1907
+ plugins: effectivePlugins,
1908
+ warn: (msg) => installWarnings.push(msg),
1909
+ });
1910
+ if (verbose && variantConverge.converged) {
1911
+ p.log.info(`Curation ${curationEnabled ? 'on' : 'off'}, compliance ${complianceEnabled ? 'on' : 'off'} variants: ` +
1912
+ `${variantConverge.commandsRewritten.length} command(s) and ${variantConverge.agentsRewritten.length} agent(s) ` +
1913
+ `rewritten, ${variantConverge.unchanged} already current`);
1914
+ }
1915
+ // A compliance-off converge that cannot prove the installed prompts free of posting steps
1916
+ // warns and names the files; init never exits non-zero for it (AC-28 binds the compliance command).
1917
+ installWarnings.push(...describePostingLeft(variantConverge.posting));
1918
+ // I41: emit legacy-upgrade notice when compliance is disabled AND a pre-existing rule
1919
+ // was found. The skill is no signal — converge installs it on every machine
1920
+ // (D-COMPLIANCE-INSTALL-ALWAYS) — so removedPreexisting reports the rule alone, on a
1921
+ // partial install; hadComplianceRule covers full installs, where installViaFileCopy
1922
+ // wipes the rules dir before converge probes it.
1923
+ if (!complianceEnabled && (variantConverge.compliance.removedPreexisting || hadComplianceRule)) {
1924
+ p.log.info('Compliance rule removed — if you previously had devflow-compliance installed, ' +
1925
+ 'run `devflow compliance --enable` to re-enable with your framework selection.');
1873
1926
  }
1874
1927
  // D-BEHAVIOR-OPT-IN: converge the behavior rule. The plugin installer never copies it (no
1875
1928
  // plugin declares it) and a full install wiped rules/devflow/ above, so this is the one
@@ -1898,52 +1951,12 @@ export const initCommand = new Command('init')
1898
1951
  if (staleRemoved > 0 && verbose) {
1899
1952
  p.log.info(`Cleaned up ${staleRemoved} legacy skill(s)`);
1900
1953
  }
1901
- // Clean up stale commands from previous installations (e.g., /review → /code-review)
1902
- const commandsDir = path.join(claudeDir, 'commands', 'devflow');
1903
- let staleCommandsRemoved = 0;
1904
- for (const legacy of LEGACY_COMMAND_NAMES) {
1905
- for (const suffix of ['.md']) {
1906
- const legacyPath = path.join(commandsDir, `${legacy}${suffix}`);
1907
- try {
1908
- await fs.rm(legacyPath);
1909
- staleCommandsRemoved++;
1910
- }
1911
- catch {
1912
- // Doesn't exist — expected for most entries
1913
- }
1914
- }
1915
- }
1916
- // Sweep orphaned *-teams.md workflow command variants left by the Agent Teams
1917
- // refactor. None are ever re-installed, so a blanket sweep is safe on any
1918
- // install type (the full-install dir wipe only covers full installs).
1919
- try {
1920
- for (const f of await fs.readdir(commandsDir)) {
1921
- if (f.endsWith('-teams.md')) {
1922
- try {
1923
- await fs.rm(path.join(commandsDir, f));
1924
- staleCommandsRemoved++;
1925
- }
1926
- catch { /* already gone */ }
1927
- }
1928
- }
1929
- }
1930
- catch { /* commands dir absent — nothing to sweep */ }
1954
+ // Clean up stale commands (e.g., /review → /code-review) and rules from previous installations.
1955
+ // A folder the install refused, or that is a link, is not swept: nothing is read or removed through it.
1956
+ const { staleCommandsRemoved, staleRulesRemoved } = await sweepLegacyDevflowFiles({ claudeDir, report: installReport });
1931
1957
  if (staleCommandsRemoved > 0 && verbose) {
1932
1958
  p.log.info(`Cleaned up ${staleCommandsRemoved} legacy command(s)`);
1933
1959
  }
1934
- // Clean up stale rules from previous installations
1935
- const rulesDir = path.join(claudeDir, 'rules', 'devflow');
1936
- let staleRulesRemoved = 0;
1937
- for (const legacy of LEGACY_RULE_NAMES) {
1938
- const legacyPath = path.join(rulesDir, `${legacy}.md`);
1939
- try {
1940
- await fs.rm(legacyPath);
1941
- staleRulesRemoved++;
1942
- }
1943
- catch {
1944
- // Doesn't exist — expected for most entries
1945
- }
1946
- }
1947
1960
  if (staleRulesRemoved > 0 && verbose) {
1948
1961
  p.log.info(`Cleaned up ${staleRulesRemoved} legacy rule(s)`);
1949
1962
  }
@@ -2113,33 +2126,9 @@ export const initCommand = new Command('init')
2113
2126
  // MUST run AFTER the proxy preflight block above: preflight can force proxyEnabled=false
2114
2127
  // on failure, and reapply's dormancy (GPT models materialize only while proxy enabled)
2115
2128
  // depends on the FINAL proxyEnabled value — running earlier would leave GPT model lines
2116
- // in agent frontmatter after a preflight failure. Per-item failures are non-fatal.
2117
- //
2118
- // Init-only optimization: skip reapply when mapping is empty AND proxy is off.
2119
- // An empty mapping means every agent uses its shipped default; the file copy already
2120
- // wrote those defaults, so reapply would read ~34 files and write zero. The
2121
- // disable/revert paths call reapplyAgentMapping directly and always need the full walk.
2122
- {
2123
- const agentInstallDir = path.join(claudeDir, 'agents', 'devflow');
2124
- const preCheckMapping = await readAgentMapping(devflowDir);
2125
- // hasAgentMappingEntries counts agent entries only: an agents.memory-only
2126
- // mapping names no installed file, so it keeps this gate closed (D-WORKER-AGENTS).
2127
- const hasMappingEntries = preCheckMapping.ok && hasAgentMappingEntries(preCheckMapping.value);
2128
- if (hasMappingEntries || proxyEnabled) {
2129
- const reapplyResult = await reapplyAgentMapping({
2130
- proxyEnabled,
2131
- installDir: agentInstallDir,
2132
- devflowDir,
2133
- onWarning: (msg) => { if (verbose)
2134
- p.log.warn(msg); },
2135
- });
2136
- if (reapplyResult.updated.length > 0) {
2137
- if (verbose) {
2138
- p.log.info(`Agent model mapping reapplied: ${reapplyResult.updated.length} agent(s) updated`);
2139
- }
2140
- }
2141
- }
2142
- }
2129
+ // in agent frontmatter after a preflight failure. Per-item failures are non-fatal. The links
2130
+ // the walk met are printed below, through the install summary.
2131
+ const agentMappingLinks = await reapplyAgentMappingForInit({ proxyEnabled, claudeDir, devflowDir, verbose });
2143
2132
  // Configure ambient hook, memory hooks, and HUD statusLine in a single read-modify-write pass
2144
2133
  try {
2145
2134
  let content = await fs.readFile(settingsPath, 'utf-8');
@@ -2436,6 +2425,10 @@ export const initCommand = new Command('init')
2436
2425
  // Reference-overlay reporting: the overlay rewrites files inside an installed skill
2437
2426
  // the user may have shadowed, and reports any unit it had to leave alone.
2438
2427
  logSummaryLines(formatOverlaySummary(installReport));
2428
+ // A link the install or the agent model reapply replaced is a change to a path the user made.
2429
+ // A folder either refused is not refreshed, and looks refreshed on disk. A path the two steps
2430
+ // both met is named once.
2431
+ logSummaryLines(formatLinksSummary(mergeLinkReports(installReport, agentMappingLinks)));
2439
2432
  // Skill-scoping reporting: a deselected skill is deleted and a dormant shadow
2440
2433
  // is inert, and neither is distinguishable from "never installed" on disk.
2441
2434
  //
@@ -2483,6 +2476,7 @@ export const initCommand = new Command('init')
2483
2476
  }
2484
2477
  // Write installation manifest for upgrade tracking (non-fatal — install already succeeded)
2485
2478
  const now = new Date().toISOString();
2479
+ const installRecord = installRecordFor(variantConverge.converged, { curation: curationEnabled, compliance: complianceEnabled });
2486
2480
  const manifestData = {
2487
2481
  version,
2488
2482
  // Resolved above, before the install, because the skills install set is
@@ -2524,6 +2518,9 @@ export const initCommand = new Command('init')
2524
2518
  },
2525
2519
  installedAt: existingManifest?.installedAt ?? now,
2526
2520
  updatedAt: now,
2521
+ // The install record, written only after a converge that finished clean (AC-29): an
2522
+ // unclean one leaves the field out, which reads as unknown and names `devflow init`.
2523
+ ...(installRecord !== null ? { installedVariants: installRecord } : {}),
2527
2524
  };
2528
2525
  // ── Manifest write + tracker selection lifecycle (the ONE call site) ──────
2529
2526
  // persistManifestThenConvergeTracker owns the ordering invariant: the
@@ -2541,6 +2538,14 @@ export const initCommand = new Command('init')
2541
2538
  else
2542
2539
  p.log.info(msg.text);
2543
2540
  }
2541
+ // The posting notice (AC-30): once, when the record moves from posting (compliance on, or no
2542
+ // record before this release) to non-posting. It follows the record write, so a record that
2543
+ // was withheld or never reached the disk leaves the notice due for the next init.
2544
+ if (trackerLifecycle.manifestWritten &&
2545
+ installRecord !== null &&
2546
+ postingNoticeDue(readInstalledVariants(existingManifest?.installedVariants), complianceEnabled)) {
2547
+ p.log.info(POSTING_OFF_NOTICE);
2548
+ }
2544
2549
  // Only now that the machine-wide switch is on disk (D-INIT-DRAIN-AFTER-SWITCH).
2545
2550
  const drainRoot = await lookupDrainLedgerRoot({ gitRoot, curationEnabled, manifestWritten: trackerLifecycle.manifestWritten });
2546
2551
  if (drainRoot.warning !== null)
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import color from 'picocolors';
11
11
  import { overlayUnitLabel } from '../../targets/claude-code/installer.js';
12
+ import { refusedFolderMessage, replacedLinkMessage } from '../../targets/claude-code/linked-folder.js';
12
13
  import { SKILL_REFS_SKILL_NAME } from '../../core/mds-variants.js';
13
14
  import { prefixSkillName } from '../../core/plugins.js';
14
15
  /**
@@ -49,6 +50,39 @@ export function formatOverlaySummary(report, skillName = SKILL_REFS_SKILL_NAME)
49
50
  }
50
51
  return lines;
51
52
  }
53
+ /**
54
+ * Turn the replaced-link half of an InstallReport into summary lines, one per link.
55
+ *
56
+ * An install removes a link it finds where it keeps one of its own files or folders and writes a
57
+ * real file or folder there. What the link pointed to is not touched, which looks the same from the
58
+ * filesystem as an install that never met the link, so the line says both.
59
+ *
60
+ * Pure function — returns lines, logs nothing.
61
+ */
62
+ export function formatReplacedLinksSummary(report) {
63
+ return report.replacedLinks.map(linkPath => ({ level: 'warn', message: replacedLinkMessage(linkPath) }));
64
+ }
65
+ /**
66
+ * Turn the refused-folder half of an InstallReport into summary lines, one per folder.
67
+ *
68
+ * An install writes nothing at a path that is a link it could not remove, or not a folder where it
69
+ * needs one, and leaves the entry as it was. That looks the same on disk as an install that refreshed
70
+ * the path, so the line says what was skipped and what to do about it.
71
+ *
72
+ * Pure function — returns lines, logs nothing.
73
+ */
74
+ export function formatRefusedFoldersSummary(report) {
75
+ return report.refusedFolders.map(folder => ({ level: 'warn', message: refusedFolderMessage(folder) }));
76
+ }
77
+ /**
78
+ * Both halves in one list, the replaced links first: for a command that prints the links one
79
+ * converge met and has no other summary of its own.
80
+ *
81
+ * Pure function — returns lines, logs nothing.
82
+ */
83
+ export function formatLinksSummary(report) {
84
+ return [...formatReplacedLinksSummary(report), ...formatRefusedFoldersSummary(report)];
85
+ }
52
86
  /**
53
87
  * The half of an overlay warning that describes what is actually on disk.
54
88
  *
@@ -0,0 +1,64 @@
1
+ import { promises as fs } from 'fs';
2
+ import * as path from 'path';
3
+ import { LEGACY_COMMAND_NAMES, LEGACY_RULE_NAMES } from '../../core/plugins.js';
4
+ import { isUnrealFolder } from '../../targets/claude-code/linked-folder.js';
5
+ /**
6
+ * Whether the sweep may remove files from `dir`: the install did not refuse it, and `lstat` shows a
7
+ * real folder or nothing. `readdir` and `rm` of a path below a link act on the folder it points to.
8
+ */
9
+ async function mayRemoveFrom(report, dir) {
10
+ return !report.refusedFolders.includes(dir) && !(await isUnrealFolder(dir));
11
+ }
12
+ /**
13
+ * Remove the files that earlier releases installed into `commands/devflow` and `rules/devflow` and
14
+ * that no release installs now: the legacy command and rule names, and every `*-teams.md` command.
15
+ * Runs on every install shape, since the full install's pre-clean covers only a full install.
16
+ *
17
+ * A folder the install refused (`report.refusedFolders`) or that is a link now is skipped whole: the
18
+ * sweep reads and removes nothing there, and the folder it points to is left as it is. Never throws.
19
+ *
20
+ * @param opts.legacyCommands - Legacy command names; defaults to the registry list.
21
+ * @param opts.legacyRules - Legacy rule names; defaults to the registry list.
22
+ */
23
+ export async function sweepLegacyDevflowFiles(opts) {
24
+ const { claudeDir, report } = opts;
25
+ const legacyCommands = opts.legacyCommands ?? LEGACY_COMMAND_NAMES;
26
+ const legacyRules = opts.legacyRules ?? LEGACY_RULE_NAMES;
27
+ const result = { staleCommandsRemoved: 0, staleRulesRemoved: 0 };
28
+ const commandsDir = path.join(claudeDir, 'commands', 'devflow');
29
+ if (await mayRemoveFrom(report, commandsDir)) {
30
+ for (const legacy of legacyCommands) {
31
+ try {
32
+ await fs.rm(path.join(commandsDir, `${legacy}.md`));
33
+ result.staleCommandsRemoved++;
34
+ }
35
+ catch { /* absent — expected for most entries */ }
36
+ }
37
+ // Orphaned *-teams.md workflow command variants left by the Agent Teams refactor. None is ever
38
+ // re-installed, so a blanket sweep is safe on any install type.
39
+ try {
40
+ for (const entry of await fs.readdir(commandsDir)) {
41
+ if (!entry.endsWith('-teams.md'))
42
+ continue;
43
+ try {
44
+ await fs.rm(path.join(commandsDir, entry));
45
+ result.staleCommandsRemoved++;
46
+ }
47
+ catch { /* already gone */ }
48
+ }
49
+ }
50
+ catch { /* commands folder absent — nothing to sweep */ }
51
+ }
52
+ const rulesDir = path.join(claudeDir, 'rules', 'devflow');
53
+ if (await mayRemoveFrom(report, rulesDir)) {
54
+ for (const legacy of legacyRules) {
55
+ try {
56
+ await fs.rm(path.join(rulesDir, `${legacy}.md`));
57
+ result.staleRulesRemoved++;
58
+ }
59
+ catch { /* absent — expected for most entries */ }
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+ //# sourceMappingURL=legacy-sweep.js.map