@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,162 @@
1
+ /**
2
+ * `contextify init --install-hooks` implementation.
3
+ *
4
+ * 1. Materialize the three hook scripts into <state>/hooks/ with executable bit.
5
+ * 2. Snapshot ~/.claude/settings.json to <state>/backups/settings.<UTC>.json.
6
+ * 3. Merge a SessionStart / Stop / SessionEnd entry into settings.json,
7
+ * skipping any event that already references our hook command.
8
+ *
9
+ * Idempotent: rerunning is a no-op (modulo the harmless backup).
10
+ *
11
+ * See docs/DESIGN-claude-code-hooks.md §5.
12
+ */
13
+ import { existsSync } from 'node:fs';
14
+ import { chmod, copyFile, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
15
+ import { homedir } from 'node:os';
16
+ import { dirname, join } from 'node:path';
17
+ export const HOOK_EVENTS = ['SessionStart', 'Stop', 'SessionEnd'];
18
+ const SCRIPT_FILES = {
19
+ SessionStart: 'session-start.sh',
20
+ Stop: 'stop.sh',
21
+ SessionEnd: 'session-end.sh',
22
+ };
23
+ const SCRIPT_BODIES = {
24
+ SessionStart: `#!/usr/bin/env bash
25
+ # Contextify hook — SessionStart.
26
+ # Pipes the Claude Code hook stdin into 'contextify hooks session-start'.
27
+ # Silent on success, never blocks.
28
+ exec contextify hooks session-start
29
+ `,
30
+ Stop: `#!/usr/bin/env bash
31
+ # Contextify hook — Stop.
32
+ # Pipes the Claude Code hook stdin into 'contextify hooks stop'.
33
+ # Silent on success, never blocks.
34
+ exec contextify hooks stop
35
+ `,
36
+ SessionEnd: `#!/usr/bin/env bash
37
+ # Contextify hook — SessionEnd.
38
+ # Pipes the Claude Code hook stdin into 'contextify hooks session-end'.
39
+ # Silent on success, never blocks.
40
+ exec contextify hooks session-end
41
+ `,
42
+ };
43
+ export function defaultStateRoot(env = process.env) {
44
+ return env.CONTEXTIFY_STATE_DIR ?? join(homedir(), '.contextify');
45
+ }
46
+ export function defaultClaudeSettingsPath(env = process.env) {
47
+ return env.CLAUDE_SETTINGS_PATH ?? join(homedir(), '.claude', 'settings.json');
48
+ }
49
+ function hookCommandFor(stateRoot, event) {
50
+ return join(stateRoot, 'hooks', SCRIPT_FILES[event]);
51
+ }
52
+ async function ensureDir(path) {
53
+ if (!existsSync(path))
54
+ await mkdir(path, { recursive: true });
55
+ }
56
+ async function atomicWrite(path, content) {
57
+ await ensureDir(dirname(path));
58
+ const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
59
+ await writeFile(tmp, content, 'utf8');
60
+ await rename(tmp, path);
61
+ }
62
+ async function readSettings(path) {
63
+ if (!existsSync(path))
64
+ return {};
65
+ const raw = await readFile(path, 'utf8');
66
+ if (raw.trim().length === 0)
67
+ return {};
68
+ try {
69
+ const parsed = JSON.parse(raw);
70
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
71
+ return parsed;
72
+ }
73
+ throw new Error('settings.json root must be an object');
74
+ }
75
+ catch (err) {
76
+ throw new Error(`failed to parse ${path}: ${err.message}`);
77
+ }
78
+ }
79
+ function eventHasCommand(groups, command) {
80
+ if (!groups)
81
+ return false;
82
+ for (const g of groups) {
83
+ if (!g || !Array.isArray(g.hooks))
84
+ continue;
85
+ for (const h of g.hooks) {
86
+ if (h && h.type === 'command' && h.command === command)
87
+ return true;
88
+ }
89
+ }
90
+ return false;
91
+ }
92
+ /**
93
+ * Append `command` under `hooks.<event>` without disturbing other groups.
94
+ * Returns true if a new entry was appended, false if already present.
95
+ */
96
+ export function appendHookCommand(settings, event, command) {
97
+ const hooks = settings.hooks ?? {};
98
+ const existing = hooks[event];
99
+ if (eventHasCommand(existing, command))
100
+ return false;
101
+ const groups = Array.isArray(existing) ? [...existing] : [];
102
+ groups.push({ hooks: [{ type: 'command', command }] });
103
+ hooks[event] = groups;
104
+ settings.hooks = hooks;
105
+ return true;
106
+ }
107
+ async function materializeScripts(stateRoot, bodies) {
108
+ const hooksDir = join(stateRoot, 'hooks');
109
+ await ensureDir(hooksDir);
110
+ for (const event of HOOK_EVENTS) {
111
+ const target = join(hooksDir, SCRIPT_FILES[event]);
112
+ await atomicWrite(target, bodies[event]);
113
+ await chmod(target, 0o755);
114
+ }
115
+ return hooksDir;
116
+ }
117
+ async function snapshotSettings(stateRoot, settingsPath) {
118
+ if (!existsSync(settingsPath))
119
+ return null;
120
+ const backupDir = join(stateRoot, 'backups');
121
+ await ensureDir(backupDir);
122
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
123
+ const backupPath = join(backupDir, `settings.${stamp}.json`);
124
+ await copyFile(settingsPath, backupPath);
125
+ return backupPath;
126
+ }
127
+ export async function installHooks(opts = {}) {
128
+ const env = opts.env ?? process.env;
129
+ const stateRoot = opts.stateRoot ?? defaultStateRoot(env);
130
+ const settingsPath = opts.claudeSettingsPath ?? defaultClaudeSettingsPath(env);
131
+ const bodies = {
132
+ SessionStart: opts.scriptBodies?.SessionStart ?? SCRIPT_BODIES.SessionStart,
133
+ Stop: opts.scriptBodies?.Stop ?? SCRIPT_BODIES.Stop,
134
+ SessionEnd: opts.scriptBodies?.SessionEnd ?? SCRIPT_BODIES.SessionEnd,
135
+ };
136
+ const hooksDir = await materializeScripts(stateRoot, bodies);
137
+ const backupPath = await snapshotSettings(stateRoot, settingsPath);
138
+ const settings = await readSettings(settingsPath);
139
+ const appended = [];
140
+ const present = [];
141
+ for (const event of HOOK_EVENTS) {
142
+ const command = hookCommandFor(stateRoot, event);
143
+ if (appendHookCommand(settings, event, command)) {
144
+ appended.push(event);
145
+ }
146
+ else {
147
+ present.push(event);
148
+ }
149
+ }
150
+ // Even when nothing changed, write settings back so the file always
151
+ // exists when we get to this point (rare edge case: empty default).
152
+ await atomicWrite(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
153
+ return {
154
+ stateRoot,
155
+ hooksDir,
156
+ settingsPath,
157
+ backupPath,
158
+ appendedEvents: appended,
159
+ alreadyPresentEvents: present,
160
+ };
161
+ }
162
+ //# sourceMappingURL=install-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-hooks.js","sourceRoot":"","sources":["../../src/commands/install-hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `contextify install` — zero-config global install.
3
+ *
4
+ * Materializes the three hook scripts under ~/.contextify/hooks/ and
5
+ * merges hook entries into ~/.claude/settings.json. Idempotent. Does
6
+ * NOT write .contextify.json into the cwd — that's `init`'s job.
7
+ *
8
+ * Also persists API credentials when --key (or CONTEXTIFY_API_KEY) is
9
+ * available, so IDE-spawned hook subprocesses (which do not inherit the
10
+ * operator's shell env) can authenticate against the SaaS via the
11
+ * on-disk ~/.contextify/credentials.json. Without this step, hooks ship
12
+ * unauthenticated and the server routes the data to LEGACY_TENANT_ID.
13
+ *
14
+ * Designed for users who want one-time global wiring: run once anywhere,
15
+ * and every Claude Code session in any directory gets captured. The hook
16
+ * scripts resolve project_id dynamically from each session's cwd at
17
+ * fire-time via identity.ts, so no per-project setup is required.
18
+ *
19
+ * Flags:
20
+ * --key <ctx_live_...> Persist this API key (chmod 600).
21
+ * --server <url> Persist server URL alongside the key.
22
+ * --name <label> Optional label stored next to the key.
23
+ * --dry-run Print what would change without modifying anything.
24
+ *
25
+ * Env auto-detect (when flags absent):
26
+ * CONTEXTIFY_API_KEY Used as --key.
27
+ * CONTEXTIFY_SERVER_URL Used as --server.
28
+ */
29
+ import { saveCredentials } from '../credentials.js';
30
+ import { type InstallHooksResult } from './install-hooks.js';
31
+ export interface InstallArgs {
32
+ readonly dryRun?: boolean;
33
+ readonly apiKey?: string;
34
+ readonly serverUrl?: string;
35
+ readonly name?: string;
36
+ }
37
+ export interface InstallOptions {
38
+ readonly env?: NodeJS.ProcessEnv;
39
+ readonly stdout?: NodeJS.WriteStream;
40
+ readonly stderr?: NodeJS.WriteStream;
41
+ /** For tests: bypass the real ~/.claude / ~/.contextify dirs. */
42
+ readonly stateRoot?: string;
43
+ readonly claudeSettingsPath?: string;
44
+ /** For tests / dry-run preview. */
45
+ readonly runner?: (opts: {
46
+ stateRoot?: string;
47
+ claudeSettingsPath?: string;
48
+ }) => Promise<InstallHooksResult>;
49
+ /** For tests: override credentials persistence. */
50
+ readonly saveCredentialsImpl?: typeof saveCredentials;
51
+ }
52
+ export declare function runInstall(args: InstallArgs, opts?: InstallOptions): Promise<number>;
53
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAgB,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE3E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;IACrC,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,mCAAmC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC7B,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAClC,mDAAmD;IACnD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,eAAe,CAAC;CACvD;AA+BD,wBAAsB,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0F9F"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * `contextify install` — zero-config global install.
3
+ *
4
+ * Materializes the three hook scripts under ~/.contextify/hooks/ and
5
+ * merges hook entries into ~/.claude/settings.json. Idempotent. Does
6
+ * NOT write .contextify.json into the cwd — that's `init`'s job.
7
+ *
8
+ * Also persists API credentials when --key (or CONTEXTIFY_API_KEY) is
9
+ * available, so IDE-spawned hook subprocesses (which do not inherit the
10
+ * operator's shell env) can authenticate against the SaaS via the
11
+ * on-disk ~/.contextify/credentials.json. Without this step, hooks ship
12
+ * unauthenticated and the server routes the data to LEGACY_TENANT_ID.
13
+ *
14
+ * Designed for users who want one-time global wiring: run once anywhere,
15
+ * and every Claude Code session in any directory gets captured. The hook
16
+ * scripts resolve project_id dynamically from each session's cwd at
17
+ * fire-time via identity.ts, so no per-project setup is required.
18
+ *
19
+ * Flags:
20
+ * --key <ctx_live_...> Persist this API key (chmod 600).
21
+ * --server <url> Persist server URL alongside the key.
22
+ * --name <label> Optional label stored next to the key.
23
+ * --dry-run Print what would change without modifying anything.
24
+ *
25
+ * Env auto-detect (when flags absent):
26
+ * CONTEXTIFY_API_KEY Used as --key.
27
+ * CONTEXTIFY_SERVER_URL Used as --server.
28
+ */
29
+ import { saveCredentials } from '../credentials.js';
30
+ import { KEY_RE } from './login.js';
31
+ import { installHooks } from './install-hooks.js';
32
+ function resolveInstallCreds(args, env) {
33
+ const flagKey = args.apiKey?.trim();
34
+ if (flagKey) {
35
+ return {
36
+ apiKey: flagKey,
37
+ serverUrl: args.serverUrl?.trim() || env.CONTEXTIFY_SERVER_URL?.trim() || undefined,
38
+ name: args.name?.trim() || undefined,
39
+ source: 'flag',
40
+ };
41
+ }
42
+ const envKey = env.CONTEXTIFY_API_KEY?.trim();
43
+ if (envKey) {
44
+ return {
45
+ apiKey: envKey,
46
+ serverUrl: args.serverUrl?.trim() || env.CONTEXTIFY_SERVER_URL?.trim() || undefined,
47
+ name: args.name?.trim() || undefined,
48
+ source: 'env',
49
+ };
50
+ }
51
+ return null;
52
+ }
53
+ export async function runInstall(args, opts = {}) {
54
+ const stdout = opts.stdout ?? process.stdout;
55
+ const stderr = opts.stderr ?? process.stderr;
56
+ const runner = opts.runner ?? installHooks;
57
+ const env = opts.env ?? process.env;
58
+ const persist = opts.saveCredentialsImpl ?? saveCredentials;
59
+ const creds = resolveInstallCreds(args, env);
60
+ if (creds && !KEY_RE.test(creds.apiKey)) {
61
+ stderr.write(`contextify install: API key from ${creds.source === 'flag' ? '--key' : 'CONTEXTIFY_API_KEY'} does not look like a contextify api key.\n` +
62
+ `Expected format: ctx_live_<8>_<32>\n`);
63
+ return 2;
64
+ }
65
+ if (args.dryRun) {
66
+ const { defaultStateRoot, defaultClaudeSettingsPath } = await import('./install-hooks.js');
67
+ const stateRoot = opts.stateRoot ?? defaultStateRoot(env);
68
+ const settingsPath = opts.claudeSettingsPath ?? defaultClaudeSettingsPath(env);
69
+ stdout.write(`${JSON.stringify({
70
+ dryRun: true,
71
+ wouldWriteHooks: ['SessionStart', 'Stop', 'SessionEnd'],
72
+ hooksDir: `${stateRoot}/hooks`,
73
+ settingsPath,
74
+ backupDir: `${stateRoot}/backups`,
75
+ wouldPersistCredentials: creds
76
+ ? { source: creds.source, serverUrl: creds.serverUrl ?? null }
77
+ : null,
78
+ }, null, 2)}\n`);
79
+ return 0;
80
+ }
81
+ let result;
82
+ try {
83
+ result = await runner({
84
+ stateRoot: opts.stateRoot,
85
+ claudeSettingsPath: opts.claudeSettingsPath,
86
+ });
87
+ }
88
+ catch (err) {
89
+ stderr.write(`contextify install: ${err.message}\n`);
90
+ return 1;
91
+ }
92
+ let credentialsPath = null;
93
+ if (creds) {
94
+ try {
95
+ credentialsPath = persist({
96
+ apiKey: creds.apiKey,
97
+ name: creds.name,
98
+ serverUrl: creds.serverUrl,
99
+ });
100
+ }
101
+ catch (err) {
102
+ stderr.write(`contextify install: failed to persist credentials: ${err.message}\n`);
103
+ return 1;
104
+ }
105
+ }
106
+ const allPresent = result.appendedEvents.length === 0 && result.alreadyPresentEvents.length === 3;
107
+ stdout.write(`${allPresent ? 'Hooks already installed' : 'Installed Contextify hooks'} (`);
108
+ stdout.write(`appended=${JSON.stringify(result.appendedEvents)}, `);
109
+ stdout.write(`alreadyPresent=${JSON.stringify(result.alreadyPresentEvents)})\n`);
110
+ stdout.write(` hooks dir: ${result.hooksDir}\n`);
111
+ stdout.write(` settings: ${result.settingsPath}\n`);
112
+ if (result.backupPath) {
113
+ stdout.write(` backup: ${result.backupPath}\n`);
114
+ }
115
+ if (credentialsPath) {
116
+ stdout.write(` credentials: ${credentialsPath} (chmod 600, source=${creds.source})\n`);
117
+ }
118
+ else {
119
+ stdout.write(` credentials: NOT PERSISTED — hooks will ship unauthenticated and the server\n` +
120
+ ` will route data to LEGACY_TENANT_ID. Re-run with --key or set\n` +
121
+ ` CONTEXTIFY_API_KEY before invoking install.\n`);
122
+ }
123
+ stdout.write(`\n`);
124
+ stdout.write(`Hooks fire automatically on every Claude Code session. project_id\n`);
125
+ stdout.write(`is derived from each session's cwd at fire-time via the identity stack\n`);
126
+ stdout.write(`(env override → .contextify.json → git remote → folder realpath).\n`);
127
+ return 0;
128
+ }
129
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface LoginArgs {
2
+ readonly apiKey?: string;
3
+ readonly serverUrl?: string;
4
+ readonly name?: string;
5
+ }
6
+ export declare const KEY_RE: RegExp;
7
+ export declare function runLogin(args: LoginArgs): Promise<number>;
8
+ export declare function logoutPath(): string;
9
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,MAAM,QAAwC,CAAC;AAE5D,wBAAsB,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB/D;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * `contextify login --key <ctx_live_...> [--server <url>] [--name <label>]`
3
+ *
4
+ * Writes ~/.contextify/credentials.json (chmod 600) so subsequent
5
+ * `contextify wrap`, `contextify prompt`, and `contextify ship` calls
6
+ * send `Authorization: Bearer <key>`.
7
+ *
8
+ * Per the P3.5 codex consult, env var still takes precedence over the
9
+ * file at request time, so this command can be safely run on machines
10
+ * that ALSO set CONTEXTIFY_API_KEY — the env wins.
11
+ */
12
+ import { saveCredentials, credentialsPath } from '../credentials.js';
13
+ export const KEY_RE = /^ctx_live_[a-z2-9]{8}_[a-z2-9]{32}$/;
14
+ export async function runLogin(args) {
15
+ if (!args.apiKey) {
16
+ process.stderr.write('contextify login: --key <ctx_live_...> required.\n' +
17
+ 'Get a key from /dashboard/keys on your contextify server.\n');
18
+ return 2;
19
+ }
20
+ if (!KEY_RE.test(args.apiKey)) {
21
+ process.stderr.write('contextify login: --key does not look like a contextify api key.\n' +
22
+ 'Expected format: ctx_live_<8>_<32>\n');
23
+ return 2;
24
+ }
25
+ const path = saveCredentials({
26
+ apiKey: args.apiKey,
27
+ name: args.name,
28
+ serverUrl: args.serverUrl,
29
+ });
30
+ process.stdout.write(`saved credentials to ${path} (chmod 600)\n`);
31
+ return 0;
32
+ }
33
+ export function logoutPath() {
34
+ return credentialsPath();
35
+ }
36
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAQrE,MAAM,CAAC,MAAM,MAAM,GAAG,qCAAqC,CAAC;AAE5D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAe;IAC5C,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oDAAoD;YAClD,6DAA6D,CAChE,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oEAAoE;YAClE,sCAAsC,CACzC,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,IAAI,GAAG,eAAe,CAAC;QAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,IAAI,gBAAgB,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,eAAe,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,17 @@
1
+ export interface PromptArgs {
2
+ /** The draft text. Use null to read from stdin. */
3
+ readonly draft: string | null;
4
+ readonly topK?: number;
5
+ readonly showMemories?: boolean;
6
+ readonly json?: boolean;
7
+ }
8
+ export interface PromptOptions {
9
+ readonly cwd: string;
10
+ readonly env?: NodeJS.ProcessEnv;
11
+ readonly fetchImpl?: typeof fetch;
12
+ readonly readStdin?: () => Promise<string>;
13
+ readonly stdout?: NodeJS.WriteStream;
14
+ readonly stderr?: NodeJS.WriteStream;
15
+ }
16
+ export declare function runPrompt(args: PromptArgs, opts: PromptOptions): Promise<number>;
17
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/commands/prompt.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;CACtC;AAkCD,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAiGtF"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * `contextify prompt <draft|->`
3
+ *
4
+ * The Active Loop CLI — pipes a rough draft to /api/prompt/generate,
5
+ * receives an XML prompt with project memories + GStack directives baked
6
+ * in, and prints it to stdout so it can be redirected/piped.
7
+ *
8
+ * Unix-y by design:
9
+ * contextify prompt "build a date picker" | pbcopy
10
+ * echo "draft from stdin" | contextify prompt -
11
+ * contextify prompt "draft" --json | jq '.retrievedMemories'
12
+ *
13
+ * stderr is reserved for diagnostics (--show-memories) and errors so
14
+ * stdout stays a single clean artifact.
15
+ */
16
+ import { resolveConfig } from '../config.js';
17
+ import { resolveApiKey } from '../credentials.js';
18
+ const MAX_DRAFT_CHARS = 20_000;
19
+ async function defaultReadStdin() {
20
+ if (process.stdin.isTTY)
21
+ return '';
22
+ return new Promise((resolve, reject) => {
23
+ let buf = '';
24
+ process.stdin.setEncoding('utf8');
25
+ process.stdin.on('data', (chunk) => {
26
+ buf += chunk;
27
+ });
28
+ process.stdin.on('end', () => resolve(buf));
29
+ process.stdin.on('error', reject);
30
+ });
31
+ }
32
+ export async function runPrompt(args, opts) {
33
+ const stdout = opts.stdout ?? process.stdout;
34
+ const stderr = opts.stderr ?? process.stderr;
35
+ // Resolve config (project + server) — same path as `wrap` / `ship`.
36
+ let config;
37
+ try {
38
+ config = await resolveConfig(opts.cwd, opts.env);
39
+ }
40
+ catch (err) {
41
+ stderr.write(`contextify: ${err.message}\n`);
42
+ return 2;
43
+ }
44
+ // Resolve draft: positional arg, or stdin if "-".
45
+ let draft;
46
+ if (args.draft === null) {
47
+ const readStdin = opts.readStdin ?? defaultReadStdin;
48
+ draft = (await readStdin()).trim();
49
+ if (draft.length === 0) {
50
+ stderr.write('contextify: prompt: no draft provided (stdin was empty)\n');
51
+ return 2;
52
+ }
53
+ }
54
+ else {
55
+ draft = args.draft.trim();
56
+ if (draft.length === 0) {
57
+ stderr.write('contextify: prompt: draft cannot be empty\n');
58
+ return 2;
59
+ }
60
+ }
61
+ if (draft.length > MAX_DRAFT_CHARS) {
62
+ stderr.write(`contextify: prompt: draft exceeds ${MAX_DRAFT_CHARS} chars (got ${draft.length})\n`);
63
+ return 2;
64
+ }
65
+ // Validate flags.
66
+ if (args.topK !== undefined &&
67
+ (!Number.isInteger(args.topK) || args.topK < 1 || args.topK > 25)) {
68
+ stderr.write(`contextify: prompt: --top-k must be an integer in [1, 25]\n`);
69
+ return 2;
70
+ }
71
+ const fetchImpl = opts.fetchImpl ?? fetch;
72
+ const url = new URL('/api/prompt/generate', config.serverUrl).toString();
73
+ const body = {
74
+ projectId: config.projectId,
75
+ projectName: config.projectName,
76
+ draft,
77
+ topK: args.topK,
78
+ };
79
+ // Attach the same Bearer key the hook ships with so /api/prompt/generate
80
+ // accepts the request. Without this the route 401s on fail-closed deploys.
81
+ const creds = resolveApiKey(opts.env);
82
+ const headers = { 'content-type': 'application/json' };
83
+ if (creds)
84
+ headers.authorization = `Bearer ${creds.apiKey}`;
85
+ let res;
86
+ try {
87
+ res = await fetchImpl(url, {
88
+ method: 'POST',
89
+ headers,
90
+ body: JSON.stringify(body),
91
+ });
92
+ }
93
+ catch (err) {
94
+ stderr.write(`contextify: prompt: request failed: ${err.message}\n`);
95
+ return 1;
96
+ }
97
+ if (!res.ok) {
98
+ const text = await res.text().catch(() => '');
99
+ stderr.write(`contextify: prompt: server returned ${res.status} ${res.statusText}\n`);
100
+ if (text.length > 0)
101
+ stderr.write(`${text}\n`);
102
+ return 1;
103
+ }
104
+ let json;
105
+ try {
106
+ json = (await res.json());
107
+ }
108
+ catch (err) {
109
+ stderr.write(`contextify: prompt: invalid JSON response: ${err.message}\n`);
110
+ return 1;
111
+ }
112
+ if (args.showMemories) {
113
+ writeMemoriesSummary(stderr, json);
114
+ }
115
+ if (args.json) {
116
+ stdout.write(`${JSON.stringify(json, null, 2)}\n`);
117
+ }
118
+ else {
119
+ stdout.write(json.xml.endsWith('\n') ? json.xml : `${json.xml}\n`);
120
+ }
121
+ return 0;
122
+ }
123
+ function writeMemoriesSummary(stderr, json) {
124
+ const memCount = json.retrievedMemories.length;
125
+ const dirCount = json.directives.length;
126
+ stderr.write(`\n# Retrieved ${memCount} memor${memCount === 1 ? 'y' : 'ies'}`);
127
+ if (dirCount > 0) {
128
+ stderr.write(`, ${dirCount} directive${dirCount === 1 ? '' : 's'}`);
129
+ }
130
+ stderr.write(`:\n`);
131
+ for (const m of json.retrievedMemories) {
132
+ const tag = `[${m.kind}/${m.source}, d=${m.distance.toFixed(3)}]`;
133
+ const text = m.content.length > 200 ? `${m.content.slice(0, 197)}...` : m.content;
134
+ stderr.write(` - ${tag} ${text}\n`);
135
+ }
136
+ for (const d of json.directives) {
137
+ stderr.write(` > ${d.skill} — ${d.reason}\n`);
138
+ }
139
+ stderr.write(`\n`);
140
+ }
141
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/commands/prompt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface ShipArgs {
2
+ readonly cwd: string;
3
+ readonly env?: NodeJS.ProcessEnv;
4
+ }
5
+ export declare function runShip(args: ShipArgs): Promise<number>;
6
+ //# sourceMappingURL=ship.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ship.d.ts","sourceRoot":"","sources":["../../src/commands/ship.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB7D"}
@@ -0,0 +1,20 @@
1
+ import { resolveConfig } from '../config.js';
2
+ import { flushSpool } from '../shipper.js';
3
+ export async function runShip(args) {
4
+ let config;
5
+ try {
6
+ config = await resolveConfig(args.cwd, args.env);
7
+ }
8
+ catch (err) {
9
+ process.stderr.write(`contextify: ${err.message}\n`);
10
+ return 2;
11
+ }
12
+ const result = await flushSpool({ serverUrl: config.serverUrl, cwd: args.cwd });
13
+ process.stdout.write(`${JSON.stringify({
14
+ attempted: result.attempted,
15
+ sent: result.sent,
16
+ remaining: result.remaining,
17
+ })}\n`);
18
+ return result.remaining === 0 ? 0 : 1;
19
+ }
20
+ //# sourceMappingURL=ship.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ship.js","sourceRoot":"","sources":["../../src/commands/ship.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAO3C,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAc;IAC1C,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAgB,GAAa,CAAC,OAAO,IAAI,CAAC,CAAC;QAChE,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAChF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,CAAC,SAAS,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC,IAAI,CACP,CAAC;IACF,OAAO,MAAM,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export interface WrapOptions {
2
+ readonly argv: readonly string[];
3
+ readonly cwd: string;
4
+ readonly env?: NodeJS.ProcessEnv;
5
+ readonly maxBytes?: number;
6
+ readonly maxIdleMs?: number;
7
+ readonly forceSpool?: boolean;
8
+ }
9
+ export declare function runWrap(opts: WrapOptions): Promise<number>;
10
+ //# sourceMappingURL=wrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../../src/commands/wrap.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAKD,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAkEhE"}