devflow-kit 2.5.0 → 3.0.1
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 +82 -0
- package/README.md +44 -19
- package/dist/agents/git.md +13 -15
- package/dist/cli/commands/ambient.js +160 -145
- package/dist/cli/commands/capture.js +29 -55
- package/dist/cli/commands/compliance.js +32 -61
- 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 +40 -4
- package/dist/cli/commands/init.js +249 -271
- package/dist/cli/commands/install-report.js +10 -15
- package/dist/cli/commands/knowledge/index.js +1 -1
- package/dist/cli/commands/knowledge/toggle.js +11 -3
- package/dist/cli/commands/learning.js +52 -37
- package/dist/cli/commands/legacy-hooks.js +11 -14
- package/dist/cli/commands/memory.js +67 -78
- package/dist/cli/commands/proxy.js +23 -41
- package/dist/cli/commands/security.js +5 -13
- package/dist/cli/commands/skills.js +21 -3
- package/dist/cli/commands/tracker.js +100 -228
- package/dist/cli/commands/uninstall.js +343 -138
- package/dist/commands/bug-analysis.md +38 -12
- package/dist/commands/code-review.md +70 -21
- package/dist/commands/debug.md +37 -7
- package/dist/commands/dynamic-build.md +66 -17
- package/dist/commands/dynamic-plan.md +19 -8
- package/dist/commands/dynamic-profile.md +24 -10
- package/dist/commands/dynamic-tickets.md +22 -11
- package/dist/commands/explore.md +37 -7
- package/dist/commands/implement.md +96 -32
- package/dist/commands/plan.md +62 -19
- package/dist/commands/release.md +2 -2
- package/dist/commands/research.md +34 -8
- package/dist/commands/resolve.md +65 -17
- package/dist/commands/self-review.md +45 -9
- package/dist/core/compliance-compose.js +27 -27
- package/dist/core/evidence-policy.js +240 -24
- package/dist/core/feature-config.js +94 -25
- package/dist/core/feature-switch.js +1 -1
- package/dist/core/flags.js +30 -2
- 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 +6 -4
- package/dist/core/mds-variants.js +34 -97
- package/dist/core/migrations.js +49 -23
- package/dist/core/plugins.js +5 -4
- package/dist/core/project-paths.js +0 -17
- package/dist/core/same-location.js +25 -0
- package/dist/core/tracker.js +226 -139
- 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/pr/check-merge-readiness.md +1 -1
- package/dist/skills/git/references/pr/ensure-pr-ready.md +1 -1
- package/dist/skills/git/references/pr/update-pr-evidence.md +1 -1
- package/dist/skills/git/references/tracker/_mcp.md +1 -1
- package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +1 -1
- package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +1 -1
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +2 -2
- package/dist/skills/git/references/tracker/github/manage-debt.md +3 -3
- package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +1 -1
- package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +1 -1
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +2 -2
- package/dist/skills/git/references/tracker/jira/manage-debt.md +1 -1
- package/dist/skills/git/references/tracker/jira/post-wave-report.md +1 -1
- package/dist/skills/git/references/tracker/jira/setup-task.md +1 -1
- package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +1 -1
- package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +1 -1
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +2 -2
- package/dist/skills/git/references/tracker/linear/manage-debt.md +1 -1
- package/dist/skills/git/references/tracker/linear/post-wave-report.md +1 -1
- package/dist/skills/git/references/tracker/linear/setup-task.md +1 -1
- 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 +30 -57
- package/dist/targets/claude-code/post-install.js +246 -139
- package/dist/targets/claude-code/tracker-install.js +38 -65
- package/package.json +5 -4
- package/src/assets/agents/code.md +4 -3
- package/src/assets/agents/design.md +1 -0
- package/src/assets/agents/git.mds +55 -57
- package/src/assets/agents/knowledge.md +2 -2
- package/src/assets/agents/review.md +3 -1
- package/src/assets/agents/tracker.md +37 -30
- 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 +2 -2
- package/src/assets/commands/_partials/_evidence_policy.mds +3 -3
- 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 +2 -2
- package/src/assets/commands/_partials/_preamble.mds +1 -1
- package/src/assets/commands/_partials/_publication.mds +6 -2
- package/src/assets/commands/_partials/_settings.mds +28 -0
- package/src/assets/commands/_partials/_ticket_template.mds +3 -3
- package/src/assets/commands/_partials/_tracker.mds +4 -4
- package/src/assets/commands/_partials/_wave.mds +4 -4
- package/src/assets/commands/bug-analysis.mds +19 -17
- package/src/assets/commands/code-review.mds +39 -33
- package/src/assets/commands/debug.mds +4 -5
- package/src/assets/commands/dynamic-build.mds +75 -53
- package/src/assets/commands/dynamic-plan.mds +20 -15
- package/src/assets/commands/dynamic-profile.mds +24 -11
- package/src/assets/commands/dynamic-tickets.mds +25 -20
- package/src/assets/commands/explore.mds +4 -5
- package/src/assets/commands/implement.mds +58 -45
- package/src/assets/commands/plan.mds +34 -29
- package/src/assets/commands/release.md +2 -2
- package/src/assets/commands/research.mds +11 -9
- package/src/assets/commands/resolve.mds +41 -39
- package/src/assets/commands/self-review.mds +24 -25
- package/src/assets/mds/git/_pr.mds +61 -61
- package/src/assets/mds/git/_references.mds +19 -19
- package/src/assets/mds/tracker/_common.mds +8 -8
- package/src/assets/mds/tracker/_github.mds +71 -71
- package/src/assets/mds/tracker/_jira.mds +74 -74
- package/src/assets/mds/tracker/_linear.mds +75 -75
- package/src/assets/mds/tracker/_mcp.mds +23 -17
- package/src/assets/scripts/hooks/background-memory-update +35 -19
- package/src/assets/scripts/hooks/capture-prompt +18 -12
- package/src/assets/scripts/hooks/capture-question +18 -12
- package/src/assets/scripts/hooks/capture-turn +27 -17
- 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 +111 -36
- package/src/assets/scripts/hooks/git-marker +48 -0
- package/src/assets/scripts/hooks/json-helper.cjs +6 -1
- package/src/assets/scripts/hooks/lib/project-paths.cjs +0 -19
- package/src/assets/scripts/hooks/log-paths +80 -0
- package/src/assets/scripts/hooks/memory-worker +17 -15
- package/src/assets/scripts/hooks/pre-compact-memory +41 -16
- package/src/assets/scripts/hooks/queue-append +104 -30
- package/src/assets/scripts/hooks/resolve-project-root +101 -7
- package/src/assets/scripts/hooks/session-start-context +289 -122
- package/src/assets/scripts/hooks/session-start-memory +35 -16
- package/src/assets/scripts/lib/project-config.cjs +633 -0
- package/src/assets/scripts/resolve-evidence-policy.cjs +300 -220
- package/src/assets/scripts/resolve-settings.cjs +1054 -0
- package/src/assets/scripts/verify-evidence.cjs +1 -1
- package/src/assets/skills/compliance/SKILL.md +2 -2
- package/src/assets/skills/docs-framework/SKILL.md +6 -7
- 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/references/github-api.md +9 -9
- package/src/assets/skills/git/references/patterns.md +1 -1
- 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/dist/hud/git.js
CHANGED
|
@@ -2,15 +2,55 @@ import { execFile } from 'node:child_process';
|
|
|
2
2
|
import { isTrunkBranch } from '../core/git.js';
|
|
3
3
|
const GIT_TIMEOUT = 1000; // 1s per command
|
|
4
4
|
const GIT_MAXBUFFER = 16 * 1024 * 1024; // 16 MiB — covers >500k refs at ~30 B/ref
|
|
5
|
-
function shellExec(cmd, args, cwd) {
|
|
5
|
+
function shellExec(cmd, args, cwd, trim = 'both') {
|
|
6
6
|
return new Promise((resolve) => {
|
|
7
7
|
execFile(cmd, args, { cwd, timeout: GIT_TIMEOUT, maxBuffer: GIT_MAXBUFFER }, (err, stdout) => {
|
|
8
|
-
|
|
8
|
+
if (err)
|
|
9
|
+
return resolve('');
|
|
10
|
+
resolve(trim === 'trailing' ? stdout.trimEnd() : stdout.trim());
|
|
9
11
|
});
|
|
10
12
|
});
|
|
11
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* The override that stops git running a repository's `core.fsmonitor` command.
|
|
16
|
+
*
|
|
17
|
+
* D-NO-FSMONITOR: git runs the command a repository's config names in
|
|
18
|
+
* `core.fsmonitor` whenever it reads the index — code chosen by whatever
|
|
19
|
+
* repository the status line is drawn in, on every prompt. Every HUD git call
|
|
20
|
+
* carries this override (`gitExec`), except the index reads' carve-out below.
|
|
21
|
+
*/
|
|
22
|
+
const FSMONITOR_OFF = ['-c', 'core.fsmonitor=false'];
|
|
23
|
+
/** `core.fsmonitor` as git itself reads it, without touching the index. */
|
|
24
|
+
const FSMONITOR_CONFIG_READ = ['config', '--type=bool', '--get', 'core.fsmonitor'];
|
|
25
|
+
/**
|
|
26
|
+
* Every HUD git call except the index reads: the override first, so no call
|
|
27
|
+
* that never needs fsmonitor can run a repository's hook.
|
|
28
|
+
*/
|
|
12
29
|
function gitExec(args, cwd) {
|
|
13
|
-
return shellExec('git', args, cwd);
|
|
30
|
+
return shellExec('git', ['-c', 'core.fsmonitor=false', ...args], cwd);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* D-NO-FSMONITOR carve-out: the override for an index read, given the
|
|
34
|
+
* trimmed stdout of `git config --type=bool --get core.fsmonitor`.
|
|
35
|
+
*
|
|
36
|
+
* `true` is the built-in fsmonitor daemon — git's own code, not a command from
|
|
37
|
+
* the repository — and it is what keeps `status` fast in huge repositories,
|
|
38
|
+
* where the HUD's 1s timeout would otherwise expire and draw a clean tree. Only
|
|
39
|
+
* that exact answer drops the override. A hook path (which `--type=bool`
|
|
40
|
+
* refuses), any other value, an unset key and a failed read all come back as
|
|
41
|
+
* something else, and keep it: the carve-out fails closed.
|
|
42
|
+
*
|
|
43
|
+
* @param fsmonitorConfig - the config read's trimmed stdout; '' when it failed
|
|
44
|
+
*/
|
|
45
|
+
export function fsmonitorOverride(fsmonitorConfig) {
|
|
46
|
+
return fsmonitorConfig === 'true' ? [] : FSMONITOR_OFF;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The HUD's index reads (`status`, `diff`): the override unless this refresh's
|
|
50
|
+
* `core.fsmonitor` read named the built-in daemon (`fsmonitorOverride`).
|
|
51
|
+
*/
|
|
52
|
+
function gitIndexRead(args, cwd, fsmonitorConfig, trim = 'both') {
|
|
53
|
+
return shellExec('git', [...fsmonitorOverride(fsmonitorConfig), ...args], cwd, trim);
|
|
14
54
|
}
|
|
15
55
|
/**
|
|
16
56
|
* Gather git status for the given working directory.
|
|
@@ -21,8 +61,13 @@ export async function gatherGitStatus(cwd) {
|
|
|
21
61
|
const topLevel = await gitExec(['rev-parse', '--show-toplevel'], cwd);
|
|
22
62
|
if (!topLevel)
|
|
23
63
|
return null;
|
|
24
|
-
// Branch name — 'HEAD' means detached HEAD state
|
|
25
|
-
|
|
64
|
+
// Branch name — 'HEAD' means detached HEAD state. core.fsmonitor is read once
|
|
65
|
+
// per refresh, never cached (the setting can change between prompts), and
|
|
66
|
+
// without the override, which would answer for it.
|
|
67
|
+
const [branch, fsmonitorConfig] = await Promise.all([
|
|
68
|
+
gitExec(['rev-parse', '--abbrev-ref', 'HEAD'], cwd),
|
|
69
|
+
shellExec('git', [...FSMONITOR_CONFIG_READ], cwd),
|
|
70
|
+
]);
|
|
26
71
|
if (!branch)
|
|
27
72
|
return null;
|
|
28
73
|
// Dirty check — porcelain v1: two-char XY status prefix per path.
|
|
@@ -30,7 +75,7 @@ export async function gatherGitStatus(cwd) {
|
|
|
30
75
|
// `git status --no-optional-locks` is rejected as an unknown option, which makes
|
|
31
76
|
// shellExec return '' and silently reports every tree as clean. Keeping the flag
|
|
32
77
|
// (in the right position) stops the HUD from writing .git/index on every prompt.
|
|
33
|
-
const statusOutput = await
|
|
78
|
+
const statusOutput = await gitIndexRead(['--no-optional-locks', 'status', '--porcelain'], cwd, fsmonitorConfig, 'trailing');
|
|
34
79
|
let dirty = false;
|
|
35
80
|
let staged = false;
|
|
36
81
|
for (const line of statusOutput.split('\n')) {
|
|
@@ -71,7 +116,7 @@ export async function gatherGitStatus(cwd) {
|
|
|
71
116
|
// NOTE: diff includes the working tree; ahead/behind counts commits only. This asymmetry is
|
|
72
117
|
// deliberate — both reference the same merge base but differ in working-tree inclusion.
|
|
73
118
|
if (mergeBase) {
|
|
74
|
-
const diffStat = await
|
|
119
|
+
const diffStat = await gitIndexRead(['diff', '--shortstat', mergeBase], cwd, fsmonitorConfig);
|
|
75
120
|
const filesMatch = diffStat.match(/(\d+)\s+file/);
|
|
76
121
|
const addMatch = diffStat.match(/(\d+)\s+insertion/);
|
|
77
122
|
const delMatch = diffStat.match(/(\d+)\s+deletion/);
|
package/dist/hud/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { gatherGitStatus } from './git.js';
|
|
|
7
7
|
import { parseTranscript } from './transcript.js';
|
|
8
8
|
import { persistSessionCost, aggregateCosts } from './cost-history.js';
|
|
9
9
|
import { gatherConfigCounts } from './components/config-counts.js';
|
|
10
|
-
import {
|
|
10
|
+
import { gatherLedgerLearningCounts } from './components/learning-counts.js';
|
|
11
11
|
import { render } from './render.js';
|
|
12
12
|
const OVERALL_TIMEOUT = 2000; // 2 second overall timeout
|
|
13
13
|
/**
|
|
@@ -55,8 +55,8 @@ async function run() {
|
|
|
55
55
|
const resolved = resolveComponents(config);
|
|
56
56
|
const components = new Set(resolved);
|
|
57
57
|
const cwd = stdin.cwd || process.cwd();
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
// D-ONE-HOME: always $HOME/.devflow — no environment variable relocates it.
|
|
59
|
+
const devflowDir = path.join(process.env.HOME || homedir(), '.devflow');
|
|
60
60
|
// Determine what data to gather based on enabled components
|
|
61
61
|
const needsGit = components.has('gitBranch') ||
|
|
62
62
|
components.has('gitAheadBehind') ||
|
|
@@ -68,12 +68,14 @@ async function run() {
|
|
|
68
68
|
const needsConfigCounts = components.has('configCounts');
|
|
69
69
|
const needsLearningCounts = components.has('learningCounts');
|
|
70
70
|
const needsSessionCost = components.has('sessionCost');
|
|
71
|
-
// Parallel data gathering — only fetch what's needed
|
|
72
|
-
|
|
71
|
+
// Parallel data gathering — only fetch what's needed. The learning counts
|
|
72
|
+
// ride here because resolving the ledger root is one git call.
|
|
73
|
+
const [git, transcript, learningCountsData] = await Promise.all([
|
|
73
74
|
needsGit ? gatherGitStatus(cwd) : Promise.resolve(null),
|
|
74
75
|
needsTranscript && stdin.transcript_path
|
|
75
76
|
? parseTranscript(stdin.transcript_path)
|
|
76
77
|
: Promise.resolve(null),
|
|
78
|
+
needsLearningCounts ? gatherLedgerLearningCounts(cwd) : Promise.resolve(null),
|
|
77
79
|
]);
|
|
78
80
|
// Extract usage quota from stdin rate_limits (replaces OAuth fetch)
|
|
79
81
|
const usage = components.has('usageQuota') ? extractUsageFromStdin(stdin) : null;
|
|
@@ -90,10 +92,6 @@ async function run() {
|
|
|
90
92
|
const configCountsData = needsConfigCounts
|
|
91
93
|
? gatherConfigCounts(cwd)
|
|
92
94
|
: null;
|
|
93
|
-
// Decisions/pitfalls counts (fast, synchronous filesystem read)
|
|
94
|
-
const learningCountsData = needsLearningCounts
|
|
95
|
-
? gatherLearningCounts(cwd)
|
|
96
|
-
: null;
|
|
97
95
|
// Cost tracking: persist current session cost, aggregate for weekly/monthly
|
|
98
96
|
const sessionId = stdin.session_id;
|
|
99
97
|
const costUsd = stdin.cost?.total_cost_usd ?? 0;
|
|
@@ -11,7 +11,7 @@ Load for `check-merge-readiness` under every tracker provider.
|
|
|
11
11
|
- Values: `APPROVED`, `CHANGES_REQUESTED`, `REVIEW_REQUIRED`, or null
|
|
12
12
|
3. Fetch CI status (same logic as `check-ci-status`)
|
|
13
13
|
Those steps are in `references/pr/check-ci-status.md` — load it and apply them to this `PR_NUMBER`, every arm unchanged.
|
|
14
|
-
4. Read the test-plan evidence at the current head, from `WORKTREE_PATH` (else cwd): `node "$
|
|
14
|
+
4. Read the test-plan evidence at the current head, from `WORKTREE_PATH` (else cwd): `node "$HOME/.devflow/scripts/verify-evidence.cjs" verify --pr {PR_NUMBER} --approval; echo "exit=$?"`. The evidence is *known* only on `exit=0` with stdout exactly one `EVIDENCE pr:{PR_NUMBER} …` line; otherwise it is *unknown*. From it read `total`, `VERIFIED-CI`, `ATTESTED-LOCAL` (report the two apart), `exceptions` and `approval`; *verified* = `VERIFIED-CI` + `ATTESTED-LOCAL`, never inferred from an absent field. The script re-derives every state at the head and decides `approval` by the trust rule; it prints nothing it read from the PR.
|
|
15
15
|
5. Classify (first matching rule wins):
|
|
16
16
|
- `NOT_READY (unresolved threads: {n})` — unresolved_threads > 0
|
|
17
17
|
- `NOT_READY (changes requested)` — reviewDecision == `CHANGES_REQUESTED`
|
|
@@ -10,7 +10,7 @@ Load for `ensure-pr-ready` under every tracker provider.
|
|
|
10
10
|
2. Check for uncommitted changes - if any, create atomic commit using `devflow:git` patterns
|
|
11
11
|
3. Check if branch pushed to remote - if not, push with `-u` flag. If that push is refused and the branch's open PR is cross-repository with maintainer edits off (`gh pr view --json isCrossRepository,maintainerCanModify`), emit `TRACEABILITY: DEGRADED (cannot push to fork)` and go to 4a (4a–4c edit only the PR).
|
|
12
12
|
4a. Check if PR exists - if not, create PR using guidance from (in priority order): (a) `PR_DESCRIPTION_GUIDANCE` if given and not `(none)`, (b) generated from branch context. Compose the PR body via the `devflow:git` template to `$DEVFLOW_BODY_RAW` (a D11 sink: it publishes at repo visibility), then append the caller blocks. Apply the Comment-sink scrub (D11) — a failed one posts neither block; on success: `gh pr create … --body-file "$DEVFLOW_BODY"`.
|
|
13
|
-
- **Caller blocks**, in order: `PR_WAVE_BLOCK` (`check wave`), then `PR_TEST_PLAN_BLOCK` (`check block`), each when given and not `(none)`. Write it byte for byte to a fresh `mktemp` file with the Write tool, never via a shell string, and run `node "$
|
|
13
|
+
- **Caller blocks**, in order: `PR_WAVE_BLOCK` (`check wave`), then `PR_TEST_PLAN_BLOCK` (`check block`), each when given and not `(none)`. Write it byte for byte to a fresh `mktemp` file with the Write tool, never via a shell string, and run `node "$HOME/.devflow/scripts/verify-evidence.cjs" check <wave|block> <file>; echo "exit=$?"`. Only `exit=0` admits it, verbatim; else omit it, never repaired or partly pasted, and emit `TRACEABILITY: DEGRADED (wave block does not match its grammar)` or `TRACEABILITY: DEGRADED (test-plan block does not match its grammar)` with steps 4b/4c's lines. An admitted wave block is the body's only `## Related Issues`: skip 4b.
|
|
14
14
|
4c. Retitle, only when `APPLY_CONVENTIONS` is `true`: if the PR title breaks the convention in the PR Titles section of `.devflow/conventions.md`, retitle it; skip silently when that file is absent. Two rules, because the title derives from third-party PR titles:
|
|
15
15
|
- **Validate before use.** Skip the retitle (leave the PR title as-is, no error) if the composed title contains any of `` $ ` \ " ' ; | & < > `` or a newline.
|
|
16
16
|
- **Pass as argv, never as command text.** Bind it to a shell variable and pass that variable: `gh pr edit {PR_NUMBER} --title "$DEVFLOW_PR_TITLE"`. Never interpolate it: `$(...)`, backticks and `${...}` all expand inside double quotes.
|
|
@@ -6,7 +6,7 @@ Load for `update-pr-evidence` under every tracker provider.
|
|
|
6
6
|
|
|
7
7
|
### Process
|
|
8
8
|
|
|
9
|
-
Run steps 1–4 as ONE Bash invocation from `WORKTREE_PATH` (else cwd) — the trap removes `$S` and the temp files when it exits — with `V="$
|
|
9
|
+
Run steps 1–4 as ONE Bash invocation from `WORKTREE_PATH` (else cwd) — the trap removes `$S` and the temp files when it exits — with `V="$HOME/.devflow/scripts"`.
|
|
10
10
|
|
|
11
11
|
1. **Verify.** Set `S=`, arm the D11 trap with `[ -n "$S" ] && { rm -- "$S/base" "$S/base.sha256"; rmdir -- "$S"; } 2>/dev/null` added before its `exit`, then the four `mktemp`s and `S="$(mktemp -d)"`. Run `node "$V/verify-evidence.cjs" verify --pr {PR_NUMBER} --state "$S" --block-out "$DEVFLOW_NOTES_RAW" --comment-out "$DEVFLOW_BODY_RAW"`, adding `--publication {REVIEW_PUBLICATION}` only when that is `auto`, `full`, `off` or `stub`, and `--evidence "{EVIDENCE_FILE}"` when given — only a value matching `^[A-Za-z0-9._/-]{1,255}$` reaches the shell; any other is a failed run. Continue only on exit 0 with stdout exactly one line, `EVIDENCE pr:<n> head:<sha> total:<n> VERIFIED-CI:<n> ATTESTED-LOCAL:<n> UNVERIFIED:<n> STALE:<n> FAILED:<n> INDETERMINATE:<n> stale:<ids|none> exceptions:<kinds|none> approval:<yes|no|unchecked> key:<hex> posted:<yes|no|n/a> body:<same|changed>`; otherwise emit `TRACEABILITY: DEGRADED (evidence unavailable)` and stop. The script makes this op's one `gh pr view` read and prints nothing it read from the PR; never read the PR another way.
|
|
12
12
|
2. **Body**, only on `body:changed` (else `UNCHANGED`): `node "$V/redact-secrets.cjs" "$DEVFLOW_NOTES_RAW" "$DEVFLOW_NOTES" && node "$V/verify-evidence.cjs" splice --pr {PR_NUMBER} --state "$S" --block "$DEVFLOW_NOTES" --out "$DEVFLOW_BODY" && gh pr edit {PR_NUMBER} --body-file "$DEVFLOW_BODY"`. Only the composed block is scrubbed (D11); every byte outside its markers is the PR's own and stays identical. `splice` re-reads the body: unchanged since step 1 → it writes; changed → it splices once onto the fresh body and re-reads; changed again → `SPLICE conflict`: `SKIPPED` and `TRACEABILITY: DEGRADED (concurrent edit)`. Any other non-zero → `DEGRADED ({reason})`, naming a printed `SPLICE` token. No edit either way; go to step 4.
|
|
@@ -91,7 +91,7 @@ framing line instead.
|
|
|
91
91
|
```bash
|
|
92
92
|
DEVFLOW_BODY_RAW="$(mktemp)"
|
|
93
93
|
# …compose the body into "$DEVFLOW_BODY_RAW"…
|
|
94
|
-
node "$
|
|
94
|
+
node "$HOME/.devflow/scripts/redact-secrets.cjs" --emit "$DEVFLOW_BODY_RAW"
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
Line 1 of that result is the framing:
|
|
@@ -16,7 +16,7 @@ The D4 degradation contract and the D11 comment-sink scrub state the rules; what
|
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
trap 'GATE=$?; rm -- "$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY" 2>/dev/null; exit "$GATE"' EXIT INT TERM
|
|
19
|
-
node "$
|
|
19
|
+
node "$HOME/.devflow/scripts/redact-secrets.cjs" "$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY" \
|
|
20
20
|
&& gh issue comment {number} --body-file "$DEVFLOW_BODY"
|
|
21
21
|
```
|
|
22
22
|
|
|
@@ -40,7 +40,7 @@ Login fails when using SSO authentication.
|
|
|
40
40
|
## Expected Behavior
|
|
41
41
|
User should be logged in successfully.
|
|
42
42
|
EOF
|
|
43
|
-
} && node "$
|
|
43
|
+
} && node "$HOME/.devflow/scripts/redact-secrets.cjs" \
|
|
44
44
|
"$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY" \
|
|
45
45
|
&& gh issue create \
|
|
46
46
|
--title "Bug: Login fails for SSO users" \
|
|
@@ -6,7 +6,7 @@ Load when the resolved tracker provider is `github` and the operation is `gather
|
|
|
6
6
|
|
|
7
7
|
### Process
|
|
8
8
|
|
|
9
|
-
1a. **Last release tag.** Step 1's `git describe` can return a non-release marker tag. From `WORKTREE_PATH` (else cwd), run `node "$
|
|
9
|
+
1a. **Last release tag.** Step 1's `git describe` can return a non-release marker tag. From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/release-trace.cjs" last-tag`: `LAST_TAG <tag>` ⇒ that tag is `{last_tag}`; `LAST_TAG none` ⇒ step 1's initial-commit rule; anything else ⇒ keep step 1's tag and report status `INDETERMINATE (last release tag unresolved)`.
|
|
10
10
|
3a. **Closing-keyword rule.** A candidate follows, on the same line, a whitespace token matching `^\(?(close[sd]?|fix(e[sd])?|resolve[sd]?|refs):?$` (case-insensitive). Take the next token, plus each further token while the previous one ends in `,`. Split each on `,`, strip one leading `(` and every trailing character in `[.,;:)\]!?]`, drop empties, then apply step 5's anchored gate unchanged. Read each message as `git log --format=%B` lines.
|
|
11
11
|
3b. **This provider's history grammar** is `^#[1-9][0-9]{0,8}$`. A bare number is not a reference here either: a keyword-anchored candidate must carry the `#`, and step 4 renders every number it reads from a merged PR as `#{n}` before step 5's gate.
|
|
12
12
|
4. If `gh` is authenticated and remote is reachable, resolve which issues the range's merged PRs close — **one listing, never one call per commit** — and merge the result with the commit-message set:
|
|
@@ -16,4 +16,4 @@ Load when the resolved tracker provider is `github` and the operation is `gather
|
|
|
16
16
|
- **Coverage:** a range subject carries a PR marker (`(#N)` or `Merge pull request #N`) but no listed PR maps into the range ⇒ `TRACEABILITY: DEGRADED (merged-PR listing did not cover the range)`. A listing of exactly 200 ⇒ status `INDETERMINATE (merged-PR listing hit its 200 cap)`, returning what was collected.
|
|
17
17
|
- **The listing fails** (an older `gh` reports `Unknown JSON field`) ⇒ `TRACEABILITY: DEGRADED ({reason})`, then fall back to `gh pr view N --json closingIssuesReferences` over the PR numbers in `(#N)` / `Merge pull request #N` subjects — after a listing that succeeds, also over each range `(#N)` naming no listed PR — each N gated `^[1-9][0-9]{0,8}$`, filtered and rendered as above, bounded at ≤25 PRs; report the remainder as `THROTTLED ({n} not processed)` and never report the enrichment as complete while PRs went unresolved.
|
|
18
18
|
- On any 4xx → DEGRADED for that item, continue. On 5xx → 1 retry; still 5xx → DEGRADED for that item, continue. On the secondary rate limit of `### Provider signals (GitHub)` in this operation's `backlink-shipped-issues` reference → stop GitHub enrichment immediately, report remaining as `THROTTLED`.
|
|
19
|
-
6. **Per-commit trace map.** In one shell: `trap 'rm -- "$T"' EXIT; T="$(mktemp)"`, then one 40-hex SHA per line into `$T` — each range commit step 4 tied to a PR with ≥1 kept reference (its `mergeCommit.oid`, or a subject naming it). From `WORKTREE_PATH` (else cwd), run `node "$
|
|
19
|
+
6. **Per-commit trace map.** In one shell: `trap 'rm -- "$T"' EXIT; T="$(mktemp)"`, then one 40-hex SHA per line into `$T` — each range commit step 4 tied to a PR with ≥1 kept reference (its `mergeCommit.oid`, or a subject naming it). From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/release-trace.cjs" map --from {last_tag} --grammar github --traced-file "$T"; echo "exit=$?"`. Accept only `exit=0` after a first line `TRACE from:<ref> scanned:<n> traced:<n> untraced:<n> exempt:<n> unmatched:<n> bound:<ok|hit>`; copy every line above `exit=0` verbatim under `### TRACE_MAP`. Anything else ⇒ `TRACEABILITY: DEGRADED (trace map unavailable)`, `### TRACE_MAP` = `(unavailable)`, status `INDETERMINATE (trace map unavailable)`. `bound:hit` ⇒ status `INDETERMINATE (trace scan bound 500 hit)`. An `INDETERMINATE` status outranks every other.
|
|
@@ -34,7 +34,7 @@ post_scrubbed() {
|
|
|
34
34
|
# scrubber scrub — and the chain publish — whatever the RAW file last held.
|
|
35
35
|
# Never a pipeline: a pipeline's exit status hides a scrubber crash (fail-open).
|
|
36
36
|
printf '%s\n' "$1" > "$DEVFLOW_BODY_RAW" \
|
|
37
|
-
&& node "$
|
|
37
|
+
&& node "$HOME/.devflow/scripts/redact-secrets.cjs" \
|
|
38
38
|
"$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY" \
|
|
39
39
|
&& gh issue comment "$2" --body-file "$DEVFLOW_BODY"
|
|
40
40
|
}
|
|
@@ -61,7 +61,7 @@ add_tech_debt_item() {
|
|
|
61
61
|
# edit` replaces the whole body, so what is composed is the body just read plus
|
|
62
62
|
# the new item, under its trailing `## Items` heading.
|
|
63
63
|
printf '%s\n%s\n' "$current_body" "$new_item" > "$DEVFLOW_BODY_RAW" \
|
|
64
|
-
&& node "$
|
|
64
|
+
&& node "$HOME/.devflow/scripts/redact-secrets.cjs" \
|
|
65
65
|
"$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY" \
|
|
66
66
|
&& gh issue edit "$TECH_DEBT_ISSUE" --body-file "$DEVFLOW_BODY"
|
|
67
67
|
}
|
|
@@ -83,7 +83,7 @@ archive_tech_debt_issue() {
|
|
|
83
83
|
|
|
84
84
|
## Items
|
|
85
85
|
" > "$DEVFLOW_BODY_RAW" \
|
|
86
|
-
&& node "$
|
|
86
|
+
&& node "$HOME/.devflow/scripts/redact-secrets.cjs" \
|
|
87
87
|
"$DEVFLOW_BODY_RAW" "$DEVFLOW_BODY" \
|
|
88
88
|
&& new_url=$(gh issue create \
|
|
89
89
|
--title "Tech Debt Backlog" \
|
|
@@ -44,6 +44,6 @@ For each issue the hoist did not answer, within the operation's `≤50` bound:
|
|
|
44
44
|
The tool-call contract governs the write; this operation names its steps and restates none of its rules.
|
|
45
45
|
|
|
46
46
|
1. Compose this post's own content into `$DEVFLOW_BODY_RAW` — a fresh `mktemp` per invocation, under D11's removal `trap`.
|
|
47
|
-
2. Run `node "$
|
|
47
|
+
2. Run `node "$HOME/.devflow/scripts/redact-secrets.cjs" --emit "$DEVFLOW_BODY_RAW"`.
|
|
48
48
|
3. Require line 1 to be `D11-OK`; verify `<bytes>` against the received body's byte length; echo `SCRUB: N [type:count,…]`; and when N > 0 also emit `SECRET-EXPOSED (rotate {type} credential — the source file still holds it)`.
|
|
49
49
|
4. Post through the *add comment* capability with arguments (issue key, body: {SCRUBBED_BODY}).
|
|
@@ -41,7 +41,7 @@ Caller-supplied prose reaches the tracker as a QUERY here and nowhere else in th
|
|
|
41
41
|
The tool-call contract governs every write below; this operation names its steps and restates none of its rules.
|
|
42
42
|
|
|
43
43
|
1. Compose this post's own content into `$DEVFLOW_BODY_RAW` — a fresh `mktemp` per invocation, under D11's removal `trap`.
|
|
44
|
-
2. Run `node "$
|
|
44
|
+
2. Run `node "$HOME/.devflow/scripts/redact-secrets.cjs" --emit "$DEVFLOW_BODY_RAW"`.
|
|
45
45
|
3. Require line 1 to be `D11-OK`; verify `<bytes>` against the received body's byte length; echo `SCRUB: N [type:count,…]`; and when N > 0 also emit `SECRET-EXPOSED (rotate {type} credential — the source file still holds it)`.
|
|
46
46
|
4. Post through the *add comment* capability with arguments (issue key, body: {SCRUBBED_BODY}), or on a new issue through the *create issue* capability with the description field carrying the same gated value.
|
|
47
47
|
|
|
@@ -6,7 +6,7 @@ Load when the resolved tracker provider is `jira` and the operation is `gather-r
|
|
|
6
6
|
|
|
7
7
|
### Process
|
|
8
8
|
|
|
9
|
-
1a. **Last release tag.** Step 1's `git describe` can return a non-release marker tag. From `WORKTREE_PATH` (else cwd), run `node "$
|
|
9
|
+
1a. **Last release tag.** Step 1's `git describe` can return a non-release marker tag. From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/release-trace.cjs" last-tag`: `LAST_TAG <tag>` ⇒ that tag is `{last_tag}`; `LAST_TAG none` ⇒ step 1's initial-commit rule; anything else ⇒ keep step 1's tag and report status `INDETERMINATE (last release tag unresolved)`.
|
|
10
10
|
3a. **Closing-keyword rule.** A candidate follows, on the same line, a whitespace token matching `^\(?(close[sd]?|fix(e[sd])?|resolve[sd]?|refs):?$` (case-insensitive). Take the next token, plus each further token while the previous one ends in `,`. Split each on `,`, strip one leading `(` and every trailing character in `[.,;:)\]!?]`, drop empties, then apply step 5's anchored gate unchanged. Read each message as `git log --format=%B` lines.
|
|
11
11
|
4. Resolve which issues the commit range closes:
|
|
12
12
|
- **There is no closing-reference capability on this provider.** Emit `TRACEABILITY: DEGRADED (unsupported by jira)` once for the whole step and fall back to the commit-message set alone — the refs parsed out of the candidate references the agent extracted from the range's commit messages.
|
|
@@ -15,4 +15,4 @@ Load when the resolved tracker provider is `jira` and the operation is `gather-r
|
|
|
15
15
|
- Confirm the survivors exist with **one** call to the *batch fetch* capability over the whole set, bounded `≤50` with `TRUNCATED ({n} not processed)` for the remainder — **one query, never a per-item loop**.
|
|
16
16
|
- **Because the closing-reference step degraded, the enrichment is incomplete by construction: never report the status as `COMPLETE`.** Report `PARTIAL ({n} DEGRADED)` whenever any step above degraded, and `TRUNCATED ({n} not processed)` whenever the bound was reached. A release that reads `COMPLETE` over an unresolvable evidence set is the one report nobody re-checks.
|
|
17
17
|
- On a tool error for an individual item → DEGRADED for that item, continue. On backpressure → follow `### Provider signals (Jira)` in this operation's `backlink-shipped-issues` reference, which is where this provider's one signal is stated.
|
|
18
|
-
6. **Per-commit trace map.** `{KEY}` is the resolved project key; with none usable, skip the run and take the arm below. From `WORKTREE_PATH` (else cwd), run `node "$
|
|
18
|
+
6. **Per-commit trace map.** `{KEY}` is the resolved project key; with none usable, skip the run and take the arm below. From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/release-trace.cjs" map --from {last_tag} --grammar jira --key {KEY}; echo "exit=$?"`. Accept only `exit=0` after a first line `TRACE from:<ref> scanned:<n> traced:<n> untraced:<n> exempt:<n> unmatched:<n> bound:<ok|hit>`; copy every line above `exit=0` verbatim under `### TRACE_MAP`. Anything else ⇒ `TRACEABILITY: DEGRADED (trace map unavailable)`, `### TRACE_MAP` = `(unavailable)`, status `INDETERMINATE (trace map unavailable)`. `bound:hit` ⇒ status `INDETERMINATE (trace scan bound 500 hit)`. An `INDETERMINATE` status outranks every other.
|
|
@@ -31,7 +31,7 @@ Over `32767` characters the rolling item is closed and a successor is created, e
|
|
|
31
31
|
The tool-call contract governs every write below; this operation names its steps and restates none of its rules.
|
|
32
32
|
|
|
33
33
|
1. Compose this post's own content into `$DEVFLOW_BODY_RAW` — a fresh `mktemp` per invocation, under D11's removal `trap`. `$DEVFLOW_BODY_RAW` is the scrubber's input and nothing else ever reads it.
|
|
34
|
-
2. Run `node "$
|
|
34
|
+
2. Run `node "$HOME/.devflow/scripts/redact-secrets.cjs" --emit "$DEVFLOW_BODY_RAW"`.
|
|
35
35
|
3. Require line 1 to be `D11-OK`; verify `<bytes>` against the received body's byte length; echo `SCRUB: N [type:count,…]`; and when N > 0 also emit `SECRET-EXPOSED (rotate {type} credential — the source file still holds it)`.
|
|
36
36
|
4. Post through the *update description* capability with arguments (issue key, description: {SCRUBBED_BODY}).
|
|
37
37
|
5. Over the `32767` cap after redaction, truncate in **preservation order** — the first line, then the status and DEGRADED lines, then the pointer sentence; the untrusted middle is what gets cut — and end with `NOTE: body exceeded the 32767-character cap after redaction — truncated/stub posted`.
|
|
@@ -28,6 +28,6 @@ Load when the resolved tracker provider is `jira` and the operation is `post-wav
|
|
|
28
28
|
The tool-call contract governs the write; this operation names its steps and restates none of its rules.
|
|
29
29
|
|
|
30
30
|
1. Compose this post's own content into `$DEVFLOW_BODY_RAW` — a fresh `mktemp` per invocation, under D11's removal `trap`.
|
|
31
|
-
2. Run `node "$
|
|
31
|
+
2. Run `node "$HOME/.devflow/scripts/redact-secrets.cjs" --emit "$DEVFLOW_BODY_RAW"`.
|
|
32
32
|
3. Require line 1 to be `D11-OK`; verify `<bytes>` against the received body's byte length; echo `SCRUB: N [type:count,…]`; and when N > 0 also emit `SECRET-EXPOSED (rotate {type} credential — the source file still holds it)`.
|
|
33
33
|
4. Post through the *add comment* capability with arguments (issue key, body: {SCRUBBED_BODY}).
|
|
@@ -7,7 +7,7 @@ Load when the resolved tracker provider is `jira` and the operation is `setup-ta
|
|
|
7
7
|
### Setup — session-scoped, resolved once before any step below
|
|
8
8
|
|
|
9
9
|
- Resolve the capability set and the current-user identity exactly once per spawn, per the tool-call contract. Nothing in this operation probes a second time.
|
|
10
|
-
- **Site.**
|
|
10
|
+
- **Site.** The settings line's `SITE`, else `## Project` in the configuration the preamble already read. It must satisfy `^https://[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9-]+)+$` — **no userinfo, no port, no path**. Anything else ⇒ `TRACEABILITY: DEGRADED (unusable site)` and no tracker call.
|
|
11
11
|
- **Project key.** Resolved and shape-gated by the preamble's chain; consumed here, never re-derived.
|
|
12
12
|
- **Issue types.** Read the *project and issue-type metadata* capability HERE, once, and enumerate the types this run may use. Required-field metadata is read at this same point and nowhere else.
|
|
13
13
|
- No usable site or no project key ⇒ `TRACEABILITY: DEGRADED (tracker not configured)`.
|
|
@@ -48,6 +48,6 @@ For each issue the hoist did not answer, within the operation's `≤50` bound:
|
|
|
48
48
|
The tool-call contract governs the write; this operation names its steps and restates none of its rules.
|
|
49
49
|
|
|
50
50
|
1. Compose this post's own content into `$DEVFLOW_BODY_RAW` — a fresh `mktemp` per invocation, under D11's removal `trap`.
|
|
51
|
-
2. Run `node "$
|
|
51
|
+
2. Run `node "$HOME/.devflow/scripts/redact-secrets.cjs" --emit "$DEVFLOW_BODY_RAW"`.
|
|
52
52
|
3. Require line 1 to be `D11-OK`; verify `<bytes>` against the received body's byte length; echo `SCRUB: N [type:count,…]`; and when N > 0 also emit `SECRET-EXPOSED (rotate {type} credential — the source file still holds it)`.
|
|
53
53
|
4. Post through the *add comment* capability with arguments (issue reference, body: {SCRUBBED_BODY}).
|
|
@@ -41,7 +41,7 @@ Caller-supplied prose reaches the tracker as a QUERY here and nowhere else in th
|
|
|
41
41
|
The tool-call contract governs every write below; this operation names its steps and restates none of its rules.
|
|
42
42
|
|
|
43
43
|
1. Compose this post's own content into `$DEVFLOW_BODY_RAW` — a fresh `mktemp` per invocation, under D11's removal `trap`.
|
|
44
|
-
2. Run `node "$
|
|
44
|
+
2. Run `node "$HOME/.devflow/scripts/redact-secrets.cjs" --emit "$DEVFLOW_BODY_RAW"`.
|
|
45
45
|
3. Require line 1 to be `D11-OK`; verify `<bytes>` against the received body's byte length; echo `SCRUB: N [type:count,…]`; and when N > 0 also emit `SECRET-EXPOSED (rotate {type} credential — the source file still holds it)`.
|
|
46
46
|
4. Post through the *add comment* capability with arguments (issue reference, body: {SCRUBBED_BODY}), or on a new issue through the *create issue* capability with the description field carrying the same gated value.
|
|
47
47
|
|
|
@@ -6,7 +6,7 @@ Load when the resolved tracker provider is `linear` and the operation is `gather
|
|
|
6
6
|
|
|
7
7
|
### Process
|
|
8
8
|
|
|
9
|
-
1a. **Last release tag.** Step 1's `git describe` can return a non-release marker tag. From `WORKTREE_PATH` (else cwd), run `node "$
|
|
9
|
+
1a. **Last release tag.** Step 1's `git describe` can return a non-release marker tag. From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/release-trace.cjs" last-tag`: `LAST_TAG <tag>` ⇒ that tag is `{last_tag}`; `LAST_TAG none` ⇒ step 1's initial-commit rule; anything else ⇒ keep step 1's tag and report status `INDETERMINATE (last release tag unresolved)`.
|
|
10
10
|
3a. **Closing-keyword rule.** A candidate follows, on the same line, a whitespace token matching `^\(?(close[sd]?|fix(e[sd])?|resolve[sd]?|refs):?$` (case-insensitive). Take the next token, plus each further token while the previous one ends in `,`. Split each on `,`, strip one leading `(` and every trailing character in `[.,;:)\]!?]`, drop empties, then apply step 5's anchored gate unchanged. Read each message as `git log --format=%B` lines.
|
|
11
11
|
4. Resolve which issues the commit range closes:
|
|
12
12
|
- **There is no closing-reference capability on this provider.** Emit `TRACEABILITY: DEGRADED (unsupported by linear)` once for the whole step and fall back to the commit-message set alone — the references parsed out of the candidate references the agent extracted from the range's commit messages. The magic words this provider recognises in a pull-request body are the SERVER's own behaviour and are not a capability this operation can read back: a body that closed an issue leaves no signal here, which is precisely why this step degrades instead of guessing.
|
|
@@ -15,4 +15,4 @@ Load when the resolved tracker provider is `linear` and the operation is `gather
|
|
|
15
15
|
- Confirm the survivors exist with **one** call to the *batch fetch* capability over the whole set, bounded `≤50` with `TRUNCATED ({n} not processed)` for the remainder — **one query, never a per-item loop**.
|
|
16
16
|
- **Because the closing-reference step degraded, the enrichment is incomplete by construction: never report the status as `COMPLETE`.** Report `PARTIAL ({n} DEGRADED)` whenever any step above degraded, and `TRUNCATED ({n} not processed)` whenever the bound was reached. A release that reads `COMPLETE` over an unresolvable evidence set is the one report nobody re-checks.
|
|
17
17
|
- On a tool error for an individual item → DEGRADED for that item, continue. On backpressure → follow `### Provider signals (Linear)` in this operation's `backlink-shipped-issues` reference, which is where this provider's one signal is stated.
|
|
18
|
-
6. **Per-commit trace map.** `{KEY}` is the resolved team key; with none usable, skip the run and take the arm below. From `WORKTREE_PATH` (else cwd), run `node "$
|
|
18
|
+
6. **Per-commit trace map.** `{KEY}` is the resolved team key; with none usable, skip the run and take the arm below. From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/release-trace.cjs" map --from {last_tag} --grammar linear --key {KEY}; echo "exit=$?"`. Accept only `exit=0` after a first line `TRACE from:<ref> scanned:<n> traced:<n> untraced:<n> exempt:<n> unmatched:<n> bound:<ok|hit>`; copy every line above `exit=0` verbatim under `### TRACE_MAP`. Anything else ⇒ `TRACEABILITY: DEGRADED (trace map unavailable)`, `### TRACE_MAP` = `(unavailable)`, status `INDETERMINATE (trace map unavailable)`. `bound:hit` ⇒ status `INDETERMINATE (trace scan bound 500 hit)`. An `INDETERMINATE` status outranks every other.
|
|
@@ -31,7 +31,7 @@ Over `32767` characters the rolling item is closed and a successor is created, e
|
|
|
31
31
|
The tool-call contract governs every write below; this operation names its steps and restates none of its rules.
|
|
32
32
|
|
|
33
33
|
1. Compose this post's own content into `$DEVFLOW_BODY_RAW` — a fresh `mktemp` per invocation, under D11's removal `trap`. `$DEVFLOW_BODY_RAW` is the scrubber's input and nothing else ever reads it.
|
|
34
|
-
2. Run `node "$
|
|
34
|
+
2. Run `node "$HOME/.devflow/scripts/redact-secrets.cjs" --emit "$DEVFLOW_BODY_RAW"`.
|
|
35
35
|
3. Require line 1 to be `D11-OK`; verify `<bytes>` against the received body's byte length; echo `SCRUB: N [type:count,…]`; and when N > 0 also emit `SECRET-EXPOSED (rotate {type} credential — the source file still holds it)`.
|
|
36
36
|
4. Post through the *update description* capability with arguments (issue reference, description: {SCRUBBED_BODY}).
|
|
37
37
|
5. Over the `32767` cap after redaction, truncate in **preservation order** — the first line, then the status and DEGRADED lines, then the pointer sentence; the untrusted middle is what gets cut — and end with `NOTE: body exceeded the 32767-character cap after redaction — truncated/stub posted`.
|
|
@@ -28,6 +28,6 @@ Load when the resolved tracker provider is `linear` and the operation is `post-w
|
|
|
28
28
|
The tool-call contract governs the write; this operation names its steps and restates none of its rules.
|
|
29
29
|
|
|
30
30
|
1. Compose this post's own content into `$DEVFLOW_BODY_RAW` — a fresh `mktemp` per invocation, under D11's removal `trap`.
|
|
31
|
-
2. Run `node "$
|
|
31
|
+
2. Run `node "$HOME/.devflow/scripts/redact-secrets.cjs" --emit "$DEVFLOW_BODY_RAW"`.
|
|
32
32
|
3. Require line 1 to be `D11-OK`; verify `<bytes>` against the received body's byte length; echo `SCRUB: N [type:count,…]`; and when N > 0 also emit `SECRET-EXPOSED (rotate {type} credential — the source file still holds it)`.
|
|
33
33
|
4. Post through the *add comment* capability with arguments (issue reference, body: {SCRUBBED_BODY}).
|
|
@@ -7,7 +7,7 @@ Load when the resolved tracker provider is `linear` and the operation is `setup-
|
|
|
7
7
|
### Setup — session-scoped, resolved once before any step below
|
|
8
8
|
|
|
9
9
|
- Resolve the capability set exactly once per spawn, per the tool-call contract. Nothing in this operation probes a second time, or waits on *identify current user* — absent on a stock server here.
|
|
10
|
-
- **Site.**
|
|
10
|
+
- **Site.** The settings line's `SITE`, else `## Project` in the configuration the preamble already read. It must satisfy `^https://[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9-]+)+$` — **no userinfo, no port, no path**. Anything else ⇒ `TRACEABILITY: DEGRADED (unusable site)` and no tracker call.
|
|
11
11
|
- **Team key.** Resolved and shape-gated by the preamble's chain; consumed here, never re-derived.
|
|
12
12
|
- **Issue types.** Read the *project and issue-type metadata* capability HERE, once, and enumerate the types this run may use. Required-field metadata is read at this same point and nowhere else.
|
|
13
13
|
- No usable site or no team key ⇒ `TRACEABILITY: DEGRADED (tracker not configured)`.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { homedir, platform } from 'os';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import { getGitRoot } from '../../core/git.js';
|
|
4
3
|
/**
|
|
5
4
|
* Get the OS-specific path for Claude Code managed settings.
|
|
6
5
|
* Managed settings have highest precedence and cannot be overridden by users.
|
|
@@ -19,6 +18,22 @@ export function getManagedSettingsPath() {
|
|
|
19
18
|
}
|
|
20
19
|
throw new Error(`Managed settings not supported on platform: ${os}`);
|
|
21
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* The home directory — `HOME`, else the passwd entry `os.homedir()` reports —
|
|
23
|
+
* or null when neither names one. Never throws: `os.homedir()` itself throws on
|
|
24
|
+
* a system with no passwd entry for the user.
|
|
25
|
+
*/
|
|
26
|
+
export function readHomeDirectory(env = process.env, osHomedir = homedir) {
|
|
27
|
+
const fromEnv = env.HOME;
|
|
28
|
+
if (fromEnv)
|
|
29
|
+
return fromEnv;
|
|
30
|
+
try {
|
|
31
|
+
return osHomedir() || null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
22
37
|
/**
|
|
23
38
|
* Get home directory with proper fallback and validation
|
|
24
39
|
* Priority: process.env.HOME > os.homedir()
|
|
@@ -26,81 +41,68 @@ export function getManagedSettingsPath() {
|
|
|
26
41
|
* @throws {Error} If unable to determine home directory
|
|
27
42
|
*/
|
|
28
43
|
export function getHomeDirectory() {
|
|
29
|
-
const home =
|
|
30
|
-
if (
|
|
44
|
+
const home = readHomeDirectory();
|
|
45
|
+
if (home === null) {
|
|
31
46
|
throw new Error('Unable to determine home directory. Set HOME environment variable.');
|
|
32
47
|
}
|
|
33
48
|
return home;
|
|
34
49
|
}
|
|
35
50
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
51
|
+
* The Claude Code configuration directory: `CLAUDE_CONFIG_DIR` when it is an
|
|
52
|
+
* absolute path, else `~/.claude`.
|
|
38
53
|
*
|
|
39
|
-
*
|
|
54
|
+
* D-CLAUDE-CONFIG-DIR: Claude Code itself relocates its whole configuration tree
|
|
55
|
+
* (settings.json, agents, skills, rules, history.jsonl) to `CLAUDE_CONFIG_DIR`, so
|
|
56
|
+
* devflow installs into — and uninstalls from — the directory Claude Code actually
|
|
57
|
+
* reads, and no other variable names the Claude directory: an install anywhere
|
|
58
|
+
* else is invisible to the session.
|
|
59
|
+
* A relative value is ignored rather than resolved against the cwd: an install
|
|
60
|
+
* target that moves with the working directory is never the one Claude Code loads.
|
|
61
|
+
* Never throws — the fallback is always a well-formed path.
|
|
40
62
|
*/
|
|
41
63
|
export function getClaudeDirectory() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (!path.isAbsolute(customDir)) {
|
|
46
|
-
throw new Error('CLAUDE_CODE_DIR must be an absolute path');
|
|
47
|
-
}
|
|
48
|
-
// Warn if outside home directory (security best practice)
|
|
49
|
-
const home = getHomeDirectory();
|
|
50
|
-
if (!customDir.startsWith(home)) {
|
|
51
|
-
console.warn('⚠️ CLAUDE_CODE_DIR is outside home directory. Ensure this is intentional.');
|
|
52
|
-
}
|
|
53
|
-
return customDir;
|
|
64
|
+
const configured = process.env.CLAUDE_CONFIG_DIR;
|
|
65
|
+
if (configured !== undefined && configured !== '' && path.isAbsolute(configured)) {
|
|
66
|
+
return configured;
|
|
54
67
|
}
|
|
55
68
|
return path.join(getHomeDirectory(), '.claude');
|
|
56
69
|
}
|
|
57
70
|
/**
|
|
58
|
-
*
|
|
59
|
-
* Priority: DEVFLOW_DIR env var > ~/.devflow
|
|
71
|
+
* The devflow machine root: always `~/.devflow`.
|
|
60
72
|
*
|
|
61
|
-
*
|
|
73
|
+
* D-ONE-HOME: there is one machine root and no environment variable relocates it.
|
|
74
|
+
* The CLI, the HUD, every hook and every prompt resolve `$HOME/.devflow` the same
|
|
75
|
+
* way, so no value exported in one shell can split an install from the hooks and
|
|
76
|
+
* prompts that read it. Per-repo data lives under `<repo>/.devflow`, which is
|
|
77
|
+
* project data, not an install location.
|
|
62
78
|
*/
|
|
63
79
|
export function getDevFlowDirectory() {
|
|
64
|
-
if (process.env.DEVFLOW_DIR) {
|
|
65
|
-
const customDir = process.env.DEVFLOW_DIR;
|
|
66
|
-
// Validate path is absolute
|
|
67
|
-
if (!path.isAbsolute(customDir)) {
|
|
68
|
-
throw new Error('DEVFLOW_DIR must be an absolute path');
|
|
69
|
-
}
|
|
70
|
-
// Warn if outside home directory (security best practice)
|
|
71
|
-
const home = getHomeDirectory();
|
|
72
|
-
if (!customDir.startsWith(home)) {
|
|
73
|
-
console.warn('⚠️ DEVFLOW_DIR is outside home directory. Ensure this is intentional.');
|
|
74
|
-
}
|
|
75
|
-
return customDir;
|
|
76
|
-
}
|
|
77
80
|
return path.join(getHomeDirectory(), '.devflow');
|
|
78
81
|
}
|
|
79
82
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
83
|
+
* The machine-wide install locations.
|
|
84
|
+
*
|
|
85
|
+
* D-SCOPE-RETIRED: devflow has exactly one install scope — the user's machine —
|
|
86
|
+
* because every hook and prompt reads `~/.devflow`. `init --scope local`
|
|
87
|
+
* refuses and points at `devflow uninstall --scope local`, the one reader of a
|
|
88
|
+
* repo-local layout (D-LEGACY-LOCAL-CLEANUP in uninstall.ts).
|
|
84
89
|
*/
|
|
85
|
-
export
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
claudeDir: path.join(gitRoot, '.claude'),
|
|
101
|
-
devflowDir: path.join(gitRoot, '.devflow'),
|
|
102
|
-
gitRoot,
|
|
103
|
-
};
|
|
90
|
+
export function getInstallationPaths() {
|
|
91
|
+
return {
|
|
92
|
+
claudeDir: getClaudeDirectory(),
|
|
93
|
+
devflowDir: getDevFlowDirectory(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* {@link getInstallationPaths} as a Result: the one way these paths fail is a
|
|
98
|
+
* process with no home directory, which a command reports and exits on rather
|
|
99
|
+
* than catching a throw.
|
|
100
|
+
*/
|
|
101
|
+
export function resolveInstallationPaths() {
|
|
102
|
+
const homeDir = readHomeDirectory();
|
|
103
|
+
if (homeDir === null) {
|
|
104
|
+
return { ok: false, error: 'Unable to determine home directory. Set HOME environment variable.' };
|
|
104
105
|
}
|
|
106
|
+
return { ok: true, value: { homeDir, ...getInstallationPaths() } };
|
|
105
107
|
}
|
|
106
108
|
//# sourceMappingURL=claude-paths.js.map
|