devflow-kit 2.4.0 → 3.0.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 (213) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/README.md +111 -18
  3. package/dist/agents/git.md +822 -0
  4. package/dist/cli/commands/agents.js +6 -1
  5. package/dist/cli/commands/ambient.js +160 -145
  6. package/dist/cli/commands/attribution-prompts.js +1 -1
  7. package/dist/cli/commands/capture.js +29 -55
  8. package/dist/cli/commands/compliance-prompts.js +1 -1
  9. package/dist/cli/commands/compliance.js +48 -55
  10. package/dist/cli/commands/context.js +17 -32
  11. package/dist/cli/commands/debug.js +65 -26
  12. package/dist/cli/commands/flags.js +3 -3
  13. package/dist/cli/commands/hud.js +34 -10
  14. package/dist/cli/commands/init-seed.js +61 -27
  15. package/dist/cli/commands/init.js +649 -240
  16. package/dist/cli/commands/install-report.js +200 -0
  17. package/dist/cli/commands/knowledge/index.js +2 -2
  18. package/dist/cli/commands/knowledge/toggle.js +35 -37
  19. package/dist/cli/commands/learning.js +79 -57
  20. package/dist/cli/commands/legacy-hooks.js +11 -14
  21. package/dist/cli/commands/memory.js +134 -135
  22. package/dist/cli/commands/prompt-io.js +4 -4
  23. package/dist/cli/commands/proxy.js +23 -41
  24. package/dist/cli/commands/security.js +81 -29
  25. package/dist/cli/commands/skills.js +71 -7
  26. package/dist/cli/commands/tracker-prompts.js +145 -0
  27. package/dist/cli/commands/tracker.js +277 -0
  28. package/dist/cli/commands/uninstall.js +520 -169
  29. package/dist/cli.js +2 -0
  30. package/dist/commands/bug-analysis.md +58 -14
  31. package/dist/commands/code-review.md +110 -32
  32. package/dist/commands/debug.md +55 -11
  33. package/dist/commands/dynamic-build.md +344 -73
  34. package/dist/commands/dynamic-plan.md +77 -27
  35. package/dist/commands/dynamic-profile.md +25 -11
  36. package/dist/commands/dynamic-tickets.md +76 -15
  37. package/dist/commands/explore.md +37 -7
  38. package/dist/commands/implement.md +314 -62
  39. package/dist/commands/plan.md +146 -32
  40. package/dist/commands/release.md +64 -17
  41. package/dist/commands/research.md +34 -8
  42. package/dist/commands/resolve.md +196 -68
  43. package/dist/commands/self-review.md +45 -9
  44. package/dist/core/agent-models.js +55 -12
  45. package/dist/core/assets.js +58 -2
  46. package/dist/core/compliance-compose.js +27 -27
  47. package/dist/core/evidence-policy.js +363 -0
  48. package/dist/core/feature-config.js +200 -65
  49. package/dist/core/feature-switch.js +112 -0
  50. package/dist/core/flags.js +34 -6
  51. package/dist/core/fs-atomic.js +27 -0
  52. package/dist/core/hook-log-dirs.js +104 -0
  53. package/dist/core/learning-tuning-config.js +5 -3
  54. package/dist/core/ledger-root.js +102 -0
  55. package/dist/core/manifest.js +38 -10
  56. package/dist/core/mds-variants.js +798 -0
  57. package/dist/core/migrations.js +49 -23
  58. package/dist/core/model-discovery.js +12 -1
  59. package/dist/core/plugins.js +361 -12
  60. package/dist/core/project-paths.js +1 -18
  61. package/dist/core/proxy-log.js +8 -6
  62. package/dist/core/proxy-state.js +11 -8
  63. package/dist/core/reference-sweep.js +136 -0
  64. package/dist/core/same-location.js +25 -0
  65. package/dist/core/tracker.js +494 -0
  66. package/dist/hud/components/config-counts.js +15 -4
  67. package/dist/hud/components/learning-counts.js +14 -0
  68. package/dist/hud/config.js +2 -1
  69. package/dist/hud/cost-history.js +2 -4
  70. package/dist/hud/git.js +52 -7
  71. package/dist/hud/index.js +7 -9
  72. package/dist/skills/git/references/decision-markers.md +19 -0
  73. package/dist/skills/git/references/learn-conventions.md +56 -0
  74. package/dist/skills/git/references/pr/check-ci-status.md +14 -0
  75. package/dist/skills/git/references/pr/check-merge-readiness.md +28 -0
  76. package/dist/skills/git/references/pr/ensure-pr-ready.md +24 -0
  77. package/dist/skills/git/references/pr/fetch-review-threads.md +22 -0
  78. package/dist/skills/git/references/pr/post-resolution-summary.md +40 -0
  79. package/dist/skills/git/references/pr/post-review-summary.md +42 -0
  80. package/dist/skills/git/references/pr/resolve-review-threads.md +35 -0
  81. package/dist/skills/git/references/pr/update-pr-evidence.md +14 -0
  82. package/dist/skills/git/references/pr/validate-branch.md +18 -0
  83. package/dist/skills/git/references/publication-gate.md +13 -0
  84. package/dist/skills/git/references/tracker/_mcp.md +153 -0
  85. package/dist/skills/git/references/tracker/github/associate-release.md +18 -0
  86. package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +40 -0
  87. package/dist/skills/git/references/tracker/github/create-release.md +11 -0
  88. package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +16 -0
  89. package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +69 -0
  90. package/dist/skills/git/references/tracker/github/fetch-issue.md +32 -0
  91. package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +17 -0
  92. package/dist/skills/git/references/tracker/github/gather-release-evidence.md +19 -0
  93. package/dist/skills/git/references/tracker/github/manage-debt.md +101 -0
  94. package/dist/skills/git/references/tracker/github/post-wave-report.md +28 -0
  95. package/dist/skills/git/references/tracker/github/setup-task.md +26 -0
  96. package/dist/skills/git/references/tracker/jira/associate-release.md +18 -0
  97. package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +49 -0
  98. package/dist/skills/git/references/tracker/jira/create-release.md +17 -0
  99. package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +22 -0
  100. package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +53 -0
  101. package/dist/skills/git/references/tracker/jira/fetch-issue.md +14 -0
  102. package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +15 -0
  103. package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +18 -0
  104. package/dist/skills/git/references/tracker/jira/manage-debt.md +37 -0
  105. package/dist/skills/git/references/tracker/jira/post-wave-report.md +33 -0
  106. package/dist/skills/git/references/tracker/jira/setup-task.md +31 -0
  107. package/dist/skills/git/references/tracker/linear/associate-release.md +18 -0
  108. package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +53 -0
  109. package/dist/skills/git/references/tracker/linear/create-release.md +17 -0
  110. package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +22 -0
  111. package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +53 -0
  112. package/dist/skills/git/references/tracker/linear/fetch-issue.md +14 -0
  113. package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +15 -0
  114. package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +18 -0
  115. package/dist/skills/git/references/tracker/linear/manage-debt.md +37 -0
  116. package/dist/skills/git/references/tracker/linear/post-wave-report.md +33 -0
  117. package/dist/skills/git/references/tracker/linear/setup-task.md +32 -0
  118. package/dist/skills/git/references/trust-rule.md +7 -0
  119. package/dist/targets/claude-code/claude-paths.js +59 -57
  120. package/dist/targets/claude-code/compliance-install.js +49 -65
  121. package/dist/targets/claude-code/hooks.js +108 -3
  122. package/dist/targets/claude-code/installer.js +1187 -32
  123. package/dist/targets/claude-code/legacy.js +5 -0
  124. package/dist/targets/claude-code/post-install.js +366 -151
  125. package/dist/targets/claude-code/tracker-install.js +134 -0
  126. package/package.json +8 -6
  127. package/src/assets/agents/code.md +45 -6
  128. package/src/assets/agents/design.md +2 -1
  129. package/src/assets/agents/git.mds +825 -0
  130. package/src/assets/agents/knowledge.md +3 -3
  131. package/src/assets/agents/learning.md +11 -0
  132. package/src/assets/agents/review.md +3 -1
  133. package/src/assets/agents/synthesize.md +1 -1
  134. package/src/assets/agents/test.md +16 -5
  135. package/src/assets/agents/tracker.md +474 -0
  136. package/src/assets/agents/validate.md +7 -5
  137. package/src/assets/commands/_partials/_compliance.mds +19 -1
  138. package/src/assets/commands/_partials/_decisions.mds +15 -3
  139. package/src/assets/commands/_partials/_docs_root.mds +35 -0
  140. package/src/assets/commands/_partials/_engine.mds +13 -11
  141. package/src/assets/commands/_partials/_evidence_policy.mds +30 -0
  142. package/src/assets/commands/_partials/_factory.mds +1 -1
  143. package/src/assets/commands/_partials/_knowledge.mds +27 -9
  144. package/src/assets/commands/_partials/_plan_contract.mds +22 -7
  145. package/src/assets/commands/_partials/_preamble.mds +2 -2
  146. package/src/assets/commands/_partials/_publication.mds +8 -2
  147. package/src/assets/commands/_partials/_settings.mds +28 -0
  148. package/src/assets/commands/_partials/_ticket_template.mds +3 -2
  149. package/src/assets/commands/_partials/_tracker.mds +18 -0
  150. package/src/assets/commands/_partials/_wave.mds +16 -10
  151. package/src/assets/commands/bug-analysis.mds +31 -19
  152. package/src/assets/commands/code-review.mds +67 -41
  153. package/src/assets/commands/debug.mds +13 -7
  154. package/src/assets/commands/dynamic-build.mds +274 -66
  155. package/src/assets/commands/dynamic-plan.mds +50 -23
  156. package/src/assets/commands/dynamic-profile.mds +24 -11
  157. package/src/assets/commands/dynamic-tickets.mds +63 -16
  158. package/src/assets/commands/explore.mds +4 -5
  159. package/src/assets/commands/implement.mds +234 -67
  160. package/src/assets/commands/plan.mds +91 -33
  161. package/src/assets/commands/release.md +64 -17
  162. package/src/assets/commands/research.mds +11 -9
  163. package/src/assets/commands/resolve.mds +150 -78
  164. package/src/assets/commands/self-review.mds +24 -25
  165. package/src/assets/mds/git/_pr.mds +331 -0
  166. package/src/assets/mds/git/_references.mds +135 -0
  167. package/src/assets/mds/tracker/_common.mds +156 -0
  168. package/src/assets/mds/tracker/_github.mds +472 -0
  169. package/src/assets/mds/tracker/_jira.mds +407 -0
  170. package/src/assets/mds/tracker/_linear.mds +449 -0
  171. package/src/assets/mds/tracker/_mcp.mds +305 -0
  172. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +5 -8
  173. package/src/assets/scripts/hooks/background-memory-update +40 -19
  174. package/src/assets/scripts/hooks/capture-prompt +18 -8
  175. package/src/assets/scripts/hooks/capture-question +18 -8
  176. package/src/assets/scripts/hooks/capture-turn +27 -13
  177. package/src/assets/scripts/hooks/debug-trace +11 -6
  178. package/src/assets/scripts/hooks/ensure-devflow-init +33 -6
  179. package/src/assets/scripts/hooks/ensure-proxy +9 -8
  180. package/src/assets/scripts/hooks/ensure-root-gitignore +236 -60
  181. package/src/assets/scripts/hooks/git-marker +48 -0
  182. package/src/assets/scripts/hooks/hook-log-init +3 -1
  183. package/src/assets/scripts/hooks/json-helper.cjs +228 -5
  184. package/src/assets/scripts/hooks/lib/project-paths.cjs +1 -20
  185. package/src/assets/scripts/hooks/log-paths +80 -0
  186. package/src/assets/scripts/hooks/memory-worker +22 -13
  187. package/src/assets/scripts/hooks/pre-compact-memory +44 -15
  188. package/src/assets/scripts/hooks/preamble +1 -4
  189. package/src/assets/scripts/hooks/queue-append +146 -28
  190. package/src/assets/scripts/hooks/resolve-project-root +101 -7
  191. package/src/assets/scripts/hooks/session-start-context +534 -20
  192. package/src/assets/scripts/hooks/session-start-memory +38 -15
  193. package/src/assets/scripts/lib/project-config.cjs +633 -0
  194. package/src/assets/scripts/pr-evidence.cjs +1961 -0
  195. package/src/assets/scripts/redact-secrets.cjs +490 -62
  196. package/src/assets/scripts/release-trace.cjs +1143 -0
  197. package/src/assets/scripts/resolve-evidence-policy.cjs +1145 -0
  198. package/src/assets/scripts/resolve-settings.cjs +1054 -0
  199. package/src/assets/scripts/verify-evidence.cjs +1822 -0
  200. package/src/assets/skills/compliance/SKILL.md +4 -2
  201. package/src/assets/skills/docs-framework/SKILL.md +11 -10
  202. package/src/assets/skills/docs-framework/references/patterns.md +10 -17
  203. package/src/assets/skills/gap-analysis/SKILL.md +2 -2
  204. package/src/assets/skills/git/SKILL.md +8 -78
  205. package/src/assets/skills/git/references/github-api.md +179 -141
  206. package/src/assets/skills/git/references/patterns.md +11 -6
  207. package/src/assets/skills/review-methodology/SKILL.md +1 -1
  208. package/src/assets/skills/review-methodology/references/patterns.md +6 -61
  209. package/src/assets/skills/review-methodology/references/violations.md +14 -22
  210. package/src/assets/skills/worktree-support/SKILL.md +1 -1
  211. package/src/assets/skills/worktree-support/references/roots.md +29 -0
  212. package/src/targets/claude-code/templates/managed-settings.json +25 -9
  213. package/src/assets/agents/git.md +0 -938
@@ -0,0 +1,363 @@
1
+ /**
2
+ * The CLI's view of the evidence policy — a typed seam onto the package's own
3
+ * `resolve-evidence-policy.cjs`, never a second implementation of it.
4
+ *
5
+ * D-POLICY-CJS-SEAM: the resolver is plain CommonJS under src/assets/scripts/,
6
+ * outside every tsconfig (PF-043, PF-069), so the interfaces below are
7
+ * TRANSCRIBED from its JSDoc typedefs and are the only shape authority on this
8
+ * side — open those typedefs before changing anything here. The module is loaded
9
+ * with `require()` from `scriptsDir()`, which resolves under the package root both
10
+ * from `dist/cli.js` and under vitest (`package.json` `files` ships src/assets/),
11
+ * so the CLI and the resolver it runs are always the same version. The installed
12
+ * `~/.devflow/scripts` copy is never loaded: it may be older than this CLI. There
13
+ * is deliberately no TypeScript copy of the parser, the fold or the grammar.
14
+ *
15
+ * The same seam loads the sibling `resolve-settings.cjs` (loadSettingsModule),
16
+ * the local resolver of the per-repository settings layer — `.devflow/project.json`,
17
+ * the personal `.devflow/config.json` and the machine manifest. Its shapes are
18
+ * transcribed the same way, and there is no TypeScript copy of its fold either.
19
+ * It also loads the shared strict parser both resolvers use,
20
+ * `lib/project-config.cjs` (loadProjectConfigLib), so the CLI judges a config
21
+ * file's bytes exactly as the resolvers do.
22
+ *
23
+ * D-POLICY-NO-WRITE (applies ADR-024): `.devflow/project.json` is team-owned, and
24
+ * devflow never writes or replaces a shared file it cannot prove it wrote. This
25
+ * module therefore imports no fs API; the CLI only PRINTS the bytes a team may
26
+ * choose to commit (`evidencePolicySuggestion`, and the migration lines of
27
+ * `repoComplianceStatusLines`), all from the settings resolver's project.json
28
+ * serializer.
29
+ *
30
+ * D-POLICY-JSON-RETIRED: the evidence resolver never parses `.devflow/policy.json`;
31
+ * at a source whose project.json has no `evidence`, the file's presence alone
32
+ * resolves `required` (see the resolver's own note). This side neither reads nor
33
+ * serializes it — it only names it in the migration hint.
34
+ */
35
+ import { createRequire } from 'module';
36
+ import { join } from 'path';
37
+ import { scriptsDir } from './assets.js';
38
+ // ── Transcribed shapes (resolve-evidence-policy.cjs JSDoc) ─────────────────────
39
+ /** Basename of the resolver under src/assets/scripts/ (and ~/.devflow/scripts/). */
40
+ export const RESOLVER_SCRIPT_NAME = 'resolve-evidence-policy.cjs';
41
+ /** Basename of the settings resolver under src/assets/scripts/ (and ~/.devflow/scripts/). */
42
+ export const SETTINGS_SCRIPT_NAME = 'resolve-settings.cjs';
43
+ /** The shared strict config parser, relative to src/assets/scripts/ (and ~/.devflow/scripts/). */
44
+ export const PROJECT_CONFIG_LIB_NAME = join('lib', 'project-config.cjs');
45
+ /** The team file the CLI suggests committing, relative to a repository root. */
46
+ const PROJECT_FILE = '.devflow/project.json';
47
+ /** The retired team file project.json's `evidence` replaces, relative to a repository root. */
48
+ const RETIRED_POLICY_FILE = '.devflow/policy.json';
49
+ /**
50
+ * Every key of EvidencePolicyModule and the runtime kind the loader requires of
51
+ * it. `satisfies` makes the compiler reject an interface key missing here.
52
+ */
53
+ export const EVIDENCE_POLICY_MODULE_SURFACE = Object.freeze({
54
+ POLICIES: 'string-array',
55
+ SOURCES: 'string-array',
56
+ WARNINGS: 'string-array',
57
+ MECHANISM_INPUTS: 'object',
58
+ OUTPUT_LINE_RE: 'regexp',
59
+ FAIL_CLOSED_LINE: 'string',
60
+ complianceDefault: 'function',
61
+ resolve: 'function',
62
+ });
63
+ /** Every key of SettingsModule and the runtime kind the loader requires of it. */
64
+ export const SETTINGS_MODULE_SURFACE = Object.freeze({
65
+ SETTINGS_LINE_RE: 'regexp',
66
+ SETTINGS_FAIL_CLOSED_LINE: 'string',
67
+ resolveSettings: 'function',
68
+ serializeProjectSuggestion: 'function',
69
+ });
70
+ /** Every key of ProjectConfigLib and the runtime kind the loader requires of it. */
71
+ export const PROJECT_CONFIG_LIB_SURFACE = Object.freeze({
72
+ MAX_CONFIG_BYTES: 'number',
73
+ decodeConfigBytes: 'function',
74
+ readBoundedRegularFile: 'function',
75
+ collectDuplicateKeyPaths: 'function',
76
+ });
77
+ function hasKind(value, kind) {
78
+ switch (kind) {
79
+ case 'string-array': return Array.isArray(value) && value.every(v => typeof v === 'string');
80
+ case 'object': return typeof value === 'object' && value !== null;
81
+ case 'regexp': return value instanceof RegExp;
82
+ case 'string': return typeof value === 'string';
83
+ case 'number': return typeof value === 'number' && Number.isFinite(value);
84
+ case 'function': return typeof value === 'function';
85
+ default: {
86
+ const exhaustive = kind;
87
+ return exhaustive;
88
+ }
89
+ }
90
+ }
91
+ /** Surface keys that are absent or of the wrong kind on `value`, in surface order. */
92
+ function surfaceMismatches(value, surface) {
93
+ if (typeof value !== 'object' || value === null)
94
+ return Object.keys(surface);
95
+ const record = value;
96
+ return Object.entries(surface)
97
+ .filter(([key, kind]) => !hasKind(record[key], kind))
98
+ .map(([key]) => key);
99
+ }
100
+ /**
101
+ * require() one package script and shape-check it against `surface`. Never
102
+ * throws: a missing file is `not-found`; a module that throws on load or lacks a
103
+ * surface key is `unusable`. The caller's type parameter is justified by the
104
+ * surface check, which `satisfies` ties to the interface's keys.
105
+ */
106
+ function loadScript(file, surface) {
107
+ let loaded;
108
+ try {
109
+ loaded = createRequire(import.meta.url)(file);
110
+ }
111
+ catch (err) {
112
+ const code = err.code;
113
+ if (code === 'MODULE_NOT_FOUND')
114
+ return { ok: false, error: { kind: 'not-found', path: file } };
115
+ const detail = err instanceof Error ? err.message : String(err);
116
+ return { ok: false, error: { kind: 'unusable', path: file, detail } };
117
+ }
118
+ const mismatches = surfaceMismatches(loaded, surface);
119
+ if (mismatches.length > 0) {
120
+ return { ok: false, error: { kind: 'unusable', path: file, detail: `missing or mistyped: ${mismatches.join(', ')}` } };
121
+ }
122
+ return { ok: true, value: loaded };
123
+ }
124
+ /**
125
+ * Load the evidence resolver from `dir` (default: the package's own scripts
126
+ * directory) and shape-check its surface.
127
+ */
128
+ export function loadEvidencePolicyModule(dir = scriptsDir()) {
129
+ return loadScript(join(dir, RESOLVER_SCRIPT_NAME), EVIDENCE_POLICY_MODULE_SURFACE);
130
+ }
131
+ /**
132
+ * Load the settings resolver from `dir` (default: the package's own scripts
133
+ * directory) and shape-check its surface. A `resolveSettings()` call makes one
134
+ * local `git` call and no network call (D-SETTINGS-LOCAL-ONLY).
135
+ */
136
+ export function loadSettingsModule(dir = scriptsDir()) {
137
+ return loadScript(join(dir, SETTINGS_SCRIPT_NAME), SETTINGS_MODULE_SURFACE);
138
+ }
139
+ /**
140
+ * Load the shared strict config parser from `dir` (default: the package's own
141
+ * scripts directory) and shape-check its surface.
142
+ */
143
+ export function loadProjectConfigLib(dir = scriptsDir()) {
144
+ return loadScript(join(dir, PROJECT_CONFIG_LIB_NAME), PROJECT_CONFIG_LIB_SURFACE);
145
+ }
146
+ // ── Presentation (pure) ────────────────────────────────────────────────────────
147
+ /** `Evidence policy: <policy> (source: <source>)`, plus ` [warn: a, b]` when warnings exist. */
148
+ export function formatEvidencePolicyStatus(r) {
149
+ const warn = r.warnings.length > 0 ? ` [warn: ${r.warnings.join(', ')}]` : '';
150
+ return `Evidence policy: ${r.policy} (source: ${r.source})${warn}`;
151
+ }
152
+ /**
153
+ * The line shown in place of a policy when the resolver cannot be loaded. The
154
+ * remedy is a package reinstall: the CLI loads the package's own copy, which
155
+ * `devflow init` does not restore.
156
+ */
157
+ export function formatEvidencePolicyUnavailable(error) {
158
+ switch (error.kind) {
159
+ case 'not-found': return 'Evidence policy: unavailable (resolver not found — reinstall devflow-kit)';
160
+ case 'unusable': return 'Evidence policy: unavailable (resolver failed to load — reinstall devflow-kit)';
161
+ default: {
162
+ const exhaustive = error;
163
+ return exhaustive;
164
+ }
165
+ }
166
+ }
167
+ /**
168
+ * The `compliance --status` line: the resolved policy for `opts.dir`, or the
169
+ * unavailable line when the loader failed — that line is the whole handling
170
+ * (ADR-028). The caller passes the compliance state it already read, so the
171
+ * manifest is never read twice. `resolve()` makes at most three `gh` calls and
172
+ * bounds every subprocess with a timeout, so an offline machine degrades to a
173
+ * flagged result rather than a hang.
174
+ */
175
+ export function evidencePolicyStatusLine(loaded, opts) {
176
+ if (!loaded.ok)
177
+ return formatEvidencePolicyUnavailable(loaded.error);
178
+ return formatEvidencePolicyStatus(loaded.value.resolve(opts));
179
+ }
180
+ /**
181
+ * The frameworks a compliance state names, for the suggestion: the raw list when
182
+ * the state is well-formed, else none. The settings resolver's serializer
183
+ * normalizes and drops unknown ids, so no id reaches the printed bytes unchecked.
184
+ */
185
+ function suggestedFrameworks(complianceState) {
186
+ if (typeof complianceState !== 'object' || complianceState === null)
187
+ return [];
188
+ const frameworks = complianceState.frameworks;
189
+ return Array.isArray(frameworks) && frameworks.every(f => typeof f === 'string') ? frameworks : [];
190
+ }
191
+ /**
192
+ * What `--enable`/`--set` print when compliance is on: the keys to add to a
193
+ * repository's `.devflow/project.json` on its default branch — merged into the
194
+ * file when it already has one, never replacing it — to hold every developer to
195
+ * what this machine now gets by default: the required evidence policy and this
196
+ * machine's frameworks. Returned only when the evidence resolver's own
197
+ * `complianceDefault` says `required` (compliance enabled, at any framework
198
+ * count); `null` otherwise. The bytes come
199
+ * from the settings resolver's `serializeProjectSuggestion`, which returns them
200
+ * only when they read back through the shared parser as exactly what was asked.
201
+ * Nothing is written (D-POLICY-NO-WRITE, applies ADR-024).
202
+ */
203
+ export function evidencePolicySuggestion(complianceState, policy, settings) {
204
+ if (policy.complianceDefault(complianceState) !== 'required')
205
+ return null;
206
+ const body = settings.serializeProjectSuggestion({
207
+ evidence: 'required',
208
+ compliance: suggestedFrameworks(complianceState),
209
+ });
210
+ if (body === null)
211
+ return null;
212
+ return [
213
+ 'Compliance is enabled on this machine, so repositories without a committed',
214
+ 'evidence setting default to the required evidence policy here. To apply it for',
215
+ `everyone working in a repository, add these keys to its ${PROJECT_FILE} on its`,
216
+ 'default branch — merged into the file when it already has one, never replacing it:',
217
+ '',
218
+ `${body}`,
219
+ 'devflow never writes this file: the team owns it, and once committed it applies',
220
+ 'repo-wide.',
221
+ ].join('\n');
222
+ }
223
+ // ── The settings layer, for `--status` (pure) ──────────────────────────────────
224
+ /** A repo layer's file, as a `--status` line names it. */
225
+ export function settingsSourceFile(source) {
226
+ switch (source) {
227
+ case 'project': return PROJECT_FILE;
228
+ case 'personal': return '.devflow/config.json';
229
+ default: {
230
+ const exhaustive = source;
231
+ return exhaustive;
232
+ }
233
+ }
234
+ }
235
+ /**
236
+ * The effective state of a feature switch in this repository, ONLY when a repo
237
+ * layer narrows it — `disabled (.devflow/project.json)` — and null otherwise, so a
238
+ * `--status` whose machine switch alone decides prints exactly what it always has
239
+ * (D-FEATURES-NARROW-ONLY). A repository file that exists but is unreadable fails
240
+ * every field closed but the compliance lens, and a switch that closed off is
241
+ * labelled with that file —
242
+ * `disabled (.devflow/project.json is unreadable)` — since commands act on it. Any
243
+ * other failure (the resolver failed to load, or git could not answer) yields
244
+ * null: it knows nothing about this repository.
245
+ */
246
+ export function narrowedSwitchLabel(loaded, opts, feature) {
247
+ if (!loaded.ok)
248
+ return null;
249
+ const settings = loaded.value.resolveSettings(opts);
250
+ if (!settings.ok) {
251
+ if (settings.unreadable === null || settings.switches[feature].on)
252
+ return null;
253
+ return `disabled (${settingsSourceFile(settings.unreadable)} is unreadable)`;
254
+ }
255
+ const state = settings.switches[feature];
256
+ if (state.on || state.source === 'machine')
257
+ return null;
258
+ return `disabled (${settingsSourceFile(state.source)})`;
259
+ }
260
+ /**
261
+ * The tracker in effect in the repository at `opts.dir`, ONLY when a repository
262
+ * layer decides it — its committed project.json, or the personal config.json
263
+ * narrowing — and null otherwise. A machine whose own selection (or the github
264
+ * default) decides gets null, so `tracker --status` prints exactly what it always
265
+ * has there. So does a resolver that failed to load or failed closed: it knows
266
+ * nothing about this repository, and a fail-closed `github` is not a selection
267
+ * anyone made.
268
+ */
269
+ export function repoTrackerSelection(loaded, opts) {
270
+ if (!loaded.ok)
271
+ return null;
272
+ const settings = loaded.value.resolveSettings(opts);
273
+ if (!settings.ok)
274
+ return null;
275
+ const source = settings.trackerSource;
276
+ if (source !== 'project' && source !== 'personal')
277
+ return null;
278
+ return { provider: settings.tracker, source };
279
+ }
280
+ /** A declared id list as a `--status` line shows it. */
281
+ function idsLabel(ids) {
282
+ return ids.length > 0 ? ids.join(', ') : 'generic controls only';
283
+ }
284
+ /**
285
+ * The `compliance --status` lines about the repository in `opts.dir`, mirroring
286
+ * the resolver's lens fold (D-LENS-UNION: machine ∪ default branch ∪ worktree):
287
+ * the ids this checkout's project.json declares (`generic controls only` for an
288
+ * empty or malformed list), the ids the default branch's copy declares, the
289
+ * effective lens those add up to with the machine's, and a migration hint while
290
+ * the retired policy file is in the working tree.
291
+ *
292
+ * A broken file affects only the keys it owns. An unreadable project.json is a
293
+ * malformed declaration — generic — and says so, naming the file; an unreadable
294
+ * config.json owns no compliance, so the lines are those of a readable one. Empty
295
+ * when the resolver is unavailable or failed closed for any other reason, or no
296
+ * repository layer declares anything and there is no policy file — the status
297
+ * output is then unchanged.
298
+ *
299
+ * The hint states the rule (D-POLICY-JSON-RETIRED): the file is not read, and
300
+ * while project.json has no `evidence` its presence holds the repository at
301
+ * `required`. The value is not read either, so the hint shows the project.json
302
+ * line for each value the file may hold, from the settings resolver's serializer.
303
+ */
304
+ export function repoComplianceStatusLines(loaded, opts) {
305
+ if (!loaded.ok)
306
+ return [];
307
+ const settings = loaded.value.resolveSettings(opts);
308
+ if (!settings.ok && settings.unreadable === null)
309
+ return [];
310
+ const lines = [];
311
+ if (settings.unreadable === 'project') {
312
+ lines.push(`Repository: generic controls only (${PROJECT_FILE} is unreadable)`);
313
+ }
314
+ else if (settings.repoCompliance !== null) {
315
+ lines.push(`Repository: ${idsLabel(settings.repoCompliance)} (${PROJECT_FILE})`);
316
+ }
317
+ if (settings.defaultBranchCompliance !== null) {
318
+ lines.push(`Default branch: ${idsLabel(settings.defaultBranchCompliance)} (its ${PROJECT_FILE})`);
319
+ }
320
+ if (lines.length > 0) {
321
+ const lens = settings.compliance;
322
+ lines.push(`Effective here: ${lens.enabled ? idsLabel(lens.frameworks) : 'off'} (this machine + the default branch + this checkout)`);
323
+ }
324
+ if (settings.retiredPolicyFile)
325
+ lines.push(...retiredPolicyHint(loaded.value));
326
+ return lines;
327
+ }
328
+ /**
329
+ * The warning a `--status` prints when this checkout's `.devflow/config.json` is
330
+ * tracked by git, or null (D-PERSONAL-UNTRACKED). The resolver ignores such a file
331
+ * and says so on stderr, but prompts run it with stderr discarded, so a status
332
+ * command is where the user sees why their personal settings have no effect.
333
+ */
334
+ export function personalConfigTrackedWarning(loaded, opts) {
335
+ if (!loaded.ok)
336
+ return null;
337
+ if (!loaded.value.resolveSettings(opts).personalTracked)
338
+ return null;
339
+ const file = settingsSourceFile('personal');
340
+ return `${file} is tracked by git, so devflow ignores it — it holds personal settings. ` +
341
+ `Untrack it with: git rm --cached ${file}`;
342
+ }
343
+ /** The policies the hint maps, in the order it prints them. */
344
+ const HINT_POLICIES = ['standard', 'required'];
345
+ /**
346
+ * The migration hint for a working tree holding the retired policy file: what the
347
+ * file does now, and the project.json line that states each value it may hold.
348
+ * A value whose line the serializer refuses is left out rather than hand-built.
349
+ */
350
+ function retiredPolicyHint(settings) {
351
+ const mappings = HINT_POLICIES.flatMap((policy) => {
352
+ const body = settings.serializeProjectSuggestion({ evidence: policy });
353
+ return body === null ? [] : [` ${policy.padEnd(8)} → ${body.trimEnd()}`];
354
+ });
355
+ return [
356
+ `Migration: ${RETIRED_POLICY_FILE} is not read. While ${PROJECT_FILE} has no "evidence",`,
357
+ ' its presence alone holds this repository at required. Add its value to',
358
+ ` ${PROJECT_FILE} as "evidence", and keep ${RETIRED_POLICY_FILE} until every`,
359
+ ` teammate runs devflow 3.0 or later; only then delete it:`,
360
+ ...mappings,
361
+ ];
362
+ }
363
+ //# sourceMappingURL=evidence-policy.js.map