swarmkit 0.0.6 → 0.0.8

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 (40) hide show
  1. package/dist/cli.js +2 -0
  2. package/dist/commands/configure/configure.d.ts +5 -0
  3. package/dist/commands/configure/configure.js +354 -0
  4. package/dist/commands/configure/configure.test.d.ts +1 -0
  5. package/dist/commands/configure/configure.test.js +539 -0
  6. package/dist/commands/configure/read-config.d.ts +12 -0
  7. package/dist/commands/configure/read-config.js +81 -0
  8. package/dist/commands/configure.d.ts +2 -0
  9. package/dist/commands/configure.js +14 -0
  10. package/dist/commands/init/phases/configure.js +0 -21
  11. package/dist/commands/init/phases/global-setup.d.ts +1 -1
  12. package/dist/commands/init/phases/global-setup.js +22 -44
  13. package/dist/commands/init/phases/integrations.d.ts +16 -0
  14. package/dist/commands/init/phases/integrations.js +172 -0
  15. package/dist/commands/init/phases/package-config.d.ts +10 -0
  16. package/dist/commands/init/phases/package-config.js +117 -0
  17. package/dist/commands/init/phases/phases.test.d.ts +1 -0
  18. package/dist/commands/init/phases/phases.test.js +711 -0
  19. package/dist/commands/init/phases/project.js +17 -0
  20. package/dist/commands/init/phases/review.d.ts +8 -0
  21. package/dist/commands/init/phases/review.js +79 -0
  22. package/dist/commands/init/phases/use-case.js +41 -27
  23. package/dist/commands/init/phases/wizard-flow.test.d.ts +1 -0
  24. package/dist/commands/init/phases/wizard-flow.test.js +657 -0
  25. package/dist/commands/init/phases/wizard-modes.test.d.ts +1 -0
  26. package/dist/commands/init/phases/wizard-modes.test.js +270 -0
  27. package/dist/commands/init/state.d.ts +31 -1
  28. package/dist/commands/init/state.js +4 -0
  29. package/dist/commands/init/state.test.js +7 -0
  30. package/dist/commands/init/wizard.d.ts +1 -0
  31. package/dist/commands/init/wizard.js +31 -23
  32. package/dist/commands/init.js +2 -0
  33. package/dist/index.d.ts +3 -2
  34. package/dist/index.js +2 -1
  35. package/dist/packages/registry.d.ts +66 -0
  36. package/dist/packages/registry.js +258 -0
  37. package/dist/packages/setup.d.ts +42 -0
  38. package/dist/packages/setup.js +244 -15
  39. package/dist/packages/setup.test.js +520 -13
  40. package/package.json +1 -1
@@ -1,5 +1,4 @@
1
1
  import { select, password } from "@inquirer/prompts";
2
- import chalk from "chalk";
3
2
  import { readConfig, writeConfig } from "../../../config/global.js";
4
3
  import { writeKey } from "../../../config/keys.js";
5
4
  import * as ui from "../../../utils/ui.js";
@@ -61,25 +60,5 @@ export async function configureKeys(state) {
61
60
  writeConfig(config);
62
61
  }
63
62
  }
64
- // Always ask for Anthropic key (needed for agents)
65
- console.log();
66
- const hasAnthropicKey = !!process.env["ANTHROPIC_API_KEY"];
67
- if (hasAnthropicKey) {
68
- ui.info(`Anthropic API key detected from environment ${chalk.dim("(ANTHROPIC_API_KEY)")}`);
69
- }
70
- else {
71
- const anthropicKey = await password({
72
- message: "Anthropic API key (for running agents):",
73
- mask: "*",
74
- });
75
- if (anthropicKey) {
76
- updated.apiKeys["anthropic"] = anthropicKey;
77
- writeKey("anthropic", anthropicKey);
78
- ui.success("Anthropic API key stored");
79
- }
80
- else {
81
- ui.info("Skipped — you can set ANTHROPIC_API_KEY later.");
82
- }
83
- }
84
63
  return updated;
85
64
  }
@@ -1,2 +1,2 @@
1
1
  import type { WizardState } from "../state.js";
2
- export declare function initGlobal(state: WizardState): Promise<void>;
2
+ export declare function initGlobal(state: WizardState, force?: boolean): Promise<void>;
@@ -1,14 +1,15 @@
1
- import { input, select } from "@inquirer/prompts";
2
1
  import chalk from "chalk";
3
2
  import { GLOBAL_CONFIG_DIRS, isGlobalInit, initGlobalPackage, } from "../../../packages/setup.js";
4
3
  import * as ui from "../../../utils/ui.js";
5
4
  /** Global packages in setup order */
6
- const GLOBAL_SETUP_ORDER = ["skill-tree", "openhive"];
7
- export async function initGlobal(state) {
5
+ const GLOBAL_SETUP_ORDER = ["skill-tree", "openhive", "claude-code-swarm"];
6
+ export async function initGlobal(state, force = false) {
8
7
  const globalPackages = GLOBAL_SETUP_ORDER.filter((pkg) => state.selectedPackages.includes(pkg));
9
8
  if (globalPackages.length === 0)
10
9
  return;
11
- const uninitialized = globalPackages.filter((pkg) => !isGlobalInit(pkg));
10
+ const uninitialized = force
11
+ ? globalPackages
12
+ : globalPackages.filter((pkg) => !isGlobalInit(pkg));
12
13
  if (uninitialized.length === 0) {
13
14
  ui.blank();
14
15
  ui.info("All global packages already configured.");
@@ -19,51 +20,23 @@ export async function initGlobal(state) {
19
20
  packages: state.selectedPackages,
20
21
  embeddingProvider: state.embeddingProvider,
21
22
  apiKeys: state.apiKeys,
23
+ packageConfigs: state.packageConfigs,
22
24
  };
23
25
  for (const pkg of uninitialized) {
26
+ // Skip packages whose CLI wizard already ran in the config phase
27
+ if (state.packageConfigs[pkg]?.usedCliWizard) {
28
+ ui.success(`${pkg} ${chalk.dim("(configured via wizard)")}`);
29
+ continue;
30
+ }
24
31
  let openhiveOpts;
25
- // Openhive: intercept into the wizard for inline configuration
32
+ // Build OpenhiveOptions from packageConfigs (collected in the config phase)
26
33
  if (pkg === "openhive") {
27
- console.log();
28
- ui.info("Configuring OpenHive...");
29
- console.log();
30
- const name = await input({
31
- message: "OpenHive instance name:",
32
- default: "OpenHive",
33
- });
34
- const port = await input({
35
- message: "OpenHive port:",
36
- default: "3000",
37
- });
38
- const verification = await select({
39
- message: "Registration mode:",
40
- choices: [
41
- { name: "Open — anyone can register", value: "open" },
42
- {
43
- name: "Invite — require an invite code",
44
- value: "invite",
45
- },
46
- {
47
- name: "Manual — admin approves each",
48
- value: "manual",
49
- },
50
- ],
51
- });
52
- const authMode = await select({
53
- message: "Auth mode:",
54
- choices: [
55
- { name: "Local — no login, single-user", value: "local" },
56
- {
57
- name: "Token — email/password registration",
58
- value: "token",
59
- },
60
- ],
61
- });
34
+ const values = state.packageConfigs.openhive?.values ?? {};
62
35
  openhiveOpts = {
63
- name,
64
- port: parseInt(port, 10) || 3000,
65
- authMode,
66
- verification,
36
+ name: values.name ?? "OpenHive",
37
+ port: parseInt(String(values.port ?? "3000"), 10) || 3000,
38
+ authMode: values.authMode ?? "local",
39
+ verification: values.verification ?? "open",
67
40
  };
68
41
  }
69
42
  const result = await initGlobalPackage(pkg, ctx, openhiveOpts);
@@ -73,6 +46,11 @@ export async function initGlobal(state) {
73
46
  : "";
74
47
  if (result.success) {
75
48
  ui.success(`${pkg} ${chalk.dim("→")} ~/${configDir}/${suffix}`);
49
+ state.configsWritten.push({
50
+ package: pkg,
51
+ path: `~/${configDir}/`,
52
+ description: `Global ${pkg} config`,
53
+ });
76
54
  }
77
55
  else {
78
56
  ui.fail(`${pkg}: ${result.message ?? "setup failed"}`);
@@ -0,0 +1,16 @@
1
+ import type { WizardState } from "../state.js";
2
+ /**
3
+ * Phase: Integration wiring.
4
+ *
5
+ * Walks through active integrations between selected packages:
6
+ * - Auto-detected integrations (requiresWiring: false) are shown as info
7
+ * - Wirable integrations get interactive config prompts
8
+ */
9
+ export declare function configureIntegrations(state: WizardState): Promise<WizardState>;
10
+ /**
11
+ * Apply collected integration wiring values to package config files on disk.
12
+ *
13
+ * Each configOption in INTEGRATIONS specifies a targetPackage and configPath.
14
+ * This function reads the target config file, patches the value, and writes it back.
15
+ */
16
+ export declare function applyIntegrationWiring(state: WizardState): void;
@@ -0,0 +1,172 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { confirm, input, select } from "@inquirer/prompts";
4
+ import chalk from "chalk";
5
+ import { getActiveIntegrations } from "../../../packages/registry.js";
6
+ import { projectConfigDirs, GLOBAL_CONFIG_DIRS, } from "../../../packages/setup.js";
7
+ import { homedir } from "node:os";
8
+ import * as ui from "../../../utils/ui.js";
9
+ /**
10
+ * Phase: Integration wiring.
11
+ *
12
+ * Walks through active integrations between selected packages:
13
+ * - Auto-detected integrations (requiresWiring: false) are shown as info
14
+ * - Wirable integrations get interactive config prompts
15
+ */
16
+ export async function configureIntegrations(state) {
17
+ const updated = { ...state, integrationWiring: [...state.integrationWiring] };
18
+ const activeIntegrations = getActiveIntegrations(updated.selectedPackages);
19
+ if (activeIntegrations.length === 0)
20
+ return updated;
21
+ const autoDetected = activeIntegrations.filter((i) => !i.requiresWiring);
22
+ const wirable = activeIntegrations.filter((i) => i.requiresWiring && i.configOptions && i.configOptions.length > 0);
23
+ ui.heading(" Integration wiring");
24
+ // Show auto-detected integrations
25
+ if (autoDetected.length > 0) {
26
+ for (const integration of autoDetected) {
27
+ const [a, b] = integration.packages;
28
+ ui.success(`${a} ${chalk.dim("↔")} ${b} ${chalk.dim(integration.description)} ${chalk.dim("(auto-detected)")}`);
29
+ }
30
+ }
31
+ // Walk through wirable integrations
32
+ if (wirable.length > 0 && !updated.quick) {
33
+ for (const integration of wirable) {
34
+ const [a, b] = integration.packages;
35
+ const key = `${a}:${b}`;
36
+ console.log();
37
+ console.log(` ${chalk.bold(a)} ${chalk.dim("↔")} ${chalk.bold(b)}`);
38
+ ui.info(integration.description);
39
+ const enable = await confirm({
40
+ message: `Configure this integration?`,
41
+ default: true,
42
+ });
43
+ const wiring = {
44
+ key,
45
+ enabled: enable,
46
+ values: {},
47
+ };
48
+ if (enable && integration.configOptions) {
49
+ for (const option of integration.configOptions) {
50
+ const value = await promptConfigOption(option);
51
+ wiring.values[option.key] = value;
52
+ }
53
+ }
54
+ updated.integrationWiring.push(wiring);
55
+ }
56
+ }
57
+ else if (wirable.length > 0) {
58
+ // Quick mode — show what's available but skip config
59
+ for (const integration of wirable) {
60
+ const [a, b] = integration.packages;
61
+ ui.info(`${a} ${chalk.dim("↔")} ${b} ${chalk.dim(integration.description)} ${chalk.dim("(configure later)")}`);
62
+ }
63
+ }
64
+ return updated;
65
+ }
66
+ /**
67
+ * Apply collected integration wiring values to package config files on disk.
68
+ *
69
+ * Each configOption in INTEGRATIONS specifies a targetPackage and configPath.
70
+ * This function reads the target config file, patches the value, and writes it back.
71
+ */
72
+ export function applyIntegrationWiring(state) {
73
+ if (state.integrationWiring.length === 0)
74
+ return;
75
+ const cwd = process.cwd();
76
+ const activeIntegrations = getActiveIntegrations(state.selectedPackages);
77
+ for (const wiring of state.integrationWiring) {
78
+ if (!wiring.enabled)
79
+ continue;
80
+ // Find the matching integration definition to get configOptions
81
+ const integration = activeIntegrations.find((i) => {
82
+ const [a, b] = i.packages;
83
+ return `${a}:${b}` === wiring.key;
84
+ });
85
+ if (!integration?.configOptions)
86
+ continue;
87
+ // Apply each config option that has a collected value
88
+ for (const option of integration.configOptions) {
89
+ const value = wiring.values[option.key];
90
+ if (value === undefined)
91
+ continue;
92
+ applyWiringValue(option.targetPackage, option.configPath, value, cwd, state.usePrefix);
93
+ }
94
+ }
95
+ }
96
+ /**
97
+ * Write a single value to a package's config file on disk.
98
+ */
99
+ function applyWiringValue(pkg, configPath, value, cwd, usePrefix) {
100
+ const filePath = resolveConfigFile(pkg, cwd, usePrefix);
101
+ if (!filePath)
102
+ return;
103
+ try {
104
+ const config = existsSync(filePath)
105
+ ? JSON.parse(readFileSync(filePath, "utf-8"))
106
+ : {};
107
+ setNestedValue(config, configPath, value);
108
+ writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n");
109
+ }
110
+ catch {
111
+ // Non-fatal — integration wiring is best-effort
112
+ }
113
+ }
114
+ function resolveConfigFile(pkg, cwd, usePrefix) {
115
+ const configDirs = projectConfigDirs(usePrefix);
116
+ const configFile = pkg === "sessionlog" ? "settings.json" : "config.json";
117
+ // Check project config
118
+ const projectDir = configDirs[pkg];
119
+ if (projectDir) {
120
+ const projectPath = join(cwd, projectDir, configFile);
121
+ if (existsSync(projectPath))
122
+ return projectPath;
123
+ }
124
+ // Check global config
125
+ const globalDir = GLOBAL_CONFIG_DIRS[pkg];
126
+ if (globalDir) {
127
+ const globalPath = join(homedir(), globalDir, "config.json");
128
+ if (existsSync(globalPath))
129
+ return globalPath;
130
+ }
131
+ return null;
132
+ }
133
+ function setNestedValue(obj, keyPath, value) {
134
+ const parts = keyPath.split(".");
135
+ let target = obj;
136
+ for (let i = 0; i < parts.length - 1; i++) {
137
+ if (typeof target[parts[i]] !== "object" || target[parts[i]] === null) {
138
+ target[parts[i]] = {};
139
+ }
140
+ target = target[parts[i]];
141
+ }
142
+ const finalKey = parts[parts.length - 1];
143
+ if (typeof value === "string" && /^\d+(\.\d+)?$/.test(value)) {
144
+ target[finalKey] = parseFloat(value);
145
+ }
146
+ else {
147
+ target[finalKey] = value;
148
+ }
149
+ }
150
+ /** Prompt the user for a single integration config option */
151
+ async function promptConfigOption(option) {
152
+ switch (option.type) {
153
+ case "confirm":
154
+ return confirm({
155
+ message: option.label,
156
+ default: option.default,
157
+ });
158
+ case "select":
159
+ return select({
160
+ message: option.label,
161
+ choices: option.choices ?? [],
162
+ default: option.default,
163
+ });
164
+ case "input":
165
+ return input({
166
+ message: option.label,
167
+ default: option.default,
168
+ });
169
+ default:
170
+ return option.default;
171
+ }
172
+ }
@@ -0,0 +1,10 @@
1
+ import type { WizardState } from "../state.js";
2
+ /**
3
+ * Phase: Per-package interactive configuration.
4
+ *
5
+ * For each selected package that has setup config:
6
+ * - If quick mode, skip entirely (defaults will be used)
7
+ * - If package has a cliWizard, offer to delegate to it
8
+ * - If package has inlineOptions (and no wizard or wizard declined), walk through prompts
9
+ */
10
+ export declare function configurePackages(state: WizardState): Promise<WizardState>;
@@ -0,0 +1,117 @@
1
+ import { confirm, input, select, password } from "@inquirer/prompts";
2
+ import chalk from "chalk";
3
+ import { PACKAGES } from "../../../packages/registry.js";
4
+ import { runPackageWizard, relocateAfterWizard, PREFIX_ENV_VARS, PROJECT_ROOT, } from "../../../packages/setup.js";
5
+ import * as ui from "../../../utils/ui.js";
6
+ import { join } from "node:path";
7
+ /**
8
+ * Phase: Per-package interactive configuration.
9
+ *
10
+ * For each selected package that has setup config:
11
+ * - If quick mode, skip entirely (defaults will be used)
12
+ * - If package has a cliWizard, offer to delegate to it
13
+ * - If package has inlineOptions (and no wizard or wizard declined), walk through prompts
14
+ */
15
+ export async function configurePackages(state) {
16
+ const updated = { ...state, packageConfigs: { ...state.packageConfigs } };
17
+ const configurablePackages = updated.selectedPackages.filter((pkg) => PACKAGES[pkg]?.setup);
18
+ if (configurablePackages.length === 0 || updated.quick) {
19
+ return updated;
20
+ }
21
+ ui.heading(" Package configuration");
22
+ ui.info("Configure each package, or accept defaults.");
23
+ const cwd = process.cwd();
24
+ for (const pkgName of configurablePackages) {
25
+ const pkg = PACKAGES[pkgName];
26
+ const setupConfig = pkg.setup;
27
+ const config = { values: {}, usedCliWizard: false };
28
+ console.log();
29
+ console.log(` ${chalk.bold(pkgName)} ${chalk.dim(pkg.description)}`);
30
+ // Try CLI wizard first if available
31
+ if (setupConfig.cliWizard) {
32
+ const useWizard = await confirm({
33
+ message: `Run ${pkgName}'s interactive setup wizard?`,
34
+ default: false,
35
+ });
36
+ if (useWizard) {
37
+ // Build prefix-aware env vars for project-level packages
38
+ const wizardConfig = { ...setupConfig.cliWizard };
39
+ if (updated.usePrefix && PREFIX_ENV_VARS[pkgName]) {
40
+ const envVar = PREFIX_ENV_VARS[pkgName];
41
+ const prefixDir = PROJECT_ROOT + "/" +
42
+ (pkgName === "opentasks" ? "opentasks" :
43
+ pkgName === "self-driving-repo" ? "self-driving" :
44
+ pkgName === "openteams" ? "openteams" :
45
+ pkgName === "cognitive-core" ? "cognitive-core" : pkgName);
46
+ wizardConfig.env = {
47
+ ...wizardConfig.env,
48
+ // Some packages need absolute paths, others relative
49
+ [envVar]: pkgName === "cognitive-core"
50
+ ? join(cwd, prefixDir)
51
+ : prefixDir,
52
+ };
53
+ }
54
+ const result = await runPackageWizard(pkgName, wizardConfig, updated, { cwd });
55
+ if (result.success) {
56
+ // Relocate from flat to prefixed location if needed
57
+ relocateAfterWizard(cwd, pkgName, updated.usePrefix);
58
+ config.usedCliWizard = true;
59
+ ui.success(`${pkgName} wizard completed.`);
60
+ updated.packageConfigs[pkgName] = config;
61
+ continue;
62
+ }
63
+ else {
64
+ ui.warn(`${pkgName} wizard failed: ${result.message ?? "unknown error"}`);
65
+ ui.info("Falling back to inline configuration.");
66
+ }
67
+ }
68
+ }
69
+ // Inline options
70
+ if (setupConfig.inlineOptions && setupConfig.inlineOptions.length > 0) {
71
+ const customize = await confirm({
72
+ message: `Customize ${pkgName} settings?`,
73
+ default: false,
74
+ });
75
+ if (customize) {
76
+ for (const option of setupConfig.inlineOptions) {
77
+ // Check conditional visibility
78
+ if (option.when && !option.when(updated.selectedPackages)) {
79
+ continue;
80
+ }
81
+ const value = await promptOption(option);
82
+ config.values[option.key] = value;
83
+ }
84
+ }
85
+ }
86
+ updated.packageConfigs[pkgName] = config;
87
+ }
88
+ return updated;
89
+ }
90
+ /** Prompt the user for a single inline option */
91
+ async function promptOption(option) {
92
+ switch (option.type) {
93
+ case "confirm":
94
+ return confirm({
95
+ message: option.label,
96
+ default: option.default,
97
+ });
98
+ case "select":
99
+ return select({
100
+ message: option.label,
101
+ choices: option.choices ?? [],
102
+ default: option.default,
103
+ });
104
+ case "input":
105
+ return input({
106
+ message: option.label,
107
+ default: option.default,
108
+ });
109
+ case "password":
110
+ return password({
111
+ message: option.label,
112
+ mask: "*",
113
+ });
114
+ default:
115
+ return option.default;
116
+ }
117
+ }
@@ -0,0 +1 @@
1
+ export {};