@contractspec/module.ai-chat 1.57.0 → 1.59.0

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 (120) hide show
  1. package/dist/ai-chat.capability.d.ts +2 -0
  2. package/dist/ai-chat.capability.d.ts.map +1 -0
  3. package/dist/ai-chat.feature.d.ts +1 -6
  4. package/dist/ai-chat.feature.d.ts.map +1 -1
  5. package/dist/ai-chat.operations.d.ts +217 -223
  6. package/dist/ai-chat.operations.d.ts.map +1 -1
  7. package/dist/browser/context/index.js +415 -0
  8. package/dist/browser/core/index.js +336 -0
  9. package/dist/browser/index.js +2291 -0
  10. package/dist/browser/presentation/components/index.js +974 -0
  11. package/dist/browser/presentation/hooks/index.js +556 -0
  12. package/dist/browser/presentation/index.js +1520 -0
  13. package/dist/browser/providers/index.js +51 -0
  14. package/dist/context/chat.test.d.ts +2 -0
  15. package/dist/context/chat.test.d.ts.map +1 -0
  16. package/dist/context/context-builder.d.ts +37 -36
  17. package/dist/context/context-builder.d.ts.map +1 -1
  18. package/dist/context/file-operations.d.ts +64 -67
  19. package/dist/context/file-operations.d.ts.map +1 -1
  20. package/dist/context/index.d.ts +7 -4
  21. package/dist/context/index.d.ts.map +1 -0
  22. package/dist/context/index.js +409 -4
  23. package/dist/context/workspace-context.d.ts +84 -87
  24. package/dist/context/workspace-context.d.ts.map +1 -1
  25. package/dist/core/chat-service.d.ts +56 -60
  26. package/dist/core/chat-service.d.ts.map +1 -1
  27. package/dist/core/conversation-store.d.ts +60 -61
  28. package/dist/core/conversation-store.d.ts.map +1 -1
  29. package/dist/core/index.d.ts +7 -4
  30. package/dist/core/index.d.ts.map +1 -0
  31. package/dist/core/index.js +330 -3
  32. package/dist/core/message-types.d.ts +94 -97
  33. package/dist/core/message-types.d.ts.map +1 -1
  34. package/dist/docs/ai-chat.docblock.d.ts +2 -0
  35. package/dist/docs/ai-chat.docblock.d.ts.map +1 -0
  36. package/dist/docs/index.d.ts +7 -0
  37. package/dist/docs/index.d.ts.map +1 -0
  38. package/dist/events.d.ts +103 -109
  39. package/dist/events.d.ts.map +1 -1
  40. package/dist/index.d.ts +16 -21
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +2286 -23
  43. package/dist/node/context/index.js +410 -0
  44. package/dist/node/core/index.js +331 -0
  45. package/dist/node/index.js +2286 -0
  46. package/dist/node/presentation/components/index.js +969 -0
  47. package/dist/node/presentation/hooks/index.js +551 -0
  48. package/dist/node/presentation/index.js +1515 -0
  49. package/dist/node/providers/index.js +46 -0
  50. package/dist/presentation/components/ChatContainer.d.ts +7 -16
  51. package/dist/presentation/components/ChatContainer.d.ts.map +1 -1
  52. package/dist/presentation/components/ChatInput.d.ts +17 -30
  53. package/dist/presentation/components/ChatInput.d.ts.map +1 -1
  54. package/dist/presentation/components/ChatMessage.d.ts +9 -19
  55. package/dist/presentation/components/ChatMessage.d.ts.map +1 -1
  56. package/dist/presentation/components/CodePreview.d.ts +20 -35
  57. package/dist/presentation/components/CodePreview.d.ts.map +1 -1
  58. package/dist/presentation/components/ContextIndicator.d.ts +11 -21
  59. package/dist/presentation/components/ContextIndicator.d.ts.map +1 -1
  60. package/dist/presentation/components/ModelPicker.d.ts +21 -32
  61. package/dist/presentation/components/ModelPicker.d.ts.map +1 -1
  62. package/dist/presentation/components/index.d.ts +10 -7
  63. package/dist/presentation/components/index.d.ts.map +1 -0
  64. package/dist/presentation/components/index.js +968 -7
  65. package/dist/presentation/hooks/index.d.ts +6 -3
  66. package/dist/presentation/hooks/index.d.ts.map +1 -0
  67. package/dist/presentation/hooks/index.js +550 -3
  68. package/dist/presentation/hooks/use-chat.test.d.ts +2 -0
  69. package/dist/presentation/hooks/use-chat.test.d.ts.map +1 -0
  70. package/dist/presentation/hooks/useChat.d.ts +50 -54
  71. package/dist/presentation/hooks/useChat.d.ts.map +1 -1
  72. package/dist/presentation/hooks/useProviders.d.ts +21 -25
  73. package/dist/presentation/hooks/useProviders.d.ts.map +1 -1
  74. package/dist/presentation/index.d.ts +8 -11
  75. package/dist/presentation/index.d.ts.map +1 -0
  76. package/dist/presentation/index.js +1515 -12
  77. package/dist/providers/chat-utilities.d.ts +18 -7
  78. package/dist/providers/chat-utilities.d.ts.map +1 -1
  79. package/dist/providers/index.d.ts +8 -3
  80. package/dist/providers/index.d.ts.map +1 -0
  81. package/dist/providers/index.js +45 -3
  82. package/dist/schema.d.ts +195 -200
  83. package/dist/schema.d.ts.map +1 -1
  84. package/package.json +123 -34
  85. package/dist/ai-chat.feature.js +0 -102
  86. package/dist/ai-chat.feature.js.map +0 -1
  87. package/dist/ai-chat.operations.js +0 -172
  88. package/dist/ai-chat.operations.js.map +0 -1
  89. package/dist/context/context-builder.js +0 -148
  90. package/dist/context/context-builder.js.map +0 -1
  91. package/dist/context/file-operations.js +0 -175
  92. package/dist/context/file-operations.js.map +0 -1
  93. package/dist/context/workspace-context.js +0 -124
  94. package/dist/context/workspace-context.js.map +0 -1
  95. package/dist/core/chat-service.js +0 -227
  96. package/dist/core/chat-service.js.map +0 -1
  97. package/dist/core/conversation-store.js +0 -109
  98. package/dist/core/conversation-store.js.map +0 -1
  99. package/dist/events.js +0 -98
  100. package/dist/events.js.map +0 -1
  101. package/dist/presentation/components/ChatContainer.js +0 -63
  102. package/dist/presentation/components/ChatContainer.js.map +0 -1
  103. package/dist/presentation/components/ChatInput.js +0 -149
  104. package/dist/presentation/components/ChatInput.js.map +0 -1
  105. package/dist/presentation/components/ChatMessage.js +0 -136
  106. package/dist/presentation/components/ChatMessage.js.map +0 -1
  107. package/dist/presentation/components/CodePreview.js +0 -127
  108. package/dist/presentation/components/CodePreview.js.map +0 -1
  109. package/dist/presentation/components/ContextIndicator.js +0 -97
  110. package/dist/presentation/components/ContextIndicator.js.map +0 -1
  111. package/dist/presentation/components/ModelPicker.js +0 -202
  112. package/dist/presentation/components/ModelPicker.js.map +0 -1
  113. package/dist/presentation/hooks/useChat.js +0 -172
  114. package/dist/presentation/hooks/useChat.js.map +0 -1
  115. package/dist/presentation/hooks/useProviders.js +0 -41
  116. package/dist/presentation/hooks/useProviders.js.map +0 -1
  117. package/dist/providers/chat-utilities.js +0 -17
  118. package/dist/providers/chat-utilities.js.map +0 -1
  119. package/dist/schema.js +0 -100
  120. package/dist/schema.js.map +0 -1
@@ -1,175 +0,0 @@
1
- //#region src/context/file-operations.ts
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 };
175
- //# sourceMappingURL=file-operations.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-operations.js","names":[],"sources":["../../src/context/file-operations.ts"],"sourcesContent":["/**\n * File operations for workspace context\n *\n * Provides read/write operations for files in the workspace.\n */\n\n/**\n * Result of a file read operation\n */\nexport interface FileReadResult {\n success: boolean;\n path: string;\n content?: string;\n error?: string;\n}\n\n/**\n * Result of a file write operation\n */\nexport interface FileWriteResult {\n success: boolean;\n path: string;\n error?: string;\n}\n\n/**\n * File operation to perform\n */\nexport interface FileOperation {\n type: 'read' | 'write' | 'create' | 'delete';\n path: string;\n content?: string;\n}\n\n/**\n * Result of a file operation\n */\nexport interface FileOperationResult {\n operation: FileOperation;\n success: boolean;\n content?: string;\n error?: string;\n}\n\n/**\n * Interface for file system operations\n */\nexport interface FileSystem {\n /**\n * Read a file's contents\n */\n readFile(path: string): Promise<string>;\n\n /**\n * Write content to a file\n */\n writeFile(path: string, content: string): Promise<void>;\n\n /**\n * Check if a file exists\n */\n exists(path: string): Promise<boolean>;\n\n /**\n * Delete a file\n */\n deleteFile(path: string): Promise<void>;\n\n /**\n * List files in a directory\n */\n listFiles(\n directory: string,\n options?: { recursive?: boolean; pattern?: string }\n ): Promise<string[]>;\n}\n\n/**\n * File operations executor\n */\nexport class FileOperations {\n constructor(\n private readonly fs: FileSystem,\n private readonly workspacePath: string,\n private readonly allowWrites = false\n ) {}\n\n /**\n * Read a file\n */\n async read(relativePath: string): Promise<FileReadResult> {\n const fullPath = this.resolvePath(relativePath);\n\n try {\n const content = await this.fs.readFile(fullPath);\n return { success: true, path: relativePath, content };\n } catch (error) {\n return {\n success: false,\n path: relativePath,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n /**\n * Write to a file\n */\n async write(relativePath: string, content: string): Promise<FileWriteResult> {\n if (!this.allowWrites) {\n return {\n success: false,\n path: relativePath,\n error: 'File writes are not enabled',\n };\n }\n\n const fullPath = this.resolvePath(relativePath);\n\n try {\n await this.fs.writeFile(fullPath, content);\n return { success: true, path: relativePath };\n } catch (error) {\n return {\n success: false,\n path: relativePath,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n /**\n * Execute multiple file operations\n */\n async execute(operations: FileOperation[]): Promise<FileOperationResult[]> {\n const results: FileOperationResult[] = [];\n\n for (const operation of operations) {\n let result: FileOperationResult;\n\n switch (operation.type) {\n case 'read': {\n const readResult = await this.read(operation.path);\n result = {\n operation,\n success: readResult.success,\n content: readResult.content,\n error: readResult.error,\n };\n break;\n }\n\n case 'write':\n case 'create': {\n if (!operation.content) {\n result = {\n operation,\n success: false,\n error: 'Content is required for write operations',\n };\n } else {\n const writeResult = await this.write(\n operation.path,\n operation.content\n );\n result = {\n operation,\n success: writeResult.success,\n error: writeResult.error,\n };\n }\n break;\n }\n\n case 'delete': {\n if (!this.allowWrites) {\n result = {\n operation,\n success: false,\n error: 'File writes are not enabled',\n };\n } else {\n try {\n await this.fs.deleteFile(this.resolvePath(operation.path));\n result = { operation, success: true };\n } catch (error) {\n result = {\n operation,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n break;\n }\n\n default:\n result = {\n operation,\n success: false,\n error: `Unknown operation type: ${(operation as FileOperation).type}`,\n };\n }\n\n results.push(result);\n }\n\n return results;\n }\n\n /**\n * Resolve a relative path to an absolute path\n */\n private resolvePath(relativePath: string): string {\n // Prevent path traversal\n const normalized = relativePath.replace(/\\.\\./g, '').replace(/^\\//, '');\n return `${this.workspacePath}/${normalized}`;\n }\n}\n\n/**\n * Create a file operations instance with Node.js fs\n */\nexport function createNodeFileOperations(\n workspacePath: string,\n allowWrites = false\n): FileOperations {\n // Dynamic import to avoid issues in browser environments\n const fs: FileSystem = {\n async readFile(path: string): Promise<string> {\n const { readFile } = await import('node:fs/promises');\n return readFile(path, 'utf-8');\n },\n async writeFile(path: string, content: string): Promise<void> {\n const { writeFile, mkdir } = await import('node:fs/promises');\n const { dirname } = await import('node:path');\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content, 'utf-8');\n },\n async exists(path: string): Promise<boolean> {\n const { access } = await import('node:fs/promises');\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n },\n async deleteFile(path: string): Promise<void> {\n const { unlink } = await import('node:fs/promises');\n await unlink(path);\n },\n async listFiles(\n directory: string,\n options?: { recursive?: boolean; pattern?: string }\n ): Promise<string[]> {\n const { readdir } = await import('node:fs/promises');\n const { join } = await import('node:path');\n\n const files: string[] = [];\n const entries = await readdir(directory, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(directory, entry.name);\n if (entry.isDirectory() && options?.recursive) {\n const subFiles = await this.listFiles(fullPath, options);\n files.push(...subFiles);\n } else if (entry.isFile()) {\n if (\n !options?.pattern ||\n entry.name.match(new RegExp(options.pattern))\n ) {\n files.push(fullPath);\n }\n }\n }\n\n return files;\n },\n };\n\n return new FileOperations(fs, workspacePath, allowWrites);\n}\n"],"mappings":"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"}
@@ -1,124 +0,0 @@
1
- //#region src/context/workspace-context.ts
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 };
124
- //# sourceMappingURL=workspace-context.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workspace-context.js","names":[],"sources":["../../src/context/workspace-context.ts"],"sourcesContent":["/**\n * Workspace context management\n *\n * Provides access to specs, files, and codebase information\n * for context-aware AI chat assistance.\n */\n\n/**\n * Spec information for context\n */\nexport interface SpecInfo {\n name: string;\n version: string;\n type: 'command' | 'query' | 'event' | 'presentation';\n path: string;\n description?: string;\n tags?: string[];\n}\n\n/**\n * File information for context\n */\nexport interface FileInfo {\n path: string;\n relativePath: string;\n name: string;\n extension: string;\n size: number;\n isSpec: boolean;\n}\n\n/**\n * Workspace summary for context\n */\nexport interface WorkspaceSummary {\n name: string;\n path: string;\n specs: {\n total: number;\n commands: number;\n queries: number;\n events: number;\n presentations: number;\n };\n files: {\n total: number;\n typescript: number;\n specFiles: number;\n };\n}\n\n/**\n * Configuration for workspace context\n */\nexport interface WorkspaceContextConfig {\n /** Root path of the workspace */\n workspacePath: string;\n /** File patterns to include */\n includePatterns?: string[];\n /** File patterns to exclude */\n excludePatterns?: string[];\n /** Maximum file size to read (bytes) */\n maxFileSize?: number;\n /** Whether to enable file writes */\n allowWrites?: boolean;\n}\n\n/**\n * Workspace context for AI chat\n */\nexport class WorkspaceContext {\n readonly workspacePath: string;\n readonly allowWrites: boolean;\n\n private specs: SpecInfo[] = [];\n private files: FileInfo[] = [];\n private initialized = false;\n\n constructor(config: WorkspaceContextConfig) {\n this.workspacePath = config.workspacePath;\n this.allowWrites = config.allowWrites ?? false;\n }\n\n /**\n * Initialize the workspace context by scanning files\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // This would scan the workspace for specs and files\n // For now, we just mark as initialized\n this.initialized = true;\n }\n\n /**\n * Get all discovered specs\n */\n getSpecs(): SpecInfo[] {\n return this.specs;\n }\n\n /**\n * Get all discovered files\n */\n getFiles(): FileInfo[] {\n return this.files;\n }\n\n /**\n * Add specs to the context\n */\n addSpecs(specs: SpecInfo[]): void {\n this.specs.push(...specs);\n }\n\n /**\n * Add files to the context\n */\n addFiles(files: FileInfo[]): void {\n this.files.push(...files);\n }\n\n /**\n * Get a summary of the workspace for context\n */\n getSummary(): WorkspaceSummary {\n const commands = this.specs.filter((s) => s.type === 'command').length;\n const queries = this.specs.filter((s) => s.type === 'query').length;\n const events = this.specs.filter((s) => s.type === 'event').length;\n const presentations = this.specs.filter(\n (s) => s.type === 'presentation'\n ).length;\n\n const tsFiles = this.files.filter((f) => f.extension === '.ts').length;\n const specFiles = this.files.filter((f) => f.isSpec).length;\n\n return {\n name: this.workspacePath.split('/').pop() ?? 'workspace',\n path: this.workspacePath,\n specs: {\n total: this.specs.length,\n commands,\n queries,\n events,\n presentations,\n },\n files: {\n total: this.files.length,\n typescript: tsFiles,\n specFiles,\n },\n };\n }\n\n /**\n * Get a context summary for LLM prompts\n */\n getContextSummary(): string {\n const summary = this.getSummary();\n\n const parts: string[] = [\n `Workspace: ${summary.name}`,\n `Path: ${summary.path}`,\n '',\n '### Specs',\n `- Commands: ${summary.specs.commands}`,\n `- Queries: ${summary.specs.queries}`,\n `- Events: ${summary.specs.events}`,\n `- Presentations: ${summary.specs.presentations}`,\n ];\n\n if (this.specs.length > 0) {\n parts.push('', '### Available Specs');\n for (const spec of this.specs.slice(0, 20)) {\n parts.push(`- ${spec.name} (${spec.type})`);\n }\n if (this.specs.length > 20) {\n parts.push(`- ... and ${this.specs.length - 20} more`);\n }\n }\n\n return parts.join('\\n');\n }\n\n /**\n * Find specs matching a query\n */\n findSpecs(query: string): SpecInfo[] {\n const lowerQuery = query.toLowerCase();\n return this.specs.filter(\n (s) =>\n s.name.toLowerCase().includes(lowerQuery) ||\n s.description?.toLowerCase().includes(lowerQuery) ||\n s.tags?.some((t) => t.toLowerCase().includes(lowerQuery))\n );\n }\n\n /**\n * Find files matching a query\n */\n findFiles(query: string): FileInfo[] {\n const lowerQuery = query.toLowerCase();\n return this.files.filter(\n (f) =>\n f.path.toLowerCase().includes(lowerQuery) ||\n f.name.toLowerCase().includes(lowerQuery)\n );\n }\n}\n\n/**\n * Create a workspace context from a path\n */\nexport async function createWorkspaceContext(\n path: string,\n options?: Partial<WorkspaceContextConfig>\n): Promise<WorkspaceContext> {\n const context = new WorkspaceContext({\n workspacePath: path,\n ...options,\n });\n await context.initialize();\n return context;\n}\n"],"mappings":";;;;AAsEA,IAAa,mBAAb,MAA8B;CAC5B,AAAS;CACT,AAAS;CAET,AAAQ,QAAoB,EAAE;CAC9B,AAAQ,QAAoB,EAAE;CAC9B,AAAQ,cAAc;CAEtB,YAAY,QAAgC;AAC1C,OAAK,gBAAgB,OAAO;AAC5B,OAAK,cAAc,OAAO,eAAe;;;;;CAM3C,MAAM,aAA4B;AAChC,MAAI,KAAK,YAAa;AAItB,OAAK,cAAc;;;;;CAMrB,WAAuB;AACrB,SAAO,KAAK;;;;;CAMd,WAAuB;AACrB,SAAO,KAAK;;;;;CAMd,SAAS,OAAyB;AAChC,OAAK,MAAM,KAAK,GAAG,MAAM;;;;;CAM3B,SAAS,OAAyB;AAChC,OAAK,MAAM,KAAK,GAAG,MAAM;;;;;CAM3B,aAA+B;EAC7B,MAAM,WAAW,KAAK,MAAM,QAAQ,MAAM,EAAE,SAAS,UAAU,CAAC;EAChE,MAAM,UAAU,KAAK,MAAM,QAAQ,MAAM,EAAE,SAAS,QAAQ,CAAC;EAC7D,MAAM,SAAS,KAAK,MAAM,QAAQ,MAAM,EAAE,SAAS,QAAQ,CAAC;EAC5D,MAAM,gBAAgB,KAAK,MAAM,QAC9B,MAAM,EAAE,SAAS,eACnB,CAAC;EAEF,MAAM,UAAU,KAAK,MAAM,QAAQ,MAAM,EAAE,cAAc,MAAM,CAAC;EAChE,MAAM,YAAY,KAAK,MAAM,QAAQ,MAAM,EAAE,OAAO,CAAC;AAErD,SAAO;GACL,MAAM,KAAK,cAAc,MAAM,IAAI,CAAC,KAAK,IAAI;GAC7C,MAAM,KAAK;GACX,OAAO;IACL,OAAO,KAAK,MAAM;IAClB;IACA;IACA;IACA;IACD;GACD,OAAO;IACL,OAAO,KAAK,MAAM;IAClB,YAAY;IACZ;IACD;GACF;;;;;CAMH,oBAA4B;EAC1B,MAAM,UAAU,KAAK,YAAY;EAEjC,MAAM,QAAkB;GACtB,cAAc,QAAQ;GACtB,SAAS,QAAQ;GACjB;GACA;GACA,eAAe,QAAQ,MAAM;GAC7B,cAAc,QAAQ,MAAM;GAC5B,aAAa,QAAQ,MAAM;GAC3B,oBAAoB,QAAQ,MAAM;GACnC;AAED,MAAI,KAAK,MAAM,SAAS,GAAG;AACzB,SAAM,KAAK,IAAI,sBAAsB;AACrC,QAAK,MAAM,QAAQ,KAAK,MAAM,MAAM,GAAG,GAAG,CACxC,OAAM,KAAK,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,GAAG;AAE7C,OAAI,KAAK,MAAM,SAAS,GACtB,OAAM,KAAK,aAAa,KAAK,MAAM,SAAS,GAAG,OAAO;;AAI1D,SAAO,MAAM,KAAK,KAAK;;;;;CAMzB,UAAU,OAA2B;EACnC,MAAM,aAAa,MAAM,aAAa;AACtC,SAAO,KAAK,MAAM,QACf,MACC,EAAE,KAAK,aAAa,CAAC,SAAS,WAAW,IACzC,EAAE,aAAa,aAAa,CAAC,SAAS,WAAW,IACjD,EAAE,MAAM,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,WAAW,CAAC,CAC5D;;;;;CAMH,UAAU,OAA2B;EACnC,MAAM,aAAa,MAAM,aAAa;AACtC,SAAO,KAAK,MAAM,QACf,MACC,EAAE,KAAK,aAAa,CAAC,SAAS,WAAW,IACzC,EAAE,KAAK,aAAa,CAAC,SAAS,WAAW,CAC5C;;;;;;AAOL,eAAsB,uBACpB,MACA,SAC2B;CAC3B,MAAM,UAAU,IAAI,iBAAiB;EACnC,eAAe;EACf,GAAG;EACJ,CAAC;AACF,OAAM,QAAQ,YAAY;AAC1B,QAAO"}
@@ -1,227 +0,0 @@
1
- import { InMemoryConversationStore } from "./conversation-store.js";
2
- import { generateText, streamText } from "ai";
3
-
4
- //#region src/core/chat-service.ts
5
- /**
6
- * Main chat orchestration service
7
- */
8
- /**
9
- * Default system prompt for ContractSpec vibe coding
10
- */
11
- const DEFAULT_SYSTEM_PROMPT = `You are ContractSpec AI, an expert coding assistant specialized in ContractSpec development.
12
-
13
- Your capabilities:
14
- - Help users create, modify, and understand ContractSpec specifications
15
- - Generate code that follows ContractSpec patterns and best practices
16
- - Explain concepts from the ContractSpec documentation
17
- - Suggest improvements and identify issues in specs and implementations
18
-
19
- Guidelines:
20
- - Be concise but thorough
21
- - Provide code examples when helpful
22
- - Reference relevant ContractSpec concepts and patterns
23
- - Ask clarifying questions when the user's intent is unclear
24
- - When suggesting code changes, explain the rationale`;
25
- /**
26
- * Main chat service for AI-powered conversations
27
- */
28
- var ChatService = class {
29
- provider;
30
- context;
31
- store;
32
- systemPrompt;
33
- maxHistoryMessages;
34
- onUsage;
35
- constructor(config) {
36
- this.provider = config.provider;
37
- this.context = config.context;
38
- this.store = config.store ?? new InMemoryConversationStore();
39
- this.systemPrompt = config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
40
- this.maxHistoryMessages = config.maxHistoryMessages ?? 20;
41
- this.onUsage = config.onUsage;
42
- }
43
- /**
44
- * Send a message and get a complete response
45
- */
46
- async send(options) {
47
- let conversation;
48
- if (options.conversationId) {
49
- const existing = await this.store.get(options.conversationId);
50
- if (!existing) throw new Error(`Conversation ${options.conversationId} not found`);
51
- conversation = existing;
52
- } else conversation = await this.store.create({
53
- status: "active",
54
- provider: this.provider.name,
55
- model: this.provider.model,
56
- messages: [],
57
- workspacePath: this.context?.workspacePath
58
- });
59
- await this.store.appendMessage(conversation.id, {
60
- role: "user",
61
- content: options.content,
62
- status: "completed",
63
- attachments: options.attachments
64
- });
65
- const prompt = this.buildPrompt(conversation, options);
66
- const model = this.provider.getModel();
67
- try {
68
- const result = await generateText({
69
- model,
70
- prompt,
71
- system: this.systemPrompt
72
- });
73
- const assistantMessage = await this.store.appendMessage(conversation.id, {
74
- role: "assistant",
75
- content: result.text,
76
- status: "completed"
77
- });
78
- const updatedConversation = await this.store.get(conversation.id);
79
- if (!updatedConversation) throw new Error("Conversation lost after update");
80
- return {
81
- message: assistantMessage,
82
- conversation: updatedConversation
83
- };
84
- } catch (error) {
85
- await this.store.appendMessage(conversation.id, {
86
- role: "assistant",
87
- content: "",
88
- status: "error",
89
- error: {
90
- code: "generation_failed",
91
- message: error instanceof Error ? error.message : String(error)
92
- }
93
- });
94
- throw error;
95
- }
96
- }
97
- /**
98
- * Send a message and get a streaming response
99
- */
100
- async stream(options) {
101
- let conversation;
102
- if (options.conversationId) {
103
- const existing = await this.store.get(options.conversationId);
104
- if (!existing) throw new Error(`Conversation ${options.conversationId} not found`);
105
- conversation = existing;
106
- } else conversation = await this.store.create({
107
- status: "active",
108
- provider: this.provider.name,
109
- model: this.provider.model,
110
- messages: [],
111
- workspacePath: this.context?.workspacePath
112
- });
113
- await this.store.appendMessage(conversation.id, {
114
- role: "user",
115
- content: options.content,
116
- status: "completed",
117
- attachments: options.attachments
118
- });
119
- const assistantMessage = await this.store.appendMessage(conversation.id, {
120
- role: "assistant",
121
- content: "",
122
- status: "streaming"
123
- });
124
- const prompt = this.buildPrompt(conversation, options);
125
- const model = this.provider.getModel();
126
- const self = {
127
- systemPrompt: this.systemPrompt,
128
- store: this.store
129
- };
130
- async function* streamGenerator() {
131
- let fullContent = "";
132
- try {
133
- const result = streamText({
134
- model,
135
- prompt,
136
- system: self.systemPrompt
137
- });
138
- for await (const chunk of result.textStream) {
139
- fullContent += chunk;
140
- yield {
141
- type: "text",
142
- content: chunk
143
- };
144
- }
145
- await self.store.updateMessage(conversation.id, assistantMessage.id, {
146
- content: fullContent,
147
- status: "completed"
148
- });
149
- yield { type: "done" };
150
- } catch (error) {
151
- await self.store.updateMessage(conversation.id, assistantMessage.id, {
152
- content: fullContent,
153
- status: "error",
154
- error: {
155
- code: "stream_failed",
156
- message: error instanceof Error ? error.message : String(error)
157
- }
158
- });
159
- yield {
160
- type: "error",
161
- error: {
162
- code: "stream_failed",
163
- message: error instanceof Error ? error.message : String(error)
164
- }
165
- };
166
- }
167
- }
168
- return {
169
- conversationId: conversation.id,
170
- messageId: assistantMessage.id,
171
- stream: streamGenerator()
172
- };
173
- }
174
- /**
175
- * Get a conversation by ID
176
- */
177
- async getConversation(conversationId) {
178
- return this.store.get(conversationId);
179
- }
180
- /**
181
- * List conversations
182
- */
183
- async listConversations(options) {
184
- return this.store.list({
185
- status: "active",
186
- ...options
187
- });
188
- }
189
- /**
190
- * Delete a conversation
191
- */
192
- async deleteConversation(conversationId) {
193
- return this.store.delete(conversationId);
194
- }
195
- /**
196
- * Build prompt string for LLM
197
- */
198
- buildPrompt(conversation, options) {
199
- let prompt = "";
200
- const historyStart = Math.max(0, conversation.messages.length - this.maxHistoryMessages);
201
- for (let i = historyStart; i < conversation.messages.length; i++) {
202
- const msg = conversation.messages[i];
203
- if (!msg) continue;
204
- if (msg.role === "user" || msg.role === "assistant") prompt += `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}\n\n`;
205
- }
206
- let content = options.content;
207
- if (options.attachments?.length) {
208
- const attachmentInfo = options.attachments.map((a) => {
209
- if (a.type === "file" || a.type === "code") return `\n\n### ${a.name}\n\`\`\`\n${a.content}\n\`\`\``;
210
- return `\n\n[Attachment: ${a.name}]`;
211
- }).join("");
212
- content += attachmentInfo;
213
- }
214
- prompt += `User: ${content}\n\nAssistant:`;
215
- return prompt;
216
- }
217
- };
218
- /**
219
- * Create a chat service with the given configuration
220
- */
221
- function createChatService(config) {
222
- return new ChatService(config);
223
- }
224
-
225
- //#endregion
226
- export { ChatService, createChatService };
227
- //# sourceMappingURL=chat-service.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chat-service.js","names":[],"sources":["../../src/core/chat-service.ts"],"sourcesContent":["/**\n * Main chat orchestration service\n */\nimport { generateText, streamText } from 'ai';\nimport type { Provider as ChatProvider } from '@contractspec/lib.ai-providers';\nimport type { WorkspaceContext } from '../context/workspace-context';\nimport type { ConversationStore } from './conversation-store';\nimport { InMemoryConversationStore } from './conversation-store';\nimport type {\n ChatConversation,\n ChatStreamChunk,\n SendMessageOptions,\n SendMessageResult,\n StreamMessageResult,\n} from './message-types';\n\n/**\n * Configuration for ChatService\n */\nexport interface ChatServiceConfig {\n /** LLM provider to use */\n provider: ChatProvider;\n /** Optional workspace context for code-aware chat */\n context?: WorkspaceContext;\n /** Optional conversation store (defaults to in-memory) */\n store?: ConversationStore;\n /** Default system prompt */\n systemPrompt?: string;\n /** Maximum conversation history to include */\n maxHistoryMessages?: number;\n /** Callback for usage tracking */\n onUsage?: (usage: { inputTokens: number; outputTokens: number }) => void;\n}\n\n/**\n * Default system prompt for ContractSpec vibe coding\n */\nconst DEFAULT_SYSTEM_PROMPT = `You are ContractSpec AI, an expert coding assistant specialized in ContractSpec development.\n\nYour capabilities:\n- Help users create, modify, and understand ContractSpec specifications\n- Generate code that follows ContractSpec patterns and best practices\n- Explain concepts from the ContractSpec documentation\n- Suggest improvements and identify issues in specs and implementations\n\nGuidelines:\n- Be concise but thorough\n- Provide code examples when helpful\n- Reference relevant ContractSpec concepts and patterns\n- Ask clarifying questions when the user's intent is unclear\n- When suggesting code changes, explain the rationale`;\n\n/**\n * Main chat service for AI-powered conversations\n */\nexport class ChatService {\n private readonly provider: ChatProvider;\n private readonly context?: WorkspaceContext;\n private readonly store: ConversationStore;\n private readonly systemPrompt: string;\n private readonly maxHistoryMessages: number;\n private readonly onUsage?: (usage: {\n inputTokens: number;\n outputTokens: number;\n }) => void;\n\n constructor(config: ChatServiceConfig) {\n this.provider = config.provider;\n this.context = config.context;\n this.store = config.store ?? new InMemoryConversationStore();\n this.systemPrompt = config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n this.maxHistoryMessages = config.maxHistoryMessages ?? 20;\n this.onUsage = config.onUsage;\n }\n\n /**\n * Send a message and get a complete response\n */\n async send(options: SendMessageOptions): Promise<SendMessageResult> {\n // Get or create conversation\n let conversation: ChatConversation;\n if (options.conversationId) {\n const existing = await this.store.get(options.conversationId);\n if (!existing) {\n throw new Error(`Conversation ${options.conversationId} not found`);\n }\n conversation = existing;\n } else {\n conversation = await this.store.create({\n status: 'active',\n provider: this.provider.name,\n model: this.provider.model,\n messages: [],\n workspacePath: this.context?.workspacePath,\n });\n }\n\n // Add user message\n await this.store.appendMessage(conversation.id, {\n role: 'user',\n content: options.content,\n status: 'completed',\n attachments: options.attachments,\n });\n\n // Build prompt from messages\n const prompt = this.buildPrompt(conversation, options);\n\n // Get the language model\n const model = this.provider.getModel();\n\n try {\n // Generate response\n const result = await generateText({\n model,\n prompt,\n system: this.systemPrompt,\n });\n\n // Save assistant message\n const assistantMessage = await this.store.appendMessage(conversation.id, {\n role: 'assistant',\n content: result.text,\n status: 'completed',\n });\n\n // Refresh conversation\n const updatedConversation = await this.store.get(conversation.id);\n if (!updatedConversation) {\n throw new Error('Conversation lost after update');\n }\n\n return {\n message: assistantMessage,\n conversation: updatedConversation,\n };\n } catch (error) {\n // Save error message\n await this.store.appendMessage(conversation.id, {\n role: 'assistant',\n content: '',\n status: 'error',\n error: {\n code: 'generation_failed',\n message: error instanceof Error ? error.message : String(error),\n },\n });\n\n throw error;\n }\n }\n\n /**\n * Send a message and get a streaming response\n */\n async stream(options: SendMessageOptions): Promise<StreamMessageResult> {\n // Get or create conversation\n let conversation: ChatConversation;\n if (options.conversationId) {\n const existing = await this.store.get(options.conversationId);\n if (!existing) {\n throw new Error(`Conversation ${options.conversationId} not found`);\n }\n conversation = existing;\n } else {\n conversation = await this.store.create({\n status: 'active',\n provider: this.provider.name,\n model: this.provider.model,\n messages: [],\n workspacePath: this.context?.workspacePath,\n });\n }\n\n // Add user message\n await this.store.appendMessage(conversation.id, {\n role: 'user',\n content: options.content,\n status: 'completed',\n attachments: options.attachments,\n });\n\n // Create placeholder for assistant message\n const assistantMessage = await this.store.appendMessage(conversation.id, {\n role: 'assistant',\n content: '',\n status: 'streaming',\n });\n\n // Build prompt\n const prompt = this.buildPrompt(conversation, options);\n\n // Get the language model\n const model = this.provider.getModel();\n\n // Create async generator for streaming\n const self = {\n systemPrompt: this.systemPrompt,\n store: this.store,\n };\n async function* streamGenerator(): AsyncIterable<ChatStreamChunk> {\n let fullContent = '';\n\n try {\n const result = streamText({\n model,\n prompt,\n system: self.systemPrompt,\n });\n\n for await (const chunk of result.textStream) {\n fullContent += chunk;\n yield { type: 'text', content: chunk };\n }\n\n // Update message with final content\n await self.store.updateMessage(conversation.id, assistantMessage.id, {\n content: fullContent,\n status: 'completed',\n });\n\n yield {\n type: 'done',\n };\n } catch (error) {\n await self.store.updateMessage(conversation.id, assistantMessage.id, {\n content: fullContent,\n status: 'error',\n error: {\n code: 'stream_failed',\n message: error instanceof Error ? error.message : String(error),\n },\n });\n\n yield {\n type: 'error',\n error: {\n code: 'stream_failed',\n message: error instanceof Error ? error.message : String(error),\n },\n };\n }\n }\n\n return {\n conversationId: conversation.id,\n messageId: assistantMessage.id,\n stream: streamGenerator(),\n };\n }\n\n /**\n * Get a conversation by ID\n */\n async getConversation(\n conversationId: string\n ): Promise<ChatConversation | null> {\n return this.store.get(conversationId);\n }\n\n /**\n * List conversations\n */\n async listConversations(options?: {\n limit?: number;\n offset?: number;\n }): Promise<ChatConversation[]> {\n return this.store.list({\n status: 'active',\n ...options,\n });\n }\n\n /**\n * Delete a conversation\n */\n async deleteConversation(conversationId: string): Promise<boolean> {\n return this.store.delete(conversationId);\n }\n\n /**\n * Build prompt string for LLM\n */\n private buildPrompt(\n conversation: ChatConversation,\n options: SendMessageOptions\n ): string {\n let prompt = '';\n\n // Add conversation history (limited)\n const historyStart = Math.max(\n 0,\n conversation.messages.length - this.maxHistoryMessages\n );\n for (let i = historyStart; i < conversation.messages.length; i++) {\n const msg = conversation.messages[i];\n if (!msg) continue;\n if (msg.role === 'user' || msg.role === 'assistant') {\n prompt += `${msg.role === 'user' ? 'User' : 'Assistant'}: ${msg.content}\\n\\n`;\n }\n }\n\n // Add current message with attachments\n let content = options.content;\n if (options.attachments?.length) {\n const attachmentInfo = options.attachments\n .map((a) => {\n if (a.type === 'file' || a.type === 'code') {\n return `\\n\\n### ${a.name}\\n\\`\\`\\`\\n${a.content}\\n\\`\\`\\``;\n }\n return `\\n\\n[Attachment: ${a.name}]`;\n })\n .join('');\n content += attachmentInfo;\n }\n\n prompt += `User: ${content}\\n\\nAssistant:`;\n\n return prompt;\n }\n}\n\n/**\n * Create a chat service with the given configuration\n */\nexport function createChatService(config: ChatServiceConfig): ChatService {\n return new ChatService(config);\n}\n"],"mappings":"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"}