@specverse/engines 4.1.5 → 4.1.6
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/libs/instance-factories/applications/templates/generic/backend-env-generator.js +22 -0
- package/dist/libs/instance-factories/applications/templates/generic/backend-package-json-generator.js +66 -0
- package/dist/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.js +54 -0
- package/dist/libs/instance-factories/applications/templates/generic/main-generator.js +290 -0
- package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js +530 -0
- package/dist/libs/instance-factories/applications/templates/react/api-client-generator.js +437 -0
- package/dist/libs/instance-factories/applications/templates/react/api-types-generator.js +146 -0
- package/dist/libs/instance-factories/applications/templates/react/app-tsx-generator.js +73 -0
- package/dist/libs/instance-factories/applications/templates/react/env-example-generator.js +18 -0
- package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js +99 -0
- package/dist/libs/instance-factories/applications/templates/react/gitignore-generator.js +35 -0
- package/dist/libs/instance-factories/applications/templates/react/index-css-generator.js +9 -0
- package/dist/libs/instance-factories/applications/templates/react/index-html-generator.js +23 -0
- package/dist/libs/instance-factories/applications/templates/react/main-tsx-generator.js +29 -0
- package/dist/libs/instance-factories/applications/templates/react/package-json-generator.js +49 -0
- package/dist/libs/instance-factories/applications/templates/react/pattern-adapter-generator.js +156 -0
- package/dist/libs/instance-factories/applications/templates/react/react-pattern-adapter.js +935 -0
- package/dist/libs/instance-factories/applications/templates/react/relationship-field-generator.js +143 -0
- package/dist/libs/instance-factories/applications/templates/react/runtime-app-tsx-generator.js +101 -0
- package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js +50 -0
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.js +646 -0
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.js +65 -0
- package/dist/libs/instance-factories/applications/templates/react/tsconfig-generator.js +28 -0
- package/dist/libs/instance-factories/applications/templates/react/use-api-hooks-generator.js +132 -0
- package/dist/libs/instance-factories/applications/templates/react/view-dashboard-generator.js +143 -0
- package/dist/libs/instance-factories/applications/templates/react/view-detail-generator.js +143 -0
- package/dist/libs/instance-factories/applications/templates/react/view-form-generator.js +355 -0
- package/dist/libs/instance-factories/applications/templates/react/view-list-generator.js +91 -0
- package/dist/libs/instance-factories/applications/templates/react/view-router-generator.js +79 -0
- package/dist/libs/instance-factories/applications/templates/react/vite-config-generator.js +42 -0
- package/dist/libs/instance-factories/cli/templates/commander/cli-bin-wrapper-generator.js +11 -0
- package/dist/libs/instance-factories/cli/templates/commander/cli-entry-generator.js +111 -0
- package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +928 -0
- package/dist/libs/instance-factories/communication/templates/eventemitter/bus-generator.js +83 -0
- package/dist/libs/instance-factories/communication/templates/eventemitter/publisher-generator.js +91 -0
- package/dist/libs/instance-factories/communication/templates/eventemitter/subscriber-generator.js +86 -0
- package/dist/libs/instance-factories/controllers/templates/fastify/meta-routes-generator.js +93 -0
- package/dist/libs/instance-factories/controllers/templates/fastify/routes-generator.js +280 -0
- package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +125 -0
- package/dist/libs/instance-factories/infrastructure/templates/docker-k8s/infrastructure-generator.js +25 -0
- package/dist/libs/instance-factories/orms/templates/prisma/schema-generator.js +371 -0
- package/dist/libs/instance-factories/orms/templates/prisma/services-generator.js +266 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/env-example-generator.js +51 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/env-generator.js +61 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/gitignore-generator.js +59 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/package-json-generator.js +126 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/readme-generator.js +159 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.js +56 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-react-generator.js +37 -0
- package/dist/libs/instance-factories/sdks/templates/python/sdk-generator.js +29 -0
- package/dist/libs/instance-factories/sdks/templates/typescript/sdk-generator.js +28 -0
- package/dist/libs/instance-factories/services/templates/memory/generate-interpreter.js +14 -0
- package/dist/libs/instance-factories/services/templates/memory/step-conventions-memory.js +415 -0
- package/dist/libs/instance-factories/services/templates/prisma/behavior-generator.js +177 -0
- package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +413 -0
- package/dist/libs/instance-factories/services/templates/prisma/service-generator.js +243 -0
- package/dist/libs/instance-factories/services/templates/prisma/step-conventions.js +264 -0
- package/dist/libs/instance-factories/services/templates/shared-patterns.js +24 -0
- package/dist/libs/instance-factories/shared/path-resolver.js +59 -0
- package/dist/libs/instance-factories/storage/templates/mongodb/config-generator.js +13 -0
- package/dist/libs/instance-factories/storage/templates/mongodb/docker-generator.js +16 -0
- package/dist/libs/instance-factories/storage/templates/postgresql/config-generator.js +45 -0
- package/dist/libs/instance-factories/storage/templates/postgresql/docker-generator.js +46 -0
- package/dist/libs/instance-factories/storage/templates/redis/config-generator.js +14 -0
- package/dist/libs/instance-factories/storage/templates/redis/docker-generator.js +16 -0
- package/dist/libs/instance-factories/test-generation.js +145 -0
- package/dist/libs/instance-factories/testing/templates/vitest/tests-generator.js +30 -0
- package/dist/libs/instance-factories/tools/templates/mcp/mcp-server-generator.js +149 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/controllers/MCPServerController.js +232 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/events/EventEmitter.js +49 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/index.js +18 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/interfaces/ResourceProvider.js +0 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/LibrarySuggestion.js +97 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/SpecVerseResource.js +64 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/server/mcp-server.js +182 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/CLIProxyService.js +1210 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EmbeddedResourcesAdapter.js +172 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EntityModuleService.js +240 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/HybridResourcesProvider.js +147 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/LibraryToolsService.js +281 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorBridge.js +409 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorToolsService.js +414 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/PromptToolsService.js +467 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/ResourcesProviderService.js +135 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/types/index.js +0 -0
- package/dist/libs/instance-factories/tools/templates/vscode/static/extension.js +965 -0
- package/dist/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.js +238 -0
- package/dist/libs/instance-factories/validation/templates/zod/validation-generator.js +25 -0
- package/dist/libs/instance-factories/views/index.js +48 -0
- package/dist/libs/instance-factories/views/templates/react/adapters/antd-adapter.js +742 -0
- package/dist/libs/instance-factories/views/templates/react/adapters/mui-adapter.js +824 -0
- package/dist/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.js +719 -0
- package/dist/libs/instance-factories/views/templates/react/app-generator.js +45 -0
- package/dist/libs/instance-factories/views/templates/react/components-generator.js +779 -0
- package/dist/libs/instance-factories/views/templates/react/forms-generator.js +285 -0
- package/dist/libs/instance-factories/views/templates/react/frontend-package-json-generator.js +46 -0
- package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +111 -0
- package/dist/libs/instance-factories/views/templates/react/index-css-generator.js +9 -0
- package/dist/libs/instance-factories/views/templates/react/index-html-generator.js +23 -0
- package/dist/libs/instance-factories/views/templates/react/main-tsx-generator.js +21 -0
- package/dist/libs/instance-factories/views/templates/react/react-component-generator.js +299 -0
- package/dist/libs/instance-factories/views/templates/react/router-generator.js +136 -0
- package/dist/libs/instance-factories/views/templates/react/router-generic-generator.js +107 -0
- package/dist/libs/instance-factories/views/templates/react/shared-utils-generator.js +179 -0
- package/dist/libs/instance-factories/views/templates/react/spec-json-generator.js +7 -0
- package/dist/libs/instance-factories/views/templates/react/types-generator.js +56 -0
- package/dist/libs/instance-factories/views/templates/react/views-metadata-generator.js +27 -0
- package/dist/libs/instance-factories/views/templates/react/vite-config-generator.js +29 -0
- package/dist/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js +261 -0
- package/dist/libs/instance-factories/views/templates/shared/adapter-types.js +34 -0
- package/dist/libs/instance-factories/views/templates/shared/atomic-components-registry.js +800 -0
- package/dist/libs/instance-factories/views/templates/shared/base-generator.js +305 -0
- package/dist/libs/instance-factories/views/templates/shared/component-metadata.js +517 -0
- package/dist/libs/instance-factories/views/templates/shared/composite-pattern-types.js +0 -0
- package/dist/libs/instance-factories/views/templates/shared/composite-patterns.js +445 -0
- package/dist/libs/instance-factories/views/templates/shared/index.js +80 -0
- package/dist/libs/instance-factories/views/templates/shared/pattern-validator.js +210 -0
- package/dist/libs/instance-factories/views/templates/shared/property-mapper.js +492 -0
- package/dist/libs/instance-factories/views/templates/shared/syntax-mapper.js +321 -0
- package/package.json +3 -2
|
@@ -0,0 +1,965 @@
|
|
|
1
|
+
import * as vscode from "vscode";
|
|
2
|
+
import { join, dirname, basename, extname } from "path";
|
|
3
|
+
import { exec } from "child_process";
|
|
4
|
+
import { promisify } from "util";
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
function activate(context) {
|
|
7
|
+
console.log("SpecVerse v3.5 extension activated");
|
|
8
|
+
const provider = new SpecVerseProvider(context);
|
|
9
|
+
const diagnostics = vscode.languages.createDiagnosticCollection("specverse");
|
|
10
|
+
context.subscriptions.push(diagnostics);
|
|
11
|
+
const onSaveListener = vscode.workspace.onDidSaveTextDocument(async (document) => {
|
|
12
|
+
if (isSpecVerseDocument(document)) {
|
|
13
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
14
|
+
if (config.get("validation.onSave")) {
|
|
15
|
+
await validateDocument(document, diagnostics);
|
|
16
|
+
}
|
|
17
|
+
if (config.get("diagrams.autoGenerate")) {
|
|
18
|
+
await generateDiagramForDocument(document);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
context.subscriptions.push(onSaveListener);
|
|
23
|
+
let validationTimeout;
|
|
24
|
+
const onChangeListener = vscode.workspace.onDidChangeTextDocument((event) => {
|
|
25
|
+
const document = event.document;
|
|
26
|
+
if (isSpecVerseDocument(document)) {
|
|
27
|
+
clearTimeout(validationTimeout);
|
|
28
|
+
validationTimeout = setTimeout(async () => {
|
|
29
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
30
|
+
if (config.get("validation.enabled")) {
|
|
31
|
+
await validateDocument(document, diagnostics);
|
|
32
|
+
}
|
|
33
|
+
}, 1e3);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
context.subscriptions.push(onChangeListener);
|
|
37
|
+
const packageJson = require("../package.json");
|
|
38
|
+
const autoGeneratedCommands = packageJson.contributes.commands.filter((cmd) => cmd.command.startsWith("specverse.")).map((cmd) => {
|
|
39
|
+
const commandName = cmd.command.replace("specverse.", "");
|
|
40
|
+
return vscode.commands.registerCommand(cmd.command, async (uri) => {
|
|
41
|
+
const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
|
|
42
|
+
if (document && isSpecVerseDocument(document)) {
|
|
43
|
+
await executeCliCommand(document, commandName);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
const commands = [
|
|
48
|
+
vscode.commands.registerCommand("specverse.validate", async (uri) => {
|
|
49
|
+
const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
|
|
50
|
+
if (document && isSpecVerseDocument(document)) {
|
|
51
|
+
await validateDocument(document, diagnostics);
|
|
52
|
+
vscode.window.showInformationMessage("SpecVerse validation completed");
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
55
|
+
vscode.commands.registerCommand("specverse.format", async () => {
|
|
56
|
+
const editor = vscode.window.activeTextEditor;
|
|
57
|
+
if (editor && isSpecVerseDocument(editor.document)) {
|
|
58
|
+
await formatDocument(editor);
|
|
59
|
+
}
|
|
60
|
+
}),
|
|
61
|
+
vscode.commands.registerCommand("specverse.generateDiagram", async (uri) => {
|
|
62
|
+
const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
|
|
63
|
+
if (document && isSpecVerseDocument(document)) {
|
|
64
|
+
await generateDiagramForDocument(document);
|
|
65
|
+
}
|
|
66
|
+
}),
|
|
67
|
+
vscode.commands.registerCommand("specverse.generateDocs", async (uri) => {
|
|
68
|
+
const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
|
|
69
|
+
if (document && isSpecVerseDocument(document)) {
|
|
70
|
+
await generateDocsForDocument(document);
|
|
71
|
+
}
|
|
72
|
+
}),
|
|
73
|
+
vscode.commands.registerCommand("specverse.generateAI", async (uri) => {
|
|
74
|
+
const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
|
|
75
|
+
if (document && isSpecVerseDocument(document)) {
|
|
76
|
+
await generateAIForDocument(document);
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
vscode.commands.registerCommand("specverse.processSpecly", async (uri) => {
|
|
80
|
+
const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
|
|
81
|
+
if (document && document.fileName.endsWith(".specly")) {
|
|
82
|
+
await processSpeclyDocument(document);
|
|
83
|
+
} else {
|
|
84
|
+
vscode.window.showErrorMessage("This command only works with .specly files");
|
|
85
|
+
}
|
|
86
|
+
}),
|
|
87
|
+
vscode.commands.registerCommand("specverse.infer", async (uri) => {
|
|
88
|
+
const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
|
|
89
|
+
if (document && isSpecVerseDocument(document)) {
|
|
90
|
+
await inferCompleteSystemForDocument(document);
|
|
91
|
+
}
|
|
92
|
+
}),
|
|
93
|
+
vscode.commands.registerCommand("specverse.testCycle", async (uri) => {
|
|
94
|
+
const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
|
|
95
|
+
if (document && isSpecVerseDocument(document)) {
|
|
96
|
+
await runTestCycleForDocument(document);
|
|
97
|
+
}
|
|
98
|
+
}),
|
|
99
|
+
vscode.commands.registerCommand("specverse.batchGenerate", async () => {
|
|
100
|
+
await runBatchDiagramGeneration();
|
|
101
|
+
}),
|
|
102
|
+
vscode.commands.registerCommand("specverse.migrate", async (uri) => {
|
|
103
|
+
let targetDir;
|
|
104
|
+
if (uri) {
|
|
105
|
+
const stat = await vscode.workspace.fs.stat(uri);
|
|
106
|
+
if (stat.type === vscode.FileType.Directory) {
|
|
107
|
+
targetDir = uri.fsPath;
|
|
108
|
+
} else {
|
|
109
|
+
targetDir = dirname(uri.fsPath);
|
|
110
|
+
}
|
|
111
|
+
} else if (vscode.window.activeTextEditor) {
|
|
112
|
+
targetDir = dirname(vscode.window.activeTextEditor.document.fileName);
|
|
113
|
+
} else if (vscode.workspace.workspaceFolders?.[0]) {
|
|
114
|
+
targetDir = vscode.workspace.workspaceFolders[0].uri.fsPath;
|
|
115
|
+
} else {
|
|
116
|
+
vscode.window.showErrorMessage("No directory to migrate. Open a file or folder first.");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
await runMigrateCommand(targetDir);
|
|
120
|
+
})
|
|
121
|
+
];
|
|
122
|
+
context.subscriptions.push(...commands, ...autoGeneratedCommands);
|
|
123
|
+
const completionProvider = vscode.languages.registerCompletionItemProvider(
|
|
124
|
+
{ scheme: "file", language: "specly" },
|
|
125
|
+
new SpecVerseCompletionProvider(),
|
|
126
|
+
":",
|
|
127
|
+
" "
|
|
128
|
+
);
|
|
129
|
+
context.subscriptions.push(completionProvider);
|
|
130
|
+
const hoverProvider = vscode.languages.registerHoverProvider(
|
|
131
|
+
{ scheme: "file", language: "specly" },
|
|
132
|
+
new SpecVerseHoverProvider()
|
|
133
|
+
);
|
|
134
|
+
context.subscriptions.push(hoverProvider);
|
|
135
|
+
const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
|
136
|
+
statusBar.command = "specverse.validate";
|
|
137
|
+
context.subscriptions.push(statusBar);
|
|
138
|
+
const onActiveEditorChange = vscode.window.onDidChangeActiveTextEditor((editor) => {
|
|
139
|
+
if (editor && isSpecVerseDocument(editor.document)) {
|
|
140
|
+
statusBar.text = "$(check) SpecVerse";
|
|
141
|
+
statusBar.tooltip = "Click to validate SpecVerse file";
|
|
142
|
+
statusBar.show();
|
|
143
|
+
} else {
|
|
144
|
+
statusBar.hide();
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
context.subscriptions.push(onActiveEditorChange);
|
|
148
|
+
if (vscode.window.activeTextEditor && isSpecVerseDocument(vscode.window.activeTextEditor.document)) {
|
|
149
|
+
statusBar.text = "$(check) SpecVerse";
|
|
150
|
+
statusBar.tooltip = "Click to validate SpecVerse file";
|
|
151
|
+
statusBar.show();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
class SpecVerseProvider {
|
|
155
|
+
constructor(context) {
|
|
156
|
+
this.context = context;
|
|
157
|
+
}
|
|
158
|
+
getCliPath() {
|
|
159
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
160
|
+
return config.get("cli.path") || "specverse";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
class SpecVerseCompletionProvider {
|
|
164
|
+
provideCompletionItems(document, position, token, context) {
|
|
165
|
+
if (!isSpecVerseDocument(document)) {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
const line = document.lineAt(position).text;
|
|
169
|
+
const linePrefix = line.substring(0, position.character);
|
|
170
|
+
const context_info = analyzeContext(document, position);
|
|
171
|
+
const completions = [];
|
|
172
|
+
if (context_info.isAttributeType) {
|
|
173
|
+
const types = [
|
|
174
|
+
"String",
|
|
175
|
+
"Text",
|
|
176
|
+
"Integer",
|
|
177
|
+
"Number",
|
|
178
|
+
"Boolean",
|
|
179
|
+
"Date",
|
|
180
|
+
"DateTime",
|
|
181
|
+
"UUID",
|
|
182
|
+
"Email",
|
|
183
|
+
"URL",
|
|
184
|
+
"JSON",
|
|
185
|
+
"Object",
|
|
186
|
+
"Array"
|
|
187
|
+
];
|
|
188
|
+
types.forEach((type) => {
|
|
189
|
+
const item = new vscode.CompletionItem(type, vscode.CompletionItemKind.TypeParameter);
|
|
190
|
+
item.documentation = new vscode.MarkdownString(`SpecVerse type: \`${type}\``);
|
|
191
|
+
completions.push(item);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (context_info.isModifier) {
|
|
195
|
+
const modifiers = [
|
|
196
|
+
{ name: "required", description: "Field is required" },
|
|
197
|
+
{ name: "unique", description: "Field must be unique" },
|
|
198
|
+
{ name: "searchable", description: "Field is searchable" },
|
|
199
|
+
{ name: "verified", description: "Field requires verification" },
|
|
200
|
+
{ name: "auto=uuid4", description: "Auto-generate UUID" },
|
|
201
|
+
{ name: "auto=now", description: "Auto-set current timestamp" },
|
|
202
|
+
{ name: "auto=token", description: "Auto-generate token" },
|
|
203
|
+
{ name: "default=", description: "Default value" },
|
|
204
|
+
{ name: "min=", description: "Minimum value" },
|
|
205
|
+
{ name: "max=", description: "Maximum value" }
|
|
206
|
+
];
|
|
207
|
+
modifiers.forEach((mod) => {
|
|
208
|
+
const item = new vscode.CompletionItem(mod.name, vscode.CompletionItemKind.Keyword);
|
|
209
|
+
item.documentation = new vscode.MarkdownString(mod.description);
|
|
210
|
+
if (mod.name.endsWith("=")) {
|
|
211
|
+
item.insertText = mod.name;
|
|
212
|
+
item.command = { command: "editor.action.triggerSuggest", title: "Trigger Suggest" };
|
|
213
|
+
}
|
|
214
|
+
completions.push(item);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
if (context_info.isRelationshipType) {
|
|
218
|
+
const relationships = [
|
|
219
|
+
{ name: "hasMany", description: "One-to-many relationship" },
|
|
220
|
+
{ name: "hasOne", description: "One-to-one relationship" },
|
|
221
|
+
{ name: "belongsTo", description: "Many-to-one relationship" },
|
|
222
|
+
{ name: "manyToMany", description: "Many-to-many relationship" }
|
|
223
|
+
];
|
|
224
|
+
relationships.forEach((rel) => {
|
|
225
|
+
const item = new vscode.CompletionItem(rel.name, vscode.CompletionItemKind.Keyword);
|
|
226
|
+
item.documentation = new vscode.MarkdownString(rel.description);
|
|
227
|
+
completions.push(item);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (context_info.isTopLevel) {
|
|
231
|
+
const sections = [
|
|
232
|
+
// v3.5 container format
|
|
233
|
+
"components",
|
|
234
|
+
"deployments",
|
|
235
|
+
// Component-level sections
|
|
236
|
+
"component",
|
|
237
|
+
"deployment",
|
|
238
|
+
"version",
|
|
239
|
+
"description",
|
|
240
|
+
"tags",
|
|
241
|
+
"import",
|
|
242
|
+
"export",
|
|
243
|
+
"models",
|
|
244
|
+
"controllers",
|
|
245
|
+
"services",
|
|
246
|
+
"views",
|
|
247
|
+
"events",
|
|
248
|
+
// v3.5 CURED operations
|
|
249
|
+
"cured",
|
|
250
|
+
// Deployment sections
|
|
251
|
+
"instances",
|
|
252
|
+
"environment",
|
|
253
|
+
"communications",
|
|
254
|
+
// v3.5 advanced features
|
|
255
|
+
"profiles",
|
|
256
|
+
"profile-attachment",
|
|
257
|
+
"steps",
|
|
258
|
+
"subscribes_to"
|
|
259
|
+
];
|
|
260
|
+
sections.forEach((section) => {
|
|
261
|
+
const item = new vscode.CompletionItem(section, vscode.CompletionItemKind.Module);
|
|
262
|
+
if (section === "components") {
|
|
263
|
+
item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 container format: array of components`);
|
|
264
|
+
} else if (section === "deployments") {
|
|
265
|
+
item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 container format: array of deployments`);
|
|
266
|
+
} else if (section === "cured") {
|
|
267
|
+
item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 CURED operations: Create, Update, Retrieve, Evolve, Destroy`);
|
|
268
|
+
} else if (section === "instances") {
|
|
269
|
+
item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 deployment instances: controllers, services, views, communications`);
|
|
270
|
+
} else if (section === "communications") {
|
|
271
|
+
item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 communication channels: pubsub, rpc, queue, streaming`);
|
|
272
|
+
} else {
|
|
273
|
+
item.documentation = new vscode.MarkdownString(`SpecVerse section: \`${section}\``);
|
|
274
|
+
}
|
|
275
|
+
completions.push(item);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
if (context_info.isDeploymentProperty) {
|
|
279
|
+
const deploymentProps = [
|
|
280
|
+
{ name: "component", description: "Component reference for this instance" },
|
|
281
|
+
{ name: "namespace", description: "Logical namespace for this instance" },
|
|
282
|
+
{ name: "advertises", description: "Capabilities this instance advertises" },
|
|
283
|
+
{ name: "uses", description: "Capabilities this instance uses" },
|
|
284
|
+
{ name: "scale", description: "Number of instances to run" },
|
|
285
|
+
{ name: "capabilities", description: "Available capabilities on communication channel" },
|
|
286
|
+
{ name: "type", description: "Communication channel type (pubsub, rpc, queue, streaming)" }
|
|
287
|
+
];
|
|
288
|
+
deploymentProps.forEach((prop) => {
|
|
289
|
+
const item = new vscode.CompletionItem(prop.name, vscode.CompletionItemKind.Property);
|
|
290
|
+
item.documentation = new vscode.MarkdownString(prop.description);
|
|
291
|
+
completions.push(item);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return completions;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
class SpecVerseHoverProvider {
|
|
298
|
+
provideHover(document, position, token) {
|
|
299
|
+
if (!isSpecVerseDocument(document)) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const line = document.lineAt(position).text;
|
|
303
|
+
const wordRange = document.getWordRangeAtPosition(position);
|
|
304
|
+
const word = wordRange ? document.getText(wordRange) : "";
|
|
305
|
+
const attributeMatch = line.match(/^\s*(\w+):\s*(\w+)(.*)/);
|
|
306
|
+
if (attributeMatch && wordRange) {
|
|
307
|
+
const [, name, type, modifiers] = attributeMatch;
|
|
308
|
+
if (word === type) {
|
|
309
|
+
return new vscode.Hover([
|
|
310
|
+
`**SpecVerse Type:** \`${type}\``,
|
|
311
|
+
"",
|
|
312
|
+
getTypeDocumentation(type)
|
|
313
|
+
]);
|
|
314
|
+
}
|
|
315
|
+
if (word === name) {
|
|
316
|
+
return new vscode.Hover([
|
|
317
|
+
`**Attribute:** \`${name}\``,
|
|
318
|
+
`**Type:** \`${type}\``,
|
|
319
|
+
`**Modifiers:** \`${modifiers.trim() || "none"}\``,
|
|
320
|
+
"",
|
|
321
|
+
"SpecVerse attribute definition using conventions"
|
|
322
|
+
]);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const flowMatch = line.match(/flow:\s*(.+)/);
|
|
326
|
+
if (flowMatch && line.includes(word)) {
|
|
327
|
+
return new vscode.Hover([
|
|
328
|
+
"**SpecVerse Lifecycle Flow**",
|
|
329
|
+
"",
|
|
330
|
+
"Shorthand lifecycle definition.",
|
|
331
|
+
"States are connected with `->` transitions.",
|
|
332
|
+
"",
|
|
333
|
+
"Example: `draft -> published -> archived`"
|
|
334
|
+
]);
|
|
335
|
+
}
|
|
336
|
+
if (word === "steps") {
|
|
337
|
+
return new vscode.Hover([
|
|
338
|
+
"**Steps** *(Array<string>)*",
|
|
339
|
+
"",
|
|
340
|
+
"Sequential steps describing the execution process.",
|
|
341
|
+
"",
|
|
342
|
+
"**Usage:** Available in behaviors, controller actions, service operations, and CURED operations.",
|
|
343
|
+
"",
|
|
344
|
+
"**Example:**",
|
|
345
|
+
"```yaml",
|
|
346
|
+
"steps:",
|
|
347
|
+
' - "Validate input data"',
|
|
348
|
+
' - "Process business logic"',
|
|
349
|
+
' - "Return results"',
|
|
350
|
+
"```"
|
|
351
|
+
]);
|
|
352
|
+
}
|
|
353
|
+
if (word === "parameters") {
|
|
354
|
+
return new vscode.Hover([
|
|
355
|
+
"**Parameters** *(Object)*",
|
|
356
|
+
"",
|
|
357
|
+
"Input parameters for executable properties (actions, operations, behaviors).",
|
|
358
|
+
"",
|
|
359
|
+
"**Usage:** Replaces `payload:` in v3.5 - defines input data structure.",
|
|
360
|
+
"",
|
|
361
|
+
"**Example:**",
|
|
362
|
+
"```yaml",
|
|
363
|
+
"parameters:",
|
|
364
|
+
" userId: UUID required",
|
|
365
|
+
" name: String required max=100",
|
|
366
|
+
"```"
|
|
367
|
+
]);
|
|
368
|
+
}
|
|
369
|
+
if (word === "publishes") {
|
|
370
|
+
return new vscode.Hover([
|
|
371
|
+
"**Publishes** *(Array<string>)*",
|
|
372
|
+
"",
|
|
373
|
+
"Events published by this executable property.",
|
|
374
|
+
"",
|
|
375
|
+
"**Usage:** Replaces `events:` in v3.5 - defines output events.",
|
|
376
|
+
"",
|
|
377
|
+
"**Example:**",
|
|
378
|
+
"```yaml",
|
|
379
|
+
"publishes: [UserCreated, EmailSent]",
|
|
380
|
+
"```"
|
|
381
|
+
]);
|
|
382
|
+
}
|
|
383
|
+
if (word === "subscribes_to") {
|
|
384
|
+
return new vscode.Hover([
|
|
385
|
+
"**Subscribes To** *(Array<string> | Object)*",
|
|
386
|
+
"",
|
|
387
|
+
"Events this component subscribes to.",
|
|
388
|
+
"",
|
|
389
|
+
"**Formats:**",
|
|
390
|
+
'- Array: `["EventName1", "EventName2"]`',
|
|
391
|
+
'- Object: `EventName: "handlerMethod"`',
|
|
392
|
+
"",
|
|
393
|
+
"**Example:**",
|
|
394
|
+
"```yaml",
|
|
395
|
+
"subscribes_to:",
|
|
396
|
+
" - UserCreated",
|
|
397
|
+
" - OrderPlaced",
|
|
398
|
+
"```"
|
|
399
|
+
]);
|
|
400
|
+
}
|
|
401
|
+
if (word === "profiles") {
|
|
402
|
+
return new vscode.Hover([
|
|
403
|
+
"**Profiles** *(Array<string>)*",
|
|
404
|
+
"",
|
|
405
|
+
"Runtime profiles that can be applied to models.",
|
|
406
|
+
"",
|
|
407
|
+
"**Usage:** Defines which profiles are available for profile attachment.",
|
|
408
|
+
"",
|
|
409
|
+
"**Example:**",
|
|
410
|
+
"```yaml",
|
|
411
|
+
'profiles: ["CustomerProfile", "AdminProfile"]',
|
|
412
|
+
"```"
|
|
413
|
+
]);
|
|
414
|
+
}
|
|
415
|
+
if (word === "profile-attachment") {
|
|
416
|
+
return new vscode.Hover([
|
|
417
|
+
"**Profile Attachment** *(Object)*",
|
|
418
|
+
"",
|
|
419
|
+
"Configuration for attaching profiles to models at runtime.",
|
|
420
|
+
"",
|
|
421
|
+
"**Properties:**",
|
|
422
|
+
"- `profiles`: Array of available profiles",
|
|
423
|
+
"- `conditions`: Optional conditions for attachment",
|
|
424
|
+
"- `priority`: Attachment priority (1-10)",
|
|
425
|
+
"",
|
|
426
|
+
"**Example:**",
|
|
427
|
+
"```yaml",
|
|
428
|
+
"profile-attachment:",
|
|
429
|
+
' profiles: ["VisibilityProfile"]',
|
|
430
|
+
' conditions: ["user.role === admin"]',
|
|
431
|
+
" priority: 5",
|
|
432
|
+
"```"
|
|
433
|
+
]);
|
|
434
|
+
}
|
|
435
|
+
if (word === "instances") {
|
|
436
|
+
return new vscode.Hover([
|
|
437
|
+
"**Instances** *(Object)*",
|
|
438
|
+
"",
|
|
439
|
+
"Logical deployment instances for controllers, services, views, and communications.",
|
|
440
|
+
"",
|
|
441
|
+
"**Structure:**",
|
|
442
|
+
"- `controllers`: Controller instance configurations",
|
|
443
|
+
"- `services`: Service instance configurations",
|
|
444
|
+
"- `views`: View instance configurations",
|
|
445
|
+
"- `communications`: Communication channel definitions"
|
|
446
|
+
]);
|
|
447
|
+
}
|
|
448
|
+
if (word === "advertises") {
|
|
449
|
+
return new vscode.Hover([
|
|
450
|
+
"**Advertises** *(string | Array<string>)*",
|
|
451
|
+
"",
|
|
452
|
+
"Capabilities that this instance makes available to other instances.",
|
|
453
|
+
"",
|
|
454
|
+
"**Common patterns:**",
|
|
455
|
+
'- `"*"` - All capabilities',
|
|
456
|
+
'- `"behaviors.*"` - All behavior capabilities',
|
|
457
|
+
'- `["create", "read", "update"]` - Specific operations'
|
|
458
|
+
]);
|
|
459
|
+
}
|
|
460
|
+
if (word === "uses") {
|
|
461
|
+
return new vscode.Hover([
|
|
462
|
+
"**Uses** *(Array<string>)*",
|
|
463
|
+
"",
|
|
464
|
+
"Capabilities that this instance consumes from communication channels.",
|
|
465
|
+
"",
|
|
466
|
+
"**Examples:**",
|
|
467
|
+
'- `["user.*", "order.create"]` - Specific capability patterns',
|
|
468
|
+
'- `["database.*"]` - All database capabilities'
|
|
469
|
+
]);
|
|
470
|
+
}
|
|
471
|
+
if (word === "capabilities") {
|
|
472
|
+
return new vscode.Hover([
|
|
473
|
+
"**Capabilities** *(Array<string>)*",
|
|
474
|
+
"",
|
|
475
|
+
"List of capabilities available on this communication channel.",
|
|
476
|
+
"",
|
|
477
|
+
"**Used by:** Communication channel definitions to specify what operations are available for instances to advertise/use."
|
|
478
|
+
]);
|
|
479
|
+
}
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function isSpecVerseDocument(document) {
|
|
484
|
+
return document.languageId === "specly" || document.fileName.endsWith(".specly");
|
|
485
|
+
}
|
|
486
|
+
function analyzeContext(document, position) {
|
|
487
|
+
const line = document.lineAt(position).text;
|
|
488
|
+
const linePrefix = line.substring(0, position.character);
|
|
489
|
+
const isInAttributesSection = (() => {
|
|
490
|
+
for (let i = position.line; i >= 0; i--) {
|
|
491
|
+
const checkLine = document.lineAt(i).text;
|
|
492
|
+
if (checkLine.trim() === "attributes:") return true;
|
|
493
|
+
if (checkLine.trim().endsWith(":") && !checkLine.includes(" ")) return false;
|
|
494
|
+
}
|
|
495
|
+
return false;
|
|
496
|
+
})();
|
|
497
|
+
const isInRelationshipsSection = (() => {
|
|
498
|
+
for (let i = position.line; i >= 0; i--) {
|
|
499
|
+
const checkLine = document.lineAt(i).text;
|
|
500
|
+
if (checkLine.trim() === "relationships:") return true;
|
|
501
|
+
if (checkLine.trim().endsWith(":") && !checkLine.includes(" ")) return false;
|
|
502
|
+
}
|
|
503
|
+
return false;
|
|
504
|
+
})();
|
|
505
|
+
const isInDeploymentSection = (() => {
|
|
506
|
+
for (let i = position.line; i >= 0; i--) {
|
|
507
|
+
const checkLine = document.lineAt(i).text;
|
|
508
|
+
if (checkLine.trim() === "instances:" || checkLine.trim() === "communications:") return true;
|
|
509
|
+
if (checkLine.trim().endsWith(":") && !checkLine.includes(" ")) return false;
|
|
510
|
+
}
|
|
511
|
+
return false;
|
|
512
|
+
})();
|
|
513
|
+
return {
|
|
514
|
+
isAttributeType: isInAttributesSection && linePrefix.match(/^\s*\w+:\s*$/),
|
|
515
|
+
isModifier: isInAttributesSection && linePrefix.match(/^\s*\w+:\s*\w+\s+$/),
|
|
516
|
+
isRelationshipType: isInRelationshipsSection && linePrefix.match(/^\s*\w+:\s*$/),
|
|
517
|
+
isTopLevel: !linePrefix.includes(" ") && linePrefix.trim().endsWith(":"),
|
|
518
|
+
isDeploymentProperty: isInDeploymentSection && linePrefix.match(/^\s+\w+:\s*$/)
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function getTypeDocumentation(type) {
|
|
522
|
+
const docs = {
|
|
523
|
+
"String": "Variable-length text field (default max 255 characters)",
|
|
524
|
+
"Text": "Long text field for large content",
|
|
525
|
+
"Integer": "Whole number",
|
|
526
|
+
"Number": "Decimal number",
|
|
527
|
+
"Boolean": "True/false value",
|
|
528
|
+
"Date": "Date without time",
|
|
529
|
+
"DateTime": "Date and time timestamp",
|
|
530
|
+
"UUID": "Universally Unique Identifier",
|
|
531
|
+
"Email": "Email address with validation",
|
|
532
|
+
"URL": "Web URL with validation",
|
|
533
|
+
"JSON": "JSON object or array",
|
|
534
|
+
"Object": "Structured object",
|
|
535
|
+
"Array": "List of values"
|
|
536
|
+
};
|
|
537
|
+
return docs[type] || "SpecVerse data type";
|
|
538
|
+
}
|
|
539
|
+
async function validateDocument(document, diagnostics) {
|
|
540
|
+
try {
|
|
541
|
+
diagnostics.clear();
|
|
542
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
543
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
544
|
+
const workingDir = dirname(document.fileName);
|
|
545
|
+
const { stdout: stdout2, stderr } = await execAsync(`"${cliPath}" validate "${document.fileName}"`, { cwd: workingDir });
|
|
546
|
+
if (stdout2.includes("\u2705") && !stderr) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const errors = [];
|
|
550
|
+
const allOutput = (stdout2 + "\n" + stderr).split("\n");
|
|
551
|
+
allOutput.forEach((line) => {
|
|
552
|
+
line = line.trim();
|
|
553
|
+
if (!line) return;
|
|
554
|
+
let errorMessage = "";
|
|
555
|
+
let severity = vscode.DiagnosticSeverity.Error;
|
|
556
|
+
let lineNumber = 0;
|
|
557
|
+
const numberedErrorMatch = line.match(/^\d+\.\s*(.+)$/);
|
|
558
|
+
if (numberedErrorMatch) {
|
|
559
|
+
errorMessage = numberedErrorMatch[1].trim();
|
|
560
|
+
} else if (line.includes("\u274C") && !line.includes("Validation failed")) {
|
|
561
|
+
errorMessage = line.replace(/❌/g, "").trim();
|
|
562
|
+
} else if (line.includes("Error:")) {
|
|
563
|
+
errorMessage = line.replace(/Error:/, "").trim();
|
|
564
|
+
} else if (line.includes("\u26A0\uFE0F") || line.includes("Warning:")) {
|
|
565
|
+
errorMessage = line.replace(/⚠️|Warning:/, "").trim();
|
|
566
|
+
severity = vscode.DiagnosticSeverity.Warning;
|
|
567
|
+
} else if (line.includes("at line") || line.includes("line:")) {
|
|
568
|
+
const lineMatch = line.match(/(?:at line|line:?\s*)(\d+)/i);
|
|
569
|
+
if (lineMatch) {
|
|
570
|
+
lineNumber = Math.max(0, parseInt(lineMatch[1]) - 1);
|
|
571
|
+
}
|
|
572
|
+
errorMessage = line;
|
|
573
|
+
} else if (line.includes("should") && (line.includes("property") || line.includes("type") || line.includes("required"))) {
|
|
574
|
+
errorMessage = line;
|
|
575
|
+
severity = vscode.DiagnosticSeverity.Error;
|
|
576
|
+
} else if (line.includes("Parse error") || line.includes("Syntax error")) {
|
|
577
|
+
errorMessage = line;
|
|
578
|
+
} else if (line.includes("Validation failed") || line.includes("Validation Errors:") || line.includes("\u{1F4DD}")) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
if (errorMessage) {
|
|
582
|
+
const diagnostic = new vscode.Diagnostic(
|
|
583
|
+
new vscode.Range(lineNumber, 0, lineNumber, Number.MAX_SAFE_INTEGER),
|
|
584
|
+
errorMessage,
|
|
585
|
+
severity
|
|
586
|
+
);
|
|
587
|
+
diagnostic.source = "SpecVerse";
|
|
588
|
+
errors.push(diagnostic);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
if (errors.length > 0) {
|
|
592
|
+
diagnostics.set(document.uri, errors);
|
|
593
|
+
} else if (!stdout2.includes("\u2705")) {
|
|
594
|
+
const diagnostic = new vscode.Diagnostic(
|
|
595
|
+
new vscode.Range(0, 0, 0, 0),
|
|
596
|
+
"SpecVerse validation completed with unknown status. Check output for details.",
|
|
597
|
+
vscode.DiagnosticSeverity.Information
|
|
598
|
+
);
|
|
599
|
+
diagnostic.source = "SpecVerse";
|
|
600
|
+
diagnostics.set(document.uri, [diagnostic]);
|
|
601
|
+
}
|
|
602
|
+
} catch (error) {
|
|
603
|
+
if (error.code === "ENOENT") {
|
|
604
|
+
const diagnostic2 = new vscode.Diagnostic(
|
|
605
|
+
new vscode.Range(0, 0, 0, 0),
|
|
606
|
+
"SpecVerse CLI not found. Please configure cli.path in settings or ensure specverse is on PATH",
|
|
607
|
+
vscode.DiagnosticSeverity.Error
|
|
608
|
+
);
|
|
609
|
+
diagnostic2.source = "SpecVerse";
|
|
610
|
+
diagnostics.set(document.uri, [diagnostic2]);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (error.stdout) {
|
|
614
|
+
const errors = [];
|
|
615
|
+
const errorLines = error.stdout.split("\n");
|
|
616
|
+
errorLines.forEach((line) => {
|
|
617
|
+
line = line.trim();
|
|
618
|
+
if (!line) return;
|
|
619
|
+
let errorMessage = "";
|
|
620
|
+
let severity = vscode.DiagnosticSeverity.Error;
|
|
621
|
+
let lineNumber = 0;
|
|
622
|
+
const numberedErrorMatch = line.match(/^\d+\.\s*(.+)$/);
|
|
623
|
+
if (numberedErrorMatch) {
|
|
624
|
+
errorMessage = numberedErrorMatch[1].trim();
|
|
625
|
+
} else if (line.includes("\u274C") && !line.includes("Validation failed")) {
|
|
626
|
+
errorMessage = line.replace(/❌/g, "").trim();
|
|
627
|
+
} else if (line.includes("Unknown property") || line.includes("should") || line.includes("required")) {
|
|
628
|
+
errorMessage = line;
|
|
629
|
+
} else if (line.includes("/") && line.includes(":")) {
|
|
630
|
+
errorMessage = line;
|
|
631
|
+
} else if (line.includes("\u26A0\uFE0F") || line.includes("Warning:")) {
|
|
632
|
+
errorMessage = line.replace(/⚠️|Warning:/, "").trim();
|
|
633
|
+
severity = vscode.DiagnosticSeverity.Warning;
|
|
634
|
+
} else if (line.includes("Validation failed") || line.includes("Validation Errors:") || line.includes("\u{1F4DD}")) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (errorMessage) {
|
|
638
|
+
const diagnostic2 = new vscode.Diagnostic(
|
|
639
|
+
new vscode.Range(lineNumber, 0, lineNumber, Number.MAX_SAFE_INTEGER),
|
|
640
|
+
errorMessage,
|
|
641
|
+
severity
|
|
642
|
+
);
|
|
643
|
+
diagnostic2.source = "SpecVerse";
|
|
644
|
+
errors.push(diagnostic2);
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
if (errors.length > 0) {
|
|
648
|
+
diagnostics.set(document.uri, errors);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const diagnostic = new vscode.Diagnostic(
|
|
653
|
+
new vscode.Range(0, 0, 0, 0),
|
|
654
|
+
`SpecVerse CLI error: ${error.message}`,
|
|
655
|
+
vscode.DiagnosticSeverity.Error
|
|
656
|
+
);
|
|
657
|
+
diagnostic.source = "SpecVerse";
|
|
658
|
+
diagnostics.set(document.uri, [diagnostic]);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
async function formatDocument(editor) {
|
|
662
|
+
try {
|
|
663
|
+
const document = editor.document;
|
|
664
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
665
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
666
|
+
vscode.window.showInformationMessage("Format command is not currently available in SpecVerse CLI");
|
|
667
|
+
return;
|
|
668
|
+
if (stdout) {
|
|
669
|
+
const edit = new vscode.WorkspaceEdit();
|
|
670
|
+
const fullRange = new vscode.Range(
|
|
671
|
+
document.positionAt(0),
|
|
672
|
+
document.positionAt(document.getText().length)
|
|
673
|
+
);
|
|
674
|
+
edit.replace(document.uri, fullRange, stdout);
|
|
675
|
+
await vscode.workspace.applyEdit(edit);
|
|
676
|
+
}
|
|
677
|
+
} catch (error) {
|
|
678
|
+
vscode.window.showErrorMessage(`Format failed: ${error.message}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
async function generateDiagramForDocument(document) {
|
|
682
|
+
try {
|
|
683
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
684
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
685
|
+
const diagramType = await vscode.window.showQuickPick([
|
|
686
|
+
"er",
|
|
687
|
+
"sequence",
|
|
688
|
+
"architecture",
|
|
689
|
+
"lifecycle",
|
|
690
|
+
"deployment",
|
|
691
|
+
"all"
|
|
692
|
+
], {
|
|
693
|
+
placeHolder: "Select diagram type to generate"
|
|
694
|
+
});
|
|
695
|
+
if (!diagramType) return;
|
|
696
|
+
const workingDir = dirname(document.fileName);
|
|
697
|
+
const baseName = basename(document.fileName, extname(document.fileName));
|
|
698
|
+
const outputFile = join(workingDir, `${baseName}-${diagramType}.mmd`);
|
|
699
|
+
console.log(`[SpecVerse] Working directory: ${workingDir}`);
|
|
700
|
+
console.log(`[SpecVerse] Output file: ${outputFile}`);
|
|
701
|
+
console.log(`[SpecVerse] Command: "${cliPath}" gen uml "${document.fileName}" --output "${outputFile}"`);
|
|
702
|
+
await execAsync(`"${cliPath}" gen uml "${document.fileName}" --output "${outputFile}"`, { cwd: workingDir });
|
|
703
|
+
vscode.window.showInformationMessage(`${diagramType} diagram generated successfully`);
|
|
704
|
+
} catch (error) {
|
|
705
|
+
vscode.window.showErrorMessage(`Diagram generation failed: ${error.message}`);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
async function generateDocsForDocument(document) {
|
|
709
|
+
try {
|
|
710
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
711
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
712
|
+
const format = await vscode.window.showQuickPick([
|
|
713
|
+
"markdown",
|
|
714
|
+
"html",
|
|
715
|
+
"openapi"
|
|
716
|
+
], {
|
|
717
|
+
placeHolder: "Select documentation format"
|
|
718
|
+
});
|
|
719
|
+
if (!format) return;
|
|
720
|
+
const workingDir = dirname(document.fileName);
|
|
721
|
+
await execAsync(`"${cliPath}" gen docs "${document.fileName}"`, { cwd: workingDir });
|
|
722
|
+
vscode.window.showInformationMessage(`${format} documentation generated successfully`);
|
|
723
|
+
} catch (error) {
|
|
724
|
+
vscode.window.showErrorMessage(`Documentation generation failed: ${error.message}`);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
async function generateAIForDocument(document) {
|
|
728
|
+
try {
|
|
729
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
730
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
731
|
+
const workingDir = dirname(document.fileName);
|
|
732
|
+
await execAsync(`"${cliPath}" gen views "${document.fileName}"`, { cwd: workingDir });
|
|
733
|
+
vscode.window.showInformationMessage("AI-optimized specification generated successfully");
|
|
734
|
+
} catch (error) {
|
|
735
|
+
vscode.window.showErrorMessage(`AI generation failed: ${error.message}`);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
async function processSpeclyDocument(document) {
|
|
739
|
+
try {
|
|
740
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
741
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
742
|
+
const workingDir = dirname(document.fileName);
|
|
743
|
+
const outputFile = document.fileName.replace(".specly", ".yaml");
|
|
744
|
+
await execAsync(`"${cliPath}" gen yaml "${document.fileName}" --output "${outputFile}"`, { cwd: workingDir });
|
|
745
|
+
vscode.window.showInformationMessage(`Generated expanded YAML: ${outputFile}`);
|
|
746
|
+
const openFile = await vscode.window.showInformationMessage(
|
|
747
|
+
"Open generated .yaml file?",
|
|
748
|
+
"Open",
|
|
749
|
+
"Cancel"
|
|
750
|
+
);
|
|
751
|
+
if (openFile === "Open") {
|
|
752
|
+
const yamlDocument = await vscode.workspace.openTextDocument(outputFile);
|
|
753
|
+
await vscode.window.showTextDocument(yamlDocument);
|
|
754
|
+
}
|
|
755
|
+
} catch (error) {
|
|
756
|
+
vscode.window.showErrorMessage(`Processing failed: ${error.message}`);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
async function inferCompleteSystemForDocument(document) {
|
|
760
|
+
try {
|
|
761
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
762
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
763
|
+
if (!config.get("inference.enabled")) {
|
|
764
|
+
vscode.window.showInformationMessage("AI inference is disabled in settings");
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
const outputFile = document.fileName.replace(".specly", "-inferred.specly");
|
|
768
|
+
await vscode.window.withProgress({
|
|
769
|
+
location: vscode.ProgressLocation.Notification,
|
|
770
|
+
title: "Running AI inference...",
|
|
771
|
+
cancellable: false
|
|
772
|
+
}, async (progress) => {
|
|
773
|
+
progress.report({ message: "Generating complete system architecture" });
|
|
774
|
+
const workingDir = dirname(document.fileName);
|
|
775
|
+
await execAsync(`node dist/inference-engine/test-cli.js "${document.fileName}" -o "${outputFile}"`, { cwd: workingDir });
|
|
776
|
+
});
|
|
777
|
+
vscode.window.showInformationMessage(`AI-inferred complete system generated: ${outputFile}`);
|
|
778
|
+
const openFile = await vscode.window.showInformationMessage(
|
|
779
|
+
"Open AI-inferred complete system?",
|
|
780
|
+
"Open",
|
|
781
|
+
"Cancel"
|
|
782
|
+
);
|
|
783
|
+
if (openFile === "Open") {
|
|
784
|
+
const inferredDocument = await vscode.workspace.openTextDocument(outputFile);
|
|
785
|
+
await vscode.window.showTextDocument(inferredDocument);
|
|
786
|
+
}
|
|
787
|
+
} catch (error) {
|
|
788
|
+
vscode.window.showErrorMessage(`AI inference failed: ${error.message}`);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
async function runTestCycleForDocument(document) {
|
|
792
|
+
try {
|
|
793
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
794
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
795
|
+
if (!config.get("testing.enableCycle")) {
|
|
796
|
+
vscode.window.showInformationMessage("Test cycle is disabled in settings");
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
await vscode.window.withProgress({
|
|
800
|
+
location: vscode.ProgressLocation.Notification,
|
|
801
|
+
title: "Running test cycle...",
|
|
802
|
+
cancellable: false
|
|
803
|
+
}, async (progress) => {
|
|
804
|
+
progress.report({ message: "Validating \u2192 Processing \u2192 Validating" });
|
|
805
|
+
const workingDir = dirname(document.fileName);
|
|
806
|
+
await execAsync(`"${cliPath}" test cycle "${document.fileName}"`, { cwd: workingDir });
|
|
807
|
+
});
|
|
808
|
+
vscode.window.showInformationMessage("Test cycle completed successfully");
|
|
809
|
+
} catch (error) {
|
|
810
|
+
vscode.window.showErrorMessage(`Test cycle failed: ${error.message}`);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
async function executeCliCommand(document, commandName) {
|
|
814
|
+
const commandMap = {
|
|
815
|
+
// Single commands (unchanged)
|
|
816
|
+
"validate": "validate",
|
|
817
|
+
"infer": "infer",
|
|
818
|
+
"init": "init",
|
|
819
|
+
"cache": "cache",
|
|
820
|
+
"help": "help",
|
|
821
|
+
"gen": "gen",
|
|
822
|
+
"dev": "dev",
|
|
823
|
+
"test": "test",
|
|
824
|
+
// Grouped commands - map compressed names to CLI format
|
|
825
|
+
"genyaml": "gen yaml",
|
|
826
|
+
"gendiagram": "gen diagram",
|
|
827
|
+
"gendocs": "gen docs",
|
|
828
|
+
"genviews": "gen views",
|
|
829
|
+
"genall": "gen all",
|
|
830
|
+
"devquick": "dev quick",
|
|
831
|
+
"devformat": "dev format",
|
|
832
|
+
"devwatch": "dev watch",
|
|
833
|
+
"testcycle": "test cycle",
|
|
834
|
+
"testbatch": "test batch"
|
|
835
|
+
};
|
|
836
|
+
const cliCommand = commandMap[commandName] || commandName;
|
|
837
|
+
try {
|
|
838
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
839
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
840
|
+
const workingDir = dirname(document.fileName);
|
|
841
|
+
const fullCommand = `"${cliPath}" ${cliCommand} "${document.fileName}"`;
|
|
842
|
+
console.log(`[SpecVerse] Executing: ${fullCommand}`);
|
|
843
|
+
await vscode.window.withProgress({
|
|
844
|
+
location: vscode.ProgressLocation.Notification,
|
|
845
|
+
title: `Running SpecVerse ${cliCommand}...`,
|
|
846
|
+
cancellable: false
|
|
847
|
+
}, async (progress) => {
|
|
848
|
+
progress.report({ message: "Processing specification file" });
|
|
849
|
+
const { stdout: stdout2, stderr } = await execAsync(fullCommand, { cwd: workingDir });
|
|
850
|
+
if (stdout2) console.log(`[SpecVerse] stdout:`, stdout2);
|
|
851
|
+
if (stderr) console.log(`[SpecVerse] stderr:`, stderr);
|
|
852
|
+
});
|
|
853
|
+
vscode.window.showInformationMessage(`SpecVerse ${cliCommand} completed successfully`);
|
|
854
|
+
} catch (error) {
|
|
855
|
+
console.log(`[SpecVerse] Command failed:`, error);
|
|
856
|
+
const diagnostics = vscode.languages.createDiagnosticCollection(`specverse-${commandName}`);
|
|
857
|
+
const errors = [];
|
|
858
|
+
const allOutput = ((error.stdout || "") + "\n" + (error.stderr || "")).split("\n");
|
|
859
|
+
allOutput.forEach((line) => {
|
|
860
|
+
line = line.trim();
|
|
861
|
+
if (!line) return;
|
|
862
|
+
let errorMessage = "";
|
|
863
|
+
let severity = vscode.DiagnosticSeverity.Error;
|
|
864
|
+
let lineNumber = 0;
|
|
865
|
+
const numberedErrorMatch = line.match(/^\d+\.\s*(.+)$/);
|
|
866
|
+
if (numberedErrorMatch) {
|
|
867
|
+
errorMessage = numberedErrorMatch[1].trim();
|
|
868
|
+
} else if (line.includes("\u274C") && !line.includes("Validation failed")) {
|
|
869
|
+
errorMessage = line.replace(/❌/g, "").trim();
|
|
870
|
+
} else if (line.includes("Unknown property") || line.includes("should") || line.includes("required")) {
|
|
871
|
+
errorMessage = line;
|
|
872
|
+
} else if (line.includes("/") && line.includes(":")) {
|
|
873
|
+
errorMessage = line;
|
|
874
|
+
} else if (line.includes("\u26A0\uFE0F") || line.includes("Warning:")) {
|
|
875
|
+
errorMessage = line.replace(/⚠️|Warning:/, "").trim();
|
|
876
|
+
severity = vscode.DiagnosticSeverity.Warning;
|
|
877
|
+
} else if (line.includes("Validation failed") || line.includes("Validation Errors:") || line.includes("\u{1F4DD}")) {
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
if (errorMessage) {
|
|
881
|
+
const diagnostic = new vscode.Diagnostic(
|
|
882
|
+
new vscode.Range(lineNumber, 0, lineNumber, Number.MAX_SAFE_INTEGER),
|
|
883
|
+
errorMessage,
|
|
884
|
+
severity
|
|
885
|
+
);
|
|
886
|
+
diagnostic.source = "SpecVerse";
|
|
887
|
+
errors.push(diagnostic);
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
if (errors.length > 0) {
|
|
891
|
+
diagnostics.set(document.uri, errors);
|
|
892
|
+
vscode.window.showErrorMessage(`SpecVerse ${cliCommand} failed with ${errors.length} errors. See Problems panel for details.`);
|
|
893
|
+
} else {
|
|
894
|
+
const diagnostic = new vscode.Diagnostic(
|
|
895
|
+
new vscode.Range(0, 0, 0, 0),
|
|
896
|
+
`SpecVerse ${cliCommand} failed: ${error.message}`,
|
|
897
|
+
vscode.DiagnosticSeverity.Error
|
|
898
|
+
);
|
|
899
|
+
diagnostic.source = "SpecVerse";
|
|
900
|
+
diagnostics.set(document.uri, [diagnostic]);
|
|
901
|
+
vscode.window.showErrorMessage(`SpecVerse ${cliCommand} failed: ${error.message}`);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
async function runMigrateCommand(targetDir) {
|
|
906
|
+
try {
|
|
907
|
+
const config = vscode.workspace.getConfiguration("specverse");
|
|
908
|
+
const cliPath = config.get("cli.path") || "specverse";
|
|
909
|
+
const dryRun = await vscode.window.showQuickPick([
|
|
910
|
+
{ label: "Migrate", description: "Apply migration to files", value: false },
|
|
911
|
+
{ label: "Dry Run", description: "Preview changes without modifying files", value: true }
|
|
912
|
+
], {
|
|
913
|
+
placeHolder: "Choose migration mode"
|
|
914
|
+
});
|
|
915
|
+
if (!dryRun) return;
|
|
916
|
+
const dryRunFlag = dryRun.value ? "--dry-run" : "";
|
|
917
|
+
const fullCommand = `"${cliPath}" migrate ${dryRunFlag} "${targetDir}"`;
|
|
918
|
+
console.log(`[SpecVerse] Executing: ${fullCommand}`);
|
|
919
|
+
await vscode.window.withProgress({
|
|
920
|
+
location: vscode.ProgressLocation.Notification,
|
|
921
|
+
title: `Migrating specs to v3.5 format...`,
|
|
922
|
+
cancellable: false
|
|
923
|
+
}, async (progress) => {
|
|
924
|
+
progress.report({ message: dryRun.value ? "Previewing changes..." : "Applying migration..." });
|
|
925
|
+
const { stdout: stdout2, stderr } = await execAsync(fullCommand, { cwd: targetDir });
|
|
926
|
+
const outputChannel = vscode.window.createOutputChannel("SpecVerse Migration");
|
|
927
|
+
outputChannel.clear();
|
|
928
|
+
if (stdout2) outputChannel.appendLine(stdout2);
|
|
929
|
+
if (stderr) outputChannel.appendLine(stderr);
|
|
930
|
+
outputChannel.show();
|
|
931
|
+
});
|
|
932
|
+
vscode.window.showInformationMessage(
|
|
933
|
+
dryRun.value ? "Migration preview complete. Check output for details." : "Migration completed successfully!"
|
|
934
|
+
);
|
|
935
|
+
} catch (error) {
|
|
936
|
+
vscode.window.showErrorMessage(`Migration failed: ${error.message}`);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
async function runBatchDiagramGeneration() {
|
|
940
|
+
try {
|
|
941
|
+
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
|
942
|
+
if (!workspaceFolder) {
|
|
943
|
+
vscode.window.showErrorMessage("No workspace folder found");
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
await vscode.window.withProgress({
|
|
947
|
+
location: vscode.ProgressLocation.Notification,
|
|
948
|
+
title: "Generating batch diagrams...",
|
|
949
|
+
cancellable: false
|
|
950
|
+
}, async (progress) => {
|
|
951
|
+
progress.report({ message: "Processing all .specly files and generating enhanced diagrams" });
|
|
952
|
+
await execAsync("node scripts/generate-diagrams-batch.js", { cwd: workspaceFolder.uri.fsPath });
|
|
953
|
+
});
|
|
954
|
+
vscode.window.showInformationMessage("Batch diagram generation completed successfully");
|
|
955
|
+
} catch (error) {
|
|
956
|
+
vscode.window.showErrorMessage(`Batch diagram generation failed: ${error.message}`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
function deactivate() {
|
|
960
|
+
console.log("SpecVerse v3.5 extension deactivated");
|
|
961
|
+
}
|
|
962
|
+
export {
|
|
963
|
+
activate,
|
|
964
|
+
deactivate
|
|
965
|
+
};
|