@vpxa/kb 0.1.1 → 0.1.2

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 (136) hide show
  1. package/package.json +1 -1
  2. package/packages/analyzers/dist/blast-radius-analyzer.js +13 -114
  3. package/packages/analyzers/dist/dependency-analyzer.js +11 -425
  4. package/packages/analyzers/dist/diagram-generator.js +4 -86
  5. package/packages/analyzers/dist/entry-point-analyzer.js +5 -239
  6. package/packages/analyzers/dist/index.js +1 -23
  7. package/packages/analyzers/dist/knowledge-producer.js +24 -113
  8. package/packages/analyzers/dist/pattern-analyzer.js +5 -359
  9. package/packages/analyzers/dist/regex-call-graph.js +1 -428
  10. package/packages/analyzers/dist/structure-analyzer.js +4 -258
  11. package/packages/analyzers/dist/symbol-analyzer.js +13 -442
  12. package/packages/analyzers/dist/ts-call-graph.js +1 -160
  13. package/packages/analyzers/dist/types.js +0 -1
  14. package/packages/chunker/dist/call-graph-extractor.js +1 -90
  15. package/packages/chunker/dist/chunker-factory.js +1 -36
  16. package/packages/chunker/dist/chunker.interface.js +0 -1
  17. package/packages/chunker/dist/code-chunker.js +14 -134
  18. package/packages/chunker/dist/generic-chunker.js +5 -72
  19. package/packages/chunker/dist/index.js +1 -21
  20. package/packages/chunker/dist/markdown-chunker.js +7 -119
  21. package/packages/chunker/dist/treesitter-chunker.js +8 -234
  22. package/packages/cli/dist/commands/analyze.js +3 -112
  23. package/packages/cli/dist/commands/context-cmds.js +1 -155
  24. package/packages/cli/dist/commands/environment.js +2 -204
  25. package/packages/cli/dist/commands/execution.js +1 -137
  26. package/packages/cli/dist/commands/graph.js +7 -81
  27. package/packages/cli/dist/commands/init.js +9 -87
  28. package/packages/cli/dist/commands/knowledge.js +1 -139
  29. package/packages/cli/dist/commands/search.js +8 -267
  30. package/packages/cli/dist/commands/system.js +4 -241
  31. package/packages/cli/dist/commands/workspace.js +2 -388
  32. package/packages/cli/dist/context.js +1 -14
  33. package/packages/cli/dist/helpers.js +3 -458
  34. package/packages/cli/dist/index.js +3 -69
  35. package/packages/cli/dist/kb-init.js +1 -82
  36. package/packages/cli/dist/types.js +0 -1
  37. package/packages/core/dist/constants.js +1 -43
  38. package/packages/core/dist/content-detector.js +1 -79
  39. package/packages/core/dist/errors.js +1 -40
  40. package/packages/core/dist/index.js +1 -9
  41. package/packages/core/dist/logger.js +1 -34
  42. package/packages/core/dist/types.js +0 -1
  43. package/packages/embeddings/dist/embedder.interface.js +0 -1
  44. package/packages/embeddings/dist/index.js +1 -5
  45. package/packages/embeddings/dist/onnx-embedder.js +1 -82
  46. package/packages/indexer/dist/file-hasher.js +1 -13
  47. package/packages/indexer/dist/filesystem-crawler.js +1 -125
  48. package/packages/indexer/dist/graph-extractor.js +1 -111
  49. package/packages/indexer/dist/incremental-indexer.js +1 -278
  50. package/packages/indexer/dist/index.js +1 -14
  51. package/packages/server/dist/api.js +1 -9
  52. package/packages/server/dist/config.js +1 -75
  53. package/packages/server/dist/curated-manager.js +9 -356
  54. package/packages/server/dist/index.js +1 -134
  55. package/packages/server/dist/replay-interceptor.js +1 -38
  56. package/packages/server/dist/resources/resources.js +2 -40
  57. package/packages/server/dist/server.js +1 -247
  58. package/packages/server/dist/tools/analyze.tools.js +1 -288
  59. package/packages/server/dist/tools/forge.tools.js +11 -499
  60. package/packages/server/dist/tools/forget.tool.js +3 -39
  61. package/packages/server/dist/tools/graph.tool.js +5 -110
  62. package/packages/server/dist/tools/list.tool.js +5 -53
  63. package/packages/server/dist/tools/lookup.tool.js +8 -51
  64. package/packages/server/dist/tools/onboard.tool.js +2 -112
  65. package/packages/server/dist/tools/produce.tool.js +4 -74
  66. package/packages/server/dist/tools/read.tool.js +4 -47
  67. package/packages/server/dist/tools/reindex.tool.js +2 -70
  68. package/packages/server/dist/tools/remember.tool.js +3 -42
  69. package/packages/server/dist/tools/replay.tool.js +6 -88
  70. package/packages/server/dist/tools/search.tool.js +17 -327
  71. package/packages/server/dist/tools/status.tool.js +3 -68
  72. package/packages/server/dist/tools/toolkit.tools.js +20 -1673
  73. package/packages/server/dist/tools/update.tool.js +3 -39
  74. package/packages/server/dist/tools/utility.tools.js +19 -456
  75. package/packages/store/dist/graph-store.interface.js +0 -1
  76. package/packages/store/dist/index.js +1 -9
  77. package/packages/store/dist/lance-store.js +1 -258
  78. package/packages/store/dist/sqlite-graph-store.js +8 -309
  79. package/packages/store/dist/store-factory.js +1 -14
  80. package/packages/store/dist/store.interface.js +0 -1
  81. package/packages/tools/dist/batch.js +1 -45
  82. package/packages/tools/dist/changelog.js +2 -112
  83. package/packages/tools/dist/check.js +2 -59
  84. package/packages/tools/dist/checkpoint.js +2 -43
  85. package/packages/tools/dist/codemod.js +2 -69
  86. package/packages/tools/dist/compact.js +3 -60
  87. package/packages/tools/dist/data-transform.js +1 -124
  88. package/packages/tools/dist/dead-symbols.js +2 -71
  89. package/packages/tools/dist/delegate.js +3 -128
  90. package/packages/tools/dist/diff-parse.js +3 -153
  91. package/packages/tools/dist/digest.js +7 -242
  92. package/packages/tools/dist/encode.js +1 -46
  93. package/packages/tools/dist/env-info.js +1 -58
  94. package/packages/tools/dist/eval.js +3 -79
  95. package/packages/tools/dist/evidence-map.js +3 -203
  96. package/packages/tools/dist/file-summary.js +2 -106
  97. package/packages/tools/dist/file-walk.js +1 -75
  98. package/packages/tools/dist/find-examples.js +3 -48
  99. package/packages/tools/dist/find.js +1 -120
  100. package/packages/tools/dist/forge-classify.js +2 -319
  101. package/packages/tools/dist/forge-ground.js +1 -184
  102. package/packages/tools/dist/git-context.js +3 -46
  103. package/packages/tools/dist/graph-query.js +1 -194
  104. package/packages/tools/dist/health.js +1 -118
  105. package/packages/tools/dist/http-request.js +1 -58
  106. package/packages/tools/dist/index.js +1 -273
  107. package/packages/tools/dist/lane.js +7 -227
  108. package/packages/tools/dist/measure.js +2 -119
  109. package/packages/tools/dist/onboard.js +42 -1136
  110. package/packages/tools/dist/parse-output.js +2 -158
  111. package/packages/tools/dist/process-manager.js +1 -69
  112. package/packages/tools/dist/queue.js +2 -126
  113. package/packages/tools/dist/regex-test.js +1 -39
  114. package/packages/tools/dist/rename.js +2 -70
  115. package/packages/tools/dist/replay.js +6 -108
  116. package/packages/tools/dist/schema-validate.js +1 -141
  117. package/packages/tools/dist/scope-map.js +1 -72
  118. package/packages/tools/dist/snippet.js +1 -80
  119. package/packages/tools/dist/stash.js +2 -60
  120. package/packages/tools/dist/stratum-card.js +5 -238
  121. package/packages/tools/dist/symbol.js +3 -87
  122. package/packages/tools/dist/test-run.js +2 -55
  123. package/packages/tools/dist/text-utils.js +2 -31
  124. package/packages/tools/dist/time-utils.js +1 -135
  125. package/packages/tools/dist/trace.js +2 -114
  126. package/packages/tools/dist/truncation.js +10 -41
  127. package/packages/tools/dist/watch.js +1 -61
  128. package/packages/tools/dist/web-fetch.js +9 -244
  129. package/packages/tools/dist/web-search.js +1 -46
  130. package/packages/tools/dist/workset.js +2 -77
  131. package/packages/tui/dist/App.js +260 -52468
  132. package/packages/tui/dist/index.js +286 -54551
  133. package/packages/tui/dist/panels/CuratedPanel.js +211 -34291
  134. package/packages/tui/dist/panels/LogPanel.js +259 -51703
  135. package/packages/tui/dist/panels/SearchPanel.js +212 -34824
  136. package/packages/tui/dist/panels/StatusPanel.js +211 -34304
@@ -1,141 +1 @@
1
- function schemaValidate(options) {
2
- const errors = [];
3
- validateNode(options.data, options.schema, "$", errors);
4
- return { valid: errors.length === 0, errors };
5
- }
6
- function validateNode(data, schema, path, errors) {
7
- if ("type" in schema) {
8
- const expected = schema.type;
9
- if (!checkType(data, expected)) {
10
- errors.push({
11
- path,
12
- message: `Expected type "${expected}"`,
13
- expected,
14
- received: getType(data)
15
- });
16
- return;
17
- }
18
- }
19
- if ("enum" in schema) {
20
- const allowed = schema.enum;
21
- if (!allowed.some((v) => JSON.stringify(v) === JSON.stringify(data))) {
22
- errors.push({
23
- path,
24
- message: `Must be one of: ${JSON.stringify(allowed)}`,
25
- received: JSON.stringify(data)
26
- });
27
- }
28
- }
29
- if ("const" in schema) {
30
- if (JSON.stringify(data) !== JSON.stringify(schema.const)) {
31
- errors.push({
32
- path,
33
- message: `Must equal ${JSON.stringify(schema.const)}`,
34
- received: JSON.stringify(data)
35
- });
36
- }
37
- }
38
- if (typeof data === "string") {
39
- if ("minLength" in schema && data.length < schema.minLength) {
40
- errors.push({
41
- path,
42
- message: `String too short (min: ${schema.minLength})`,
43
- received: `length ${data.length}`
44
- });
45
- }
46
- if ("maxLength" in schema && data.length > schema.maxLength) {
47
- errors.push({
48
- path,
49
- message: `String too long (max: ${schema.maxLength})`,
50
- received: `length ${data.length}`
51
- });
52
- }
53
- if ("pattern" in schema && !new RegExp(schema.pattern).test(data)) {
54
- errors.push({ path, message: `Does not match pattern: ${schema.pattern}` });
55
- }
56
- }
57
- if (typeof data === "number") {
58
- if ("minimum" in schema && data < schema.minimum) {
59
- errors.push({ path, message: `Below minimum (${schema.minimum})`, received: String(data) });
60
- }
61
- if ("maximum" in schema && data > schema.maximum) {
62
- errors.push({ path, message: `Above maximum (${schema.maximum})`, received: String(data) });
63
- }
64
- }
65
- if (Array.isArray(data)) {
66
- if ("minItems" in schema && data.length < schema.minItems) {
67
- errors.push({
68
- path,
69
- message: `Too few items (min: ${schema.minItems})`,
70
- received: `length ${data.length}`
71
- });
72
- }
73
- if ("maxItems" in schema && data.length > schema.maxItems) {
74
- errors.push({
75
- path,
76
- message: `Too many items (max: ${schema.maxItems})`,
77
- received: `length ${data.length}`
78
- });
79
- }
80
- if ("items" in schema) {
81
- for (let i = 0; i < data.length; i++) {
82
- validateNode(data[i], schema.items, `${path}[${i}]`, errors);
83
- }
84
- }
85
- }
86
- if (data && typeof data === "object" && !Array.isArray(data)) {
87
- const obj = data;
88
- if ("required" in schema) {
89
- for (const key of schema.required) {
90
- if (!(key in obj)) {
91
- errors.push({ path: `${path}.${key}`, message: "Required property missing" });
92
- }
93
- }
94
- }
95
- if ("properties" in schema) {
96
- const props = schema.properties;
97
- for (const [key, propSchema] of Object.entries(props)) {
98
- if (key in obj) {
99
- validateNode(obj[key], propSchema, `${path}.${key}`, errors);
100
- }
101
- }
102
- }
103
- if ("additionalProperties" in schema && schema.additionalProperties === false) {
104
- const defined = Object.keys(schema.properties ?? {});
105
- for (const key of Object.keys(obj)) {
106
- if (!defined.includes(key)) {
107
- errors.push({ path: `${path}.${key}`, message: "Additional property not allowed" });
108
- }
109
- }
110
- }
111
- }
112
- }
113
- function checkType(data, type) {
114
- switch (type) {
115
- case "string":
116
- return typeof data === "string";
117
- case "number":
118
- return typeof data === "number" && !Number.isNaN(data);
119
- case "integer":
120
- return typeof data === "number" && Number.isInteger(data);
121
- case "boolean":
122
- return typeof data === "boolean";
123
- case "null":
124
- return data === null;
125
- case "array":
126
- return Array.isArray(data);
127
- case "object":
128
- return data !== null && typeof data === "object" && !Array.isArray(data);
129
- default:
130
- return true;
131
- }
132
- }
133
- function getType(data) {
134
- if (data === null) return "null";
135
- if (Array.isArray(data)) return "array";
136
- return typeof data;
137
- }
138
- export {
139
- schemaValidate
140
- };
141
- //# sourceMappingURL=schema-validate.js.map
1
+ function m(e){const n=[];return u(e.data,e.schema,"$",n),{valid:n.length===0,errors:n}}function u(e,n,r,t){if("type"in n){const i=n.type;if(!f(e,i)){t.push({path:r,message:`Expected type "${i}"`,expected:i,received:l(e)});return}}if("enum"in n){const i=n.enum;i.some(o=>JSON.stringify(o)===JSON.stringify(e))||t.push({path:r,message:`Must be one of: ${JSON.stringify(i)}`,received:JSON.stringify(e)})}if("const"in n&&JSON.stringify(e)!==JSON.stringify(n.const)&&t.push({path:r,message:`Must equal ${JSON.stringify(n.const)}`,received:JSON.stringify(e)}),typeof e=="string"&&("minLength"in n&&e.length<n.minLength&&t.push({path:r,message:`String too short (min: ${n.minLength})`,received:`length ${e.length}`}),"maxLength"in n&&e.length>n.maxLength&&t.push({path:r,message:`String too long (max: ${n.maxLength})`,received:`length ${e.length}`}),"pattern"in n&&!new RegExp(n.pattern).test(e)&&t.push({path:r,message:`Does not match pattern: ${n.pattern}`})),typeof e=="number"&&("minimum"in n&&e<n.minimum&&t.push({path:r,message:`Below minimum (${n.minimum})`,received:String(e)}),"maximum"in n&&e>n.maximum&&t.push({path:r,message:`Above maximum (${n.maximum})`,received:String(e)})),Array.isArray(e)&&("minItems"in n&&e.length<n.minItems&&t.push({path:r,message:`Too few items (min: ${n.minItems})`,received:`length ${e.length}`}),"maxItems"in n&&e.length>n.maxItems&&t.push({path:r,message:`Too many items (max: ${n.maxItems})`,received:`length ${e.length}`}),"items"in n))for(let i=0;i<e.length;i++)u(e[i],n.items,`${r}[${i}]`,t);if(e&&typeof e=="object"&&!Array.isArray(e)){const i=e;if("required"in n)for(const o of n.required)o in i||t.push({path:`${r}.${o}`,message:"Required property missing"});if("properties"in n){const o=n.properties;for(const[s,g]of Object.entries(o))s in i&&u(i[s],g,`${r}.${s}`,t)}if("additionalProperties"in n&&n.additionalProperties===!1){const o=Object.keys(n.properties??{});for(const s of Object.keys(i))o.includes(s)||t.push({path:`${r}.${s}`,message:"Additional property not allowed"})}}}function f(e,n){switch(n){case"string":return typeof e=="string";case"number":return typeof e=="number"&&!Number.isNaN(e);case"integer":return typeof e=="number"&&Number.isInteger(e);case"boolean":return typeof e=="boolean";case"null":return e===null;case"array":return Array.isArray(e);case"object":return e!==null&&typeof e=="object"&&!Array.isArray(e);default:return!0}}function l(e){return e===null?"null":Array.isArray(e)?"array":typeof e}export{m as schemaValidate};
@@ -1,72 +1 @@
1
- function estimateTokens(text) {
2
- return Math.ceil(text.length / 4);
3
- }
4
- async function scopeMap(embedder, store, options) {
5
- const { task, maxFiles = 15, contentType, origin } = options;
6
- const queryVector = await embedder.embed(task);
7
- const searchOpts = {
8
- limit: maxFiles * 3,
9
- // Over-fetch to group by file
10
- contentType,
11
- origin
12
- };
13
- const results = await store.search(queryVector, searchOpts);
14
- const fileMap = /* @__PURE__ */ new Map();
15
- for (const result of results) {
16
- const path = result.record.sourcePath;
17
- const existing = fileMap.get(path);
18
- if (existing) {
19
- existing.chunks.push(result);
20
- existing.totalChars += result.record.content.length;
21
- existing.maxScore = Math.max(existing.maxScore, result.score);
22
- } else {
23
- fileMap.set(path, {
24
- chunks: [result],
25
- totalChars: result.record.content.length,
26
- maxScore: result.score
27
- });
28
- }
29
- }
30
- const entries = [...fileMap.entries()].sort(([, a], [, b]) => b.maxScore - a.maxScore).slice(0, maxFiles).map(([path, { chunks, maxScore }]) => {
31
- const focusRanges = chunks.sort((a, b) => a.record.startLine - b.record.startLine).map((c) => ({
32
- start: c.record.startLine,
33
- end: c.record.endLine,
34
- heading: c.record.headingPath
35
- }));
36
- const topChunk = chunks.sort((a, b) => b.score - a.score)[0];
37
- const reason = topChunk.record.headingPath ? `Matches: ${topChunk.record.headingPath}` : `Contains relevant ${topChunk.record.contentType} content`;
38
- return {
39
- path,
40
- reason,
41
- estimatedTokens: 0,
42
- // computed below from actual content
43
- relevance: maxScore,
44
- focusRanges
45
- };
46
- });
47
- for (const entry of entries) {
48
- const fileChunks = fileMap.get(entry.path);
49
- if (fileChunks) {
50
- entry.estimatedTokens = estimateTokens(
51
- fileChunks.chunks.map((c) => c.record.content).join("")
52
- );
53
- }
54
- }
55
- const totalEstimatedTokens = entries.reduce((sum, e) => sum + e.estimatedTokens, 0);
56
- const readingOrder = [...entries].sort((a, b) => {
57
- const aIsConfig = a.path.includes("config") || a.path.includes("types") ? -1 : 0;
58
- const bIsConfig = b.path.includes("config") || b.path.includes("types") ? -1 : 0;
59
- if (aIsConfig !== bIsConfig) return aIsConfig - bIsConfig;
60
- return b.relevance - a.relevance;
61
- }).map((e) => e.path);
62
- return {
63
- task,
64
- files: entries,
65
- totalEstimatedTokens,
66
- readingOrder
67
- };
68
- }
69
- export {
70
- scopeMap
71
- };
72
- //# sourceMappingURL=scope-map.js.map
1
+ function M(c){return Math.ceil(c.length/4)}async function T(c,h,u){const{task:d,maxFiles:m=15,contentType:l,origin:g}=u,f=await c.embed(d),S={limit:m*3,contentType:l,origin:g},k=await h.search(f,S),o=new Map;for(const e of k){const t=e.record.sourcePath,r=o.get(t);r?(r.chunks.push(e),r.totalChars+=e.record.content.length,r.maxScore=Math.max(r.maxScore,e.score)):o.set(t,{chunks:[e],totalChars:e.record.content.length,maxScore:e.score})}const s=[...o.entries()].sort(([,e],[,t])=>t.maxScore-e.maxScore).slice(0,m).map(([e,{chunks:t,maxScore:r}])=>{const a=t.sort((n,p)=>n.record.startLine-p.record.startLine).map(n=>({start:n.record.startLine,end:n.record.endLine,heading:n.record.headingPath})),i=t.sort((n,p)=>p.score-n.score)[0],x=i.record.headingPath?`Matches: ${i.record.headingPath}`:`Contains relevant ${i.record.contentType} content`;return{path:e,reason:x,estimatedTokens:0,relevance:r,focusRanges:a}});for(const e of s){const t=o.get(e.path);t&&(e.estimatedTokens=M(t.chunks.map(r=>r.record.content).join("")))}const y=s.reduce((e,t)=>e+t.estimatedTokens,0),b=[...s].sort((e,t)=>{const r=e.path.includes("config")||e.path.includes("types")?-1:0,a=t.path.includes("config")||t.path.includes("types")?-1:0;return r!==a?r-a:t.relevance-e.relevance}).map(e=>e.path);return{task:d,files:s,totalEstimatedTokens:y,readingOrder:b}}export{T as scopeMap};
@@ -1,80 +1 @@
1
- import {
2
- existsSync,
3
- mkdirSync,
4
- readdirSync,
5
- readFileSync,
6
- unlinkSync,
7
- writeFileSync
8
- } from "node:fs";
9
- import { join } from "node:path";
10
- const SNIPPETS_DIR = () => join(process.cwd(), ".kb-state", "snippets");
11
- function ensureDir() {
12
- const dir = SNIPPETS_DIR();
13
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
14
- return dir;
15
- }
16
- function safeName(name) {
17
- const safe = name.replace(/[^a-zA-Z0-9_-]/g, "_");
18
- if (!safe) throw new Error("Invalid snippet name");
19
- return safe;
20
- }
21
- function snippet(options) {
22
- switch (options.action) {
23
- case "save": {
24
- if (!options.name || !options.code) throw new Error("name and code required for save");
25
- const dir = ensureDir();
26
- const name = safeName(options.name);
27
- const filePath = join(dir, `${name}.json`);
28
- const existing = existsSync(filePath) ? JSON.parse(readFileSync(filePath, "utf8")) : null;
29
- const now = (/* @__PURE__ */ new Date()).toISOString();
30
- const entry = {
31
- name: options.name,
32
- language: options.language ?? "text",
33
- code: options.code,
34
- tags: options.tags ?? [],
35
- created: existing?.created ?? now,
36
- updated: now
37
- };
38
- writeFileSync(filePath, JSON.stringify(entry, null, 2));
39
- return entry;
40
- }
41
- case "get": {
42
- if (!options.name) throw new Error("name required for get");
43
- const filePath = join(SNIPPETS_DIR(), `${safeName(options.name)}.json`);
44
- if (!existsSync(filePath)) throw new Error(`Snippet not found: ${options.name}`);
45
- return JSON.parse(readFileSync(filePath, "utf8"));
46
- }
47
- case "list": {
48
- const dir = ensureDir();
49
- const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
50
- const snippets = files.map((f) => {
51
- const data = JSON.parse(readFileSync(join(dir, f), "utf8"));
52
- return { name: data.name, language: data.language, tags: data.tags, updated: data.updated };
53
- });
54
- return { snippets };
55
- }
56
- case "search": {
57
- if (!options.query) throw new Error("query required for search");
58
- const q = options.query.toLowerCase();
59
- const dir = ensureDir();
60
- const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
61
- const snippets = files.map((f) => JSON.parse(readFileSync(join(dir, f), "utf8"))).filter(
62
- (s) => s.name.toLowerCase().includes(q) || s.tags.some((t) => t.toLowerCase().includes(q)) || s.language.toLowerCase().includes(q) || s.code.toLowerCase().includes(q)
63
- ).map((s) => ({ name: s.name, language: s.language, tags: s.tags, updated: s.updated }));
64
- return { snippets };
65
- }
66
- case "delete": {
67
- if (!options.name) throw new Error("name required for delete");
68
- const filePath = join(SNIPPETS_DIR(), `${safeName(options.name)}.json`);
69
- if (!existsSync(filePath)) return { deleted: false };
70
- unlinkSync(filePath);
71
- return { deleted: true };
72
- }
73
- default:
74
- throw new Error(`Unknown action: ${options.action}`);
75
- }
76
- }
77
- export {
78
- snippet
79
- };
80
- //# sourceMappingURL=snippet.js.map
1
+ import{existsSync as p,mkdirSync as f,readdirSync as l,readFileSync as u,unlinkSync as m,writeFileSync as S}from"node:fs";import{join as a}from"node:path";const o=()=>a(process.cwd(),".kb-state","snippets");function d(){const e=o();return p(e)||f(e,{recursive:!0}),e}function g(e){const n=e.replace(/[^a-zA-Z0-9_-]/g,"_");if(!n)throw new Error("Invalid snippet name");return n}function h(e){switch(e.action){case"save":{if(!e.name||!e.code)throw new Error("name and code required for save");const n=d(),s=g(e.name),i=a(n,`${s}.json`),r=p(i)?JSON.parse(u(i,"utf8")):null,t=new Date().toISOString(),c={name:e.name,language:e.language??"text",code:e.code,tags:e.tags??[],created:r?.created??t,updated:t};return S(i,JSON.stringify(c,null,2)),c}case"get":{if(!e.name)throw new Error("name required for get");const n=a(o(),`${g(e.name)}.json`);if(!p(n))throw new Error(`Snippet not found: ${e.name}`);return JSON.parse(u(n,"utf8"))}case"list":{const n=d();return{snippets:l(n).filter(r=>r.endsWith(".json")).map(r=>{const t=JSON.parse(u(a(n,r),"utf8"));return{name:t.name,language:t.language,tags:t.tags,updated:t.updated}})}}case"search":{if(!e.query)throw new Error("query required for search");const n=e.query.toLowerCase(),s=d();return{snippets:l(s).filter(t=>t.endsWith(".json")).map(t=>JSON.parse(u(a(s,t),"utf8"))).filter(t=>t.name.toLowerCase().includes(n)||t.tags.some(c=>c.toLowerCase().includes(n))||t.language.toLowerCase().includes(n)||t.code.toLowerCase().includes(n)).map(t=>({name:t.name,language:t.language,tags:t.tags,updated:t.updated}))}}case"delete":{if(!e.name)throw new Error("name required for delete");const n=a(o(),`${g(e.name)}.json`);return p(n)?(m(n),{deleted:!0}):{deleted:!1}}default:throw new Error(`Unknown action: ${e.action}`)}}export{h as snippet};
@@ -1,60 +1,2 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { dirname, resolve } from "node:path";
3
- const STASH_DIR = ".kb-state";
4
- const STASH_FILE = "stash.json";
5
- function stashPath(cwd) {
6
- const root = cwd ?? process.cwd();
7
- return resolve(root, STASH_DIR, STASH_FILE);
8
- }
9
- function loadStash(cwd) {
10
- const path = stashPath(cwd);
11
- if (!existsSync(path)) return {};
12
- const raw = readFileSync(path, "utf-8");
13
- return JSON.parse(raw);
14
- }
15
- function saveStash(data, cwd) {
16
- const path = stashPath(cwd);
17
- const dir = dirname(path);
18
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
19
- writeFileSync(path, `${JSON.stringify(data, null, 2)}
20
- `, "utf-8");
21
- }
22
- function stashSet(key, value, cwd) {
23
- const stash = loadStash(cwd);
24
- const entry = {
25
- key,
26
- value,
27
- type: typeof value,
28
- storedAt: (/* @__PURE__ */ new Date()).toISOString()
29
- };
30
- stash[key] = entry;
31
- saveStash(stash, cwd);
32
- return entry;
33
- }
34
- function stashGet(key, cwd) {
35
- return loadStash(cwd)[key];
36
- }
37
- function stashList(cwd) {
38
- return Object.values(loadStash(cwd));
39
- }
40
- function stashDelete(key, cwd) {
41
- const stash = loadStash(cwd);
42
- if (!(key in stash)) return false;
43
- delete stash[key];
44
- saveStash(stash, cwd);
45
- return true;
46
- }
47
- function stashClear(cwd) {
48
- const stash = loadStash(cwd);
49
- const count = Object.keys(stash).length;
50
- saveStash({}, cwd);
51
- return count;
52
- }
53
- export {
54
- stashClear,
55
- stashDelete,
56
- stashGet,
57
- stashList,
58
- stashSet
59
- };
60
- //# sourceMappingURL=stash.js.map
1
+ import{existsSync as a,mkdirSync as u,readFileSync as h,writeFileSync as S}from"node:fs";import{dirname as g,resolve as f}from"node:path";const y=".kb-state",p="stash.json";function c(t){const n=t??process.cwd();return f(n,y,p)}function e(t){const n=c(t);if(!a(n))return{};const r=h(n,"utf-8");return JSON.parse(r)}function o(t,n){const r=c(n),s=g(r);a(s)||u(s,{recursive:!0}),S(r,`${JSON.stringify(t,null,2)}
2
+ `,"utf-8")}function E(t,n,r){const s=e(r),i={key:t,value:n,type:typeof n,storedAt:new Date().toISOString()};return s[t]=i,o(s,r),i}function m(t,n){return e(n)[t]}function x(t){return Object.values(e(t))}function b(t,n){const r=e(n);return t in r?(delete r[t],o(r,n),!0):!1}function v(t){const n=e(t),r=Object.keys(n).length;return o({},t),r}export{v as stashClear,b as stashDelete,m as stashGet,x as stashList,E as stashSet};
@@ -1,239 +1,6 @@
1
- import { readFile } from "node:fs/promises";
2
- import { basename, extname, relative } from "node:path";
3
- import { fileSummary } from "./file-summary.js";
4
- import { cosineSimilarity, estimateTokens, segment } from "./text-utils.js";
5
- const DEFAULT_MAX_CONTENT_CHARS = 800;
6
- const UNKNOWN_LIMIT = 3;
7
- async function stratumCard(embedder, options) {
8
- const { files, query, tier = "T1", maxContentChars = DEFAULT_MAX_CONTENT_CHARS } = options;
9
- const queryVector = tier === "T2" ? await embedder.embedQuery(query) : null;
10
- const cards = await Promise.all(
11
- files.map(async (filePath) => {
12
- try {
13
- const content = await readFile(filePath, "utf-8");
14
- const originalTokenEstimate = estimateTokens(content);
15
- if (content.includes("\0")) {
16
- return buildErrorCard(filePath, tier, "binary", "binary file", originalTokenEstimate);
17
- }
18
- if (content.trim().length === 0) {
19
- const card2 = formatCard({
20
- displayPath: formatDisplayPath(filePath),
21
- tier,
22
- role: "empty",
23
- deps: [],
24
- exports: [],
25
- unknowns: [],
26
- riskTier: "low"
27
- });
28
- return {
29
- path: filePath,
30
- tier,
31
- card: card2,
32
- unknowns: [],
33
- riskTier: "low",
34
- tokenEstimate: estimateTokens(card2),
35
- originalTokenEstimate
36
- };
37
- }
38
- const summary = await fileSummary({ path: filePath });
39
- const role = inferRole(filePath, summary);
40
- const unknowns = detectUnknowns(content, summary);
41
- const riskTier = classifyRisk(filePath, summary);
42
- const deps = getTopDeps(summary);
43
- const exports = [...new Set(summary.exports)].slice(0, 5);
44
- let card = formatCard({
45
- displayPath: formatDisplayPath(filePath),
46
- tier,
47
- role,
48
- deps,
49
- exports,
50
- unknowns,
51
- riskTier
52
- });
53
- if (tier === "T2" && queryVector) {
54
- const context = await compressContent(embedder, queryVector, content, maxContentChars);
55
- if (context.length > 0) {
56
- card = `${card}
1
+ import{readFile as F}from"node:fs/promises";import{basename as h,extname as I,relative as A}from"node:path";import{fileSummary as X}from"./file-summary.js";import{cosineSimilarity as j,estimateTokens as y,segment as D}from"./text-utils.js";const M=800,S=3;async function B(t,e){const{files:r,query:o,tier:n="T1",maxContentChars:m=M}=e,u=n==="T2"?await t.embedQuery(o):null,l=await Promise.all(r.map(async i=>{try{const c=await F(i,"utf-8"),p=y(c);if(c.includes("\0"))return C(i,n,"binary","binary file",p);if(c.trim().length===0){const g=x({displayPath:T(i),tier:n,role:"empty",deps:[],exports:[],unknowns:[],riskTier:"low"});return{path:i,tier:n,card:g,unknowns:[],riskTier:"low",tokenEstimate:y(g),originalTokenEstimate:p}}const d=await X({path:i}),N=U(i,d),E=W(c,d),w=_(i,d),O=$(d),R=[...new Set(d.exports)].slice(0,5);let f=x({displayPath:T(i),tier:n,role:N,deps:O,exports:R,unknowns:E,riskTier:w});if(n==="T2"&&u){const g=await v(t,u,c,m);g.length>0&&(f=`${f}
57
2
  CONTEXT:
58
- ${context}`;
59
- }
60
- }
61
- return {
62
- path: filePath,
63
- tier,
64
- card,
65
- unknowns,
66
- riskTier,
67
- tokenEstimate: estimateTokens(card),
68
- originalTokenEstimate
69
- };
70
- } catch (error) {
71
- const message = error.code === "ENOENT" ? "file missing" : "unreadable file";
72
- const role = error.code === "ENOENT" ? "missing" : "unreadable";
73
- return buildErrorCard(filePath, tier, role, message, 0);
74
- }
75
- })
76
- );
77
- const totalTokenEstimate = cards.reduce((total, card) => total + card.tokenEstimate, 0);
78
- const totalOriginalTokenEstimate = cards.reduce(
79
- (total, card) => total + card.originalTokenEstimate,
80
- 0
81
- );
82
- return {
83
- cards,
84
- totalTokenEstimate,
85
- totalOriginalTokenEstimate,
86
- compressionRatio: totalOriginalTokenEstimate === 0 ? 0 : totalTokenEstimate / totalOriginalTokenEstimate
87
- };
88
- }
89
- function buildErrorCard(filePath, tier, role, unknown, originalTokenEstimate) {
90
- const card = formatCard({
91
- displayPath: formatDisplayPath(filePath),
92
- tier,
93
- role,
94
- deps: [],
95
- exports: [],
96
- unknowns: [unknown],
97
- riskTier: "low"
98
- });
99
- return {
100
- path: filePath,
101
- tier,
102
- card,
103
- unknowns: [unknown],
104
- riskTier: "low",
105
- tokenEstimate: estimateTokens(card),
106
- originalTokenEstimate
107
- };
108
- }
109
- function formatCard(input) {
110
- const { displayPath, tier, role, deps, exports, unknowns, riskTier } = input;
111
- return [
112
- `[${tier}: ${displayPath}]`,
113
- `ROLE: ${role}`,
114
- `DEPS: ${joinOrNone(deps)}`,
115
- `EXPORTS: ${joinOrNone(exports)}`,
116
- `UNKNOWNS: ${joinOrNone(unknowns, "; ")}`,
117
- `RISK: ${riskTier}`
118
- ].join("\n");
119
- }
120
- function formatDisplayPath(filePath) {
121
- const relPath = relative(process.cwd(), filePath).replace(/\\/g, "/");
122
- if (!relPath || relPath.startsWith("..")) {
123
- return basename(filePath);
124
- }
125
- return relPath;
126
- }
127
- function joinOrNone(values, separator = ", ") {
128
- return values.length > 0 ? values.join(separator) : "none";
129
- }
130
- function inferRole(path, summary) {
131
- const name = basename(path);
132
- const extension = extname(name).toLowerCase();
133
- if ([".json", ".yaml", ".yml", ".env"].includes(extension) || /config|settings/i.test(name)) {
134
- return "configuration";
135
- }
136
- if (/types?\.ts$|\.d\.ts$/i.test(name)) return "type-definitions";
137
- if (/schema/i.test(name)) return "schema";
138
- if (/test|spec/i.test(name)) return "test";
139
- if (/index\.[jt]sx?$/i.test(name)) return "barrel-export";
140
- if (/handler|controller|route/i.test(name)) return "entry-point";
141
- if (/model|entity/i.test(name)) return "data-model";
142
- if (/util|helper/i.test(name)) return "utility";
143
- if (/service|provider/i.test(name)) return "service";
144
- if (summary.classes.length > 0) return "class-module";
145
- if (summary.interfaces.length > 2) return "type-definitions";
146
- return "implementation";
147
- }
148
- function detectUnknowns(content, summary) {
149
- const unknowns = [];
150
- const seen = /* @__PURE__ */ new Set();
151
- for (const match of content.matchAll(/\/\/\s*(TODO|FIXME|HACK|XXX)\s*:?\s*(.+)?$/gm)) {
152
- const text = `${match[1]}: ${(match[2] ?? "").trim()}`.trim();
153
- addUnknown(unknowns, seen, text.replace(/:\s*$/, ""));
154
- }
155
- if (hasExportedAny(content)) {
156
- addUnknown(unknowns, seen, "exported any usage");
157
- }
158
- for (const dep of getTopDeps(summary)) {
159
- addUnknown(unknowns, seen, `cross-package import: ${dep}`);
160
- }
161
- return unknowns.slice(0, UNKNOWN_LIMIT);
162
- }
163
- function addUnknown(unknowns, seen, value) {
164
- if (unknowns.length >= UNKNOWN_LIMIT) return;
165
- if (!value || seen.has(value)) return;
166
- seen.add(value);
167
- unknowns.push(value);
168
- }
169
- function hasExportedAny(content) {
170
- const patterns = [
171
- /export\s+(?:async\s+)?function\s+\w+[^\n{;]*\bany\b/g,
172
- /export\s+interface\s+\w+[\s\S]*?\{[\s\S]*?\bany\b[\s\S]*?\}/g,
173
- /export\s+type\s+\w+\s*=.*\bany\b/g,
174
- /export\s+const\s+\w+[^\n=]*\bany\b/g
175
- ];
176
- return patterns.some((pattern) => pattern.test(content));
177
- }
178
- function classifyRisk(path, summary) {
179
- if (/auth|token|permission|secret|credential|encrypt/i.test(path)) return "high";
180
- if (/types?\.ts$|schema|contract|\.d\.ts$/i.test(basename(path))) return "medium";
181
- if (summary.exports.length > 10) return "medium";
182
- return "low";
183
- }
184
- function getTopDeps(summary) {
185
- return summary.imports.map(extractImportSpecifier).filter((specifier) => Boolean(specifier)).filter((specifier) => !specifier.startsWith("./") && !specifier.startsWith("../")).slice(0, 3);
186
- }
187
- function extractImportSpecifier(importLine) {
188
- const fromMatch = importLine.match(/from\s+['"]([^'"]+)['"]/);
189
- if (fromMatch) {
190
- return fromMatch[1];
191
- }
192
- const sideEffectMatch = importLine.match(/^import\s+['"]([^'"]+)['"]/);
193
- return sideEffectMatch ? sideEffectMatch[1] : null;
194
- }
195
- async function compressContent(embedder, queryVector, content, maxContentChars) {
196
- if (maxContentChars <= 0) {
197
- return "";
198
- }
199
- const segments = segment(content, "paragraph");
200
- if (segments.length === 0) {
201
- return "";
202
- }
203
- const scoredSegments = await Promise.all(
204
- segments.map(async (text, index) => {
205
- const embedding = await embedder.embed(text);
206
- return {
207
- index,
208
- text,
209
- score: cosineSimilarity(queryVector, embedding)
210
- };
211
- })
212
- );
213
- const selected = [];
214
- let usedChars = 0;
215
- for (const segmentInfo of scoredSegments.sort((left, right) => right.score - left.score)) {
216
- const availableChars = maxContentChars - usedChars;
217
- if (availableChars <= 0) {
218
- break;
219
- }
220
- if (segmentInfo.text.length <= availableChars) {
221
- selected.push({ index: segmentInfo.index, text: segmentInfo.text });
222
- usedChars += segmentInfo.text.length;
223
- continue;
224
- }
225
- if (selected.length === 0) {
226
- selected.push({
227
- index: segmentInfo.index,
228
- text: segmentInfo.text.slice(0, availableChars).trimEnd()
229
- });
230
- usedChars = maxContentChars;
231
- }
232
- break;
233
- }
234
- return selected.sort((left, right) => left.index - right.index).map((entry) => entry.text).filter((entry) => entry.length > 0).join("\n\n");
235
- }
236
- export {
237
- stratumCard
238
- };
239
- //# sourceMappingURL=stratum-card.js.map
3
+ ${g}`)}return{path:i,tier:n,card:f,unknowns:E,riskTier:w,tokenEstimate:y(f),originalTokenEstimate:p}}catch(c){const p=c.code==="ENOENT"?"file missing":"unreadable file",d=c.code==="ENOENT"?"missing":"unreadable";return C(i,n,d,p,0)}})),s=l.reduce((i,c)=>i+c.tokenEstimate,0),a=l.reduce((i,c)=>i+c.originalTokenEstimate,0);return{cards:l,totalTokenEstimate:s,totalOriginalTokenEstimate:a,compressionRatio:a===0?0:s/a}}function C(t,e,r,o,n){const m=x({displayPath:T(t),tier:e,role:r,deps:[],exports:[],unknowns:[o],riskTier:"low"});return{path:t,tier:e,card:m,unknowns:[o],riskTier:"low",tokenEstimate:y(m),originalTokenEstimate:n}}function x(t){const{displayPath:e,tier:r,role:o,deps:n,exports:m,unknowns:u,riskTier:l}=t;return[`[${r}: ${e}]`,`ROLE: ${o}`,`DEPS: ${k(n)}`,`EXPORTS: ${k(m)}`,`UNKNOWNS: ${k(u,"; ")}`,`RISK: ${l}`].join(`
4
+ `)}function T(t){const e=A(process.cwd(),t).replace(/\\/g,"/");return!e||e.startsWith("..")?h(t):e}function k(t,e=", "){return t.length>0?t.join(e):"none"}function U(t,e){const r=h(t),o=I(r).toLowerCase();return[".json",".yaml",".yml",".env"].includes(o)||/config|settings/i.test(r)?"configuration":/types?\.ts$|\.d\.ts$/i.test(r)?"type-definitions":/schema/i.test(r)?"schema":/test|spec/i.test(r)?"test":/index\.[jt]sx?$/i.test(r)?"barrel-export":/handler|controller|route/i.test(r)?"entry-point":/model|entity/i.test(r)?"data-model":/util|helper/i.test(r)?"utility":/service|provider/i.test(r)?"service":e.classes.length>0?"class-module":e.interfaces.length>2?"type-definitions":"implementation"}function W(t,e){const r=[],o=new Set;for(const n of t.matchAll(/\/\/\s*(TODO|FIXME|HACK|XXX)\s*:?\s*(.+)?$/gm)){const m=`${n[1]}: ${(n[2]??"").trim()}`.trim();b(r,o,m.replace(/:\s*$/,""))}K(t)&&b(r,o,"exported any usage");for(const n of $(e))b(r,o,`cross-package import: ${n}`);return r.slice(0,S)}function b(t,e,r){t.length>=S||!r||e.has(r)||(e.add(r),t.push(r))}function K(t){return[/export\s+(?:async\s+)?function\s+\w+[^\n{;]*\bany\b/g,/export\s+interface\s+\w+[\s\S]*?\{[\s\S]*?\bany\b[\s\S]*?\}/g,/export\s+type\s+\w+\s*=.*\bany\b/g,/export\s+const\s+\w+[^\n=]*\bany\b/g].some(r=>r.test(t))}function _(t,e){return/auth|token|permission|secret|credential|encrypt/i.test(t)?"high":/types?\.ts$|schema|contract|\.d\.ts$/i.test(h(t))||e.exports.length>10?"medium":"low"}function $(t){return t.imports.map(q).filter(e=>!!e).filter(e=>!e.startsWith("./")&&!e.startsWith("../")).slice(0,3)}function q(t){const e=t.match(/from\s+['"]([^'"]+)['"]/);if(e)return e[1];const r=t.match(/^import\s+['"]([^'"]+)['"]/);return r?r[1]:null}async function v(t,e,r,o){if(o<=0)return"";const n=D(r,"paragraph");if(n.length===0)return"";const m=await Promise.all(n.map(async(s,a)=>{const i=await t.embed(s);return{index:a,text:s,score:j(e,i)}})),u=[];let l=0;for(const s of m.sort((a,i)=>i.score-a.score)){const a=o-l;if(a<=0)break;if(s.text.length<=a){u.push({index:s.index,text:s.text}),l+=s.text.length;continue}u.length===0&&(u.push({index:s.index,text:s.text.slice(0,a).trimEnd()}),l=o);break}return u.sort((s,a)=>s.index-a.index).map(s=>s.text).filter(s=>s.length>0).join(`
5
+
6
+ `)}export{B as stratumCard};