@prodara/compiler 0.1.0 → 0.1.2
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/README.md +26 -3
- package/dist/cli/agent-setup.d.ts.map +1 -1
- package/dist/cli/agent-setup.js +3 -1
- package/dist/cli/agent-setup.js.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +144 -74
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/ui.d.ts +25 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +115 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/config/config.d.ts +2 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +6 -1
- package/dist/config/config.js.map +1 -1
- package/dist/implement/implement.d.ts +18 -2
- package/dist/implement/implement.d.ts.map +1 -1
- package/dist/implement/implement.js +98 -2
- package/dist/implement/implement.js.map +1 -1
- package/dist/implement/index.d.ts +3 -2
- package/dist/implement/index.d.ts.map +1 -1
- package/dist/implement/index.js +1 -1
- package/dist/implement/index.js.map +1 -1
- package/dist/implement/types.d.ts +5 -0
- package/dist/implement/types.d.ts.map +1 -1
- package/dist/orchestrator/pipeline.d.ts +15 -1
- package/dist/orchestrator/pipeline.d.ts.map +1 -1
- package/dist/orchestrator/pipeline.js +117 -9
- package/dist/orchestrator/pipeline.js.map +1 -1
- package/package.json +5 -2
package/dist/cli/main.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Command } from 'commander';
|
|
|
6
6
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, watch } from 'node:fs';
|
|
7
7
|
import { resolve, dirname, join, relative } from 'node:path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
9
10
|
import { compile } from './compile.js';
|
|
10
11
|
import { runPipeline } from '../orchestrator/pipeline.js';
|
|
11
12
|
import { formatDiagnosticsJson, formatDiagnosticsHuman } from '../diagnostics/reporter.js';
|
|
@@ -19,6 +20,7 @@ import { getStarterTemplate, listStarterTemplates } from './starters.js';
|
|
|
19
20
|
import { explainNode, collectAllNodeIds, formatExplanation, getDiagnosticInfo } from './explain.js';
|
|
20
21
|
import { registerShutdownHandlers, setActiveRoot, clearActiveRoot } from './lifecycle.js';
|
|
21
22
|
import { generateSlashCommands, writeSlashCommands, isValidAgentId, listSupportedAgents, getAgentConfig } from './agent-setup.js';
|
|
23
|
+
import { banner, success, error as uiError, warn as uiWarn, info as uiInfo, phaseIcon, box, table, dim, bold, isInteractive, createSpinner } from './ui.js';
|
|
22
24
|
import { createProposal, listProposals, applyProposal, archiveProposal } from '../proposal/proposal.js';
|
|
23
25
|
import { generateChecklist, formatChecklistHuman } from './checklist.js';
|
|
24
26
|
import { analyzeGraph, formatAnalysisHuman } from './analyze.js';
|
|
@@ -53,7 +55,8 @@ export function createProgram() {
|
|
|
53
55
|
.option('--auto-commit', 'Auto-commit changes after successful build')
|
|
54
56
|
.option('--notify', 'Send desktop notification on build completion')
|
|
55
57
|
.option('--party', 'Run multi-agent party mode review')
|
|
56
|
-
.
|
|
58
|
+
.option('--dry-run', 'Show implementation tasks without executing')
|
|
59
|
+
.action(async (path, opts) => {
|
|
57
60
|
const root = resolve(path);
|
|
58
61
|
registerShutdownHandlers();
|
|
59
62
|
setActiveRoot(root);
|
|
@@ -64,26 +67,38 @@ export function createProgram() {
|
|
|
64
67
|
headless: opts.headless ?? false,
|
|
65
68
|
noImplement: opts.implement === false,
|
|
66
69
|
noReview: opts.review === false,
|
|
70
|
+
dryRun: opts.dryRun ?? false,
|
|
67
71
|
onProgress: (phase, index, total) => {
|
|
68
72
|
if (opts.format !== 'json') {
|
|
69
|
-
process.stderr.write(`[${index + 1}/${total}] ${phase}
|
|
73
|
+
process.stderr.write(`${uiInfo(`[${index + 1}/${total}] ${phase}...`)}\n`);
|
|
70
74
|
}
|
|
71
75
|
},
|
|
72
76
|
};
|
|
73
|
-
|
|
77
|
+
if (opts.format !== 'json') {
|
|
78
|
+
process.stdout.write(banner('Prodara Build') + '\n\n');
|
|
79
|
+
}
|
|
80
|
+
const result = await runPipeline(root, config, pipelineOpts);
|
|
74
81
|
if (opts.format === 'json') {
|
|
75
82
|
process.stdout.write(JSON.stringify({
|
|
76
83
|
status: result.status,
|
|
77
84
|
phases: result.phases.map((p) => ({ phase: p.phase, status: p.status, detail: p.detail, duration_ms: p.duration_ms })),
|
|
78
85
|
duration_ms: result.duration_ms,
|
|
86
|
+
implementResult: result.implementResult ?? undefined,
|
|
79
87
|
}, null, 2) + '\n');
|
|
80
88
|
}
|
|
81
89
|
else {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
const phaseRows = result.phases.map(p => [
|
|
91
|
+
`${phaseIcon(p.status)} ${p.phase}`,
|
|
92
|
+
p.detail,
|
|
93
|
+
dim(`${p.duration_ms}ms`),
|
|
94
|
+
]);
|
|
95
|
+
process.stdout.write(table(['Phase', 'Detail', 'Time'], phaseRows) + '\n\n');
|
|
96
|
+
if (result.status === 'success') {
|
|
97
|
+
process.stdout.write(box('Build Result', [success(`Build completed in ${result.duration_ms}ms`)]) + '\n');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
process.stdout.write(box('Build Result', [uiError(`Build failed (${result.duration_ms}ms)`)]) + '\n');
|
|
85
101
|
}
|
|
86
|
-
process.stdout.write(`\nBuild ${result.status} (${result.duration_ms}ms)\n`);
|
|
87
102
|
}
|
|
88
103
|
clearActiveRoot();
|
|
89
104
|
// Auto-commit after successful build
|
|
@@ -91,7 +106,7 @@ export function createProgram() {
|
|
|
91
106
|
/* v8 ignore next -- graph always exists when status is success */
|
|
92
107
|
const hash = autoCommit(root, result.graph?.product.name ?? 'unknown', result.phases.length);
|
|
93
108
|
if (hash && opts.format !== 'json') {
|
|
94
|
-
process.stdout.write(`Auto-committed: ${hash}\n
|
|
109
|
+
process.stdout.write(success(`Auto-committed: ${hash}`) + '\n');
|
|
95
110
|
}
|
|
96
111
|
}
|
|
97
112
|
// Desktop notification
|
|
@@ -114,17 +129,45 @@ export function createProgram() {
|
|
|
114
129
|
.option('--template <template>', 'Starter template: minimal | saas | marketplace | internal-tool | api')
|
|
115
130
|
.option('--ai <agent>', 'Configure AI agent slash commands (e.g. copilot, claude, cursor)')
|
|
116
131
|
.option('--ai-commands-dir <dir>', 'Custom directory for slash commands (use with --ai generic)')
|
|
117
|
-
.
|
|
132
|
+
.option('--skip-install', 'Skip automatic npm init and compiler installation')
|
|
133
|
+
.action(async (path, opts) => {
|
|
118
134
|
const root = resolve(path);
|
|
119
135
|
const appPrd = join(root, 'app.prd');
|
|
120
136
|
const configFile = join(root, CONFIG_FILENAME);
|
|
121
137
|
const prodaraDir = join(root, '.prodara');
|
|
122
138
|
if (existsSync(appPrd)) {
|
|
123
|
-
process.stderr.write(`Already initialized: ${appPrd} exists\n
|
|
139
|
+
process.stderr.write(uiError(`Already initialized: ${appPrd} exists`) + '\n');
|
|
124
140
|
process.exitCode = 1;
|
|
125
141
|
return;
|
|
126
142
|
}
|
|
143
|
+
process.stdout.write(banner('Prodara Init') + '\n\n');
|
|
127
144
|
mkdirSync(root, { recursive: true });
|
|
145
|
+
// Ensure npm is initialized and compiler is installed
|
|
146
|
+
if (!opts.skipInstall) {
|
|
147
|
+
const pkgJsonPath = join(root, 'package.json');
|
|
148
|
+
if (!existsSync(pkgJsonPath)) {
|
|
149
|
+
const spinner = createSpinner('Initializing npm project...').start();
|
|
150
|
+
try {
|
|
151
|
+
execSync('npm init -y', { cwd: root, stdio: 'pipe' });
|
|
152
|
+
spinner.succeed('Created package.json');
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
spinner.fail('Failed to initialize npm project');
|
|
156
|
+
process.stderr.write(uiError('Run `npm init` manually.') + '\n');
|
|
157
|
+
process.exitCode = 1;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const installSpinner = createSpinner('Installing @prodara/compiler...').start();
|
|
162
|
+
try {
|
|
163
|
+
execSync('npm install --save-dev @prodara/compiler', { cwd: root, stdio: 'pipe' });
|
|
164
|
+
installSpinner.succeed('Installed @prodara/compiler');
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
installSpinner.fail('Failed to install @prodara/compiler');
|
|
168
|
+
process.stderr.write(uiWarn('Install it manually: npm install --save-dev @prodara/compiler') + '\n');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
128
171
|
mkdirSync(prodaraDir, { recursive: true });
|
|
129
172
|
mkdirSync(join(prodaraDir, 'runs'), { recursive: true });
|
|
130
173
|
mkdirSync(join(prodaraDir, 'reviewers'), { recursive: true });
|
|
@@ -132,8 +175,8 @@ export function createProgram() {
|
|
|
132
175
|
const tmpl = getStarterTemplate(opts.template, opts.name);
|
|
133
176
|
if (!tmpl) {
|
|
134
177
|
const available = listStarterTemplates().map(t => t.name).join(', ');
|
|
135
|
-
process.stderr.write(`Unknown template: ${opts.template}\n
|
|
136
|
-
process.stderr.write(`Available templates: ${available}\n
|
|
178
|
+
process.stderr.write(uiError(`Unknown template: ${opts.template}`) + '\n');
|
|
179
|
+
process.stderr.write(uiInfo(`Available templates: ${available}`) + '\n');
|
|
137
180
|
process.exitCode = 1;
|
|
138
181
|
return;
|
|
139
182
|
}
|
|
@@ -142,9 +185,9 @@ export function createProgram() {
|
|
|
142
185
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
143
186
|
writeFileSync(filePath, f.content, 'utf-8');
|
|
144
187
|
}
|
|
145
|
-
process.stdout.write(`Initialized Prodara project
|
|
188
|
+
process.stdout.write(success(`Initialized Prodara project (template: ${tmpl.name})`) + '\n');
|
|
146
189
|
for (const f of tmpl.files) {
|
|
147
|
-
process.stdout.write(`
|
|
190
|
+
process.stdout.write(` ${success(f.path)}\n`);
|
|
148
191
|
}
|
|
149
192
|
}
|
|
150
193
|
else {
|
|
@@ -164,8 +207,7 @@ module core {
|
|
|
164
207
|
|
|
165
208
|
}
|
|
166
209
|
`, 'utf-8');
|
|
167
|
-
process.stdout.write(
|
|
168
|
-
process.stdout.write(` Created app.prd\n`);
|
|
210
|
+
process.stdout.write(success('Created app.prd') + '\n');
|
|
169
211
|
}
|
|
170
212
|
writeFileSync(configFile, JSON.stringify({
|
|
171
213
|
phases: {},
|
|
@@ -174,20 +216,39 @@ module core {
|
|
|
174
216
|
validation: { lint: null, typecheck: null, test: null, build: null },
|
|
175
217
|
audit: { enabled: true, path: '.prodara/runs/' },
|
|
176
218
|
}, null, 2) + '\n', 'utf-8');
|
|
177
|
-
process.stdout.write(`
|
|
178
|
-
process.stdout.write(
|
|
179
|
-
// Slash command generation
|
|
219
|
+
process.stdout.write(success(`Created ${CONFIG_FILENAME}`) + '\n');
|
|
220
|
+
process.stdout.write(success('Created .prodara/') + '\n');
|
|
221
|
+
// Slash command generation — interactive prompt if --ai not provided
|
|
222
|
+
let agentId = null;
|
|
180
223
|
if (opts.ai) {
|
|
181
224
|
if (!isValidAgentId(opts.ai)) {
|
|
182
225
|
const supported = listSupportedAgents().join(', ');
|
|
183
|
-
process.stderr.write(`Unknown AI agent: ${opts.ai}\n
|
|
184
|
-
process.stderr.write(`Supported agents: ${supported}\n
|
|
226
|
+
process.stderr.write(uiError(`Unknown AI agent: ${opts.ai}`) + '\n');
|
|
227
|
+
process.stderr.write(uiInfo(`Supported agents: ${supported}`) + '\n');
|
|
185
228
|
process.exitCode = 1;
|
|
186
229
|
return;
|
|
187
230
|
}
|
|
188
|
-
|
|
231
|
+
agentId = opts.ai;
|
|
232
|
+
}
|
|
233
|
+
else if (isInteractive()) {
|
|
234
|
+
const { select } = await import('@inquirer/prompts');
|
|
235
|
+
const agents = listSupportedAgents();
|
|
236
|
+
const popular = ['copilot', 'claude', 'cursor', 'gemini'];
|
|
237
|
+
const others = agents.filter(a => !popular.includes(a) && a !== 'generic');
|
|
238
|
+
const choices = [
|
|
239
|
+
...popular.map(a => ({ name: a.charAt(0).toUpperCase() + a.slice(1), value: a })),
|
|
240
|
+
...others.map(a => ({ name: a.charAt(0).toUpperCase() + a.slice(1), value: a })),
|
|
241
|
+
{ name: 'Generic', value: 'generic' },
|
|
242
|
+
{ name: 'Skip (no agent setup)', value: 'skip' },
|
|
243
|
+
];
|
|
244
|
+
const selected = await select({ message: 'Which AI agent do you use?', choices });
|
|
245
|
+
if (selected !== 'skip') {
|
|
246
|
+
agentId = selected;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (agentId) {
|
|
189
250
|
if (agentId === 'generic' && !opts.aiCommandsDir) {
|
|
190
|
-
process.stderr.write(
|
|
251
|
+
process.stderr.write(uiError('--ai generic requires --ai-commands-dir <dir>') + '\n');
|
|
191
252
|
process.exitCode = 1;
|
|
192
253
|
return;
|
|
193
254
|
}
|
|
@@ -196,13 +257,17 @@ module core {
|
|
|
196
257
|
const agentConfig = getAgentConfig(agentId);
|
|
197
258
|
/* v8 ignore next -- triple fallback for command dir */
|
|
198
259
|
const commandsDir = opts.aiCommandsDir ?? agentConfig?.commandsDir ?? '.ai/commands';
|
|
199
|
-
process.stdout.write(`
|
|
260
|
+
process.stdout.write(success(`Created slash commands in ${commandsDir}/`) + '\n');
|
|
200
261
|
for (const cmd of commands) {
|
|
201
262
|
const rel = relative(root, cmd.path);
|
|
202
|
-
process.stdout.write(`
|
|
263
|
+
process.stdout.write(` ${dim(rel)}\n`);
|
|
203
264
|
}
|
|
204
265
|
}
|
|
205
|
-
process.stdout.write(
|
|
266
|
+
process.stdout.write('\n' + box('Next Steps', [
|
|
267
|
+
`${bold('cd')} ${root === resolve('.') ? '.' : root}`,
|
|
268
|
+
`${bold('prodara build')} — compile and validate your spec`,
|
|
269
|
+
`${bold('prodara doctor')} — check your installation`,
|
|
270
|
+
]) + '\n');
|
|
206
271
|
process.exitCode = 0;
|
|
207
272
|
});
|
|
208
273
|
// -----------------------------------------------------------------------
|
|
@@ -217,6 +282,9 @@ module core {
|
|
|
217
282
|
const root = resolve(path);
|
|
218
283
|
const result = compile(root, { stopAfter: 'validate' });
|
|
219
284
|
outputDiagnostics(result.diagnostics, opts.format);
|
|
285
|
+
if (!result.diagnostics.hasErrors && opts.format !== 'json') {
|
|
286
|
+
process.stdout.write(success('No errors found') + '\n');
|
|
287
|
+
}
|
|
220
288
|
process.exitCode = result.diagnostics.hasErrors ? 1 : 0;
|
|
221
289
|
});
|
|
222
290
|
// -----------------------------------------------------------------------
|
|
@@ -240,7 +308,7 @@ module core {
|
|
|
240
308
|
if (opts.output) {
|
|
241
309
|
writeFileSync(resolve(opts.output), result.graphJson, 'utf-8');
|
|
242
310
|
if (opts.format !== 'json') {
|
|
243
|
-
process.stderr.write(`Graph written to ${opts.output}\n
|
|
311
|
+
process.stderr.write(success(`Graph written to ${opts.output}`) + '\n');
|
|
244
312
|
}
|
|
245
313
|
}
|
|
246
314
|
else {
|
|
@@ -292,7 +360,7 @@ module core {
|
|
|
292
360
|
const r = result.testResults;
|
|
293
361
|
process.stdout.write(`Tests: ${r.totalPassed} passed, ${r.totalFailed} failed\n`);
|
|
294
362
|
for (const t of r.results) {
|
|
295
|
-
const icon = t.passed ? '
|
|
363
|
+
const icon = t.passed ? phaseIcon('ok') : phaseIcon('error');
|
|
296
364
|
process.stdout.write(` ${icon} ${t.name}\n`);
|
|
297
365
|
for (const f of t.failures) {
|
|
298
366
|
process.stdout.write(` ${f}\n`);
|
|
@@ -328,7 +396,7 @@ module core {
|
|
|
328
396
|
if (opts.semantic) {
|
|
329
397
|
const prevGraph = readPreviousGraph(root);
|
|
330
398
|
if (!prevGraph) {
|
|
331
|
-
process.stdout.write('No previous graph found — nothing to diff
|
|
399
|
+
process.stdout.write(uiInfo('No previous graph found — nothing to diff.') + '\n');
|
|
332
400
|
process.exitCode = 0;
|
|
333
401
|
return;
|
|
334
402
|
}
|
|
@@ -370,12 +438,14 @@ module core {
|
|
|
370
438
|
.action((path) => {
|
|
371
439
|
const root = resolve(path);
|
|
372
440
|
const files = discoverFiles(root);
|
|
373
|
-
process.stdout.write(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
441
|
+
process.stdout.write(box('Prodara Doctor', [
|
|
442
|
+
`Compiler ${bold(`v${pkg.version}`)}`,
|
|
443
|
+
`Node.js ${bold(process.version)}`,
|
|
444
|
+
`Workspace ${root}`,
|
|
445
|
+
`Found ${bold(String(files.length))} .prd file(s)`,
|
|
446
|
+
]) + '\n');
|
|
377
447
|
for (const f of files) {
|
|
378
|
-
process.stdout.write(` ${f}\n`);
|
|
448
|
+
process.stdout.write(` ${dim(f)}\n`);
|
|
379
449
|
}
|
|
380
450
|
process.exitCode = 0;
|
|
381
451
|
});
|
|
@@ -416,7 +486,7 @@ module core {
|
|
|
416
486
|
const result = compile(root, { stopAfter: 'test' });
|
|
417
487
|
outputDiagnostics(result.diagnostics, opts.format);
|
|
418
488
|
if (!result.diagnostics.hasErrors) {
|
|
419
|
-
process.stdout.write('Build OK\n');
|
|
489
|
+
process.stdout.write(success('Build OK') + '\n');
|
|
420
490
|
}
|
|
421
491
|
};
|
|
422
492
|
// Initial build
|
|
@@ -463,12 +533,12 @@ module core {
|
|
|
463
533
|
}
|
|
464
534
|
const explanation = explainNode(result.graph, nodeId);
|
|
465
535
|
if (!explanation) {
|
|
466
|
-
process.stderr.write(`Node not found: ${nodeId}\n
|
|
536
|
+
process.stderr.write(uiError(`Node not found: ${nodeId}`) + '\n');
|
|
467
537
|
const allIds = collectAllNodeIds(result.graph);
|
|
468
538
|
if (allIds.size > 0) {
|
|
469
539
|
const matches = [...allIds].filter(id => id.includes(/* v8 ignore next -- pop() on non-empty array */ nodeId.split('.').pop() ?? ''));
|
|
470
540
|
if (matches.length > 0) {
|
|
471
|
-
process.stderr.write(`Did you mean: ${matches.slice(0, 5).join(', ')}
|
|
541
|
+
process.stderr.write(uiInfo(`Did you mean: ${matches.slice(0, 5).join(', ')}?`) + '\n');
|
|
472
542
|
}
|
|
473
543
|
}
|
|
474
544
|
process.exitCode = 1;
|
|
@@ -492,11 +562,11 @@ module core {
|
|
|
492
562
|
.action((code) => {
|
|
493
563
|
const info = getDiagnosticInfo(code);
|
|
494
564
|
if (!info) {
|
|
495
|
-
process.stderr.write(`Unknown diagnostic code: ${code}\n
|
|
565
|
+
process.stderr.write(uiError(`Unknown diagnostic code: ${code}`) + '\n');
|
|
496
566
|
process.exitCode = 1;
|
|
497
567
|
return;
|
|
498
568
|
}
|
|
499
|
-
process.stdout.write(`${code}: ${info.title}\n`);
|
|
569
|
+
process.stdout.write(`${bold(code)}: ${info.title}\n`);
|
|
500
570
|
process.stdout.write(` Phase: ${info.phase}\n`);
|
|
501
571
|
process.stdout.write(` Severity: ${info.severity}\n`);
|
|
502
572
|
process.stdout.write(` ${info.description}\n`);
|
|
@@ -514,15 +584,15 @@ module core {
|
|
|
514
584
|
const root = resolve(path);
|
|
515
585
|
try {
|
|
516
586
|
const proposal = createProposal(description, root);
|
|
517
|
-
process.stdout.write(`Created change proposal: ${proposal.name}\n
|
|
518
|
-
process.stdout.write(` Directory: ${relative(root, proposal.path)}/\n`);
|
|
587
|
+
process.stdout.write(success(`Created change proposal: ${proposal.name}`) + '\n');
|
|
588
|
+
process.stdout.write(` Directory: ${dim(relative(root, proposal.path))}/\n`);
|
|
519
589
|
process.stdout.write(` delta.prd: Edit to define specification changes\n`);
|
|
520
590
|
process.stdout.write(` proposal.md: Document motivation and scope\n`);
|
|
521
591
|
process.stdout.write(` tasks.md: Track implementation tasks\n`);
|
|
522
592
|
process.exitCode = 0;
|
|
523
593
|
}
|
|
524
594
|
catch (e) {
|
|
525
|
-
process.stderr.write(
|
|
595
|
+
process.stderr.write(uiError(e.message) + '\n');
|
|
526
596
|
process.exitCode = 1;
|
|
527
597
|
}
|
|
528
598
|
});
|
|
@@ -541,11 +611,11 @@ module core {
|
|
|
541
611
|
process.stdout.write(JSON.stringify(proposals, null, 2) + '\n');
|
|
542
612
|
}
|
|
543
613
|
else if (proposals.length === 0) {
|
|
544
|
-
process.stdout.write('No active change proposals
|
|
545
|
-
process.stdout.write('Run `prodara propose "<description>"` to create one
|
|
614
|
+
process.stdout.write(uiInfo('No active change proposals.') + '\n');
|
|
615
|
+
process.stdout.write(dim('Run `prodara propose "<description>"` to create one.') + '\n');
|
|
546
616
|
}
|
|
547
617
|
else {
|
|
548
|
-
process.stdout.write(`Active change proposals (${proposals.length})
|
|
618
|
+
process.stdout.write(bold(`Active change proposals (${proposals.length}):`) + '\n\n');
|
|
549
619
|
for (const p of proposals) {
|
|
550
620
|
/* v8 ignore next -- proposal status ternary */
|
|
551
621
|
const icon = p.status === 'proposed' ? '○' : p.status === 'in-progress' ? '◐' : '●';
|
|
@@ -571,7 +641,7 @@ module core {
|
|
|
571
641
|
const proposal = applyProposal(change, root);
|
|
572
642
|
const deltaPath = join(proposal.path, 'delta.prd');
|
|
573
643
|
if (!existsSync(deltaPath)) {
|
|
574
|
-
process.stderr.write(`No delta.prd found in proposal: ${change}\n
|
|
644
|
+
process.stderr.write(uiError(`No delta.prd found in proposal: ${change}`) + '\n');
|
|
575
645
|
process.exitCode = 1;
|
|
576
646
|
return;
|
|
577
647
|
}
|
|
@@ -583,8 +653,8 @@ module core {
|
|
|
583
653
|
process.exitCode = 1;
|
|
584
654
|
return;
|
|
585
655
|
}
|
|
586
|
-
process.stdout.write(`Applied change proposal: ${proposal.name}\n
|
|
587
|
-
process.stdout.write(` Status: ${proposal.status}\n`);
|
|
656
|
+
process.stdout.write(success(`Applied change proposal: ${proposal.name}`) + '\n');
|
|
657
|
+
process.stdout.write(` Status: ${dim(proposal.status)}\n`);
|
|
588
658
|
if (result.graph) {
|
|
589
659
|
const prevGraph = readPreviousGraph(root);
|
|
590
660
|
if (prevGraph) {
|
|
@@ -600,7 +670,7 @@ module core {
|
|
|
600
670
|
process.exitCode = 0;
|
|
601
671
|
}
|
|
602
672
|
catch (e) {
|
|
603
|
-
process.stderr.write(
|
|
673
|
+
process.stderr.write(uiError(e.message) + '\n');
|
|
604
674
|
process.exitCode = 1;
|
|
605
675
|
}
|
|
606
676
|
});
|
|
@@ -616,12 +686,12 @@ module core {
|
|
|
616
686
|
const root = resolve(path);
|
|
617
687
|
try {
|
|
618
688
|
const proposal = archiveProposal(change, root);
|
|
619
|
-
process.stdout.write(`Archived change proposal: ${proposal.name}\n
|
|
620
|
-
process.stdout.write(` Moved to: ${relative(root, proposal.path)}/\n`);
|
|
689
|
+
process.stdout.write(success(`Archived change proposal: ${proposal.name}`) + '\n');
|
|
690
|
+
process.stdout.write(` Moved to: ${dim(relative(root, proposal.path))}/\n`);
|
|
621
691
|
process.exitCode = 0;
|
|
622
692
|
}
|
|
623
693
|
catch (e) {
|
|
624
|
-
process.stderr.write(
|
|
694
|
+
process.stderr.write(uiError(e.message) + '\n');
|
|
625
695
|
process.exitCode = 1;
|
|
626
696
|
}
|
|
627
697
|
});
|
|
@@ -654,14 +724,14 @@ module core {
|
|
|
654
724
|
const resolved = autoResolveClarifications(clarifyResult.data.questions, config.phases.clarify.ambiguityThreshold, result.graph);
|
|
655
725
|
questions = resolved.needsInput;
|
|
656
726
|
if (opts.format !== 'json') {
|
|
657
|
-
process.stdout.write(`Auto-resolved ${resolved.autoResolved.length} question(s)\n\n
|
|
727
|
+
process.stdout.write(success(`Auto-resolved ${resolved.autoResolved.length} question(s)`) + '\n\n');
|
|
658
728
|
}
|
|
659
729
|
}
|
|
660
730
|
if (opts.format === 'json') {
|
|
661
731
|
const output = JSON.stringify({ questions, total: questions.length }, null, 2);
|
|
662
732
|
if (opts.output) {
|
|
663
733
|
writeFileSync(resolve(opts.output), output + '\n', 'utf-8');
|
|
664
|
-
process.stdout.write(`Questions written to ${opts.output}\n
|
|
734
|
+
process.stdout.write(success(`Questions written to ${opts.output}`) + '\n');
|
|
665
735
|
}
|
|
666
736
|
else {
|
|
667
737
|
process.stdout.write(output + '\n');
|
|
@@ -669,10 +739,10 @@ module core {
|
|
|
669
739
|
}
|
|
670
740
|
else {
|
|
671
741
|
if (questions.length === 0) {
|
|
672
|
-
process.stdout.write('No ambiguities found in the specification
|
|
742
|
+
process.stdout.write(success('No ambiguities found in the specification.') + '\n');
|
|
673
743
|
}
|
|
674
744
|
else {
|
|
675
|
-
process.stdout.write(`Found ${questions.length} ambiguity question(s)
|
|
745
|
+
process.stdout.write(bold(`Found ${questions.length} ambiguity question(s):`) + '\n\n');
|
|
676
746
|
for (let i = 0; i < questions.length; i++) {
|
|
677
747
|
const q = questions[i];
|
|
678
748
|
process.stdout.write(` ${i + 1}. [${q.category}] ${q.question}\n`);
|
|
@@ -683,7 +753,7 @@ module core {
|
|
|
683
753
|
if (opts.output) {
|
|
684
754
|
const output = questions.map((q, i) => `${i + 1}. [${q.category}] ${q.question}`).join('\n');
|
|
685
755
|
writeFileSync(resolve(opts.output), output + '\n', 'utf-8');
|
|
686
|
-
process.stdout.write(`Questions written to ${opts.output}\n
|
|
756
|
+
process.stdout.write(success(`Questions written to ${opts.output}`) + '\n');
|
|
687
757
|
}
|
|
688
758
|
}
|
|
689
759
|
process.exitCode = 0;
|
|
@@ -712,7 +782,7 @@ module core {
|
|
|
712
782
|
const output = JSON.stringify(checklist, null, 2);
|
|
713
783
|
if (opts.output) {
|
|
714
784
|
writeFileSync(resolve(opts.output), output + '\n', 'utf-8');
|
|
715
|
-
process.stdout.write(`Checklist written to ${opts.output}\n
|
|
785
|
+
process.stdout.write(success(`Checklist written to ${opts.output}`) + '\n');
|
|
716
786
|
}
|
|
717
787
|
else {
|
|
718
788
|
process.stdout.write(output + '\n');
|
|
@@ -722,7 +792,7 @@ module core {
|
|
|
722
792
|
const output = formatChecklistHuman(checklist);
|
|
723
793
|
if (opts.output) {
|
|
724
794
|
writeFileSync(resolve(opts.output), output + '\n', 'utf-8');
|
|
725
|
-
process.stdout.write(`Checklist written to ${opts.output}\n
|
|
795
|
+
process.stdout.write(success(`Checklist written to ${opts.output}`) + '\n');
|
|
726
796
|
}
|
|
727
797
|
else {
|
|
728
798
|
process.stdout.write(output + '\n');
|
|
@@ -797,7 +867,7 @@ module core {
|
|
|
797
867
|
const output = JSON.stringify(data, null, 2);
|
|
798
868
|
if (opts.output) {
|
|
799
869
|
writeFileSync(resolve(opts.output), output + '\n', 'utf-8');
|
|
800
|
-
process.stdout.write(`Onboarding doc written to ${opts.output}\n
|
|
870
|
+
process.stdout.write(success(`Onboarding doc written to ${opts.output}`) + '\n');
|
|
801
871
|
}
|
|
802
872
|
else {
|
|
803
873
|
process.stdout.write(output + '\n');
|
|
@@ -841,7 +911,7 @@ module core {
|
|
|
841
911
|
const output = lines.join('\n');
|
|
842
912
|
if (opts.output) {
|
|
843
913
|
writeFileSync(resolve(opts.output), output + '\n', 'utf-8');
|
|
844
|
-
process.stdout.write(`Onboarding doc written to ${opts.output}\n
|
|
914
|
+
process.stdout.write(success(`Onboarding doc written to ${opts.output}`) + '\n');
|
|
845
915
|
}
|
|
846
916
|
else {
|
|
847
917
|
process.stdout.write(output + '\n');
|
|
@@ -878,13 +948,13 @@ module core {
|
|
|
878
948
|
process.stdout.write(JSON.stringify(records, null, 2) + '\n');
|
|
879
949
|
}
|
|
880
950
|
else if (records.length === 0) {
|
|
881
|
-
process.stdout.write('No build history recorded
|
|
882
|
-
process.stdout.write('Run `prodara build` to create a build record
|
|
951
|
+
process.stdout.write(uiInfo('No build history recorded.') + '\n');
|
|
952
|
+
process.stdout.write(dim('Run `prodara build` to create a build record.') + '\n');
|
|
883
953
|
}
|
|
884
954
|
else {
|
|
885
|
-
process.stdout.write(`Build History (last ${records.length})
|
|
955
|
+
process.stdout.write(bold(`Build History (last ${records.length}):`) + '\n\n');
|
|
886
956
|
for (const r of records) {
|
|
887
|
-
const icon = r.outcome === 'success' ? '
|
|
957
|
+
const icon = r.outcome === 'success' ? phaseIcon('ok') : phaseIcon('error');
|
|
888
958
|
const date = new Date(r.timestamp).toLocaleString();
|
|
889
959
|
const phaseSummary = r.phases.map(p => `${p.name}:${p.status}`).join(' ');
|
|
890
960
|
process.stdout.write(` ${icon} ${date} — ${r.outcome}\n`);
|
|
@@ -910,10 +980,10 @@ module core {
|
|
|
910
980
|
const root = resolve(path);
|
|
911
981
|
const extensions = listInstalledExtensions(root);
|
|
912
982
|
if (extensions.length === 0) {
|
|
913
|
-
process.stdout.write('No extensions installed
|
|
983
|
+
process.stdout.write(uiInfo('No extensions installed.') + '\n');
|
|
914
984
|
}
|
|
915
985
|
else {
|
|
916
|
-
process.stdout.write(`Installed extensions (${extensions.length})
|
|
986
|
+
process.stdout.write(bold(`Installed extensions (${extensions.length}):`) + '\n\n');
|
|
917
987
|
for (const ext of extensions) {
|
|
918
988
|
const caps = ext.manifest.capabilities.map(c => c.kind).join(', ');
|
|
919
989
|
process.stdout.write(` ${ext.manifest.name}@${ext.manifest.version} — ${ext.manifest.description}\n`);
|
|
@@ -931,7 +1001,7 @@ module core {
|
|
|
931
1001
|
const root = resolve(path);
|
|
932
1002
|
const manifest = JSON.parse(readFileSync(resolve(manifestPath), 'utf-8'));
|
|
933
1003
|
const installed = installExtension(root, manifest.name, manifest);
|
|
934
|
-
process.stdout.write(`Installed extension "${manifest.name}" at ${installed.path}\n
|
|
1004
|
+
process.stdout.write(success(`Installed extension "${manifest.name}" at ${installed.path}`) + '\n');
|
|
935
1005
|
process.exitCode = 0;
|
|
936
1006
|
});
|
|
937
1007
|
ext
|
|
@@ -942,7 +1012,7 @@ module core {
|
|
|
942
1012
|
.action((name, path) => {
|
|
943
1013
|
const root = resolve(path);
|
|
944
1014
|
removeExtension(root, name);
|
|
945
|
-
process.stdout.write(`Removed extension "${name}"
|
|
1015
|
+
process.stdout.write(success(`Removed extension "${name}".`) + '\n');
|
|
946
1016
|
process.exitCode = 0;
|
|
947
1017
|
});
|
|
948
1018
|
// ---------------------------------------------------------------------------
|
|
@@ -959,10 +1029,10 @@ module core {
|
|
|
959
1029
|
const root = resolve(path);
|
|
960
1030
|
const presets = loadPresets(root);
|
|
961
1031
|
if (presets.length === 0) {
|
|
962
|
-
process.stdout.write('No presets installed
|
|
1032
|
+
process.stdout.write(uiInfo('No presets installed.') + '\n');
|
|
963
1033
|
}
|
|
964
1034
|
else {
|
|
965
|
-
process.stdout.write(`Installed presets (${presets.length})
|
|
1035
|
+
process.stdout.write(bold(`Installed presets (${presets.length}):`) + '\n\n');
|
|
966
1036
|
for (const p of presets) {
|
|
967
1037
|
process.stdout.write(` ${p.manifest.name}@${p.manifest.version} — ${p.manifest.description}\n`);
|
|
968
1038
|
process.stdout.write(` Priority: ${p.priority}\n\n`);
|
|
@@ -979,7 +1049,7 @@ module core {
|
|
|
979
1049
|
const root = resolve(path);
|
|
980
1050
|
const manifest = JSON.parse(readFileSync(resolve(manifestPath), 'utf-8'));
|
|
981
1051
|
const installed = installPreset(root, manifest.name, manifest);
|
|
982
|
-
process.stdout.write(`Installed preset "${manifest.name}" at ${installed.path}\n
|
|
1052
|
+
process.stdout.write(success(`Installed preset "${manifest.name}" at ${installed.path}`) + '\n');
|
|
983
1053
|
process.exitCode = 0;
|
|
984
1054
|
});
|
|
985
1055
|
pst
|
|
@@ -990,7 +1060,7 @@ module core {
|
|
|
990
1060
|
.action((name, path) => {
|
|
991
1061
|
const root = resolve(path);
|
|
992
1062
|
removePreset(root, name);
|
|
993
|
-
process.stdout.write(`Removed preset "${name}"
|
|
1063
|
+
process.stdout.write(success(`Removed preset "${name}".`) + '\n');
|
|
994
1064
|
process.exitCode = 0;
|
|
995
1065
|
});
|
|
996
1066
|
// ---------------------------------------------------------------------------
|
|
@@ -1015,7 +1085,7 @@ module core {
|
|
|
1015
1085
|
: config.docs;
|
|
1016
1086
|
const files = generateDocs(result.graph, docsConfig, root);
|
|
1017
1087
|
writeDocs(files, docsConfig.outputDir, root);
|
|
1018
|
-
process.stdout.write(`Generated ${files.length} doc file(s) in ${docsConfig.outputDir}
|
|
1088
|
+
process.stdout.write(success(`Generated ${files.length} doc file(s) in ${docsConfig.outputDir}/`) + '\n');
|
|
1019
1089
|
process.exitCode = 0;
|
|
1020
1090
|
});
|
|
1021
1091
|
// ---------------------------------------------------------------------------
|