@moreih29/nexus-core 0.11.0 → 0.13.0

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 (210) hide show
  1. package/README.md +48 -63
  2. package/assets/agents/architect/body.ko.md +177 -0
  3. package/{agents → assets/agents}/architect/body.md +16 -0
  4. package/assets/agents/designer/body.ko.md +125 -0
  5. package/{agents → assets/agents}/designer/body.md +16 -0
  6. package/assets/agents/engineer/body.ko.md +106 -0
  7. package/{agents → assets/agents}/engineer/body.md +14 -0
  8. package/assets/agents/lead/body.ko.md +70 -0
  9. package/assets/agents/lead/body.md +70 -0
  10. package/assets/agents/postdoc/body.ko.md +122 -0
  11. package/{agents → assets/agents}/postdoc/body.md +16 -0
  12. package/assets/agents/researcher/body.ko.md +137 -0
  13. package/{agents → assets/agents}/researcher/body.md +15 -0
  14. package/assets/agents/reviewer/body.ko.md +138 -0
  15. package/{agents → assets/agents}/reviewer/body.md +15 -0
  16. package/assets/agents/strategist/body.ko.md +116 -0
  17. package/{agents → assets/agents}/strategist/body.md +16 -0
  18. package/assets/agents/tester/body.ko.md +195 -0
  19. package/{agents → assets/agents}/tester/body.md +15 -0
  20. package/assets/agents/writer/body.ko.md +122 -0
  21. package/{agents → assets/agents}/writer/body.md +14 -0
  22. package/assets/capability-matrix.yml +198 -0
  23. package/assets/hooks/agent-bootstrap/handler.test.ts +368 -0
  24. package/assets/hooks/agent-bootstrap/handler.ts +119 -0
  25. package/assets/hooks/agent-bootstrap/meta.yml +10 -0
  26. package/assets/hooks/agent-finalize/handler.test.ts +368 -0
  27. package/assets/hooks/agent-finalize/handler.ts +76 -0
  28. package/assets/hooks/agent-finalize/meta.yml +10 -0
  29. package/assets/hooks/capability-matrix.yml +313 -0
  30. package/assets/hooks/post-tool-telemetry/handler.test.ts +302 -0
  31. package/assets/hooks/post-tool-telemetry/handler.ts +49 -0
  32. package/assets/hooks/post-tool-telemetry/meta.yml +11 -0
  33. package/assets/hooks/prompt-router/handler.test.ts +801 -0
  34. package/assets/hooks/prompt-router/handler.ts +261 -0
  35. package/assets/hooks/prompt-router/meta.yml +11 -0
  36. package/assets/hooks/session-init/handler.test.ts +274 -0
  37. package/assets/hooks/session-init/handler.ts +30 -0
  38. package/assets/hooks/session-init/meta.yml +9 -0
  39. package/assets/lsp-servers.json +55 -0
  40. package/assets/schema/lsp-servers.schema.json +67 -0
  41. package/assets/skills/nx-init/body.ko.md +197 -0
  42. package/{skills → assets/skills}/nx-init/body.md +11 -0
  43. package/assets/skills/nx-plan/body.ko.md +361 -0
  44. package/{skills → assets/skills}/nx-plan/body.md +13 -0
  45. package/assets/skills/nx-run/body.ko.md +161 -0
  46. package/{skills → assets/skills}/nx-run/body.md +11 -0
  47. package/assets/skills/nx-sync/body.ko.md +92 -0
  48. package/{skills → assets/skills}/nx-sync/body.md +10 -0
  49. package/assets/tools/tool-name-map.yml +353 -0
  50. package/dist/hooks/opencode-mount.d.ts +35 -0
  51. package/dist/hooks/opencode-mount.d.ts.map +1 -0
  52. package/dist/hooks/opencode-mount.js +332 -0
  53. package/dist/hooks/opencode-mount.js.map +1 -0
  54. package/dist/hooks/runtime.d.ts +37 -0
  55. package/dist/hooks/runtime.d.ts.map +1 -0
  56. package/dist/hooks/runtime.js +274 -0
  57. package/dist/hooks/runtime.js.map +1 -0
  58. package/dist/hooks/types.d.ts +196 -0
  59. package/dist/hooks/types.d.ts.map +1 -0
  60. package/dist/hooks/types.js +85 -0
  61. package/dist/hooks/types.js.map +1 -0
  62. package/dist/lsp/cache.d.ts +9 -0
  63. package/dist/lsp/cache.d.ts.map +1 -0
  64. package/dist/lsp/cache.js +216 -0
  65. package/dist/lsp/cache.js.map +1 -0
  66. package/dist/lsp/client.d.ts +24 -0
  67. package/dist/lsp/client.d.ts.map +1 -0
  68. package/dist/lsp/client.js +166 -0
  69. package/dist/lsp/client.js.map +1 -0
  70. package/dist/lsp/detect.d.ts +77 -0
  71. package/dist/lsp/detect.d.ts.map +1 -0
  72. package/dist/lsp/detect.js +116 -0
  73. package/dist/lsp/detect.js.map +1 -0
  74. package/dist/mcp/server.d.ts +5 -0
  75. package/dist/mcp/server.d.ts.map +1 -0
  76. package/dist/mcp/server.js +34 -0
  77. package/dist/mcp/server.js.map +1 -0
  78. package/dist/mcp/tools/artifact.d.ts +4 -0
  79. package/dist/mcp/tools/artifact.d.ts.map +1 -0
  80. package/dist/mcp/tools/artifact.js +36 -0
  81. package/dist/mcp/tools/artifact.js.map +1 -0
  82. package/dist/mcp/tools/history.d.ts +3 -0
  83. package/dist/mcp/tools/history.d.ts.map +1 -0
  84. package/dist/mcp/tools/history.js +29 -0
  85. package/dist/mcp/tools/history.js.map +1 -0
  86. package/dist/mcp/tools/lsp.d.ts +13 -0
  87. package/dist/mcp/tools/lsp.d.ts.map +1 -0
  88. package/dist/mcp/tools/lsp.js +225 -0
  89. package/dist/mcp/tools/lsp.js.map +1 -0
  90. package/dist/mcp/tools/plan.d.ts +3 -0
  91. package/dist/mcp/tools/plan.d.ts.map +1 -0
  92. package/dist/mcp/tools/plan.js +317 -0
  93. package/dist/mcp/tools/plan.js.map +1 -0
  94. package/dist/mcp/tools/task.d.ts +3 -0
  95. package/dist/mcp/tools/task.d.ts.map +1 -0
  96. package/dist/mcp/tools/task.js +252 -0
  97. package/dist/mcp/tools/task.js.map +1 -0
  98. package/dist/shared/invocations.d.ts +74 -0
  99. package/dist/shared/invocations.d.ts.map +1 -0
  100. package/dist/shared/invocations.js +247 -0
  101. package/dist/shared/invocations.js.map +1 -0
  102. package/dist/shared/json-store.d.ts +37 -0
  103. package/dist/shared/json-store.d.ts.map +1 -0
  104. package/dist/shared/json-store.js +163 -0
  105. package/dist/shared/json-store.js.map +1 -0
  106. package/dist/shared/mcp-utils.d.ts +3 -0
  107. package/dist/shared/mcp-utils.d.ts.map +1 -0
  108. package/dist/shared/mcp-utils.js +6 -0
  109. package/dist/shared/mcp-utils.js.map +1 -0
  110. package/dist/shared/paths.d.ts +21 -0
  111. package/dist/shared/paths.d.ts.map +1 -0
  112. package/dist/shared/paths.js +81 -0
  113. package/dist/shared/paths.js.map +1 -0
  114. package/dist/shared/tool-log.d.ts +8 -0
  115. package/dist/shared/tool-log.d.ts.map +1 -0
  116. package/dist/shared/tool-log.js +22 -0
  117. package/dist/shared/tool-log.js.map +1 -0
  118. package/dist/types/state.d.ts +862 -0
  119. package/dist/types/state.d.ts.map +1 -0
  120. package/dist/types/state.js +66 -0
  121. package/dist/types/state.js.map +1 -0
  122. package/docs/consuming/codex-lead-merge.md +106 -0
  123. package/docs/plugin-guide.md +360 -0
  124. package/docs/plugin-template/claude/.github/workflows/build.yml +60 -0
  125. package/docs/plugin-template/claude/README.md +110 -0
  126. package/docs/plugin-template/claude/package.json +16 -0
  127. package/docs/plugin-template/codex/.github/workflows/build.yml +51 -0
  128. package/docs/plugin-template/codex/README.md +147 -0
  129. package/docs/plugin-template/codex/package.json +17 -0
  130. package/docs/plugin-template/opencode/.github/workflows/build.yml +61 -0
  131. package/docs/plugin-template/opencode/README.md +121 -0
  132. package/docs/plugin-template/opencode/package.json +25 -0
  133. package/package.json +21 -21
  134. package/scripts/build-agents.test.ts +1279 -0
  135. package/scripts/build-agents.ts +978 -0
  136. package/scripts/build-hooks.test.ts +1385 -0
  137. package/scripts/build-hooks.ts +584 -0
  138. package/scripts/cli.test.ts +367 -0
  139. package/scripts/cli.ts +547 -0
  140. package/agents/architect/meta.yml +0 -13
  141. package/agents/designer/meta.yml +0 -13
  142. package/agents/engineer/meta.yml +0 -11
  143. package/agents/postdoc/meta.yml +0 -13
  144. package/agents/researcher/meta.yml +0 -12
  145. package/agents/reviewer/meta.yml +0 -12
  146. package/agents/strategist/meta.yml +0 -13
  147. package/agents/tester/meta.yml +0 -12
  148. package/agents/writer/meta.yml +0 -11
  149. package/conformance/README.md +0 -311
  150. package/conformance/examples/plan.extension.schema.example.json +0 -25
  151. package/conformance/lifecycle/README.md +0 -48
  152. package/conformance/lifecycle/agent-complete.json +0 -44
  153. package/conformance/lifecycle/agent-resume.json +0 -43
  154. package/conformance/lifecycle/agent-spawn.json +0 -36
  155. package/conformance/lifecycle/memory-access-record.json +0 -27
  156. package/conformance/lifecycle/session-end.json +0 -48
  157. package/conformance/scenarios/full-plan-cycle.json +0 -147
  158. package/conformance/scenarios/task-deps-ordering.json +0 -95
  159. package/conformance/schema/fixture.schema.json +0 -354
  160. package/conformance/state-schemas/agent-tracker.schema.json +0 -63
  161. package/conformance/state-schemas/history.schema.json +0 -134
  162. package/conformance/state-schemas/memory-access.schema.json +0 -36
  163. package/conformance/state-schemas/plan.schema.json +0 -77
  164. package/conformance/state-schemas/tasks.schema.json +0 -98
  165. package/conformance/tools/artifact-write.json +0 -97
  166. package/conformance/tools/context.json +0 -172
  167. package/conformance/tools/history-search.json +0 -219
  168. package/conformance/tools/plan-decide.json +0 -139
  169. package/conformance/tools/plan-start.json +0 -81
  170. package/conformance/tools/plan-status.json +0 -127
  171. package/conformance/tools/plan-update.json +0 -341
  172. package/conformance/tools/task-add.json +0 -156
  173. package/conformance/tools/task-close.json +0 -161
  174. package/conformance/tools/task-list.json +0 -177
  175. package/conformance/tools/task-update.json +0 -167
  176. package/docs/behavioral-contracts.md +0 -145
  177. package/docs/consumer-implementation-guide.md +0 -852
  178. package/docs/memory-lifecycle-contract.md +0 -119
  179. package/docs/nexus-layout.md +0 -224
  180. package/docs/nexus-outputs-contract.md +0 -344
  181. package/docs/nexus-state-overview.md +0 -170
  182. package/docs/nexus-tools-contract.md +0 -438
  183. package/manifest.json +0 -449
  184. package/schema/README.md +0 -69
  185. package/schema/agent.schema.json +0 -23
  186. package/schema/common.schema.json +0 -17
  187. package/schema/manifest.schema.json +0 -78
  188. package/schema/memory-policy.schema.json +0 -98
  189. package/schema/skill.schema.json +0 -54
  190. package/schema/task-exceptions.schema.json +0 -40
  191. package/schema/vocabulary.schema.json +0 -167
  192. package/scripts/.gitkeep +0 -0
  193. package/scripts/conformance-coverage.ts +0 -466
  194. package/scripts/import-from-claude-nexus.ts +0 -403
  195. package/scripts/lib/frontmatter.ts +0 -71
  196. package/scripts/lib/lint.ts +0 -348
  197. package/scripts/lib/structure.ts +0 -159
  198. package/scripts/lib/validate.ts +0 -794
  199. package/scripts/validate.ts +0 -90
  200. package/skills/nx-init/meta.yml +0 -8
  201. package/skills/nx-plan/meta.yml +0 -10
  202. package/skills/nx-run/meta.yml +0 -9
  203. package/skills/nx-sync/meta.yml +0 -7
  204. package/vocabulary/capabilities.yml +0 -65
  205. package/vocabulary/categories.yml +0 -11
  206. package/vocabulary/invocations.yml +0 -147
  207. package/vocabulary/memory_policy.yml +0 -88
  208. package/vocabulary/resume-tiers.yml +0 -11
  209. package/vocabulary/tags.yml +0 -60
  210. package/vocabulary/task-exceptions.yml +0 -29
package/scripts/cli.ts ADDED
@@ -0,0 +1,547 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/cli.ts
4
+ *
5
+ * nexus-core CLI — subcommand dispatcher.
6
+ *
7
+ * Usage:
8
+ * nexus-core <command> [flags]
9
+ * bun run scripts/cli.ts <command> [flags]
10
+ *
11
+ * Commands:
12
+ * sync Build agents + hooks (build-agents + build-hooks pipeline)
13
+ * init Copy plugin template to a target directory
14
+ * list List agents, skills, and hooks from assets/
15
+ * validate Validate frontmatter and YAML assets
16
+ * mcp Start the MCP stdio server (same as nexus-mcp)
17
+ *
18
+ * Flags:
19
+ * --help, -h Show help for the current command
20
+ */
21
+
22
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
23
+ import { join, resolve, dirname } from "node:path";
24
+ import { fileURLToPath } from "node:url";
25
+ import { parse as parseYaml } from "yaml";
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Constants
29
+ // ---------------------------------------------------------------------------
30
+
31
+ const __dirname = dirname(fileURLToPath(import.meta.url));
32
+ export const ROOT = resolve(__dirname, "..");
33
+ export const ASSETS_DIR = join(ROOT, "assets");
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Parsed flags helpers
37
+ // ---------------------------------------------------------------------------
38
+
39
+ export interface ParsedFlags {
40
+ harness?: string;
41
+ target?: string;
42
+ dryRun: boolean;
43
+ force: boolean;
44
+ strict: boolean;
45
+ only?: string;
46
+ help: boolean;
47
+ remaining: string[];
48
+ }
49
+
50
+ export function parseFlags(argv: string[]): ParsedFlags {
51
+ const flags: ParsedFlags = {
52
+ dryRun: false,
53
+ force: false,
54
+ strict: false,
55
+ help: false,
56
+ remaining: [],
57
+ };
58
+
59
+ for (const arg of argv) {
60
+ if (arg === "--help" || arg === "-h") {
61
+ flags.help = true;
62
+ } else if (arg.startsWith("--harness=")) {
63
+ flags.harness = arg.slice("--harness=".length);
64
+ } else if (arg.startsWith("--target=")) {
65
+ flags.target = resolve(arg.slice("--target=".length));
66
+ } else if (arg === "--dry-run") {
67
+ flags.dryRun = true;
68
+ } else if (arg === "--force") {
69
+ flags.force = true;
70
+ } else if (arg === "--strict") {
71
+ flags.strict = true;
72
+ } else if (arg.startsWith("--only=")) {
73
+ flags.only = arg.slice("--only=".length);
74
+ } else {
75
+ flags.remaining.push(arg);
76
+ }
77
+ }
78
+
79
+ return flags;
80
+ }
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Help messages
84
+ // ---------------------------------------------------------------------------
85
+
86
+ const HELP_MAIN = `
87
+ nexus-core — Nexus ecosystem CLI
88
+
89
+ Usage:
90
+ nexus-core <command> [flags]
91
+
92
+ Commands:
93
+ sync Build agents + hooks (runs build-agents + build-hooks pipelines)
94
+ init Copy plugin template to a target directory
95
+ list List agents, skills, and hooks from assets/
96
+ validate Validate frontmatter and YAML assets
97
+ mcp Start the MCP stdio server (same as nexus-mcp)
98
+
99
+ Flags:
100
+ --help, -h Show help for the current command
101
+
102
+ Run \`nexus-core <command> --help\` for command-specific flags.
103
+ `.trim();
104
+
105
+ const HELP_SYNC = `
106
+ nexus-core sync — Build agents + hooks
107
+
108
+ Usage:
109
+ nexus-core sync [flags]
110
+
111
+ Flags:
112
+ --harness=<claude|opencode|codex> Restrict to one harness (default: all)
113
+ --target=<dir> Output directory (default: dist/)
114
+ --dry-run Print affected files, no writes
115
+ --force Force template file overwrite
116
+ --strict Error if managed output has untracked modifications
117
+ --only=<name> Restrict to a single agent or skill name
118
+ `.trim();
119
+
120
+ const HELP_INIT = `
121
+ nexus-core init — Copy plugin template to target directory
122
+
123
+ Usage:
124
+ nexus-core init [flags]
125
+
126
+ Flags:
127
+ --harness=<claude|opencode|codex> Harness template to copy (default: claude)
128
+ --target=<dir> Destination directory (required)
129
+ `.trim();
130
+
131
+ const HELP_LIST = `
132
+ nexus-core list — List assets from assets/
133
+
134
+ Usage:
135
+ nexus-core list [flags]
136
+
137
+ Flags:
138
+ (none currently)
139
+
140
+ Prints agents, skills, and hooks with their descriptions.
141
+ `.trim();
142
+
143
+ const HELP_VALIDATE = `
144
+ nexus-core validate — Validate frontmatter and YAML assets
145
+
146
+ Usage:
147
+ nexus-core validate [flags]
148
+
149
+ Flags:
150
+ (none currently)
151
+
152
+ Validates:
153
+ - assets/agents/*/body.md frontmatter (required fields: id, name, category, model_tier)
154
+ - assets/skills/*/body.md frontmatter (required fields: id, name)
155
+ - assets/capability-matrix.yml YAML parse
156
+ - assets/tools/tool-name-map.yml YAML parse
157
+ `.trim();
158
+
159
+ const HELP_MCP = `
160
+ nexus-core mcp — Start the MCP stdio server
161
+
162
+ Usage:
163
+ nexus-core mcp
164
+
165
+ Starts the nexus-core MCP server on stdio. Equivalent to running nexus-mcp directly.
166
+ `.trim();
167
+
168
+ // ---------------------------------------------------------------------------
169
+ // Subcommand: sync
170
+ // ---------------------------------------------------------------------------
171
+
172
+ export async function runSync(argv: string[]): Promise<void> {
173
+ const flags = parseFlags(argv);
174
+
175
+ if (flags.help) {
176
+ console.log(HELP_SYNC);
177
+ return;
178
+ }
179
+
180
+ // Build argv for build-agents: reconstruct flags as process.argv-style
181
+ // build-agents.parseArgs expects process.argv (slices [2:])
182
+ const fakeArgv = ["node", "build-agents.ts"];
183
+ if (flags.harness) fakeArgv.push(`--harness=${flags.harness}`);
184
+ if (flags.target) fakeArgv.push(`--target=${flags.target}`);
185
+ if (flags.dryRun) fakeArgv.push("--dry-run");
186
+ if (flags.force) fakeArgv.push("--force");
187
+ if (flags.strict) fakeArgv.push("--strict");
188
+ if (flags.only) fakeArgv.push(`--only=${flags.only}`);
189
+
190
+ const { buildAgents, parseArgs: parseAgentArgs } = await import("./build-agents.js");
191
+
192
+ const agentOpts = parseAgentArgs(fakeArgv);
193
+ await buildAgents(agentOpts);
194
+
195
+ if (flags.dryRun) {
196
+ console.log(`[nexus-core sync] --dry-run: skipping buildHooks (no writes)`);
197
+ return;
198
+ }
199
+
200
+ const { buildHooks } = await import("./build-hooks.js");
201
+ await buildHooks();
202
+ }
203
+
204
+ // ---------------------------------------------------------------------------
205
+ // Subcommand: init
206
+ // ---------------------------------------------------------------------------
207
+
208
+ export async function runInit(argv: string[]): Promise<void> {
209
+ const flags = parseFlags(argv);
210
+
211
+ if (flags.help) {
212
+ console.log(HELP_INIT);
213
+ return;
214
+ }
215
+
216
+ const harness = flags.harness ?? "claude";
217
+ const target = flags.target;
218
+
219
+ if (!target) {
220
+ process.stderr.write(`[nexus-core init] --target=<dir> is required\n`);
221
+ process.exit(1);
222
+ }
223
+
224
+ const templateDir = join(ROOT, "docs", "plugin-template", harness);
225
+
226
+ if (!existsSync(templateDir)) {
227
+ process.stderr.write(
228
+ `[nexus-core init] Template not yet available for harness "${harness}".\n` +
229
+ ` Expected path: ${templateDir}\n` +
230
+ ` Templates are generated by the T5 phase. Use "nexus-core sync" to build dist/ first.\n`,
231
+ );
232
+ process.exit(1);
233
+ }
234
+
235
+ // Copy template directory to target using recursive copy
236
+ const { cpSync } = await import("node:fs");
237
+ cpSync(templateDir, target, { recursive: true });
238
+ console.log(`[nexus-core init] Initialized ${harness} plugin template → ${target}`);
239
+ }
240
+
241
+ // ---------------------------------------------------------------------------
242
+ // Frontmatter parsing (lightweight, no dependency on build-agents)
243
+ // ---------------------------------------------------------------------------
244
+
245
+ const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
246
+
247
+ function parseFrontmatterRaw(raw: string): Record<string, unknown> | null {
248
+ const match = FRONTMATTER_RE.exec(raw);
249
+ if (!match) return null;
250
+ try {
251
+ return parseYaml(match[1]!) as Record<string, unknown>;
252
+ } catch {
253
+ return null;
254
+ }
255
+ }
256
+
257
+ // ---------------------------------------------------------------------------
258
+ // Subcommand: list
259
+ // ---------------------------------------------------------------------------
260
+
261
+ interface ListEntry {
262
+ kind: "agent" | "skill" | "hook";
263
+ name: string;
264
+ description: string;
265
+ }
266
+
267
+ export function collectListEntries(): ListEntry[] {
268
+ const entries: ListEntry[] = [];
269
+
270
+ // Agents
271
+ const agentsDir = join(ASSETS_DIR, "agents");
272
+ if (existsSync(agentsDir)) {
273
+ for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
274
+ if (!entry.isDirectory()) continue;
275
+ const bodyPath = join(agentsDir, entry.name, "body.md");
276
+ if (!existsSync(bodyPath)) continue;
277
+ const fm = parseFrontmatterRaw(readFileSync(bodyPath, "utf-8"));
278
+ entries.push({
279
+ kind: "agent",
280
+ name: entry.name,
281
+ description: typeof fm?.description === "string" ? fm.description : "",
282
+ });
283
+ }
284
+ }
285
+
286
+ // Skills
287
+ const skillsDir = join(ASSETS_DIR, "skills");
288
+ if (existsSync(skillsDir)) {
289
+ for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
290
+ if (!entry.isDirectory()) continue;
291
+ const bodyPath = join(skillsDir, entry.name, "body.md");
292
+ if (!existsSync(bodyPath)) continue;
293
+ const fm = parseFrontmatterRaw(readFileSync(bodyPath, "utf-8"));
294
+ entries.push({
295
+ kind: "skill",
296
+ name: entry.name,
297
+ description: typeof fm?.description === "string" ? fm.description : "",
298
+ });
299
+ }
300
+ }
301
+
302
+ // Hooks
303
+ const hooksDir = join(ASSETS_DIR, "hooks");
304
+ if (existsSync(hooksDir)) {
305
+ for (const entry of readdirSync(hooksDir, { withFileTypes: true })) {
306
+ if (!entry.isDirectory()) continue;
307
+ const metaPath = join(hooksDir, entry.name, "meta.yml");
308
+ if (!existsSync(metaPath)) continue;
309
+ let description = "";
310
+ try {
311
+ const meta = parseYaml(readFileSync(metaPath, "utf-8")) as Record<string, unknown>;
312
+ description = typeof meta?.description === "string" ? meta.description : "";
313
+ } catch {
314
+ // ignore parse errors in list
315
+ }
316
+ entries.push({ kind: "hook", name: entry.name, description });
317
+ }
318
+ }
319
+
320
+ return entries;
321
+ }
322
+
323
+ export async function runList(argv: string[]): Promise<void> {
324
+ const flags = parseFlags(argv);
325
+
326
+ if (flags.help) {
327
+ console.log(HELP_LIST);
328
+ return;
329
+ }
330
+
331
+ const entries = collectListEntries();
332
+ const agents = entries.filter((e) => e.kind === "agent");
333
+ const skills = entries.filter((e) => e.kind === "skill");
334
+ const hooks = entries.filter((e) => e.kind === "hook");
335
+
336
+ console.log(`Agents (${agents.length}):`);
337
+ for (const e of agents) {
338
+ console.log(` ${e.name.padEnd(20)} ${e.description}`);
339
+ }
340
+
341
+ console.log(`\nSkills (${skills.length}):`);
342
+ for (const e of skills) {
343
+ console.log(` ${e.name.padEnd(20)} ${e.description}`);
344
+ }
345
+
346
+ console.log(`\nHooks (${hooks.length}):`);
347
+ for (const e of hooks) {
348
+ console.log(` ${e.name.padEnd(20)} ${e.description}`);
349
+ }
350
+ }
351
+
352
+ // ---------------------------------------------------------------------------
353
+ // Subcommand: validate
354
+ // ---------------------------------------------------------------------------
355
+
356
+ export interface ValidationResult {
357
+ ok: boolean;
358
+ errors: string[];
359
+ warnings: string[];
360
+ checked: number;
361
+ }
362
+
363
+ export function runValidateSync(): ValidationResult {
364
+ const errors: string[] = [];
365
+ const warnings: string[] = [];
366
+ let checked = 0;
367
+
368
+ // Validate agents
369
+ const agentsDir = join(ASSETS_DIR, "agents");
370
+ if (existsSync(agentsDir)) {
371
+ for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
372
+ if (!entry.isDirectory()) continue;
373
+ const bodyPath = join(agentsDir, entry.name, "body.md");
374
+ if (!existsSync(bodyPath)) {
375
+ errors.push(`agents/${entry.name}: missing body.md`);
376
+ continue;
377
+ }
378
+ checked++;
379
+ const raw = readFileSync(bodyPath, "utf-8");
380
+ const fm = parseFrontmatterRaw(raw);
381
+ if (!fm) {
382
+ errors.push(`agents/${entry.name}/body.md: missing or malformed frontmatter`);
383
+ continue;
384
+ }
385
+ for (const field of ["id", "name", "category", "model_tier"] as const) {
386
+ if (!fm[field]) {
387
+ errors.push(`agents/${entry.name}/body.md: missing required field "${field}"`);
388
+ }
389
+ }
390
+ }
391
+ }
392
+
393
+ // Validate skills
394
+ const skillsDir = join(ASSETS_DIR, "skills");
395
+ if (existsSync(skillsDir)) {
396
+ for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
397
+ if (!entry.isDirectory()) continue;
398
+ const bodyPath = join(skillsDir, entry.name, "body.md");
399
+ if (!existsSync(bodyPath)) {
400
+ errors.push(`skills/${entry.name}: missing body.md`);
401
+ continue;
402
+ }
403
+ checked++;
404
+ const raw = readFileSync(bodyPath, "utf-8");
405
+ const fm = parseFrontmatterRaw(raw);
406
+ if (!fm) {
407
+ errors.push(`skills/${entry.name}/body.md: missing or malformed frontmatter`);
408
+ continue;
409
+ }
410
+ for (const field of ["id", "name"] as const) {
411
+ if (!fm[field]) {
412
+ errors.push(`skills/${entry.name}/body.md: missing required field "${field}"`);
413
+ }
414
+ }
415
+ }
416
+ }
417
+
418
+ // Validate capability-matrix.yml
419
+ const capMatrixPath = join(ASSETS_DIR, "capability-matrix.yml");
420
+ if (!existsSync(capMatrixPath)) {
421
+ warnings.push(`capability-matrix.yml: not found at ${capMatrixPath}`);
422
+ } else {
423
+ checked++;
424
+ try {
425
+ const parsed = parseYaml(readFileSync(capMatrixPath, "utf-8")) as Record<string, unknown>;
426
+ if (!parsed?.capabilities) {
427
+ errors.push(`capability-matrix.yml: missing 'capabilities' top-level key`);
428
+ }
429
+ } catch (err) {
430
+ errors.push(`capability-matrix.yml: YAML parse error — ${String(err)}`);
431
+ }
432
+ }
433
+
434
+ // Validate tools/tool-name-map.yml
435
+ const toolMapPath = join(ASSETS_DIR, "tools", "tool-name-map.yml");
436
+ if (!existsSync(toolMapPath)) {
437
+ warnings.push(`tool-name-map.yml: not found at ${toolMapPath}`);
438
+ } else {
439
+ checked++;
440
+ try {
441
+ const parsed = parseYaml(readFileSync(toolMapPath, "utf-8")) as Record<string, unknown>;
442
+ if (!parsed?.tools && !parsed?.invocations) {
443
+ warnings.push(`tool-name-map.yml: neither 'tools' nor 'invocations' top-level key found`);
444
+ }
445
+ } catch (err) {
446
+ errors.push(`tool-name-map.yml: YAML parse error — ${String(err)}`);
447
+ }
448
+ }
449
+
450
+ return { ok: errors.length === 0, errors, warnings, checked };
451
+ }
452
+
453
+ export async function runValidate(argv: string[]): Promise<void> {
454
+ const flags = parseFlags(argv);
455
+
456
+ if (flags.help) {
457
+ console.log(HELP_VALIDATE);
458
+ return;
459
+ }
460
+
461
+ const result = runValidateSync();
462
+
463
+ console.log(`[nexus-core validate] Checked ${result.checked} assets`);
464
+
465
+ for (const w of result.warnings) {
466
+ process.stderr.write(` WARN ${w}\n`);
467
+ }
468
+
469
+ if (result.ok) {
470
+ console.log(` OK — no errors found`);
471
+ } else {
472
+ for (const e of result.errors) {
473
+ process.stderr.write(` ERROR ${e}\n`);
474
+ }
475
+ process.stderr.write(`[nexus-core validate] ${result.errors.length} error(s) found\n`);
476
+ process.exit(1);
477
+ }
478
+ }
479
+
480
+ // ---------------------------------------------------------------------------
481
+ // Subcommand: mcp
482
+ // ---------------------------------------------------------------------------
483
+
484
+ export async function runMcp(argv: string[]): Promise<void> {
485
+ const flags = parseFlags(argv);
486
+
487
+ if (flags.help) {
488
+ console.log(HELP_MCP);
489
+ return;
490
+ }
491
+
492
+ // Forward to MCP server main()
493
+ const { main } = await import("../src/mcp/server.js");
494
+ await main();
495
+ }
496
+
497
+ // ---------------------------------------------------------------------------
498
+ // Main dispatcher
499
+ // ---------------------------------------------------------------------------
500
+
501
+ export async function main(argv: string[]): Promise<void> {
502
+ const cmd = argv[0];
503
+ const rest = argv.slice(1);
504
+
505
+ switch (cmd) {
506
+ case "sync":
507
+ return runSync(rest);
508
+
509
+ case "init":
510
+ return runInit(rest);
511
+
512
+ case "list":
513
+ return runList(rest);
514
+
515
+ case "validate":
516
+ return runValidate(rest);
517
+
518
+ case "mcp":
519
+ return runMcp(rest);
520
+
521
+ case "--help":
522
+ case "-h":
523
+ case undefined:
524
+ console.log(HELP_MAIN);
525
+ return;
526
+
527
+ default:
528
+ process.stderr.write(`[nexus-core] Unknown command: ${cmd}\n`);
529
+ process.stderr.write(`Run \`nexus-core --help\` for available commands.\n`);
530
+ process.exit(1);
531
+ }
532
+ }
533
+
534
+ // ---------------------------------------------------------------------------
535
+ // Direct execution
536
+ // ---------------------------------------------------------------------------
537
+
538
+ if (
539
+ import.meta.url === `file://${process.argv[1]}` ||
540
+ process.argv[1]?.endsWith("cli.ts") ||
541
+ process.argv[1]?.endsWith("cli.js")
542
+ ) {
543
+ main(process.argv.slice(2)).catch((err: unknown) => {
544
+ process.stderr.write(`[nexus-core] FATAL: ${String(err)}\n`);
545
+ process.exit(1);
546
+ });
547
+ }
@@ -1,13 +0,0 @@
1
- name: architect
2
- description: Technical design — evaluates How, reviews architecture, advises on
3
- implementation approach
4
- task: Architecture, technical design, code review
5
- alias_ko: 아키텍트
6
- category: how
7
- resume_tier: persistent
8
- model_tier: high
9
- capabilities:
10
- - no_file_edit
11
- - no_task_create
12
- - no_task_update
13
- id: architect
@@ -1,13 +0,0 @@
1
- name: designer
2
- description: UX/UI design — evaluates user experience, interaction patterns, and
3
- how users will experience the product
4
- task: UI/UX design, interaction patterns, user experience
5
- alias_ko: 디자이너
6
- category: how
7
- resume_tier: persistent
8
- model_tier: high
9
- capabilities:
10
- - no_file_edit
11
- - no_task_create
12
- - no_task_update
13
- id: designer
@@ -1,11 +0,0 @@
1
- name: engineer
2
- description: Implementation — writes code, debugs issues, follows specifications
3
- from Lead and architect
4
- task: Code implementation, edits, debugging
5
- alias_ko: 엔지니어
6
- category: do
7
- resume_tier: bounded
8
- model_tier: standard
9
- capabilities:
10
- - no_task_create
11
- id: engineer
@@ -1,13 +0,0 @@
1
- name: postdoc
2
- description: Research methodology and synthesis — designs investigation
3
- approach, evaluates evidence quality, writes synthesis documents
4
- task: Research methodology, evidence synthesis
5
- alias_ko: 포닥
6
- category: how
7
- resume_tier: persistent
8
- model_tier: high
9
- capabilities:
10
- - no_file_edit
11
- - no_task_create
12
- - no_task_update
13
- id: postdoc
@@ -1,12 +0,0 @@
1
- name: researcher
2
- description: Independent investigation — conducts web searches, gathers
3
- evidence, and reports findings with citations
4
- task: Web search, independent investigation
5
- alias_ko: 리서처
6
- category: do
7
- resume_tier: persistent
8
- model_tier: standard
9
- capabilities:
10
- - no_file_edit
11
- - no_task_create
12
- id: researcher
@@ -1,12 +0,0 @@
1
- name: reviewer
2
- description: Content verification — validates accuracy, checks facts, confirms
3
- grammar and format of non-code deliverables
4
- task: Content verification, fact-checking, grammar review
5
- alias_ko: 리뷰어
6
- category: check
7
- resume_tier: ephemeral
8
- model_tier: standard
9
- capabilities:
10
- - no_file_edit
11
- - no_task_create
12
- id: reviewer
@@ -1,13 +0,0 @@
1
- name: strategist
2
- description: Business strategy — evaluates market positioning, competitive
3
- landscape, and business viability of decisions
4
- task: Business strategy, market analysis, competitive positioning
5
- alias_ko: 전략가
6
- category: how
7
- resume_tier: persistent
8
- model_tier: high
9
- capabilities:
10
- - no_file_edit
11
- - no_task_create
12
- - no_task_update
13
- id: strategist
@@ -1,12 +0,0 @@
1
- name: tester
2
- description: Testing and verification — tests, verifies, validates stability and
3
- security of implementations
4
- task: Testing, verification, security review
5
- alias_ko: 테스터
6
- category: check
7
- resume_tier: ephemeral
8
- model_tier: standard
9
- capabilities:
10
- - no_file_edit
11
- - no_task_create
12
- id: tester
@@ -1,11 +0,0 @@
1
- name: writer
2
- description: Technical writing — transforms research findings, code, and
3
- analysis into clear documents and presentations for the intended audience
4
- task: Technical writing, documentation, presentations
5
- alias_ko: 라이터
6
- category: do
7
- resume_tier: bounded
8
- model_tier: standard
9
- capabilities:
10
- - no_task_create
11
- id: writer