devflow-kit 3.7.0 → 3.9.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 +479 -1
- package/README.md +15 -15
- package/dist/agents/code.md +6 -8
- package/dist/agents/design.md +1 -1
- package/dist/agents/fix.md +7 -8
- package/dist/agents/git.md +3 -3
- package/dist/agents/harden.md +1 -1
- package/dist/agents/refactor.md +2 -4
- package/dist/agents/review.md +4 -4
- package/dist/agents/skim.md +2 -2
- package/dist/agents/triage.md +9 -2
- package/dist/cli/commands/capture.js +1 -1
- package/dist/cli/commands/curation-move-io.js +102 -0
- package/dist/cli/commands/curation-move-step.js +374 -0
- package/dist/cli/commands/curation.js +808 -0
- package/dist/cli/commands/gate-level-prompts.js +428 -0
- package/dist/cli/commands/gates.js +35 -11
- package/dist/cli/commands/init-seed.js +11 -8
- package/dist/cli/commands/init.js +244 -57
- package/dist/cli/commands/memory.js +60 -13
- package/dist/cli/commands/prompt-io.js +37 -0
- package/dist/cli/commands/uninstall.js +157 -17
- package/dist/cli.js +2 -2
- package/dist/commands/bug-analysis.md +25 -36
- package/dist/commands/code-review.md +40 -44
- package/dist/commands/debug.md +5 -5
- package/dist/commands/dynamic-build.md +26 -25
- package/dist/commands/dynamic-plan.md +5 -5
- package/dist/commands/dynamic-profile.md +5 -5
- package/dist/commands/dynamic-tickets.md +5 -5
- package/dist/commands/explore.md +6 -6
- package/dist/commands/implement.md +53 -82
- package/dist/commands/plan.md +6 -6
- package/dist/commands/release.md +5 -5
- package/dist/commands/research.md +6 -6
- package/dist/commands/resolve.md +39 -41
- package/dist/commands/self-review.md +5 -13
- package/dist/core/area-pause-notice.js +44 -0
- package/dist/core/assets.js +20 -20
- package/dist/core/curate-tuning-config.js +5 -6
- package/dist/core/curation-move-notice.js +84 -0
- package/dist/core/curation-move-skip.js +35 -0
- package/dist/core/curation-move.js +35 -0
- package/dist/core/curation-queue-cleanup.js +135 -0
- package/dist/core/curation-store.js +99 -0
- package/dist/core/{learning-variants.js → curation-variants.js} +65 -41
- package/dist/core/echo-value.js +11 -5
- package/dist/core/evidence-policy.js +11 -9
- package/dist/core/feature-switch.js +95 -19
- package/dist/core/fs-atomic.js +1 -1
- package/dist/core/gate-level.js +10 -2
- package/dist/core/git.js +16 -14
- package/dist/core/ledger-root.js +82 -22
- package/dist/core/linked-path.js +4 -4
- package/dist/core/manifest-lock.js +238 -0
- package/dist/core/manifest.js +58 -14
- package/dist/core/migrations.js +1 -13
- package/dist/core/observations.js +2 -2
- package/dist/core/plugins.js +19 -19
- package/dist/core/pool.js +26 -0
- package/dist/core/project-paths.js +103 -34
- package/dist/core/queue-drain.js +2 -2
- package/dist/core/tracked-areas.js +159 -0
- package/dist/core/tracker.js +1 -1
- package/dist/{learning-off → curation-off}/agents/code.md +6 -8
- package/dist/{learning-off → curation-off}/agents/fix.md +7 -8
- package/dist/{learning-off → curation-off}/agents/refactor.md +2 -4
- package/dist/{learning-off → curation-off}/agents/review.md +3 -3
- package/dist/{learning-off → curation-off}/agents/triage.md +8 -1
- package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
- package/dist/{learning-off → curation-off}/commands/code-review.md +37 -41
- package/dist/{learning-off → curation-off}/commands/debug.md +2 -2
- package/dist/{learning-off → curation-off}/commands/dynamic-build.md +23 -22
- package/dist/{learning-off → curation-off}/commands/explore.md +2 -2
- package/dist/{learning-off → curation-off}/commands/implement.md +50 -79
- package/dist/{learning-off → curation-off}/commands/plan.md +2 -2
- package/dist/{learning-off → curation-off}/commands/resolve.md +36 -38
- package/dist/{learning-off → curation-off}/commands/self-review.md +2 -10
- package/dist/hud/components/{learning-counts.js → curation-counts.js} +63 -17
- package/dist/hud/config.js +1 -1
- package/dist/hud/index.js +6 -6
- package/dist/hud/render.js +3 -3
- package/dist/skills/git/references/pr/post-resolution-summary.md +12 -11
- package/dist/skills/git/references/pr/post-review-summary.md +12 -11
- package/dist/skills/git/references/pr/update-pr-evidence.md +2 -2
- package/dist/skills/git/references/pr/validate-branch.md +8 -5
- package/dist/skills/git/references/publication-gate.md +6 -7
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/github/manage-debt.md +6 -2
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/jira/manage-debt.md +6 -2
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/linear/manage-debt.md +6 -2
- package/dist/targets/claude-code/{learning-install.js → curation-install.js} +85 -65
- package/dist/targets/claude-code/installer.js +15 -15
- package/dist/targets/claude-code/language-stamp.js +5 -5
- package/dist/targets/claude-code/post-install.js +2 -2
- package/dist/targets/claude-code/tracker-servers.js +1 -1
- package/package.json +3 -2
- package/src/assets/agents/_partials/_code.mds +11 -11
- package/src/assets/agents/code.mds +6 -6
- package/src/assets/agents/curate.md +8 -8
- package/src/assets/agents/debug.mds +6 -6
- package/src/assets/agents/design.mds +18 -18
- package/src/assets/agents/evaluate.md +6 -12
- package/src/assets/agents/fix.mds +7 -6
- package/src/assets/agents/git.mds +3 -3
- package/src/assets/agents/harden.mds +7 -7
- package/src/assets/agents/knowledge.mds +23 -23
- package/src/assets/agents/refactor.mds +2 -2
- package/src/assets/agents/research.mds +15 -15
- package/src/assets/agents/review.mds +43 -43
- package/src/assets/agents/skim.mds +11 -11
- package/src/assets/agents/triage.mds +29 -22
- package/src/assets/agents/validate.md +30 -50
- package/src/assets/commands/_partials/_decisions.mds +22 -20
- package/src/assets/commands/_partials/_engine.mds +8 -8
- package/src/assets/commands/_partials/_knowledge.mds +29 -15
- package/src/assets/commands/_partials/_preamble.mds +4 -4
- package/src/assets/commands/_partials/_settings.mds +3 -3
- package/src/assets/commands/_partials/_wave.mds +7 -7
- package/src/assets/commands/bug-analysis.mds +37 -48
- package/src/assets/commands/code-review.mds +53 -57
- package/src/assets/commands/debug.mds +14 -14
- package/src/assets/commands/dynamic-build.mds +38 -37
- package/src/assets/commands/dynamic-plan.mds +22 -22
- package/src/assets/commands/dynamic-profile.mds +2 -2
- package/src/assets/commands/dynamic-tickets.mds +8 -8
- package/src/assets/commands/explore.mds +20 -20
- package/src/assets/commands/implement.mds +77 -98
- package/src/assets/commands/plan.mds +27 -27
- package/src/assets/commands/release.mds +12 -12
- package/src/assets/commands/research.mds +19 -19
- package/src/assets/commands/resolve.mds +66 -60
- package/src/assets/commands/self-review.mds +20 -20
- package/src/assets/mds/git/_ops.mds +9 -2
- package/src/assets/mds/git/_ops_pr.mds +21 -11
- package/src/assets/mds/git/_ops_steps.mds +20 -4
- package/src/assets/mds/git/_pr.mds +21 -18
- package/src/assets/mds/git/_references.mds +6 -7
- package/src/assets/mds/tracker/_github.mds +1 -4
- package/src/assets/mds/tracker/_jira.mds +1 -4
- package/src/assets/mds/tracker/_linear.mds +1 -4
- package/src/assets/rules/quality.md +1 -0
- package/src/assets/scripts/ci-wait.cjs +389 -92
- package/src/assets/scripts/hooks/background-memory-update +81 -3
- package/src/assets/scripts/hooks/capture-prompt +12 -12
- package/src/assets/scripts/hooks/capture-question +11 -11
- package/src/assets/scripts/hooks/capture-turn +12 -12
- package/src/assets/scripts/hooks/{learning-lock → curation-lock} +2 -2
- package/src/assets/scripts/hooks/curation-move +293 -0
- package/src/assets/scripts/hooks/ensure-devflow-init +45 -13
- package/src/assets/scripts/hooks/ensure-proxy +4 -4
- package/src/assets/scripts/hooks/ensure-root-gitignore +4 -4
- package/src/assets/scripts/hooks/git-marker +81 -0
- package/src/assets/scripts/hooks/json-helper.cjs +176 -51
- package/src/assets/scripts/hooks/json-parse +1 -1
- package/src/assets/scripts/hooks/lib/curation-move.cjs +1582 -0
- package/src/assets/scripts/hooks/lib/{learning-store.cjs → curation-store.cjs} +308 -122
- package/src/assets/scripts/hooks/lib/decisions-format.cjs +3 -3
- package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +24 -7
- package/src/assets/scripts/hooks/lib/project-paths.cjs +132 -39
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +42 -22
- package/src/assets/scripts/hooks/lib/tracked-areas.cjs +452 -0
- package/src/assets/scripts/hooks/memory-permission +10 -2
- package/src/assets/scripts/hooks/memory-worker +12 -1
- package/src/assets/scripts/hooks/pre-compact-memory +17 -16
- package/src/assets/scripts/hooks/queue-append +444 -102
- package/src/assets/scripts/hooks/resolve-project-root +2 -2
- package/src/assets/scripts/hooks/session-start-context +198 -72
- package/src/assets/scripts/hooks/session-start-memory +42 -43
- package/src/assets/scripts/lib/project-config.cjs +12 -5
- package/src/assets/scripts/pr-evidence.cjs +5 -2
- package/src/assets/scripts/resolve-base.cjs +1043 -0
- package/src/assets/scripts/resolve-settings.cjs +283 -45
- package/src/assets/scripts/verify-evidence.cjs +307 -75
- package/src/assets/skills/agent-references/SKILL.md +11 -2
- package/src/assets/skills/agent-references/references/commands/implement-levels.md +29 -0
- package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +6 -4
- package/src/assets/skills/agent-references/references/commands/resolve-levels.md +25 -0
- package/src/assets/skills/agent-references/references/synthesize/review.md +4 -4
- package/src/assets/skills/agent-references/references/triage/severity-tiers.md +43 -0
- package/src/assets/skills/agent-references/references/validate/passed-alone.md +14 -0
- package/src/assets/skills/apply-decisions/SKILL.md +1 -1
- package/src/assets/skills/docs-framework/SKILL.md +7 -7
- package/src/assets/skills/docs-framework/references/patterns.md +3 -4
- package/src/assets/skills/quality-gates/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/references/roots.md +2 -2
- package/templates/lint-pack/.lint-pack.conf +86 -0
- package/templates/lint-pack/README.md +190 -0
- package/templates/lint-pack/lint-pack.sh +905 -0
- package/dist/cli/commands/learning.js +0 -554
- package/dist/core/learning-queue-cleanup.js +0 -27
- package/dist/core/learning-store.js +0 -61
- /package/dist/{learning-off → curation-off}/agents/debug.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/design.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/harden.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/knowledge.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/research.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/skim.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-plan.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-profile.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-tickets.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/release.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/research.md +0 -0
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
// src/assets/scripts/hooks/json-helper.cjs
|
|
4
4
|
// Provides jq-equivalent operations for hooks when jq is not installed, and the
|
|
5
|
-
//
|
|
5
|
+
// curation ops the Curate agent runs from the project root.
|
|
6
6
|
// SECURITY: This is a local CLI helper invoked only by shell hooks and the
|
|
7
7
|
// Curate agent with controlled arguments. No operation takes a file path: a
|
|
8
|
-
// file's content arrives on stdin (json-parse redirects it), and the
|
|
8
|
+
// file's content arrives on stdin (json-parse redirects it), and the curation ops
|
|
9
9
|
// build every path from the current directory.
|
|
10
10
|
// Usage: node json-helper.cjs <operation> [args...]
|
|
11
11
|
//
|
|
@@ -37,13 +37,24 @@
|
|
|
37
37
|
// show <anchor|obs_id> Read-only: print one entry as pretty JSON
|
|
38
38
|
// claim-due Hand out the entries due for maintenance, leased
|
|
39
39
|
// for a day, after the ref their claims are checked at
|
|
40
|
-
// claim-queue Claim the
|
|
40
|
+
// claim-queue Claim the curation queue for this run; prints
|
|
41
41
|
// claimed <token>[ takeover] | busy | none
|
|
42
42
|
// release-claim <token> Release the claim the token owns; prints
|
|
43
43
|
// released | not-owner | gone
|
|
44
|
+
// move-ledger <root> Move a project's old ledger folder to the new name,
|
|
45
|
+
// once; prints none | busy | busy claim |
|
|
46
|
+
// skipped tracked | skipped unknown | moved <mode>
|
|
47
|
+
// and its backup, cause and notes
|
|
48
|
+
// tracked-area <curation|memory|both> <root>
|
|
49
|
+
// Does git track files in the data folders of the area
|
|
50
|
+
// at the root? Prints tracked | untracked | unknown, and
|
|
51
|
+
// for unknown a second line `cause <word>`
|
|
44
52
|
//
|
|
45
|
-
// Every
|
|
46
|
-
// the mtime of an existing queue claim — the heartbeat (D-OWNED-CLAIM).
|
|
53
|
+
// Every curation op above except claim-queue, release-claim, move-ledger and tracked-area
|
|
54
|
+
// first refreshes the mtime of an existing queue claim — the heartbeat (D-OWNED-CLAIM).
|
|
55
|
+
// Where git tracks files in the curation data folders, or does not say whether it does,
|
|
56
|
+
// every curation op refuses with kind `tracked`, exit 1 and an empty stdout, and changes
|
|
57
|
+
// nothing; the answer is asked once per process.
|
|
47
58
|
|
|
48
59
|
'use strict';
|
|
49
60
|
|
|
@@ -53,21 +64,50 @@ const { constants: { MAX_STRING_LENGTH } } = require('buffer');
|
|
|
53
64
|
const op = process.argv[2];
|
|
54
65
|
const args = process.argv.slice(3);
|
|
55
66
|
|
|
56
|
-
/** The
|
|
57
|
-
let
|
|
67
|
+
/** The curation modules, once loaded; see curation(). */
|
|
68
|
+
let curationModules = null;
|
|
58
69
|
|
|
59
70
|
/**
|
|
60
|
-
* The
|
|
71
|
+
* The curation store, loaded on first use and memoized. The generic ops never
|
|
61
72
|
* call it, so a hook that falls back from jq to node never pays for loading it,
|
|
62
73
|
* and the store loads the renderer only when an op renders.
|
|
63
74
|
*
|
|
64
75
|
* @returns {{ store: object }}
|
|
65
76
|
*/
|
|
66
|
-
function
|
|
67
|
-
if (
|
|
68
|
-
|
|
77
|
+
function curation() {
|
|
78
|
+
if (curationModules === null) {
|
|
79
|
+
curationModules = { store: require('./lib/curation-store.cjs') };
|
|
69
80
|
}
|
|
70
|
-
return
|
|
81
|
+
return curationModules;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The ledger move module, once loaded; see curationMove(). */
|
|
85
|
+
let curationMoveModule = null;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The ledger move module, loaded on first use and memoized. Only the move op calls
|
|
89
|
+
* it, so no other curation op loads the move code.
|
|
90
|
+
*
|
|
91
|
+
* @returns {object}
|
|
92
|
+
*/
|
|
93
|
+
function curationMove() {
|
|
94
|
+
if (curationMoveModule === null) curationMoveModule = require('./lib/curation-move.cjs');
|
|
95
|
+
return curationMoveModule;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The tracked-areas check, once loaded; see trackedAreas(). */
|
|
99
|
+
let trackedAreasModule = null;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The tracked-areas check, loaded on first use and memoized. Only the tracked-area op
|
|
103
|
+
* loads it from here: a leaf that requires node built-ins and the path helpers, never
|
|
104
|
+
* the store.
|
|
105
|
+
*
|
|
106
|
+
* @returns {object}
|
|
107
|
+
*/
|
|
108
|
+
function trackedAreas() {
|
|
109
|
+
if (trackedAreasModule === null) trackedAreasModule = require('./lib/tracked-areas.cjs');
|
|
110
|
+
return trackedAreasModule;
|
|
71
111
|
}
|
|
72
112
|
|
|
73
113
|
/** The bytes each read of stdin asks for. */
|
|
@@ -84,7 +124,7 @@ const STDIN_WAIT_CELL = new Int32Array(new SharedArrayBuffer(4));
|
|
|
84
124
|
|
|
85
125
|
/**
|
|
86
126
|
* Read stdin to its end, keeping at most `maxBytes`. Every op that reads stdin
|
|
87
|
-
* reads it here, so the generic ops and the
|
|
127
|
+
* reads it here, so the generic ops and the curation ops cannot read it two ways.
|
|
88
128
|
* Never throws: a failure is a refusal.
|
|
89
129
|
*
|
|
90
130
|
* D-STDIN-FD0: stdin is read from file descriptor 0 itself, in reads repeated
|
|
@@ -168,14 +208,16 @@ function parseArgs(argList) {
|
|
|
168
208
|
}
|
|
169
209
|
|
|
170
210
|
// ---------------------------------------------------------------------------
|
|
171
|
-
//
|
|
211
|
+
// Curation-op adapter
|
|
172
212
|
// ---------------------------------------------------------------------------
|
|
173
213
|
|
|
174
214
|
/**
|
|
175
|
-
* Print a
|
|
215
|
+
* Print a curation op's Result: `format(value)` and a newline on stdout, or the
|
|
176
216
|
* error message and a newline on stderr. Returns the exit code for the op to set
|
|
177
217
|
* as process.exitCode, so the process exits once, after the op has returned and
|
|
178
|
-
* every lock it took is released.
|
|
218
|
+
* every lock it took is released. The error message keeps its line structure (a
|
|
219
|
+
* refusal lists one problem per line), and each line has no control character, as it
|
|
220
|
+
* may name a file the repository holds. At most REFUSAL_MAX_LINES lines print.
|
|
179
221
|
*
|
|
180
222
|
* @param {{ ok: true, value: unknown } | { ok: false, error: { message: string } }} result
|
|
181
223
|
* @param {(value: any) => string} format - the stdout text for the value
|
|
@@ -186,10 +228,43 @@ function emit(result, format) {
|
|
|
186
228
|
process.stdout.write(`${format(result.value)}\n`);
|
|
187
229
|
return 0;
|
|
188
230
|
}
|
|
189
|
-
process.stderr.write(`${result.error.message}\n`);
|
|
231
|
+
process.stderr.write(`${refusalText(result.error.message)}\n`);
|
|
190
232
|
return 1;
|
|
191
233
|
}
|
|
192
234
|
|
|
235
|
+
/** The most lines of one refusal message that print. */
|
|
236
|
+
const REFUSAL_MAX_LINES = 200;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* A refusal message with each line scrubbed (singleLine) and the line structure kept.
|
|
240
|
+
* Past REFUSAL_MAX_LINES lines, the rest become one `... N more lines` line.
|
|
241
|
+
*
|
|
242
|
+
* @param {string} message
|
|
243
|
+
* @returns {string}
|
|
244
|
+
*/
|
|
245
|
+
function refusalText(message) {
|
|
246
|
+
const { singleLine } = curation().store;
|
|
247
|
+
const lines = String(message).split('\n');
|
|
248
|
+
if (lines.length <= REFUSAL_MAX_LINES) return lines.map(line => singleLine(line)).join('\n');
|
|
249
|
+
const shown = lines.slice(0, REFUSAL_MAX_LINES).map(line => singleLine(line));
|
|
250
|
+
return [...shown, `... ${lines.length - REFUSAL_MAX_LINES} more lines`].join('\n');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* The first line of move-ledger's answer: the state, with `moved` followed by its mode,
|
|
255
|
+
* `busy` followed by `claim` when a fresh queue claim holds the move, and `skipped`
|
|
256
|
+
* followed by its reason (`tracked` or `unknown`).
|
|
257
|
+
*
|
|
258
|
+
* @param {{ state: string, mode?: string, reason?: string }} moved
|
|
259
|
+
* @returns {string}
|
|
260
|
+
*/
|
|
261
|
+
function moveAnswerWord(moved) {
|
|
262
|
+
if (moved.state === 'moved') return `moved ${moved.mode}`;
|
|
263
|
+
if (moved.state === 'busy' && moved.reason === 'claim') return 'busy claim';
|
|
264
|
+
if (moved.state === 'skipped') return `skipped ${moved.reason}`;
|
|
265
|
+
return moved.state;
|
|
266
|
+
}
|
|
267
|
+
|
|
193
268
|
/**
|
|
194
269
|
* Refuse a malformed command line: print `<op>: usage: <usage>` on stderr and exit 1,
|
|
195
270
|
* before the op takes any lock.
|
|
@@ -202,11 +277,11 @@ function exitWithUsage(usage) {
|
|
|
202
277
|
process.exit(1);
|
|
203
278
|
}
|
|
204
279
|
|
|
205
|
-
/** The most stdin a
|
|
280
|
+
/** The most stdin a curation op reads: far above any valid input, so a runaway writer is refused, not parsed. */
|
|
206
281
|
const STDIN_JSON_MAX_BYTES = 64 * 1024;
|
|
207
282
|
|
|
208
283
|
/**
|
|
209
|
-
* A
|
|
284
|
+
* A curation op's stdin as one JSON object: the one way text reaches a curation
|
|
210
285
|
* op, so no field of it ever passes through argv or a shell word. Never throws.
|
|
211
286
|
*
|
|
212
287
|
* @param {string} opName - for the message
|
|
@@ -235,30 +310,30 @@ const PUT_MODES = new Map([['--create', 'create'], ['--update', 'update'], ['--r
|
|
|
235
310
|
const ENTRY_TYPES = new Set(['decision', 'pitfall']);
|
|
236
311
|
|
|
237
312
|
/**
|
|
238
|
-
* The
|
|
313
|
+
* The curation ops whose run sends the claim heartbeat first (D-OWNED-CLAIM).
|
|
239
314
|
* claim-queue and release-claim manage the claim themselves, and the generic ops
|
|
240
|
-
* never touch it. A new
|
|
315
|
+
* never touch it. A new curation op joins this set.
|
|
241
316
|
*/
|
|
242
|
-
const
|
|
317
|
+
const CURATION_OPS = new Set([
|
|
243
318
|
'assign-anchor', 'retire-anchor', 'restore-anchor', 'refresh-anchor', 'rotate-observations',
|
|
244
319
|
'put-observation', 'list', 'show', 'claim-due',
|
|
245
320
|
]);
|
|
246
321
|
|
|
247
322
|
/** Send the claim heartbeat; a failure is reported on stderr and never stops the op. */
|
|
248
323
|
function heartbeat(root) {
|
|
249
|
-
const beat =
|
|
324
|
+
const beat = curation().store.touchClaim(root);
|
|
250
325
|
if (!beat.ok) process.stderr.write(`${beat.error.message}\n`);
|
|
251
326
|
}
|
|
252
327
|
|
|
253
|
-
// The
|
|
328
|
+
// The curation ops run from the project root and take no path to a curation file:
|
|
254
329
|
// each builds its paths from the current directory. Every op that writes takes the
|
|
255
|
-
// store's one
|
|
256
|
-
// refuses, creating nothing, when .devflow/
|
|
330
|
+
// store's one curation lock through withDecisionsLock (D-ONE-LEARNING-LOCK) and
|
|
331
|
+
// refuses, creating nothing, when .devflow/curation/ is absent (D-NO-STRAY-TREE)
|
|
257
332
|
// or when it, or .devflow, is a symbolic link (D-NO-LINKED-TREE).
|
|
258
333
|
// A locked body returns its Result; emit prints it once the lock is released.
|
|
259
334
|
if (require.main === module) {
|
|
260
335
|
try {
|
|
261
|
-
if (
|
|
336
|
+
if (CURATION_OPS.has(op)) heartbeat(process.cwd());
|
|
262
337
|
switch (op) {
|
|
263
338
|
case 'get-field': {
|
|
264
339
|
const input = JSON.parse(readStdin());
|
|
@@ -338,7 +413,6 @@ try {
|
|
|
338
413
|
console.log(JSON.stringify({
|
|
339
414
|
timestamp: data.ts || '',
|
|
340
415
|
trigger: 'pre-compact',
|
|
341
|
-
memory_snapshot: data.memory || '',
|
|
342
416
|
git: {
|
|
343
417
|
branch: data.branch || '',
|
|
344
418
|
status: data.status || '',
|
|
@@ -353,12 +427,12 @@ try {
|
|
|
353
427
|
// assign-anchor <decision|pitfall> <obs_id>
|
|
354
428
|
// Promote a v2 observation no ledger row carries to the next entry of its
|
|
355
429
|
// type, skipping each number a tracked file cites (assignAnchor,
|
|
356
|
-
//
|
|
430
|
+
// curation-store.cjs: D-LEDGER-REGISTRY, D-E4-SKIP). It never writes the log.
|
|
357
431
|
// stdout: the anchor; stderr: `assign-anchor: skipped <anchor>, cited in
|
|
358
432
|
// <path>:<line>` for each number skipped
|
|
359
433
|
// -------------------------------------------------------------------------
|
|
360
434
|
case 'assign-anchor': {
|
|
361
|
-
const { store } =
|
|
435
|
+
const { store } = curation();
|
|
362
436
|
if (args.length !== 2 || !ENTRY_TYPES.has(args[0]) || !store.OBS_ID_RE.test(args[1])) {
|
|
363
437
|
exitWithUsage('assign-anchor <decision|pitfall> <obs_id> (run from the project root)');
|
|
364
438
|
}
|
|
@@ -377,12 +451,12 @@ try {
|
|
|
377
451
|
// Make an active entry inactive with the one JSON object on stdin its status
|
|
378
452
|
// takes: { reason } for Retired and Deprecated, { by } for Superseded, and
|
|
379
453
|
// { at, quote } for Encoded, the quote checked at the verify ref
|
|
380
|
-
// (retireAnchor,
|
|
454
|
+
// (retireAnchor, curation-store.cjs: D-ENCODED-QUOTE).
|
|
381
455
|
// stdout: the new status in lower case and the anchor, then `repointed
|
|
382
456
|
// <anchor>` for each entry re-pointed to the successor
|
|
383
457
|
// -------------------------------------------------------------------------
|
|
384
458
|
case 'retire-anchor': {
|
|
385
|
-
const { store } =
|
|
459
|
+
const { store } = curation();
|
|
386
460
|
if (args.length !== 2 || !store.ANCHOR_ID_RE.test(args[0]) || !store.INACTIVE_STATUSES.includes(args[1])) {
|
|
387
461
|
exitWithUsage('retire-anchor <anchor> <Encoded|Superseded|Retired|Deprecated> (one JSON object on stdin; run from the project root)');
|
|
388
462
|
}
|
|
@@ -400,11 +474,11 @@ try {
|
|
|
400
474
|
// Make an inactive entry active again, its notes, last_verified and
|
|
401
475
|
// last_attempt cleared so it is due for maintenance again, ordered after
|
|
402
476
|
// integrity problems and legacy entries, or among them when it is one
|
|
403
|
-
// (restoreAnchor,
|
|
477
|
+
// (restoreAnchor, curation-store.cjs: D-DUE-ORDER).
|
|
404
478
|
// stdout: restored <anchor>
|
|
405
479
|
// -------------------------------------------------------------------------
|
|
406
480
|
case 'restore-anchor': {
|
|
407
|
-
const { store } =
|
|
481
|
+
const { store } = curation();
|
|
408
482
|
if (args.length !== 1 || !store.ANCHOR_ID_RE.test(args[0])) {
|
|
409
483
|
exitWithUsage('restore-anchor <anchor> (run from the project root)');
|
|
410
484
|
}
|
|
@@ -415,12 +489,12 @@ try {
|
|
|
415
489
|
// -------------------------------------------------------------------------
|
|
416
490
|
// refresh-anchor <anchor> [<anchor>...] [--verified]
|
|
417
491
|
// Re-project active v2 entries from their log rows, or under --verified stamp
|
|
418
|
-
// them verified today; all or nothing (refreshAnchors,
|
|
492
|
+
// them verified today; all or nothing (refreshAnchors, curation-store.cjs).
|
|
419
493
|
// stdout: `reprojected <anchor>` or `unchanged <anchor>` per anchor, or
|
|
420
494
|
// `verified <anchor>` under --verified, in the order given
|
|
421
495
|
// -------------------------------------------------------------------------
|
|
422
496
|
case 'refresh-anchor': {
|
|
423
|
-
const { store } =
|
|
497
|
+
const { store } = curation();
|
|
424
498
|
const verifiedFlags = args.filter(arg => arg === '--verified').length;
|
|
425
499
|
const anchors = args.filter(arg => arg !== '--verified');
|
|
426
500
|
if (verifiedFlags > 1 || anchors.length === 0 || !anchors.every(arg => store.ANCHOR_ID_RE.test(arg))) {
|
|
@@ -435,13 +509,13 @@ try {
|
|
|
435
509
|
// rotate-observations
|
|
436
510
|
// Archive the log rows no ledger row carries once 30 days have passed since
|
|
437
511
|
// their last activity, and delete the usage telemetry's leftovers
|
|
438
|
-
// (D-ROTATE-UNREFERENCED,
|
|
512
|
+
// (D-ROTATE-UNREFERENCED, curation-store.cjs). Takes no argument: the log and
|
|
439
513
|
// the archive are the project root's.
|
|
440
514
|
// stdout: rotated <N> observations
|
|
441
515
|
// -------------------------------------------------------------------------
|
|
442
516
|
case 'rotate-observations': {
|
|
443
517
|
if (args.length > 0) exitWithUsage('rotate-observations (no arguments; run from the project root)');
|
|
444
|
-
const result =
|
|
518
|
+
const result = curation().store.rotateObservations(process.cwd());
|
|
445
519
|
process.exitCode = emit(result, ({ rotated }) => `rotated ${rotated} observations`);
|
|
446
520
|
break;
|
|
447
521
|
}
|
|
@@ -449,7 +523,7 @@ try {
|
|
|
449
523
|
// -------------------------------------------------------------------------
|
|
450
524
|
// put-observation --create|--update|--reinforce
|
|
451
525
|
// Store one observation from the JSON object on stdin (D-PUT-NOT-MERGE,
|
|
452
|
-
// D-PUT-REPROJECTS,
|
|
526
|
+
// D-PUT-REPROJECTS, curation-store.cjs). Exactly one mode flag; no other argv.
|
|
453
527
|
// stdout: created <id> | updated <id> | unchanged <id> | reinforced <id> <n>,
|
|
454
528
|
// then one `reprojected <anchor>` line per entry re-projected
|
|
455
529
|
// -------------------------------------------------------------------------
|
|
@@ -459,7 +533,7 @@ try {
|
|
|
459
533
|
exitWithUsage('put-observation --create|--update|--reinforce (one JSON object on stdin; run from the project root)');
|
|
460
534
|
}
|
|
461
535
|
const input = readStdinJson('put-observation');
|
|
462
|
-
const result = input.ok ?
|
|
536
|
+
const result = input.ok ? curation().store.putObservation(process.cwd(), mode, input.value) : input;
|
|
463
537
|
process.exitCode = emit(result, put => [
|
|
464
538
|
put.outcome === 'reinforced' ? `reinforced ${put.id} ${put.observations}` : `${put.outcome} ${put.id}`,
|
|
465
539
|
...put.reprojected.map(anchorId => `reprojected ${anchorId}`),
|
|
@@ -470,25 +544,25 @@ try {
|
|
|
470
544
|
// -------------------------------------------------------------------------
|
|
471
545
|
// list
|
|
472
546
|
// Print the ledger and the log by section, read-only (readListing and
|
|
473
|
-
// formatListing,
|
|
547
|
+
// formatListing, curation-store.cjs). Takes no argument.
|
|
474
548
|
// stdout: the ACTIVE, INACTIVE, OBSERVATIONS and INTEGRITY sections, then
|
|
475
549
|
// MALFORMED when lines were skipped
|
|
476
550
|
// -------------------------------------------------------------------------
|
|
477
551
|
case 'list': {
|
|
478
552
|
if (args.length > 0) exitWithUsage('list (no arguments; run from the project root)');
|
|
479
|
-
const { store } =
|
|
553
|
+
const { store } = curation();
|
|
480
554
|
process.exitCode = emit(store.readListing(process.cwd()), store.formatListing);
|
|
481
555
|
break;
|
|
482
556
|
}
|
|
483
557
|
|
|
484
558
|
// -------------------------------------------------------------------------
|
|
485
559
|
// show <anchor|obs_id>
|
|
486
|
-
// Print one entry, read-only (showByKey,
|
|
560
|
+
// Print one entry, read-only (showByKey, curation-store.cjs).
|
|
487
561
|
// stdout: pretty JSON { key, ledger, log, history_versions, flags }, plus
|
|
488
562
|
// malformed when lines were skipped
|
|
489
563
|
// -------------------------------------------------------------------------
|
|
490
564
|
case 'show': {
|
|
491
|
-
const { store } =
|
|
565
|
+
const { store } = curation();
|
|
492
566
|
if (args.length !== 1 || !(store.ANCHOR_ID_RE.test(args[0]) || store.OBS_ID_RE.test(args[0]))) {
|
|
493
567
|
exitWithUsage('show <anchor|obs_id> (run from the project root)');
|
|
494
568
|
}
|
|
@@ -499,13 +573,13 @@ try {
|
|
|
499
573
|
// -------------------------------------------------------------------------
|
|
500
574
|
// claim-due
|
|
501
575
|
// Hand out the entries due for maintenance and lease each for a day
|
|
502
|
-
// (claimDue,
|
|
576
|
+
// (claimDue, curation-store.cjs: D-DUE-ORDER, D-VERIFY-REF). Takes no argument.
|
|
503
577
|
// stdout: ref <origin/HEAD|HEAD> <sha12>, or ref none; then one
|
|
504
578
|
// `<anchor> <reason> <bytes>` line per entry handed out, or due none
|
|
505
579
|
// -------------------------------------------------------------------------
|
|
506
580
|
case 'claim-due': {
|
|
507
581
|
if (args.length > 0) exitWithUsage('claim-due (no arguments; run from the project root)');
|
|
508
|
-
const { store } =
|
|
582
|
+
const { store } = curation();
|
|
509
583
|
process.exitCode = emit(store.claimDue(process.cwd()), ({ ref, due }) => [
|
|
510
584
|
ref === null ? 'ref none' : `ref ${ref.ref} ${ref.commit.slice(0, 12)}`,
|
|
511
585
|
...(due.length > 0 ? due.map(entry => `${store.singleLine(entry.anchor_id)} ${entry.reason} ${entry.bytes}`) : ['due none']),
|
|
@@ -515,13 +589,13 @@ try {
|
|
|
515
589
|
|
|
516
590
|
// -------------------------------------------------------------------------
|
|
517
591
|
// claim-queue
|
|
518
|
-
// Claim the
|
|
592
|
+
// Claim the curation queue for this run (D-OWNED-CLAIM, curation-store.cjs).
|
|
519
593
|
// Takes no argument; mints the token itself.
|
|
520
594
|
// stdout: claimed <token> | claimed <token> takeover | busy | none
|
|
521
595
|
// -------------------------------------------------------------------------
|
|
522
596
|
case 'claim-queue': {
|
|
523
597
|
if (args.length > 0) exitWithUsage('claim-queue (no arguments; run from the project root)');
|
|
524
|
-
const result =
|
|
598
|
+
const result = curation().store.claimQueue(process.cwd());
|
|
525
599
|
process.exitCode = emit(result, claim => (claim.state === 'claimed'
|
|
526
600
|
? `claimed ${claim.token}${claim.takeover ? ' takeover' : ''}`
|
|
527
601
|
: claim.state));
|
|
@@ -530,11 +604,11 @@ try {
|
|
|
530
604
|
|
|
531
605
|
// -------------------------------------------------------------------------
|
|
532
606
|
// release-claim <token>
|
|
533
|
-
// Release the claim the token owns (D-OWNED-CLAIM,
|
|
607
|
+
// Release the claim the token owns (D-OWNED-CLAIM, curation-store.cjs).
|
|
534
608
|
// stdout: released | not-owner | gone
|
|
535
609
|
// -------------------------------------------------------------------------
|
|
536
610
|
case 'release-claim': {
|
|
537
|
-
const { store } =
|
|
611
|
+
const { store } = curation();
|
|
538
612
|
if (args.length !== 1 || !store.CLAIM_TOKEN_RE.test(args[0])) {
|
|
539
613
|
exitWithUsage('release-claim <token> (the 16 hex characters claim-queue printed)');
|
|
540
614
|
}
|
|
@@ -542,12 +616,63 @@ try {
|
|
|
542
616
|
break;
|
|
543
617
|
}
|
|
544
618
|
|
|
619
|
+
// -------------------------------------------------------------------------
|
|
620
|
+
// move-ledger <root>
|
|
621
|
+
// Move the project's old ledger folder to the new name, once (moveLedger,
|
|
622
|
+
// curation-move.cjs). Not a curation op: it sends no heartbeat, because a
|
|
623
|
+
// heartbeat would refresh a claim the move has to judge stale. Unlike the
|
|
624
|
+
// curation ops it takes the root as an argument, so a caller outside the project
|
|
625
|
+
// root can name it; the root stays one argv word, never a shell word. The two
|
|
626
|
+
// folder names are the path helpers' own: no argument names them.
|
|
627
|
+
// stdout: none | busy | busy claim | skipped tracked | skipped unknown | moved <mode>,
|
|
628
|
+
// then `backup <name>` when a backup was made, `cause <word>` for `skipped unknown`
|
|
629
|
+
// (one fixed word that says why git did not answer), and one `note <text>` line for
|
|
630
|
+
// each note. `busy claim` means a fresh queue claim holds the move, which can last 15
|
|
631
|
+
// minutes; plain `busy` means a lock does. `skipped tracked` means git tracks files in
|
|
632
|
+
// a curation data folder, `skipped unknown` that git did not answer: nothing changed.
|
|
633
|
+
// Every line is scrubbed here (singleLine):
|
|
634
|
+
// a note may name a file the repository holds, and no line may carry a control
|
|
635
|
+
// character or break into two.
|
|
636
|
+
// -------------------------------------------------------------------------
|
|
637
|
+
case 'move-ledger': {
|
|
638
|
+
if (args.length !== 1) exitWithUsage('move-ledger <root> (the root is an absolute path)');
|
|
639
|
+
const { store } = curation();
|
|
640
|
+
process.exitCode = emit(curationMove().moveLedger(args[0]), moved => [
|
|
641
|
+
moveAnswerWord(moved),
|
|
642
|
+
...(moved.state === 'moved' && moved.backup !== null ? [`backup ${moved.backup}`] : []),
|
|
643
|
+
...(moved.state === 'skipped' && moved.cause !== undefined ? [`cause ${moved.cause}`] : []),
|
|
644
|
+
...moved.notes.map(note => `note ${note}`),
|
|
645
|
+
].map(line => store.singleLine(line)).join('\n'));
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// -------------------------------------------------------------------------
|
|
650
|
+
// tracked-area <curation|memory|both> <root>
|
|
651
|
+
// Does git track files in the data folders of the area at the root
|
|
652
|
+
// (inspectTrackedAreaOnce, tracked-areas.cjs)? Not a curation op: it sends no
|
|
653
|
+
// heartbeat and loads only the check. The root is an argument, so the shell gate
|
|
654
|
+
// names the root it decided on; it stays one argv word, never a shell word. The
|
|
655
|
+
// answer is asked once per root and area for the life of the process.
|
|
656
|
+
// stdout: tracked | untracked | unknown, and for unknown a second line
|
|
657
|
+
// `cause <word>` (one fixed word that says why git did not answer)
|
|
658
|
+
// -------------------------------------------------------------------------
|
|
659
|
+
case 'tracked-area': {
|
|
660
|
+
const { AREA_PATHSPECS, inspectTrackedAreaOnce } = trackedAreas();
|
|
661
|
+
if (args.length !== 2 || !Object.prototype.hasOwnProperty.call(AREA_PATHSPECS, args[0])) {
|
|
662
|
+
exitWithUsage('tracked-area <curation|memory|both> <root> (the root is an absolute path)');
|
|
663
|
+
}
|
|
664
|
+
const { answer, cause } = inspectTrackedAreaOnce(args[1], args[0]);
|
|
665
|
+
process.stdout.write(answer === 'unknown' ? `unknown\ncause ${cause}\n` : `${answer}\n`);
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
|
|
545
669
|
default:
|
|
546
670
|
process.stderr.write(`json-helper: unknown operation "${op}"\n`);
|
|
547
671
|
process.exit(1);
|
|
548
672
|
}
|
|
549
673
|
} catch (err) {
|
|
550
|
-
|
|
674
|
+
const reason = err && err.message ? err.message : String(err);
|
|
675
|
+
process.stderr.write(`json-helper error: ${curationModules === null ? reason : curationModules.store.singleLine(reason)}\n`);
|
|
551
676
|
process.exit(1);
|
|
552
677
|
}
|
|
553
678
|
} // end if (require.main === module)
|
|
@@ -170,7 +170,7 @@ json_prompt_output() {
|
|
|
170
170
|
# Build pre-compact backup JSON. Usage: json_backup_construct --arg ts X --arg branch Y ...
|
|
171
171
|
json_backup_construct() {
|
|
172
172
|
if [ "$_HAS_JQ" = "true" ]; then
|
|
173
|
-
jq -n "$@" '{timestamp: $ts, trigger: "pre-compact",
|
|
173
|
+
jq -n "$@" '{timestamp: $ts, trigger: "pre-compact", git: {branch: $branch, status: $status, log: $log, diff_stat: $diff}}'
|
|
174
174
|
else
|
|
175
175
|
node "$_JSON_HELPER" backup-construct "$@"
|
|
176
176
|
fi
|