@productbrain/cli 0.1.0-beta.30 → 0.1.0-beta.32
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 +62 -178
- package/dist/__tests__/constants.test.js +1 -1
- package/dist/__tests__/constants.test.js.map +1 -1
- package/dist/__tests__/errors.test.d.ts +2 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.js +117 -0
- package/dist/__tests__/errors.test.js.map +1 -0
- package/dist/__tests__/glossary.test.d.ts +2 -0
- package/dist/__tests__/glossary.test.d.ts.map +1 -0
- package/dist/__tests__/glossary.test.js +32 -0
- package/dist/__tests__/glossary.test.js.map +1 -0
- package/dist/__tests__/login.test.js +1 -1
- package/dist/__tests__/login.test.js.map +1 -1
- package/dist/__tests__/profiles.test.d.ts +2 -0
- package/dist/__tests__/profiles.test.d.ts.map +1 -0
- package/dist/__tests__/profiles.test.js +168 -0
- package/dist/__tests__/profiles.test.js.map +1 -0
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +3 -2
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/doctor.d.ts +9 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +112 -25
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.d.ts +2 -1
- package/dist/commands/doctor.test.d.ts.map +1 -1
- package/dist/commands/doctor.test.js +166 -3
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/profile.d.ts +24 -0
- package/dist/commands/profile.d.ts.map +1 -0
- package/dist/commands/profile.js +82 -0
- package/dist/commands/profile.js.map +1 -0
- package/dist/commands/promote.d.ts.map +1 -1
- package/dist/commands/promote.js +3 -2
- package/dist/commands/promote.js.map +1 -1
- package/dist/formatters/promote.d.ts +1 -0
- package/dist/formatters/promote.d.ts.map +1 -1
- package/dist/formatters/promote.js +1 -0
- package/dist/formatters/promote.js.map +1 -1
- package/dist/index.js +240 -299
- package/dist/index.js.map +1 -1
- package/dist/lib/client.d.ts +2 -2
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/client.js +30 -11
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/config.d.ts +9 -3
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +49 -12
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/constants.d.ts +1 -1
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +1 -1
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/errors.d.ts +57 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +65 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/glossary.d.ts +19 -0
- package/dist/lib/glossary.d.ts.map +1 -0
- package/dist/lib/glossary.js +53 -0
- package/dist/lib/glossary.js.map +1 -0
- package/dist/lib/profiles.d.ts +34 -0
- package/dist/lib/profiles.d.ts.map +1 -0
- package/dist/lib/profiles.js +173 -0
- package/dist/lib/profiles.js.map +1 -0
- package/dist/lib/runner.d.ts +2 -0
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +33 -4
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/style.d.ts +65 -0
- package/dist/lib/style.d.ts.map +1 -0
- package/dist/lib/style.js +108 -0
- package/dist/lib/style.js.map +1 -0
- package/dist/lib/style.test.d.ts +7 -0
- package/dist/lib/style.test.d.ts.map +1 -0
- package/dist/lib/style.test.js +195 -0
- package/dist/lib/style.test.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
import { readFileSync } from 'node:fs';
|
|
8
8
|
import { dirname, join } from 'node:path';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
|
-
import { Command } from 'commander';
|
|
10
|
+
import { Command, CommanderError } from 'commander';
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
const cliPackageVersion = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
13
|
-
import { setOutputMode } from './lib/runner.js';
|
|
13
|
+
import { setOutputMode, setQuietMode, isJsonMode } from './lib/runner.js';
|
|
14
|
+
import { CLIError, ErrorCode } from './lib/errors.js';
|
|
14
15
|
import { runContext } from './commands/context.js';
|
|
15
16
|
import { runGet, runGetMany } from './commands/get.js';
|
|
16
17
|
import { runLogin } from './commands/login.js';
|
|
@@ -39,14 +40,67 @@ import { runVerify } from './commands/verify.js';
|
|
|
39
40
|
import { runCodexPrep } from './commands/codex-prep.js';
|
|
40
41
|
import { runCollectionsList, runCollectionsGet, runCollectionsAudit, runCollectionsExport } from './commands/collections.js';
|
|
41
42
|
import { runDoctor } from './commands/doctor.js';
|
|
43
|
+
import { runProfileList, runProfileCreate, runProfileUse, runProfileDelete } from './commands/profile.js';
|
|
44
|
+
import { GLOSSARY, formatGlossary } from './lib/glossary.js';
|
|
45
|
+
import { hint, heading, bold } from './lib/style.js';
|
|
42
46
|
const program = new Command();
|
|
47
|
+
/**
|
|
48
|
+
* Global error handler — single exit point for all CLI errors.
|
|
49
|
+
* CLIError: format with code + category + guidance.
|
|
50
|
+
* CommanderError: re-throw for --help / --version (exitCode 0), format others.
|
|
51
|
+
* Regular Error: wrap as INTERNAL.
|
|
52
|
+
*/
|
|
53
|
+
function handleError(err) {
|
|
54
|
+
// Commander help/version exits with code 0 — let them through
|
|
55
|
+
if (err instanceof CommanderError && err.exitCode === 0) {
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
const json = isJsonMode();
|
|
59
|
+
if (err instanceof CLIError) {
|
|
60
|
+
if (json) {
|
|
61
|
+
process.stderr.write(JSON.stringify(err.toJSON()) + '\n');
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
process.stderr.write(err.message + '\n');
|
|
65
|
+
if (err.guidance) {
|
|
66
|
+
process.stderr.write(err.guidance + '\n');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
// CommanderError (bad usage, missing args, etc.)
|
|
72
|
+
if (err instanceof CommanderError) {
|
|
73
|
+
const cliErr = new CLIError(err.message, {
|
|
74
|
+
code: ErrorCode.VALIDATION_FAILED,
|
|
75
|
+
category: 'validation',
|
|
76
|
+
});
|
|
77
|
+
if (json) {
|
|
78
|
+
process.stderr.write(JSON.stringify(cliErr.toJSON()) + '\n');
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
process.stderr.write(err.message + '\n');
|
|
82
|
+
}
|
|
83
|
+
process.exit(err.exitCode);
|
|
84
|
+
}
|
|
85
|
+
// Unknown / unstructured errors
|
|
86
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
87
|
+
if (json) {
|
|
88
|
+
process.stderr.write(JSON.stringify({ error: message, code: 'INTERNAL', category: 'internal' }) + '\n');
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
process.stderr.write(message + '\n');
|
|
92
|
+
}
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
43
95
|
program
|
|
44
96
|
.name('pb')
|
|
45
97
|
.description('Product Brain — Chain knowledge + write-back CLI')
|
|
46
98
|
.version(cliPackageVersion.version)
|
|
47
99
|
// Global output mode flags (DEC-299, BET-181)
|
|
48
100
|
.option('--json', 'Output machine-readable JSON (overrides TTY auto-detection)')
|
|
49
|
-
.option('--pretty', 'Force human-readable output even when piped')
|
|
101
|
+
.option('--pretty', 'Force human-readable output even when piped')
|
|
102
|
+
.option('-q, --quiet', 'Suppress non-essential output (banners, hints, progress)')
|
|
103
|
+
.exitOverride(); // Throw CommanderError instead of calling process.exit directly
|
|
50
104
|
// Apply global output mode BEFORE subcommand actions run
|
|
51
105
|
program.hook('preAction', (thisCommand) => {
|
|
52
106
|
const globalOpts = program.opts();
|
|
@@ -57,8 +111,43 @@ program.hook('preAction', (thisCommand) => {
|
|
|
57
111
|
setOutputMode('pretty');
|
|
58
112
|
}
|
|
59
113
|
// else: 'auto' (default) — TTY detection handles it
|
|
114
|
+
if (globalOpts.quiet) {
|
|
115
|
+
setQuietMode(true);
|
|
116
|
+
}
|
|
60
117
|
void thisCommand;
|
|
61
118
|
});
|
|
119
|
+
// Default action — contextual guidance when `pb` is invoked with no command.
|
|
120
|
+
// `pb --help` still works because Commander intercepts --help before the action runs.
|
|
121
|
+
program.action(() => {
|
|
122
|
+
// If in JSON mode, output structured data
|
|
123
|
+
if (isJsonMode()) {
|
|
124
|
+
process.stdout.write(JSON.stringify({ hint: 'Run pb --help for available commands.' }) + '\n');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const apiKey = process.env.PRODUCTBRAIN_API_KEY ?? '';
|
|
128
|
+
const hasKey = apiKey.startsWith('pb_sk_');
|
|
129
|
+
process.stdout.write('\n');
|
|
130
|
+
process.stdout.write(`${bold('Product Brain')}\n\n`);
|
|
131
|
+
if (!hasKey) {
|
|
132
|
+
process.stdout.write(`${heading('Get started')}\n\n`);
|
|
133
|
+
hint('pb login Save your API key');
|
|
134
|
+
hint('pb doctor Check configuration');
|
|
135
|
+
hint('pb setup Guided first-time setup');
|
|
136
|
+
process.stdout.write('\n');
|
|
137
|
+
hint('pb --help Show all commands');
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
process.stdout.write(`${heading('Quick start')}\n\n`);
|
|
141
|
+
hint('pb orient -b Workspace overview');
|
|
142
|
+
hint('pb search <q> Search the Chain');
|
|
143
|
+
hint('pb capture <text> Capture knowledge');
|
|
144
|
+
hint('pb session start Start a write session');
|
|
145
|
+
process.stdout.write('\n');
|
|
146
|
+
hint('pb --help Show all commands');
|
|
147
|
+
hint('pb glossary Key terms');
|
|
148
|
+
}
|
|
149
|
+
process.stdout.write('\n');
|
|
150
|
+
});
|
|
62
151
|
program
|
|
63
152
|
.command('get <entry-id>')
|
|
64
153
|
.description('Display full entry by ID (data, relations, last 10 history events)')
|
|
@@ -67,25 +156,13 @@ program
|
|
|
67
156
|
program.commands.find((c) => c.name() === 'get')?.help();
|
|
68
157
|
return;
|
|
69
158
|
}
|
|
70
|
-
|
|
71
|
-
await runGet({ entryId: entryId.trim() });
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
159
|
+
await runGet({ entryId: entryId.trim() });
|
|
77
160
|
});
|
|
78
161
|
program
|
|
79
162
|
.command('get-many <entry-ids...>')
|
|
80
163
|
.description('Fetch multiple entries in parallel (space-separated IDs)')
|
|
81
164
|
.action(async (entryIds) => {
|
|
82
|
-
|
|
83
|
-
await runGetMany({ entryIds: entryIds.map((id) => id.trim()).filter(Boolean) });
|
|
84
|
-
}
|
|
85
|
-
catch (err) {
|
|
86
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
87
|
-
process.exit(1);
|
|
88
|
-
}
|
|
165
|
+
await runGetMany({ entryIds: entryIds.map((id) => id.trim()).filter(Boolean) });
|
|
89
166
|
});
|
|
90
167
|
program
|
|
91
168
|
.command('context <entry-id>')
|
|
@@ -95,26 +172,14 @@ program
|
|
|
95
172
|
program.commands.find((c) => c.name() === 'context')?.help();
|
|
96
173
|
return;
|
|
97
174
|
}
|
|
98
|
-
|
|
99
|
-
await runContext({ entryId: entryId.trim() });
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
175
|
+
await runContext({ entryId: entryId.trim() });
|
|
105
176
|
});
|
|
106
177
|
program
|
|
107
178
|
.command('changes')
|
|
108
179
|
.description('Detect entries modified and relations created since a given time (BET-239)')
|
|
109
180
|
.requiredOption('--since <duration>', 'Time window: 1h, 6h, 1d, 7d, 30d')
|
|
110
181
|
.action(async (opts) => {
|
|
111
|
-
|
|
112
|
-
await runChanges({ since: opts.since });
|
|
113
|
-
}
|
|
114
|
-
catch (err) {
|
|
115
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
182
|
+
await runChanges({ since: opts.since });
|
|
118
183
|
});
|
|
119
184
|
program
|
|
120
185
|
.command('walk <entry-id>')
|
|
@@ -123,36 +188,26 @@ program
|
|
|
123
188
|
.option('--direction <dir>', 'Traversal direction: outgoing or incoming (default outgoing)', 'outgoing')
|
|
124
189
|
.option('-t, --type <relation-type>', 'Filter by relation type (e.g. depends_on, informs, governs)')
|
|
125
190
|
.action(async (entryId, opts) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
await runChainWalk({
|
|
133
|
-
entryId: entryId.trim(),
|
|
134
|
-
depth,
|
|
135
|
-
direction: opts.direction,
|
|
136
|
-
type: opts.type,
|
|
191
|
+
const depth = parseInt(opts.depth, 10);
|
|
192
|
+
if (isNaN(depth) || depth < 1 || depth > 4) {
|
|
193
|
+
throw new CLIError('--depth must be between 1 and 4.', {
|
|
194
|
+
code: ErrorCode.VALIDATION_FAILED,
|
|
195
|
+
category: 'validation',
|
|
137
196
|
});
|
|
138
197
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
198
|
+
await runChainWalk({
|
|
199
|
+
entryId: entryId.trim(),
|
|
200
|
+
depth,
|
|
201
|
+
direction: opts.direction,
|
|
202
|
+
type: opts.type,
|
|
203
|
+
});
|
|
143
204
|
});
|
|
144
205
|
program
|
|
145
206
|
.command('cross-cut')
|
|
146
207
|
.description('Structural aggregation — all relations of a given type grouped by source collection (BET-239)')
|
|
147
208
|
.requiredOption('--type <relation-type>', 'Relation type to scan (e.g. part_of, informs, governs)')
|
|
148
209
|
.action(async (opts) => {
|
|
149
|
-
|
|
150
|
-
await runCrossCut({ type: opts.type });
|
|
151
|
-
}
|
|
152
|
-
catch (err) {
|
|
153
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
154
|
-
process.exit(1);
|
|
155
|
-
}
|
|
210
|
+
await runCrossCut({ type: opts.type });
|
|
156
211
|
});
|
|
157
212
|
program
|
|
158
213
|
.command('brief [type]')
|
|
@@ -165,27 +220,20 @@ program
|
|
|
165
220
|
.option('--since-last', 'Compare against last brief run (incremental mode)')
|
|
166
221
|
.option('--since <timestamp>', 'ISO 8601 timestamp for delta type (e.g. 2026-03-24T00:00:00Z)')
|
|
167
222
|
.action(async (type, opts) => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
// Otherwise, require --skill for incremental brief
|
|
175
|
-
if (!opts.skill) {
|
|
176
|
-
console.error('Usage:\n' +
|
|
177
|
-
' pb brief --skill <name> Incremental delta\n' +
|
|
178
|
-
' pb brief steering Compound steering brief\n' +
|
|
179
|
-
' pb brief confidence Compound confidence pass\n' +
|
|
180
|
-
' pb brief delta Compound delta sync');
|
|
181
|
-
process.exit(1);
|
|
182
|
-
}
|
|
183
|
-
await runBrief({ skill: opts.skill, sinceLast: opts.sinceLast });
|
|
223
|
+
// If positional arg is a compound type, route to compound brief
|
|
224
|
+
if (type && isCompoundType(type)) {
|
|
225
|
+
await runCompoundBrief({ type, since: opts.since });
|
|
226
|
+
return;
|
|
184
227
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
228
|
+
// Otherwise, require --skill for incremental brief
|
|
229
|
+
if (!opts.skill) {
|
|
230
|
+
throw new CLIError('Usage:\n' +
|
|
231
|
+
' pb brief --skill <name> Incremental delta\n' +
|
|
232
|
+
' pb brief steering Compound steering brief\n' +
|
|
233
|
+
' pb brief confidence Compound confidence pass\n' +
|
|
234
|
+
' pb brief delta Compound delta sync', { code: ErrorCode.VALIDATION_FAILED, category: 'validation' });
|
|
188
235
|
}
|
|
236
|
+
await runBrief({ skill: opts.skill, sinceLast: opts.sinceLast });
|
|
189
237
|
});
|
|
190
238
|
program
|
|
191
239
|
.command('search [query...]')
|
|
@@ -196,13 +244,7 @@ program
|
|
|
196
244
|
program.commands.find((c) => c.name() === 'search')?.help();
|
|
197
245
|
return;
|
|
198
246
|
}
|
|
199
|
-
|
|
200
|
-
await runSearch({ query });
|
|
201
|
-
}
|
|
202
|
-
catch (err) {
|
|
203
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
204
|
-
process.exit(1);
|
|
205
|
-
}
|
|
247
|
+
await runSearch({ query });
|
|
206
248
|
});
|
|
207
249
|
program
|
|
208
250
|
.command('orient')
|
|
@@ -212,13 +254,7 @@ program
|
|
|
212
254
|
// SYNC: domain list must match TaskDomain in convex/mcpKnowledge/startupResolver.ts
|
|
213
255
|
.option('-s, --scope <domain>', 'Domain scope for governance filtering. Valid values: auth, governance, architecture, product, data-foundation, chainwork, capture-pipeline, ingestion, intelligence-and-operations, review-and-learning, general')
|
|
214
256
|
.action(async (opts) => {
|
|
215
|
-
|
|
216
|
-
await runOrient({ brief: opts.brief, task: opts.task?.trim() || undefined, scope: opts.scope?.trim() || undefined });
|
|
217
|
-
}
|
|
218
|
-
catch (err) {
|
|
219
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
220
|
-
process.exit(1);
|
|
221
|
-
}
|
|
257
|
+
await runOrient({ brief: opts.brief, task: opts.task?.trim() || undefined, scope: opts.scope?.trim() || undefined });
|
|
222
258
|
});
|
|
223
259
|
program
|
|
224
260
|
.command('handshake')
|
|
@@ -229,17 +265,11 @@ program
|
|
|
229
265
|
.option('--level <level>', 'With --init: trust level (guide|work|silent|full-trust). Without --init: content tier (beginner|intermediate|expert)')
|
|
230
266
|
.option('--generate', 'Fetch governance entries from the Chain and merge generated rules (BET-286)')
|
|
231
267
|
.action(async (opts) => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
await runHandshake({ force: opts.force, dryRun: opts.dryRun, level: opts.level, generate: opts.generate });
|
|
238
|
-
}
|
|
239
|
-
catch (err) {
|
|
240
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
241
|
-
process.exit(1);
|
|
268
|
+
if (opts.init) {
|
|
269
|
+
await runHandshakeInit({ level: opts.level, dryRun: opts.dryRun });
|
|
270
|
+
return;
|
|
242
271
|
}
|
|
272
|
+
await runHandshake({ force: opts.force, dryRun: opts.dryRun, level: opts.level, generate: opts.generate });
|
|
243
273
|
});
|
|
244
274
|
program
|
|
245
275
|
.command('codex-prep <task...>')
|
|
@@ -251,49 +281,27 @@ program
|
|
|
251
281
|
program.commands.find((c) => c.name() === 'codex-prep')?.help();
|
|
252
282
|
return;
|
|
253
283
|
}
|
|
254
|
-
|
|
255
|
-
await runCodexPrep({ task, dryRun: opts.dryRun });
|
|
256
|
-
}
|
|
257
|
-
catch (err) {
|
|
258
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
259
|
-
process.exit(1);
|
|
260
|
-
}
|
|
284
|
+
await runCodexPrep({ task, dryRun: opts.dryRun });
|
|
261
285
|
});
|
|
262
286
|
program
|
|
263
287
|
.command('login')
|
|
264
288
|
.description('Save your API key to ~/.config/productbrain/.env (works from any directory)')
|
|
265
289
|
.action(async () => {
|
|
266
|
-
|
|
267
|
-
await runLogin();
|
|
268
|
-
}
|
|
269
|
-
catch (err) {
|
|
270
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
271
|
-
process.exit(1);
|
|
272
|
-
}
|
|
290
|
+
await runLogin();
|
|
273
291
|
});
|
|
274
292
|
program
|
|
275
293
|
.command('doctor')
|
|
276
294
|
.description('Check CLI configuration and connectivity')
|
|
277
|
-
.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
catch (err) {
|
|
282
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
283
|
-
process.exit(1);
|
|
284
|
-
}
|
|
295
|
+
.option('--fix', 'Auto-repair common configuration issues')
|
|
296
|
+
.option('--dry-run', 'Preview what --fix would do without changing anything')
|
|
297
|
+
.action(async (opts) => {
|
|
298
|
+
await runDoctor({ fix: opts.fix, dryRun: opts.dryRun });
|
|
285
299
|
});
|
|
286
300
|
program
|
|
287
301
|
.command('setup')
|
|
288
302
|
.description('Guided first-time setup: account, login, workspace, first capture')
|
|
289
303
|
.action(async () => {
|
|
290
|
-
|
|
291
|
-
await runSetup();
|
|
292
|
-
}
|
|
293
|
-
catch (err) {
|
|
294
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
295
|
-
process.exit(1);
|
|
296
|
-
}
|
|
304
|
+
await runSetup();
|
|
297
305
|
});
|
|
298
306
|
// --- Write commands (require active session) ---
|
|
299
307
|
const sessionCmd = program
|
|
@@ -304,38 +312,20 @@ sessionCmd
|
|
|
304
312
|
.description('Start a tracked write session (opens session, refreshes context)')
|
|
305
313
|
.option('--json', 'Output machine-readable JSON (deprecated: use global --json flag)')
|
|
306
314
|
.action(async (opts) => {
|
|
307
|
-
|
|
308
|
-
await runSessionStart({ json: opts.json });
|
|
309
|
-
}
|
|
310
|
-
catch (err) {
|
|
311
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
312
|
-
process.exit(1);
|
|
313
|
-
}
|
|
315
|
+
await runSessionStart({ json: opts.json });
|
|
314
316
|
});
|
|
315
317
|
sessionCmd
|
|
316
318
|
.command('id')
|
|
317
319
|
.description('Print current session ID to stdout (machine-readable, TEN-707)')
|
|
318
320
|
.action(() => {
|
|
319
|
-
|
|
320
|
-
runSessionId();
|
|
321
|
-
}
|
|
322
|
-
catch (err) {
|
|
323
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
324
|
-
process.exit(1);
|
|
325
|
-
}
|
|
321
|
+
runSessionId();
|
|
326
322
|
});
|
|
327
323
|
sessionCmd
|
|
328
324
|
.command('close')
|
|
329
325
|
.description('Close the active session (wrapup, refresh context)')
|
|
330
326
|
.option('--force', 'Clear local session state even if server close fails')
|
|
331
327
|
.action(async (opts) => {
|
|
332
|
-
|
|
333
|
-
await runSessionClose({ force: opts.force });
|
|
334
|
-
}
|
|
335
|
-
catch (err) {
|
|
336
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
337
|
-
process.exit(1);
|
|
338
|
-
}
|
|
328
|
+
await runSessionClose({ force: opts.force });
|
|
339
329
|
});
|
|
340
330
|
program
|
|
341
331
|
.command('capture [text...]')
|
|
@@ -354,23 +344,17 @@ program
|
|
|
354
344
|
program.commands.find((c) => c.name() === 'capture')?.help();
|
|
355
345
|
return;
|
|
356
346
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
catch (err) {
|
|
371
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
372
|
-
process.exit(1);
|
|
373
|
-
}
|
|
347
|
+
await runCapture({
|
|
348
|
+
text: text || opts.name || '',
|
|
349
|
+
name: opts.name,
|
|
350
|
+
description: opts.description,
|
|
351
|
+
collection: opts.collection,
|
|
352
|
+
link: opts.link,
|
|
353
|
+
type: opts.type,
|
|
354
|
+
sourceRef: opts.sourceRef,
|
|
355
|
+
sourceExcerpt: opts.sourceExcerpt,
|
|
356
|
+
json: opts.json,
|
|
357
|
+
});
|
|
374
358
|
});
|
|
375
359
|
// --- Update command (TEN-703) ---
|
|
376
360
|
program
|
|
@@ -382,20 +366,14 @@ program
|
|
|
382
366
|
.option('--workflow-status <status>', 'Set workflow status (server-validated)')
|
|
383
367
|
.option('--note <text>', 'Change note for history')
|
|
384
368
|
.action(async (entryId, opts) => {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
catch (err) {
|
|
396
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
397
|
-
process.exit(1);
|
|
398
|
-
}
|
|
369
|
+
await runUpdate({
|
|
370
|
+
entryId,
|
|
371
|
+
field: opts.field,
|
|
372
|
+
name: opts.name,
|
|
373
|
+
status: opts.status,
|
|
374
|
+
workflowStatus: opts.workflowStatus,
|
|
375
|
+
note: opts.note,
|
|
376
|
+
});
|
|
399
377
|
});
|
|
400
378
|
// --- Promote command (Wave 2 CLI Polish) ---
|
|
401
379
|
program
|
|
@@ -406,26 +384,14 @@ program
|
|
|
406
384
|
program.commands.find((c) => c.name() === 'verify')?.help();
|
|
407
385
|
return;
|
|
408
386
|
}
|
|
409
|
-
|
|
410
|
-
await runVerify({ entryId: entryId.trim() });
|
|
411
|
-
}
|
|
412
|
-
catch (err) {
|
|
413
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
414
|
-
process.exit(1);
|
|
415
|
-
}
|
|
387
|
+
await runVerify({ entryId: entryId.trim() });
|
|
416
388
|
});
|
|
417
389
|
program
|
|
418
390
|
.command('promote <entry-id>')
|
|
419
391
|
.description('Promote entry from draft to active (commit to SSOT)')
|
|
420
392
|
.option('-m, --message <text>', 'Commit message')
|
|
421
393
|
.action(async (entryId, opts) => {
|
|
422
|
-
|
|
423
|
-
await runPromote({ entryId, message: opts.message });
|
|
424
|
-
}
|
|
425
|
-
catch (err) {
|
|
426
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
427
|
-
process.exit(1);
|
|
428
|
-
}
|
|
394
|
+
await runPromote({ entryId, message: opts.message });
|
|
429
395
|
});
|
|
430
396
|
// --- Relate / Unrelate commands (TEN-704) ---
|
|
431
397
|
program
|
|
@@ -433,25 +399,13 @@ program
|
|
|
433
399
|
.description('Add a typed relation between two entries (requires active session)')
|
|
434
400
|
.option('--if-missing', 'Only create relation if it does not already exist')
|
|
435
401
|
.action(async (fromId, type, toId, opts) => {
|
|
436
|
-
|
|
437
|
-
await runRelate({ fromId, type, toId, ifMissing: opts.ifMissing });
|
|
438
|
-
}
|
|
439
|
-
catch (err) {
|
|
440
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
441
|
-
process.exit(1);
|
|
442
|
-
}
|
|
402
|
+
await runRelate({ fromId, type, toId, ifMissing: opts.ifMissing });
|
|
443
403
|
});
|
|
444
404
|
program
|
|
445
405
|
.command('unrelate <from-id> <type> <to-id>')
|
|
446
406
|
.description('Remove a typed relation between two entries (requires active session)')
|
|
447
407
|
.action(async (fromId, type, toId) => {
|
|
448
|
-
|
|
449
|
-
await runUnrelate({ fromId, type, toId });
|
|
450
|
-
}
|
|
451
|
-
catch (err) {
|
|
452
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
453
|
-
process.exit(1);
|
|
454
|
-
}
|
|
408
|
+
await runUnrelate({ fromId, type, toId });
|
|
455
409
|
});
|
|
456
410
|
// --- Ingest command (BET-81) ---
|
|
457
411
|
program
|
|
@@ -461,23 +415,19 @@ program
|
|
|
461
415
|
.option('--resume', 'Skip files whose sourceRef is already committed in staging.')
|
|
462
416
|
.option('--concurrency <n>', 'Process up to N files in parallel (default 1, max 5).', '1')
|
|
463
417
|
.action(async (pattern, opts) => {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
await runIngest({
|
|
471
|
-
pattern,
|
|
472
|
-
dryRun: opts.dryRun,
|
|
473
|
-
resume: opts.resume,
|
|
474
|
-
concurrency,
|
|
418
|
+
const concurrency = parseInt(opts.concurrency ?? '1', 10);
|
|
419
|
+
if (Number.isNaN(concurrency) || concurrency < 1) {
|
|
420
|
+
throw new CLIError('--concurrency must be a positive integer.', {
|
|
421
|
+
code: ErrorCode.VALIDATION_FAILED,
|
|
422
|
+
category: 'validation',
|
|
475
423
|
});
|
|
476
424
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
425
|
+
await runIngest({
|
|
426
|
+
pattern,
|
|
427
|
+
dryRun: opts.dryRun,
|
|
428
|
+
resume: opts.resume,
|
|
429
|
+
concurrency,
|
|
430
|
+
});
|
|
481
431
|
});
|
|
482
432
|
// --- Fields command (BET-181 Slice 2) ---
|
|
483
433
|
program
|
|
@@ -488,13 +438,7 @@ program
|
|
|
488
438
|
program.commands.find((c) => c.name() === 'fields')?.help();
|
|
489
439
|
return;
|
|
490
440
|
}
|
|
491
|
-
|
|
492
|
-
await runFields({ collectionSlug: collection.trim() });
|
|
493
|
-
}
|
|
494
|
-
catch (err) {
|
|
495
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
496
|
-
process.exit(1);
|
|
497
|
-
}
|
|
441
|
+
await runFields({ collectionSlug: collection.trim() });
|
|
498
442
|
});
|
|
499
443
|
// --- Constellation command (BET-181 Slice 3) ---
|
|
500
444
|
program
|
|
@@ -505,13 +449,7 @@ program
|
|
|
505
449
|
program.commands.find((c) => c.name() === 'constellation')?.help();
|
|
506
450
|
return;
|
|
507
451
|
}
|
|
508
|
-
|
|
509
|
-
await runConstellation({ entryId: entryId.trim() });
|
|
510
|
-
}
|
|
511
|
-
catch (err) {
|
|
512
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
513
|
-
process.exit(1);
|
|
514
|
-
}
|
|
452
|
+
await runConstellation({ entryId: entryId.trim() });
|
|
515
453
|
});
|
|
516
454
|
// --- Audit command (BET-182 Slice 2) ---
|
|
517
455
|
program
|
|
@@ -522,19 +460,13 @@ program
|
|
|
522
460
|
.option('--fix', 'Auto-execute exact fixes via pb update, rerun once')
|
|
523
461
|
.option('--verbose', 'Show all gates including PASS detail')
|
|
524
462
|
.action(async (entryIds, opts) => {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
catch (err) {
|
|
535
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
536
|
-
process.exit(1);
|
|
537
|
-
}
|
|
463
|
+
await runAudit({
|
|
464
|
+
entryIds: entryIds.map((id) => id.trim()).filter(Boolean),
|
|
465
|
+
phase: opts.phase,
|
|
466
|
+
gate: opts.gate,
|
|
467
|
+
fix: opts.fix,
|
|
468
|
+
verbose: opts.verbose,
|
|
469
|
+
});
|
|
538
470
|
});
|
|
539
471
|
program
|
|
540
472
|
.command('brand-pack')
|
|
@@ -549,13 +481,7 @@ program
|
|
|
549
481
|
.command('proposals')
|
|
550
482
|
.description('List open consent proposals with expiry countdown (BET-221)')
|
|
551
483
|
.action(async () => {
|
|
552
|
-
|
|
553
|
-
await runProposals();
|
|
554
|
-
}
|
|
555
|
-
catch (err) {
|
|
556
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
557
|
-
process.exit(1);
|
|
558
|
-
}
|
|
484
|
+
await runProposals();
|
|
559
485
|
});
|
|
560
486
|
program
|
|
561
487
|
.command('accept [proposal-id]')
|
|
@@ -563,29 +489,19 @@ program
|
|
|
563
489
|
.option('-a, --auto', 'Auto-approve all open proposals (skip contradictions)')
|
|
564
490
|
.action(async (proposalId, opts) => {
|
|
565
491
|
if (!proposalId && !opts.auto) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
await runAccept({ proposalId, auto: opts.auto });
|
|
571
|
-
}
|
|
572
|
-
catch (err) {
|
|
573
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
574
|
-
process.exit(1);
|
|
492
|
+
throw new CLIError('Usage: pb accept <proposal-id> or pb accept --auto', {
|
|
493
|
+
code: ErrorCode.VALIDATION_FAILED,
|
|
494
|
+
category: 'validation',
|
|
495
|
+
});
|
|
575
496
|
}
|
|
497
|
+
await runAccept({ proposalId, auto: opts.auto });
|
|
576
498
|
});
|
|
577
499
|
program
|
|
578
500
|
.command('reject <proposal-id>')
|
|
579
501
|
.description('Reject a consent proposal with a required reason (BR-7)')
|
|
580
502
|
.requiredOption('-r, --reason <text>', 'Reason for rejection (required)')
|
|
581
503
|
.action(async (proposalId, opts) => {
|
|
582
|
-
|
|
583
|
-
await runReject({ proposalId, reason: opts.reason });
|
|
584
|
-
}
|
|
585
|
-
catch (err) {
|
|
586
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
587
|
-
process.exit(1);
|
|
588
|
-
}
|
|
504
|
+
await runReject({ proposalId, reason: opts.reason });
|
|
589
505
|
});
|
|
590
506
|
// --- Collections command (BET-280 Slice 1) ---
|
|
591
507
|
const collectionsCmd = program
|
|
@@ -595,13 +511,7 @@ collectionsCmd
|
|
|
595
511
|
.command('list')
|
|
596
512
|
.description('List all collections — slug, name, field count, icon')
|
|
597
513
|
.action(async () => {
|
|
598
|
-
|
|
599
|
-
await runCollectionsList();
|
|
600
|
-
}
|
|
601
|
-
catch (err) {
|
|
602
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
603
|
-
process.exit(1);
|
|
604
|
-
}
|
|
514
|
+
await runCollectionsList();
|
|
605
515
|
});
|
|
606
516
|
collectionsCmd
|
|
607
517
|
.command('get <slug>')
|
|
@@ -611,37 +521,68 @@ collectionsCmd
|
|
|
611
521
|
program.commands.find((c) => c.name() === 'collections')?.help();
|
|
612
522
|
return;
|
|
613
523
|
}
|
|
614
|
-
|
|
615
|
-
await runCollectionsGet({ slug: slug.trim() });
|
|
616
|
-
}
|
|
617
|
-
catch (err) {
|
|
618
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
619
|
-
process.exit(1);
|
|
620
|
-
}
|
|
524
|
+
await runCollectionsGet({ slug: slug.trim() });
|
|
621
525
|
});
|
|
622
526
|
collectionsCmd
|
|
623
527
|
.command('audit')
|
|
624
528
|
.description('Collection health report — classification, icon, displayHint coverage, schema gaps')
|
|
625
529
|
.action(async () => {
|
|
626
|
-
|
|
627
|
-
await runCollectionsAudit();
|
|
628
|
-
}
|
|
629
|
-
catch (err) {
|
|
630
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
631
|
-
process.exit(1);
|
|
632
|
-
}
|
|
530
|
+
await runCollectionsAudit();
|
|
633
531
|
});
|
|
634
532
|
collectionsCmd
|
|
635
533
|
.command('export')
|
|
636
534
|
.description('Export all system_collection_definitions with full classification metadata (admin only)')
|
|
637
535
|
.action(async () => {
|
|
638
|
-
|
|
639
|
-
await runCollectionsExport();
|
|
640
|
-
}
|
|
641
|
-
catch (err) {
|
|
642
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
643
|
-
process.exit(1);
|
|
644
|
-
}
|
|
536
|
+
await runCollectionsExport();
|
|
645
537
|
});
|
|
646
|
-
|
|
538
|
+
// --- Profile commands (WP-302 Slice 2) ---
|
|
539
|
+
const profileCmd = program
|
|
540
|
+
.command('profile')
|
|
541
|
+
.description('Manage workspace profiles (WP-302: multi-workspace support)');
|
|
542
|
+
profileCmd
|
|
543
|
+
.command('list')
|
|
544
|
+
.description('List all profiles, mark active one')
|
|
545
|
+
.action(async () => {
|
|
546
|
+
await runProfileList();
|
|
547
|
+
});
|
|
548
|
+
profileCmd
|
|
549
|
+
.command('create <name>')
|
|
550
|
+
.description('Create a new profile with an API key')
|
|
551
|
+
.requiredOption('--api-key <key>', 'API key (pb_sk_...)')
|
|
552
|
+
.option('--url <url>', 'Convex site URL (defaults to production)')
|
|
553
|
+
.action(async (name, opts) => {
|
|
554
|
+
await runProfileCreate({ name, apiKey: opts.apiKey, url: opts.url });
|
|
555
|
+
});
|
|
556
|
+
profileCmd
|
|
557
|
+
.command('use <name>')
|
|
558
|
+
.description('Switch active profile (closes any active session)')
|
|
559
|
+
.action(async (name) => {
|
|
560
|
+
await runProfileUse({ name });
|
|
561
|
+
});
|
|
562
|
+
profileCmd
|
|
563
|
+
.command('delete <name>')
|
|
564
|
+
.description('Delete a profile (cannot delete active or last profile)')
|
|
565
|
+
.action(async (name) => {
|
|
566
|
+
await runProfileDelete({ name });
|
|
567
|
+
});
|
|
568
|
+
// --- Glossary command (WP-302 Slice 4) ---
|
|
569
|
+
program
|
|
570
|
+
.command('glossary')
|
|
571
|
+
.description('Show key Product Brain CLI terms and definitions')
|
|
572
|
+
.action(() => {
|
|
573
|
+
if (isJsonMode()) {
|
|
574
|
+
process.stdout.write(JSON.stringify(GLOSSARY) + '\n');
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const lines = [
|
|
578
|
+
'',
|
|
579
|
+
heading('Glossary'),
|
|
580
|
+
'',
|
|
581
|
+
formatGlossary(),
|
|
582
|
+
'',
|
|
583
|
+
];
|
|
584
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
585
|
+
});
|
|
586
|
+
// Parse with global error handler — all uncaught errors route through handleError
|
|
587
|
+
program.parseAsync().catch(handleError);
|
|
647
588
|
//# sourceMappingURL=index.js.map
|