@pugi/cli 0.1.0-beta.28 → 0.1.0-beta.29
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/artifact-chain/dispatcher.js +148 -0
- package/dist/core/artifact-chain/exporter.js +164 -0
- package/dist/core/artifact-chain/state.js +243 -0
- package/dist/core/artifact-chain/steps.js +169 -0
- package/dist/core/repl/session.js +32 -0
- package/dist/core/repl/slash-commands.js +10 -0
- package/dist/runtime/cli.js +42 -0
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/delegate.js +23 -0
- package/dist/runtime/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact chain step definitions — Pugi α7 Wave 6 (2026-05-27).
|
|
3
|
+
*
|
|
4
|
+
* The chain encodes Pugi's moat: an operator drops one high-level intent
|
|
5
|
+
* (`pugi chain new "<intent>"`) and the CLI walks a deterministic 7-step
|
|
6
|
+
* pipeline that produces verifiable artifacts at each stage. Every step
|
|
7
|
+
* dispatches to a specialist persona via the existing `pugi delegate`
|
|
8
|
+
* surface so the same Tier 1 roster powers both ad-hoc dispatch and the
|
|
9
|
+
* artifact chain.
|
|
10
|
+
*
|
|
11
|
+
* The pipeline:
|
|
12
|
+
*
|
|
13
|
+
* 1. prd — Olivia (PM): structured PRD (goals / users /
|
|
14
|
+
* acceptance criteria / scope)
|
|
15
|
+
* 2. adr — Marcus (CTO): architectural decision record
|
|
16
|
+
* against the team ADR template
|
|
17
|
+
* 3. mindmap — Marcus (CTO): mermaid mindmap of the solution
|
|
18
|
+
* surface (modules + boundaries)
|
|
19
|
+
* 4. er — Hiroshi (Lead Dev):entity-relationship mermaid for
|
|
20
|
+
* the data model the design implies
|
|
21
|
+
* 5. sequence — Hiroshi (Lead Dev):mermaid sequence diagram covering
|
|
22
|
+
* the critical happy + sad paths
|
|
23
|
+
* 6. tests — Vera (QA): spec scaffolding mapped from the
|
|
24
|
+
* PRD acceptance criteria
|
|
25
|
+
* 7. code — Hiroshi (Lead Dev):implementation diff against the
|
|
26
|
+
* scaffolded specs
|
|
27
|
+
*
|
|
28
|
+
* Persona slugs are lowercase ASCII to satisfy the server-side delegate
|
|
29
|
+
* grammar (`^[a-z]+$`, mirrors `PUGI_DELEGATE_REGEX` in
|
|
30
|
+
* `apps/admin-api/src/pugi/sessions.controller.ts`). The chain never
|
|
31
|
+
* invents new personas — it only orchestrates the existing roster.
|
|
32
|
+
*
|
|
33
|
+
* Module contract:
|
|
34
|
+
*
|
|
35
|
+
* - This file is PURE data. No fs, no network, no side effects.
|
|
36
|
+
* Importing it from a hot path (REPL keystroke handler) is safe.
|
|
37
|
+
* - The step ORDER is authoritative; downstream consumers MUST iterate
|
|
38
|
+
* `CHAIN_STEPS` instead of hand-rolling their own arrays so future
|
|
39
|
+
* re-ordering lands in exactly one place.
|
|
40
|
+
* - Personas are looked up by slug at dispatch time so a roster change
|
|
41
|
+
* does not silently break the chain — the dispatcher surfaces an
|
|
42
|
+
* `unknown_persona` outcome the operator can see.
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* Ordered, frozen table of the seven steps. The chain state machine
|
|
46
|
+
* advances through this array exactly once; re-ordering OR insertion
|
|
47
|
+
* is a breaking change for any chain currently on disk.
|
|
48
|
+
*/
|
|
49
|
+
export const CHAIN_STEPS = Object.freeze([
|
|
50
|
+
{
|
|
51
|
+
id: 'prd',
|
|
52
|
+
ordinal: 1,
|
|
53
|
+
persona: 'olivia',
|
|
54
|
+
personaLabel: 'Olivia (PM)',
|
|
55
|
+
artifactFilename: 'PRD.md',
|
|
56
|
+
briefTemplate: 'Produce a structured PRD for chain {{chainId}}. ' +
|
|
57
|
+
'Operator intent: "{{intent}}". ' +
|
|
58
|
+
'Cover: goals, target users, success metrics, ' +
|
|
59
|
+
'numbered acceptance criteria (## Acceptance Criteria), scope + non-scope, ' +
|
|
60
|
+
'risks. Output markdown ready for prd-check ingestion.',
|
|
61
|
+
gloss: 'Structured PRD — goals, users, acceptance criteria, scope',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'adr',
|
|
65
|
+
ordinal: 2,
|
|
66
|
+
persona: 'marcus',
|
|
67
|
+
personaLabel: 'Marcus (CTO)',
|
|
68
|
+
artifactFilename: 'ADR.md',
|
|
69
|
+
briefTemplate: 'Produce an architectural decision record for chain {{chainId}}. ' +
|
|
70
|
+
'Read the PRD at .pugi/chains/{{chainId}}/PRD.md verbatim. ' +
|
|
71
|
+
'Follow the team ADR template: Status, Context, Decision, ' +
|
|
72
|
+
'Consequences, Alternatives Considered. ' +
|
|
73
|
+
'Be explicit about boundary changes the decision implies.',
|
|
74
|
+
gloss: 'Architectural decision record per team ADR template',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'mindmap',
|
|
78
|
+
ordinal: 3,
|
|
79
|
+
persona: 'marcus',
|
|
80
|
+
personaLabel: 'Marcus (CTO)',
|
|
81
|
+
artifactFilename: 'MINDMAP.mmd',
|
|
82
|
+
briefTemplate: 'Produce a mermaid mindmap of the solution surface for chain {{chainId}}. ' +
|
|
83
|
+
'Read PRD + ADR at .pugi/chains/{{chainId}}/PRD.md and ADR.md. ' +
|
|
84
|
+
'Root node = the feature name; first-level branches = subsystems; ' +
|
|
85
|
+
'leaves = concrete modules / endpoints / tables. ' +
|
|
86
|
+
'Output ONE mermaid `mindmap` fenced block — no prose.',
|
|
87
|
+
gloss: 'Mermaid mindmap — solution surface (subsystems + modules)',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'er',
|
|
91
|
+
ordinal: 4,
|
|
92
|
+
persona: 'hiroshi',
|
|
93
|
+
personaLabel: 'Hiroshi (Lead Dev)',
|
|
94
|
+
artifactFilename: 'ER.mmd',
|
|
95
|
+
briefTemplate: 'Produce a mermaid entity-relationship diagram for chain {{chainId}}. ' +
|
|
96
|
+
'Source: PRD + ADR + MINDMAP under .pugi/chains/{{chainId}}/. ' +
|
|
97
|
+
'Use `erDiagram` syntax. Cover every persisted entity the ' +
|
|
98
|
+
'design implies; mark PK / FK relationships explicitly.',
|
|
99
|
+
gloss: 'Mermaid ER diagram — persisted entities + relationships',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'sequence',
|
|
103
|
+
ordinal: 5,
|
|
104
|
+
persona: 'hiroshi',
|
|
105
|
+
personaLabel: 'Hiroshi (Lead Dev)',
|
|
106
|
+
artifactFilename: 'SEQUENCE.mmd',
|
|
107
|
+
briefTemplate: 'Produce mermaid sequence diagrams for chain {{chainId}}. ' +
|
|
108
|
+
'Cover the critical happy path + at least one sad path. ' +
|
|
109
|
+
'Source: PRD acceptance criteria + ER diagram. ' +
|
|
110
|
+
'Use `sequenceDiagram` syntax; one diagram per fenced block.',
|
|
111
|
+
gloss: 'Mermaid sequence diagrams — happy + sad path flows',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'tests',
|
|
115
|
+
ordinal: 6,
|
|
116
|
+
persona: 'vera',
|
|
117
|
+
personaLabel: 'Vera (QA)',
|
|
118
|
+
artifactFilename: 'TESTS.md',
|
|
119
|
+
briefTemplate: 'Produce spec scaffolding for chain {{chainId}}. ' +
|
|
120
|
+
'Map every numbered PRD acceptance criterion (under ## Acceptance Criteria) ' +
|
|
121
|
+
'to one or more concrete test descriptions using node:test + node:assert. ' +
|
|
122
|
+
'Format: criterion-id → test file path → `it(...)` blocks. ' +
|
|
123
|
+
'Output markdown only — no executable code.',
|
|
124
|
+
gloss: 'Spec scaffolding — PRD criteria mapped to test descriptions',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'code',
|
|
128
|
+
ordinal: 7,
|
|
129
|
+
persona: 'hiroshi',
|
|
130
|
+
personaLabel: 'Hiroshi (Lead Dev)',
|
|
131
|
+
artifactFilename: 'CODE.md',
|
|
132
|
+
briefTemplate: 'Produce an implementation plan + diff references for chain {{chainId}}. ' +
|
|
133
|
+
'Read PRD, ADR, MINDMAP, ER, SEQUENCE, TESTS under .pugi/chains/{{chainId}}/. ' +
|
|
134
|
+
'Output: file-by-file changes (path, change kind, summary), ' +
|
|
135
|
+
'with each change tied back to a PRD criterion id. ' +
|
|
136
|
+
'NO inline patches — patches land via `pugi patch apply` after operator review.',
|
|
137
|
+
gloss: 'Implementation plan — file changes mapped to PRD criteria',
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
/**
|
|
141
|
+
* Resolve a step descriptor by id. Returns `undefined` for unknown ids
|
|
142
|
+
* so callers can surface a structured error instead of panicking.
|
|
143
|
+
*/
|
|
144
|
+
export function findStep(id) {
|
|
145
|
+
return CHAIN_STEPS.find((step) => step.id === id);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Index of step ids in chain order. Exported so the state machine can
|
|
149
|
+
* answer "what is the next step after X?" without re-scanning the
|
|
150
|
+
* table on every transition.
|
|
151
|
+
*/
|
|
152
|
+
export const CHAIN_STEP_IDS = Object.freeze(CHAIN_STEPS.map((s) => s.id));
|
|
153
|
+
/**
|
|
154
|
+
* Total number of steps in the chain. Hard-coded constant exported so
|
|
155
|
+
* downstream renderers can size their progress bars without iterating.
|
|
156
|
+
*/
|
|
157
|
+
export const CHAIN_STEP_COUNT = CHAIN_STEPS.length;
|
|
158
|
+
/**
|
|
159
|
+
* Interpolate the brief template with chain context. The template
|
|
160
|
+
* grammar is intentionally minimal — only `{{chainId}}` and `{{intent}}`
|
|
161
|
+
* are honoured. Unknown placeholders are left verbatim so a template
|
|
162
|
+
* typo surfaces in the dispatched brief instead of silently dropping.
|
|
163
|
+
*/
|
|
164
|
+
export function renderBrief(template, ctx) {
|
|
165
|
+
return template
|
|
166
|
+
.replace(/\{\{chainId\}\}/g, ctx.chainId)
|
|
167
|
+
.replace(/\{\{intent\}\}/g, ctx.intent);
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=steps.js.map
|
|
@@ -1023,6 +1023,38 @@ export class ReplSession {
|
|
|
1023
1023
|
}
|
|
1024
1024
|
return verdict;
|
|
1025
1025
|
}
|
|
1026
|
+
case 'chain': {
|
|
1027
|
+
// Wave 6 (2026-05-27): forward to the shell-surface runner so
|
|
1028
|
+
// the slash + top-level CLI share one parser + dispatcher.
|
|
1029
|
+
// Dynamic import keeps the chain module out of the REPL hot
|
|
1030
|
+
// path. The slash variant does NOT inject the live delegate
|
|
1031
|
+
// wire-up — operators wanting full dispatch run `pugi chain
|
|
1032
|
+
// next` from a fresh shell. The slash form is best-effort for
|
|
1033
|
+
// status / show / list which are read-only.
|
|
1034
|
+
try {
|
|
1035
|
+
const { runChainCommand } = await import('../../runtime/commands/chain.js');
|
|
1036
|
+
const lines = [];
|
|
1037
|
+
await runChainCommand(verdict.args, {
|
|
1038
|
+
cwd: process.cwd(),
|
|
1039
|
+
json: false,
|
|
1040
|
+
writeOutput: (_payload, text) => {
|
|
1041
|
+
const trimmed = text.replace(/\n+$/u, '');
|
|
1042
|
+
if (trimmed.length > 0)
|
|
1043
|
+
lines.push(trimmed);
|
|
1044
|
+
},
|
|
1045
|
+
});
|
|
1046
|
+
for (const line of lines)
|
|
1047
|
+
this.appendSystemLine(line);
|
|
1048
|
+
if (lines.length === 0) {
|
|
1049
|
+
this.appendSystemLine('/chain: no output.');
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
catch (error) {
|
|
1053
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1054
|
+
this.appendSystemLine(`/chain failed: ${message}`);
|
|
1055
|
+
}
|
|
1056
|
+
return verdict;
|
|
1057
|
+
}
|
|
1026
1058
|
case 'permissions': {
|
|
1027
1059
|
// Leak L6: handle the `/permissions [mode] [--persist]` flow.
|
|
1028
1060
|
// The session module forwards to the runtime helper so the
|
|
@@ -94,6 +94,7 @@ export const SLASH_COMMAND_HELP = Object.freeze([
|
|
|
94
94
|
{ name: 'version', args: '', gloss: 'Show CLI version', group: 'Meta' },
|
|
95
95
|
{ name: 'doctor', args: '', gloss: 'Environment health report (auth · API · Node · disk · MCP · …)', group: 'Meta' },
|
|
96
96
|
{ name: 'prd-check', args: '<prd-path | --all> [--json]', gloss: 'Verify PRD acceptance criteria against committed code/tests/docs (Wave 6)', group: 'Meta' },
|
|
97
|
+
{ name: 'chain', args: '<new|status|next|show|export|list> [...args]', gloss: 'Artifact chain — PRD → ADR → mindmap → ER → sequence → tests → code (Wave 6)', group: 'Meta' },
|
|
97
98
|
{ name: 'stickers', args: '', gloss: 'show Pugi brand stickers (gimmick)', group: 'Meta' },
|
|
98
99
|
{ name: 'feedback', args: '', gloss: 'file a bug / feature / general comment without leaving the REPL', group: 'Meta' },
|
|
99
100
|
{ name: 'share', args: '[--gist|--pugi] [--redact] [--preview]', gloss: 'Export session transcript to gist / pugi.io (leak L20)', group: 'Meta' },
|
|
@@ -441,6 +442,15 @@ export function parseSlashCommand(input) {
|
|
|
441
442
|
const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
|
|
442
443
|
return { kind: 'prd-check', args: tokens };
|
|
443
444
|
}
|
|
445
|
+
case 'chain': {
|
|
446
|
+
// Wave 6 (2026-05-27): forward the tokenized argv to
|
|
447
|
+
// `runChainCommand` via the session module. Subcommands are
|
|
448
|
+
// single tokens (new / status / next / show / export / list);
|
|
449
|
+
// the `new` subcommand accepts the intent as the joined tail so
|
|
450
|
+
// operators can write `/chain new add auth flow` без quoting.
|
|
451
|
+
const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
|
|
452
|
+
return { kind: 'chain', args: tokens };
|
|
453
|
+
}
|
|
444
454
|
case 'compact': {
|
|
445
455
|
// Leak L8 (2026-05-27): graduated from stub. The session module
|
|
446
456
|
// owns the summariser round-trip; tail args are ignored today
|
package/dist/runtime/cli.js
CHANGED
|
@@ -37,6 +37,7 @@ import { runPrivacyCommand } from './commands/privacy.js';
|
|
|
37
37
|
import { runReport } from './commands/report.js';
|
|
38
38
|
import { runDoctorCommand, defaultHome as defaultDoctorHome } from './commands/doctor.js';
|
|
39
39
|
import { parsePrdCheckArgs, runPrdCheckCommand, } from './commands/prd-check.js';
|
|
40
|
+
import { runChainCommand, } from './commands/chain.js';
|
|
40
41
|
import { runStatusCommand, defaultStatusHome, } from './commands/status.js';
|
|
41
42
|
import { runStickersCommand } from './commands/stickers.js';
|
|
42
43
|
import { runRepoMapCommand } from './commands/repo-map.js';
|
|
@@ -96,6 +97,11 @@ const handlers = {
|
|
|
96
97
|
ask: dispatchAsk,
|
|
97
98
|
build: runEngineTask('build_task'),
|
|
98
99
|
budget: dispatchBudget,
|
|
100
|
+
// Wave 6 (2026-05-27): `pugi chain` walks the deterministic 7-step
|
|
101
|
+
// artifact pipeline (PRD → ADR → mindmap → ER → sequence → tests →
|
|
102
|
+
// code). Subcommands: new / status / next / show / export / list.
|
|
103
|
+
// Same handler powers the in-REPL `/chain` slash via session.ts.
|
|
104
|
+
chain: dispatchChain,
|
|
99
105
|
code: runEngineTask('code'),
|
|
100
106
|
config: dispatchConfig,
|
|
101
107
|
cost: dispatchCost,
|
|
@@ -597,6 +603,32 @@ async function dispatchDelegate(args, flags, _session) {
|
|
|
597
603
|
},
|
|
598
604
|
});
|
|
599
605
|
}
|
|
606
|
+
/**
|
|
607
|
+
* `pugi chain` — Wave 6 artifact chain dispatcher (2026-05-27).
|
|
608
|
+
* Forwards to `runChainCommand` with the live credential + session
|
|
609
|
+
* opener wired so the dispatcher can hit Anvil. The slash counterpart
|
|
610
|
+
* `/chain` shares the same handler via session.ts so the surface
|
|
611
|
+
* stays single-sourced.
|
|
612
|
+
*/
|
|
613
|
+
async function dispatchChain(args, flags, _session) {
|
|
614
|
+
await runChainCommand(args, {
|
|
615
|
+
cwd: process.cwd(),
|
|
616
|
+
json: flags.json,
|
|
617
|
+
writeOutput: (payload, text) => writeOutput(flags, payload, text),
|
|
618
|
+
resolveConfig: () => {
|
|
619
|
+
const credential = resolveActiveCredential();
|
|
620
|
+
if (!credential)
|
|
621
|
+
return null;
|
|
622
|
+
return buildRuntimeConfig({ apiUrl: credential.apiUrl, apiKey: credential.apiKey });
|
|
623
|
+
},
|
|
624
|
+
openSession: async (config, workspaceCwd) => {
|
|
625
|
+
const result = await openPugiSession(config, { workspaceCwd });
|
|
626
|
+
if (result.status === 'ok')
|
|
627
|
+
return { sessionId: result.response.sessionId };
|
|
628
|
+
return { error: `${result.status}: ${result.message}` };
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
}
|
|
600
632
|
async function dispatchUndo(args, flags, session) {
|
|
601
633
|
await runUndoCommand(args, {
|
|
602
634
|
workspaceRoot: process.cwd(),
|
|
@@ -1626,6 +1658,16 @@ const COMMAND_HELP_BODIES = {
|
|
|
1626
1658
|
'Slugs (Tier 1 alpha 7.5): dev qa pm devops researcher analyst designer',
|
|
1627
1659
|
'frontend architect. `pugi roster` lists the live set.',
|
|
1628
1660
|
],
|
|
1661
|
+
chain: [
|
|
1662
|
+
'pugi chain — Wave 6 artifact chain (PRD → ADR → mindmap → ER → sequence → tests → code).',
|
|
1663
|
+
'',
|
|
1664
|
+
' new "<intent>" Start a new chain from a one-sentence intent.',
|
|
1665
|
+
' status [<chain-id>] Show current cursor + per-step table.',
|
|
1666
|
+
' next [<chain-id>] Approve the last step and dispatch the next.',
|
|
1667
|
+
' show <step> [<chain-id>] Render one artifact (prd/adr/mindmap/er/sequence/tests/code).',
|
|
1668
|
+
' export [<chain-id>] [--json] Bundle every artifact as markdown / JSON.',
|
|
1669
|
+
' list Every chain in this workspace.',
|
|
1670
|
+
],
|
|
1629
1671
|
dispatch: [
|
|
1630
1672
|
'pugi dispatch <sub> — inspect + GC fork-subagent prompt-cache inherit refs.',
|
|
1631
1673
|
'',
|