@thegitai/cli 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +30 -0
- package/dist/bin/ai.js +438 -0
- package/dist/parsers/tree-sitter-c-sharp.wasm +0 -0
- package/dist/parsers/tree-sitter-c.wasm +0 -0
- package/dist/parsers/tree-sitter-cpp.wasm +0 -0
- package/dist/parsers/tree-sitter-css.wasm +0 -0
- package/dist/parsers/tree-sitter-go.wasm +0 -0
- package/dist/parsers/tree-sitter-html.wasm +0 -0
- package/dist/parsers/tree-sitter-java.wasm +0 -0
- package/dist/parsers/tree-sitter-javascript.wasm +0 -0
- package/dist/parsers/tree-sitter-objc.wasm +0 -0
- package/dist/parsers/tree-sitter-php.wasm +0 -0
- package/dist/parsers/tree-sitter-python.wasm +0 -0
- package/dist/parsers/tree-sitter-ruby.wasm +0 -0
- package/dist/parsers/tree-sitter-rust.wasm +0 -0
- package/dist/parsers/tree-sitter-tsx.wasm +0 -0
- package/dist/parsers/tree-sitter-typescript.wasm +0 -0
- package/dist/src/agent-mode.js +142 -0
- package/dist/src/api/auth.js +81 -0
- package/dist/src/api/browser-login.js +184 -0
- package/dist/src/api/chat.js +346 -0
- package/dist/src/api/contracts.js +1 -0
- package/dist/src/api/http.js +44 -0
- package/dist/src/api/index.js +11 -0
- package/dist/src/api/models.js +110 -0
- package/dist/src/api/sessions.js +72 -0
- package/dist/src/artifact-policy.js +207 -0
- package/dist/src/client-state.js +14 -0
- package/dist/src/core/clipboard.js +208 -0
- package/dist/src/core/open-url.js +32 -0
- package/dist/src/edit-journal.js +133 -0
- package/dist/src/executor.js +924 -0
- package/dist/src/extractors/cpp.js +18 -0
- package/dist/src/extractors/csharp.js +16 -0
- package/dist/src/extractors/css.js +12 -0
- package/dist/src/extractors/go.js +27 -0
- package/dist/src/extractors/index.js +52 -0
- package/dist/src/extractors/java.js +14 -0
- package/dist/src/extractors/javascript.js +33 -0
- package/dist/src/extractors/objc.js +14 -0
- package/dist/src/extractors/php.js +20 -0
- package/dist/src/extractors/python.js +11 -0
- package/dist/src/extractors/ruby.js +13 -0
- package/dist/src/extractors/rust.js +17 -0
- package/dist/src/extractors/utils.js +58 -0
- package/dist/src/help-text.js +125 -0
- package/dist/src/markdown-renderer.js +112 -0
- package/dist/src/patcher.js +279 -0
- package/dist/src/project-index.js +221 -0
- package/dist/src/repo-map-languages.js +100 -0
- package/dist/src/runtime-mode.js +35 -0
- package/dist/src/scanner.js +362 -0
- package/dist/src/secret-preview.js +137 -0
- package/dist/src/session-exit.js +17 -0
- package/dist/src/session-safety.js +1012 -0
- package/dist/src/session-store.js +266 -0
- package/dist/src/session.js +93 -0
- package/dist/src/tool-executor.js +188 -0
- package/dist/src/tools/code-intel.js +472 -0
- package/dist/src/tools/delete-file.js +27 -0
- package/dist/src/tools/exec-utils.js +17 -0
- package/dist/src/tools/find-symbol.js +70 -0
- package/dist/src/tools/get-diagnostics.js +22 -0
- package/dist/src/tools/grep-code.js +331 -0
- package/dist/src/tools/hover-symbol.js +95 -0
- package/dist/src/tools/index.js +73 -0
- package/dist/src/tools/list-checkpoints.js +11 -0
- package/dist/src/tools/list-directories.js +16 -0
- package/dist/src/tools/list-files.js +13 -0
- package/dist/src/tools/list-session-edits.js +9 -0
- package/dist/src/tools/list-symbols.js +55 -0
- package/dist/src/tools/patch-file.js +88 -0
- package/dist/src/tools/path-listing.js +83 -0
- package/dist/src/tools/read-document.js +111 -0
- package/dist/src/tools/read-file.js +109 -0
- package/dist/src/tools/restore-checkpoint.js +100 -0
- package/dist/src/tools/ripgrep.js +29 -0
- package/dist/src/tools/run-command.js +94 -0
- package/dist/src/tools/run-node-script.js +210 -0
- package/dist/src/tools/search-code.js +37 -0
- package/dist/src/tools/shell-diagnostics.js +707 -0
- package/dist/src/tools/signature-help.js +118 -0
- package/dist/src/tools/str-replace.js +193 -0
- package/dist/src/tools/types.js +1 -0
- package/dist/src/tools/undo-edit.js +202 -0
- package/dist/src/tools/write-file.js +59 -0
- package/dist/src/tree-sitter-runtime.js +135 -0
- package/dist/src/types.js +1 -0
- package/dist/src/ui/paste-collapse.js +22 -0
- package/dist/src/ui/prompt-history-store.js +96 -0
- package/dist/src/ui/repl.js +2238 -0
- package/dist/src/ui/tui/bridge.js +175 -0
- package/dist/src/ui/tui/build-frame.js +718 -0
- package/dist/src/ui/tui/markdown-render.js +455 -0
- package/dist/src/ui/tui/shell-input.js +488 -0
- package/dist/src/ui/tui/text.js +30 -0
- package/dist/src/ui/tui/types.js +1 -0
- package/dist/src/usage.js +47 -0
- package/dist/src/utils.js +38 -0
- package/package.json +38 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
3
|
+
import { syncIndexFromDisk } from '../project-index.js';
|
|
4
|
+
import { isTuiMode } from '../runtime-mode.js';
|
|
5
|
+
import { buildDeferredShellDiagnostics, invalidateShellDiagnosticsCache, } from './shell-diagnostics.js';
|
|
6
|
+
const DEFAULT_TIMEOUT = 5 * 60 * 1000;
|
|
7
|
+
const MAX_OUTPUT_CHARS = 4000;
|
|
8
|
+
const MAX_CAPTURE_CHARS = 1024 * 1024;
|
|
9
|
+
const COMMAND_LABEL = 'node --input-type=module <script via stdin>';
|
|
10
|
+
const MAX_APPROVAL_SCRIPT_CHARS = 4000;
|
|
11
|
+
function describeNodeScriptForApproval(script) {
|
|
12
|
+
if (script.length <= MAX_APPROVAL_SCRIPT_CHARS) {
|
|
13
|
+
return `${COMMAND_LABEL}\n${script}`;
|
|
14
|
+
}
|
|
15
|
+
return `${COMMAND_LABEL}\n${script.slice(0, MAX_APPROVAL_SCRIPT_CHARS)}\n... (${script.length - MAX_APPROVAL_SCRIPT_CHARS} more characters truncated)`;
|
|
16
|
+
}
|
|
17
|
+
function appendCapturedText(current, chunk) {
|
|
18
|
+
if (!chunk || current.length >= MAX_CAPTURE_CHARS)
|
|
19
|
+
return current;
|
|
20
|
+
return current + chunk.slice(0, MAX_CAPTURE_CHARS - current.length);
|
|
21
|
+
}
|
|
22
|
+
function trimOutput(text) {
|
|
23
|
+
if (!text)
|
|
24
|
+
return '';
|
|
25
|
+
if (text.length <= MAX_OUTPUT_CHARS)
|
|
26
|
+
return text;
|
|
27
|
+
const headSize = Math.floor(MAX_OUTPUT_CHARS * 0.2);
|
|
28
|
+
const tailSize = MAX_OUTPUT_CHARS - headSize;
|
|
29
|
+
return (text.slice(0, headSize) +
|
|
30
|
+
`\n\n... (${text.length - headSize - tailSize} chars truncated) ...\n\n` +
|
|
31
|
+
text.slice(-tailSize));
|
|
32
|
+
}
|
|
33
|
+
function terminateChild(child, signal) {
|
|
34
|
+
if (!child?.pid)
|
|
35
|
+
return;
|
|
36
|
+
if (process.platform === 'win32') {
|
|
37
|
+
try {
|
|
38
|
+
child.kill(signal);
|
|
39
|
+
}
|
|
40
|
+
catch { }
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
process.kill(-child.pid, signal);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
try {
|
|
48
|
+
child.kill(signal);
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function buildOutput(stdout, stderr, exitCode) {
|
|
54
|
+
if (exitCode === 0)
|
|
55
|
+
return trimOutput(stdout.trim());
|
|
56
|
+
return trimOutput([stdout, stderr].filter(Boolean).join('\n').trim());
|
|
57
|
+
}
|
|
58
|
+
function emptyRepoSync(reason) {
|
|
59
|
+
return {
|
|
60
|
+
added: 0,
|
|
61
|
+
modified: 0,
|
|
62
|
+
removed: 0,
|
|
63
|
+
indexedChunks: 0,
|
|
64
|
+
retrievalTokensUsed: 0,
|
|
65
|
+
skipped: true,
|
|
66
|
+
...(reason ? { reason } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function readGitStatusSignature(rootDir) {
|
|
70
|
+
try {
|
|
71
|
+
return execFileSync('git', ['-C', rootDir, 'status', '--porcelain=v1', '--untracked-files=all'], {
|
|
72
|
+
encoding: 'utf-8',
|
|
73
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
74
|
+
timeout: 10_000,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function executeNodeScript(rootDir, script, timeout) {
|
|
82
|
+
const effectiveTimeout = typeof timeout === 'number' && timeout > 0
|
|
83
|
+
? timeout
|
|
84
|
+
: DEFAULT_TIMEOUT;
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
let stdout = '';
|
|
87
|
+
let stderr = '';
|
|
88
|
+
let timedOut = false;
|
|
89
|
+
let settled = false;
|
|
90
|
+
let timeoutTimer = null;
|
|
91
|
+
let killTimer = null;
|
|
92
|
+
const child = spawn(process.execPath, ['--input-type=module'], {
|
|
93
|
+
cwd: rootDir,
|
|
94
|
+
detached: process.platform !== 'win32',
|
|
95
|
+
env: {
|
|
96
|
+
...process.env,
|
|
97
|
+
CI: 'true',
|
|
98
|
+
npm_config_yes: 'true',
|
|
99
|
+
npm_config_progress: 'false',
|
|
100
|
+
npm_config_fund: 'false',
|
|
101
|
+
npm_config_audit: 'false',
|
|
102
|
+
},
|
|
103
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
104
|
+
});
|
|
105
|
+
const finish = (exitCode) => {
|
|
106
|
+
if (settled)
|
|
107
|
+
return;
|
|
108
|
+
settled = true;
|
|
109
|
+
if (timeoutTimer)
|
|
110
|
+
clearTimeout(timeoutTimer);
|
|
111
|
+
if (killTimer)
|
|
112
|
+
clearTimeout(killTimer);
|
|
113
|
+
resolve({
|
|
114
|
+
exitCode,
|
|
115
|
+
stdout,
|
|
116
|
+
stderr,
|
|
117
|
+
output: buildOutput(stdout, stderr, exitCode),
|
|
118
|
+
timedOut,
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
timeoutTimer = setTimeout(() => {
|
|
122
|
+
timedOut = true;
|
|
123
|
+
terminateChild(child, 'SIGTERM');
|
|
124
|
+
killTimer = setTimeout(() => terminateChild(child, 'SIGKILL'), 2000);
|
|
125
|
+
killTimer.unref?.();
|
|
126
|
+
}, effectiveTimeout);
|
|
127
|
+
timeoutTimer.unref?.();
|
|
128
|
+
child.stdout?.on('data', (chunk) => {
|
|
129
|
+
const text = chunk.toString('utf-8');
|
|
130
|
+
stdout = appendCapturedText(stdout, text);
|
|
131
|
+
});
|
|
132
|
+
child.stderr?.on('data', (chunk) => {
|
|
133
|
+
const text = chunk.toString('utf-8');
|
|
134
|
+
stderr = appendCapturedText(stderr, text);
|
|
135
|
+
});
|
|
136
|
+
child.on('error', (error) => {
|
|
137
|
+
stderr = appendCapturedText(stderr, error.message ? `${error.message}\n` : '');
|
|
138
|
+
finish(1);
|
|
139
|
+
});
|
|
140
|
+
child.on('close', (code, signal) => {
|
|
141
|
+
finish(timedOut || signal === 'SIGTERM' || signal === 'SIGKILL'
|
|
142
|
+
? 1
|
|
143
|
+
: code ?? 1);
|
|
144
|
+
});
|
|
145
|
+
child.stdin?.end(script);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
export async function runNodeScript(context, args) {
|
|
149
|
+
const { rootDir, autoYes, confirmCommand } = context;
|
|
150
|
+
const script = typeof args.script === 'string' ? args.script : '';
|
|
151
|
+
if (!script.trim()) {
|
|
152
|
+
return { ok: false, error: 'script is required' };
|
|
153
|
+
}
|
|
154
|
+
const commandForApproval = describeNodeScriptForApproval(script);
|
|
155
|
+
if (!isTuiMode()) {
|
|
156
|
+
console.log(chalk.bold.yellow(`\n ⚡ Node script:\n${commandForApproval}\n`));
|
|
157
|
+
console.log(chalk.dim(` in: ${rootDir}\n`));
|
|
158
|
+
}
|
|
159
|
+
if (!autoYes) {
|
|
160
|
+
if (confirmCommand == null) {
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
command: COMMAND_LABEL,
|
|
164
|
+
error: 'confirmCommand is required when autoYes is false',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const approved = await confirmCommand(commandForApproval);
|
|
168
|
+
if (!approved) {
|
|
169
|
+
if (!isTuiMode()) {
|
|
170
|
+
console.log(chalk.dim(` ⏭ Skipped: ${COMMAND_LABEL}`));
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
skipped: true,
|
|
175
|
+
command: COMMAND_LABEL,
|
|
176
|
+
error: 'User declined command execution',
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const beforeGitStatus = readGitStatusSignature(rootDir);
|
|
181
|
+
const result = await executeNodeScript(rootDir, script, typeof args.timeout_ms === 'number' && args.timeout_ms > 0
|
|
182
|
+
? args.timeout_ms
|
|
183
|
+
: undefined);
|
|
184
|
+
const afterGitStatus = readGitStatusSignature(rootDir);
|
|
185
|
+
const gitStatusCleanBeforeAndAfter = beforeGitStatus === '' && afterGitStatus === '';
|
|
186
|
+
const shouldSync = context.projectIndex.initialized && !gitStatusCleanBeforeAndAfter;
|
|
187
|
+
const repoSync = shouldSync
|
|
188
|
+
? await syncIndexFromDisk(context.projectIndex)
|
|
189
|
+
: emptyRepoSync(!context.projectIndex.initialized
|
|
190
|
+
? 'project index not initialized'
|
|
191
|
+
: gitStatusCleanBeforeAndAfter
|
|
192
|
+
? 'git status clean before and after'
|
|
193
|
+
: 'sync not needed');
|
|
194
|
+
invalidateShellDiagnosticsCache(rootDir);
|
|
195
|
+
if (repoSync.added || repoSync.modified || repoSync.removed) {
|
|
196
|
+
context.onStatus(`Synced repo state after Node script (${repoSync.added} added, ${repoSync.modified} modified, ${repoSync.removed} removed).`);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
ok: result.exitCode === 0,
|
|
200
|
+
command: COMMAND_LABEL,
|
|
201
|
+
exitCode: result.exitCode,
|
|
202
|
+
timedOut: result.timedOut,
|
|
203
|
+
stdout: result.stdout,
|
|
204
|
+
stderr: result.stderr,
|
|
205
|
+
output: result.output,
|
|
206
|
+
repoSync,
|
|
207
|
+
retrievalTokensUsed: repoSync.retrievalTokensUsed,
|
|
208
|
+
diagnostics: buildDeferredShellDiagnostics('run_node_script'),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { formatIndexResults, searchIndex, } from '../project-index.js';
|
|
2
|
+
import { clampInteger } from '../utils.js';
|
|
3
|
+
export async function searchCode(projectIndex, args) {
|
|
4
|
+
const query = String(args.query ?? '').trim();
|
|
5
|
+
if (!query) {
|
|
6
|
+
return { ok: false, error: 'query is required' };
|
|
7
|
+
}
|
|
8
|
+
const limit = clampInteger(args.limit, 5, 20);
|
|
9
|
+
let searchResult;
|
|
10
|
+
try {
|
|
11
|
+
searchResult = await searchIndex(projectIndex, query, limit);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15
|
+
if (/semantic index/i.test(message)) {
|
|
16
|
+
return {
|
|
17
|
+
ok: false,
|
|
18
|
+
skipped: true,
|
|
19
|
+
failureCategory: 'tool_exception',
|
|
20
|
+
failureDetails: {
|
|
21
|
+
category: 'tool_exception',
|
|
22
|
+
tool: 'search_code',
|
|
23
|
+
action: 'Search is temporarily unavailable.',
|
|
24
|
+
},
|
|
25
|
+
error: 'Search is temporarily unavailable.',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
const { results, retrievalTokensUsed } = searchResult;
|
|
31
|
+
return {
|
|
32
|
+
ok: true,
|
|
33
|
+
query,
|
|
34
|
+
results: formatIndexResults(results),
|
|
35
|
+
retrievalTokensUsed,
|
|
36
|
+
};
|
|
37
|
+
}
|