devflow-kit 3.9.0 → 3.11.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 (168) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/README.md +1 -1
  3. package/dist/agents/code.md +6 -6
  4. package/dist/agents/fix.md +6 -6
  5. package/dist/agents/harden.md +7 -6
  6. package/dist/agents/refactor.md +6 -6
  7. package/dist/cli/commands/ambient.js +10 -2
  8. package/dist/cli/commands/backup-session.js +131 -0
  9. package/dist/cli/commands/compliance-prompts.js +9 -4
  10. package/dist/cli/commands/compliance.js +184 -24
  11. package/dist/cli/commands/curation.js +23 -10
  12. package/dist/cli/commands/gate-level-prompts.js +39 -25
  13. package/dist/cli/commands/gates.js +5 -7
  14. package/dist/cli/commands/init.js +169 -184
  15. package/dist/cli/commands/install-report.js +129 -0
  16. package/dist/cli/commands/legacy-sweep.js +159 -0
  17. package/dist/cli/commands/proxy.js +6 -1
  18. package/dist/cli/commands/rules.js +75 -21
  19. package/dist/cli/commands/skills.js +42 -9
  20. package/dist/cli/commands/tracker.js +9 -3
  21. package/dist/cli/commands/uninstall.js +339 -75
  22. package/dist/commands/bug-analysis.md +69 -120
  23. package/dist/commands/dynamic-build.md +23 -23
  24. package/dist/commands/implement.md +46 -32
  25. package/dist/commands/release.md +1 -1
  26. package/dist/commands/resolve.md +35 -31
  27. package/dist/commands/self-review.md +8 -7
  28. package/dist/compliance-off/agents/git.md +120 -0
  29. package/dist/compliance-off/agents/triage.md +180 -0
  30. package/dist/compliance-off/commands/bug-analysis.md +398 -0
  31. package/dist/compliance-off/commands/code-review.md +520 -0
  32. package/dist/compliance-off/commands/dynamic-build.md +1251 -0
  33. package/dist/compliance-off/commands/dynamic-tickets.md +676 -0
  34. package/dist/compliance-off/commands/implement.md +736 -0
  35. package/dist/compliance-off/commands/plan.md +671 -0
  36. package/dist/compliance-off/commands/release.md +309 -0
  37. package/dist/compliance-off/commands/resolve.md +742 -0
  38. package/dist/compliance-off/skills/git/references/decision-markers.md +17 -0
  39. package/dist/compliance-off/skills/git/references/learn-conventions.md +80 -0
  40. package/dist/compliance-off/skills/git/references/pr/check-ci-status.md +33 -0
  41. package/dist/compliance-off/skills/git/references/pr/check-merge-readiness.md +49 -0
  42. package/dist/compliance-off/skills/git/references/pr/fetch-review-threads.md +52 -0
  43. package/dist/compliance-off/skills/git/references/pr/validate-branch.md +47 -0
  44. package/dist/compliance-off/skills/git/references/tracker/_contract.md +33 -0
  45. package/dist/compliance-off/skills/git/references/tracker/_mcp.md +153 -0
  46. package/dist/compliance-off/skills/git/references/tracker/github/create-release.md +35 -0
  47. package/dist/compliance-off/skills/git/references/tracker/github/ensure-pr-ready.md +100 -0
  48. package/dist/compliance-off/skills/git/references/tracker/github/ensure-traceable-issue.md +74 -0
  49. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issue.md +69 -0
  50. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issues-batch.md +65 -0
  51. package/dist/compliance-off/skills/git/references/tracker/github/gather-release-evidence.md +48 -0
  52. package/dist/compliance-off/skills/git/references/tracker/github/setup-task.md +77 -0
  53. package/dist/compliance-off/skills/git/references/tracker/jira/create-release.md +35 -0
  54. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-pr-ready.md +100 -0
  55. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-traceable-issue.md +61 -0
  56. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issue.md +51 -0
  57. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issues-batch.md +63 -0
  58. package/dist/compliance-off/skills/git/references/tracker/jira/gather-release-evidence.md +47 -0
  59. package/dist/compliance-off/skills/git/references/tracker/jira/setup-task.md +82 -0
  60. package/dist/compliance-off/skills/git/references/tracker/linear/create-release.md +35 -0
  61. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-pr-ready.md +100 -0
  62. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-traceable-issue.md +61 -0
  63. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issue.md +51 -0
  64. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issues-batch.md +63 -0
  65. package/dist/compliance-off/skills/git/references/tracker/linear/gather-release-evidence.md +47 -0
  66. package/dist/compliance-off/skills/git/references/tracker/linear/setup-task.md +83 -0
  67. package/dist/compliance-off/skills/git/references/trust-rule.md +7 -0
  68. package/dist/core/agent-models.js +19 -4
  69. package/dist/core/assets.js +126 -17
  70. package/dist/core/compliance-off-files.js +27 -0
  71. package/dist/core/compliance.js +14 -0
  72. package/dist/core/curation-variants.js +460 -112
  73. package/dist/core/flags.js +2 -2
  74. package/dist/core/fs-atomic.js +5 -1
  75. package/dist/core/installed-variants.js +63 -0
  76. package/dist/core/manifest.js +48 -5
  77. package/dist/core/mds-variants.js +700 -3
  78. package/dist/core/orphan-sweep.js +29 -2
  79. package/dist/core/path-set.js +15 -0
  80. package/dist/core/plugins.js +68 -10
  81. package/dist/core/reference-sweep.js +42 -25
  82. package/dist/core/retired-shipped-names.js +278 -0
  83. package/dist/curation-compliance-off/agents/triage.md +176 -0
  84. package/dist/curation-compliance-off/commands/bug-analysis.md +349 -0
  85. package/dist/curation-compliance-off/commands/code-review.md +481 -0
  86. package/dist/curation-compliance-off/commands/dynamic-build.md +1211 -0
  87. package/dist/curation-compliance-off/commands/dynamic-tickets.md +634 -0
  88. package/dist/curation-compliance-off/commands/implement.md +689 -0
  89. package/dist/curation-compliance-off/commands/plan.md +631 -0
  90. package/dist/curation-compliance-off/commands/release.md +279 -0
  91. package/dist/curation-compliance-off/commands/resolve.md +692 -0
  92. package/dist/curation-off/agents/code.md +6 -6
  93. package/dist/curation-off/agents/fix.md +6 -6
  94. package/dist/curation-off/agents/harden.md +7 -6
  95. package/dist/curation-off/agents/refactor.md +6 -6
  96. package/dist/curation-off/commands/bug-analysis.md +69 -119
  97. package/dist/curation-off/commands/dynamic-build.md +23 -23
  98. package/dist/curation-off/commands/implement.md +46 -32
  99. package/dist/curation-off/commands/release.md +1 -1
  100. package/dist/curation-off/commands/resolve.md +35 -31
  101. package/dist/curation-off/commands/self-review.md +8 -7
  102. package/dist/skills/git/references/pr/resolve-review-threads.md +1 -1
  103. package/dist/targets/claude-code/behavior-install.js +23 -9
  104. package/dist/targets/claude-code/charter-render.js +374 -0
  105. package/dist/targets/claude-code/compliance-install.js +62 -17
  106. package/dist/targets/claude-code/compliance-off-files.js +249 -0
  107. package/dist/targets/claude-code/curation-install.js +632 -117
  108. package/dist/targets/claude-code/folder-swap.js +142 -0
  109. package/dist/targets/claude-code/install-backup.js +780 -0
  110. package/dist/targets/claude-code/installer.js +795 -199
  111. package/dist/targets/claude-code/language-stamp.js +6 -0
  112. package/dist/targets/claude-code/legacy-skill-hashes.js +170 -0
  113. package/dist/targets/claude-code/linked-folder.js +176 -0
  114. package/dist/targets/claude-code/reference-converge.js +131 -0
  115. package/dist/targets/claude-code/rules-folder.js +34 -0
  116. package/dist/targets/claude-code/shipped-set.js +160 -0
  117. package/dist/targets/claude-code/tracker-install.js +21 -8
  118. package/dist/targets/claude-code/tracker-servers.js +20 -6
  119. package/dist/targets/claude-code/workflow-folder-install.js +251 -0
  120. package/dist/variant-axes.json +109 -0
  121. package/package.json +1 -1
  122. package/src/assets/agents/_partials/_code.mds +6 -6
  123. package/src/assets/agents/evaluate.md +1 -1
  124. package/src/assets/agents/git.mds +32 -0
  125. package/src/assets/agents/harden.mds +7 -6
  126. package/src/assets/agents/test.md +21 -12
  127. package/src/assets/agents/triage.mds +2 -0
  128. package/src/assets/agents/validate.md +10 -10
  129. package/src/assets/commands/_partials/_compliance.mds +5 -3
  130. package/src/assets/commands/_partials/_engine.mds +9 -8
  131. package/src/assets/commands/_partials/_publication.mds +10 -1
  132. package/src/assets/commands/bug-analysis.mds +80 -126
  133. package/src/assets/commands/code-review.mds +43 -0
  134. package/src/assets/commands/dynamic-build.mds +109 -15
  135. package/src/assets/commands/dynamic-tickets.mds +13 -0
  136. package/src/assets/commands/implement.mds +145 -33
  137. package/src/assets/commands/plan.mds +19 -0
  138. package/src/assets/commands/release.mds +50 -1
  139. package/src/assets/commands/resolve.mds +217 -32
  140. package/src/assets/commands/self-review.mds +8 -7
  141. package/src/assets/mds/git/_ops.mds +23 -0
  142. package/src/assets/mds/git/_ops_steps.mds +17 -0
  143. package/src/assets/mds/git/_pr.mds +1 -1
  144. package/src/assets/mds/git/_references.mds +6 -0
  145. package/src/assets/mds/tracker/_common.mds +4 -0
  146. package/src/assets/mds/tracker/_github.mds +42 -0
  147. package/src/assets/mds/tracker/_jira.mds +43 -0
  148. package/src/assets/mds/tracker/_linear.mds +43 -0
  149. package/src/assets/rules/quality.md +2 -0
  150. package/src/assets/scripts/ci-wait.cjs +204 -126
  151. package/src/assets/scripts/hooks/assets/implement-installed +1 -0
  152. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +13 -11
  153. package/src/assets/scripts/hooks/preamble +18 -8
  154. package/src/assets/scripts/hooks/session-start-orchestrator +4 -2
  155. package/src/assets/scripts/pr-evidence.cjs +41 -2
  156. package/src/assets/scripts/verify-evidence.cjs +408 -56
  157. package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +3 -3
  158. package/src/assets/skills/agent-references/references/commands/plan-levels.md +1 -1
  159. package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +6 -2
  160. package/src/assets/skills/agent-references/references/validate/passed-alone.md +7 -7
  161. package/src/assets/skills/docs-framework/SKILL.md +5 -2
  162. package/src/assets/skills/git/SKILL.md +3 -3
  163. package/src/assets/skills/git/references/github-api.md +2 -139
  164. package/src/assets/skills/git/references/github-posting.md +158 -0
  165. package/src/assets/skills/review-methodology/SKILL.md +1 -1
  166. package/src/assets/skills/review-methodology/references/patterns.md +1 -1
  167. package/src/assets/skills/review-methodology/references/report-template.md +1 -2
  168. package/src/assets/skills/testing/SKILL.md +2 -4
@@ -441,8 +441,8 @@ export const FLAG_REGISTRY = [
441
441
  {
442
442
  // D-FOREGROUND-RUN: every agent runs builds and tests in the foreground under an explicit
443
443
  // Bash `timeout` whose ceiling is Claude Code's 600000 ms, or this variable when set. A
444
- // suite that cannot be split under that ceiling is reported BLOCKED with the remedy
445
- // `devflow flags --set bash-max-timeout-ms=<ms>`, so this flag is that remedy.
444
+ // run that passes its timeout is waited for and read, so raising the ceiling is optional:
445
+ // it only lets a long run end inside one call.
446
446
  // The env name was confirmed against Claude Code 2.1.294 (docs/reference/claude-code-flags-probe.md):
447
447
  // the binary reads BASH_MAX_TIMEOUT_MS, ignores a non-positive or NaN value, and takes the
448
448
  // larger of it and the default timeout. Neutral by default (undefined → manifest null →
@@ -10,6 +10,10 @@ import { promises as fs } from 'fs';
10
10
  * intentionally remains a separate implementation — same semantics, different module
11
11
  * system. Any change to the retry logic here MUST be mirrored there.
12
12
  */
13
+ /** The sibling `filePath` is written to before the rename: `<file>.tmp.<pid>`. */
14
+ export function atomicTempPath(filePath) {
15
+ return `${filePath}.tmp.${process.pid}`;
16
+ }
13
17
  /**
14
18
  * Atomically write `filePath` by writing to a sibling `.tmp` then renaming.
15
19
  *
@@ -38,7 +42,7 @@ export async function writeFileAtomicExclusive(filePath, data, createMode) {
38
42
  // (e.g., two Claude Code sessions) never collide on the same .tmp path.
39
43
  // mirrors proxy-log.ts rotation at src/core/proxy-log.ts which PID-scopes
40
44
  // for the same reason.
41
- const tmp = `${filePath}.tmp.${process.pid}`;
45
+ const tmp = atomicTempPath(filePath);
42
46
  const options = { encoding: 'utf-8', flag: 'wx', ...(createMode === undefined ? {} : { mode: createMode }) };
43
47
  try {
44
48
  await fs.writeFile(tmp, data, options);
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The install record of the prompt variants (#648, AC-29 and AC-31).
3
+ *
4
+ * After a converge that finished clean, the manifest records which variant of each axis the
5
+ * installed prompts hold: `installedVariants: { curation, compliance }`. The record is the only
6
+ * way to tell what is installed without reading every prompt, and it answers one question the
7
+ * switches alone cannot: does this machine still carry posting steps from before the switch moved?
8
+ *
9
+ * The manifest reader carries the field raw, so a hand-edited or newer-version value reaches this
10
+ * module unchecked. {@link readInstalledVariants} is the one parse: anything but two booleans is
11
+ * unknown, and an absent field is unknown. Nothing heals an unknown record. Pure, with no I/O.
12
+ */
13
+ /** The record as the manifest carries it, or null when it is absent or is not two booleans (unknown). */
14
+ export function readInstalledVariants(raw) {
15
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
16
+ return null;
17
+ const { curation, compliance } = raw;
18
+ if (typeof curation !== 'boolean' || typeof compliance !== 'boolean')
19
+ return null;
20
+ return { curation, compliance };
21
+ }
22
+ /**
23
+ * The record to write for a converge, or null when none is owed (AC-29). Only a converge that
24
+ * finished clean records the switches the prompts were converged onto. Any other converge writes
25
+ * nothing, so the field is left out and reads as unknown: it names `devflow init`, and the posting
26
+ * notice stays due. Pure; the result is a fresh object.
27
+ */
28
+ export function installRecordFor(converged, switches) {
29
+ return converged ? { curation: switches.curation, compliance: switches.compliance } : null;
30
+ }
31
+ /**
32
+ * Whether the posting steps are installed, from the record and the machine's compliance switch.
33
+ *
34
+ * `installed` and `not installed` are the two settled states: the record agrees with the switch, and
35
+ * the on variant carries the posting steps while the off variant carries none. A record that
36
+ * disagrees with the switch is `mismatch`, and a record that is absent or unreadable is `unknown`;
37
+ * both name the remedy.
38
+ */
39
+ export function postingStatus(installed, complianceEnabled) {
40
+ if (installed === null)
41
+ return 'unknown (run devflow init)';
42
+ if (installed.compliance !== complianceEnabled)
43
+ return 'mismatch (run devflow init)';
44
+ return complianceEnabled ? 'installed' : 'not installed';
45
+ }
46
+ /** What `devflow init` prints, once, when the installed prompts move from posting steps to none. */
47
+ export const POSTING_OFF_NOTICE = 'Compliance is off on this machine, so devflow installed no step that posts test-plan evidence, ' +
48
+ 'review or resolution summaries, backlog entries, review-thread replies, wave reports, plan artifacts ' +
49
+ 'or release back-links to GitHub or a tracker. Results stay in local files under .devflow/docs/. ' +
50
+ 'To install these steps, run `devflow compliance --enable`.';
51
+ /**
52
+ * Whether init owes the posting notice: compliance is off and the record does not already say the
53
+ * installed prompts carry no posting step. A record that is absent or unreadable is unknown, and
54
+ * unknown counts as posting, so a machine from before the record existed hears the notice once.
55
+ * The caller also requires a clean converge and a written record, so the notice and the record
56
+ * move together and the next init finds the record and stays quiet.
57
+ */
58
+ export function postingNoticeDue(installed, complianceEnabled) {
59
+ if (complianceEnabled)
60
+ return false;
61
+ return installed === null || installed.compliance;
62
+ }
63
+ //# sourceMappingURL=installed-variants.js.map
@@ -210,6 +210,9 @@ export async function readManifest(devflowDir, opts) {
210
210
  },
211
211
  installedAt: data.installedAt,
212
212
  updatedAt: data.updatedAt,
213
+ // The install record of the prompt variants, raw (see the field's doc comment). Last, where
214
+ // the writer that records it puts it, so a read and a write never move the key.
215
+ ...(Object.hasOwn(data, 'installedVariants') ? { installedVariants: data.installedVariants } : {}),
213
216
  };
214
217
  if (needsHeal) {
215
218
  // D39: wrap the heal-write in its own try/catch so a write failure does NOT
@@ -293,7 +296,8 @@ export async function syncManifestFeature(devflowDir, key, value) {
293
296
  * re-init install it again. Going through readManifest()/writeManifest() instead
294
297
  * would drop every key ManifestData does not model (one a newer devflow wrote,
295
298
  * say) and persist that reader's unrelated heals as a side effect of a removal.
296
- * Entries that are not strings are neither matched nor dropped.
299
+ * Entries that are not strings are neither matched nor dropped. A legacy name matches its current plugin
300
+ * ({@link doomedEntryTest}).
297
301
  */
298
302
  export function withoutManifestPlugins(rawManifest, names, now) {
299
303
  if (typeof rawManifest !== 'object' || rawManifest === null || Array.isArray(rawManifest))
@@ -301,12 +305,25 @@ export function withoutManifestPlugins(rawManifest, names, now) {
301
305
  const record = rawManifest;
302
306
  if (!Array.isArray(record.plugins))
303
307
  return null;
304
- const doomed = new Set(names);
305
- const kept = record.plugins.filter(entry => !(typeof entry === 'string' && doomed.has(entry)));
308
+ const isDoomed = doomedEntryTest(names);
309
+ const kept = record.plugins.filter(entry => !(typeof entry === 'string' && isDoomed(entry)));
306
310
  if (kept.length === record.plugins.length)
307
311
  return null;
308
312
  return { ...record, plugins: kept, updatedAt: now };
309
313
  }
314
+ /**
315
+ * Whether a recorded plugin name is one of `names`, a legacy name counting as its current plugin on both sides.
316
+ * Removing `devflow-plan` therefore also drops a record that still holds `devflow-specify`. Any other name,
317
+ * a name the registry does not know included, matches only itself.
318
+ *
319
+ * The record is file content, so only a key LEGACY_PLUGIN_NAMES owns maps; one it inherits from Object,
320
+ * such as `constructor`, is an ordinary name.
321
+ */
322
+ function doomedEntryTest(names) {
323
+ const current = (name) => (Object.hasOwn(LEGACY_PLUGIN_NAMES, name) ? LEGACY_PLUGIN_NAMES[name] : name);
324
+ const doomed = new Set(names.map(current));
325
+ return entry => doomed.has(current(entry));
326
+ }
310
327
  /**
311
328
  * D-UNINSTALL-DROPS-PLUGIN: record a selective uninstall in `<devflowDir>/manifest.json`.
312
329
  *
@@ -339,9 +356,35 @@ export async function removeManifestPlugins(devflowDir, names) {
339
356
  catch (error) {
340
357
  return { ok: false, error: error instanceof Error ? error.message : String(error) };
341
358
  }
342
- const doomed = new Set(names);
359
+ const isDoomed = doomedEntryTest(names);
343
360
  const before = raw.plugins;
344
- return { ok: true, removed: before.filter((entry) => typeof entry === 'string' && doomed.has(entry)) };
361
+ return { ok: true, removed: before.filter((entry) => typeof entry === 'string' && isDoomed(entry)) };
362
+ }
363
+ /**
364
+ * The plugin names `<devflowDir>/manifest.json` records as installed, or null when there is no usable record.
365
+ * Read only: unlike {@link readManifest} it never heals, never takes the manifest lock and writes nothing, so
366
+ * the file stays byte-identical. That is the property a selective uninstall needs. Its caller works from the
367
+ * record as `removeManifestPlugins` does, and a heal would rewrite a manifest the uninstall did not mean to
368
+ * touch (and write through a link in a legacy local install).
369
+ *
370
+ * A record is a manifest {@link readManifest} would accept: valid JSON that holds every required field.
371
+ * Anything else is null, never an empty list, so a caller can tell "nothing recorded" from "nothing
372
+ * installed". Entries that are not strings are skipped.
373
+ */
374
+ export async function readManifestPlugins(devflowDir) {
375
+ let raw;
376
+ try {
377
+ raw = JSON.parse(await fs.readFile(path.join(devflowDir, 'manifest.json'), 'utf-8'));
378
+ }
379
+ catch {
380
+ return null;
381
+ }
382
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
383
+ return null;
384
+ const record = raw;
385
+ if (!hasRequiredManifestFields(record))
386
+ return null;
387
+ return record.plugins.filter((entry) => typeof entry === 'string');
345
388
  }
346
389
  /**
347
390
  * Merge new plugins into existing plugin list (union, no duplicates).