@lhi/n8m 0.2.4 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -15
- package/dist/agentic/graph.d.ts +16 -4
- package/dist/agentic/nodes/architect.d.ts +2 -2
- package/dist/agentic/nodes/architect.js +5 -1
- package/dist/agentic/nodes/engineer.d.ts +6 -0
- package/dist/agentic/nodes/engineer.js +39 -5
- package/dist/commands/create.js +38 -1
- package/dist/commands/deploy.d.ts +2 -1
- package/dist/commands/deploy.js +119 -19
- package/dist/commands/fixture.js +31 -8
- package/dist/commands/learn.d.ts +19 -0
- package/dist/commands/learn.js +277 -0
- package/dist/commands/modify.js +210 -68
- package/dist/commands/test.d.ts +4 -0
- package/dist/commands/test.js +118 -14
- package/dist/services/ai.service.d.ts +33 -0
- package/dist/services/ai.service.js +337 -2
- package/dist/services/node-definitions.service.d.ts +8 -0
- package/dist/services/node-definitions.service.js +45 -0
- package/dist/utils/fixtureManager.d.ts +10 -0
- package/dist/utils/fixtureManager.js +43 -4
- package/dist/utils/multilinePrompt.js +33 -47
- package/dist/utils/n8nClient.js +60 -11
- package/docs/DEVELOPER_GUIDE.md +598 -0
- package/docs/N8N_NODE_REFERENCE.md +369 -0
- package/docs/patterns/bigquery-via-http.md +110 -0
- package/oclif.manifest.json +82 -3
- package/package.json +3 -1
- package/dist/fixture-schema.json +0 -162
- package/dist/resources/node-definitions-fallback.json +0 -390
- package/dist/resources/node-test-hints.json +0 -188
- package/dist/resources/workflow-test-fixtures.json +0 -42
package/README.md
CHANGED
|
@@ -198,15 +198,18 @@ Two ways to create a fixture:
|
|
|
198
198
|
# Pull real execution data from n8n (no test run required)
|
|
199
199
|
n8m fixture capture <workflowId>
|
|
200
200
|
|
|
201
|
+
# Browse local files + remote instance interactively
|
|
202
|
+
n8m fixture capture
|
|
203
|
+
|
|
201
204
|
# Scaffold an empty template to fill in by hand
|
|
202
205
|
n8m fixture init <workflowId>
|
|
203
206
|
```
|
|
204
207
|
|
|
205
|
-
**`capture`** connects to your n8n instance, fetches the
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
**`capture`** connects to your n8n instance, fetches the most recent executions
|
|
209
|
+
for the workflow, and presents an interactive menu to pick which one to save.
|
|
210
|
+
You'll be prompted for a fixture name (e.g. `happy-path`, `missing-field`) and
|
|
211
|
+
expected outcome (`pass` or `fail`). Fixtures are saved to
|
|
212
|
+
`.n8m/fixtures/<workflowId>/<name>.json`.
|
|
210
213
|
|
|
211
214
|
```bash
|
|
212
215
|
n8m fixture capture abc123
|
|
@@ -215,12 +218,21 @@ n8m fixture capture abc123
|
|
|
215
218
|
# → #177916 success 3/4/2026, 10:48:47 AM
|
|
216
219
|
# → #177914 success 3/4/2026, 10:48:23 AM
|
|
217
220
|
# → ❯ #177913 error 3/4/2026, 10:47:59 AM
|
|
218
|
-
# →
|
|
219
|
-
# →
|
|
221
|
+
# → Name this fixture (e.g. happy-path, missing-field, bad-auth): error-case
|
|
222
|
+
# → Expected test outcome: fail
|
|
223
|
+
# → Fixture saved to .n8m/fixtures/abc123/error-case.json
|
|
220
224
|
# → Workflow: My Workflow
|
|
221
225
|
# → Execution: error · 5 node(s) captured
|
|
222
226
|
```
|
|
223
227
|
|
|
228
|
+
Multiple named fixtures per workflow let you test different scenarios (happy
|
|
229
|
+
path, edge cases, error handling) with one command:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
n8m test --fixture .n8m/fixtures/abc123 # run ALL fixtures for a workflow
|
|
233
|
+
n8m test --fixture .n8m/fixtures/abc123/error-case.json # run one specific fixture
|
|
234
|
+
```
|
|
235
|
+
|
|
224
236
|
**`init`** creates an empty template when you want to define the fixture data
|
|
225
237
|
yourself, without needing a live execution first.
|
|
226
238
|
|
|
@@ -230,6 +242,8 @@ yourself, without needing a live execution first.
|
|
|
230
242
|
"version": "1.0",
|
|
231
243
|
"workflowId": "abc123",
|
|
232
244
|
"workflowName": "My Workflow",
|
|
245
|
+
"description": "happy-path",
|
|
246
|
+
"expectedOutcome": "pass",
|
|
233
247
|
"workflow": { "name": "My Workflow", "nodes": [], "connections": {} },
|
|
234
248
|
"execution": {
|
|
235
249
|
"status": "success",
|
|
@@ -249,7 +263,7 @@ Fill in `execution.data.resultData.runData` with the actual output of each node
|
|
|
249
263
|
(keyed by exact node name). Then test against it:
|
|
250
264
|
|
|
251
265
|
```bash
|
|
252
|
-
n8m test --fixture .n8m/fixtures/abc123.json
|
|
266
|
+
n8m test --fixture .n8m/fixtures/abc123/happy-path.json
|
|
253
267
|
```
|
|
254
268
|
|
|
255
269
|
Fixture files are project-local (`.n8m/fixtures/`) and should be committed to
|
|
@@ -271,6 +285,117 @@ n8m deploy ./workflows/my-flow.json --activate
|
|
|
271
285
|
|
|
272
286
|
---
|
|
273
287
|
|
|
288
|
+
### `n8m learn` — Build the pattern library
|
|
289
|
+
|
|
290
|
+
Extract reusable engineering knowledge from validated workflows and store it so
|
|
291
|
+
the AI engineer applies the same proven techniques in future generations.
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Interactive: pick a workflow from ./workflows/
|
|
295
|
+
n8m learn
|
|
296
|
+
|
|
297
|
+
# Learn from a specific workflow
|
|
298
|
+
n8m learn ./workflows/my-flow/workflow.json
|
|
299
|
+
|
|
300
|
+
# Batch-generate patterns for every workflow in the directory
|
|
301
|
+
n8m learn --all
|
|
302
|
+
|
|
303
|
+
# Import patterns from a GitHub archive (awesome-n8n style)
|
|
304
|
+
n8m learn --github owner/repo
|
|
305
|
+
n8m learn --github owner/repo@main --github-path patterns/google
|
|
306
|
+
n8m learn --github owner/repo --token ghp_xxx # or set GITHUB_TOKEN env var
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**How it works:**
|
|
310
|
+
|
|
311
|
+
- **Local mode**: The AI reads a validated workflow JSON, identifies the
|
|
312
|
+
techniques it demonstrates, extracts critical rules (including gotchas to
|
|
313
|
+
avoid), and writes a `.md` pattern file to `.n8m/patterns/`.
|
|
314
|
+
- **GitHub mode**: Fetches `.md` pattern files from any public GitHub repo via
|
|
315
|
+
the GitHub API. Shows a checkbox menu so you choose exactly which patterns to
|
|
316
|
+
import. Recurses into subdirectories automatically.
|
|
317
|
+
|
|
318
|
+
Patterns are Markdown files with a `<!-- keywords: ... -->` comment. When you
|
|
319
|
+
run `n8m create`, the engineer searches all pattern files and injects any that
|
|
320
|
+
match the workflow's goal into the prompt — teaching it to reproduce proven
|
|
321
|
+
approaches exactly.
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
.n8m/patterns/ ← your project-local library (user patterns)
|
|
325
|
+
docs/patterns/ ← built-in patterns shipped with n8m
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
User patterns in `.n8m/patterns/` take priority over built-in patterns. A
|
|
329
|
+
pattern file with the same filename overrides the built-in version, so you can
|
|
330
|
+
customize any default.
|
|
331
|
+
|
|
332
|
+
**Contributing default patterns** (for n8m maintainers):
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
# Generate docs/patterns/ from every workflow in ./workflows/
|
|
336
|
+
npm run generate-patterns
|
|
337
|
+
|
|
338
|
+
# Overwrite any already-existing pattern files
|
|
339
|
+
npm run generate-patterns -- --overwrite
|
|
340
|
+
|
|
341
|
+
# Preview what would be written without touching the filesystem
|
|
342
|
+
npm run generate-patterns -- --dry-run
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The script scans `workflows/` recursively, calls the AI on each `workflow.json`,
|
|
346
|
+
and writes the result to `docs/patterns/<slug>.md`. Patterns in `docs/patterns/`
|
|
347
|
+
are included in the npm package and available to all users automatically.
|
|
348
|
+
|
|
349
|
+
**Pattern file format:**
|
|
350
|
+
|
|
351
|
+
```markdown
|
|
352
|
+
<!-- keywords: bigquery, google bigquery, sql, merge, http request -->
|
|
353
|
+
|
|
354
|
+
# Pattern: BigQuery Operations via HTTP Request
|
|
355
|
+
|
|
356
|
+
## Critical Rules
|
|
357
|
+
- NEVER use n8n-nodes-base.googleBigQuery — it returns no output for DDL/DML.
|
|
358
|
+
Always use n8n-nodes-base.httpRequest with the BigQuery REST API.
|
|
359
|
+
|
|
360
|
+
## DDL / DML Queries
|
|
361
|
+
...
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
### `n8m mcp` — Launch the MCP server
|
|
367
|
+
|
|
368
|
+
Expose `n8m`'s agentic capabilities as tools to any
|
|
369
|
+
[Model Context Protocol](https://modelcontextprotocol.io/) client (Claude
|
|
370
|
+
Desktop, Cursor, etc.).
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
n8m mcp
|
|
374
|
+
# → Starting n8m MCP Server...
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
The server runs over stdio and registers two tools:
|
|
378
|
+
|
|
379
|
+
| Tool | Description |
|
|
380
|
+
|---|---|
|
|
381
|
+
| `create_workflow` | Generate an n8n workflow from a natural-language goal |
|
|
382
|
+
| `test_workflow` | Deploy a workflow ephemerally to n8n and validate it |
|
|
383
|
+
|
|
384
|
+
Add it to your MCP client config (e.g. Claude Desktop's `claude_desktop_config.json`):
|
|
385
|
+
|
|
386
|
+
```json
|
|
387
|
+
{
|
|
388
|
+
"mcpServers": {
|
|
389
|
+
"n8m": {
|
|
390
|
+
"command": "npx",
|
|
391
|
+
"args": ["n8m", "mcp"]
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
274
399
|
### `n8m resume` — Resume a paused session
|
|
275
400
|
|
|
276
401
|
The agent can pause mid-run for human review (HITL). Resume it with its thread
|
|
@@ -330,8 +455,8 @@ n8m config
|
|
|
330
455
|
Developer → n8m create "..."
|
|
331
456
|
│
|
|
332
457
|
▼
|
|
333
|
-
┌─────────────┐
|
|
334
|
-
│ Architect │
|
|
458
|
+
┌─────────────┐ .n8m/patterns/ docs/patterns/
|
|
459
|
+
│ Architect │ ◄── RAG: node defs + matched patterns
|
|
335
460
|
└──────┬──────┘
|
|
336
461
|
│
|
|
337
462
|
▼
|
|
@@ -345,10 +470,18 @@ Developer → n8m create "..."
|
|
|
345
470
|
└──────┬──────┘ └─────────────┘
|
|
346
471
|
│ passed
|
|
347
472
|
▼
|
|
348
|
-
▼
|
|
349
473
|
./workflows/<slug>/
|
|
350
474
|
├── workflow.json
|
|
351
475
|
└── README.md (with Mermaid diagram)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
Developer → n8m learn <workflow.json>
|
|
479
|
+
│
|
|
480
|
+
▼
|
|
481
|
+
AI analyzes validated workflow
|
|
482
|
+
│
|
|
483
|
+
▼
|
|
484
|
+
.n8m/patterns/<slug>.md ← feeds back into Engineer on next create
|
|
352
485
|
```
|
|
353
486
|
|
|
354
487
|
- **Local first**: credentials and workflow files live on your machine
|
|
@@ -358,6 +491,8 @@ Developer → n8m create "..."
|
|
|
358
491
|
- **HITL pauses**: the agent stops for your review before committing
|
|
359
492
|
- **Bring your own AI**: works with OpenAI, Claude, Gemini, Ollama, or any
|
|
360
493
|
OpenAI-compatible API
|
|
494
|
+
- **Self-improving**: every validated workflow you `learn` from strengthens
|
|
495
|
+
future generations
|
|
361
496
|
|
|
362
497
|
> **For developers**: See the [Developer Guide](docs/DEVELOPER_GUIDE.md) for a
|
|
363
498
|
> deep-dive into the agentic graph internals, RAG implementation, how to add new
|
|
@@ -391,13 +526,14 @@ npm run dev
|
|
|
391
526
|
|
|
392
527
|
## Roadmap
|
|
393
528
|
|
|
529
|
+
### Shipped
|
|
530
|
+
|
|
394
531
|
- [x] Agentic graph (Architect → Engineer → QA)
|
|
395
532
|
- [x] SQLite session persistence
|
|
396
533
|
- [x] HITL interrupts and resume
|
|
397
534
|
- [x] Sub-workflow dependency resolution in tests
|
|
398
535
|
- [x] Open source — no account required
|
|
399
|
-
- [x] Multi-provider AI support (OpenAI, Claude, Gemini, Ollama, any
|
|
400
|
-
OpenAI-compatible API)
|
|
536
|
+
- [x] Multi-provider AI support (OpenAI, Claude, Gemini, Ollama, any OpenAI-compatible API)
|
|
401
537
|
- [x] Automatic documentation generation (Mermaid + AI Summary)
|
|
402
538
|
- [x] Project-based folder organization
|
|
403
539
|
- [x] AI-driven test scenario generation (`--ai-scenarios`)
|
|
@@ -405,5 +541,30 @@ npm run dev
|
|
|
405
541
|
- [x] Multi-workflow project generation support
|
|
406
542
|
- [x] Fixture record & replay — offline testing with real execution data
|
|
407
543
|
- [x] Hand-crafted fixture scaffolding (`n8m fixture init`) with JSON Schema
|
|
408
|
-
- [
|
|
409
|
-
- [
|
|
544
|
+
- [x] Pattern library — extract & reuse knowledge from validated workflows (`n8m learn`)
|
|
545
|
+
- [x] GitHub pattern archive import (`n8m learn --github owner/repo`)
|
|
546
|
+
- [x] MCP server — expose n8m as tools for Claude Desktop and other MCP clients
|
|
547
|
+
|
|
548
|
+
### Near-term
|
|
549
|
+
|
|
550
|
+
- [ ] **`n8m watch`** — file-system watcher that auto-deploys on save with live reload in the n8n editor
|
|
551
|
+
- [ ] **`n8m diff`** — human-readable structural diff between two versions of a workflow (nodes added/removed/changed)
|
|
552
|
+
- [ ] **`n8m rollback`** — restore a workflow to a previous git-tracked version with a single command
|
|
553
|
+
- [ ] **Credential awareness** — AI consults available credential types on the target instance so it stops generating nodes it can't authenticate
|
|
554
|
+
- [ ] **Parallel test runs** — fire multiple fixture payloads concurrently and report aggregate pass/fail
|
|
555
|
+
|
|
556
|
+
### Medium-term
|
|
557
|
+
|
|
558
|
+
- [ ] **`n8m debug`** — attach to a running execution and stream node-by-node output to the terminal in real time
|
|
559
|
+
- [ ] **`n8m chat`** — interactive REPL where every message applies an incremental AI edit and immediately re-tests
|
|
560
|
+
- [ ] **Workflow linter** — static analysis before deploy: dead branches, missing error handlers, unconnected nodes, credential gaps
|
|
561
|
+
- [ ] **`n8m learn --from-executions`** — scrape recent successful executions and distil reusable patterns into `.n8m/patterns/` automatically
|
|
562
|
+
- [ ] **Multi-instance deploy** — deploy the same workflow to staging and production in one command with environment-variable substitution
|
|
563
|
+
|
|
564
|
+
### Longer-term
|
|
565
|
+
|
|
566
|
+
- [ ] **Visual diff** — `n8m diff --ui` opens a side-by-side workflow comparison in the n8n canvas
|
|
567
|
+
- [ ] **Workflow marketplace** — `n8m publish` / `n8m install <slug>` to share and consume community patterns with version pinning
|
|
568
|
+
- [ ] **Execution replay** — record a live execution and automatically promote it to a fixture, closing the capture loop entirely
|
|
569
|
+
- [ ] **Agent memory across sessions** — the engineer remembers which node patterns worked well for a given integration and prefers them on future builds
|
|
570
|
+
- [ ] **LangGraph trace export** — export the full agentic reasoning trace (Architect → Engineer → Reviewer → QA) as a structured log for debugging and auditing
|
package/dist/agentic/graph.d.ts
CHANGED
|
@@ -157,10 +157,10 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
157
157
|
maxRevisions: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
158
158
|
}, import("@langchain/langgraph").StateDefinition, {
|
|
159
159
|
architect: {
|
|
160
|
-
spec
|
|
160
|
+
spec: any;
|
|
161
|
+
collaborationLog: string[];
|
|
161
162
|
strategies?: undefined;
|
|
162
163
|
needsClarification?: undefined;
|
|
163
|
-
collaborationLog?: undefined;
|
|
164
164
|
} | {
|
|
165
165
|
spec: import("../services/ai.service.js").WorkflowSpec;
|
|
166
166
|
strategies: {
|
|
@@ -197,6 +197,12 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
197
197
|
validationErrors?: undefined;
|
|
198
198
|
workflowJson?: undefined;
|
|
199
199
|
candidates?: undefined;
|
|
200
|
+
} | {
|
|
201
|
+
workflowJson: any;
|
|
202
|
+
revisionCount?: undefined;
|
|
203
|
+
validationStatus?: undefined;
|
|
204
|
+
validationErrors?: undefined;
|
|
205
|
+
candidates?: undefined;
|
|
200
206
|
} | {
|
|
201
207
|
candidates: any[];
|
|
202
208
|
revisionCount?: undefined;
|
|
@@ -411,10 +417,10 @@ export declare const runAgenticWorkflow: (goal: string, initialState?: Partial<t
|
|
|
411
417
|
*/
|
|
412
418
|
export declare const runAgenticWorkflowStream: (goal: string, threadId?: string) => Promise<import("@langchain/core/utils/stream").IterableReadableStream<{
|
|
413
419
|
architect?: {
|
|
414
|
-
spec
|
|
420
|
+
spec: any;
|
|
421
|
+
collaborationLog: string[];
|
|
415
422
|
strategies?: undefined;
|
|
416
423
|
needsClarification?: undefined;
|
|
417
|
-
collaborationLog?: undefined;
|
|
418
424
|
} | {
|
|
419
425
|
spec: import("../services/ai.service.js").WorkflowSpec;
|
|
420
426
|
strategies: {
|
|
@@ -451,6 +457,12 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
|
|
|
451
457
|
validationErrors?: undefined;
|
|
452
458
|
workflowJson?: undefined;
|
|
453
459
|
candidates?: undefined;
|
|
460
|
+
} | {
|
|
461
|
+
workflowJson: any;
|
|
462
|
+
revisionCount?: undefined;
|
|
463
|
+
validationStatus?: undefined;
|
|
464
|
+
validationErrors?: undefined;
|
|
465
|
+
candidates?: undefined;
|
|
454
466
|
} | {
|
|
455
467
|
candidates: any[];
|
|
456
468
|
revisionCount?: undefined;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { TeamState } from "../state.js";
|
|
2
2
|
export declare const architectNode: (state: typeof TeamState.State) => Promise<{
|
|
3
|
-
spec
|
|
3
|
+
spec: any;
|
|
4
|
+
collaborationLog: string[];
|
|
4
5
|
strategies?: undefined;
|
|
5
6
|
needsClarification?: undefined;
|
|
6
|
-
collaborationLog?: undefined;
|
|
7
7
|
} | {
|
|
8
8
|
spec: import("../../services/ai.service.js").WorkflowSpec;
|
|
9
9
|
strategies: {
|
|
@@ -10,7 +10,11 @@ export const architectNode = async (state) => {
|
|
|
10
10
|
// parallel engineers (via Send) to rebuild the workflow from scratch, which
|
|
11
11
|
// produces very large JSON that is error-prone and throws away the user's work.
|
|
12
12
|
if (state.workflowJson) {
|
|
13
|
-
|
|
13
|
+
const plan = await aiService.generateModificationPlan(state.userGoal, state.workflowJson);
|
|
14
|
+
return {
|
|
15
|
+
spec: plan,
|
|
16
|
+
collaborationLog: [`Architect: Modification plan — ${plan.description}`],
|
|
17
|
+
};
|
|
14
18
|
}
|
|
15
19
|
try {
|
|
16
20
|
const spec = await aiService.generateSpec(state.userGoal);
|
|
@@ -17,6 +17,12 @@ export declare const engineerNode: (state: typeof TeamState.State) => Promise<{
|
|
|
17
17
|
validationErrors?: undefined;
|
|
18
18
|
workflowJson?: undefined;
|
|
19
19
|
candidates?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
workflowJson: any;
|
|
22
|
+
revisionCount?: undefined;
|
|
23
|
+
validationStatus?: undefined;
|
|
24
|
+
validationErrors?: undefined;
|
|
25
|
+
candidates?: undefined;
|
|
20
26
|
} | {
|
|
21
27
|
candidates: any[];
|
|
22
28
|
revisionCount?: undefined;
|
|
@@ -12,8 +12,13 @@ export const engineerNode = async (state) => {
|
|
|
12
12
|
// Search for relevant nodes (limit 8 to save context)
|
|
13
13
|
const relevantDefs = nodeService.search(queryText, 8);
|
|
14
14
|
const staticRef = nodeService.getStaticReference();
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// Search pattern library for proven working examples
|
|
16
|
+
const matchedPatterns = nodeService.searchPatterns(queryText);
|
|
17
|
+
const patternsContext = matchedPatterns.length > 0
|
|
18
|
+
? `\n\n[PROVEN WORKFLOW PATTERNS - FOLLOW THESE EXACTLY]\n${matchedPatterns.join('\n\n---\n\n')}`
|
|
19
|
+
: "";
|
|
20
|
+
const ragContext = (relevantDefs.length > 0 || staticRef || matchedPatterns.length > 0)
|
|
21
|
+
? `\n\n[N8N NODE REFERENCE GUIDE]\n${staticRef}\n\n[AVAILABLE NODE SCHEMAS - USE THESE EXACT PARAMETERS]\n${nodeService.formatForLLM(relevantDefs)}${patternsContext}`
|
|
17
22
|
: "";
|
|
18
23
|
// Self-Correction Loop Check
|
|
19
24
|
if (state.validationErrors && state.validationErrors.length > 0) {
|
|
@@ -57,9 +62,13 @@ export const engineerNode = async (state) => {
|
|
|
57
62
|
throw error;
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
|
-
//
|
|
65
|
+
// Modification mode: existing workflow + spec from architect
|
|
61
66
|
if (state.workflowJson) {
|
|
62
|
-
|
|
67
|
+
if (!state.spec) {
|
|
68
|
+
return {}; // No plan — nothing to do
|
|
69
|
+
}
|
|
70
|
+
const modifiedWorkflow = await aiService.applyModification(state.workflowJson, state.userGoal, state.spec, state.userFeedback, state.availableNodeTypes || []);
|
|
71
|
+
return { workflowJson: modifiedWorkflow };
|
|
63
72
|
}
|
|
64
73
|
// Standard Creation Flow
|
|
65
74
|
// console.log("⚙️ Engineer is building the workflow...");
|
|
@@ -88,6 +97,31 @@ export const engineerNode = async (state) => {
|
|
|
88
97
|
- Use "n8n-nodes-base.htmlExtract" for HTML/Cheerio extraction.
|
|
89
98
|
6. Connections Structure: The "connections" object keys MUST BE THE SOURCE NODE NAME. The "node" field inside the connection array MUST BE THE TARGET NODE NAME.
|
|
90
99
|
7. Connection Nesting: Ensure the correct n8n connection structure: "SourceNodeName": { "main": [ [ { "node": "TargetNodeName", "type": "main", "index": 0 } ] ] }.
|
|
100
|
+
8. Error Connections: In addition to "main", nodes support an "error" output that fires when a node fails. Use this for error handling and cleanup flows. Example:
|
|
101
|
+
"NodeThatMightFail": {
|
|
102
|
+
"main": [ [ { "node": "NextNode", "type": "main", "index": 0 } ] ],
|
|
103
|
+
"error": [ [ { "node": "CleanupOrErrorHandler", "type": "main", "index": 0 } ] ]
|
|
104
|
+
}
|
|
105
|
+
Any node that could fail mid-workflow AND where partial execution would leave side effects (e.g. temporary DB tables, uploaded files, open transactions) MUST have an error connection to a cleanup node.
|
|
106
|
+
9. HTTP Request Configuration: The method determines required fields.
|
|
107
|
+
- GET/DELETE: only "url" (and optional "sendQuery"/"sendHeaders") are needed — do NOT include "sendBody".
|
|
108
|
+
- POST/PUT/PATCH: MUST include "sendBody": true AND a "body" object:
|
|
109
|
+
{ "method": "POST", "url": "...", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ JSON.stringify($json) }}" }
|
|
110
|
+
- Authentication: use "authentication": "predefinedCredentialType" + "nodeCredentialType": "<CredentialTypeName>" for service credentials.
|
|
111
|
+
- Minimal config: only include fields relevant to the method. Do not add empty optional fields.
|
|
112
|
+
10. Resource/Operation Nodes (Slack, Google Sheets, Airtable, Gmail, etc.): The "resource" + "operation" pair together determine which parameters are required. Different operations need different fields:
|
|
113
|
+
- post/create operations typically need target identifiers (channel, spreadsheetId, etc.) and content fields.
|
|
114
|
+
- update/patch operations typically need a record ID (messageId, rowId, etc.) and the fields to update.
|
|
115
|
+
- get/list operations typically need filter/search parameters, not content fields.
|
|
116
|
+
Always set both "resource" and "operation" first, then configure only the fields that operation requires.
|
|
117
|
+
11. Credentials Format: Credential references must follow this structure:
|
|
118
|
+
"credentials": { "<credentialTypeName>": { "id": "CREDENTIAL_ID", "name": "Human Readable Name" } }
|
|
119
|
+
Use the exact credential type name that matches the node (e.g. "slackApi", "googleSheetsOAuth2Api", "googleBigQueryOAuth2Api").
|
|
120
|
+
For Google services, prefer service account credentials over OAuth2 when available:
|
|
121
|
+
- BigQuery: use "googleApi" (service account) instead of "googleBigQueryOAuth2Api"
|
|
122
|
+
- Google Sheets: use "googleSheetsServiceAccountApi" instead of "googleSheetsOAuth2Api"
|
|
123
|
+
- Google Drive: use "googleDriveServiceAccountApi" instead of "googleDriveOAuth2Api"
|
|
124
|
+
- Other Google nodes: check if a service account variant exists (typically named "<serviceName>ServiceAccountApi")
|
|
91
125
|
|
|
92
126
|
Output a JSON object with this structure:
|
|
93
127
|
{
|
|
@@ -114,7 +148,7 @@ export const engineerNode = async (state) => {
|
|
|
114
148
|
throw new Error("AI generated invalid JSON for workflow from spec");
|
|
115
149
|
}
|
|
116
150
|
if (result.workflows && Array.isArray(result.workflows)) {
|
|
117
|
-
result.workflows = result.workflows.map((wf) => aiService.fixHallucinatedNodes(wf));
|
|
151
|
+
result.workflows = result.workflows.map((wf) => aiService.wireOrphanedErrorHandlers(aiService.fixHallucinatedNodes(wf)));
|
|
118
152
|
}
|
|
119
153
|
return {
|
|
120
154
|
// Only push to candidates — the Supervisor sets workflowJson after fan-in.
|
package/dist/commands/create.js
CHANGED
|
@@ -10,6 +10,7 @@ import { promptMultiline } from '../utils/multilinePrompt.js';
|
|
|
10
10
|
import { DocService } from '../services/doc.service.js';
|
|
11
11
|
import { ConfigManager } from '../utils/config.js';
|
|
12
12
|
import { N8nClient } from '../utils/n8nClient.js';
|
|
13
|
+
import { AIService } from '../services/ai.service.js';
|
|
13
14
|
export default class Create extends Command {
|
|
14
15
|
static args = {
|
|
15
16
|
description: Args.string({
|
|
@@ -147,6 +148,11 @@ export default class Create extends Command {
|
|
|
147
148
|
});
|
|
148
149
|
}
|
|
149
150
|
choices.push(new inquirer.Separator());
|
|
151
|
+
choices.push({
|
|
152
|
+
name: 'Discuss details with the Engineer',
|
|
153
|
+
value: { type: 'chat' },
|
|
154
|
+
short: 'Discuss',
|
|
155
|
+
});
|
|
150
156
|
choices.push({
|
|
151
157
|
name: 'Add feedback before building',
|
|
152
158
|
value: { type: 'feedback' },
|
|
@@ -171,7 +177,38 @@ export default class Create extends Command {
|
|
|
171
177
|
}
|
|
172
178
|
let chosenSpec = choice.strategy ?? spec;
|
|
173
179
|
let stateUpdate = { spec: chosenSpec, userFeedback: undefined };
|
|
174
|
-
if (choice.type === '
|
|
180
|
+
if (choice.type === 'chat') {
|
|
181
|
+
const aiService = AIService.getInstance();
|
|
182
|
+
const chatHistory = [];
|
|
183
|
+
let currentSpec = chosenSpec ?? strategies[0] ?? spec;
|
|
184
|
+
this.log(theme.header('\nCHATTING WITH THE ENGINEER'));
|
|
185
|
+
this.log(theme.muted(` Plan: ${currentSpec?.suggestedName}`));
|
|
186
|
+
this.log(theme.muted(` Type your question or request. Enter "done" when ready to build.\n`));
|
|
187
|
+
// eslint-disable-next-line no-constant-condition
|
|
188
|
+
while (true) {
|
|
189
|
+
const { message } = await inquirer.prompt([{
|
|
190
|
+
type: 'input',
|
|
191
|
+
name: 'message',
|
|
192
|
+
message: 'You:',
|
|
193
|
+
}]);
|
|
194
|
+
const trimmed = message.trim();
|
|
195
|
+
if (!trimmed || /^(done|build|approve|go|ok|yes)$/i.test(trimmed)) {
|
|
196
|
+
this.log(theme.agent(`Understood. Building "${currentSpec?.suggestedName}"...\n`));
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
const { reply, updatedSpec } = await aiService.chatAboutSpec(currentSpec, chatHistory, trimmed);
|
|
200
|
+
chatHistory.push({ role: 'user', content: trimmed });
|
|
201
|
+
chatHistory.push({ role: 'assistant', content: reply });
|
|
202
|
+
currentSpec = updatedSpec;
|
|
203
|
+
this.log(`\n${theme.agent('Engineer:')} ${reply}\n`);
|
|
204
|
+
if (updatedSpec.suggestedName !== (chosenSpec ?? spec)?.suggestedName) {
|
|
205
|
+
this.log(theme.muted(` (Plan updated: ${updatedSpec.suggestedName})`));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
chosenSpec = currentSpec;
|
|
209
|
+
stateUpdate = { spec: chosenSpec, userFeedback: undefined };
|
|
210
|
+
}
|
|
211
|
+
else if (choice.type === 'feedback') {
|
|
175
212
|
const { feedback } = await inquirer.prompt([{
|
|
176
213
|
type: 'input',
|
|
177
214
|
name: 'feedback',
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class Deploy extends Command {
|
|
3
3
|
static args: {
|
|
4
|
-
workflow: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
4
|
+
workflow: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
9
|
instance: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
activate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
};
|
|
12
13
|
run(): Promise<void>;
|
|
13
14
|
}
|