devflow-kit 2.3.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 (169) hide show
  1. package/CHANGELOG.md +177 -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 +144 -0
  6. package/dist/cli/commands/compliance-prompts.js +7 -11
  7. package/dist/cli/commands/compliance.js +23 -1
  8. package/dist/cli/commands/init-seed.js +69 -30
  9. package/dist/cli/commands/init.js +551 -77
  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 +38 -0
  16. package/dist/cli/commands/proxy.js +91 -31
  17. package/dist/cli/commands/security.js +76 -16
  18. package/dist/cli/commands/skills.js +53 -7
  19. package/dist/cli/commands/tracker-prompts.js +145 -0
  20. package/dist/cli/commands/tracker.js +405 -0
  21. package/dist/cli/commands/uninstall.js +233 -77
  22. package/dist/cli.js +2 -0
  23. package/dist/commands/bug-analysis.md +22 -4
  24. package/dist/commands/code-review.md +44 -15
  25. package/dist/commands/debug.md +20 -6
  26. package/dist/commands/dynamic-build.md +289 -67
  27. package/dist/commands/dynamic-plan.md +60 -21
  28. package/dist/commands/dynamic-profile.md +1 -1
  29. package/dist/commands/dynamic-tickets.md +58 -8
  30. package/dist/commands/explore.md +2 -2
  31. package/dist/commands/implement.md +241 -53
  32. package/dist/commands/plan.md +88 -17
  33. package/dist/commands/release.md +64 -17
  34. package/dist/commands/resolve.md +138 -58
  35. package/dist/commands/self-review.md +2 -2
  36. package/dist/core/agent-models.js +55 -12
  37. package/dist/core/assets.js +58 -2
  38. package/dist/core/evidence-policy.js +147 -0
  39. package/dist/core/feature-config.js +130 -64
  40. package/dist/core/feature-switch.js +112 -0
  41. package/dist/core/flags.js +149 -8
  42. package/dist/core/manifest.js +33 -7
  43. package/dist/core/mds-variants.js +861 -0
  44. package/dist/core/model-discovery.js +12 -1
  45. package/dist/core/plugins.js +357 -9
  46. package/dist/core/project-paths.js +1 -1
  47. package/dist/core/proxy-log.js +8 -4
  48. package/dist/core/proxy-state.js +100 -43
  49. package/dist/core/reference-sweep.js +136 -0
  50. package/dist/core/tracker.js +407 -0
  51. package/dist/skills/git/references/decision-markers.md +19 -0
  52. package/dist/skills/git/references/learn-conventions.md +56 -0
  53. package/dist/skills/git/references/pr/check-ci-status.md +14 -0
  54. package/dist/skills/git/references/pr/check-merge-readiness.md +28 -0
  55. package/dist/skills/git/references/pr/ensure-pr-ready.md +24 -0
  56. package/dist/skills/git/references/pr/fetch-review-threads.md +22 -0
  57. package/dist/skills/git/references/pr/post-resolution-summary.md +40 -0
  58. package/dist/skills/git/references/pr/post-review-summary.md +42 -0
  59. package/dist/skills/git/references/pr/resolve-review-threads.md +35 -0
  60. package/dist/skills/git/references/pr/update-pr-evidence.md +14 -0
  61. package/dist/skills/git/references/pr/validate-branch.md +18 -0
  62. package/dist/skills/git/references/publication-gate.md +13 -0
  63. package/dist/skills/git/references/tracker/_mcp.md +153 -0
  64. package/dist/skills/git/references/tracker/github/associate-release.md +18 -0
  65. package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +40 -0
  66. package/dist/skills/git/references/tracker/github/create-release.md +11 -0
  67. package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +16 -0
  68. package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +69 -0
  69. package/dist/skills/git/references/tracker/github/fetch-issue.md +32 -0
  70. package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +17 -0
  71. package/dist/skills/git/references/tracker/github/gather-release-evidence.md +19 -0
  72. package/dist/skills/git/references/tracker/github/manage-debt.md +101 -0
  73. package/dist/skills/git/references/tracker/github/post-wave-report.md +28 -0
  74. package/dist/skills/git/references/tracker/github/setup-task.md +26 -0
  75. package/dist/skills/git/references/tracker/jira/associate-release.md +18 -0
  76. package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +49 -0
  77. package/dist/skills/git/references/tracker/jira/create-release.md +17 -0
  78. package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +22 -0
  79. package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +53 -0
  80. package/dist/skills/git/references/tracker/jira/fetch-issue.md +14 -0
  81. package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +15 -0
  82. package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +18 -0
  83. package/dist/skills/git/references/tracker/jira/manage-debt.md +37 -0
  84. package/dist/skills/git/references/tracker/jira/post-wave-report.md +33 -0
  85. package/dist/skills/git/references/tracker/jira/setup-task.md +31 -0
  86. package/dist/skills/git/references/tracker/linear/associate-release.md +18 -0
  87. package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +53 -0
  88. package/dist/skills/git/references/tracker/linear/create-release.md +17 -0
  89. package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +22 -0
  90. package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +53 -0
  91. package/dist/skills/git/references/tracker/linear/fetch-issue.md +14 -0
  92. package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +15 -0
  93. package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +18 -0
  94. package/dist/skills/git/references/tracker/linear/manage-debt.md +37 -0
  95. package/dist/skills/git/references/tracker/linear/post-wave-report.md +33 -0
  96. package/dist/skills/git/references/tracker/linear/setup-task.md +32 -0
  97. package/dist/skills/git/references/trust-rule.md +7 -0
  98. package/dist/targets/claude-code/installer.js +1213 -31
  99. package/dist/targets/claude-code/legacy.js +5 -0
  100. package/dist/targets/claude-code/post-install.js +303 -104
  101. package/dist/targets/claude-code/tracker-install.js +161 -0
  102. package/package.json +4 -3
  103. package/src/assets/agents/code.md +42 -4
  104. package/src/assets/agents/design.md +1 -1
  105. package/src/assets/agents/git.mds +827 -0
  106. package/src/assets/agents/knowledge.md +1 -1
  107. package/src/assets/agents/learning.md +11 -0
  108. package/src/assets/agents/synthesize.md +1 -1
  109. package/src/assets/agents/test.md +16 -5
  110. package/src/assets/agents/tracker.md +467 -0
  111. package/src/assets/agents/validate.md +7 -5
  112. package/src/assets/commands/_partials/_engine.mds +11 -9
  113. package/src/assets/commands/_partials/_evidence_policy.mds +30 -0
  114. package/src/assets/commands/_partials/_knowledge.mds +2 -2
  115. package/src/assets/commands/_partials/_plan_contract.mds +22 -7
  116. package/src/assets/commands/_partials/_preamble.mds +1 -1
  117. package/src/assets/commands/_partials/_publication.mds +3 -1
  118. package/src/assets/commands/_partials/_ticket_template.mds +3 -2
  119. package/src/assets/commands/_partials/_tracker.mds +18 -0
  120. package/src/assets/commands/_partials/_wave.mds +16 -10
  121. package/src/assets/commands/bug-analysis.mds +15 -5
  122. package/src/assets/commands/code-review.mds +34 -14
  123. package/src/assets/commands/debug.mds +11 -4
  124. package/src/assets/commands/dynamic-build.mds +227 -41
  125. package/src/assets/commands/dynamic-plan.mds +35 -13
  126. package/src/assets/commands/dynamic-tickets.mds +47 -5
  127. package/src/assets/commands/implement.mds +206 -52
  128. package/src/assets/commands/plan.mds +70 -17
  129. package/src/assets/commands/release.md +64 -17
  130. package/src/assets/commands/resolve.mds +126 -56
  131. package/src/assets/mds/git/_pr.mds +331 -0
  132. package/src/assets/mds/git/_references.mds +135 -0
  133. package/src/assets/mds/tracker/_common.mds +156 -0
  134. package/src/assets/mds/tracker/_github.mds +472 -0
  135. package/src/assets/mds/tracker/_jira.mds +407 -0
  136. package/src/assets/mds/tracker/_linear.mds +449 -0
  137. package/src/assets/mds/tracker/_mcp.mds +299 -0
  138. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +5 -8
  139. package/src/assets/scripts/hooks/background-memory-update +14 -9
  140. package/src/assets/scripts/hooks/capture-prompt +6 -2
  141. package/src/assets/scripts/hooks/capture-question +6 -2
  142. package/src/assets/scripts/hooks/capture-turn +6 -2
  143. package/src/assets/scripts/hooks/ensure-devflow-init +1 -1
  144. package/src/assets/scripts/hooks/ensure-proxy +64 -5
  145. package/src/assets/scripts/hooks/ensure-root-gitignore +161 -60
  146. package/src/assets/scripts/hooks/hook-log-init +3 -1
  147. package/src/assets/scripts/hooks/json-helper.cjs +223 -5
  148. package/src/assets/scripts/hooks/lib/project-paths.cjs +1 -1
  149. package/src/assets/scripts/hooks/memory-worker +15 -8
  150. package/src/assets/scripts/hooks/pre-compact-memory +12 -8
  151. package/src/assets/scripts/hooks/preamble +1 -4
  152. package/src/assets/scripts/hooks/queue-append +68 -24
  153. package/src/assets/scripts/hooks/session-start-context +355 -8
  154. package/src/assets/scripts/hooks/session-start-memory +12 -8
  155. package/src/assets/scripts/pr-evidence.cjs +1961 -0
  156. package/src/assets/scripts/redact-secrets.cjs +490 -62
  157. package/src/assets/scripts/release-trace.cjs +1143 -0
  158. package/src/assets/scripts/resolve-evidence-policy.cjs +1065 -0
  159. package/src/assets/scripts/verify-evidence.cjs +1822 -0
  160. package/src/assets/skills/compliance/SKILL.md +2 -0
  161. package/src/assets/skills/docs-framework/SKILL.md +5 -3
  162. package/src/assets/skills/git/SKILL.md +8 -82
  163. package/src/assets/skills/git/references/github-api.md +179 -141
  164. package/src/assets/skills/git/references/patterns.md +11 -12
  165. package/src/assets/skills/review-methodology/SKILL.md +1 -1
  166. package/src/assets/skills/review-methodology/references/patterns.md +6 -61
  167. package/src/assets/skills/review-methodology/references/violations.md +14 -22
  168. package/src/targets/claude-code/templates/settings.json +0 -4
  169. package/src/assets/agents/git.md +0 -938
@@ -373,7 +373,12 @@ export const agentsCommand = new Command('agents')
373
373
  }
374
374
  const mapping = mappingResult.value;
375
375
  const proxyEnabled = await isProxyEnabled(devflowDir);
376
- const shippedDefaults = await loadShippedDefaults();
376
+ // An agent with no shipped default renders a blank DEFAULT column and can
377
+ // never be reverted off an external model; surface the gap rather than
378
+ // letting the table imply the agent simply ships without one.
379
+ const shippedDefaults = await loadShippedDefaults(undefined, {
380
+ onWarning: (msg) => p.log.warn(msg),
381
+ });
377
382
  // ── --list ──────────────────────────────────────────────────────────────
378
383
  if (options.list) {
379
384
  const agentNames = getAllAgentNames().sort();
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Attribution prompt helpers for devflow init.
3
+ *
4
+ * CLI-layer module (ADR-013): prompt-rendering logic lives in src/cli/commands/,
5
+ * core business logic stays in src/core/.
6
+ *
7
+ * Applies PF-029: the gate is an exported pure predicate with an explicit isTTY guard,
8
+ * so --recommended (flag, no prompt) and the non-TTY fallback keep their promptless
9
+ * contracts and the reachability rule is unit-testable without a terminal.
10
+ * Applies PF-014: runAttributionStep never calls process.exit() or throws — callers
11
+ * own the cancel idiom (p.cancel + process.exit(0)), keeping try/finally cleanup safe.
12
+ *
13
+ * D27: suppress-attribution flag — gates Claude Code's AI-attribution injection.
14
+ * The question is ADVANCED-ONLY; Recommended never asks. See shouldRunAttributionStep.
15
+ *
16
+ * Shared DI seam (PromptOutcome, WizardPromptIO, clackNote, clackSelect) lives in
17
+ * prompt-io.ts — one definition, both wizard modules import from there.
18
+ */
19
+ import { clackNote, clackSelect } from './prompt-io.js';
20
+ // ── Gate predicate ─────────────────────────────────────────────────────────────
21
+ /**
22
+ * Determines whether the attribution wizard step should run for a given init invocation.
23
+ *
24
+ * D27 — ADVANCED-ONLY. The attribution question is reachable from the Advanced path
25
+ * and nowhere else. This DIVERGES DELIBERATELY from shouldRunComplianceStep, which also
26
+ * runs on interactive Recommended: attribution rewrites the user's git history metadata,
27
+ * so Recommended stays a zero-question path and silently applies the seeded value
28
+ * (fresh install: off). Do not "restore symmetry" with the compliance gate.
29
+ *
30
+ * Gate table:
31
+ *
32
+ * --advanced flag / re-init (banner path) / prompt → Advanced → yes
33
+ * Interactive mode-prompt → Recommended → NO (D27 divergence)
34
+ * --recommended flag → no
35
+ * !isTTY (any mode) → no
36
+ *
37
+ * Gating on the mode name is sound here because 'advanced' is only ever resolved on an
38
+ * interactive path (the Advanced branch exit-1s on non-TTY), and the explicit isTTY guard
39
+ * keeps the promptless contracts of --recommended and the non-TTY fallback pinned
40
+ * regardless (PF-029).
41
+ *
42
+ * There is no CLI override for attribution — it is toggled post-install via
43
+ * `devflow flags --enable/--disable suppress-attribution`.
44
+ *
45
+ * Pure predicate — no side effects, fully testable without a TTY.
46
+ */
47
+ export function shouldRunAttributionStep(input) {
48
+ if (!input.isTTY)
49
+ return false;
50
+ return input.mode === 'advanced';
51
+ }
52
+ /**
53
+ * Build the real (clack) AttributionPromptIO adapter.
54
+ * Delegates to the shared clackNote / clackSelect adapters (prompt-io.ts).
55
+ */
56
+ export function buildClackAttributionPrompts() {
57
+ return {
58
+ note: clackNote,
59
+ select: (opts) => clackSelect(opts),
60
+ };
61
+ }
62
+ // ── Seed / apply helpers ───────────────────────────────────────────────────────
63
+ /**
64
+ * Derive the boolean seed for the attribution prompt from the current FlagsRecord.
65
+ *
66
+ * Returns true only when `suppress-attribution` is explicitly set to the boolean
67
+ * true — undefined, null, and false all map to false, giving `p.select` a real
68
+ * boolean rather than an unchecked cast (PF-018: non-vacuous path).
69
+ *
70
+ * Pure function — no side effects, fully testable without a TTY.
71
+ */
72
+ export function attributionSeedFrom(flags) {
73
+ return flags['suppress-attribution'] === true;
74
+ }
75
+ /**
76
+ * Apply the resolved wizard answer back onto the FlagsRecord.
77
+ *
78
+ * Returns a new record with `suppress-attribution` updated to outcome.suppress.
79
+ * Never mutates the input (immutability principle). The caller replaces its local
80
+ * enabledFlags binding with the return value.
81
+ *
82
+ * D27: this is the single merge site for the wizard answer; init.ts must not
83
+ * duplicate the spread inline.
84
+ *
85
+ * Pure function — no side effects, fully testable without a TTY.
86
+ */
87
+ export function applyAttributionAnswer(flags, outcome) {
88
+ return { ...flags, 'suppress-attribution': outcome.suppress };
89
+ }
90
+ /**
91
+ * Run the attribution wizard step.
92
+ *
93
+ * Flow:
94
+ * 1. Note — "Current setting: …" header with context about what the flag does.
95
+ * 2. Enable select — labeled Yes / No with hints (seeded from prior state);
96
+ * p.select is immune to Enter-through muscle memory while still preserving
97
+ * the seeded value (ambient-prompt style — per PF-029).
98
+ *
99
+ * Returns:
100
+ * {kind:'resolved', suppress, messages} — step completed; `suppress` is the chosen
101
+ * boolean; `messages` are emitted by the caller.
102
+ * {kind:'cancelled'} — user pressed Escape; caller runs p.cancel + process.exit(0).
103
+ *
104
+ * Invariants (PF-014):
105
+ * - Never calls process.exit(), never throws.
106
+ * - All I/O is routed through the `prompts` parameter (injectable for tests).
107
+ */
108
+ export async function runAttributionStep(opts) {
109
+ const { seed, prompts } = opts;
110
+ const currentStr = seed ? 'suppressed' : 'shown (default)';
111
+ // security-02: name the destructive branch (Yes) BEFORE the user consents.
112
+ // ADR-024 corollary (b): turning the flag ON replaces any existing attribution
113
+ // value, including a custom one — this is deliberate. Only the exact
114
+ // devflow-managed shape {"commit":"","pr":""} is removed on disable.
115
+ // security-04: surface the org AI-disclosure-policy dimension as a note (not a gate).
116
+ prompts.note(`Current setting: ${currentStr}\n\n` +
117
+ 'Choosing Yes REPLACES any existing \`attribution\` value in settings.json,\n' +
118
+ 'including a custom one, with {"commit":"","pr":""}.\n' +
119
+ 'Choosing No leaves a custom value untouched — only the exact\n' +
120
+ 'devflow-managed block is ever removed on disable.\n' +
121
+ 'Some organisations require machine-readable AI-authorship disclosure\n' +
122
+ '— check your policy before enabling.\n\n' +
123
+ 'Toggle any time with: devflow flags --enable suppress-attribution', 'AI Attribution');
124
+ const enableOutcome = await prompts.select({
125
+ message: 'Suppress AI attribution in commits and PRs?',
126
+ options: [
127
+ { value: true, label: 'Yes', hint: 'hides Claude attribution labels in git history' },
128
+ { value: false, label: 'No', hint: 'keeps Claude attribution labels (default)' },
129
+ ],
130
+ initialValue: seed,
131
+ });
132
+ if (enableOutcome.kind === 'cancel')
133
+ return { kind: 'cancelled' };
134
+ const suppress = enableOutcome.value;
135
+ const text = suppress
136
+ ? 'Attribution: suppressed — disable with devflow flags --disable suppress-attribution'
137
+ : 'Attribution: shown (default)';
138
+ return {
139
+ kind: 'resolved',
140
+ suppress,
141
+ messages: [{ level: suppress ? 'success' : 'info', text }],
142
+ };
143
+ }
144
+ //# sourceMappingURL=attribution-prompts.js.map
@@ -9,9 +9,13 @@
9
9
  * no prompt) and the non-TTY fallback preserve their promptless contracts.
10
10
  * Applies PF-014: runComplianceStep never calls process.exit() or throws — callers
11
11
  * own the cancel idiom (p.cancel + process.exit(0)), keeping try/finally cleanup safe.
12
+ *
13
+ * Shared DI seam (PromptOutcome, WizardPromptIO, clackNote, clackSelect) lives in
14
+ * prompt-io.ts — one definition, both wizard modules import from there.
12
15
  */
13
16
  import * as p from '@clack/prompts';
14
17
  import { COMPLIANCE_FRAMEWORKS } from '../../core/compliance.js';
18
+ import { clackNote, clackSelect } from './prompt-io.js';
15
19
  // ── Shared prompt content ──────────────────────────────────────────────────────
16
20
  /** Message shown on the compliance framework multiselect prompt. */
17
21
  export const FRAMEWORK_SELECT_MESSAGE = 'Select compliance frameworks (Enter to skip — generic controls only)';
@@ -75,21 +79,13 @@ export function shouldRunComplianceStep(input) {
75
79
  }
76
80
  /**
77
81
  * Build the real (clack) CompliancePromptIO adapter.
82
+ * Delegates the shared note + select to the shared adapters (prompt-io.ts).
78
83
  * Translates clack's cancel symbol into the PromptOutcome discriminated union.
79
84
  */
80
85
  export function buildClackCompliancePrompts() {
81
86
  return {
82
- note: (message, title) => p.note(message, title),
83
- select: async (opts) => {
84
- const result = await p.select({
85
- message: opts.message,
86
- options: opts.options,
87
- initialValue: opts.initialValue,
88
- });
89
- if (p.isCancel(result))
90
- return { kind: 'cancel' };
91
- return { kind: 'value', value: result };
92
- },
87
+ note: clackNote,
88
+ select: (opts) => clackSelect(opts),
93
89
  multiselect: async (opts) => {
94
90
  const result = await p.multiselect({
95
91
  message: opts.message,
@@ -6,6 +6,9 @@
6
6
  * Applies ADR-001: compliance is manifest-group (like proxy), not config.json-gated.
7
7
  * Avoids PF-009: per-artifact failures are warn-not-throw.
8
8
  * Avoids PF-015: enable/disable each converge BOTH artifacts unconditionally.
9
+ * The evidence-policy lines (--status, and the --enable/--set suggestion) come
10
+ * from src/core/evidence-policy.ts, the seam onto the package's own resolver;
11
+ * the CLI prints .devflow/policy.json and never writes it (applies ADR-024).
9
12
  */
10
13
  import { Command } from 'commander';
11
14
  import { promises as fs } from 'fs';
@@ -15,6 +18,7 @@ import color from 'picocolors';
15
18
  import { ALWAYS_PRESENT_REFS, COMPLIANCE_FRAMEWORKS, normalizeFrameworks, parseFrameworkList, } from '../../core/compliance.js';
16
19
  import { frameworkChoices, FRAMEWORK_SELECT_MESSAGE } from './compliance-prompts.js';
17
20
  import { COMPLIANCE_SKILL_TOKENS } from '../../core/compliance-compose.js';
21
+ import { evidencePolicyStatusLine, evidencePolicySuggestion, formatEvidencePolicyUnavailable, loadEvidencePolicyModule, } from '../../core/evidence-policy.js';
18
22
  import { readManifest, writeManifest } from '../../core/manifest.js';
19
23
  import { convergeFromManifest } from '../../targets/claude-code/compliance-install.js';
20
24
  import { validateRuleShadow, validateSkillShadow } from '../../targets/claude-code/installer.js';
@@ -177,7 +181,7 @@ export const complianceCommand = new Command('compliance')
177
181
  .description('Enable, disable, or configure the compliance feature')
178
182
  .option('--enable', 'Enable compliance (restores previously selected frameworks)')
179
183
  .option('--disable', 'Disable compliance (artifacts removed; frameworks remembered for re-enable)')
180
- .option('--status', 'Show compliance state: manifest, installed artifacts, and shadow presence')
184
+ .option('--status', 'Show compliance state: manifest, installed artifacts, shadow presence, and the evidence policy for the current repository')
181
185
  .option('--set <list>', 'Set active frameworks (comma-separated IDs); enables compliance. Use --set "" for zero frameworks (generic controls only)')
182
186
  .action(async (options) => {
183
187
  const claudeDir = getClaudeDirectory();
@@ -247,6 +251,11 @@ export const complianceCommand = new Command('compliance')
247
251
  ? color.yellow(' (withheld — rules disabled)')
248
252
  : '') +
249
253
  (isRuleShadowed ? color.green(' [shadowed]') : ''),
254
+ '',
255
+ // The repository in cwd, resolved by the package's own resolver with the
256
+ // compliance state already read above (D-POLICY-CJS-SEAM). Bounded: at most
257
+ // two `gh` calls, each with a timeout, so offline degrades to a flagged line.
258
+ evidencePolicyStatusLine(loadEvidencePolicyModule(), { dir: process.cwd(), compliance: current }),
250
259
  ];
251
260
  if (driftInstalled.length > 0 || validMissing.length > 0 || invalidIds.length > 0) {
252
261
  lines.push('');
@@ -333,5 +342,18 @@ export const complianceCommand = new Command('compliance')
333
342
  p.log.info(color.dim('Note: compliance rule withheld (rules disabled) — ' +
334
343
  'run `devflow rules --enable` to install the stamped rule'));
335
344
  }
345
+ // Suggest the team policy file compliance now implies. Printed, never written:
346
+ // .devflow/policy.json is team-owned (D-POLICY-NO-WRITE, applies ADR-024).
347
+ if (resolved.nextState.enabled) {
348
+ const policyModule = loadEvidencePolicyModule();
349
+ if (!policyModule.ok) {
350
+ p.log.warn(formatEvidencePolicyUnavailable(policyModule.error));
351
+ }
352
+ else {
353
+ const suggestion = evidencePolicySuggestion(resolved.nextState, policyModule.value);
354
+ if (suggestion !== null)
355
+ p.note(suggestion, 'Evidence policy');
356
+ }
357
+ }
336
358
  });
337
359
  //# sourceMappingURL=compliance.js.map
@@ -3,7 +3,6 @@
3
3
  *
4
4
  * Computes the initial state (seed) for init prompts from:
5
5
  * - The existing manifest (from a prior install)
6
- * - The project feature config (.devflow/config.json)
7
6
  * - The current settings.json snapshot (for view-mode resolution)
8
7
  * - The plugin registry
9
8
  *
@@ -13,8 +12,9 @@
13
12
  * beside init.ts in src/cli/commands/ rather than in src/core/ (which holds
14
13
  * agent-neutral, target-agnostic utilities).
15
14
  */
16
- import { resolveExistingViewMode, FLAG_REGISTRY, defaultValueOf, readViewMode, } from '../../core/flags.js';
15
+ import { resolveExistingViewMode, settingHoldsManagedShape, FLAG_REGISTRY, defaultValueOf, readViewMode, } from '../../core/flags.js';
17
16
  import { partitionSelectablePlugins } from '../../core/plugins.js';
17
+ import { DEFAULT_TRACKER_PROVIDER } from '../../core/tracker.js';
18
18
  /** Registry defaults — all features enabled except proxy (advanced-only, off by default). */
19
19
  export const FEATURE_DEFAULTS = {
20
20
  ambient: true,
@@ -25,24 +25,21 @@ export const FEATURE_DEFAULTS = {
25
25
  rules: true,
26
26
  proxy: false,
27
27
  compliance: { enabled: false, frameworks: [] },
28
+ tracker: { provider: DEFAULT_TRACKER_PROVIDER },
28
29
  };
29
30
  // ── Helpers ───────────────────────────────────────────────────────────────────
30
31
  /**
31
- * Resolve feature booleans for the init seed.
32
+ * Resolve feature booleans for the init seed: every feature comes from
33
+ * manifest.features, with registry defaults when the manifest is absent.
32
34
  *
33
- * - memory / learning / knowledge come from projectConfig WHENEVER present,
34
- * independent of whether a manifest exists (covers config-present /
35
- * manifest-missing cases such as a fresh project with a prior learning run).
36
- * - ambient / hud / rules come from manifest.features; registry defaults
37
- * (all true) are used when the manifest is absent.
38
- *
39
- * Applies ADR-001: .devflow/config.json is the source of truth for
40
- * memory/learning/knowledge; manifest reflects the last install choices for
41
- * the remaining toggles.
35
+ * D-FEATURES-MACHINE-WIDE (src/core/feature-switch.ts): memory, learning and
36
+ * knowledge are machine-wide, recorded in the manifest alone, so the seed takes
37
+ * no per-repo input at all. Seeding from whatever repo init happens to run in
38
+ * would flip the switch for EVERY repo as a side effect — a stale per-repo
39
+ * `true` would silently re-enable a feature the user turned off. ADR-014's
40
+ * state-aware re-init preserves the prior machine-wide choice, the manifest's.
42
41
  */
43
- export function resolveSeedFeatures(manifest, projectConfig) {
44
- // ambient/hud/rules/proxy/compliance: manifest is the source; fall back to registry defaults.
45
- // proxy and compliance follow the manifest group (like ambient) per ADR-001 — NOT config.json-gated.
42
+ export function resolveSeedFeatures(manifest) {
46
43
  const ambient = manifest?.features.ambient ?? FEATURE_DEFAULTS.ambient;
47
44
  const hud = manifest?.features.hud ?? FEATURE_DEFAULTS.hud;
48
45
  const rules = manifest?.features.rules ?? FEATURE_DEFAULTS.rules;
@@ -52,15 +49,15 @@ export function resolveSeedFeatures(manifest, projectConfig) {
52
49
  // the module-level default by reference — downstream mutation would corrupt it process-wide.
53
50
  const rawCompliance = manifest?.features.compliance ?? FEATURE_DEFAULTS.compliance;
54
51
  const compliance = { ...rawCompliance, frameworks: [...rawCompliance.frameworks] };
55
- // memory/learning/knowledge: projectConfig wins whenever present (ADR-001).
56
- // Helper eliminates the repeated projectConfig !== null ternary pattern.
57
- const fromConfig = (key) => projectConfig !== null
58
- ? projectConfig[key]
59
- : (manifest?.features[key] ?? FEATURE_DEFAULTS[key]);
60
- const memory = fromConfig('memory');
61
- const knowledge = fromConfig('knowledge');
62
- const learning = fromConfig('learning');
63
- return { ambient, memory, hud, knowledge, learning, rules, proxy, compliance };
52
+ // Same defensive spread, same reason: `?? FEATURE_DEFAULTS.tracker` alone would
53
+ // return the module-level default BY REFERENCE and downstream mutation would
54
+ // corrupt it process-wide.
55
+ const rawTracker = manifest?.features.tracker ?? FEATURE_DEFAULTS.tracker;
56
+ const tracker = { ...rawTracker };
57
+ const memory = manifest?.features.memory ?? FEATURE_DEFAULTS.memory;
58
+ const knowledge = manifest?.features.knowledge ?? FEATURE_DEFAULTS.knowledge;
59
+ const learning = manifest?.features.learning ?? FEATURE_DEFAULTS.learning;
60
+ return { ambient, memory, hud, knowledge, learning, rules, proxy, compliance, tracker };
64
61
  }
65
62
  /**
66
63
  * Resolve the flag record for the init seed.
@@ -161,17 +158,41 @@ export function resolveSeedPlugins(manifestPlugins, knownPlugins, allPlugins) {
161
158
  return { workflowPlugins, languagePlugins };
162
159
  }
163
160
  /**
164
- * Compose the full init seed from manifest, project config, settings, and registry.
161
+ * Extract the attribution suppression state from a settings JSON string.
162
+ *
163
+ * Returns `true` when the exact devflow-managed attribution shape is present —
164
+ * delegating to `settingHoldsManagedShape('suppress-attribution')`, which is the
165
+ * single place that decides "on-disk value equals the managed shape" (consistency-01).
166
+ * Returns `undefined` when the block is absent, holds a custom value, or JSON is
167
+ * malformed — callers fall through to the manifest FlagsRecord entry or the registry
168
+ * default.
169
+ *
170
+ * Return type is `true | undefined` (typescript-06): settings.json can signal ON or
171
+ * nothing — never explicitly OFF. That asymmetry is load-bearing for seeding priority:
172
+ * `undefined` lets the manifest entry or the registry default win, while `true` is an
173
+ * unambiguous "this key was devflow-managed and active".
174
+ *
175
+ * Mirrors resolveExistingViewMode: returns undefined on malformed JSON, absent key,
176
+ * or any non-devflow attribution value.
177
+ *
178
+ * Pure function — no I/O, no side effects.
179
+ */
180
+ export function resolveExistingAttributionSuppression(settingsJson) {
181
+ return settingHoldsManagedShape(settingsJson, 'suppress-attribution') ? true : undefined;
182
+ }
183
+ /**
184
+ * Compose the full init seed from manifest, settings, and registry.
165
185
  *
166
186
  * view-mode priority: existing settings.json (non-default) → manifest → 'default'.
167
- * The resolved view mode is encoded into flags['view-mode'] so all flag state lives
168
- * in one FlagsRecord (applying PF-015: fold before strip — the fold happens here).
187
+ * suppress-attribution priority: settings.json exact devflow shape → manifest → false.
188
+ * All flag overrides are encoded into flags so all flag state lives in one FlagsRecord
189
+ * (applying PF-015: fold before strip — the fold happens here).
169
190
  *
170
191
  * This is the single composition point; callers (init.ts hoist block) call this
171
192
  * once and pass `seed` down to prompt wiring.
172
193
  */
173
- export function resolveInitSeed(seedManifest, seedConfig, settingsSnapshot, plugins) {
174
- const features = resolveSeedFeatures(seedManifest, seedConfig);
194
+ export function resolveInitSeed(seedManifest, settingsSnapshot, plugins) {
195
+ const features = resolveSeedFeatures(seedManifest);
175
196
  // features.flags is FlagsRecord; null for fresh install (no manifest).
176
197
  // seedManifest?.features.flags may be absent at runtime on very old manifests not yet
177
198
  // healed — ?? null collapses to fresh-install behavior (all flags at registry defaults).
@@ -194,9 +215,26 @@ export function resolveInitSeed(seedManifest, seedConfig, settingsSnapshot, plug
194
215
  else {
195
216
  resolvedViewMode = 'default'; // fall back to neutral
196
217
  }
218
+ // Encode resolved attribution suppression (D27) into flags['suppress-attribution'].
219
+ // Priority: settings.json exact devflow shape → manifest FlagsRecord entry → false.
220
+ // resolveExistingAttributionSuppression returns true only for the exact managed shape
221
+ // {"commit":"","pr":""}; custom values return undefined so the manifest entry wins.
222
+ const existingAttr = resolveExistingAttributionSuppression(settingsSnapshot);
223
+ const resolvedSuppressAttr = existingAttr !== undefined
224
+ ? existingAttr // settings.json exact shape wins
225
+ : flags['suppress-attribution'] === true; // manifest or registry default
197
226
  // Return a fresh spread rather than mutating flags in place — keeps this function pure
198
227
  // per the module docblock and avoids aliasing if the caller inspects seed.flags.
199
- return { features, flags: { ...flags, 'view-mode': resolvedViewMode }, workflowPlugins, languagePlugins };
228
+ return {
229
+ features,
230
+ flags: {
231
+ ...flags,
232
+ 'view-mode': resolvedViewMode,
233
+ 'suppress-attribution': resolvedSuppressAttr,
234
+ },
235
+ workflowPlugins,
236
+ languagePlugins,
237
+ };
200
238
  }
201
239
  /**
202
240
  * Resolve the three seed inputs under the --reset gate.
@@ -239,6 +277,7 @@ export function applyCliToggles(base, toggles) {
239
277
  rules: toggles.rules ?? base.rules,
240
278
  proxy: toggles.proxy ?? base.proxy,
241
279
  compliance: toggles.compliance ?? base.compliance,
280
+ tracker: toggles.tracker ?? base.tracker,
242
281
  };
243
282
  }
244
283
  //# sourceMappingURL=init-seed.js.map