swarmkit 0.0.1 → 0.0.3

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 (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -1
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +33 -0
  5. package/dist/commands/add.d.ts +2 -0
  6. package/dist/commands/add.js +98 -0
  7. package/dist/commands/doctor.d.ts +2 -0
  8. package/dist/commands/doctor.js +100 -0
  9. package/dist/commands/hive.d.ts +2 -0
  10. package/dist/commands/hive.js +248 -0
  11. package/dist/commands/init/phases/configure.d.ts +2 -0
  12. package/dist/commands/init/phases/configure.js +85 -0
  13. package/dist/commands/init/phases/global-setup.d.ts +2 -0
  14. package/dist/commands/init/phases/global-setup.js +81 -0
  15. package/dist/commands/init/phases/packages.d.ts +2 -0
  16. package/dist/commands/init/phases/packages.js +30 -0
  17. package/dist/commands/init/phases/project.d.ts +2 -0
  18. package/dist/commands/init/phases/project.js +56 -0
  19. package/dist/commands/init/phases/use-case.d.ts +2 -0
  20. package/dist/commands/init/phases/use-case.js +41 -0
  21. package/dist/commands/init/state.d.ts +13 -0
  22. package/dist/commands/init/state.js +9 -0
  23. package/dist/commands/init/state.test.d.ts +1 -0
  24. package/dist/commands/init/state.test.js +21 -0
  25. package/dist/commands/init/wizard.d.ts +4 -0
  26. package/dist/commands/init/wizard.js +108 -0
  27. package/dist/commands/init.d.ts +2 -0
  28. package/dist/commands/init.js +11 -0
  29. package/dist/commands/login.d.ts +2 -0
  30. package/dist/commands/login.js +91 -0
  31. package/dist/commands/logout.d.ts +2 -0
  32. package/dist/commands/logout.js +19 -0
  33. package/dist/commands/remove.d.ts +2 -0
  34. package/dist/commands/remove.js +55 -0
  35. package/dist/commands/status.d.ts +2 -0
  36. package/dist/commands/status.js +87 -0
  37. package/dist/commands/update.d.ts +2 -0
  38. package/dist/commands/update.js +54 -0
  39. package/dist/commands/whoami.d.ts +2 -0
  40. package/dist/commands/whoami.js +40 -0
  41. package/dist/config/global.d.ts +26 -0
  42. package/dist/config/global.js +71 -0
  43. package/dist/config/global.test.d.ts +1 -0
  44. package/dist/config/global.test.js +167 -0
  45. package/dist/config/keys.d.ts +10 -0
  46. package/dist/config/keys.js +47 -0
  47. package/dist/config/keys.test.d.ts +1 -0
  48. package/dist/config/keys.test.js +87 -0
  49. package/dist/doctor/checks.d.ts +31 -0
  50. package/dist/doctor/checks.js +226 -0
  51. package/dist/doctor/checks.test.d.ts +1 -0
  52. package/dist/doctor/checks.test.js +301 -0
  53. package/dist/doctor/types.d.ts +29 -0
  54. package/dist/doctor/types.js +1 -0
  55. package/dist/hub/auth-flow.d.ts +16 -0
  56. package/dist/hub/auth-flow.js +118 -0
  57. package/dist/hub/auth-flow.test.d.ts +1 -0
  58. package/dist/hub/auth-flow.test.js +98 -0
  59. package/dist/hub/client.d.ts +51 -0
  60. package/dist/hub/client.js +107 -0
  61. package/dist/hub/client.test.d.ts +1 -0
  62. package/dist/hub/client.test.js +177 -0
  63. package/dist/hub/credentials.d.ts +14 -0
  64. package/dist/hub/credentials.js +41 -0
  65. package/dist/hub/credentials.test.d.ts +1 -0
  66. package/dist/hub/credentials.test.js +102 -0
  67. package/dist/index.d.ts +17 -1
  68. package/dist/index.js +10 -2
  69. package/dist/packages/installer.d.ts +42 -0
  70. package/dist/packages/installer.js +158 -0
  71. package/dist/packages/installer.test.d.ts +1 -0
  72. package/dist/packages/installer.test.js +283 -0
  73. package/dist/packages/plugin.d.ts +13 -0
  74. package/dist/packages/plugin.js +33 -0
  75. package/dist/packages/plugin.test.d.ts +1 -0
  76. package/dist/packages/plugin.test.js +99 -0
  77. package/dist/packages/registry.d.ts +37 -0
  78. package/dist/packages/registry.js +154 -0
  79. package/dist/packages/registry.test.d.ts +1 -0
  80. package/dist/packages/registry.test.js +188 -0
  81. package/dist/packages/setup.d.ts +55 -0
  82. package/dist/packages/setup.js +414 -0
  83. package/dist/packages/setup.test.d.ts +1 -0
  84. package/dist/packages/setup.test.js +808 -0
  85. package/dist/utils/ui.d.ts +10 -0
  86. package/dist/utils/ui.js +47 -0
  87. package/dist/utils/ui.test.d.ts +1 -0
  88. package/dist/utils/ui.test.js +102 -0
  89. package/package.json +29 -6
@@ -0,0 +1,414 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, symlinkSync, writeFileSync, } from "node:fs";
4
+ import { basename, join } from "node:path";
5
+ import { homedir } from "node:os";
6
+ const execFileAsync = promisify(execFile);
7
+ // ─── Project-level setup ─────────────────────────────────────────────────────
8
+ /** Root directory for all swarmkit project-level config */
9
+ export const PROJECT_ROOT = ".swarm";
10
+ /** Config directories with .swarm/ prefix (default layout) */
11
+ export const PROJECT_CONFIG_DIRS = {
12
+ opentasks: ".swarm/opentasks",
13
+ minimem: ".swarm/minimem",
14
+ "cognitive-core": ".swarm/cognitive-core",
15
+ "skill-tree": ".swarm/skilltree",
16
+ "self-driving-repo": ".swarm/self-driving",
17
+ openteams: ".swarm/openteams",
18
+ sessionlog: ".swarm/sessionlog",
19
+ "claude-code-swarm": ".swarm/claude-swarm",
20
+ };
21
+ /** Config directories without prefix (flat layout, --no-prefix) */
22
+ export const FLAT_PROJECT_CONFIG_DIRS = {
23
+ opentasks: ".opentasks",
24
+ minimem: ".minimem",
25
+ "cognitive-core": ".cognitive-core",
26
+ "skill-tree": ".skilltree",
27
+ "self-driving-repo": ".self-driving",
28
+ openteams: ".openteams",
29
+ sessionlog: ".sessionlog",
30
+ "claude-code-swarm": ".claude-swarm",
31
+ };
32
+ /** Get config dirs for the given mode */
33
+ export function projectConfigDirs(usePrefix) {
34
+ return usePrefix ? PROJECT_CONFIG_DIRS : FLAT_PROJECT_CONFIG_DIRS;
35
+ }
36
+ /**
37
+ * Project-level packages in correct init order.
38
+ * Order matters: minimem before cognitive-core (runtime detection).
39
+ */
40
+ export const PROJECT_INIT_ORDER = [
41
+ "opentasks",
42
+ "minimem",
43
+ "cognitive-core",
44
+ "skill-tree",
45
+ "self-driving-repo",
46
+ "openteams",
47
+ "sessionlog",
48
+ "claude-code-swarm",
49
+ ];
50
+ /** Check whether a package is already initialized in a project (checks both layouts) */
51
+ export function isProjectInit(cwd, pkg) {
52
+ const prefixed = PROJECT_CONFIG_DIRS[pkg];
53
+ const flat = FLAT_PROJECT_CONFIG_DIRS[pkg];
54
+ return (prefixed ? existsSync(join(cwd, prefixed)) : false) ||
55
+ (flat ? existsSync(join(cwd, flat)) : false);
56
+ }
57
+ /** Ensure the .swarm/ project root directory exists */
58
+ function ensureProjectRoot(cwd) {
59
+ const root = join(cwd, PROJECT_ROOT);
60
+ if (!existsSync(root)) {
61
+ mkdirSync(root, { recursive: true });
62
+ }
63
+ }
64
+ /**
65
+ * After init, relocate a package's config dir into .swarm/ if the CLI
66
+ * created it at the legacy top-level location, then leave a symlink at
67
+ * the old path so the package can still find its data at runtime.
68
+ * This is a no-op when the package already respects its env-var override.
69
+ */
70
+ function relocate(cwd, legacyName, targetName) {
71
+ const src = join(cwd, legacyName);
72
+ const dest = join(cwd, PROJECT_ROOT, targetName);
73
+ // Move the directory if it was created at the legacy location
74
+ if (existsSync(src) && !isSymlink(src) && !existsSync(dest)) {
75
+ try {
76
+ renameSync(src, dest);
77
+ }
78
+ catch {
79
+ return; // Non-fatal — leave in legacy location
80
+ }
81
+ }
82
+ // Create a symlink at the legacy location so packages find their data
83
+ ensureSymlink(cwd, legacyName, targetName);
84
+ }
85
+ /** Create a relative symlink: cwd/<legacyName> → .swarm/<targetName> */
86
+ function ensureSymlink(cwd, legacyName, targetName) {
87
+ const link = join(cwd, legacyName);
88
+ const target = join(PROJECT_ROOT, targetName); // relative path
89
+ if (isSymlink(link))
90
+ return; // Already symlinked
91
+ if (existsSync(link))
92
+ return; // Real directory exists — don't overwrite
93
+ try {
94
+ symlinkSync(target, link);
95
+ }
96
+ catch {
97
+ // Non-fatal — package can still be configured via env var
98
+ }
99
+ }
100
+ function isSymlink(path) {
101
+ try {
102
+ return lstatSync(path).isSymbolicLink();
103
+ }
104
+ catch {
105
+ return false;
106
+ }
107
+ }
108
+ /** Initialize a single project-level package */
109
+ export async function initProjectPackage(pkg, ctx) {
110
+ if (ctx.usePrefix) {
111
+ ensureProjectRoot(ctx.cwd);
112
+ }
113
+ switch (pkg) {
114
+ case "opentasks": {
115
+ const result = await shellInit("opentasks", ["init", "--name", getProjectName(ctx.cwd)], ctx.cwd, ctx.usePrefix
116
+ ? { OPENTASKS_PROJECT_DIR: join(PROJECT_ROOT, "opentasks") }
117
+ : undefined);
118
+ // relocate handles packages that don't yet respect the env var;
119
+ // for packages that create directly in .swarm/, this is a no-op.
120
+ if (ctx.usePrefix)
121
+ relocate(ctx.cwd, ".opentasks", "opentasks");
122
+ return result;
123
+ }
124
+ case "minimem":
125
+ return initMinimem(ctx);
126
+ case "cognitive-core": {
127
+ const result = await shellInit("cognitive-core", ["init"], ctx.cwd, ctx.usePrefix
128
+ ? { COGNITIVE_CORE_HOME: join(ctx.cwd, PROJECT_ROOT, "cognitive-core") }
129
+ : undefined);
130
+ if (ctx.usePrefix)
131
+ relocate(ctx.cwd, ".cognitive-core", "cognitive-core");
132
+ return result;
133
+ }
134
+ case "skill-tree":
135
+ return initSkillTreeProject(ctx);
136
+ case "self-driving-repo":
137
+ return initSdr(ctx);
138
+ case "openteams":
139
+ return initOpenteamsProject(ctx);
140
+ case "sessionlog":
141
+ return initSessionlogProject(ctx);
142
+ case "claude-code-swarm":
143
+ return initClaudeSwarmProject(ctx);
144
+ default:
145
+ return { package: pkg, success: false, message: "Unknown package" };
146
+ }
147
+ }
148
+ // ─── Global-level setup ──────────────────────────────────────────────────────
149
+ /** Config directories for global packages (relative to homedir) */
150
+ export const GLOBAL_CONFIG_DIRS = {
151
+ "skill-tree": ".skill-tree",
152
+ openhive: ".openhive",
153
+ };
154
+ /** Check whether a global package is already configured */
155
+ export function isGlobalInit(pkg) {
156
+ const dir = GLOBAL_CONFIG_DIRS[pkg];
157
+ if (!dir)
158
+ return false;
159
+ return existsSync(join(homedir(), dir));
160
+ }
161
+ /** Initialize a single global package */
162
+ export async function initGlobalPackage(pkg, ctx, openhiveOpts) {
163
+ switch (pkg) {
164
+ case "skill-tree":
165
+ return initSkillTree();
166
+ case "openhive":
167
+ return openhiveOpts
168
+ ? initOpenhive(openhiveOpts)
169
+ : {
170
+ package: "openhive",
171
+ success: false,
172
+ message: "No openhive options provided",
173
+ };
174
+ case "openteams":
175
+ return { package: "openteams", success: true, message: "no setup required" };
176
+ case "sessionlog":
177
+ return { package: "sessionlog", success: true, message: "no setup required" };
178
+ case "claude-code-swarm":
179
+ return { package: "claude-code-swarm", success: true, message: "no setup required" };
180
+ default:
181
+ return { package: pkg, success: false, message: "Unknown package" };
182
+ }
183
+ }
184
+ // ─── Package-specific init functions ─────────────────────────────────────────
185
+ /** Build a clean env for child processes (strips test-runner vars like VITEST) */
186
+ function cleanEnv() {
187
+ const env = { ...process.env };
188
+ delete env.VITEST;
189
+ delete env.VITEST_POOL_ID;
190
+ delete env.VITEST_WORKER_ID;
191
+ return env;
192
+ }
193
+ /** Shell out to a package's CLI init command */
194
+ async function shellInit(command, args, cwd, envOverrides) {
195
+ const pkg = command === "sdr" ? "self-driving-repo" : command;
196
+ try {
197
+ const env = envOverrides
198
+ ? { ...cleanEnv(), ...envOverrides }
199
+ : cleanEnv();
200
+ await execFileAsync(command, args, { cwd, timeout: 30_000, env });
201
+ return { package: pkg, success: true };
202
+ }
203
+ catch (err) {
204
+ return { package: pkg, success: false, message: formatError(err) };
205
+ }
206
+ }
207
+ async function initMinimem(ctx) {
208
+ const targetDir = ctx.usePrefix
209
+ ? join(ctx.cwd, PROJECT_ROOT, "minimem")
210
+ : join(ctx.cwd, ".minimem");
211
+ try {
212
+ await execFileAsync("minimem", ["init"], {
213
+ cwd: ctx.cwd,
214
+ timeout: 30_000,
215
+ env: ctx.usePrefix
216
+ ? { ...cleanEnv(), MINIMEM_CONFIG_DIR: join(PROJECT_ROOT, "minimem") }
217
+ : cleanEnv(),
218
+ });
219
+ // relocate handles packages that don't yet respect the env var;
220
+ // for packages that create directly in .swarm/, this is a no-op.
221
+ if (ctx.usePrefix) {
222
+ relocate(ctx.cwd, ".minimem", "minimem");
223
+ }
224
+ // Patch embedding provider if configured
225
+ if (ctx.embeddingProvider && ctx.embeddingProvider !== "local") {
226
+ const configPath = join(targetDir, "config.json");
227
+ if (existsSync(configPath)) {
228
+ try {
229
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
230
+ config.embedding = config.embedding || {};
231
+ config.embedding.provider = ctx.embeddingProvider;
232
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
233
+ }
234
+ catch {
235
+ // Non-fatal — user can configure manually
236
+ }
237
+ }
238
+ }
239
+ return { package: "minimem", success: true };
240
+ }
241
+ catch (err) {
242
+ return { package: "minimem", success: false, message: formatError(err) };
243
+ }
244
+ }
245
+ async function initSdr(ctx) {
246
+ const result = await shellInit("sdr", ["init", "-t", "triage-only"], ctx.cwd, ctx.usePrefix
247
+ ? { SDR_CONFIG_DIR: join(PROJECT_ROOT, "self-driving") }
248
+ : undefined);
249
+ // relocate handles packages that don't yet respect the env var;
250
+ // for packages that create directly in .swarm/, this is a no-op.
251
+ if (ctx.usePrefix) {
252
+ relocate(ctx.cwd, ".self-driving", "self-driving");
253
+ }
254
+ return result;
255
+ }
256
+ async function initSkillTreeProject(ctx) {
257
+ try {
258
+ const targetDir = ctx.usePrefix
259
+ ? join(ctx.cwd, PROJECT_ROOT, "skilltree")
260
+ : join(ctx.cwd, ".skilltree");
261
+ mkdirSync(targetDir, { recursive: true });
262
+ mkdirSync(join(targetDir, "skills"), { recursive: true });
263
+ return { package: "skill-tree", success: true };
264
+ }
265
+ catch (err) {
266
+ return {
267
+ package: "skill-tree",
268
+ success: false,
269
+ message: formatError(err),
270
+ };
271
+ }
272
+ }
273
+ async function initOpenteamsProject(ctx) {
274
+ try {
275
+ const targetDir = ctx.usePrefix
276
+ ? join(ctx.cwd, PROJECT_ROOT, "openteams")
277
+ : join(ctx.cwd, ".openteams");
278
+ mkdirSync(targetDir, { recursive: true });
279
+ // Write default config.json (matches `openteams template init` with no options)
280
+ const configPath = join(targetDir, "config.json");
281
+ if (!existsSync(configPath)) {
282
+ writeFileSync(configPath, JSON.stringify({}, null, 2) + "\n");
283
+ }
284
+ return { package: "openteams", success: true };
285
+ }
286
+ catch (err) {
287
+ return {
288
+ package: "openteams",
289
+ success: false,
290
+ message: formatError(err),
291
+ };
292
+ }
293
+ }
294
+ async function initSessionlogProject(ctx) {
295
+ try {
296
+ const targetDir = ctx.usePrefix
297
+ ? join(ctx.cwd, PROJECT_ROOT, "sessionlog")
298
+ : join(ctx.cwd, ".sessionlog");
299
+ mkdirSync(targetDir, { recursive: true });
300
+ // Write default settings.json
301
+ const settingsPath = join(targetDir, "settings.json");
302
+ if (!existsSync(settingsPath)) {
303
+ writeFileSync(settingsPath, JSON.stringify({ enabled: false, strategy: "manual-commit" }, null, 2) + "\n");
304
+ }
305
+ return { package: "sessionlog", success: true };
306
+ }
307
+ catch (err) {
308
+ return {
309
+ package: "sessionlog",
310
+ success: false,
311
+ message: formatError(err),
312
+ };
313
+ }
314
+ }
315
+ async function initSkillTree() {
316
+ const configPath = join(homedir(), ".skill-tree", "config.yaml");
317
+ if (existsSync(configPath)) {
318
+ return {
319
+ package: "skill-tree",
320
+ success: true,
321
+ message: "already configured",
322
+ };
323
+ }
324
+ try {
325
+ const env = { ...cleanEnv(), HOME: homedir() };
326
+ await execFileAsync("skill-tree", ["config", "init"], {
327
+ timeout: 15_000,
328
+ env,
329
+ });
330
+ return { package: "skill-tree", success: true };
331
+ }
332
+ catch (err) {
333
+ return {
334
+ package: "skill-tree",
335
+ success: false,
336
+ message: formatError(err),
337
+ };
338
+ }
339
+ }
340
+ async function initOpenhive(opts) {
341
+ try {
342
+ const args = [
343
+ "init",
344
+ "--name",
345
+ opts.name,
346
+ "--port",
347
+ String(opts.port),
348
+ "--auth-mode",
349
+ opts.authMode,
350
+ "--verification",
351
+ opts.verification,
352
+ ];
353
+ if (opts.dataDir) {
354
+ args.unshift("--data-dir", opts.dataDir);
355
+ }
356
+ const env = { ...cleanEnv(), HOME: homedir() };
357
+ await execFileAsync("openhive", args, { timeout: 60_000, env });
358
+ return { package: "openhive", success: true };
359
+ }
360
+ catch (err) {
361
+ return {
362
+ package: "openhive",
363
+ success: false,
364
+ message: formatError(err),
365
+ };
366
+ }
367
+ }
368
+ async function initClaudeSwarmProject(ctx) {
369
+ try {
370
+ const targetDir = ctx.usePrefix
371
+ ? join(ctx.cwd, PROJECT_ROOT, "claude-swarm")
372
+ : join(ctx.cwd, ".claude-swarm");
373
+ mkdirSync(targetDir, { recursive: true });
374
+ // Write default config.json
375
+ const configPath = join(targetDir, "config.json");
376
+ if (!existsSync(configPath)) {
377
+ writeFileSync(configPath, JSON.stringify({}, null, 2) + "\n");
378
+ }
379
+ // Write .gitignore for tmp/
380
+ const gitignorePath = join(targetDir, ".gitignore");
381
+ if (!existsSync(gitignorePath)) {
382
+ writeFileSync(gitignorePath, "tmp/\n");
383
+ }
384
+ return { package: "claude-code-swarm", success: true };
385
+ }
386
+ catch (err) {
387
+ return {
388
+ package: "claude-code-swarm",
389
+ success: false,
390
+ message: formatError(err),
391
+ };
392
+ }
393
+ }
394
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
395
+ function getProjectName(cwd) {
396
+ const pkgPath = join(cwd, "package.json");
397
+ if (existsSync(pkgPath)) {
398
+ try {
399
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
400
+ if (typeof pkg.name === "string" && pkg.name)
401
+ return pkg.name;
402
+ }
403
+ catch {
404
+ // Fall through
405
+ }
406
+ }
407
+ return basename(cwd) || "project";
408
+ }
409
+ function formatError(err) {
410
+ if (err instanceof Error) {
411
+ return err.message.split("\n")[0];
412
+ }
413
+ return String(err);
414
+ }
@@ -0,0 +1 @@
1
+ export {};