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
|
@@ -1,554 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { promises as fs } from 'fs';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import * as p from '@clack/prompts';
|
|
5
|
-
import color from 'picocolors';
|
|
6
|
-
import { getLearningDir, getCurateTuningConfigPath, } from '../../core/project-paths.js';
|
|
7
|
-
import { loadShippedAgentDefaults } from '../../core/agent-models.js';
|
|
8
|
-
import { readMachineFeature, writeMachineFeature } from '../../core/feature-switch.js';
|
|
9
|
-
import { loadSettingsModule, narrowedSwitchLabel, personalConfigTrackedWarning } from '../../core/evidence-policy.js';
|
|
10
|
-
import { getClaudeDirectory, getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
|
|
11
|
-
import { applyLearningToggle, describeLearningConverge, readRunningVersion, } from '../../targets/claude-code/learning-install.js';
|
|
12
|
-
import { getLedgerRoot } from '../../core/ledger-root.js';
|
|
13
|
-
import { drainLearningQueue } from '../../core/learning-queue-cleanup.js';
|
|
14
|
-
import { firstSymbolicLink } from '../../core/linked-path.js';
|
|
15
|
-
import { formatRefusedDrain } from '../../core/queue-drain.js';
|
|
16
|
-
import { formatLearningStoreUnavailable, loadLearningStore, } from '../../core/learning-store.js';
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
// Shared helpers
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
/** What the read and restore commands say about a project with no `.devflow/learning/`. */
|
|
21
|
-
const NO_LEARNING_DATA = 'No learning data in this project yet.';
|
|
22
|
-
/**
|
|
23
|
-
* How long a writer here (--restore, --clear, --reset) waits for the learning lock
|
|
24
|
-
* before it refuses as busy. An op holds the lock for milliseconds, so a longer
|
|
25
|
-
* wait means a stuck holder, and the store's own 30 s wait would leave the command
|
|
26
|
-
* looking hung.
|
|
27
|
-
*/
|
|
28
|
-
const LOCK_WAIT_MS = 5000;
|
|
29
|
-
/**
|
|
30
|
-
* Code point ranges JSON.stringify leaves raw that a terminal may act on, or may
|
|
31
|
-
* display reordered: DEL and the C1 controls, the directional marks, the line and
|
|
32
|
-
* paragraph separators, and the bidirectional embeddings, overrides and isolates.
|
|
33
|
-
*/
|
|
34
|
-
const TERMINAL_UNSAFE_RANGES = [
|
|
35
|
-
[0x7f, 0x9f],
|
|
36
|
-
[0x200e, 0x200f],
|
|
37
|
-
[0x2028, 0x2029],
|
|
38
|
-
[0x202a, 0x202e],
|
|
39
|
-
[0x2066, 0x2069],
|
|
40
|
-
];
|
|
41
|
-
/**
|
|
42
|
-
* `value` as pretty JSON a terminal shows as written: every character in
|
|
43
|
-
* TERMINAL_UNSAFE_RANGES becomes its `\uXXXX` escape. Such characters can occur
|
|
44
|
-
* only inside JSON strings, where the escape means the same character, so the
|
|
45
|
-
* text still parses back to `value`.
|
|
46
|
-
*/
|
|
47
|
-
function terminalSafeJson(value) {
|
|
48
|
-
let text = '';
|
|
49
|
-
for (const ch of JSON.stringify(value, null, 2)) {
|
|
50
|
-
const code = ch.codePointAt(0) ?? 0;
|
|
51
|
-
const unsafe = TERMINAL_UNSAFE_RANGES.some(([low, high]) => code >= low && code <= high);
|
|
52
|
-
text += unsafe ? `\\u${code.toString(16).padStart(4, '0')}` : ch;
|
|
53
|
-
}
|
|
54
|
-
return text;
|
|
55
|
-
}
|
|
56
|
-
/** `1 decision`, `2 decisions`. */
|
|
57
|
-
function counted(count, noun) {
|
|
58
|
-
return `${count} ${noun}${count === 1 ? '' : 's'}`;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* The learning store (D-LEARNING-STORE-SEAM), or null after reporting why it
|
|
62
|
-
* cannot be used and setting exit code 1.
|
|
63
|
-
*/
|
|
64
|
-
function requireStore() {
|
|
65
|
-
const loaded = loadLearningStore();
|
|
66
|
-
if (loaded.ok)
|
|
67
|
-
return loaded.value;
|
|
68
|
-
p.log.error(`Learning: ${formatLearningStoreUnavailable(loaded.error)}`);
|
|
69
|
-
process.exitCode = 1;
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
/** What a writer says when the store refused; `undone` completes "Nothing was …". */
|
|
73
|
-
function storeRefusal(error, undone) {
|
|
74
|
-
switch (error.kind) {
|
|
75
|
-
case 'no-learning-dir': return NO_LEARNING_DATA;
|
|
76
|
-
case 'busy': return `The learning store is busy: another run holds its lock. Nothing was ${undone}; try again in a moment.`;
|
|
77
|
-
default: return error.message;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Ask `message` on a terminal and say whether to go on, logging `cancelled` when the
|
|
82
|
-
* answer is no. With no terminal there is no one to ask: a script that passed the
|
|
83
|
-
* flag has decided.
|
|
84
|
-
*/
|
|
85
|
-
async function confirmOnTerminal(message, cancelled) {
|
|
86
|
-
if (!process.stdin.isTTY)
|
|
87
|
-
return true;
|
|
88
|
-
const confirmed = await p.confirm({ message, initialValue: false });
|
|
89
|
-
if (p.isCancel(confirmed) || !confirmed) {
|
|
90
|
-
p.log.info(cancelled);
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
/** True when `dir` is a directory; false when nothing, or something else, is there. */
|
|
96
|
-
async function isDirectory(dir) {
|
|
97
|
-
try {
|
|
98
|
-
return (await fs.stat(dir)).isDirectory();
|
|
99
|
-
}
|
|
100
|
-
catch (err) {
|
|
101
|
-
const code = err.code;
|
|
102
|
-
if (code === 'ENOENT' || code === 'ENOTDIR')
|
|
103
|
-
return false;
|
|
104
|
-
throw err;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
// Sub-command handlers
|
|
109
|
-
// ---------------------------------------------------------------------------
|
|
110
|
-
function printUsage() {
|
|
111
|
-
p.intro(color.bgCyan(color.black(' Learning ')));
|
|
112
|
-
p.note(`${color.cyan('devflow learning --enable')} Enable learning in every project (a repository can opt out)\n` +
|
|
113
|
-
`${color.cyan('devflow learning --disable')} Disable learning in every project (drains this project's queue)\n` +
|
|
114
|
-
`${color.cyan('devflow learning --status')} Show learning status and entry counts\n` +
|
|
115
|
-
`${color.cyan('devflow learning --list')} List entries, inactive entries and observations\n` +
|
|
116
|
-
`${color.cyan('devflow learning --show <id>')} Print one entry or observation as JSON\n` +
|
|
117
|
-
`${color.cyan('devflow learning --restore <id>')} Make an inactive entry active again\n` +
|
|
118
|
-
`${color.cyan('devflow learning --configure')} Configuration wizard\n` +
|
|
119
|
-
`${color.cyan('devflow learning --clear')} Drop the observations no entry uses, and drain the queue\n` +
|
|
120
|
-
`${color.cyan('devflow learning --reset')} Remove all learning state files`, 'Usage');
|
|
121
|
-
p.outro(color.dim('Detects architectural decisions and known pitfalls from your sessions'));
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Resolve the ledger root for a state-mutating subcommand, warning and
|
|
125
|
-
* returning null if the caller isn't inside a git project. `actionSuffix`
|
|
126
|
-
* completes "Could not resolve git root — {actionSuffix}".
|
|
127
|
-
*
|
|
128
|
-
* D-LEDGER-MAIN-WORKTREE: every subcommand here resolves the ledger with
|
|
129
|
-
* getLedgerRoot — the hooks' DF_LEDGER_ROOT rule — so in a linked worktree it
|
|
130
|
-
* reads, clears and drains the main checkout's ledger the hooks write.
|
|
131
|
-
*/
|
|
132
|
-
async function requireLedgerRoot(actionSuffix) {
|
|
133
|
-
const ledgerRoot = await getLedgerRoot();
|
|
134
|
-
if (!ledgerRoot) {
|
|
135
|
-
p.log.warn(`Could not resolve git root — ${actionSuffix}`);
|
|
136
|
-
}
|
|
137
|
-
return ledgerRoot;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* The --status entry lines: active entries by type, inactive ones by status (in
|
|
141
|
-
* the store's order, each status present), the active entries still in the v1
|
|
142
|
-
* format — the migration still to do — and the observations.
|
|
143
|
-
*/
|
|
144
|
-
function entryCountLines(listing, logRowCount, inactiveStatuses) {
|
|
145
|
-
const active = listing.active;
|
|
146
|
-
const decisions = active.filter(entry => entry.type === 'decision').length;
|
|
147
|
-
const pitfalls = active.filter(entry => entry.type === 'pitfall').length;
|
|
148
|
-
const byStatus = inactiveStatuses
|
|
149
|
-
.map(status => ({ status, count: listing.inactive.filter(entry => entry.status === status).length }))
|
|
150
|
-
.filter(({ count }) => count > 0)
|
|
151
|
-
.map(({ status, count }) => `${status} ${count}`);
|
|
152
|
-
const legacy = active.filter(entry => entry.schema === 1).length;
|
|
153
|
-
return [
|
|
154
|
-
`Entries: ${active.length} active (${counted(decisions, 'decision')}, ${counted(pitfalls, 'pitfall')}), `
|
|
155
|
-
+ `${listing.inactive.length} inactive${byStatus.length > 0 ? ` (${byStatus.join(', ')})` : ''}`,
|
|
156
|
-
`Legacy v1 entries: ${legacy} of ${active.length} active`,
|
|
157
|
-
`Observations: ${logRowCount} in the log, ${listing.observations.length} not yet promoted`,
|
|
158
|
-
];
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* `--status`: the machine switch, then the counts the store reads. Reads only:
|
|
162
|
-
* malformed lines are counted, never quarantined, and no scope is checked.
|
|
163
|
-
*/
|
|
164
|
-
async function handleStatus() {
|
|
165
|
-
// D-FEATURES-NARROW-ONLY: the machine switch is the manifest's and reads the
|
|
166
|
-
// same from every directory; a repository layer can only narrow it, and adds a
|
|
167
|
-
// line only when it does. The entry counts are per-project.
|
|
168
|
-
const enabled = await readMachineFeature(getDevFlowDirectory(), 'learning');
|
|
169
|
-
const settingsModule = loadSettingsModule();
|
|
170
|
-
const narrowed = enabled ? narrowedSwitchLabel(settingsModule, { dir: process.cwd() }, 'learning') : null;
|
|
171
|
-
const stateLine = `Learning: ${enabled ? 'enabled' : 'disabled'}`
|
|
172
|
-
+ (narrowed === null ? '' : `\nEffective here: ${narrowed}`);
|
|
173
|
-
const trackedWarning = personalConfigTrackedWarning(settingsModule, { dir: process.cwd() });
|
|
174
|
-
if (trackedWarning !== null)
|
|
175
|
-
p.log.warn(trackedWarning);
|
|
176
|
-
const ledgerRoot = await getLedgerRoot();
|
|
177
|
-
if (!ledgerRoot) {
|
|
178
|
-
p.log.info(`${stateLine}\nEntries: not in a git project`);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const loaded = loadLearningStore();
|
|
182
|
-
if (!loaded.ok) {
|
|
183
|
-
p.log.info(`${stateLine}\nEntries: unavailable (${formatLearningStoreUnavailable(loaded.error)})`);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const store = loaded.value;
|
|
187
|
-
const { ledgerRows, logRows, rejected } = store.readLearningState(ledgerRoot);
|
|
188
|
-
const listing = store.buildListing(ledgerRows, logRows, { rejected });
|
|
189
|
-
p.log.info([stateLine, ...entryCountLines(listing, logRows.length, store.INACTIVE_STATUSES)].join('\n'));
|
|
190
|
-
const { ledger, log } = listing.malformed;
|
|
191
|
-
if (ledger + log > 0) {
|
|
192
|
-
p.log.warn(`Malformed lines skipped: ${ledger} in the ledger, ${log} in the log. ` +
|
|
193
|
-
'The next op that rewrites a file moves its malformed lines to a .rejected.jsonl file beside it.');
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* `--list`: the store's listing — the same text json-helper's `list` op prints —
|
|
198
|
-
* on stdout. Read-only. Outside a git project it reads the current directory.
|
|
199
|
-
*/
|
|
200
|
-
async function handleList() {
|
|
201
|
-
const root = (await getLedgerRoot()) ?? process.cwd();
|
|
202
|
-
const store = requireStore();
|
|
203
|
-
if (!store)
|
|
204
|
-
return;
|
|
205
|
-
const listed = store.readListing(root);
|
|
206
|
-
if (!listed.ok) {
|
|
207
|
-
if (listed.error.kind === 'no-learning-dir') {
|
|
208
|
-
p.log.info(NO_LEARNING_DATA);
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
p.log.error(listed.error.message);
|
|
212
|
-
process.exitCode = 1;
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
process.stdout.write(`${store.formatListing(listed.value)}\n`);
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* `--show <id>`: one entry, by its anchor or its observation id, as the JSON
|
|
219
|
-
* json-helper's `show` op prints, made terminal-safe. Read-only. Outside a git
|
|
220
|
-
* project it reads the current directory.
|
|
221
|
-
*/
|
|
222
|
-
async function handleShow(key) {
|
|
223
|
-
const root = (await getLedgerRoot()) ?? process.cwd();
|
|
224
|
-
const store = requireStore();
|
|
225
|
-
if (!store)
|
|
226
|
-
return;
|
|
227
|
-
const shown = store.showByKey(root, key);
|
|
228
|
-
if (!shown.ok) {
|
|
229
|
-
p.log.error(shown.error.kind === 'no-learning-dir' ? NO_LEARNING_DATA : shown.error.message);
|
|
230
|
-
process.exitCode = 1;
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
process.stdout.write(`${terminalSafeJson(shown.value)}\n`);
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* `--restore <id>`: make an inactive entry active again through the store's
|
|
237
|
-
* restoreAnchor, which clears its notes and its verification so maintenance
|
|
238
|
-
* reviews it again, and re-renders the files. A refusal writes nothing.
|
|
239
|
-
*/
|
|
240
|
-
async function handleRestore(anchor) {
|
|
241
|
-
const ledgerRoot = await requireLedgerRoot('restore not performed');
|
|
242
|
-
if (!ledgerRoot) {
|
|
243
|
-
process.exitCode = 1;
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
const store = requireStore();
|
|
247
|
-
if (!store)
|
|
248
|
-
return;
|
|
249
|
-
if (!store.ANCHOR_ID_RE.test(anchor)) {
|
|
250
|
-
p.log.error(`--restore takes an entry id (ADR-NNN or PF-NNN), not ${JSON.stringify(anchor)}`);
|
|
251
|
-
process.exitCode = 1;
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
const restored = store.restoreAnchor(ledgerRoot, anchor, { timeoutMs: LOCK_WAIT_MS });
|
|
255
|
-
if (!restored.ok) {
|
|
256
|
-
p.log.error(storeRefusal(restored.error, 'restored'));
|
|
257
|
-
process.exitCode = 1;
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
p.log.success(`Restored ${restored.value.anchor_id} (${restored.value.status}); it is due for review again.`);
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* The scope choices of `devflow learning --configure`.
|
|
264
|
-
*
|
|
265
|
-
* D-LEARNING-MODEL-PRECEDENCE: the session-start hook takes the first layer that
|
|
266
|
-
* supplies a model — the project file, then a `devflow agents` Curate mapping,
|
|
267
|
-
* then the global file. A global file therefore has no effect for a user who has
|
|
268
|
-
* such a mapping, and its hint says so.
|
|
269
|
-
*/
|
|
270
|
-
export const CONFIGURE_SCOPE_OPTIONS = [
|
|
271
|
-
{ value: 'project', label: 'Project', hint: 'This project only (.devflow/learning/learning.json)' },
|
|
272
|
-
{
|
|
273
|
-
value: 'global',
|
|
274
|
-
label: 'Global',
|
|
275
|
-
hint: 'All projects (~/.devflow/learning.json); a devflow agents Curate mapping takes precedence over it',
|
|
276
|
-
},
|
|
277
|
-
];
|
|
278
|
-
/** The models `devflow learning --configure` offers, each with what it is good for. */
|
|
279
|
-
const CURATE_MODEL_CHOICES = [
|
|
280
|
-
{ value: 'opus', label: 'Opus', note: 'highest quality for detection + curation judgment' },
|
|
281
|
-
{ value: 'sonnet', label: 'Sonnet', note: 'good balance of quality and speed' },
|
|
282
|
-
{ value: 'haiku', label: 'Haiku', note: 'fastest, lowest cost' },
|
|
283
|
-
];
|
|
284
|
-
/**
|
|
285
|
-
* D-LEARNING-SHIPPED-ROW: the model offered as "Recommended" is the one the Curate
|
|
286
|
-
* agent ships with, the `model:` line of its frontmatter. It is read from there and
|
|
287
|
-
* written nowhere else, so a change of the shipped tier moves the recommendation with
|
|
288
|
-
* it and this list never has to be edited. A shipped model outside the three offered
|
|
289
|
-
* (or none) recommends nothing.
|
|
290
|
-
*/
|
|
291
|
-
export function curateModelOptions(shippedModel) {
|
|
292
|
-
return CURATE_MODEL_CHOICES.map(choice => ({
|
|
293
|
-
value: choice.value,
|
|
294
|
-
label: choice.label,
|
|
295
|
-
hint: choice.value === shippedModel
|
|
296
|
-
? `Recommended — ${choice.note}`
|
|
297
|
-
: choice.note.charAt(0).toUpperCase() + choice.note.slice(1),
|
|
298
|
-
}));
|
|
299
|
-
}
|
|
300
|
-
async function handleConfigure() {
|
|
301
|
-
p.intro(color.bgCyan(color.black(' Learning Configuration ')));
|
|
302
|
-
const shippedModel = (await loadShippedAgentDefaults()).curate?.model;
|
|
303
|
-
const model = await p.select({
|
|
304
|
-
message: 'Model for decision detection',
|
|
305
|
-
options: curateModelOptions(shippedModel),
|
|
306
|
-
});
|
|
307
|
-
if (p.isCancel(model)) {
|
|
308
|
-
p.cancel('Configuration cancelled.');
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
const debugMode = await p.confirm({
|
|
312
|
-
message: 'Enable debug logging? (logs session content excerpts to ~/.devflow/logs/)',
|
|
313
|
-
initialValue: false,
|
|
314
|
-
});
|
|
315
|
-
if (p.isCancel(debugMode)) {
|
|
316
|
-
p.cancel('Configuration cancelled.');
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
const scope = await p.select({
|
|
320
|
-
message: 'Configuration scope',
|
|
321
|
-
options: [...CONFIGURE_SCOPE_OPTIONS],
|
|
322
|
-
});
|
|
323
|
-
if (p.isCancel(scope)) {
|
|
324
|
-
p.cancel('Configuration cancelled.');
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
const config = {
|
|
328
|
-
model,
|
|
329
|
-
debug: debugMode,
|
|
330
|
-
};
|
|
331
|
-
const configJson = JSON.stringify(config, null, 2) + '\n';
|
|
332
|
-
if (scope === 'global') {
|
|
333
|
-
const globalDir = getDevFlowDirectory();
|
|
334
|
-
await fs.mkdir(globalDir, { recursive: true });
|
|
335
|
-
await fs.writeFile(path.join(globalDir, 'learning.json'), configJson, 'utf-8');
|
|
336
|
-
p.log.success(`Global config written to ${color.dim(path.join(globalDir, 'learning.json'))}`);
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
// D-LEDGER-MAIN-WORKTREE: session-start-context reads the project tuning config
|
|
340
|
-
// from the ledger ($LEDGER_ROOT/.devflow/learning/), so write it there — the
|
|
341
|
-
// main checkout in a linked worktree; the current directory outside git.
|
|
342
|
-
const projectRoot = (await getLedgerRoot()) ?? process.cwd();
|
|
343
|
-
const learningDir = getLearningDir(projectRoot);
|
|
344
|
-
const projectConfigPath = getCurateTuningConfigPath(projectRoot);
|
|
345
|
-
// D-CLI-NO-SYMLINK (core/linked-path.ts): nothing is written through a .devflow,
|
|
346
|
-
// a learning folder or a learning.json that is a symbolic link.
|
|
347
|
-
const linked = await firstSymbolicLink([path.dirname(learningDir), learningDir, projectConfigPath]);
|
|
348
|
-
if (linked !== null) {
|
|
349
|
-
p.log.error(`Project config not written: ${linked} is a symbolic link, and devflow writes nothing through one`);
|
|
350
|
-
process.exitCode = 1;
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
await fs.mkdir(learningDir, { recursive: true });
|
|
354
|
-
await fs.writeFile(projectConfigPath, configJson, 'utf-8');
|
|
355
|
-
p.log.success(`Project config written to ${color.dim(projectConfigPath)}`);
|
|
356
|
-
}
|
|
357
|
-
p.outro(color.green('Configuration saved.'));
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* `--reset`: remove all learning state — entries, observations, rendered files,
|
|
361
|
-
* the tuning config, and the queue with its claim — through the store's
|
|
362
|
-
* resetLearning, under the learning lock every learning writer takes
|
|
363
|
-
* (D-ONE-LEARNING-LOCK, D-RESET-UNDER-LOCK). A project with no learning directory
|
|
364
|
-
* has nothing to reset, and nothing is created there (D-NO-STRAY-TREE).
|
|
365
|
-
*/
|
|
366
|
-
async function handleReset() {
|
|
367
|
-
const ledgerRoot = await requireLedgerRoot('reset not performed');
|
|
368
|
-
if (!ledgerRoot)
|
|
369
|
-
return;
|
|
370
|
-
const store = requireStore();
|
|
371
|
-
if (!store)
|
|
372
|
-
return;
|
|
373
|
-
if (!(await isDirectory(getLearningDir(ledgerRoot)))) {
|
|
374
|
-
p.log.info('No learning data to reset.');
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
const proceed = await confirmOnTerminal('Remove all learning state files? This cannot be undone.', 'Reset cancelled.');
|
|
378
|
-
if (!proceed)
|
|
379
|
-
return;
|
|
380
|
-
const reset = store.resetLearning(ledgerRoot, { timeoutMs: LOCK_WAIT_MS });
|
|
381
|
-
if (!reset.ok) {
|
|
382
|
-
if (reset.error.kind === 'no-learning-dir') {
|
|
383
|
-
p.log.info('No learning data to reset.');
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
p.log.error(storeRefusal(reset.error, 'reset'));
|
|
387
|
-
process.exitCode = 1;
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
p.log.success('Reset complete — removed .devflow/learning/ state.');
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* `--clear`: drop the observations no entry uses (D-CLEAR-UNREFERENCED), then
|
|
394
|
-
* drain the learning queue. The queue is drained only once the clear succeeded:
|
|
395
|
-
* a busy lock or a refusal leaves both the log and the queue as they were, so a
|
|
396
|
-
* failed clear changes nothing.
|
|
397
|
-
*/
|
|
398
|
-
async function handleClear() {
|
|
399
|
-
const ledgerRoot = await requireLedgerRoot('clear not performed');
|
|
400
|
-
if (!ledgerRoot)
|
|
401
|
-
return;
|
|
402
|
-
const store = requireStore();
|
|
403
|
-
if (!store)
|
|
404
|
-
return;
|
|
405
|
-
if (!(await isDirectory(getLearningDir(ledgerRoot)))) {
|
|
406
|
-
p.log.info('No learning data to clear.');
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
const proceed = await confirmOnTerminal('Drop every observation no entry uses? Entries and the observations they use are kept. This cannot be undone.', 'Clear cancelled.');
|
|
410
|
-
if (!proceed)
|
|
411
|
-
return;
|
|
412
|
-
const cleared = store.clearUnreferenced(ledgerRoot, { timeoutMs: LOCK_WAIT_MS });
|
|
413
|
-
if (!cleared.ok) {
|
|
414
|
-
if (cleared.error.kind === 'no-learning-dir') {
|
|
415
|
-
p.log.info('No learning data to clear.');
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
p.log.error(storeRefusal(cleared.error, 'cleared'));
|
|
419
|
-
process.exitCode = 1;
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
// A mid-run Curate agent whose claimed batch vanishes stops without further
|
|
423
|
-
// writes — the desired outcome of clearing.
|
|
424
|
-
const drain = await drainLearningQueue(ledgerRoot);
|
|
425
|
-
p.log.success(`Cleared ${counted(cleared.value.cleared, 'observation')} no entry uses and kept ${cleared.value.kept} ` +
|
|
426
|
-
`that entries use${drain.drained ? '; drained the learning queue.' : '.'}`);
|
|
427
|
-
if (!drain.drained)
|
|
428
|
-
p.log.warn(formatRefusedDrain('learning', drain.linkedFolder));
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Make the installed prompts match the switch just written
|
|
432
|
-
* (D-LEARNING-VARIANT-INSTALL): the learning-on or learning-off commands and
|
|
433
|
-
* agents, and the apply-decisions skill. The same convergence `devflow init` runs,
|
|
434
|
-
* followed by reapplying the saved agent model mapping so `devflow agents`
|
|
435
|
-
* overrides survive.
|
|
436
|
-
*
|
|
437
|
-
* Warn-not-fail: the switch itself is already recorded, and either mismatch is
|
|
438
|
-
* safe (an on variant is still gated at run time, an off variant loads no
|
|
439
|
-
* decisions), so a problem here never changes the exit code.
|
|
440
|
-
*/
|
|
441
|
-
async function convergeInstalledVariants(enabled) {
|
|
442
|
-
try {
|
|
443
|
-
const outcome = await applyLearningToggle({
|
|
444
|
-
claudeDir: getClaudeDirectory(),
|
|
445
|
-
devflowDir: getDevFlowDirectory(),
|
|
446
|
-
learning: enabled,
|
|
447
|
-
runningVersion: await readRunningVersion(),
|
|
448
|
-
warn: (msg) => p.log.warn(msg),
|
|
449
|
-
});
|
|
450
|
-
if (outcome.kind === 'skipped') {
|
|
451
|
-
p.log.warn(outcome.message);
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
const changed = describeLearningConverge(outcome.result, enabled);
|
|
455
|
-
if (changed !== null)
|
|
456
|
-
p.log.info(color.dim(`Installed prompts: ${changed}`));
|
|
457
|
-
}
|
|
458
|
-
catch (err) {
|
|
459
|
-
p.log.warn(`Could not update the installed prompts for the new learning setting — run ${color.cyan('devflow init')}: ${String(err)}`);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* `--enable` / `--disable`: the machine-wide switch (D-FEATURES-NARROW-ONLY),
|
|
464
|
-
* converged exactly as `devflow init --learning / --no-learning` converges it —
|
|
465
|
-
* the manifest value, the installed prompt variants and skill
|
|
466
|
-
* (D-LEARNING-VARIANT-INSTALL), and on disable a drained queue in the current
|
|
467
|
-
* project. Never requires a git root: the switch is not a per-project setting.
|
|
468
|
-
*/
|
|
469
|
-
async function handleToggle(enabled) {
|
|
470
|
-
const recorded = await writeMachineFeature(getDevFlowDirectory(), 'learning', enabled);
|
|
471
|
-
if (!recorded.ok) {
|
|
472
|
-
p.log.error(`Devflow is not installed on this machine — run ${color.cyan('devflow init')} first`);
|
|
473
|
-
process.exitCode = 1;
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
await convergeInstalledVariants(enabled);
|
|
477
|
-
if (enabled) {
|
|
478
|
-
p.log.success('Learning enabled in every project (a repository can opt out)');
|
|
479
|
-
p.log.info(color.dim('Architectural decisions and pitfalls will be detected from your sessions'));
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
// Drain the current project's learning (decisions-detection) queue so stale
|
|
483
|
-
// turns don't process on re-enable. A mid-run Curate agent whose claimed
|
|
484
|
-
// batch vanishes aborts without changes — the desired outcome of disabling.
|
|
485
|
-
const ledgerRoot = await getLedgerRoot();
|
|
486
|
-
if (ledgerRoot) {
|
|
487
|
-
const drain = await drainLearningQueue(ledgerRoot);
|
|
488
|
-
if (!drain.drained)
|
|
489
|
-
p.log.warn(formatRefusedDrain('learning', drain.linkedFolder));
|
|
490
|
-
}
|
|
491
|
-
p.log.success('Learning disabled in every project');
|
|
492
|
-
}
|
|
493
|
-
export const learningCommand = new Command('learning')
|
|
494
|
-
.description('Enable or disable learning (decision/pitfall detection) in every project')
|
|
495
|
-
.option('--enable', 'Enable learning in every project (a repository can opt out)')
|
|
496
|
-
.option('--disable', 'Disable learning in every project')
|
|
497
|
-
.option('--status', 'Show learning status and entry counts')
|
|
498
|
-
.option('--list', 'List entries, inactive entries with their notes, and observations')
|
|
499
|
-
.option('--show <id>', 'Print one entry (ADR-NNN or PF-NNN) or observation (obs_...) as JSON')
|
|
500
|
-
.option('--restore <id>', 'Make an inactive entry active again')
|
|
501
|
-
.option('--configure', 'Interactive configuration wizard for learning.json')
|
|
502
|
-
.option('--clear', 'Drop the observations no entry uses, and drain the learning queue')
|
|
503
|
-
.option('--reset', 'Remove all learning state files and artifacts')
|
|
504
|
-
.action(async (options) => {
|
|
505
|
-
const knownFlags = [
|
|
506
|
-
'enable', 'disable', 'status', 'list', 'show', 'restore', 'configure',
|
|
507
|
-
'clear', 'reset',
|
|
508
|
-
];
|
|
509
|
-
const hasFlag = knownFlags.some((f) => options[f] !== undefined);
|
|
510
|
-
if (!hasFlag) {
|
|
511
|
-
printUsage();
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
// Thin router — the read-only commands first (status, list, show), then
|
|
515
|
-
// configure, reset, clear, restore, enable, disable. Each handler owns its own
|
|
516
|
-
// path resolution and I/O.
|
|
517
|
-
if (options.status) {
|
|
518
|
-
await handleStatus();
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
if (options.list) {
|
|
522
|
-
await handleList();
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
if (options.show !== undefined) {
|
|
526
|
-
await handleShow(options.show);
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
if (options.configure) {
|
|
530
|
-
await handleConfigure();
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
if (options.reset) {
|
|
534
|
-
await handleReset();
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
if (options.clear) {
|
|
538
|
-
await handleClear();
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
if (options.restore !== undefined) {
|
|
542
|
-
await handleRestore(options.restore);
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
if (options.enable) {
|
|
546
|
-
await handleToggle(true);
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
if (options.disable) {
|
|
550
|
-
await handleToggle(false);
|
|
551
|
-
return;
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
//# sourceMappingURL=learning.js.map
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file learning-queue-cleanup.ts
|
|
3
|
-
*
|
|
4
|
-
* `drainLearningQueue`, the learning queue drain shared by `devflow learning
|
|
5
|
-
* --clear` and `--disable` (src/cli/commands/learning.ts) and by the drain
|
|
6
|
-
* `devflow init` runs when learning is switched off (src/cli/commands/init.ts).
|
|
7
|
-
*/
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import { getLearningClaimOwnerPath, getLearningDir, getLearningPendingTurnsPath, getLearningPendingTurnsProcessingPath, } from './project-paths.js';
|
|
10
|
-
import { drainQueueFiles } from './queue-drain.js';
|
|
11
|
-
/**
|
|
12
|
-
* Drain the learning (decisions-detection) pending-turns queue, its claimed
|
|
13
|
-
* batch and the claim's owner file so stale turns don't process later — used by
|
|
14
|
-
* both `--clear` and `--disable`. A mid-run Curate agent whose claimed batch
|
|
15
|
-
* vanishes aborts without changes, which is the desired outcome in both cases.
|
|
16
|
-
* Refused, deleting nothing, when `.devflow` or `.devflow/learning` under `gitRoot`
|
|
17
|
-
* is a symbolic link (D-CLI-NO-SYMLINK). ENOENT-tolerant; other errors propagate.
|
|
18
|
-
*/
|
|
19
|
-
export async function drainLearningQueue(gitRoot) {
|
|
20
|
-
const learningDir = getLearningDir(gitRoot);
|
|
21
|
-
return drainQueueFiles([path.dirname(learningDir), learningDir], [
|
|
22
|
-
getLearningPendingTurnsPath(gitRoot),
|
|
23
|
-
getLearningPendingTurnsProcessingPath(gitRoot),
|
|
24
|
-
getLearningClaimOwnerPath(gitRoot),
|
|
25
|
-
]);
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=learning-queue-cleanup.js.map
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The CLI's view of the learning store — a typed seam onto the package's own
|
|
3
|
-
* `hooks/lib/learning-store.cjs`, never a second implementation of it.
|
|
4
|
-
*
|
|
5
|
-
* D-LEARNING-STORE-SEAM: `devflow learning` reads and writes the learning 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 LEARNING_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 learning 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 LEARNING_STORE_SCRIPT_NAME = join('hooks', 'lib', 'learning-store.cjs');
|
|
23
|
-
/**
|
|
24
|
-
* Every key of LearningStoreModule and the runtime kind the loader requires of
|
|
25
|
-
* it. `satisfies` makes the compiler reject an interface key missing here.
|
|
26
|
-
*/
|
|
27
|
-
export const LEARNING_STORE_SURFACE = Object.freeze({
|
|
28
|
-
INACTIVE_STATUSES: 'string-array',
|
|
29
|
-
ANCHOR_ID_RE: 'regexp',
|
|
30
|
-
readLearningState: 'function',
|
|
31
|
-
buildListing: 'function',
|
|
32
|
-
readListing: 'function',
|
|
33
|
-
formatListing: 'function',
|
|
34
|
-
showByKey: 'function',
|
|
35
|
-
restoreAnchor: 'function',
|
|
36
|
-
clearUnreferenced: 'function',
|
|
37
|
-
resetLearning: 'function',
|
|
38
|
-
});
|
|
39
|
-
/**
|
|
40
|
-
* Load the learning store from `dir` (default: the package's own scripts
|
|
41
|
-
* directory) and shape-check its surface. Never throws: a missing file is
|
|
42
|
-
* `not-found`; a module that throws on load or lacks a surface key is `unusable`.
|
|
43
|
-
*/
|
|
44
|
-
export function loadLearningStore(dir = scriptsDir()) {
|
|
45
|
-
return loadScript(join(dir, LEARNING_STORE_SCRIPT_NAME), LEARNING_STORE_SURFACE);
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Why the store cannot be used, and the remedy: a package reinstall, since the
|
|
49
|
-
* CLI loads the package's own copy, which `devflow init` does not restore.
|
|
50
|
-
*/
|
|
51
|
-
export function formatLearningStoreUnavailable(error) {
|
|
52
|
-
switch (error.kind) {
|
|
53
|
-
case 'not-found': return 'learning store not found — reinstall devflow-kit';
|
|
54
|
-
case 'unusable': return 'learning store failed to load — reinstall devflow-kit';
|
|
55
|
-
default: {
|
|
56
|
-
const exhaustive = error;
|
|
57
|
-
return exhaustive;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
//# sourceMappingURL=learning-store.js.map
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|