@chllming/wave-orchestration 0.5.3 → 0.5.4

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 (79) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +56 -509
  3. package/docs/README.md +39 -0
  4. package/docs/concepts/context7-vs-skills.md +94 -0
  5. package/docs/concepts/operating-modes.md +91 -0
  6. package/docs/concepts/runtime-agnostic-orchestration.md +95 -0
  7. package/docs/concepts/what-is-a-wave.md +133 -0
  8. package/docs/guides/planner.md +113 -0
  9. package/docs/guides/terminal-surfaces.md +80 -0
  10. package/docs/image.png +0 -0
  11. package/docs/plans/context7-wave-orchestrator.md +2 -0
  12. package/docs/plans/current-state.md +10 -0
  13. package/docs/plans/master-plan.md +3 -3
  14. package/docs/plans/migration.md +4 -3
  15. package/docs/plans/wave-orchestrator.md +27 -3
  16. package/docs/reference/runtime-config/README.md +19 -0
  17. package/docs/reference/skills.md +156 -0
  18. package/docs/roadmap.md +160 -564
  19. package/package.json +2 -1
  20. package/releases/manifest.json +17 -0
  21. package/scripts/wave-orchestrator/config.mjs +17 -0
  22. package/scripts/wave-orchestrator/context7.mjs +9 -0
  23. package/scripts/wave-orchestrator/coordination.mjs +16 -0
  24. package/scripts/wave-orchestrator/executors.mjs +24 -11
  25. package/scripts/wave-orchestrator/install.mjs +41 -2
  26. package/scripts/wave-orchestrator/launcher.mjs +113 -20
  27. package/scripts/wave-orchestrator/planner.mjs +1328 -0
  28. package/scripts/wave-orchestrator/project-profile.mjs +190 -0
  29. package/scripts/wave-orchestrator/shared.mjs +2 -0
  30. package/scripts/wave-orchestrator/skills.mjs +448 -0
  31. package/scripts/wave-orchestrator/terminals.mjs +16 -0
  32. package/scripts/wave-orchestrator/traces.mjs +23 -0
  33. package/scripts/wave-orchestrator/wave-files.mjs +299 -84
  34. package/scripts/wave.mjs +11 -0
  35. package/skills/provider-aws/SKILL.md +6 -0
  36. package/skills/provider-aws/skill.json +5 -0
  37. package/skills/provider-custom-deploy/SKILL.md +5 -0
  38. package/skills/provider-custom-deploy/skill.json +5 -0
  39. package/skills/provider-docker-compose/SKILL.md +6 -0
  40. package/skills/provider-docker-compose/skill.json +5 -0
  41. package/skills/provider-github-release/SKILL.md +6 -0
  42. package/skills/provider-github-release/skill.json +5 -0
  43. package/skills/provider-kubernetes/SKILL.md +6 -0
  44. package/skills/provider-kubernetes/skill.json +5 -0
  45. package/skills/provider-railway/SKILL.md +6 -0
  46. package/skills/provider-railway/adapters/claude.md +1 -0
  47. package/skills/provider-railway/adapters/codex.md +1 -0
  48. package/skills/provider-railway/adapters/local.md +1 -0
  49. package/skills/provider-railway/adapters/opencode.md +1 -0
  50. package/skills/provider-railway/skill.json +5 -0
  51. package/skills/provider-ssh-manual/SKILL.md +6 -0
  52. package/skills/provider-ssh-manual/skill.json +5 -0
  53. package/skills/repo-coding-rules/SKILL.md +7 -0
  54. package/skills/repo-coding-rules/skill.json +5 -0
  55. package/skills/role-deploy/SKILL.md +6 -0
  56. package/skills/role-deploy/skill.json +5 -0
  57. package/skills/role-documentation/SKILL.md +6 -0
  58. package/skills/role-documentation/skill.json +5 -0
  59. package/skills/role-evaluator/SKILL.md +6 -0
  60. package/skills/role-evaluator/skill.json +5 -0
  61. package/skills/role-implementation/SKILL.md +6 -0
  62. package/skills/role-implementation/skill.json +5 -0
  63. package/skills/role-infra/SKILL.md +6 -0
  64. package/skills/role-infra/skill.json +5 -0
  65. package/skills/role-integration/SKILL.md +6 -0
  66. package/skills/role-integration/skill.json +5 -0
  67. package/skills/role-research/SKILL.md +6 -0
  68. package/skills/role-research/skill.json +5 -0
  69. package/skills/runtime-claude/SKILL.md +6 -0
  70. package/skills/runtime-claude/skill.json +5 -0
  71. package/skills/runtime-codex/SKILL.md +6 -0
  72. package/skills/runtime-codex/skill.json +5 -0
  73. package/skills/runtime-local/SKILL.md +5 -0
  74. package/skills/runtime-local/skill.json +5 -0
  75. package/skills/runtime-opencode/SKILL.md +6 -0
  76. package/skills/runtime-opencode/skill.json +5 -0
  77. package/skills/wave-core/SKILL.md +7 -0
  78. package/skills/wave-core/skill.json +5 -0
  79. package/wave.config.json +27 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chllming/wave-orchestration",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "license": "MIT",
5
5
  "description": "Generic wave-based multi-agent orchestration for repository work.",
6
6
  "repository": {
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "files": [
18
18
  "scripts",
19
+ "skills",
19
20
  "docs",
20
21
  "releases",
21
22
  "wave.config.json",
@@ -2,6 +2,23 @@
2
2
  "schemaVersion": 1,
3
3
  "packageName": "@chllming/wave-orchestration",
4
4
  "releases": [
5
+ {
6
+ "version": "0.5.4",
7
+ "date": "2026-03-22",
8
+ "summary": "Planner foundation, cross-runtime skills, and clearer docs and reconcile output.",
9
+ "features": [
10
+ "The planner foundation now ships with `.wave/project-profile.json`, `wave project setup|show`, and `wave draft`, which writes both structured JSON specs and launcher-compatible wave markdown.",
11
+ "Canonical repo-owned skill bundles now live under `skills/`, with attachment by base, role, runtime, deploy kind, and explicit per-agent `### Skills`, then projection into Codex, Claude Code, OpenCode, and local executor overlays.",
12
+ "The docs surface now includes a docs index plus focused concept, guide, and reference pages for waves, planner workflow, operating modes, Context7 vs skills, terminal surfaces, and runtime-agnostic orchestration.",
13
+ "Reconcile output now explains why incomplete waves cannot be reconstructed, making `wave launch --reconcile-status` failures easier to interpret."
14
+ ],
15
+ "manualSteps": [
16
+ "After upgrading, run `pnpm exec wave project setup` once so the repo has an explicit project profile with default lane, oversight mode, terminal surface, and deploy-environment memory.",
17
+ "Review `wave.config.json` `skills` mappings and the seeded `skills/` bundles before relying on runtime-specific or deploy-specific skill attachment.",
18
+ "Dry-run at least one generated or existing wave with `pnpm exec wave launch --lane main --dry-run --no-dashboard` to inspect the new skill overlay artifacts and launch previews."
19
+ ],
20
+ "breaking": false
21
+ },
5
22
  {
6
23
  "version": "0.5.3",
7
24
  "date": "2026-03-22",
@@ -1,6 +1,11 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { WORKSPACE_ROOT } from "./roots.mjs";
4
+ import {
5
+ emptySkillsConfig,
6
+ mergeSkillsConfig,
7
+ normalizeSkillsConfig,
8
+ } from "./skills.mjs";
4
9
 
5
10
  const REPO_ROOT = WORKSPACE_ROOT;
6
11
 
@@ -388,6 +393,10 @@ function normalizeRuntimePolicy(rawRuntimePolicy = {}) {
388
393
  };
389
394
  }
390
395
 
396
+ function normalizeLaneSkills(rawSkills = {}, lane = "skills", options = {}) {
397
+ return normalizeSkillsConfig(rawSkills, lane, options);
398
+ }
399
+
391
400
  function normalizeClaudePromptMode(value, label = "executors.claude.appendSystemPromptMode") {
392
401
  const normalized = String(value || "append")
393
402
  .trim()
@@ -751,6 +760,7 @@ export function loadWaveConfig(configPath = DEFAULT_WAVE_CONFIG_PATH) {
751
760
  roles: normalizeRoles(rawConfig.roles),
752
761
  validation: normalizeValidation(rawConfig.validation),
753
762
  executors: normalizeExecutors(rawConfig.executors),
763
+ skills: normalizeLaneSkills(rawConfig.skills, "skills"),
754
764
  capabilityRouting: normalizeCapabilityRouting(rawConfig.capabilityRouting),
755
765
  runtimePolicy: normalizeRuntimePolicy(rawConfig.runtimePolicy),
756
766
  sharedPlanDocs,
@@ -785,6 +795,12 @@ export function resolveLaneProfile(config, laneInput = config.defaultLane) {
785
795
  const executors = normalizeExecutors(
786
796
  mergeExecutors(config.executors, laneConfig.executors),
787
797
  );
798
+ const skills = mergeSkillsConfig(
799
+ config.skills || emptySkillsConfig(),
800
+ normalizeLaneSkills(laneConfig.skills, `${lane}.skills`, {
801
+ preserveOmittedDir: true,
802
+ }),
803
+ );
788
804
  const capabilityRouting = normalizeCapabilityRouting({
789
805
  ...config.capabilityRouting,
790
806
  ...(laneConfig.capabilityRouting || {}),
@@ -812,6 +828,7 @@ export function resolveLaneProfile(config, laneInput = config.defaultLane) {
812
828
  roles,
813
829
  validation,
814
830
  executors,
831
+ skills,
815
832
  capabilityRouting,
816
833
  runtimePolicy,
817
834
  paths: {
@@ -221,6 +221,15 @@ export function buildAgentPromptFingerprintSource(agent) {
221
221
  components: agent?.components || [],
222
222
  componentTargets: agent?.componentTargets || null,
223
223
  executorResolved: agent?.executorResolved || null,
224
+ skillsResolved: agent?.skillsResolved
225
+ ? {
226
+ ids: agent.skillsResolved.ids || [],
227
+ role: agent.skillsResolved.role || null,
228
+ runtime: agent.skillsResolved.runtime || null,
229
+ deployKind: agent.skillsResolved.deployKind || null,
230
+ promptHash: agent.skillsResolved.promptHash || null,
231
+ }
232
+ : null,
224
233
  });
225
234
  }
226
235
 
@@ -349,6 +349,21 @@ export function buildExecutionPrompt({
349
349
  "",
350
350
  ]
351
351
  : [];
352
+ const skillLines =
353
+ Array.isArray(agent.skillsResolved?.ids) && agent.skillsResolved.ids.length > 0
354
+ ? [
355
+ "Active skill packs for this run:",
356
+ ...agent.skillsResolved.ids.map((skillId) => `- ${skillId}`),
357
+ "- Treat the following skill payload as additive guidance. Repository source, standing role prompts, and ownership boundaries remain authoritative.",
358
+ "",
359
+ "## Skill context",
360
+ "",
361
+ "```text",
362
+ agent.skillsResolved.promptText || "",
363
+ "```",
364
+ "",
365
+ ]
366
+ : [];
352
367
 
353
368
  return [
354
369
  `Working directory: ${REPO_ROOT}`,
@@ -413,6 +428,7 @@ export function buildExecutionPrompt({
413
428
  ...promotedComponentLines,
414
429
  ...ownedComponentLines,
415
430
  ...deliverableLines,
431
+ ...skillLines,
416
432
  ...context7PromptLines,
417
433
  "Assigned implementation prompt:",
418
434
  "```",
@@ -134,7 +134,7 @@ function buildClaudeSettingsPath(executor, overlayDir) {
134
134
  return settingsPath;
135
135
  }
136
136
 
137
- function buildOpenCodeConfig({ agent, executor, agentName, promptFileName, overlayDir }) {
137
+ function buildOpenCodeConfig({ agent, executor, agentName, promptFileName, overlayDir, skillProjection }) {
138
138
  const promptAgent = {
139
139
  description: `Wave agent ${agent.agentId}: ${agent.title}`,
140
140
  mode: "primary",
@@ -149,7 +149,11 @@ function buildOpenCodeConfig({ agent, executor, agentName, promptFileName, overl
149
149
  const baseAgentConfig = isPlainObject(baseConfig.agent) ? baseConfig.agent : {};
150
150
  const config = mergeJsonObjects(baseConfig, {
151
151
  $schema: baseConfig.$schema || "https://opencode.ai/config.json",
152
- instructions: mergeUniqueStringArrays(baseConfig.instructions, executor.opencode.instructions),
152
+ instructions: mergeUniqueStringArrays(
153
+ baseConfig.instructions,
154
+ executor.opencode.instructions,
155
+ skillProjection?.opencodeInstructions,
156
+ ),
153
157
  agent: {
154
158
  ...baseAgentConfig,
155
159
  [agentName]: mergeJsonObjects(baseAgentConfig[agentName], promptAgent),
@@ -201,7 +205,11 @@ export function buildCodexExecInvocation(
201
205
  function buildClaudeLaunchSpec({ agent, promptPath, logPath, overlayDir }) {
202
206
  const executor = agent.executorResolved;
203
207
  const systemPromptPath = path.join(overlayDir, "claude-system-prompt.txt");
204
- writeTextAtomic(systemPromptPath, `${renderHarnessSystemPrompt(agent, "claude")}\n`);
208
+ const skillText = String(agent?.skillsResolved?.promptText || "").trim();
209
+ writeTextAtomic(
210
+ systemPromptPath,
211
+ `${renderHarnessSystemPrompt(agent, "claude")}${skillText ? `\n\n${skillText}` : ""}\n`,
212
+ );
205
213
  const tokens = [executor.claude.command, "-p", "--no-session-persistence"];
206
214
  const settingsPath = buildClaudeSettingsPath(executor, overlayDir);
207
215
  appendSingleValueFlag(tokens, "--output-format", executor.claude.outputFormat || "text");
@@ -234,7 +242,7 @@ function buildClaudeLaunchSpec({ agent, promptPath, logPath, overlayDir }) {
234
242
  };
235
243
  }
236
244
 
237
- function buildOpenCodeLaunchSpec({ agent, promptPath, logPath, overlayDir }) {
245
+ function buildOpenCodeLaunchSpec({ agent, promptPath, logPath, overlayDir, skillProjection }) {
238
246
  const executor = agent.executorResolved;
239
247
  const requestedAgentName = String(executor.opencode.agent || `wave-${agent.agentId}`)
240
248
  .trim()
@@ -252,12 +260,17 @@ function buildOpenCodeLaunchSpec({ agent, promptPath, logPath, overlayDir }) {
252
260
  agentName,
253
261
  promptFileName,
254
262
  overlayDir,
263
+ skillProjection,
255
264
  });
256
265
  const tokens = [executor.opencode.command, "run", "--agent", shellQuote(agentName)];
257
266
  appendSingleValueFlag(tokens, "--model", executor.opencode.model || executor.model);
258
267
  appendSingleValueFlag(tokens, "--format", executor.opencode.format || "default");
259
268
  appendSingleValueFlag(tokens, "--attach", executor.opencode.attach);
260
- appendRepeatedFlag(tokens, "--file", executor.opencode.files);
269
+ appendRepeatedFlag(
270
+ tokens,
271
+ "--file",
272
+ mergeUniqueStringArrays(executor.opencode.files, skillProjection?.opencodeFiles),
273
+ );
261
274
  appendSingleValueFlag(tokens, "--title", `wave-${agent.agentId}`);
262
275
  return {
263
276
  executorId: "opencode",
@@ -286,7 +299,7 @@ function buildLocalLaunchSpec({ promptPath, logPath }) {
286
299
  };
287
300
  }
288
301
 
289
- function buildCodexLaunchSpec({ agent, promptPath, logPath }) {
302
+ function buildCodexLaunchSpec({ agent, promptPath, logPath, skillProjection }) {
290
303
  const executor = agent.executorResolved;
291
304
  return {
292
305
  executorId: "codex",
@@ -304,7 +317,7 @@ function buildCodexLaunchSpec({ agent, promptPath, logPath }) {
304
317
  config: executor.codex.config,
305
318
  search: executor.codex.search,
306
319
  images: executor.codex.images,
307
- addDirs: executor.codex.addDirs,
320
+ addDirs: mergeUniqueStringArrays(executor.codex.addDirs, skillProjection?.codexAddDirs),
308
321
  json: executor.codex.json,
309
322
  ephemeral: executor.codex.ephemeral,
310
323
  },
@@ -313,19 +326,19 @@ function buildCodexLaunchSpec({ agent, promptPath, logPath }) {
313
326
  };
314
327
  }
315
328
 
316
- export function buildExecutorLaunchSpec({ agent, promptPath, logPath, overlayDir }) {
329
+ export function buildExecutorLaunchSpec({ agent, promptPath, logPath, overlayDir, skillProjection }) {
317
330
  const executorId = normalizeExecutorMode(agent?.executorResolved?.id || DEFAULT_EXECUTOR_MODE);
318
331
  ensureDirectory(overlayDir);
319
332
  if (executorId === "local") {
320
333
  return buildLocalLaunchSpec({ promptPath, logPath });
321
334
  }
322
335
  if (executorId === "claude") {
323
- return buildClaudeLaunchSpec({ agent, promptPath, logPath, overlayDir });
336
+ return buildClaudeLaunchSpec({ agent, promptPath, logPath, overlayDir, skillProjection });
324
337
  }
325
338
  if (executorId === "opencode") {
326
- return buildOpenCodeLaunchSpec({ agent, promptPath, logPath, overlayDir });
339
+ return buildOpenCodeLaunchSpec({ agent, promptPath, logPath, overlayDir, skillProjection });
327
340
  }
328
- return buildCodexLaunchSpec({ agent, promptPath, logPath });
341
+ return buildCodexLaunchSpec({ agent, promptPath, logPath, skillProjection });
329
342
  }
330
343
 
331
344
  export function commandForExecutor(executor, executorId = executor?.id) {
@@ -7,6 +7,7 @@ import {
7
7
  import { buildLanePaths, ensureDirectory, PACKAGE_ROOT, readJsonOrNull, REPO_ROOT, writeJsonAtomic } from "./shared.mjs";
8
8
  import { loadWaveConfig } from "./config.mjs";
9
9
  import { applyExecutorSelectionsToWave, parseWaveFiles, validateWaveDefinition } from "./wave-files.mjs";
10
+ import { validateLaneSkillConfiguration } from "./skills.mjs";
10
11
 
11
12
  export const INSTALL_STATE_SCHEMA_VERSION = 1;
12
13
  export const INSTALL_STATE_DIR = ".wave";
@@ -16,10 +17,17 @@ export const CHANGELOG_MANIFEST_PATH = path.join(PACKAGE_ROOT, "releases", "mani
16
17
  export const PACKAGE_METADATA_PATH = path.join(PACKAGE_ROOT, "package.json");
17
18
  export const STARTER_TEMPLATE_PATHS = [
18
19
  "wave.config.json",
20
+ "docs/README.md",
19
21
  "docs/agents/wave-documentation-role.md",
20
22
  "docs/agents/wave-evaluator-role.md",
21
23
  "docs/agents/wave-integration-role.md",
24
+ "docs/concepts/context7-vs-skills.md",
25
+ "docs/concepts/operating-modes.md",
26
+ "docs/concepts/runtime-agnostic-orchestration.md",
27
+ "docs/concepts/what-is-a-wave.md",
22
28
  "docs/context7/bundles.json",
29
+ "docs/guides/planner.md",
30
+ "docs/guides/terminal-surfaces.md",
23
31
  "docs/plans/component-cutover-matrix.json",
24
32
  "docs/plans/component-cutover-matrix.md",
25
33
  "docs/plans/context7-wave-orchestrator.md",
@@ -29,6 +37,7 @@ export const STARTER_TEMPLATE_PATHS = [
29
37
  "docs/plans/wave-orchestrator.md",
30
38
  "docs/plans/waves/wave-0.md",
31
39
  "docs/reference/repository-guidance.md",
40
+ "docs/reference/skills.md",
32
41
  "docs/reference/runtime-config/README.md",
33
42
  "docs/reference/runtime-config/codex.md",
34
43
  "docs/reference/runtime-config/claude.md",
@@ -37,6 +46,8 @@ export const STARTER_TEMPLATE_PATHS = [
37
46
  ];
38
47
  const REQUIRED_GITIGNORE_ENTRIES = [
39
48
  ".tmp/",
49
+ ".wave/",
50
+ ".vscode/terminals.json",
40
51
  "docs/research/cache/",
41
52
  "docs/research/agent-context-cache/",
42
53
  "docs/research/papers/",
@@ -69,7 +80,7 @@ function ensureWorkspaceSubdir(relPath) {
69
80
  }
70
81
 
71
82
  function templateStatusList() {
72
- return STARTER_TEMPLATE_PATHS.map((relPath) => ({
83
+ return starterTemplatePaths().map((relPath) => ({
73
84
  path: relPath,
74
85
  sourcePath: path.join(PACKAGE_ROOT, relPath),
75
86
  targetPath: path.join(REPO_ROOT, relPath),
@@ -77,6 +88,30 @@ function templateStatusList() {
77
88
  }));
78
89
  }
79
90
 
91
+ function starterSkillTemplatePaths() {
92
+ const skillsRoot = path.join(PACKAGE_ROOT, "skills");
93
+ if (!fs.existsSync(skillsRoot)) {
94
+ return [];
95
+ }
96
+ const files = [];
97
+ const visit = (targetDir) => {
98
+ for (const entry of fs.readdirSync(targetDir, { withFileTypes: true })) {
99
+ const fullPath = path.join(targetDir, entry.name);
100
+ if (entry.isDirectory()) {
101
+ visit(fullPath);
102
+ } else {
103
+ files.push(path.relative(PACKAGE_ROOT, fullPath).replaceAll(path.sep, "/"));
104
+ }
105
+ }
106
+ };
107
+ visit(skillsRoot);
108
+ return files.toSorted();
109
+ }
110
+
111
+ function starterTemplatePaths() {
112
+ return [...STARTER_TEMPLATE_PATHS, ...starterSkillTemplatePaths()];
113
+ }
114
+
80
115
  function existingBootstrapMarkers() {
81
116
  return templateStatusList()
82
117
  .filter((entry) => entry.exists)
@@ -242,6 +277,10 @@ export function runDoctor() {
242
277
  errors.push(`Missing required Wave file: ${relPath}`);
243
278
  }
244
279
  }
280
+ const skillValidation = validateLaneSkillConfiguration(lanePaths.laneProfile);
281
+ if (!skillValidation.ok) {
282
+ errors.push(...skillValidation.errors);
283
+ }
245
284
  if (fs.existsSync(lanePaths.wavesDir)) {
246
285
  const context7BundleIndex = loadContext7BundleIndex(lanePaths.context7BundleIndexPath);
247
286
  parseWaveFiles(lanePaths.wavesDir, { laneProfile: lanePaths.laneProfile })
@@ -314,7 +353,7 @@ export function initializeWorkspace(options = {}) {
314
353
  if (adoptExisting) {
315
354
  adoptedFiles.push(...markers);
316
355
  } else {
317
- for (const relPath of STARTER_TEMPLATE_PATHS) {
356
+ for (const relPath of starterTemplatePaths()) {
318
357
  copyTemplateFile(relPath);
319
358
  seededFiles.push(relPath);
320
359
  }