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.
Files changed (113) hide show
  1. package/README.md +152 -2
  2. package/dist/chunk-2NTK7H4W.js +10 -0
  3. package/dist/chunk-3X4FTHLC.cjs +369 -0
  4. package/dist/chunk-BXVNR4E2.js +399 -0
  5. package/dist/chunk-C7DGE6MY.cjs +1456 -0
  6. package/dist/chunk-DUUCVLC3.cjs +254 -0
  7. package/dist/chunk-FAHI53KO.cjs +125 -0
  8. package/dist/chunk-G7KVJ7NF.js +369 -0
  9. package/dist/chunk-GNEUSRGP.js +52 -0
  10. package/dist/chunk-IGNEAOLT.cjs +130 -0
  11. package/dist/chunk-IS5XFUDB.js +125 -0
  12. package/dist/chunk-JLYC76XL.js +2448 -0
  13. package/dist/chunk-KQOABC2O.cjs +52 -0
  14. package/dist/chunk-OVMK33AC.cjs +104 -0
  15. package/dist/chunk-OWYK2IGV.js +250 -0
  16. package/dist/chunk-PQSQN4CN.js +126 -0
  17. package/dist/chunk-QF6AG3M5.cjs +410 -0
  18. package/dist/chunk-QSAMLXML.js +1456 -0
  19. package/dist/chunk-VEKYRKPF.cjs +399 -0
  20. package/dist/chunk-Y6H7W7PI.cjs +2451 -0
  21. package/dist/chunk-YKSYW77R.js +410 -0
  22. package/dist/chunk-Z2Y65YOY.cjs +7 -0
  23. package/dist/chunk-ZJQRNIK7.js +104 -0
  24. package/dist/cli-3J7EYI6G.cjs +651 -0
  25. package/dist/cli-FIJLKAGI.js +649 -0
  26. package/dist/cli-JQEIE7RQ.js +120 -0
  27. package/dist/cli-K3OS2QQH.cjs +122 -0
  28. package/dist/cli-OSYG6LJD.cjs +89 -0
  29. package/dist/cli-TXRW5PG6.js +89 -0
  30. package/dist/cli.cjs +81 -0
  31. package/dist/cli.js +81 -0
  32. package/dist/config-5KEQLN6L.cjs +13 -0
  33. package/dist/config-PJPYKDLQ.js +13 -0
  34. package/dist/graph-IH56SCPK.js +8 -0
  35. package/dist/graph-ZUXXCJ5A.cjs +8 -0
  36. package/dist/index.cjs +481 -0
  37. package/dist/index.d.cts +461 -0
  38. package/dist/index.d.ts +461 -0
  39. package/dist/index.js +479 -0
  40. package/dist/locate-5XFSXJ5J.cjs +15 -0
  41. package/dist/locate-NKSUGL3A.js +15 -0
  42. package/dist/refactor-5FWSZIBN.cjs +19 -0
  43. package/dist/refactor-BOB3SZSA.js +19 -0
  44. package/dist/scan-4R7GQG2W.cjs +9 -0
  45. package/dist/scan-VF54GAAX.js +9 -0
  46. package/dist/ui/probe/server.cjs +505 -0
  47. package/dist/ui/probe/server.js +507 -0
  48. package/dist/vite.cjs +12 -0
  49. package/dist/vite.d.cts +12 -0
  50. package/dist/vite.d.ts +12 -0
  51. package/dist/vite.js +12 -0
  52. package/package.json +82 -9
  53. package/src/cli.ts +107 -0
  54. package/src/index.ts +54 -0
  55. package/src/read/cli.ts +650 -0
  56. package/src/read/compact.ts +286 -0
  57. package/src/read/config.ts +62 -0
  58. package/src/read/graph.ts +182 -0
  59. package/src/read/index.ts +12 -0
  60. package/src/read/inject.ts +121 -0
  61. package/src/read/locate.ts +104 -0
  62. package/src/read/panel.ts +335 -0
  63. package/src/read/pipeline.ts +78 -0
  64. package/src/read/refactor.ts +576 -0
  65. package/src/read/render.ts +1118 -0
  66. package/src/read/scan.ts +61 -0
  67. package/src/read/seam.ts +0 -0
  68. package/src/read/security.ts +171 -0
  69. package/src/read/signals.ts +333 -0
  70. package/src/read/state.ts +71 -0
  71. package/src/read/stategraph.ts +205 -0
  72. package/src/read/types.ts +162 -0
  73. package/src/read/vite.ts +77 -0
  74. package/src/ui/babel/line-profiler.ts +197 -0
  75. package/src/ui/babel/source-loc.ts +68 -0
  76. package/src/ui/bridge/cdp-bridge.ts +138 -0
  77. package/src/ui/bridge/compile-probe.ts +80 -0
  78. package/src/ui/bridge/transport.ts +26 -0
  79. package/src/ui/bridge/vite-bridge.ts +116 -0
  80. package/src/ui/client/client-patch.ts +899 -0
  81. package/src/ui/client/client.ts +2562 -0
  82. package/src/ui/core/action.ts +747 -0
  83. package/src/ui/core/candidates.ts +348 -0
  84. package/src/ui/core/canvas.ts +305 -0
  85. package/src/ui/core/check.ts +34 -0
  86. package/src/ui/core/compact.ts +314 -0
  87. package/src/ui/core/detail.ts +244 -0
  88. package/src/ui/core/diff.ts +253 -0
  89. package/src/ui/core/emit.ts +198 -0
  90. package/src/ui/core/knob-exec.ts +137 -0
  91. package/src/ui/core/perf.ts +254 -0
  92. package/src/ui/core/types.ts +164 -0
  93. package/src/ui/core/util.ts +221 -0
  94. package/src/ui/index.ts +5 -0
  95. package/src/ui/probe/cli.ts +139 -0
  96. package/src/ui/probe/server.ts +468 -0
  97. package/src/ui/self/act.ts +47 -0
  98. package/src/ui/self/discover.ts +101 -0
  99. package/src/ui/self/grow.ts +121 -0
  100. package/src/ui/self/install.ts +100 -0
  101. package/src/ui/self/probe.ts +105 -0
  102. package/src/ui/self/screen-hook.ts +44 -0
  103. package/src/ui/self/self.ts +48 -0
  104. package/src/ui/self/store-refs.ts +123 -0
  105. package/src/ui/self/store-schema.ts +65 -0
  106. package/src/ui/self/synth.ts +37 -0
  107. package/src/ui/server/cli.ts +102 -0
  108. package/src/ui/server/dispatch.ts +276 -0
  109. package/src/ui/server/help-text.ts +237 -0
  110. package/src/ui/server/knob-schema.ts +87 -0
  111. package/src/ui/server/plugin.ts +1151 -0
  112. package/src/vite.ts +39 -0
  113. package/index.js +0 -2
@@ -0,0 +1,369 @@
1
+ import {
2
+ getLang
3
+ } from "./chunk-OWYK2IGV.js";
4
+
5
+ // src/read/panel.ts
6
+ import { readFileSync } from "fs";
7
+ import { Parser, Query } from "web-tree-sitter";
8
+ var ATTR_QUERY = `
9
+ (jsx_attribute
10
+ (property_identifier) @name
11
+ (jsx_expression) @val) @attr
12
+ `;
13
+ function collapseWs(s) {
14
+ const tokens = [];
15
+ let cur = "";
16
+ for (const ch of s) {
17
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
18
+ if (cur) {
19
+ tokens.push(cur);
20
+ cur = "";
21
+ }
22
+ } else {
23
+ cur += ch;
24
+ }
25
+ }
26
+ if (cur) tokens.push(cur);
27
+ return tokens.join(" ");
28
+ }
29
+ function storeWritesIn(node) {
30
+ const out = [];
31
+ const walk = (n) => {
32
+ if (n.type === "assignment_expression") {
33
+ const left = n.childForFieldName("left");
34
+ const right = n.childForFieldName("right");
35
+ if (left?.type === "member_expression") {
36
+ const obj = left.childForFieldName("object");
37
+ const prop = left.childForFieldName("property");
38
+ if (obj?.type === "identifier" && obj.text.endsWith("Store") && prop)
39
+ out.push({ store: obj.text, field: prop.text, to: collapseWs(right?.text ?? "") });
40
+ }
41
+ } else if (n.type === "call_expression") {
42
+ const fn = n.childForFieldName("function");
43
+ if (fn?.type === "member_expression") {
44
+ const obj = fn.childForFieldName("object");
45
+ const method = fn.childForFieldName("property");
46
+ if (obj?.type === "identifier" && obj.text.endsWith("Store") && method) {
47
+ const argsNode = n.childForFieldName("arguments");
48
+ const inner = argsNode ? collapseWs(argsNode.text).slice(1, -1).trim() : "";
49
+ out.push({ store: obj.text, field: `${method.text}()`, to: "", args: inner });
50
+ }
51
+ }
52
+ }
53
+ for (let i = 0; i < n.namedChildCount; i++) walk(n.namedChild(i));
54
+ };
55
+ walk(node);
56
+ return out;
57
+ }
58
+ function labelOf(opening) {
59
+ for (let i = 0; i < opening.namedChildCount; i++) {
60
+ const a = opening.namedChild(i);
61
+ if (a.type !== "jsx_attribute") continue;
62
+ if (a.namedChild(0)?.text !== "label" && a.namedChild(0)?.text !== "title") continue;
63
+ const val = a.namedChild(1);
64
+ if (!val) continue;
65
+ if (val.type === "string") return val.text.slice(1, -1);
66
+ let found = null;
67
+ const dig = (n) => {
68
+ if (found) return;
69
+ if (n.type === "string") {
70
+ found = n.text.slice(1, -1);
71
+ return;
72
+ }
73
+ for (let j = 0; j < n.namedChildCount; j++) dig(n.namedChild(j));
74
+ };
75
+ dig(val);
76
+ if (found) return found;
77
+ }
78
+ const element = opening.parent;
79
+ if (element?.type === "jsx_element") {
80
+ const parts = [];
81
+ for (let i = 0; i < element.namedChildCount; i++) {
82
+ const child = element.namedChild(i);
83
+ if (child.type === "jsx_text") {
84
+ const t = collapseWs(child.text).trim();
85
+ if (t) parts.push(t);
86
+ }
87
+ }
88
+ if (parts.length) return parts.join(" ");
89
+ }
90
+ return null;
91
+ }
92
+ function extractKnobs(filePath, code, lang) {
93
+ const parser = new Parser();
94
+ parser.setLanguage(lang);
95
+ const tree = parser.parse(code);
96
+ if (!tree) return [];
97
+ const knobs = [];
98
+ for (const c of new Query(lang, ATTR_QUERY).captures(tree.rootNode)) {
99
+ if (c.name !== "attr") continue;
100
+ const event = c.node.namedChild(0)?.text;
101
+ if (!event || !event.startsWith("on")) continue;
102
+ const writes = storeWritesIn(c.node);
103
+ if (!writes.length) continue;
104
+ const opening = c.node.parent;
105
+ const byField = /* @__PURE__ */ new Map();
106
+ for (const w of writes) byField.set(w.field, { to: w.to, args: w.args });
107
+ const transitions = [...byField].map(([field, v]) => v.args !== void 0 ? { field, to: v.to, args: v.args } : { field, to: v.to });
108
+ knobs.push({
109
+ label: labelOf(opening),
110
+ tag: opening.childForFieldName("name")?.text ?? "?",
111
+ event,
112
+ transitions,
113
+ store: writes[0].store,
114
+ filePath,
115
+ line: c.node.startPosition.row + 1
116
+ });
117
+ }
118
+ return knobs;
119
+ }
120
+ function fmtTransitions(ts) {
121
+ return ts.map((t) => t.to ? `${t.field}=${t.to}` : t.field).join(", ");
122
+ }
123
+ function formatPanel(knobs) {
124
+ const byFile = /* @__PURE__ */ new Map();
125
+ for (const k of knobs) {
126
+ const arr = byFile.get(k.filePath) ?? [];
127
+ arr.push(k);
128
+ byFile.set(k.filePath, arr);
129
+ }
130
+ const lines = [];
131
+ for (const [file, ks] of byFile) {
132
+ lines.push(file);
133
+ for (const k of ks) {
134
+ const name = k.label ? `\u300C${k.label}\u300D` : `<${k.tag}>`;
135
+ lines.push(` ${name} ${k.event} \u2192 ${k.store}.{ ${fmtTransitions(k.transitions)} } :${k.line}`);
136
+ }
137
+ }
138
+ return lines.join("\n");
139
+ }
140
+ function isIdentChar(ch) {
141
+ return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "_" || ch === "$";
142
+ }
143
+ function isLiteral(to) {
144
+ if (to === "true" || to === "false" || to === "null") return true;
145
+ if (to.length >= 2) {
146
+ const q = to[0];
147
+ if ((q === "'" || q === '"' || q === "`") && to[to.length - 1] === q) {
148
+ for (let i = 1; i < to.length - 1; i++) {
149
+ if (to[i] === q && to[i - 1] !== "\\") return false;
150
+ }
151
+ return true;
152
+ }
153
+ }
154
+ let dot = 0;
155
+ for (let i = 0; i < to.length; i++) {
156
+ const ch = to[i];
157
+ if (ch === "-" && i === 0) continue;
158
+ if (ch === ".") {
159
+ dot++;
160
+ if (dot > 1) return false;
161
+ continue;
162
+ }
163
+ if (ch < "0" || ch > "9") return false;
164
+ }
165
+ return to.length > 0 && to !== "-" && to !== ".";
166
+ }
167
+ function isMemberPath(s) {
168
+ if (!s || s[0] === "." || s[s.length - 1] === ".") return false;
169
+ let prevDot = false;
170
+ for (const ch of s) {
171
+ if (ch === ".") {
172
+ if (prevDot) return false;
173
+ prevDot = true;
174
+ continue;
175
+ }
176
+ if (!isIdentChar(ch)) return false;
177
+ prevDot = false;
178
+ }
179
+ return true;
180
+ }
181
+ function isPlaceholder(to) {
182
+ if (!isMemberPath(to)) return false;
183
+ const segs = to.split(".");
184
+ if (segs.length > 2) return false;
185
+ if (segs[0].endsWith("Store")) return false;
186
+ return true;
187
+ }
188
+ function lastSeg(path) {
189
+ const i = path.lastIndexOf(".");
190
+ return i < 0 ? path : path.slice(i + 1);
191
+ }
192
+ function classifyTransition(t) {
193
+ const field = t.field;
194
+ const to = t.to;
195
+ if (field.endsWith("()")) {
196
+ if (t.args && t.args.length > 0)
197
+ return { field, kind: "context", arity: "nullary", reason: `\u65B9\u6CD5\u5E26\u53C2 ${field.slice(0, -2)}(${t.args})\uFF0C\u5B9E\u53C2\u4F9D\u8D56\u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587` };
198
+ return { field, kind: "call", arity: "nullary" };
199
+ }
200
+ if (isLiteral(to)) return { field, kind: "assign", arity: "nullary", to };
201
+ if (to.startsWith("!")) {
202
+ const inner = to.slice(1);
203
+ if (isMemberPath(inner)) return { field: lastSeg(inner), kind: "toggle", arity: "nullary" };
204
+ }
205
+ if (isPlaceholder(to)) return { field, kind: "assign", arity: "param" };
206
+ let reason = "\u4F9D\u8D56\u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587";
207
+ if (to.includes("e.target") || to.includes(".target.")) reason = "\u4F9D\u8D56\u4E8B\u4EF6\u5BF9\u8C61 e.target";
208
+ else if (to.includes(".filter(") || to.includes(".map(") || to.includes(".find(")) reason = "\u6570\u7EC4\u8FD0\u7B97,\u4F9D\u8D56\u5F53\u524D\u96C6\u5408";
209
+ else if (to.includes("...")) reason = "\u5C55\u5F00\u8FD0\u7B97\u7B26,\u4F9D\u8D56\u5F53\u524D\u5FEB\u7167";
210
+ else if (to.includes("=>")) reason = "\u503C\u662F\u7BAD\u5934\u51FD\u6570/\u95ED\u5305";
211
+ else if (to.includes("?") && to.includes(":")) reason = "\u4E09\u5143\u8868\u8FBE\u5F0F,\u4F9D\u8D56\u8FD0\u884C\u65F6\u5206\u652F";
212
+ else if (to.includes(".message") || to.includes(".id") || to.includes(".name")) reason = "\u8BFB\u8FD0\u884C\u65F6\u5BF9\u8C61\u5C5E\u6027";
213
+ return { field, kind: "context", arity: "nullary", reason };
214
+ }
215
+ function classifyKnob(transitions) {
216
+ const ops = transitions.map(classifyTransition);
217
+ const executable = ops.length > 0 && ops.every((o) => o.kind !== "context");
218
+ const arity = ops.some((o) => o.arity === "param") ? "param" : "nullary";
219
+ return { ops, arity, executable };
220
+ }
221
+ async function buildPanel(filePaths, contents) {
222
+ await Parser.init();
223
+ const tsx = await getLang(".tsx");
224
+ if (!tsx) return [];
225
+ const panel = [];
226
+ for (const fp of filePaths) {
227
+ if (!fp.endsWith(".tsx") && !fp.endsWith(".jsx")) continue;
228
+ let code;
229
+ if (contents?.has(fp)) code = contents.get(fp);
230
+ else {
231
+ try {
232
+ code = readFileSync(fp, "utf-8");
233
+ } catch {
234
+ continue;
235
+ }
236
+ }
237
+ panel.push(...extractKnobs(fp, code, tsx));
238
+ }
239
+ return panel;
240
+ }
241
+
242
+ // src/read/stategraph.ts
243
+ import { existsSync } from "fs";
244
+ import { resolve } from "path";
245
+ import process from "process";
246
+ import { Node as TsNode, SyntaxKind } from "ts-morph";
247
+ async function openProject(cwd = process.cwd()) {
248
+ const { Project } = await import("ts-morph");
249
+ const tsConfigFilePath = ["tsconfig.app.json", "tsconfig.json"].map((f) => resolve(cwd, f)).find(existsSync);
250
+ if (!tsConfigFilePath)
251
+ throw new Error(`stategraph: no tsconfig.json (or tsconfig.app.json) found under ${cwd}`);
252
+ return new Project({ tsConfigFilePath });
253
+ }
254
+ function isModuleLevel(v) {
255
+ const stmt = v.getVariableStatement();
256
+ return !!stmt && stmt.getParent()?.getKind() === SyntaxKind.SourceFile;
257
+ }
258
+ var DEFAULT_STORE_MARKER = "_loading";
259
+ function isStateFactory(v, marker) {
260
+ const init = v.getInitializer();
261
+ if (!init || !TsNode.isCallExpression(init))
262
+ return false;
263
+ return !!v.getType().getProperty(marker);
264
+ }
265
+ function classify(v, marker = DEFAULT_STORE_MARKER) {
266
+ const init = v.getInitializer();
267
+ if (!init)
268
+ return null;
269
+ const k = init.getKind();
270
+ if (k === SyntaxKind.ArrowFunction || k === SyntaxKind.FunctionExpression || k === SyntaxKind.ClassExpression)
271
+ return "view";
272
+ if (isStateFactory(v, marker))
273
+ return "state";
274
+ return null;
275
+ }
276
+ function fieldOf(pa) {
277
+ if (!TsNode.isPropertyAssignment(pa))
278
+ return null;
279
+ const valueInit = pa.getInitializer();
280
+ if (!valueInit)
281
+ return null;
282
+ const init = TsNode.isAsExpression(valueInit) ? valueInit.getExpression().getText() : valueInit.getText();
283
+ const t = valueInit.getType();
284
+ const domain = t.isUnion() ? t.getUnionTypes().filter((u) => u.isStringLiteral()).map((u) => u.getLiteralValue()) : [];
285
+ return { name: pa.getName(), init, type: t.getText(valueInit), domain, line: pa.getStartLineNumber() };
286
+ }
287
+ function resolveStateObject(arg) {
288
+ if (TsNode.isObjectLiteralExpression(arg))
289
+ return arg;
290
+ if (TsNode.isIdentifier(arg)) {
291
+ const decl = arg.getSymbol()?.getDeclarations()?.[0];
292
+ if (decl && TsNode.isVariableDeclaration(decl)) {
293
+ const di = decl.getInitializer();
294
+ if (di && TsNode.isObjectLiteralExpression(di))
295
+ return di;
296
+ }
297
+ }
298
+ return null;
299
+ }
300
+ function extractStoreFields(v, marker = DEFAULT_STORE_MARKER) {
301
+ if (!isStateFactory(v, marker))
302
+ return [];
303
+ const init = v.getInitializer();
304
+ if (!TsNode.isCallExpression(init))
305
+ return [];
306
+ const arg = init.getArguments()[0];
307
+ const stateObj = arg && resolveStateObject(arg);
308
+ if (!stateObj || !TsNode.isObjectLiteralExpression(stateObj))
309
+ return [];
310
+ const out = [];
311
+ for (const pa of stateObj.getProperties()) {
312
+ const f = fieldOf(pa);
313
+ if (f)
314
+ out.push(f);
315
+ }
316
+ return out;
317
+ }
318
+ function growFromDecl(v, file, marker = DEFAULT_STORE_MARKER) {
319
+ const fields = extractStoreFields(v, marker).map((f) => ({
320
+ name: f.name,
321
+ type: f.type,
322
+ domain: f.domain,
323
+ line: f.line
324
+ }));
325
+ return {
326
+ store: v.getName(),
327
+ anchor: `${file}:${v.getStartLineNumber()}`,
328
+ fields
329
+ };
330
+ }
331
+ function buildStateGraph(project, fileFor = (p) => p.split("/").slice(-2).join("/"), marker = DEFAULT_STORE_MARKER) {
332
+ const nodes = [];
333
+ for (const sf of project.getSourceFiles()) {
334
+ const fp = sf.getFilePath();
335
+ if (fp.endsWith(".d.ts") || fp.includes("/node_modules/"))
336
+ continue;
337
+ const file = fileFor(fp);
338
+ for (const v of sf.getVariableDeclarations()) {
339
+ if (!isModuleLevel(v) || classify(v, marker) !== "state")
340
+ continue;
341
+ const grown = growFromDecl(v, file, marker);
342
+ const seen = /* @__PURE__ */ new Set();
343
+ let refs = 0;
344
+ try {
345
+ for (const ref of v.findReferencesAsNodes()) {
346
+ refs++;
347
+ seen.add(ref.getSourceFile().getFilePath());
348
+ }
349
+ } catch {
350
+ }
351
+ nodes.push({ ...grown, refs, files: seen.size, rank: refs + seen.size * 3 });
352
+ }
353
+ }
354
+ nodes.sort((a, b) => b.rank - a.rank);
355
+ return { nodes };
356
+ }
357
+
358
+ export {
359
+ extractKnobs,
360
+ fmtTransitions,
361
+ formatPanel,
362
+ classifyKnob,
363
+ buildPanel,
364
+ openProject,
365
+ classify,
366
+ extractStoreFields,
367
+ growFromDecl,
368
+ buildStateGraph
369
+ };
@@ -0,0 +1,52 @@
1
+ // src/read/scan.ts
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { globSync } from "glob";
4
+ import ignore from "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 = ignore();
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 = globSync(config.read.include, {
40
+ nodir: true,
41
+ dot: true,
42
+ ignore: ["**/node_modules/**"]
43
+ });
44
+ const gitignoreText = existsSync(".gitignore") ? readFileSync(".gitignore", "utf-8") : "";
45
+ const existingExplicit = config.read.include.filter((p) => !hasGlob(p) && existsSync(p));
46
+ return filterAndSort(matched, gitignoreText, config, existingExplicit);
47
+ }
48
+
49
+ export {
50
+ filterAndSort,
51
+ scan
52
+ };
@@ -0,0 +1,130 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
+
3
+ var _chunkZ2Y65YOYcjs = require('./chunk-Z2Y65YOY.cjs');
4
+
5
+ // src/read/config.ts
6
+ var _fs = require('fs');
7
+ var _path = require('path');
8
+ var _url = require('url');
9
+ var _jiti = require('jiti');
10
+
11
+ // src/read/types.ts
12
+ var READ_DEFAULTS = {
13
+ include: [
14
+ "package.json",
15
+ "index.html",
16
+ "tsconfig.json",
17
+ "tsconfig.*.json",
18
+ "*.config.{js,ts,mjs,cjs}"
19
+ ],
20
+ ignore: [],
21
+ fileDetailLevel: {
22
+ "package-lock.json": "tree",
23
+ "pnpm-lock.yaml": "tree",
24
+ "yarn.lock": "tree",
25
+ "bun.lock": "tree",
26
+ "**/*.test.ts": "tree",
27
+ "**/*.test.tsx": "tree",
28
+ "**/*.spec.ts": "tree",
29
+ "**/*.spec.tsx": "tree"
30
+ },
31
+ injectDetailLevel: "tree",
32
+ injectTargetFiles: ["CLAUDE.md", "AGENTS.md"],
33
+ showNoParamFn: false,
34
+ treeFoldThreshold: 8,
35
+ injectDevToolInstructions: true,
36
+ injectCodemap: true,
37
+ maxTokens: 5e3
38
+ };
39
+ var DEFAULT_CONFIG = {
40
+ read: { enabled: true, ...READ_DEFAULTS },
41
+ refactor: { enabled: true },
42
+ runtime: { enabled: true, storeMarker: "_loading" }
43
+ };
44
+ function resolveModule(sw, defaults) {
45
+ if (sw === false)
46
+ return { enabled: false, ...defaults };
47
+ if (sw === void 0 || sw === true)
48
+ return { enabled: true, ...defaults };
49
+ return { enabled: true, ...defaults, ...sw };
50
+ }
51
+ function defineConfig(config) {
52
+ return config;
53
+ }
54
+ function configTemplate() {
55
+ return ` // aihand = three capability modules. Each: true (on, defaults) \xB7 false (off) \xB7 { ...overrides }.
56
+ // Most repos only need the three switches below \u2014 tuning knobs have smart defaults.
57
+
58
+ // Codemap injection into CLAUDE.md/AGENTS.md
59
+ read: true,
60
+ // AST refactor commands (move-file / rename / move-symbol)
61
+ refactor: true,
62
+ // Inspect & drive the running app (runtime browser probe \u2014 needs the vite plugin);
63
+ // set false on repos without a dev server.
64
+ // Non-MobX store factory? runtime: { storeMarker: 'getState' } \u2014 the reactive
65
+ // fingerprint field on the store's return type (zustand 'getState', default '_loading').
66
+ runtime: true,
67
+
68
+ // Need to tune read? Open it up (all optional, shown with defaults):
69
+ // read: {
70
+ // include: ['src/**'], // what to scan (root config files always included)
71
+ // ignore: [], // extra skips (.gitignore already applied)
72
+ // maxTokens: 5_000, // budget \u2014 auto-downgrades less important files
73
+ // injectCodemap: true, // false \u2192 Overview + a pointer to \`aihand read --stdout\`
74
+ // },`;
75
+ }
76
+
77
+ // src/read/config.ts
78
+ var CONFIG_PATH = "aihand.config.ts";
79
+ var SELF_PATH = _url.fileURLToPath.call(void 0, _chunkZ2Y65YOYcjs.importMetaUrl);
80
+ var TS_EXTS = [".ts", ".tsx", ".js", ".jsx"];
81
+ var isTsFile = (p) => TS_EXTS.some((ext) => p.endsWith(ext));
82
+ async function loadConfig(root = _path.resolve.call(void 0, ".")) {
83
+ const configFile = _path.join.call(void 0, root, CONFIG_PATH);
84
+ if (!_fs.existsSync.call(void 0, configFile))
85
+ return DEFAULT_CONFIG;
86
+ const selfDir = _path.dirname.call(void 0, SELF_PATH);
87
+ const aihandIndex = _nullishCoalesce([_path.join.call(void 0, selfDir, "index.js"), _path.resolve.call(void 0, selfDir, "../index.ts")].find(_fs.existsSync), () => ( _path.join.call(void 0, selfDir, "index.js")));
88
+ const jiti = _jiti.createJiti.call(void 0, root, {
89
+ alias: { aihand: aihandIndex },
90
+ moduleCache: false,
91
+ fsCache: false
92
+ });
93
+ const mod = await jiti.import(configFile);
94
+ const raw = _nullishCoalesce(mod.default, () => ( mod));
95
+ return normalizeConfig(raw);
96
+ }
97
+ function normalizeConfig(raw) {
98
+ const read = resolveModule(raw.read, {});
99
+ const d = DEFAULT_CONFIG.read;
100
+ return {
101
+ read: {
102
+ enabled: read.enabled,
103
+ include: [...d.include, ..._nullishCoalesce(read.include, () => ( ["src/**"]))],
104
+ ignore: _nullishCoalesce(read.ignore, () => ( d.ignore)),
105
+ fileDetailLevel: { ...d.fileDetailLevel, ...read.fileDetailLevel },
106
+ injectDetailLevel: _nullishCoalesce(read.injectDetailLevel, () => ( d.injectDetailLevel)),
107
+ injectTargetFiles: _nullishCoalesce(read.injectTargetFiles, () => ( d.injectTargetFiles)),
108
+ injectDevToolInstructions: _nullishCoalesce(read.injectDevToolInstructions, () => ( d.injectDevToolInstructions)),
109
+ injectCodemap: _nullishCoalesce(read.injectCodemap, () => ( d.injectCodemap)),
110
+ showNoParamFn: _nullishCoalesce(read.showNoParamFn, () => ( d.showNoParamFn)),
111
+ treeFoldThreshold: _nullishCoalesce(read.treeFoldThreshold, () => ( d.treeFoldThreshold)),
112
+ maxTokens: typeof read.maxTokens === "number" ? read.maxTokens : d.maxTokens
113
+ },
114
+ refactor: { enabled: raw.refactor !== false },
115
+ runtime: (() => {
116
+ const r = resolveModule(raw.runtime, {});
117
+ return { enabled: r.enabled, storeMarker: _nullishCoalesce(r.storeMarker, () => ( "_loading")) };
118
+ })()
119
+ };
120
+ }
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+ exports.DEFAULT_CONFIG = DEFAULT_CONFIG; exports.defineConfig = defineConfig; exports.configTemplate = configTemplate; exports.CONFIG_PATH = CONFIG_PATH; exports.isTsFile = isTsFile; exports.loadConfig = loadConfig; exports.normalizeConfig = normalizeConfig;
@@ -0,0 +1,125 @@
1
+ import {
2
+ getLang
3
+ } from "./chunk-OWYK2IGV.js";
4
+
5
+ // src/read/graph.ts
6
+ import { readFileSync } from "fs";
7
+ import { Parser, Query } from "web-tree-sitter";
8
+ var DEFS_QUERY = `
9
+ (function_declaration name: (identifier) @name) @def
10
+ (generator_function_declaration name: (identifier) @name) @def
11
+ (class_declaration name: (type_identifier) @name) @def
12
+ (method_definition name: (property_identifier) @name) @def
13
+ (variable_declarator name: (identifier) @name value: (arrow_function)) @def
14
+ `;
15
+ var CALLS_QUERY = `
16
+ (call_expression function: (identifier) @callee)
17
+ (call_expression function: (member_expression property: (property_identifier) @callee))
18
+ (new_expression constructor: (identifier) @callee)
19
+ `;
20
+ function extractFileGraph(code, parser, defsQuery, callsQuery) {
21
+ const tree = parser.parse(code);
22
+ if (!tree)
23
+ return { defs: [], attributed: [] };
24
+ const defs = [];
25
+ for (const match of defsQuery.matches(tree.rootNode)) {
26
+ const defCapture = match.captures.find((c) => c.name === "def");
27
+ const nameCapture = match.captures.find((c) => c.name === "name");
28
+ if (!defCapture || !nameCapture)
29
+ continue;
30
+ const KIND_MAP = {
31
+ class_declaration: "class",
32
+ method_definition: "method"
33
+ };
34
+ const kind = KIND_MAP[defCapture.node.type] ?? "function";
35
+ defs.push({
36
+ name: nameCapture.node.text,
37
+ kind,
38
+ startLine: defCapture.node.startPosition.row,
39
+ endLine: defCapture.node.endPosition.row
40
+ });
41
+ }
42
+ const rawCalls = [];
43
+ for (const { node } of callsQuery.captures(tree.rootNode))
44
+ rawCalls.push({ callee: node.text, row: node.startPosition.row });
45
+ const attributed = [];
46
+ for (const call of rawCalls) {
47
+ let best = null;
48
+ let bestSize = Infinity;
49
+ for (const def of defs) {
50
+ if (call.row >= def.startLine && call.row <= def.endLine) {
51
+ const size = def.endLine - def.startLine;
52
+ if (size < bestSize) {
53
+ best = def;
54
+ bestSize = size;
55
+ }
56
+ }
57
+ }
58
+ if (best)
59
+ attributed.push({ callerDef: best, callee: call.callee });
60
+ }
61
+ tree.delete();
62
+ return { defs, attributed };
63
+ }
64
+ async function buildCallGraph(filePaths, contents) {
65
+ await Parser.init();
66
+ const parser = new Parser();
67
+ let lang = null;
68
+ for (const fp of filePaths) {
69
+ const ext = fp.slice(fp.lastIndexOf("."));
70
+ lang = await getLang(ext);
71
+ if (lang)
72
+ break;
73
+ }
74
+ if (!lang)
75
+ return { symbols: {}, out: {}, in: {} };
76
+ parser.setLanguage(lang);
77
+ const defsQuery = new Query(lang, DEFS_QUERY);
78
+ const callsQuery = new Query(lang, CALLS_QUERY);
79
+ const symbols = {};
80
+ const nameToUids = /* @__PURE__ */ new Map();
81
+ const fileGraphs = [];
82
+ for (const filePath of filePaths) {
83
+ let code;
84
+ if (contents?.has(filePath)) {
85
+ code = contents.get(filePath);
86
+ } else {
87
+ try {
88
+ code = readFileSync(filePath, "utf-8");
89
+ } catch {
90
+ continue;
91
+ }
92
+ }
93
+ const { defs, attributed } = extractFileGraph(code, parser, defsQuery, callsQuery);
94
+ for (const def of defs) {
95
+ const uid = `${filePath}::${def.name}:${def.startLine}`;
96
+ symbols[uid] = { uid, name: def.name, kind: def.kind, filePath, startLine: def.startLine, endLine: def.endLine };
97
+ if (!nameToUids.has(def.name))
98
+ nameToUids.set(def.name, []);
99
+ nameToUids.get(def.name).push(uid);
100
+ }
101
+ fileGraphs.push({ filePath, attributed });
102
+ }
103
+ const outSets = {};
104
+ const inSets = {};
105
+ for (const { filePath, attributed } of fileGraphs) {
106
+ for (const { callerDef, callee } of attributed) {
107
+ const callerUid = `${filePath}::${callerDef.name}:${callerDef.startLine}`;
108
+ for (const calleeUid of nameToUids.get(callee) ?? []) {
109
+ ;
110
+ (outSets[callerUid] ??= /* @__PURE__ */ new Set()).add(calleeUid);
111
+ (inSets[calleeUid] ??= /* @__PURE__ */ new Set()).add(callerUid);
112
+ }
113
+ }
114
+ }
115
+ const out = Object.fromEntries(Object.entries(outSets).map(([k, v]) => [k, [...v]]));
116
+ const inEdges = Object.fromEntries(Object.entries(inSets).map(([k, v]) => [k, [...v]]));
117
+ defsQuery.delete();
118
+ callsQuery.delete();
119
+ parser.delete();
120
+ return { symbols, out, in: inEdges };
121
+ }
122
+
123
+ export {
124
+ buildCallGraph
125
+ };