devflow-kit 2.4.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.
- package/CHANGELOG.md +156 -0
- package/README.md +86 -18
- package/dist/agents/git.md +824 -0
- package/dist/cli/commands/agents.js +6 -1
- package/dist/cli/commands/attribution-prompts.js +1 -1
- package/dist/cli/commands/compliance-prompts.js +1 -1
- package/dist/cli/commands/compliance.js +23 -1
- package/dist/cli/commands/init-seed.js +24 -26
- package/dist/cli/commands/init.js +502 -71
- package/dist/cli/commands/install-report.js +205 -0
- package/dist/cli/commands/knowledge/index.js +2 -2
- package/dist/cli/commands/knowledge/toggle.js +27 -37
- package/dist/cli/commands/learning.js +37 -30
- package/dist/cli/commands/memory.js +79 -69
- package/dist/cli/commands/prompt-io.js +4 -4
- package/dist/cli/commands/security.js +76 -16
- package/dist/cli/commands/skills.js +53 -7
- package/dist/cli/commands/tracker-prompts.js +145 -0
- package/dist/cli/commands/tracker.js +405 -0
- package/dist/cli/commands/uninstall.js +211 -65
- package/dist/cli.js +2 -0
- package/dist/commands/bug-analysis.md +22 -4
- package/dist/commands/code-review.md +44 -15
- package/dist/commands/debug.md +20 -6
- package/dist/commands/dynamic-build.md +289 -67
- package/dist/commands/dynamic-plan.md +60 -21
- package/dist/commands/dynamic-profile.md +1 -1
- package/dist/commands/dynamic-tickets.md +58 -8
- package/dist/commands/explore.md +2 -2
- package/dist/commands/implement.md +241 -53
- package/dist/commands/plan.md +88 -17
- package/dist/commands/release.md +64 -17
- package/dist/commands/resolve.md +138 -58
- package/dist/commands/self-review.md +2 -2
- package/dist/core/agent-models.js +55 -12
- package/dist/core/assets.js +58 -2
- package/dist/core/evidence-policy.js +147 -0
- package/dist/core/feature-config.js +130 -64
- package/dist/core/feature-switch.js +112 -0
- package/dist/core/flags.js +4 -4
- package/dist/core/manifest.js +33 -7
- package/dist/core/mds-variants.js +861 -0
- package/dist/core/model-discovery.js +12 -1
- package/dist/core/plugins.js +357 -9
- package/dist/core/project-paths.js +1 -1
- 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/tracker.js +407 -0
- 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/installer.js +1213 -31
- package/dist/targets/claude-code/legacy.js +5 -0
- package/dist/targets/claude-code/post-install.js +196 -74
- package/dist/targets/claude-code/tracker-install.js +161 -0
- package/package.json +4 -3
- package/src/assets/agents/code.md +42 -4
- package/src/assets/agents/design.md +1 -1
- package/src/assets/agents/git.mds +827 -0
- package/src/assets/agents/knowledge.md +1 -1
- package/src/assets/agents/learning.md +11 -0
- package/src/assets/agents/synthesize.md +1 -1
- package/src/assets/agents/test.md +16 -5
- package/src/assets/agents/tracker.md +467 -0
- package/src/assets/agents/validate.md +7 -5
- package/src/assets/commands/_partials/_engine.mds +11 -9
- package/src/assets/commands/_partials/_evidence_policy.mds +30 -0
- package/src/assets/commands/_partials/_knowledge.mds +2 -2
- package/src/assets/commands/_partials/_plan_contract.mds +22 -7
- package/src/assets/commands/_partials/_preamble.mds +1 -1
- package/src/assets/commands/_partials/_publication.mds +3 -1
- 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 +15 -5
- package/src/assets/commands/code-review.mds +34 -14
- package/src/assets/commands/debug.mds +11 -4
- package/src/assets/commands/dynamic-build.mds +227 -41
- package/src/assets/commands/dynamic-plan.mds +35 -13
- package/src/assets/commands/dynamic-tickets.mds +47 -5
- package/src/assets/commands/implement.mds +206 -52
- package/src/assets/commands/plan.mds +70 -17
- package/src/assets/commands/release.md +64 -17
- package/src/assets/commands/resolve.mds +126 -56
- 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 +299 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +5 -8
- package/src/assets/scripts/hooks/background-memory-update +14 -9
- package/src/assets/scripts/hooks/capture-prompt +6 -2
- package/src/assets/scripts/hooks/capture-question +6 -2
- package/src/assets/scripts/hooks/capture-turn +6 -2
- package/src/assets/scripts/hooks/ensure-devflow-init +1 -1
- package/src/assets/scripts/hooks/ensure-root-gitignore +161 -60
- package/src/assets/scripts/hooks/hook-log-init +3 -1
- package/src/assets/scripts/hooks/json-helper.cjs +223 -5
- package/src/assets/scripts/hooks/lib/project-paths.cjs +1 -1
- package/src/assets/scripts/hooks/memory-worker +15 -8
- package/src/assets/scripts/hooks/pre-compact-memory +12 -8
- package/src/assets/scripts/hooks/preamble +1 -4
- package/src/assets/scripts/hooks/queue-append +68 -24
- package/src/assets/scripts/hooks/session-start-context +355 -8
- package/src/assets/scripts/hooks/session-start-memory +12 -8
- 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 +1065 -0
- package/src/assets/scripts/verify-evidence.cjs +1822 -0
- package/src/assets/skills/compliance/SKILL.md +2 -0
- package/src/assets/skills/docs-framework/SKILL.md +5 -3
- 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/agents/git.md +0 -938
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
* Users who skip major versions should run uninstall + reinstall.
|
|
8
8
|
*
|
|
9
9
|
* Organized by era to make scanning for duplicates tractable.
|
|
10
|
+
*
|
|
11
|
+
* These lists are FROZEN deletion manifests for names that once shipped: the
|
|
12
|
+
* `*_V2` suffix is the frozen spelling of an era, not a version to bump, and a
|
|
13
|
+
* name is removed from a list only when its pruning window has passed — never
|
|
14
|
+
* renamed to match a current registry name (avoids PF-012).
|
|
10
15
|
*/
|
|
11
16
|
/** Pre-v1.0.0: devflow- prefixed skill names from the original install scheme. */
|
|
12
17
|
const LEGACY_SKILLS_PRE_V1 = [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { promises as fs, writeFileSync, unlinkSync } from 'fs';
|
|
2
|
-
import {
|
|
2
|
+
import { execFileSync } from 'child_process';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import * as os from 'os';
|
|
5
5
|
import * as p from '@clack/prompts';
|
|
@@ -25,6 +25,28 @@ export function computeGitignoreAppend(existingContent, entries) {
|
|
|
25
25
|
const existingLines = existingContent.split('\n').map(l => l.trim());
|
|
26
26
|
return entries.filter(entry => !existingLines.includes(entry));
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Sentinel line whose presence means the current (v3-and-later) carve-out block is
|
|
30
|
+
* installed. Devflow-unique: no user writes `!.devflow/conventions.md` by hand.
|
|
31
|
+
*/
|
|
32
|
+
const DEVFLOW_GITIGNORE_SENTINEL_V3 = '!.devflow/conventions.md';
|
|
33
|
+
/** Sentinel line whose presence means the v2 carve-out block is installed (no conventions.md line). */
|
|
34
|
+
const DEVFLOW_GITIGNORE_SENTINEL_V2 = '!.devflow/features/*/KNOWLEDGE.md';
|
|
35
|
+
/**
|
|
36
|
+
* The block's final line: ignore the devflow-managed `.claudeignore` file.
|
|
37
|
+
* NOT a sentinel — users legitimately author this line themselves.
|
|
38
|
+
*/
|
|
39
|
+
const CLAUDEIGNORE_LINE = '.claudeignore';
|
|
40
|
+
/** A user's explicit un-ignore of `.claudeignore`; never overridden. */
|
|
41
|
+
const CLAUDEIGNORE_NEGATION = '!.claudeignore';
|
|
42
|
+
/**
|
|
43
|
+
* Re-includes the team-owned evidence policy file (D-GITIGNORE-V5). A COMPLETION
|
|
44
|
+
* line, never a presence sentinel: users may author it themselves, so its presence
|
|
45
|
+
* proves nothing about the devflow block (avoids PF-059). It sits after `.devflow/*`
|
|
46
|
+
* (which it overrides under last-match-wins) and before `.claudeignore`, so the
|
|
47
|
+
* block's final line stays `.claudeignore`.
|
|
48
|
+
*/
|
|
49
|
+
const DEVFLOW_POLICY_LINE = '!.devflow/policy.json';
|
|
28
50
|
/**
|
|
29
51
|
* The shared .devflow/ gitignore block. Everything under .devflow/ is local
|
|
30
52
|
* (memory, learning, docs, locks) EXCEPT:
|
|
@@ -32,6 +54,9 @@ export function computeGitignoreAppend(existingContent, entries) {
|
|
|
32
54
|
* + committed (the Knowledge agent commits them at workflow end).
|
|
33
55
|
* - conventions.md: naming-convention authority written by the Git learn-conventions
|
|
34
56
|
* operation; GIT-TRACKED so the team shares a single naming source.
|
|
57
|
+
* - policy.json: the team-owned evidence policy, read from the default branch by
|
|
58
|
+
* resolve-evidence-policy.cjs; GIT-TRACKED so a team can commit it without `git add -f`.
|
|
59
|
+
* Devflow never writes it.
|
|
35
60
|
*
|
|
36
61
|
* Re-including files under an ignored tree needs a `dir/*` + `!dir/keep` pair at
|
|
37
62
|
* each level — a bare `.devflow/` excludes the directory so git never descends and
|
|
@@ -39,65 +64,121 @@ export function computeGitignoreAppend(existingContent, entries) {
|
|
|
39
64
|
*
|
|
40
65
|
* Kept BYTE-IDENTICAL to the block emitted by src/assets/scripts/hooks/ensure-root-gitignore
|
|
41
66
|
* so the init-time path and the always-on hook path produce the same file.
|
|
42
|
-
*
|
|
43
|
-
* D-GITIGNORE-V3: v3 of the carve-out block (adds !.devflow/conventions.md).
|
|
44
67
|
*/
|
|
45
|
-
|
|
68
|
+
const DEVFLOW_GITIGNORE_BLOCK_LINES = [
|
|
46
69
|
'# Devflow runtime data — local by default (memory, learning, docs, locks).',
|
|
47
|
-
'#
|
|
48
|
-
'#
|
|
49
|
-
'#
|
|
50
|
-
'# to your own .gitignore.',
|
|
70
|
+
'# Shared via git: feature knowledge bases under .devflow/features/ (index.md and',
|
|
71
|
+
'# every {slug}/KNOWLEDGE.md), .devflow/conventions.md (naming authority) and',
|
|
72
|
+
'# .devflow/policy.json (evidence policy). To stop sharing the first two, re-add',
|
|
73
|
+
'# `.devflow/features/` or `.devflow/conventions.md` to your own .gitignore.',
|
|
51
74
|
'.devflow/*',
|
|
52
75
|
'!.devflow/features/',
|
|
53
76
|
'.devflow/features/*',
|
|
54
77
|
'!.devflow/features/index.md',
|
|
55
78
|
'!.devflow/features/*/',
|
|
56
79
|
'.devflow/features/*/*',
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
const
|
|
80
|
+
DEVFLOW_GITIGNORE_SENTINEL_V2,
|
|
81
|
+
DEVFLOW_GITIGNORE_SENTINEL_V3,
|
|
82
|
+
DEVFLOW_POLICY_LINE,
|
|
83
|
+
CLAUDEIGNORE_LINE,
|
|
84
|
+
];
|
|
85
|
+
/** The full carve-out block, `.claudeignore` line included. */
|
|
86
|
+
export const DEVFLOW_GITIGNORE_BLOCK = DEVFLOW_GITIGNORE_BLOCK_LINES.join('\n');
|
|
87
|
+
/**
|
|
88
|
+
* The carve-out block without its final `.claudeignore` line — emitted instead of
|
|
89
|
+
* the full block when the target .gitignore already carries a `.claudeignore` or
|
|
90
|
+
* `!.claudeignore` entry of the user's own.
|
|
91
|
+
*/
|
|
92
|
+
export const DEVFLOW_GITIGNORE_BLOCK_WITHOUT_CLAUDEIGNORE = DEVFLOW_GITIGNORE_BLOCK_LINES.slice(0, -1).join('\n');
|
|
64
93
|
/** The legacy wholesale comment our pre-carve-out writers emitted. */
|
|
65
94
|
const LEGACY_DEVFLOW_COMMENT = '# Devflow runtime data (local by default; remove to share via git)';
|
|
66
95
|
/**
|
|
67
96
|
* PURE: given existing .gitignore content, return the content that ignores
|
|
68
|
-
* `.devflow/` with the feature-knowledge + conventions.md carve-out —
|
|
69
|
-
* when no change is needed. Idempotent: feeding its own output back returns `null`.
|
|
97
|
+
* `.devflow/` with the feature-knowledge + conventions.md + policy.json carve-out —
|
|
98
|
+
* or `null` when no change is needed. Idempotent: feeding its own output back returns `null`.
|
|
99
|
+
*
|
|
100
|
+
* D-GITIGNORE-V5: the block is detected ONLY by its own devflow-unique sentinel
|
|
101
|
+
* (`!.devflow/conventions.md`). `.claudeignore` and `!.devflow/policy.json` are
|
|
102
|
+
* COMPLETION lines — users legitimately author both themselves — so each is topped up
|
|
103
|
+
* when missing and never read as proof the block exists. A presence check on a
|
|
104
|
+
* user-authored line inverts both halves of the contract: projects that already carry
|
|
105
|
+
* that line are told the block is installed when it is not, and a user's
|
|
106
|
+
* `!.claudeignore` un-ignore is silently reversed by re-appending `.claudeignore`
|
|
107
|
+
* under last-match-wins (avoids PF-059).
|
|
108
|
+
*
|
|
109
|
+
* `hasClaudeignoreEntry` is true when some whole line, trimmed, is exactly
|
|
110
|
+
* `.claudeignore` OR `!.claudeignore`. Treating both forms as "present" both honours
|
|
111
|
+
* an un-ignore and makes every branch converge on re-run. `hasPolicyLine` is true when
|
|
112
|
+
* some whole line, trimmed, is exactly `!.devflow/policy.json`. The missing completion
|
|
113
|
+
* lines, in block order, are [policy line, `.claudeignore` (only when
|
|
114
|
+
* `!hasClaudeignoreEntry`)].
|
|
70
115
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
116
|
+
* 1. A `/.devflow/` line present → `null` (user opt-out; respect manual config).
|
|
117
|
+
* 2. v3 sentinel present → append the missing completion lines; `null` when none are
|
|
118
|
+
* missing. This is the v4→v5 upgrade: a v4 block gains only the policy line, after
|
|
119
|
+
* its `.claudeignore` line, and keeps its old comment.
|
|
120
|
+
* 3. v2 sentinel present, no v3 → append `!.devflow/conventions.md` followed by the
|
|
121
|
+
* missing completion lines.
|
|
122
|
+
* 4. Legacy bare `.devflow/` present → strip it (+ our old comment), then append the
|
|
123
|
+
* block; no block at all → append the block. The block is emitted MINUS its final
|
|
124
|
+
* `.claudeignore` line when `hasClaudeignoreEntry`. A user's own policy line is
|
|
125
|
+
* duplicated harmlessly here, and the re-run is a no-op.
|
|
126
|
+
* 5. Neither completion line is ever a sentinel. The marker file
|
|
127
|
+
* (`.devflow/.root-gitignore-configured-v5`) is a fast-path claim, never proof.
|
|
128
|
+
*
|
|
129
|
+
* Line matching is whole-line, whitespace-tolerant, exact text — never substring.
|
|
130
|
+
* Both append forms are mirrored byte-for-byte in the shell twin
|
|
131
|
+
* (src/assets/scripts/hooks/ensure-root-gitignore), which is what the cross-implementation
|
|
132
|
+
* parity table in tests/shell-hooks.test.ts pins.
|
|
76
133
|
*/
|
|
77
134
|
export function computeDevflowGitignore(existingContent) {
|
|
78
135
|
const lines = existingContent.split('\n');
|
|
79
136
|
const trimmed = lines.map(l => l.trim());
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
137
|
+
const hasClaudeignoreEntry = trimmed.some(l => l === CLAUDEIGNORE_LINE || l === CLAUDEIGNORE_NEGATION);
|
|
138
|
+
const hasPolicyLine = trimmed.includes(DEVFLOW_POLICY_LINE);
|
|
139
|
+
/** The block-completing lines this file lacks, in block order. */
|
|
140
|
+
const missingCompletionLines = [
|
|
141
|
+
...(hasPolicyLine ? [] : [DEVFLOW_POLICY_LINE]),
|
|
142
|
+
...(hasClaudeignoreEntry ? [] : [CLAUDEIGNORE_LINE]),
|
|
143
|
+
];
|
|
144
|
+
/**
|
|
145
|
+
* Continue an existing devflow block with the lines it is missing. One newline
|
|
146
|
+
* guard, no blank separator — the appended lines belong to the block above them.
|
|
147
|
+
*/
|
|
148
|
+
const appendLines = (body, block) => `${body}${body.endsWith('\n') ? '' : '\n'}${block}\n`;
|
|
149
|
+
/**
|
|
150
|
+
* Start a new block after unrelated content: one blank separator line. Existing
|
|
151
|
+
* trailing newlines are preserved verbatim (no trimEnd, no blank-line dedupe) so
|
|
152
|
+
* the shell twin's `tail -c 1` guard produces the identical bytes.
|
|
153
|
+
*/
|
|
154
|
+
const appendBlock = (body, block) => body.length === 0
|
|
155
|
+
? `${block}\n`
|
|
156
|
+
: `${body}${body.endsWith('\n') ? '' : '\n'}\n${block}\n`;
|
|
157
|
+
// 1. User opt-out wins over every sentinel.
|
|
158
|
+
if (trimmed.includes('/.devflow/'))
|
|
83
159
|
return null;
|
|
84
|
-
//
|
|
85
|
-
|
|
160
|
+
// 2. Block installed (v3 and later) — top up only the completion lines it lacks.
|
|
161
|
+
if (trimmed.includes(DEVFLOW_GITIGNORE_SENTINEL_V3)) {
|
|
162
|
+
return missingCompletionLines.length === 0
|
|
163
|
+
? null
|
|
164
|
+
: appendLines(existingContent, missingCompletionLines.join('\n'));
|
|
165
|
+
}
|
|
166
|
+
// 3. v2 block installed — append the lines it lacks, in block order.
|
|
86
167
|
if (trimmed.includes(DEVFLOW_GITIGNORE_SENTINEL_V2)) {
|
|
87
|
-
|
|
88
|
-
return `${existingContent}${sep}${DEVFLOW_GITIGNORE_SENTINEL_V3}\n`;
|
|
168
|
+
return appendLines(existingContent, [DEVFLOW_GITIGNORE_SENTINEL_V3, ...missingCompletionLines].join('\n'));
|
|
89
169
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
170
|
+
// 4. No devflow block — install one, respecting any .claudeignore entry of the user's own.
|
|
171
|
+
const block = hasClaudeignoreEntry
|
|
172
|
+
? DEVFLOW_GITIGNORE_BLOCK_WITHOUT_CLAUDEIGNORE
|
|
173
|
+
: DEVFLOW_GITIGNORE_BLOCK;
|
|
174
|
+
if (trimmed.includes('.devflow/')) {
|
|
94
175
|
// Upgrade our legacy wholesale entry: drop the bare line + old comment, append block.
|
|
95
176
|
const kept = lines
|
|
96
177
|
.filter(l => l.trim() !== '.devflow/' && l.trim() !== LEGACY_DEVFLOW_COMMENT)
|
|
97
178
|
.join('\n');
|
|
98
|
-
return
|
|
179
|
+
return appendBlock(kept, block);
|
|
99
180
|
}
|
|
100
|
-
return
|
|
181
|
+
return appendBlock(existingContent, block);
|
|
101
182
|
}
|
|
102
183
|
/**
|
|
103
184
|
* Merge Devflow deny entries into an existing settings JSON object.
|
|
@@ -531,11 +612,14 @@ export async function installManagedSettings(rootDir, verbose) {
|
|
|
531
612
|
return false;
|
|
532
613
|
}
|
|
533
614
|
try {
|
|
534
|
-
|
|
535
|
-
//
|
|
615
|
+
execFileSync('sudo', ['mkdir', '-p', managedDir], { stdio: 'inherit' });
|
|
616
|
+
// Stage the JSON in a file and copy it with sudo. Every sudo call takes an
|
|
617
|
+
// argv (execFileSync, no shell), so no path is ever re-parsed by a shell — a
|
|
618
|
+
// package root under a home directory holding a quote cannot break or extend
|
|
619
|
+
// the root command.
|
|
536
620
|
const tmpFile = path.join(rootDir, '.managed-settings-tmp.json');
|
|
537
621
|
await fs.writeFile(tmpFile, content, 'utf-8');
|
|
538
|
-
|
|
622
|
+
execFileSync('sudo', ['cp', tmpFile, managedPath], { stdio: 'inherit' });
|
|
539
623
|
await fs.rm(tmpFile, { force: true });
|
|
540
624
|
if (verbose) {
|
|
541
625
|
p.log.success(`Managed settings written to ${managedPath} (via sudo)`);
|
|
@@ -557,14 +641,26 @@ export async function installManagedSettings(rootDir, verbose) {
|
|
|
557
641
|
* 1. Try direct write/delete
|
|
558
642
|
* 2. If EACCES and TTY, ask user before sudo
|
|
559
643
|
* 3. Non-TTY: return false (caller logs preservation message)
|
|
644
|
+
*
|
|
645
|
+
* `managedPathOverride` exists so a test can point the removal at a temp file;
|
|
646
|
+
* production callers omit it and get the platform's system path. It is a
|
|
647
|
+
* parameter, deliberately not an environment variable: this function may run
|
|
648
|
+
* `sudo rm` / `sudo cp` on the path, and an env-selectable target would let
|
|
649
|
+
* whoever controls the environment aim a root write the user consented to for
|
|
650
|
+
* "managed settings" at any file.
|
|
560
651
|
*/
|
|
561
|
-
export async function removeManagedSettings(rootDir, verbose) {
|
|
652
|
+
export async function removeManagedSettings(rootDir, verbose, managedPathOverride) {
|
|
562
653
|
let managedPath;
|
|
563
|
-
|
|
564
|
-
managedPath =
|
|
654
|
+
if (managedPathOverride !== undefined) {
|
|
655
|
+
managedPath = managedPathOverride;
|
|
565
656
|
}
|
|
566
|
-
|
|
567
|
-
|
|
657
|
+
else {
|
|
658
|
+
try {
|
|
659
|
+
managedPath = getManagedSettingsPath();
|
|
660
|
+
}
|
|
661
|
+
catch {
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
568
664
|
}
|
|
569
665
|
let existingContent;
|
|
570
666
|
try {
|
|
@@ -639,12 +735,12 @@ export async function removeManagedSettings(rootDir, verbose) {
|
|
|
639
735
|
}
|
|
640
736
|
try {
|
|
641
737
|
if (shouldDelete) {
|
|
642
|
-
|
|
738
|
+
execFileSync('sudo', ['rm', managedPath], { stdio: 'inherit' });
|
|
643
739
|
}
|
|
644
740
|
else {
|
|
645
741
|
const tmpFile = path.join(rootDir, '.managed-settings-tmp.json');
|
|
646
742
|
await fs.writeFile(tmpFile, updatedContent, 'utf-8');
|
|
647
|
-
|
|
743
|
+
execFileSync('sudo', ['cp', tmpFile, managedPath], { stdio: 'inherit' });
|
|
648
744
|
await fs.rm(tmpFile, { force: true });
|
|
649
745
|
}
|
|
650
746
|
if (verbose) {
|
|
@@ -949,45 +1045,73 @@ export async function updateGitignore(gitRoot, verbose) {
|
|
|
949
1045
|
}
|
|
950
1046
|
}
|
|
951
1047
|
}
|
|
952
|
-
/**
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1048
|
+
/**
|
|
1049
|
+
* Current carve-out marker version. Bump when the block format changes — together
|
|
1050
|
+
* with the shell twin's stamp (ensure-root-gitignore) and the ensure-devflow-init
|
|
1051
|
+
* fast path, in one commit (D-GITIGNORE-V5).
|
|
1052
|
+
*/
|
|
1053
|
+
const GITIGNORE_MARKER_V5 = '.root-gitignore-configured-v5';
|
|
1054
|
+
/**
|
|
1055
|
+
* Earlier markers, the unversioned (v1) one included — every one is removed
|
|
1056
|
+
* whenever the project is v5-stamped, on the fast path too: an older devflow can
|
|
1057
|
+
* re-stamp one beside v5, and the shell twin drops the same four.
|
|
1058
|
+
*/
|
|
1059
|
+
const LEGACY_GITIGNORE_MARKERS = [
|
|
1060
|
+
'.root-gitignore-configured-v4',
|
|
1061
|
+
'.root-gitignore-configured-v3',
|
|
1062
|
+
'.root-gitignore-configured-v2',
|
|
1063
|
+
'.root-gitignore-configured',
|
|
1064
|
+
];
|
|
1065
|
+
/** Remove every legacy marker; an absent one is a no-op. Call only once v5 is stamped. */
|
|
1066
|
+
async function removeLegacyGitignoreMarkers(devflowDir) {
|
|
1067
|
+
for (const legacy of LEGACY_GITIGNORE_MARKERS) {
|
|
1068
|
+
try {
|
|
1069
|
+
await fs.rm(path.join(devflowDir, legacy), { force: true });
|
|
1070
|
+
}
|
|
1071
|
+
catch { /* ok if absent */ }
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
956
1074
|
/**
|
|
957
1075
|
* Deterministically ensure the project root .gitignore applies the `.devflow/`
|
|
958
|
-
* carve-out (local by default
|
|
1076
|
+
* carve-out (local by default; feature knowledge, conventions.md and the evidence
|
|
1077
|
+
* policy shared via git).
|
|
959
1078
|
*
|
|
960
1079
|
* Manages ONLY `.devflow/` — never `.claude/` — because user-scope installs must
|
|
961
1080
|
* not gitignore `.claude/`. This is the init-time counterpart to the always-on
|
|
962
|
-
* src/assets/scripts/hooks/ensure-root-gitignore shell helper; both
|
|
963
|
-
*
|
|
964
|
-
*
|
|
965
|
-
*
|
|
1081
|
+
* src/assets/scripts/hooks/ensure-root-gitignore shell helper; both resolve the same
|
|
1082
|
+
* shape for a given .gitignore — DEVFLOW_GITIGNORE_BLOCK, or
|
|
1083
|
+
* DEVFLOW_GITIGNORE_BLOCK_WITHOUT_CLAUDEIGNORE when the project owns that entry — and
|
|
1084
|
+
* emit identical bytes, so the two paths are byte-compatible and mutually idempotent.
|
|
1085
|
+
* Called unconditionally (independent of install scope and every feature toggle)
|
|
1086
|
+
* whenever a git root is known.
|
|
966
1087
|
*
|
|
967
|
-
* Uses a versioned marker file (`.devflow/.root-gitignore-configured-
|
|
968
|
-
* detection — the same pattern as the shell twin.
|
|
969
|
-
*
|
|
1088
|
+
* Uses a versioned project-local marker file (`.devflow/.root-gitignore-configured-v5`)
|
|
1089
|
+
* for fast-path detection — the same pattern as the shell twin. The marker is a claim,
|
|
1090
|
+
* not proof, so even a marked install re-reads .gitignore and re-runs
|
|
1091
|
+
* computeDevflowGitignore; bumping the version forces a re-run once per install, which
|
|
1092
|
+
* is how a v4-marked project gains the policy line and is re-stamped v5.
|
|
970
1093
|
*
|
|
971
|
-
* Idempotent:
|
|
972
|
-
*
|
|
1094
|
+
* Idempotent: computeDevflowGitignore returns null for a converged file, so a
|
|
1095
|
+
* marked install performs one read and no write. Errors are swallowed
|
|
1096
|
+
* (verbose-logged) — a gitignore write must never abort init.
|
|
973
1097
|
*/
|
|
974
1098
|
export async function ensureDevflowGitignore(gitRoot, verbose) {
|
|
975
1099
|
try {
|
|
976
1100
|
const devflowDir = path.join(gitRoot, '.devflow');
|
|
977
|
-
const
|
|
1101
|
+
const markerV5 = path.join(devflowDir, GITIGNORE_MARKER_V5);
|
|
978
1102
|
const gitignorePath = path.join(gitRoot, '.gitignore');
|
|
979
|
-
// Fast-path with verification:
|
|
1103
|
+
// Fast-path with verification: v5 marker normally means the block is installed,
|
|
980
1104
|
// but the marker is a claim, not proof — a merge-conflict resolution may have
|
|
981
1105
|
// dropped the block. Even when the marker exists, read .gitignore (one cheap
|
|
982
1106
|
// read) and run computeDevflowGitignore; write only when it returns non-null.
|
|
983
|
-
// Idempotent:
|
|
984
|
-
let
|
|
1107
|
+
// Idempotent: converged file → computeDevflowGitignore returns null → no write.
|
|
1108
|
+
let v5Marked = false;
|
|
985
1109
|
try {
|
|
986
|
-
await fs.access(
|
|
987
|
-
|
|
1110
|
+
await fs.access(markerV5);
|
|
1111
|
+
v5Marked = true;
|
|
988
1112
|
}
|
|
989
1113
|
catch { /* absent */ }
|
|
990
|
-
if (
|
|
1114
|
+
if (v5Marked) {
|
|
991
1115
|
let existingContent = '';
|
|
992
1116
|
try {
|
|
993
1117
|
existingContent = await fs.readFile(gitignorePath, 'utf-8');
|
|
@@ -997,9 +1121,10 @@ export async function ensureDevflowGitignore(gitRoot, verbose) {
|
|
|
997
1121
|
if (healContent !== null) {
|
|
998
1122
|
await fs.writeFile(gitignorePath, healContent, 'utf-8');
|
|
999
1123
|
if (verbose) {
|
|
1000
|
-
p.log.success('.gitignore configured (.devflow/ local; feature knowledge + conventions shared)');
|
|
1124
|
+
p.log.success('.gitignore configured (.devflow/ local; feature knowledge + conventions + evidence policy shared)');
|
|
1001
1125
|
}
|
|
1002
1126
|
}
|
|
1127
|
+
await removeLegacyGitignoreMarkers(devflowDir);
|
|
1003
1128
|
return;
|
|
1004
1129
|
}
|
|
1005
1130
|
let gitignoreContent = '';
|
|
@@ -1011,16 +1136,13 @@ export async function ensureDevflowGitignore(gitRoot, verbose) {
|
|
|
1011
1136
|
if (newContent !== null) {
|
|
1012
1137
|
await fs.writeFile(gitignorePath, newContent, 'utf-8');
|
|
1013
1138
|
if (verbose) {
|
|
1014
|
-
p.log.success('.gitignore configured (.devflow/ local; feature knowledge + conventions shared)');
|
|
1139
|
+
p.log.success('.gitignore configured (.devflow/ local; feature knowledge + conventions + evidence policy shared)');
|
|
1015
1140
|
}
|
|
1016
1141
|
}
|
|
1017
|
-
// Stamp
|
|
1142
|
+
// Stamp v5 marker so subsequent runs fast-path; drop every legacy marker.
|
|
1018
1143
|
await fs.mkdir(devflowDir, { recursive: true });
|
|
1019
|
-
await fs.writeFile(
|
|
1020
|
-
|
|
1021
|
-
await fs.rm(path.join(devflowDir, GITIGNORE_MARKER_V2), { force: true });
|
|
1022
|
-
}
|
|
1023
|
-
catch { /* ok if absent */ }
|
|
1144
|
+
await fs.writeFile(markerV5, '', 'utf-8');
|
|
1145
|
+
await removeLegacyGitignoreMarkers(devflowDir);
|
|
1024
1146
|
}
|
|
1025
1147
|
catch (error) {
|
|
1026
1148
|
if (verbose) {
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracker artifact installer for the Claude Code target.
|
|
3
|
+
*
|
|
4
|
+
* Convergence function for the ONE artifact whose presence advertises the
|
|
5
|
+
* selected tracker provider: the Tracker agent file. The generated reference
|
|
6
|
+
* subtree converges too, but through {@link overlayInstalledReferences} in
|
|
7
|
+
* installer.ts — that is the installer's one overlay spelling, and a mode flag
|
|
8
|
+
* on this function would have made it a second (design review M2).
|
|
9
|
+
*
|
|
10
|
+
* Applies ADR-013: I/O orchestration in src/targets/; pure helpers in src/core/.
|
|
11
|
+
* Applies PF-009: warn-not-throw, so one failing artifact never aborts an install.
|
|
12
|
+
* Applies PF-015: the biconditional converges in BOTH directions — selecting a
|
|
13
|
+
* provider installs the agent, returning to github removes it.
|
|
14
|
+
*/
|
|
15
|
+
import { promises as fs } from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import { agentSourceDirs } from '../../core/assets.js';
|
|
18
|
+
import { mdFileName } from '../../core/orphan-sweep.js';
|
|
19
|
+
/**
|
|
20
|
+
* The agent whose presence is conditional on the provider.
|
|
21
|
+
*
|
|
22
|
+
* It stays DECLARED in `devflow-core-skills.agents` and is filtered at install
|
|
23
|
+
* time rather than being lifted into a feature-owned set: the compliance
|
|
24
|
+
* precedent does not transfer, because compliance's plugin was deleted while
|
|
25
|
+
* `devflow-core-skills` is a live, non-optional owner. A feature-owned set would
|
|
26
|
+
* cost three new union sites and a rewrite of the pinned agent-roster floor to
|
|
27
|
+
* solve a problem the agent SWEEP does not have — the sweep keys on the full
|
|
28
|
+
* registry, so it never sees this file as an orphan.
|
|
29
|
+
*
|
|
30
|
+
* Exported because the filter has more than one reader and may have only one
|
|
31
|
+
* authority (D-TRACKER-AGENT-OWNER): {@link convergeTrackerArtifacts} below,
|
|
32
|
+
* which decides whether the file exists, and, in installer.ts, both the generic
|
|
33
|
+
* agent copy loop — which has to skip the one agent it does not own — and the
|
|
34
|
+
* full-install pre-clean, which has to empty the agent directory around it. A
|
|
35
|
+
* second literal at any of those sites is the shape this export exists to forbid.
|
|
36
|
+
*/
|
|
37
|
+
export const TRACKER_AGENT_NAME = 'tracker';
|
|
38
|
+
// ── Internals ──────────────────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* The provider whose mechanics need no Tracker agent.
|
|
41
|
+
*
|
|
42
|
+
* GitHub conventions are not inferred: the Git agent runs `gh`, whose issue
|
|
43
|
+
* grammar this repo already speaks. The agent exists to infer conventions from a
|
|
44
|
+
* connected tool-call server, which is a thing only the other providers have.
|
|
45
|
+
*/
|
|
46
|
+
const AGENTLESS_PROVIDER = 'github';
|
|
47
|
+
function agentTarget(claudeDir) {
|
|
48
|
+
return path.join(claudeDir, 'agents', 'devflow', mdFileName(TRACKER_AGENT_NAME));
|
|
49
|
+
}
|
|
50
|
+
async function pathExists(p) {
|
|
51
|
+
try {
|
|
52
|
+
await fs.access(p);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** First path in `candidates` that exists, or undefined when none do. */
|
|
60
|
+
async function firstExisting(candidates) {
|
|
61
|
+
for (const candidate of candidates) {
|
|
62
|
+
if (await pathExists(candidate))
|
|
63
|
+
return candidate;
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Would copying `source` over `target` change anything?
|
|
69
|
+
*
|
|
70
|
+
* Asked once, between resolving the source and copying it, so a run that would write a
|
|
71
|
+
* byte-identical copy of the installed agent reports `unchanged` instead of `installed`
|
|
72
|
+
* — the same question the reference overlay asks per unit, for the same reason: a
|
|
73
|
+
* summary that can only ever say "installed" says nothing, and a steady-state re-init
|
|
74
|
+
* announcing "tracker agent installed" is the noise this closes (QA S2).
|
|
75
|
+
*
|
|
76
|
+
* It does NOT weaken the self-heal. A hand-edited or truncated agent differs from its
|
|
77
|
+
* source, so it is copied and reported as written; only an identical file is skipped,
|
|
78
|
+
* and skipping a copy of what is already there changes nothing on disk.
|
|
79
|
+
*
|
|
80
|
+
* Any error — an absent target, an unreadable one — answers "no". The fallback is the
|
|
81
|
+
* copy that was going to happen anyway, so a failure to compare costs a write, never
|
|
82
|
+
* correctness.
|
|
83
|
+
*/
|
|
84
|
+
async function copyWouldChangeNothing(source, target) {
|
|
85
|
+
try {
|
|
86
|
+
const [from, to] = await Promise.all([fs.readFile(source), fs.readFile(target)]);
|
|
87
|
+
return from.equals(to);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// ── Convergence ────────────────────────────────────────────────────────────
|
|
94
|
+
/**
|
|
95
|
+
* Converge the Tracker agent file onto the resolved provider.
|
|
96
|
+
*
|
|
97
|
+
* Convergence matrix:
|
|
98
|
+
* provider !== github → copy the agent in, unless the installed file is already
|
|
99
|
+
* byte-identical to the source. Compared rather than trusted
|
|
100
|
+
* for existing, so a truncated or hand-edited file self-heals;
|
|
101
|
+
* compared rather than re-copied blind, so a run that changes
|
|
102
|
+
* nothing reports `unchanged` and the summary stays quiet
|
|
103
|
+
* (see {@link copyWouldChangeNothing})
|
|
104
|
+
* provider === github → remove it, absent or not
|
|
105
|
+
*
|
|
106
|
+
* Never throws. A caller gates on `converged`; it does not catch. The one
|
|
107
|
+
* refusal that is not an I/O degradation — a claudeDir that is not absolute —
|
|
108
|
+
* is reported the same way rather than thrown, because it reaches here from a
|
|
109
|
+
* manifest read and an install must not die on it.
|
|
110
|
+
*/
|
|
111
|
+
export async function convergeTrackerArtifacts(opts) {
|
|
112
|
+
const { claudeDir, provider, warn } = opts;
|
|
113
|
+
// Precondition, asserted in production code rather than only in tests: an
|
|
114
|
+
// empty or relative claudeDir would make the target resolve somewhere
|
|
115
|
+
// unexpected, and the removal branch runs fs.rm against it.
|
|
116
|
+
if (!path.isAbsolute(claudeDir)) {
|
|
117
|
+
warn(`tracker: claudeDir is not an absolute path ("${claudeDir}") — skipping convergence`);
|
|
118
|
+
return { converged: false, agentPresent: false, agent: 'unchanged' };
|
|
119
|
+
}
|
|
120
|
+
const target = agentTarget(claudeDir);
|
|
121
|
+
if (provider === AGENTLESS_PROVIDER) {
|
|
122
|
+
const existed = await pathExists(target);
|
|
123
|
+
if (!existed)
|
|
124
|
+
return { converged: true, agentPresent: false, agent: 'unchanged' };
|
|
125
|
+
try {
|
|
126
|
+
await fs.rm(target, { force: true });
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
warn(`tracker: failed to remove the Tracker agent (${target}) — ${String(err)}`);
|
|
130
|
+
return { converged: false, agentPresent: true, agent: 'unchanged' };
|
|
131
|
+
}
|
|
132
|
+
return { converged: true, agentPresent: false, agent: 'removed' };
|
|
133
|
+
}
|
|
134
|
+
const dirs = opts.agentSourceDirs ?? agentSourceDirs();
|
|
135
|
+
const candidates = dirs.map(dir => path.join(dir, mdFileName(TRACKER_AGENT_NAME)));
|
|
136
|
+
const source = await firstExisting(candidates);
|
|
137
|
+
if (source === undefined) {
|
|
138
|
+
warn(`tracker: agent source not found for "${TRACKER_AGENT_NAME}" (searched: ${candidates.join(', ')}) — ` +
|
|
139
|
+
`run \`npm run build:mds\` if it is compiled from an .mds generator host`);
|
|
140
|
+
// Probed rather than assumed: a previous run may have left a copy that is
|
|
141
|
+
// still spawnable, and that is the difference between "this run did nothing"
|
|
142
|
+
// and "there is nothing there".
|
|
143
|
+
return { converged: false, agentPresent: await pathExists(target), agent: 'unchanged' };
|
|
144
|
+
}
|
|
145
|
+
// Already converged — nothing to write, and nothing for the summary to announce.
|
|
146
|
+
// The directory is not created either: an identical file at the target means it is
|
|
147
|
+
// already there (see {@link copyWouldChangeNothing}).
|
|
148
|
+
if (await copyWouldChangeNothing(source, target)) {
|
|
149
|
+
return { converged: true, agentPresent: true, agent: 'unchanged' };
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
153
|
+
await fs.copyFile(source, target);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
warn(`tracker: failed to install the Tracker agent (${target}) — ${String(err)}`);
|
|
157
|
+
return { converged: false, agentPresent: await pathExists(target), agent: 'unchanged' };
|
|
158
|
+
}
|
|
159
|
+
return { converged: true, agentPresent: true, agent: 'installed' };
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=tracker-install.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devflow-kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "A meta-harness for Claude Code — turns a single coding agent into an engineering team: orchestration, parallel review, persistent memory, self-learning, and graph workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"version:bump": "npx tsx scripts/bump-version.ts",
|
|
27
27
|
"test": "vitest run",
|
|
28
28
|
"test:watch": "vitest",
|
|
29
|
-
"test:integration": "vitest run --config vitest.integration.config.ts"
|
|
29
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
30
|
+
"test:golden:update": "npx tsx scripts/update-golden.ts"
|
|
30
31
|
},
|
|
31
32
|
"keywords": [
|
|
32
33
|
"claude",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"@clack/prompts": "^0.9.1",
|
|
64
65
|
"commander": "^12.0.0",
|
|
65
66
|
"picocolors": "^1.1.1",
|
|
66
|
-
"subswitch": "0.
|
|
67
|
+
"subswitch": "0.5.0"
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"@mdscript/mds": "0.2.0",
|