wicked-bus 1.1.0 → 1.1.1

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 (2) hide show
  1. package/install.mjs +88 -15
  2. package/package.json +1 -1
package/install.mjs CHANGED
@@ -11,8 +11,46 @@ const __dirname = fileURLToPath(new URL(".", import.meta.url));
11
11
  const skillsSource = join(__dirname, "skills");
12
12
  const home = homedir();
13
13
 
14
+ // Claude-root candidate builder. Mirrors the 1.1.1 wicked-testing /
15
+ // wicked-brain fix: $CLAUDE_CONFIG_DIR is authoritative when set;
16
+ // otherwise probe common alt-config layouts. Claude Code's config root
17
+ // is redirectable, and hardcoded ~/.claude silently misses users on
18
+ // shared-home / multi-tenant setups.
19
+ function buildClaudeTarget(rootDir, source, { trusted = false } = {}) {
20
+ return {
21
+ name: "claude",
22
+ rootDir,
23
+ dir: join(rootDir, "skills"),
24
+ platform: "claude",
25
+ identityMarkers: ["settings.json", "plugins", "projects"],
26
+ source,
27
+ trusted,
28
+ };
29
+ }
30
+
31
+ function resolveClaudeCandidates() {
32
+ const envDir = process.env.CLAUDE_CONFIG_DIR;
33
+ if (envDir && typeof envDir === "string" && envDir.trim()) {
34
+ // Function replacement avoids `$&` etc. being interpreted as regex
35
+ // back-references if $HOME contains those literals.
36
+ const root = resolve(envDir.trim().replace(/^~/, () => home));
37
+ return [buildClaudeTarget(root, "env:CLAUDE_CONFIG_DIR", { trusted: true })];
38
+ }
39
+ return [
40
+ buildClaudeTarget(join(home, ".claude"), "default"),
41
+ buildClaudeTarget(join(home, "alt-configs", ".claude"), "alt-configs"),
42
+ buildClaudeTarget(join(home, ".config", "claude"), "xdg"),
43
+ ];
44
+ }
45
+
46
+ function claudeHasIdentityMarker(target) {
47
+ if (target.trusted) return true;
48
+ if (!existsSync(target.rootDir)) return false;
49
+ return (target.identityMarkers || []).some(m => existsSync(join(target.rootDir, m)));
50
+ }
51
+
52
+ // Non-claude canonical targets. Claude is expanded dynamically above.
14
53
  const CLI_TARGETS = [
15
- { name: "claude", dir: join(home, ".claude", "skills"), platform: "claude" },
16
54
  { name: "gemini", dir: join(home, ".gemini", "skills"), platform: "gemini" },
17
55
  { name: "copilot", dir: join(home, ".github", "skills"), platform: "copilot" },
18
56
  { name: "codex", dir: join(home, ".codex", "skills"), platform: "codex" },
@@ -24,19 +62,43 @@ const CLI_TARGETS = [
24
62
  console.log("wicked-bus installer\n");
25
63
 
26
64
  const args = argv.slice(2);
27
- const argValue = (a) => a.split("=")[1];
28
- const cliArg = args.find((a) => a.startsWith("--cli="));
29
- const pathArg = args.find((a) => a.startsWith("--path="));
65
+
66
+ // Flag parser supporting both --flag=value and --flag value forms, plus
67
+ // narrow string-boolean coercion ("true" / "false" → booleans). Previously
68
+ // the ad-hoc parser silently dropped space-separated values — same bug
69
+ // that hit wicked-testing 0.3.2 / wicked-brain 0.3.7.
70
+ const flagValue = (name) => {
71
+ const f = args.find(a => a === `--${name}` || a.startsWith(`--${name}=`));
72
+ if (!f) return null;
73
+ let val;
74
+ if (f.includes("=")) {
75
+ // slice from the first '=' forward — split("=")[1] would truncate at
76
+ // the second '=' (e.g. --path=/volumes/build=artifacts).
77
+ val = f.slice(f.indexOf("=") + 1);
78
+ } else {
79
+ const idx = args.indexOf(f);
80
+ const next = args[idx + 1];
81
+ val = (next && !next.startsWith("-")) ? next : true;
82
+ }
83
+ if (val === "false") return false;
84
+ if (val === "true") return true;
85
+ return val;
86
+ };
87
+
88
+ const cliArg = flagValue("cli");
89
+ const pathArg = flagValue("path");
90
+
91
+ // Validate --cli upfront so a mistyped --cli / --cli= fails fast
92
+ // instead of silently falling through to "all detected".
93
+ if (cliArg === true || cliArg === "") {
94
+ console.error("Error: --cli requires a value (e.g. --cli=claude or --cli claude)");
95
+ process.exit(1);
96
+ }
30
97
 
31
98
  let targets;
32
99
 
33
- if (pathArg) {
34
- const rawPath = argValue(pathArg);
35
- if (!rawPath) {
36
- console.error("Error: --path requires a value (e.g. --path=~/.claude)");
37
- process.exit(1);
38
- }
39
- const customPath = resolve(rawPath.replace(/^~/, home));
100
+ if (pathArg && typeof pathArg === "string" && pathArg !== "") {
101
+ const customPath = resolve(pathArg.replace(/^~/, () => home));
40
102
  const dirName = basename(customPath).replace(/^\./, "");
41
103
  targets = [{
42
104
  name: dirName,
@@ -44,18 +106,29 @@ if (pathArg) {
44
106
  platform: dirName,
45
107
  }];
46
108
  console.log(`Custom path: ${customPath}\n`);
109
+ } else if (pathArg === true || pathArg === "") {
110
+ console.error("Error: --path requires a value (e.g. --path=~/.claude or --path ~/.claude)");
111
+ process.exit(1);
47
112
  } else {
48
- const detected = CLI_TARGETS.filter((t) => existsSync(resolve(t.dir, "..")));
113
+ // Expanded detection: claude candidates (env var OR alt-config probes,
114
+ // identity-marker gated) + non-claude parent-dir-exists heuristic.
115
+ const claudeDetected = resolveClaudeCandidates().filter(claudeHasIdentityMarker);
116
+ const otherDetected = CLI_TARGETS.filter((t) => existsSync(resolve(t.dir, "..")));
117
+ const detected = [...claudeDetected, ...otherDetected];
49
118
 
50
119
  if (detected.length === 0) {
51
120
  console.log("No supported AI CLIs detected. Supported: claude, gemini, copilot, codex, cursor, kiro, antigravity");
52
- console.log("Install skills manually by copying the skills/ directory.");
121
+ console.log("Install skills manually by copying the skills/ directory, or set CLAUDE_CONFIG_DIR.");
53
122
  process.exit(1);
54
123
  }
55
124
 
56
- console.log(`Detected CLIs: ${detected.map((d) => d.name).join(", ")}\n`);
125
+ const claudeCount = claudeDetected.length;
126
+ const label = (d) => d.name === "claude" && claudeCount > 1 && d.source
127
+ ? `${d.name}[${d.source}]`
128
+ : d.name;
129
+ console.log(`Detected CLIs: ${detected.map(label).join(", ")}\n`);
57
130
 
58
- const cliFilter = cliArg ? argValue(cliArg).split(",") : null;
131
+ const cliFilter = (typeof cliArg === "string" && cliArg !== "") ? cliArg.split(",") : null;
59
132
  targets = cliFilter ? detected.filter((d) => cliFilter.includes(d.name)) : detected;
60
133
  }
61
134
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-bus",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Lightweight, local-first SQLite event bus for AI agents and developer tools",
5
5
  "type": "module",
6
6
  "exports": {