devflow-kit 3.6.0 → 3.8.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 (197) hide show
  1. package/CHANGELOG.md +438 -1
  2. package/README.md +15 -11
  3. package/dist/agents/code.md +9 -15
  4. package/dist/agents/design.md +1 -1
  5. package/dist/agents/fix.md +11 -13
  6. package/dist/agents/git.md +1 -1
  7. package/dist/agents/harden.md +1 -1
  8. package/dist/agents/refactor.md +7 -10
  9. package/dist/agents/review.md +6 -4
  10. package/dist/agents/skim.md +2 -2
  11. package/dist/agents/triage.md +2 -2
  12. package/dist/cli/commands/capture.js +1 -1
  13. package/dist/cli/commands/curation-move-io.js +102 -0
  14. package/dist/cli/commands/curation-move-step.js +374 -0
  15. package/dist/cli/commands/{learning.js → curation.js} +302 -99
  16. package/dist/cli/commands/gate-level-prompts.js +426 -0
  17. package/dist/cli/commands/gates.js +299 -0
  18. package/dist/cli/commands/init-seed.js +16 -5
  19. package/dist/cli/commands/init.js +263 -54
  20. package/dist/cli/commands/memory.js +20 -2
  21. package/dist/cli/commands/prompt-io.js +37 -0
  22. package/dist/cli/commands/uninstall.js +81 -9
  23. package/dist/cli.js +4 -2
  24. package/dist/commands/bug-analysis.md +25 -36
  25. package/dist/commands/code-review.md +77 -73
  26. package/dist/commands/debug.md +5 -5
  27. package/dist/commands/dynamic-build.md +76 -83
  28. package/dist/commands/dynamic-plan.md +5 -5
  29. package/dist/commands/dynamic-profile.md +5 -5
  30. package/dist/commands/dynamic-tickets.md +5 -5
  31. package/dist/commands/explore.md +6 -6
  32. package/dist/commands/implement.md +68 -102
  33. package/dist/commands/plan.md +19 -29
  34. package/dist/commands/release.md +5 -5
  35. package/dist/commands/research.md +6 -6
  36. package/dist/commands/resolve.md +29 -31
  37. package/dist/commands/self-review.md +5 -13
  38. package/dist/core/assets.js +20 -20
  39. package/dist/core/curate-tuning-config.js +5 -6
  40. package/dist/core/curation-move-notice.js +84 -0
  41. package/dist/core/curation-move-skip.js +36 -0
  42. package/dist/core/curation-move.js +35 -0
  43. package/dist/core/curation-queue-cleanup.js +134 -0
  44. package/dist/core/curation-store.js +98 -0
  45. package/dist/core/{learning-variants.js → curation-variants.js} +65 -41
  46. package/dist/core/echo-value.js +48 -0
  47. package/dist/core/evidence-policy.js +18 -6
  48. package/dist/core/feature-switch.js +95 -19
  49. package/dist/core/fs-atomic.js +1 -1
  50. package/dist/core/gate-level.js +221 -0
  51. package/dist/core/git.js +16 -14
  52. package/dist/core/ledger-root.js +82 -22
  53. package/dist/core/linked-path.js +4 -4
  54. package/dist/core/manifest-lock.js +238 -0
  55. package/dist/core/manifest.js +84 -24
  56. package/dist/core/migrations.js +1 -13
  57. package/dist/core/model-discovery.js +7 -1
  58. package/dist/core/observations.js +2 -2
  59. package/dist/core/plugins.js +15 -15
  60. package/dist/core/pool.js +26 -0
  61. package/dist/core/project-paths.js +103 -34
  62. package/dist/core/queue-drain.js +2 -2
  63. package/dist/core/tracked-areas.js +35 -0
  64. package/dist/core/tracker.js +6 -34
  65. package/dist/{learning-off → curation-off}/agents/code.md +9 -15
  66. package/dist/{learning-off → curation-off}/agents/fix.md +11 -13
  67. package/dist/{learning-off → curation-off}/agents/refactor.md +7 -10
  68. package/dist/{learning-off → curation-off}/agents/review.md +5 -3
  69. package/dist/{learning-off → curation-off}/agents/triage.md +1 -1
  70. package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
  71. package/dist/{learning-off → curation-off}/commands/code-review.md +74 -70
  72. package/dist/{learning-off → curation-off}/commands/debug.md +2 -2
  73. package/dist/{learning-off → curation-off}/commands/dynamic-build.md +73 -80
  74. package/dist/{learning-off → curation-off}/commands/explore.md +2 -2
  75. package/dist/{learning-off → curation-off}/commands/implement.md +65 -99
  76. package/dist/{learning-off → curation-off}/commands/plan.md +15 -25
  77. package/dist/{learning-off → curation-off}/commands/resolve.md +26 -28
  78. package/dist/{learning-off → curation-off}/commands/self-review.md +2 -10
  79. package/dist/hud/components/{learning-counts.js → curation-counts.js} +36 -17
  80. package/dist/hud/config.js +1 -1
  81. package/dist/hud/index.js +6 -6
  82. package/dist/hud/render.js +3 -3
  83. package/dist/skills/git/references/pr/validate-branch.md +8 -5
  84. package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +20 -14
  85. package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +20 -20
  86. package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +20 -20
  87. package/dist/targets/claude-code/{learning-install.js → curation-install.js} +99 -67
  88. package/dist/targets/claude-code/installer.js +15 -15
  89. package/dist/targets/claude-code/language-stamp.js +5 -5
  90. package/dist/targets/claude-code/post-install.js +2 -2
  91. package/dist/targets/claude-code/tracker-servers.js +1 -1
  92. package/package.json +3 -2
  93. package/src/assets/agents/_partials/_code.mds +16 -17
  94. package/src/assets/agents/code.mds +4 -7
  95. package/src/assets/agents/curate.md +12 -14
  96. package/src/assets/agents/debug.mds +6 -6
  97. package/src/assets/agents/design.mds +18 -18
  98. package/src/assets/agents/fix.mds +6 -5
  99. package/src/assets/agents/git.mds +1 -1
  100. package/src/assets/agents/harden.mds +7 -7
  101. package/src/assets/agents/knowledge.mds +23 -23
  102. package/src/assets/agents/refactor.mds +2 -2
  103. package/src/assets/agents/research.mds +15 -15
  104. package/src/assets/agents/review.mds +45 -43
  105. package/src/assets/agents/skim.mds +11 -11
  106. package/src/assets/agents/synthesize.md +2 -2
  107. package/src/assets/agents/triage.mds +22 -22
  108. package/src/assets/agents/validate.md +13 -26
  109. package/src/assets/commands/_partials/_decisions.mds +13 -13
  110. package/src/assets/commands/_partials/_engine.mds +8 -8
  111. package/src/assets/commands/_partials/_knowledge.mds +29 -15
  112. package/src/assets/commands/_partials/_preamble.mds +4 -4
  113. package/src/assets/commands/_partials/_settings.mds +3 -3
  114. package/src/assets/commands/_partials/_wave.mds +17 -24
  115. package/src/assets/commands/bug-analysis.mds +37 -48
  116. package/src/assets/commands/code-review.mds +90 -86
  117. package/src/assets/commands/debug.mds +14 -14
  118. package/src/assets/commands/dynamic-build.mds +78 -78
  119. package/src/assets/commands/dynamic-plan.mds +22 -22
  120. package/src/assets/commands/dynamic-profile.mds +2 -2
  121. package/src/assets/commands/dynamic-tickets.mds +8 -8
  122. package/src/assets/commands/explore.mds +20 -20
  123. package/src/assets/commands/implement.mds +92 -118
  124. package/src/assets/commands/plan.mds +41 -51
  125. package/src/assets/commands/release.mds +12 -12
  126. package/src/assets/commands/research.mds +19 -19
  127. package/src/assets/commands/resolve.mds +56 -50
  128. package/src/assets/commands/self-review.mds +20 -20
  129. package/src/assets/mds/git/_ops.mds +3 -1
  130. package/src/assets/mds/git/_ops_pr.mds +1 -1
  131. package/src/assets/mds/git/_ops_steps.mds +13 -7
  132. package/src/assets/mds/git/_pr.mds +7 -4
  133. package/src/assets/mds/tracker/_common.mds +23 -12
  134. package/src/assets/mds/tracker/_github.mds +2 -9
  135. package/src/assets/mds/tracker/_jira.mds +2 -15
  136. package/src/assets/mds/tracker/_linear.mds +2 -15
  137. package/src/assets/rules/quality.md +1 -0
  138. package/src/assets/scripts/ci-wait.cjs +22 -4
  139. package/src/assets/scripts/claude-md-audit.cjs +66 -51
  140. package/src/assets/scripts/hooks/background-memory-update +406 -97
  141. package/src/assets/scripts/hooks/capture-prompt +12 -12
  142. package/src/assets/scripts/hooks/capture-question +11 -11
  143. package/src/assets/scripts/hooks/capture-turn +12 -12
  144. package/src/assets/scripts/hooks/{learning-lock → curation-lock} +2 -2
  145. package/src/assets/scripts/hooks/curation-move +293 -0
  146. package/src/assets/scripts/hooks/ensure-devflow-init +45 -13
  147. package/src/assets/scripts/hooks/ensure-proxy +4 -4
  148. package/src/assets/scripts/hooks/ensure-root-gitignore +4 -4
  149. package/src/assets/scripts/hooks/git-marker +81 -0
  150. package/src/assets/scripts/hooks/json-helper.cjs +145 -51
  151. package/src/assets/scripts/hooks/json-parse +19 -4
  152. package/src/assets/scripts/hooks/lib/curation-move.cjs +1582 -0
  153. package/src/assets/scripts/hooks/lib/{learning-store.cjs → curation-store.cjs} +242 -118
  154. package/src/assets/scripts/hooks/lib/decisions-format.cjs +3 -3
  155. package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +24 -7
  156. package/src/assets/scripts/hooks/lib/project-paths.cjs +132 -39
  157. package/src/assets/scripts/hooks/lib/render-decisions.cjs +34 -22
  158. package/src/assets/scripts/hooks/lib/tracked-areas.cjs +259 -0
  159. package/src/assets/scripts/hooks/memory-permission +358 -0
  160. package/src/assets/scripts/hooks/memory-worker +1 -1
  161. package/src/assets/scripts/hooks/pre-compact-memory +6 -16
  162. package/src/assets/scripts/hooks/queue-append +228 -103
  163. package/src/assets/scripts/hooks/resolve-project-root +2 -2
  164. package/src/assets/scripts/hooks/session-start-context +145 -71
  165. package/src/assets/scripts/hooks/session-start-memory +27 -46
  166. package/src/assets/scripts/lib/project-config.cjs +31 -9
  167. package/src/assets/scripts/resolve-base.cjs +1043 -0
  168. package/src/assets/scripts/resolve-settings.cjs +238 -46
  169. package/src/assets/skills/agent-references/SKILL.md +9 -2
  170. package/src/assets/skills/agent-references/references/commands/code-review-levels.md +19 -0
  171. package/src/assets/skills/agent-references/references/commands/implement-levels.md +27 -0
  172. package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +6 -4
  173. package/src/assets/skills/agent-references/references/commands/plan-levels.md +40 -0
  174. package/src/assets/skills/agent-references/references/commands/resolve-levels.md +24 -0
  175. package/src/assets/skills/agent-references/references/synthesize/planning.md +9 -22
  176. package/src/assets/skills/agent-references/references/synthesize/review.md +11 -11
  177. package/src/assets/skills/apply-decisions/SKILL.md +1 -1
  178. package/src/assets/skills/docs-framework/SKILL.md +7 -7
  179. package/src/assets/skills/docs-framework/references/patterns.md +3 -4
  180. package/src/assets/skills/quality-gates/SKILL.md +1 -1
  181. package/src/assets/skills/worktree-support/references/roots.md +2 -2
  182. package/templates/lint-pack/.lint-pack.conf +86 -0
  183. package/templates/lint-pack/README.md +190 -0
  184. package/templates/lint-pack/lint-pack.sh +905 -0
  185. package/dist/core/learning-queue-cleanup.js +0 -27
  186. package/dist/core/learning-store.js +0 -61
  187. /package/dist/{learning-off → curation-off}/agents/debug.md +0 -0
  188. /package/dist/{learning-off → curation-off}/agents/design.md +0 -0
  189. /package/dist/{learning-off → curation-off}/agents/harden.md +0 -0
  190. /package/dist/{learning-off → curation-off}/agents/knowledge.md +0 -0
  191. /package/dist/{learning-off → curation-off}/agents/research.md +0 -0
  192. /package/dist/{learning-off → curation-off}/agents/skim.md +0 -0
  193. /package/dist/{learning-off → curation-off}/commands/dynamic-plan.md +0 -0
  194. /package/dist/{learning-off → curation-off}/commands/dynamic-profile.md +0 -0
  195. /package/dist/{learning-off → curation-off}/commands/dynamic-tickets.md +0 -0
  196. /package/dist/{learning-off → curation-off}/commands/release.md +0 -0
  197. /package/dist/{learning-off → curation-off}/commands/research.md +0 -0
@@ -0,0 +1,299 @@
1
+ /**
2
+ * devflow gates — Show or set the machine's default gate level.
3
+ *
4
+ * The gate level is one of `full`, `standard` or `lean`. `--set` writes the MACHINE
5
+ * default, `features.gateLevel` in `~/.devflow/manifest.json`, and nothing else: not
6
+ * the repository's `project.json`, not the personal `.devflow/config.json` (no
7
+ * command writes that key), and no sentinel, re-arm or install step. `--status` shows
8
+ * the level that applies in the current directory, which layer decided it and what
9
+ * the machine records; it writes nothing.
10
+ *
11
+ * Three parts, so no business logic throws or prints:
12
+ * gatherGateStatus resolves the settings ONCE and reads the manifest raw, and
13
+ * returns a {@link GateStatus} that is a discriminated union
14
+ * renderGateStatus pure: one {@link GateStatus} to lines and warnings
15
+ * gatesCommand the thin shell: parse, call, print, set the exit code
16
+ *
17
+ * No code here chooses a level from a task or a diff: the level comes from the
18
+ * resolver (personal key, machine key, default) or from the word typed after `--set`.
19
+ */
20
+ import { Command } from 'commander';
21
+ import * as path from 'path';
22
+ import * as p from '@clack/prompts';
23
+ import color from 'picocolors';
24
+ import { DEFAULT_GATE_LEVEL, GATE_LEVELS, MALFORMED_GATE_LEVEL_READS_AS, machineGateLevel, parseGateLevel, readGateManifest, writeMachineGateLevel, } from '../../core/gate-level.js';
25
+ import { describeEchoedValue } from '../../core/echo-value.js';
26
+ import { loadSettingsModule, settingsSourceFile, trackedConfigWarning, } from '../../core/evidence-policy.js';
27
+ import { getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
28
+ // ── Wording ────────────────────────────────────────────────────────────────────
29
+ /**
30
+ * What each level means, in one line. Each line says what the word stands for and
31
+ * not what a command does with it. The line for MALFORMED_GATE_LEVEL_READS_AS says
32
+ * that a value that is not a level reads as it.
33
+ */
34
+ export const GATE_LEVEL_MEANINGS = Object.freeze({
35
+ full: 'the most checking; any value that is not a level also reads as full',
36
+ standard: 'the default when no level is set',
37
+ lean: 'the least checking',
38
+ });
39
+ /**
40
+ * Which commands read the level today; each later command that reads it updates this text.
41
+ *
42
+ * The init prompt says what each level does in GATE_LEVEL_HINTS (gate-level-prompts.ts).
43
+ * Change the hints too when what a level does changes. A command that reads the level and
44
+ * has no rule that changes a phase leaves the hints true, so it changes this text alone.
45
+ * One test pins the exact text of both tables, so a change to either fails it until you
46
+ * check the other table and update the pin; further rows check the clauses the two tables
47
+ * share (the lint pack level, the levels that run fewer review focuses and the levels that
48
+ * skip Polish). The clause that lean skips Harden on a docs-only diff has no facet row in
49
+ * `tests/gate-level-prompts.test.ts`: the docs guard checks it at each doc site, against
50
+ * the row in the /implement level reference.
51
+ */
52
+ export const GATE_LEVEL_EFFECT = '/code-review and /plan read the level: standard and lean run fewer review focuses and fewer planning agents. At lean, /implement has Validate run the project\'s lint pack. /implement and /resolve also read the level and name it in their report; at standard and lean they skip Polish, and full keeps it. At lean, /implement skips Harden on a docs-only diff.';
53
+ const MACHINE_MANIFEST_NAME = 'manifest.json';
54
+ const MACHINE_INVALID_LINE = `invalid (${MACHINE_MANIFEST_NAME}; reads as ${MALFORMED_GATE_LEVEL_READS_AS})`;
55
+ const VALID_LEVELS = GATE_LEVELS.join(', ');
56
+ const VALID_LEVELS_OR = `${GATE_LEVELS.slice(0, -1).join(', ')}, or ${GATE_LEVELS[GATE_LEVELS.length - 1]}`;
57
+ /** The resolved settings, or null when the resolver is unusable or throws despite its contract. */
58
+ function resolveOnce(module, dir) {
59
+ try {
60
+ return module.resolveSettings({ dir });
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
66
+ function manifestState(read) {
67
+ switch (read.kind) {
68
+ case 'absent': return 'absent';
69
+ case 'object': return 'readable';
70
+ case 'rejected': return 'rejected';
71
+ case 'corrupt': return 'unreadable';
72
+ default: {
73
+ const exhaustive = read;
74
+ return exhaustive;
75
+ }
76
+ }
77
+ }
78
+ /** Why a fail-closed result failed, or null when the resolver names no reason. */
79
+ function failClosedReason(settings) {
80
+ return settings.unreadable === null ? null : `${settingsSourceFile(settings.unreadable)} is unreadable`;
81
+ }
82
+ /**
83
+ * Gather one {@link GateStatus}. Never throws, never writes.
84
+ *
85
+ * The settings resolve ONCE, and that one result feeds the level, the machine value
86
+ * and the tracked-config warning. The manifest is read with the gate reader, never
87
+ * with `readManifest()`, which heal-writes and returns null for a manifest it
88
+ * rejects: a read-only status must do neither. The manifest is read twice, once by
89
+ * the gate reader here and once by the resolver; a write between the two reads can
90
+ * mix two versions, which is acceptable because the result is display only and no
91
+ * action follows it.
92
+ */
93
+ export async function gatherGateStatus(deps) {
94
+ const manifestPath = path.join(deps.devflowDir, MACHINE_MANIFEST_NAME);
95
+ const read = await deps.readManifest(deps.devflowDir);
96
+ const base = { manifest: manifestState(read), manifestPath };
97
+ // The TypeScript reader's machine value, for a result that carries none of its own.
98
+ const stored = read.kind === 'object' || read.kind === 'rejected' ? machineGateLevel(read.raw) : null;
99
+ const loaded = deps.loadSettings();
100
+ const settings = loaded.ok ? resolveOnce(loaded.value, deps.dir) : null;
101
+ if (settings === null) {
102
+ return { ...base, kind: 'unloadable', machine: stored };
103
+ }
104
+ const trackedWarning = trackedConfigWarning(settings);
105
+ if (!settings.ok || settings.gateLevelSource === null) {
106
+ // Only a result that names an unreadable file carries the manifest's value; a
107
+ // fail-closed result without one (git did not answer) never read the manifest.
108
+ const machine = settings.unreadable !== null ? settings.gateLevelMachine : stored;
109
+ return { ...base, kind: 'fail-closed', reason: failClosedReason(settings), machine, trackedWarning };
110
+ }
111
+ return {
112
+ ...base,
113
+ kind: 'resolved',
114
+ level: settings.gateLevel,
115
+ source: settings.gateLevelSource,
116
+ invalid: settings.gateLevelInvalid,
117
+ machine: settings.gateLevelMachine,
118
+ trackedWarning,
119
+ };
120
+ }
121
+ /** The machine line: `none` when the manifest records no level or cannot be read (its own warning says which). */
122
+ function machineLine(machine) {
123
+ if (machine === 'invalid')
124
+ return `Machine: ${MACHINE_INVALID_LINE}`;
125
+ if (machine === null)
126
+ return 'Machine: none';
127
+ return `Machine: ${machine}`;
128
+ }
129
+ function manifestWarning(status) {
130
+ switch (status.manifest) {
131
+ case 'absent':
132
+ case 'readable':
133
+ return null;
134
+ case 'rejected':
135
+ return `Manifest at ${status.manifestPath} is a manifest that devflow rejects; devflow init will replace this file and keep a gate level stored in it unless you choose another level or use --reset.`;
136
+ case 'unreadable':
137
+ return `Manifest at ${status.manifestPath} cannot be read by devflow gates; devflow init will replace this file. Init keeps a gate level stored in it only if init can read the file. If init reports that it could not read the level, set it again with devflow gates --set <level>.`;
138
+ default: {
139
+ const exhaustive = status.manifest;
140
+ return exhaustive;
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * Render a {@link GateStatus}. Pure and total over every `kind`.
146
+ *
147
+ * A malformed level is reported by the layer that holds it and its state, never by
148
+ * its value: a raw config value never reaches a terminal.
149
+ */
150
+ export function renderGateStatus(status) {
151
+ const warnings = [];
152
+ const lines = [];
153
+ switch (status.kind) {
154
+ case 'resolved': {
155
+ lines.push(`Effective: ${status.level} (${status.source}${status.invalid ? ', invalid' : ''})`);
156
+ lines.push(machineLine(status.machine));
157
+ if (status.invalid && status.source === 'personal') {
158
+ warnings.push(`gateLevel in ${settingsSourceFile('personal')} is not one of ${VALID_LEVELS}, so ${status.level} is in effect. ` +
159
+ 'The value is not shown.');
160
+ }
161
+ if (status.trackedWarning !== null)
162
+ warnings.push(status.trackedWarning);
163
+ break;
164
+ }
165
+ case 'fail-closed': {
166
+ lines.push(`Effective: full (fail-closed: ${status.reason ?? 'reason unknown'})`);
167
+ lines.push(machineLine(status.machine));
168
+ if (status.trackedWarning !== null)
169
+ warnings.push(status.trackedWarning);
170
+ break;
171
+ }
172
+ case 'unloadable': {
173
+ lines.push('Effective: level unknown');
174
+ lines.push(machineLine(status.machine));
175
+ warnings.push('The settings resolver could not be loaded, so the level that applies is unknown.');
176
+ break;
177
+ }
178
+ default: {
179
+ const exhaustive = status;
180
+ return exhaustive;
181
+ }
182
+ }
183
+ const fromManifest = manifestWarning(status);
184
+ if (fromManifest !== null)
185
+ warnings.push(fromManifest);
186
+ return { lines, warnings };
187
+ }
188
+ /**
189
+ * Map the writer's result to a message and an exit code. Pure and total. Every
190
+ * refusal exits 1 and says nothing was written, except a failed write, which names
191
+ * the path and the error code.
192
+ */
193
+ export function describeGateWrite(result, level, manifestPath) {
194
+ switch (result.kind) {
195
+ case 'written':
196
+ return { kind: 'success', message: `Gate level: ${level}`, exitCode: 0 };
197
+ case 'unchanged':
198
+ return { kind: 'info', message: `Gate level: ${level} (unchanged)`, exitCode: 0 };
199
+ case 'not-installed':
200
+ return { kind: 'error', message: `No manifest found at ${manifestPath}. Run devflow init first.`, exitCode: 1 };
201
+ case 'unreadable':
202
+ return {
203
+ kind: 'error',
204
+ message: `Manifest at ${result.path} exists but cannot be read; nothing changed. Move it aside, then run devflow init.`,
205
+ exitCode: 1,
206
+ };
207
+ case 'write-failed':
208
+ return {
209
+ kind: 'error',
210
+ message: `Could not write the manifest at ${result.path} (${result.reason}); nothing changed.`,
211
+ exitCode: 1,
212
+ };
213
+ case 'invalid-level':
214
+ return {
215
+ kind: 'error',
216
+ message: `Unknown gate level: "${describeEchoedValue(level)}"; nothing changed. Valid levels: ${VALID_LEVELS}`,
217
+ exitCode: 1,
218
+ };
219
+ default: {
220
+ const exhaustive = result;
221
+ return exhaustive;
222
+ }
223
+ }
224
+ }
225
+ /**
226
+ * Write the machine default and describe the result. Never throws. `now` is injected
227
+ * so a test fixes the `updatedAt` stamp.
228
+ */
229
+ export async function runGatesSet(devflowDir, level, now = () => new Date().toISOString()) {
230
+ const result = await writeMachineGateLevel(devflowDir, level, now);
231
+ return describeGateWrite(result, level, path.join(devflowDir, MACHINE_MANIFEST_NAME));
232
+ }
233
+ function printOutcome(outcome) {
234
+ switch (outcome.kind) {
235
+ case 'success':
236
+ p.log.success(outcome.message);
237
+ break;
238
+ case 'info':
239
+ p.log.info(outcome.message);
240
+ break;
241
+ case 'error':
242
+ p.log.error(outcome.message);
243
+ break;
244
+ default: {
245
+ const exhaustive = outcome.kind;
246
+ return exhaustive;
247
+ }
248
+ }
249
+ }
250
+ function levelsHelp() {
251
+ const width = Math.max(...GATE_LEVELS.map(level => level.length));
252
+ return GATE_LEVELS.map(level => ` ${level.padEnd(width)} ${GATE_LEVEL_MEANINGS[level]}`).join('\n');
253
+ }
254
+ export const gatesCommand = new Command('gates')
255
+ .description('Show or set the machine\'s default gate level')
256
+ .option('--status', 'Show the gate level in effect here, which layer decided it, and the machine default')
257
+ .option(`--set <${GATE_LEVELS.join('|')}>`, `Set the machine's default gate level: ${VALID_LEVELS_OR}`)
258
+ .addHelpText('after', `\nGate levels:\n${levelsHelp()}\n\n` +
259
+ `The default is ${DEFAULT_GATE_LEVEL}. --set writes only the machine default. A personal .devflow/config.json ` +
260
+ '"gateLevel" overrides it in one repository; no command writes that key.\n' +
261
+ GATE_LEVEL_EFFECT)
262
+ .action(async (options) => {
263
+ const devflowDir = getDevFlowDirectory();
264
+ if (!options.status && options.set === undefined) {
265
+ p.intro(color.bgCyan(color.white(' Gates ')));
266
+ p.note(`${color.cyan('devflow gates --status')} Show the gate level in effect here and the machine default\n` +
267
+ `${color.cyan('devflow gates --set <level>')} Set the machine default\n\n` +
268
+ `Valid levels:\n${levelsHelp()}`, 'Usage');
269
+ p.outro(color.dim(GATE_LEVEL_EFFECT));
270
+ return;
271
+ }
272
+ // Validate --set before any I/O, even beside --status: a bad word is rejected,
273
+ // never repaired, and exits 1 as it does in `devflow tracker` and `devflow compliance`.
274
+ const parsed = options.set === undefined ? undefined : parseGateLevel(options.set);
275
+ if (parsed !== undefined && !parsed.ok) {
276
+ p.log.error(parsed.error);
277
+ process.exitCode = 1;
278
+ return;
279
+ }
280
+ // --status wins over a valid --set and writes nothing, mirroring `devflow tracker`.
281
+ // With neither flag the usage printed above, so no --set here means --status alone.
282
+ if (options.status || parsed === undefined) {
283
+ const status = await gatherGateStatus({
284
+ devflowDir,
285
+ dir: process.cwd(),
286
+ loadSettings: loadSettingsModule,
287
+ readManifest: readGateManifest,
288
+ });
289
+ const rendered = renderGateStatus(status);
290
+ p.note(rendered.lines.join('\n'), 'Gate Level Status');
291
+ for (const warning of rendered.warnings)
292
+ p.log.warn(warning);
293
+ return;
294
+ }
295
+ const outcome = await runGatesSet(devflowDir, parsed.value);
296
+ printOutcome(outcome);
297
+ process.exitCode = outcome.exitCode;
298
+ });
299
+ //# sourceMappingURL=gates.js.map
@@ -21,12 +21,13 @@ export const FEATURE_DEFAULTS = {
21
21
  memory: true,
22
22
  hud: true,
23
23
  knowledge: true,
24
- learning: true,
24
+ curation: true,
25
25
  rules: true,
26
26
  proxy: false,
27
27
  behavior: false,
28
28
  compliance: { enabled: false, frameworks: [] },
29
29
  tracker: { provider: DEFAULT_TRACKER_PROVIDER },
30
+ gateLevel: undefined,
30
31
  };
31
32
  // ── Helpers ───────────────────────────────────────────────────────────────────
32
33
  /**
@@ -34,7 +35,7 @@ export const FEATURE_DEFAULTS = {
34
35
  * manifest.features, with registry defaults when the manifest is absent.
35
36
  *
36
37
  * D-FEATURES-NARROW-ONLY (src/core/feature-switch.ts): the machine switches for
37
- * memory, learning and knowledge are recorded in the manifest alone (a repository
38
+ * memory, curation and knowledge are recorded in the manifest alone (a repository
38
39
  * only narrows them at read time), so the seed takes no per-repo input at all. Seeding from whatever repo init happens to run in
39
40
  * would flip the switch for EVERY repo as a side effect — a stale per-repo
40
41
  * `true` would silently re-enable a feature the user turned off. State-aware
@@ -59,8 +60,12 @@ export function resolveSeedFeatures(manifest) {
59
60
  const tracker = { ...rawTracker };
60
61
  const memory = manifest?.features.memory ?? FEATURE_DEFAULTS.memory;
61
62
  const knowledge = manifest?.features.knowledge ?? FEATURE_DEFAULTS.knowledge;
62
- const learning = manifest?.features.learning ?? FEATURE_DEFAULTS.learning;
63
- return { ambient, memory, hud, knowledge, learning, rules, proxy, behavior, compliance, tracker };
63
+ const curation = manifest?.features.curation ?? FEATURE_DEFAULTS.curation;
64
+ // The raw stored level, with no default operator: `??` would turn a stored `null`
65
+ // into the default, and init would then write the default over a value the
66
+ // fold reads as the strictest level.
67
+ const gateLevel = manifest?.features.gateLevel;
68
+ return { ambient, memory, hud, knowledge, curation, rules, proxy, behavior, compliance, tracker, gateLevel };
64
69
  }
65
70
  /**
66
71
  * Resolve the flag record for the init seed.
@@ -305,6 +310,11 @@ export function resolveResetGatedInputs(reset, manifest, projectConfig, settings
305
310
  * undefined means "user did not specify this flag, keep the seed value".
306
311
  *
307
312
  * Applies explicit --ambient/--no-ambient etc. passed alongside --recommended.
313
+ *
314
+ * The machine gate level is not a toggle: `resolveGateLevelForInit` (gate-level-prompts.ts)
315
+ * is the one place that decides it (--gate-level, then the wizard answer, then the stored
316
+ * value), and init holds that decision itself. The result carries the seed's raw value only
317
+ * to stay a complete FeatureSeed.
308
318
  */
309
319
  export function applyCliToggles(base, toggles) {
310
320
  return {
@@ -312,12 +322,13 @@ export function applyCliToggles(base, toggles) {
312
322
  memory: toggles.memory ?? base.memory,
313
323
  hud: toggles.hud ?? base.hud,
314
324
  knowledge: toggles.knowledge ?? base.knowledge,
315
- learning: toggles.learning ?? base.learning,
325
+ curation: toggles.curation ?? base.curation,
316
326
  rules: toggles.rules ?? base.rules,
317
327
  proxy: toggles.proxy ?? base.proxy,
318
328
  behavior: toggles.behavior ?? base.behavior,
319
329
  compliance: toggles.compliance ?? base.compliance,
320
330
  tracker: toggles.tracker ?? base.tracker,
331
+ gateLevel: base.gateLevel,
321
332
  };
322
333
  }
323
334
  //# sourceMappingURL=init-seed.js.map