@pugi/cli 0.1.0-beta.25 → 0.1.0-beta.27
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/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/compact/summarizer.js +12 -0
- package/dist/core/dispatch/cache-cleanup.js +197 -0
- package/dist/core/dispatch/cache-handoff.js +295 -0
- package/dist/core/memory-sync/queue.js +158 -0
- package/dist/core/memory-sync/queue.spec.js +105 -0
- package/dist/core/prd-check/parser.js +215 -0
- package/dist/core/prd-check/reporter.js +127 -0
- package/dist/core/prd-check/verifiers.js +223 -0
- package/dist/core/repl/session.js +82 -1
- package/dist/core/repl/slash-commands.js +19 -0
- package/dist/core/repl/store/session-store.js +31 -2
- package/dist/core/telemetry/emitter.js +229 -0
- package/dist/core/telemetry/queue.js +251 -0
- package/dist/runtime/cli.js +190 -0
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/memory.js +508 -0
- package/dist/runtime/commands/memory.spec.js +174 -0
- package/dist/runtime/commands/prd-check.js +235 -0
- package/dist/runtime/commands/resume.js +118 -0
- package/dist/runtime/commands/rewind.js +333 -0
- package/dist/runtime/commands/sessions.js +163 -0
- package/dist/runtime/version.js +1 -1
- package/dist/tools/agent-tool.js +23 -0
- package/dist/tui/repl-splash-mascot.js +7 -19
- package/package.json +3 -3
package/dist/runtime/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ import { buildRuntimeConfig, fetchPersonaRoster, loadRuntimeConfig, openPugiSess
|
|
|
22
22
|
import { PUGI_TAGLINE } from '@pugi/personas';
|
|
23
23
|
import { resolveRoster, renderRosterTable } from './commands/roster.js';
|
|
24
24
|
import { runDelegateCommand } from './commands/delegate.js';
|
|
25
|
+
import { runDispatchCommand } from './commands/dispatch.js';
|
|
25
26
|
import { clearApiKey, DEFAULT_API_URL, listStoredCredentials, maskApiKey, normalizeApiUrl, purgeAllCredentials, readCredentialsFile, resolveActiveCredential, storeApiKey, switchActiveAccount, } from '../core/credentials.js';
|
|
26
27
|
import { resolveAndValidateEnvLogin, } from '../core/auth/env-provider.js';
|
|
27
28
|
import { runDeployCommand } from '../commands/deploy.js';
|
|
@@ -35,12 +36,19 @@ import { isOnboarded } from '../core/onboarding/marker.js';
|
|
|
35
36
|
import { runPrivacyCommand } from './commands/privacy.js';
|
|
36
37
|
import { runReport } from './commands/report.js';
|
|
37
38
|
import { runDoctorCommand, defaultHome as defaultDoctorHome } from './commands/doctor.js';
|
|
39
|
+
import { parsePrdCheckArgs, runPrdCheckCommand, } from './commands/prd-check.js';
|
|
38
40
|
import { runStatusCommand, defaultStatusHome, } from './commands/status.js';
|
|
39
41
|
import { runStickersCommand } from './commands/stickers.js';
|
|
40
42
|
import { runRepoMapCommand } from './commands/repo-map.js';
|
|
41
43
|
import { runReleaseNotesCommand, defaultReleaseNotesHome, } from './commands/release-notes.js';
|
|
42
44
|
import { runUndoCommand } from './commands/undo.js';
|
|
43
45
|
import { runCompactCommand } from './commands/compact.js';
|
|
46
|
+
import { runRewindCommand } from './commands/rewind.js';
|
|
47
|
+
import { runSessionsCommand } from './commands/sessions.js';
|
|
48
|
+
// Day 4 ADR-0063: persona-memory operator surface (list / recall / write /
|
|
49
|
+
// forget / sync). The runner is shared by `pugi memory` top-level and the
|
|
50
|
+
// in-REPL `/memory` slash so the two surfaces stay single-sourced.
|
|
51
|
+
import { runMemoryCommand } from './commands/memory.js';
|
|
44
52
|
import { runBudgetCommand } from './commands/budget.js';
|
|
45
53
|
import { BARE_MODE_BANNER, isBareMode, setBareMode, } from '../core/bare-mode/index.js';
|
|
46
54
|
import { runCostCommand } from './commands/cost.js';
|
|
@@ -92,6 +100,11 @@ const handlers = {
|
|
|
92
100
|
config: dispatchConfig,
|
|
93
101
|
cost: dispatchCost,
|
|
94
102
|
delegate: dispatchDelegate,
|
|
103
|
+
// Leak L10 (2026-05-27): `pugi dispatch list-cache-refs` /
|
|
104
|
+
// `clear-cache-refs` operate on `.pugi/cache-refs/` — the persisted
|
|
105
|
+
// prompt-cache inheritance handles for fork-subagent dispatches. The
|
|
106
|
+
// handler module lives in commands/dispatch.ts so the table stays narrow.
|
|
107
|
+
dispatch: dispatchSubagentCacheRefs,
|
|
95
108
|
deploy: dispatchDeploy,
|
|
96
109
|
doctor,
|
|
97
110
|
explain: runEngineTask('explain'),
|
|
@@ -106,11 +119,20 @@ const handlers = {
|
|
|
106
119
|
logout,
|
|
107
120
|
lsp: dispatchLsp,
|
|
108
121
|
mcp: dispatchMcp,
|
|
122
|
+
// ADR-0063 Day 4: `pugi memory list|recall|write|forget|sync`. Routes
|
|
123
|
+
// to `runMemoryCommand` (admin-api `/api/persona-memory` + offline
|
|
124
|
+
// queue at `~/.pugi/memory-queue.jsonl`).
|
|
125
|
+
memory: dispatchMemory,
|
|
109
126
|
patch: dispatchPatch,
|
|
110
127
|
permissions: dispatchPermissions,
|
|
111
128
|
perms: dispatchPermissions,
|
|
112
129
|
plan: dispatchPlan,
|
|
113
130
|
'plan-review': dispatchPlanReview,
|
|
131
|
+
// Wave 6 (2026-05-27): `pugi prd-check` verifies PRD acceptance
|
|
132
|
+
// criteria against committed code/tests/docs/commands BEFORE an
|
|
133
|
+
// operator (or autonomous agent) claims a feature done. Same
|
|
134
|
+
// handler powers the in-REPL `/prd-check` slash via session.ts.
|
|
135
|
+
'prd-check': dispatchPrdCheck,
|
|
114
136
|
privacy: dispatchPrivacy,
|
|
115
137
|
// L24 (2026-05-27): `pugi release-notes` shows the bundled CHANGELOG
|
|
116
138
|
// diff between the operator's last-seen version + installed version.
|
|
@@ -159,6 +181,11 @@ const handlers = {
|
|
|
159
181
|
vim: dispatchVim,
|
|
160
182
|
undo: dispatchUndo,
|
|
161
183
|
compact: dispatchCompact,
|
|
184
|
+
// Leak L9 (2026-05-27): `pugi rewind [N | --to <id>]` rolls the
|
|
185
|
+
// conversation back to a checkpoint by appending a tombstone marker
|
|
186
|
+
// to the NDJSON event log. The slash counterpart `/rewind` forwards
|
|
187
|
+
// to the same runner via session.ts.
|
|
188
|
+
rewind: dispatchRewind,
|
|
162
189
|
// L19 (2026-05-27): `pugi usage` is an alias of `pugi cost` — same
|
|
163
190
|
// handler, same flags. Operators trained on Claude Code expect either
|
|
164
191
|
// verb to surface the per-model token + USD table.
|
|
@@ -343,6 +370,41 @@ async function dispatchPrivacy(args, flags, _session) {
|
|
|
343
370
|
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
344
371
|
});
|
|
345
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* ADR-0063 Day 4 — `pugi memory <sub>` top-level dispatcher.
|
|
375
|
+
*
|
|
376
|
+
* Forwards to the shared `runMemoryCommand` runner. Exit codes:
|
|
377
|
+
*
|
|
378
|
+
* - 0 — happy paths (listed / recalled / written / forgot / synced /
|
|
379
|
+
* queued_offline / sync_noop / sync_partial)
|
|
380
|
+
* - 1 — unauthenticated / feature_disabled / unknown_sub
|
|
381
|
+
* - 2 — invalid_args
|
|
382
|
+
*
|
|
383
|
+
* `forget_not_found` exits 0 because the operator-visible behaviour
|
|
384
|
+
* (the memory is gone) matches their intent; the JSON envelope still
|
|
385
|
+
* carries the `forget_not_found` status flag for scripted callers.
|
|
386
|
+
*/
|
|
387
|
+
async function dispatchMemory(args, flags, _session) {
|
|
388
|
+
const result = await runMemoryCommand(args, {
|
|
389
|
+
workspaceRoot: process.cwd(),
|
|
390
|
+
json: flags.json,
|
|
391
|
+
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
392
|
+
});
|
|
393
|
+
switch (result.status) {
|
|
394
|
+
case 'unauthenticated':
|
|
395
|
+
case 'feature_disabled':
|
|
396
|
+
case 'unknown_sub':
|
|
397
|
+
process.exitCode = 1;
|
|
398
|
+
return;
|
|
399
|
+
case 'invalid_args':
|
|
400
|
+
process.exitCode = 2;
|
|
401
|
+
return;
|
|
402
|
+
default:
|
|
403
|
+
// 'listed' | 'recalled' | 'written' | 'queued_offline' | 'forgot' |
|
|
404
|
+
// 'forget_not_found' | 'synced' | 'sync_partial' | 'sync_noop' — exit 0.
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
346
408
|
/**
|
|
347
409
|
* Leak L18 (2026-05-27) — `pugi style` top-level dispatcher.
|
|
348
410
|
*
|
|
@@ -570,6 +632,31 @@ async function dispatchBudget(args, flags, _session) {
|
|
|
570
632
|
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
571
633
|
});
|
|
572
634
|
}
|
|
635
|
+
/**
|
|
636
|
+
* Leak L9 (2026-05-27) — `pugi rewind [N | --to <id>]` rolls the
|
|
637
|
+
* conversation back to a checkpoint by appending a tombstone marker to
|
|
638
|
+
* the NDJSON event log. Append-only: events stay durable; `pugi
|
|
639
|
+
* sessions undo-rewind` reverses the operation. The slash `/rewind`
|
|
640
|
+
* forwards through this same runner via session.ts.
|
|
641
|
+
*/
|
|
642
|
+
async function dispatchRewind(args, flags, _session) {
|
|
643
|
+
const result = await runRewindCommand(args, {
|
|
644
|
+
workspaceRoot: process.cwd(),
|
|
645
|
+
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
646
|
+
});
|
|
647
|
+
if (result.status === 'failed_no_session'
|
|
648
|
+
|| result.status === 'failed_store') {
|
|
649
|
+
process.exitCode = 1;
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
if (result.status === 'failed_parse') {
|
|
653
|
+
process.exitCode = 2;
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (result.status === 'noop_zero' || result.status === 'noop_empty') {
|
|
657
|
+
process.exitCode = 2;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
573
660
|
/**
|
|
574
661
|
* Leak L6 — `pugi permissions [mode] [--persist] [--confirm]`.
|
|
575
662
|
*
|
|
@@ -745,6 +832,19 @@ async function dispatchAgents(args, flags, _session) {
|
|
|
745
832
|
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
746
833
|
});
|
|
747
834
|
}
|
|
835
|
+
/**
|
|
836
|
+
* Leak L10 (2026-05-27): `pugi dispatch <sub>` — operator-facing
|
|
837
|
+
* inspection + GC for fork-subagent prompt-cache inherit refs
|
|
838
|
+
* (.pugi/cache-refs/). Delegates to the standalone runner in
|
|
839
|
+
* commands/dispatch.ts so the cli.ts table stays under control.
|
|
840
|
+
*/
|
|
841
|
+
async function dispatchSubagentCacheRefs(args, flags, _session) {
|
|
842
|
+
await runDispatchCommand(args, {
|
|
843
|
+
workspaceRoot: process.cwd(),
|
|
844
|
+
json: flags.json,
|
|
845
|
+
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
846
|
+
});
|
|
847
|
+
}
|
|
748
848
|
/**
|
|
749
849
|
* `pugi web <url>` — Sprint α6.15 Phase 1 quick-win subcommand.
|
|
750
850
|
*
|
|
@@ -1526,6 +1626,17 @@ const COMMAND_HELP_BODIES = {
|
|
|
1526
1626
|
'Slugs (Tier 1 alpha 7.5): dev qa pm devops researcher analyst designer',
|
|
1527
1627
|
'frontend architect. `pugi roster` lists the live set.',
|
|
1528
1628
|
],
|
|
1629
|
+
dispatch: [
|
|
1630
|
+
'pugi dispatch <sub> — inspect + GC fork-subagent prompt-cache inherit refs.',
|
|
1631
|
+
'',
|
|
1632
|
+
' list-cache-refs Table of every active ref under .pugi/cache-refs/.',
|
|
1633
|
+
' clear-cache-refs [--older-than 1h] Evict refs older than the window (default 24h).',
|
|
1634
|
+
'',
|
|
1635
|
+
'Leak L10 (2026-05-27): when Mira spawns a child via the `agent` tool,',
|
|
1636
|
+
'a prompt-cache handle is persisted so the child loop can request',
|
|
1637
|
+
'parent-context reuse on the wire. These commands surface + clean up',
|
|
1638
|
+
'the persisted refs.',
|
|
1639
|
+
],
|
|
1529
1640
|
roster: [
|
|
1530
1641
|
'pugi roster — list the live Tier 1 personas + roles.',
|
|
1531
1642
|
],
|
|
@@ -1536,6 +1647,22 @@ const COMMAND_HELP_BODIES = {
|
|
|
1536
1647
|
'event log, settings), permission mode, and the capability matrix per',
|
|
1537
1648
|
'engine adapter. Safe to run anywhere; no network calls.',
|
|
1538
1649
|
],
|
|
1650
|
+
'prd-check': [
|
|
1651
|
+
'pugi prd-check <prd-path> | --all — Wave 6 verified-deliverable gate.',
|
|
1652
|
+
'',
|
|
1653
|
+
'Reads a markdown PRD, parses the acceptance-criteria section, and',
|
|
1654
|
+
'runs verifiers against committed artifacts:',
|
|
1655
|
+
' file:<path> fs.existsSync',
|
|
1656
|
+
' test:<spec> spec file exists + has ≥1 test()/it() block',
|
|
1657
|
+
' doc:<path> doc exists + has > 100 chars',
|
|
1658
|
+
' command:<name> CLI registry contains the command',
|
|
1659
|
+
' route:METHOD /p best-effort grep of controllers',
|
|
1660
|
+
'',
|
|
1661
|
+
' --all Scan docs/prd/**.md instead of one file.',
|
|
1662
|
+
' --json Emit a structured envelope to stdout.',
|
|
1663
|
+
'',
|
|
1664
|
+
'Exit codes: 0 healthy · 1 failing · 2 unparsed / arg error.',
|
|
1665
|
+
],
|
|
1539
1666
|
status: [
|
|
1540
1667
|
'pugi status — concise session snapshot.',
|
|
1541
1668
|
'',
|
|
@@ -1694,6 +1821,8 @@ async function help(args, flags, _session) {
|
|
|
1694
1821
|
'Persona dispatch (α7.5):',
|
|
1695
1822
|
' pugi roster List the live Tier 1 personas + roles.',
|
|
1696
1823
|
' pugi delegate <slug> "<brief>" Dispatch a brief to one specialist.',
|
|
1824
|
+
' pugi dispatch list-cache-refs Inspect fork-subagent prompt-cache inherit refs.',
|
|
1825
|
+
' pugi dispatch clear-cache-refs GC stale cache refs (--older-than 1h).',
|
|
1697
1826
|
'',
|
|
1698
1827
|
'Plan decomposition (α6.8):',
|
|
1699
1828
|
' pugi plan --decompose <idea> Split a high-level idea into 3-7 components.',
|
|
@@ -1757,6 +1886,48 @@ async function doctor(_args, flags, _session) {
|
|
|
1757
1886
|
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
1758
1887
|
});
|
|
1759
1888
|
}
|
|
1889
|
+
/**
|
|
1890
|
+
* `pugi prd-check` — Wave 6 verified-deliverable gate (2026-05-27).
|
|
1891
|
+
*
|
|
1892
|
+
* Reads `docs/prd/<feature>.md` (or any explicit path), parses the
|
|
1893
|
+
* acceptance-criteria section, and runs file / test / doc / command
|
|
1894
|
+
* / route verifiers per criterion. Same handler powers the in-REPL
|
|
1895
|
+
* `/prd-check` slash via session.ts so the verdict is identical
|
|
1896
|
+
* between surfaces.
|
|
1897
|
+
*
|
|
1898
|
+
* The `knownCommands` set is sourced from the same `handlers` map
|
|
1899
|
+
* used by the CLI dispatcher (one source of truth), so a PRD that
|
|
1900
|
+
* mentions `pugi <name>` resolves against the EXACT registry the
|
|
1901
|
+
* shell exposes.
|
|
1902
|
+
*
|
|
1903
|
+
* Exit codes (from reporter.exitCodeFor):
|
|
1904
|
+
* 0 — healthy (every criterion PASS or SKIPPED)
|
|
1905
|
+
* 1 — failing (≥1 FAIL)
|
|
1906
|
+
* 2 — unparsed (PRD has no acceptance section) OR arg error
|
|
1907
|
+
*/
|
|
1908
|
+
async function dispatchPrdCheck(args, flags, _session) {
|
|
1909
|
+
const parsed = parsePrdCheckArgs(args, { jsonDefault: flags.json });
|
|
1910
|
+
if (!parsed.ok) {
|
|
1911
|
+
writeOutput(flags, { ok: false, error: parsed.error }, parsed.error);
|
|
1912
|
+
process.exitCode = 2;
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
await runPrdCheckCommand({
|
|
1916
|
+
cwd: process.cwd(),
|
|
1917
|
+
...(parsed.prdPath !== undefined ? { prdPath: parsed.prdPath } : {}),
|
|
1918
|
+
flags: parsed.flags,
|
|
1919
|
+
knownCommands: knownCommandNames(),
|
|
1920
|
+
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Snapshot the set of registered CLI command names — used by the
|
|
1925
|
+
* prd-check `command:` verifier so PRD mentions of `pugi <name>`
|
|
1926
|
+
* resolve against the exact same registry the shell exposes.
|
|
1927
|
+
*/
|
|
1928
|
+
function knownCommandNames() {
|
|
1929
|
+
return new Set(Object.keys(handlers));
|
|
1930
|
+
}
|
|
1760
1931
|
/**
|
|
1761
1932
|
* `pugi update` — Leak L27 (2026-05-27). Channel-aware npm registry
|
|
1762
1933
|
* probe + optional shell-out to `npm install -g @pugi/cli@<tag>`.
|
|
@@ -3339,6 +3510,25 @@ async function handoff(args, flags, session) {
|
|
|
3339
3510
|
writeOutput(flags, bundle, ['Pugi handoff bundle created', `Bundle: ${bundle.path}`].join('\n'));
|
|
3340
3511
|
}
|
|
3341
3512
|
async function sessions(args, flags, _session) {
|
|
3513
|
+
// L9 (2026-05-27): `pugi sessions undo-rewind [<session-id>]` rolls
|
|
3514
|
+
// back the latest /rewind by appending an inverse marker. Append-only,
|
|
3515
|
+
// reversible. Falls through to the legacy artifact-based handler when
|
|
3516
|
+
// the sub-command is not recognised.
|
|
3517
|
+
if (args[0] === 'undo-rewind') {
|
|
3518
|
+
const result = await runSessionsCommand(args, {
|
|
3519
|
+
workspaceRoot: process.cwd(),
|
|
3520
|
+
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
3521
|
+
});
|
|
3522
|
+
if (result) {
|
|
3523
|
+
if (result.status === 'failed_no_session' || result.status === 'failed_store') {
|
|
3524
|
+
process.exitCode = 1;
|
|
3525
|
+
}
|
|
3526
|
+
else if (result.status === 'noop_no_rewind') {
|
|
3527
|
+
process.exitCode = 2;
|
|
3528
|
+
}
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3342
3532
|
// α6.4: `pugi sessions --local` / `--search "query"` route to the
|
|
3343
3533
|
// local SessionStore. The default surface stays artifact-based for
|
|
3344
3534
|
// backward compat — operators who relied on the index.json view get
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pugi dispatch <sub>` — fork-subagent cache-handoff surface (Leak L10 — 2026-05-27).
|
|
3
|
+
*
|
|
4
|
+
* Sub-commands:
|
|
5
|
+
*
|
|
6
|
+
* - `pugi dispatch list-cache-refs` — show active cache-inherit refs
|
|
7
|
+
* persisted under `.pugi/cache-refs/`. Renders an aligned table or
|
|
8
|
+
* JSON depending on `--json`.
|
|
9
|
+
*
|
|
10
|
+
* - `pugi dispatch clear-cache-refs [--older-than 1h] [-v]` — GC stale
|
|
11
|
+
* refs. Without `--older-than`, defaults to 24h (the same cutoff
|
|
12
|
+
* the REPL boot-time auto-sweep uses). The flag accepts duration
|
|
13
|
+
* strings the `parseDuration` helper recognises (`1h`, `30m`,
|
|
14
|
+
* `7d`, `500ms`, ...).
|
|
15
|
+
*
|
|
16
|
+
* The command lives in its own module (not inline in cli.ts) so the
|
|
17
|
+
* dispatch table stays narrow and tests can drive it without spinning
|
|
18
|
+
* up the full CLI.
|
|
19
|
+
*/
|
|
20
|
+
import { cleanupStaleCacheRefs, parseDuration, } from '../../core/dispatch/cache-cleanup.js';
|
|
21
|
+
import { listCacheRefs } from '../../core/dispatch/cache-handoff.js';
|
|
22
|
+
const USAGE = [
|
|
23
|
+
'Usage:',
|
|
24
|
+
' pugi dispatch list-cache-refs',
|
|
25
|
+
' Show every active subagent cache-inherit',
|
|
26
|
+
' reference persisted under .pugi/cache-refs/.',
|
|
27
|
+
'',
|
|
28
|
+
' pugi dispatch clear-cache-refs [--older-than <duration>] [-v]',
|
|
29
|
+
' Evict stale refs. Accepts 500ms / 30s / 5m /',
|
|
30
|
+
' 1h / 7d. Default --older-than 24h.',
|
|
31
|
+
].join('\n');
|
|
32
|
+
export async function runDispatchCommand(args, ctx) {
|
|
33
|
+
const sub = args[0];
|
|
34
|
+
if (!sub || sub === '--help' || sub === '-h') {
|
|
35
|
+
ctx.writeOutput({ command: 'dispatch', usage: USAGE.split('\n') }, USAGE);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
switch (sub) {
|
|
39
|
+
case 'list-cache-refs':
|
|
40
|
+
return runListCacheRefs(ctx);
|
|
41
|
+
case 'clear-cache-refs':
|
|
42
|
+
return runClearCacheRefs(args.slice(1), ctx);
|
|
43
|
+
default:
|
|
44
|
+
ctx.writeOutput({
|
|
45
|
+
ok: false,
|
|
46
|
+
error: `unknown subcommand: ${sub}`,
|
|
47
|
+
usage: USAGE.split('\n'),
|
|
48
|
+
}, `pugi dispatch: unknown subcommand '${sub}'\n\n${USAGE}`);
|
|
49
|
+
process.exitCode = 2;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function runListCacheRefs(ctx) {
|
|
54
|
+
const refs = listCacheRefs(ctx.workspaceRoot);
|
|
55
|
+
if (ctx.json) {
|
|
56
|
+
ctx.writeOutput({ ok: true, count: refs.length, refs }, '');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (refs.length === 0) {
|
|
60
|
+
ctx.writeOutput({ ok: true, count: 0, refs: [] }, 'No active cache-inherit refs under .pugi/cache-refs/.');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Aligned column table. Widths derived from the data — no hardcoded
|
|
64
|
+
// sizes so a long childAgentId does not overflow the layout.
|
|
65
|
+
const header = ['CHILD AGENT', 'CACHE ID', 'PARENT SESSION', 'CREATED'];
|
|
66
|
+
const rows = refs.map((r) => [
|
|
67
|
+
r.childAgentId,
|
|
68
|
+
r.cacheId,
|
|
69
|
+
r.parentSessionId,
|
|
70
|
+
r.createdAt,
|
|
71
|
+
]);
|
|
72
|
+
const widths = header.map((h, i) => Math.max(h.length, ...rows.map((row) => (row[i] ?? '').length)));
|
|
73
|
+
const lines = [];
|
|
74
|
+
lines.push(header.map((h, i) => h.padEnd(widths[i] ?? 0)).join(' '));
|
|
75
|
+
lines.push(widths.map((w) => '-'.repeat(w)).join(' '));
|
|
76
|
+
for (const row of rows) {
|
|
77
|
+
lines.push(row.map((cell, i) => (cell ?? '').padEnd(widths[i] ?? 0)).join(' '));
|
|
78
|
+
}
|
|
79
|
+
ctx.writeOutput({ ok: true, count: refs.length, refs }, lines.join('\n'));
|
|
80
|
+
}
|
|
81
|
+
function runClearCacheRefs(args, ctx) {
|
|
82
|
+
let olderThanArg;
|
|
83
|
+
let verbose = false;
|
|
84
|
+
for (let i = 0; i < args.length; i++) {
|
|
85
|
+
const arg = args[i];
|
|
86
|
+
if (arg === '--older-than') {
|
|
87
|
+
olderThanArg = args[i + 1];
|
|
88
|
+
i++;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (arg && arg.startsWith('--older-than=')) {
|
|
92
|
+
olderThanArg = arg.slice('--older-than='.length);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (arg === '-v' || arg === '--verbose') {
|
|
96
|
+
verbose = true;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Default 24h matches the REPL boot-time auto-sweep cutoff.
|
|
101
|
+
const olderThanMs = olderThanArg ? parseDuration(olderThanArg) : 24 * 60 * 60 * 1000;
|
|
102
|
+
if (olderThanMs === null) {
|
|
103
|
+
ctx.writeOutput({
|
|
104
|
+
ok: false,
|
|
105
|
+
error: `invalid --older-than value: ${olderThanArg ?? '(missing)'}`,
|
|
106
|
+
usage: USAGE.split('\n'),
|
|
107
|
+
}, `pugi dispatch clear-cache-refs: invalid --older-than '${olderThanArg ?? '(missing)'}'\n\n${USAGE}`);
|
|
108
|
+
process.exitCode = 2;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const result = cleanupStaleCacheRefs(ctx.workspaceRoot, {
|
|
112
|
+
olderThanMs,
|
|
113
|
+
verbose,
|
|
114
|
+
});
|
|
115
|
+
const summary = `Removed ${result.removedCount} ref(s) (${result.corruptCount} corrupt), kept ${result.keptCount}.`;
|
|
116
|
+
ctx.writeOutput({
|
|
117
|
+
ok: true,
|
|
118
|
+
olderThanMs,
|
|
119
|
+
removedCount: result.removedCount,
|
|
120
|
+
keptCount: result.keptCount,
|
|
121
|
+
corruptCount: result.corruptCount,
|
|
122
|
+
removed: result.removed,
|
|
123
|
+
corrupt: result.corrupt,
|
|
124
|
+
}, summary);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=dispatch.js.map
|