swarmkit 0.0.6 → 0.0.7
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.
- package/dist/cli.js +2 -0
- package/dist/commands/configure/configure.d.ts +5 -0
- package/dist/commands/configure/configure.js +354 -0
- package/dist/commands/configure/configure.test.d.ts +1 -0
- package/dist/commands/configure/configure.test.js +539 -0
- package/dist/commands/configure/read-config.d.ts +12 -0
- package/dist/commands/configure/read-config.js +81 -0
- package/dist/commands/configure.d.ts +2 -0
- package/dist/commands/configure.js +14 -0
- package/dist/commands/init/phases/configure.js +0 -21
- package/dist/commands/init/phases/global-setup.d.ts +1 -1
- package/dist/commands/init/phases/global-setup.js +22 -44
- package/dist/commands/init/phases/integrations.d.ts +16 -0
- package/dist/commands/init/phases/integrations.js +172 -0
- package/dist/commands/init/phases/package-config.d.ts +10 -0
- package/dist/commands/init/phases/package-config.js +117 -0
- package/dist/commands/init/phases/phases.test.d.ts +1 -0
- package/dist/commands/init/phases/phases.test.js +711 -0
- package/dist/commands/init/phases/project.js +17 -0
- package/dist/commands/init/phases/review.d.ts +8 -0
- package/dist/commands/init/phases/review.js +79 -0
- package/dist/commands/init/phases/use-case.js +41 -27
- package/dist/commands/init/phases/wizard-flow.test.d.ts +1 -0
- package/dist/commands/init/phases/wizard-flow.test.js +657 -0
- package/dist/commands/init/phases/wizard-modes.test.d.ts +1 -0
- package/dist/commands/init/phases/wizard-modes.test.js +270 -0
- package/dist/commands/init/state.d.ts +31 -1
- package/dist/commands/init/state.js +4 -0
- package/dist/commands/init/state.test.js +7 -0
- package/dist/commands/init/wizard.d.ts +1 -0
- package/dist/commands/init/wizard.js +31 -23
- package/dist/commands/init.js +2 -0
- package/dist/packages/registry.d.ts +66 -0
- package/dist/packages/registry.js +258 -0
- package/dist/packages/setup.d.ts +42 -0
- package/dist/packages/setup.js +244 -15
- package/dist/packages/setup.test.js +520 -13
- package/package.json +1 -1
package/dist/packages/setup.js
CHANGED
|
@@ -1,9 +1,99 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
1
|
+
import { execFile, spawn } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
3
|
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
4
4
|
import { basename, join } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
const execFileAsync = promisify(execFile);
|
|
7
|
+
/**
|
|
8
|
+
* Run a package's own setup wizard.
|
|
9
|
+
*
|
|
10
|
+
* In interactive mode (default): uses `stdio: 'inherit'` so the package's
|
|
11
|
+
* prompts take over the terminal.
|
|
12
|
+
*
|
|
13
|
+
* In non-interactive mode: uses `stdio: 'pipe'` with `nonInteractiveArgs`
|
|
14
|
+
* (falling back to `args`), suitable for --quick mode, CI, and testing.
|
|
15
|
+
*/
|
|
16
|
+
export async function runPackageWizard(pkg, wizardConfig, state, opts) {
|
|
17
|
+
// Backwards compat: opts used to be just cwd string
|
|
18
|
+
const options = typeof opts === "string"
|
|
19
|
+
? { cwd: opts }
|
|
20
|
+
: opts ?? {};
|
|
21
|
+
const interactive = options.interactive ?? true;
|
|
22
|
+
const env = {
|
|
23
|
+
...cleanEnv(),
|
|
24
|
+
HOME: homedir(),
|
|
25
|
+
};
|
|
26
|
+
// Pass swarmkit context via env vars
|
|
27
|
+
if (state.embeddingProvider) {
|
|
28
|
+
env.SWARMKIT_EMBEDDING_PROVIDER = state.embeddingProvider;
|
|
29
|
+
}
|
|
30
|
+
if (state.usePrefix !== undefined) {
|
|
31
|
+
env.SWARMKIT_USE_PREFIX = String(state.usePrefix);
|
|
32
|
+
}
|
|
33
|
+
if (state.apiKeys.openai) {
|
|
34
|
+
env.OPENAI_API_KEY = state.apiKeys.openai;
|
|
35
|
+
}
|
|
36
|
+
if (state.apiKeys.gemini) {
|
|
37
|
+
env.GEMINI_API_KEY = state.apiKeys.gemini;
|
|
38
|
+
}
|
|
39
|
+
// Merge package-specific env overrides
|
|
40
|
+
if (wizardConfig.env) {
|
|
41
|
+
Object.assign(env, wizardConfig.env);
|
|
42
|
+
}
|
|
43
|
+
const timeout = wizardConfig.timeout ?? 120_000;
|
|
44
|
+
const args = (!interactive && wizardConfig.nonInteractiveArgs)
|
|
45
|
+
? wizardConfig.nonInteractiveArgs
|
|
46
|
+
: wizardConfig.args;
|
|
47
|
+
const stdio = interactive ? "inherit" : "pipe";
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
const child = spawn(wizardConfig.command, args, {
|
|
50
|
+
stdio,
|
|
51
|
+
env,
|
|
52
|
+
cwd: options.cwd,
|
|
53
|
+
timeout,
|
|
54
|
+
});
|
|
55
|
+
child.on("close", (code) => {
|
|
56
|
+
if (code === 0) {
|
|
57
|
+
resolve({ package: pkg, success: true });
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
resolve({
|
|
61
|
+
package: pkg,
|
|
62
|
+
success: false,
|
|
63
|
+
message: `exited with code ${code}`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
child.on("error", (err) => {
|
|
68
|
+
resolve({
|
|
69
|
+
package: pkg,
|
|
70
|
+
success: false,
|
|
71
|
+
message: formatError(err),
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Env var overrides that tell each package's CLI to create config in the
|
|
78
|
+
* .swarm/<pkg>/ prefix layout instead of the default flat location.
|
|
79
|
+
* Used by both initProjectPackage (via shellInit) and runPackageWizard.
|
|
80
|
+
*/
|
|
81
|
+
export const PREFIX_ENV_VARS = {
|
|
82
|
+
opentasks: "OPENTASKS_PROJECT_DIR",
|
|
83
|
+
"cognitive-core": "COGNITIVE_CORE_HOME",
|
|
84
|
+
"self-driving-repo": "SDR_CONFIG_DIR",
|
|
85
|
+
openteams: "OPENTEAMS_PROJECT_DIR",
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Flat dir name → prefixed dir name for relocation after CLI wizard runs.
|
|
89
|
+
* Packages that create at .<name>/ need relocation to .swarm/<name>/.
|
|
90
|
+
*/
|
|
91
|
+
export const RELOCATE_MAP = {
|
|
92
|
+
opentasks: [".opentasks", "opentasks"],
|
|
93
|
+
"cognitive-core": [".cognitive-core", "cognitive-core"],
|
|
94
|
+
"self-driving-repo": [".self-driving", "self-driving"],
|
|
95
|
+
openteams: [".openteams", "openteams"],
|
|
96
|
+
};
|
|
7
97
|
// ─── Project-level setup ─────────────────────────────────────────────────────
|
|
8
98
|
/** Root directory for all swarmkit project-level config */
|
|
9
99
|
export const PROJECT_ROOT = ".swarm";
|
|
@@ -33,6 +123,23 @@ export const FLAT_PROJECT_CONFIG_DIRS = {
|
|
|
33
123
|
export function projectConfigDirs(usePrefix) {
|
|
34
124
|
return usePrefix ? PROJECT_CONFIG_DIRS : FLAT_PROJECT_CONFIG_DIRS;
|
|
35
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* After a CLI wizard runs for a project-level package, relocate its output
|
|
128
|
+
* from the flat location to the .swarm/ prefix location if needed.
|
|
129
|
+
*/
|
|
130
|
+
export function relocateAfterWizard(cwd, pkg, usePrefix) {
|
|
131
|
+
if (!usePrefix)
|
|
132
|
+
return;
|
|
133
|
+
const entry = RELOCATE_MAP[pkg];
|
|
134
|
+
if (!entry)
|
|
135
|
+
return;
|
|
136
|
+
const root = join(cwd, PROJECT_ROOT);
|
|
137
|
+
if (!existsSync(root)) {
|
|
138
|
+
mkdirSync(root, { recursive: true });
|
|
139
|
+
}
|
|
140
|
+
const [legacyName, targetName] = entry;
|
|
141
|
+
relocate(cwd, legacyName, targetName);
|
|
142
|
+
}
|
|
36
143
|
/**
|
|
37
144
|
* Project-level packages in correct init order.
|
|
38
145
|
* Order matters: minimem before cognitive-core (runtime detection).
|
|
@@ -137,6 +244,7 @@ export async function initProjectPackage(pkg, ctx) {
|
|
|
137
244
|
export const GLOBAL_CONFIG_DIRS = {
|
|
138
245
|
"skill-tree": ".skill-tree",
|
|
139
246
|
openhive: ".openhive",
|
|
247
|
+
"claude-code-swarm": ".claude-swarm",
|
|
140
248
|
};
|
|
141
249
|
/** Check whether a global package is already configured */
|
|
142
250
|
export function isGlobalInit(pkg) {
|
|
@@ -163,7 +271,7 @@ export async function initGlobalPackage(pkg, ctx, openhiveOpts) {
|
|
|
163
271
|
case "sessionlog":
|
|
164
272
|
return { package: "sessionlog", success: true, message: "no setup required" };
|
|
165
273
|
case "claude-code-swarm":
|
|
166
|
-
return
|
|
274
|
+
return initClaudeSwarmGlobal(ctx);
|
|
167
275
|
default:
|
|
168
276
|
return { package: pkg, success: false, message: "Unknown package" };
|
|
169
277
|
}
|
|
@@ -203,20 +311,26 @@ async function initMinimem(ctx) {
|
|
|
203
311
|
if (!existsSync(join(absTarget, "config.json"))) {
|
|
204
312
|
initMinimemInline(absTarget);
|
|
205
313
|
}
|
|
206
|
-
// Patch
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
314
|
+
// Patch config with wizard choices
|
|
315
|
+
const configPath = join(absTarget, "config.json");
|
|
316
|
+
try {
|
|
317
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
318
|
+
// Patch embedding provider if the wizard chose one
|
|
319
|
+
const provider = ctx.embeddingProvider && ctx.embeddingProvider !== "local"
|
|
320
|
+
? ctx.embeddingProvider
|
|
321
|
+
: null;
|
|
322
|
+
if (provider) {
|
|
214
323
|
config.embedding = { ...config.embedding, provider };
|
|
215
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
216
324
|
}
|
|
217
|
-
|
|
218
|
-
|
|
325
|
+
// Apply per-package config overrides from the interactive config phase
|
|
326
|
+
const overrides = ctx.packageConfigs?.minimem?.values;
|
|
327
|
+
if (overrides) {
|
|
328
|
+
applyNestedOverrides(config, overrides);
|
|
219
329
|
}
|
|
330
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
// Non-fatal
|
|
220
334
|
}
|
|
221
335
|
return { package: "minimem", success: true };
|
|
222
336
|
}
|
|
@@ -242,7 +356,10 @@ function initMinimemInline(targetDir) {
|
|
|
242
356
|
}
|
|
243
357
|
}
|
|
244
358
|
async function initSdr(ctx) {
|
|
245
|
-
|
|
359
|
+
// Use template from interactive config if available, otherwise default
|
|
360
|
+
const template = ctx.packageConfigs?.["self-driving-repo"]?.values?.template ??
|
|
361
|
+
"triage-only";
|
|
362
|
+
const result = await shellInit("sdr", ["init", "-t", template], ctx.cwd, ctx.usePrefix
|
|
246
363
|
? { SDR_CONFIG_DIR: join(PROJECT_ROOT, "self-driving") }
|
|
247
364
|
: undefined);
|
|
248
365
|
// relocate handles packages that don't yet respect the env var;
|
|
@@ -311,7 +428,15 @@ async function initSessionlogProject(ctx) {
|
|
|
311
428
|
? join(ctx.cwd, PROJECT_ROOT, "sessionlog")
|
|
312
429
|
: join(ctx.cwd, ".sessionlog");
|
|
313
430
|
mkdirSync(targetDir, { recursive: true });
|
|
314
|
-
//
|
|
431
|
+
// Apply per-package config overrides
|
|
432
|
+
const overrides = ctx.packageConfigs?.sessionlog?.values;
|
|
433
|
+
// Extract session repo config from dot-notation overrides.
|
|
434
|
+
// sessionRepo.remote and sessionRepo.directory are committable (go to settings.json).
|
|
435
|
+
// sessionRepo.localPath is machine-specific (goes to settings.local.json).
|
|
436
|
+
const repoRemote = overrides?.["sessionRepo.remote"];
|
|
437
|
+
const repoDirectory = overrides?.["sessionRepo.directory"];
|
|
438
|
+
const repoLocalPath = overrides?.["sessionRepo.localPath"];
|
|
439
|
+
// Write settings.json with defaults (committed, shared across clones)
|
|
315
440
|
const settingsPath = join(targetDir, "settings.json");
|
|
316
441
|
if (!existsSync(settingsPath)) {
|
|
317
442
|
const defaultSettings = {
|
|
@@ -321,8 +446,48 @@ async function initSessionlogProject(ctx) {
|
|
|
321
446
|
telemetryEnabled: false,
|
|
322
447
|
summarizationEnabled: false,
|
|
323
448
|
};
|
|
449
|
+
// Apply overrides, stripping session repo dot-notation keys
|
|
450
|
+
if (overrides) {
|
|
451
|
+
const sessionRepoKeys = ["sessionRepo.remote", "sessionRepo.directory", "sessionRepo.localPath", "sessionRepo.autoPush"];
|
|
452
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
453
|
+
if (!sessionRepoKeys.includes(key)) {
|
|
454
|
+
defaultSettings[key] = value;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// Build sessionRepo object for committable fields (remote + directory)
|
|
459
|
+
if (repoRemote) {
|
|
460
|
+
const sessionRepo = { remote: repoRemote };
|
|
461
|
+
if (repoDirectory) {
|
|
462
|
+
sessionRepo.directory = repoDirectory;
|
|
463
|
+
}
|
|
464
|
+
defaultSettings.sessionRepo = sessionRepo;
|
|
465
|
+
}
|
|
324
466
|
writeFileSync(settingsPath, JSON.stringify(defaultSettings, null, 2) + "\n");
|
|
325
467
|
}
|
|
468
|
+
// Write machine-specific session repo fields to settings.local.json
|
|
469
|
+
if (repoLocalPath) {
|
|
470
|
+
const localSettingsPath = join(targetDir, "settings.local.json");
|
|
471
|
+
const localSettings = {};
|
|
472
|
+
if (existsSync(localSettingsPath)) {
|
|
473
|
+
try {
|
|
474
|
+
Object.assign(localSettings, JSON.parse(readFileSync(localSettingsPath, "utf-8")));
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
// Start fresh
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
localSettings.sessionRepo = {
|
|
481
|
+
...(localSettings.sessionRepo ?? {}),
|
|
482
|
+
localPath: repoLocalPath,
|
|
483
|
+
};
|
|
484
|
+
writeFileSync(localSettingsPath, JSON.stringify(localSettings, null, 2) + "\n");
|
|
485
|
+
}
|
|
486
|
+
// Ensure settings.local.json is gitignored
|
|
487
|
+
const gitignorePath = join(targetDir, ".gitignore");
|
|
488
|
+
if (!existsSync(gitignorePath)) {
|
|
489
|
+
writeFileSync(gitignorePath, "# Sessionlog local files (not committed)\nsettings.local.json\ntmp/\nlogs/\n");
|
|
490
|
+
}
|
|
326
491
|
return { package: "sessionlog", success: true };
|
|
327
492
|
}
|
|
328
493
|
catch (err) {
|
|
@@ -386,6 +551,40 @@ async function initOpenhive(opts) {
|
|
|
386
551
|
};
|
|
387
552
|
}
|
|
388
553
|
}
|
|
554
|
+
async function initClaudeSwarmGlobal(ctx) {
|
|
555
|
+
try {
|
|
556
|
+
const targetDir = join(homedir(), ".claude-swarm");
|
|
557
|
+
mkdirSync(targetDir, { recursive: true });
|
|
558
|
+
// Write default global config.json (user preferences that apply across projects)
|
|
559
|
+
const configPath = join(targetDir, "config.json");
|
|
560
|
+
if (!existsSync(configPath)) {
|
|
561
|
+
const defaultConfig = {
|
|
562
|
+
map: {
|
|
563
|
+
server: "",
|
|
564
|
+
sidecar: "session",
|
|
565
|
+
},
|
|
566
|
+
sessionlog: {
|
|
567
|
+
enabled: false,
|
|
568
|
+
sync: "off",
|
|
569
|
+
},
|
|
570
|
+
};
|
|
571
|
+
// Apply per-package config overrides
|
|
572
|
+
const overrides = ctx.packageConfigs?.["claude-code-swarm"]?.values;
|
|
573
|
+
if (overrides) {
|
|
574
|
+
applyNestedOverrides(defaultConfig, overrides);
|
|
575
|
+
}
|
|
576
|
+
writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + "\n", { mode: 0o600 });
|
|
577
|
+
}
|
|
578
|
+
return { package: "claude-code-swarm", success: true };
|
|
579
|
+
}
|
|
580
|
+
catch (err) {
|
|
581
|
+
return {
|
|
582
|
+
package: "claude-code-swarm",
|
|
583
|
+
success: false,
|
|
584
|
+
message: formatError(err),
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
389
588
|
async function initClaudeSwarmProject(ctx) {
|
|
390
589
|
try {
|
|
391
590
|
const targetDir = ctx.usePrefix
|
|
@@ -409,6 +608,11 @@ async function initClaudeSwarmProject(ctx) {
|
|
|
409
608
|
sync: "off",
|
|
410
609
|
},
|
|
411
610
|
};
|
|
611
|
+
// Apply per-package config overrides
|
|
612
|
+
const overrides = ctx.packageConfigs?.["claude-code-swarm"]?.values;
|
|
613
|
+
if (overrides) {
|
|
614
|
+
applyNestedOverrides(defaultConfig, overrides);
|
|
615
|
+
}
|
|
412
616
|
writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + "\n");
|
|
413
617
|
}
|
|
414
618
|
// Write .gitignore for tmp/
|
|
@@ -427,6 +631,31 @@ async function initClaudeSwarmProject(ctx) {
|
|
|
427
631
|
}
|
|
428
632
|
}
|
|
429
633
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
634
|
+
/**
|
|
635
|
+
* Apply dotted-key overrides to a nested config object.
|
|
636
|
+
* e.g., { "hybrid.vectorWeight": 0.8 } sets config.hybrid.vectorWeight = 0.8
|
|
637
|
+
*/
|
|
638
|
+
function applyNestedOverrides(config, overrides) {
|
|
639
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
640
|
+
const parts = key.split(".");
|
|
641
|
+
let target = config;
|
|
642
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
643
|
+
if (typeof target[parts[i]] !== "object" ||
|
|
644
|
+
target[parts[i]] === null) {
|
|
645
|
+
target[parts[i]] = {};
|
|
646
|
+
}
|
|
647
|
+
target = target[parts[i]];
|
|
648
|
+
}
|
|
649
|
+
const finalKey = parts[parts.length - 1];
|
|
650
|
+
// Coerce numeric strings to numbers for config values
|
|
651
|
+
if (typeof value === "string" && /^\d+(\.\d+)?$/.test(value)) {
|
|
652
|
+
target[finalKey] = parseFloat(value);
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
target[finalKey] = value;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
430
659
|
/** Check if a directory exists and contains at least one file (recursively) */
|
|
431
660
|
function hasContent(dir) {
|
|
432
661
|
if (!existsSync(dir))
|