@chllming/wave-orchestration 0.5.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +549 -0
  3. package/docs/agents/wave-deploy-verifier-role.md +34 -0
  4. package/docs/agents/wave-documentation-role.md +30 -0
  5. package/docs/agents/wave-evaluator-role.md +43 -0
  6. package/docs/agents/wave-infra-role.md +34 -0
  7. package/docs/agents/wave-integration-role.md +32 -0
  8. package/docs/agents/wave-launcher-role.md +37 -0
  9. package/docs/context7/bundles.json +91 -0
  10. package/docs/plans/component-cutover-matrix.json +112 -0
  11. package/docs/plans/component-cutover-matrix.md +49 -0
  12. package/docs/plans/context7-wave-orchestrator.md +130 -0
  13. package/docs/plans/current-state.md +44 -0
  14. package/docs/plans/master-plan.md +16 -0
  15. package/docs/plans/migration.md +23 -0
  16. package/docs/plans/wave-orchestrator.md +254 -0
  17. package/docs/plans/waves/wave-0.md +165 -0
  18. package/docs/reference/github-packages-setup.md +52 -0
  19. package/docs/reference/migration-0.2-to-0.5.md +622 -0
  20. package/docs/reference/npmjs-trusted-publishing.md +55 -0
  21. package/docs/reference/repository-guidance.md +18 -0
  22. package/docs/reference/runtime-config/README.md +85 -0
  23. package/docs/reference/runtime-config/claude.md +105 -0
  24. package/docs/reference/runtime-config/codex.md +81 -0
  25. package/docs/reference/runtime-config/opencode.md +93 -0
  26. package/docs/research/agent-context-sources.md +57 -0
  27. package/docs/roadmap.md +626 -0
  28. package/package.json +53 -0
  29. package/releases/manifest.json +101 -0
  30. package/scripts/context7-api-check.sh +21 -0
  31. package/scripts/context7-export-env.sh +52 -0
  32. package/scripts/research/agent-context-archive.mjs +472 -0
  33. package/scripts/research/generate-agent-context-indexes.mjs +85 -0
  34. package/scripts/research/import-agent-context-archive.mjs +793 -0
  35. package/scripts/research/manifests/harness-and-blackboard-2026-03-21.mjs +201 -0
  36. package/scripts/wave-autonomous.mjs +13 -0
  37. package/scripts/wave-cli-bootstrap.mjs +27 -0
  38. package/scripts/wave-dashboard.mjs +11 -0
  39. package/scripts/wave-human-feedback.mjs +11 -0
  40. package/scripts/wave-launcher.mjs +11 -0
  41. package/scripts/wave-local-executor.mjs +13 -0
  42. package/scripts/wave-orchestrator/agent-state.mjs +416 -0
  43. package/scripts/wave-orchestrator/autonomous.mjs +367 -0
  44. package/scripts/wave-orchestrator/clarification-triage.mjs +605 -0
  45. package/scripts/wave-orchestrator/config.mjs +848 -0
  46. package/scripts/wave-orchestrator/context7.mjs +464 -0
  47. package/scripts/wave-orchestrator/coord-cli.mjs +286 -0
  48. package/scripts/wave-orchestrator/coordination-store.mjs +987 -0
  49. package/scripts/wave-orchestrator/coordination.mjs +768 -0
  50. package/scripts/wave-orchestrator/dashboard-renderer.mjs +254 -0
  51. package/scripts/wave-orchestrator/dashboard-state.mjs +473 -0
  52. package/scripts/wave-orchestrator/dep-cli.mjs +219 -0
  53. package/scripts/wave-orchestrator/docs-queue.mjs +75 -0
  54. package/scripts/wave-orchestrator/executors.mjs +385 -0
  55. package/scripts/wave-orchestrator/feedback.mjs +372 -0
  56. package/scripts/wave-orchestrator/install.mjs +540 -0
  57. package/scripts/wave-orchestrator/launcher.mjs +3879 -0
  58. package/scripts/wave-orchestrator/ledger.mjs +332 -0
  59. package/scripts/wave-orchestrator/local-executor.mjs +263 -0
  60. package/scripts/wave-orchestrator/replay.mjs +246 -0
  61. package/scripts/wave-orchestrator/roots.mjs +10 -0
  62. package/scripts/wave-orchestrator/routing-state.mjs +542 -0
  63. package/scripts/wave-orchestrator/shared.mjs +405 -0
  64. package/scripts/wave-orchestrator/terminals.mjs +209 -0
  65. package/scripts/wave-orchestrator/traces.mjs +1094 -0
  66. package/scripts/wave-orchestrator/wave-files.mjs +1923 -0
  67. package/scripts/wave.mjs +103 -0
  68. package/wave.config.json +115 -0
@@ -0,0 +1,254 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { analyzeMessageBoardCommunication } from "./coordination.mjs";
4
+ import { commsAgeSummary, deploymentSummary, renderCountsByState } from "./dashboard-state.mjs";
5
+ import {
6
+ DEFAULT_REFRESH_MS,
7
+ DEFAULT_WAVE_LANE,
8
+ FINAL_EXIT_DELAY_MS,
9
+ REPO_ROOT,
10
+ TERMINAL_STATES,
11
+ formatAgeFromTimestamp,
12
+ formatElapsed,
13
+ pad,
14
+ sleep,
15
+ truncate,
16
+ } from "./shared.mjs";
17
+
18
+ export function parseDashboardArgs(argv) {
19
+ const options = {
20
+ lane: DEFAULT_WAVE_LANE,
21
+ dashboardFile: null,
22
+ messageBoard: null,
23
+ watch: false,
24
+ refreshMs: DEFAULT_REFRESH_MS,
25
+ };
26
+ for (let i = 0; i < argv.length; i += 1) {
27
+ const arg = argv[i];
28
+ if (arg === "--") {
29
+ continue;
30
+ }
31
+ if (arg === "--watch") {
32
+ options.watch = true;
33
+ } else if (arg === "--lane") {
34
+ options.lane =
35
+ String(argv[++i] || "")
36
+ .trim()
37
+ .toLowerCase() || DEFAULT_WAVE_LANE;
38
+ } else if (arg === "--dashboard-file") {
39
+ options.dashboardFile = path.resolve(REPO_ROOT, argv[++i] || "");
40
+ } else if (arg === "--message-board") {
41
+ options.messageBoard = path.resolve(REPO_ROOT, argv[++i] || "");
42
+ } else if (arg === "--refresh-ms") {
43
+ options.refreshMs = Number.parseInt(String(argv[++i] || ""), 10);
44
+ } else if (arg === "--help" || arg === "-h") {
45
+ return { help: true, options };
46
+ } else {
47
+ throw new Error(`Unknown argument: ${arg}`);
48
+ }
49
+ }
50
+ if (!options.dashboardFile) {
51
+ throw new Error("--dashboard-file is required");
52
+ }
53
+ return { help: false, options };
54
+ }
55
+
56
+ function readMessageBoardTail(messageBoardPath, maxLines = 24) {
57
+ if (!messageBoardPath) {
58
+ return ["(message board path unavailable)"];
59
+ }
60
+ if (!fs.existsSync(messageBoardPath)) {
61
+ return ["(message board missing)"];
62
+ }
63
+ const raw = fs.readFileSync(messageBoardPath, "utf8").trim();
64
+ if (!raw) {
65
+ return ["(message board currently empty)"];
66
+ }
67
+ return raw.split(/\r?\n/).slice(-maxLines);
68
+ }
69
+
70
+ function resolveMessageBoardPath(state, overridePath) {
71
+ if (overridePath) {
72
+ return overridePath;
73
+ }
74
+ if (typeof state?.messageBoardPath === "string") {
75
+ return path.resolve(REPO_ROOT, state.messageBoardPath);
76
+ }
77
+ return null;
78
+ }
79
+
80
+ function isGlobalDashboardState(state) {
81
+ return Boolean(state && Array.isArray(state.waves) && !Array.isArray(state.agents));
82
+ }
83
+
84
+ function renderWaveDashboard({ state, dashboardPath, messageBoardPath, lane }) {
85
+ if (!state) {
86
+ return `Dashboard file not found or invalid: ${dashboardPath}`;
87
+ }
88
+ const lines = [];
89
+ const laneName = String(state?.lane || lane || "").trim() || DEFAULT_WAVE_LANE;
90
+ lines.push(
91
+ `${laneName} Wave Dashboard | wave=${state.wave ?? "?"} | status=${state.status ?? "unknown"} | attempt=${state.attempt ?? "?"}/${state.maxAttempts ?? "?"}`,
92
+ );
93
+ lines.push(
94
+ `Updated ${formatAgeFromTimestamp(Date.parse(state.updatedAt || ""))} | Started ${state.startedAt || "n/a"} | Elapsed ${formatElapsed(state.startedAt)}`,
95
+ );
96
+ lines.push(`Run tag: ${state.runTag || "n/a"} | Wave file: ${state.waveFile || "n/a"}`);
97
+ lines.push(`Counts: ${renderCountsByState(state.agents || []) || "none"}`);
98
+ const comms = analyzeMessageBoardCommunication(messageBoardPath);
99
+ if (!comms.available) {
100
+ lines.push(`Comms: unavailable ${comms.reason || ""}`.trim());
101
+ } else {
102
+ lines.push(
103
+ `Comms: requests=${comms.actionableRequests} unresolved=${comms.unresolvedRequests} unacknowledged=${comms.unacknowledgedRequests} malformed=${comms.malformedEntries} placeholder-ts=${comms.placeholderTimestampEntries}`,
104
+ );
105
+ lines.push(
106
+ `Comms age: last-ack=${commsAgeSummary(comms.lastAcknowledgementTimestamp)} oldest-unack=${commsAgeSummary(comms.oldestUnacknowledgedTimestamp)}`,
107
+ );
108
+ }
109
+ lines.push("");
110
+ lines.push("Agents:");
111
+ lines.push(
112
+ `${pad("ID", 8)} ${pad("State", 12)} ${pad("Attempts", 8)} ${pad("Exit", 6)} ${pad("Deploy", 24)} ${pad("Last Update", 12)} Detail`,
113
+ );
114
+ lines.push(
115
+ `${"-".repeat(8)} ${"-".repeat(12)} ${"-".repeat(8)} ${"-".repeat(6)} ${"-".repeat(24)} ${"-".repeat(12)} ${"-".repeat(36)}`,
116
+ );
117
+ for (const agent of state.agents || []) {
118
+ lines.push(
119
+ `${pad(agent.agentId || "-", 8)} ${pad(agent.state || "-", 12)} ${pad(agent.attempts ?? 0, 8)} ${pad(
120
+ agent.exitCode ?? "-",
121
+ 6,
122
+ )} ${pad(deploymentSummary({ service: agent.deploymentService, state: agent.deploymentState }), 24)} ${pad(
123
+ formatAgeFromTimestamp(Date.parse(agent.lastUpdateAt || "")),
124
+ 12,
125
+ )} ${truncate(agent.detail || "", 72)}`,
126
+ );
127
+ }
128
+ lines.push("");
129
+ lines.push("Recent events:");
130
+ for (const event of (state.events || []).slice(-12)) {
131
+ const prefix = event.agentId ? `[${event.agentId}]` : "[wave]";
132
+ lines.push(
133
+ `${event.at || "n/a"} ${pad(event.level || "info", 5)} ${prefix} ${event.message || ""}`,
134
+ );
135
+ }
136
+ if ((state.events || []).length === 0) {
137
+ lines.push("(none)");
138
+ }
139
+ lines.push("");
140
+ lines.push("Rolling message board:");
141
+ lines.push(`Path: ${messageBoardPath || "n/a"}`);
142
+ lines.push(...readMessageBoardTail(messageBoardPath));
143
+ return lines.join("\n");
144
+ }
145
+
146
+ function renderGlobalDashboard({ state, dashboardPath, lane }) {
147
+ if (!state) {
148
+ return `Dashboard file not found or invalid: ${dashboardPath}`;
149
+ }
150
+ const lines = [];
151
+ const laneName = String(state?.lane || lane || "").trim() || DEFAULT_WAVE_LANE;
152
+ lines.push(
153
+ `${laneName} Wave Global Dashboard | run=${state.runId || "n/a"} | status=${state.status || "unknown"}`,
154
+ );
155
+ lines.push(
156
+ `Updated ${formatAgeFromTimestamp(Date.parse(state.updatedAt || ""))} | Started ${state.startedAt || "n/a"} | Elapsed ${formatElapsed(state.startedAt)}`,
157
+ );
158
+ lines.push(
159
+ `Options: autoNext=${state.options?.autoNext ? "true" : "false"} start=${state.options?.startWave ?? "?"} end=${state.options?.endWave ?? "last"} retries=${state.options?.maxRetriesPerWave ?? "?"}`,
160
+ );
161
+ lines.push("");
162
+ lines.push("Waves:");
163
+ lines.push(
164
+ `${pad("Wave", 6)} ${pad("Status", 10)} ${pad("Attempt", 9)} ${pad("Agents", 16)} ${pad("Started", 12)} ${pad("Last Message", 70)}`,
165
+ );
166
+ lines.push(
167
+ `${"-".repeat(6)} ${"-".repeat(10)} ${"-".repeat(9)} ${"-".repeat(16)} ${"-".repeat(12)} ${"-".repeat(70)}`,
168
+ );
169
+ for (const wave of state.waves || []) {
170
+ const agents = `${wave.agentsCompleted ?? 0}/${wave.agentsTotal ?? 0} ok, ${wave.agentsFailed ?? 0} fail`;
171
+ lines.push(
172
+ `${pad(wave.wave ?? "-", 6)} ${pad(wave.status || "-", 10)} ${pad(
173
+ `${wave.attempt ?? 0}/${wave.maxAttempts ?? 0}`,
174
+ 9,
175
+ )} ${pad(agents, 16)} ${pad(
176
+ formatAgeFromTimestamp(Date.parse(wave.startedAt || "")),
177
+ 12,
178
+ )} ${truncate(wave.lastMessage || "", 70)}`,
179
+ );
180
+ const deployments = Array.isArray(wave.deployments) ? wave.deployments : [];
181
+ if (deployments.length > 0) {
182
+ const deployLine = deployments
183
+ .slice(-3)
184
+ .map((deployment) => `${deployment.agentId}:${deployment.service}:${deployment.state}`)
185
+ .join(" | ");
186
+ lines.push(` Deploy: ${truncate(deployLine, 120)}`);
187
+ }
188
+ }
189
+ lines.push("");
190
+ lines.push("Recent events:");
191
+ for (const event of (state.events || []).slice(-16)) {
192
+ const waveTag = event.wave ? `wave:${event.wave}` : "wave:-";
193
+ lines.push(
194
+ `${event.at || "n/a"} ${pad(event.level || "info", 5)} [${waveTag}] ${event.message || ""}`,
195
+ );
196
+ }
197
+ if ((state.events || []).length === 0) {
198
+ lines.push("(none)");
199
+ }
200
+ return lines.join("\n");
201
+ }
202
+
203
+ export function renderDashboard({ state, dashboardPath, messageBoardPath, lane }) {
204
+ return isGlobalDashboardState(state)
205
+ ? renderGlobalDashboard({ state, dashboardPath, lane })
206
+ : renderWaveDashboard({ state, dashboardPath, messageBoardPath, lane });
207
+ }
208
+
209
+ export async function runDashboardCli(argv) {
210
+ const { help, options } = parseDashboardArgs(argv);
211
+ if (help) {
212
+ console.log(`Usage: pnpm exec wave dashboard --dashboard-file <path> [options]
213
+
214
+ Options:
215
+ --lane <name> Wave lane name (default: ${DEFAULT_WAVE_LANE})
216
+ --dashboard-file <path> Path to wave/global dashboard JSON
217
+ --message-board <path> Optional message board path override
218
+ --watch Refresh continuously
219
+ --refresh-ms <n> Refresh interval in ms (default: ${DEFAULT_REFRESH_MS})
220
+ `);
221
+ return;
222
+ }
223
+
224
+ let terminalStateReachedAt = null;
225
+ while (true) {
226
+ const raw = fs.existsSync(options.dashboardFile)
227
+ ? JSON.parse(fs.readFileSync(options.dashboardFile, "utf8"))
228
+ : null;
229
+ const boardPath = resolveMessageBoardPath(raw, options.messageBoard);
230
+ const rendered = renderDashboard({
231
+ state: raw,
232
+ dashboardPath: options.dashboardFile,
233
+ messageBoardPath: boardPath,
234
+ lane: options.lane,
235
+ });
236
+ if (process.stdout.isTTY) {
237
+ process.stdout.write("\u001bc");
238
+ }
239
+ process.stdout.write(`${rendered}\n`);
240
+ if (!options.watch) {
241
+ return;
242
+ }
243
+ const currentStatus = raw?.status || "";
244
+ if (TERMINAL_STATES.has(currentStatus)) {
245
+ terminalStateReachedAt ??= Date.now();
246
+ if (Date.now() - terminalStateReachedAt >= FINAL_EXIT_DELAY_MS) {
247
+ return;
248
+ }
249
+ } else {
250
+ terminalStateReachedAt = null;
251
+ }
252
+ await sleep(options.refreshMs);
253
+ }
254
+ }