devflow-kit 3.7.0 → 3.8.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 +359 -1
- package/README.md +11 -11
- package/dist/agents/code.md +2 -4
- package/dist/agents/design.md +1 -1
- package/dist/agents/fix.md +6 -7
- package/dist/agents/git.md +1 -1
- 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 +2 -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/{learning.js → curation.js} +302 -99
- package/dist/cli/commands/gate-level-prompts.js +426 -0
- package/dist/cli/commands/gates.js +34 -11
- package/dist/cli/commands/init-seed.js +11 -8
- package/dist/cli/commands/init.js +240 -54
- package/dist/cli/commands/memory.js +2 -2
- package/dist/cli/commands/prompt-io.js +37 -0
- package/dist/cli/commands/uninstall.js +81 -9
- package/dist/cli.js +2 -2
- package/dist/commands/bug-analysis.md +24 -35
- package/dist/commands/code-review.md +32 -36
- package/dist/commands/debug.md +4 -4
- package/dist/commands/dynamic-build.md +25 -24
- package/dist/commands/dynamic-plan.md +4 -4
- package/dist/commands/dynamic-profile.md +4 -4
- package/dist/commands/dynamic-tickets.md +4 -4
- package/dist/commands/explore.md +5 -5
- package/dist/commands/implement.md +52 -81
- package/dist/commands/plan.md +5 -5
- package/dist/commands/release.md +4 -4
- package/dist/commands/research.md +5 -5
- package/dist/commands/resolve.md +28 -30
- package/dist/commands/self-review.md +4 -12
- 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 +36 -0
- package/dist/core/curation-move.js +35 -0
- package/dist/core/curation-queue-cleanup.js +134 -0
- package/dist/core/curation-store.js +98 -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 +1 -1
- 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 +15 -15
- 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 +35 -0
- package/dist/core/tracker.js +1 -1
- package/dist/{learning-off → curation-off}/agents/code.md +2 -4
- package/dist/{learning-off → curation-off}/agents/fix.md +6 -7
- 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 +1 -1
- package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
- package/dist/{learning-off → curation-off}/commands/code-review.md +29 -33
- package/dist/{learning-off → curation-off}/commands/debug.md +1 -1
- package/dist/{learning-off → curation-off}/commands/dynamic-build.md +22 -21
- package/dist/{learning-off → curation-off}/commands/explore.md +1 -1
- package/dist/{learning-off → curation-off}/commands/implement.md +49 -78
- package/dist/{learning-off → curation-off}/commands/plan.md +1 -1
- package/dist/{learning-off → curation-off}/commands/resolve.md +25 -27
- package/dist/{learning-off → curation-off}/commands/self-review.md +1 -9
- package/dist/hud/components/{learning-counts.js → curation-counts.js} +36 -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/validate-branch.md +8 -5
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +13 -5
- 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 +2 -2
- 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/fix.mds +6 -5
- package/src/assets/agents/git.mds +1 -1
- 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 +22 -22
- package/src/assets/agents/validate.md +13 -26
- package/src/assets/commands/_partials/_decisions.mds +13 -13
- 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 +2 -2
- 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 +46 -50
- 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 +56 -50
- package/src/assets/commands/self-review.mds +20 -20
- package/src/assets/mds/git/_ops.mds +3 -1
- package/src/assets/mds/git/_ops_pr.mds +1 -1
- package/src/assets/mds/git/_ops_steps.mds +10 -4
- package/src/assets/mds/git/_pr.mds +7 -4
- package/src/assets/rules/quality.md +1 -0
- package/src/assets/scripts/hooks/background-memory-update +2 -2
- 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 +134 -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} +242 -118
- 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 +34 -22
- package/src/assets/scripts/hooks/lib/tracked-areas.cjs +259 -0
- package/src/assets/scripts/hooks/memory-worker +1 -1
- package/src/assets/scripts/hooks/pre-compact-memory +6 -16
- package/src/assets/scripts/hooks/queue-append +228 -103
- package/src/assets/scripts/hooks/resolve-project-root +2 -2
- package/src/assets/scripts/hooks/session-start-context +143 -69
- package/src/assets/scripts/hooks/session-start-memory +3 -43
- package/src/assets/scripts/lib/project-config.cjs +12 -5
- package/src/assets/scripts/resolve-base.cjs +1043 -0
- package/src/assets/scripts/resolve-settings.cjs +42 -18
- package/src/assets/skills/agent-references/SKILL.md +5 -2
- package/src/assets/skills/agent-references/references/commands/implement-levels.md +27 -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 +24 -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/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
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file curation-queue-cleanup.ts
|
|
3
|
+
*
|
|
4
|
+
* The curation queue drains: `drainCurationQueue`, which `devflow curation --clear`
|
|
5
|
+
* runs (src/cli/commands/curation.ts), and `drainDisabledCurationQueue`, which `devflow
|
|
6
|
+
* curation --disable` and the drain `devflow init` runs when curation is switched off
|
|
7
|
+
* (src/cli/commands/init.ts) run: the same drain, and the pending rows of the backups
|
|
8
|
+
* a move left as well.
|
|
9
|
+
*
|
|
10
|
+
* Both drains answer `skipped`, and delete nothing, when git tracks any entry under a
|
|
11
|
+
* data folder or a backup folder, or does not answer (the tracked-areas check). The
|
|
12
|
+
* line a caller prints for such a drain is {@link formatCurationNotDrained}.
|
|
13
|
+
*/
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { LEGACY_NAMES, getCurationClaimOwnerPath, getCurationDir, getCurationPendingTurnsPath, getCurationPendingTurnsProcessingPath, } from './project-paths.js';
|
|
16
|
+
import { loadCurationMove } from './curation-move.js';
|
|
17
|
+
import { MOVE_SKIP_NOTICE } from './curation-move-skip.js';
|
|
18
|
+
import { formatCurationStoreUnavailable } from './curation-store.js';
|
|
19
|
+
import { drainQueueFiles, formatRefusedDrain } from './queue-drain.js';
|
|
20
|
+
import { loadTrackedAreas } from './tracked-areas.js';
|
|
21
|
+
/** The folders to check for links, and the queue files to delete, in the live and the old ledger folder of `gitRoot`. */
|
|
22
|
+
function liveQueueTargets(gitRoot) {
|
|
23
|
+
const curationDir = getCurationDir(gitRoot);
|
|
24
|
+
const devflowDir = path.dirname(curationDir);
|
|
25
|
+
const oldDir = path.join(devflowDir, LEGACY_NAMES.folder);
|
|
26
|
+
const queueFiles = [
|
|
27
|
+
getCurationPendingTurnsPath(gitRoot),
|
|
28
|
+
getCurationPendingTurnsProcessingPath(gitRoot),
|
|
29
|
+
getCurationClaimOwnerPath(gitRoot),
|
|
30
|
+
];
|
|
31
|
+
return {
|
|
32
|
+
folders: [devflowDir, curationDir, oldDir],
|
|
33
|
+
files: [...queueFiles, ...queueFiles.map(file => path.join(oldDir, path.basename(file)))],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Drain the curation (decisions-detection) pending-turns queue, its claimed
|
|
38
|
+
* batch and the claim's owner file so stale turns don't process later — used by
|
|
39
|
+
* `--clear`. A mid-run Curate agent whose claimed batch vanishes aborts without
|
|
40
|
+
* changes, which is the desired outcome.
|
|
41
|
+
*
|
|
42
|
+
* Drains each ledger folder that exists, the new name and the old one: a move that
|
|
43
|
+
* was busy or refused leaves the old folder in place, and its queue must not process
|
|
44
|
+
* on a later re-enable. A folder or file that is absent is skipped. Only the three
|
|
45
|
+
* queue files go; the ledger, the log and the tuning file stay, and so does every
|
|
46
|
+
* backup folder a move left (see {@link drainDisabledCurationQueue}).
|
|
47
|
+
*
|
|
48
|
+
* Asks first the question the disable drain asks (see {@link drainDisabledCurationQueue}):
|
|
49
|
+
* when git tracks any entry under the data folders or a backup folder of `gitRoot`, or
|
|
50
|
+
* a `.git` entry stands and git does not answer, nothing is deleted and the answer is
|
|
51
|
+
* `skipped`.
|
|
52
|
+
*
|
|
53
|
+
* Refused, deleting nothing in either folder, when `.devflow` or either ledger
|
|
54
|
+
* folder under `gitRoot` is a symbolic link (D-CLI-NO-SYMLINK). ENOENT-tolerant;
|
|
55
|
+
* other errors propagate.
|
|
56
|
+
*
|
|
57
|
+
* `loadTracking` is the check's loader; its default is the package's own copy.
|
|
58
|
+
*/
|
|
59
|
+
export async function drainCurationQueue(gitRoot, loadTracking = loadTrackedAreas) {
|
|
60
|
+
const skipped = trackedSkip(gitRoot, loadTracking);
|
|
61
|
+
if (skipped !== null)
|
|
62
|
+
return skipped;
|
|
63
|
+
const { folders, files } = liveQueueTargets(gitRoot);
|
|
64
|
+
return drainQueueFiles(folders, files);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The drain of a switched-off curation: {@link drainCurationQueue}, and the pending
|
|
68
|
+
* rows of the backup folders a move left as well. A backup's queue, claim, private and
|
|
69
|
+
* overflow files go (the files `devflow curation --status` counts as pending rows);
|
|
70
|
+
* its ledger files, its swept files and the claim's owner file stay. Without this, a
|
|
71
|
+
* later re-enable and init would merge the backup's rows into the live queue, which is
|
|
72
|
+
* the turns the drain exists to discard. The backups are read within the sweep's
|
|
73
|
+
* bounds: 500 backup folders, and 200 files in each.
|
|
74
|
+
*
|
|
75
|
+
* Asks first whether git tracks any entry under the data folders or a backup folder of
|
|
76
|
+
* `gitRoot`, the question the move asks (the tracked-areas check). A folder that git
|
|
77
|
+
* tracks is the repository's content, so the drain leaves the project alone: it deletes
|
|
78
|
+
* nothing in the live folder, the old folder or any backup, and answers `skipped` with
|
|
79
|
+
* the reason `tracked`, or `unknown` when a `.git` entry stands and git did not answer
|
|
80
|
+
* (a check that cannot be loaded, or a root that is not absolute, answers `unknown`
|
|
81
|
+
* too). A root with no `.git` entry makes no git call and drains as usual.
|
|
82
|
+
*
|
|
83
|
+
* Refused, deleting nothing anywhere, when a link is in the way: `.devflow`, either
|
|
84
|
+
* ledger folder, a backup folder that may hold rows, or one of a backup's queue files.
|
|
85
|
+
* The answer names the link. ENOENT-tolerant. Any other failure of the backup list, a
|
|
86
|
+
* move module that does not load or a folder that cannot be read, never stops the
|
|
87
|
+
* drain of the live and the old folder: those drain as usual, and the answer carries
|
|
88
|
+
* a `warning` that the backups were not drained, which the caller prints.
|
|
89
|
+
*
|
|
90
|
+
* `load` is the move module's loader and `loadTracking` the check's; each default is
|
|
91
|
+
* the package's own copy.
|
|
92
|
+
*/
|
|
93
|
+
export async function drainDisabledCurationQueue(gitRoot, load = loadCurationMove, loadTracking = loadTrackedAreas) {
|
|
94
|
+
const skipped = trackedSkip(gitRoot, loadTracking);
|
|
95
|
+
if (skipped !== null)
|
|
96
|
+
return skipped;
|
|
97
|
+
const { folders, files } = liveQueueTargets(gitRoot);
|
|
98
|
+
const loaded = load();
|
|
99
|
+
const listed = loaded.ok ? loaded.value.backupQueueFiles(gitRoot) : null;
|
|
100
|
+
if (listed !== null && !listed.ok && listed.error.kind === 'refused' && listed.error.path !== undefined) {
|
|
101
|
+
return { drained: false, linkedFolder: listed.error.path };
|
|
102
|
+
}
|
|
103
|
+
const drain = await drainQueueFiles(folders, [...files, ...(listed !== null && listed.ok ? listed.value.files : [])]);
|
|
104
|
+
if (!drain.drained)
|
|
105
|
+
return drain;
|
|
106
|
+
const failure = !loaded.ok ? formatCurationStoreUnavailable(loaded.error) : listed !== null && !listed.ok ? listed.error.message : null;
|
|
107
|
+
return failure === null ? drain : { drained: true, warning: formatBackupsNotDrained(failure) };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The skipped answer for `root`, or null when the drain may go on. The tracked-areas
|
|
111
|
+
* check decides; a check that cannot be loaded cannot answer, so it is `unknown`.
|
|
112
|
+
*/
|
|
113
|
+
function trackedSkip(root, load) {
|
|
114
|
+
const loaded = load();
|
|
115
|
+
const answer = loaded.ok ? loaded.value.checkTrackedAreas(root) : 'unknown';
|
|
116
|
+
return answer === 'untracked' ? null : { drained: false, skipped: answer };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The line a caller prints for a drain that deleted nothing: the fixed notice of a
|
|
120
|
+
* skipped project (no repository content in it), or the refusal at a link. When the
|
|
121
|
+
* caller has just printed that notice (`shown`, the reason it showed), the line does
|
|
122
|
+
* not repeat it.
|
|
123
|
+
*/
|
|
124
|
+
export function formatCurationNotDrained(drain, shown = null) {
|
|
125
|
+
if (!('skipped' in drain))
|
|
126
|
+
return formatRefusedDrain('curation', drain.linkedFolder);
|
|
127
|
+
const line = 'The curation queue was not drained.';
|
|
128
|
+
return shown === drain.skipped ? line : `${line} ${MOVE_SKIP_NOTICE[drain.skipped]}`;
|
|
129
|
+
}
|
|
130
|
+
/** The warning for a disable drain that could not list the backups: the live queue is drained, the backups are not. */
|
|
131
|
+
function formatBackupsNotDrained(reason) {
|
|
132
|
+
return `The queue files of the backup folders were not drained: ${reason}. The live curation queue was drained.`;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=curation-queue-cleanup.js.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLI's view of the curation store — a typed seam onto the package's own
|
|
3
|
+
* `hooks/lib/curation-store.cjs`, never a second implementation of it.
|
|
4
|
+
*
|
|
5
|
+
* D-LEARNING-STORE-SEAM: `devflow curation` reads and writes the curation tree only
|
|
6
|
+
* through the store module json-helper's ops and the hooks run, loaded with the
|
|
7
|
+
* evidence-policy seam's `loadScript` from the package's own scripts directory and
|
|
8
|
+
* shape-checked against CURATION_STORE_SURFACE; the interfaces below are
|
|
9
|
+
* TRANSCRIBED from the store's JSDoc and are this side's only shape authority.
|
|
10
|
+
* Reason: the CLI's own TypeScript reader judged every row by the v1 shape, so it
|
|
11
|
+
* counted v2 observations as invalid, and its `--clear` truncated the log without
|
|
12
|
+
* the lock every curation writer takes.
|
|
13
|
+
*
|
|
14
|
+
* As with the evidence resolver (D-POLICY-CJS-SEAM), the CLI loads the package
|
|
15
|
+
* copy, which `npm` installs with this CLI, not `~/.devflow/scripts`, which
|
|
16
|
+
* `devflow init` refreshes: the two can differ until the next init.
|
|
17
|
+
*/
|
|
18
|
+
import { join } from 'path';
|
|
19
|
+
import { scriptsDir } from './assets.js';
|
|
20
|
+
import { loadScript } from './evidence-policy.js';
|
|
21
|
+
/** The store, relative to src/assets/scripts/ (and ~/.devflow/scripts/). */
|
|
22
|
+
export const CURATION_STORE_SCRIPT_NAME = join('hooks', 'lib', 'curation-store.cjs');
|
|
23
|
+
/**
|
|
24
|
+
* Every kind a store or move function can refuse with: the `kind` literals in
|
|
25
|
+
* curation-store.cjs and curation-move.cjs, the refusal builders' kinds, and the
|
|
26
|
+
* `successor-` kinds the retire op derives from the kind of its successor lookup.
|
|
27
|
+
* A test keeps this list equal to the kinds those modules name.
|
|
28
|
+
*/
|
|
29
|
+
export const CURATION_STORE_ERROR_KINDS = [
|
|
30
|
+
'no-curation-dir',
|
|
31
|
+
'not-a-directory',
|
|
32
|
+
'not-a-file',
|
|
33
|
+
'busy',
|
|
34
|
+
'refused',
|
|
35
|
+
'io-error',
|
|
36
|
+
'invalid-input',
|
|
37
|
+
'invalid-key',
|
|
38
|
+
'not-found',
|
|
39
|
+
'duplicate-anchor',
|
|
40
|
+
'ledger-malformed',
|
|
41
|
+
'heartbeat-failed',
|
|
42
|
+
'restore-first',
|
|
43
|
+
'duplicate-log-id',
|
|
44
|
+
'cannot-reproject',
|
|
45
|
+
'not-in-log',
|
|
46
|
+
'already-promoted',
|
|
47
|
+
'v1-observation',
|
|
48
|
+
'type-mismatch',
|
|
49
|
+
'cited-numbers-exhausted',
|
|
50
|
+
'no-verify-ref',
|
|
51
|
+
'not-at-ref',
|
|
52
|
+
'git-failed',
|
|
53
|
+
'quote-not-found',
|
|
54
|
+
'already-inactive',
|
|
55
|
+
'already-active',
|
|
56
|
+
'successor-not-found',
|
|
57
|
+
'successor-duplicate-anchor',
|
|
58
|
+
'successor-inactive',
|
|
59
|
+
];
|
|
60
|
+
/**
|
|
61
|
+
* Every key of CurationStoreModule and the runtime kind the loader requires of
|
|
62
|
+
* it. `satisfies` makes the compiler reject an interface key missing here.
|
|
63
|
+
*/
|
|
64
|
+
export const CURATION_STORE_SURFACE = Object.freeze({
|
|
65
|
+
INACTIVE_STATUSES: 'string-array',
|
|
66
|
+
ANCHOR_ID_RE: 'regexp',
|
|
67
|
+
readCurationState: 'function',
|
|
68
|
+
buildListing: 'function',
|
|
69
|
+
readListing: 'function',
|
|
70
|
+
formatListing: 'function',
|
|
71
|
+
showByKey: 'function',
|
|
72
|
+
restoreAnchor: 'function',
|
|
73
|
+
clearUnreferenced: 'function',
|
|
74
|
+
resetCuration: 'function',
|
|
75
|
+
});
|
|
76
|
+
/**
|
|
77
|
+
* Load the curation store from `dir` (default: the package's own scripts
|
|
78
|
+
* directory) and shape-check its surface. Never throws: a missing file is
|
|
79
|
+
* `not-found`; a module that throws on load or lacks a surface key is `unusable`.
|
|
80
|
+
*/
|
|
81
|
+
export function loadCurationStore(dir = scriptsDir()) {
|
|
82
|
+
return loadScript(join(dir, CURATION_STORE_SCRIPT_NAME), CURATION_STORE_SURFACE);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Why the store cannot be used, and the remedy: a package reinstall, since the
|
|
86
|
+
* CLI loads the package's own copy, which `devflow init` does not restore.
|
|
87
|
+
*/
|
|
88
|
+
export function formatCurationStoreUnavailable(error) {
|
|
89
|
+
switch (error.kind) {
|
|
90
|
+
case 'not-found': return 'curation store not found — reinstall devflow-kit';
|
|
91
|
+
case 'unusable': return 'curation store failed to load — reinstall devflow-kit';
|
|
92
|
+
default: {
|
|
93
|
+
const exhaustive = error;
|
|
94
|
+
return exhaustive;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=curation-store.js.map
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Curation-variant splitter: one compiled prompt body in, its curation-on and
|
|
3
|
+
* curation-off variants out.
|
|
4
4
|
*
|
|
5
5
|
* Pure module, zero I/O. Every refusal is a Result; the exiting shell is
|
|
6
6
|
* scripts/build-mds.ts, which renders these errors into the build's single exit.
|
|
7
7
|
*
|
|
8
|
-
* D-LEARNING-VARIANTS: when
|
|
8
|
+
* D-LEARNING-VARIANTS: when curation is off on a machine, the decisions text is
|
|
9
9
|
* ABSENT from every prompt it would have reached, not merely gated at run time.
|
|
10
|
-
* The build therefore emits two variants of a prompt that carries
|
|
11
|
-
* the
|
|
12
|
-
* and the
|
|
10
|
+
* The build therefore emits two variants of a prompt that carries curation text:
|
|
11
|
+
* the curation-on variant at today's path (dist/commands/x.md, dist/agents/x.md)
|
|
12
|
+
* and the curation-off variant at dist/curation-off/{commands,agents}/x.md, only
|
|
13
13
|
* for a host that has an arm. A prompt with no arm has one variant and no
|
|
14
|
-
*
|
|
14
|
+
* curation-off file.
|
|
15
15
|
*
|
|
16
16
|
* The arms are whole-line markers in the host or in a partial it expands:
|
|
17
17
|
*
|
|
18
|
-
* <!--
|
|
19
|
-
* <!--
|
|
18
|
+
* <!-- curation:on --> opens an arm that only the curation-on variant keeps
|
|
19
|
+
* <!-- curation:off --> opens an arm that only the curation-off variant keeps;
|
|
20
20
|
* directly after an on arm it is that arm's else
|
|
21
|
-
* <!--
|
|
21
|
+
* <!-- curation:end --> closes the arm
|
|
22
22
|
*
|
|
23
|
-
* A marker line matches
|
|
23
|
+
* A marker line matches CURATION_MARKER_RE. It may be indented and may sit inside
|
|
24
24
|
* a fenced block, a list, a define body or an agent's second frontmatter block,
|
|
25
25
|
* because MDS passes an HTML comment through untouched. The splitter runs on the
|
|
26
26
|
* COMPILED body, after the MDS compile, so it adds no compileFile call, no import
|
|
@@ -36,21 +36,26 @@
|
|
|
36
36
|
* fence- and indent-aware `@if` (dean0x/mdscript#452). That swap, with the
|
|
37
37
|
* @mdscript/mds upgrade it rides on proven byte-identical in dist/ and this module
|
|
38
38
|
* deleted, is dean0x/devflow#439. The mapping is one to one: `on` becomes
|
|
39
|
-
* `@if
|
|
40
|
-
* `off` arm, with no on arm before it, becomes an `@if
|
|
39
|
+
* `@if curation:`, `off` becomes `@else:` and `end` becomes `@end`. A standalone
|
|
40
|
+
* `off` arm, with no on arm before it, becomes an `@if curation:` with an empty
|
|
41
41
|
* body followed by `@else:`.
|
|
42
42
|
*
|
|
43
|
-
* What the splitter refuses, each as a
|
|
43
|
+
* What the splitter refuses, each as a CurationSplitError with the source line:
|
|
44
44
|
* - nesting: an `on` marker while an arm is open, or a second `off` in one arm;
|
|
45
45
|
* - an `end` with no open arm;
|
|
46
46
|
* - an arm still open at the end of the body;
|
|
47
47
|
* - an empty arm (no line with content between its marker and the next);
|
|
48
|
-
* - a
|
|
48
|
+
* - a retired marker: a line that names the pre-rename spelling of the marker, in
|
|
49
|
+
* any spacing or case, which would otherwise ship as prompt text;
|
|
50
|
+
* - a stray marker: a line that names `<!-- curation:` but is not a whole-line
|
|
49
51
|
* marker, which would otherwise ship as prompt text.
|
|
50
52
|
*
|
|
53
|
+
* The retired spelling is never accepted as an alias: the build fails on it, so a
|
|
54
|
+
* host that still carries it cannot ship a prompt with both variants silently equal.
|
|
55
|
+
*
|
|
51
56
|
* Scope guarantee: this module answers one question (what are the two variants of
|
|
52
57
|
* this body?) and one cheap one for the build to ask of a body that must carry no
|
|
53
|
-
* arms (
|
|
58
|
+
* arms (containsCurationMarker). It decides nothing about WHICH text belongs in an
|
|
54
59
|
* arm: that is authoring, and the guards over the built trees hold it.
|
|
55
60
|
*/
|
|
56
61
|
function Ok(value) {
|
|
@@ -60,52 +65,65 @@ function Err(error) {
|
|
|
60
65
|
return { ok: false, error };
|
|
61
66
|
}
|
|
62
67
|
/**
|
|
63
|
-
* Repo-relative root of the
|
|
68
|
+
* Repo-relative root of the curation-off variants.
|
|
64
69
|
*
|
|
65
70
|
* Exported because the build's orphan prune names this directory even when no
|
|
66
71
|
* host carries an arm, which is exactly the case where every file in it is an
|
|
67
72
|
* orphan, so it cannot be derived from the plan.
|
|
68
73
|
*/
|
|
69
|
-
export const
|
|
74
|
+
export const CURATION_OFF_OUTPUT_DIR = 'dist/curation-off';
|
|
75
|
+
/**
|
|
76
|
+
* Repo-relative root of the retired off tree, from before the rename.
|
|
77
|
+
*
|
|
78
|
+
* The build removes this folder whole after a clean run, so a checkout that built
|
|
79
|
+
* before the rename keeps no stale prompts there: nothing reads the folder, and the
|
|
80
|
+
* package would otherwise ship it.
|
|
81
|
+
*/
|
|
82
|
+
export const LEGACY_OFF_OUTPUT_DIR = 'dist/learning-off';
|
|
70
83
|
/**
|
|
71
|
-
* POSIX repo-relative path of the
|
|
84
|
+
* POSIX repo-relative path of the curation-off variant of an output file.
|
|
72
85
|
*
|
|
73
|
-
* `fileName` is the already-validated basename the
|
|
86
|
+
* `fileName` is the already-validated basename the curation-on variant is written
|
|
74
87
|
* under (`implement.md`), so the two variants of one host always share a name.
|
|
75
88
|
*/
|
|
76
|
-
export function
|
|
77
|
-
return `${
|
|
89
|
+
export function curationOffRelPath(kind, fileName) {
|
|
90
|
+
return `${CURATION_OFF_OUTPUT_DIR}/${kind}/${fileName}`;
|
|
78
91
|
}
|
|
79
92
|
// ---------------------------------------------------------------------------
|
|
80
93
|
// Markers
|
|
81
94
|
// ---------------------------------------------------------------------------
|
|
82
95
|
/**
|
|
83
|
-
* A whole-line
|
|
96
|
+
* A whole-line curation marker. The group captures `on`, `off` or `end`.
|
|
84
97
|
*
|
|
85
98
|
* Anchored at both ends with no nested quantifier, so a match is linear in the
|
|
86
99
|
* line length. Leading and trailing space or tab is allowed; anything else on the
|
|
87
100
|
* line is not a marker.
|
|
88
101
|
*/
|
|
89
|
-
export const
|
|
102
|
+
export const CURATION_MARKER_RE = /^[ \t]*<!-- curation:(on|off|end) -->[ \t]*$/;
|
|
90
103
|
/**
|
|
91
104
|
* The fragment that makes a line a marker ATTEMPT: loose on spacing and case so a
|
|
92
105
|
* malformed marker is caught rather than shipped. Every valid marker contains it.
|
|
93
106
|
*/
|
|
94
|
-
const MARKER_FRAGMENT_RE = /<!--\s*
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
const MARKER_FRAGMENT_RE = /<!--\s*curation\s*:/i;
|
|
108
|
+
/**
|
|
109
|
+
* The same attempt test for the retired spelling of the marker. No spelling of it is
|
|
110
|
+
* valid, so a line that matches is always refused.
|
|
111
|
+
*/
|
|
112
|
+
const LEGACY_MARKER_FRAGMENT_RE = /<!--\s*learning\s*:/i;
|
|
113
|
+
/** True when the text names a marker anywhere, in the current or the retired spelling, valid or not. */
|
|
114
|
+
export function containsCurationMarker(text) {
|
|
115
|
+
return MARKER_FRAGMENT_RE.test(text) || LEGACY_MARKER_FRAGMENT_RE.test(text);
|
|
98
116
|
}
|
|
99
117
|
function markerOf(line) {
|
|
100
118
|
// A CRLF body carries the carriage return on the line; it is not part of the marker.
|
|
101
119
|
const bare = line.endsWith('\r') ? line.slice(0, -1) : line;
|
|
102
|
-
const match =
|
|
120
|
+
const match = CURATION_MARKER_RE.exec(bare);
|
|
103
121
|
return match === null ? null : match[1];
|
|
104
122
|
}
|
|
105
123
|
/** The longest stretch of a stray line a refusal quotes. */
|
|
106
124
|
const STRAY_QUOTE_MAX = 80;
|
|
107
125
|
/**
|
|
108
|
-
* Split a compiled body into its
|
|
126
|
+
* Split a compiled body into its curation-on and curation-off variants.
|
|
109
127
|
*
|
|
110
128
|
* Total and pure: the same input gives the same output, and a refusal is a
|
|
111
129
|
* Result. The loop is one pass over the body's lines, so its bound is the body's
|
|
@@ -115,7 +133,7 @@ const STRAY_QUOTE_MAX = 80;
|
|
|
115
133
|
* refused as empty, because it would add blank lines to one variant and nothing to
|
|
116
134
|
* the other, which is a mistake rather than a design.
|
|
117
135
|
*/
|
|
118
|
-
export function
|
|
136
|
+
export function splitCurationVariants(body) {
|
|
119
137
|
const lines = body.split('\n');
|
|
120
138
|
const on = [];
|
|
121
139
|
const off = [];
|
|
@@ -130,6 +148,9 @@ export function splitLearningVariants(body) {
|
|
|
130
148
|
const lineNo = index + 1;
|
|
131
149
|
const marker = markerOf(line);
|
|
132
150
|
if (marker === null) {
|
|
151
|
+
if (LEGACY_MARKER_FRAGMENT_RE.test(line)) {
|
|
152
|
+
return Err({ kind: 'legacy-marker', line: lineNo, text: line.trim().slice(0, STRAY_QUOTE_MAX) });
|
|
153
|
+
}
|
|
133
154
|
if (MARKER_FRAGMENT_RE.test(line)) {
|
|
134
155
|
return Err({ kind: 'stray-marker', line: lineNo, text: line.trim().slice(0, STRAY_QUOTE_MAX) });
|
|
135
156
|
}
|
|
@@ -186,28 +207,31 @@ export function splitLearningVariants(body) {
|
|
|
186
207
|
* fall through into a message that does not describe it. Shared by the build and
|
|
187
208
|
* the tests, so the text asserted is the text shipped.
|
|
188
209
|
*/
|
|
189
|
-
export function
|
|
210
|
+
export function describeCurationSplitError(error) {
|
|
190
211
|
switch (error.kind) {
|
|
191
212
|
case 'nested-arm':
|
|
192
|
-
return (`line ${error.line}: a
|
|
193
|
-
`${error.openedAt} — arms do not nest; close it with
|
|
213
|
+
return (`line ${error.line}: a curation:on marker inside the ${error.arm} arm opened at line ` +
|
|
214
|
+
`${error.openedAt} — arms do not nest; close it with curation:end first`);
|
|
194
215
|
case 'repeated-off':
|
|
195
|
-
return (`line ${error.line}: a second
|
|
216
|
+
return (`line ${error.line}: a second curation:off marker inside the off arm opened at line ` +
|
|
196
217
|
`${error.openedAt} — an arm has at most one else`);
|
|
197
218
|
case 'unopened-end':
|
|
198
|
-
return `line ${error.line}: a
|
|
219
|
+
return `line ${error.line}: a curation:end marker with no open arm`;
|
|
199
220
|
case 'unclosed-arm':
|
|
200
|
-
return `the ${error.arm} arm opened at line ${error.openedAt} is never closed with
|
|
221
|
+
return `the ${error.arm} arm opened at line ${error.openedAt} is never closed with curation:end`;
|
|
201
222
|
case 'empty-arm':
|
|
202
223
|
return (`lines ${error.openedAt}-${error.closedAt}: the ${error.arm} arm holds no line with content — ` +
|
|
203
224
|
`delete the arm or put its text inside it`);
|
|
225
|
+
case 'legacy-marker':
|
|
226
|
+
return (`line ${error.line}: "${error.text}" uses the retired learning marker — rename it to ` +
|
|
227
|
+
`<!-- curation:on -->, <!-- curation:off --> or <!-- curation:end -->`);
|
|
204
228
|
case 'stray-marker':
|
|
205
|
-
return (`line ${error.line}: "${error.text}" names a
|
|
206
|
-
`is a whole line, exactly <!--
|
|
229
|
+
return (`line ${error.line}: "${error.text}" names a curation marker but is not one — a marker ` +
|
|
230
|
+
`is a whole line, exactly <!-- curation:on -->, <!-- curation:off --> or <!-- curation:end -->`);
|
|
207
231
|
default: {
|
|
208
232
|
const unhandled = error;
|
|
209
|
-
return `unhandled
|
|
233
|
+
return `unhandled curation split error ${JSON.stringify(unhandled)}`;
|
|
210
234
|
}
|
|
211
235
|
}
|
|
212
236
|
}
|
|
213
|
-
//# sourceMappingURL=
|
|
237
|
+
//# sourceMappingURL=curation-variants.js.map
|
package/dist/core/echo-value.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* shares (the tracker provider and the gate level today), so no parser imports
|
|
6
6
|
* another feature's module just to reach it.
|
|
7
7
|
*/
|
|
8
|
-
/** Longest rejected value echoed back to the terminal, in CODE POINTS. */
|
|
8
|
+
/** Longest rejected value echoed back to the terminal, in CODE POINTS, unless the caller names another. */
|
|
9
9
|
const MAX_ECHOED_VALUE = 40;
|
|
10
10
|
/**
|
|
11
11
|
* Render an untrusted token for a terminal message.
|
|
@@ -14,6 +14,10 @@ const MAX_ECHOED_VALUE = 40;
|
|
|
14
14
|
* third-party input at a display sink: control characters (terminal escapes,
|
|
15
15
|
* BEL, newlines) are replaced and the value is truncated.
|
|
16
16
|
*
|
|
17
|
+
* `maxLength` is the cut, in code points. A caller that echoes a whole message (a name
|
|
18
|
+
* inside a sentence) names a longer bound; anything that is not a positive integer takes
|
|
19
|
+
* the default, so the render stays bounded whatever a caller passes.
|
|
20
|
+
*
|
|
17
21
|
* Takes `unknown`, and a non-string renders as its TYPE: the never-throws
|
|
18
22
|
* contract of the callers has to hold for what reaches this sink, not
|
|
19
23
|
* only for what the signature says — a caller reads hand-editable
|
|
@@ -22,21 +26,23 @@ const MAX_ECHOED_VALUE = 40;
|
|
|
22
26
|
* caller-supplied `toString` — itself both a throw path and an echo path this
|
|
23
27
|
* function exists to close.
|
|
24
28
|
*/
|
|
25
|
-
export function describeEchoedValue(raw) {
|
|
29
|
+
export function describeEchoedValue(raw, maxLength = MAX_ECHOED_VALUE) {
|
|
26
30
|
if (typeof raw !== 'string')
|
|
27
31
|
return `<${raw === null ? 'null' : typeof raw}>`;
|
|
32
|
+
const limit = Number.isInteger(maxLength) && maxLength > 0 ? maxLength : MAX_ECHOED_VALUE;
|
|
28
33
|
// The class is written with ESCAPES, never literal control bytes. A raw NUL
|
|
29
34
|
// makes grep classify this whole file as binary — it prints "Binary file
|
|
30
35
|
// matches" and skips the lines — so every grep-based sweep over src/core/
|
|
31
36
|
// silently stops covering the file while still exiting 0. Pinned by
|
|
32
37
|
// tests/guards/no-control-bytes.test.ts.
|
|
38
|
+
// C0, DEL and C1: a terminal acts on all three (U+009B is the one-character CSI).
|
|
33
39
|
// eslint-disable-next-line no-control-regex
|
|
34
|
-
const stripped = raw.replace(/[\x00-\x1f\x7f]/g, '?');
|
|
40
|
+
const stripped = raw.replace(/[\x00-\x1f\x7f-\x9f]/g, '?');
|
|
35
41
|
// Cut by CODE POINT. `slice` cuts UTF-16 code units, so a cut landing inside an
|
|
36
42
|
// astral character (emoji, CJK ext-B) emits the lone surrogate half of it.
|
|
37
43
|
const points = [...stripped];
|
|
38
|
-
return points.length >
|
|
39
|
-
? `${points.slice(0,
|
|
44
|
+
return points.length > limit
|
|
45
|
+
? `${points.slice(0, limit).join('')}…`
|
|
40
46
|
: stripped;
|
|
41
47
|
}
|
|
42
48
|
//# sourceMappingURL=echo-value.js.map
|
|
@@ -102,7 +102,7 @@ function surfaceMismatches(value, surface) {
|
|
|
102
102
|
* throws: a missing file is `not-found`; a module that throws on load or lacks a
|
|
103
103
|
* surface key is `unusable`. The caller's type parameter is justified by the
|
|
104
104
|
* surface check, which `satisfies` ties to the interface's keys. Exported for
|
|
105
|
-
* src/core/
|
|
105
|
+
* src/core/curation-store.ts, which loads the curation store the same way.
|
|
106
106
|
*/
|
|
107
107
|
export function loadScript(scriptPath, surface) {
|
|
108
108
|
let loaded;
|