@furkankoykiran/contextify-cli 0.4.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.
Files changed (64) hide show
  1. package/dist/batcher.d.ts +36 -0
  2. package/dist/batcher.d.ts.map +1 -0
  3. package/dist/batcher.js +94 -0
  4. package/dist/batcher.js.map +1 -0
  5. package/dist/commands/hooks.d.ts +32 -0
  6. package/dist/commands/hooks.d.ts.map +1 -0
  7. package/dist/commands/hooks.js +253 -0
  8. package/dist/commands/hooks.js.map +1 -0
  9. package/dist/commands/init.d.ts +9 -0
  10. package/dist/commands/init.d.ts.map +1 -0
  11. package/dist/commands/init.js +49 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/install-hooks.d.ts +42 -0
  14. package/dist/commands/install-hooks.d.ts.map +1 -0
  15. package/dist/commands/install-hooks.js +162 -0
  16. package/dist/commands/install-hooks.js.map +1 -0
  17. package/dist/commands/install.d.ts +53 -0
  18. package/dist/commands/install.d.ts.map +1 -0
  19. package/dist/commands/install.js +129 -0
  20. package/dist/commands/install.js.map +1 -0
  21. package/dist/commands/login.d.ts +9 -0
  22. package/dist/commands/login.d.ts.map +1 -0
  23. package/dist/commands/login.js +36 -0
  24. package/dist/commands/login.js.map +1 -0
  25. package/dist/commands/prompt.d.ts +17 -0
  26. package/dist/commands/prompt.d.ts.map +1 -0
  27. package/dist/commands/prompt.js +141 -0
  28. package/dist/commands/prompt.js.map +1 -0
  29. package/dist/commands/ship.d.ts +6 -0
  30. package/dist/commands/ship.d.ts.map +1 -0
  31. package/dist/commands/ship.js +20 -0
  32. package/dist/commands/ship.js.map +1 -0
  33. package/dist/commands/wrap.d.ts +10 -0
  34. package/dist/commands/wrap.d.ts.map +1 -0
  35. package/dist/commands/wrap.js +83 -0
  36. package/dist/commands/wrap.js.map +1 -0
  37. package/dist/config.d.ts +16 -0
  38. package/dist/config.d.ts.map +1 -0
  39. package/dist/config.js +84 -0
  40. package/dist/config.js.map +1 -0
  41. package/dist/credentials.d.ts +34 -0
  42. package/dist/credentials.d.ts.map +1 -0
  43. package/dist/credentials.js +68 -0
  44. package/dist/credentials.js.map +1 -0
  45. package/dist/identity.d.ts +42 -0
  46. package/dist/identity.d.ts.map +1 -0
  47. package/dist/identity.js +201 -0
  48. package/dist/identity.js.map +1 -0
  49. package/dist/index.d.ts +9 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +0 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/shipper.d.ts +49 -0
  54. package/dist/shipper.d.ts.map +1 -0
  55. package/dist/shipper.js +110 -0
  56. package/dist/shipper.js.map +1 -0
  57. package/dist/transcript.d.ts +52 -0
  58. package/dist/transcript.d.ts.map +1 -0
  59. package/dist/transcript.js +216 -0
  60. package/dist/transcript.js.map +1 -0
  61. package/package.json +69 -0
  62. package/src/hooks/session-end.sh +5 -0
  63. package/src/hooks/session-start.sh +5 -0
  64. package/src/hooks/stop.sh +5 -0
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Bounded in-memory batcher.
3
+ *
4
+ * Appends data; flushes when either (a) the buffer crosses `maxBytes`
5
+ * or (b) `maxIdleMs` has elapsed since the last append. Concurrency
6
+ * model:
7
+ * - Only one flush runs at a time.
8
+ * - Calls to flush() while one is in-flight chain after it — they
9
+ * await the same shared promise rather than dropping. This lets
10
+ * `close()` reliably drain everything that arrived during the
11
+ * last in-flight flush.
12
+ */
13
+ export interface BatcherOptions {
14
+ readonly maxBytes: number;
15
+ readonly maxIdleMs: number;
16
+ readonly flush: (payload: string) => Promise<void>;
17
+ readonly setInterval?: typeof setInterval;
18
+ readonly clearInterval?: typeof clearInterval;
19
+ }
20
+ export declare class Batcher {
21
+ #private;
22
+ constructor(opts: BatcherOptions);
23
+ start(): void;
24
+ append(chunk: string): void;
25
+ get bufferedBytes(): number;
26
+ /**
27
+ * Drain the buffer.
28
+ *
29
+ * If a flush is already in-flight, callers wait for it AND for whatever
30
+ * the buffer holds when that flush ends. This makes close() correct
31
+ * even when an append() races against an in-flight flush.
32
+ */
33
+ flush(): Promise<void>;
34
+ close(): Promise<void>;
35
+ }
36
+ //# sourceMappingURL=batcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batcher.d.ts","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,WAAW,CAAC;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,aAAa,CAAC;CAC/C;AAED,qBAAa,OAAO;;gBAaN,IAAI,EAAE,cAAc;IAQhC,KAAK,IAAI,IAAI;IAYb,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAU3B,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED;;;;;;OAMG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBhB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7B"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Bounded in-memory batcher.
3
+ *
4
+ * Appends data; flushes when either (a) the buffer crosses `maxBytes`
5
+ * or (b) `maxIdleMs` has elapsed since the last append. Concurrency
6
+ * model:
7
+ * - Only one flush runs at a time.
8
+ * - Calls to flush() while one is in-flight chain after it — they
9
+ * await the same shared promise rather than dropping. This lets
10
+ * `close()` reliably drain everything that arrived during the
11
+ * last in-flight flush.
12
+ */
13
+ export class Batcher {
14
+ #maxBytes;
15
+ #maxIdleMs;
16
+ #flushCb;
17
+ #setInterval;
18
+ #clearInterval;
19
+ #buffer = [];
20
+ #bytes = 0;
21
+ #timer = null;
22
+ #pending = null;
23
+ #closed = false;
24
+ constructor(opts) {
25
+ this.#maxBytes = opts.maxBytes;
26
+ this.#maxIdleMs = opts.maxIdleMs;
27
+ this.#flushCb = opts.flush;
28
+ this.#setInterval = opts.setInterval ?? setInterval;
29
+ this.#clearInterval = opts.clearInterval ?? clearInterval;
30
+ }
31
+ start() {
32
+ if (this.#timer || this.#closed)
33
+ return;
34
+ this.#timer = this.#setInterval(() => {
35
+ this.flush().catch(() => {
36
+ /* swallow — the shipper already handles errors */
37
+ });
38
+ }, this.#maxIdleMs);
39
+ if (typeof this.#timer?.unref === 'function') {
40
+ this.#timer.unref();
41
+ }
42
+ }
43
+ append(chunk) {
44
+ if (this.#closed)
45
+ return;
46
+ if (chunk.length === 0)
47
+ return;
48
+ this.#buffer.push(chunk);
49
+ this.#bytes += Buffer.byteLength(chunk, 'utf8');
50
+ if (this.#bytes >= this.#maxBytes) {
51
+ this.flush().catch(() => { });
52
+ }
53
+ }
54
+ get bufferedBytes() {
55
+ return this.#bytes;
56
+ }
57
+ /**
58
+ * Drain the buffer.
59
+ *
60
+ * If a flush is already in-flight, callers wait for it AND for whatever
61
+ * the buffer holds when that flush ends. This makes close() correct
62
+ * even when an append() races against an in-flight flush.
63
+ */
64
+ flush() {
65
+ if (this.#pending) {
66
+ const pending = this.#pending;
67
+ // Chain: after the in-flight flush completes, drain anything left.
68
+ const next = pending.then(() => this.#drainOnce()).catch(() => undefined);
69
+ return next;
70
+ }
71
+ return this.#drainOnce();
72
+ }
73
+ #drainOnce() {
74
+ if (this.#buffer.length === 0)
75
+ return Promise.resolve();
76
+ const payload = this.#buffer.join('');
77
+ this.#buffer = [];
78
+ this.#bytes = 0;
79
+ const promise = this.#flushCb(payload).finally(() => {
80
+ this.#pending = null;
81
+ });
82
+ this.#pending = promise;
83
+ return promise;
84
+ }
85
+ async close() {
86
+ this.#closed = true;
87
+ if (this.#timer) {
88
+ this.#clearInterval(this.#timer);
89
+ this.#timer = null;
90
+ }
91
+ await this.flush();
92
+ }
93
+ }
94
+ //# sourceMappingURL=batcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batcher.js","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export type HookEvent = 'session-start' | 'stop' | 'session-end';
2
+ export interface HookPayload {
3
+ session_id?: string;
4
+ transcript_path?: string;
5
+ cwd?: string;
6
+ hook_event_name?: string;
7
+ source?: string;
8
+ reason?: string;
9
+ [k: string]: unknown;
10
+ }
11
+ export interface SessionState {
12
+ projectId: string;
13
+ projectName: string;
14
+ serverUrl: string;
15
+ cwd: string;
16
+ startedAt: string;
17
+ lastShippedUuid?: string;
18
+ }
19
+ export interface HookDeps {
20
+ readonly env?: NodeJS.ProcessEnv;
21
+ /** For tests: where to read stdin from. Default: process.stdin. */
22
+ readonly readStdin?: () => Promise<string>;
23
+ /** For tests: override the contextify state directory. */
24
+ readonly stateRoot?: string;
25
+ /** For tests: HTTP client override. */
26
+ readonly fetchImpl?: typeof fetch;
27
+ }
28
+ export declare function stateRootFor(env: NodeJS.ProcessEnv, override?: string): string;
29
+ export declare function runHook(event: HookEvent, deps?: HookDeps): Promise<number>;
30
+ /** Build a fresh session id (used only by tests that synthesize payloads). */
31
+ export declare function newSessionId(): string;
32
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"AAwBA,MAAM,MAAM,SAAS,GAAG,eAAe,GAAG,MAAM,GAAG,aAAa,CAAC;AAEjE,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,uCAAuC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACnC;AAgBD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAG9E;AAoMD,wBAAsB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,GAAE,QAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BpF;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
@@ -0,0 +1,253 @@
1
+ /**
2
+ * `contextify hooks <event>` — internal entry point for the Claude Code hooks.
3
+ *
4
+ * The three hook events delegate to this command. It reads the Claude Code
5
+ * hook payload from stdin, resolves identity at the cwd, and:
6
+ * - session-start: upserts the project and writes session state.
7
+ * - stop: parses the transcript for the latest turn and ships it.
8
+ * - session-end: flushes the spool and clears session state.
9
+ *
10
+ * The CLI bin (`contextify`) routes to this when called as
11
+ * `contextify hooks <event>`. Bash hook scripts do nothing except pipe
12
+ * the hook stdin into this and discard the exit code.
13
+ */
14
+ import { readFile, mkdir, writeFile, unlink, rename } from 'node:fs/promises';
15
+ import { existsSync } from 'node:fs';
16
+ import { homedir } from 'node:os';
17
+ import { dirname, join } from 'node:path';
18
+ import { randomUUID } from 'node:crypto';
19
+ import { DEFAULT_SERVER_URL } from '../config.js';
20
+ import { resolveApiKey } from '../credentials.js';
21
+ import { resolveIdentity } from '../identity.js';
22
+ import { flushSpool, shipBatch } from '../shipper.js';
23
+ import { parseLatestTurn } from '../transcript.js';
24
+ /** Default stdin reader — fully consumes process.stdin. */
25
+ async function defaultReadStdin() {
26
+ if (process.stdin.isTTY)
27
+ return '';
28
+ return new Promise((resolve, reject) => {
29
+ let buf = '';
30
+ process.stdin.setEncoding('utf8');
31
+ process.stdin.on('data', (chunk) => {
32
+ buf += chunk;
33
+ });
34
+ process.stdin.on('end', () => resolve(buf));
35
+ process.stdin.on('error', reject);
36
+ });
37
+ }
38
+ export function stateRootFor(env, override) {
39
+ if (override)
40
+ return override;
41
+ return env.CONTEXTIFY_STATE_DIR ?? join(homedir(), '.contextify');
42
+ }
43
+ function sessionFile(stateRoot, sessionId) {
44
+ return join(stateRoot, 'sessions', `${sessionId}.json`);
45
+ }
46
+ async function ensureDir(path) {
47
+ if (!existsSync(path))
48
+ await mkdir(path, { recursive: true });
49
+ }
50
+ async function atomicWriteJson(path, data) {
51
+ await ensureDir(dirname(path));
52
+ const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
53
+ await writeFile(tmp, JSON.stringify(data, null, 2), 'utf8');
54
+ await rename(tmp, path);
55
+ }
56
+ async function readSession(stateRoot, sessionId) {
57
+ const path = sessionFile(stateRoot, sessionId);
58
+ if (!existsSync(path))
59
+ return null;
60
+ try {
61
+ const raw = await readFile(path, 'utf8');
62
+ return JSON.parse(raw);
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ async function appendLog(stateRoot, line) {
69
+ try {
70
+ await ensureDir(stateRoot);
71
+ const path = join(stateRoot, 'hooks.log');
72
+ const { appendFile } = await import('node:fs/promises');
73
+ await appendFile(path, `${new Date().toISOString()} ${line}\n`, 'utf8');
74
+ }
75
+ catch {
76
+ // never block on logging
77
+ }
78
+ }
79
+ function resolveServerUrl(env, creds) {
80
+ return env.CONTEXTIFY_SERVER_URL || creds?.serverUrl || DEFAULT_SERVER_URL;
81
+ }
82
+ async function upsertProject(serverUrl, identity, fetchImpl, creds) {
83
+ const url = new URL('/api/projects', serverUrl).toString();
84
+ const headers = { 'content-type': 'application/json' };
85
+ if (creds)
86
+ headers.authorization = `Bearer ${creds.apiKey}`;
87
+ await fetchImpl(url, {
88
+ method: 'POST',
89
+ headers,
90
+ body: JSON.stringify({ id: identity.projectId, name: identity.projectName }),
91
+ });
92
+ }
93
+ async function runSessionStart(payload, deps) {
94
+ const cwd = payload.cwd ?? deps.env.PWD ?? process.cwd();
95
+ const sessionId = payload.session_id;
96
+ if (!sessionId) {
97
+ await appendLog(deps.stateRoot, 'session-start: missing session_id');
98
+ return 0;
99
+ }
100
+ let identity;
101
+ try {
102
+ identity = await resolveIdentity({ cwd, env: deps.env });
103
+ }
104
+ catch (err) {
105
+ await appendLog(deps.stateRoot, `session-start: identity error: ${err.message}`);
106
+ return 0;
107
+ }
108
+ const creds = resolveApiKey(deps.env);
109
+ const serverUrl = resolveServerUrl(deps.env, creds);
110
+ const state = {
111
+ projectId: identity.projectId,
112
+ projectName: identity.projectName,
113
+ serverUrl,
114
+ cwd,
115
+ startedAt: new Date().toISOString(),
116
+ };
117
+ await atomicWriteJson(sessionFile(deps.stateRoot, sessionId), state);
118
+ try {
119
+ await upsertProject(serverUrl, identity, deps.fetchImpl, creds);
120
+ }
121
+ catch (err) {
122
+ await appendLog(deps.stateRoot, `session-start: upsert failed: ${err.message}`);
123
+ }
124
+ return 0;
125
+ }
126
+ async function runStop(payload, deps) {
127
+ const sessionId = payload.session_id;
128
+ const transcriptPath = payload.transcript_path;
129
+ if (!sessionId || !transcriptPath) {
130
+ await appendLog(deps.stateRoot, 'stop: missing session_id or transcript_path');
131
+ return 0;
132
+ }
133
+ const state = await readSession(deps.stateRoot, sessionId);
134
+ if (!state) {
135
+ await appendLog(deps.stateRoot, `stop: no session state for ${sessionId} — skipping`);
136
+ return 0;
137
+ }
138
+ let transcript;
139
+ try {
140
+ transcript = await readFile(transcriptPath, 'utf8');
141
+ }
142
+ catch (err) {
143
+ await appendLog(deps.stateRoot, `stop: cannot read transcript: ${err.message}`);
144
+ return 0;
145
+ }
146
+ const turn = parseLatestTurn(transcript);
147
+ if (!turn) {
148
+ await appendLog(deps.stateRoot, 'stop: no completed text turn — skipping');
149
+ return 0;
150
+ }
151
+ if (state.lastShippedUuid && state.lastShippedUuid === turn.transcriptUuid) {
152
+ // Already shipped this exact turn — Stop must have re-fired.
153
+ return 0;
154
+ }
155
+ const dialogEnvelope = {
156
+ source: 'claude-code',
157
+ turn,
158
+ };
159
+ const batch = {
160
+ projectId: state.projectId,
161
+ projectName: state.projectName,
162
+ sessionId,
163
+ payload: JSON.stringify(dialogEnvelope),
164
+ source: 'claude-code',
165
+ };
166
+ const ship = await shipBatch(batch, {
167
+ serverUrl: state.serverUrl,
168
+ cwd: state.cwd,
169
+ fetchImpl: deps.fetchImpl,
170
+ });
171
+ if (ship.status === 'error') {
172
+ await appendLog(deps.stateRoot, `stop: shipBatch error: ${ship.error ?? 'unknown'}`);
173
+ }
174
+ await atomicWriteJson(sessionFile(deps.stateRoot, sessionId), {
175
+ ...state,
176
+ lastShippedUuid: turn.transcriptUuid,
177
+ });
178
+ return 0;
179
+ }
180
+ async function runSessionEnd(payload, deps) {
181
+ const sessionId = payload.session_id;
182
+ if (!sessionId) {
183
+ await appendLog(deps.stateRoot, 'session-end: missing session_id');
184
+ return 0;
185
+ }
186
+ const state = await readSession(deps.stateRoot, sessionId);
187
+ if (state) {
188
+ try {
189
+ await flushSpool({
190
+ serverUrl: state.serverUrl,
191
+ cwd: state.cwd,
192
+ fetchImpl: deps.fetchImpl,
193
+ });
194
+ }
195
+ catch (err) {
196
+ await appendLog(deps.stateRoot, `session-end: flush error: ${err.message}`);
197
+ }
198
+ }
199
+ // Best-effort cleanup; missing file is fine.
200
+ try {
201
+ await unlink(sessionFile(deps.stateRoot, sessionId));
202
+ }
203
+ catch {
204
+ // ignored
205
+ }
206
+ return 0;
207
+ }
208
+ function resolveDeps(deps = {}) {
209
+ const env = deps.env ?? process.env;
210
+ return {
211
+ env,
212
+ readStdin: deps.readStdin ?? defaultReadStdin,
213
+ stateRoot: stateRootFor(env, deps.stateRoot),
214
+ fetchImpl: deps.fetchImpl ?? fetch,
215
+ };
216
+ }
217
+ export async function runHook(event, deps = {}) {
218
+ const resolved = resolveDeps(deps);
219
+ let payload = {};
220
+ try {
221
+ const raw = (await resolved.readStdin()).trim();
222
+ if (raw.length > 0)
223
+ payload = JSON.parse(raw);
224
+ }
225
+ catch (err) {
226
+ await appendLog(resolved.stateRoot, `${event}: stdin parse error: ${err.message}`);
227
+ return 0;
228
+ }
229
+ try {
230
+ switch (event) {
231
+ case 'session-start':
232
+ return await runSessionStart(payload, resolved);
233
+ case 'stop':
234
+ return await runStop(payload, resolved);
235
+ case 'session-end':
236
+ return await runSessionEnd(payload, resolved);
237
+ default:
238
+ // Unreachable via the CLI dispatcher, but guard anyway.
239
+ await appendLog(resolved.stateRoot, `unknown hook event: ${event}`);
240
+ return 0;
241
+ }
242
+ }
243
+ catch (err) {
244
+ // Absolute backstop — hooks never propagate failure.
245
+ await appendLog(resolved.stateRoot, `${event}: unhandled: ${err.message}`);
246
+ return 0;
247
+ }
248
+ }
249
+ /** Build a fresh session id (used only by tests that synthesize payloads). */
250
+ export function newSessionId() {
251
+ return randomUUID();
252
+ }
253
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface InitArgs {
2
+ readonly projectId?: string;
3
+ readonly projectName?: string;
4
+ readonly serverUrl?: string;
5
+ readonly force?: boolean;
6
+ readonly installHooks?: boolean;
7
+ }
8
+ export declare function runInit(args: InitArgs, cwd: string): Promise<number>;
9
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C1E"}
@@ -0,0 +1,49 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { configPath, writeConfig, DEFAULT_SERVER_URL } from '../config.js';
3
+ import { resolveIdentity } from '../identity.js';
4
+ import { installHooks } from './install-hooks.js';
5
+ export async function runInit(args, cwd) {
6
+ const path = configPath(cwd);
7
+ if (existsSync(path) && !args.force) {
8
+ process.stderr.write(`error: ${path} exists. Pass --force to overwrite.\n`);
9
+ return 1;
10
+ }
11
+ // Resolve identity: explicit positional arg wins; otherwise derive.
12
+ let projectId = args.projectId;
13
+ let projectName = args.projectName;
14
+ if (!projectId) {
15
+ try {
16
+ const id = await resolveIdentity({ cwd });
17
+ projectId = id.projectId;
18
+ projectName = projectName ?? id.projectName;
19
+ }
20
+ catch (err) {
21
+ process.stderr.write(`error: ${err.message}\n`);
22
+ return 2;
23
+ }
24
+ }
25
+ await writeConfig(cwd, {
26
+ projectId,
27
+ projectName,
28
+ serverUrl: args.serverUrl ?? DEFAULT_SERVER_URL,
29
+ });
30
+ process.stdout.write(`Wrote ${path}\n`);
31
+ if (args.installHooks) {
32
+ try {
33
+ const result = await installHooks();
34
+ const summary = {
35
+ installed: result.appendedEvents,
36
+ alreadyPresent: result.alreadyPresentEvents,
37
+ hooksDir: result.hooksDir,
38
+ backup: result.backupPath,
39
+ };
40
+ process.stdout.write(`Installed hooks: ${JSON.stringify(summary)}\n`);
41
+ }
42
+ catch (err) {
43
+ process.stderr.write(`error: install-hooks failed: ${err.message}\n`);
44
+ return 1;
45
+ }
46
+ }
47
+ return 0;
48
+ }
49
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAUlD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAc,EAAE,GAAW;IACvD,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,IAAI,uCAAuC,CAAC,CAAC;QAC5E,OAAO,CAAC,CAAC;IACX,CAAC;IAED,oEAAoE;IACpE,IAAI,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IAC/B,IAAI,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACnC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,eAAe,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;YAC1C,SAAS,GAAG,EAAE,CAAC,SAAS,CAAC;YACzB,WAAW,GAAG,WAAW,IAAI,EAAE,CAAC,WAAW,CAAC;QAC9C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAW,GAAa,CAAC,OAAO,IAAI,CAAC,CAAC;YAC3D,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IAED,MAAM,WAAW,CAAC,GAAG,EAAE;QACrB,SAAS;QACT,WAAW;QACX,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,kBAAkB;KAChD,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;IAExC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG;gBACd,SAAS,EAAE,MAAM,CAAC,cAAc;gBAChC,cAAc,EAAE,MAAM,CAAC,oBAAoB;gBAC3C,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,MAAM,EAAE,MAAM,CAAC,UAAU;aAC1B,CAAC;YACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAiC,GAAa,CAAC,OAAO,IAAI,CAAC,CAAC;YACjF,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,42 @@
1
+ export declare const HOOK_EVENTS: readonly ["SessionStart", "Stop", "SessionEnd"];
2
+ export type ClaudeHookEvent = (typeof HOOK_EVENTS)[number];
3
+ export interface InstallHooksOptions {
4
+ readonly stateRoot?: string;
5
+ readonly claudeSettingsPath?: string;
6
+ readonly env?: NodeJS.ProcessEnv;
7
+ /** For tests — override the script bodies. */
8
+ readonly scriptBodies?: Partial<Record<ClaudeHookEvent, string>>;
9
+ }
10
+ export interface InstallHooksResult {
11
+ readonly stateRoot: string;
12
+ readonly hooksDir: string;
13
+ readonly settingsPath: string;
14
+ readonly backupPath: string | null;
15
+ /** Events whose hook entry we appended. */
16
+ readonly appendedEvents: readonly ClaudeHookEvent[];
17
+ /** Events that already had our hook entry — left untouched. */
18
+ readonly alreadyPresentEvents: readonly ClaudeHookEvent[];
19
+ }
20
+ interface HookCommandEntry {
21
+ type: 'command';
22
+ command: string;
23
+ }
24
+ interface HookMatcherGroup {
25
+ matcher?: string;
26
+ hooks: HookCommandEntry[];
27
+ }
28
+ type SettingsHooks = Partial<Record<string, HookMatcherGroup[] | undefined>>;
29
+ interface ClaudeSettings {
30
+ hooks?: SettingsHooks;
31
+ [k: string]: unknown;
32
+ }
33
+ export declare function defaultStateRoot(env?: NodeJS.ProcessEnv): string;
34
+ export declare function defaultClaudeSettingsPath(env?: NodeJS.ProcessEnv): string;
35
+ /**
36
+ * Append `command` under `hooks.<event>` without disturbing other groups.
37
+ * Returns true if a new entry was appended, false if already present.
38
+ */
39
+ export declare function appendHookCommand(settings: ClaudeSettings, event: ClaudeHookEvent, command: string): boolean;
40
+ export declare function installHooks(opts?: InstallHooksOptions): Promise<InstallHooksResult>;
41
+ export {};
42
+ //# sourceMappingURL=install-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-hooks.d.ts","sourceRoot":"","sources":["../../src/commands/install-hooks.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,WAAW,iDAAkD,CAAC;AAC3E,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AA6B3D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,8CAA8C;IAC9C,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;CAClE;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,2CAA2C;IAC3C,QAAQ,CAAC,cAAc,EAAE,SAAS,eAAe,EAAE,CAAC;IACpD,+DAA+D;IAC/D,QAAQ,CAAC,oBAAoB,EAAE,SAAS,eAAe,EAAE,CAAC;CAC3D;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,gBAAgB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,KAAK,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;AAE7E,UAAU,cAAc;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE7E;AAED,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAEtF;AA2CD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,MAAM,GACd,OAAO,CAST;AA0BD,wBAAsB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAuC9F"}