@productbrain/cli 0.1.0-beta.1 → 0.1.0-beta.14
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 +167 -0
- package/dist/__tests__/audit.test.d.ts +2 -0
- package/dist/__tests__/audit.test.d.ts.map +1 -0
- package/dist/__tests__/audit.test.js +394 -0
- package/dist/__tests__/audit.test.js.map +1 -0
- package/dist/__tests__/capture.test.d.ts +2 -0
- package/dist/__tests__/capture.test.d.ts.map +1 -0
- package/dist/__tests__/capture.test.js +86 -0
- package/dist/__tests__/capture.test.js.map +1 -0
- package/dist/__tests__/constellation.test.d.ts +2 -0
- package/dist/__tests__/constellation.test.d.ts.map +1 -0
- package/dist/__tests__/constellation.test.js +260 -0
- package/dist/__tests__/constellation.test.js.map +1 -0
- package/dist/__tests__/context-strategy.test.d.ts +2 -0
- package/dist/__tests__/context-strategy.test.d.ts.map +1 -0
- package/dist/__tests__/context-strategy.test.js +79 -0
- package/dist/__tests__/context-strategy.test.js.map +1 -0
- package/dist/__tests__/fields.test.d.ts +2 -0
- package/dist/__tests__/fields.test.d.ts.map +1 -0
- package/dist/__tests__/fields.test.js +238 -0
- package/dist/__tests__/fields.test.js.map +1 -0
- package/dist/__tests__/handshake.test.d.ts +2 -0
- package/dist/__tests__/handshake.test.d.ts.map +1 -0
- package/dist/__tests__/handshake.test.js +187 -0
- package/dist/__tests__/handshake.test.js.map +1 -0
- package/dist/__tests__/ingest.test.d.ts +2 -0
- package/dist/__tests__/ingest.test.d.ts.map +1 -0
- package/dist/__tests__/ingest.test.js +185 -0
- package/dist/__tests__/ingest.test.js.map +1 -0
- package/dist/__tests__/promote.test.d.ts +2 -0
- package/dist/__tests__/promote.test.d.ts.map +1 -0
- package/dist/__tests__/promote.test.js +139 -0
- package/dist/__tests__/promote.test.js.map +1 -0
- package/dist/__tests__/proposals.test.d.ts +2 -0
- package/dist/__tests__/proposals.test.d.ts.map +1 -0
- package/dist/__tests__/proposals.test.js +190 -0
- package/dist/__tests__/proposals.test.js.map +1 -0
- package/dist/__tests__/relate.test.d.ts +2 -0
- package/dist/__tests__/relate.test.d.ts.map +1 -0
- package/dist/__tests__/relate.test.js +105 -0
- package/dist/__tests__/relate.test.js.map +1 -0
- package/dist/__tests__/repo-detect.test.d.ts +2 -0
- package/dist/__tests__/repo-detect.test.d.ts.map +1 -0
- package/dist/__tests__/repo-detect.test.js +119 -0
- package/dist/__tests__/repo-detect.test.js.map +1 -0
- package/dist/__tests__/runner.test.d.ts +2 -0
- package/dist/__tests__/runner.test.d.ts.map +1 -0
- package/dist/__tests__/runner.test.js +215 -0
- package/dist/__tests__/runner.test.js.map +1 -0
- package/dist/__tests__/session-touch.test.d.ts +2 -0
- package/dist/__tests__/session-touch.test.d.ts.map +1 -0
- package/dist/__tests__/session-touch.test.js +134 -0
- package/dist/__tests__/session-touch.test.js.map +1 -0
- package/dist/__tests__/session.test.d.ts +2 -0
- package/dist/__tests__/session.test.d.ts.map +1 -0
- package/dist/__tests__/session.test.js +52 -0
- package/dist/__tests__/session.test.js.map +1 -0
- package/dist/__tests__/strip.test.d.ts +2 -0
- package/dist/__tests__/strip.test.d.ts.map +1 -0
- package/dist/__tests__/strip.test.js +136 -0
- package/dist/__tests__/strip.test.js.map +1 -0
- package/dist/__tests__/update.test.d.ts +2 -0
- package/dist/__tests__/update.test.d.ts.map +1 -0
- package/dist/__tests__/update.test.js +237 -0
- package/dist/__tests__/update.test.js.map +1 -0
- package/dist/commands/accept.d.ts +18 -0
- package/dist/commands/accept.d.ts.map +1 -0
- package/dist/commands/accept.js +72 -0
- package/dist/commands/accept.js.map +1 -0
- package/dist/commands/audit.d.ts +25 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +188 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/brand-pack.d.ts +2 -0
- package/dist/commands/brand-pack.d.ts.map +1 -0
- package/dist/commands/brand-pack.js +25 -0
- package/dist/commands/brand-pack.js.map +1 -0
- package/dist/commands/brief.d.ts +28 -0
- package/dist/commands/brief.d.ts.map +1 -0
- package/dist/commands/brief.js +70 -0
- package/dist/commands/brief.js.map +1 -0
- package/dist/commands/capture.d.ts +21 -0
- package/dist/commands/capture.d.ts.map +1 -0
- package/dist/commands/capture.js +100 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/chain-walk.d.ts +14 -0
- package/dist/commands/chain-walk.d.ts.map +1 -0
- package/dist/commands/chain-walk.js +33 -0
- package/dist/commands/chain-walk.js.map +1 -0
- package/dist/commands/changes.d.ts +11 -0
- package/dist/commands/changes.d.ts.map +1 -0
- package/dist/commands/changes.js +41 -0
- package/dist/commands/changes.js.map +1 -0
- package/dist/commands/constellation.d.ts +11 -0
- package/dist/commands/constellation.d.ts.map +1 -0
- package/dist/commands/constellation.js +28 -0
- package/dist/commands/constellation.js.map +1 -0
- package/dist/commands/context.d.ts +2 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +19 -9
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/cross-cut.d.ts +11 -0
- package/dist/commands/cross-cut.d.ts.map +1 -0
- package/dist/commands/cross-cut.js +23 -0
- package/dist/commands/cross-cut.js.map +1 -0
- package/dist/commands/fields.d.ts +9 -0
- package/dist/commands/fields.d.ts.map +1 -0
- package/dist/commands/fields.js +26 -0
- package/dist/commands/fields.js.map +1 -0
- package/dist/commands/get.d.ts +8 -1
- package/dist/commands/get.d.ts.map +1 -1
- package/dist/commands/get.js +55 -6
- package/dist/commands/get.js.map +1 -1
- package/dist/commands/handshake.d.ts +18 -0
- package/dist/commands/handshake.d.ts.map +1 -0
- package/dist/commands/handshake.js +378 -0
- package/dist/commands/handshake.js.map +1 -0
- package/dist/commands/ingest.d.ts +14 -0
- package/dist/commands/ingest.d.ts.map +1 -0
- package/dist/commands/ingest.js +181 -0
- package/dist/commands/ingest.js.map +1 -0
- package/dist/commands/login.d.ts +5 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +53 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/orient.d.ts +2 -0
- package/dist/commands/orient.d.ts.map +1 -1
- package/dist/commands/orient.js +17 -8
- package/dist/commands/orient.js.map +1 -1
- package/dist/commands/promote.d.ts +12 -0
- package/dist/commands/promote.d.ts.map +1 -0
- package/dist/commands/promote.js +48 -0
- package/dist/commands/promote.js.map +1 -0
- package/dist/commands/proposals.d.ts +9 -0
- package/dist/commands/proposals.d.ts.map +1 -0
- package/dist/commands/proposals.js +24 -0
- package/dist/commands/proposals.js.map +1 -0
- package/dist/commands/reject.d.ts +14 -0
- package/dist/commands/reject.d.ts.map +1 -0
- package/dist/commands/reject.js +37 -0
- package/dist/commands/reject.js.map +1 -0
- package/dist/commands/relate.d.ts +16 -0
- package/dist/commands/relate.d.ts.map +1 -0
- package/dist/commands/relate.js +80 -0
- package/dist/commands/relate.js.map +1 -0
- package/dist/commands/search.d.ts +1 -0
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +9 -3
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +134 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/update.d.ts +16 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +139 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.d.ts +13 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +43 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/formatters/audit.d.ts +46 -0
- package/dist/formatters/audit.d.ts.map +1 -0
- package/dist/formatters/audit.js +81 -0
- package/dist/formatters/audit.js.map +1 -0
- package/dist/formatters/brief.d.ts +112 -0
- package/dist/formatters/brief.d.ts.map +1 -0
- package/dist/formatters/brief.js +179 -0
- package/dist/formatters/brief.js.map +1 -0
- package/dist/formatters/capture.d.ts +30 -0
- package/dist/formatters/capture.d.ts.map +1 -0
- package/dist/formatters/capture.js +58 -0
- package/dist/formatters/capture.js.map +1 -0
- package/dist/formatters/chain-walk.d.ts +33 -0
- package/dist/formatters/chain-walk.d.ts.map +1 -0
- package/dist/formatters/chain-walk.js +54 -0
- package/dist/formatters/chain-walk.js.map +1 -0
- package/dist/formatters/changes.d.ts +25 -0
- package/dist/formatters/changes.d.ts.map +1 -0
- package/dist/formatters/changes.js +60 -0
- package/dist/formatters/changes.js.map +1 -0
- package/dist/formatters/constellation.d.ts +34 -0
- package/dist/formatters/constellation.d.ts.map +1 -0
- package/dist/formatters/constellation.js +38 -0
- package/dist/formatters/constellation.js.map +1 -0
- package/dist/formatters/cross-cut.d.ts +21 -0
- package/dist/formatters/cross-cut.d.ts.map +1 -0
- package/dist/formatters/cross-cut.js +32 -0
- package/dist/formatters/cross-cut.js.map +1 -0
- package/dist/formatters/entry.d.ts +5 -0
- package/dist/formatters/entry.d.ts.map +1 -1
- package/dist/formatters/entry.js +5 -1
- package/dist/formatters/entry.js.map +1 -1
- package/dist/formatters/fields.d.ts +32 -0
- package/dist/formatters/fields.d.ts.map +1 -0
- package/dist/formatters/fields.js +49 -0
- package/dist/formatters/fields.js.map +1 -0
- package/dist/formatters/handshake.d.ts +17 -0
- package/dist/formatters/handshake.d.ts.map +1 -0
- package/dist/formatters/handshake.js +51 -0
- package/dist/formatters/handshake.js.map +1 -0
- package/dist/formatters/orient.d.ts +1 -0
- package/dist/formatters/orient.d.ts.map +1 -1
- package/dist/formatters/orient.js +4 -2
- package/dist/formatters/orient.js.map +1 -1
- package/dist/formatters/promote.d.ts +29 -0
- package/dist/formatters/promote.d.ts.map +1 -0
- package/dist/formatters/promote.js +38 -0
- package/dist/formatters/promote.js.map +1 -0
- package/dist/formatters/proposals.d.ts +45 -0
- package/dist/formatters/proposals.d.ts.map +1 -0
- package/dist/formatters/proposals.js +62 -0
- package/dist/formatters/proposals.js.map +1 -0
- package/dist/formatters/relate.d.ts +12 -0
- package/dist/formatters/relate.d.ts.map +1 -0
- package/dist/formatters/relate.js +13 -0
- package/dist/formatters/relate.js.map +1 -0
- package/dist/formatters/session.d.ts +11 -0
- package/dist/formatters/session.d.ts.map +1 -0
- package/dist/formatters/session.js +51 -0
- package/dist/formatters/session.js.map +1 -0
- package/dist/formatters/update.d.ts +17 -0
- package/dist/formatters/update.d.ts.map +1 -0
- package/dist/formatters/update.js +43 -0
- package/dist/formatters/update.js.map +1 -0
- package/dist/formatters/verify.d.ts +11 -0
- package/dist/formatters/verify.d.ts.map +1 -0
- package/dist/formatters/verify.js +11 -0
- package/dist/formatters/verify.js.map +1 -0
- package/dist/generators/adapters.d.ts +10 -0
- package/dist/generators/adapters.d.ts.map +1 -0
- package/dist/generators/adapters.js +102 -0
- package/dist/generators/adapters.js.map +1 -0
- package/dist/generators/briefing-md.d.ts +8 -0
- package/dist/generators/briefing-md.d.ts.map +1 -0
- package/dist/generators/briefing-md.js +51 -0
- package/dist/generators/briefing-md.js.map +1 -0
- package/dist/generators/context-md.d.ts +8 -0
- package/dist/generators/context-md.d.ts.map +1 -0
- package/dist/generators/context-md.js +123 -0
- package/dist/generators/context-md.js.map +1 -0
- package/dist/generators/portable-knowledge.d.ts +72 -0
- package/dist/generators/portable-knowledge.d.ts.map +1 -0
- package/dist/generators/portable-knowledge.js +246 -0
- package/dist/generators/portable-knowledge.js.map +1 -0
- package/dist/generators/portable-knowledge.test.d.ts +2 -0
- package/dist/generators/portable-knowledge.test.d.ts.map +1 -0
- package/dist/generators/portable-knowledge.test.js +399 -0
- package/dist/generators/portable-knowledge.test.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +462 -6
- package/dist/index.js.map +1 -1
- package/dist/lib/client.d.ts +34 -0
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/client.js +114 -9
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/config.d.ts +19 -2
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +95 -14
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/repo-detect.d.ts +14 -0
- package/dist/lib/repo-detect.d.ts.map +1 -0
- package/dist/lib/repo-detect.js +58 -0
- package/dist/lib/repo-detect.js.map +1 -0
- package/dist/lib/runner.d.ts +31 -0
- package/dist/lib/runner.d.ts.map +1 -0
- package/dist/lib/runner.js +65 -0
- package/dist/lib/runner.js.map +1 -0
- package/dist/lib/session.d.ts +17 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/session.js +43 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/strip.d.ts +11 -0
- package/dist/lib/strip.d.ts.map +1 -0
- package/dist/lib/strip.js +26 -0
- package/dist/lib/strip.js.map +1 -0
- package/package.json +8 -4
package/dist/lib/client.js
CHANGED
|
@@ -1,20 +1,125 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP HTTP client — POST to Convex gateway with Bearer auth.
|
|
3
|
+
*
|
|
4
|
+
* TEN-729: Detects sandbox-blocked fetch (Claude Code, Cursor, etc.) and
|
|
5
|
+
* provides an actionable error instead of the opaque "fetch failed".
|
|
6
|
+
*
|
|
7
|
+
* Sandbox proxy support: Node.js fetch (undici) ignores proxy env vars by
|
|
8
|
+
* default. We detect HTTPS_PROXY and configure a ProxyAgent so pb works in
|
|
9
|
+
* sandboxed environments (Claude Code, Cursor) without requiring
|
|
10
|
+
* NODE_OPTIONS="--use-env-proxy".
|
|
3
11
|
*/
|
|
4
12
|
import { getConfig } from './config.js';
|
|
13
|
+
import { readSession } from './session.js';
|
|
14
|
+
/** Error thrown by mcpCall/mcpCallWithSession when the server returns a non-OK response. */
|
|
15
|
+
export class McpError extends Error {
|
|
16
|
+
details;
|
|
17
|
+
code;
|
|
18
|
+
constructor(message, code, details) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = 'McpError';
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.details = details;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Configure proxy at module load — runs once, before any fetch call.
|
|
26
|
+
// Node.js fetch (undici) ignores HTTPS_PROXY env vars. We detect the proxy
|
|
27
|
+
// and install a ProxyAgent as the global dispatcher so all fetch() calls
|
|
28
|
+
// route through the sandbox proxy automatically.
|
|
29
|
+
try {
|
|
30
|
+
const _proxy = process.env.https_proxy || process.env.HTTPS_PROXY;
|
|
31
|
+
if (_proxy) {
|
|
32
|
+
const { ProxyAgent, setGlobalDispatcher } = await import('undici');
|
|
33
|
+
setGlobalDispatcher(new ProxyAgent(_proxy));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// undici unavailable or proxy config failed — rethrowWithGuidance handles it.
|
|
38
|
+
}
|
|
39
|
+
/** Detect a sandbox/network-blocked fetch and re-throw with guidance. */
|
|
40
|
+
function rethrowWithGuidance(err, siteUrl) {
|
|
41
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
42
|
+
if (msg === 'fetch failed' || msg.includes('ECONNREFUSED') || msg.includes('ENOTFOUND')) {
|
|
43
|
+
const host = new URL(siteUrl).hostname;
|
|
44
|
+
const proxy = process.env.https_proxy || process.env.HTTPS_PROXY;
|
|
45
|
+
// When a proxy is set, the sandbox is routing traffic through it.
|
|
46
|
+
// Node.js fetch (undici) does NOT respect proxy env vars by default.
|
|
47
|
+
// The fix is --use-env-proxy (Node 22.14+), not adding the host to the allowlist.
|
|
48
|
+
const proxyHint = proxy
|
|
49
|
+
? `A proxy is configured (${proxy}) but Node.js fetch() is not using it.\n` +
|
|
50
|
+
`Fix: set NODE_OPTIONS="--use-env-proxy" in your environment,\n` +
|
|
51
|
+
` or re-run with: NODE_OPTIONS="--use-env-proxy" pb <command>\n`
|
|
52
|
+
: `If you are running inside an AI code editor sandbox (Claude Code, Cursor, etc.),\n` +
|
|
53
|
+
`the sandbox may be blocking outbound connections.\n\n` +
|
|
54
|
+
`Fix: add "${host}" to your sandbox network allowlist.\n` +
|
|
55
|
+
` • Claude Code: run /sandbox → add network host\n` +
|
|
56
|
+
` • Or re-run: pb handshake --init (configures sandbox automatically)\n`;
|
|
57
|
+
throw new Error(`Could not reach ${host}.\n\n${proxyHint}`);
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
5
61
|
export async function mcpCall(fn, args = {}) {
|
|
6
62
|
const { apiKey, siteUrl } = getConfig();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
63
|
+
let res;
|
|
64
|
+
try {
|
|
65
|
+
res = await fetch(`${siteUrl}/api/mcp`, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: {
|
|
68
|
+
'Content-Type': 'application/json',
|
|
69
|
+
Authorization: `Bearer ${apiKey}`,
|
|
70
|
+
},
|
|
71
|
+
body: JSON.stringify({ fn, args }),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
rethrowWithGuidance(err, siteUrl);
|
|
76
|
+
}
|
|
15
77
|
const json = (await res.json());
|
|
16
78
|
if (!res.ok || json.error) {
|
|
17
|
-
throw new
|
|
79
|
+
throw new McpError(json.error ?? res.statusText, json.code, json.details);
|
|
80
|
+
}
|
|
81
|
+
return json.data;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* mcpCall variant that injects X-Agent-Session-Id header for write operations.
|
|
85
|
+
* All write commands (update, relate, unrelate, capture) should use this.
|
|
86
|
+
*
|
|
87
|
+
* After a successful write, fires a non-blocking `agent.touchSession` call to
|
|
88
|
+
* renew the session TTL — mirroring MCP server behavior (DEC-299, BET-181 Slice 1).
|
|
89
|
+
* The touch is fire-and-forget: it does not block the write response, and errors
|
|
90
|
+
* are silently swallowed. No touch is attempted if no session is active or if
|
|
91
|
+
* the write itself fails.
|
|
92
|
+
*/
|
|
93
|
+
export async function mcpCallWithSession(fn, args = {}) {
|
|
94
|
+
const { apiKey, siteUrl } = getConfig();
|
|
95
|
+
const session = readSession();
|
|
96
|
+
const headers = {
|
|
97
|
+
'Content-Type': 'application/json',
|
|
98
|
+
Authorization: `Bearer ${apiKey}`,
|
|
99
|
+
};
|
|
100
|
+
if (session?.sessionId) {
|
|
101
|
+
headers['X-Agent-Session-Id'] = session.sessionId;
|
|
102
|
+
}
|
|
103
|
+
let res;
|
|
104
|
+
try {
|
|
105
|
+
res = await fetch(`${siteUrl}/api/mcp`, {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers,
|
|
108
|
+
body: JSON.stringify({ fn, args }),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
rethrowWithGuidance(err, siteUrl);
|
|
113
|
+
}
|
|
114
|
+
const json = (await res.json());
|
|
115
|
+
if (!res.ok || json.error) {
|
|
116
|
+
throw new McpError(json.error ?? res.statusText, json.code, json.details);
|
|
117
|
+
}
|
|
118
|
+
// Fire-and-forget session touch — best-effort, never blocks the write response.
|
|
119
|
+
// Only touch when a session is active. Uses mcpCall (not mcpCallWithSession) to
|
|
120
|
+
// avoid recursion.
|
|
121
|
+
if (session?.sessionId) {
|
|
122
|
+
mcpCall('agent.touchSession', { sessionId: session.sessionId }).catch(() => { });
|
|
18
123
|
}
|
|
19
124
|
return json.data;
|
|
20
125
|
}
|
package/dist/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI config — reads PRODUCTBRAIN_API_KEY + CONVEX_SITE_URL from
|
|
3
|
-
*
|
|
2
|
+
* CLI config — reads PRODUCTBRAIN_API_KEY + CONVEX_SITE_URL from:
|
|
3
|
+
* 1. Environment variables
|
|
4
|
+
* 2. ~/.config/productbrain/.env (works from any directory)
|
|
5
|
+
* 3. CWD .env.mcp / packages/mcp-server/.env.mcp / .env
|
|
6
|
+
*
|
|
7
|
+
* When key is missing and retry is provided (TTY), runs guided flow per docs/cli-unauthenticated-user-journey.md.
|
|
4
8
|
*/
|
|
9
|
+
declare const HOME_CONFIG_DIR: string;
|
|
10
|
+
declare const HOME_ENV_PATH: string;
|
|
5
11
|
export declare function getConfig(): {
|
|
6
12
|
apiKey: string;
|
|
7
13
|
siteUrl: string;
|
|
8
14
|
};
|
|
15
|
+
export type Config = {
|
|
16
|
+
apiKey: string;
|
|
17
|
+
siteUrl: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Get config; if key is missing and stdin is a TTY, run guided flow (y/n → paste key → save → retry).
|
|
21
|
+
* @param retry Called after saving key so the original command runs in-process.
|
|
22
|
+
* @returns Config, or null if we ran retry (caller should return without running again).
|
|
23
|
+
*/
|
|
24
|
+
export declare function getConfigOrGuide(retry: () => Promise<void>): Promise<Config | null>;
|
|
25
|
+
export { HOME_CONFIG_DIR, HOME_ENV_PATH };
|
|
9
26
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,QAAA,MAAM,eAAe,QAAgD,CAAC;AACtE,QAAA,MAAM,aAAa,QAAmC,CAAC;AA2FvD,wBAAgB,SAAS,IAAI;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAe/D;AAED,MAAM,MAAM,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAczF;AAED,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/lib/config.js
CHANGED
|
@@ -1,36 +1,117 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI config — reads PRODUCTBRAIN_API_KEY + CONVEX_SITE_URL from
|
|
3
|
-
*
|
|
2
|
+
* CLI config — reads PRODUCTBRAIN_API_KEY + CONVEX_SITE_URL from:
|
|
3
|
+
* 1. Environment variables
|
|
4
|
+
* 2. ~/.config/productbrain/.env (works from any directory)
|
|
5
|
+
* 3. CWD .env.mcp / packages/mcp-server/.env.mcp / .env
|
|
6
|
+
*
|
|
7
|
+
* When key is missing and retry is provided (TTY), runs guided flow per docs/cli-unauthenticated-user-journey.md.
|
|
4
8
|
*/
|
|
5
|
-
import { readFileSync, existsSync } from 'fs';
|
|
9
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
6
10
|
import { resolve } from 'path';
|
|
7
|
-
|
|
11
|
+
import { homedir } from 'os';
|
|
12
|
+
import { createInterface } from 'readline';
|
|
13
|
+
const DEFAULT_SITE_URL = 'https://trustworthy-kangaroo-277.convex.site';
|
|
14
|
+
const HOME_CONFIG_DIR = resolve(homedir(), '.config', 'productbrain');
|
|
15
|
+
const HOME_ENV_PATH = resolve(HOME_CONFIG_DIR, '.env');
|
|
16
|
+
const CWD_ENV_PATHS = ['.env.mcp', 'packages/mcp-server/.env.mcp', '.env'];
|
|
17
|
+
// Short error when not TTY (scripts/pipes must not hang)
|
|
18
|
+
const NON_TTY_MESSAGE = 'No API key. Set PRODUCTBRAIN_API_KEY or run pb login.';
|
|
19
|
+
function parseEnvContent(content) {
|
|
20
|
+
for (const line of content.split('\n')) {
|
|
21
|
+
const m = line.match(/^([^#=]+)=(.*)$/);
|
|
22
|
+
if (m && !process.env[m[1].trim()]) {
|
|
23
|
+
process.env[m[1].trim()] = m[2].trim().replace(/^["']|["']$/g, '');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
8
27
|
function loadEnv() {
|
|
9
|
-
|
|
28
|
+
if (existsSync(HOME_ENV_PATH)) {
|
|
29
|
+
parseEnvContent(readFileSync(HOME_ENV_PATH, 'utf8'));
|
|
30
|
+
}
|
|
31
|
+
for (const p of CWD_ENV_PATHS) {
|
|
10
32
|
const full = resolve(process.cwd(), p);
|
|
11
33
|
if (existsSync(full)) {
|
|
12
|
-
|
|
13
|
-
for (const line of content.split('\n')) {
|
|
14
|
-
const m = line.match(/^([^#=]+)=(.*)$/);
|
|
15
|
-
if (m && !process.env[m[1].trim()]) {
|
|
16
|
-
process.env[m[1].trim()] = m[2].trim().replace(/^["']|["']$/g, '');
|
|
17
|
-
}
|
|
18
|
-
}
|
|
34
|
+
parseEnvContent(readFileSync(full, 'utf8'));
|
|
19
35
|
break;
|
|
20
36
|
}
|
|
21
37
|
}
|
|
22
38
|
}
|
|
23
39
|
loadEnv();
|
|
24
|
-
|
|
40
|
+
function question(rl, prompt) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
rl.question(prompt, (answer) => resolve((answer ?? '').trim().toLowerCase()));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function questionRaw(rl, prompt) {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
rl.question(prompt, (answer) => resolve((answer ?? '').trim()));
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Guided flow when key is missing (TTY only). Per docs/cli-unauthenticated-user-journey.md.
|
|
52
|
+
* Returns true if we saved and ran retry; false if user said No (caller should exit).
|
|
53
|
+
*/
|
|
54
|
+
async function runGuidedFlow(retry) {
|
|
55
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
56
|
+
console.log('Do you have an API key from Product Brain? (y/n)');
|
|
57
|
+
console.log(' Get one: Product Brain app → Settings → API Keys');
|
|
58
|
+
const hasKey = await question(rl, '> ');
|
|
59
|
+
if (hasKey !== 'y' && hasKey !== 'yes') {
|
|
60
|
+
console.log('Get your key: Product Brain app → Settings → API Keys. Then run: pb login');
|
|
61
|
+
rl.close();
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
let apiKey = await questionRaw(rl, 'Paste your API key (pb_sk_...): ');
|
|
65
|
+
if (!apiKey || !apiKey.startsWith('pb_sk_')) {
|
|
66
|
+
console.log('Key must start with pb_sk_. Try again or run pb login.');
|
|
67
|
+
apiKey = await questionRaw(rl, 'Paste your API key (pb_sk_...): ');
|
|
68
|
+
}
|
|
69
|
+
rl.close();
|
|
70
|
+
if (!apiKey || !apiKey.startsWith('pb_sk_')) {
|
|
71
|
+
console.log('Get your key: Product Brain app → Settings → API Keys. Then run: pb login');
|
|
72
|
+
process.exit(0);
|
|
73
|
+
}
|
|
74
|
+
const content = [
|
|
75
|
+
`# Product Brain CLI — saved by guided setup`,
|
|
76
|
+
`PRODUCTBRAIN_API_KEY=${apiKey}`,
|
|
77
|
+
`CONVEX_SITE_URL=${DEFAULT_SITE_URL.replace(/\/$/, '')}`,
|
|
78
|
+
'',
|
|
79
|
+
].join('\n');
|
|
80
|
+
mkdirSync(HOME_CONFIG_DIR, { recursive: true });
|
|
81
|
+
writeFileSync(HOME_ENV_PATH, content, { mode: 0o600 });
|
|
82
|
+
process.env.PRODUCTBRAIN_API_KEY = apiKey;
|
|
83
|
+
process.env.CONVEX_SITE_URL = DEFAULT_SITE_URL.replace(/\/$/, '');
|
|
84
|
+
console.log('Saved your key. Running your command…\n');
|
|
85
|
+
await retry();
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
25
88
|
export function getConfig() {
|
|
26
89
|
const apiKey = process.env.PRODUCTBRAIN_API_KEY ?? '';
|
|
27
90
|
const siteUrl = (process.env.CONVEX_SITE_URL ?? DEFAULT_SITE_URL).replace(/\/$/, '');
|
|
28
91
|
if (!apiKey || !apiKey.startsWith('pb_sk_')) {
|
|
29
|
-
throw new Error(
|
|
92
|
+
throw new Error(NON_TTY_MESSAGE);
|
|
30
93
|
}
|
|
31
94
|
if (!siteUrl.startsWith('https://')) {
|
|
32
95
|
throw new Error(`CONVEX_SITE_URL must use HTTPS (got "${siteUrl.slice(0, 30)}…"). API keys must not be sent over unencrypted connections.`);
|
|
33
96
|
}
|
|
34
97
|
return { apiKey, siteUrl };
|
|
35
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Get config; if key is missing and stdin is a TTY, run guided flow (y/n → paste key → save → retry).
|
|
101
|
+
* @param retry Called after saving key so the original command runs in-process.
|
|
102
|
+
* @returns Config, or null if we ran retry (caller should return without running again).
|
|
103
|
+
*/
|
|
104
|
+
export async function getConfigOrGuide(retry) {
|
|
105
|
+
const apiKey = process.env.PRODUCTBRAIN_API_KEY ?? '';
|
|
106
|
+
const siteUrl = (process.env.CONVEX_SITE_URL ?? DEFAULT_SITE_URL).replace(/\/$/, '');
|
|
107
|
+
if (apiKey.startsWith('pb_sk_') && siteUrl.startsWith('https://')) {
|
|
108
|
+
return { apiKey, siteUrl };
|
|
109
|
+
}
|
|
110
|
+
if (!process.stdin.isTTY) {
|
|
111
|
+
throw new Error(NON_TTY_MESSAGE);
|
|
112
|
+
}
|
|
113
|
+
const didRetry = await runGuidedFlow(retry);
|
|
114
|
+
return didRetry ? null : null;
|
|
115
|
+
}
|
|
116
|
+
export { HOME_CONFIG_DIR, HOME_ENV_PATH };
|
|
36
117
|
//# sourceMappingURL=config.js.map
|
package/dist/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local repo detection — reads filesystem signals to build a RepoContext.
|
|
3
|
+
* No API calls. Pure filesystem inspection.
|
|
4
|
+
*/
|
|
5
|
+
export interface RepoContext {
|
|
6
|
+
name: string | null;
|
|
7
|
+
description: string | null;
|
|
8
|
+
gitRemote: string | null;
|
|
9
|
+
repoSlug: string | null;
|
|
10
|
+
detectedStack: string[];
|
|
11
|
+
isMonorepo: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function detectRepo(cwd?: string): RepoContext;
|
|
14
|
+
//# sourceMappingURL=repo-detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-detect.d.ts","sourceRoot":"","sources":["../../src/lib/repo-detect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,GAAG,SAAgB,GAAG,WAAW,CAiD3D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local repo detection — reads filesystem signals to build a RepoContext.
|
|
3
|
+
* No API calls. Pure filesystem inspection.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync } from 'fs';
|
|
6
|
+
import { resolve, basename } from 'path';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
export function detectRepo(cwd = process.cwd()) {
|
|
9
|
+
const ctx = {
|
|
10
|
+
name: null,
|
|
11
|
+
description: null,
|
|
12
|
+
gitRemote: null,
|
|
13
|
+
repoSlug: null,
|
|
14
|
+
detectedStack: [],
|
|
15
|
+
isMonorepo: false,
|
|
16
|
+
};
|
|
17
|
+
// package.json
|
|
18
|
+
const pkgPath = resolve(cwd, 'package.json');
|
|
19
|
+
if (existsSync(pkgPath)) {
|
|
20
|
+
try {
|
|
21
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
22
|
+
ctx.name = pkg.name?.replace(/^@[^/]+\//, '') ?? null;
|
|
23
|
+
ctx.description = pkg.description ?? null;
|
|
24
|
+
if (pkg.workspaces)
|
|
25
|
+
ctx.isMonorepo = true;
|
|
26
|
+
}
|
|
27
|
+
catch { /* ignore parse errors */ }
|
|
28
|
+
}
|
|
29
|
+
// git remote
|
|
30
|
+
try {
|
|
31
|
+
const remote = execSync('git remote get-url origin', { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
32
|
+
ctx.gitRemote = remote;
|
|
33
|
+
// Extract owner/repo from SSH or HTTPS URL
|
|
34
|
+
const m = remote.match(/[:/]([^/]+\/[^/.]+?)(?:\.git)?$/);
|
|
35
|
+
if (m)
|
|
36
|
+
ctx.repoSlug = m[1];
|
|
37
|
+
}
|
|
38
|
+
catch { /* not a git repo or no remote */ }
|
|
39
|
+
// Stack detection
|
|
40
|
+
const checks = [
|
|
41
|
+
['sveltekit', ['svelte.config.js', 'svelte.config.ts']],
|
|
42
|
+
['convex', ['convex']],
|
|
43
|
+
['typescript', ['tsconfig.json']],
|
|
44
|
+
['nextjs', ['next.config.js', 'next.config.ts', 'next.config.mjs']],
|
|
45
|
+
['vite', ['vite.config.ts', 'vite.config.js']],
|
|
46
|
+
['react', ['src/App.tsx', 'src/App.jsx']],
|
|
47
|
+
];
|
|
48
|
+
for (const [tech, paths] of checks) {
|
|
49
|
+
if (paths.some((p) => existsSync(resolve(cwd, p)))) {
|
|
50
|
+
ctx.detectedStack.push(tech);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Fallback name from directory
|
|
54
|
+
if (!ctx.name)
|
|
55
|
+
ctx.name = basename(cwd);
|
|
56
|
+
return ctx;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=repo-detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-detect.js","sourceRoot":"","sources":["../../src/lib/repo-detect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAWzC,MAAM,UAAU,UAAU,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC5C,MAAM,GAAG,GAAgB;QACvB,IAAI,EAAE,IAAI;QACV,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,IAAI;QACf,QAAQ,EAAE,IAAI;QACd,aAAa,EAAE,EAAE;QACjB,UAAU,EAAE,KAAK;KAClB,CAAC;IAEF,eAAe;IACf,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAC7C,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;YACtD,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC;YACtD,GAAG,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC;YAC1C,IAAI,GAAG,CAAC,UAAU;gBAAE,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC,CAAC,yBAAyB,CAAC,CAAC;IACvC,CAAC;IAED,aAAa;IACb,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,2BAA2B,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACxH,GAAG,CAAC,SAAS,GAAG,MAAM,CAAC;QACvB,2CAA2C;QAC3C,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC1D,IAAI,CAAC;YAAE,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC,CAAC,iCAAiC,CAAC,CAAC;IAE7C,kBAAkB;IAClB,MAAM,MAAM,GAA8B;QACxC,CAAC,WAAW,EAAE,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;QACvD,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC;QACtB,CAAC,YAAY,EAAE,CAAC,eAAe,CAAC,CAAC;QACjC,CAAC,QAAQ,EAAE,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,CAAC,CAAC;QACnE,CAAC,MAAM,EAAE,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;QAC9C,CAAC,OAAO,EAAE,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;KAC1C,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACnD,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAExC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runCliCommand — shared command runner for the Product Brain CLI.
|
|
3
|
+
*
|
|
4
|
+
* Owns:
|
|
5
|
+
* (a) TTY detection via process.stdout.isTTY
|
|
6
|
+
* (b) JSON vs human output branching
|
|
7
|
+
* (c) stripConvexInternals() application in JSON mode
|
|
8
|
+
* (d) --json and --pretty explicit overrides that take precedence over auto-detection
|
|
9
|
+
* (e) Error handling: JSON error object in JSON mode, formatted message in TTY mode
|
|
10
|
+
*
|
|
11
|
+
* Chain: DEC-299 (JSON-default), STD-65 (agent responses: structured, parseable, minimal)
|
|
12
|
+
*/
|
|
13
|
+
export declare function setOutputMode(mode: 'auto' | 'json' | 'pretty'): void;
|
|
14
|
+
export declare function getOutputMode(): 'auto' | 'json' | 'pretty';
|
|
15
|
+
/** Returns true if the current output mode resolves to JSON. */
|
|
16
|
+
export declare function isJsonMode(): boolean;
|
|
17
|
+
export interface RunCliCommandOptions<T> {
|
|
18
|
+
/** The async function that performs the MCP call and returns data. */
|
|
19
|
+
fn: () => Promise<T>;
|
|
20
|
+
/** Formats data for human-readable TTY output. */
|
|
21
|
+
formatPretty: (data: T) => string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Execute a CLI command with unified output handling.
|
|
25
|
+
*
|
|
26
|
+
* - JSON mode: strip Convex internals, JSON.stringify to stdout
|
|
27
|
+
* - Pretty mode: call formatPretty callback, print result
|
|
28
|
+
* - Errors: JSON error object in JSON mode, plain message in pretty mode
|
|
29
|
+
*/
|
|
30
|
+
export declare function runCliCommand<T>(options: RunCliCommandOptions<T>): Promise<void>;
|
|
31
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runCliCommand — shared command runner for the Product Brain CLI.
|
|
3
|
+
*
|
|
4
|
+
* Owns:
|
|
5
|
+
* (a) TTY detection via process.stdout.isTTY
|
|
6
|
+
* (b) JSON vs human output branching
|
|
7
|
+
* (c) stripConvexInternals() application in JSON mode
|
|
8
|
+
* (d) --json and --pretty explicit overrides that take precedence over auto-detection
|
|
9
|
+
* (e) Error handling: JSON error object in JSON mode, formatted message in TTY mode
|
|
10
|
+
*
|
|
11
|
+
* Chain: DEC-299 (JSON-default), STD-65 (agent responses: structured, parseable, minimal)
|
|
12
|
+
*/
|
|
13
|
+
import { stripConvexInternals } from './strip.js';
|
|
14
|
+
/**
|
|
15
|
+
* Global output mode — set by the CLI entry point when --json or --pretty flags are parsed.
|
|
16
|
+
* 'auto' = detect via TTY; 'json' = force JSON; 'pretty' = force human.
|
|
17
|
+
*/
|
|
18
|
+
let outputMode = 'auto';
|
|
19
|
+
export function setOutputMode(mode) {
|
|
20
|
+
outputMode = mode;
|
|
21
|
+
}
|
|
22
|
+
export function getOutputMode() {
|
|
23
|
+
return outputMode;
|
|
24
|
+
}
|
|
25
|
+
/** Returns true if the current output mode resolves to JSON. */
|
|
26
|
+
export function isJsonMode() {
|
|
27
|
+
if (outputMode === 'json')
|
|
28
|
+
return true;
|
|
29
|
+
if (outputMode === 'pretty')
|
|
30
|
+
return false;
|
|
31
|
+
// auto: non-TTY (piped) → JSON, TTY → human
|
|
32
|
+
return !process.stdout.isTTY;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Execute a CLI command with unified output handling.
|
|
36
|
+
*
|
|
37
|
+
* - JSON mode: strip Convex internals, JSON.stringify to stdout
|
|
38
|
+
* - Pretty mode: call formatPretty callback, print result
|
|
39
|
+
* - Errors: JSON error object in JSON mode, plain message in pretty mode
|
|
40
|
+
*/
|
|
41
|
+
export async function runCliCommand(options) {
|
|
42
|
+
const json = isJsonMode();
|
|
43
|
+
try {
|
|
44
|
+
const data = await options.fn();
|
|
45
|
+
if (json) {
|
|
46
|
+
const stripped = stripConvexInternals(data);
|
|
47
|
+
process.stdout.write(JSON.stringify(stripped) + '\n');
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const formatted = options.formatPretty(data);
|
|
51
|
+
process.stdout.write(formatted + '\n');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
56
|
+
if (json) {
|
|
57
|
+
process.stderr.write(JSON.stringify({ error: message }) + '\n');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
process.stderr.write(message + '\n');
|
|
61
|
+
}
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session state — persists active session ID between CLI invocations.
|
|
3
|
+
* Stored at .productbrain/session.json in the project root.
|
|
4
|
+
* Solves TEN-264 (option 1: explicit session token).
|
|
5
|
+
*/
|
|
6
|
+
export interface SessionState {
|
|
7
|
+
sessionId: string;
|
|
8
|
+
workspaceId: string;
|
|
9
|
+
workspaceName: string;
|
|
10
|
+
startedAt: string;
|
|
11
|
+
entriesCaptured: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function readSession(): SessionState | null;
|
|
14
|
+
export declare function writeSession(state: SessionState): void;
|
|
15
|
+
export declare function clearSession(): void;
|
|
16
|
+
export declare function addCapturedEntry(entryId: string): void;
|
|
17
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAUD,wBAAgB,WAAW,IAAI,YAAY,GAAG,IAAI,CAQjD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAItD;AAED,wBAAgB,YAAY,IAAI,IAAI,CAGnC;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKtD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session state — persists active session ID between CLI invocations.
|
|
3
|
+
* Stored at .productbrain/session.json in the project root.
|
|
4
|
+
* Solves TEN-264 (option 1: explicit session token).
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
function getSessionDir() {
|
|
9
|
+
return join(process.cwd(), '.productbrain');
|
|
10
|
+
}
|
|
11
|
+
function getSessionPath() {
|
|
12
|
+
return join(getSessionDir(), 'session.json');
|
|
13
|
+
}
|
|
14
|
+
export function readSession() {
|
|
15
|
+
const p = getSessionPath();
|
|
16
|
+
if (!existsSync(p))
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(readFileSync(p, 'utf8'));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function writeSession(state) {
|
|
26
|
+
const dir = getSessionDir();
|
|
27
|
+
if (!existsSync(dir))
|
|
28
|
+
mkdirSync(dir, { recursive: true });
|
|
29
|
+
writeFileSync(getSessionPath(), JSON.stringify(state, null, 2));
|
|
30
|
+
}
|
|
31
|
+
export function clearSession() {
|
|
32
|
+
const p = getSessionPath();
|
|
33
|
+
if (existsSync(p))
|
|
34
|
+
unlinkSync(p);
|
|
35
|
+
}
|
|
36
|
+
export function addCapturedEntry(entryId) {
|
|
37
|
+
const session = readSession();
|
|
38
|
+
if (!session)
|
|
39
|
+
return;
|
|
40
|
+
session.entriesCaptured.push(entryId);
|
|
41
|
+
writeSession(session);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpF,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAU5B,SAAS,aAAa;IACpB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,cAAc;IACrB,OAAO,IAAI,CAAC,aAAa,EAAE,EAAE,cAAc,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,MAAM,CAAC,GAAG,cAAc,EAAE,CAAC;IAC3B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAiB,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAmB;IAC9C,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,aAAa,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,CAAC,GAAG,cAAc,EAAE,CAAC;IAC3B,IAAI,UAAU,CAAC,CAAC,CAAC;QAAE,UAAU,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,YAAY,CAAC,OAAO,CAAC,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stripConvexInternals — recursively removes Convex system fields from API responses.
|
|
3
|
+
*
|
|
4
|
+
* Strips `_id` and `_creationTime` (Convex internal fields, TEN-340).
|
|
5
|
+
* Preserves `entryId` (human-readable ID) and all other fields.
|
|
6
|
+
* Handles nested objects and arrays. Returns a new object (does not mutate).
|
|
7
|
+
*
|
|
8
|
+
* Chain: DEC-299 (JSON-default output), TEN-340 (Convex internal exposure)
|
|
9
|
+
*/
|
|
10
|
+
export declare function stripConvexInternals<T>(data: T): T;
|
|
11
|
+
//# sourceMappingURL=strip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip.d.ts","sourceRoot":"","sources":["../../src/lib/strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAelD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stripConvexInternals — recursively removes Convex system fields from API responses.
|
|
3
|
+
*
|
|
4
|
+
* Strips `_id` and `_creationTime` (Convex internal fields, TEN-340).
|
|
5
|
+
* Preserves `entryId` (human-readable ID) and all other fields.
|
|
6
|
+
* Handles nested objects and arrays. Returns a new object (does not mutate).
|
|
7
|
+
*
|
|
8
|
+
* Chain: DEC-299 (JSON-default output), TEN-340 (Convex internal exposure)
|
|
9
|
+
*/
|
|
10
|
+
const STRIP_KEYS = new Set(['_id', '_creationTime']);
|
|
11
|
+
export function stripConvexInternals(data) {
|
|
12
|
+
if (Array.isArray(data)) {
|
|
13
|
+
return data.map((item) => stripConvexInternals(item));
|
|
14
|
+
}
|
|
15
|
+
if (data !== null && typeof data === 'object') {
|
|
16
|
+
const result = {};
|
|
17
|
+
for (const [key, value] of Object.entries(data)) {
|
|
18
|
+
if (STRIP_KEYS.has(key))
|
|
19
|
+
continue;
|
|
20
|
+
result[key] = stripConvexInternals(value);
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=strip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip.js","sourceRoot":"","sources":["../../src/lib/strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC;AAErD,MAAM,UAAU,oBAAoB,CAAI,IAAO;IAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAiB,CAAC;IACxE,CAAC;IAED,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9C,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;YAC3E,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAClC,MAAM,CAAC,GAAG,CAAC,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,MAAsB,CAAC;IAChC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|