@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,135 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { addSignatureForNode } from './extractors/index.js';
|
|
6
|
+
import { getRepoMapLanguageForFile, } from './repo-map-languages.js';
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const TreeSitter = require('web-tree-sitter');
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
let parserInitPromise = null;
|
|
11
|
+
const parserCache = Object.create(null);
|
|
12
|
+
const languageCache = Object.create(null);
|
|
13
|
+
function getWasmPath(wasmFile) {
|
|
14
|
+
const candidates = [
|
|
15
|
+
path.resolve(__dirname, '..', 'parsers', wasmFile),
|
|
16
|
+
path.resolve(__dirname, '..', '..', 'parsers', wasmFile),
|
|
17
|
+
];
|
|
18
|
+
return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
|
|
19
|
+
}
|
|
20
|
+
async function initParserRuntime() {
|
|
21
|
+
if (!parserInitPromise) {
|
|
22
|
+
const ts = TreeSitter;
|
|
23
|
+
const initFn = ts.init ||
|
|
24
|
+
ts.default?.init ||
|
|
25
|
+
ts.Parser?.init ||
|
|
26
|
+
ts.default?.Parser?.init;
|
|
27
|
+
parserInitPromise =
|
|
28
|
+
typeof initFn === 'function' ? initFn() : Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
await parserInitPromise;
|
|
31
|
+
}
|
|
32
|
+
function createParser() {
|
|
33
|
+
const ParserClass = TreeSitter.Parser || TreeSitter.default?.Parser || TreeSitter.default;
|
|
34
|
+
if (typeof ParserClass !== 'function') {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return Reflect.construct(ParserClass, []);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function getParserForLanguage(languageConfig) {
|
|
45
|
+
if (parserCache[languageConfig.id]) {
|
|
46
|
+
return parserCache[languageConfig.id];
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
await initParserRuntime();
|
|
50
|
+
const wasmPath = getWasmPath(languageConfig.wasmFile);
|
|
51
|
+
if (!existsSync(wasmPath)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
let language = languageCache[languageConfig.id];
|
|
55
|
+
if (!language) {
|
|
56
|
+
const ts = TreeSitter;
|
|
57
|
+
const LanguageClass = ts.Language || ts.default?.Language;
|
|
58
|
+
if (!LanguageClass?.load) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
language = await LanguageClass.load(wasmPath);
|
|
62
|
+
languageCache[languageConfig.id] = language;
|
|
63
|
+
}
|
|
64
|
+
const parser = createParser();
|
|
65
|
+
if (!parser) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
parser.setLanguage(language);
|
|
69
|
+
parserCache[languageConfig.id] = parser;
|
|
70
|
+
return parser;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export async function parseRepoSource(relPath, code) {
|
|
77
|
+
const languageConfig = getRepoMapLanguageForFile(relPath, path);
|
|
78
|
+
if (!languageConfig) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const parser = await getParserForLanguage(languageConfig);
|
|
82
|
+
if (!parser) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const tree = parser.parse(code);
|
|
87
|
+
if (!tree) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
code,
|
|
92
|
+
languageConfig,
|
|
93
|
+
languageId: languageConfig.id,
|
|
94
|
+
tree,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function collectNodeSignatures(node, languageId, limit = Number.POSITIVE_INFINITY) {
|
|
102
|
+
const signatures = [];
|
|
103
|
+
function visit(current) {
|
|
104
|
+
if (!current || signatures.length >= limit)
|
|
105
|
+
return;
|
|
106
|
+
addSignatureForNode(current, languageId, signatures);
|
|
107
|
+
if (signatures.length >= limit)
|
|
108
|
+
return;
|
|
109
|
+
for (let i = 0; i < current.namedChildCount; i++) {
|
|
110
|
+
visit(current.namedChild(i));
|
|
111
|
+
if (signatures.length >= limit)
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
visit(node);
|
|
116
|
+
return signatures;
|
|
117
|
+
}
|
|
118
|
+
export function getNodePrimarySignature(node, languageId) {
|
|
119
|
+
return collectNodeSignatures(node, languageId, 1)[0] ?? null;
|
|
120
|
+
}
|
|
121
|
+
export function getStructuralChildren(node) {
|
|
122
|
+
const body = node?.childForFieldName?.('body');
|
|
123
|
+
const source = body && body.namedChildCount > 0 ? body : node;
|
|
124
|
+
const children = [];
|
|
125
|
+
if (!source) {
|
|
126
|
+
return children;
|
|
127
|
+
}
|
|
128
|
+
for (let i = 0; i < source.namedChildCount; i++) {
|
|
129
|
+
const child = source.namedChild(i);
|
|
130
|
+
if (child && child.startIndex < child.endIndex) {
|
|
131
|
+
children.push(child);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return children;
|
|
135
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const PASTE_LINE_THRESHOLD = 5;
|
|
2
|
+
const PASTE_CHAR_THRESHOLD = 200;
|
|
3
|
+
export function shouldCollapsePaste(text) {
|
|
4
|
+
return (text.split('\n').length > PASTE_LINE_THRESHOLD ||
|
|
5
|
+
text.length > PASTE_CHAR_THRESHOLD);
|
|
6
|
+
}
|
|
7
|
+
export function buildPastePlaceholder(text, id) {
|
|
8
|
+
const lineCount = text.split('\n').length;
|
|
9
|
+
const label = lineCount > PASTE_LINE_THRESHOLD
|
|
10
|
+
? `${lineCount} lines`
|
|
11
|
+
: `${text.length} chars`;
|
|
12
|
+
return id === 1
|
|
13
|
+
? `[Pasted Text: ${label}]`
|
|
14
|
+
: `[Pasted Text: ${label} #${id}]`;
|
|
15
|
+
}
|
|
16
|
+
export function expandPastedChunks(input, chunks) {
|
|
17
|
+
let result = input;
|
|
18
|
+
for (const chunk of chunks) {
|
|
19
|
+
result = result.replace(chunk.placeholder, () => chunk.text);
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getClientStateDir } from '../client-state.js';
|
|
4
|
+
const HISTORY_FILE = 'prompt-history.json';
|
|
5
|
+
const LEGACY_HISTORY_FILE = 'prompt-history.txt';
|
|
6
|
+
export const MAX_PROMPT_HISTORY_ENTRIES = 15;
|
|
7
|
+
const ENTRY_SEPARATOR = '\x1e';
|
|
8
|
+
function getHistoryFilePath(env = process.env) {
|
|
9
|
+
return path.join(getClientStateDir(env), HISTORY_FILE);
|
|
10
|
+
}
|
|
11
|
+
function getLegacyHistoryFilePath(env = process.env) {
|
|
12
|
+
return path.join(getClientStateDir(env), LEGACY_HISTORY_FILE);
|
|
13
|
+
}
|
|
14
|
+
function sanitizeEntry(value) {
|
|
15
|
+
return value.replace(new RegExp(ENTRY_SEPARATOR, 'g'), '').trim();
|
|
16
|
+
}
|
|
17
|
+
function normalizePromptHistoryEntries(entries) {
|
|
18
|
+
const uniqueLatest = new Map();
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
const sanitized = sanitizeEntry(entry);
|
|
21
|
+
if (!sanitized || sanitized.startsWith('/'))
|
|
22
|
+
continue;
|
|
23
|
+
uniqueLatest.delete(sanitized);
|
|
24
|
+
uniqueLatest.set(sanitized, sanitized);
|
|
25
|
+
}
|
|
26
|
+
const normalized = [...uniqueLatest.values()];
|
|
27
|
+
return normalized.slice(Math.max(0, normalized.length - MAX_PROMPT_HISTORY_ENTRIES));
|
|
28
|
+
}
|
|
29
|
+
function parseJsonPromptHistory(content) {
|
|
30
|
+
const parsed = JSON.parse(content);
|
|
31
|
+
if (Array.isArray(parsed))
|
|
32
|
+
return parsed.map((entry) => String(entry));
|
|
33
|
+
if (!parsed || typeof parsed !== 'object')
|
|
34
|
+
return [];
|
|
35
|
+
const prompts = parsed.prompts;
|
|
36
|
+
return Array.isArray(prompts) ? prompts.map((entry) => String(entry)) : [];
|
|
37
|
+
}
|
|
38
|
+
function parseLegacyPromptHistory(content) {
|
|
39
|
+
return content.replace(new RegExp(ENTRY_SEPARATOR, 'g'), '\n').split(/\r?\n/);
|
|
40
|
+
}
|
|
41
|
+
function serializePromptHistory(entries) {
|
|
42
|
+
return `${JSON.stringify({ version: 1, prompts: entries }, null, 2)}\n`;
|
|
43
|
+
}
|
|
44
|
+
function writePromptHistoryFile(filePath, entries) {
|
|
45
|
+
writeFileSync(filePath, serializePromptHistory(entries), {
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
mode: existsSync(filePath) ? undefined : 0o600,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export function loadPromptHistory(env = process.env) {
|
|
51
|
+
const filePath = getHistoryFilePath(env);
|
|
52
|
+
if (existsSync(filePath)) {
|
|
53
|
+
try {
|
|
54
|
+
const content = readFileSync(filePath, 'utf8');
|
|
55
|
+
if (!content)
|
|
56
|
+
return [];
|
|
57
|
+
return normalizePromptHistoryEntries(parseJsonPromptHistory(content));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const legacyFilePath = getLegacyHistoryFilePath(env);
|
|
64
|
+
if (!existsSync(legacyFilePath))
|
|
65
|
+
return [];
|
|
66
|
+
try {
|
|
67
|
+
const content = readFileSync(legacyFilePath, 'utf8');
|
|
68
|
+
if (!content)
|
|
69
|
+
return [];
|
|
70
|
+
const entries = normalizePromptHistoryEntries(parseLegacyPromptHistory(content));
|
|
71
|
+
mkdirSync(getClientStateDir(env), { recursive: true });
|
|
72
|
+
writePromptHistoryFile(filePath, entries);
|
|
73
|
+
return entries;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function appendPromptToFile(prompt, env = process.env) {
|
|
80
|
+
const sanitized = sanitizeEntry(String(prompt ?? ''));
|
|
81
|
+
if (!sanitized || sanitized.startsWith('/'))
|
|
82
|
+
return;
|
|
83
|
+
try {
|
|
84
|
+
const dir = getClientStateDir(env);
|
|
85
|
+
mkdirSync(dir, { recursive: true });
|
|
86
|
+
const filePath = getHistoryFilePath(env);
|
|
87
|
+
const entries = normalizePromptHistoryEntries([
|
|
88
|
+
...loadPromptHistory(env),
|
|
89
|
+
sanitized,
|
|
90
|
+
]);
|
|
91
|
+
writePromptHistoryFile(filePath, entries);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|