flavor-code 1.4.0-beta.1 → 1.4.0-beta.4

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 (159) hide show
  1. package/dist/agent/loop.d.ts +7 -2
  2. package/dist/{app-C5ZQUOKJ.js → app-UWIEIWBQ.js} +7 -7
  3. package/dist/{chunk-FOTMP4ZS.js → chunk-4RR5VVX4.js} +8 -2
  4. package/dist/{chunk-6MNOZ2TX.js → chunk-DCNUBQCH.js} +1376 -641
  5. package/dist/{chunk-ODRWR4KK.js → chunk-IQAZ42TM.js} +1 -1
  6. package/dist/{chunk-URUVFECH.js → chunk-KQMKY7C5.js} +8 -4
  7. package/dist/chunk-LKQTBL3R.js +107 -0
  8. package/dist/{chunk-3WCDZ6ZL.js → chunk-ZJCAQXA7.js} +2 -1
  9. package/dist/{claude-ink-LP32NSX5.js → claude-ink-ECZ6QF3P.js} +2 -2
  10. package/dist/cli-main.js +22 -15
  11. package/dist/cli.js +24 -5
  12. package/dist/config/schema.d.ts +4 -0
  13. package/dist/context/manager.d.ts +9 -0
  14. package/dist/desktop/main.js +1770 -949
  15. package/dist/desktop-renderer/assets/{index-CqRIr-hz.js → index-C0t1nGih.js} +2 -2
  16. package/dist/desktop-renderer/assets/{interactive-terminal-C4o7MNPO.js → interactive-terminal-BJqe96mZ.js} +1 -1
  17. package/dist/desktop-renderer/index.html +1 -1
  18. package/dist/git/service.d.ts +3 -3
  19. package/dist/git/tools.d.ts +1 -1
  20. package/dist/goal/classifier.d.ts +2 -0
  21. package/dist/goal/orchestrator.d.ts +18 -0
  22. package/dist/goal/planner.d.ts +2 -0
  23. package/dist/goal/store.d.ts +2 -0
  24. package/dist/goal/types.d.ts +8 -0
  25. package/dist/harness/journal.d.ts +2 -0
  26. package/dist/harness/local.d.ts +2 -0
  27. package/dist/init/project.d.ts +0 -6
  28. package/dist/jobs/registry.d.ts +7 -1
  29. package/dist/{load-V2UIYYQI.js → load-F4AYLCF6.js} +1 -1
  30. package/dist/loop/orchestrator.d.ts +18 -0
  31. package/dist/loop/types.d.ts +7 -0
  32. package/dist/production.d.ts +17 -0
  33. package/dist/sdk/index.js +5 -5
  34. package/dist/session/tree.d.ts +2 -0
  35. package/dist/tools/lsp.d.ts +13 -10
  36. package/dist/tools/runtime.d.ts +2 -1
  37. package/dist/tools/search.d.ts +1 -0
  38. package/dist/tools/types.d.ts +1 -1
  39. package/dist/ui/session.d.ts +9 -0
  40. package/dist/utils/abort.d.ts +4 -0
  41. package/dist/utils/execFileNoThrow.d.ts +1 -0
  42. package/dist/utils/heap.d.ts +58 -0
  43. package/dist/utils/memory-restart.d.ts +38 -3
  44. package/dist/utils/process-tree.d.ts +2 -0
  45. package/dist/utils/rotation-census.d.ts +36 -0
  46. package/package.json +2 -2
  47. package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +7 -7
  48. package/dist/astgraph/db.mjs +0 -237
  49. package/dist/astgraph/extract.mjs +0 -239
  50. package/dist/astgraph/flavor-plugin.json +0 -25
  51. package/dist/astgraph/grammars.mjs +0 -68
  52. package/dist/astgraph/index.js +0 -301
  53. package/dist/astgraph/indexer.mjs +0 -175
  54. package/dist/astgraph/query.mjs +0 -165
  55. package/dist/astgraph/resolve.mjs +0 -93
  56. package/dist/astgraph/vendor/tree-sitter-javascript.wasm +0 -0
  57. package/dist/astgraph/vendor/tree-sitter-tsx.wasm +0 -0
  58. package/dist/astgraph/vendor/tree-sitter-typescript.wasm +0 -0
  59. package/dist/astgraph/vendor/tree-sitter.js +0 -3980
  60. package/dist/astgraph/vendor/tree-sitter.wasm +0 -0
  61. package/dist/astgraph/vendor/zod/LICENSE +0 -21
  62. package/dist/astgraph/vendor/zod/index.js +0 -4
  63. package/dist/astgraph/vendor/zod/locales/index.js +0 -1
  64. package/dist/astgraph/vendor/zod/locales/package.json +0 -7
  65. package/dist/astgraph/vendor/zod/package.json +0 -135
  66. package/dist/astgraph/vendor/zod/v4/classic/checks.js +0 -1
  67. package/dist/astgraph/vendor/zod/v4/classic/coerce.js +0 -17
  68. package/dist/astgraph/vendor/zod/v4/classic/compat.js +0 -31
  69. package/dist/astgraph/vendor/zod/v4/classic/errors.js +0 -48
  70. package/dist/astgraph/vendor/zod/v4/classic/external.js +0 -20
  71. package/dist/astgraph/vendor/zod/v4/classic/from-json-schema.js +0 -599
  72. package/dist/astgraph/vendor/zod/v4/classic/index.js +0 -4
  73. package/dist/astgraph/vendor/zod/v4/classic/iso.js +0 -30
  74. package/dist/astgraph/vendor/zod/v4/classic/package.json +0 -7
  75. package/dist/astgraph/vendor/zod/v4/classic/parse.js +0 -15
  76. package/dist/astgraph/vendor/zod/v4/classic/schemas.js +0 -1395
  77. package/dist/astgraph/vendor/zod/v4/core/api.js +0 -1087
  78. package/dist/astgraph/vendor/zod/v4/core/checks.js +0 -575
  79. package/dist/astgraph/vendor/zod/v4/core/core.js +0 -78
  80. package/dist/astgraph/vendor/zod/v4/core/doc.js +0 -35
  81. package/dist/astgraph/vendor/zod/v4/core/errors.js +0 -185
  82. package/dist/astgraph/vendor/zod/v4/core/index.js +0 -16
  83. package/dist/astgraph/vendor/zod/v4/core/json-schema-generator.js +0 -95
  84. package/dist/astgraph/vendor/zod/v4/core/json-schema-processors.js +0 -601
  85. package/dist/astgraph/vendor/zod/v4/core/json-schema.js +0 -1
  86. package/dist/astgraph/vendor/zod/v4/core/package.json +0 -7
  87. package/dist/astgraph/vendor/zod/v4/core/parse.js +0 -93
  88. package/dist/astgraph/vendor/zod/v4/core/regexes.js +0 -139
  89. package/dist/astgraph/vendor/zod/v4/core/registries.js +0 -51
  90. package/dist/astgraph/vendor/zod/v4/core/schemas.js +0 -2239
  91. package/dist/astgraph/vendor/zod/v4/core/standard-schema.js +0 -1
  92. package/dist/astgraph/vendor/zod/v4/core/to-json-schema.js +0 -448
  93. package/dist/astgraph/vendor/zod/v4/core/util.js +0 -674
  94. package/dist/astgraph/vendor/zod/v4/core/versions.js +0 -5
  95. package/dist/astgraph/vendor/zod/v4/index.js +0 -3
  96. package/dist/astgraph/vendor/zod/v4/locales/ar.js +0 -106
  97. package/dist/astgraph/vendor/zod/v4/locales/az.js +0 -105
  98. package/dist/astgraph/vendor/zod/v4/locales/be.js +0 -156
  99. package/dist/astgraph/vendor/zod/v4/locales/bg.js +0 -120
  100. package/dist/astgraph/vendor/zod/v4/locales/ca.js +0 -107
  101. package/dist/astgraph/vendor/zod/v4/locales/cs.js +0 -111
  102. package/dist/astgraph/vendor/zod/v4/locales/da.js +0 -115
  103. package/dist/astgraph/vendor/zod/v4/locales/de.js +0 -108
  104. package/dist/astgraph/vendor/zod/v4/locales/el.js +0 -109
  105. package/dist/astgraph/vendor/zod/v4/locales/en.js +0 -113
  106. package/dist/astgraph/vendor/zod/v4/locales/eo.js +0 -109
  107. package/dist/astgraph/vendor/zod/v4/locales/es.js +0 -132
  108. package/dist/astgraph/vendor/zod/v4/locales/fa.js +0 -114
  109. package/dist/astgraph/vendor/zod/v4/locales/fi.js +0 -112
  110. package/dist/astgraph/vendor/zod/v4/locales/fr-CA.js +0 -107
  111. package/dist/astgraph/vendor/zod/v4/locales/fr.js +0 -125
  112. package/dist/astgraph/vendor/zod/v4/locales/he.js +0 -214
  113. package/dist/astgraph/vendor/zod/v4/locales/hr.js +0 -122
  114. package/dist/astgraph/vendor/zod/v4/locales/hu.js +0 -108
  115. package/dist/astgraph/vendor/zod/v4/locales/hy.js +0 -147
  116. package/dist/astgraph/vendor/zod/v4/locales/id.js +0 -106
  117. package/dist/astgraph/vendor/zod/v4/locales/index.js +0 -52
  118. package/dist/astgraph/vendor/zod/v4/locales/is.js +0 -109
  119. package/dist/astgraph/vendor/zod/v4/locales/it.js +0 -108
  120. package/dist/astgraph/vendor/zod/v4/locales/ja.js +0 -107
  121. package/dist/astgraph/vendor/zod/v4/locales/ka.js +0 -112
  122. package/dist/astgraph/vendor/zod/v4/locales/kh.js +0 -5
  123. package/dist/astgraph/vendor/zod/v4/locales/km.js +0 -110
  124. package/dist/astgraph/vendor/zod/v4/locales/ko.js +0 -111
  125. package/dist/astgraph/vendor/zod/v4/locales/lt.js +0 -203
  126. package/dist/astgraph/vendor/zod/v4/locales/mk.js +0 -109
  127. package/dist/astgraph/vendor/zod/v4/locales/ms.js +0 -107
  128. package/dist/astgraph/vendor/zod/v4/locales/nl.js +0 -110
  129. package/dist/astgraph/vendor/zod/v4/locales/no.js +0 -108
  130. package/dist/astgraph/vendor/zod/v4/locales/ota.js +0 -109
  131. package/dist/astgraph/vendor/zod/v4/locales/package.json +0 -7
  132. package/dist/astgraph/vendor/zod/v4/locales/pl.js +0 -109
  133. package/dist/astgraph/vendor/zod/v4/locales/ps.js +0 -114
  134. package/dist/astgraph/vendor/zod/v4/locales/pt.js +0 -108
  135. package/dist/astgraph/vendor/zod/v4/locales/ro.js +0 -119
  136. package/dist/astgraph/vendor/zod/v4/locales/ru.js +0 -156
  137. package/dist/astgraph/vendor/zod/v4/locales/sl.js +0 -109
  138. package/dist/astgraph/vendor/zod/v4/locales/sv.js +0 -110
  139. package/dist/astgraph/vendor/zod/v4/locales/ta.js +0 -110
  140. package/dist/astgraph/vendor/zod/v4/locales/th.js +0 -110
  141. package/dist/astgraph/vendor/zod/v4/locales/tr.js +0 -105
  142. package/dist/astgraph/vendor/zod/v4/locales/ua.js +0 -5
  143. package/dist/astgraph/vendor/zod/v4/locales/uk.js +0 -108
  144. package/dist/astgraph/vendor/zod/v4/locales/ur.js +0 -110
  145. package/dist/astgraph/vendor/zod/v4/locales/uz.js +0 -110
  146. package/dist/astgraph/vendor/zod/v4/locales/vi.js +0 -108
  147. package/dist/astgraph/vendor/zod/v4/locales/yo.js +0 -107
  148. package/dist/astgraph/vendor/zod/v4/locales/zh-CN.js +0 -109
  149. package/dist/astgraph/vendor/zod/v4/locales/zh-TW.js +0 -107
  150. package/dist/astgraph/vendor/zod/v4/mini/checks.js +0 -1
  151. package/dist/astgraph/vendor/zod/v4/mini/coerce.js +0 -22
  152. package/dist/astgraph/vendor/zod/v4/mini/external.js +0 -14
  153. package/dist/astgraph/vendor/zod/v4/mini/index.js +0 -3
  154. package/dist/astgraph/vendor/zod/v4/mini/iso.js +0 -34
  155. package/dist/astgraph/vendor/zod/v4/mini/package.json +0 -7
  156. package/dist/astgraph/vendor/zod/v4/mini/parse.js +0 -1
  157. package/dist/astgraph/vendor/zod/v4/mini/schemas.js +0 -961
  158. package/dist/astgraph/vendor/zod/v4/package.json +0 -7
  159. package/dist/chunk-CIVPKHF4.js +0 -53
@@ -0,0 +1,36 @@
1
+ import type { HeapReading } from "./heap.js";
2
+ /**
3
+ * Rotation forensics. Every heap rotation leaves a retention census: each
4
+ * major in-memory holder reports its retained size, so the next recurrence
5
+ * names its own grower instead of restarting with zero evidence. A full
6
+ * .heapsnapshot accompanies rotations whose verified live set is near the
7
+ * hard limit (or every rotation under FLAVOR_HEAP_SNAPSHOT_ON_ROTATE=1).
8
+ */
9
+ export interface RotationCensusHolder {
10
+ entries?: number;
11
+ chars?: number;
12
+ bytes?: number;
13
+ }
14
+ export interface RotationCensus {
15
+ at: string;
16
+ reason: string;
17
+ sessionId: string;
18
+ heap: {
19
+ heapUsedMb: number;
20
+ heapLimitMb: number;
21
+ rawRatio: number;
22
+ verifiedRatio?: number;
23
+ gcVerified: boolean;
24
+ rssMb: number;
25
+ externalMb: number;
26
+ arrayBuffersMb: number;
27
+ memoryLimitMb?: number;
28
+ rssRatio?: number;
29
+ };
30
+ holders: Record<string, RotationCensusHolder>;
31
+ }
32
+ export declare function censusHeapBlock(reading: HeapReading): RotationCensus["heap"];
33
+ /** Best-effort: a failed census must never block the rotation itself. */
34
+ export declare function writeRotationCensus(workspace: string, census: RotationCensus): string | undefined;
35
+ /** Best-effort heap snapshot; synchronous and slow on large heaps by nature. */
36
+ export declare function writeRotationHeapSnapshot(workspace: string): string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flavor-code",
3
- "version": "1.4.0-beta.1",
3
+ "version": "1.4.0-beta.4",
4
4
  "author": "YachuanWzh",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -31,7 +31,7 @@
31
31
  "scripts": {
32
32
  "build": "npm run build:icons && npm run build:cli && npm run build:desktop",
33
33
  "build:icons": "node scripts/build-icons.mjs",
34
- "build:cli": "tsup && tsc -p tsconfig.sdk.json && node -e \"require('fs').cpSync('src/init/astgraph','dist/astgraph',{recursive:true})\"",
34
+ "build:cli": "tsup && tsc -p tsconfig.sdk.json",
35
35
  "build:desktop": "npm run build:desktop:main && npm run build:desktop:renderer",
36
36
  "build:desktop:main": "tsup --config tsup.desktop.config.ts",
37
37
  "build:desktop:renderer": "vite build --config vite.desktop.config.ts",
@@ -405,7 +405,7 @@ sequenceDiagram
405
405
  U->>CLI: 输入 prompt
406
406
  CLI->>L: submit(prompt, signal)
407
407
 
408
- loop 每轮迭代(最多 80 轮)
408
+ loop 每轮迭代(最多 300 轮)
409
409
  L->>C: prepareForModelCall()
410
410
  C->>C: 微压缩(清理旧工具输出)
411
411
  alt 仍超阈值
@@ -560,13 +560,13 @@ FlavorConfigSchema = {
560
560
  },
561
561
  language: "zh-CN", // BCP47 语言标签
562
562
  maxIterations: {
563
- main: 10-500, // 主 Agent 迭代上限(默认 80)
564
- subagent: 10-200, // 子 Agent 迭代上限(默认 40)
563
+ main: 10-500, // 主 Agent 迭代上限(默认 300)
564
+ subagent: 10-200, // 子 Agent 迭代上限(默认 100)
565
565
  softLimitFactor: 0.8, // 软限制比例
566
- extendBy: 20 // 扩展步数
566
+ extendBy: 50 // 扩展步数
567
567
  },
568
568
  loop: {
569
- maxCycles: 20, // 每次用户授权增加的 cycle 数
569
+ maxCycles: 100, // 每次用户授权增加的 cycle 数
570
570
  maxTokens: 500_000, // 每次用户授权增加的模型 token 数
571
571
  isolation: "auto" // 只读用当前目录;改代码自动用 worktree
572
572
  },
@@ -749,8 +749,8 @@ flowchart TD
749
749
  maxTokens = 5,000,000
750
750
  5,000,000 → 确认 → 10,000,000 → 确认 → 15,000,000 → ...
751
751
 
752
- maxCycles = 20
753
- 20 → 确认 → 40 → 确认 → 60 → ...
752
+ maxCycles = 100
753
+ 100 → 确认 → 200 → 确认 → 300 → ...
754
754
  ```
755
755
 
756
756
  token 统计包括 worker 的所有模型调用以及上下文压缩模型调用;本地 Shell 验证命令不计 token。用户拒绝产生 `budget_exhausted`;非交互模式无法确认时产生 `needs_human`。预算批准记录维度、旧/新 checkpoint、当时累计用量和时间戳。
@@ -1,237 +0,0 @@
1
- // astgraph graph database — SQLite-backed node/edge graph (codegraph-inspired).
2
- // Uses node:sqlite with WAL mode so concurrent readers never block on a writer.
3
-
4
- import { mkdirSync } from "node:fs";
5
- import { dirname } from "node:path";
6
- import { DatabaseSync } from "node:sqlite";
7
-
8
- const SCHEMA = `
9
- CREATE TABLE IF NOT EXISTS schema_versions (
10
- version INTEGER PRIMARY KEY,
11
- applied_at INTEGER NOT NULL,
12
- description TEXT
13
- );
14
-
15
- CREATE TABLE IF NOT EXISTS files (
16
- path TEXT PRIMARY KEY,
17
- content_hash TEXT NOT NULL,
18
- language TEXT NOT NULL,
19
- size INTEGER NOT NULL,
20
- modified_at INTEGER NOT NULL DEFAULT 0,
21
- indexed_at INTEGER NOT NULL DEFAULT 0,
22
- node_count INTEGER NOT NULL DEFAULT 0
23
- );
24
-
25
- CREATE TABLE IF NOT EXISTS nodes (
26
- id TEXT PRIMARY KEY,
27
- kind TEXT NOT NULL,
28
- name TEXT NOT NULL,
29
- qualified_name TEXT NOT NULL,
30
- file_path TEXT NOT NULL,
31
- language TEXT NOT NULL,
32
- start_line INTEGER NOT NULL,
33
- end_line INTEGER NOT NULL,
34
- signature TEXT,
35
- docstring TEXT,
36
- is_exported INTEGER NOT NULL DEFAULT 0,
37
- is_async INTEGER NOT NULL DEFAULT 0
38
- );
39
-
40
- CREATE TABLE IF NOT EXISTS edges (
41
- id INTEGER PRIMARY KEY AUTOINCREMENT,
42
- source TEXT NOT NULL,
43
- target TEXT NOT NULL,
44
- kind TEXT NOT NULL,
45
- line INTEGER,
46
- col INTEGER,
47
- FOREIGN KEY (source) REFERENCES nodes(id) ON DELETE CASCADE,
48
- FOREIGN KEY (target) REFERENCES nodes(id) ON DELETE CASCADE
49
- );
50
-
51
- CREATE TABLE IF NOT EXISTS unresolved_refs (
52
- id INTEGER PRIMARY KEY AUTOINCREMENT,
53
- from_node_id TEXT,
54
- reference_name TEXT NOT NULL,
55
- reference_kind TEXT NOT NULL,
56
- line INTEGER NOT NULL,
57
- col INTEGER NOT NULL,
58
- file_path TEXT NOT NULL DEFAULT '',
59
- module_specifier TEXT,
60
- status TEXT NOT NULL DEFAULT 'pending',
61
- FOREIGN KEY (from_node_id) REFERENCES nodes(id) ON DELETE CASCADE
62
- );
63
-
64
- CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
65
- CREATE INDEX IF NOT EXISTS idx_nodes_lower_name ON nodes(lower(name));
66
- CREATE INDEX IF NOT EXISTS idx_nodes_file_path ON nodes(file_path);
67
- CREATE INDEX IF NOT EXISTS idx_nodes_kind ON nodes(kind);
68
- CREATE INDEX IF NOT EXISTS idx_edges_source_kind ON edges(source, kind);
69
- CREATE INDEX IF NOT EXISTS idx_edges_target_kind ON edges(target, kind);
70
- CREATE UNIQUE INDEX IF NOT EXISTS idx_edges_identity
71
- ON edges(source, target, kind, IFNULL(line, -1), IFNULL(col, -1));
72
- CREATE INDEX IF NOT EXISTS idx_unresolved_from ON unresolved_refs(from_node_id);
73
- CREATE INDEX IF NOT EXISTS idx_unresolved_name ON unresolved_refs(reference_name);
74
-
75
- CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
76
- name,
77
- qualified_name,
78
- signature,
79
- docstring,
80
- content='nodes',
81
- content_rowid='rowid'
82
- );
83
-
84
- CREATE TRIGGER IF NOT EXISTS nodes_ai AFTER INSERT ON nodes BEGIN
85
- INSERT INTO nodes_fts(rowid, name, qualified_name, signature, docstring)
86
- VALUES (NEW.rowid, NEW.name, NEW.qualified_name, NEW.signature, NEW.docstring);
87
- END;
88
-
89
- CREATE TRIGGER IF NOT EXISTS nodes_ad AFTER DELETE ON nodes BEGIN
90
- INSERT INTO nodes_fts(nodes_fts, rowid, name, qualified_name, signature, docstring)
91
- VALUES ('delete', OLD.rowid, OLD.name, OLD.qualified_name, OLD.signature, OLD.docstring);
92
- END;
93
-
94
- CREATE TRIGGER IF NOT EXISTS nodes_au AFTER UPDATE ON nodes BEGIN
95
- INSERT INTO nodes_fts(nodes_fts, rowid, name, qualified_name, signature, docstring)
96
- VALUES ('delete', OLD.rowid, OLD.name, OLD.qualified_name, OLD.signature, OLD.docstring);
97
- INSERT INTO nodes_fts(rowid, name, qualified_name, signature, docstring)
98
- VALUES (NEW.rowid, NEW.name, NEW.qualified_name, NEW.signature, NEW.docstring);
99
- END;
100
-
101
- CREATE TABLE IF NOT EXISTS project_metadata (
102
- key TEXT PRIMARY KEY,
103
- value TEXT NOT NULL,
104
- updated_at INTEGER NOT NULL
105
- );
106
- `;
107
-
108
- /** Open (creating when absent) the graph database at `path`. Returns a DatabaseSync. */
109
- export function openDb(path) {
110
- if (path !== ":memory:") mkdirSync(dirname(path), { recursive: true });
111
- const db = new DatabaseSync(path);
112
- db.exec("PRAGMA journal_mode = WAL");
113
- db.exec("PRAGMA foreign_keys = ON");
114
- db.exec("PRAGMA synchronous = NORMAL");
115
- db.exec(SCHEMA);
116
- const version = db.prepare("SELECT COUNT(*) AS count FROM schema_versions").get();
117
- if (version.count === 0) {
118
- db.prepare("INSERT INTO schema_versions(version, applied_at, description) VALUES (?, ?, ?)")
119
- .run(1, Date.now(), "Initial astgraph schema");
120
- }
121
- return db;
122
- }
123
-
124
- /** Insert or replace the tracking record for one source file. */
125
- export function upsertFileRecord(db, record) {
126
- db.prepare(`
127
- INSERT INTO files(path, content_hash, language, size, modified_at, indexed_at, node_count)
128
- VALUES (?, ?, ?, ?, ?, ?, ?)
129
- ON CONFLICT(path) DO UPDATE SET
130
- content_hash = excluded.content_hash,
131
- language = excluded.language,
132
- size = excluded.size,
133
- modified_at = excluded.modified_at,
134
- indexed_at = excluded.indexed_at,
135
- node_count = excluded.node_count
136
- `).run(
137
- record.path, record.contentHash, record.language, record.size,
138
- record.modifiedAt ?? Date.now(), record.indexedAt ?? Date.now(), record.nodeCount ?? 0,
139
- );
140
- }
141
-
142
- /** Read the tracking record for one source file, or undefined when absent. */
143
- export function getFileRecord(db, path) {
144
- const row = db.prepare("SELECT * FROM files WHERE path = ?").get(path);
145
- if (row === undefined) return undefined;
146
- return {
147
- path: row.path, contentHash: row.content_hash, language: row.language,
148
- size: row.size, modifiedAt: row.modified_at, indexedAt: row.indexed_at,
149
- nodeCount: row.node_count,
150
- };
151
- }
152
-
153
- /** Delete a file record; nodes cascade (and with them edges + refs + FTS rows). */
154
- export function deleteFileRecord(db, path) {
155
- db.prepare("DELETE FROM nodes WHERE file_path = ?").run(path);
156
- db.prepare("DELETE FROM files WHERE path = ?").run(path);
157
- }
158
-
159
- /**
160
- * Replace every node belonging to a file. Runs inside one transaction; the FTS
161
- * triggers keep nodes_fts in sync.
162
- */
163
- export function replaceNodes(db, filePath, nodes) {
164
- const insert = db.prepare(`
165
- INSERT OR REPLACE INTO nodes(
166
- id, kind, name, qualified_name, file_path, language,
167
- start_line, end_line, signature, docstring, is_exported, is_async
168
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
169
- `);
170
- const clear = db.prepare("DELETE FROM nodes WHERE file_path = ?");
171
- db.exec("BEGIN");
172
- try {
173
- clear.run(filePath);
174
- for (const node of nodes) {
175
- insert.run(
176
- node.id, node.kind, node.name, node.qualifiedName, node.filePath, node.language,
177
- node.startLine, node.endLine, node.signature ?? null, node.docstring ?? null,
178
- node.isExported ? 1 : 0, node.isAsync ? 1 : 0,
179
- );
180
- }
181
- db.exec("COMMIT");
182
- } catch (error) {
183
- db.exec("ROLLBACK");
184
- throw error;
185
- }
186
- }
187
-
188
- /** Insert one edge; duplicate (source, target, kind, line, col) rows are ignored. */
189
- export function insertEdge(db, edge) {
190
- db.prepare(`
191
- INSERT OR IGNORE INTO edges(source, target, kind, line, col) VALUES (?, ?, ?, ?, ?)
192
- `).run(edge.source, edge.target, edge.kind, edge.line ?? null, edge.col ?? null);
193
- }
194
-
195
- /** Delete every edge whose source OR target belongs to a file. */
196
- export function deleteEdgesForFile(db, filePath) {
197
- db.prepare(`
198
- DELETE FROM edges WHERE source IN (SELECT id FROM nodes WHERE file_path = ?)
199
- OR target IN (SELECT id FROM nodes WHERE file_path = ?)
200
- `).run(filePath, filePath);
201
- }
202
-
203
- /** Insert one unresolved cross-file reference (status 'pending'). */
204
- export function insertUnresolvedRef(db, ref) {
205
- db.prepare(`
206
- INSERT INTO unresolved_refs(from_node_id, reference_name, reference_kind, line, col, file_path, module_specifier, status)
207
- VALUES (?, ?, ?, ?, ?, ?, ?, 'pending')
208
- `).run(ref.fromNodeId, ref.referenceName, ref.referenceKind, ref.line, ref.col, ref.filePath, ref.moduleSpecifier ?? null);
209
- }
210
-
211
- /** Delete every unresolved reference originating from a file. */
212
- export function deleteUnresolvedRefsForFile(db, filePath) {
213
- db.prepare("DELETE FROM unresolved_refs WHERE file_path = ?").run(filePath);
214
- }
215
-
216
- /** Summary counters used by /ast status. */
217
- export function stats(db) {
218
- const files = db.prepare("SELECT COUNT(*) AS count FROM files").get().count;
219
- const nodes = db.prepare("SELECT COUNT(*) AS count FROM nodes").get().count;
220
- const edges = db.prepare("SELECT COUNT(*) AS count FROM edges").get().count;
221
- const unresolved = db.prepare("SELECT COUNT(*) AS count FROM unresolved_refs WHERE status = 'pending'").get().count;
222
- return { files, nodes, edges, unresolved };
223
- }
224
-
225
- /** Store a provenance metadata value. */
226
- export function setMetadata(db, key, value) {
227
- db.prepare(`
228
- INSERT INTO project_metadata(key, value, updated_at) VALUES (?, ?, ?)
229
- ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
230
- `).run(key, value, Date.now());
231
- }
232
-
233
- /** Read a provenance metadata value, or undefined when absent. */
234
- export function getMetadata(db, key) {
235
- const row = db.prepare("SELECT value FROM project_metadata WHERE key = ?").get(key);
236
- return row?.value;
237
- }
@@ -1,239 +0,0 @@
1
- // astgraph TypeScript/JavaScript structural extractor.
2
- // Walks the tree-sitter syntax tree and produces:
3
- // nodes — functions, classes, methods, interfaces, type aliases, variables
4
- // edges — intra-file calls / extends / implements
5
- // refs — cross-file references (imports + unresolved calls/heritage) resolved later
6
- //
7
- // Lines are 1-based in all outputs so they match editor/Read tool coordinates.
8
-
9
- import { parseSource } from "./grammars.mjs";
10
-
11
- const DECLARATION_KINDS = new Map([
12
- ["function_declaration", "function"],
13
- ["class_declaration", "class"],
14
- ["interface_declaration", "interface"],
15
- ["type_alias_declaration", "type"],
16
- ]);
17
-
18
- /** Extract the doc text from a `/** ... */` or `// ...` comment node. */
19
- function docstringFrom(commentNode) {
20
- if (commentNode === null || commentNode.type !== "comment") return undefined;
21
- const text = commentNode.text;
22
- if (text.startsWith("/**") || text.startsWith("/*!")) {
23
- return text
24
- .replace(/^\/\*+[!*]?/, "")
25
- .replace(/\*+\/$/, "")
26
- .split("\n")
27
- .map((line) => line.replace(/^\s*\*+\s?/, "").trim())
28
- .filter((line) => line.length > 0)
29
- .join("\n") || undefined;
30
- }
31
- if (text.startsWith("//")) return text.replace(/^\/\/+\s?/, "").trim() || undefined;
32
- return undefined;
33
- }
34
-
35
- function firstLine(text, limit = 240) {
36
- const line = text.split("\n")[0].trim();
37
- return line.length > limit ? `${line.slice(0, limit)}…` : line;
38
- }
39
-
40
- function signatureOf(node) {
41
- const text = node.text;
42
- const brace = text.indexOf("{");
43
- const head = brace >= 0 ? text.slice(0, brace) : text.split("\n")[0];
44
- return firstLine(head.replace(/\s+/g, " "));
45
- }
46
-
47
- function nameOf(node) {
48
- const nameNode = node.childForFieldName("name");
49
- return nameNode === null ? undefined : nameNode.text;
50
- }
51
-
52
- function isAsync(node) {
53
- return node.children.some((child) => child.type === "async");
54
- }
55
-
56
- export async function extract(source, filePath, language) {
57
- const tree = await parseSource(source, language);
58
-
59
- /** @type {Array<object>} */
60
- const nodes = [];
61
- /** @type {Array<object>} */
62
- const edges = [];
63
- /** @type {Array<object>} */
64
- const refs = [];
65
- /** Map local symbol name → node id (methods registered as name only for call matching). */
66
- const localNames = new Map();
67
- /** Map imported binding name → module specifier. */
68
- const imports = new Map();
69
- /** Map declaration AST node id → node record, for enclosing-scope lookup.
70
- * Keyed by numeric node.id because web-tree-sitter returns fresh node object
71
- * instances on every navigation (parent/childForFieldName), so object identity
72
- * cannot be used as a Map key. */
73
- const scopeByNodeId = new Map();
74
-
75
- const makeNode = (astNode, kind, name, qualifiedName, exported, extra = {}) => {
76
- // The docstring lives on the top-level statement's preceding sibling (comments
77
- // attach to program/export_statement, not to the inner declaration node).
78
- const docSource = extra.docSource ?? astNode;
79
- const record = {
80
- id: `${filePath}#${qualifiedName}`,
81
- kind,
82
- name,
83
- qualifiedName,
84
- filePath,
85
- language,
86
- startLine: astNode.startPosition.row + 1,
87
- endLine: astNode.endPosition.row + 1,
88
- signature: signatureOf(astNode),
89
- docstring: docstringFrom(docSource.previousSibling),
90
- isExported: exported,
91
- isAsync: extra.isAsync ?? false,
92
- ...extra,
93
- };
94
- delete record.docSource;
95
- nodes.push(record);
96
- scopeByNodeId.set(astNode.id, record);
97
- if (!localNames.has(name)) localNames.set(name, record.id);
98
- return record;
99
- };
100
-
101
- const collectDeclaration = (statement, exported) => {
102
- const inner = statement.type === "export_statement" ? statement.namedChildren.find((child) => DECLARATION_KINDS.has(child.type) || child.type === "lexical_declaration" || child.type === "abstract_class_declaration") : statement;
103
- const target = inner ?? statement;
104
- const kind = DECLARATION_KINDS.get(target.type);
105
- if (kind !== undefined) {
106
- const name = nameOf(target);
107
- if (name !== undefined) {
108
- // Comments attach to the top-level statement (export_statement / declaration),
109
- // so the docstring is read from the statement's preceding sibling.
110
- makeNode(target, kind, name, name, exported, {
111
- ...(kind === "function" ? { isAsync: isAsync(target) } : {}),
112
- docSource: statement,
113
- });
114
- if (target.type === "class_declaration" || target.type === "abstract_class_declaration") collectClassMembers(target, exported);
115
- collectHeritage(target, exported);
116
- }
117
- return;
118
- }
119
- if (target.type === "lexical_declaration") {
120
- for (const declarator of target.namedChildren.filter((child) => child.type === "variable_declarator")) {
121
- const name = declarator.childForFieldName("name")?.text;
122
- const value = declarator.childForFieldName("value");
123
- if (name === undefined || value === null) continue;
124
- const isFunction = value.type === "arrow_function" || value.type === "function_expression" || value.type === "function";
125
- makeNode(declarator, isFunction ? "function" : "variable", name, name, exported, {
126
- isAsync: isFunction && value.children.some((child) => child.type === "async"),
127
- docSource: statement,
128
- });
129
- }
130
- }
131
- };
132
-
133
- const collectClassMembers = (classNode, classExported) => {
134
- const className = nameOf(classNode);
135
- const body = classNode.childForFieldName("body");
136
- if (body === null) return;
137
- for (const member of body.namedChildren) {
138
- if (member.type !== "method_definition") continue;
139
- const name = member.childForFieldName("name")?.text;
140
- if (name === undefined) continue;
141
- const qualified = `${className}.${name}`;
142
- makeNode(member, "method", name, qualified, classExported, { isAsync: isAsync(member) });
143
- }
144
- };
145
-
146
- const collectHeritage = (classNode, _exported) => {
147
- const heritage = classNode.namedChildren.find((child) => child.type === "class_heritage");
148
- if (heritage === undefined) return;
149
- const sourceName = nameOf(classNode);
150
- if (sourceName === undefined) return;
151
- const sourceId = `${filePath}#${sourceName}`;
152
- for (const clause of heritage.namedChildren) {
153
- const kind = clause.type === "extends_clause" ? "extends" : clause.type === "implements_clause" ? "implements" : undefined;
154
- if (kind === undefined) continue;
155
- for (const typeNode of clause.namedChildren.filter((child) => child.type === "type_identifier" || child.type === "identifier")) {
156
- const typeName = typeNode.text;
157
- if (localNames.has(typeName)) {
158
- edges.push({ source: sourceId, target: localNames.get(typeName), kind, line: typeNode.startPosition.row + 1, col: typeNode.startPosition.column });
159
- } else {
160
- refs.push({
161
- fromNodeId: sourceId, referenceName: typeName, referenceKind: kind,
162
- line: typeNode.startPosition.row + 1, col: typeNode.startPosition.column,
163
- filePath, moduleSpecifier: undefined,
164
- });
165
- }
166
- }
167
- }
168
- };
169
-
170
- // Pass 1 — top-level declarations and imports.
171
- for (const statement of tree.rootNode.namedChildren) {
172
- if (statement.type === "import_statement") {
173
- const sourceNode = statement.childForFieldName("source");
174
- const moduleSpecifier = sourceNode === null ? undefined : sourceNode.text.replace(/^["']|["']$/g, "");
175
- for (const clause of statement.namedChildren.filter((child) => child.type === "import_clause")) {
176
- for (const named of clause.namedChildren.filter((child) => child.type === "named_imports")) {
177
- for (const specifier of named.namedChildren.filter((child) => child.type === "import_specifier")) {
178
- const nameNode = specifier.childForFieldName("name");
179
- if (nameNode === null) continue;
180
- const importedName = nameNode.text;
181
- imports.set(importedName, moduleSpecifier);
182
- refs.push({
183
- fromNodeId: null, referenceName: importedName, referenceKind: "import",
184
- line: nameNode.startPosition.row + 1, col: nameNode.startPosition.column,
185
- filePath, moduleSpecifier,
186
- });
187
- }
188
- }
189
- const defaultName = clause.namedChildren.find((child) => child.type === "identifier");
190
- if (defaultName !== undefined) {
191
- imports.set(defaultName.text, moduleSpecifier);
192
- refs.push({
193
- fromNodeId: null, referenceName: defaultName.text, referenceKind: "import",
194
- line: defaultName.startPosition.row + 1, col: defaultName.startPosition.column,
195
- filePath, moduleSpecifier,
196
- });
197
- }
198
- }
199
- continue;
200
- }
201
- collectDeclaration(statement, statement.type === "export_statement");
202
- }
203
-
204
- // Pass 2 — call expressions anywhere in the file.
205
- const enclosingScope = (node) => {
206
- let cursor = node.parent;
207
- while (cursor !== null) {
208
- const record = scopeByNodeId.get(cursor.id);
209
- if (record !== undefined) return record;
210
- cursor = cursor.parent;
211
- }
212
- return undefined;
213
- };
214
-
215
- const visitCalls = (node) => {
216
- if (node.type === "call_expression") {
217
- const callee = node.childForFieldName("function");
218
- if (callee !== null && callee.type === "identifier") {
219
- const name = callee.text;
220
- const scope = enclosingScope(node);
221
- if (scope !== undefined) {
222
- if (localNames.has(name) && localNames.get(name) !== scope.id) {
223
- edges.push({ source: scope.id, target: localNames.get(name), kind: "calls", line: callee.startPosition.row + 1, col: callee.startPosition.column });
224
- } else if (!localNames.has(name)) {
225
- refs.push({
226
- fromNodeId: scope.id, referenceName: name, referenceKind: "call",
227
- line: callee.startPosition.row + 1, col: callee.startPosition.column,
228
- filePath, moduleSpecifier: imports.get(name),
229
- });
230
- }
231
- }
232
- }
233
- }
234
- for (const child of node.namedChildren) visitCalls(child);
235
- };
236
- visitCalls(tree.rootNode);
237
-
238
- return { nodes, edges, refs, imports: Object.fromEntries(imports) };
239
- }
@@ -1,25 +0,0 @@
1
- {
2
- "name": "astgraph",
3
- "version": "0.1.0",
4
- "apiVersion": "1",
5
- "main": "index.js",
6
- "permissions": [],
7
- "contributes": {
8
- "commands": [
9
- { "name": "ast" }
10
- ],
11
- "tools": [
12
- { "name": "ast_search" },
13
- { "name": "ast_callers" },
14
- { "name": "ast_callees" },
15
- { "name": "ast_impact" },
16
- { "name": "ast_context" }
17
- ],
18
- "skillRoots": [],
19
- "modelAdapters": [],
20
- "hooks": [
21
- { "name": "SessionStart" },
22
- { "name": "PostToolUse" }
23
- ]
24
- }
25
- }
@@ -1,68 +0,0 @@
1
- // astgraph grammar loading — web-tree-sitter WASM runtime, vendored under ./vendor.
2
- // Resolves the vendor directory relative to this module so it works both from the
3
- // source tree (src/init/astgraph) and the copied plugin root (.flavor/plugins/astgraph).
4
-
5
- import { dirname, join } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
-
8
- import { Language, Parser } from "./vendor/tree-sitter.js";
9
-
10
- const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
11
- export const VENDOR_DIR = join(MODULE_DIR, "vendor");
12
-
13
- const GRAMMAR_FILES = {
14
- typescript: "tree-sitter-typescript.wasm",
15
- tsx: "tree-sitter-tsx.wasm",
16
- javascript: "tree-sitter-javascript.wasm",
17
- jsx: "tree-sitter-javascript.wasm",
18
- };
19
-
20
- export const SUPPORTED_LANGUAGES = Object.keys(GRAMMAR_FILES);
21
-
22
- export function languageForFile(path) {
23
- const lower = path.toLowerCase();
24
- if (lower.endsWith(".tsx")) return "tsx";
25
- if (lower.endsWith(".ts") || lower.endsWith(".mts") || lower.endsWith(".cts")) return "typescript";
26
- if (lower.endsWith(".jsx")) return "jsx";
27
- if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs")) return "javascript";
28
- return undefined;
29
- }
30
-
31
- let initialized = false;
32
- let initPromise;
33
- const languageCache = new Map();
34
- const parserCache = new Map();
35
-
36
- /** Initialize the WASM runtime once. Safe to call repeatedly. */
37
- export async function initGrammars() {
38
- if (initialized) return;
39
- initPromise ??= Parser.init({ locateFile: (name) => join(VENDOR_DIR, name) }).then(() => {
40
- initialized = true;
41
- });
42
- await initPromise;
43
- }
44
-
45
- /** Load (and cache) a grammar for one of the supported languages. */
46
- export async function loadLanguage(language) {
47
- const file = GRAMMAR_FILES[language];
48
- if (file === undefined) throw new Error(`astgraph: unsupported language "${language}"`);
49
- await initGrammars();
50
- let languageInstance = languageCache.get(language);
51
- if (languageInstance === undefined) {
52
- languageInstance = await Language.load(join(VENDOR_DIR, file));
53
- languageCache.set(language, languageInstance);
54
- }
55
- return languageInstance;
56
- }
57
-
58
- /** Parse source text and return the syntax tree. */
59
- export async function parseSource(source, language) {
60
- const languageInstance = await loadLanguage(language);
61
- let parser = parserCache.get(language);
62
- if (parser === undefined) {
63
- parser = new Parser();
64
- parser.setLanguage(languageInstance);
65
- parserCache.set(language, parser);
66
- }
67
- return parser.parse(source);
68
- }