@kienntpixon/dieu-phoi 0.5.3 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +95 -0
- package/README.md +20 -5
- package/agents/dp-exec.md +2 -0
- package/agents/dp-helper.md +1 -0
- package/agents/dp-scout.md +2 -0
- package/agents/dp-verify.md +3 -0
- package/bin/dieu-phoi.cjs +29 -4
- package/hook.cjs +675 -8
- package/lib/args.cjs +19 -2
- package/lib/cbm.cjs +490 -0
- package/lib/commands.cjs +149 -26
- package/lib/knowledge.cjs +1002 -0
- package/lib/profile.cjs +204 -0
- package/lib/prune.cjs +57 -20
- package/package.json +1 -1
package/lib/commands.cjs
CHANGED
|
@@ -8,7 +8,8 @@ const core = require('../hook.cjs');
|
|
|
8
8
|
const agg = require('./aggregate.cjs');
|
|
9
9
|
const chooser = require('./choose.cjs');
|
|
10
10
|
const setupLib = require('./setup.cjs');
|
|
11
|
-
const
|
|
11
|
+
const cbm = require('./cbm.cjs');
|
|
12
|
+
const { claudeDirOf, ctxOf, yesNo, repoOf } = require('./args.cjs');
|
|
12
13
|
|
|
13
14
|
const AGENT_OUTCOMES = ['pass', 'fail', 'timeout', 'aborted'];
|
|
14
15
|
const USER_OUTCOMES = ['accept', 'reject'];
|
|
@@ -48,6 +49,7 @@ function table(rows) {
|
|
|
48
49
|
// ------------------------------------------------------------------ setup / uninstall
|
|
49
50
|
|
|
50
51
|
function cmdSetup(opts) {
|
|
52
|
+
if (opts['with-codebase-memory']) return cmdSetupWithCbm(opts);
|
|
51
53
|
const res = setupLib.setup({
|
|
52
54
|
claudeDir: claudeDirOf(opts),
|
|
53
55
|
dept: typeof opts.dept === 'string' ? opts.dept : undefined,
|
|
@@ -75,6 +77,30 @@ function cmdSetup(opts) {
|
|
|
75
77
|
return { code: 0, out: lines.join('\n') };
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
/** `setup --with-codebase-memory [--force] [--cbm-dir D]`: normal setup, then install + register codebase-memory-mcp. */
|
|
81
|
+
async function cmdSetupWithCbm(opts) {
|
|
82
|
+
const base = cmdSetup(Object.assign({}, opts, { 'with-codebase-memory': false, json: false }));
|
|
83
|
+
let res;
|
|
84
|
+
let err = null;
|
|
85
|
+
try {
|
|
86
|
+
res = await cbm.install({ claudeDir: claudeDirOf(opts), force: !!opts.force, dir: typeof opts['cbm-dir'] === 'string' ? opts['cbm-dir'] : undefined, allowCustomSource: !!opts['allow-custom-source'] });
|
|
87
|
+
} catch (e) {
|
|
88
|
+
err = e.message;
|
|
89
|
+
}
|
|
90
|
+
if (opts.json) return { code: err ? 1 : 0, out: JSON.stringify({ setup: 'ok', codebase_memory: err ? { ok: false, error: err } : res }, null, 2) };
|
|
91
|
+
const lines = [base.out];
|
|
92
|
+
if (err) {
|
|
93
|
+
lines.push(`codebase-memory-mcp: LỖI — ${err} (setup dieu-phoi vẫn xong; không có gì bị ghi đè)`);
|
|
94
|
+
} else {
|
|
95
|
+
const act = { kept: 'đã có, giữ nguyên (--force để cài lại)', installed: 'đã cài', replaced: 'đã thay' }[res.action];
|
|
96
|
+
lines.push(`codebase-memory-mcp: ${act} v${res.version || '?'} tại ${res.binary.replace(/\\/g, '/')}${res.backup ? ` (bản cũ: ${path.basename(res.backup)})` : ''}`);
|
|
97
|
+
lines.push(`MCP (user scope): ${{ kept: 'đã đăng ký, giữ nguyên', 'kept-other': 'đã đăng ký (binary khác), giữ nguyên', added: 'đã đăng ký bằng `claude mcp add`', replaced: 'đã đăng ký lại', restored: 'đăng ký mới lỗi — đã khôi phục đăng ký cũ', manual: 'chưa đăng ký (xem ghi chú)' }[res.mcp]}`);
|
|
98
|
+
for (const n of res.notes) lines.push(` - ${n}`);
|
|
99
|
+
if (res.action !== 'kept' || res.mcp === 'added' || res.mcp === 'replaced') lines.push('Khởi động lại các phiên Claude Code để nạp MCP; kiểm bằng `dieu-phoi doctor`.');
|
|
100
|
+
}
|
|
101
|
+
return { code: err ? 1 : 0, out: lines.join('\n') };
|
|
102
|
+
}
|
|
103
|
+
|
|
78
104
|
function cmdUninstall(opts) {
|
|
79
105
|
const res = setupLib.uninstall({ claudeDir: claudeDirOf(opts), purge: !!opts.purge });
|
|
80
106
|
if (opts.json) return { code: 0, out: JSON.stringify(res, null, 2) };
|
|
@@ -116,6 +142,14 @@ async function cmdDoctor(opts) {
|
|
|
116
142
|
const sd = ctx.config.skill_dir;
|
|
117
143
|
add(!!sd && fs.existsSync(path.join(sd, 'profile', 'profile.json')), 'profile',
|
|
118
144
|
sd ? path.join(sd, 'profile', 'profile.json') : 'không có skill_dir — choose dùng priors mặc định', 'CẢNH BÁO');
|
|
145
|
+
// codebase-memory-mcp (optional): started here so its `daemon status` (~2.7 s, only with --cbm/--net; else a 10-minute
|
|
146
|
+
// cache) overlaps the hook-run check below
|
|
147
|
+
let cbmP = Promise.resolve([]);
|
|
148
|
+
try {
|
|
149
|
+
cbmP = cbm.doctorChecks({ claudeDir, repo: repoOf(opts), net: !!opts.net, deep: !!opts.cbm, cacheFile: path.join(ctx.paths.data, 'cbm-daemon.json') }).catch((e) => [{ ok: true, level: 'OK', name: 'codebase-memory', detail: `bỏ qua kiểm (${e.message})` }]);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
cbmP = Promise.resolve([{ ok: true, level: 'OK', name: 'codebase-memory', detail: `bỏ qua kiểm (${e.message})` }]);
|
|
152
|
+
}
|
|
119
153
|
if (reg.commands.length) {
|
|
120
154
|
const cmd = reg.commands[0];
|
|
121
155
|
const t0 = Date.now();
|
|
@@ -168,13 +202,17 @@ async function cmdDoctor(opts) {
|
|
|
168
202
|
const cached = opts.net ? null : core.sinkStatusCached(ctx);
|
|
169
203
|
if (cached && (cached.state === 'ok' || cached.state === 'fail')) {
|
|
170
204
|
add(cached.state === 'ok', 'sink', `${ctx.config.sink_url} → ${cached.state === 'ok' ? 'ok' : `lỗi: ${cached.detail}`} (bộ nhớ đệm: ${cached.source} lúc ${cached.ts}; \`doctor --net\` để kiểm trực tiếp)`);
|
|
205
|
+
} else if (!opts.net && recentSinkTimeout(ctx)) {
|
|
206
|
+
const t = recentSinkTimeout(ctx);
|
|
207
|
+
add(false, 'sink', `${ctx.config.sink_url} → chậm/không xác định: lần kiểm lúc ${t.ts} không phản hồi kịp (chưa coi là lỗi); \`doctor --net\` để thử lại`, 'CẢNH BÁO');
|
|
171
208
|
} else {
|
|
172
209
|
const live = await checkSinkLive(ctx);
|
|
173
|
-
add(live.ok, 'sink', `${ctx.config.sink_url} → ${live.detail}
|
|
210
|
+
add(live.ok, 'sink', `${ctx.config.sink_url} → ${live.detail}`, live.slow ? 'CẢNH BÁO' : undefined);
|
|
174
211
|
}
|
|
175
212
|
} else {
|
|
176
213
|
add(true, 'sink', 'chưa cấu hình (chỉ lưu cục bộ)');
|
|
177
214
|
}
|
|
215
|
+
for (const c of await cbmP) checks.push(c);
|
|
178
216
|
const failed = checks.some((c) => c.level === 'LỖI');
|
|
179
217
|
if (opts.json) return { code: failed ? 1 : 0, out: JSON.stringify({ ok: !failed, runs_dir: reg.runs_dir, checks }, null, 2) };
|
|
180
218
|
const out = checks.map((c) => `[${c.level}] ${c.name}: ${c.detail}`).join('\n');
|
|
@@ -208,26 +246,45 @@ function sweepStaleTmp(ctx) {
|
|
|
208
246
|
return { removed, left };
|
|
209
247
|
}
|
|
210
248
|
|
|
211
|
-
/**
|
|
249
|
+
/** The doctor's own timed-out sink check for the current sink, if less than 1 h old (else null): plain `doctor` does not retry a 20 s wait each time. */
|
|
250
|
+
function recentSinkTimeout(ctx) {
|
|
251
|
+
const chk = core.readJsonFile(ctx.paths.sinkCheck, null);
|
|
252
|
+
if (!chk || !chk.timeout || chk.url !== core.sinkUrlKey(ctx.config)) return null;
|
|
253
|
+
const t = Date.parse(chk.ts);
|
|
254
|
+
return t && Date.now() - t < 3600 * 1000 && t <= Date.now() + 60000 ? { ts: chk.ts } : null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Live sink check (view=stats); result cached in data/sink-check.json for 24 h. Apps Script often needs 8-15 s, so the
|
|
259
|
+
* timeout is 20 s (DIEU_PHOI_SINK_CHECK_TIMEOUT_MS overrides, for tests). A timeout is not a hard failure: it is
|
|
260
|
+
* reported as slow/unknown (CẢNH BÁO) and cached with `timeout: true`, which the cache readers ignore.
|
|
261
|
+
*/
|
|
212
262
|
async function checkSinkLive(ctx) {
|
|
213
263
|
let ok = false;
|
|
264
|
+
let slow = false;
|
|
214
265
|
let detail;
|
|
266
|
+
const ms = Number(process.env.DIEU_PHOI_SINK_CHECK_TIMEOUT_MS) > 0 ? Number(process.env.DIEU_PHOI_SINK_CHECK_TIMEOUT_MS) : 20000;
|
|
215
267
|
try {
|
|
216
268
|
const u = new URL(ctx.config.sink_url);
|
|
217
269
|
u.searchParams.set('token', ctx.config.sink_token);
|
|
218
270
|
u.searchParams.set('view', 'stats');
|
|
219
|
-
const res = await fetch(u, { redirect: 'follow', signal: AbortSignal.timeout(
|
|
271
|
+
const res = await fetch(u, { redirect: 'follow', signal: AbortSignal.timeout(ms) });
|
|
220
272
|
const txt = await res.text();
|
|
221
273
|
ok = res.ok;
|
|
222
274
|
try { const b = JSON.parse(txt); if (b && b.ok === false) ok = false; } catch (_) { ok = false; }
|
|
223
275
|
detail = `HTTP ${res.status}`;
|
|
224
276
|
} catch (e) {
|
|
225
|
-
|
|
277
|
+
if (e && (e.name === 'TimeoutError' || e.name === 'AbortError')) {
|
|
278
|
+
slow = true;
|
|
279
|
+
detail = `chậm/không xác định: không phản hồi trong ${Math.round(ms / 1000)} s (Apps Script thường mất 8-15 s; chạy lại \`doctor --net\`, hoặc xem kết quả sync nền)`;
|
|
280
|
+
} else {
|
|
281
|
+
detail = e.message;
|
|
282
|
+
}
|
|
226
283
|
}
|
|
227
284
|
try {
|
|
228
|
-
core.writeFileAtomic(ctx.paths.sinkCheck, `${JSON.stringify({ ts: core.nowIso(), ok, detail, url: core.sinkUrlKey(ctx.config) })}\n`);
|
|
285
|
+
core.writeFileAtomic(ctx.paths.sinkCheck, `${JSON.stringify({ ts: core.nowIso(), ok, detail, url: core.sinkUrlKey(ctx.config), ...(slow ? { timeout: true } : {}) })}\n`);
|
|
229
286
|
} catch (_) { /* cache is best effort */ }
|
|
230
|
-
return { ok, detail };
|
|
287
|
+
return { ok, detail, slow };
|
|
231
288
|
}
|
|
232
289
|
|
|
233
290
|
// ------------------------------------------------------------------ start
|
|
@@ -239,13 +296,17 @@ async function checkSinkLive(ctx) {
|
|
|
239
296
|
function cmdStart(opts) {
|
|
240
297
|
const ctx = ctxOf(opts);
|
|
241
298
|
const reg = setupLib.registration(claudeDirOf(opts));
|
|
242
|
-
const info = core.startInfo(ctx);
|
|
299
|
+
const info = core.startInfo(ctx, repoOf(opts));
|
|
243
300
|
const h = info.health;
|
|
244
301
|
if (!fs.existsSync(ctx.paths.hook)) h.problems.unshift(`chưa cài hook (${ctx.paths.hook}) — chạy \`dieu-phoi setup --dept <phòng>\`, log không được ghi`);
|
|
245
302
|
else if (!(reg.PostToolUse && reg.SubagentStop && reg.SessionStart)) h.problems.unshift('hook chưa đăng ký đủ trong settings.json — chạy lại `dieu-phoi setup`, log/trạng thái không được ghi');
|
|
246
303
|
if (!reg.UserPromptSubmit) h.problems.push('thiếu hook UserPromptSubmit — chạy lại `dieu-phoi setup` để bỏ được lệnh này');
|
|
247
304
|
if (!reg.permissions || reg.legacy_permissions) h.problems.push(`thiếu quyền ghi cho ${reg.permission_entries.dir} — chạy lại \`dieu-phoi setup\` (agent nền có thể không ghi được report)`);
|
|
248
305
|
h.ok = !h.problems.length;
|
|
306
|
+
// core 0.6.0: project of --repo / cwd (file reads only; null outside a git repo)
|
|
307
|
+
let proj = null;
|
|
308
|
+
try { proj = core.resolveProject(repoOf(opts)); } catch (_) { /* no project */ }
|
|
309
|
+
info.project = proj ? { dir_name: proj.dir_name, key: proj.key, kind: proj.kind, dir: proj.dir, exists: proj.exists } : null;
|
|
249
310
|
if (opts.json) return { code: 0, out: JSON.stringify(info, null, 2) };
|
|
250
311
|
const out = [
|
|
251
312
|
`root_task_id: ${info.id}`,
|
|
@@ -254,6 +315,7 @@ function cmdStart(opts) {
|
|
|
254
315
|
`health: ${core.healthLine(h)}`,
|
|
255
316
|
`scout_brief: ${info.scout_brief || '(không thấy — dùng references/brief-template.md mục E)'}`,
|
|
256
317
|
...core.orchLessonLines(info),
|
|
318
|
+
...core.knowledgeLines(info),
|
|
257
319
|
];
|
|
258
320
|
return { code: 0, out: out.join('\n') };
|
|
259
321
|
}
|
|
@@ -415,11 +477,49 @@ function verdictRoot(opts, ctx, events) {
|
|
|
415
477
|
const judged = new Set(events.filter((e) => e.kind === 'verdict' && agg.normalizeVerdict(e).by === 'user').map((e) => e.task_id));
|
|
416
478
|
const gone = agg.discardedTaskIds(events);
|
|
417
479
|
const todo = steps.filter((x) => execTasks.has(x.task_id) && !judged.has(x.task_id) && !gone.has(x.task_id));
|
|
418
|
-
|
|
419
|
-
return todo.map((x) => ({
|
|
480
|
+
const list = todo.map((x) => ({
|
|
420
481
|
step: String(x.id),
|
|
421
482
|
ev: verdictUser(Object.assign({}, opts, { task: x.task_id, _: ['accept'], round: undefined, cause: undefined }), ctx, events),
|
|
422
483
|
}));
|
|
484
|
+
// No step waiting is not an error by itself: the root's own and untagged deltas still need merging (cmdVerdict decides).
|
|
485
|
+
if (!list.length) list.emptyMsg = `Không có bước nào của ${root} cần nghiệm thu (chưa giao exec, hoặc đã có verdict người dùng).`;
|
|
486
|
+
return list;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** Root task of a step: the approved plan that lists it, else the task itself (a loose task is its own run). */
|
|
490
|
+
function rootOfTask(ctx, taskId) {
|
|
491
|
+
let files = [];
|
|
492
|
+
try { files = fs.readdirSync(ctx.paths.plans).filter((f) => f.endsWith('.json')); } catch (_) { return taskId; }
|
|
493
|
+
for (const f of files) {
|
|
494
|
+
const p = core.readJsonFile(path.join(ctx.paths.plans, f), null);
|
|
495
|
+
if (p && typeof p.root_task_id === 'string' && Array.isArray(p.steps) && p.steps.some((x) => x && x.task_id === taskId)) return p.root_task_id;
|
|
496
|
+
}
|
|
497
|
+
return taskId;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Gate 3 (core 0.6.0): a user accept merges the run's knowledge deltas of the accepted tasks
|
|
502
|
+
* (knowledge-delta-<task_id>[-r<n>].md; on --root also the root's own and untagged ones). Never fails the verdict:
|
|
503
|
+
* conflicts/rejections are reported with the command to retry. --no-knowledge skips it.
|
|
504
|
+
*/
|
|
505
|
+
function knowledgeOnAccept(opts, ctx, root, tasks, untagged) {
|
|
506
|
+
if (opts['no-knowledge']) return null;
|
|
507
|
+
const kn = require('./knowledge.cjs');
|
|
508
|
+
const runDir = path.join(core.runsDir(), root);
|
|
509
|
+
const filter = { tasks: new Set(tasks), untagged };
|
|
510
|
+
try {
|
|
511
|
+
if (!fs.statSync(runDir).isDirectory() || !kn.deltaFiles(runDir, filter).length) return null;
|
|
512
|
+
} catch (_) { return null; }
|
|
513
|
+
const taskArg = tasks.join(',');
|
|
514
|
+
const retry = `dieu-phoi knowledge merge --run ${root} --task ${taskArg}${untagged ? ' --untagged' : ''}`;
|
|
515
|
+
let r;
|
|
516
|
+
try {
|
|
517
|
+
r = kn.cmdMerge({ _: [], run: runDir, task: taskArg, untagged, repo: opts.repo });
|
|
518
|
+
} catch (e) {
|
|
519
|
+
return { code: 1, out: `knowledge: gộp lỗi (${e.message}) — chạy lại: ${retry}` };
|
|
520
|
+
}
|
|
521
|
+
const tail = r.code ? `\n→ có mục chưa gộp: get lại mục đó, chép dòng base: mới vào delta (hoặc bỏ mục), rồi ${retry}` : '';
|
|
522
|
+
return { code: r.code, out: `knowledge (tự gộp khi accept):\n${r.out}${tail}` };
|
|
423
523
|
}
|
|
424
524
|
|
|
425
525
|
function cmdVerdict(opts) {
|
|
@@ -428,14 +528,33 @@ function cmdVerdict(opts) {
|
|
|
428
528
|
if (typeof opts.root === 'string' && typeof opts.task !== 'string') {
|
|
429
529
|
const list = verdictRoot(opts, ctx, events);
|
|
430
530
|
for (const x of list) core.appendEvent(ctx, x.ev);
|
|
431
|
-
|
|
432
|
-
|
|
531
|
+
const root = String(opts.root);
|
|
532
|
+
const kn = knowledgeOnAccept(opts, ctx, root, [root].concat(list.map((x) => x.ev.task_id)), true);
|
|
533
|
+
if (!list.length) {
|
|
534
|
+
// Every step was already judged (e.g. accepted one by one with --task): still merge the root + untagged deltas.
|
|
535
|
+
// Exit code follows the merge (0 merged or nothing new, 2 conflict or reject, 1 error); no delta at all keeps the old "nothing to do" error.
|
|
536
|
+
if (!kn) throw new Error(list.emptyMsg);
|
|
537
|
+
const msg = `Không còn bước nào của ${root} chờ nghiệm thu; chỉ gộp delta của root và delta không thẻ.`;
|
|
538
|
+
if (opts.json) { process.stderr.write(`${msg}\n${kn.out}\n`); return { code: kn.code, out: '[]' }; }
|
|
539
|
+
return { code: kn.code, out: `${msg}\n${kn.out}` };
|
|
540
|
+
}
|
|
541
|
+
if (opts.json) {
|
|
542
|
+
if (kn) process.stderr.write(`${kn.out}\n`);
|
|
543
|
+
return { code: 0, out: JSON.stringify(list.map((x) => x.ev)) };
|
|
544
|
+
}
|
|
545
|
+
const msg = `Đã ghi người dùng accept cho ${list.length} bước của ${opts.root}: ${list.map((x) => `${x.step}=${x.ev.task_id}`).join(', ')}.`;
|
|
546
|
+
return { code: 0, out: kn ? `${msg}\n${kn.out}` : msg };
|
|
433
547
|
}
|
|
434
548
|
const ev = typeof opts.task === 'string' ? verdictUser(opts, ctx, events) : verdictAgent(opts, ctx, events);
|
|
435
549
|
core.appendEvent(ctx, ev);
|
|
436
|
-
|
|
550
|
+
const kn = ev.by === 'user' && ev.outcome === 'accept' ? knowledgeOnAccept(opts, ctx, rootOfTask(ctx, ev.task_id), [ev.task_id], false) : null;
|
|
551
|
+
if (opts.json) {
|
|
552
|
+
if (kn) process.stderr.write(`${kn.out}\n`);
|
|
553
|
+
return { code: 0, out: JSON.stringify(ev) };
|
|
554
|
+
}
|
|
437
555
|
if (ev.by === 'user') {
|
|
438
|
-
|
|
556
|
+
const msg = `Đã ghi người dùng ${ev.outcome}${ev.cause ? ` (${ev.cause})` : ''} cho task ${ev.task_id}, vòng ${ev.round}.`;
|
|
557
|
+
return { code: 0, out: kn ? `${msg}\n${kn.out}` : msg };
|
|
439
558
|
}
|
|
440
559
|
return { code: 0, out: `Đã ghi ${ev.outcome} cho ${ev.agent_id}${ev.task_id ? ` (task ${ev.task_id})` : ''}.` };
|
|
441
560
|
}
|
|
@@ -553,17 +672,21 @@ function ctxWarnOnce(ctx, m) {
|
|
|
553
672
|
const file = path.join(ctx.paths.data, 'ctx-warn.json');
|
|
554
673
|
const level = ctxWarnLevel(m.ctx_growth, m.soft_cap);
|
|
555
674
|
try {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
675
|
+
// Core 0.5.4: read-modify-write under ctx-warn.lock — two sessions warning at once no longer overwrite each
|
|
676
|
+
// other's entry (a lost entry made the warning repeat). Lock timeout => go on unlocked (state is advisory).
|
|
677
|
+
return core.withFileLock(ctx.paths.ctxWarnLock, { timeoutMs: 2000, staleMs: 15000 }, () => {
|
|
678
|
+
const st = core.readJsonFile(file, {}) || {};
|
|
679
|
+
const cur = st[m.session_id];
|
|
680
|
+
const comp = typeof m.compactions === 'number' ? m.compactions : null;
|
|
681
|
+
const warn = !cur || cur.compactions !== comp || level > (Number(cur.level) || 0);
|
|
682
|
+
if (warn) {
|
|
683
|
+
const now = Date.now();
|
|
684
|
+
st[m.session_id] = { level, compactions: comp, t: now };
|
|
685
|
+
for (const k of Object.keys(st)) if (!(now - Number(st[k] && st[k].t) < CTX_WARN_KEEP_MS)) delete st[k];
|
|
686
|
+
core.writeFileAtomic(file, `${JSON.stringify(st)}\n`);
|
|
687
|
+
}
|
|
688
|
+
return warn;
|
|
689
|
+
}).value;
|
|
567
690
|
} catch (_) {
|
|
568
691
|
return true;
|
|
569
692
|
}
|
|
@@ -1071,6 +1194,6 @@ function cmdPlanTable(opts) {
|
|
|
1071
1194
|
}
|
|
1072
1195
|
|
|
1073
1196
|
module.exports = {
|
|
1074
|
-
cmdSetup, cmdUninstall, cmdDoctor, cmdStart, checkSinkLive, cmdHook, cmdSync, cmdVerdict, cmdSteer, cmdDiscard, ctxWarnOnce, ctxWarnLevel, sweepStaleTmp, planOverrideEvents, recordPlanSuggestions, cmdStats, cmdRetro, cmdPrune: (o) => require('./prune.cjs').cmdPrune(o), cmdChoose, cmdCtx, cmdState, measureOrch,
|
|
1197
|
+
skillDirOf, cmdSetup, cmdUninstall, cmdDoctor, cmdStart, checkSinkLive, cmdHook, cmdSync, cmdVerdict, cmdSteer, cmdDiscard, ctxWarnOnce, ctxWarnLevel, sweepStaleTmp, planOverrideEvents, recordPlanSuggestions, cmdStats, cmdRetro, cmdPrune: (o) => require('./prune.cjs').cmdPrune(o), cmdChoose, cmdCtx, cmdState, measureOrch,
|
|
1075
1198
|
cmdPlanTable, cmdNewId, requireTaskId, buildPlanTable, recordApprovedPlan, planTaskIdProblems, criticalPath, resolveAgentId, AGENT_OUTCOMES, USER_OUTCOMES, VERIFY,
|
|
1076
1199
|
};
|