claude-eidetic 0.1.0 → 0.1.2

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 (96) hide show
  1. package/README.md +333 -0
  2. package/dist/config.d.ts +25 -0
  3. package/dist/config.js +29 -14
  4. package/dist/core/cleanup.d.ts +8 -0
  5. package/dist/core/cleanup.js +41 -0
  6. package/dist/core/doc-indexer.d.ts +13 -0
  7. package/dist/core/doc-indexer.js +76 -0
  8. package/dist/core/doc-searcher.d.ts +13 -0
  9. package/dist/core/doc-searcher.js +65 -0
  10. package/dist/core/file-category.d.ts +7 -0
  11. package/dist/core/file-category.js +75 -0
  12. package/dist/core/indexer.js +12 -4
  13. package/dist/core/preview.d.ts +1 -2
  14. package/dist/core/preview.js +2 -5
  15. package/dist/core/repo-map.d.ts +33 -0
  16. package/dist/core/repo-map.js +144 -0
  17. package/dist/core/searcher.d.ts +1 -13
  18. package/dist/core/searcher.js +20 -24
  19. package/dist/core/snapshot-io.js +2 -2
  20. package/dist/core/sync.d.ts +5 -25
  21. package/dist/core/sync.js +90 -65
  22. package/dist/core/targeted-indexer.d.ts +19 -0
  23. package/dist/core/targeted-indexer.js +127 -0
  24. package/dist/embedding/factory.d.ts +0 -13
  25. package/dist/embedding/factory.js +0 -17
  26. package/dist/embedding/openai.d.ts +2 -14
  27. package/dist/embedding/openai.js +7 -20
  28. package/dist/errors.d.ts +2 -0
  29. package/dist/errors.js +2 -0
  30. package/dist/format.d.ts +12 -0
  31. package/dist/format.js +160 -31
  32. package/dist/hooks/post-tool-use.d.ts +13 -0
  33. package/dist/hooks/post-tool-use.js +113 -0
  34. package/dist/hooks/stop-hook.d.ts +11 -0
  35. package/dist/hooks/stop-hook.js +121 -0
  36. package/dist/hooks/targeted-runner.d.ts +11 -0
  37. package/dist/hooks/targeted-runner.js +66 -0
  38. package/dist/index.js +102 -24
  39. package/dist/infra/qdrant-bootstrap.js +14 -12
  40. package/dist/memory/history.d.ts +19 -0
  41. package/dist/memory/history.js +40 -0
  42. package/dist/memory/llm.d.ts +2 -0
  43. package/dist/memory/llm.js +56 -0
  44. package/dist/memory/prompts.d.ts +5 -0
  45. package/dist/memory/prompts.js +36 -0
  46. package/dist/memory/reconciler.d.ts +12 -0
  47. package/dist/memory/reconciler.js +36 -0
  48. package/dist/memory/store.d.ts +20 -0
  49. package/dist/memory/store.js +206 -0
  50. package/dist/memory/types.d.ts +28 -0
  51. package/dist/memory/types.js +2 -0
  52. package/dist/paths.d.ts +3 -4
  53. package/dist/paths.js +14 -4
  54. package/dist/precompact/hook.d.ts +9 -0
  55. package/dist/precompact/hook.js +170 -0
  56. package/dist/precompact/index-runner.d.ts +9 -0
  57. package/dist/precompact/index-runner.js +52 -0
  58. package/dist/precompact/note-writer.d.ts +15 -0
  59. package/dist/precompact/note-writer.js +109 -0
  60. package/dist/precompact/session-indexer.d.ts +13 -0
  61. package/dist/precompact/session-indexer.js +31 -0
  62. package/dist/precompact/tier0-inject.d.ts +16 -0
  63. package/dist/precompact/tier0-inject.js +88 -0
  64. package/dist/precompact/tier0-writer.d.ts +16 -0
  65. package/dist/precompact/tier0-writer.js +74 -0
  66. package/dist/precompact/transcript-parser.d.ts +10 -0
  67. package/dist/precompact/transcript-parser.js +148 -0
  68. package/dist/precompact/types.d.ts +93 -0
  69. package/dist/precompact/types.js +5 -0
  70. package/dist/precompact/utils.d.ts +29 -0
  71. package/dist/precompact/utils.js +95 -0
  72. package/dist/setup-message.d.ts +3 -0
  73. package/dist/setup-message.js +42 -0
  74. package/dist/splitter/ast.js +84 -22
  75. package/dist/splitter/line.d.ts +0 -4
  76. package/dist/splitter/line.js +1 -7
  77. package/dist/splitter/symbol-extract.d.ts +16 -0
  78. package/dist/splitter/symbol-extract.js +61 -0
  79. package/dist/splitter/types.d.ts +5 -0
  80. package/dist/splitter/types.js +1 -1
  81. package/dist/state/doc-metadata.d.ts +18 -0
  82. package/dist/state/doc-metadata.js +59 -0
  83. package/dist/state/registry.d.ts +1 -3
  84. package/dist/state/snapshot.d.ts +0 -1
  85. package/dist/state/snapshot.js +3 -19
  86. package/dist/tool-schemas.d.ts +251 -1
  87. package/dist/tool-schemas.js +307 -0
  88. package/dist/tools.d.ts +69 -0
  89. package/dist/tools.js +286 -17
  90. package/dist/vectordb/milvus.d.ts +7 -5
  91. package/dist/vectordb/milvus.js +116 -19
  92. package/dist/vectordb/qdrant.d.ts +8 -10
  93. package/dist/vectordb/qdrant.js +105 -33
  94. package/dist/vectordb/types.d.ts +20 -0
  95. package/messages.yaml +50 -0
  96. package/package.json +31 -6
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Maintain .session-index.json for Tier-0 fast SessionStart context injection.
3
+ */
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { extractDate, writeFileAtomic } from './utils.js';
7
+ const MAX_SESSIONS = 10;
8
+ const INDEX_FILENAME = '.session-index.json';
9
+ /**
10
+ * Update the session index with a new session record.
11
+ * Prepends the new session and keeps only the last 10.
12
+ * Uses atomic write to prevent corruption from concurrent access.
13
+ */
14
+ export function updateSessionIndex(notesDir, session, noteFile) {
15
+ const indexPath = path.join(notesDir, INDEX_FILENAME);
16
+ // Load existing index or create new
17
+ let index;
18
+ if (fs.existsSync(indexPath)) {
19
+ try {
20
+ const content = fs.readFileSync(indexPath, 'utf-8');
21
+ index = JSON.parse(content);
22
+ }
23
+ catch {
24
+ // Corrupted index, start fresh
25
+ index = createEmptyIndex(session.projectName);
26
+ }
27
+ }
28
+ else {
29
+ index = createEmptyIndex(session.projectName);
30
+ }
31
+ // Create new record
32
+ const record = {
33
+ sessionId: session.sessionId,
34
+ date: extractDate(session.startTime),
35
+ branch: session.branch,
36
+ filesModified: session.filesModified,
37
+ tasksCreated: session.tasksCreated,
38
+ trigger: session.trigger,
39
+ noteFile,
40
+ };
41
+ // Prepend new session and trim to max
42
+ index.sessions = [record, ...index.sessions].slice(0, MAX_SESSIONS);
43
+ index.project = session.projectName;
44
+ index.lastUpdated = new Date().toISOString();
45
+ // Ensure directory exists
46
+ fs.mkdirSync(notesDir, { recursive: true });
47
+ // Write atomically to prevent corruption from concurrent hooks
48
+ writeFileAtomic(indexPath, JSON.stringify(index, null, 2));
49
+ }
50
+ /**
51
+ * Read the session index for a project.
52
+ * Returns null if not found or corrupted.
53
+ */
54
+ export function readSessionIndex(notesDir) {
55
+ const indexPath = path.join(notesDir, INDEX_FILENAME);
56
+ if (!fs.existsSync(indexPath)) {
57
+ return null;
58
+ }
59
+ try {
60
+ const content = fs.readFileSync(indexPath, 'utf-8');
61
+ return JSON.parse(content);
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ }
67
+ function createEmptyIndex(project) {
68
+ return {
69
+ project,
70
+ sessions: [],
71
+ lastUpdated: new Date().toISOString(),
72
+ };
73
+ }
74
+ //# sourceMappingURL=tier0-writer.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Parse Claude Code transcript JSONL to extract session data.
3
+ * Extracts deterministic data from tool calls - no LLM needed.
4
+ */
5
+ import type { ExtractedSession } from './types.js';
6
+ /**
7
+ * Parse a transcript JSONL file and extract session data.
8
+ */
9
+ export declare function parseTranscript(transcriptPath: string, sessionId: string, projectName: string, projectPath: string, trigger?: 'auto' | 'manual' | 'session_end'): Promise<ExtractedSession>;
10
+ //# sourceMappingURL=transcript-parser.d.ts.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Parse Claude Code transcript JSONL to extract session data.
3
+ * Extracts deterministic data from tool calls - no LLM needed.
4
+ */
5
+ import fs from 'node:fs';
6
+ import readline from 'node:readline';
7
+ import { truncateUnicode } from './utils.js';
8
+ const MAX_BASH_COMMANDS = 20;
9
+ const MAX_USER_MESSAGES = 5;
10
+ const BASH_COMMAND_MAX_LENGTH = 120;
11
+ const USER_MESSAGE_MAX_LENGTH = 200;
12
+ /**
13
+ * Parse a transcript JSONL file and extract session data.
14
+ */
15
+ export async function parseTranscript(transcriptPath, sessionId, projectName, projectPath, trigger = 'auto') {
16
+ const filesModified = new Set();
17
+ const bashCommands = [];
18
+ const mcpToolsCalled = new Set();
19
+ const tasksCreated = [];
20
+ const tasksUpdated = [];
21
+ const taskIdToSubject = new Map();
22
+ const userMessages = [];
23
+ let branch = null;
24
+ let startTime = null;
25
+ let endTime = null;
26
+ const fileStream = fs.createReadStream(transcriptPath);
27
+ const rl = readline.createInterface({
28
+ input: fileStream,
29
+ crlfDelay: Infinity,
30
+ });
31
+ for await (const line of rl) {
32
+ if (!line.trim())
33
+ continue;
34
+ let parsed;
35
+ try {
36
+ parsed = JSON.parse(line);
37
+ }
38
+ catch {
39
+ // Skip malformed JSON lines
40
+ continue;
41
+ }
42
+ // Extract timestamps
43
+ if (parsed.timestamp) {
44
+ if (!startTime)
45
+ startTime = parsed.timestamp;
46
+ endTime = parsed.timestamp;
47
+ }
48
+ // Extract git branch from first entry that has it
49
+ if (!branch && parsed.gitBranch) {
50
+ branch = parsed.gitBranch;
51
+ }
52
+ // Extract user messages
53
+ if (parsed.type === 'user' && userMessages.length < MAX_USER_MESSAGES) {
54
+ const text = extractUserText(parsed);
55
+ if (text) {
56
+ userMessages.push(truncateUnicode(text, USER_MESSAGE_MAX_LENGTH));
57
+ }
58
+ }
59
+ // Extract tool calls from assistant messages
60
+ if (parsed.type === 'assistant' && parsed.message?.content) {
61
+ for (const content of parsed.message.content) {
62
+ if (content.type !== 'tool_use')
63
+ continue;
64
+ const toolContent = content;
65
+ processToolCall(toolContent, {
66
+ filesModified,
67
+ bashCommands,
68
+ mcpToolsCalled,
69
+ tasksCreated,
70
+ tasksUpdated,
71
+ taskIdToSubject,
72
+ });
73
+ }
74
+ }
75
+ }
76
+ return {
77
+ sessionId,
78
+ projectName,
79
+ projectPath,
80
+ branch,
81
+ startTime: startTime ?? 'unknown',
82
+ endTime: endTime ?? 'unknown',
83
+ filesModified: Array.from(filesModified).sort(),
84
+ bashCommands,
85
+ mcpToolsCalled: Array.from(mcpToolsCalled).sort(),
86
+ tasksCreated,
87
+ tasksUpdated,
88
+ userMessages,
89
+ trigger,
90
+ };
91
+ }
92
+ function processToolCall(content, state) {
93
+ const { name, input } = content;
94
+ // File modifications
95
+ if (name === 'Write' || name === 'Edit') {
96
+ const filePath = input.file_path;
97
+ if (typeof filePath === 'string') {
98
+ state.filesModified.add(filePath);
99
+ }
100
+ }
101
+ // Bash commands (enforce limit during collection)
102
+ if (name === 'Bash' && state.bashCommands.length < MAX_BASH_COMMANDS) {
103
+ const command = input.command;
104
+ if (typeof command === 'string') {
105
+ state.bashCommands.push(truncateUnicode(command, BASH_COMMAND_MAX_LENGTH));
106
+ }
107
+ }
108
+ // Task operations - track subject by taskId for later updates
109
+ if (name === 'TaskCreate') {
110
+ const subject = input.subject;
111
+ const taskId = input.taskId;
112
+ if (typeof subject === 'string') {
113
+ state.tasksCreated.push(subject);
114
+ // Track subject by taskId if available (for future TaskUpdate lookups)
115
+ if (typeof taskId === 'string') {
116
+ state.taskIdToSubject.set(taskId, subject);
117
+ }
118
+ }
119
+ }
120
+ if (name === 'TaskUpdate') {
121
+ const taskId = input.taskId;
122
+ const status = input.status;
123
+ // Try to get subject from input first, then from tracked tasks
124
+ let subject = input.subject;
125
+ if (typeof subject !== 'string' && typeof taskId === 'string') {
126
+ subject = state.taskIdToSubject.get(taskId);
127
+ }
128
+ if (typeof subject === 'string' && typeof status === 'string') {
129
+ state.tasksUpdated.push(`${subject} → ${status}`);
130
+ }
131
+ }
132
+ // MCP tools
133
+ if (name.startsWith('mcp__')) {
134
+ state.mcpToolsCalled.add(name);
135
+ }
136
+ }
137
+ function extractUserText(line) {
138
+ const content = line.message?.content;
139
+ if (!Array.isArray(content))
140
+ return null;
141
+ for (const block of content) {
142
+ if (block.type === 'text' && typeof block.text === 'string') {
143
+ return block.text;
144
+ }
145
+ }
146
+ return null;
147
+ }
148
+ //# sourceMappingURL=transcript-parser.js.map
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Types for PreCompact hook - automatic session persistence before context compaction.
3
+ */
4
+ /**
5
+ * Input received from Claude Code PreCompact hook via stdin.
6
+ */
7
+ export interface PreCompactInput {
8
+ session_id: string;
9
+ transcript_path: string;
10
+ cwd: string;
11
+ trigger: 'auto' | 'manual';
12
+ hook_event_name: 'PreCompact';
13
+ }
14
+ /**
15
+ * Input received from Claude Code SessionEnd hook via stdin.
16
+ */
17
+ export interface SessionEndInput {
18
+ session_id: string;
19
+ transcript_path: string;
20
+ cwd: string;
21
+ hook_event_name: 'SessionEnd';
22
+ reason?: string;
23
+ }
24
+ /**
25
+ * Session data extracted from transcript JSONL.
26
+ * Contains deterministic data parsed directly from tool calls.
27
+ */
28
+ export interface ExtractedSession {
29
+ sessionId: string;
30
+ projectName: string;
31
+ projectPath: string;
32
+ branch: string | null;
33
+ startTime: string;
34
+ endTime: string;
35
+ filesModified: string[];
36
+ bashCommands: string[];
37
+ mcpToolsCalled: string[];
38
+ tasksCreated: string[];
39
+ tasksUpdated: string[];
40
+ userMessages: string[];
41
+ trigger: 'auto' | 'manual' | 'session_end';
42
+ }
43
+ /**
44
+ * Compact session record for Tier-0 fast lookup.
45
+ * Stored in .session-index.json for instant SessionStart injection.
46
+ */
47
+ export interface Tier0Record {
48
+ sessionId: string;
49
+ date: string;
50
+ branch: string | null;
51
+ filesModified: string[];
52
+ tasksCreated: string[];
53
+ trigger: 'auto' | 'manual' | 'session_end';
54
+ noteFile: string;
55
+ }
56
+ /**
57
+ * Session index for a project - enables fast SessionStart context injection.
58
+ * Stored at ~/.eidetic/notes/<project>/.session-index.json
59
+ */
60
+ export interface SessionIndex {
61
+ project: string;
62
+ sessions: Tier0Record[];
63
+ lastUpdated: string;
64
+ }
65
+ /**
66
+ * A single line from the Claude Code transcript JSONL.
67
+ */
68
+ export interface TranscriptLine {
69
+ type: 'user' | 'assistant' | 'system';
70
+ timestamp?: string;
71
+ gitBranch?: string;
72
+ message?: {
73
+ role?: string;
74
+ content?: TranscriptContent[];
75
+ };
76
+ }
77
+ /**
78
+ * Content block within a transcript message.
79
+ */
80
+ export type TranscriptContent = {
81
+ type: 'text';
82
+ text: string;
83
+ } | {
84
+ type: 'tool_use';
85
+ id: string;
86
+ name: string;
87
+ input: Record<string, unknown>;
88
+ } | {
89
+ type: 'tool_result';
90
+ tool_use_id: string;
91
+ content: unknown;
92
+ };
93
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Types for PreCompact hook - automatic session persistence before context compaction.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared utilities for precompact module.
3
+ */
4
+ /**
5
+ * Extract YYYY-MM-DD date from ISO timestamp or return today's date.
6
+ */
7
+ export declare function extractDate(timestamp: string): string;
8
+ /**
9
+ * Get the notes directory for a project.
10
+ * Uses paths.ts normalization for consistency.
11
+ */
12
+ export declare function getNotesDir(projectName: string): string;
13
+ /**
14
+ * Truncate a string to maxLength with proper Unicode handling.
15
+ * Avoids splitting surrogate pairs (emoji, CJK characters).
16
+ * Adds ellipsis if truncated.
17
+ */
18
+ export declare function truncateUnicode(str: string, maxLength: number): string;
19
+ /**
20
+ * Write file atomically using write-to-temp-then-rename pattern.
21
+ * Prevents corruption from concurrent writes or process termination.
22
+ */
23
+ export declare function writeFileAtomic(filePath: string, content: string): void;
24
+ /**
25
+ * Generate a stable project identifier from path.
26
+ * Handles project name collisions by including path hash.
27
+ */
28
+ export declare function getProjectId(projectPath: string): string;
29
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Shared utilities for precompact module.
3
+ */
4
+ import path from 'node:path';
5
+ import fs from 'node:fs';
6
+ import { getConfig } from '../config.js';
7
+ import { normalizePath } from '../paths.js';
8
+ /**
9
+ * Extract YYYY-MM-DD date from ISO timestamp or return today's date.
10
+ */
11
+ export function extractDate(timestamp) {
12
+ if (timestamp === 'unknown' || !timestamp) {
13
+ return new Date().toISOString().slice(0, 10);
14
+ }
15
+ // Handle ISO format: 2026-02-19T10:00:00Z
16
+ const match = /^(\d{4}-\d{2}-\d{2})/.exec(timestamp);
17
+ if (match) {
18
+ return match[1];
19
+ }
20
+ return new Date().toISOString().slice(0, 10);
21
+ }
22
+ /**
23
+ * Get the notes directory for a project.
24
+ * Uses paths.ts normalization for consistency.
25
+ */
26
+ export function getNotesDir(projectName) {
27
+ const config = getConfig();
28
+ // Expand ~ and normalize path
29
+ const dataDir = normalizePath(config.eideticDataDir);
30
+ return path.join(dataDir, 'notes', projectName);
31
+ }
32
+ /**
33
+ * Truncate a string to maxLength with proper Unicode handling.
34
+ * Avoids splitting surrogate pairs (emoji, CJK characters).
35
+ * Adds ellipsis if truncated.
36
+ */
37
+ export function truncateUnicode(str, maxLength) {
38
+ if (str.length <= maxLength)
39
+ return str;
40
+ // Convert to array of code points to handle surrogate pairs correctly
41
+ const codePoints = Array.from(str);
42
+ if (codePoints.length <= maxLength)
43
+ return str;
44
+ // Leave room for ellipsis
45
+ const truncated = codePoints.slice(0, maxLength - 1).join('');
46
+ return truncated + '…';
47
+ }
48
+ /**
49
+ * Write file atomically using write-to-temp-then-rename pattern.
50
+ * Prevents corruption from concurrent writes or process termination.
51
+ */
52
+ export function writeFileAtomic(filePath, content) {
53
+ const dir = path.dirname(filePath);
54
+ fs.mkdirSync(dir, { recursive: true });
55
+ // Create temp file in same directory (required for atomic rename)
56
+ const tempPath = path.join(dir, `.tmp-${process.pid}-${Date.now()}`);
57
+ try {
58
+ fs.writeFileSync(tempPath, content, 'utf-8');
59
+ fs.renameSync(tempPath, filePath);
60
+ }
61
+ catch (err) {
62
+ // Clean up temp file on failure
63
+ try {
64
+ fs.unlinkSync(tempPath);
65
+ }
66
+ catch {
67
+ // Ignore cleanup errors
68
+ }
69
+ throw err;
70
+ }
71
+ }
72
+ /**
73
+ * Generate a stable project identifier from path.
74
+ * Handles project name collisions by including path hash.
75
+ */
76
+ export function getProjectId(projectPath) {
77
+ const normalized = normalizePath(projectPath);
78
+ const basename = path.basename(normalized);
79
+ // Create short hash of full path to disambiguate same-named projects
80
+ const hash = simpleHash(normalized).slice(0, 6);
81
+ return `${basename}-${hash}`;
82
+ }
83
+ /**
84
+ * Simple non-cryptographic hash for path disambiguation.
85
+ */
86
+ function simpleHash(str) {
87
+ let hash = 0;
88
+ for (let i = 0; i < str.length; i++) {
89
+ const char = str.charCodeAt(i);
90
+ hash = (hash << 5) - hash + char;
91
+ hash = hash & hash; // Convert to 32-bit integer
92
+ }
93
+ return Math.abs(hash).toString(36);
94
+ }
95
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,3 @@
1
+ export type SetupContext = 'missing' | 'invalid' | 'unknown';
2
+ export declare function getSetupErrorMessage(errorDetail: string, context?: SetupContext): string;
3
+ //# sourceMappingURL=setup-message.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, join } from 'node:path';
4
+ import { parse as parseYaml } from 'yaml';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+ const yamlPath = join(__dirname, '..', 'messages.yaml');
8
+ let _cached = null;
9
+ function loadMessages() {
10
+ if (_cached)
11
+ return _cached;
12
+ _cached = parseYaml(readFileSync(yamlPath, 'utf-8'));
13
+ return _cached;
14
+ }
15
+ function detectContext() {
16
+ const hasKey = !!process.env.OPENAI_API_KEY;
17
+ const isOllama = process.env.EMBEDDING_PROVIDER === 'ollama';
18
+ if (!hasKey && !isOllama)
19
+ return 'missing';
20
+ return 'invalid';
21
+ }
22
+ export function getSetupErrorMessage(errorDetail, context) {
23
+ const ctx = context ?? detectContext();
24
+ const msgs = loadMessages();
25
+ const block = msgs.setup[ctx];
26
+ const header = block.header.replace('{error}', errorDetail);
27
+ const diagnosis = block.diagnosis.trim() ? `**Diagnosis:** ${block.diagnosis.trim()}\n\n` : '';
28
+ return (`${header}\n\n` +
29
+ diagnosis +
30
+ '## How to fix\n\n' +
31
+ `1. ${block.step1}\n` +
32
+ '2. **Set or update your config** (pick one):\n\n' +
33
+ msgs.setup.config_instructions +
34
+ `3. ${msgs.setup.footer}`);
35
+ }
36
+ // Called by plugin/hooks/session-start.sh
37
+ if (process.argv[1] === __filename) {
38
+ const context = process.argv[2] ?? 'missing';
39
+ const detail = process.argv[3] ?? 'OPENAI_API_KEY is not set.';
40
+ console.log(JSON.stringify({ additionalContext: getSetupErrorMessage(detail, context) }));
41
+ }
42
+ //# sourceMappingURL=setup-message.js.map