@rryando/arcs 3.0.0 → 3.1.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 (48) hide show
  1. package/README.md +246 -340
  2. package/dist/cli/arcs-orchestrate.d.ts +1 -1
  3. package/dist/cli/arcs-orchestrate.d.ts.map +1 -1
  4. package/dist/cli/arcs-orchestrate.js +4 -3
  5. package/dist/cli/arcs-orchestrate.js.map +1 -1
  6. package/dist/cli/commands/next.js +26 -5
  7. package/dist/cli/commands/next.js.map +1 -1
  8. package/dist/cli/commands/task.d.ts.map +1 -1
  9. package/dist/cli/commands/task.js +14 -2
  10. package/dist/cli/commands/task.js.map +1 -1
  11. package/dist/retrieval/graph-builder.d.ts.map +1 -1
  12. package/dist/retrieval/graph-builder.js +18 -0
  13. package/dist/retrieval/graph-builder.js.map +1 -1
  14. package/dist/retrieval/graph-types.d.ts +1 -1
  15. package/dist/retrieval/graph-types.d.ts.map +1 -1
  16. package/dist/retrieval/graph-types.js +1 -0
  17. package/dist/retrieval/graph-types.js.map +1 -1
  18. package/dist/utils/diagram-generator.d.ts +3 -2
  19. package/dist/utils/diagram-generator.d.ts.map +1 -1
  20. package/dist/utils/diagram-generator.js +67 -6
  21. package/dist/utils/diagram-generator.js.map +1 -1
  22. package/dist/utils/errors.d.ts +2 -0
  23. package/dist/utils/errors.d.ts.map +1 -1
  24. package/dist/utils/errors.js +6 -0
  25. package/dist/utils/errors.js.map +1 -1
  26. package/dist/utils/task-store.d.ts +3 -0
  27. package/dist/utils/task-store.d.ts.map +1 -1
  28. package/dist/utils/task-store.js +35 -1
  29. package/dist/utils/task-store.js.map +1 -1
  30. package/dist/utils/toposort.d.ts +21 -0
  31. package/dist/utils/toposort.d.ts.map +1 -0
  32. package/dist/utils/toposort.js +126 -0
  33. package/dist/utils/toposort.js.map +1 -0
  34. package/dist/utils/workflow-policy.d.ts +1 -0
  35. package/dist/utils/workflow-policy.d.ts.map +1 -1
  36. package/dist/utils/workflow-policy.js +18 -2
  37. package/dist/utils/workflow-policy.js.map +1 -1
  38. package/opencode/arcs/prompts/arcs-orchestrate-caveman.txt +4 -3
  39. package/opencode/arcs/prompts/arcs-orchestrate.txt +4 -3
  40. package/opencode/arcs/skills/brainstorming/SKILL.md +2 -0
  41. package/opencode/arcs/skills/executing-plans/SKILL.md +2 -0
  42. package/opencode/arcs/skills/to-diagram/SKILL.md +2 -0
  43. package/package.json +4 -3
  44. package/scripts/arcs-init.mjs +81 -0
  45. package/scripts/build-opencode-bundle.mjs +178 -0
  46. package/scripts/deploy-opencode-bundle.mjs +203 -0
  47. package/scripts/lib/bundle-helpers.mjs +172 -0
  48. package/scripts/lint-bundle.mjs +206 -0
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+ // Bundle linter — detects drift/issues in the opencode ARCS bundle without
3
+ // overwriting anything. The repo bundle directory is the source of truth.
4
+ //
5
+ // Env vars:
6
+ // BUNDLE_LINT_BUNDLE_ROOT — override bundle root (default: opencode/arcs)
7
+ //
8
+ // Outputs JSON to stdout: { issues: [...], summary: { errors, warnings } }
9
+ // Exit code: 0 if no errors, 1 if errors found.
10
+
11
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
12
+ import { relative, resolve } from "node:path";
13
+
14
+ const repoRoot = resolve(import.meta.dirname, "..");
15
+ const defaultBundleRoot = resolve(repoRoot, "opencode/arcs");
16
+
17
+ const bundleRoot = process.env.BUNDLE_LINT_BUNDLE_ROOT
18
+ ? resolve(repoRoot, process.env.BUNDLE_LINT_BUNDLE_ROOT)
19
+ : defaultBundleRoot;
20
+
21
+ // Preserved files that are repo-authored, not sourced from manifest.
22
+ const preservedFiles = new Set([
23
+ "manifest.json",
24
+ "bundle-runtime.json",
25
+ ".opencode/plugins/arcs.js",
26
+ "skills/loop/SKILL.md",
27
+ "skills/caveman-commit/SKILL.md",
28
+ "skills/caveman-review/SKILL.md",
29
+ "skills/init-project/SKILL.md",
30
+ // Sub-agent prompt files (repo-authored, referenced from manifest.json
31
+ // requiredMerges, not from bundle-runtime.json's `agents` array).
32
+ "prompts/software-engineer.txt",
33
+ "prompts/tech-architect.txt",
34
+ "prompts/qa-analyst.txt",
35
+ "prompts/oncall-ops.txt",
36
+ "prompts/arcs-docs.txt",
37
+ "prompts/system-architect.txt",
38
+ "prompts/code-reviewer.txt",
39
+ "prompts/docs-researcher.txt",
40
+ // Orchestrator prompt files — generated from src/cli/arcs-orchestrate*.ts
41
+ // by build-opencode-bundle.mjs. Committed mirrors so the bundle is
42
+ // self-describing alongside the static sub-agent prompts.
43
+ "prompts/arcs-orchestrate.txt",
44
+ "prompts/arcs-orchestrate-caveman.txt",
45
+ ]);
46
+
47
+ /** @type {Array<{severity: 'error'|'warning', kind: string, message: string, file?: string, repair?: string}>} */const issues = [];
48
+
49
+ function addIssue(severity, kind, message, file, repair) {
50
+ const issue = { severity, kind, message };
51
+ if (file) issue.file = file;
52
+ if (repair) issue.repair = repair;
53
+ issues.push(issue);
54
+ }
55
+
56
+ function listAllFiles(rootPath, currentPath = rootPath) {
57
+ if (!existsSync(currentPath)) return [];
58
+ const entries = readdirSync(currentPath, { withFileTypes: true });
59
+ return entries.flatMap((entry) => {
60
+ const entryPath = resolve(currentPath, entry.name);
61
+ if (entry.isDirectory()) return listAllFiles(rootPath, entryPath);
62
+ return [relative(rootPath, entryPath).replace(/\\/g, "/")];
63
+ });
64
+ }
65
+
66
+ // --- Read manifest ---
67
+ const manifestPath = resolve(bundleRoot, "bundle-runtime.json");
68
+ if (!existsSync(manifestPath)) {
69
+ addIssue("error", "manifest-missing", `bundle-runtime.json not found at ${manifestPath}`);
70
+ output();
71
+ }
72
+
73
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
74
+
75
+ // --- Check 1: Every manifest-declared bundled file exists ---
76
+ const declaredFiles = new Set();
77
+
78
+ for (const [skillName, skillFiles] of Object.entries(manifest.skills ?? {})) {
79
+ for (const skillFile of skillFiles) {
80
+ const relativePath = `skills/${skillName}/${skillFile}`;
81
+ declaredFiles.add(relativePath);
82
+ if (!existsSync(resolve(bundleRoot, relativePath))) {
83
+ addIssue(
84
+ "error",
85
+ "missing-declared-file",
86
+ `Manifest declares ${relativePath} but file is missing`,
87
+ relativePath,
88
+ `Run bundle build or add the file`,
89
+ );
90
+ }
91
+ }
92
+ }
93
+
94
+ for (const agentFile of manifest.agents ?? []) {
95
+ declaredFiles.add(agentFile);
96
+ if (!existsSync(resolve(bundleRoot, agentFile))) {
97
+ addIssue("error", "missing-declared-file", `Manifest declares ${agentFile} but file is missing`, agentFile);
98
+ }
99
+ }
100
+
101
+ for (const pluginFile of manifest.plugin ?? []) {
102
+ declaredFiles.add(pluginFile);
103
+ if (!existsSync(resolve(bundleRoot, pluginFile))) {
104
+ addIssue("error", "missing-declared-file", `Manifest declares ${pluginFile} but file is missing`, pluginFile);
105
+ }
106
+ }
107
+
108
+ // --- Check 2: No extra undeclared files in bundle ---
109
+ const allBundleFiles = listAllFiles(bundleRoot);
110
+ const allowedFiles = new Set([...declaredFiles, ...preservedFiles]);
111
+
112
+ for (const file of allBundleFiles) {
113
+ if (!allowedFiles.has(file)) {
114
+ addIssue(
115
+ "warning",
116
+ "undeclared-file",
117
+ `File ${file} exists in bundle but is not declared in manifest`,
118
+ file,
119
+ `Remove the file or add it to bundle-runtime.json`,
120
+ );
121
+ }
122
+ }
123
+
124
+ // --- Check 3: Every skill includes SKILL.md ---
125
+ for (const [skillName, skillFiles] of Object.entries(manifest.skills ?? {})) {
126
+ if (!skillFiles.includes("SKILL.md")) {
127
+ addIssue(
128
+ "error",
129
+ "skill-missing-entry",
130
+ `Skill "${skillName}" does not include SKILL.md in its file list`,
131
+ `skills/${skillName}/SKILL.md`,
132
+ `Add "SKILL.md" to the skill's file array in bundle-runtime.json`,
133
+ );
134
+ }
135
+ }
136
+
137
+ // --- Check 4: .mjs scripts declared should be parseable ---
138
+ for (const [skillName, skillFiles] of Object.entries(manifest.skills ?? {})) {
139
+ for (const skillFile of skillFiles) {
140
+ if (skillFile.endsWith(".mjs")) {
141
+ const filePath = resolve(bundleRoot, `skills/${skillName}/${skillFile}`);
142
+ if (existsSync(filePath)) {
143
+ // Quick syntax check — try to parse as module
144
+ try {
145
+ const content = readFileSync(filePath, "utf-8");
146
+ // Basic check: not empty
147
+ if (content.trim().length === 0) {
148
+ addIssue(
149
+ "warning",
150
+ "empty-script",
151
+ `Bundled script skills/${skillName}/${skillFile} is empty`,
152
+ `skills/${skillName}/${skillFile}`,
153
+ );
154
+ }
155
+ } catch (err) {
156
+ addIssue(
157
+ "error",
158
+ "unreadable-script",
159
+ `Cannot read bundled script: ${err.message}`,
160
+ `skills/${skillName}/${skillFile}`,
161
+ );
162
+ }
163
+ }
164
+ }
165
+ }
166
+ }
167
+
168
+ // --- Check 5: arcs-dashboard/package.json type field ---
169
+ const dashboardPkgPath = resolve(bundleRoot, "skills/arcs-dashboard/package.json");
170
+ if (existsSync(dashboardPkgPath)) {
171
+ try {
172
+ const pkg = JSON.parse(readFileSync(dashboardPkgPath, "utf-8"));
173
+ if (!pkg.type) {
174
+ addIssue(
175
+ "error",
176
+ "package-json-invalid",
177
+ `arcs-dashboard package.json missing "type" field (expected "commonjs")`,
178
+ "skills/arcs-dashboard/package.json",
179
+ `Add "type": "commonjs" to the package.json`,
180
+ );
181
+ }
182
+ } catch (err) {
183
+ addIssue(
184
+ "error",
185
+ "package-json-invalid",
186
+ `arcs-dashboard package.json is not valid JSON: ${err.message}`,
187
+ "skills/arcs-dashboard/package.json",
188
+ );
189
+ }
190
+ }
191
+
192
+ // --- Check 6: REMOVED. The repo bundle is the source of truth — there is no
193
+ // external "config root" mirror to compare against. Drift detection against
194
+ // ~/.config/opencode/ has been deleted; use `arcs deploy-superpowers --dry-run`
195
+ // if you want to preview what would change in the deployment target.
196
+
197
+ function output() {
198
+ const errors = issues.filter((i) => i.severity === "error").length;
199
+ const warnings = issues.filter((i) => i.severity === "warning").length;
200
+ const result = { issues, summary: { errors, warnings } };
201
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
202
+ process.exitCode = errors > 0 ? 1 : 0;
203
+ process.exit();
204
+ }
205
+
206
+ output();