@pugi/cli 0.1.0-beta.27 → 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.
@@ -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
@@ -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
  '',