@contextq/cli 0.0.1 → 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * mcp-config.ts — idempotent .mcp.json merge.
3
+ *
4
+ * Mirrors skills/contextq/scripts/install-hook.sh's wire_mcp(): never
5
+ * clobbers an existing .mcp.json, only ever sets/reads the "contextq" key
6
+ * under mcpServers, and defaults the key value to the ${CONTEXT_API_KEY}
7
+ * placeholder (safe default -- no secret on disk) unless --embed-key is
8
+ * passed. Claude Code expands ${VAR} from ITS OWN process environment, not
9
+ * from the credential file this installer writes -- same documented gap
10
+ * install-hook.sh warns about.
11
+ */
12
+ export interface McpWireResult {
13
+ path: string;
14
+ action: 'created' | 'added' | 'skipped';
15
+ detail: string;
16
+ }
17
+ export declare function wireMcpJson(root: string, apiUrl: string, embedKey: boolean, apiKey: string): McpWireResult;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * mcp-config.ts — idempotent .mcp.json merge.
3
+ *
4
+ * Mirrors skills/contextq/scripts/install-hook.sh's wire_mcp(): never
5
+ * clobbers an existing .mcp.json, only ever sets/reads the "contextq" key
6
+ * under mcpServers, and defaults the key value to the ${CONTEXT_API_KEY}
7
+ * placeholder (safe default -- no secret on disk) unless --embed-key is
8
+ * passed. Claude Code expands ${VAR} from ITS OWN process environment, not
9
+ * from the credential file this installer writes -- same documented gap
10
+ * install-hook.sh warns about.
11
+ */
12
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
13
+ import { execFileSync } from 'node:child_process';
14
+ import { join } from 'node:path';
15
+ /**
16
+ * T474: whether `relPath` (relative to `root`) is covered by a gitignore rule.
17
+ * Returns null when this can't be determined (no git repo at `root`, or git
18
+ * itself is unavailable) -- callers must treat that as "unknown", not "safe".
19
+ */
20
+ function isPathGitIgnored(root, relPath) {
21
+ try {
22
+ execFileSync('git', ['check-ignore', '-q', '--', relPath], { cwd: root, stdio: 'ignore' });
23
+ return true;
24
+ }
25
+ catch (err) {
26
+ const status = err.status;
27
+ if (status === 1)
28
+ return false;
29
+ return null;
30
+ }
31
+ }
32
+ export function wireMcpJson(root, apiUrl, embedKey, apiKey) {
33
+ const path = join(root, '.mcp.json');
34
+ const existed = existsSync(path);
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ let doc = {};
37
+ if (existed) {
38
+ const raw = readFileSync(path, 'utf-8');
39
+ try {
40
+ doc = raw.trim() === '' ? {} : JSON.parse(raw);
41
+ }
42
+ catch (err) {
43
+ throw new Error(`.mcp.json exists but is not valid JSON (${err.message}) -- refusing to touch it`);
44
+ }
45
+ }
46
+ doc.mcpServers ??= {};
47
+ if (doc.mcpServers.contextq) {
48
+ return { path, action: 'skipped', detail: 'contextq server entry already present' };
49
+ }
50
+ // T474: --embed-key is about to write the raw secret to disk -- refuse when
51
+ // .mcp.json would land in a commit, since that turns a local convenience
52
+ // flag into a leaked-credential incident the moment someone runs `git add .`.
53
+ if (embedKey) {
54
+ const ignored = isPathGitIgnored(root, '.mcp.json');
55
+ if (ignored === false) {
56
+ throw new Error('.mcp.json is not covered by .gitignore in this repo -- refusing to write the raw API key into a ' +
57
+ 'file git would track. Add ".mcp.json" to .gitignore first, or re-run without --embed-key to use ' +
58
+ 'the safer ${CONTEXT_API_KEY} placeholder instead.');
59
+ }
60
+ if (ignored === null) {
61
+ console.log('[warn] could not verify .mcp.json is gitignored (no git repo detected at this path) -- writing the raw key anyway.');
62
+ }
63
+ }
64
+ doc.mcpServers.contextq = {
65
+ command: 'npx',
66
+ args: ['-y', '@contextq/mcp'],
67
+ env: {
68
+ CONTEXT_API_URL: apiUrl,
69
+ CONTEXT_API_KEY: embedKey ? apiKey : '${CONTEXT_API_KEY}',
70
+ },
71
+ };
72
+ writeFileSync(path, `${JSON.stringify(doc, null, 2)}\n`);
73
+ return {
74
+ path,
75
+ action: existed ? 'added' : 'created',
76
+ detail: `contextq server wired (key=${embedKey ? 'embedded literal' : '${CONTEXT_API_KEY} placeholder'})`,
77
+ };
78
+ }
79
+ //# sourceMappingURL=mcp-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-config.js","sourceRoot":"","sources":["../src/mcp-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * platform-cred.ts — read/write the contextq platform credential file.
3
+ *
4
+ * Same path + mode as the existing T144 web installer (src/routes/installer.ts)
5
+ * and the same path the contextq skill's bash scripts (contextq-api.sh,
6
+ * install-hook.sh, contextq-boot.sh) and Bun cred-resolve.ts already read:
7
+ *
8
+ * macOS: ~/Library/Application Support/contextq/credentials
9
+ * Windows: %APPDATA%/contextq/credentials
10
+ * Linux: $XDG_CONFIG_HOME/contextq/credentials (default ~/.config)
11
+ *
12
+ * The existing readers disagree on which key names they check: install.sh
13
+ * (T144) writes CTQ_API_KEY/CTQ_API_URL (read by cred-resolve.ts's Bun path),
14
+ * while every bash script (contextq-api.sh, install-hook.sh, contextq-boot.sh)
15
+ * only ever reads CONTEXT_API_KEY/CONTEXT_API_URL from this same file. Rather
16
+ * than picking a side, this installer writes BOTH pairs so the credential it
17
+ * writes is visible to every existing reader regardless of that pre-existing
18
+ * inconsistency.
19
+ *
20
+ * Never logs or prints the key value.
21
+ *
22
+ * T676: writeCreds() itself remains an unconditional overwrite -- callers
23
+ * decide when that's appropriate. The safety layer lives in the helpers
24
+ * below (credsFileExists/credsEqual/backupCredsFile), which index.ts's
25
+ * runInit() composes into: no write on an ambient env var alone when a
26
+ * credential file already exists, a timestamped backup before any write
27
+ * that would actually change an existing file, and a no-op when the new
28
+ * value is identical to what's already on disk.
29
+ */
30
+ export interface Creds {
31
+ apiKey: string;
32
+ apiUrl: string;
33
+ }
34
+ /**
35
+ * T676: test-only override so cli/ tests can point every read/write at a
36
+ * throwaway temp dir instead of the real machine-wide credential path.
37
+ * Checked first, ahead of every OS-specific branch below. Production never
38
+ * sets this -- there is no code path that sets it outside a test.
39
+ */
40
+ export declare function platformCredDir(): string;
41
+ export declare function platformCredFile(): string;
42
+ /** Does a credential file already exist on disk (real or overridden dir)? */
43
+ export declare function credsFileExists(): boolean;
44
+ /** Same apiKey AND same apiUrl -- the "nothing would actually change" case. */
45
+ export declare function credsEqual(a: Creds, b: Creds): boolean;
46
+ /**
47
+ * Copy whatever credential file is currently on disk to a timestamped
48
+ * sibling (mode 600, same directory) BEFORE it gets overwritten. Returns
49
+ * the backup path, or null when there was no existing file to back up.
50
+ * Never throws on missing source -- callers gate this on credsFileExists().
51
+ */
52
+ export declare function backupCredsFile(): string | null;
53
+ /** Read whatever is already on disk. Both apiKey/apiUrl are '' when absent. */
54
+ export declare function readExistingCreds(): Creds;
55
+ /**
56
+ * Write the credential file at mode 0600 (dir at 0700). Overwrites whatever
57
+ * was there before — callers decide when that's appropriate (explicit --key,
58
+ * or no existing credential at all).
59
+ */
60
+ export declare function writeCreds(creds: Creds): string;
61
+ export declare const KEY_RE: RegExp;
62
+ export interface CredentialDecisionInput {
63
+ /** `--key` flag value, if the caller passed one. */
64
+ explicitKey?: string;
65
+ /** Name of whichever ambient env var supplied a key, or null when neither is set. */
66
+ ambientVarName: 'CONTEXT_API_KEY' | 'CTQ_API_KEY' | null;
67
+ /** The ambient env var's value; '' when ambientVarName is null. */
68
+ ambientKey: string;
69
+ apiUrl: string;
70
+ }
71
+ export interface CredentialDecision {
72
+ apiKey: string;
73
+ credLine: string;
74
+ /**
75
+ * null only when the caller must fall through to an interactive prompt:
76
+ * no --key, no ambient env var, and no existing file to fall back to.
77
+ */
78
+ action: 'created' | 'rotated' | 'skipped' | null;
79
+ backupPath: string | null;
80
+ }
81
+ /**
82
+ * T676: the non-interactive half of `contextq init`'s credential
83
+ * resolution. Fixes the real incident this task is named for -- a demo run
84
+ * with only an AMBIENT env var set (direnv-exported CONTEXT_API_KEY from
85
+ * some repo's .env.local) used to be treated exactly like an explicit
86
+ * --key, silently overwriting a pre-existing machine-wide credential file
87
+ * with no backup. Branches, matching T676's required behavior 1-4:
88
+ *
89
+ * 1. no --key, ambient env only, file exists -> file untouched, used as-is,
90
+ * "skipped" naming the ignored env var
91
+ * 2. --key given, file exists with a different key or url -> back up the
92
+ * existing file (mode 600, timestamped) THEN write, "rotated"
93
+ * 3. --key given, file exists with the identical key AND url -> no write,
94
+ * "skipped (unchanged)"
95
+ * 4. no existing file at all -> unchanged prior behavior, "created"
96
+ * (works from either --key or an ambient env var, since there is
97
+ * nothing to protect)
98
+ *
99
+ * The one case this function does not resolve on its own is "no --key, no
100
+ * ambient env var, no existing file" -- action comes back null and the
101
+ * caller must prompt interactively, then call this function again with the
102
+ * typed value as `explicitKey` to get a consistent created/rotated/skipped
103
+ * decision (falls into branch 4 in practice, since there is no file yet).
104
+ */
105
+ export declare function decideCredentialWrite(input: CredentialDecisionInput): CredentialDecision;
@@ -0,0 +1,210 @@
1
+ /**
2
+ * platform-cred.ts — read/write the contextq platform credential file.
3
+ *
4
+ * Same path + mode as the existing T144 web installer (src/routes/installer.ts)
5
+ * and the same path the contextq skill's bash scripts (contextq-api.sh,
6
+ * install-hook.sh, contextq-boot.sh) and Bun cred-resolve.ts already read:
7
+ *
8
+ * macOS: ~/Library/Application Support/contextq/credentials
9
+ * Windows: %APPDATA%/contextq/credentials
10
+ * Linux: $XDG_CONFIG_HOME/contextq/credentials (default ~/.config)
11
+ *
12
+ * The existing readers disagree on which key names they check: install.sh
13
+ * (T144) writes CTQ_API_KEY/CTQ_API_URL (read by cred-resolve.ts's Bun path),
14
+ * while every bash script (contextq-api.sh, install-hook.sh, contextq-boot.sh)
15
+ * only ever reads CONTEXT_API_KEY/CONTEXT_API_URL from this same file. Rather
16
+ * than picking a side, this installer writes BOTH pairs so the credential it
17
+ * writes is visible to every existing reader regardless of that pre-existing
18
+ * inconsistency.
19
+ *
20
+ * Never logs or prints the key value.
21
+ *
22
+ * T676: writeCreds() itself remains an unconditional overwrite -- callers
23
+ * decide when that's appropriate. The safety layer lives in the helpers
24
+ * below (credsFileExists/credsEqual/backupCredsFile), which index.ts's
25
+ * runInit() composes into: no write on an ambient env var alone when a
26
+ * credential file already exists, a timestamped backup before any write
27
+ * that would actually change an existing file, and a no-op when the new
28
+ * value is identical to what's already on disk.
29
+ */
30
+ import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
31
+ import { homedir } from 'node:os';
32
+ import { join } from 'node:path';
33
+ /**
34
+ * T676: test-only override so cli/ tests can point every read/write at a
35
+ * throwaway temp dir instead of the real machine-wide credential path.
36
+ * Checked first, ahead of every OS-specific branch below. Production never
37
+ * sets this -- there is no code path that sets it outside a test.
38
+ */
39
+ export function platformCredDir() {
40
+ const override = process.env.CTQ_CRED_DIR || process.env.CONTEXTQ_CRED_DIR;
41
+ if (override)
42
+ return override;
43
+ const candidates = CRED_DIR_NAMES.map(platformCredDirFor);
44
+ return candidates.find((dir) => existsSync(join(dir, 'credentials'))) ?? candidates[0];
45
+ }
46
+ const CRED_DIR_NAMES = ['contextq', 'ctq-brain'];
47
+ function platformCredDirFor(dirName) {
48
+ const home = homedir();
49
+ if (process.platform === 'darwin') {
50
+ return join(home, 'Library', 'Application Support', dirName);
51
+ }
52
+ if (process.platform === 'win32') {
53
+ const appData = process.env.APPDATA ?? join(home, 'AppData', 'Roaming');
54
+ return join(appData, dirName);
55
+ }
56
+ const xdg = process.env.XDG_CONFIG_HOME ?? join(home, '.config');
57
+ return join(xdg, dirName);
58
+ }
59
+ export function platformCredFile() {
60
+ return join(platformCredDir(), 'credentials');
61
+ }
62
+ /** Does a credential file already exist on disk (real or overridden dir)? */
63
+ export function credsFileExists() {
64
+ return existsSync(platformCredFile());
65
+ }
66
+ /** Same apiKey AND same apiUrl -- the "nothing would actually change" case. */
67
+ export function credsEqual(a, b) {
68
+ return a.apiKey === b.apiKey && a.apiUrl === b.apiUrl;
69
+ }
70
+ function utcTimestamp() {
71
+ const d = new Date();
72
+ const pad = (n) => String(n).padStart(2, '0');
73
+ return (`${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}` +
74
+ `T${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}Z`);
75
+ }
76
+ /**
77
+ * Copy whatever credential file is currently on disk to a timestamped
78
+ * sibling (mode 600, same directory) BEFORE it gets overwritten. Returns
79
+ * the backup path, or null when there was no existing file to back up.
80
+ * Never throws on missing source -- callers gate this on credsFileExists().
81
+ */
82
+ export function backupCredsFile() {
83
+ const file = platformCredFile();
84
+ if (!existsSync(file))
85
+ return null;
86
+ const dir = platformCredDir();
87
+ const backupPath = join(dir, `credentials.bak-${utcTimestamp()}`);
88
+ copyFileSync(file, backupPath);
89
+ chmodSync(backupPath, 0o600);
90
+ return backupPath;
91
+ }
92
+ function readEnvVal(key, filePath) {
93
+ if (!existsSync(filePath))
94
+ return '';
95
+ try {
96
+ const content = readFileSync(filePath, 'utf-8');
97
+ for (const line of content.split('\n')) {
98
+ if (line.startsWith(`${key}=`))
99
+ return line.slice(key.length + 1).trim();
100
+ }
101
+ }
102
+ catch {
103
+ // never throw on a read error — treat as "no credential found"
104
+ }
105
+ return '';
106
+ }
107
+ /** Read whatever is already on disk. Both apiKey/apiUrl are '' when absent. */
108
+ export function readExistingCreds() {
109
+ const file = platformCredFile();
110
+ const apiKey = readEnvVal('CTQ_API_KEY', file) || readEnvVal('CONTEXT_API_KEY', file);
111
+ const apiUrl = readEnvVal('CTQ_API_URL', file) || readEnvVal('CONTEXT_API_URL', file);
112
+ return { apiKey, apiUrl };
113
+ }
114
+ /**
115
+ * Write the credential file at mode 0600 (dir at 0700). Overwrites whatever
116
+ * was there before — callers decide when that's appropriate (explicit --key,
117
+ * or no existing credential at all).
118
+ */
119
+ export function writeCreds(creds) {
120
+ const dir = platformCredDir();
121
+ const file = platformCredFile();
122
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
123
+ chmodSync(dir, 0o700);
124
+ const body = `CTQ_API_KEY=${creds.apiKey}\n` +
125
+ `CTQ_API_URL=${creds.apiUrl}\n` +
126
+ `CONTEXT_API_KEY=${creds.apiKey}\n` +
127
+ `CONTEXT_API_URL=${creds.apiUrl}\n`;
128
+ writeFileSync(file, body, { mode: 0o600 });
129
+ chmodSync(file, 0o600);
130
+ return file;
131
+ }
132
+ export const KEY_RE = /^sk_live_[0-9a-f]{40}$/;
133
+ const LABEL_CREATED = '[create]';
134
+ const LABEL_ROTATED = '[rotate]';
135
+ const LABEL_SKIPPED = '[skip] ';
136
+ /**
137
+ * T676: the non-interactive half of `contextq init`'s credential
138
+ * resolution. Fixes the real incident this task is named for -- a demo run
139
+ * with only an AMBIENT env var set (direnv-exported CONTEXT_API_KEY from
140
+ * some repo's .env.local) used to be treated exactly like an explicit
141
+ * --key, silently overwriting a pre-existing machine-wide credential file
142
+ * with no backup. Branches, matching T676's required behavior 1-4:
143
+ *
144
+ * 1. no --key, ambient env only, file exists -> file untouched, used as-is,
145
+ * "skipped" naming the ignored env var
146
+ * 2. --key given, file exists with a different key or url -> back up the
147
+ * existing file (mode 600, timestamped) THEN write, "rotated"
148
+ * 3. --key given, file exists with the identical key AND url -> no write,
149
+ * "skipped (unchanged)"
150
+ * 4. no existing file at all -> unchanged prior behavior, "created"
151
+ * (works from either --key or an ambient env var, since there is
152
+ * nothing to protect)
153
+ *
154
+ * The one case this function does not resolve on its own is "no --key, no
155
+ * ambient env var, no existing file" -- action comes back null and the
156
+ * caller must prompt interactively, then call this function again with the
157
+ * typed value as `explicitKey` to get a consistent created/rotated/skipped
158
+ * decision (falls into branch 4 in practice, since there is no file yet).
159
+ */
160
+ export function decideCredentialWrite(input) {
161
+ const existing = readExistingCreds();
162
+ const fileExists = credsFileExists();
163
+ const writeSafely = (newCreds) => {
164
+ if (!fileExists) {
165
+ writeCreds(newCreds);
166
+ return { action: 'created', backupPath: null };
167
+ }
168
+ if (credsEqual(existing, newCreds)) {
169
+ return { action: 'skipped', backupPath: null };
170
+ }
171
+ const backupPath = backupCredsFile();
172
+ writeCreds(newCreds);
173
+ return { action: 'rotated', backupPath };
174
+ };
175
+ if (input.explicitKey) {
176
+ if (!KEY_RE.test(input.explicitKey)) {
177
+ throw new Error('the provided API key does not look valid (expected sk_live_<40 hex chars>)');
178
+ }
179
+ const apiKey = input.explicitKey;
180
+ const result = writeSafely({ apiKey, apiUrl: input.apiUrl });
181
+ const credLine = result.action === 'skipped'
182
+ ? `${LABEL_SKIPPED} credentials (unchanged)`
183
+ : result.action === 'rotated'
184
+ ? `${LABEL_ROTATED} credentials (${platformCredFile()}; previous saved to ${result.backupPath})`
185
+ : `${LABEL_CREATED} credentials (${platformCredFile()}, mode 600)`;
186
+ return { apiKey, credLine, action: result.action, backupPath: result.backupPath };
187
+ }
188
+ if (fileExists && existing.apiKey) {
189
+ // Branch 1: an ambient env var, if any, is deliberately never even
190
+ // validated here -- it is not going to be used, so a garbage value must
191
+ // not throw.
192
+ const credLine = input.ambientVarName
193
+ ? `${LABEL_SKIPPED} credentials (already configured at ${platformCredFile()}; env ${input.ambientVarName} ignored, pass --key to rotate)`
194
+ : `${LABEL_SKIPPED} credentials (already configured at ${platformCredFile()}; pass --key to rotate)`;
195
+ return { apiKey: existing.apiKey, credLine, action: 'skipped', backupPath: null };
196
+ }
197
+ if (input.ambientKey) {
198
+ if (!KEY_RE.test(input.ambientKey)) {
199
+ throw new Error('the provided API key does not look valid (expected sk_live_<40 hex chars>)');
200
+ }
201
+ const apiKey = input.ambientKey;
202
+ const result = writeSafely({ apiKey, apiUrl: input.apiUrl });
203
+ const credLine = result.action === 'rotated'
204
+ ? `${LABEL_ROTATED} credentials (${platformCredFile()}; previous saved to ${result.backupPath})`
205
+ : `${LABEL_CREATED} credentials (${platformCredFile()}, mode 600)`;
206
+ return { apiKey, credLine, action: result.action, backupPath: result.backupPath };
207
+ }
208
+ return { apiKey: '', credLine: '', action: null, backupPath: null };
209
+ }
210
+ //# sourceMappingURL=platform-cred.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-cred.js","sourceRoot":"","sources":["../src/platform-cred.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * prompt.ts — masked stdin prompt for the API key, no extra dependency.
3
+ *
4
+ * Returns '' immediately when stdin is not a TTY (non-interactive/CI run) --
5
+ * callers must treat that as "no key available" and fail with a clear
6
+ * message rather than hang waiting for input that will never arrive.
7
+ */
8
+ export declare function promptHidden(question: string): Promise<string>;
package/dist/prompt.js ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * prompt.ts — masked stdin prompt for the API key, no extra dependency.
3
+ *
4
+ * Returns '' immediately when stdin is not a TTY (non-interactive/CI run) --
5
+ * callers must treat that as "no key available" and fail with a clear
6
+ * message rather than hang waiting for input that will never arrive.
7
+ */
8
+ const CTRL_C = String.fromCharCode(3);
9
+ const CTRL_D = String.fromCharCode(4);
10
+ const DEL = String.fromCharCode(127);
11
+ export async function promptHidden(question) {
12
+ if (!process.stdin.isTTY)
13
+ return '';
14
+ return new Promise((resolve) => {
15
+ const stdin = process.stdin;
16
+ const stdout = process.stdout;
17
+ stdout.write(question);
18
+ let input = '';
19
+ const onData = (chunk) => {
20
+ const char = chunk.toString('utf8');
21
+ switch (char) {
22
+ case '\n':
23
+ case '\r':
24
+ case CTRL_D:
25
+ stdin.setRawMode?.(false);
26
+ stdin.pause();
27
+ stdin.removeListener('data', onData);
28
+ stdout.write('\n');
29
+ resolve(input.trim());
30
+ break;
31
+ case CTRL_C:
32
+ stdout.write('\n');
33
+ process.exit(130);
34
+ break;
35
+ case DEL:
36
+ case '\b':
37
+ if (input.length > 0) {
38
+ input = input.slice(0, -1);
39
+ stdout.write('\b \b');
40
+ }
41
+ break;
42
+ default:
43
+ input += char;
44
+ stdout.write('*');
45
+ }
46
+ };
47
+ stdin.setRawMode?.(true);
48
+ stdin.resume();
49
+ stdin.setEncoding('utf8');
50
+ stdin.on('data', onData);
51
+ });
52
+ }
53
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACtC,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACtC,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEvB,IAAI,KAAK,GAAG,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,CAAC,KAAa,EAAQ,EAAE;YACrC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACpC,QAAQ,IAAI,EAAE,CAAC;gBACb,KAAK,IAAI,CAAC;gBACV,KAAK,IAAI,CAAC;gBACV,KAAK,MAAM;oBACT,KAAK,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC;oBAC1B,KAAK,CAAC,KAAK,EAAE,CAAC;oBACd,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;oBACrC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACnB,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;oBACtB,MAAM;gBACR,KAAK,MAAM;oBACT,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACnB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBAClB,MAAM;gBACR,KAAK,GAAG,CAAC;gBACT,KAAK,IAAI;oBACP,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACrB,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;wBAC3B,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBACxB,CAAC;oBACD,MAAM;gBACR;oBACE,KAAK,IAAI,IAAI,CAAC;oBACd,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACtB,CAAC;QACH,CAAC,CAAC;QAEF,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,CAAC,MAAM,EAAE,CAAC;QACf,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC1B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * session-hook.ts — idempotent SessionStart boot-hook merge, for BOTH
3
+ * Claude Code and Codex CLI (T665).
4
+ *
5
+ * Targets <root>/.claude/settings.local.json for Claude Code, NOT
6
+ * settings.json -- mirroring skills/contextq/scripts/install-hook.sh's own
7
+ * explicit rule: the baked hook command embeds an absolute $HOME/... path,
8
+ * which is per-machine and MUST stay in a gitignored file, never a
9
+ * shared/global settings file (see install-hook.sh header comment).
10
+ *
11
+ * Codex's own convention treats project-level `.codex/config.toml` /
12
+ * `.codex/hooks.json` as "trusted-repo settings" meant to be COMMITTED and
13
+ * shared across a team (verified against developers.openai.com/codex docs,
14
+ * 2026-08-20: "Project `.codex/config.toml` -> trusted-repo Codex settings
15
+ * such as sandbox, MCP, hooks, model, or reasoning defaults") -- there is no
16
+ * Codex-native equivalent of Claude's settings.json/settings.local.json
17
+ * split. Since the command we write embeds the SAME per-machine absolute
18
+ * path either way, wireCodexSessionHook() deliberately breaks from that
19
+ * convention and gitignores `.codex/hooks.json` itself (see
20
+ * ensureGitignored below) rather than let a per-machine path get committed
21
+ * and silently break for every teammate whose contextq skill lives
22
+ * somewhere else (or isn't installed at all).
23
+ *
24
+ * This installer POINTS AT the contextq skill's boot script when it is
25
+ * already installed globally at ~/.claude/skills/contextq -- it never
26
+ * copies/vendors the skill tree into the target project (out of this
27
+ * installer's declared write-scope: .mcp.json, .claude/settings*.json,
28
+ * .codex/hooks.json, .contextq/, and the credential file only).
29
+ *
30
+ * ONE shared code path for both harnesses (T665 requirement), in two
31
+ * layers:
32
+ * - buildBootCommand() is the single command-string builder both writers
33
+ * call -- same env vars, same shellQuote()-based escaping, same target
34
+ * script.
35
+ * - mergeSessionStartHook() is the single read-merge-idempotent-write
36
+ * implementation both writers call. Claude's `.claude/settings.local.json`
37
+ * and Codex's `.codex/hooks.json` use the IDENTICAL JSON shape for a
38
+ * SessionStart entry -- { hooks: { SessionStart: [ { hooks: [ { type:
39
+ * 'command', command } ] } ] } } (verified against Codex's `hooks.json`
40
+ * schema at developers.openai.com/codex/hooks, 2026-08-20) -- so there is
41
+ * exactly one implementation of "does this file already have our hook,
42
+ * and if not, append it", parameterized by which file path to merge
43
+ * into. The two exported writers (wireSessionHook, wireCodexSessionHook)
44
+ * are thin wrappers supplying only the path + an optional top-level
45
+ * default field Codex's schema additionally supports ("description").
46
+ */
47
+ export interface HookWireResult {
48
+ path: string;
49
+ action: 'wired' | 'skipped';
50
+ detail: string;
51
+ }
52
+ /**
53
+ * T656: POSIX single-quote wrap, safe for any input including shell
54
+ * metacharacters, unbalanced quotes, backticks, `$()`, `;`, etc. This is the
55
+ * defense-in-depth layer: slug.ts's validateSlug()/sanitizeSlugFallback()
56
+ * already constrain --agent/--workspace to a safe charset before they reach
57
+ * here, but this function must make the written command safe on its own,
58
+ * because a future caller of wireSessionHook()/wireCodexSessionHook() might
59
+ * bypass that validation (it takes plain strings, not a branded/validated
60
+ * type) -- every value substituted into the command string, including
61
+ * bootScript itself, goes through this.
62
+ *
63
+ * Standard technique: wrap in single quotes (nothing is interpolated inside
64
+ * single quotes in POSIX sh/bash), and for each embedded single quote,
65
+ * close the quote, emit an escaped literal quote, then reopen: ' -> '\''.
66
+ */
67
+ export declare function shellQuote(value: string): string;
68
+ export declare function ctqBrainBootScript(): string;
69
+ /**
70
+ * buildBootCommand — the ONE harness-agnostic command builder (T665). Both
71
+ * wireSessionHook (Claude) and wireCodexSessionHook (Codex) call this to
72
+ * produce the exact same shell command string. Codex's SessionStart stdin
73
+ * contract and its `hookSpecificOutput.additionalContext` output shape are
74
+ * verified (2026-08-20, developers.openai.com/codex/hooks) to match Claude's
75
+ * field-for-field (session_id, transcript_path, cwd, hook_event_name on
76
+ * stdin; the same hookSpecificOutput.additionalContext JSON, or plain text,
77
+ * on stdout) -- so contextq-boot.sh's existing --hook output needs no
78
+ * per-harness branching, and this builder needs none either.
79
+ */
80
+ export declare function buildBootCommand(agentSlug: string, workspace: string, bootScript: string): string;
81
+ /**
82
+ * `bootScriptOverride` exists purely for tests: production callers (index.ts)
83
+ * always call this with 3 args and get ctqBrainBootScript(), which points at
84
+ * a real, per-machine, possibly-absent path under $HOME -- tests need to
85
+ * exercise the "wired" branch deterministically without depending on the
86
+ * host actually having contextq installed.
87
+ */
88
+ export declare function wireSessionHook(root: string, agentSlug: string, workspace: string, bootScriptOverride?: string): HookWireResult;
89
+ /**
90
+ * wireCodexSessionHook — the Codex writer (T665). Same command, same merge
91
+ * core as wireSessionHook above; only the target path, the top-level
92
+ * "description" default, and the gitignore step differ. `bootScriptOverride`
93
+ * exists for the same test-determinism reason as wireSessionHook's.
94
+ */
95
+ export declare function wireCodexSessionHook(root: string, agentSlug: string, workspace: string, bootScriptOverride?: string): HookWireResult;