gswd 1.0.0 → 1.1.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.
Files changed (86) hide show
  1. package/bin/gswd-tools.cjs +228 -0
  2. package/bin/install.js +8 -0
  3. package/commands/gswd/imagine.md +7 -1
  4. package/commands/gswd/start.md +507 -32
  5. package/dist/lib/audit.d.ts +205 -0
  6. package/dist/lib/audit.js +805 -0
  7. package/dist/lib/bootstrap.d.ts +103 -0
  8. package/dist/lib/bootstrap.js +563 -0
  9. package/dist/lib/compile.d.ts +239 -0
  10. package/dist/lib/compile.js +1152 -0
  11. package/dist/lib/config.d.ts +49 -0
  12. package/dist/lib/config.js +150 -0
  13. package/dist/lib/imagine-agents.d.ts +54 -0
  14. package/dist/lib/imagine-agents.js +185 -0
  15. package/dist/lib/imagine-gate.d.ts +47 -0
  16. package/dist/lib/imagine-gate.js +131 -0
  17. package/dist/lib/imagine-input.d.ts +46 -0
  18. package/dist/lib/imagine-input.js +233 -0
  19. package/dist/lib/imagine-synthesis.d.ts +90 -0
  20. package/dist/lib/imagine-synthesis.js +453 -0
  21. package/dist/lib/imagine.d.ts +56 -0
  22. package/dist/lib/imagine.js +413 -0
  23. package/dist/lib/intake.d.ts +27 -0
  24. package/dist/lib/intake.js +82 -0
  25. package/dist/lib/parse.d.ts +59 -0
  26. package/dist/lib/parse.js +171 -0
  27. package/dist/lib/render.d.ts +309 -0
  28. package/dist/lib/render.js +624 -0
  29. package/dist/lib/specify-agents.d.ts +120 -0
  30. package/dist/lib/specify-agents.js +269 -0
  31. package/dist/lib/specify-journeys.d.ts +124 -0
  32. package/dist/lib/specify-journeys.js +279 -0
  33. package/dist/lib/specify-nfr.d.ts +45 -0
  34. package/dist/lib/specify-nfr.js +159 -0
  35. package/dist/lib/specify-roles.d.ts +46 -0
  36. package/dist/lib/specify-roles.js +88 -0
  37. package/dist/lib/specify.d.ts +70 -0
  38. package/dist/lib/specify.js +676 -0
  39. package/dist/lib/state.d.ts +140 -0
  40. package/dist/lib/state.js +340 -0
  41. package/dist/tests/audit.test.d.ts +4 -0
  42. package/dist/tests/audit.test.js +1579 -0
  43. package/dist/tests/bootstrap.test.d.ts +5 -0
  44. package/dist/tests/bootstrap.test.js +611 -0
  45. package/dist/tests/compile.test.d.ts +4 -0
  46. package/dist/tests/compile.test.js +862 -0
  47. package/dist/tests/config.test.d.ts +4 -0
  48. package/dist/tests/config.test.js +191 -0
  49. package/dist/tests/imagine-agents.test.d.ts +6 -0
  50. package/dist/tests/imagine-agents.test.js +179 -0
  51. package/dist/tests/imagine-gate.test.d.ts +6 -0
  52. package/dist/tests/imagine-gate.test.js +264 -0
  53. package/dist/tests/imagine-input.test.d.ts +6 -0
  54. package/dist/tests/imagine-input.test.js +283 -0
  55. package/dist/tests/imagine-synthesis.test.d.ts +7 -0
  56. package/dist/tests/imagine-synthesis.test.js +380 -0
  57. package/dist/tests/imagine.test.d.ts +8 -0
  58. package/dist/tests/imagine.test.js +406 -0
  59. package/dist/tests/parse.test.d.ts +4 -0
  60. package/dist/tests/parse.test.js +285 -0
  61. package/dist/tests/render.test.d.ts +4 -0
  62. package/dist/tests/render.test.js +236 -0
  63. package/dist/tests/specify-agents.test.d.ts +4 -0
  64. package/dist/tests/specify-agents.test.js +352 -0
  65. package/dist/tests/specify-journeys.test.d.ts +5 -0
  66. package/dist/tests/specify-journeys.test.js +440 -0
  67. package/dist/tests/specify-nfr.test.d.ts +4 -0
  68. package/dist/tests/specify-nfr.test.js +205 -0
  69. package/dist/tests/specify-roles.test.d.ts +4 -0
  70. package/dist/tests/specify-roles.test.js +136 -0
  71. package/dist/tests/specify.test.d.ts +9 -0
  72. package/dist/tests/specify.test.js +544 -0
  73. package/dist/tests/state.test.d.ts +4 -0
  74. package/dist/tests/state.test.js +316 -0
  75. package/lib/bootstrap.ts +37 -11
  76. package/lib/compile.ts +426 -4
  77. package/lib/imagine-agents.ts +53 -7
  78. package/lib/imagine-synthesis.ts +170 -6
  79. package/lib/imagine.ts +59 -5
  80. package/lib/intake.ts +60 -0
  81. package/lib/parse.ts +2 -1
  82. package/lib/render.ts +566 -5
  83. package/lib/specify-agents.ts +25 -3
  84. package/lib/state.ts +115 -0
  85. package/package.json +4 -2
  86. package/templates/gswd/DECISIONS.template.md +3 -0
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ /**
3
+ * GSWD Parse Module — ID extraction, heading validation, normalization
4
+ *
5
+ * Parses GSWD artifact files for IDs (J-NNN, FR-NNN, NFR-NNN, I-NNN, C-NNN),
6
+ * validates required heading structure, and normalizes malformed IDs.
7
+ *
8
+ * Schema: GSWD_SPEC.md Section 6.1-6.3
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.REQUIRED_HEADINGS = void 0;
12
+ exports.normalizeId = normalizeId;
13
+ exports.extractIds = extractIds;
14
+ exports.validateHeadings = validateHeadings;
15
+ exports.extractHeadingContent = extractHeadingContent;
16
+ // ─── Required Headings ──────────────────────────────────────────────────────
17
+ /**
18
+ * Required headings per artifact file type, from GSWD_SPEC Section 6.3.
19
+ * These are the stable anchors used by audit/compile parsers.
20
+ */
21
+ exports.REQUIRED_HEADINGS = {
22
+ 'JOURNEYS.md': ['## Journeys'],
23
+ 'SPEC.md': ['## Roles & Permissions', '## Functional Requirements', '## Acceptance Criteria'],
24
+ 'NFR.md': ['## Non-Functional Requirements'],
25
+ 'INTEGRATIONS.md': ['## Integrations'],
26
+ 'ARCHITECTURE.md': ['## Architecture', '### Components', '### Data Model'],
27
+ 'DECISIONS.md': [
28
+ '## Vision',
29
+ '## Frozen Decisions',
30
+ '## Success Metrics',
31
+ '## Out of Scope',
32
+ '## Risks & Mitigations',
33
+ '## Open Questions',
34
+ ],
35
+ 'AUDIT.md': ['## Coverage Matrix', '## Check Results'],
36
+ 'PROJECT.md': ['## What This Is', '## Target User', '## Problem Statement', '## Wedge / MVP Boundary', '## Success Metrics', '## Out of Scope', '## Context'],
37
+ 'REQUIREMENTS.md': ['## Functional Requirements', '## Non-Functional Requirements', '## Traceability'],
38
+ 'ROADMAP.md': ['## Overview', '## Phases'],
39
+ 'STATE.md': ['## Frozen Decisions', '## Approvals', '## Open Questions', '## Risks'],
40
+ };
41
+ // ─── ID Normalization ────────────────────────────────────────────────────────
42
+ /** Valid ID prefixes */
43
+ const ID_PREFIXES = ['J', 'FR', 'NFR', 'I', 'C'];
44
+ /**
45
+ * Normalize an ID to canonical format: PREFIX-NNN (3-digit minimum, zero-padded).
46
+ *
47
+ * - FR-1 -> FR-001
48
+ * - FR-01 -> FR-001
49
+ * - FR-001 -> FR-001 (no change)
50
+ * - FR-1000 -> FR-1000 (4+ digits kept as-is)
51
+ * - INVALID -> INVALID (unrecognized format returned as-is)
52
+ */
53
+ function normalizeId(rawId) {
54
+ if (!rawId)
55
+ return rawId;
56
+ const match = rawId.match(/^(J|FR|NFR|I|C)-(\d+)$/);
57
+ if (!match)
58
+ return rawId;
59
+ const prefix = match[1];
60
+ const num = parseInt(match[2], 10);
61
+ const padded = num.toString().padStart(3, '0');
62
+ return `${prefix}-${padded}`;
63
+ }
64
+ /**
65
+ * Extract IDs from content using regex.
66
+ *
67
+ * Regex: /\b(J|FR|NFR|I|C)-(\d{1,4})\b/g
68
+ * - Word boundary prevents partial matches (e.g., INFRASTRUCTURE-001)
69
+ * - Returns deduplicated array sorted by normalized ID ascending
70
+ * - Optional filter by idType (e.g., 'FR')
71
+ */
72
+ function extractIds(content, idType) {
73
+ const regex = /\b(J|FR|NFR|I|C)-(\d{1,4})\b/g;
74
+ const seen = new Map();
75
+ let match;
76
+ while ((match = regex.exec(content)) !== null) {
77
+ const raw = match[0];
78
+ const prefix = match[1];
79
+ // Filter by ID type if specified
80
+ if (idType && prefix !== idType)
81
+ continue;
82
+ const normalized = normalizeId(raw);
83
+ if (!seen.has(normalized)) {
84
+ seen.set(normalized, {
85
+ id: normalized,
86
+ raw,
87
+ normalized: raw !== normalized,
88
+ });
89
+ }
90
+ }
91
+ // Sort by normalized ID ascending
92
+ return Array.from(seen.values()).sort((a, b) => {
93
+ // Split into prefix and number for proper sorting
94
+ const aParts = a.id.match(/^(.+)-(\d+)$/);
95
+ const bParts = b.id.match(/^(.+)-(\d+)$/);
96
+ if (!aParts || !bParts)
97
+ return a.id.localeCompare(b.id);
98
+ // Sort by prefix first, then by number
99
+ if (aParts[1] !== bParts[1])
100
+ return aParts[1].localeCompare(bParts[1]);
101
+ return parseInt(aParts[2], 10) - parseInt(bParts[2], 10);
102
+ });
103
+ }
104
+ /**
105
+ * Validate that a file contains all required headings for its type.
106
+ *
107
+ * - Looks up required headings from REQUIRED_HEADINGS[fileType]
108
+ * - Case-insensitive search as safety layer
109
+ * - Returns which headings are present and which are missing
110
+ * - Unknown file types are always valid (no required headings)
111
+ */
112
+ function validateHeadings(content, fileType) {
113
+ const required = exports.REQUIRED_HEADINGS[fileType];
114
+ // Unknown file type — no required headings, always valid
115
+ if (!required) {
116
+ return { valid: true, missing: [], present: [] };
117
+ }
118
+ const contentLower = content.toLowerCase();
119
+ const missing = [];
120
+ const present = [];
121
+ for (const heading of required) {
122
+ if (contentLower.includes(heading.toLowerCase())) {
123
+ present.push(heading);
124
+ }
125
+ else {
126
+ missing.push(heading);
127
+ }
128
+ }
129
+ return {
130
+ valid: missing.length === 0,
131
+ missing,
132
+ present,
133
+ };
134
+ }
135
+ // ─── Heading Content Extraction ──────────────────────────────────────────────
136
+ /**
137
+ * Extract content between a heading and the next heading of same or higher level.
138
+ *
139
+ * @param content - Full file content
140
+ * @param heading - The heading to extract content from (e.g., "## Section A")
141
+ * @returns Content string (trimmed) or null if heading not found
142
+ */
143
+ function extractHeadingContent(content, heading) {
144
+ const lines = content.split('\n');
145
+ const headingLevel = (heading.match(/^#+/) || [''])[0].length;
146
+ let capturing = false;
147
+ let startIdx = -1;
148
+ let endIdx = lines.length;
149
+ for (let i = 0; i < lines.length; i++) {
150
+ const line = lines[i];
151
+ if (!capturing) {
152
+ // Look for the target heading
153
+ if (line.trim().toLowerCase() === heading.toLowerCase()) {
154
+ capturing = true;
155
+ startIdx = i + 1;
156
+ }
157
+ }
158
+ else {
159
+ // Check if we hit another heading of same or higher level
160
+ const lineHeadingMatch = line.match(/^(#+)\s/);
161
+ if (lineHeadingMatch && lineHeadingMatch[1].length <= headingLevel) {
162
+ endIdx = i;
163
+ break;
164
+ }
165
+ }
166
+ }
167
+ if (startIdx === -1)
168
+ return null;
169
+ const extracted = lines.slice(startIdx, endIdx).join('\n').trim();
170
+ return extracted;
171
+ }
@@ -0,0 +1,309 @@
1
+ /**
2
+ * GSWD Render Module — Terminal UI rendering functions
3
+ *
4
+ * All functions return strings (not print to stdout).
5
+ * Zero external dependencies. Uses only string operations.
6
+ *
7
+ * Schema: GSWD_SPEC.md Section 7.1-7.3
8
+ */
9
+ export declare const SYMBOLS: {
10
+ readonly complete: "✓";
11
+ readonly failed: "✗";
12
+ readonly inProgress: "◆";
13
+ readonly pending: "○";
14
+ readonly autoApproved: "⚡";
15
+ readonly warning: "⚠";
16
+ };
17
+ /**
18
+ * ANSI color codes for GSWD branding.
19
+ * Electric orange/amber is the brand color (per CONTEXT.md locked decision).
20
+ * 256-color code \x1b[38;5;208m; dim uses \x1b[2m.
21
+ */
22
+ export declare const COLORS: {
23
+ readonly orange: "\u001B[38;5;208m";
24
+ readonly dim: "\u001B[2m";
25
+ readonly reset: "\u001B[0m";
26
+ };
27
+ /**
28
+ * Wrap text in ANSI color. No-op when stdout is not a TTY (CI/Docker/pipe safety).
29
+ * Respects FORCE_COLOR env var to override TTY detection.
30
+ */
31
+ export declare function colorize(text: string, color: keyof typeof COLORS): string;
32
+ /**
33
+ * Render the GSWD ASCII logo with catchphrase.
34
+ * Logo shape matches GSD's block-letter style from install.js.
35
+ * Static string — no figlet (STATE.md architectural decision).
36
+ *
37
+ * @param version - Optional version string (e.g., "1.0.1")
38
+ */
39
+ export declare function renderLogo(version?: string): string;
40
+ /**
41
+ * Render the condensed first-run command guide.
42
+ * Per CONTEXT.md: primary commands only, no descriptions.
43
+ * Followed by automatic /gswd:start proposal for zero-friction onboarding.
44
+ */
45
+ export declare function renderFirstRunGuide(): string;
46
+ /**
47
+ * Pad or truncate a string to exact width.
48
+ * If str exceeds width, truncate and append '...' (total = width).
49
+ */
50
+ export declare function padRight(str: string, width: number): string;
51
+ /**
52
+ * Render a stage banner matching GSWD_SPEC Section 7.1.
53
+ *
54
+ * ```
55
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
56
+ * GSWD ► {STAGE NAME}
57
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
58
+ * ```
59
+ *
60
+ * Banner width = 55 characters. Stage name is uppercased.
61
+ */
62
+ export declare function renderBanner(stageName: string): string;
63
+ /**
64
+ * Render a checkpoint box matching GSWD_SPEC Section 7.2.
65
+ *
66
+ * Box total width: 56 characters (54 inner + 2 border characters).
67
+ * Uses single-line box-drawing characters.
68
+ *
69
+ * @param type - Header text (e.g., "Decision Required")
70
+ * @param context - Context paragraph
71
+ * @param options - Numbered options list
72
+ * @param actionPrompt - Action prompt (appears after → )
73
+ */
74
+ export declare function renderCheckpoint(type: string, context: string, options: string[], actionPrompt: string): string;
75
+ /**
76
+ * Render a Next Up block matching GSWD_SPEC Section 7.3.
77
+ *
78
+ * ```
79
+ * ✓ Wrote: file1, file2, ...
80
+ *
81
+ * Next up:
82
+ * {nextCommand}
83
+ *
84
+ * Tip:
85
+ * If context is getting crowded, run /clear.
86
+ * ```
87
+ */
88
+ export declare function renderNextUp(files: string[], nextCommand: string, alsoAvailable?: string[]): string;
89
+ /**
90
+ * Render a single status line with the appropriate symbol.
91
+ *
92
+ * Maps: done/pass -> ✓, fail -> ✗, in_progress -> ◆, not_started -> ○
93
+ */
94
+ export declare function renderStatusLine(stage: string, status: string): string;
95
+ /**
96
+ * Render an ASCII progress bar.
97
+ *
98
+ * ```
99
+ * Progress: ████████░░ 80%
100
+ * ```
101
+ *
102
+ * @param current - Current value
103
+ * @param total - Total value
104
+ * @param width - Bar width in characters (default 10)
105
+ */
106
+ export declare function renderProgressBar(current: number, total: number, width?: number): string;
107
+ /**
108
+ * Input for stage summary rendering.
109
+ * Used at manual-mode checkpoints to show what a stage produced.
110
+ */
111
+ export interface StageSummarySection {
112
+ name: string;
113
+ summary: string;
114
+ }
115
+ export interface StageSummaryInput {
116
+ stageName: string;
117
+ headline: string;
118
+ sections: StageSummarySection[];
119
+ filesCreated: string[];
120
+ }
121
+ /**
122
+ * Render a structured stage summary for manual-mode checkpoints.
123
+ * Consistent template per CONTEXT.md locked decision:
124
+ * Headline -> section paragraphs -> file list
125
+ *
126
+ * ```
127
+ * ## Imagine Complete
128
+ *
129
+ * Imagine produced 3 product directions with ICP, GTM, and competition analysis.
130
+ *
131
+ * **Key outputs:**
132
+ * - ICP Analysis: Identified 2 primary personas with distinct pain points...
133
+ * - GTM Strategy: Recommended freemium launch with enterprise upgrade path...
134
+ *
135
+ * **Created:** IMAGINE.md, ICP.md, GTM.md, COMPETITION.md, DECISIONS.md
136
+ * ```
137
+ */
138
+ /**
139
+ * Research summary section for post-agent display.
140
+ * Mirrors ResearchSummarySection from imagine-synthesis.ts.
141
+ */
142
+ export interface RenderResearchSection {
143
+ displayName: string;
144
+ takeaways: string[];
145
+ bridge: string;
146
+ }
147
+ /**
148
+ * Render structured research summary after agent completion.
149
+ * Product-contextualized: section intros reference user's product.
150
+ * Per CONTEXT.md: 3-5 takeaway bullets per section, bridging paragraph per section.
151
+ *
152
+ * @param sections - Research summary sections from buildResearchSummary
153
+ * @param productContext - Brief description of the product (from brief.vision)
154
+ * @param targetUser - Who the product is for (from brief.target_user)
155
+ */
156
+ export declare function renderResearchSummary(sections: RenderResearchSection[], productContext: string, targetUser: string): string;
157
+ /**
158
+ * Direction card input for rendering.
159
+ */
160
+ export interface RenderDirectionInput {
161
+ label: string;
162
+ rationale: string[];
163
+ icp_summary: string;
164
+ problem_framing: string;
165
+ wedge: string;
166
+ differentiator: string;
167
+ risks: string[];
168
+ }
169
+ /**
170
+ * Render a direction card with "Why this makes sense" rationale section.
171
+ * Per CONTEXT.md: rationale section comes FIRST, before existing fields.
172
+ * Recommended direction gets a visual marker.
173
+ *
174
+ * @param direction - Direction data
175
+ * @param index - 0-based index for display numbering
176
+ * @param isRecommended - Whether this is the proposed/recommended direction
177
+ */
178
+ export declare function renderDirectionCard(direction: RenderDirectionInput, index: number, isRecommended: boolean): string;
179
+ export declare function renderStageSummary(input: StageSummaryInput): string;
180
+ /**
181
+ * Minimal product context for contextual messaging throughout the pipeline.
182
+ * Extracted from INTAKE.json and DECISIONS.md with graceful fallbacks.
183
+ */
184
+ export interface ProductContext {
185
+ /** Product domain (e.g., "spec-writing tool") */
186
+ domain: string;
187
+ /** Target user (e.g., "solo founders") */
188
+ targetUser: string;
189
+ /** Stage-specific context string */
190
+ stageContext: string;
191
+ /** Whether this is a re-run (changes banner messaging) */
192
+ isRerun: boolean;
193
+ /** Re-run feedback context (if re-run) */
194
+ rerunContext?: string;
195
+ }
196
+ /**
197
+ * Extract product context from DECISIONS.md and INTAKE.json.
198
+ * DECISIONS.md is preferred (more precise, available after Imagine).
199
+ * INTAKE.json is fallback (available from pipeline start).
200
+ * Returns safe defaults if neither file exists.
201
+ *
202
+ * @param planningDir - Path to .planning/ directory
203
+ * @param isRerun - Whether this is a re-run
204
+ */
205
+ export declare function extractProductContext(planningDir: string, isRerun?: boolean): ProductContext;
206
+ /**
207
+ * Stage-specific context line generators for contextual banners.
208
+ * Each returns a product-contextualized one-liner for the banner.
209
+ * Compile is deliberately excluded (per CONTEXT.md: deterministic, no banner needed).
210
+ */
211
+ export declare const STAGE_CONTEXT_TEMPLATES: Record<string, (ctx: ProductContext) => string>;
212
+ /**
213
+ * Product-contextualized one-liner descriptions for Imagine research agents.
214
+ * Each function receives ProductContext and returns a purpose string.
215
+ */
216
+ export declare const IMAGINE_AGENT_BRIEFINGS: Record<string, (ctx: ProductContext) => string>;
217
+ /**
218
+ * Product-contextualized one-liner descriptions for Specify agents.
219
+ */
220
+ export declare const SPECIFY_AGENT_BRIEFINGS: Record<string, (ctx: ProductContext) => string>;
221
+ /**
222
+ * Render a stage banner with a product-contextualized line below the title.
223
+ * Extension of renderBanner() for consultant-style messaging (Phase 16).
224
+ *
225
+ * ```
226
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
227
+ * GSWD ► IMAGINE
228
+ * Validating product direction for a spec-writing tool targeting founders
229
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
230
+ * ```
231
+ *
232
+ * @param stageName - Stage name (will be uppercased)
233
+ * @param contextLine - Product-contextualized one-liner
234
+ */
235
+ export declare function renderContextBanner(stageName: string, contextLine: string): string;
236
+ /**
237
+ * Input for agent pre-briefing display.
238
+ */
239
+ export interface AgentBriefingEntry {
240
+ name: string;
241
+ productPurpose: string;
242
+ }
243
+ /**
244
+ * Render agent pre-briefing block before spawning.
245
+ * Technical transparency: name each agent explicitly with product-specific purpose.
246
+ *
247
+ * ```
248
+ * ◆ Spawning 5 research agents:
249
+ * → market-researcher: mapping the spec-tooling landscape to find gaps
250
+ * → icp-persona: profiling founders who struggle with product specification
251
+ * ```
252
+ *
253
+ * @param agents - Array of agent names with product-contextualized purposes
254
+ * @param stageContext - Optional stage context description
255
+ */
256
+ export declare function renderAgentBriefing(agents: AgentBriefingEntry[], stageContext?: string): string;
257
+ /**
258
+ * Render a single agent completion line with its key finding headline.
259
+ *
260
+ * ✓ market-researcher: 3 direct competitors in the spec-tooling space
261
+ * ✗ icp-persona: Agent failed — timeout
262
+ *
263
+ * @param agentName - Agent name
264
+ * @param headline - Key finding or error message
265
+ * @param status - Agent completion status
266
+ */
267
+ export declare function renderAgentCompletion(agentName: string, headline: string, status: 'complete' | 'failed'): string;
268
+ /**
269
+ * Render full agent progress block showing pending and completed agents.
270
+ * Pending agents show ○, completed show ✓ with headline, failed show ✗.
271
+ *
272
+ * ✓ market-researcher: 3 direct competitors found
273
+ * ✓ icp-persona: primary persona is technical founder
274
+ * ○ positioning (pending)
275
+ * ○ brainstorm-alternatives (pending)
276
+ * ○ devils-advocate (pending)
277
+ */
278
+ export declare function renderAgentProgress(agents: Array<{
279
+ name: string;
280
+ status: 'pending' | 'complete' | 'failed';
281
+ headline?: string;
282
+ }>): string;
283
+ /**
284
+ * Render a file inventory table listing all generated files with descriptions.
285
+ * Used at pipeline end to give users a deliverable checklist.
286
+ *
287
+ * @param files - Array of { path, description } objects
288
+ * @returns Markdown table string
289
+ */
290
+ export declare function renderFileInventory(files: Array<{
291
+ path: string;
292
+ description: string;
293
+ }>): string;
294
+ /**
295
+ * Extract a one-line headline from agent output for completion display.
296
+ * Multi-strategy extraction with robust fallback.
297
+ *
298
+ * Strategy priority:
299
+ * 1. First sentence after "## Summary" heading
300
+ * 2. Text after "Key finding:" prefix
301
+ * 3. First non-heading, non-empty line
302
+ * 4. Fallback: "{agentName} analysis complete"
303
+ *
304
+ * Truncates to 80 chars. Never returns empty string.
305
+ *
306
+ * @param agentOutput - Raw agent output content
307
+ * @param agentName - Agent name (for fallback)
308
+ */
309
+ export declare function extractHeadline(agentOutput: string, agentName: string): string;