@grant-vine/wunderkind 0.8.0 → 0.9.2

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 (47) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +209 -27
  3. package/commands/docs-index.md +44 -0
  4. package/dist/agents/docs-config.d.ts +6 -0
  5. package/dist/agents/docs-config.d.ts.map +1 -1
  6. package/dist/agents/docs-config.js +15 -1
  7. package/dist/agents/docs-config.js.map +1 -1
  8. package/dist/agents/docs-index-plan.d.ts +28 -0
  9. package/dist/agents/docs-index-plan.d.ts.map +1 -0
  10. package/dist/agents/docs-index-plan.js +118 -0
  11. package/dist/agents/docs-index-plan.js.map +1 -0
  12. package/dist/cli/cli-installer.d.ts +9 -1
  13. package/dist/cli/cli-installer.d.ts.map +1 -1
  14. package/dist/cli/cli-installer.js +65 -7
  15. package/dist/cli/cli-installer.js.map +1 -1
  16. package/dist/cli/config-manager/index.d.ts +17 -1
  17. package/dist/cli/config-manager/index.d.ts.map +1 -1
  18. package/dist/cli/config-manager/index.js +415 -177
  19. package/dist/cli/config-manager/index.js.map +1 -1
  20. package/dist/cli/doctor.d.ts +4 -0
  21. package/dist/cli/doctor.d.ts.map +1 -1
  22. package/dist/cli/doctor.js +113 -35
  23. package/dist/cli/doctor.js.map +1 -1
  24. package/dist/cli/index.js +69 -22
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/init.d.ts +0 -4
  27. package/dist/cli/init.d.ts.map +1 -1
  28. package/dist/cli/init.js +144 -38
  29. package/dist/cli/init.js.map +1 -1
  30. package/dist/cli/personality-meta.d.ts +8 -0
  31. package/dist/cli/personality-meta.d.ts.map +1 -0
  32. package/dist/cli/personality-meta.js +213 -0
  33. package/dist/cli/personality-meta.js.map +1 -0
  34. package/dist/cli/tui-installer.d.ts.map +1 -1
  35. package/dist/cli/tui-installer.js +57 -42
  36. package/dist/cli/tui-installer.js.map +1 -1
  37. package/dist/cli/types.d.ts +34 -22
  38. package/dist/cli/types.d.ts.map +1 -1
  39. package/dist/cli/uninstall.d.ts +6 -0
  40. package/dist/cli/uninstall.d.ts.map +1 -0
  41. package/dist/cli/uninstall.js +64 -0
  42. package/dist/cli/uninstall.js.map +1 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +6 -8
  45. package/dist/index.js.map +1 -1
  46. package/package.json +3 -1
  47. package/schemas/wunderkind.config.schema.json +67 -0
@@ -1,8 +1,10 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
4
5
  import { parse as parseJsonc } from "jsonc-parser";
5
6
  const PACKAGE_NAME = "@grant-vine/wunderkind";
7
+ const WUNDERKIND_SCHEMA_URL = "https://raw.githubusercontent.com/grant-vine/wunderkind/main/schemas/wunderkind.config.schema.json";
6
8
  const CONFIG_DIR = join(homedir(), ".config", "opencode");
7
9
  const CONFIG_JSON = join(CONFIG_DIR, "opencode.json");
8
10
  const CONFIG_JSONC = join(CONFIG_DIR, "opencode.jsonc");
@@ -13,16 +15,107 @@ const GLOBAL_WUNDERKIND_CONFIG = join(GLOBAL_WUNDERKIND_DIR, "wunderkind.config.
13
15
  const WUNDERKIND_DIR = join(process.cwd(), ".wunderkind");
14
16
  const WUNDERKIND_CONFIG = join(WUNDERKIND_DIR, "wunderkind.config.jsonc");
15
17
  const LEGACY_WUNDERKIND_CONFIG = join(process.cwd(), "wunderkind.config.jsonc");
16
- function getConfigPath() {
18
+ const PROJECT_CONFIG_KEYS = [
19
+ "teamCulture",
20
+ "orgStructure",
21
+ "cisoPersonality",
22
+ "ctoPersonality",
23
+ "cmoPersonality",
24
+ "qaPersonality",
25
+ "productPersonality",
26
+ "opsPersonality",
27
+ "creativePersonality",
28
+ "brandPersonality",
29
+ "devrelPersonality",
30
+ "legalPersonality",
31
+ "supportPersonality",
32
+ "dataAnalystPersonality",
33
+ "docsEnabled",
34
+ "docsPath",
35
+ "docHistoryMode",
36
+ ];
37
+ const DEFAULT_INSTALL_CONFIG = {
38
+ region: "Global",
39
+ industry: "",
40
+ primaryRegulation: "GDPR",
41
+ secondaryRegulation: "",
42
+ teamCulture: "pragmatic-balanced",
43
+ orgStructure: "flat",
44
+ cisoPersonality: "pragmatic-risk-manager",
45
+ ctoPersonality: "code-archaeologist",
46
+ cmoPersonality: "data-driven",
47
+ qaPersonality: "risk-based-pragmatist",
48
+ productPersonality: "outcome-obsessed",
49
+ opsPersonality: "on-call-veteran",
50
+ creativePersonality: "pragmatic-problem-solver",
51
+ brandPersonality: "authentic-builder",
52
+ devrelPersonality: "dx-engineer",
53
+ legalPersonality: "pragmatic-advisor",
54
+ supportPersonality: "systematic-triage",
55
+ dataAnalystPersonality: "insight-storyteller",
56
+ docsEnabled: false,
57
+ docsPath: "./docs",
58
+ docHistoryMode: "overwrite",
59
+ };
60
+ const DEFAULT_GLOBAL_CONFIG = {
61
+ region: DEFAULT_INSTALL_CONFIG.region,
62
+ industry: DEFAULT_INSTALL_CONFIG.industry,
63
+ primaryRegulation: DEFAULT_INSTALL_CONFIG.primaryRegulation,
64
+ secondaryRegulation: DEFAULT_INSTALL_CONFIG.secondaryRegulation,
65
+ };
66
+ const DEFAULT_PROJECT_CONFIG = {
67
+ teamCulture: DEFAULT_INSTALL_CONFIG.teamCulture,
68
+ orgStructure: DEFAULT_INSTALL_CONFIG.orgStructure,
69
+ cisoPersonality: DEFAULT_INSTALL_CONFIG.cisoPersonality,
70
+ ctoPersonality: DEFAULT_INSTALL_CONFIG.ctoPersonality,
71
+ cmoPersonality: DEFAULT_INSTALL_CONFIG.cmoPersonality,
72
+ qaPersonality: DEFAULT_INSTALL_CONFIG.qaPersonality,
73
+ productPersonality: DEFAULT_INSTALL_CONFIG.productPersonality,
74
+ opsPersonality: DEFAULT_INSTALL_CONFIG.opsPersonality,
75
+ creativePersonality: DEFAULT_INSTALL_CONFIG.creativePersonality,
76
+ brandPersonality: DEFAULT_INSTALL_CONFIG.brandPersonality,
77
+ devrelPersonality: DEFAULT_INSTALL_CONFIG.devrelPersonality,
78
+ legalPersonality: DEFAULT_INSTALL_CONFIG.legalPersonality,
79
+ supportPersonality: DEFAULT_INSTALL_CONFIG.supportPersonality,
80
+ dataAnalystPersonality: DEFAULT_INSTALL_CONFIG.dataAnalystPersonality,
81
+ docsEnabled: DEFAULT_INSTALL_CONFIG.docsEnabled,
82
+ docsPath: DEFAULT_INSTALL_CONFIG.docsPath,
83
+ docHistoryMode: DEFAULT_INSTALL_CONFIG.docHistoryMode,
84
+ };
85
+ export function getDefaultInstallConfig() {
86
+ return { ...DEFAULT_INSTALL_CONFIG };
87
+ }
88
+ export function getDefaultGlobalConfig() {
89
+ return { ...DEFAULT_GLOBAL_CONFIG };
90
+ }
91
+ export function getDefaultProjectConfig() {
92
+ return { ...DEFAULT_PROJECT_CONFIG };
93
+ }
94
+ export function resolveOpenCodeConfigPath(scope) {
95
+ if (scope === "project") {
96
+ const projectJson = join(process.cwd(), "opencode.json");
97
+ const projectJsonc = join(process.cwd(), "opencode.jsonc");
98
+ const projectLegacyJson = join(process.cwd(), "config.json");
99
+ const projectLegacyJsonc = join(process.cwd(), "config.jsonc");
100
+ if (existsSync(projectJson))
101
+ return { path: projectJson, format: "json", source: "opencode.json" };
102
+ if (existsSync(projectJsonc))
103
+ return { path: projectJsonc, format: "jsonc", source: "opencode.jsonc" };
104
+ if (existsSync(projectLegacyJson))
105
+ return { path: projectLegacyJson, format: "json", source: "config.json" };
106
+ if (existsSync(projectLegacyJsonc))
107
+ return { path: projectLegacyJsonc, format: "jsonc", source: "config.jsonc" };
108
+ return { path: projectJson, format: "none", source: "default" };
109
+ }
17
110
  if (existsSync(CONFIG_JSON))
18
- return { path: CONFIG_JSON, format: "json" };
111
+ return { path: CONFIG_JSON, format: "json", source: "opencode.json" };
19
112
  if (existsSync(CONFIG_JSONC))
20
- return { path: CONFIG_JSONC, format: "jsonc" };
113
+ return { path: CONFIG_JSONC, format: "jsonc", source: "opencode.jsonc" };
21
114
  if (existsSync(LEGACY_CONFIG_JSON))
22
- return { path: LEGACY_CONFIG_JSON, format: "json" };
115
+ return { path: LEGACY_CONFIG_JSON, format: "json", source: "config.json" };
23
116
  if (existsSync(LEGACY_CONFIG_JSONC))
24
- return { path: LEGACY_CONFIG_JSONC, format: "jsonc" };
25
- return { path: CONFIG_JSON, format: "none" };
117
+ return { path: LEGACY_CONFIG_JSONC, format: "jsonc", source: "config.jsonc" };
118
+ return { path: CONFIG_JSON, format: "none", source: "default" };
26
119
  }
27
120
  function parseConfig(path) {
28
121
  try {
@@ -50,128 +143,280 @@ function parseWunderkindConfig(path) {
50
143
  return null;
51
144
  }
52
145
  }
146
+ function coerceGlobalConfig(source) {
147
+ const result = {};
148
+ if (typeof source["region"] === "string")
149
+ result.region = source["region"];
150
+ if (typeof source["industry"] === "string")
151
+ result.industry = source["industry"];
152
+ if (typeof source["primaryRegulation"] === "string")
153
+ result.primaryRegulation = source["primaryRegulation"];
154
+ if (typeof source["secondaryRegulation"] === "string")
155
+ result.secondaryRegulation = source["secondaryRegulation"];
156
+ return result;
157
+ }
158
+ function coerceProjectConfig(source) {
159
+ const result = {};
160
+ if (typeof source["teamCulture"] === "string")
161
+ result.teamCulture = source["teamCulture"];
162
+ if (typeof source["orgStructure"] === "string")
163
+ result.orgStructure = source["orgStructure"];
164
+ if (typeof source["cisoPersonality"] === "string")
165
+ result.cisoPersonality = source["cisoPersonality"];
166
+ if (typeof source["ctoPersonality"] === "string")
167
+ result.ctoPersonality = source["ctoPersonality"];
168
+ if (typeof source["cmoPersonality"] === "string")
169
+ result.cmoPersonality = source["cmoPersonality"];
170
+ if (typeof source["qaPersonality"] === "string")
171
+ result.qaPersonality = source["qaPersonality"];
172
+ if (typeof source["productPersonality"] === "string") {
173
+ result.productPersonality = source["productPersonality"];
174
+ }
175
+ if (typeof source["opsPersonality"] === "string")
176
+ result.opsPersonality = source["opsPersonality"];
177
+ if (typeof source["creativePersonality"] === "string") {
178
+ result.creativePersonality = source["creativePersonality"];
179
+ }
180
+ if (typeof source["brandPersonality"] === "string")
181
+ result.brandPersonality = source["brandPersonality"];
182
+ if (typeof source["devrelPersonality"] === "string")
183
+ result.devrelPersonality = source["devrelPersonality"];
184
+ if (typeof source["legalPersonality"] === "string")
185
+ result.legalPersonality = source["legalPersonality"];
186
+ if (typeof source["supportPersonality"] === "string") {
187
+ result.supportPersonality = source["supportPersonality"];
188
+ }
189
+ if (typeof source["dataAnalystPersonality"] === "string") {
190
+ result.dataAnalystPersonality = source["dataAnalystPersonality"];
191
+ }
192
+ if (typeof source["docsEnabled"] === "boolean")
193
+ result.docsEnabled = source["docsEnabled"];
194
+ if (typeof source["docsPath"] === "string")
195
+ result.docsPath = source["docsPath"];
196
+ if (typeof source["docHistoryMode"] === "string")
197
+ result.docHistoryMode = source["docHistoryMode"];
198
+ return result;
199
+ }
200
+ function listLegacyGlobalProjectFields(source) {
201
+ return PROJECT_CONFIG_KEYS.filter((key) => key in source);
202
+ }
203
+ function hasWunderkindPlugin(plugins) {
204
+ return plugins.some((p) => p === PACKAGE_NAME || p === "wunderkind" || p.startsWith(`${PACKAGE_NAME}@`) || p.startsWith("wunderkind@"));
205
+ }
206
+ function detectRegistration() {
207
+ const projectResolution = resolveOpenCodeConfigPath("project");
208
+ const globalResolution = resolveOpenCodeConfigPath("global");
209
+ const projectOpenCodeConfigPath = projectResolution.path;
210
+ const globalOpenCodeConfigPath = globalResolution.path;
211
+ const projectConfig = existsSync(projectOpenCodeConfigPath) ? parseConfig(projectOpenCodeConfigPath) : null;
212
+ const globalConfig = existsSync(globalOpenCodeConfigPath) ? parseConfig(globalOpenCodeConfigPath) : null;
213
+ const projectInstalled = hasWunderkindPlugin((projectConfig?.plugin ?? []));
214
+ const globalInstalled = hasWunderkindPlugin((globalConfig?.plugin ?? []));
215
+ let registrationScope = "none";
216
+ if (projectInstalled && globalInstalled) {
217
+ registrationScope = "both";
218
+ }
219
+ else if (projectInstalled) {
220
+ registrationScope = "project";
221
+ }
222
+ else if (globalInstalled) {
223
+ registrationScope = "global";
224
+ }
225
+ return {
226
+ projectInstalled,
227
+ globalInstalled,
228
+ registrationScope,
229
+ projectOpenCodeConfigPath,
230
+ globalOpenCodeConfigPath,
231
+ };
232
+ }
53
233
  export function readWunderkindConfig() {
54
234
  const projectConfig = existsSync(WUNDERKIND_CONFIG) ? parseWunderkindConfig(WUNDERKIND_CONFIG) : null;
55
235
  const globalConfig = existsSync(GLOBAL_WUNDERKIND_CONFIG) ? parseWunderkindConfig(GLOBAL_WUNDERKIND_CONFIG) : null;
56
236
  if (!projectConfig && !globalConfig) {
57
237
  return null;
58
238
  }
59
- const merged = {
60
- ...(globalConfig ?? {}),
61
- ...(projectConfig ?? {}),
239
+ const globalSafe = coerceGlobalConfig(globalConfig ?? {});
240
+ const projectLocal = coerceProjectConfig(projectConfig ?? {});
241
+ return {
242
+ ...globalSafe,
243
+ ...projectLocal,
62
244
  };
63
- const result = {};
64
- if (typeof merged["region"] === "string")
65
- result.region = merged["region"];
66
- if (typeof merged["industry"] === "string")
67
- result.industry = merged["industry"];
68
- if (typeof merged["primaryRegulation"] === "string")
69
- result.primaryRegulation = merged["primaryRegulation"];
70
- if (typeof merged["secondaryRegulation"] === "string")
71
- result.secondaryRegulation = merged["secondaryRegulation"];
72
- if (typeof merged["teamCulture"] === "string")
73
- result.teamCulture = merged["teamCulture"];
74
- if (typeof merged["orgStructure"] === "string")
75
- result.orgStructure = merged["orgStructure"];
76
- if (typeof merged["cisoPersonality"] === "string")
77
- result.cisoPersonality = merged["cisoPersonality"];
78
- if (typeof merged["ctoPersonality"] === "string")
79
- result.ctoPersonality = merged["ctoPersonality"];
80
- if (typeof merged["cmoPersonality"] === "string")
81
- result.cmoPersonality = merged["cmoPersonality"];
82
- if (typeof merged["qaPersonality"] === "string")
83
- result.qaPersonality = merged["qaPersonality"];
84
- if (typeof merged["productPersonality"] === "string")
85
- result.productPersonality = merged["productPersonality"];
86
- if (typeof merged["opsPersonality"] === "string")
87
- result.opsPersonality = merged["opsPersonality"];
88
- if (typeof merged["creativePersonality"] === "string") {
89
- result.creativePersonality = merged["creativePersonality"];
90
- }
91
- if (typeof merged["brandPersonality"] === "string")
92
- result.brandPersonality = merged["brandPersonality"];
93
- if (typeof merged["devrelPersonality"] === "string")
94
- result.devrelPersonality = merged["devrelPersonality"];
95
- if (typeof merged["legalPersonality"] === "string")
96
- result.legalPersonality = merged["legalPersonality"];
97
- if (typeof merged["supportPersonality"] === "string")
98
- result.supportPersonality = merged["supportPersonality"];
99
- if (typeof merged["dataAnalystPersonality"] === "string") {
100
- result.dataAnalystPersonality = merged["dataAnalystPersonality"];
101
- }
102
- if (typeof merged["docsEnabled"] === "boolean")
103
- result.docsEnabled = merged["docsEnabled"];
104
- if (typeof merged["docsPath"] === "string")
105
- result.docsPath = merged["docsPath"];
106
- if (typeof merged["docHistoryMode"] === "string")
107
- result.docHistoryMode = merged["docHistoryMode"];
108
- return result;
245
+ }
246
+ export function readGlobalWunderkindConfig() {
247
+ const globalConfig = existsSync(GLOBAL_WUNDERKIND_CONFIG) ? parseWunderkindConfig(GLOBAL_WUNDERKIND_CONFIG) : null;
248
+ return globalConfig ? coerceGlobalConfig(globalConfig) : null;
249
+ }
250
+ export function readProjectWunderkindConfig() {
251
+ const projectConfig = existsSync(WUNDERKIND_CONFIG) ? parseWunderkindConfig(WUNDERKIND_CONFIG) : null;
252
+ return projectConfig ? coerceProjectConfig(projectConfig) : null;
253
+ }
254
+ function ensureConfigDir(configDir, configPath) {
255
+ try {
256
+ if (!existsSync(configDir)) {
257
+ mkdirSync(configDir, { recursive: true });
258
+ }
259
+ return null;
260
+ }
261
+ catch (err) {
262
+ return { success: false, configPath, error: String(err) };
263
+ }
264
+ }
265
+ function renderGlobalWunderkindConfig(config) {
266
+ return [
267
+ `// Wunderkind global configuration — safe defaults shared across projects`,
268
+ `{`,
269
+ ` "$schema": ${JSON.stringify(WUNDERKIND_SCHEMA_URL)},`,
270
+ ` // Geographic region — e.g. "South Africa", "United States", "United Kingdom", "Australia"`,
271
+ ` "region": ${JSON.stringify(config.region)},`,
272
+ ` // Industry vertical — e.g. "SaaS", "FinTech", "eCommerce", "HealthTech"`,
273
+ ` "industry": ${JSON.stringify(config.industry)},`,
274
+ ` // Primary data-protection regulation — e.g. "GDPR", "POPIA", "CCPA", "LGPD"`,
275
+ ` "primaryRegulation": ${JSON.stringify(config.primaryRegulation)},`,
276
+ ` // Optional secondary regulation`,
277
+ ` "secondaryRegulation": ${JSON.stringify(config.secondaryRegulation)}`,
278
+ `}`,
279
+ ``,
280
+ ].join("\n");
281
+ }
282
+ function renderProjectWunderkindConfig(config) {
283
+ return [
284
+ `// Wunderkind project configuration — edit these values to tailor agents to this project`,
285
+ `{`,
286
+ ` "$schema": ${JSON.stringify(WUNDERKIND_SCHEMA_URL)},`,
287
+ ` // Team culture baseline — affects all agents' communication style and decision rigour`,
288
+ ` // "formal-strict" | "pragmatic-balanced" | "experimental-informal"`,
289
+ ` "teamCulture": ${JSON.stringify(config.teamCulture)},`,
290
+ ` // Org structure — "flat" (peers, escalate to user) | "hierarchical" (domain authority applies, CISO has hard veto)`,
291
+ ` "orgStructure": ${JSON.stringify(config.orgStructure)},`,
292
+ ``,
293
+ ` // Agent personalities — controls each agent's default character archetype`,
294
+ ` // CISO: "paranoid-enforcer" | "pragmatic-risk-manager" | "educator-collaborator"`,
295
+ ` "cisoPersonality": ${JSON.stringify(config.cisoPersonality)},`,
296
+ ` // CTO/Fullstack: "grizzled-sysadmin" | "startup-bro" | "code-archaeologist"`,
297
+ ` "ctoPersonality": ${JSON.stringify(config.ctoPersonality)},`,
298
+ ` // CMO/Marketing: "data-driven" | "brand-storyteller" | "growth-hacker"`,
299
+ ` "cmoPersonality": ${JSON.stringify(config.cmoPersonality)},`,
300
+ ` // QA: "rule-enforcer" | "risk-based-pragmatist" | "rubber-duck"`,
301
+ ` "qaPersonality": ${JSON.stringify(config.qaPersonality)},`,
302
+ ` // Product: "user-advocate" | "velocity-optimizer" | "outcome-obsessed"`,
303
+ ` "productPersonality": ${JSON.stringify(config.productPersonality)},`,
304
+ ` // Operations: "on-call-veteran" | "efficiency-maximiser" | "process-purist"`,
305
+ ` "opsPersonality": ${JSON.stringify(config.opsPersonality)},`,
306
+ ` // Creative Director: "perfectionist-craftsperson" | "bold-provocateur" | "pragmatic-problem-solver"`,
307
+ ` "creativePersonality": ${JSON.stringify(config.creativePersonality)},`,
308
+ ` // Brand Builder: "community-evangelist" | "pr-spinner" | "authentic-builder"`,
309
+ ` "brandPersonality": ${JSON.stringify(config.brandPersonality)},`,
310
+ ` // DevRel Wunderkind: "community-champion" | "docs-perfectionist" | "dx-engineer"`,
311
+ ` "devrelPersonality": ${JSON.stringify(config.devrelPersonality)},`,
312
+ ` // Legal Counsel: "cautious-gatekeeper" | "pragmatic-advisor" | "plain-english-counselor"`,
313
+ ` "legalPersonality": ${JSON.stringify(config.legalPersonality)},`,
314
+ ` // Support Engineer: "empathetic-resolver" | "systematic-triage" | "knowledge-builder"`,
315
+ ` "supportPersonality": ${JSON.stringify(config.supportPersonality)},`,
316
+ ` // Data Analyst: "rigorous-statistician" | "insight-storyteller" | "pragmatic-quant"`,
317
+ ` "dataAnalystPersonality": ${JSON.stringify(config.dataAnalystPersonality)},`,
318
+ ``,
319
+ ` // Docs output settings`,
320
+ ` // Enable or disable writing docs outputs to disk`,
321
+ ` "docsEnabled": ${JSON.stringify(config.docsEnabled)},`,
322
+ ` // Directory path where docs outputs are written`,
323
+ ` "docsPath": ${JSON.stringify(config.docsPath)},`,
324
+ ` // History mode: "overwrite" | "append-dated" | "new-dated-file" | "overwrite-archive"`,
325
+ ` "docHistoryMode": ${JSON.stringify(config.docHistoryMode)}`,
326
+ `}`,
327
+ ``,
328
+ ].join("\n");
329
+ }
330
+ export function writeGlobalWunderkindConfig(config) {
331
+ const setupError = ensureConfigDir(GLOBAL_WUNDERKIND_DIR, GLOBAL_WUNDERKIND_CONFIG);
332
+ if (setupError)
333
+ return setupError;
334
+ try {
335
+ writeFileSync(GLOBAL_WUNDERKIND_CONFIG, renderGlobalWunderkindConfig(config));
336
+ return { success: true, configPath: GLOBAL_WUNDERKIND_CONFIG };
337
+ }
338
+ catch (err) {
339
+ return { success: false, configPath: GLOBAL_WUNDERKIND_CONFIG, error: String(err) };
340
+ }
341
+ }
342
+ export function writeProjectWunderkindConfig(config) {
343
+ const setupError = ensureConfigDir(WUNDERKIND_DIR, WUNDERKIND_CONFIG);
344
+ if (setupError)
345
+ return setupError;
346
+ try {
347
+ writeFileSync(WUNDERKIND_CONFIG, renderProjectWunderkindConfig(config));
348
+ return { success: true, configPath: WUNDERKIND_CONFIG };
349
+ }
350
+ catch (err) {
351
+ return { success: false, configPath: WUNDERKIND_CONFIG, error: String(err) };
352
+ }
353
+ }
354
+ export function readWunderkindConfigForScope(scope) {
355
+ if (scope === "global") {
356
+ return readGlobalWunderkindConfig();
357
+ }
358
+ return readProjectWunderkindConfig();
109
359
  }
110
360
  export function detectCurrentConfig() {
111
- const defaults = {
361
+ const projectResolution = resolveOpenCodeConfigPath("project");
362
+ const globalResolution = resolveOpenCodeConfigPath("global");
363
+ const defaults = getDefaultInstallConfig();
364
+ const detectedDefaults = {
112
365
  isInstalled: false,
113
366
  scope: "global",
114
- region: "Global",
115
- industry: "",
116
- primaryRegulation: "GDPR",
117
- secondaryRegulation: "",
118
- teamCulture: "pragmatic-balanced",
119
- orgStructure: "flat",
120
- cisoPersonality: "pragmatic-risk-manager",
121
- ctoPersonality: "code-archaeologist",
122
- cmoPersonality: "data-driven",
123
- qaPersonality: "risk-based-pragmatist",
124
- productPersonality: "outcome-obsessed",
125
- opsPersonality: "on-call-veteran",
126
- creativePersonality: "pragmatic-problem-solver",
127
- brandPersonality: "authentic-builder",
128
- devrelPersonality: "dx-engineer",
129
- legalPersonality: "pragmatic-advisor",
130
- supportPersonality: "systematic-triage",
131
- dataAnalystPersonality: "insight-storyteller",
132
- docsEnabled: false,
133
- docsPath: "./docs",
134
- docHistoryMode: "overwrite",
367
+ projectInstalled: false,
368
+ globalInstalled: false,
369
+ registrationScope: "none",
370
+ projectOpenCodeConfigPath: projectResolution.path,
371
+ globalOpenCodeConfigPath: globalResolution.path,
372
+ ...defaults,
135
373
  };
136
- const { path, format } = getConfigPath();
137
- if (format === "none")
138
- return defaults;
139
- const config = parseConfig(path);
140
- if (!config)
141
- return defaults;
142
- const plugins = config.plugin ?? [];
143
- const isInstalled = plugins.some((p) => p === PACKAGE_NAME || p === "wunderkind" || p.startsWith(`${PACKAGE_NAME}@`) || p.startsWith("wunderkind@"));
144
- if (!isInstalled)
145
- return defaults;
146
- const wk = readWunderkindConfig();
374
+ const registration = detectRegistration();
375
+ if (registration.registrationScope === "none") {
376
+ return {
377
+ ...detectedDefaults,
378
+ ...registration,
379
+ };
380
+ }
381
+ const globalConfig = existsSync(GLOBAL_WUNDERKIND_CONFIG) ? parseWunderkindConfig(GLOBAL_WUNDERKIND_CONFIG) : null;
382
+ const legacyGlobalProjectFields = globalConfig ? listLegacyGlobalProjectFields(globalConfig) : [];
383
+ const globalSafe = readGlobalWunderkindConfig();
384
+ const projectLocal = readProjectWunderkindConfig();
385
+ const legacyGlobalProject = coerceProjectConfig(globalConfig ?? {});
147
386
  return {
148
387
  isInstalled: true,
149
- scope: "global",
150
- region: wk?.region ?? defaults.region,
151
- industry: wk?.industry ?? defaults.industry,
152
- primaryRegulation: wk?.primaryRegulation ?? defaults.primaryRegulation,
153
- secondaryRegulation: wk?.secondaryRegulation ?? defaults.secondaryRegulation,
154
- teamCulture: wk?.teamCulture ?? defaults.teamCulture,
155
- orgStructure: wk?.orgStructure ?? defaults.orgStructure,
156
- cisoPersonality: wk?.cisoPersonality ?? defaults.cisoPersonality,
157
- ctoPersonality: wk?.ctoPersonality ?? defaults.ctoPersonality,
158
- cmoPersonality: wk?.cmoPersonality ?? defaults.cmoPersonality,
159
- qaPersonality: wk?.qaPersonality ?? defaults.qaPersonality,
160
- productPersonality: wk?.productPersonality ?? defaults.productPersonality,
161
- opsPersonality: wk?.opsPersonality ?? defaults.opsPersonality,
162
- creativePersonality: wk?.creativePersonality ?? defaults.creativePersonality,
163
- brandPersonality: wk?.brandPersonality ?? defaults.brandPersonality,
164
- devrelPersonality: wk?.devrelPersonality ?? defaults.devrelPersonality,
165
- legalPersonality: wk?.legalPersonality ?? defaults.legalPersonality,
166
- supportPersonality: wk?.supportPersonality ?? defaults.supportPersonality,
167
- dataAnalystPersonality: wk?.dataAnalystPersonality ?? defaults.dataAnalystPersonality,
168
- docsEnabled: wk?.docsEnabled ?? defaults.docsEnabled,
169
- docsPath: wk?.docsPath ?? defaults.docsPath,
170
- docHistoryMode: wk?.docHistoryMode ?? defaults.docHistoryMode,
388
+ scope: registration.projectInstalled ? "project" : "global",
389
+ projectInstalled: registration.projectInstalled,
390
+ globalInstalled: registration.globalInstalled,
391
+ registrationScope: registration.registrationScope,
392
+ projectOpenCodeConfigPath: registration.projectOpenCodeConfigPath,
393
+ globalOpenCodeConfigPath: registration.globalOpenCodeConfigPath,
394
+ legacyGlobalProjectFields,
395
+ region: globalSafe?.region ?? defaults.region,
396
+ industry: globalSafe?.industry ?? defaults.industry,
397
+ primaryRegulation: globalSafe?.primaryRegulation ?? defaults.primaryRegulation,
398
+ secondaryRegulation: globalSafe?.secondaryRegulation ?? defaults.secondaryRegulation,
399
+ teamCulture: projectLocal?.teamCulture ?? legacyGlobalProject.teamCulture ?? defaults.teamCulture,
400
+ orgStructure: projectLocal?.orgStructure ?? legacyGlobalProject.orgStructure ?? defaults.orgStructure,
401
+ cisoPersonality: projectLocal?.cisoPersonality ?? legacyGlobalProject.cisoPersonality ?? defaults.cisoPersonality,
402
+ ctoPersonality: projectLocal?.ctoPersonality ?? legacyGlobalProject.ctoPersonality ?? defaults.ctoPersonality,
403
+ cmoPersonality: projectLocal?.cmoPersonality ?? legacyGlobalProject.cmoPersonality ?? defaults.cmoPersonality,
404
+ qaPersonality: projectLocal?.qaPersonality ?? legacyGlobalProject.qaPersonality ?? defaults.qaPersonality,
405
+ productPersonality: projectLocal?.productPersonality ?? legacyGlobalProject.productPersonality ?? defaults.productPersonality,
406
+ opsPersonality: projectLocal?.opsPersonality ?? legacyGlobalProject.opsPersonality ?? defaults.opsPersonality,
407
+ creativePersonality: projectLocal?.creativePersonality ?? legacyGlobalProject.creativePersonality ?? defaults.creativePersonality,
408
+ brandPersonality: projectLocal?.brandPersonality ?? legacyGlobalProject.brandPersonality ?? defaults.brandPersonality,
409
+ devrelPersonality: projectLocal?.devrelPersonality ?? legacyGlobalProject.devrelPersonality ?? defaults.devrelPersonality,
410
+ legalPersonality: projectLocal?.legalPersonality ?? legacyGlobalProject.legalPersonality ?? defaults.legalPersonality,
411
+ supportPersonality: projectLocal?.supportPersonality ?? legacyGlobalProject.supportPersonality ?? defaults.supportPersonality,
412
+ dataAnalystPersonality: projectLocal?.dataAnalystPersonality ?? legacyGlobalProject.dataAnalystPersonality ?? defaults.dataAnalystPersonality,
413
+ docsEnabled: projectLocal?.docsEnabled ?? legacyGlobalProject.docsEnabled ?? defaults.docsEnabled,
414
+ docsPath: projectLocal?.docsPath ?? legacyGlobalProject.docsPath ?? defaults.docsPath,
415
+ docHistoryMode: projectLocal?.docHistoryMode ?? legacyGlobalProject.docHistoryMode ?? defaults.docHistoryMode,
171
416
  };
172
417
  }
173
418
  export function addPluginToOpenCodeConfig(scope) {
174
- const targetPath = scope === "project" ? join(process.cwd(), "opencode.json") : CONFIG_JSON;
419
+ const targetPath = resolveOpenCodeConfigPath(scope).path;
175
420
  const targetDir = scope === "project" ? process.cwd() : CONFIG_DIR;
176
421
  try {
177
422
  if (!existsSync(targetDir)) {
@@ -189,7 +434,7 @@ export function addPluginToOpenCodeConfig(scope) {
189
434
  }
190
435
  const config = parseConfig(targetPath) ?? {};
191
436
  const plugins = (config.plugin ?? []);
192
- const already = plugins.some((p) => p === PACKAGE_NAME || p === "wunderkind" || p.startsWith(`${PACKAGE_NAME}@`) || p.startsWith("wunderkind@"));
437
+ const already = hasWunderkindPlugin(plugins);
193
438
  if (already) {
194
439
  const idx = plugins.findIndex((p) => p === "wunderkind" || p.startsWith("wunderkind@"));
195
440
  if (idx !== -1) {
@@ -209,79 +454,72 @@ export function addPluginToOpenCodeConfig(scope) {
209
454
  }
210
455
  }
211
456
  export function writeWunderkindConfig(installConfig, scope) {
212
- const configPath = scope === "global" ? GLOBAL_WUNDERKIND_CONFIG : WUNDERKIND_CONFIG;
213
- const configDir = scope === "global" ? GLOBAL_WUNDERKIND_DIR : WUNDERKIND_DIR;
457
+ if (scope === "global") {
458
+ return writeGlobalWunderkindConfig(installConfig);
459
+ }
460
+ return writeProjectWunderkindConfig(installConfig);
461
+ }
462
+ export function detectLegacyConfig() {
463
+ return existsSync(LEGACY_WUNDERKIND_CONFIG);
464
+ }
465
+ export function removePluginFromOpenCodeConfig(scope) {
466
+ const targetPath = resolveOpenCodeConfigPath(scope).path;
214
467
  try {
215
- if (!existsSync(configDir)) {
216
- mkdirSync(configDir, { recursive: true });
468
+ if (!existsSync(targetPath)) {
469
+ return { success: true, configPath: targetPath, changed: false };
470
+ }
471
+ const config = parseConfig(targetPath);
472
+ if (!config) {
473
+ return { success: false, configPath: targetPath, error: "Invalid OpenCode config format" };
474
+ }
475
+ const plugins = (config.plugin ?? []);
476
+ if (plugins.length === 0) {
477
+ return { success: true, configPath: targetPath, changed: false };
478
+ }
479
+ const filtered = plugins.filter((p) => !(p === PACKAGE_NAME || p === "wunderkind" || p.startsWith(`${PACKAGE_NAME}@`) || p.startsWith("wunderkind@")));
480
+ if (filtered.length === plugins.length) {
481
+ return { success: true, configPath: targetPath, changed: false };
217
482
  }
483
+ if (filtered.length === 0) {
484
+ delete config.plugin;
485
+ }
486
+ else {
487
+ config.plugin = filtered;
488
+ }
489
+ writeFileSync(targetPath, JSON.stringify(config, null, 2) + "\n");
490
+ return { success: true, configPath: targetPath, changed: true };
218
491
  }
219
492
  catch (err) {
220
- return { success: false, configPath, error: String(err) };
493
+ return { success: false, configPath: targetPath, error: String(err) };
221
494
  }
495
+ }
496
+ export function writeOmoAgentConfig(targetDir) {
497
+ const omoConfigPath = join(targetDir, ".opencode", "oh-my-opencode.jsonc");
222
498
  try {
223
- const content = [
224
- `// Wunderkind configuration — edit these values to tailor agents to your project context`,
225
- `{`,
226
- ` // Geographic region — e.g. "South Africa", "United States", "United Kingdom", "Australia"`,
227
- ` "region": ${JSON.stringify(installConfig.region)},`,
228
- ` // Industry vertical — e.g. "SaaS", "FinTech", "eCommerce", "HealthTech"`,
229
- ` "industry": ${JSON.stringify(installConfig.industry)},`,
230
- ` // Primary data-protection regulation — e.g. "GDPR", "POPIA", "CCPA", "LGPD"`,
231
- ` "primaryRegulation": ${JSON.stringify(installConfig.primaryRegulation)},`,
232
- ` // Optional secondary regulation`,
233
- ` "secondaryRegulation": ${JSON.stringify(installConfig.secondaryRegulation)},`,
234
- ``,
235
- ` // Team culture baseline — affects all agents' communication style and decision rigour`,
236
- ` // "formal-strict" | "pragmatic-balanced" | "experimental-informal"`,
237
- ` "teamCulture": ${JSON.stringify(installConfig.teamCulture)},`,
238
- ` // Org structure — "flat" (peers, escalate to user) | "hierarchical" (domain authority applies, CISO has hard veto)`,
239
- ` "orgStructure": ${JSON.stringify(installConfig.orgStructure)},`,
240
- ``,
241
- ` // Agent personalities — controls each agent's default character archetype`,
242
- ` // CISO: "paranoid-enforcer" | "pragmatic-risk-manager" | "educator-collaborator"`,
243
- ` "cisoPersonality": ${JSON.stringify(installConfig.cisoPersonality)},`,
244
- ` // CTO/Fullstack: "grizzled-sysadmin" | "startup-bro" | "code-archaeologist"`,
245
- ` "ctoPersonality": ${JSON.stringify(installConfig.ctoPersonality)},`,
246
- ` // CMO/Marketing: "data-driven" | "brand-storyteller" | "growth-hacker"`,
247
- ` "cmoPersonality": ${JSON.stringify(installConfig.cmoPersonality)},`,
248
- ` // QA: "rule-enforcer" | "risk-based-pragmatist" | "rubber-duck"`,
249
- ` "qaPersonality": ${JSON.stringify(installConfig.qaPersonality)},`,
250
- ` // Product: "user-advocate" | "velocity-optimizer" | "outcome-obsessed"`,
251
- ` "productPersonality": ${JSON.stringify(installConfig.productPersonality)},`,
252
- ` // Operations: "on-call-veteran" | "efficiency-maximiser" | "process-purist"`,
253
- ` "opsPersonality": ${JSON.stringify(installConfig.opsPersonality)},`,
254
- ` // Creative Director: "perfectionist-craftsperson" | "bold-provocateur" | "pragmatic-problem-solver"`,
255
- ` "creativePersonality": ${JSON.stringify(installConfig.creativePersonality)},`,
256
- ` // Brand Builder: "community-evangelist" | "pr-spinner" | "authentic-builder"`,
257
- ` "brandPersonality": ${JSON.stringify(installConfig.brandPersonality)},`,
258
- ` // DevRel Wunderkind: "community-champion" | "docs-perfectionist" | "dx-engineer"`,
259
- ` "devrelPersonality": ${JSON.stringify(installConfig.devrelPersonality)},`,
260
- ` // Legal Counsel: "cautious-gatekeeper" | "pragmatic-advisor" | "plain-english-counselor"`,
261
- ` "legalPersonality": ${JSON.stringify(installConfig.legalPersonality)},`,
262
- ` // Support Engineer: "empathetic-resolver" | "systematic-triage" | "knowledge-builder"`,
263
- ` "supportPersonality": ${JSON.stringify(installConfig.supportPersonality)},`,
264
- ` // Data Analyst: "rigorous-statistician" | "insight-storyteller" | "pragmatic-quant"`,
265
- ` "dataAnalystPersonality": ${JSON.stringify(installConfig.dataAnalystPersonality)},`,
266
- ``,
267
- ` // Docs output settings`,
268
- ` // Enable or disable writing docs outputs to disk`,
269
- ` "docsEnabled": ${JSON.stringify(installConfig.docsEnabled)},`,
270
- ` // Directory path where docs outputs are written`,
271
- ` "docsPath": ${JSON.stringify(installConfig.docsPath)},`,
272
- ` // History mode: "overwrite" | "append-dated" | "new-dated-file" | "overwrite-archive"`,
273
- ` "docHistoryMode": ${JSON.stringify(installConfig.docHistoryMode)}`,
274
- `}`,
275
- ``,
276
- ].join("\n");
277
- writeFileSync(configPath, content);
278
- return { success: true, configPath };
499
+ const sourceUrl = new URL("../../../oh-my-opencode.jsonc", import.meta.url);
500
+ const sourceFilePath = fileURLToPath(sourceUrl);
501
+ const contents = readFileSync(sourceFilePath, "utf-8");
502
+ mkdirSync(join(targetDir, ".opencode"), { recursive: true });
503
+ writeFileSync(omoConfigPath, contents);
504
+ return { success: true, configPath: omoConfigPath };
279
505
  }
280
506
  catch (err) {
281
- return { success: false, configPath, error: String(err) };
507
+ return { success: false, configPath: omoConfigPath, error: String(err) };
282
508
  }
283
509
  }
284
- export function detectLegacyConfig() {
285
- return existsSync(LEGACY_WUNDERKIND_CONFIG);
510
+ export function removeGlobalWunderkindConfig() {
511
+ try {
512
+ if (!existsSync(GLOBAL_WUNDERKIND_CONFIG)) {
513
+ return { success: true, configPath: GLOBAL_WUNDERKIND_CONFIG, changed: false };
514
+ }
515
+ rmSync(GLOBAL_WUNDERKIND_CONFIG, { force: true });
516
+ if (existsSync(GLOBAL_WUNDERKIND_DIR) && readdirSync(GLOBAL_WUNDERKIND_DIR).length === 0) {
517
+ rmSync(GLOBAL_WUNDERKIND_DIR, { recursive: false, force: true });
518
+ }
519
+ return { success: true, configPath: GLOBAL_WUNDERKIND_CONFIG, changed: true };
520
+ }
521
+ catch (err) {
522
+ return { success: false, configPath: GLOBAL_WUNDERKIND_CONFIG, error: String(err) };
523
+ }
286
524
  }
287
525
  //# sourceMappingURL=index.js.map