@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,18 @@
|
|
|
1
|
+
import { findNamedChild, getIdentifierLikeText, getParametersText, pushSignature, } from './utils.js';
|
|
2
|
+
export function addCppFamilySignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'class_specifier':
|
|
5
|
+
case 'struct_specifier':
|
|
6
|
+
case 'enum_specifier':
|
|
7
|
+
pushSignature(signatures, `${node.type.split('_')[0]} ${getIdentifierLikeText(node)}`);
|
|
8
|
+
return;
|
|
9
|
+
case 'function_definition':
|
|
10
|
+
case 'declaration': {
|
|
11
|
+
const funcDeclarator = findNamedChild(node, new Set(['function_declarator']));
|
|
12
|
+
if (funcDeclarator) {
|
|
13
|
+
pushSignature(signatures, `function ${getIdentifierLikeText(funcDeclarator)}${getParametersText(funcDeclarator)}`);
|
|
14
|
+
}
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getIdentifierLikeText, getParametersText, pushSignature, } from './utils.js';
|
|
2
|
+
export function addCSharpSignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'class_declaration':
|
|
5
|
+
case 'interface_declaration':
|
|
6
|
+
case 'struct_declaration':
|
|
7
|
+
case 'enum_declaration':
|
|
8
|
+
case 'record_declaration':
|
|
9
|
+
pushSignature(signatures, `${node.type.split('_')[0]} ${getIdentifierLikeText(node)}`);
|
|
10
|
+
return;
|
|
11
|
+
case 'method_declaration':
|
|
12
|
+
case 'constructor_declaration':
|
|
13
|
+
pushSignature(signatures, `method ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { findNamedChild, pushSignature } from './utils.js';
|
|
2
|
+
export function addCssFamilySignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'rule_set': {
|
|
5
|
+
const selectors = findNamedChild(node, new Set(['selectors']));
|
|
6
|
+
if (selectors) {
|
|
7
|
+
pushSignature(signatures, `${selectors.text} { ... }`);
|
|
8
|
+
}
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getIdentifierLikeText, getNamedFieldText, getParametersText, pushSignature, } from './utils.js';
|
|
2
|
+
export function addGoSignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'function_declaration':
|
|
5
|
+
pushSignature(signatures, `func ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
6
|
+
return;
|
|
7
|
+
case 'method_declaration':
|
|
8
|
+
pushSignature(signatures, `method ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
9
|
+
return;
|
|
10
|
+
case 'type_spec': {
|
|
11
|
+
const name = getIdentifierLikeText(node);
|
|
12
|
+
const typeText = getNamedFieldText(node, 'type');
|
|
13
|
+
if (!name)
|
|
14
|
+
return;
|
|
15
|
+
if (typeText.startsWith('struct')) {
|
|
16
|
+
pushSignature(signatures, `type ${name} struct`);
|
|
17
|
+
}
|
|
18
|
+
else if (typeText.startsWith('interface')) {
|
|
19
|
+
pushSignature(signatures, `type ${name} interface`);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
pushSignature(signatures, `type ${name}`);
|
|
23
|
+
}
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { addCppFamilySignature } from './cpp.js';
|
|
2
|
+
import { addCSharpSignature } from './csharp.js';
|
|
3
|
+
import { addCssFamilySignature } from './css.js';
|
|
4
|
+
import { addGoSignature } from './go.js';
|
|
5
|
+
import { addJavaSignature } from './java.js';
|
|
6
|
+
import { addJsFamilySignature } from './javascript.js';
|
|
7
|
+
import { addObjcSignature } from './objc.js';
|
|
8
|
+
import { addPhpSignature } from './php.js';
|
|
9
|
+
import { addPythonSignature } from './python.js';
|
|
10
|
+
import { addRubySignature } from './ruby.js';
|
|
11
|
+
import { addRustSignature } from './rust.js';
|
|
12
|
+
export function addSignatureForNode(node, languageId, signatures) {
|
|
13
|
+
switch (languageId) {
|
|
14
|
+
case 'javascript':
|
|
15
|
+
case 'typescript':
|
|
16
|
+
case 'tsx':
|
|
17
|
+
addJsFamilySignature(node, signatures);
|
|
18
|
+
return;
|
|
19
|
+
case 'python':
|
|
20
|
+
addPythonSignature(node, signatures);
|
|
21
|
+
return;
|
|
22
|
+
case 'go':
|
|
23
|
+
addGoSignature(node, signatures);
|
|
24
|
+
return;
|
|
25
|
+
case 'rust':
|
|
26
|
+
addRustSignature(node, signatures);
|
|
27
|
+
return;
|
|
28
|
+
case 'php':
|
|
29
|
+
addPhpSignature(node, signatures);
|
|
30
|
+
return;
|
|
31
|
+
case 'java':
|
|
32
|
+
addJavaSignature(node, signatures);
|
|
33
|
+
return;
|
|
34
|
+
case 'c':
|
|
35
|
+
case 'cpp':
|
|
36
|
+
addCppFamilySignature(node, signatures);
|
|
37
|
+
return;
|
|
38
|
+
case 'csharp':
|
|
39
|
+
addCSharpSignature(node, signatures);
|
|
40
|
+
return;
|
|
41
|
+
case 'objc':
|
|
42
|
+
addObjcSignature(node, signatures);
|
|
43
|
+
return;
|
|
44
|
+
case 'ruby':
|
|
45
|
+
addRubySignature(node, signatures);
|
|
46
|
+
return;
|
|
47
|
+
case 'css':
|
|
48
|
+
case 'scss':
|
|
49
|
+
addCssFamilySignature(node, signatures);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getIdentifierLikeText, getParametersText, pushSignature, } from './utils.js';
|
|
2
|
+
export function addJavaSignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'class_declaration':
|
|
5
|
+
case 'interface_declaration':
|
|
6
|
+
case 'enum_declaration':
|
|
7
|
+
pushSignature(signatures, `${node.type.split('_')[0]} ${getIdentifierLikeText(node)}`);
|
|
8
|
+
return;
|
|
9
|
+
case 'method_declaration':
|
|
10
|
+
case 'constructor_declaration':
|
|
11
|
+
pushSignature(signatures, `method ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getIdentifierLikeText, getParametersText, getVariableNameFromFunctionNode, pushSignature, } from './utils.js';
|
|
2
|
+
export function addJsFamilySignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'class_declaration':
|
|
5
|
+
pushSignature(signatures, `class ${getIdentifierLikeText(node)}`);
|
|
6
|
+
return;
|
|
7
|
+
case 'interface_declaration':
|
|
8
|
+
pushSignature(signatures, `interface ${getIdentifierLikeText(node)}`);
|
|
9
|
+
return;
|
|
10
|
+
case 'type_alias_declaration':
|
|
11
|
+
pushSignature(signatures, `type ${getIdentifierLikeText(node)}`);
|
|
12
|
+
return;
|
|
13
|
+
case 'enum_declaration':
|
|
14
|
+
pushSignature(signatures, `enum ${getIdentifierLikeText(node)}`);
|
|
15
|
+
return;
|
|
16
|
+
case 'function_declaration':
|
|
17
|
+
case 'function_signature':
|
|
18
|
+
pushSignature(signatures, `function ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
19
|
+
return;
|
|
20
|
+
case 'method_definition':
|
|
21
|
+
case 'method_signature':
|
|
22
|
+
pushSignature(signatures, `method ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
23
|
+
return;
|
|
24
|
+
case 'arrow_function':
|
|
25
|
+
case 'function_expression': {
|
|
26
|
+
const name = getVariableNameFromFunctionNode(node);
|
|
27
|
+
if (name) {
|
|
28
|
+
pushSignature(signatures, `function ${name}${getParametersText(node)}`);
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getIdentifierLikeText, pushSignature } from './utils.js';
|
|
2
|
+
export function addObjcSignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'class_interface':
|
|
5
|
+
case 'class_implementation':
|
|
6
|
+
case 'protocol_declaration':
|
|
7
|
+
pushSignature(signatures, `class ${getIdentifierLikeText(node)}`);
|
|
8
|
+
return;
|
|
9
|
+
case 'method_declaration':
|
|
10
|
+
case 'method_definition':
|
|
11
|
+
pushSignature(signatures, `method ${getIdentifierLikeText(node)}`);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getIdentifierLikeText, getParametersText, pushSignature, } from './utils.js';
|
|
2
|
+
export function addPhpSignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'class_declaration':
|
|
5
|
+
pushSignature(signatures, `class ${getIdentifierLikeText(node)}`);
|
|
6
|
+
return;
|
|
7
|
+
case 'interface_declaration':
|
|
8
|
+
pushSignature(signatures, `interface ${getIdentifierLikeText(node)}`);
|
|
9
|
+
return;
|
|
10
|
+
case 'trait_declaration':
|
|
11
|
+
pushSignature(signatures, `trait ${getIdentifierLikeText(node)}`);
|
|
12
|
+
return;
|
|
13
|
+
case 'function_definition':
|
|
14
|
+
pushSignature(signatures, `function ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
15
|
+
return;
|
|
16
|
+
case 'method_declaration':
|
|
17
|
+
pushSignature(signatures, `method ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getIdentifierLikeText, getParametersText, pushSignature, } from './utils.js';
|
|
2
|
+
export function addPythonSignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'class_definition':
|
|
5
|
+
pushSignature(signatures, `class ${getIdentifierLikeText(node)}`);
|
|
6
|
+
return;
|
|
7
|
+
case 'function_definition':
|
|
8
|
+
pushSignature(signatures, `def ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getIdentifierLikeText, getParametersText, pushSignature, } from './utils.js';
|
|
2
|
+
export function addRubySignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'class':
|
|
5
|
+
case 'module':
|
|
6
|
+
pushSignature(signatures, `${node.type} ${getIdentifierLikeText(node)}`);
|
|
7
|
+
return;
|
|
8
|
+
case 'method':
|
|
9
|
+
case 'singleton_method':
|
|
10
|
+
pushSignature(signatures, `def ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getIdentifierLikeText, getParametersText, pushSignature, } from './utils.js';
|
|
2
|
+
export function addRustSignature(node, signatures) {
|
|
3
|
+
switch (node.type) {
|
|
4
|
+
case 'struct_item':
|
|
5
|
+
pushSignature(signatures, `struct ${getIdentifierLikeText(node)}`);
|
|
6
|
+
return;
|
|
7
|
+
case 'enum_item':
|
|
8
|
+
pushSignature(signatures, `enum ${getIdentifierLikeText(node)}`);
|
|
9
|
+
return;
|
|
10
|
+
case 'trait_item':
|
|
11
|
+
pushSignature(signatures, `trait ${getIdentifierLikeText(node)}`);
|
|
12
|
+
return;
|
|
13
|
+
case 'function_item':
|
|
14
|
+
pushSignature(signatures, `fn ${getIdentifierLikeText(node)}${getParametersText(node)}`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function pushSignature(signatures, line) {
|
|
2
|
+
const normalized = String(line ?? '').trim();
|
|
3
|
+
if (!normalized)
|
|
4
|
+
return;
|
|
5
|
+
if (!signatures.includes(normalized)) {
|
|
6
|
+
signatures.push(normalized);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function getNamedFieldText(node, fieldName) {
|
|
10
|
+
return node?.childForFieldName(fieldName)?.text ?? '';
|
|
11
|
+
}
|
|
12
|
+
export function findNamedChild(node, allowedTypes) {
|
|
13
|
+
if (!node)
|
|
14
|
+
return null;
|
|
15
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
16
|
+
const child = node.namedChild(i);
|
|
17
|
+
if (child && allowedTypes.has(child.type)) {
|
|
18
|
+
return child;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
export function getIdentifierLikeText(node) {
|
|
24
|
+
if (!node)
|
|
25
|
+
return '';
|
|
26
|
+
const directName = getNamedFieldText(node, 'name') ||
|
|
27
|
+
getNamedFieldText(node, 'declarator') ||
|
|
28
|
+
getNamedFieldText(node, 'pattern');
|
|
29
|
+
if (directName)
|
|
30
|
+
return directName;
|
|
31
|
+
const identifierNode = findNamedChild(node, new Set([
|
|
32
|
+
'identifier',
|
|
33
|
+
'property_identifier',
|
|
34
|
+
'type_identifier',
|
|
35
|
+
'variable_name',
|
|
36
|
+
'name',
|
|
37
|
+
'namespace_name',
|
|
38
|
+
]));
|
|
39
|
+
return identifierNode?.text ?? '';
|
|
40
|
+
}
|
|
41
|
+
export function getParametersText(node, fallback = '()') {
|
|
42
|
+
if (!node)
|
|
43
|
+
return fallback;
|
|
44
|
+
const fromField = getNamedFieldText(node, 'parameters') ||
|
|
45
|
+
getNamedFieldText(node, 'parameter') ||
|
|
46
|
+
getNamedFieldText(node, 'arguments');
|
|
47
|
+
if (fromField)
|
|
48
|
+
return fromField;
|
|
49
|
+
const paramsNode = findNamedChild(node, new Set(['formal_parameters', 'parameters', 'parameter_list']));
|
|
50
|
+
return paramsNode?.text ?? fallback;
|
|
51
|
+
}
|
|
52
|
+
export function getVariableNameFromFunctionNode(node) {
|
|
53
|
+
const parent = node?.parent;
|
|
54
|
+
if (!parent || parent.type !== 'variable_declarator') {
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
return getNamedFieldText(parent, 'name') || getIdentifierLikeText(parent);
|
|
58
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
// The bound keys (Enter/Esc/Ctrl+C/Tab/arrows) are identical across platforms in
|
|
3
|
+
// a terminal. The one thing that genuinely differs is the terminal's paste
|
|
4
|
+
// shortcut, so surface the one for the host OS (right-click paste works
|
|
5
|
+
// everywhere regardless).
|
|
6
|
+
function pasteShortcutForPlatform() {
|
|
7
|
+
switch (process.platform) {
|
|
8
|
+
case 'darwin':
|
|
9
|
+
return 'Cmd+V';
|
|
10
|
+
case 'win32':
|
|
11
|
+
return 'Ctrl+V';
|
|
12
|
+
default:
|
|
13
|
+
return 'Ctrl+Shift+V';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const PASTE_SHORTCUT = pasteShortcutForPlatform();
|
|
17
|
+
const HELP_MARKDOWN = [
|
|
18
|
+
'# TheGitAI',
|
|
19
|
+
'',
|
|
20
|
+
'Interactive terminal coding agent. Local repo indexing, file edits, and',
|
|
21
|
+
'shell commands run on your machine. Model inference and server-executed',
|
|
22
|
+
'tools run on the server.',
|
|
23
|
+
'',
|
|
24
|
+
'## Usage',
|
|
25
|
+
'',
|
|
26
|
+
'- `ai` — start an interactive chat session in the current repo',
|
|
27
|
+
'- `ai "<request>"` — start an interactive session with `<request>` as the first message',
|
|
28
|
+
'',
|
|
29
|
+
'## Auth',
|
|
30
|
+
'',
|
|
31
|
+
'- `ai login` — sign in to TheGitAI (opens your browser to the website)',
|
|
32
|
+
'- `ai login --no-browser` — print a URL and paste the code (for SSH/headless)',
|
|
33
|
+
'- `ai whoami` — show the signed-in account',
|
|
34
|
+
'- `ai --usage` — show account usage percentage and reset times',
|
|
35
|
+
'- `ai logout` — sign out',
|
|
36
|
+
'',
|
|
37
|
+
'## Sessions',
|
|
38
|
+
'',
|
|
39
|
+
'- `ai --list-sessions` — list saved sessions for this repo',
|
|
40
|
+
'- `ai --session <id|name>` — resume a saved session by id or name',
|
|
41
|
+
'- Sessions are stored locally and scoped to the current repo. The five',
|
|
42
|
+
' most recent sessions per repo are kept.',
|
|
43
|
+
'',
|
|
44
|
+
'## Options',
|
|
45
|
+
'',
|
|
46
|
+
'- `-y, --yes` — start in Auto-Accept mode',
|
|
47
|
+
'- `-h, --help` — show this help',
|
|
48
|
+
'',
|
|
49
|
+
'## Modes',
|
|
50
|
+
'',
|
|
51
|
+
'- Default — asks before shell commands and file edits.',
|
|
52
|
+
'- Auto-Accept — approves shell commands and file edits for the session.',
|
|
53
|
+
'- Plan — read-only; the agent can inspect, ask typed questions, and plan,',
|
|
54
|
+
' with file-reading shell commands only. It will not edit files or run tests,',
|
|
55
|
+
' builds, diagnostics, package managers, project code, or network probes.',
|
|
56
|
+
'- In the TUI, Shift+Tab cycles modes, mouse drag copies transcript/footer text, left-click opens rendered links, right-click copies rendered links or pastes in the sticky composer/footer when no selection is active, double-click copies a word, and triple-click copies a row.',
|
|
57
|
+
'',
|
|
58
|
+
'## Keys & clipboard',
|
|
59
|
+
'',
|
|
60
|
+
'- **Enter** sends • **Shift+Tab** cycles modes • **Esc** cancels the turn •',
|
|
61
|
+
' **Ctrl+C** quits. These are the same on macOS, Linux, and Windows.',
|
|
62
|
+
`- **Paste** into the composer with your terminal's paste shortcut (\`${PASTE_SHORTCUT}\``,
|
|
63
|
+
' on this system) or by right-clicking the composer.',
|
|
64
|
+
'- **Copy** from the transcript by dragging to select; double-click copies a',
|
|
65
|
+
' word and triple-click copies a row. Left-click opens rendered links.',
|
|
66
|
+
...(process.platform === 'darwin'
|
|
67
|
+
? ['- On Mac laptops without dedicated keys, **Home/End** are `Fn+←` / `Fn+→`.']
|
|
68
|
+
: []),
|
|
69
|
+
'',
|
|
70
|
+
'## Chat commands',
|
|
71
|
+
'',
|
|
72
|
+
'- `/help` — show this help',
|
|
73
|
+
'- `/usage` — show account usage percentage and reset times',
|
|
74
|
+
'- `/model` — list supported models and pick one',
|
|
75
|
+
'- `/model <id>` — switch the active model without clearing history',
|
|
76
|
+
'- `/resume` — resume a saved session for this repo',
|
|
77
|
+
'- `/clear` — clear the current conversation history',
|
|
78
|
+
'- `/exit` — quit the session',
|
|
79
|
+
'',
|
|
80
|
+
'## Safety & approvals',
|
|
81
|
+
'',
|
|
82
|
+
'- TheGitAI asks before running shell commands or applying file edits.',
|
|
83
|
+
'- At each prompt: **y** approves once, **a** approves the rest of the',
|
|
84
|
+
' session, **n** denies.',
|
|
85
|
+
'- If an approved `sudo` command needs a password, the TUI shows the exact',
|
|
86
|
+
' command and keeps the password masked and local.',
|
|
87
|
+
'- `-y` / `--yes` at startup auto-approves every shell command and file',
|
|
88
|
+
' edit for the whole session — use with care.',
|
|
89
|
+
'- File and shell operations are confined to the target repo root.',
|
|
90
|
+
'- Sensitive directories (`.git`, `node_modules`, build output) are',
|
|
91
|
+
' never indexed.',
|
|
92
|
+
'',
|
|
93
|
+
'## Troubleshooting',
|
|
94
|
+
'',
|
|
95
|
+
'- "Not logged in" → run `ai login`.',
|
|
96
|
+
'- Auth or permission errors → run `ai whoami` to confirm the signed-in',
|
|
97
|
+
' account.',
|
|
98
|
+
'- Usage or quota errors → run `ai --usage`.',
|
|
99
|
+
'- Stuck on the wrong account → `ai logout`, then `ai login` again.',
|
|
100
|
+
'- For anything else, re-run the command and report the printed error',
|
|
101
|
+
' message — there is no client-side debug mode by design.',
|
|
102
|
+
].join('\n');
|
|
103
|
+
export function formatHelpMarkdown() {
|
|
104
|
+
return HELP_MARKDOWN;
|
|
105
|
+
}
|
|
106
|
+
export function formatInteractiveHelpText() {
|
|
107
|
+
return HELP_MARKDOWN;
|
|
108
|
+
}
|
|
109
|
+
export function formatCliHelpText({ color = false } = {}) {
|
|
110
|
+
if (!color)
|
|
111
|
+
return HELP_MARKDOWN;
|
|
112
|
+
return HELP_MARKDOWN.split('\n')
|
|
113
|
+
.map((line) => {
|
|
114
|
+
const heading = line.match(/^(#{1,6})\s+(.*)$/);
|
|
115
|
+
if (heading) {
|
|
116
|
+
const level = heading[1].length;
|
|
117
|
+
const text = heading[2] ?? '';
|
|
118
|
+
return level === 1 ? chalk.bold.cyan(text) : chalk.bold(text);
|
|
119
|
+
}
|
|
120
|
+
let styled = line.replace(/`([^`]+)`/g, (_match, code) => chalk.cyan(code));
|
|
121
|
+
styled = styled.replace(/\*\*([^*]+)\*\*/g, (_match, bold) => chalk.bold(bold));
|
|
122
|
+
return styled;
|
|
123
|
+
})
|
|
124
|
+
.join('\n');
|
|
125
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
function renderInline(text) {
|
|
3
|
+
const parts = String(text ?? '').split(/(`[^`]+`)/g);
|
|
4
|
+
return parts
|
|
5
|
+
.map((part) => part.startsWith('`') && part.endsWith('`') && part.length > 1
|
|
6
|
+
? chalk.cyan(part.slice(1, -1))
|
|
7
|
+
: part)
|
|
8
|
+
.join('');
|
|
9
|
+
}
|
|
10
|
+
function isTableSeparator(line) {
|
|
11
|
+
const cells = splitTableRow(line);
|
|
12
|
+
return (cells.length > 1 &&
|
|
13
|
+
cells.every((cell) => /^:?-{3,}:?$/.test(cell.trim())));
|
|
14
|
+
}
|
|
15
|
+
function splitTableRow(line) {
|
|
16
|
+
const trimmed = String(line ?? '').trim();
|
|
17
|
+
if (!trimmed.includes('|'))
|
|
18
|
+
return [];
|
|
19
|
+
return trimmed
|
|
20
|
+
.replace(/^\|/, '')
|
|
21
|
+
.replace(/\|$/, '')
|
|
22
|
+
.split('|')
|
|
23
|
+
.map((cell) => cell.trim());
|
|
24
|
+
}
|
|
25
|
+
function tableWidth(text) {
|
|
26
|
+
return text.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
27
|
+
}
|
|
28
|
+
function padCell(text, width) {
|
|
29
|
+
return `${text}${' '.repeat(Math.max(0, width - tableWidth(text)))}`;
|
|
30
|
+
}
|
|
31
|
+
function renderTable(rows) {
|
|
32
|
+
if (rows.length < 2 || !isTableSeparator(rows[1].join('|')))
|
|
33
|
+
return [];
|
|
34
|
+
const headers = rows[0];
|
|
35
|
+
const body = rows.slice(2).filter((row) => row.length > 0);
|
|
36
|
+
const columnCount = Math.max(headers.length, ...body.map((row) => row.length));
|
|
37
|
+
const normalizedRows = [headers, ...body].map((row) => Array.from({ length: columnCount }, (_, index) => renderInline(row[index] ?? '')));
|
|
38
|
+
const widths = Array.from({ length: columnCount }, (_, index) => Math.max(...normalizedRows.map((row) => tableWidth(row[index] ?? '')), 3));
|
|
39
|
+
const border = `+${widths.map((width) => '-'.repeat(width + 2)).join('+')}+`;
|
|
40
|
+
const renderRow = (row) => `| ${row.map((cell, index) => padCell(cell, widths[index])).join(' | ')} |`;
|
|
41
|
+
return [
|
|
42
|
+
border,
|
|
43
|
+
renderRow(normalizedRows[0].map((cell) => chalk.bold(cell))),
|
|
44
|
+
border,
|
|
45
|
+
...normalizedRows.slice(1).map(renderRow),
|
|
46
|
+
border,
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
function readTableBlock(lines, startIndex) {
|
|
50
|
+
if (startIndex + 1 >= lines.length)
|
|
51
|
+
return null;
|
|
52
|
+
const header = splitTableRow(lines[startIndex]);
|
|
53
|
+
const separator = splitTableRow(lines[startIndex + 1]);
|
|
54
|
+
if (header.length < 2 || separator.length < 2 || !isTableSeparator(lines[startIndex + 1])) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const rows = [header, separator];
|
|
58
|
+
let index = startIndex + 2;
|
|
59
|
+
while (index < lines.length) {
|
|
60
|
+
const row = splitTableRow(lines[index]);
|
|
61
|
+
if (row.length < 2)
|
|
62
|
+
break;
|
|
63
|
+
rows.push(row);
|
|
64
|
+
index += 1;
|
|
65
|
+
}
|
|
66
|
+
const rendered = renderTable(rows);
|
|
67
|
+
return rendered.length ? { rendered, nextIndex: index } : null;
|
|
68
|
+
}
|
|
69
|
+
export function renderMarkdownForTerminal(markdown) {
|
|
70
|
+
const lines = String(markdown ?? '').replace(/\r\n?/g, '\n').split('\n');
|
|
71
|
+
const output = [];
|
|
72
|
+
let inCodeBlock = false;
|
|
73
|
+
for (let index = 0; index < lines.length; index++) {
|
|
74
|
+
const line = lines[index];
|
|
75
|
+
if (/^\s*```/.test(line)) {
|
|
76
|
+
inCodeBlock = !inCodeBlock;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (inCodeBlock) {
|
|
80
|
+
output.push(chalk.dim(` ${line}`));
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const table = readTableBlock(lines, index);
|
|
84
|
+
if (table) {
|
|
85
|
+
output.push(...table.rendered);
|
|
86
|
+
index = table.nextIndex - 1;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const heading = line.match(/^\s{0,3}#{1,6}\s+(.+)$/);
|
|
90
|
+
if (heading) {
|
|
91
|
+
output.push(chalk.bold(renderInline(heading[1].trim())));
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const bullet = line.match(/^(\s*)[-*]\s+(.+)$/);
|
|
95
|
+
if (bullet) {
|
|
96
|
+
output.push(`${bullet[1]}- ${renderInline(bullet[2].trim())}`);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const numbered = line.match(/^(\s*)\d+[.)]\s+(.+)$/);
|
|
100
|
+
if (numbered) {
|
|
101
|
+
output.push(`${numbered[1]}- ${renderInline(numbered[2].trim())}`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const quote = line.match(/^\s*>\s?(.+)$/);
|
|
105
|
+
if (quote) {
|
|
106
|
+
output.push(chalk.dim(`> ${renderInline(quote[1].trim())}`));
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
output.push(renderInline(line));
|
|
110
|
+
}
|
|
111
|
+
return output.join('\n').trimEnd();
|
|
112
|
+
}
|