agent-workflow-kit-cli 1.3.3 ā 1.3.5
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/README.md +158 -9
- package/dist/cli/commands/add.js +1 -1
- package/dist/cli/commands/doctor.js +145 -47
- package/dist/cli/commands/ui.js +192 -0
- package/dist/cli/index.js +15 -1
- package/package.json +4 -2
- package/templates/devops/AGENTS.md.hbs +32 -0
- package/templates/devops/skills/devops/SKILL.md +477 -0
- package/templates/diagram/AGENTS.md.hbs +30 -0
- package/templates/diagram/skills/drawio-diagram/SKILL.md +427 -0
- package/templates/dotnet/AGENTS.md.hbs +11 -7
- package/templates/express/AGENTS.md.hbs +13 -9
- package/templates/fastapi/AGENTS.md.hbs +25 -3
- package/templates/fastapi/rules/api-testing.md +24 -0
- package/templates/fastapi/rules/database-async.md +26 -0
- package/templates/golang/AGENTS.md.hbs +15 -9
- package/templates/golang/skills/golang-db/SKILL.md +27 -0
- package/templates/golang/skills/golang-feature/SKILL.md +42 -0
- package/templates/nestjs/AGENTS.md.hbs +13 -9
- package/templates/next-js/AGENTS.md.hbs +13 -9
- package/templates/rust/AGENTS.md.hbs +16 -9
- package/templates/rust/skills/rust-db/SKILL.md +27 -0
- package/templates/rust/skills/rust-feature/SKILL.md +34 -0
- package/ui-dist/assets/Antigravity-IRHfUNd0.webp +0 -0
- package/ui-dist/assets/Codex-B3jt494H.png +0 -0
- package/ui-dist/assets/Logo-DARneFJW.png +0 -0
- package/ui-dist/assets/ReactTS-Cv7D5v-r.png +0 -0
- package/ui-dist/assets/devops-DfKGji1l.png +0 -0
- package/ui-dist/assets/drawio-D1K35acK.png +0 -0
- package/ui-dist/assets/expressjs-cjiJ1MIq.png +0 -0
- package/ui-dist/assets/fastapi-x75ez5Tf.png +0 -0
- package/ui-dist/assets/golang-DWpOzDNa.png +0 -0
- package/ui-dist/assets/index-BhHU4Khx.js +372 -0
- package/ui-dist/assets/index-C0BHmZv8.css +1 -0
- package/ui-dist/assets/nestjs-CZk_FY6t.png +0 -0
- package/ui-dist/assets/nextjs-DIQjv1J3.png +0 -0
- package/ui-dist/assets/python-CfV_cs4B.png +0 -0
- package/ui-dist/assets/rust-A_NnBwqP.png +0 -0
- package/ui-dist/assets/springbootjava--7jHXzq_.jpg +0 -0
- package/ui-dist/index.html +47 -0
package/README.md
CHANGED
|
@@ -162,21 +162,78 @@ packages = ["src"]
|
|
|
162
162
|
|
|
163
163
|
---
|
|
164
164
|
|
|
165
|
+
### š Next.js Frontend Pack
|
|
166
|
+
Tailored for Next.js App or Pages Router web application structures.
|
|
167
|
+
- **Default Architecture:** Standard layouts, page component separations, and shared Tailwind/styling assets.
|
|
168
|
+
- **Tooling Defaults:** Next CLI tools, ESLint, strict TypeScript rules.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### š”ļø NestJS & Express Backend Packs
|
|
173
|
+
Designed for structured modular Node.js backend systems.
|
|
174
|
+
- **Default Architecture:** Dependency injection modular patterns (NestJS) or routing/middleware setups (Express).
|
|
175
|
+
- **Tooling Defaults:** ESLint, Prettier, and Vitest/Jest for automated testing.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### š¤ Python AI Pack
|
|
180
|
+
Custom-tailored rules and structures for AI modeling, machine learning, and data science.
|
|
181
|
+
- **Default Architecture:** Machine learning pipelines, model loaders, script helpers, and notebook guides.
|
|
182
|
+
- **Tooling Defaults:** Ruff linting, mypy strict type checking, and standard AI environment libraries.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### āļø .NET Core C# Pack
|
|
187
|
+
Designed for enterprise-level applications written in C# on .NET Core.
|
|
188
|
+
- **Default Architecture:** Clean-architecture layers (controllers, application services, domain interfaces, and infrastructure/data layers).
|
|
189
|
+
- **Tooling Defaults:** C# styling guides, standard `dotnet test` suites.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### š¹ Go Pack
|
|
194
|
+
High-performance Go backend structures.
|
|
195
|
+
- **Default Architecture:** Go standard layout structures, clear service boundary interfaces, and separation of concerns.
|
|
196
|
+
- **Tooling Defaults:** `go fmt`, `go vet`, and standard go test execution.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### š¦ Rust Pack
|
|
201
|
+
Memory-safe, high-performance systems and backend libraries in Rust.
|
|
202
|
+
- **Default Architecture:** Workspace modules, cargo configurations, and library/binary structures.
|
|
203
|
+
- **Tooling Defaults:** `cargo clippy`, `cargo fmt`, and standard Rust test suites.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### š¦ DevOps Stack Pack
|
|
208
|
+
Standardizes container, pipeline, and automation configurations.
|
|
209
|
+
- **Default Architecture:** Multi-stage Dockerfiles, Docker Compose environment setups, and CI/CD pipelines (GitHub Actions, GitLab CI).
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### š Diagram Pack
|
|
214
|
+
Organizes system designs and architectures using standard diagrams.
|
|
215
|
+
- **Default Architecture:** Standard guidelines for UML, Mermaid blocks, and PlantUML designs.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
165
219
|
## š Quick Start
|
|
166
220
|
|
|
167
221
|
Initialize the toolkit instantly inside your project repository using `npx` (no local installation required):
|
|
168
222
|
|
|
169
223
|
```bash
|
|
170
|
-
# 1.
|
|
224
|
+
# 1. Launch the local configuration wizard web dashboard (Optional)
|
|
225
|
+
npx agent-workflow-kit-cli ui
|
|
226
|
+
|
|
227
|
+
# 2. Or initialize rules and custom skills directly via CLI
|
|
171
228
|
npx agent-workflow-kit-cli init
|
|
172
229
|
|
|
173
|
-
#
|
|
230
|
+
# 3. View the generated AGENTS.md guidelines at your repository root
|
|
174
231
|
cat AGENTS.md
|
|
175
232
|
|
|
176
|
-
#
|
|
233
|
+
# 4. Export custom skills to register them with Antigravity
|
|
177
234
|
npx agent-workflow-kit-cli export antigravity
|
|
178
235
|
|
|
179
|
-
#
|
|
236
|
+
# 5. Enable pre-commit auto-validation hook
|
|
180
237
|
npx agent-workflow-kit-cli doctor --install-hook
|
|
181
238
|
```
|
|
182
239
|
|
|
@@ -191,12 +248,12 @@ npx agent-workflow-kit-cli <command> [options]
|
|
|
191
248
|
```
|
|
192
249
|
|
|
193
250
|
### 1. `init`
|
|
194
|
-
Analyzes the directory manifests (`package.json`, `pom.xml`, `pyproject.toml
|
|
251
|
+
Analyzes the directory manifests (`package.json`, `pom.xml`, `pyproject.toml`, etc.) and bootstraps agent guidelines (`AGENTS.md`) and custom skills (`.agents/`).
|
|
195
252
|
|
|
196
253
|
* **Usage:** `npx agent-workflow-kit-cli init [options]`
|
|
197
254
|
* **Options:**
|
|
198
255
|
* `--stack <stack>`: Specify the target project stack.
|
|
199
|
-
* Values: `auto`, `spring-boot`, `react-ts`, `fastapi`
|
|
256
|
+
* Values: `auto`, `spring-boot`, `react-ts`, `next-js`, `nestjs`, `express`, `fastapi`, `python-ai`, `dotnet`, `golang`, `rust`, `devops`, `diagram`
|
|
200
257
|
* Default: `auto` (auto-detects project manifests)
|
|
201
258
|
* `--agent <agent>`: Target profile settings.
|
|
202
259
|
* Values: `both`, `codex`, `antigravity`
|
|
@@ -205,7 +262,30 @@ Analyzes the directory manifests (`package.json`, `pom.xml`, `pyproject.toml`) a
|
|
|
205
262
|
|
|
206
263
|
---
|
|
207
264
|
|
|
208
|
-
### 2. `
|
|
265
|
+
### 2. `ui`
|
|
266
|
+
Launches the local configuration wizard web dashboard in your browser.
|
|
267
|
+
|
|
268
|
+
* **Usage:** `npx agent-workflow-kit-cli ui [options]`
|
|
269
|
+
* **Options:**
|
|
270
|
+
* `-p, --port <port>`: Port to run the UI server on.
|
|
271
|
+
* Default: `4321`
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
### 3. `add <stack>`
|
|
276
|
+
Manually installs a specific stack pack guidelines and templates to a folder.
|
|
277
|
+
|
|
278
|
+
* **Usage:** `npx agent-workflow-kit-cli add <stack> [options]`
|
|
279
|
+
* **Arguments:**
|
|
280
|
+
* `<stack>`: The stack pack name (e.g. `spring-boot`, `react-ts`, `rust`, etc.).
|
|
281
|
+
* **Options:**
|
|
282
|
+
* `--path <path>`: Target folder path to install the guidelines (default: `.`).
|
|
283
|
+
* `--agent <agent>`: Specify target agent profile: `both | codex | antigravity` (default: `both`).
|
|
284
|
+
* `--dry-run`: Simulation mode. Logs actions without writing files to disk.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### 4. `sync`
|
|
209
289
|
Re-evaluates the manifests and updates existing guidelines/skills inside the workspace without touching user modifications. It writes changes inside the `<!-- AWK-START: <id> -->` managed blocks.
|
|
210
290
|
|
|
211
291
|
* **Usage:** `npx agent-workflow-kit-cli sync [options]`
|
|
@@ -214,7 +294,7 @@ Re-evaluates the manifests and updates existing guidelines/skills inside the wor
|
|
|
214
294
|
|
|
215
295
|
---
|
|
216
296
|
|
|
217
|
-
###
|
|
297
|
+
### 5. `doctor`
|
|
218
298
|
Checks consistency of active agent configurations, environment setups, and executes the validation test commands configured for detected stacks.
|
|
219
299
|
|
|
220
300
|
* **Usage:** `npx agent-workflow-kit-cli doctor [options]`
|
|
@@ -223,7 +303,7 @@ Checks consistency of active agent configurations, environment setups, and execu
|
|
|
223
303
|
|
|
224
304
|
---
|
|
225
305
|
|
|
226
|
-
###
|
|
306
|
+
### 6. `export <target>`
|
|
227
307
|
Exports and bundles custom workflows and skills in `.agents/skills/` into a single consolidated string optimized for the target AI agent console.
|
|
228
308
|
|
|
229
309
|
* **Usage:** `npx agent-workflow-kit-cli export <target> [options]`
|
|
@@ -231,6 +311,75 @@ Exports and bundles custom workflows and skills in `.agents/skills/` into a sing
|
|
|
231
311
|
* `<target>`: The name of the target agent (e.g., `antigravity`).
|
|
232
312
|
* **Options:**
|
|
233
313
|
* `--no-clipboard`: Prevents the CLI from copying the bundled instructions directly to the system clipboard (by default, it will attempt to copy to the clipboard first, fallback to printing on stdout).
|
|
314
|
+
* `-o, --output <file>`: Write the exported guidelines to a specific file.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### 7. `run <workflow>`
|
|
319
|
+
Runs an AWOS graph workflow directly from the command line.
|
|
320
|
+
|
|
321
|
+
* **Usage:** `npx agent-workflow-kit-cli run <workflow> [options]`
|
|
322
|
+
* **Arguments:**
|
|
323
|
+
* `<workflow>`: Path to the workflow JSON configuration.
|
|
324
|
+
* **Options:**
|
|
325
|
+
* `--inputs <inputs>`: Path to input parameters JSON file or inline JSON string.
|
|
326
|
+
* `--dry-run`: Run workflow nodes in dry-run simulation mode.
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
### 8. `resume <runId>`
|
|
331
|
+
Resumes a suspended or paused AWOS workflow run.
|
|
332
|
+
|
|
333
|
+
* **Usage:** `npx agent-workflow-kit-cli resume <runId>`
|
|
334
|
+
* **Arguments:**
|
|
335
|
+
* `<runId>`: The ID of the suspended workflow run.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
### 9. `profile`
|
|
340
|
+
Validates directory architecture structure and rule boundaries using custom structural rules.
|
|
341
|
+
|
|
342
|
+
* **Usage:** `npx agent-workflow-kit-cli profile [options]`
|
|
343
|
+
* **Options:**
|
|
344
|
+
* `--profile <profile>`: Target profile name (e.g. `clean-architecture`).
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
### 10. `workflow` (Subcommands)
|
|
349
|
+
Manage and execute registered AWOS workflow packs.
|
|
350
|
+
|
|
351
|
+
* **Subcommands:**
|
|
352
|
+
* `list`: List all discovered/registered workflow packs.
|
|
353
|
+
* `show <id>`: Show details, steps, and metadata of a workflow pack.
|
|
354
|
+
* `validate <id>`: Validate a workflow pack's JSON schema and graph cycle constraints.
|
|
355
|
+
* `run <id> [options]`: Execute a registered workflow pack by ID (options: `--inputs`, `--dry-run`).
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
### 11. `role` (Subcommands)
|
|
360
|
+
Manage agent role profiles.
|
|
361
|
+
|
|
362
|
+
* **Subcommands:**
|
|
363
|
+
* `list`: List all discovered agent role profiles in the catalog.
|
|
364
|
+
* `show <id>`: Show agent role details, checkpoints, and inputs schema.
|
|
365
|
+
* `validate`: Validate all role catalog configuration files against schema parameters.
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
### 12. `adr` (Subcommands)
|
|
370
|
+
Manage Architectural Decision Records (ADR).
|
|
371
|
+
|
|
372
|
+
* **Subcommands:**
|
|
373
|
+
* `create [options]`: Create a new numbered ADR document. Options:
|
|
374
|
+
* `--title <title>`: ADR title.
|
|
375
|
+
* `--status <status>`: Status: `proposed | accepted | rejected | superseded` (default: `proposed`).
|
|
376
|
+
* `--context <context>`: Background context.
|
|
377
|
+
* `--decision <decision>`: Decision details.
|
|
378
|
+
* `--consequences <consequences>`: Repercussions/consequences.
|
|
379
|
+
* `--decision-maker <maker>`: Name/role of decision maker (default: `AWOS System`).
|
|
380
|
+
* `list`: List all saved ADR documents.
|
|
381
|
+
* `show <id>`: Display details of a numbered ADR document.
|
|
382
|
+
* `search <keyword>`: Search all ADR documents for keyword text matches.
|
|
234
383
|
|
|
235
384
|
---
|
|
236
385
|
|
package/dist/cli/commands/add.js
CHANGED
|
@@ -11,7 +11,7 @@ import { analyzeModule } from "../../core/analyzer.js";
|
|
|
11
11
|
import { updateGitignore } from "./init.js";
|
|
12
12
|
export async function runAdd(stack, options) {
|
|
13
13
|
const targetStack = stack.toLowerCase();
|
|
14
|
-
const validStacks = ["spring-boot", "react-ts", "next-js", "nestjs", "express", "fastapi", "python-ai", "dotnet", "golang", "rust"];
|
|
14
|
+
const validStacks = ["spring-boot", "react-ts", "next-js", "nestjs", "express", "fastapi", "python-ai", "dotnet", "golang", "rust", "diagram", "devops"];
|
|
15
15
|
if (!validStacks.includes(targetStack)) {
|
|
16
16
|
console.error(chalk.red(`Error: Invalid stack '${stack}'. Supported stacks are: ${validStacks.join(", ")}`));
|
|
17
17
|
process.exit(1);
|
|
@@ -6,7 +6,7 @@ import chalk from "chalk";
|
|
|
6
6
|
import { promises as fs } from "fs";
|
|
7
7
|
import path from "path";
|
|
8
8
|
import { execa } from "execa";
|
|
9
|
-
import {
|
|
9
|
+
import { detectProjectModules } from "../../core/detector.js";
|
|
10
10
|
export async function runDoctor(options) {
|
|
11
11
|
const cwd = process.cwd();
|
|
12
12
|
if (options.installHook) {
|
|
@@ -55,63 +55,161 @@ npx agent-workflow-kit-cli doctor || exit 1
|
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
console.log(chalk.blue("Running doctor checks on repository..."));
|
|
58
|
-
// 1. Detect
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
console.log(chalk.yellow("No standard stack pack detected. Nothing to validate."));
|
|
58
|
+
// 1. Detect Modules
|
|
59
|
+
const modules = await detectProjectModules(cwd);
|
|
60
|
+
if (modules.length === 0) {
|
|
61
|
+
console.log(chalk.yellow("No standard project modules detected. Nothing to validate."));
|
|
63
62
|
return;
|
|
64
63
|
}
|
|
64
|
+
console.log(chalk.gray(`Detected modules: ${modules.map(m => `${m.name} [${m.stacks.join(", ")}]`).join(", ")}`));
|
|
65
65
|
// 2. Validate
|
|
66
66
|
let passed = true;
|
|
67
|
-
for (const
|
|
68
|
-
console.log(chalk.blue(`\nValidating
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
for (const mod of modules) {
|
|
68
|
+
console.log(chalk.blue(`\nValidating module: ${mod.name === "." ? "root" : mod.name}...`));
|
|
69
|
+
for (const stack of mod.stacks) {
|
|
70
|
+
console.log(chalk.cyan(` Validating stack: ${stack}...`));
|
|
71
|
+
try {
|
|
72
|
+
if (stack === "spring-boot") {
|
|
73
|
+
// Check for maven wrapper or gradle wrapper
|
|
74
|
+
const hasGradle = (await fs.stat(path.join(mod.dir, "build.gradle")).then((s) => s.isFile()).catch(() => false)) ||
|
|
75
|
+
(await fs.stat(path.join(mod.dir, "build.gradle.kts")).then((s) => s.isFile()).catch(() => false));
|
|
76
|
+
if (hasGradle) {
|
|
77
|
+
let gradlewPath = "./gradlew";
|
|
78
|
+
try {
|
|
79
|
+
await fs.access(path.join(mod.dir, "gradlew"));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
try {
|
|
83
|
+
await fs.access(path.join(cwd, "gradlew"));
|
|
84
|
+
gradlewPath = path.relative(mod.dir, path.join(cwd, "gradlew")).replace(/\\/g, "/");
|
|
85
|
+
if (!gradlewPath.startsWith("."))
|
|
86
|
+
gradlewPath = "./" + gradlewPath;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
gradlewPath = "gradle";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
console.log(chalk.gray(`Running: ${gradlewPath} check in ${mod.dir}`));
|
|
93
|
+
await execa(gradlewPath, ["check"], { cwd: mod.dir, stdio: "inherit" });
|
|
86
94
|
}
|
|
87
95
|
else {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
args = ["run", "python", "-m", "ruff", "check", "."];
|
|
96
|
+
let mvnwPath = "./mvnw";
|
|
97
|
+
try {
|
|
98
|
+
await fs.access(path.join(mod.dir, "mvnw"));
|
|
92
99
|
}
|
|
100
|
+
catch {
|
|
101
|
+
try {
|
|
102
|
+
await fs.access(path.join(cwd, "mvnw"));
|
|
103
|
+
mvnwPath = path.relative(mod.dir, path.join(cwd, "mvnw")).replace(/\\/g, "/");
|
|
104
|
+
if (!mvnwPath.startsWith("."))
|
|
105
|
+
mvnwPath = "./" + mvnwPath;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
mvnwPath = "mvn";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
console.log(chalk.gray(`Running: ${mvnwPath} clean compile in ${mod.dir}`));
|
|
112
|
+
await execa(mvnwPath, ["clean", "compile"], { cwd: mod.dir, stdio: "inherit" });
|
|
93
113
|
}
|
|
94
114
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
else if (stack === "fastapi") {
|
|
116
|
+
console.log(chalk.gray(`Running: ruff check . in ${mod.dir}`));
|
|
117
|
+
await execa("ruff", ["check", "."], { cwd: mod.dir, stdio: "inherit" });
|
|
118
|
+
}
|
|
119
|
+
else if (stack === "python-ai") {
|
|
120
|
+
let cmd = "ruff";
|
|
121
|
+
let args = ["check", "."];
|
|
122
|
+
try {
|
|
123
|
+
const hasPoetry = await fs.stat(path.join(mod.dir, "poetry.lock")).then(s => s.isFile()).catch(() => false);
|
|
124
|
+
if (hasPoetry) {
|
|
125
|
+
cmd = "poetry";
|
|
126
|
+
args = ["run", "ruff", "check", "."];
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const hasPipenv = await fs.stat(path.join(mod.dir, "Pipfile")).then(s => s.isFile()).catch(() => false);
|
|
130
|
+
if (hasPipenv) {
|
|
131
|
+
cmd = "pipenv";
|
|
132
|
+
args = ["run", "ruff", "check", "."];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch { }
|
|
137
|
+
console.log(chalk.gray(`Running: ${cmd} ${args.join(" ")} in ${mod.dir}`));
|
|
138
|
+
await execa(cmd, args, { cwd: mod.dir, stdio: "inherit" });
|
|
139
|
+
}
|
|
140
|
+
else if (stack === "react-ts" ||
|
|
141
|
+
stack === "next-js" ||
|
|
142
|
+
stack === "nestjs" ||
|
|
143
|
+
stack === "express") {
|
|
144
|
+
let pm = "npm";
|
|
145
|
+
try {
|
|
146
|
+
const hasYarnLock = await fs.stat(path.join(mod.dir, "yarn.lock")).then((s) => s.isFile()).catch(() => false);
|
|
147
|
+
const hasPnpmLock = await fs.stat(path.join(mod.dir, "pnpm-lock.yaml")).then((s) => s.isFile()).catch(() => false);
|
|
148
|
+
const hasBunLockb = await fs.stat(path.join(mod.dir, "bun.lockb")).then((s) => s.isFile()).catch(() => false);
|
|
149
|
+
const hasBunLock = await fs.stat(path.join(mod.dir, "bun.lock")).then((s) => s.isFile()).catch(() => false);
|
|
150
|
+
if (hasYarnLock)
|
|
151
|
+
pm = "yarn";
|
|
152
|
+
else if (hasPnpmLock)
|
|
153
|
+
pm = "pnpm";
|
|
154
|
+
else if (hasBunLockb || hasBunLock)
|
|
155
|
+
pm = "bun";
|
|
156
|
+
}
|
|
157
|
+
catch { }
|
|
158
|
+
try {
|
|
159
|
+
const pkgContent = await fs.readFile(path.join(mod.dir, "package.json"), "utf8");
|
|
160
|
+
const pkg = JSON.parse(pkgContent);
|
|
161
|
+
const scripts = pkg.scripts || {};
|
|
162
|
+
if (scripts.lint) {
|
|
163
|
+
console.log(chalk.gray(`Running: ${pm} run lint in ${mod.dir}`));
|
|
164
|
+
await execa(pm, ["run", "lint"], { cwd: mod.dir, stdio: "inherit" });
|
|
165
|
+
}
|
|
166
|
+
console.log(chalk.gray(`Running: npx tsc --noEmit in ${mod.dir}`));
|
|
167
|
+
await execa("npx", ["tsc", "--noEmit"], { cwd: mod.dir, stdio: "inherit" });
|
|
168
|
+
if (scripts.test) {
|
|
169
|
+
const hasVitest = pkg.dependencies?.vitest || pkg.devDependencies?.vitest;
|
|
170
|
+
const hasJest = pkg.dependencies?.jest || pkg.devDependencies?.jest;
|
|
171
|
+
let args = ["run", "test"];
|
|
172
|
+
if (hasVitest || hasJest) {
|
|
173
|
+
if (pm === "npm") {
|
|
174
|
+
args = ["run", "test", "--", "--run"];
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
args = ["run", "test", "--run"];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
console.log(chalk.gray(`Running: ${pm} ${args.join(" ")} in ${mod.dir}`));
|
|
181
|
+
await execa(pm, args, { cwd: mod.dir, stdio: "inherit", env: { ...process.env, CI: "true" } });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
console.log(chalk.gray(`Running: npx tsc --noEmit in ${mod.dir}`));
|
|
186
|
+
await execa("npx", ["tsc", "--noEmit"], { cwd: mod.dir, stdio: "inherit" });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else if (stack === "dotnet") {
|
|
190
|
+
console.log(chalk.gray(`Running: dotnet build in ${mod.dir}`));
|
|
191
|
+
await execa("dotnet", ["build"], { cwd: mod.dir, stdio: "inherit" });
|
|
192
|
+
console.log(chalk.gray(`Running: dotnet test in ${mod.dir}`));
|
|
193
|
+
await execa("dotnet", ["test"], { cwd: mod.dir, stdio: "inherit" });
|
|
194
|
+
}
|
|
195
|
+
else if (stack === "golang") {
|
|
196
|
+
console.log(chalk.gray(`Running: go build ./... in ${mod.dir}`));
|
|
197
|
+
await execa("go", ["build", "./..."], { cwd: mod.dir, stdio: "inherit" });
|
|
198
|
+
console.log(chalk.gray(`Running: go test ./... in ${mod.dir}`));
|
|
199
|
+
await execa("go", ["test", "./..."], { cwd: mod.dir, stdio: "inherit" });
|
|
200
|
+
}
|
|
201
|
+
else if (stack === "rust") {
|
|
202
|
+
console.log(chalk.gray(`Running: cargo check in ${mod.dir}`));
|
|
203
|
+
await execa("cargo", ["check"], { cwd: mod.dir, stdio: "inherit" });
|
|
204
|
+
console.log(chalk.gray(`Running: cargo test in ${mod.dir}`));
|
|
205
|
+
await execa("cargo", ["test"], { cwd: mod.dir, stdio: "inherit" });
|
|
206
|
+
}
|
|
207
|
+
console.log(chalk.green(` āļø ${stack} validation passed!`));
|
|
105
208
|
}
|
|
106
|
-
|
|
107
|
-
console.
|
|
108
|
-
|
|
209
|
+
catch (err) {
|
|
210
|
+
console.error(chalk.red(` ā ${stack} validation failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
211
|
+
passed = false;
|
|
109
212
|
}
|
|
110
|
-
console.log(chalk.green(`āļø ${stack} validation passed!`));
|
|
111
|
-
}
|
|
112
|
-
catch (err) {
|
|
113
|
-
console.error(chalk.red(`ā ${stack} validation failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
114
|
-
passed = false;
|
|
115
213
|
}
|
|
116
214
|
}
|
|
117
215
|
if (passed) {
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
import http from "http";
|
|
6
|
+
import fs from "fs/promises";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { exec } from "child_process";
|
|
11
|
+
import { detectProjectModules } from "../../core/detector.js";
|
|
12
|
+
import { runInit } from "./init.js";
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
// Map file extensions to MIME types
|
|
16
|
+
const MIME_TYPES = {
|
|
17
|
+
".html": "text/html",
|
|
18
|
+
".css": "text/css",
|
|
19
|
+
".js": "text/javascript",
|
|
20
|
+
".json": "application/json",
|
|
21
|
+
".png": "image/png",
|
|
22
|
+
".jpg": "image/jpeg",
|
|
23
|
+
".jpeg": "image/jpeg",
|
|
24
|
+
".gif": "image/gif",
|
|
25
|
+
".svg": "image/svg+xml",
|
|
26
|
+
".ico": "image/x-icon",
|
|
27
|
+
};
|
|
28
|
+
// Find static assets directory in dev and production build structures
|
|
29
|
+
async function findUiDistDir() {
|
|
30
|
+
const possiblePaths = [
|
|
31
|
+
// Production path (dist/ui-dist) relative to dist/cli/commands/ui.js
|
|
32
|
+
path.join(__dirname, "..", "..", "..", "ui-dist"),
|
|
33
|
+
// Development path relative to src/cli/commands/ui.ts
|
|
34
|
+
path.join(__dirname, "..", "..", "..", "..", "agent-workflow-kit", "dist"),
|
|
35
|
+
];
|
|
36
|
+
for (const p of possiblePaths) {
|
|
37
|
+
try {
|
|
38
|
+
const stats = await fs.stat(p);
|
|
39
|
+
if (stats.isDirectory()) {
|
|
40
|
+
return p;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Continue searching
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
throw new Error("Could not find frontend UI assets directory (ui-dist). Make sure the Vite app is built.");
|
|
48
|
+
}
|
|
49
|
+
// Open URL in native browser
|
|
50
|
+
function openBrowser(url) {
|
|
51
|
+
const startCmd = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
52
|
+
if (process.platform === "win32") {
|
|
53
|
+
// Windows start requires extra care with double quotes
|
|
54
|
+
exec(`start "" "${url}"`);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
exec(`${startCmd} "${url}"`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export async function runUiServer(options) {
|
|
61
|
+
const port = parseInt(options.port, 10) || 4321;
|
|
62
|
+
const cwd = process.cwd();
|
|
63
|
+
let uiDistDir = "";
|
|
64
|
+
try {
|
|
65
|
+
uiDistDir = await findUiDistDir();
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error(chalk.red(`\nā Error: ${err.message}`));
|
|
69
|
+
console.log(chalk.yellow("š Please run 'npm run build' in the 'agent-workflow-kit' directory first.\n"));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const server = http.createServer(async (req, res) => {
|
|
73
|
+
// Enable CORS for development mode proxying
|
|
74
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
75
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
76
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
77
|
+
if (req.method === "OPTIONS") {
|
|
78
|
+
res.writeHead(200);
|
|
79
|
+
res.end();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
83
|
+
const pathname = url.pathname;
|
|
84
|
+
// API - GET /api/status
|
|
85
|
+
if (pathname === "/api/status" && req.method === "GET") {
|
|
86
|
+
try {
|
|
87
|
+
const detected = await detectProjectModules(cwd);
|
|
88
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
89
|
+
res.end(JSON.stringify({
|
|
90
|
+
isLocalCli: true,
|
|
91
|
+
cwd,
|
|
92
|
+
detectedModules: detected,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
97
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// API - POST /api/generate
|
|
102
|
+
if (pathname === "/api/generate" && req.method === "POST") {
|
|
103
|
+
let body = "";
|
|
104
|
+
req.on("data", (chunk) => {
|
|
105
|
+
body += chunk;
|
|
106
|
+
});
|
|
107
|
+
req.on("end", async () => {
|
|
108
|
+
try {
|
|
109
|
+
const config = JSON.parse(body);
|
|
110
|
+
const stack = config.stack || "auto";
|
|
111
|
+
const agent = config.agent || "both";
|
|
112
|
+
const dryRun = !!config.dryRun;
|
|
113
|
+
const logs = [];
|
|
114
|
+
const originalLog = console.log;
|
|
115
|
+
const originalWarn = console.warn;
|
|
116
|
+
const originalError = console.error;
|
|
117
|
+
// Capture console outputs to send back to the web console UI
|
|
118
|
+
console.log = (...args) => {
|
|
119
|
+
logs.push(args.join(" "));
|
|
120
|
+
originalLog(...args);
|
|
121
|
+
};
|
|
122
|
+
console.warn = (...args) => {
|
|
123
|
+
logs.push("[WARN] " + args.join(" "));
|
|
124
|
+
originalWarn(...args);
|
|
125
|
+
};
|
|
126
|
+
console.error = (...args) => {
|
|
127
|
+
logs.push("[ERROR] " + args.join(" "));
|
|
128
|
+
originalError(...args);
|
|
129
|
+
};
|
|
130
|
+
try {
|
|
131
|
+
await runInit({ stack, agent, dryRun });
|
|
132
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
133
|
+
res.end(JSON.stringify({ success: true, logs }));
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
137
|
+
res.end(JSON.stringify({ success: false, error: err.message, logs }));
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
// Restore console methods
|
|
141
|
+
console.log = originalLog;
|
|
142
|
+
console.warn = originalWarn;
|
|
143
|
+
console.error = originalError;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
148
|
+
res.end(JSON.stringify({ error: "Invalid JSON payload" }));
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Serve Static Files
|
|
154
|
+
let filePath = path.join(uiDistDir, pathname === "/" ? "index.html" : pathname);
|
|
155
|
+
// Guard against directory traversal attacks
|
|
156
|
+
if (!filePath.startsWith(uiDistDir)) {
|
|
157
|
+
res.writeHead(403);
|
|
158
|
+
res.end("Forbidden");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
let stats = await fs.stat(filePath);
|
|
163
|
+
if (stats.isDirectory()) {
|
|
164
|
+
filePath = path.join(filePath, "index.html");
|
|
165
|
+
}
|
|
166
|
+
const content = await fs.readFile(filePath);
|
|
167
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
168
|
+
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
169
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
170
|
+
res.end(content);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// For SPA fallback support: Serve index.html if file not found
|
|
174
|
+
try {
|
|
175
|
+
const indexContent = await fs.readFile(path.join(uiDistDir, "index.html"));
|
|
176
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
177
|
+
res.end(indexContent);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
res.writeHead(404);
|
|
181
|
+
res.end("Not Found");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
server.listen(port, () => {
|
|
186
|
+
const localUrl = `http://localhost:${port}`;
|
|
187
|
+
console.log(chalk.bold.green(`\nš„ļø Agent Workflow Kit local dashboard running at: ${localUrl}`));
|
|
188
|
+
console.log(chalk.dim(`Press Ctrl+C to stop the server.\n`));
|
|
189
|
+
// Automatically open in user browser
|
|
190
|
+
openBrowser(localUrl);
|
|
191
|
+
});
|
|
192
|
+
}
|