agentweaver 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +148 -27
  2. package/dist/artifacts.js +114 -3
  3. package/dist/doctor/checks/executors.js +2 -2
  4. package/dist/flow-state.js +138 -1
  5. package/dist/index.js +421 -82
  6. package/dist/interactive/controller.js +305 -36
  7. package/dist/interactive/ink/index.js +24 -3
  8. package/dist/interactive/state.js +1 -0
  9. package/dist/interactive/tree.js +2 -2
  10. package/dist/interactive/web/index.js +179 -0
  11. package/dist/interactive/web/protocol.js +154 -0
  12. package/dist/interactive/web/server.js +575 -0
  13. package/dist/interactive/web/static/app.js +709 -0
  14. package/dist/interactive/web/static/index.html +77 -0
  15. package/dist/interactive/web/static/styles.css +2 -0
  16. package/dist/interactive/web/static/styles.input.css +469 -0
  17. package/dist/pipeline/auto-flow.js +9 -6
  18. package/dist/pipeline/context.js +6 -5
  19. package/dist/pipeline/declarative-flows.js +39 -20
  20. package/dist/pipeline/flow-catalog.js +40 -14
  21. package/dist/pipeline/flow-specs/auto-common-guided.json +313 -0
  22. package/dist/pipeline/flow-specs/auto-common.json +4 -1
  23. package/dist/pipeline/flow-specs/auto-golang.json +27 -1
  24. package/dist/pipeline/flow-specs/design-review/design-review-loop.json +15 -1
  25. package/dist/pipeline/flow-specs/design-review.json +2 -0
  26. package/dist/pipeline/flow-specs/implement.json +3 -1
  27. package/dist/pipeline/flow-specs/plan.json +8 -2
  28. package/dist/pipeline/flow-specs/playbook-init.json +199 -0
  29. package/dist/pipeline/flow-specs/review/review-fix.json +3 -1
  30. package/dist/pipeline/flow-specs/review/review-loop.json +4 -0
  31. package/dist/pipeline/flow-specs/review/review.json +2 -0
  32. package/dist/pipeline/launch-profile-config.js +30 -18
  33. package/dist/pipeline/node-contract.js +1 -0
  34. package/dist/pipeline/node-registry.js +119 -5
  35. package/dist/pipeline/nodes/flow-run-node.js +200 -173
  36. package/dist/pipeline/nodes/llm-prompt-node.js +15 -33
  37. package/dist/pipeline/nodes/playbook-ensure-node.js +115 -0
  38. package/dist/pipeline/nodes/playbook-inventory-node.js +51 -0
  39. package/dist/pipeline/nodes/playbook-questions-form-node.js +166 -0
  40. package/dist/pipeline/nodes/playbook-write-node.js +243 -0
  41. package/dist/pipeline/nodes/project-guidance-node.js +69 -0
  42. package/dist/pipeline/plugin-loader.js +389 -0
  43. package/dist/pipeline/plugin-types.js +1 -0
  44. package/dist/pipeline/prompt-registry.js +4 -1
  45. package/dist/pipeline/prompt-runtime.js +6 -2
  46. package/dist/pipeline/registry.js +71 -4
  47. package/dist/pipeline/spec-compiler.js +1 -0
  48. package/dist/pipeline/spec-loader.js +14 -0
  49. package/dist/pipeline/spec-types.js +19 -0
  50. package/dist/pipeline/spec-validator.js +6 -0
  51. package/dist/pipeline/value-resolver.js +41 -2
  52. package/dist/playbook/practice-candidates.js +12 -0
  53. package/dist/playbook/repo-inventory.js +208 -0
  54. package/dist/plugin-sdk.js +1 -0
  55. package/dist/prompts.js +31 -0
  56. package/dist/runtime/artifact-registry.js +3 -0
  57. package/dist/runtime/execution-routing.js +25 -19
  58. package/dist/runtime/interactive-execution-routing.js +66 -57
  59. package/dist/runtime/playbook.js +485 -0
  60. package/dist/runtime/project-guidance.js +339 -0
  61. package/dist/structured-artifact-schema-registry.js +8 -0
  62. package/dist/structured-artifact-schemas.json +235 -0
  63. package/dist/structured-artifacts.js +7 -1
  64. package/docs/declarative-workflows.md +565 -0
  65. package/docs/example/.flows/examples/claude-example.json +50 -0
  66. package/docs/example/.plugins/claude-example-plugin/index.js +149 -0
  67. package/docs/example/.plugins/claude-example-plugin/plugin.json +8 -0
  68. package/docs/examples/.flows/claude-example.json +50 -0
  69. package/docs/examples/.plugins/claude-example-plugin/index.js +149 -0
  70. package/docs/examples/.plugins/claude-example-plugin/plugin.json +8 -0
  71. package/docs/features.md +77 -0
  72. package/docs/playbook.md +327 -0
  73. package/docs/plugin-sdk.md +731 -0
  74. package/package.json +13 -4
@@ -0,0 +1,149 @@
1
+ import { AGENTWEAVER_PLUGIN_SDK_VERSION } from "agentweaver/plugin-sdk";
2
+
3
+ export const CLAUDE_EXAMPLE_PLUGIN_SDK_VERSION = AGENTWEAVER_PLUGIN_SDK_VERSION;
4
+ export const CLAUDE_EXECUTOR_ID = "claude";
5
+ export const CLAUDE_AUTH_STATUS_ERROR = "Claude CLI authentication is required. Run 'claude auth status' and sign in before using the example flow.";
6
+ export const CLAUDE_NORMALIZATION_ERROR =
7
+ "Claude JSON normalization failed: no supported assistant text was found in result, message.content[*].text, or content[*].text.";
8
+
9
+ function nonEmptyString(value) {
10
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
11
+ }
12
+
13
+ function resolveSetting(override, env, envKey, defaultValue) {
14
+ const direct = nonEmptyString(typeof override === "number" ? String(override) : override);
15
+ if (direct) {
16
+ return direct;
17
+ }
18
+ const fromEnv = nonEmptyString(env?.[envKey]);
19
+ if (fromEnv) {
20
+ return fromEnv;
21
+ }
22
+ return nonEmptyString(defaultValue);
23
+ }
24
+
25
+ function resolveStringListSetting(override, env, envKey, defaultValue) {
26
+ const direct = nonEmptyString(override);
27
+ const envValue = nonEmptyString(env?.[envKey]);
28
+ const effective = direct ?? envValue ?? nonEmptyString(defaultValue);
29
+ if (!effective) {
30
+ return [];
31
+ }
32
+ return effective
33
+ .split(/[,\s]+/)
34
+ .map((item) => item.trim())
35
+ .filter((item) => item.length > 0);
36
+ }
37
+
38
+ function extractTextFragments(items) {
39
+ if (!Array.isArray(items)) {
40
+ return null;
41
+ }
42
+ const fragments = items
43
+ .map((item) => (item && typeof item === "object" ? item.text : undefined))
44
+ .filter((text) => typeof text === "string" && text.trim().length > 0);
45
+ return fragments.length > 0 ? fragments.join("\n") : null;
46
+ }
47
+
48
+ export function normalizeClaudePayload(payload, effectiveModel = "") {
49
+ const resultText = nonEmptyString(payload?.result);
50
+ const messageContentText = extractTextFragments(payload?.message?.content);
51
+ const contentText = extractTextFragments(payload?.content);
52
+ const output = resultText ?? messageContentText ?? contentText;
53
+ if (!output) {
54
+ throw new Error(CLAUDE_NORMALIZATION_ERROR);
55
+ }
56
+ return {
57
+ output,
58
+ model: nonEmptyString(payload?.model) ?? effectiveModel,
59
+ rawResponse: payload,
60
+ };
61
+ }
62
+
63
+ export async function verifyClaudeAuth(runtime, env, config) {
64
+ const command = runtime.resolveCmd(config.defaultCommand, config.commandEnvVar);
65
+ try {
66
+ await runtime.runCommand([command, "auth", "status"], {
67
+ env,
68
+ label: "claude:auth-status",
69
+ printFailureOutput: false,
70
+ });
71
+ } catch {
72
+ throw new Error(CLAUDE_AUTH_STATUS_ERROR);
73
+ }
74
+ return command;
75
+ }
76
+
77
+ export const claudeExecutorDefinition = {
78
+ kind: CLAUDE_EXECUTOR_ID,
79
+ version: 1,
80
+ defaultConfig: {
81
+ commandEnvVar: "CLAUDE_BIN",
82
+ defaultCommand: "claude",
83
+ modelEnvVar: "CLAUDE_MODEL",
84
+ defaultModel: "",
85
+ maxTurnsEnvVar: "CLAUDE_MAX_TURNS",
86
+ defaultMaxTurns: "",
87
+ permissionModeEnvVar: "CLAUDE_PERMISSION_MODE",
88
+ defaultPermissionMode: "bypassPermissions",
89
+ allowedToolsEnvVar: "CLAUDE_ALLOWED_TOOLS",
90
+ defaultAllowedTools: "",
91
+ disallowedToolsEnvVar: "CLAUDE_DISALLOWED_TOOLS",
92
+ defaultDisallowedTools: "",
93
+ addCwdAsAllowedDir: true,
94
+ },
95
+ async execute(context, input, config) {
96
+ const env = input?.env ?? context.env;
97
+ const command = input?.command ?? context.runtime.resolveCmd(config.defaultCommand, config.commandEnvVar);
98
+ const effectiveModel = resolveSetting(input?.model, env, config.modelEnvVar, config.defaultModel);
99
+ const effectiveMaxTurns = resolveSetting(input?.maxTurns, env, config.maxTurnsEnvVar, config.defaultMaxTurns);
100
+ const permissionMode = resolveSetting(undefined, env, config.permissionModeEnvVar, config.defaultPermissionMode);
101
+ const allowedTools = resolveStringListSetting(undefined, env, config.allowedToolsEnvVar, config.defaultAllowedTools);
102
+ const disallowedTools = resolveStringListSetting(undefined, env, config.disallowedToolsEnvVar, config.defaultDisallowedTools);
103
+ const argv = [
104
+ command,
105
+ "-p",
106
+ String(input?.prompt ?? ""),
107
+ "--output-format",
108
+ "json",
109
+ ...(config.addCwdAsAllowedDir ? ["--add-dir", context.cwd] : []),
110
+ ...(permissionMode ? ["--permission-mode", permissionMode] : []),
111
+ ...(allowedTools.length > 0 ? ["--allowedTools", ...allowedTools] : []),
112
+ ...(disallowedTools.length > 0 ? ["--disallowedTools", ...disallowedTools] : []),
113
+ ...(effectiveModel ? ["--model", effectiveModel] : []),
114
+ ...(effectiveMaxTurns ? ["--max-turns", effectiveMaxTurns] : []),
115
+ ];
116
+ const stdout = await context.runtime.runCommand(argv, {
117
+ env,
118
+ dryRun: context.dryRun,
119
+ verbose: context.verbose,
120
+ label: `claude:${effectiveModel || "default"}`,
121
+ printFailureOutput: true,
122
+ });
123
+ let payload;
124
+ try {
125
+ payload = JSON.parse(stdout);
126
+ } catch (error) {
127
+ throw new Error(`Claude CLI returned invalid JSON: ${error.message}`);
128
+ }
129
+ const normalized = normalizeClaudePayload(payload, effectiveModel ?? "");
130
+ return {
131
+ output: normalized.output,
132
+ command,
133
+ model: normalized.model ?? "",
134
+ rawResponse: normalized.rawResponse,
135
+ };
136
+ },
137
+ };
138
+
139
+ export const executors = [
140
+ {
141
+ id: CLAUDE_EXECUTOR_ID,
142
+ definition: claudeExecutorDefinition,
143
+ routing: {
144
+ kind: "llm",
145
+ defaultModel: "sonnet",
146
+ models: ["sonnet", "opus", "haiku"],
147
+ },
148
+ },
149
+ ];
@@ -0,0 +1,8 @@
1
+ {
2
+ "id": "claude-example-plugin",
3
+ "sdk_version": 1,
4
+ "entrypoint": "index.js",
5
+ "name": "Claude Example Plugin",
6
+ "version": "0.1.0",
7
+ "description": "Project-local Claude CLI reference plugin for AgentWeaver."
8
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "kind": "claude-example-flow",
3
+ "version": 1,
4
+ "description": "Project-local reference flow for the Claude example plugin.",
5
+ "phases": [
6
+ {
7
+ "id": "example",
8
+ "steps": [
9
+ {
10
+ "id": "run_claude_prompt",
11
+ "node": "llm-prompt",
12
+ "prompt": {
13
+ "inlineTemplate": "Use the current workspace. Create the JSON file `.agentweaver/.artifacts/examples/claude-example-proof.json` with exactly this shape: `{ \"status\": \"ok\", \"executor\": \"claude\", \"message\": \"<one short sentence proving the Claude example plugin executed through llm-prompt>\", \"model\": \"<the exact model name you used, or an empty string if unavailable>\" }`. Write valid JSON only to that file. After the file is written, reply with exactly `wrote .agentweaver/.artifacts/examples/claude-example-proof.json`."
14
+ },
15
+ "params": {
16
+ "labelText": {
17
+ "const": "Run Claude via llm-prompt"
18
+ },
19
+ "executor": {
20
+ "const": "claude"
21
+ },
22
+ "requiredArtifacts": {
23
+ "list": [
24
+ {
25
+ "const": ".agentweaver/.artifacts/examples/claude-example-proof.json"
26
+ }
27
+ ]
28
+ },
29
+ "missingArtifactsMessage": {
30
+ "const": "The Claude example flow requires Claude to write the fixed proof artifact."
31
+ }
32
+ },
33
+ "expect": [
34
+ {
35
+ "kind": "require-artifacts",
36
+ "paths": {
37
+ "list": [
38
+ {
39
+ "const": ".agentweaver/.artifacts/examples/claude-example-proof.json"
40
+ }
41
+ ]
42
+ },
43
+ "message": "The Claude example flow must write the fixed proof artifact."
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ ]
50
+ }
@@ -0,0 +1,149 @@
1
+ import { AGENTWEAVER_PLUGIN_SDK_VERSION } from "agentweaver/plugin-sdk";
2
+
3
+ export const CLAUDE_EXAMPLE_PLUGIN_SDK_VERSION = AGENTWEAVER_PLUGIN_SDK_VERSION;
4
+ export const CLAUDE_EXECUTOR_ID = "claude";
5
+ export const CLAUDE_AUTH_STATUS_ERROR = "Claude CLI authentication is required. Run 'claude auth status' and sign in before using the example flow.";
6
+ export const CLAUDE_NORMALIZATION_ERROR =
7
+ "Claude JSON normalization failed: no supported assistant text was found in result, message.content[*].text, or content[*].text.";
8
+
9
+ function nonEmptyString(value) {
10
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
11
+ }
12
+
13
+ function resolveSetting(override, env, envKey, defaultValue) {
14
+ const direct = nonEmptyString(typeof override === "number" ? String(override) : override);
15
+ if (direct) {
16
+ return direct;
17
+ }
18
+ const fromEnv = nonEmptyString(env?.[envKey]);
19
+ if (fromEnv) {
20
+ return fromEnv;
21
+ }
22
+ return nonEmptyString(defaultValue);
23
+ }
24
+
25
+ function resolveStringListSetting(override, env, envKey, defaultValue) {
26
+ const direct = nonEmptyString(override);
27
+ const envValue = nonEmptyString(env?.[envKey]);
28
+ const effective = direct ?? envValue ?? nonEmptyString(defaultValue);
29
+ if (!effective) {
30
+ return [];
31
+ }
32
+ return effective
33
+ .split(/[,\s]+/)
34
+ .map((item) => item.trim())
35
+ .filter((item) => item.length > 0);
36
+ }
37
+
38
+ function extractTextFragments(items) {
39
+ if (!Array.isArray(items)) {
40
+ return null;
41
+ }
42
+ const fragments = items
43
+ .map((item) => (item && typeof item === "object" ? item.text : undefined))
44
+ .filter((text) => typeof text === "string" && text.trim().length > 0);
45
+ return fragments.length > 0 ? fragments.join("\n") : null;
46
+ }
47
+
48
+ export function normalizeClaudePayload(payload, effectiveModel = "") {
49
+ const resultText = nonEmptyString(payload?.result);
50
+ const messageContentText = extractTextFragments(payload?.message?.content);
51
+ const contentText = extractTextFragments(payload?.content);
52
+ const output = resultText ?? messageContentText ?? contentText;
53
+ if (!output) {
54
+ throw new Error(CLAUDE_NORMALIZATION_ERROR);
55
+ }
56
+ return {
57
+ output,
58
+ model: nonEmptyString(payload?.model) ?? effectiveModel,
59
+ rawResponse: payload,
60
+ };
61
+ }
62
+
63
+ export async function verifyClaudeAuth(runtime, env, config) {
64
+ const command = runtime.resolveCmd(config.defaultCommand, config.commandEnvVar);
65
+ try {
66
+ await runtime.runCommand([command, "auth", "status"], {
67
+ env,
68
+ label: "claude:auth-status",
69
+ printFailureOutput: false,
70
+ });
71
+ } catch {
72
+ throw new Error(CLAUDE_AUTH_STATUS_ERROR);
73
+ }
74
+ return command;
75
+ }
76
+
77
+ export const claudeExecutorDefinition = {
78
+ kind: CLAUDE_EXECUTOR_ID,
79
+ version: 1,
80
+ defaultConfig: {
81
+ commandEnvVar: "CLAUDE_BIN",
82
+ defaultCommand: "claude",
83
+ modelEnvVar: "CLAUDE_MODEL",
84
+ defaultModel: "",
85
+ maxTurnsEnvVar: "CLAUDE_MAX_TURNS",
86
+ defaultMaxTurns: "",
87
+ permissionModeEnvVar: "CLAUDE_PERMISSION_MODE",
88
+ defaultPermissionMode: "bypassPermissions",
89
+ allowedToolsEnvVar: "CLAUDE_ALLOWED_TOOLS",
90
+ defaultAllowedTools: "",
91
+ disallowedToolsEnvVar: "CLAUDE_DISALLOWED_TOOLS",
92
+ defaultDisallowedTools: "",
93
+ addCwdAsAllowedDir: true,
94
+ },
95
+ async execute(context, input, config) {
96
+ const env = input?.env ?? context.env;
97
+ const command = input?.command ?? context.runtime.resolveCmd(config.defaultCommand, config.commandEnvVar);
98
+ const effectiveModel = resolveSetting(input?.model, env, config.modelEnvVar, config.defaultModel);
99
+ const effectiveMaxTurns = resolveSetting(input?.maxTurns, env, config.maxTurnsEnvVar, config.defaultMaxTurns);
100
+ const permissionMode = resolveSetting(undefined, env, config.permissionModeEnvVar, config.defaultPermissionMode);
101
+ const allowedTools = resolveStringListSetting(undefined, env, config.allowedToolsEnvVar, config.defaultAllowedTools);
102
+ const disallowedTools = resolveStringListSetting(undefined, env, config.disallowedToolsEnvVar, config.defaultDisallowedTools);
103
+ const argv = [
104
+ command,
105
+ "-p",
106
+ String(input?.prompt ?? ""),
107
+ "--output-format",
108
+ "json",
109
+ ...(config.addCwdAsAllowedDir ? ["--add-dir", context.cwd] : []),
110
+ ...(permissionMode ? ["--permission-mode", permissionMode] : []),
111
+ ...(allowedTools.length > 0 ? ["--allowedTools", ...allowedTools] : []),
112
+ ...(disallowedTools.length > 0 ? ["--disallowedTools", ...disallowedTools] : []),
113
+ ...(effectiveModel ? ["--model", effectiveModel] : []),
114
+ ...(effectiveMaxTurns ? ["--max-turns", effectiveMaxTurns] : []),
115
+ ];
116
+ const stdout = await context.runtime.runCommand(argv, {
117
+ env,
118
+ dryRun: context.dryRun,
119
+ verbose: context.verbose,
120
+ label: `claude:${effectiveModel || "default"}`,
121
+ printFailureOutput: true,
122
+ });
123
+ let payload;
124
+ try {
125
+ payload = JSON.parse(stdout);
126
+ } catch (error) {
127
+ throw new Error(`Claude CLI returned invalid JSON: ${error.message}`);
128
+ }
129
+ const normalized = normalizeClaudePayload(payload, effectiveModel ?? "");
130
+ return {
131
+ output: normalized.output,
132
+ command,
133
+ model: normalized.model ?? "",
134
+ rawResponse: normalized.rawResponse,
135
+ };
136
+ },
137
+ };
138
+
139
+ export const executors = [
140
+ {
141
+ id: CLAUDE_EXECUTOR_ID,
142
+ definition: claudeExecutorDefinition,
143
+ routing: {
144
+ kind: "llm",
145
+ defaultModel: "sonnet",
146
+ models: ["sonnet", "opus", "haiku"],
147
+ },
148
+ },
149
+ ];
@@ -0,0 +1,8 @@
1
+ {
2
+ "id": "claude-example-plugin",
3
+ "sdk_version": 1,
4
+ "entrypoint": "index.js",
5
+ "name": "Claude Example Plugin",
6
+ "version": "0.1.0",
7
+ "description": "Project-local Claude CLI reference plugin for AgentWeaver."
8
+ }
@@ -0,0 +1,77 @@
1
+ # Key Features
2
+
3
+ AgentWeaver is built around harness engineering: the workflow around the coding agent is explicit, versioned, inspectable, and repeatable. This document expands the key features summarized in the README and points to the deeper reference documents.
4
+
5
+ ## Declarative Agent Workflows
6
+
7
+ AgentWeaver workflows are JSON flow specs. A flow declares phases, steps, prompt bindings, params, conditions, expected artifacts, and post-step actions. Runtime behavior lives in typed nodes and executors instead of ad-hoc command handlers.
8
+
9
+ This makes workflows reviewable as source code and lets the same model power direct CLI commands, long-running automation, and the interactive TUI.
10
+
11
+ Reference: [Declarative Workflows](declarative-workflows.md)
12
+
13
+ ## Project Playbook
14
+
15
+ The project playbook stores repository-local conventions under `.agentweaver/playbook/`. It contains a manifest, stable practices, examples, templates, and baseline project context.
16
+
17
+ Guided workflows validate the playbook, select compact phase-specific guidance, and pass that guidance into planning, design review, implementation, review, and repair prompts. This helps repeated agent runs inherit the same project knowledge without copying long instructions into every prompt.
18
+
19
+ Reference: [Project Playbook](playbook.md)
20
+
21
+ ## Artifact-First Execution
22
+
23
+ AgentWeaver treats artifacts as the contract between stages. LLM-backed stages write structured JSON artifacts first, then derivative markdown for human reading.
24
+
25
+ This gives each workflow durable intermediate state: normalized task context, planning questions, design, implementation plan, QA plan, review findings, repair reports, project guidance, and other artifacts can be inspected, validated, reused, or used to resume a run.
26
+
27
+ ## Planning and Design Review
28
+
29
+ Planning is not only a prompt. The planning flow produces structured design, implementation plan, and QA plan artifacts. `design-review` then critiques those planning artifacts before implementation starts.
30
+
31
+ For planning-heavy work, `auto-common` can run a design-review loop and iterate through `plan-revise` before coding. This makes implementation less dependent on a single speculative prompt.
32
+
33
+ ## Review and Repair Loops
34
+
35
+ Review stages produce structured findings with severity levels. Repair stages can use those findings to select blockers and critical issues, build targeted fix prompts, apply changes, and run follow-up checks.
36
+
37
+ Loop flows such as `review-loop`, `run-go-tests-loop`, and `run-go-linter-loop` make repeated review/fix/check cycles explicit and bounded.
38
+
39
+ ## Resumable Automation
40
+
41
+ Long-running flows persist compact execution state under `.agentweaver/scopes/<scope>/`. AgentWeaver uses that state with artifacts and launch profile checks to support resume, continue, and restart behavior.
42
+
43
+ This is especially useful for end-to-end flows such as `auto-common`, `auto-golang`, and review/check loops where restarting from scratch would waste context and work.
44
+
45
+ ## Execution Backends
46
+
47
+ AgentWeaver routes work through executors. Built-in executors cover Codex, OpenCode, Jira fetch, GitLab diff/review fetch, shell/process checks, Git commit, and Telegram notifications.
48
+
49
+ Executor configuration is separated from flow specs, so workflows can describe what should happen while runtime profiles decide how work is executed.
50
+
51
+ ## Interactive TUI and Direct CLI
52
+
53
+ The same flow model works in direct CLI commands and in the interactive terminal UI. The TUI surfaces discovered flows, flow descriptions, active state, progress, and summary artifacts.
54
+
55
+ This lets operators choose between fast command execution and supervised long-running workflows without changing the underlying flow definitions.
56
+
57
+ ## Custom Flows
58
+
59
+ Built-in flows live under `src/pipeline/flow-specs/`, while custom flow specs can live under `~/.agentweaver/.flows/` or project-local `.agentweaver/.flows/`.
60
+
61
+ Custom flows are discovered recursively, validated at load time, and can compose built-in, global, or project-local flows through nested `flow-run` steps.
62
+
63
+ Reference: [Declarative Workflows](declarative-workflows.md)
64
+
65
+ ## Plugin SDK
66
+
67
+ Local plugins can add public-SDK-compatible nodes and executors. Plugin manifests declare ids, SDK version, runtime entrypoints, and contributed runtime capabilities.
68
+
69
+ The loader validates plugin manifests, SDK compatibility, executor definitions, node metadata, and version consistency before plugin contributions become available to flows.
70
+
71
+ Reference: [Plugin SDK](plugin-sdk.md)
72
+
73
+ ## Operational Diagnostics
74
+
75
+ The `doctor` command checks readiness before a workflow fails mid-run. It covers system requirements, executor configuration, flow readiness, node versions, current working directory context, AgentWeaver home configuration, and environment diagnostics.
76
+
77
+ Diagnostics are available as human-readable output or JSON for automation.