@productbrain/cli 0.1.0-beta.29 → 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.d.ts +2 -0
- package/dist/__tests__/constants.test.d.ts.map +1 -0
- package/dist/__tests__/constants.test.js +94 -0
- package/dist/__tests__/constants.test.js.map +1 -0
- 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.d.ts +2 -0
- package/dist/__tests__/login.test.d.ts.map +1 -0
- package/dist/__tests__/login.test.js +168 -0
- package/dist/__tests__/login.test.js.map +1 -0
- 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/__tests__/setup.test.d.ts +2 -0
- package/dist/__tests__/setup.test.d.ts.map +1 -0
- package/dist/__tests__/setup.test.js +170 -0
- package/dist/__tests__/setup.test.js.map +1 -0
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +23 -2
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/doctor.d.ts +18 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +211 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/doctor.test.d.ts +7 -0
- package/dist/commands/doctor.test.d.ts.map +1 -0
- package/dist/commands/doctor.test.js +265 -0
- package/dist/commands/doctor.test.js.map +1 -0
- package/dist/commands/login.d.ts +4 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +53 -27
- package/dist/commands/login.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/commands/setup.d.ts +16 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +213 -0
- package/dist/commands/setup.js.map +1 -0
- 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 +251 -284
- package/dist/index.js.map +1 -1
- package/dist/lib/activation.d.ts +28 -0
- package/dist/lib/activation.d.ts.map +1 -0
- package/dist/lib/activation.js +57 -0
- package/dist/lib/activation.js.map +1 -0
- package/dist/lib/activation.test.d.ts +6 -0
- package/dist/lib/activation.test.d.ts.map +1 -0
- package/dist/lib/activation.test.js +121 -0
- package/dist/lib/activation.test.js.map +1 -0
- package/dist/lib/client.d.ts +19 -2
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/client.js +71 -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 +54 -15
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/constants.d.ts +21 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +39 -0
- package/dist/lib/constants.js.map +1 -0
- 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/dist/lib/telemetry.d.ts +15 -0
- package/dist/lib/telemetry.d.ts.map +1 -0
- package/dist/lib/telemetry.js +29 -0
- package/dist/lib/telemetry.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,13 +7,15 @@
|
|
|
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';
|
|
18
|
+
import { runSetup } from './commands/setup.js';
|
|
17
19
|
import { runOrient } from './commands/orient.js';
|
|
18
20
|
import { runSearch } from './commands/search.js';
|
|
19
21
|
import { runHandshake, runHandshakeInit } from './commands/handshake.js';
|
|
@@ -37,14 +39,68 @@ import { runBrief, runCompoundBrief, isCompoundType } from './commands/brief.js'
|
|
|
37
39
|
import { runVerify } from './commands/verify.js';
|
|
38
40
|
import { runCodexPrep } from './commands/codex-prep.js';
|
|
39
41
|
import { runCollectionsList, runCollectionsGet, runCollectionsAudit, runCollectionsExport } from './commands/collections.js';
|
|
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';
|
|
40
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
|
+
}
|
|
41
95
|
program
|
|
42
96
|
.name('pb')
|
|
43
97
|
.description('Product Brain — Chain knowledge + write-back CLI')
|
|
44
98
|
.version(cliPackageVersion.version)
|
|
45
99
|
// Global output mode flags (DEC-299, BET-181)
|
|
46
100
|
.option('--json', 'Output machine-readable JSON (overrides TTY auto-detection)')
|
|
47
|
-
.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
|
|
48
104
|
// Apply global output mode BEFORE subcommand actions run
|
|
49
105
|
program.hook('preAction', (thisCommand) => {
|
|
50
106
|
const globalOpts = program.opts();
|
|
@@ -55,8 +111,43 @@ program.hook('preAction', (thisCommand) => {
|
|
|
55
111
|
setOutputMode('pretty');
|
|
56
112
|
}
|
|
57
113
|
// else: 'auto' (default) — TTY detection handles it
|
|
114
|
+
if (globalOpts.quiet) {
|
|
115
|
+
setQuietMode(true);
|
|
116
|
+
}
|
|
58
117
|
void thisCommand;
|
|
59
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
|
+
});
|
|
60
151
|
program
|
|
61
152
|
.command('get <entry-id>')
|
|
62
153
|
.description('Display full entry by ID (data, relations, last 10 history events)')
|
|
@@ -65,25 +156,13 @@ program
|
|
|
65
156
|
program.commands.find((c) => c.name() === 'get')?.help();
|
|
66
157
|
return;
|
|
67
158
|
}
|
|
68
|
-
|
|
69
|
-
await runGet({ entryId: entryId.trim() });
|
|
70
|
-
}
|
|
71
|
-
catch (err) {
|
|
72
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
159
|
+
await runGet({ entryId: entryId.trim() });
|
|
75
160
|
});
|
|
76
161
|
program
|
|
77
162
|
.command('get-many <entry-ids...>')
|
|
78
163
|
.description('Fetch multiple entries in parallel (space-separated IDs)')
|
|
79
164
|
.action(async (entryIds) => {
|
|
80
|
-
|
|
81
|
-
await runGetMany({ entryIds: entryIds.map((id) => id.trim()).filter(Boolean) });
|
|
82
|
-
}
|
|
83
|
-
catch (err) {
|
|
84
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
165
|
+
await runGetMany({ entryIds: entryIds.map((id) => id.trim()).filter(Boolean) });
|
|
87
166
|
});
|
|
88
167
|
program
|
|
89
168
|
.command('context <entry-id>')
|
|
@@ -93,26 +172,14 @@ program
|
|
|
93
172
|
program.commands.find((c) => c.name() === 'context')?.help();
|
|
94
173
|
return;
|
|
95
174
|
}
|
|
96
|
-
|
|
97
|
-
await runContext({ entryId: entryId.trim() });
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
|
175
|
+
await runContext({ entryId: entryId.trim() });
|
|
103
176
|
});
|
|
104
177
|
program
|
|
105
178
|
.command('changes')
|
|
106
179
|
.description('Detect entries modified and relations created since a given time (BET-239)')
|
|
107
180
|
.requiredOption('--since <duration>', 'Time window: 1h, 6h, 1d, 7d, 30d')
|
|
108
181
|
.action(async (opts) => {
|
|
109
|
-
|
|
110
|
-
await runChanges({ since: opts.since });
|
|
111
|
-
}
|
|
112
|
-
catch (err) {
|
|
113
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
114
|
-
process.exit(1);
|
|
115
|
-
}
|
|
182
|
+
await runChanges({ since: opts.since });
|
|
116
183
|
});
|
|
117
184
|
program
|
|
118
185
|
.command('walk <entry-id>')
|
|
@@ -121,36 +188,26 @@ program
|
|
|
121
188
|
.option('--direction <dir>', 'Traversal direction: outgoing or incoming (default outgoing)', 'outgoing')
|
|
122
189
|
.option('-t, --type <relation-type>', 'Filter by relation type (e.g. depends_on, informs, governs)')
|
|
123
190
|
.action(async (entryId, opts) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
await runChainWalk({
|
|
131
|
-
entryId: entryId.trim(),
|
|
132
|
-
depth,
|
|
133
|
-
direction: opts.direction,
|
|
134
|
-
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',
|
|
135
196
|
});
|
|
136
197
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
198
|
+
await runChainWalk({
|
|
199
|
+
entryId: entryId.trim(),
|
|
200
|
+
depth,
|
|
201
|
+
direction: opts.direction,
|
|
202
|
+
type: opts.type,
|
|
203
|
+
});
|
|
141
204
|
});
|
|
142
205
|
program
|
|
143
206
|
.command('cross-cut')
|
|
144
207
|
.description('Structural aggregation — all relations of a given type grouped by source collection (BET-239)')
|
|
145
208
|
.requiredOption('--type <relation-type>', 'Relation type to scan (e.g. part_of, informs, governs)')
|
|
146
209
|
.action(async (opts) => {
|
|
147
|
-
|
|
148
|
-
await runCrossCut({ type: opts.type });
|
|
149
|
-
}
|
|
150
|
-
catch (err) {
|
|
151
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
152
|
-
process.exit(1);
|
|
153
|
-
}
|
|
210
|
+
await runCrossCut({ type: opts.type });
|
|
154
211
|
});
|
|
155
212
|
program
|
|
156
213
|
.command('brief [type]')
|
|
@@ -163,27 +220,20 @@ program
|
|
|
163
220
|
.option('--since-last', 'Compare against last brief run (incremental mode)')
|
|
164
221
|
.option('--since <timestamp>', 'ISO 8601 timestamp for delta type (e.g. 2026-03-24T00:00:00Z)')
|
|
165
222
|
.action(async (type, opts) => {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
// Otherwise, require --skill for incremental brief
|
|
173
|
-
if (!opts.skill) {
|
|
174
|
-
console.error('Usage:\n' +
|
|
175
|
-
' pb brief --skill <name> Incremental delta\n' +
|
|
176
|
-
' pb brief steering Compound steering brief\n' +
|
|
177
|
-
' pb brief confidence Compound confidence pass\n' +
|
|
178
|
-
' pb brief delta Compound delta sync');
|
|
179
|
-
process.exit(1);
|
|
180
|
-
}
|
|
181
|
-
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;
|
|
182
227
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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' });
|
|
186
235
|
}
|
|
236
|
+
await runBrief({ skill: opts.skill, sinceLast: opts.sinceLast });
|
|
187
237
|
});
|
|
188
238
|
program
|
|
189
239
|
.command('search [query...]')
|
|
@@ -194,13 +244,7 @@ program
|
|
|
194
244
|
program.commands.find((c) => c.name() === 'search')?.help();
|
|
195
245
|
return;
|
|
196
246
|
}
|
|
197
|
-
|
|
198
|
-
await runSearch({ query });
|
|
199
|
-
}
|
|
200
|
-
catch (err) {
|
|
201
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
202
|
-
process.exit(1);
|
|
203
|
-
}
|
|
247
|
+
await runSearch({ query });
|
|
204
248
|
});
|
|
205
249
|
program
|
|
206
250
|
.command('orient')
|
|
@@ -210,13 +254,7 @@ program
|
|
|
210
254
|
// SYNC: domain list must match TaskDomain in convex/mcpKnowledge/startupResolver.ts
|
|
211
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')
|
|
212
256
|
.action(async (opts) => {
|
|
213
|
-
|
|
214
|
-
await runOrient({ brief: opts.brief, task: opts.task?.trim() || undefined, scope: opts.scope?.trim() || undefined });
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
218
|
-
process.exit(1);
|
|
219
|
-
}
|
|
257
|
+
await runOrient({ brief: opts.brief, task: opts.task?.trim() || undefined, scope: opts.scope?.trim() || undefined });
|
|
220
258
|
});
|
|
221
259
|
program
|
|
222
260
|
.command('handshake')
|
|
@@ -227,17 +265,11 @@ program
|
|
|
227
265
|
.option('--level <level>', 'With --init: trust level (guide|work|silent|full-trust). Without --init: content tier (beginner|intermediate|expert)')
|
|
228
266
|
.option('--generate', 'Fetch governance entries from the Chain and merge generated rules (BET-286)')
|
|
229
267
|
.action(async (opts) => {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
await runHandshake({ force: opts.force, dryRun: opts.dryRun, level: opts.level, generate: opts.generate });
|
|
236
|
-
}
|
|
237
|
-
catch (err) {
|
|
238
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
239
|
-
process.exit(1);
|
|
268
|
+
if (opts.init) {
|
|
269
|
+
await runHandshakeInit({ level: opts.level, dryRun: opts.dryRun });
|
|
270
|
+
return;
|
|
240
271
|
}
|
|
272
|
+
await runHandshake({ force: opts.force, dryRun: opts.dryRun, level: opts.level, generate: opts.generate });
|
|
241
273
|
});
|
|
242
274
|
program
|
|
243
275
|
.command('codex-prep <task...>')
|
|
@@ -249,25 +281,27 @@ program
|
|
|
249
281
|
program.commands.find((c) => c.name() === 'codex-prep')?.help();
|
|
250
282
|
return;
|
|
251
283
|
}
|
|
252
|
-
|
|
253
|
-
await runCodexPrep({ task, dryRun: opts.dryRun });
|
|
254
|
-
}
|
|
255
|
-
catch (err) {
|
|
256
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
257
|
-
process.exit(1);
|
|
258
|
-
}
|
|
284
|
+
await runCodexPrep({ task, dryRun: opts.dryRun });
|
|
259
285
|
});
|
|
260
286
|
program
|
|
261
287
|
.command('login')
|
|
262
288
|
.description('Save your API key to ~/.config/productbrain/.env (works from any directory)')
|
|
263
289
|
.action(async () => {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
290
|
+
await runLogin();
|
|
291
|
+
});
|
|
292
|
+
program
|
|
293
|
+
.command('doctor')
|
|
294
|
+
.description('Check CLI configuration and connectivity')
|
|
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 });
|
|
299
|
+
});
|
|
300
|
+
program
|
|
301
|
+
.command('setup')
|
|
302
|
+
.description('Guided first-time setup: account, login, workspace, first capture')
|
|
303
|
+
.action(async () => {
|
|
304
|
+
await runSetup();
|
|
271
305
|
});
|
|
272
306
|
// --- Write commands (require active session) ---
|
|
273
307
|
const sessionCmd = program
|
|
@@ -278,38 +312,20 @@ sessionCmd
|
|
|
278
312
|
.description('Start a tracked write session (opens session, refreshes context)')
|
|
279
313
|
.option('--json', 'Output machine-readable JSON (deprecated: use global --json flag)')
|
|
280
314
|
.action(async (opts) => {
|
|
281
|
-
|
|
282
|
-
await runSessionStart({ json: opts.json });
|
|
283
|
-
}
|
|
284
|
-
catch (err) {
|
|
285
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
286
|
-
process.exit(1);
|
|
287
|
-
}
|
|
315
|
+
await runSessionStart({ json: opts.json });
|
|
288
316
|
});
|
|
289
317
|
sessionCmd
|
|
290
318
|
.command('id')
|
|
291
319
|
.description('Print current session ID to stdout (machine-readable, TEN-707)')
|
|
292
320
|
.action(() => {
|
|
293
|
-
|
|
294
|
-
runSessionId();
|
|
295
|
-
}
|
|
296
|
-
catch (err) {
|
|
297
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
298
|
-
process.exit(1);
|
|
299
|
-
}
|
|
321
|
+
runSessionId();
|
|
300
322
|
});
|
|
301
323
|
sessionCmd
|
|
302
324
|
.command('close')
|
|
303
325
|
.description('Close the active session (wrapup, refresh context)')
|
|
304
326
|
.option('--force', 'Clear local session state even if server close fails')
|
|
305
327
|
.action(async (opts) => {
|
|
306
|
-
|
|
307
|
-
await runSessionClose({ force: opts.force });
|
|
308
|
-
}
|
|
309
|
-
catch (err) {
|
|
310
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
311
|
-
process.exit(1);
|
|
312
|
-
}
|
|
328
|
+
await runSessionClose({ force: opts.force });
|
|
313
329
|
});
|
|
314
330
|
program
|
|
315
331
|
.command('capture [text...]')
|
|
@@ -328,23 +344,17 @@ program
|
|
|
328
344
|
program.commands.find((c) => c.name() === 'capture')?.help();
|
|
329
345
|
return;
|
|
330
346
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
catch (err) {
|
|
345
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
346
|
-
process.exit(1);
|
|
347
|
-
}
|
|
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
|
+
});
|
|
348
358
|
});
|
|
349
359
|
// --- Update command (TEN-703) ---
|
|
350
360
|
program
|
|
@@ -356,20 +366,14 @@ program
|
|
|
356
366
|
.option('--workflow-status <status>', 'Set workflow status (server-validated)')
|
|
357
367
|
.option('--note <text>', 'Change note for history')
|
|
358
368
|
.action(async (entryId, opts) => {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
catch (err) {
|
|
370
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
371
|
-
process.exit(1);
|
|
372
|
-
}
|
|
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
|
+
});
|
|
373
377
|
});
|
|
374
378
|
// --- Promote command (Wave 2 CLI Polish) ---
|
|
375
379
|
program
|
|
@@ -380,26 +384,14 @@ program
|
|
|
380
384
|
program.commands.find((c) => c.name() === 'verify')?.help();
|
|
381
385
|
return;
|
|
382
386
|
}
|
|
383
|
-
|
|
384
|
-
await runVerify({ entryId: entryId.trim() });
|
|
385
|
-
}
|
|
386
|
-
catch (err) {
|
|
387
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
388
|
-
process.exit(1);
|
|
389
|
-
}
|
|
387
|
+
await runVerify({ entryId: entryId.trim() });
|
|
390
388
|
});
|
|
391
389
|
program
|
|
392
390
|
.command('promote <entry-id>')
|
|
393
391
|
.description('Promote entry from draft to active (commit to SSOT)')
|
|
394
392
|
.option('-m, --message <text>', 'Commit message')
|
|
395
393
|
.action(async (entryId, opts) => {
|
|
396
|
-
|
|
397
|
-
await runPromote({ entryId, message: opts.message });
|
|
398
|
-
}
|
|
399
|
-
catch (err) {
|
|
400
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
401
|
-
process.exit(1);
|
|
402
|
-
}
|
|
394
|
+
await runPromote({ entryId, message: opts.message });
|
|
403
395
|
});
|
|
404
396
|
// --- Relate / Unrelate commands (TEN-704) ---
|
|
405
397
|
program
|
|
@@ -407,25 +399,13 @@ program
|
|
|
407
399
|
.description('Add a typed relation between two entries (requires active session)')
|
|
408
400
|
.option('--if-missing', 'Only create relation if it does not already exist')
|
|
409
401
|
.action(async (fromId, type, toId, opts) => {
|
|
410
|
-
|
|
411
|
-
await runRelate({ fromId, type, toId, ifMissing: opts.ifMissing });
|
|
412
|
-
}
|
|
413
|
-
catch (err) {
|
|
414
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
415
|
-
process.exit(1);
|
|
416
|
-
}
|
|
402
|
+
await runRelate({ fromId, type, toId, ifMissing: opts.ifMissing });
|
|
417
403
|
});
|
|
418
404
|
program
|
|
419
405
|
.command('unrelate <from-id> <type> <to-id>')
|
|
420
406
|
.description('Remove a typed relation between two entries (requires active session)')
|
|
421
407
|
.action(async (fromId, type, toId) => {
|
|
422
|
-
|
|
423
|
-
await runUnrelate({ fromId, type, toId });
|
|
424
|
-
}
|
|
425
|
-
catch (err) {
|
|
426
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
427
|
-
process.exit(1);
|
|
428
|
-
}
|
|
408
|
+
await runUnrelate({ fromId, type, toId });
|
|
429
409
|
});
|
|
430
410
|
// --- Ingest command (BET-81) ---
|
|
431
411
|
program
|
|
@@ -435,23 +415,19 @@ program
|
|
|
435
415
|
.option('--resume', 'Skip files whose sourceRef is already committed in staging.')
|
|
436
416
|
.option('--concurrency <n>', 'Process up to N files in parallel (default 1, max 5).', '1')
|
|
437
417
|
.action(async (pattern, opts) => {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
}
|
|
444
|
-
await runIngest({
|
|
445
|
-
pattern,
|
|
446
|
-
dryRun: opts.dryRun,
|
|
447
|
-
resume: opts.resume,
|
|
448
|
-
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',
|
|
449
423
|
});
|
|
450
424
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
425
|
+
await runIngest({
|
|
426
|
+
pattern,
|
|
427
|
+
dryRun: opts.dryRun,
|
|
428
|
+
resume: opts.resume,
|
|
429
|
+
concurrency,
|
|
430
|
+
});
|
|
455
431
|
});
|
|
456
432
|
// --- Fields command (BET-181 Slice 2) ---
|
|
457
433
|
program
|
|
@@ -462,13 +438,7 @@ program
|
|
|
462
438
|
program.commands.find((c) => c.name() === 'fields')?.help();
|
|
463
439
|
return;
|
|
464
440
|
}
|
|
465
|
-
|
|
466
|
-
await runFields({ collectionSlug: collection.trim() });
|
|
467
|
-
}
|
|
468
|
-
catch (err) {
|
|
469
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
470
|
-
process.exit(1);
|
|
471
|
-
}
|
|
441
|
+
await runFields({ collectionSlug: collection.trim() });
|
|
472
442
|
});
|
|
473
443
|
// --- Constellation command (BET-181 Slice 3) ---
|
|
474
444
|
program
|
|
@@ -479,13 +449,7 @@ program
|
|
|
479
449
|
program.commands.find((c) => c.name() === 'constellation')?.help();
|
|
480
450
|
return;
|
|
481
451
|
}
|
|
482
|
-
|
|
483
|
-
await runConstellation({ entryId: entryId.trim() });
|
|
484
|
-
}
|
|
485
|
-
catch (err) {
|
|
486
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
487
|
-
process.exit(1);
|
|
488
|
-
}
|
|
452
|
+
await runConstellation({ entryId: entryId.trim() });
|
|
489
453
|
});
|
|
490
454
|
// --- Audit command (BET-182 Slice 2) ---
|
|
491
455
|
program
|
|
@@ -496,19 +460,13 @@ program
|
|
|
496
460
|
.option('--fix', 'Auto-execute exact fixes via pb update, rerun once')
|
|
497
461
|
.option('--verbose', 'Show all gates including PASS detail')
|
|
498
462
|
.action(async (entryIds, opts) => {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
catch (err) {
|
|
509
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
510
|
-
process.exit(1);
|
|
511
|
-
}
|
|
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
|
+
});
|
|
512
470
|
});
|
|
513
471
|
program
|
|
514
472
|
.command('brand-pack')
|
|
@@ -523,13 +481,7 @@ program
|
|
|
523
481
|
.command('proposals')
|
|
524
482
|
.description('List open consent proposals with expiry countdown (BET-221)')
|
|
525
483
|
.action(async () => {
|
|
526
|
-
|
|
527
|
-
await runProposals();
|
|
528
|
-
}
|
|
529
|
-
catch (err) {
|
|
530
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
531
|
-
process.exit(1);
|
|
532
|
-
}
|
|
484
|
+
await runProposals();
|
|
533
485
|
});
|
|
534
486
|
program
|
|
535
487
|
.command('accept [proposal-id]')
|
|
@@ -537,29 +489,19 @@ program
|
|
|
537
489
|
.option('-a, --auto', 'Auto-approve all open proposals (skip contradictions)')
|
|
538
490
|
.action(async (proposalId, opts) => {
|
|
539
491
|
if (!proposalId && !opts.auto) {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
await runAccept({ proposalId, auto: opts.auto });
|
|
545
|
-
}
|
|
546
|
-
catch (err) {
|
|
547
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
548
|
-
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
|
+
});
|
|
549
496
|
}
|
|
497
|
+
await runAccept({ proposalId, auto: opts.auto });
|
|
550
498
|
});
|
|
551
499
|
program
|
|
552
500
|
.command('reject <proposal-id>')
|
|
553
501
|
.description('Reject a consent proposal with a required reason (BR-7)')
|
|
554
502
|
.requiredOption('-r, --reason <text>', 'Reason for rejection (required)')
|
|
555
503
|
.action(async (proposalId, opts) => {
|
|
556
|
-
|
|
557
|
-
await runReject({ proposalId, reason: opts.reason });
|
|
558
|
-
}
|
|
559
|
-
catch (err) {
|
|
560
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
561
|
-
process.exit(1);
|
|
562
|
-
}
|
|
504
|
+
await runReject({ proposalId, reason: opts.reason });
|
|
563
505
|
});
|
|
564
506
|
// --- Collections command (BET-280 Slice 1) ---
|
|
565
507
|
const collectionsCmd = program
|
|
@@ -569,13 +511,7 @@ collectionsCmd
|
|
|
569
511
|
.command('list')
|
|
570
512
|
.description('List all collections — slug, name, field count, icon')
|
|
571
513
|
.action(async () => {
|
|
572
|
-
|
|
573
|
-
await runCollectionsList();
|
|
574
|
-
}
|
|
575
|
-
catch (err) {
|
|
576
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
577
|
-
process.exit(1);
|
|
578
|
-
}
|
|
514
|
+
await runCollectionsList();
|
|
579
515
|
});
|
|
580
516
|
collectionsCmd
|
|
581
517
|
.command('get <slug>')
|
|
@@ -585,37 +521,68 @@ collectionsCmd
|
|
|
585
521
|
program.commands.find((c) => c.name() === 'collections')?.help();
|
|
586
522
|
return;
|
|
587
523
|
}
|
|
588
|
-
|
|
589
|
-
await runCollectionsGet({ slug: slug.trim() });
|
|
590
|
-
}
|
|
591
|
-
catch (err) {
|
|
592
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
593
|
-
process.exit(1);
|
|
594
|
-
}
|
|
524
|
+
await runCollectionsGet({ slug: slug.trim() });
|
|
595
525
|
});
|
|
596
526
|
collectionsCmd
|
|
597
527
|
.command('audit')
|
|
598
528
|
.description('Collection health report — classification, icon, displayHint coverage, schema gaps')
|
|
599
529
|
.action(async () => {
|
|
600
|
-
|
|
601
|
-
await runCollectionsAudit();
|
|
602
|
-
}
|
|
603
|
-
catch (err) {
|
|
604
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
605
|
-
process.exit(1);
|
|
606
|
-
}
|
|
530
|
+
await runCollectionsAudit();
|
|
607
531
|
});
|
|
608
532
|
collectionsCmd
|
|
609
533
|
.command('export')
|
|
610
534
|
.description('Export all system_collection_definitions with full classification metadata (admin only)')
|
|
611
535
|
.action(async () => {
|
|
612
|
-
|
|
613
|
-
await runCollectionsExport();
|
|
614
|
-
}
|
|
615
|
-
catch (err) {
|
|
616
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
617
|
-
process.exit(1);
|
|
618
|
-
}
|
|
536
|
+
await runCollectionsExport();
|
|
619
537
|
});
|
|
620
|
-
|
|
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);
|
|
621
588
|
//# sourceMappingURL=index.js.map
|