devflow-kit 2.4.0 → 2.5.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 (166) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/README.md +86 -18
  3. package/dist/agents/git.md +824 -0
  4. package/dist/cli/commands/agents.js +6 -1
  5. package/dist/cli/commands/attribution-prompts.js +1 -1
  6. package/dist/cli/commands/compliance-prompts.js +1 -1
  7. package/dist/cli/commands/compliance.js +23 -1
  8. package/dist/cli/commands/init-seed.js +24 -26
  9. package/dist/cli/commands/init.js +502 -71
  10. package/dist/cli/commands/install-report.js +205 -0
  11. package/dist/cli/commands/knowledge/index.js +2 -2
  12. package/dist/cli/commands/knowledge/toggle.js +27 -37
  13. package/dist/cli/commands/learning.js +37 -30
  14. package/dist/cli/commands/memory.js +79 -69
  15. package/dist/cli/commands/prompt-io.js +4 -4
  16. package/dist/cli/commands/security.js +76 -16
  17. package/dist/cli/commands/skills.js +53 -7
  18. package/dist/cli/commands/tracker-prompts.js +145 -0
  19. package/dist/cli/commands/tracker.js +405 -0
  20. package/dist/cli/commands/uninstall.js +211 -65
  21. package/dist/cli.js +2 -0
  22. package/dist/commands/bug-analysis.md +22 -4
  23. package/dist/commands/code-review.md +44 -15
  24. package/dist/commands/debug.md +20 -6
  25. package/dist/commands/dynamic-build.md +289 -67
  26. package/dist/commands/dynamic-plan.md +60 -21
  27. package/dist/commands/dynamic-profile.md +1 -1
  28. package/dist/commands/dynamic-tickets.md +58 -8
  29. package/dist/commands/explore.md +2 -2
  30. package/dist/commands/implement.md +241 -53
  31. package/dist/commands/plan.md +88 -17
  32. package/dist/commands/release.md +64 -17
  33. package/dist/commands/resolve.md +138 -58
  34. package/dist/commands/self-review.md +2 -2
  35. package/dist/core/agent-models.js +55 -12
  36. package/dist/core/assets.js +58 -2
  37. package/dist/core/evidence-policy.js +147 -0
  38. package/dist/core/feature-config.js +130 -64
  39. package/dist/core/feature-switch.js +112 -0
  40. package/dist/core/flags.js +4 -4
  41. package/dist/core/manifest.js +33 -7
  42. package/dist/core/mds-variants.js +861 -0
  43. package/dist/core/model-discovery.js +12 -1
  44. package/dist/core/plugins.js +357 -9
  45. package/dist/core/project-paths.js +1 -1
  46. package/dist/core/proxy-log.js +8 -6
  47. package/dist/core/proxy-state.js +11 -8
  48. package/dist/core/reference-sweep.js +136 -0
  49. package/dist/core/tracker.js +407 -0
  50. package/dist/skills/git/references/decision-markers.md +19 -0
  51. package/dist/skills/git/references/learn-conventions.md +56 -0
  52. package/dist/skills/git/references/pr/check-ci-status.md +14 -0
  53. package/dist/skills/git/references/pr/check-merge-readiness.md +28 -0
  54. package/dist/skills/git/references/pr/ensure-pr-ready.md +24 -0
  55. package/dist/skills/git/references/pr/fetch-review-threads.md +22 -0
  56. package/dist/skills/git/references/pr/post-resolution-summary.md +40 -0
  57. package/dist/skills/git/references/pr/post-review-summary.md +42 -0
  58. package/dist/skills/git/references/pr/resolve-review-threads.md +35 -0
  59. package/dist/skills/git/references/pr/update-pr-evidence.md +14 -0
  60. package/dist/skills/git/references/pr/validate-branch.md +18 -0
  61. package/dist/skills/git/references/publication-gate.md +13 -0
  62. package/dist/skills/git/references/tracker/_mcp.md +153 -0
  63. package/dist/skills/git/references/tracker/github/associate-release.md +18 -0
  64. package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +40 -0
  65. package/dist/skills/git/references/tracker/github/create-release.md +11 -0
  66. package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +16 -0
  67. package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +69 -0
  68. package/dist/skills/git/references/tracker/github/fetch-issue.md +32 -0
  69. package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +17 -0
  70. package/dist/skills/git/references/tracker/github/gather-release-evidence.md +19 -0
  71. package/dist/skills/git/references/tracker/github/manage-debt.md +101 -0
  72. package/dist/skills/git/references/tracker/github/post-wave-report.md +28 -0
  73. package/dist/skills/git/references/tracker/github/setup-task.md +26 -0
  74. package/dist/skills/git/references/tracker/jira/associate-release.md +18 -0
  75. package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +49 -0
  76. package/dist/skills/git/references/tracker/jira/create-release.md +17 -0
  77. package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +22 -0
  78. package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +53 -0
  79. package/dist/skills/git/references/tracker/jira/fetch-issue.md +14 -0
  80. package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +15 -0
  81. package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +18 -0
  82. package/dist/skills/git/references/tracker/jira/manage-debt.md +37 -0
  83. package/dist/skills/git/references/tracker/jira/post-wave-report.md +33 -0
  84. package/dist/skills/git/references/tracker/jira/setup-task.md +31 -0
  85. package/dist/skills/git/references/tracker/linear/associate-release.md +18 -0
  86. package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +53 -0
  87. package/dist/skills/git/references/tracker/linear/create-release.md +17 -0
  88. package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +22 -0
  89. package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +53 -0
  90. package/dist/skills/git/references/tracker/linear/fetch-issue.md +14 -0
  91. package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +15 -0
  92. package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +18 -0
  93. package/dist/skills/git/references/tracker/linear/manage-debt.md +37 -0
  94. package/dist/skills/git/references/tracker/linear/post-wave-report.md +33 -0
  95. package/dist/skills/git/references/tracker/linear/setup-task.md +32 -0
  96. package/dist/skills/git/references/trust-rule.md +7 -0
  97. package/dist/targets/claude-code/installer.js +1213 -31
  98. package/dist/targets/claude-code/legacy.js +5 -0
  99. package/dist/targets/claude-code/post-install.js +196 -74
  100. package/dist/targets/claude-code/tracker-install.js +161 -0
  101. package/package.json +4 -3
  102. package/src/assets/agents/code.md +42 -4
  103. package/src/assets/agents/design.md +1 -1
  104. package/src/assets/agents/git.mds +827 -0
  105. package/src/assets/agents/knowledge.md +1 -1
  106. package/src/assets/agents/learning.md +11 -0
  107. package/src/assets/agents/synthesize.md +1 -1
  108. package/src/assets/agents/test.md +16 -5
  109. package/src/assets/agents/tracker.md +467 -0
  110. package/src/assets/agents/validate.md +7 -5
  111. package/src/assets/commands/_partials/_engine.mds +11 -9
  112. package/src/assets/commands/_partials/_evidence_policy.mds +30 -0
  113. package/src/assets/commands/_partials/_knowledge.mds +2 -2
  114. package/src/assets/commands/_partials/_plan_contract.mds +22 -7
  115. package/src/assets/commands/_partials/_preamble.mds +1 -1
  116. package/src/assets/commands/_partials/_publication.mds +3 -1
  117. package/src/assets/commands/_partials/_ticket_template.mds +3 -2
  118. package/src/assets/commands/_partials/_tracker.mds +18 -0
  119. package/src/assets/commands/_partials/_wave.mds +16 -10
  120. package/src/assets/commands/bug-analysis.mds +15 -5
  121. package/src/assets/commands/code-review.mds +34 -14
  122. package/src/assets/commands/debug.mds +11 -4
  123. package/src/assets/commands/dynamic-build.mds +227 -41
  124. package/src/assets/commands/dynamic-plan.mds +35 -13
  125. package/src/assets/commands/dynamic-tickets.mds +47 -5
  126. package/src/assets/commands/implement.mds +206 -52
  127. package/src/assets/commands/plan.mds +70 -17
  128. package/src/assets/commands/release.md +64 -17
  129. package/src/assets/commands/resolve.mds +126 -56
  130. package/src/assets/mds/git/_pr.mds +331 -0
  131. package/src/assets/mds/git/_references.mds +135 -0
  132. package/src/assets/mds/tracker/_common.mds +156 -0
  133. package/src/assets/mds/tracker/_github.mds +472 -0
  134. package/src/assets/mds/tracker/_jira.mds +407 -0
  135. package/src/assets/mds/tracker/_linear.mds +449 -0
  136. package/src/assets/mds/tracker/_mcp.mds +299 -0
  137. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +5 -8
  138. package/src/assets/scripts/hooks/background-memory-update +14 -9
  139. package/src/assets/scripts/hooks/capture-prompt +6 -2
  140. package/src/assets/scripts/hooks/capture-question +6 -2
  141. package/src/assets/scripts/hooks/capture-turn +6 -2
  142. package/src/assets/scripts/hooks/ensure-devflow-init +1 -1
  143. package/src/assets/scripts/hooks/ensure-root-gitignore +161 -60
  144. package/src/assets/scripts/hooks/hook-log-init +3 -1
  145. package/src/assets/scripts/hooks/json-helper.cjs +223 -5
  146. package/src/assets/scripts/hooks/lib/project-paths.cjs +1 -1
  147. package/src/assets/scripts/hooks/memory-worker +15 -8
  148. package/src/assets/scripts/hooks/pre-compact-memory +12 -8
  149. package/src/assets/scripts/hooks/preamble +1 -4
  150. package/src/assets/scripts/hooks/queue-append +68 -24
  151. package/src/assets/scripts/hooks/session-start-context +355 -8
  152. package/src/assets/scripts/hooks/session-start-memory +12 -8
  153. package/src/assets/scripts/pr-evidence.cjs +1961 -0
  154. package/src/assets/scripts/redact-secrets.cjs +490 -62
  155. package/src/assets/scripts/release-trace.cjs +1143 -0
  156. package/src/assets/scripts/resolve-evidence-policy.cjs +1065 -0
  157. package/src/assets/scripts/verify-evidence.cjs +1822 -0
  158. package/src/assets/skills/compliance/SKILL.md +2 -0
  159. package/src/assets/skills/docs-framework/SKILL.md +5 -3
  160. package/src/assets/skills/git/SKILL.md +8 -78
  161. package/src/assets/skills/git/references/github-api.md +179 -141
  162. package/src/assets/skills/git/references/patterns.md +11 -6
  163. package/src/assets/skills/review-methodology/SKILL.md +1 -1
  164. package/src/assets/skills/review-methodology/references/patterns.md +6 -61
  165. package/src/assets/skills/review-methodology/references/violations.md +14 -22
  166. package/src/assets/agents/git.md +0 -938
@@ -552,7 +552,18 @@ async function _discoverInternal(cacheDir, logPath, deps) {
552
552
  // completes during the spawn's SPAWN_TIMEOUT_MS window).
553
553
  const staleRawP = findStaleFallback(cacheDir, currentKey);
554
554
  // --- Live spawn ---
555
- const env = { ...scrubChildEnv(), NO_COLOR: '1' };
555
+ // @D-EFR-6 Discovery reads the runtime's built-in registry, never a user config.
556
+ // Since 0.5.0 every implicit config load merges the runtime's user-level file
557
+ // ($XDG_CONFIG_HOME/subswitch/config.json, else ~/.config/subswitch/config.json)
558
+ // under the cwd project file. Devflow's relay never sees that file — it is always
559
+ // started with an explicit SUBSWITCH_CONFIG, which bypasses the merge — so a user
560
+ // alias leaking into discovery would be offered in the picker yet never routed,
561
+ // and a malformed file would fail every live discovery. The cache key also
562
+ // assumes the catalog depends on the runtime version alone. Pointing
563
+ // XDG_CONFIG_HOME at devflow's own cache directory, which never holds a
564
+ // `subswitch/config.json`, makes the lookup a clean ENOENT. SUBSWITCH_CONFIG
565
+ // stays unset (T2): an explicit path that is missing is a hard error.
566
+ const env = { ...scrubChildEnv(), NO_COLOR: '1', XDG_CONFIG_HOME: cacheDir };
556
567
  const spawnFn = deps?.spawnAndCollect ?? buildRealSpawnAndCollect(logPath);
557
568
  let spawnResult;
558
569
  try {
@@ -29,8 +29,41 @@ export const DEVFLOW_PLUGINS = [
29
29
  name: 'devflow-core-skills',
30
30
  description: 'Auto-activating quality enforcement skills - foundation layer for all Devflow plugins',
31
31
  commands: [],
32
- agents: ['learning'],
32
+ /**
33
+ * Hook-spawned agents live here, and `commands: []` above is why that works.
34
+ *
35
+ * Guard 5 reverse (registry-integrity.test.ts, "declared agents are spawned")
36
+ * skips a plugin whose commands spawn nothing — `if (spawned.size === 0)
37
+ * continue` — and this plugin ships no commands at all. So `learning` and
38
+ * `tracker`, which are spawned by a SessionStart directive rather than by any
39
+ * command, satisfy the reverse check STRUCTURALLY.
40
+ *
41
+ * Recorded because the alternative looks equivalent and is not: adding either
42
+ * name to an exemption list would make the guard pass by being told to ignore
43
+ * them, which is the vacuous-guard trap this repo polices everywhere else. If
44
+ * this plugin ever gains a command, the right fix is a new commands-less
45
+ * plugin for the hook-spawned agents — never an exemption.
46
+ *
47
+ * Neither agent may gain a `_roster.mds` row: that file is asserted
48
+ * set-equal, both directions, against the `agentType` values present in
49
+ * dist/commands/, and a hook-spawned agent appears in none of them.
50
+ */
51
+ agents: ['learning', 'tracker'],
33
52
  skills: ['apply-decisions', 'apply-feature-knowledge', 'software-design', 'docs-framework', 'git', 'boundary-validation', 'test-driven-development', 'testing', 'dependency-research'],
53
+ requires: [
54
+ 'architecture',
55
+ 'complexity',
56
+ 'consistency',
57
+ 'database',
58
+ 'dependencies',
59
+ 'documentation',
60
+ 'performance',
61
+ 'regression',
62
+ 'reliability',
63
+ 'review-methodology',
64
+ 'security',
65
+ 'worktree-support',
66
+ ],
34
67
  rules: ['security', 'engineering', 'quality', 'reliability'],
35
68
  },
36
69
  {
@@ -39,6 +72,25 @@ export const DEVFLOW_PLUGINS = [
39
72
  commands: ['/plan'],
40
73
  agents: ['git', 'skim', 'synthesize', 'design'],
41
74
  skills: ['gap-analysis', 'design-review', 'patterns', 'worktree-support', 'feature-knowledge', 'apply-feature-knowledge'],
75
+ requires: [
76
+ 'apply-decisions',
77
+ 'architecture',
78
+ 'complexity',
79
+ 'consistency',
80
+ 'database',
81
+ 'dependencies',
82
+ 'docs-framework',
83
+ 'documentation',
84
+ 'git',
85
+ 'performance',
86
+ 'regression',
87
+ 'reliability',
88
+ 'review-methodology',
89
+ 'security',
90
+ 'software-design',
91
+ 'test-driven-development',
92
+ 'testing',
93
+ ],
42
94
  rules: [],
43
95
  },
44
96
  {
@@ -47,6 +99,26 @@ export const DEVFLOW_PLUGINS = [
47
99
  commands: ['/implement'],
48
100
  agents: ['git', 'code', 'simplify', 'scrutinize', 'evaluate', 'test', 'validate', 'knowledge'],
49
101
  skills: ['patterns', 'qa', 'quality-gates', 'worktree-support', 'feature-knowledge', 'apply-feature-knowledge'],
102
+ requires: [
103
+ 'apply-decisions',
104
+ 'architecture',
105
+ 'boundary-validation',
106
+ 'complexity',
107
+ 'consistency',
108
+ 'database',
109
+ 'dependencies',
110
+ 'dependency-research',
111
+ 'documentation',
112
+ 'git',
113
+ 'performance',
114
+ 'regression',
115
+ 'reliability',
116
+ 'review-methodology',
117
+ 'security',
118
+ 'software-design',
119
+ 'test-driven-development',
120
+ 'testing',
121
+ ],
50
122
  rules: [],
51
123
  },
52
124
  {
@@ -55,14 +127,34 @@ export const DEVFLOW_PLUGINS = [
55
127
  commands: ['/code-review'],
56
128
  agents: ['git', 'review', 'synthesize'],
57
129
  skills: ['architecture', 'complexity', 'consistency', 'database', 'dependencies', 'documentation', 'performance', 'regression', 'reliability', 'review-methodology', 'security', 'testing', 'worktree-support', 'apply-feature-knowledge'],
130
+ requires: ['apply-decisions', 'docs-framework', 'git', 'quality-gates', 'software-design'],
58
131
  rules: [],
59
132
  },
60
133
  {
61
134
  name: 'devflow-resolve',
62
135
  description: 'Process and fix code review issues with blast-radius triage, Code agent fixes, and Validate agent verification',
63
136
  commands: ['/resolve'],
64
- agents: ['git', 'triage', 'code', 'simplify', 'validate', 'knowledge'],
137
+ agents: ['git', 'triage', 'code', 'simplify', 'validate', 'test', 'knowledge'],
65
138
  skills: ['patterns', 'security', 'worktree-support', 'feature-knowledge', 'apply-feature-knowledge', 'apply-decisions'],
139
+ requires: [
140
+ 'architecture',
141
+ 'boundary-validation',
142
+ 'complexity',
143
+ 'consistency',
144
+ 'database',
145
+ 'dependencies',
146
+ 'dependency-research',
147
+ 'documentation',
148
+ 'git',
149
+ 'performance',
150
+ 'qa',
151
+ 'regression',
152
+ 'reliability',
153
+ 'review-methodology',
154
+ 'software-design',
155
+ 'test-driven-development',
156
+ 'testing',
157
+ ],
66
158
  rules: [],
67
159
  },
68
160
  {
@@ -71,6 +163,25 @@ export const DEVFLOW_PLUGINS = [
71
163
  commands: ['/debug'],
72
164
  agents: ['git', 'synthesize', 'simplify', 'knowledge'],
73
165
  skills: ['git', 'worktree-support', 'feature-knowledge', 'apply-feature-knowledge'],
166
+ requires: [
167
+ 'apply-decisions',
168
+ 'architecture',
169
+ 'complexity',
170
+ 'consistency',
171
+ 'database',
172
+ 'dependencies',
173
+ 'docs-framework',
174
+ 'documentation',
175
+ 'patterns',
176
+ 'performance',
177
+ 'regression',
178
+ 'reliability',
179
+ 'review-methodology',
180
+ 'security',
181
+ 'software-design',
182
+ 'test-driven-development',
183
+ 'testing',
184
+ ],
74
185
  rules: [],
75
186
  },
76
187
  {
@@ -79,6 +190,22 @@ export const DEVFLOW_PLUGINS = [
79
190
  commands: ['/explore'],
80
191
  agents: ['skim', 'synthesize', 'knowledge'],
81
192
  skills: ['worktree-support', 'apply-feature-knowledge', 'feature-knowledge'],
193
+ requires: [
194
+ 'apply-decisions',
195
+ 'architecture',
196
+ 'complexity',
197
+ 'consistency',
198
+ 'database',
199
+ 'dependencies',
200
+ 'docs-framework',
201
+ 'documentation',
202
+ 'performance',
203
+ 'regression',
204
+ 'reliability',
205
+ 'review-methodology',
206
+ 'security',
207
+ 'testing',
208
+ ],
82
209
  rules: [],
83
210
  },
84
211
  {
@@ -87,6 +214,22 @@ export const DEVFLOW_PLUGINS = [
87
214
  commands: ['/research'],
88
215
  agents: ['research', 'skim', 'synthesize', 'knowledge'],
89
216
  skills: ['worktree-support', 'apply-feature-knowledge', 'feature-knowledge', 'research-codebase', 'research-external', 'research-market', 'research-competitor', 'research-technology'],
217
+ requires: [
218
+ 'apply-decisions',
219
+ 'architecture',
220
+ 'complexity',
221
+ 'consistency',
222
+ 'database',
223
+ 'dependencies',
224
+ 'docs-framework',
225
+ 'documentation',
226
+ 'performance',
227
+ 'regression',
228
+ 'reliability',
229
+ 'review-methodology',
230
+ 'security',
231
+ 'testing',
232
+ ],
90
233
  rules: [],
91
234
  },
92
235
  {
@@ -95,6 +238,7 @@ export const DEVFLOW_PLUGINS = [
95
238
  commands: ['/release'],
96
239
  agents: ['git', 'validate'],
97
240
  skills: ['git', 'worktree-support'],
241
+ requires: ['testing'],
98
242
  rules: [],
99
243
  },
100
244
  {
@@ -103,6 +247,7 @@ export const DEVFLOW_PLUGINS = [
103
247
  commands: ['/self-review'],
104
248
  agents: ['simplify', 'scrutinize', 'validate', 'knowledge'],
105
249
  skills: ['quality-gates', 'software-design', 'worktree-support', 'feature-knowledge', 'apply-feature-knowledge'],
250
+ requires: ['apply-decisions', 'testing'],
106
251
  rules: [],
107
252
  },
108
253
  {
@@ -120,6 +265,17 @@ export const DEVFLOW_PLUGINS = [
120
265
  'security',
121
266
  'worktree-support',
122
267
  ],
268
+ requires: [
269
+ 'architecture',
270
+ 'database',
271
+ 'dependencies',
272
+ 'docs-framework',
273
+ 'documentation',
274
+ 'git',
275
+ 'performance',
276
+ 'review-methodology',
277
+ 'testing',
278
+ ],
123
279
  rules: [],
124
280
  },
125
281
  {
@@ -148,6 +304,21 @@ export const DEVFLOW_PLUGINS = [
148
304
  'feature-knowledge',
149
305
  'apply-feature-knowledge',
150
306
  ],
307
+ requires: [
308
+ 'apply-decisions',
309
+ 'boundary-validation',
310
+ 'dependency-research',
311
+ 'docs-framework',
312
+ 'git',
313
+ 'quality-gates',
314
+ 'research-codebase',
315
+ 'research-competitor',
316
+ 'research-external',
317
+ 'research-market',
318
+ 'research-technology',
319
+ 'software-design',
320
+ 'test-driven-development',
321
+ ],
151
322
  rules: [],
152
323
  },
153
324
  {
@@ -157,6 +328,31 @@ export const DEVFLOW_PLUGINS = [
157
328
  commands: ['/dynamic-tickets', '/dynamic-plan', '/dynamic-build', '/dynamic-profile'],
158
329
  agents: ['code', 'validate', 'simplify', 'scrutinize', 'evaluate', 'test', 'review', 'git', 'synthesize', 'knowledge', 'design'],
159
330
  skills: ['apply-decisions', 'apply-feature-knowledge', 'worktree-support', 'docs-framework'],
331
+ requires: [
332
+ 'architecture',
333
+ 'boundary-validation',
334
+ 'complexity',
335
+ 'consistency',
336
+ 'database',
337
+ 'dependencies',
338
+ 'dependency-research',
339
+ 'design-review',
340
+ 'documentation',
341
+ 'feature-knowledge',
342
+ 'gap-analysis',
343
+ 'git',
344
+ 'patterns',
345
+ 'performance',
346
+ 'qa',
347
+ 'quality-gates',
348
+ 'regression',
349
+ 'reliability',
350
+ 'review-methodology',
351
+ 'security',
352
+ 'software-design',
353
+ 'test-driven-development',
354
+ 'testing',
355
+ ],
160
356
  optional: true,
161
357
  rules: [],
162
358
  },
@@ -166,6 +362,7 @@ export const DEVFLOW_PLUGINS = [
166
362
  commands: [],
167
363
  agents: [],
168
364
  skills: ['typescript'],
365
+ requires: [],
169
366
  optional: true,
170
367
  rules: ['typescript'],
171
368
  },
@@ -175,6 +372,7 @@ export const DEVFLOW_PLUGINS = [
175
372
  commands: [],
176
373
  agents: [],
177
374
  skills: ['react'],
375
+ requires: [],
178
376
  optional: true,
179
377
  rules: ['react'],
180
378
  },
@@ -184,6 +382,7 @@ export const DEVFLOW_PLUGINS = [
184
382
  commands: [],
185
383
  agents: [],
186
384
  skills: ['accessibility'],
385
+ requires: [],
187
386
  optional: true,
188
387
  rules: ['accessibility'],
189
388
  },
@@ -193,6 +392,7 @@ export const DEVFLOW_PLUGINS = [
193
392
  commands: [],
194
393
  agents: [],
195
394
  skills: ['ui-design'],
395
+ requires: [],
196
396
  optional: true,
197
397
  rules: ['ui-design'],
198
398
  },
@@ -202,6 +402,7 @@ export const DEVFLOW_PLUGINS = [
202
402
  commands: [],
203
403
  agents: [],
204
404
  skills: ['go'],
405
+ requires: [],
205
406
  optional: true,
206
407
  rules: ['go'],
207
408
  },
@@ -211,6 +412,7 @@ export const DEVFLOW_PLUGINS = [
211
412
  commands: [],
212
413
  agents: [],
213
414
  skills: ['java'],
415
+ requires: [],
214
416
  optional: true,
215
417
  rules: ['java'],
216
418
  },
@@ -220,6 +422,7 @@ export const DEVFLOW_PLUGINS = [
220
422
  commands: [],
221
423
  agents: [],
222
424
  skills: ['python'],
425
+ requires: [],
223
426
  optional: true,
224
427
  rules: ['python'],
225
428
  },
@@ -229,6 +432,7 @@ export const DEVFLOW_PLUGINS = [
229
432
  commands: [],
230
433
  agents: [],
231
434
  skills: ['rust'],
435
+ requires: [],
232
436
  optional: true,
233
437
  rules: ['rust'],
234
438
  },
@@ -280,6 +484,59 @@ export const FEATURE_OWNED_SKILLS = ['compliance'];
280
484
  * (guarded by plugins.test.ts FEATURE_OWNED constants describe block).
281
485
  */
282
486
  export const FEATURE_OWNED_RULES = ['compliance'];
487
+ // ── Skill-closure boundaries ──────────────────────────────────────────────────
488
+ /**
489
+ * Skills that are REFERENCED but never REQUIRED — the presence-gated set.
490
+ *
491
+ * D-PRESENCE-GATED: every language/ecosystem skill ships with an optional,
492
+ * command-less plugin, so a reference to one is a reference to something the
493
+ * user may deliberately not have. The referencing prompts are written to probe
494
+ * first and proceed without it — `/code-review` checks
495
+ * `~/.claude/skills/devflow:{focus}/SKILL.md` before spawning that focus, the
496
+ * Review and Code agents continue when the Skill invocation fails. Putting them
497
+ * in a `requires` would reinstate the universal install for exactly the eight
498
+ * skills the selection prompt exists to let a user decline (AC-25).
499
+ *
500
+ * DERIVED from the registry rather than hand-listed: a ninth language plugin is
501
+ * presence-gated by being declared, with no second roster to remember. Guarded
502
+ * against its own definition in tests/guards/requires-closure.test.ts.
503
+ */
504
+ export const PRESENCE_GATED_SKILLS = [
505
+ ...new Set(DEVFLOW_PLUGINS
506
+ .filter(plugin => plugin.optional === true && plugin.commands.length === 0)
507
+ .flatMap(plugin => plugin.skills)),
508
+ ];
509
+ /**
510
+ * The classified exception to the closure guard — declared HERE, at the
511
+ * declaration site of the field it exempts, and imported by the guard.
512
+ *
513
+ * D-TEMPLATE-EXCEPTION (applies PF-067: one authority for a prohibition and its
514
+ * exemptions). Every other templated reference in the corpus carries a literal
515
+ * prefix and resolves through it — `devflow:research-{RESEARCH_TYPE}` resolves
516
+ * because five in-scope skills start with `research-`. The Review focus skill is
517
+ * the one reference with NO literal prefix: the whole skill name is substituted
518
+ * at spawn time, and the substitution set spans the presence-gated language
519
+ * skills, so there is nothing a scan or a `requires` entry could resolve it to.
520
+ *
521
+ * Two spellings, one exception: the command writes the placeholder it passes and
522
+ * the agent writes the placeholder it receives.
523
+ *
524
+ * An entry here is NOT permission to stop thinking about the reference — the
525
+ * guard asserts each literal still occurs in the corpus, so an exemption that
526
+ * outlives its site fails rather than rotting.
527
+ */
528
+ export const TEMPLATE_SKILL_REFS = [
529
+ {
530
+ literal: 'devflow:{focus}',
531
+ site: 'dist/commands/code-review.md (Review agent spawn prompt)',
532
+ why: 'the whole skill name is substituted per focus; the substitution set includes presence-gated language skills',
533
+ },
534
+ {
535
+ literal: 'devflow:{FOCUS}',
536
+ site: 'src/assets/agents/review.md (the Review agent loading its own focus skill)',
537
+ why: 'receiving half of the same substitution — the agent is told which focus it is, not which skill exists',
538
+ },
539
+ ];
283
540
  // ── Feature redirect ──────────────────────────────────────────────────────────
284
541
  /**
285
542
  * Plugin names (and aliases) that have been converted to built-in features.
@@ -405,21 +662,112 @@ export function buildAssetMaps(plugins) {
405
662
  return { skillsMap, agentsMap };
406
663
  }
407
664
  /**
408
- * Build a skills map from ALL plugins (regardless of selection).
409
- * Skills are tiny markdown files — always install all of them so commands
410
- * (review, resolve) can spawn agents that depend on skills from other plugins.
665
+ * The install closure of a plugin selection: everything those plugins own plus
666
+ * everything they use.
667
+ *
668
+ * ONE spelling of `skills ∪ requires`, because every consumer that spells it
669
+ * inline is a place the two halves can be forgotten apart: the installer's
670
+ * install set, its removal set, the skills-list scope and the closure guard all
671
+ * read this. Pure, order-independent, no I/O.
411
672
  */
412
- export function buildFullSkillsMap() {
673
+ export function skillsOf(plugins) {
674
+ const skills = new Set();
675
+ for (const plugin of plugins) {
676
+ for (const skill of plugin.skills)
677
+ skills.add(skill);
678
+ for (const skill of plugin.requires)
679
+ skills.add(skill);
680
+ }
681
+ return skills;
682
+ }
683
+ /**
684
+ * Every plugin that OWNS a skill, in registry order.
685
+ *
686
+ * D-ALL-OWNERS: returns all declarers, not the first — `devflow skills list`'s
687
+ * owner column becomes a lie the moment install is scoped, because the answer a
688
+ * user needs from it is "which plugin do I select to keep this?", and a
689
+ * first-wins answer names one plugin out of several that would each do. Empty
690
+ * for a skill no plugin owns, which for a registry-valid name cannot happen:
691
+ * a `requires` entry is guarded to be a known skill, and a known skill is one
692
+ * some plugin declares.
693
+ */
694
+ export function skillOwners(name) {
695
+ return DEVFLOW_PLUGINS.filter(plugin => plugin.skills.includes(name)).map(plugin => plugin.name);
696
+ }
697
+ /**
698
+ * Build the skill → source-plugin map for a SELECTION.
699
+ *
700
+ * The key set is the selection's closure ({@link skillsOf}); the value is the
701
+ * plugin the skill is copied on behalf of. A skill the selection only REQUIRES
702
+ * has no owner among the selected plugins, so its owner is resolved from the
703
+ * full registry ({@link skillOwners}) — the map's value is a provenance label,
704
+ * and labelling a required skill with the plugin that happens to need it would
705
+ * misattribute ownership.
706
+ */
707
+ export function buildScopedSkillsMap(plugins) {
413
708
  const skillsMap = new Map();
414
- for (const plugin of DEVFLOW_PLUGINS) {
709
+ for (const plugin of plugins) {
415
710
  for (const skill of plugin.skills) {
416
- if (!skillsMap.has(skill)) {
711
+ if (!skillsMap.has(skill))
417
712
  skillsMap.set(skill, plugin.name);
418
- }
419
713
  }
420
714
  }
715
+ for (const skill of skillsOf(plugins)) {
716
+ if (skillsMap.has(skill))
717
+ continue;
718
+ const owner = skillOwners(skill)[0];
719
+ if (owner !== undefined)
720
+ skillsMap.set(skill, owner);
721
+ }
421
722
  return skillsMap;
422
723
  }
724
+ /**
725
+ * Build a skills map over the WHOLE registry.
726
+ *
727
+ * The scoped map applied to every plugin: `skills ∪ requires` across the full
728
+ * registry is `skills` across the full registry, since a `requires` entry is
729
+ * always some plugin's owned skill. Retained for the consumers that legitimately
730
+ * want every skill regardless of selection — uninstall's removal manifest and
731
+ * `devflow skills list`'s catalogue.
732
+ */
733
+ export function buildFullSkillsMap() {
734
+ return buildScopedSkillsMap(DEVFLOW_PLUGINS);
735
+ }
736
+ /**
737
+ * Decide the skill install/remove/dormant sets for one run — pure, no fs.
738
+ *
739
+ * H4: extracted so the decision is unit-testable without a temp tree, and so
740
+ * `installViaFileCopy` consumes a decision rather than growing a fourth set of
741
+ * inline set-arithmetic.
742
+ *
743
+ * The removal set is GATED on a full install. A `--plugin=X` run is a request to
744
+ * add X, not a statement that X is the whole selection, so it may never remove
745
+ * what another plugin contributed (AC-22). {@link FEATURE_OWNED_SKILLS} is
746
+ * subtracted unconditionally: those install and uninstall with their feature,
747
+ * and sweeping them here would delete an artifact this code does not own
748
+ * (applies ADR-024).
749
+ *
750
+ * A shadow is NEVER removed, whatever the selection — `~/.devflow/skills/` is
751
+ * user content. One that falls outside the install set simply applies to
752
+ * nothing, and is reported so the user can tell "inert" from "ignored".
753
+ */
754
+ export function resolveSkillInstallPlan(input) {
755
+ const install = skillsOf(input.effectivePlugins);
756
+ const remove = new Set();
757
+ if (!input.isPartialInstall) {
758
+ for (const skill of skillsOf(DEVFLOW_PLUGINS)) {
759
+ if (install.has(skill))
760
+ continue;
761
+ if (FEATURE_OWNED_SKILLS.includes(skill))
762
+ continue;
763
+ remove.add(skill);
764
+ }
765
+ }
766
+ const dormantShadows = [...new Set(input.shadowedSkills)]
767
+ .filter(skill => !install.has(skill))
768
+ .sort();
769
+ return { install, remove, dormantShadows };
770
+ }
423
771
  /**
424
772
  * Derive unique rule names from all plugins.
425
773
  */
@@ -35,7 +35,7 @@ export function getDocsDir(projectRoot) {
35
35
  // ---------------------------------------------------------------------------
36
36
  // Feature config (neutral .devflow root — not inside learning/)
37
37
  // ---------------------------------------------------------------------------
38
- /** .devflow/config.json — feature toggles {memory, learning, knowledge} */
38
+ /** .devflow/config.json — per-repo facts {reviewPublication, tracker override} */
39
39
  export function getFeatureConfigPath(projectRoot) {
40
40
  return path.join(projectRoot, '.devflow', 'config.json');
41
41
  }
@@ -28,12 +28,14 @@ export const PROXY_LOG_TAIL_BYTES = 1_048_576;
28
28
  * Call sites compose on top of this result to add process-specific vars
29
29
  * (e.g. SUBSWITCH_CONFIG for the relay spawn and doctor spawn).
30
30
  *
31
- * applies ADR-003: the prior denylist rationale is gone — the routing runtime
32
- * reads exactly four env vars (ANTHROPIC_API_KEY, FORCE_COLOR, NO_COLOR,
33
- * SUBSWITCH_CONFIG — NO_COLOR read in dist/tty.js with presence semantics; CI is
34
- * read only by the `init` subcommand which devflow never invokes).
35
- * Verified by whole-dist grep of the 0.4.0 package. An allowlist is the correct
36
- * shape: 61 inherited vars → 6.
31
+ * applies ADR-003: the prior denylist rationale is gone — on the paths devflow
32
+ * invokes (serve, doctor, models) the routing runtime reads ANTHROPIC_API_KEY,
33
+ * FORCE_COLOR, NO_COLOR (dist/tty.js, presence semantics), SUBSWITCH_CONFIG and
34
+ * XDG_CONFIG_HOME (user-config lookup, reached only on an implicit config load —
35
+ * see D-EFR-6 in model-discovery.ts). CI is read only by `init`; CODEX_HOME and
36
+ * CLAUDE_CONFIG_DIR only by the Codex ingress, `init` and the Codex doctor, none of
37
+ * which devflow enables or invokes. Verified by whole-dist grep of the 0.5.0
38
+ * package. An allowlist is the correct shape: 61 inherited vars → 6.
37
39
  *
38
40
  * HOME is retained: the runtime's loadConfig resolves ~ paths via homedir().
39
41
  *
@@ -88,14 +88,17 @@ export async function writeProxyState(devflowDir, state) {
88
88
  // Pure helpers
89
89
  // ---------------------------------------------------------------------------
90
90
  /**
91
- * Accepted top-level keys for the subswitch 0.4.0 FileConfigSchema (z.strictObject).
92
- * Unknown top-level keys cause a hard relay startup error — never emit them.
91
+ * Top-level keys devflow carries into the routing config. Unknown top-level keys
92
+ * cause a hard relay startup error — never emit them.
93
93
  *
94
- * @D-EFR-4 Subswitch 0.4.0 routing config contract:
95
- * FileConfigSchema is a z.strictObject with exactly 5 accepted top-level keys:
96
- * port, logLevel, anthropic, providers, limits. Unknown keys cause a hard startup
97
- * error — the relay refuses to start. anthropic and limits are themselves
98
- * strictObject + prefault({}), so they may be partially specified.
94
+ * @D-EFR-4 Subswitch 0.5.0 routing config contract:
95
+ * FileConfigSchema is a z.strictObject with exactly 6 accepted top-level keys:
96
+ * port, logLevel, anthropic, providers, limits, codexIngress. Unknown keys cause a
97
+ * hard startup error — the relay refuses to start. anthropic and limits are
98
+ * themselves strictObject + prefault({}), so they may be partially specified.
99
+ * codexIngress is accepted by the relay but deliberately NOT carried: devflow's
100
+ * relay serves Claude Code only, and the ingress block would make it accept native
101
+ * Codex traffic and hold Claude credentials on devflow's behalf.
99
102
  */
100
103
  const ROUTING_CONFIG_ALLOWED_TOP_KEYS = new Set([
101
104
  'port', 'logLevel', 'anthropic', 'providers', 'limits',
@@ -163,7 +166,7 @@ export function buildRoutingConfigJson(port, existingContent) {
163
166
  const parsed = JSON.parse(existingContent);
164
167
  if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
165
168
  for (const [k, v] of Object.entries(parsed)) {
166
- // Only preserve the 5 accepted top-level keys; port is always ours.
169
+ // Only preserve the carried top-level keys; port is always ours.
167
170
  if (ROUTING_CONFIG_ALLOWED_TOP_KEYS.has(k) && k !== 'port') {
168
171
  preserved[k] = v;
169
172
  }