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.
- package/CHANGELOG.md +229 -0
- package/README.md +111 -18
- package/dist/agents/git.md +822 -0
- package/dist/cli/commands/agents.js +6 -1
- package/dist/cli/commands/ambient.js +160 -145
- package/dist/cli/commands/attribution-prompts.js +1 -1
- package/dist/cli/commands/capture.js +29 -55
- package/dist/cli/commands/compliance-prompts.js +1 -1
- package/dist/cli/commands/compliance.js +48 -55
- package/dist/cli/commands/context.js +17 -32
- package/dist/cli/commands/debug.js +65 -26
- package/dist/cli/commands/flags.js +3 -3
- package/dist/cli/commands/hud.js +34 -10
- package/dist/cli/commands/init-seed.js +61 -27
- package/dist/cli/commands/init.js +649 -240
- package/dist/cli/commands/install-report.js +200 -0
- package/dist/cli/commands/knowledge/index.js +2 -2
- package/dist/cli/commands/knowledge/toggle.js +35 -37
- package/dist/cli/commands/learning.js +79 -57
- package/dist/cli/commands/legacy-hooks.js +11 -14
- package/dist/cli/commands/memory.js +134 -135
- package/dist/cli/commands/prompt-io.js +4 -4
- package/dist/cli/commands/proxy.js +23 -41
- package/dist/cli/commands/security.js +81 -29
- package/dist/cli/commands/skills.js +71 -7
- package/dist/cli/commands/tracker-prompts.js +145 -0
- package/dist/cli/commands/tracker.js +277 -0
- package/dist/cli/commands/uninstall.js +520 -169
- package/dist/cli.js +2 -0
- package/dist/commands/bug-analysis.md +58 -14
- package/dist/commands/code-review.md +110 -32
- package/dist/commands/debug.md +55 -11
- package/dist/commands/dynamic-build.md +344 -73
- package/dist/commands/dynamic-plan.md +77 -27
- package/dist/commands/dynamic-profile.md +25 -11
- package/dist/commands/dynamic-tickets.md +76 -15
- package/dist/commands/explore.md +37 -7
- package/dist/commands/implement.md +314 -62
- package/dist/commands/plan.md +146 -32
- package/dist/commands/release.md +64 -17
- package/dist/commands/research.md +34 -8
- package/dist/commands/resolve.md +196 -68
- package/dist/commands/self-review.md +45 -9
- package/dist/core/agent-models.js +55 -12
- package/dist/core/assets.js +58 -2
- package/dist/core/compliance-compose.js +27 -27
- package/dist/core/evidence-policy.js +363 -0
- package/dist/core/feature-config.js +200 -65
- package/dist/core/feature-switch.js +112 -0
- package/dist/core/flags.js +34 -6
- package/dist/core/fs-atomic.js +27 -0
- package/dist/core/hook-log-dirs.js +104 -0
- package/dist/core/learning-tuning-config.js +5 -3
- package/dist/core/ledger-root.js +102 -0
- package/dist/core/manifest.js +38 -10
- package/dist/core/mds-variants.js +798 -0
- package/dist/core/migrations.js +49 -23
- package/dist/core/model-discovery.js +12 -1
- package/dist/core/plugins.js +361 -12
- package/dist/core/project-paths.js +1 -18
- package/dist/core/proxy-log.js +8 -6
- package/dist/core/proxy-state.js +11 -8
- package/dist/core/reference-sweep.js +136 -0
- package/dist/core/same-location.js +25 -0
- package/dist/core/tracker.js +494 -0
- package/dist/hud/components/config-counts.js +15 -4
- package/dist/hud/components/learning-counts.js +14 -0
- package/dist/hud/config.js +2 -1
- package/dist/hud/cost-history.js +2 -4
- package/dist/hud/git.js +52 -7
- package/dist/hud/index.js +7 -9
- package/dist/skills/git/references/decision-markers.md +19 -0
- package/dist/skills/git/references/learn-conventions.md +56 -0
- package/dist/skills/git/references/pr/check-ci-status.md +14 -0
- package/dist/skills/git/references/pr/check-merge-readiness.md +28 -0
- package/dist/skills/git/references/pr/ensure-pr-ready.md +24 -0
- package/dist/skills/git/references/pr/fetch-review-threads.md +22 -0
- package/dist/skills/git/references/pr/post-resolution-summary.md +40 -0
- package/dist/skills/git/references/pr/post-review-summary.md +42 -0
- package/dist/skills/git/references/pr/resolve-review-threads.md +35 -0
- package/dist/skills/git/references/pr/update-pr-evidence.md +14 -0
- package/dist/skills/git/references/pr/validate-branch.md +18 -0
- package/dist/skills/git/references/publication-gate.md +13 -0
- package/dist/skills/git/references/tracker/_mcp.md +153 -0
- package/dist/skills/git/references/tracker/github/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +40 -0
- package/dist/skills/git/references/tracker/github/create-release.md +11 -0
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +16 -0
- package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +69 -0
- package/dist/skills/git/references/tracker/github/fetch-issue.md +32 -0
- package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +17 -0
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +19 -0
- package/dist/skills/git/references/tracker/github/manage-debt.md +101 -0
- package/dist/skills/git/references/tracker/github/post-wave-report.md +28 -0
- package/dist/skills/git/references/tracker/github/setup-task.md +26 -0
- package/dist/skills/git/references/tracker/jira/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +49 -0
- package/dist/skills/git/references/tracker/jira/create-release.md +17 -0
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +22 -0
- package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +53 -0
- package/dist/skills/git/references/tracker/jira/fetch-issue.md +14 -0
- package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +15 -0
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +18 -0
- package/dist/skills/git/references/tracker/jira/manage-debt.md +37 -0
- package/dist/skills/git/references/tracker/jira/post-wave-report.md +33 -0
- package/dist/skills/git/references/tracker/jira/setup-task.md +31 -0
- package/dist/skills/git/references/tracker/linear/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +53 -0
- package/dist/skills/git/references/tracker/linear/create-release.md +17 -0
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +22 -0
- package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +53 -0
- package/dist/skills/git/references/tracker/linear/fetch-issue.md +14 -0
- package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +15 -0
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +18 -0
- package/dist/skills/git/references/tracker/linear/manage-debt.md +37 -0
- package/dist/skills/git/references/tracker/linear/post-wave-report.md +33 -0
- package/dist/skills/git/references/tracker/linear/setup-task.md +32 -0
- package/dist/skills/git/references/trust-rule.md +7 -0
- package/dist/targets/claude-code/claude-paths.js +59 -57
- package/dist/targets/claude-code/compliance-install.js +49 -65
- package/dist/targets/claude-code/hooks.js +108 -3
- package/dist/targets/claude-code/installer.js +1187 -32
- package/dist/targets/claude-code/legacy.js +5 -0
- package/dist/targets/claude-code/post-install.js +366 -151
- package/dist/targets/claude-code/tracker-install.js +134 -0
- package/package.json +8 -6
- package/src/assets/agents/code.md +45 -6
- package/src/assets/agents/design.md +2 -1
- package/src/assets/agents/git.mds +825 -0
- package/src/assets/agents/knowledge.md +3 -3
- package/src/assets/agents/learning.md +11 -0
- package/src/assets/agents/review.md +3 -1
- package/src/assets/agents/synthesize.md +1 -1
- package/src/assets/agents/test.md +16 -5
- package/src/assets/agents/tracker.md +474 -0
- package/src/assets/agents/validate.md +7 -5
- package/src/assets/commands/_partials/_compliance.mds +19 -1
- package/src/assets/commands/_partials/_decisions.mds +15 -3
- package/src/assets/commands/_partials/_docs_root.mds +35 -0
- package/src/assets/commands/_partials/_engine.mds +13 -11
- package/src/assets/commands/_partials/_evidence_policy.mds +30 -0
- package/src/assets/commands/_partials/_factory.mds +1 -1
- package/src/assets/commands/_partials/_knowledge.mds +27 -9
- package/src/assets/commands/_partials/_plan_contract.mds +22 -7
- package/src/assets/commands/_partials/_preamble.mds +2 -2
- package/src/assets/commands/_partials/_publication.mds +8 -2
- package/src/assets/commands/_partials/_settings.mds +28 -0
- package/src/assets/commands/_partials/_ticket_template.mds +3 -2
- package/src/assets/commands/_partials/_tracker.mds +18 -0
- package/src/assets/commands/_partials/_wave.mds +16 -10
- package/src/assets/commands/bug-analysis.mds +31 -19
- package/src/assets/commands/code-review.mds +67 -41
- package/src/assets/commands/debug.mds +13 -7
- package/src/assets/commands/dynamic-build.mds +274 -66
- package/src/assets/commands/dynamic-plan.mds +50 -23
- package/src/assets/commands/dynamic-profile.mds +24 -11
- package/src/assets/commands/dynamic-tickets.mds +63 -16
- package/src/assets/commands/explore.mds +4 -5
- package/src/assets/commands/implement.mds +234 -67
- package/src/assets/commands/plan.mds +91 -33
- package/src/assets/commands/release.md +64 -17
- package/src/assets/commands/research.mds +11 -9
- package/src/assets/commands/resolve.mds +150 -78
- package/src/assets/commands/self-review.mds +24 -25
- package/src/assets/mds/git/_pr.mds +331 -0
- package/src/assets/mds/git/_references.mds +135 -0
- package/src/assets/mds/tracker/_common.mds +156 -0
- package/src/assets/mds/tracker/_github.mds +472 -0
- package/src/assets/mds/tracker/_jira.mds +407 -0
- package/src/assets/mds/tracker/_linear.mds +449 -0
- package/src/assets/mds/tracker/_mcp.mds +305 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +5 -8
- package/src/assets/scripts/hooks/background-memory-update +40 -19
- package/src/assets/scripts/hooks/capture-prompt +18 -8
- package/src/assets/scripts/hooks/capture-question +18 -8
- package/src/assets/scripts/hooks/capture-turn +27 -13
- package/src/assets/scripts/hooks/debug-trace +11 -6
- package/src/assets/scripts/hooks/ensure-devflow-init +33 -6
- package/src/assets/scripts/hooks/ensure-proxy +9 -8
- package/src/assets/scripts/hooks/ensure-root-gitignore +236 -60
- package/src/assets/scripts/hooks/git-marker +48 -0
- package/src/assets/scripts/hooks/hook-log-init +3 -1
- package/src/assets/scripts/hooks/json-helper.cjs +228 -5
- package/src/assets/scripts/hooks/lib/project-paths.cjs +1 -20
- package/src/assets/scripts/hooks/log-paths +80 -0
- package/src/assets/scripts/hooks/memory-worker +22 -13
- package/src/assets/scripts/hooks/pre-compact-memory +44 -15
- package/src/assets/scripts/hooks/preamble +1 -4
- package/src/assets/scripts/hooks/queue-append +146 -28
- package/src/assets/scripts/hooks/resolve-project-root +101 -7
- package/src/assets/scripts/hooks/session-start-context +534 -20
- package/src/assets/scripts/hooks/session-start-memory +38 -15
- package/src/assets/scripts/lib/project-config.cjs +633 -0
- package/src/assets/scripts/pr-evidence.cjs +1961 -0
- package/src/assets/scripts/redact-secrets.cjs +490 -62
- package/src/assets/scripts/release-trace.cjs +1143 -0
- package/src/assets/scripts/resolve-evidence-policy.cjs +1145 -0
- package/src/assets/scripts/resolve-settings.cjs +1054 -0
- package/src/assets/scripts/verify-evidence.cjs +1822 -0
- package/src/assets/skills/compliance/SKILL.md +4 -2
- package/src/assets/skills/docs-framework/SKILL.md +11 -10
- package/src/assets/skills/docs-framework/references/patterns.md +10 -17
- package/src/assets/skills/gap-analysis/SKILL.md +2 -2
- package/src/assets/skills/git/SKILL.md +8 -78
- package/src/assets/skills/git/references/github-api.md +179 -141
- package/src/assets/skills/git/references/patterns.md +11 -6
- package/src/assets/skills/review-methodology/SKILL.md +1 -1
- package/src/assets/skills/review-methodology/references/patterns.md +6 -61
- package/src/assets/skills/review-methodology/references/violations.md +14 -22
- package/src/assets/skills/worktree-support/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/references/roots.md +29 -0
- package/src/targets/claude-code/templates/managed-settings.json +25 -9
- package/src/assets/agents/git.md +0 -938
|
@@ -24,16 +24,28 @@ Detect changed files and build context:
|
|
|
24
24
|
4. Build TASK_DESCRIPTION from recent commit messages or branch name
|
|
25
25
|
### Load DECISIONS_CONTEXT
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
The decisions ledger belongs to the repository, not to one checkout: in a linked worktree it lives in the main worktree, and a session started in a subdirectory reads the copy at the repository root. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git -C "{start}" rev-parse --path-format=absolute --show-toplevel --git-common-dir
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Line 1 is the checkout's toplevel, line 2 the repository's common git directory. A git older than 2.31 echoes `--path-format=absolute` back as a line of its own first. `{ledger}` is the first of these that applies:
|
|
34
|
+
|
|
35
|
+
1. **The main worktree** — line 2 without its trailing `/.git`, when the output is exactly two lines each beginning with `/`, line 2 ends in `/.git`, and the directory left once it is removed is not your home directory and contains a `.devflow/` directory.
|
|
36
|
+
2. **The toplevel** — line 1, or on an older git the line after the echoed flag.
|
|
37
|
+
3. **The start directory itself** — when the command failed or printed no absolute toplevel (outside a git repository).
|
|
38
|
+
|
|
39
|
+
This is the rule the learning hooks apply (D-LEDGER-MAIN-WORKTREE, D-PROMPT-ROOT), so you read the index the Learning agent writes.
|
|
28
40
|
|
|
29
41
|
**Step 1 — Read the pre-rendered index:**
|
|
30
42
|
|
|
31
|
-
Attempt to read `{
|
|
43
|
+
Attempt to read `{ledger}/.devflow/learning/index.md`.
|
|
32
44
|
|
|
33
45
|
- If the file exists and contains non-empty content: use that content as `DECISIONS_CONTEXT`.
|
|
34
46
|
- If the file is absent or empty: set `DECISIONS_CONTEXT` to `(none)`.
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
The index is one direct file read, written at render time by `render-decisions.cjs` alongside `decisions.md`/`pitfalls.md` — no `.cjs` script runs here, and the index's own footer names the files that hold each entry's full body.
|
|
37
49
|
|
|
38
50
|
**Step 2 — Apply decisions using `devflow:apply-decisions`:**
|
|
39
51
|
|
|
@@ -43,7 +55,13 @@ Pass `DECISIONS_CONTEXT` to Scrutinize agent — the compact index lists active
|
|
|
43
55
|
|
|
44
56
|
### Load Feature Knowledge
|
|
45
57
|
|
|
46
|
-
Resolve the
|
|
58
|
+
Resolve `{worktree}` as the checkout's toplevel, because feature knowledge bases are committed with the branch (D-PROMPT-ROOT): from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`) — run
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git -C "{start}" rev-parse --show-toplevel
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
and use its one-line output. If the command fails (outside a git repository), `{worktree}` is the start directory itself. All paths below are relative to `{worktree}`.
|
|
47
65
|
|
|
48
66
|
**Step 1 — Read the index cache:**
|
|
49
67
|
|
|
@@ -78,7 +96,7 @@ Concatenate the selected KNOWLEDGE.md files under slug headers:
|
|
|
78
96
|
|
|
79
97
|
If no KBs exist, no KBs are relevant, or `.devflow/features/` is absent, set `FEATURE_KNOWLEDGE` to `(none)`.
|
|
80
98
|
|
|
81
|
-
**
|
|
99
|
+
**One git call, then direct file reads — no `.cjs` script.** After resolving `{worktree}`, this step is 1 index read (or N frontmatter reads on fallback), bounded by KB count.
|
|
82
100
|
|
|
83
101
|
Pass `FEATURE_KNOWLEDGE` to Scrutinize agent.
|
|
84
102
|
|
|
@@ -165,13 +183,27 @@ Display summary:
|
|
|
165
183
|
|
|
166
184
|
### Feature Knowledge Write-Back (Conditional)
|
|
167
185
|
|
|
168
|
-
Resolve the
|
|
186
|
+
Resolve `{worktree}` as the checkout's toplevel, because feature knowledge bases are committed with the branch (D-PROMPT-ROOT): from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`) — run
|
|
169
187
|
|
|
170
|
-
|
|
188
|
+
```bash
|
|
189
|
+
git -C "{start}" rev-parse --show-toplevel
|
|
190
|
+
```
|
|
171
191
|
|
|
172
|
-
|
|
192
|
+
and use its one-line output. If the command fails (outside a git repository), `{worktree}` is the start directory itself. All paths below are relative to `{worktree}`.
|
|
173
193
|
|
|
174
|
-
|
|
194
|
+
**Step 1 — Check the opt-out gate, with `{root}` = `{worktree}`:**
|
|
195
|
+
|
|
196
|
+
**Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off` instead.
|
|
203
|
+
|
|
204
|
+
The accepted line is the only source of these values: the script alone folds the committed `.devflow/project.json`, the personal `.devflow/config.json` and the machine manifest.
|
|
205
|
+
|
|
206
|
+
If the settings line says `KNOWLEDGE=off`, skip write-back entirely. The machine switch (`devflow knowledge --disable`), the repository and the personal settings can each turn knowledge off, and none can turn it back on (D-FEATURES-NARROW-ONLY). The fail-closed line says `KNOWLEDGE=off` too, so an unresolvable line skips write-back.
|
|
175
207
|
|
|
176
208
|
**Step 2 — Evaluate whether write-back is warranted:**
|
|
177
209
|
|
|
@@ -210,6 +242,10 @@ The frontmatter in KNOWLEDGE.md is the source of truth — index.md is only a ca
|
|
|
210
242
|
After writing, commit the two files to the current worktree branch yourself by running git via your Bash tool (do not use a script). Stage ONLY .devflow/features/index.md and .devflow/features/{slug}/KNOWLEDGE.md, then commit just those paths with a docs(knowledge): message. Do NOT push, do NOT force, do NOT stage anything else. Follow your Commit Protocol — it is non-blocking, so if any git step fails, report KB_COMMIT and finish normally."
|
|
211
243
|
```
|
|
212
244
|
|
|
245
|
+
**Step 4 — Surface an uncommitted knowledge base:**
|
|
246
|
+
|
|
247
|
+
When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files were written but deliberately not committed — a commit on a detached HEAD becomes unreachable once HEAD moves. Tell the user in the workflow's final report, in one line, that the knowledge base was written but not committed, and name the uncommitted paths the agent listed, so they can commit them on a branch before the worktree is removed. Never commit them yourself.
|
|
248
|
+
|
|
213
249
|
**Failure handling**: Non-blocking. If the Knowledge agent fails, log the failure and continue — the workflow outcome is not affected by write-back success.
|
|
214
250
|
|
|
215
251
|
## Architecture
|
|
@@ -27,7 +27,7 @@ import * as path from 'path';
|
|
|
27
27
|
import { writeFileAtomicExclusive } from './fs-atomic.js';
|
|
28
28
|
import { isDormantExternalModel } from './external-models.js';
|
|
29
29
|
import { rewriteAgentFrontmatter, readFrontmatterModel, isValidModelName } from './agent-frontmatter.js';
|
|
30
|
-
import {
|
|
30
|
+
import { agentSourceDirs } from './assets.js';
|
|
31
31
|
import { getAllAgentNames } from './plugins.js';
|
|
32
32
|
import { mdEntryName, mdFileName } from './orphan-sweep.js';
|
|
33
33
|
import { isContainedIn } from './paths.js';
|
|
@@ -359,26 +359,27 @@ export function resolveEffective(agentName, mapping, shippedDefaults, proxyEnabl
|
|
|
359
359
|
// loadShippedDefaults
|
|
360
360
|
// ---------------------------------------------------------------------------
|
|
361
361
|
/**
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
362
|
+
* Parse the shipped model default out of every {name}.md in one directory.
|
|
363
|
+
*
|
|
364
|
+
* A missing or unreadable directory yields an empty map — dist/agents/ does not
|
|
365
|
+
* exist until a generator host does, and a source tree that produced no agents
|
|
366
|
+
* is caught by the registry-completeness guard rather than by a throw here.
|
|
367
|
+
* Unknown or malformed files are skipped individually.
|
|
365
368
|
*/
|
|
366
|
-
|
|
367
|
-
const sourceDir = agentsDir();
|
|
368
|
-
const defaults = {};
|
|
369
|
+
async function readDirDefaults(dir) {
|
|
369
370
|
let entries;
|
|
370
371
|
try {
|
|
371
|
-
entries = await fs.readdir(
|
|
372
|
+
entries = await fs.readdir(dir);
|
|
372
373
|
}
|
|
373
374
|
catch {
|
|
374
|
-
return
|
|
375
|
+
return {};
|
|
375
376
|
}
|
|
376
377
|
const pairs = await Promise.all(entries.map(async (file) => {
|
|
377
378
|
const agentName = mdEntryName(file);
|
|
378
379
|
if (agentName === null)
|
|
379
380
|
return null;
|
|
380
381
|
try {
|
|
381
|
-
const content = await fs.readFile(path.join(
|
|
382
|
+
const content = await fs.readFile(path.join(dir, file), 'utf-8');
|
|
382
383
|
const result = readFrontmatterModel(content);
|
|
383
384
|
if (result.ok && result.value) {
|
|
384
385
|
return [agentName, result.value];
|
|
@@ -389,6 +390,7 @@ export async function loadShippedDefaults() {
|
|
|
389
390
|
}
|
|
390
391
|
return null;
|
|
391
392
|
}));
|
|
393
|
+
const defaults = {};
|
|
392
394
|
for (const pair of pairs) {
|
|
393
395
|
if (pair !== null) {
|
|
394
396
|
defaults[pair[0]] = pair[1];
|
|
@@ -396,11 +398,51 @@ export async function loadShippedDefaults() {
|
|
|
396
398
|
}
|
|
397
399
|
return defaults;
|
|
398
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* Load shipped default models from the agent files.
|
|
403
|
+
*
|
|
404
|
+
* Directories are MOST-PREFERRED FIRST — the convention owned by
|
|
405
|
+
* agentSourceDirs() — and the first directory to supply a name wins, so once an
|
|
406
|
+
* agent is generated into dist/agents/ its frontmatter is the shipped default.
|
|
407
|
+
*
|
|
408
|
+
* A registry agent that no directory supplies is reported through `onWarning`
|
|
409
|
+
* as ONE aggregate message naming every missing agent and the build step. The
|
|
410
|
+
* installer throws on the same invariant; this is a read path whose callers must
|
|
411
|
+
* keep rendering (`devflow agents --list`), so it warns instead. Staying silent
|
|
412
|
+
* is what makes the gap dangerous: resolveEffective returns an undefined model,
|
|
413
|
+
* reapplyAgentMapping buckets the agent 'unchanged', and disabling the proxy
|
|
414
|
+
* leaves an externally-pinned agent unreverted with nothing said (PF-022).
|
|
415
|
+
*
|
|
416
|
+
* @param dirs - Agent directories, most-preferred first. Injectable so tests can
|
|
417
|
+
* prove the precedence against a temp tree; all real callers use the default.
|
|
418
|
+
* @param opts - Optional warning channel; the gap is silent without one.
|
|
419
|
+
*/
|
|
420
|
+
export async function loadShippedDefaults(dirs = agentSourceDirs(), opts) {
|
|
421
|
+
const perDir = await Promise.all(dirs.map(readDirDefaults));
|
|
422
|
+
const defaults = {};
|
|
423
|
+
for (const dirDefaults of perDir) {
|
|
424
|
+
for (const [agentName, model] of Object.entries(dirDefaults)) {
|
|
425
|
+
if (!(agentName in defaults)) {
|
|
426
|
+
defaults[agentName] = model;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const missing = getAllAgentNames().filter(name => !(name in defaults));
|
|
431
|
+
if (missing.length > 0) {
|
|
432
|
+
opts?.onWarning?.(`No shipped default found for declared agent(s): ${missing.join(', ')}. ` +
|
|
433
|
+
`Run \`npm run build:mds\` if they are compiled from .mds generator hosts, otherwise ` +
|
|
434
|
+
`ensure the agent files exist in src/assets/agents/ (searched: ${dirs.join(', ')}).`);
|
|
435
|
+
}
|
|
436
|
+
return defaults;
|
|
437
|
+
}
|
|
399
438
|
/**
|
|
400
439
|
* Idempotent convergence function: walk every installed agent file and
|
|
401
440
|
* rewrite frontmatter model/effort to match the effective mapping.
|
|
402
441
|
*
|
|
403
|
-
* - Reads shipped defaults LIVE from
|
|
442
|
+
* - Reads shipped defaults LIVE from the agent sources — agentSourceDirs(),
|
|
443
|
+
* dist/agents/ preferred over src/assets/agents/. An agent no source supplies
|
|
444
|
+
* is reported through the warning channel rather than passing as 'unchanged'
|
|
445
|
+
* with no explanation.
|
|
404
446
|
* - Gets the agent name list from the registry (getAllAgentNames()) plus
|
|
405
447
|
* any mapping entries for agents not in the registry.
|
|
406
448
|
* - Missing installed files → skip silently (recorded in skippedMissing).
|
|
@@ -419,7 +461,7 @@ export async function reapplyAgentMapping(opts) {
|
|
|
419
461
|
return { updated: [], unchanged: [], skippedMissing: [], invalidMapping: [], warnings };
|
|
420
462
|
}
|
|
421
463
|
const mapping = mappingResult.value;
|
|
422
|
-
const shippedDefaults = await loadShippedDefaults();
|
|
464
|
+
const shippedDefaults = await loadShippedDefaults(opts.agentSourceDirs, { onWarning: warn });
|
|
423
465
|
// Build the union of: all registered agent names + all names in the mapping
|
|
424
466
|
// (so agents not yet in the registry but configured are also processed).
|
|
425
467
|
const registryNames = new Set(getAllAgentNames());
|
|
@@ -525,6 +567,7 @@ export async function revertExternalAgents(opts) {
|
|
|
525
567
|
installDir: opts.installDir,
|
|
526
568
|
devflowDir: opts.devflowDir,
|
|
527
569
|
proxyEnabled: false,
|
|
570
|
+
agentSourceDirs: opts.agentSourceDirs,
|
|
528
571
|
onWarning: opts.onWarning,
|
|
529
572
|
});
|
|
530
573
|
}
|
package/dist/core/assets.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
2
|
import { getPackageRoot } from './paths.js';
|
|
3
|
+
import { SKILL_REFS_OUTPUT_DIR } from './mds-variants.js';
|
|
3
4
|
/**
|
|
4
5
|
* Flat skills source directory: src/assets/skills/{name}/
|
|
5
6
|
* All plugins' skills live here directly (no per-plugin subdirectory).
|
|
@@ -10,9 +11,13 @@ export function skillsDir() {
|
|
|
10
11
|
/**
|
|
11
12
|
* Flat agents source directory: src/assets/agents/{name}.md
|
|
12
13
|
* All plugins' agents live here directly.
|
|
14
|
+
*
|
|
15
|
+
* @param root - Package root to resolve against. Injectable so a caller working
|
|
16
|
+
* on a temp tree (the test harness) reads the layout from here rather than
|
|
17
|
+
* spelling the path itself.
|
|
13
18
|
*/
|
|
14
|
-
export function agentsDir() {
|
|
15
|
-
return join(
|
|
19
|
+
export function agentsDir(root = getPackageRoot()) {
|
|
20
|
+
return join(root, 'src', 'assets', 'agents');
|
|
16
21
|
}
|
|
17
22
|
/**
|
|
18
23
|
* Flat rules source directory: src/assets/rules/{name}.md
|
|
@@ -36,4 +41,55 @@ export function scriptsDir() {
|
|
|
36
41
|
export function commandsDir() {
|
|
37
42
|
return join(getPackageRoot(), 'dist', 'commands');
|
|
38
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Compiled agents directory: dist/agents/{name}.md
|
|
46
|
+
*
|
|
47
|
+
* Output of the .mds generator hosts. The directory is absent until at least
|
|
48
|
+
* one generator host exists, so every reader must tolerate its absence.
|
|
49
|
+
*
|
|
50
|
+
* @param root - Package root to resolve against (see agentsDir).
|
|
51
|
+
*/
|
|
52
|
+
export function compiledAgentsDir(root = getPackageRoot()) {
|
|
53
|
+
return join(root, 'dist', 'agents');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compiled skill-reference directory: dist/skills/git/references/
|
|
57
|
+
*
|
|
58
|
+
* Output of the `.mds` reference modules — the generated `devflow:git` mechanics
|
|
59
|
+
* files, one per (provider, operation) pair under `tracker/{provider}/`. Like
|
|
60
|
+
* compiledAgentsDir(), the directory is absent until the build has run, so every
|
|
61
|
+
* reader must tolerate its absence.
|
|
62
|
+
*
|
|
63
|
+
* The spelling comes from SKILL_REFS_OUTPUT_DIR in src/core/mds-variants.ts —
|
|
64
|
+
* the build's own allowlist table — rather than being retyped here, so the
|
|
65
|
+
* destination has exactly one definition.
|
|
66
|
+
*
|
|
67
|
+
* @param root - Package root to resolve against (see agentsDir).
|
|
68
|
+
*/
|
|
69
|
+
export function compiledSkillRefsDir(root = getPackageRoot()) {
|
|
70
|
+
return join(root, ...SKILL_REFS_OUTPUT_DIR.split('/'));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Agent source directories, MOST-PREFERRED FIRST.
|
|
74
|
+
*
|
|
75
|
+
* The single owner of the dist-first agent-resolution policy: a generator
|
|
76
|
+
* host's compiled artifact in dist/agents/ supersedes a hand-authored file of
|
|
77
|
+
* the same name in src/assets/agents/. Every consumer reads the order from
|
|
78
|
+
* here — the installer's first-hit-wins resolve, loadShippedDefaults's
|
|
79
|
+
* first-wins merge, and the test harness's resolveAgentSource — so the
|
|
80
|
+
* convention is stated once and cannot drift apart between call sites.
|
|
81
|
+
*
|
|
82
|
+
* Order is invisible to the type system: a list spelled least-preferred-first
|
|
83
|
+
* still typechecks and silently inverts the answer. Consumers therefore take
|
|
84
|
+
* this list as-is and never re-spell it; tests/guards/agent-source-precedence
|
|
85
|
+
* pins that they agree.
|
|
86
|
+
*
|
|
87
|
+
* The non-empty tuple makes an empty list a compile error at every call site:
|
|
88
|
+
* an empty list would survive a `??` default and resolve to nothing.
|
|
89
|
+
*
|
|
90
|
+
* @param root - Package root to resolve against (see agentsDir).
|
|
91
|
+
*/
|
|
92
|
+
export function agentSourceDirs(root = getPackageRoot()) {
|
|
93
|
+
return [compiledAgentsDir(root), agentsDir(root)];
|
|
94
|
+
}
|
|
39
95
|
//# sourceMappingURL=assets.js.map
|
|
@@ -167,44 +167,44 @@ function collectMissingFragmentWarnings(fnName, activeFrameworks, fragments, omi
|
|
|
167
167
|
}
|
|
168
168
|
return warnings;
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* How a consumer picks framework references at run time (D-COMPLIANCE-REPO-LENS).
|
|
172
|
+
*
|
|
173
|
+
* Every framework reference is installed on every machine
|
|
174
|
+
* (D-COMPLIANCE-INSTALL-ALWAYS), so file presence no longer says which
|
|
175
|
+
* frameworks apply. The caller hands the agent `COMPLIANCE_FRAMEWORKS` — the
|
|
176
|
+
* `COMPLIANCE` field of the settings line, which folds this machine's selection
|
|
177
|
+
* with the repository's `.devflow/project.json` — and those ids alone are in force.
|
|
178
|
+
*/
|
|
179
|
+
const RUNTIME_SELECTION_LINES = [
|
|
180
|
+
'Every `references/{id}.md` is installed, so presence decides nothing: the ids you were given',
|
|
181
|
+
"(`COMPLIANCE_FRAMEWORKS`, this machine's plus the repository's) are the frameworks in force.",
|
|
182
|
+
'Load `references/{id}.md` for each given id and no other; `none` means generic controls only.',
|
|
183
|
+
'Apply generic controls always.',
|
|
184
|
+
'',
|
|
185
|
+
'NEVER fabricate framework-specific guidance for a framework you were not given.',
|
|
186
|
+
];
|
|
170
187
|
/**
|
|
171
188
|
* Build the ${DEVFLOW_COMPLIANCE_ACTIVE} substitution — the body of the
|
|
172
189
|
* Active Frameworks section.
|
|
173
190
|
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
191
|
+
* The stamp names the machine's own selection; zero frameworks is the neutral
|
|
192
|
+
* stamp a compliance-off machine's skill carries. Either way the section ends in
|
|
193
|
+
* RUNTIME_SELECTION_LINES, because the frameworks a run applies are the ids its
|
|
194
|
+
* caller passes, not the stamp.
|
|
178
195
|
*
|
|
179
196
|
* C5: a framework with no fragment still appears here — only its mapping row,
|
|
180
197
|
* checklist item and reference row are omitted.
|
|
181
198
|
*
|
|
182
|
-
* Frameworks the registry cannot label are dropped entirely:
|
|
183
|
-
*
|
|
199
|
+
* Frameworks the registry cannot label are dropped entirely: no label is emitted
|
|
200
|
+
* (see resolveRegistryFrameworks).
|
|
184
201
|
*/
|
|
185
202
|
function buildActiveSection(activeFrameworks) {
|
|
186
203
|
const resolved = resolveRegistryFrameworks(activeFrameworks);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
'NEVER fabricate framework-specific guidance for absent `references/{id}.md` files.',
|
|
192
|
-
'If no reference file is present for a framework, apply generic controls only.',
|
|
193
|
-
].join('\n');
|
|
194
|
-
}
|
|
195
|
-
const labels = resolved.map(fw => fw.label);
|
|
196
|
-
const refList = resolved.map(fw => `\`references/${fw.id}.md\``).join(' and ');
|
|
197
|
-
return [
|
|
198
|
-
`**Active: ${labels.join(', ')}.**`,
|
|
199
|
-
'',
|
|
200
|
-
`Load ${refList} for framework-specific controls. Apply generic controls always.`,
|
|
201
|
-
'',
|
|
202
|
-
'File presence in the installed skill directory is the authoritative signal: if a',
|
|
203
|
-
'`references/{id}.md` file is absent, treat that framework as inactive regardless of',
|
|
204
|
-
'this list.',
|
|
205
|
-
'',
|
|
206
|
-
'NEVER fabricate framework-specific guidance for absent `references/{id}.md` files.',
|
|
207
|
-
].join('\n');
|
|
204
|
+
const stamp = resolved.length === 0
|
|
205
|
+
? 'The machine declares no framework.'
|
|
206
|
+
: `**Machine frameworks: ${resolved.map(fw => fw.label).join(', ')}.**`;
|
|
207
|
+
return [stamp, '', ...RUNTIME_SELECTION_LINES].join('\n');
|
|
208
208
|
}
|
|
209
209
|
/**
|
|
210
210
|
* Build the ${DEVFLOW_COMPLIANCE_MAPPING} substitution — the ENTIRE Framework
|