@lssm/module.ai-chat 0.0.0-canary-20251217083314 → 1.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-chat.feature.js +1 -93
- package/dist/context/context-builder.js +2 -147
- package/dist/context/file-operations.js +1 -174
- package/dist/context/index.js +1 -5
- package/dist/context/workspace-context.js +2 -123
- package/dist/core/chat-service.js +2 -211
- package/dist/core/conversation-store.js +1 -108
- package/dist/core/index.js +1 -4
- package/dist/index.js +1 -22
- package/dist/presentation/components/ChatContainer.js +1 -62
- package/dist/presentation/components/ChatInput.js +1 -149
- package/dist/presentation/components/ChatMessage.js +1 -135
- package/dist/presentation/components/CodePreview.js +2 -126
- package/dist/presentation/components/ContextIndicator.js +1 -96
- package/dist/presentation/components/ModelPicker.js +1 -197
- package/dist/presentation/components/index.js +1 -8
- package/dist/presentation/hooks/index.js +1 -4
- package/dist/presentation/hooks/useChat.js +1 -171
- package/dist/presentation/hooks/useProviders.js +1 -42
- package/dist/presentation/index.js +1 -12
- package/dist/providers/chat-utilities.js +1 -16
- package/dist/providers/index.js +1 -7
- package/package.json +17 -17
- package/dist/ai-chat.feature.d.ts +0 -11
- package/dist/context/context-builder.d.ts +0 -56
- package/dist/context/file-operations.d.ts +0 -99
- package/dist/context/index.d.ts +0 -4
- package/dist/context/workspace-context.d.ts +0 -116
- package/dist/core/chat-service.d.ts +0 -72
- package/dist/core/conversation-store.d.ts +0 -73
- package/dist/core/index.d.ts +0 -4
- package/dist/core/message-types.d.ts +0 -149
- package/dist/index.d.ts +0 -16
- package/dist/libs/ai-providers/dist/factory.js +0 -225
- package/dist/libs/ai-providers/dist/index.js +0 -4
- package/dist/libs/ai-providers/dist/legacy.js +0 -2
- package/dist/libs/ai-providers/dist/models.js +0 -299
- package/dist/libs/ai-providers/dist/validation.js +0 -60
- package/dist/libs/design-system/dist/_virtual/rolldown_runtime.js +0 -5
- package/dist/libs/design-system/dist/components/atoms/Button.js +0 -33
- package/dist/libs/design-system/dist/components/atoms/Textarea.js +0 -35
- package/dist/libs/design-system/dist/lib/keyboard.js +0 -193
- package/dist/libs/design-system/dist/ui-kit-web/dist/ui/button.js +0 -55
- package/dist/libs/design-system/dist/ui-kit-web/dist/ui/textarea.js +0 -16
- package/dist/libs/design-system/dist/ui-kit-web/dist/ui-kit-core/dist/utils.js +0 -13
- package/dist/libs/ui-kit-web/dist/ui/avatar.js +0 -25
- package/dist/libs/ui-kit-web/dist/ui/badge.js +0 -26
- package/dist/libs/ui-kit-web/dist/ui/scroll-area.js +0 -39
- package/dist/libs/ui-kit-web/dist/ui/select.js +0 -79
- package/dist/libs/ui-kit-web/dist/ui/skeleton.js +0 -14
- package/dist/libs/ui-kit-web/dist/ui/tooltip.js +0 -39
- package/dist/libs/ui-kit-web/dist/ui/utils.js +0 -10
- package/dist/libs/ui-kit-web/dist/ui-kit-core/dist/utils.js +0 -10
- package/dist/presentation/components/ChatContainer.d.ts +0 -20
- package/dist/presentation/components/ChatInput.d.ts +0 -34
- package/dist/presentation/components/ChatMessage.d.ts +0 -23
- package/dist/presentation/components/CodePreview.d.ts +0 -39
- package/dist/presentation/components/ContextIndicator.d.ts +0 -25
- package/dist/presentation/components/ModelPicker.d.ts +0 -38
- package/dist/presentation/components/index.d.ts +0 -7
- package/dist/presentation/hooks/index.d.ts +0 -3
- package/dist/presentation/hooks/useChat.d.ts +0 -66
- package/dist/presentation/hooks/useProviders.d.ts +0 -37
- package/dist/presentation/index.d.ts +0 -10
- package/dist/providers/chat-utilities.d.ts +0 -14
- package/dist/providers/index.d.ts +0 -3
package/dist/ai-chat.feature.js
CHANGED
|
@@ -1,93 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* AI Chat feature module that bundles conversational AI assistance
|
|
4
|
-
* for ContractSpec development across CLI, VSCode, and Studio.
|
|
5
|
-
*/
|
|
6
|
-
const AiChatFeature = {
|
|
7
|
-
meta: {
|
|
8
|
-
key: "ai-chat",
|
|
9
|
-
title: "AI Vibe Coding Chat",
|
|
10
|
-
description: "AI-powered conversational coding assistant with full workspace context",
|
|
11
|
-
domain: "platform",
|
|
12
|
-
owners: ["@platform.ai"],
|
|
13
|
-
tags: [
|
|
14
|
-
"ai",
|
|
15
|
-
"chat",
|
|
16
|
-
"llm",
|
|
17
|
-
"vibe-coding",
|
|
18
|
-
"assistant"
|
|
19
|
-
],
|
|
20
|
-
stability: "experimental"
|
|
21
|
-
},
|
|
22
|
-
operations: [
|
|
23
|
-
{
|
|
24
|
-
name: "ai-chat.send",
|
|
25
|
-
version: 1
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
name: "ai-chat.stream",
|
|
29
|
-
version: 1
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: "ai-chat.conversations.list",
|
|
33
|
-
version: 1
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: "ai-chat.conversations.get",
|
|
37
|
-
version: 1
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
name: "ai-chat.conversations.delete",
|
|
41
|
-
version: 1
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: "ai-chat.providers.list",
|
|
45
|
-
version: 1
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: "ai-chat.context.scan",
|
|
49
|
-
version: 1
|
|
50
|
-
}
|
|
51
|
-
],
|
|
52
|
-
events: [
|
|
53
|
-
{
|
|
54
|
-
name: "ai-chat.message.sent",
|
|
55
|
-
version: 1
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: "ai-chat.message.received",
|
|
59
|
-
version: 1
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: "ai-chat.conversation.created",
|
|
63
|
-
version: 1
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: "ai-chat.conversation.deleted",
|
|
67
|
-
version: 1
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
name: "ai-chat.error",
|
|
71
|
-
version: 1
|
|
72
|
-
}
|
|
73
|
-
],
|
|
74
|
-
presentations: [],
|
|
75
|
-
opToPresentation: [],
|
|
76
|
-
presentationsTargets: [],
|
|
77
|
-
capabilities: {
|
|
78
|
-
provides: [{
|
|
79
|
-
key: "ai-chat",
|
|
80
|
-
version: 1
|
|
81
|
-
}],
|
|
82
|
-
requires: [{
|
|
83
|
-
key: "identity",
|
|
84
|
-
version: 1
|
|
85
|
-
}, {
|
|
86
|
-
key: "metering",
|
|
87
|
-
version: 1
|
|
88
|
-
}]
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
//#endregion
|
|
93
|
-
export { AiChatFeature };
|
|
1
|
+
const e={meta:{key:`ai-chat`,title:`AI Vibe Coding Chat`,description:`AI-powered conversational coding assistant with full workspace context`,domain:`platform`,owners:[`@platform.ai`],tags:[`ai`,`chat`,`llm`,`vibe-coding`,`assistant`],stability:`experimental`},operations:[{name:`ai-chat.send`,version:1},{name:`ai-chat.stream`,version:1},{name:`ai-chat.conversations.list`,version:1},{name:`ai-chat.conversations.get`,version:1},{name:`ai-chat.conversations.delete`,version:1},{name:`ai-chat.providers.list`,version:1},{name:`ai-chat.context.scan`,version:1}],events:[{name:`ai-chat.message.sent`,version:1},{name:`ai-chat.message.received`,version:1},{name:`ai-chat.conversation.created`,version:1},{name:`ai-chat.conversation.deleted`,version:1},{name:`ai-chat.error`,version:1}],presentations:[],opToPresentation:[],presentationsTargets:[],capabilities:{provides:[{key:`ai-chat`,version:1}],requires:[{key:`identity`,version:1},{key:`metering`,version:1}]}};export{e as AiChatFeature};
|
|
@@ -1,147 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Estimates token count from string (rough approximation)
|
|
4
|
-
*/
|
|
5
|
-
function estimateTokens(text) {
|
|
6
|
-
return Math.ceil(text.length / 4);
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Calculate relevance score for a spec
|
|
10
|
-
*/
|
|
11
|
-
function scoreSpec(spec, query) {
|
|
12
|
-
if (!query) return .5;
|
|
13
|
-
const lowerQuery = query.toLowerCase();
|
|
14
|
-
let score = 0;
|
|
15
|
-
if (spec.name.toLowerCase().includes(lowerQuery)) score += .4;
|
|
16
|
-
if (spec.description?.toLowerCase().includes(lowerQuery)) score += .3;
|
|
17
|
-
if (spec.tags?.some((t) => t.toLowerCase().includes(lowerQuery))) score += .2;
|
|
18
|
-
return Math.min(score, 1);
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Calculate relevance score for a file
|
|
22
|
-
*/
|
|
23
|
-
function scoreFile(file, query) {
|
|
24
|
-
if (!query) return .5;
|
|
25
|
-
const lowerQuery = query.toLowerCase();
|
|
26
|
-
let score = 0;
|
|
27
|
-
if (file.path.toLowerCase().includes(lowerQuery)) score += .5;
|
|
28
|
-
if (file.name.toLowerCase().includes(lowerQuery)) score += .3;
|
|
29
|
-
if (file.isSpec) score += .2;
|
|
30
|
-
return Math.min(score, 1);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Context builder for creating rich LLM context
|
|
34
|
-
*/
|
|
35
|
-
var ContextBuilder = class {
|
|
36
|
-
context;
|
|
37
|
-
constructor(context) {
|
|
38
|
-
this.context = context;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Build context for a chat message
|
|
42
|
-
*/
|
|
43
|
-
build(options = {}) {
|
|
44
|
-
const maxTokens = options.maxTokens ?? 4e3;
|
|
45
|
-
const entries = [];
|
|
46
|
-
let totalTokens = 0;
|
|
47
|
-
if (options.includeSpecs?.length) for (const specName of options.includeSpecs) {
|
|
48
|
-
const spec = this.context.getSpecs().find((s) => s.name === specName);
|
|
49
|
-
if (spec) {
|
|
50
|
-
const entry = {
|
|
51
|
-
type: "spec",
|
|
52
|
-
path: spec.path,
|
|
53
|
-
summary: `${spec.type}: ${spec.name}${spec.description ? ` - ${spec.description}` : ""}`,
|
|
54
|
-
relevance: 1
|
|
55
|
-
};
|
|
56
|
-
entries.push(entry);
|
|
57
|
-
totalTokens += estimateTokens(entry.summary ?? "");
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (options.includeFiles?.length) for (const filePath of options.includeFiles) {
|
|
61
|
-
const file = this.context.getFiles().find((f) => f.path === filePath);
|
|
62
|
-
if (file) {
|
|
63
|
-
const entry = {
|
|
64
|
-
type: "file",
|
|
65
|
-
path: file.path,
|
|
66
|
-
summary: `File: ${file.relativePath}`,
|
|
67
|
-
relevance: 1
|
|
68
|
-
};
|
|
69
|
-
entries.push(entry);
|
|
70
|
-
totalTokens += estimateTokens(entry.summary ?? "");
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (options.query) {
|
|
74
|
-
const scoredSpecs = this.context.getSpecs().map((spec) => ({
|
|
75
|
-
spec,
|
|
76
|
-
score: scoreSpec(spec, options.query)
|
|
77
|
-
})).filter(({ score }) => score > .2).sort((a, b) => b.score - a.score);
|
|
78
|
-
for (const { spec, score } of scoredSpecs) {
|
|
79
|
-
if (totalTokens >= maxTokens) break;
|
|
80
|
-
if (entries.some((e) => e.path === spec.path)) continue;
|
|
81
|
-
const entry = {
|
|
82
|
-
type: "spec",
|
|
83
|
-
path: spec.path,
|
|
84
|
-
summary: `${spec.type}: ${spec.name}${spec.description ? ` - ${spec.description}` : ""}`,
|
|
85
|
-
relevance: score
|
|
86
|
-
};
|
|
87
|
-
entries.push(entry);
|
|
88
|
-
totalTokens += estimateTokens(entry.summary ?? "");
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (options.query) {
|
|
92
|
-
const scoredFiles = this.context.getFiles().map((file) => ({
|
|
93
|
-
file,
|
|
94
|
-
score: scoreFile(file, options.query)
|
|
95
|
-
})).filter(({ score }) => score > .2).sort((a, b) => b.score - a.score);
|
|
96
|
-
for (const { file, score } of scoredFiles) {
|
|
97
|
-
if (totalTokens >= maxTokens) break;
|
|
98
|
-
if (entries.some((e) => e.path === file.path)) continue;
|
|
99
|
-
const entry = {
|
|
100
|
-
type: "file",
|
|
101
|
-
path: file.path,
|
|
102
|
-
summary: `File: ${file.relativePath}`,
|
|
103
|
-
relevance: score
|
|
104
|
-
};
|
|
105
|
-
entries.push(entry);
|
|
106
|
-
totalTokens += estimateTokens(entry.summary ?? "");
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const summary = this.buildSummary(entries);
|
|
110
|
-
return {
|
|
111
|
-
entries,
|
|
112
|
-
summary,
|
|
113
|
-
totalTokensEstimate: totalTokens + estimateTokens(summary)
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Build a text summary of the context entries
|
|
118
|
-
*/
|
|
119
|
-
buildSummary(entries) {
|
|
120
|
-
if (entries.length === 0) return this.context.getContextSummary();
|
|
121
|
-
const parts = [];
|
|
122
|
-
const workspaceSummary = this.context.getSummary();
|
|
123
|
-
parts.push(`Workspace: ${workspaceSummary.name}`);
|
|
124
|
-
parts.push("");
|
|
125
|
-
const specs = entries.filter((e) => e.type === "spec");
|
|
126
|
-
if (specs.length > 0) {
|
|
127
|
-
parts.push("### Relevant Specs");
|
|
128
|
-
for (const entry of specs) parts.push(`- ${entry.summary}`);
|
|
129
|
-
parts.push("");
|
|
130
|
-
}
|
|
131
|
-
const files = entries.filter((e) => e.type === "file");
|
|
132
|
-
if (files.length > 0) {
|
|
133
|
-
parts.push("### Relevant Files");
|
|
134
|
-
for (const entry of files) parts.push(`- ${entry.summary}`);
|
|
135
|
-
}
|
|
136
|
-
return parts.join("\n");
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
/**
|
|
140
|
-
* Create a context builder
|
|
141
|
-
*/
|
|
142
|
-
function createContextBuilder(context) {
|
|
143
|
-
return new ContextBuilder(context);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
//#endregion
|
|
147
|
-
export { ContextBuilder, createContextBuilder };
|
|
1
|
+
function e(e){return Math.ceil(e.length/4)}function t(e,t){if(!t)return .5;let n=t.toLowerCase(),r=0;return e.name.toLowerCase().includes(n)&&(r+=.4),e.description?.toLowerCase().includes(n)&&(r+=.3),e.tags?.some(e=>e.toLowerCase().includes(n))&&(r+=.2),Math.min(r,1)}function n(e,t){if(!t)return .5;let n=t.toLowerCase(),r=0;return e.path.toLowerCase().includes(n)&&(r+=.5),e.name.toLowerCase().includes(n)&&(r+=.3),e.isSpec&&(r+=.2),Math.min(r,1)}var r=class{context;constructor(e){this.context=e}build(r={}){let i=r.maxTokens??4e3,a=[],o=0;if(r.includeSpecs?.length)for(let t of r.includeSpecs){let n=this.context.getSpecs().find(e=>e.name===t);if(n){let t={type:`spec`,path:n.path,summary:`${n.type}: ${n.name}${n.description?` - ${n.description}`:``}`,relevance:1};a.push(t),o+=e(t.summary??``)}}if(r.includeFiles?.length)for(let t of r.includeFiles){let n=this.context.getFiles().find(e=>e.path===t);if(n){let t={type:`file`,path:n.path,summary:`File: ${n.relativePath}`,relevance:1};a.push(t),o+=e(t.summary??``)}}if(r.query){let n=this.context.getSpecs().map(e=>({spec:e,score:t(e,r.query)})).filter(({score:e})=>e>.2).sort((e,t)=>t.score-e.score);for(let{spec:t,score:r}of n){if(o>=i)break;if(a.some(e=>e.path===t.path))continue;let n={type:`spec`,path:t.path,summary:`${t.type}: ${t.name}${t.description?` - ${t.description}`:``}`,relevance:r};a.push(n),o+=e(n.summary??``)}}if(r.query){let t=this.context.getFiles().map(e=>({file:e,score:n(e,r.query)})).filter(({score:e})=>e>.2).sort((e,t)=>t.score-e.score);for(let{file:n,score:r}of t){if(o>=i)break;if(a.some(e=>e.path===n.path))continue;let t={type:`file`,path:n.path,summary:`File: ${n.relativePath}`,relevance:r};a.push(t),o+=e(t.summary??``)}}let s=this.buildSummary(a);return{entries:a,summary:s,totalTokensEstimate:o+e(s)}}buildSummary(e){if(e.length===0)return this.context.getContextSummary();let t=[],n=this.context.getSummary();t.push(`Workspace: ${n.name}`),t.push(``);let r=e.filter(e=>e.type===`spec`);if(r.length>0){t.push(`### Relevant Specs`);for(let e of r)t.push(`- ${e.summary}`);t.push(``)}let i=e.filter(e=>e.type===`file`);if(i.length>0){t.push(`### Relevant Files`);for(let e of i)t.push(`- ${e.summary}`)}return t.join(`
|
|
2
|
+
`)}};function i(e){return new r(e)}export{r as ContextBuilder,i as createContextBuilder};
|
|
@@ -1,174 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* File operations executor
|
|
4
|
-
*/
|
|
5
|
-
var FileOperations = class {
|
|
6
|
-
constructor(fs, workspacePath, allowWrites = false) {
|
|
7
|
-
this.fs = fs;
|
|
8
|
-
this.workspacePath = workspacePath;
|
|
9
|
-
this.allowWrites = allowWrites;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Read a file
|
|
13
|
-
*/
|
|
14
|
-
async read(relativePath) {
|
|
15
|
-
const fullPath = this.resolvePath(relativePath);
|
|
16
|
-
try {
|
|
17
|
-
return {
|
|
18
|
-
success: true,
|
|
19
|
-
path: relativePath,
|
|
20
|
-
content: await this.fs.readFile(fullPath)
|
|
21
|
-
};
|
|
22
|
-
} catch (error) {
|
|
23
|
-
return {
|
|
24
|
-
success: false,
|
|
25
|
-
path: relativePath,
|
|
26
|
-
error: error instanceof Error ? error.message : String(error)
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Write to a file
|
|
32
|
-
*/
|
|
33
|
-
async write(relativePath, content) {
|
|
34
|
-
if (!this.allowWrites) return {
|
|
35
|
-
success: false,
|
|
36
|
-
path: relativePath,
|
|
37
|
-
error: "File writes are not enabled"
|
|
38
|
-
};
|
|
39
|
-
const fullPath = this.resolvePath(relativePath);
|
|
40
|
-
try {
|
|
41
|
-
await this.fs.writeFile(fullPath, content);
|
|
42
|
-
return {
|
|
43
|
-
success: true,
|
|
44
|
-
path: relativePath
|
|
45
|
-
};
|
|
46
|
-
} catch (error) {
|
|
47
|
-
return {
|
|
48
|
-
success: false,
|
|
49
|
-
path: relativePath,
|
|
50
|
-
error: error instanceof Error ? error.message : String(error)
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Execute multiple file operations
|
|
56
|
-
*/
|
|
57
|
-
async execute(operations) {
|
|
58
|
-
const results = [];
|
|
59
|
-
for (const operation of operations) {
|
|
60
|
-
let result;
|
|
61
|
-
switch (operation.type) {
|
|
62
|
-
case "read": {
|
|
63
|
-
const readResult = await this.read(operation.path);
|
|
64
|
-
result = {
|
|
65
|
-
operation,
|
|
66
|
-
success: readResult.success,
|
|
67
|
-
content: readResult.content,
|
|
68
|
-
error: readResult.error
|
|
69
|
-
};
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
case "write":
|
|
73
|
-
case "create":
|
|
74
|
-
if (!operation.content) result = {
|
|
75
|
-
operation,
|
|
76
|
-
success: false,
|
|
77
|
-
error: "Content is required for write operations"
|
|
78
|
-
};
|
|
79
|
-
else {
|
|
80
|
-
const writeResult = await this.write(operation.path, operation.content);
|
|
81
|
-
result = {
|
|
82
|
-
operation,
|
|
83
|
-
success: writeResult.success,
|
|
84
|
-
error: writeResult.error
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
break;
|
|
88
|
-
case "delete":
|
|
89
|
-
if (!this.allowWrites) result = {
|
|
90
|
-
operation,
|
|
91
|
-
success: false,
|
|
92
|
-
error: "File writes are not enabled"
|
|
93
|
-
};
|
|
94
|
-
else try {
|
|
95
|
-
await this.fs.deleteFile(this.resolvePath(operation.path));
|
|
96
|
-
result = {
|
|
97
|
-
operation,
|
|
98
|
-
success: true
|
|
99
|
-
};
|
|
100
|
-
} catch (error) {
|
|
101
|
-
result = {
|
|
102
|
-
operation,
|
|
103
|
-
success: false,
|
|
104
|
-
error: error instanceof Error ? error.message : String(error)
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
break;
|
|
108
|
-
default: result = {
|
|
109
|
-
operation,
|
|
110
|
-
success: false,
|
|
111
|
-
error: `Unknown operation type: ${operation.type}`
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
results.push(result);
|
|
115
|
-
}
|
|
116
|
-
return results;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Resolve a relative path to an absolute path
|
|
120
|
-
*/
|
|
121
|
-
resolvePath(relativePath) {
|
|
122
|
-
const normalized = relativePath.replace(/\.\./g, "").replace(/^\//, "");
|
|
123
|
-
return `${this.workspacePath}/${normalized}`;
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
/**
|
|
127
|
-
* Create a file operations instance with Node.js fs
|
|
128
|
-
*/
|
|
129
|
-
function createNodeFileOperations(workspacePath, allowWrites = false) {
|
|
130
|
-
return new FileOperations({
|
|
131
|
-
async readFile(path) {
|
|
132
|
-
const { readFile } = await import("node:fs/promises");
|
|
133
|
-
return readFile(path, "utf-8");
|
|
134
|
-
},
|
|
135
|
-
async writeFile(path, content) {
|
|
136
|
-
const { writeFile, mkdir } = await import("node:fs/promises");
|
|
137
|
-
const { dirname } = await import("node:path");
|
|
138
|
-
await mkdir(dirname(path), { recursive: true });
|
|
139
|
-
await writeFile(path, content, "utf-8");
|
|
140
|
-
},
|
|
141
|
-
async exists(path) {
|
|
142
|
-
const { access } = await import("node:fs/promises");
|
|
143
|
-
try {
|
|
144
|
-
await access(path);
|
|
145
|
-
return true;
|
|
146
|
-
} catch {
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
async deleteFile(path) {
|
|
151
|
-
const { unlink } = await import("node:fs/promises");
|
|
152
|
-
await unlink(path);
|
|
153
|
-
},
|
|
154
|
-
async listFiles(directory, options) {
|
|
155
|
-
const { readdir } = await import("node:fs/promises");
|
|
156
|
-
const { join } = await import("node:path");
|
|
157
|
-
const files = [];
|
|
158
|
-
const entries = await readdir(directory, { withFileTypes: true });
|
|
159
|
-
for (const entry of entries) {
|
|
160
|
-
const fullPath = join(directory, entry.name);
|
|
161
|
-
if (entry.isDirectory() && options?.recursive) {
|
|
162
|
-
const subFiles = await this.listFiles(fullPath, options);
|
|
163
|
-
files.push(...subFiles);
|
|
164
|
-
} else if (entry.isFile()) {
|
|
165
|
-
if (!options?.pattern || entry.name.match(new RegExp(options.pattern))) files.push(fullPath);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return files;
|
|
169
|
-
}
|
|
170
|
-
}, workspacePath, allowWrites);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
//#endregion
|
|
174
|
-
export { FileOperations, createNodeFileOperations };
|
|
1
|
+
var e=class{constructor(e,t,n=!1){this.fs=e,this.workspacePath=t,this.allowWrites=n}async read(e){let t=this.resolvePath(e);try{return{success:!0,path:e,content:await this.fs.readFile(t)}}catch(t){return{success:!1,path:e,error:t instanceof Error?t.message:String(t)}}}async write(e,t){if(!this.allowWrites)return{success:!1,path:e,error:`File writes are not enabled`};let n=this.resolvePath(e);try{return await this.fs.writeFile(n,t),{success:!0,path:e}}catch(t){return{success:!1,path:e,error:t instanceof Error?t.message:String(t)}}}async execute(e){let t=[];for(let n of e){let e;switch(n.type){case`read`:{let t=await this.read(n.path);e={operation:n,success:t.success,content:t.content,error:t.error};break}case`write`:case`create`:if(!n.content)e={operation:n,success:!1,error:`Content is required for write operations`};else{let t=await this.write(n.path,n.content);e={operation:n,success:t.success,error:t.error}}break;case`delete`:if(!this.allowWrites)e={operation:n,success:!1,error:`File writes are not enabled`};else try{await this.fs.deleteFile(this.resolvePath(n.path)),e={operation:n,success:!0}}catch(t){e={operation:n,success:!1,error:t instanceof Error?t.message:String(t)}}break;default:e={operation:n,success:!1,error:`Unknown operation type: ${n.type}`}}t.push(e)}return t}resolvePath(e){let t=e.replace(/\.\./g,``).replace(/^\//,``);return`${this.workspacePath}/${t}`}};function t(t,n=!1){return new e({async readFile(e){let{readFile:t}=await import(`node:fs/promises`);return t(e,`utf-8`)},async writeFile(e,t){let{writeFile:n,mkdir:r}=await import(`node:fs/promises`),{dirname:i}=await import(`node:path`);await r(i(e),{recursive:!0}),await n(e,t,`utf-8`)},async exists(e){let{access:t}=await import(`node:fs/promises`);try{return await t(e),!0}catch{return!1}},async deleteFile(e){let{unlink:t}=await import(`node:fs/promises`);await t(e)},async listFiles(e,t){let{readdir:n}=await import(`node:fs/promises`),{join:r}=await import(`node:path`),i=[],a=await n(e,{withFileTypes:!0});for(let n of a){let a=r(e,n.name);if(n.isDirectory()&&t?.recursive){let e=await this.listFiles(a,t);i.push(...e)}else n.isFile()&&(!t?.pattern||n.name.match(new RegExp(t.pattern)))&&i.push(a)}return i}},t,n)}export{e as FileOperations,t as createNodeFileOperations};
|
package/dist/context/index.js
CHANGED
|
@@ -1,5 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ContextBuilder, createContextBuilder } from "./context-builder.js";
|
|
3
|
-
import { FileOperations, createNodeFileOperations } from "./file-operations.js";
|
|
4
|
-
|
|
5
|
-
export { ContextBuilder, FileOperations, WorkspaceContext, createContextBuilder, createNodeFileOperations, createWorkspaceContext };
|
|
1
|
+
import{WorkspaceContext as e,createWorkspaceContext as t}from"./workspace-context.js";import{ContextBuilder as n,createContextBuilder as r}from"./context-builder.js";import{FileOperations as i,createNodeFileOperations as a}from"./file-operations.js";export{n as ContextBuilder,i as FileOperations,e as WorkspaceContext,r as createContextBuilder,a as createNodeFileOperations,t as createWorkspaceContext};
|
|
@@ -1,123 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Workspace context for AI chat
|
|
4
|
-
*/
|
|
5
|
-
var WorkspaceContext = class {
|
|
6
|
-
workspacePath;
|
|
7
|
-
allowWrites;
|
|
8
|
-
specs = [];
|
|
9
|
-
files = [];
|
|
10
|
-
initialized = false;
|
|
11
|
-
constructor(config) {
|
|
12
|
-
this.workspacePath = config.workspacePath;
|
|
13
|
-
this.allowWrites = config.allowWrites ?? false;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Initialize the workspace context by scanning files
|
|
17
|
-
*/
|
|
18
|
-
async initialize() {
|
|
19
|
-
if (this.initialized) return;
|
|
20
|
-
this.initialized = true;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Get all discovered specs
|
|
24
|
-
*/
|
|
25
|
-
getSpecs() {
|
|
26
|
-
return this.specs;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Get all discovered files
|
|
30
|
-
*/
|
|
31
|
-
getFiles() {
|
|
32
|
-
return this.files;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Add specs to the context
|
|
36
|
-
*/
|
|
37
|
-
addSpecs(specs) {
|
|
38
|
-
this.specs.push(...specs);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Add files to the context
|
|
42
|
-
*/
|
|
43
|
-
addFiles(files) {
|
|
44
|
-
this.files.push(...files);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Get a summary of the workspace for context
|
|
48
|
-
*/
|
|
49
|
-
getSummary() {
|
|
50
|
-
const commands = this.specs.filter((s) => s.type === "command").length;
|
|
51
|
-
const queries = this.specs.filter((s) => s.type === "query").length;
|
|
52
|
-
const events = this.specs.filter((s) => s.type === "event").length;
|
|
53
|
-
const presentations = this.specs.filter((s) => s.type === "presentation").length;
|
|
54
|
-
const tsFiles = this.files.filter((f) => f.extension === ".ts").length;
|
|
55
|
-
const specFiles = this.files.filter((f) => f.isSpec).length;
|
|
56
|
-
return {
|
|
57
|
-
name: this.workspacePath.split("/").pop() ?? "workspace",
|
|
58
|
-
path: this.workspacePath,
|
|
59
|
-
specs: {
|
|
60
|
-
total: this.specs.length,
|
|
61
|
-
commands,
|
|
62
|
-
queries,
|
|
63
|
-
events,
|
|
64
|
-
presentations
|
|
65
|
-
},
|
|
66
|
-
files: {
|
|
67
|
-
total: this.files.length,
|
|
68
|
-
typescript: tsFiles,
|
|
69
|
-
specFiles
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Get a context summary for LLM prompts
|
|
75
|
-
*/
|
|
76
|
-
getContextSummary() {
|
|
77
|
-
const summary = this.getSummary();
|
|
78
|
-
const parts = [
|
|
79
|
-
`Workspace: ${summary.name}`,
|
|
80
|
-
`Path: ${summary.path}`,
|
|
81
|
-
"",
|
|
82
|
-
"### Specs",
|
|
83
|
-
`- Commands: ${summary.specs.commands}`,
|
|
84
|
-
`- Queries: ${summary.specs.queries}`,
|
|
85
|
-
`- Events: ${summary.specs.events}`,
|
|
86
|
-
`- Presentations: ${summary.specs.presentations}`
|
|
87
|
-
];
|
|
88
|
-
if (this.specs.length > 0) {
|
|
89
|
-
parts.push("", "### Available Specs");
|
|
90
|
-
for (const spec of this.specs.slice(0, 20)) parts.push(`- ${spec.name} (${spec.type})`);
|
|
91
|
-
if (this.specs.length > 20) parts.push(`- ... and ${this.specs.length - 20} more`);
|
|
92
|
-
}
|
|
93
|
-
return parts.join("\n");
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Find specs matching a query
|
|
97
|
-
*/
|
|
98
|
-
findSpecs(query) {
|
|
99
|
-
const lowerQuery = query.toLowerCase();
|
|
100
|
-
return this.specs.filter((s) => s.name.toLowerCase().includes(lowerQuery) || s.description?.toLowerCase().includes(lowerQuery) || s.tags?.some((t) => t.toLowerCase().includes(lowerQuery)));
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Find files matching a query
|
|
104
|
-
*/
|
|
105
|
-
findFiles(query) {
|
|
106
|
-
const lowerQuery = query.toLowerCase();
|
|
107
|
-
return this.files.filter((f) => f.path.toLowerCase().includes(lowerQuery) || f.name.toLowerCase().includes(lowerQuery));
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Create a workspace context from a path
|
|
112
|
-
*/
|
|
113
|
-
async function createWorkspaceContext(path, options) {
|
|
114
|
-
const context = new WorkspaceContext({
|
|
115
|
-
workspacePath: path,
|
|
116
|
-
...options
|
|
117
|
-
});
|
|
118
|
-
await context.initialize();
|
|
119
|
-
return context;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
//#endregion
|
|
123
|
-
export { WorkspaceContext, createWorkspaceContext };
|
|
1
|
+
var e=class{workspacePath;allowWrites;specs=[];files=[];initialized=!1;constructor(e){this.workspacePath=e.workspacePath,this.allowWrites=e.allowWrites??!1}async initialize(){this.initialized||=!0}getSpecs(){return this.specs}getFiles(){return this.files}addSpecs(e){this.specs.push(...e)}addFiles(e){this.files.push(...e)}getSummary(){let e=this.specs.filter(e=>e.type===`command`).length,t=this.specs.filter(e=>e.type===`query`).length,n=this.specs.filter(e=>e.type===`event`).length,r=this.specs.filter(e=>e.type===`presentation`).length,i=this.files.filter(e=>e.extension===`.ts`).length,a=this.files.filter(e=>e.isSpec).length;return{name:this.workspacePath.split(`/`).pop()??`workspace`,path:this.workspacePath,specs:{total:this.specs.length,commands:e,queries:t,events:n,presentations:r},files:{total:this.files.length,typescript:i,specFiles:a}}}getContextSummary(){let e=this.getSummary(),t=[`Workspace: ${e.name}`,`Path: ${e.path}`,``,`### Specs`,`- Commands: ${e.specs.commands}`,`- Queries: ${e.specs.queries}`,`- Events: ${e.specs.events}`,`- Presentations: ${e.specs.presentations}`];if(this.specs.length>0){t.push(``,`### Available Specs`);for(let e of this.specs.slice(0,20))t.push(`- ${e.name} (${e.type})`);this.specs.length>20&&t.push(`- ... and ${this.specs.length-20} more`)}return t.join(`
|
|
2
|
+
`)}findSpecs(e){let t=e.toLowerCase();return this.specs.filter(e=>e.name.toLowerCase().includes(t)||e.description?.toLowerCase().includes(t)||e.tags?.some(e=>e.toLowerCase().includes(t)))}findFiles(e){let t=e.toLowerCase();return this.files.filter(e=>e.path.toLowerCase().includes(t)||e.name.toLowerCase().includes(t))}};async function t(t,n){let r=new e({workspacePath:t,...n});return await r.initialize(),r}export{e as WorkspaceContext,t as createWorkspaceContext};
|