@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.
Files changed (78) hide show
  1. package/README.md +62 -178
  2. package/dist/__tests__/constants.test.js +1 -1
  3. package/dist/__tests__/constants.test.js.map +1 -1
  4. package/dist/__tests__/errors.test.d.ts +2 -0
  5. package/dist/__tests__/errors.test.d.ts.map +1 -0
  6. package/dist/__tests__/errors.test.js +117 -0
  7. package/dist/__tests__/errors.test.js.map +1 -0
  8. package/dist/__tests__/glossary.test.d.ts +2 -0
  9. package/dist/__tests__/glossary.test.d.ts.map +1 -0
  10. package/dist/__tests__/glossary.test.js +32 -0
  11. package/dist/__tests__/glossary.test.js.map +1 -0
  12. package/dist/__tests__/login.test.js +1 -1
  13. package/dist/__tests__/login.test.js.map +1 -1
  14. package/dist/__tests__/profiles.test.d.ts +2 -0
  15. package/dist/__tests__/profiles.test.d.ts.map +1 -0
  16. package/dist/__tests__/profiles.test.js +168 -0
  17. package/dist/__tests__/profiles.test.js.map +1 -0
  18. package/dist/commands/capture.d.ts.map +1 -1
  19. package/dist/commands/capture.js +3 -2
  20. package/dist/commands/capture.js.map +1 -1
  21. package/dist/commands/doctor.d.ts +9 -2
  22. package/dist/commands/doctor.d.ts.map +1 -1
  23. package/dist/commands/doctor.js +112 -25
  24. package/dist/commands/doctor.js.map +1 -1
  25. package/dist/commands/doctor.test.d.ts +2 -1
  26. package/dist/commands/doctor.test.d.ts.map +1 -1
  27. package/dist/commands/doctor.test.js +166 -3
  28. package/dist/commands/doctor.test.js.map +1 -1
  29. package/dist/commands/profile.d.ts +24 -0
  30. package/dist/commands/profile.d.ts.map +1 -0
  31. package/dist/commands/profile.js +82 -0
  32. package/dist/commands/profile.js.map +1 -0
  33. package/dist/commands/promote.d.ts.map +1 -1
  34. package/dist/commands/promote.js +3 -2
  35. package/dist/commands/promote.js.map +1 -1
  36. package/dist/formatters/promote.d.ts +1 -0
  37. package/dist/formatters/promote.d.ts.map +1 -1
  38. package/dist/formatters/promote.js +1 -0
  39. package/dist/formatters/promote.js.map +1 -1
  40. package/dist/index.js +240 -299
  41. package/dist/index.js.map +1 -1
  42. package/dist/lib/client.d.ts +2 -2
  43. package/dist/lib/client.d.ts.map +1 -1
  44. package/dist/lib/client.js +30 -11
  45. package/dist/lib/client.js.map +1 -1
  46. package/dist/lib/config.d.ts +9 -3
  47. package/dist/lib/config.d.ts.map +1 -1
  48. package/dist/lib/config.js +49 -12
  49. package/dist/lib/config.js.map +1 -1
  50. package/dist/lib/constants.d.ts +1 -1
  51. package/dist/lib/constants.d.ts.map +1 -1
  52. package/dist/lib/constants.js +1 -1
  53. package/dist/lib/constants.js.map +1 -1
  54. package/dist/lib/errors.d.ts +57 -0
  55. package/dist/lib/errors.d.ts.map +1 -0
  56. package/dist/lib/errors.js +65 -0
  57. package/dist/lib/errors.js.map +1 -0
  58. package/dist/lib/glossary.d.ts +19 -0
  59. package/dist/lib/glossary.d.ts.map +1 -0
  60. package/dist/lib/glossary.js +53 -0
  61. package/dist/lib/glossary.js.map +1 -0
  62. package/dist/lib/profiles.d.ts +34 -0
  63. package/dist/lib/profiles.d.ts.map +1 -0
  64. package/dist/lib/profiles.js +173 -0
  65. package/dist/lib/profiles.js.map +1 -0
  66. package/dist/lib/runner.d.ts +2 -0
  67. package/dist/lib/runner.d.ts.map +1 -1
  68. package/dist/lib/runner.js +33 -4
  69. package/dist/lib/runner.js.map +1 -1
  70. package/dist/lib/style.d.ts +65 -0
  71. package/dist/lib/style.d.ts.map +1 -0
  72. package/dist/lib/style.js +108 -0
  73. package/dist/lib/style.js.map +1 -0
  74. package/dist/lib/style.test.d.ts +7 -0
  75. package/dist/lib/style.test.d.ts.map +1 -0
  76. package/dist/lib/style.test.js +195 -0
  77. package/dist/lib/style.test.js.map +1 -0
  78. package/package.json +1 -1
@@ -0,0 +1,53 @@
1
+ /**
2
+ * glossary.ts — Key CLI terms and their definitions.
3
+ *
4
+ * Provides a static glossary of Product Brain CLI terminology,
5
+ * plus a formatter for TTY output.
6
+ *
7
+ * WP-302 Slice 4.
8
+ */
9
+ import { bold, dim } from './style.js';
10
+ export const GLOSSARY = [
11
+ {
12
+ term: 'workspace',
13
+ definition: 'Your product knowledge space. One workspace = one product.',
14
+ },
15
+ {
16
+ term: 'entry',
17
+ definition: 'A unit of product knowledge (bet, decision, tension, standard, etc.).',
18
+ },
19
+ {
20
+ term: 'collection',
21
+ definition: 'A group of related entries (work-packages, decisions, tensions, etc.).',
22
+ },
23
+ {
24
+ term: 'relation',
25
+ definition: 'A typed link between entries (part_of, depends_on, addresses, etc.).',
26
+ },
27
+ {
28
+ term: 'session',
29
+ definition: 'A tracked write session. Required before creating or updating entries.',
30
+ },
31
+ {
32
+ term: 'capture',
33
+ definition: 'Create a new entry on the Chain via pb capture.',
34
+ },
35
+ {
36
+ term: 'profile',
37
+ definition: 'A named workspace configuration with API key and endpoint.',
38
+ },
39
+ ];
40
+ /**
41
+ * Format the glossary for TTY output.
42
+ * Returns a multi-line string with bold terms and dimmed definitions.
43
+ */
44
+ export function formatGlossary() {
45
+ const lines = [];
46
+ const maxTermLen = Math.max(...GLOSSARY.map((g) => g.term.length));
47
+ for (const entry of GLOSSARY) {
48
+ const paddedTerm = entry.term.padEnd(maxTermLen);
49
+ lines.push(` ${bold(paddedTerm)} ${dim(entry.definition)}`);
50
+ }
51
+ return lines.join('\n');
52
+ }
53
+ //# sourceMappingURL=glossary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glossary.js","sourceRoot":"","sources":["../../src/lib/glossary.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAOvC,MAAM,CAAC,MAAM,QAAQ,GAAoB;IACvC;QACE,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,4DAA4D;KACzE;IACD;QACE,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,uEAAuE;KACpF;IACD;QACE,IAAI,EAAE,YAAY;QAClB,UAAU,EAAE,wEAAwE;KACrF;IACD;QACE,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,sEAAsE;KACnF;IACD;QACE,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,wEAAwE;KACrF;IACD;QACE,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,iDAAiD;KAC9D;IACD;QACE,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,4DAA4D;KACzE;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAEnE,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Profile storage and resolution — multi-workspace support for the Product Brain CLI.
3
+ * WP-302 Slice 2: Workspace Profiles + Config Refactor.
4
+ *
5
+ * Profile directory: ~/.config/productbrain/profiles/
6
+ * Active profile tracked in: ~/.config/productbrain/active-profile
7
+ * Resolution order: PB_PROFILE env > active-profile file > "default" > legacy .env
8
+ */
9
+ declare const HOME_CONFIG_DIR: string;
10
+ declare const PROFILES_DIR: string;
11
+ declare const ACTIVE_PROFILE_PATH: string;
12
+ declare const LEGACY_ENV_PATH: string;
13
+ export interface ProfileConfig {
14
+ apiKey: string;
15
+ siteUrl?: string;
16
+ }
17
+ /** List all profile names (sorted). */
18
+ export declare function listProfiles(): string[];
19
+ /** Get the active profile name. */
20
+ export declare function getActiveProfile(): string | null;
21
+ /** Create a new profile. */
22
+ export declare function createProfile(name: string, apiKey: string, siteUrl?: string): void;
23
+ /** Switch the active profile. */
24
+ export declare function useProfile(name: string): void;
25
+ /** Delete a profile. Cannot delete the last profile or the active profile. */
26
+ export declare function deleteProfile(name: string): void;
27
+ /**
28
+ * Resolve config from the active profile.
29
+ * Resolution order: PB_PROFILE env > active-profile file > "default" > legacy .env
30
+ * Returns null if no profile or config is available.
31
+ */
32
+ export declare function resolveProfileConfig(): ProfileConfig | null;
33
+ export { PROFILES_DIR, ACTIVE_PROFILE_PATH, LEGACY_ENV_PATH, HOME_CONFIG_DIR };
34
+ //# sourceMappingURL=profiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/lib/profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,QAAA,MAAM,eAAe,QAAgD,CAAC;AACtE,QAAA,MAAM,YAAY,QAAuC,CAAC;AAC1D,QAAA,MAAM,mBAAmB,QAA6C,CAAC;AACvE,QAAA,MAAM,eAAe,QAAmC,CAAC;AAKzD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAoDD,uCAAuC;AACvC,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAOvC;AAED,mCAAmC;AACnC,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAWhD;AAED,4BAA4B;AAC5B,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAwBlF;AAED,iCAAiC;AACjC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAS7C;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAmBhD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,aAAa,GAAG,IAAI,CAgC3D;AAGD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Profile storage and resolution — multi-workspace support for the Product Brain CLI.
3
+ * WP-302 Slice 2: Workspace Profiles + Config Refactor.
4
+ *
5
+ * Profile directory: ~/.config/productbrain/profiles/
6
+ * Active profile tracked in: ~/.config/productbrain/active-profile
7
+ * Resolution order: PB_PROFILE env > active-profile file > "default" > legacy .env
8
+ */
9
+ import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, readdirSync } from 'fs';
10
+ import { resolve } from 'path';
11
+ import { homedir } from 'os';
12
+ import { CLIError, ErrorCode } from './errors.js';
13
+ const HOME_CONFIG_DIR = resolve(homedir(), '.config', 'productbrain');
14
+ const PROFILES_DIR = resolve(HOME_CONFIG_DIR, 'profiles');
15
+ const ACTIVE_PROFILE_PATH = resolve(HOME_CONFIG_DIR, 'active-profile');
16
+ const LEGACY_ENV_PATH = resolve(HOME_CONFIG_DIR, '.env');
17
+ /** Profile name validation: lowercase alphanumeric + hyphens, 1-32 chars. */
18
+ const PROFILE_NAME_RE = /^[a-z0-9][a-z0-9-]{0,31}$/;
19
+ function validateProfileName(name) {
20
+ if (!PROFILE_NAME_RE.test(name)) {
21
+ throw new CLIError(`Invalid profile name "${name}". Must be 1-32 chars, lowercase alphanumeric + hyphens, starting with alphanumeric.`, { code: ErrorCode.PROFILE_NAME_INVALID, category: 'profile' });
22
+ }
23
+ }
24
+ function profilePath(name) {
25
+ return resolve(PROFILES_DIR, `${name}.env`);
26
+ }
27
+ function parseEnvFile(content) {
28
+ const vars = {};
29
+ for (const line of content.split('\n')) {
30
+ const m = line.match(/^([^#=]+)=(.*)$/);
31
+ if (m) {
32
+ vars[m[1].trim()] = m[2].trim().replace(/^["']|["']$/g, '');
33
+ }
34
+ }
35
+ return vars;
36
+ }
37
+ function readProfileFile(path) {
38
+ if (!existsSync(path))
39
+ return null;
40
+ const vars = parseEnvFile(readFileSync(path, 'utf8'));
41
+ const apiKey = vars['PRODUCTBRAIN_API_KEY'] || '';
42
+ if (!apiKey)
43
+ return null;
44
+ return {
45
+ apiKey,
46
+ siteUrl: vars['CONVEX_SITE_URL'] || undefined,
47
+ };
48
+ }
49
+ /**
50
+ * Auto-migrate legacy ~/.config/productbrain/.env to profiles/default.env.
51
+ * Only runs if legacy file exists AND no profiles directory exists.
52
+ */
53
+ function autoMigrate() {
54
+ if (existsSync(PROFILES_DIR))
55
+ return false;
56
+ if (!existsSync(LEGACY_ENV_PATH))
57
+ return false;
58
+ mkdirSync(PROFILES_DIR, { recursive: true });
59
+ const content = readFileSync(LEGACY_ENV_PATH, 'utf8');
60
+ writeFileSync(profilePath('default'), content, { mode: 0o600 });
61
+ writeFileSync(ACTIVE_PROFILE_PATH, 'default', { mode: 0o600 });
62
+ return true;
63
+ }
64
+ /** List all profile names (sorted). */
65
+ export function listProfiles() {
66
+ autoMigrate();
67
+ if (!existsSync(PROFILES_DIR))
68
+ return [];
69
+ return readdirSync(PROFILES_DIR)
70
+ .filter((f) => f.endsWith('.env'))
71
+ .map((f) => f.replace(/\.env$/, ''))
72
+ .sort();
73
+ }
74
+ /** Get the active profile name. */
75
+ export function getActiveProfile() {
76
+ // PB_PROFILE env var takes precedence
77
+ const envProfile = process.env.PB_PROFILE?.trim();
78
+ if (envProfile)
79
+ return envProfile;
80
+ if (existsSync(ACTIVE_PROFILE_PATH)) {
81
+ const name = readFileSync(ACTIVE_PROFILE_PATH, 'utf8').trim();
82
+ if (name)
83
+ return name;
84
+ }
85
+ return null;
86
+ }
87
+ /** Create a new profile. */
88
+ export function createProfile(name, apiKey, siteUrl) {
89
+ validateProfileName(name);
90
+ if (existsSync(profilePath(name))) {
91
+ throw new CLIError(`Profile "${name}" already exists.`, { code: ErrorCode.PROFILE_ALREADY_EXISTS, category: 'profile' });
92
+ }
93
+ mkdirSync(PROFILES_DIR, { recursive: true });
94
+ const lines = [
95
+ `# Product Brain CLI profile: ${name}`,
96
+ `PRODUCTBRAIN_API_KEY=${apiKey}`,
97
+ ];
98
+ if (siteUrl) {
99
+ lines.push(`CONVEX_SITE_URL=${siteUrl}`);
100
+ }
101
+ lines.push('');
102
+ writeFileSync(profilePath(name), lines.join('\n'), { mode: 0o600 });
103
+ // If this is the first profile, make it active
104
+ if (!existsSync(ACTIVE_PROFILE_PATH)) {
105
+ writeFileSync(ACTIVE_PROFILE_PATH, name, { mode: 0o600 });
106
+ }
107
+ }
108
+ /** Switch the active profile. */
109
+ export function useProfile(name) {
110
+ validateProfileName(name);
111
+ if (!existsSync(profilePath(name))) {
112
+ throw new CLIError(`Profile "${name}" does not exist.`, { code: ErrorCode.PROFILE_NOT_FOUND, category: 'profile' });
113
+ }
114
+ mkdirSync(HOME_CONFIG_DIR, { recursive: true });
115
+ writeFileSync(ACTIVE_PROFILE_PATH, name, { mode: 0o600 });
116
+ }
117
+ /** Delete a profile. Cannot delete the last profile or the active profile. */
118
+ export function deleteProfile(name) {
119
+ validateProfileName(name);
120
+ const path = profilePath(name);
121
+ if (!existsSync(path)) {
122
+ throw new CLIError(`Profile "${name}" does not exist.`, { code: ErrorCode.PROFILE_NOT_FOUND, category: 'profile' });
123
+ }
124
+ const profiles = listProfiles();
125
+ if (profiles.length <= 1) {
126
+ throw new CLIError(`Cannot delete the last profile "${name}".`, { code: ErrorCode.PROFILE_IS_LAST, category: 'profile' });
127
+ }
128
+ const active = getActiveProfile();
129
+ if (active === name) {
130
+ throw new CLIError(`Cannot delete the active profile "${name}". Switch to another profile first.`, { code: ErrorCode.PROFILE_IS_ACTIVE, category: 'profile', guidance: 'Run `pb profile use <other-profile>` first.' });
131
+ }
132
+ unlinkSync(path);
133
+ }
134
+ /**
135
+ * Resolve config from the active profile.
136
+ * Resolution order: PB_PROFILE env > active-profile file > "default" > legacy .env
137
+ * Returns null if no profile or config is available.
138
+ */
139
+ export function resolveProfileConfig() {
140
+ autoMigrate();
141
+ // 1. Check PB_PROFILE env var
142
+ const envProfile = process.env.PB_PROFILE?.trim();
143
+ if (envProfile) {
144
+ const config = readProfileFile(profilePath(envProfile));
145
+ if (config)
146
+ return config;
147
+ }
148
+ // 2. Check active-profile file
149
+ if (existsSync(ACTIVE_PROFILE_PATH)) {
150
+ const name = readFileSync(ACTIVE_PROFILE_PATH, 'utf8').trim();
151
+ if (name) {
152
+ const config = readProfileFile(profilePath(name));
153
+ if (config)
154
+ return config;
155
+ }
156
+ }
157
+ // 3. Check "default" profile
158
+ {
159
+ const config = readProfileFile(profilePath('default'));
160
+ if (config)
161
+ return config;
162
+ }
163
+ // 4. Fall back to legacy .env
164
+ {
165
+ const config = readProfileFile(LEGACY_ENV_PATH);
166
+ if (config)
167
+ return config;
168
+ }
169
+ return null;
170
+ }
171
+ // Exported for testing
172
+ export { PROFILES_DIR, ACTIVE_PROFILE_PATH, LEGACY_ENV_PATH, HOME_CONFIG_DIR };
173
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/lib/profiles.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,8 @@ export declare function setOutputMode(mode: 'auto' | 'json' | 'pretty'): void;
14
14
  export declare function getOutputMode(): 'auto' | 'json' | 'pretty';
15
15
  /** Returns true if the current output mode resolves to JSON. */
16
16
  export declare function isJsonMode(): boolean;
17
+ export declare function setQuietMode(quiet: boolean): void;
18
+ export declare function isQuietMode(): boolean;
17
19
  export interface RunCliCommandOptions<T> {
18
20
  /** The async function that performs the MCP call and returns data. */
19
21
  fn: () => Promise<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEpE;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,MAAM,GAAG,QAAQ,CAE1D;AAED,gEAAgE;AAChE,wBAAgB,UAAU,IAAI,OAAO,CAKpC;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACrC,sEAAsE;IACtE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;IACrB,kDAAkD;IAClD,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtF"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEpE;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,MAAM,GAAG,QAAQ,CAE1D;AAED,gEAAgE;AAChE,wBAAgB,UAAU,IAAI,OAAO,CAKpC;AASD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEjD;AAED,wBAAgB,WAAW,IAAI,OAAO,CAIrC;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACrC,sEAAsE;IACtE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;IACrB,kDAAkD;IAClD,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCtF"}
@@ -11,6 +11,7 @@
11
11
  * Chain: DEC-299 (JSON-default), STD-65 (agent responses: structured, parseable, minimal)
12
12
  */
13
13
  import { stripConvexInternals } from './strip.js';
14
+ import { CLIError } from './errors.js';
14
15
  /**
15
16
  * Global output mode — set by the CLI entry point when --json or --pretty flags are parsed.
16
17
  * 'auto' = detect via TTY; 'json' = force JSON; 'pretty' = force human.
@@ -31,6 +32,21 @@ export function isJsonMode() {
31
32
  // auto: non-TTY (piped) → JSON, TTY → human
32
33
  return !process.stdout.isTTY;
33
34
  }
35
+ /**
36
+ * Quiet mode — suppresses non-essential output (banners, hints, progress).
37
+ * Only the essential result (data line or error) is shown.
38
+ * WP-302 Slice 1.
39
+ */
40
+ let quietMode = false;
41
+ export function setQuietMode(quiet) {
42
+ quietMode = quiet;
43
+ }
44
+ export function isQuietMode() {
45
+ // JSON mode is inherently quiet — no decorative output needed
46
+ if (isJsonMode())
47
+ return true;
48
+ return quietMode;
49
+ }
34
50
  /**
35
51
  * Execute a CLI command with unified output handling.
36
52
  *
@@ -52,12 +68,25 @@ export async function runCliCommand(options) {
52
68
  }
53
69
  }
54
70
  catch (err) {
55
- const message = err instanceof Error ? err.message : String(err);
56
- if (json) {
57
- process.stderr.write(JSON.stringify({ error: message }) + '\n');
71
+ if (err instanceof CLIError) {
72
+ if (json) {
73
+ process.stderr.write(JSON.stringify(err.toJSON()) + '\n');
74
+ }
75
+ else {
76
+ process.stderr.write(err.message + '\n');
77
+ if (err.guidance) {
78
+ process.stderr.write(err.guidance + '\n');
79
+ }
80
+ }
58
81
  }
59
82
  else {
60
- process.stderr.write(message + '\n');
83
+ const message = err instanceof Error ? err.message : String(err);
84
+ if (json) {
85
+ process.stderr.write(JSON.stringify({ error: message, code: 'INTERNAL', category: 'internal' }) + '\n');
86
+ }
87
+ else {
88
+ process.stderr.write(message + '\n');
89
+ }
61
90
  }
62
91
  process.exit(1);
63
92
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD;;;GAGG;AACH,IAAI,UAAU,GAA+B,MAAM,CAAC;AAEpD,MAAM,UAAU,aAAa,CAAC,IAAgC;IAC5D,UAAU,GAAG,IAAI,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU;IACxB,IAAI,UAAU,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,UAAU,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC1C,4CAA4C;IAC5C,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;AAC/B,CAAC;AASD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAI,OAAgC;IACrE,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAE1B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,EAAE,EAAE,CAAC;QAEhC,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAEjE,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;QACvC,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * CLI voice and style — centralized output formatting for Product Brain CLI.
3
+ * WP-302 Slice 1: Brand Voice + --quiet.
4
+ *
5
+ * Design: "instrument panel" — clear status, clear next action.
6
+ * Tone: confident, concise, helpful. Not chatty. Not corporate.
7
+ *
8
+ * All color output respects NO_COLOR env (https://no-color.org/) and TTY detection.
9
+ * Quiet mode suppresses banners, hints, and progress — only essential results show.
10
+ */
11
+ /** Returns true if the terminal supports color output. */
12
+ export declare function supportsColor(): boolean;
13
+ export declare const green: (s: string) => string;
14
+ export declare const red: (s: string) => string;
15
+ export declare const yellow: (s: string) => string;
16
+ export declare const cyan: (s: string) => string;
17
+ export declare const dim: (s: string) => string;
18
+ export declare const bold: (s: string) => string;
19
+ export declare const icons: {
20
+ readonly pass: "✓";
21
+ readonly fail: "✗";
22
+ readonly warn: "⚠";
23
+ readonly arrow: "→";
24
+ readonly dot: "·";
25
+ };
26
+ export declare const copy: {
27
+ readonly captured: "Captured.";
28
+ readonly promoted: "Promoted.";
29
+ readonly linked: "Linked.";
30
+ readonly updated: "Updated.";
31
+ readonly sessionStarted: "Session started.";
32
+ readonly sessionClosed: "Session closed.";
33
+ readonly noSession: "No active session. Run `pb session start` first.";
34
+ readonly sessionRequired: "Write access requires a tracked session (DEC-9).";
35
+ readonly notFound: "Not found.";
36
+ readonly authFailed: "Authentication failed.";
37
+ readonly nextOrient: "Next: pb orient -b";
38
+ readonly nextCapture: "Next: pb capture \"...\"";
39
+ readonly nextGet: (entryId: string) => string;
40
+ readonly nextFields: (collection: string) => string;
41
+ };
42
+ /**
43
+ * Print a command banner. Suppressed in quiet mode.
44
+ * Example: "Product Brain — Doctor"
45
+ */
46
+ export declare function banner(title: string): void;
47
+ /**
48
+ * Print an actionable hint line. Suppressed in quiet mode.
49
+ * Example: " Hint: Run `pb orient -b` to see your workspace."
50
+ */
51
+ export declare function hint(text: string): void;
52
+ /**
53
+ * Print a progress line (e.g. "Classifying..."). Suppressed in quiet mode.
54
+ */
55
+ export declare function progress(text: string): void;
56
+ /**
57
+ * Format a section heading — bold cyan text.
58
+ */
59
+ export declare function heading(text: string): string;
60
+ /**
61
+ * Format a check result line for doctor-style output.
62
+ * Uses colored icons for pass/fail/warn.
63
+ */
64
+ export declare function formatCheck(status: 'pass' | 'fail' | 'warn', label: string, detail: string): string;
65
+ //# sourceMappingURL=style.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/lib/style.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,0DAA0D;AAC1D,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAMD,eAAO,MAAM,KAAK,GAAI,GAAG,MAAM,KAAG,MAAuD,CAAC;AAC1F,eAAO,MAAM,GAAG,GAAI,GAAG,MAAM,KAAG,MAAuD,CAAC;AACxF,eAAO,MAAM,MAAM,GAAI,GAAG,MAAM,KAAG,MAAuD,CAAC;AAC3F,eAAO,MAAM,IAAI,GAAI,GAAG,MAAM,KAAG,MAAuD,CAAC;AACzF,eAAO,MAAM,GAAG,GAAI,GAAG,MAAM,KAAG,MAAsD,CAAC;AACvF,eAAO,MAAM,IAAI,GAAI,GAAG,MAAM,KAAG,MAAsD,CAAC;AAMxF,eAAO,MAAM,KAAK;;;;;;CAMR,CAAC;AAMX,eAAO,MAAM,IAAI;;;;;;;;;;;;;gCAkBI,MAAM;sCACA,MAAM;CACvB,CAAC;AAMX;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG1C;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGvC;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAG3C;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CASnG"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * CLI voice and style — centralized output formatting for Product Brain CLI.
3
+ * WP-302 Slice 1: Brand Voice + --quiet.
4
+ *
5
+ * Design: "instrument panel" — clear status, clear next action.
6
+ * Tone: confident, concise, helpful. Not chatty. Not corporate.
7
+ *
8
+ * All color output respects NO_COLOR env (https://no-color.org/) and TTY detection.
9
+ * Quiet mode suppresses banners, hints, and progress — only essential results show.
10
+ */
11
+ import { isQuietMode } from './runner.js';
12
+ // ---------------------------------------------------------------------------
13
+ // Color support detection
14
+ // ---------------------------------------------------------------------------
15
+ /** Returns true if the terminal supports color output. */
16
+ export function supportsColor() {
17
+ return Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
18
+ }
19
+ // ---------------------------------------------------------------------------
20
+ // Color helpers — degrade gracefully when NO_COLOR is set or not a TTY
21
+ // ---------------------------------------------------------------------------
22
+ export const green = (s) => (supportsColor() ? `\x1b[32m${s}\x1b[0m` : s);
23
+ export const red = (s) => (supportsColor() ? `\x1b[31m${s}\x1b[0m` : s);
24
+ export const yellow = (s) => (supportsColor() ? `\x1b[33m${s}\x1b[0m` : s);
25
+ export const cyan = (s) => (supportsColor() ? `\x1b[36m${s}\x1b[0m` : s);
26
+ export const dim = (s) => (supportsColor() ? `\x1b[2m${s}\x1b[0m` : s);
27
+ export const bold = (s) => (supportsColor() ? `\x1b[1m${s}\x1b[0m` : s);
28
+ // ---------------------------------------------------------------------------
29
+ // Status icons
30
+ // ---------------------------------------------------------------------------
31
+ export const icons = {
32
+ pass: '\u2713', // checkmark
33
+ fail: '\u2717', // x
34
+ warn: '\u26A0', // warning triangle
35
+ arrow: '\u2192', // right arrow
36
+ dot: '\u00B7', // middle dot
37
+ };
38
+ // ---------------------------------------------------------------------------
39
+ // Copy constants — instrument panel voice
40
+ // ---------------------------------------------------------------------------
41
+ export const copy = {
42
+ // Success preambles
43
+ captured: 'Captured.',
44
+ promoted: 'Promoted.',
45
+ linked: 'Linked.',
46
+ updated: 'Updated.',
47
+ sessionStarted: 'Session started.',
48
+ sessionClosed: 'Session closed.',
49
+ // Error preambles
50
+ noSession: 'No active session. Run `pb session start` first.',
51
+ sessionRequired: 'Write access requires a tracked session (DEC-9).',
52
+ notFound: 'Not found.',
53
+ authFailed: 'Authentication failed.',
54
+ // Next-step prompts
55
+ nextOrient: 'Next: pb orient -b',
56
+ nextCapture: 'Next: pb capture "..."',
57
+ nextGet: (entryId) => `View: pb get ${entryId}`,
58
+ nextFields: (collection) => `Fields: pb fields ${collection}`,
59
+ };
60
+ // ---------------------------------------------------------------------------
61
+ // Structured output helpers
62
+ // ---------------------------------------------------------------------------
63
+ /**
64
+ * Print a command banner. Suppressed in quiet mode.
65
+ * Example: "Product Brain — Doctor"
66
+ */
67
+ export function banner(title) {
68
+ if (isQuietMode())
69
+ return;
70
+ process.stdout.write(`\n${bold(`Product Brain — ${title}`)}\n\n`);
71
+ }
72
+ /**
73
+ * Print an actionable hint line. Suppressed in quiet mode.
74
+ * Example: " Hint: Run `pb orient -b` to see your workspace."
75
+ */
76
+ export function hint(text) {
77
+ if (isQuietMode())
78
+ return;
79
+ process.stdout.write(` ${dim(text)}\n`);
80
+ }
81
+ /**
82
+ * Print a progress line (e.g. "Classifying..."). Suppressed in quiet mode.
83
+ */
84
+ export function progress(text) {
85
+ if (isQuietMode())
86
+ return;
87
+ process.stdout.write(`${text}\n`);
88
+ }
89
+ /**
90
+ * Format a section heading — bold cyan text.
91
+ */
92
+ export function heading(text) {
93
+ return bold(cyan(text));
94
+ }
95
+ /**
96
+ * Format a check result line for doctor-style output.
97
+ * Uses colored icons for pass/fail/warn.
98
+ */
99
+ export function formatCheck(status, label, detail) {
100
+ const icon = status === 'pass'
101
+ ? green(icons.pass)
102
+ : status === 'fail'
103
+ ? red(icons.fail)
104
+ : yellow(icons.warn);
105
+ const paddedLabel = label.padEnd(16);
106
+ return `${icon} ${paddedLabel}${detail}`;
107
+ }
108
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/lib/style.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Tests for CLI style module — WP-302 Slice 1.
3
+ * Verifies color helpers respect NO_COLOR, quiet mode suppresses banners,
4
+ * and copy constants are non-empty strings.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=style.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.test.d.ts","sourceRoot":"","sources":["../../src/lib/style.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}