aihand 0.0.1 → 0.1.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/README.md +152 -2
- package/dist/chunk-2NTK7H4W.js +10 -0
- package/dist/chunk-3X4FTHLC.cjs +369 -0
- package/dist/chunk-BXVNR4E2.js +399 -0
- package/dist/chunk-C7DGE6MY.cjs +1456 -0
- package/dist/chunk-DUUCVLC3.cjs +254 -0
- package/dist/chunk-FAHI53KO.cjs +125 -0
- package/dist/chunk-G7KVJ7NF.js +369 -0
- package/dist/chunk-GNEUSRGP.js +52 -0
- package/dist/chunk-IGNEAOLT.cjs +130 -0
- package/dist/chunk-IS5XFUDB.js +125 -0
- package/dist/chunk-JLYC76XL.js +2448 -0
- package/dist/chunk-KQOABC2O.cjs +52 -0
- package/dist/chunk-OVMK33AC.cjs +104 -0
- package/dist/chunk-OWYK2IGV.js +250 -0
- package/dist/chunk-PQSQN4CN.js +126 -0
- package/dist/chunk-QF6AG3M5.cjs +410 -0
- package/dist/chunk-QSAMLXML.js +1456 -0
- package/dist/chunk-VEKYRKPF.cjs +399 -0
- package/dist/chunk-Y6H7W7PI.cjs +2451 -0
- package/dist/chunk-YKSYW77R.js +410 -0
- package/dist/chunk-Z2Y65YOY.cjs +7 -0
- package/dist/chunk-ZJQRNIK7.js +104 -0
- package/dist/cli-3J7EYI6G.cjs +651 -0
- package/dist/cli-FIJLKAGI.js +649 -0
- package/dist/cli-JQEIE7RQ.js +120 -0
- package/dist/cli-K3OS2QQH.cjs +122 -0
- package/dist/cli-OSYG6LJD.cjs +89 -0
- package/dist/cli-TXRW5PG6.js +89 -0
- package/dist/cli.cjs +81 -0
- package/dist/cli.js +81 -0
- package/dist/config-5KEQLN6L.cjs +13 -0
- package/dist/config-PJPYKDLQ.js +13 -0
- package/dist/graph-IH56SCPK.js +8 -0
- package/dist/graph-ZUXXCJ5A.cjs +8 -0
- package/dist/index.cjs +481 -0
- package/dist/index.d.cts +461 -0
- package/dist/index.d.ts +461 -0
- package/dist/index.js +479 -0
- package/dist/locate-5XFSXJ5J.cjs +15 -0
- package/dist/locate-NKSUGL3A.js +15 -0
- package/dist/refactor-5FWSZIBN.cjs +19 -0
- package/dist/refactor-BOB3SZSA.js +19 -0
- package/dist/scan-4R7GQG2W.cjs +9 -0
- package/dist/scan-VF54GAAX.js +9 -0
- package/dist/ui/probe/server.cjs +505 -0
- package/dist/ui/probe/server.js +507 -0
- package/dist/vite.cjs +12 -0
- package/dist/vite.d.cts +12 -0
- package/dist/vite.d.ts +12 -0
- package/dist/vite.js +12 -0
- package/package.json +82 -9
- package/src/cli.ts +107 -0
- package/src/index.ts +54 -0
- package/src/read/cli.ts +650 -0
- package/src/read/compact.ts +286 -0
- package/src/read/config.ts +62 -0
- package/src/read/graph.ts +182 -0
- package/src/read/index.ts +12 -0
- package/src/read/inject.ts +121 -0
- package/src/read/locate.ts +104 -0
- package/src/read/panel.ts +335 -0
- package/src/read/pipeline.ts +78 -0
- package/src/read/refactor.ts +576 -0
- package/src/read/render.ts +1118 -0
- package/src/read/scan.ts +61 -0
- package/src/read/seam.ts +0 -0
- package/src/read/security.ts +171 -0
- package/src/read/signals.ts +333 -0
- package/src/read/state.ts +71 -0
- package/src/read/stategraph.ts +205 -0
- package/src/read/types.ts +162 -0
- package/src/read/vite.ts +77 -0
- package/src/ui/babel/line-profiler.ts +197 -0
- package/src/ui/babel/source-loc.ts +68 -0
- package/src/ui/bridge/cdp-bridge.ts +138 -0
- package/src/ui/bridge/compile-probe.ts +80 -0
- package/src/ui/bridge/transport.ts +26 -0
- package/src/ui/bridge/vite-bridge.ts +116 -0
- package/src/ui/client/client-patch.ts +899 -0
- package/src/ui/client/client.ts +2562 -0
- package/src/ui/core/action.ts +747 -0
- package/src/ui/core/candidates.ts +348 -0
- package/src/ui/core/canvas.ts +305 -0
- package/src/ui/core/check.ts +34 -0
- package/src/ui/core/compact.ts +314 -0
- package/src/ui/core/detail.ts +244 -0
- package/src/ui/core/diff.ts +253 -0
- package/src/ui/core/emit.ts +198 -0
- package/src/ui/core/knob-exec.ts +137 -0
- package/src/ui/core/perf.ts +254 -0
- package/src/ui/core/types.ts +164 -0
- package/src/ui/core/util.ts +221 -0
- package/src/ui/index.ts +5 -0
- package/src/ui/probe/cli.ts +139 -0
- package/src/ui/probe/server.ts +468 -0
- package/src/ui/self/act.ts +47 -0
- package/src/ui/self/discover.ts +101 -0
- package/src/ui/self/grow.ts +121 -0
- package/src/ui/self/install.ts +100 -0
- package/src/ui/self/probe.ts +105 -0
- package/src/ui/self/screen-hook.ts +44 -0
- package/src/ui/self/self.ts +48 -0
- package/src/ui/self/store-refs.ts +123 -0
- package/src/ui/self/store-schema.ts +65 -0
- package/src/ui/self/synth.ts +37 -0
- package/src/ui/server/cli.ts +102 -0
- package/src/ui/server/dispatch.ts +276 -0
- package/src/ui/server/help-text.ts +237 -0
- package/src/ui/server/knob-schema.ts +87 -0
- package/src/ui/server/plugin.ts +1151 -0
- package/src/vite.ts +39 -0
- package/index.js +0 -2
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// src/read/scan.ts
|
|
2
|
+
var _fs = require('fs');
|
|
3
|
+
var _glob = require('glob');
|
|
4
|
+
var _ignore = require('ignore'); var _ignore2 = _interopRequireDefault(_ignore);
|
|
5
|
+
function rootPriority(name) {
|
|
6
|
+
if (name === "package.json")
|
|
7
|
+
return 0;
|
|
8
|
+
if (name.endsWith(".html"))
|
|
9
|
+
return 1;
|
|
10
|
+
if (name.startsWith("tsconfig"))
|
|
11
|
+
return 2;
|
|
12
|
+
if (name.startsWith(".env"))
|
|
13
|
+
return 3;
|
|
14
|
+
return 4;
|
|
15
|
+
}
|
|
16
|
+
var hasGlob = (p) => p.includes("*") || p.includes("?") || p.includes("{") || p.includes("[");
|
|
17
|
+
function filterAndSort(matched, gitignoreText, config, existingExplicit) {
|
|
18
|
+
const ig = _ignore2.default.call(void 0, );
|
|
19
|
+
if (gitignoreText)
|
|
20
|
+
ig.add(gitignoreText);
|
|
21
|
+
if (config.read.ignore.length)
|
|
22
|
+
ig.add(config.read.ignore);
|
|
23
|
+
const explicit = new Set(existingExplicit);
|
|
24
|
+
return matched.filter((f) => explicit.has(f) || !ig.ignores(f)).sort((a, b) => {
|
|
25
|
+
const aRoot = !a.includes("/");
|
|
26
|
+
const bRoot = !b.includes("/");
|
|
27
|
+
if (aRoot !== bRoot)
|
|
28
|
+
return aRoot ? -1 : 1;
|
|
29
|
+
if (aRoot) {
|
|
30
|
+
const pa = rootPriority(a);
|
|
31
|
+
const pb = rootPriority(b);
|
|
32
|
+
if (pa !== pb)
|
|
33
|
+
return pa - pb;
|
|
34
|
+
}
|
|
35
|
+
return a.localeCompare(b);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function scan(config) {
|
|
39
|
+
const matched = _glob.globSync.call(void 0, config.read.include, {
|
|
40
|
+
nodir: true,
|
|
41
|
+
dot: true,
|
|
42
|
+
ignore: ["**/node_modules/**"]
|
|
43
|
+
});
|
|
44
|
+
const gitignoreText = _fs.existsSync.call(void 0, ".gitignore") ? _fs.readFileSync.call(void 0, ".gitignore", "utf-8") : "";
|
|
45
|
+
const existingExplicit = config.read.include.filter((p) => !hasGlob(p) && _fs.existsSync.call(void 0, p));
|
|
46
|
+
return filterAndSort(matched, gitignoreText, config, existingExplicit);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
exports.filterAndSort = filterAndSort; exports.scan = scan;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/read/seam.ts
|
|
2
|
+
function fileOfInspPath(inspPath) {
|
|
3
|
+
const parts = inspPath.split(":");
|
|
4
|
+
return parts.length <= 3 ? parts[0] : parts.slice(0, parts.length - 3).join(":");
|
|
5
|
+
}
|
|
6
|
+
var key = (label, file) => `${label}\0${file}`;
|
|
7
|
+
function joinPanel(staticKnobs, runtime) {
|
|
8
|
+
const liveSet = /* @__PURE__ */ new Set();
|
|
9
|
+
for (const r of runtime)
|
|
10
|
+
for (const f of r.files)
|
|
11
|
+
liveSet.add(key(r.label, f));
|
|
12
|
+
return staticKnobs.map((k) => ({
|
|
13
|
+
...k,
|
|
14
|
+
live: k.label !== null && liveSet.has(key(k.label, k.filePath))
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
var COLLECT_KNOBS_SCRIPT = `JSON.stringify(window.__aihandCollectKnobs ? window.__aihandCollectKnobs() : null)`;
|
|
18
|
+
async function fetchRuntimeKnobs(port) {
|
|
19
|
+
const resp = await fetch(`http://localhost:${port}/__aihand/eval`, {
|
|
20
|
+
method: "POST",
|
|
21
|
+
body: COLLECT_KNOBS_SCRIPT
|
|
22
|
+
});
|
|
23
|
+
if (!resp.ok)
|
|
24
|
+
throw new Error(`runtime probe failed (${resp.status}) \u2014 is the dev server running with aihand() on :${port}?`);
|
|
25
|
+
const raw = JSON.parse(await resp.text());
|
|
26
|
+
if (!raw)
|
|
27
|
+
throw new Error("runtime knob collector missing (__aihandCollectKnobs) \u2014 the page is running an older aihand client; reload it");
|
|
28
|
+
return raw.map((r) => ({ label: r.label, files: r.files, tag: r.tag }));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/read/locate.ts
|
|
32
|
+
function symbolAtLine(g, file, line) {
|
|
33
|
+
let best;
|
|
34
|
+
for (const s of Object.values(g.symbols)) {
|
|
35
|
+
if (s.filePath !== file || line < s.startLine || line > s.endLine)
|
|
36
|
+
continue;
|
|
37
|
+
if (!best || s.endLine - s.startLine < best.endLine - best.startLine)
|
|
38
|
+
best = s;
|
|
39
|
+
}
|
|
40
|
+
return best;
|
|
41
|
+
}
|
|
42
|
+
function parseInspPath(p) {
|
|
43
|
+
const parts = p.split(":");
|
|
44
|
+
const file = fileOfInspPath(p);
|
|
45
|
+
const lineStr = parts.length <= 3 ? parts[1] : parts[parts.length - 3];
|
|
46
|
+
return { file, line: Number(lineStr) };
|
|
47
|
+
}
|
|
48
|
+
function locate(g, file, line) {
|
|
49
|
+
const symbol = symbolAtLine(g, file, line);
|
|
50
|
+
if (!symbol)
|
|
51
|
+
return null;
|
|
52
|
+
const byUid = (uids) => (_nullishCoalesce(uids, () => ( []))).map((u) => g.symbols[u]).filter(Boolean);
|
|
53
|
+
return {
|
|
54
|
+
symbol,
|
|
55
|
+
callers: byUid(g.in[symbol.uid]),
|
|
56
|
+
callees: byUid(g.out[symbol.uid])
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
var at = (s) => `${s.filePath}:${s.startLine + 1}`;
|
|
60
|
+
function renderLocate(r, json = false) {
|
|
61
|
+
if (json)
|
|
62
|
+
return JSON.stringify(r, null, 2);
|
|
63
|
+
const lines = [`${r.symbol.name} ${r.symbol.kind} ${at(r.symbol)}`];
|
|
64
|
+
if (r.callers.length) {
|
|
65
|
+
lines.push(`
|
|
66
|
+
Callers (${r.callers.length}):`);
|
|
67
|
+
for (const c of r.callers)
|
|
68
|
+
lines.push(` ${c.name} ${at(c)}`);
|
|
69
|
+
}
|
|
70
|
+
if (r.callees.length) {
|
|
71
|
+
lines.push(`
|
|
72
|
+
Callees (${r.callees.length}):`);
|
|
73
|
+
for (const c of r.callees)
|
|
74
|
+
lines.push(` ${c.name} ${at(c)}`);
|
|
75
|
+
}
|
|
76
|
+
if (!r.callers.length && !r.callees.length)
|
|
77
|
+
lines.push(" (no call edges found)");
|
|
78
|
+
return lines.join("\n");
|
|
79
|
+
}
|
|
80
|
+
function sourcePanelHtml(r, esc = escHtml) {
|
|
81
|
+
const row = (c) => `<div class="aihand-src-edge" data-src-path="${esc(at(c))}">${esc(c.name)} <span class="aihand-src-at">${esc(at(c))}</span></div>`;
|
|
82
|
+
const out = [`<div class="aihand-src-head">${esc(r.symbol.name)} <span class="aihand-src-kind">${esc(r.symbol.kind)}</span> <span class="aihand-src-at">${esc(at(r.symbol))}</span></div>`];
|
|
83
|
+
if (r.callers.length)
|
|
84
|
+
out.push(`<div class="aihand-src-sec">Callers (${r.callers.length})</div>`, ...r.callers.map(row));
|
|
85
|
+
if (r.callees.length)
|
|
86
|
+
out.push(`<div class="aihand-src-sec">Callees (${r.callees.length})</div>`, ...r.callees.map(row));
|
|
87
|
+
if (!r.callers.length && !r.callees.length)
|
|
88
|
+
out.push(`<div class="aihand-src-empty">(no call edges found)</div>`);
|
|
89
|
+
return out.join("");
|
|
90
|
+
}
|
|
91
|
+
function escHtml(s) {
|
|
92
|
+
return s.split("&").join("&").split("<").join("<").split(">").join(">").split('"').join(""");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
exports.fileOfInspPath = fileOfInspPath; exports.joinPanel = joinPanel; exports.fetchRuntimeKnobs = fetchRuntimeKnobs; exports.symbolAtLine = symbolAtLine; exports.parseInspPath = parseInspPath; exports.locate = locate; exports.renderLocate = renderLocate; exports.sourcePanelHtml = sourcePanelHtml;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// src/read/compact.ts
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import { dirname, join, resolve } from "path";
|
|
5
|
+
import { Language, Query } from "web-tree-sitter";
|
|
6
|
+
var queryCache = /* @__PURE__ */ new Map();
|
|
7
|
+
function cachedQuery(lang, source) {
|
|
8
|
+
let byLang = queryCache.get(lang);
|
|
9
|
+
if (!byLang) {
|
|
10
|
+
byLang = /* @__PURE__ */ new Map();
|
|
11
|
+
queryCache.set(lang, byLang);
|
|
12
|
+
}
|
|
13
|
+
let q = byLang.get(source);
|
|
14
|
+
if (!q) {
|
|
15
|
+
q = new Query(lang, source);
|
|
16
|
+
byLang.set(source, q);
|
|
17
|
+
}
|
|
18
|
+
return q;
|
|
19
|
+
}
|
|
20
|
+
var SEPARATOR = "\u22EE----";
|
|
21
|
+
function isNoParamFn(trimmed) {
|
|
22
|
+
if (!trimmed.endsWith(")"))
|
|
23
|
+
return false;
|
|
24
|
+
let i = trimmed.length - 2;
|
|
25
|
+
while (i >= 0 && (trimmed[i] === " " || trimmed[i] === " ")) i--;
|
|
26
|
+
return i >= 0 && trimmed[i] === "(";
|
|
27
|
+
}
|
|
28
|
+
function isSectionComment(text) {
|
|
29
|
+
if (!text.startsWith("//"))
|
|
30
|
+
return false;
|
|
31
|
+
let i = 2;
|
|
32
|
+
while (i < text.length && (text[i] === " " || text[i] === " ")) i++;
|
|
33
|
+
return text.startsWith("\u2500\u2500", i);
|
|
34
|
+
}
|
|
35
|
+
var IMPORT_QUERY = `
|
|
36
|
+
(import_statement
|
|
37
|
+
source: (string (string_fragment) @source))
|
|
38
|
+
`;
|
|
39
|
+
var TS_QUERY = `
|
|
40
|
+
(export_statement (function_declaration)) @fn
|
|
41
|
+
(export_statement (lexical_declaration (variable_declarator value: (arrow_function)))) @fn
|
|
42
|
+
(export_statement (generator_function_declaration)) @fn
|
|
43
|
+
(type_alias_declaration) @internal_type
|
|
44
|
+
(export_statement (type_alias_declaration)) @type
|
|
45
|
+
(interface_declaration) @internal_type
|
|
46
|
+
(export_statement (interface_declaration)) @type
|
|
47
|
+
(enum_declaration) @internal_type
|
|
48
|
+
(export_statement (enum_declaration)) @type
|
|
49
|
+
(class_declaration) @class
|
|
50
|
+
(export_statement (class_declaration)) @class
|
|
51
|
+
(export_statement value: (assignment_expression)) @export_val
|
|
52
|
+
(export_statement (lexical_declaration)) @export_val
|
|
53
|
+
(export_statement source: (string)) @reexport
|
|
54
|
+
(comment) @comment
|
|
55
|
+
`;
|
|
56
|
+
var WASM_MAP = {
|
|
57
|
+
".ts": "tree-sitter-tsx.wasm",
|
|
58
|
+
".tsx": "tree-sitter-tsx.wasm",
|
|
59
|
+
".mts": "tree-sitter-tsx.wasm",
|
|
60
|
+
".js": "tree-sitter-tsx.wasm",
|
|
61
|
+
".jsx": "tree-sitter-tsx.wasm",
|
|
62
|
+
".mjs": "tree-sitter-tsx.wasm"
|
|
63
|
+
};
|
|
64
|
+
var WASM_PROBE_PATHS = [
|
|
65
|
+
"@repomix/tree-sitter-wasms/out",
|
|
66
|
+
"@anthropic-ai/tree-sitter-wasms/out",
|
|
67
|
+
"@anthropic-ai/codemap-tree-sitter-wasms/out"
|
|
68
|
+
];
|
|
69
|
+
var langCache = /* @__PURE__ */ new Map();
|
|
70
|
+
var wasmWarnedOnce = false;
|
|
71
|
+
function findWasmDir(wasmDir) {
|
|
72
|
+
if (wasmDir)
|
|
73
|
+
return existsSync(wasmDir) ? wasmDir : null;
|
|
74
|
+
for (const probe of WASM_PROBE_PATHS) {
|
|
75
|
+
const full = resolve("node_modules", probe);
|
|
76
|
+
if (existsSync(full))
|
|
77
|
+
return full;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const req = createRequire(import.meta.url);
|
|
81
|
+
return dirname(req.resolve("@repomix/tree-sitter-wasms/out/tree-sitter-tsx.wasm"));
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
async function getLang(ext, wasmDir) {
|
|
87
|
+
const wasm = WASM_MAP[ext];
|
|
88
|
+
if (!wasm)
|
|
89
|
+
return null;
|
|
90
|
+
const dir = findWasmDir(wasmDir);
|
|
91
|
+
if (!dir) {
|
|
92
|
+
if (!wasmWarnedOnce) {
|
|
93
|
+
wasmWarnedOnce = true;
|
|
94
|
+
const pkgs = WASM_PROBE_PATHS.map((p) => p.split("/")[0]).join(", ");
|
|
95
|
+
console.warn(`[repodex] \u26A0 tree-sitter wasm not found. Install one of: ${pkgs}`);
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const wasmPath = join(dir, wasm);
|
|
100
|
+
if (!existsSync(wasmPath))
|
|
101
|
+
return null;
|
|
102
|
+
const cached = langCache.get(wasmPath);
|
|
103
|
+
if (cached)
|
|
104
|
+
return cached;
|
|
105
|
+
const lang = await Language.load(wasmPath);
|
|
106
|
+
langCache.set(wasmPath, lang);
|
|
107
|
+
return lang;
|
|
108
|
+
}
|
|
109
|
+
function cleanFnSignature(text) {
|
|
110
|
+
const lines = text.split("\n");
|
|
111
|
+
let braceDepth = 0;
|
|
112
|
+
let parenDepth = 0;
|
|
113
|
+
let angleDepth = 0;
|
|
114
|
+
let afterArrow = false;
|
|
115
|
+
let arrowI = 0;
|
|
116
|
+
let arrowJ = 0;
|
|
117
|
+
let prevTopChar = "";
|
|
118
|
+
for (let i = 0; i < lines.length; i++) {
|
|
119
|
+
for (let j = 0; j < lines[i].length; j++) {
|
|
120
|
+
const ch = lines[i][j];
|
|
121
|
+
if (ch === " " || ch === " ")
|
|
122
|
+
continue;
|
|
123
|
+
if (ch === "(") {
|
|
124
|
+
parenDepth++;
|
|
125
|
+
afterArrow = false;
|
|
126
|
+
} else if (ch === ")") {
|
|
127
|
+
parenDepth--;
|
|
128
|
+
afterArrow = false;
|
|
129
|
+
} else if (ch === "<") {
|
|
130
|
+
angleDepth++;
|
|
131
|
+
afterArrow = false;
|
|
132
|
+
} else if (ch === ">") {
|
|
133
|
+
if (angleDepth > 0)
|
|
134
|
+
angleDepth--;
|
|
135
|
+
afterArrow = false;
|
|
136
|
+
} else if (ch === "{") {
|
|
137
|
+
if (afterArrow) {
|
|
138
|
+
lines[arrowI] = lines[arrowI].slice(0, arrowJ).trimEnd();
|
|
139
|
+
return lines.slice(0, arrowI + 1).join("\n");
|
|
140
|
+
}
|
|
141
|
+
if (parenDepth <= 0 && braceDepth <= 0 && angleDepth <= 0 && prevTopChar !== ":") {
|
|
142
|
+
lines[i] = lines[i].slice(0, j).trimEnd();
|
|
143
|
+
return lines.slice(0, i + 1).join("\n");
|
|
144
|
+
}
|
|
145
|
+
braceDepth++;
|
|
146
|
+
afterArrow = false;
|
|
147
|
+
} else if (ch === "}") {
|
|
148
|
+
if (braceDepth > 0)
|
|
149
|
+
braceDepth--;
|
|
150
|
+
afterArrow = false;
|
|
151
|
+
} else if (ch === "=" && lines[i][j + 1] === ">") {
|
|
152
|
+
if (parenDepth <= 0 && braceDepth <= 0 && angleDepth <= 0) {
|
|
153
|
+
lines[i] = lines[i].slice(0, j).trimEnd();
|
|
154
|
+
return lines.slice(0, i + 1).join("\n");
|
|
155
|
+
}
|
|
156
|
+
afterArrow = true;
|
|
157
|
+
arrowI = i;
|
|
158
|
+
arrowJ = j;
|
|
159
|
+
j++;
|
|
160
|
+
} else {
|
|
161
|
+
afterArrow = false;
|
|
162
|
+
}
|
|
163
|
+
if (parenDepth <= 0 && braceDepth <= 0 && angleDepth <= 0)
|
|
164
|
+
prevTopChar = ch;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return text.split("\n")[0];
|
|
168
|
+
}
|
|
169
|
+
function compact(code, parser, lang, flags = {}, existingTree) {
|
|
170
|
+
const { skipNoParamFn = true } = flags;
|
|
171
|
+
const tree = existingTree ?? parser.parse(code);
|
|
172
|
+
if (!tree)
|
|
173
|
+
return "";
|
|
174
|
+
const query = cachedQuery(lang, TS_QUERY);
|
|
175
|
+
const captures = query.captures(tree.rootNode);
|
|
176
|
+
const ownTree = !existingTree;
|
|
177
|
+
captures.sort((a, b) => a.node.startPosition.row - b.node.startPosition.row);
|
|
178
|
+
const chunks = [];
|
|
179
|
+
const seen = /* @__PURE__ */ new Set();
|
|
180
|
+
for (const { name, node } of captures) {
|
|
181
|
+
const startRow = node.startPosition.row;
|
|
182
|
+
if (seen.has(startRow))
|
|
183
|
+
continue;
|
|
184
|
+
seen.add(startRow);
|
|
185
|
+
let content;
|
|
186
|
+
if (name === "fn" || name === "class") {
|
|
187
|
+
content = cleanFnSignature(node.text);
|
|
188
|
+
} else if (name === "comment") {
|
|
189
|
+
if (isSectionComment(node.text))
|
|
190
|
+
content = node.text;
|
|
191
|
+
else continue;
|
|
192
|
+
} else if (name === "reexport") {
|
|
193
|
+
content = node.text;
|
|
194
|
+
} else if (name === "export_val") {
|
|
195
|
+
if (node.descendantsOfType("arrow_function").length > 0) {
|
|
196
|
+
content = cleanFnSignature(node.text);
|
|
197
|
+
} else if (node.text.includes("\n")) {
|
|
198
|
+
content = node.text.split("\n")[0];
|
|
199
|
+
} else {
|
|
200
|
+
content = node.text;
|
|
201
|
+
}
|
|
202
|
+
} else if (name === "internal_type" || name === "type") {
|
|
203
|
+
content = node.text;
|
|
204
|
+
} else {
|
|
205
|
+
content = node.text;
|
|
206
|
+
}
|
|
207
|
+
const trimmed = content.trim();
|
|
208
|
+
if (skipNoParamFn && (name === "fn" || name === "export_val") && isNoParamFn(trimmed))
|
|
209
|
+
continue;
|
|
210
|
+
chunks.push({ content: trimmed, startRow, endRow: node.endPosition.row });
|
|
211
|
+
}
|
|
212
|
+
const GAP = 2;
|
|
213
|
+
const merged = [];
|
|
214
|
+
for (const chunk of chunks) {
|
|
215
|
+
const last = merged[merged.length - 1];
|
|
216
|
+
if (last && last.endRow + GAP >= chunk.startRow) {
|
|
217
|
+
last.content += `
|
|
218
|
+
${chunk.content}`;
|
|
219
|
+
last.endRow = chunk.endRow;
|
|
220
|
+
} else {
|
|
221
|
+
merged.push({ ...chunk });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const result = merged.map((c) => c.content).join(`
|
|
225
|
+
${SEPARATOR}
|
|
226
|
+
`);
|
|
227
|
+
if (ownTree)
|
|
228
|
+
tree.delete();
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
function extractImports(code, parser, lang, existingTree) {
|
|
232
|
+
const tree = existingTree ?? parser.parse(code);
|
|
233
|
+
if (!tree)
|
|
234
|
+
return [];
|
|
235
|
+
const query = cachedQuery(lang, IMPORT_QUERY);
|
|
236
|
+
const result = query.captures(tree.rootNode).map((c) => c.node.text);
|
|
237
|
+
if (!existingTree)
|
|
238
|
+
tree.delete();
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export {
|
|
243
|
+
SEPARATOR,
|
|
244
|
+
IMPORT_QUERY,
|
|
245
|
+
TS_QUERY,
|
|
246
|
+
getLang,
|
|
247
|
+
cleanFnSignature,
|
|
248
|
+
compact,
|
|
249
|
+
extractImports
|
|
250
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// src/read/config.ts
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { dirname, join, resolve } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { createJiti } from "jiti";
|
|
6
|
+
|
|
7
|
+
// src/read/types.ts
|
|
8
|
+
var READ_DEFAULTS = {
|
|
9
|
+
include: [
|
|
10
|
+
"package.json",
|
|
11
|
+
"index.html",
|
|
12
|
+
"tsconfig.json",
|
|
13
|
+
"tsconfig.*.json",
|
|
14
|
+
"*.config.{js,ts,mjs,cjs}"
|
|
15
|
+
],
|
|
16
|
+
ignore: [],
|
|
17
|
+
fileDetailLevel: {
|
|
18
|
+
"package-lock.json": "tree",
|
|
19
|
+
"pnpm-lock.yaml": "tree",
|
|
20
|
+
"yarn.lock": "tree",
|
|
21
|
+
"bun.lock": "tree",
|
|
22
|
+
"**/*.test.ts": "tree",
|
|
23
|
+
"**/*.test.tsx": "tree",
|
|
24
|
+
"**/*.spec.ts": "tree",
|
|
25
|
+
"**/*.spec.tsx": "tree"
|
|
26
|
+
},
|
|
27
|
+
injectDetailLevel: "tree",
|
|
28
|
+
injectTargetFiles: ["CLAUDE.md", "AGENTS.md"],
|
|
29
|
+
showNoParamFn: false,
|
|
30
|
+
treeFoldThreshold: 8,
|
|
31
|
+
injectDevToolInstructions: true,
|
|
32
|
+
injectCodemap: true,
|
|
33
|
+
maxTokens: 5e3
|
|
34
|
+
};
|
|
35
|
+
var DEFAULT_CONFIG = {
|
|
36
|
+
read: { enabled: true, ...READ_DEFAULTS },
|
|
37
|
+
refactor: { enabled: true },
|
|
38
|
+
runtime: { enabled: true, storeMarker: "_loading" }
|
|
39
|
+
};
|
|
40
|
+
function resolveModule(sw, defaults) {
|
|
41
|
+
if (sw === false)
|
|
42
|
+
return { enabled: false, ...defaults };
|
|
43
|
+
if (sw === void 0 || sw === true)
|
|
44
|
+
return { enabled: true, ...defaults };
|
|
45
|
+
return { enabled: true, ...defaults, ...sw };
|
|
46
|
+
}
|
|
47
|
+
function defineConfig(config) {
|
|
48
|
+
return config;
|
|
49
|
+
}
|
|
50
|
+
function configTemplate() {
|
|
51
|
+
return ` // aihand = three capability modules. Each: true (on, defaults) \xB7 false (off) \xB7 { ...overrides }.
|
|
52
|
+
// Most repos only need the three switches below \u2014 tuning knobs have smart defaults.
|
|
53
|
+
|
|
54
|
+
// Codemap injection into CLAUDE.md/AGENTS.md
|
|
55
|
+
read: true,
|
|
56
|
+
// AST refactor commands (move-file / rename / move-symbol)
|
|
57
|
+
refactor: true,
|
|
58
|
+
// Inspect & drive the running app (runtime browser probe \u2014 needs the vite plugin);
|
|
59
|
+
// set false on repos without a dev server.
|
|
60
|
+
// Non-MobX store factory? runtime: { storeMarker: 'getState' } \u2014 the reactive
|
|
61
|
+
// fingerprint field on the store's return type (zustand 'getState', default '_loading').
|
|
62
|
+
runtime: true,
|
|
63
|
+
|
|
64
|
+
// Need to tune read? Open it up (all optional, shown with defaults):
|
|
65
|
+
// read: {
|
|
66
|
+
// include: ['src/**'], // what to scan (root config files always included)
|
|
67
|
+
// ignore: [], // extra skips (.gitignore already applied)
|
|
68
|
+
// maxTokens: 5_000, // budget \u2014 auto-downgrades less important files
|
|
69
|
+
// injectCodemap: true, // false \u2192 Overview + a pointer to \`aihand read --stdout\`
|
|
70
|
+
// },`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/read/config.ts
|
|
74
|
+
var CONFIG_PATH = "aihand.config.ts";
|
|
75
|
+
var SELF_PATH = fileURLToPath(import.meta.url);
|
|
76
|
+
var TS_EXTS = [".ts", ".tsx", ".js", ".jsx"];
|
|
77
|
+
var isTsFile = (p) => TS_EXTS.some((ext) => p.endsWith(ext));
|
|
78
|
+
async function loadConfig(root = resolve(".")) {
|
|
79
|
+
const configFile = join(root, CONFIG_PATH);
|
|
80
|
+
if (!existsSync(configFile))
|
|
81
|
+
return DEFAULT_CONFIG;
|
|
82
|
+
const selfDir = dirname(SELF_PATH);
|
|
83
|
+
const aihandIndex = [join(selfDir, "index.js"), resolve(selfDir, "../index.ts")].find(existsSync) ?? join(selfDir, "index.js");
|
|
84
|
+
const jiti = createJiti(root, {
|
|
85
|
+
alias: { aihand: aihandIndex },
|
|
86
|
+
moduleCache: false,
|
|
87
|
+
fsCache: false
|
|
88
|
+
});
|
|
89
|
+
const mod = await jiti.import(configFile);
|
|
90
|
+
const raw = mod.default ?? mod;
|
|
91
|
+
return normalizeConfig(raw);
|
|
92
|
+
}
|
|
93
|
+
function normalizeConfig(raw) {
|
|
94
|
+
const read = resolveModule(raw.read, {});
|
|
95
|
+
const d = DEFAULT_CONFIG.read;
|
|
96
|
+
return {
|
|
97
|
+
read: {
|
|
98
|
+
enabled: read.enabled,
|
|
99
|
+
include: [...d.include, ...read.include ?? ["src/**"]],
|
|
100
|
+
ignore: read.ignore ?? d.ignore,
|
|
101
|
+
fileDetailLevel: { ...d.fileDetailLevel, ...read.fileDetailLevel },
|
|
102
|
+
injectDetailLevel: read.injectDetailLevel ?? d.injectDetailLevel,
|
|
103
|
+
injectTargetFiles: read.injectTargetFiles ?? d.injectTargetFiles,
|
|
104
|
+
injectDevToolInstructions: read.injectDevToolInstructions ?? d.injectDevToolInstructions,
|
|
105
|
+
injectCodemap: read.injectCodemap ?? d.injectCodemap,
|
|
106
|
+
showNoParamFn: read.showNoParamFn ?? d.showNoParamFn,
|
|
107
|
+
treeFoldThreshold: read.treeFoldThreshold ?? d.treeFoldThreshold,
|
|
108
|
+
maxTokens: typeof read.maxTokens === "number" ? read.maxTokens : d.maxTokens
|
|
109
|
+
},
|
|
110
|
+
refactor: { enabled: raw.refactor !== false },
|
|
111
|
+
runtime: (() => {
|
|
112
|
+
const r = resolveModule(raw.runtime, {});
|
|
113
|
+
return { enabled: r.enabled, storeMarker: r.storeMarker ?? "_loading" };
|
|
114
|
+
})()
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export {
|
|
119
|
+
DEFAULT_CONFIG,
|
|
120
|
+
defineConfig,
|
|
121
|
+
configTemplate,
|
|
122
|
+
CONFIG_PATH,
|
|
123
|
+
isTsFile,
|
|
124
|
+
loadConfig,
|
|
125
|
+
normalizeConfig
|
|
126
|
+
};
|