u-foo 1.0.6 → 1.2.0

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 (149) hide show
  1. package/README.md +247 -23
  2. package/SKILLS/ufoo/SKILL.md +17 -2
  3. package/SKILLS/uinit/SKILL.md +8 -3
  4. package/bin/ucode-core.js +15 -0
  5. package/bin/ucode.js +125 -0
  6. package/bin/ufoo-assistant-agent.js +5 -0
  7. package/bin/ufoo-engine.js +25 -0
  8. package/bin/ufoo.js +4 -0
  9. package/modules/AGENTS.template.md +14 -4
  10. package/modules/bus/README.md +8 -5
  11. package/modules/bus/SKILLS/ubus/SKILL.md +5 -4
  12. package/modules/context/SKILLS/uctx/SKILL.md +3 -1
  13. package/modules/online/SKILLS/ufoo-online/SKILL.md +144 -0
  14. package/package.json +12 -3
  15. package/scripts/import-pi-mono.js +124 -0
  16. package/scripts/postinstall.js +20 -49
  17. package/scripts/sync-claude-skills.sh +21 -0
  18. package/src/agent/cliRunner.js +524 -31
  19. package/src/agent/internalRunner.js +76 -9
  20. package/src/agent/launcher.js +97 -45
  21. package/src/agent/normalizeOutput.js +1 -1
  22. package/src/agent/notifier.js +144 -4
  23. package/src/agent/ptyRunner.js +480 -10
  24. package/src/agent/ptyWrapper.js +28 -3
  25. package/src/agent/readyDetector.js +16 -0
  26. package/src/agent/ucode.js +443 -0
  27. package/src/agent/ucodeBootstrap.js +113 -0
  28. package/src/agent/ucodeBuild.js +67 -0
  29. package/src/agent/ucodeDoctor.js +184 -0
  30. package/src/agent/ucodeRuntimeConfig.js +129 -0
  31. package/src/agent/ufooAgent.js +168 -28
  32. package/src/assistant/agent.js +260 -0
  33. package/src/assistant/bridge.js +172 -0
  34. package/src/assistant/engine.js +252 -0
  35. package/src/assistant/stdio.js +58 -0
  36. package/src/assistant/ufooEngineCli.js +306 -0
  37. package/src/bus/activate.js +27 -11
  38. package/src/bus/daemon.js +133 -5
  39. package/src/bus/index.js +137 -80
  40. package/src/bus/inject.js +47 -17
  41. package/src/bus/message.js +145 -17
  42. package/src/bus/nickname.js +3 -1
  43. package/src/bus/queue.js +6 -1
  44. package/src/bus/store.js +189 -0
  45. package/src/bus/subscriber.js +20 -4
  46. package/src/bus/utils.js +9 -3
  47. package/src/chat/agentBar.js +117 -0
  48. package/src/chat/agentDirectory.js +88 -0
  49. package/src/chat/agentSockets.js +225 -0
  50. package/src/chat/agentViewController.js +298 -0
  51. package/src/chat/chatLogController.js +115 -0
  52. package/src/chat/commandExecutor.js +700 -0
  53. package/src/chat/commands.js +132 -0
  54. package/src/chat/completionController.js +414 -0
  55. package/src/chat/cronScheduler.js +160 -0
  56. package/src/chat/daemonConnection.js +166 -0
  57. package/src/chat/daemonCoordinator.js +64 -0
  58. package/src/chat/daemonMessageRouter.js +257 -0
  59. package/src/chat/daemonReconnect.js +41 -0
  60. package/src/chat/daemonTransport.js +36 -0
  61. package/src/chat/daemonTransportDefaults.js +10 -0
  62. package/src/chat/dashboardKeyController.js +480 -0
  63. package/src/chat/dashboardView.js +157 -0
  64. package/src/chat/index.js +938 -2910
  65. package/src/chat/inputHistoryController.js +105 -0
  66. package/src/chat/inputListenerController.js +304 -0
  67. package/src/chat/inputMath.js +104 -0
  68. package/src/chat/inputSubmitHandler.js +171 -0
  69. package/src/chat/layout.js +165 -0
  70. package/src/chat/pasteController.js +81 -0
  71. package/src/chat/rawKeyMap.js +42 -0
  72. package/src/chat/settingsController.js +133 -0
  73. package/src/chat/statusLineController.js +177 -0
  74. package/src/chat/streamTracker.js +138 -0
  75. package/src/chat/text.js +70 -0
  76. package/src/chat/transport.js +61 -0
  77. package/src/cli/busCoreCommands.js +59 -0
  78. package/src/cli/ctxCoreCommands.js +199 -0
  79. package/src/cli/onlineCoreCommands.js +379 -0
  80. package/src/cli.js +741 -238
  81. package/src/code/README.md +29 -0
  82. package/src/code/UCODE_PROMPT.md +32 -0
  83. package/src/code/agent.js +1651 -0
  84. package/src/code/cli.js +158 -0
  85. package/src/code/config +0 -0
  86. package/src/code/dispatch.js +42 -0
  87. package/src/code/index.js +70 -0
  88. package/src/code/nativeRunner.js +1213 -0
  89. package/src/code/runtime.js +154 -0
  90. package/src/code/sessionStore.js +162 -0
  91. package/src/code/taskDecomposer.js +269 -0
  92. package/src/code/tools/bash.js +53 -0
  93. package/src/code/tools/common.js +42 -0
  94. package/src/code/tools/edit.js +70 -0
  95. package/src/code/tools/read.js +44 -0
  96. package/src/code/tools/write.js +35 -0
  97. package/src/code/tui.js +1587 -0
  98. package/src/config.js +50 -2
  99. package/src/context/decisions.js +12 -2
  100. package/src/context/index.js +18 -1
  101. package/src/context/sync.js +127 -0
  102. package/src/daemon/agentProcessManager.js +74 -0
  103. package/src/daemon/cronOps.js +241 -0
  104. package/src/daemon/index.js +662 -489
  105. package/src/daemon/ipcServer.js +99 -0
  106. package/src/daemon/ops.js +417 -179
  107. package/src/daemon/promptLoop.js +319 -0
  108. package/src/daemon/promptRequest.js +101 -0
  109. package/src/daemon/providerSessions.js +32 -17
  110. package/src/daemon/reporting.js +90 -0
  111. package/src/daemon/run.js +2 -5
  112. package/src/daemon/status.js +24 -1
  113. package/src/init/index.js +68 -14
  114. package/src/online/bridge.js +663 -0
  115. package/src/online/client.js +245 -0
  116. package/src/online/runner.js +253 -0
  117. package/src/online/server.js +992 -0
  118. package/src/online/tokens.js +103 -0
  119. package/src/report/store.js +331 -0
  120. package/src/shared/eventContract.js +35 -0
  121. package/src/shared/ptySocketContract.js +21 -0
  122. package/src/status/index.js +50 -17
  123. package/src/terminal/adapterContract.js +87 -0
  124. package/src/terminal/adapterRouter.js +84 -0
  125. package/src/terminal/adapters/externalAdapter.js +14 -0
  126. package/src/terminal/adapters/internalAdapter.js +13 -0
  127. package/src/terminal/adapters/internalPtyAdapter.js +42 -0
  128. package/src/terminal/adapters/internalQueueAdapter.js +37 -0
  129. package/src/terminal/adapters/terminalAdapter.js +31 -0
  130. package/src/terminal/adapters/tmuxAdapter.js +30 -0
  131. package/src/ufoo/agentsStore.js +69 -3
  132. package/src/utils/banner.js +5 -2
  133. package/scripts/.archived/bash-to-js-migration/README.md +0 -46
  134. package/scripts/.archived/bash-to-js-migration/banner.sh +0 -89
  135. package/scripts/.archived/bash-to-js-migration/bus-alert.sh +0 -6
  136. package/scripts/.archived/bash-to-js-migration/bus-autotrigger.sh +0 -6
  137. package/scripts/.archived/bash-to-js-migration/bus-daemon.sh +0 -231
  138. package/scripts/.archived/bash-to-js-migration/bus-inject.sh +0 -176
  139. package/scripts/.archived/bash-to-js-migration/bus-listen.sh +0 -6
  140. package/scripts/.archived/bash-to-js-migration/bus.sh +0 -986
  141. package/scripts/.archived/bash-to-js-migration/context-decisions.sh +0 -167
  142. package/scripts/.archived/bash-to-js-migration/context-doctor.sh +0 -72
  143. package/scripts/.archived/bash-to-js-migration/context-lint.sh +0 -110
  144. package/scripts/.archived/bash-to-js-migration/doctor.sh +0 -22
  145. package/scripts/.archived/bash-to-js-migration/init.sh +0 -247
  146. package/scripts/.archived/bash-to-js-migration/skills.sh +0 -113
  147. package/scripts/.archived/bash-to-js-migration/status.sh +0 -125
  148. package/scripts/banner.sh +0 -2
  149. package/src/bus/API_DESIGN.md +0 -204
@@ -34,9 +34,9 @@ fi
34
34
  **IMPORTANT**: Always check for existing subscriber ID first to avoid creating duplicates.
35
35
 
36
36
  ```bash
37
- # Check environment variable first (set by launcher/daemon)
38
- if [ -n "$UFOO_SUBSCRIBER_ID" ]; then
39
- SUBSCRIBER="$UFOO_SUBSCRIBER_ID"
37
+ # Reuse existing subscriber first (env -> whoami), join only if missing
38
+ SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
39
+ if [ -n "$SUBSCRIBER" ]; then
40
40
  echo "Using existing subscriber ID: $SUBSCRIBER"
41
41
  else
42
42
  # Not launched via uclaude/ucodex, need to join manually
@@ -48,7 +48,8 @@ fi
48
48
 
49
49
  **Why this matters**:
50
50
  - `uclaude`/`ucodex` automatically set `UFOO_SUBSCRIBER_ID` during launch
51
- - Re-joining creates a new ID, causing message loss
51
+ - `ufoo bus whoami` can recover current ID even when env is missing
52
+ - Re-joining may create identity drift and message routing issues
52
53
  - Always reuse existing ID when available
53
54
 
54
55
  To join with a custom nickname:
@@ -40,17 +40,19 @@ Create a new decision (recommended before replying when required):
40
40
  ufoo ctx decisions new "Short Title"
41
41
  ```
42
42
 
43
- **File naming:** `NNNN-short-title.md` (4-digit prefix + kebab-case slug).
43
+ **File naming:** `NNNN-<nickname>-short-title.md` (4-digit prefix + nickname + kebab-case slug).
44
44
 
45
45
  **Template for new decisions:**
46
46
  ```yaml
47
47
  ---
48
48
  status: open
49
+ nickname: <nickname>
49
50
  ---
50
51
  # DECISION NNNN: <Title>
51
52
 
52
53
  Date: YYYY-MM-DD
53
54
  Author: <agent>
55
+ Nickname: <nickname>
54
56
 
55
57
  Context:
56
58
  What led to this decision?
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: ufoo-online
3
+ description: |
4
+ Connect any agent to the ufoo-online WebSocket relay for public channel chat,
5
+ public rooms, or private room collaboration. Use when users ask to join ufoo online,
6
+ chat with other agents, or check inbox.
7
+ ---
8
+
9
+ # /ufoo-online - Online Relay Client
10
+
11
+ Connect to the ufoo-online WebSocket relay, join channels/rooms, send messages, and check inbox.
12
+
13
+ ## Quick Start
14
+
15
+ ### 1. Start a local relay server
16
+
17
+ ```bash
18
+ ufoo online server --port 8787
19
+ ```
20
+
21
+ ### 2. Connect (long-running, run in background)
22
+
23
+ ```bash
24
+ # Join a public channel
25
+ ufoo online connect --nickname my-agent --join lobby --ping-ms 15000
26
+
27
+ # Join a private room (enables bus/decisions/wake sync)
28
+ ufoo online connect --nickname my-agent --room room_001 --room-password secret --ping-ms 15000
29
+ ```
30
+
31
+ Use `run_in_background: true` to keep the connection alive in agent sessions.
32
+
33
+ ### 3. Send a message
34
+
35
+ ```bash
36
+ # Send to a channel
37
+ ufoo online send --nickname my-agent --channel lobby --text "hello everyone"
38
+
39
+ # Send to a room
40
+ ufoo online send --nickname my-agent --room room_001 --text "hello team"
41
+ ```
42
+
43
+ Messages are queued to the local outbox (`~/.ufoo/online/outbox/<nickname>.jsonl`)
44
+ and delivered by the running `connect` process. The connect process must be running
45
+ for messages to be sent.
46
+
47
+ ### 4. Check inbox
48
+
49
+ ```bash
50
+ # View all messages
51
+ ufoo online inbox my-agent
52
+
53
+ # View unread only
54
+ ufoo online inbox my-agent --unread
55
+
56
+ # Clear inbox
57
+ ufoo online inbox my-agent --clear
58
+ ```
59
+
60
+ Inbox retention: channel messages 7 days, room messages 30 days.
61
+
62
+ ## Full Connect Options
63
+
64
+ ```bash
65
+ ufoo online connect --nickname <name> [--url <ws://...>] [--subscriber <id>]
66
+ [--token <tok>] [--token-hash <hash>] [--world <name>] [--ping-ms <ms>]
67
+ [--join <channel>] [--room <room-id> --room-password <pwd>]
68
+ [--interval <ms>] [--allow-insecure-ws]
69
+ [--trust-remote] [--allow-from <subscriberId>]
70
+ ```
71
+
72
+ Features:
73
+ - Auto-reconnect with exponential backoff (500ms -> 8s)
74
+ - Auto-generates token if none exists; persists to `~/.ufoo/online/tokens.json`
75
+ - Incoming messages saved to `~/.ufoo/online/inbox/<nickname>.jsonl`
76
+ - Polls outbox for queued sends
77
+ - Prints all messages to stdout as JSON; prints `CONNECTED` on handshake
78
+ - Non-local `ws://` is blocked by default; use `wss://` or `--allow-insecure-ws`.
79
+ - **Private room mode** (`--room`): bus/decisions/wake sync is gated; use
80
+ `--trust-remote` or `--allow-from` to allow inbound sync.
81
+
82
+ ## Server Management
83
+
84
+ ```bash
85
+ # Start relay (dev mode — any token accepted)
86
+ ufoo online server --port 8787
87
+
88
+ # Start with token validation
89
+ ufoo online server --port 8787 --token-file ~/.ufoo/online/tokens.json
90
+
91
+ # Custom host/idle timeout
92
+ ufoo online server --host 0.0.0.0 --port 8787 --idle-timeout 60000
93
+ ```
94
+
95
+ ## Token Management
96
+
97
+ ```bash
98
+ ufoo online token <subscriber-id> --nickname <name> [--server <url>]
99
+ ```
100
+
101
+ Tokens are stored in `~/.ufoo/online/tokens.json`. The connect command
102
+ auto-resolves tokens by subscriber ID or nickname lookup.
103
+
104
+ ## Room & Channel Management
105
+
106
+ ```bash
107
+ # Channels (public broadcast, can join multiple)
108
+ ufoo online channel list [--server <url>]
109
+ ufoo online channel create --name <name> [--type world|public] [--server <url>]
110
+
111
+ # Rooms (collaboration, can join one)
112
+ ufoo online room list [--server <url>]
113
+ ufoo online room create --type public|private [--name <room>] [--password <pwd>] [--server <url>]
114
+ ```
115
+
116
+ If the relay requires auth, pass `--auth-token <token>` (or `--token-file` +
117
+ `--subscriber`/`--nickname`) to room/channel commands to send the Bearer token.
118
+
119
+ ## Usage Scenarios
120
+
121
+ ### 1. Public channel chat
122
+
123
+ ```bash
124
+ ufoo online server --port 8787 # Terminal 1
125
+ ufoo online connect --nickname agent-a --join lobby # Terminal 2 (background)
126
+ ufoo online connect --nickname agent-b --join lobby # Terminal 3 (background)
127
+ ufoo online send --nickname agent-a --channel lobby --text "hi all"
128
+ ufoo online inbox agent-b # See agent-a's message
129
+ ```
130
+
131
+ ### 2. Private room collaboration
132
+
133
+ ```bash
134
+ ufoo online room create --type private --password secret --server http://127.0.0.1:8787
135
+ # → returns room_id
136
+
137
+ ufoo online connect --nickname dev-1 --room room_001 --room-password secret
138
+ ufoo online connect --nickname dev-2 --room room_001 --room-password secret
139
+ ```
140
+
141
+ In private room mode, agents automatically sync:
142
+ - Bus messages (local bus <-> online relay, bidirectional)
143
+ - Decisions (new .md files synced across team)
144
+ - Wake events (remote agent can wake local agent via bus)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u-foo",
3
- "version": "1.0.6",
3
+ "version": "1.2.0",
4
4
  "description": "Multi-Agent Workspace Protocol. Just add u. claude → uclaude, codex → ucodex.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://ufoo.dev",
@@ -18,11 +18,16 @@
18
18
  "bin": {
19
19
  "ufoo": "bin/ufoo.js",
20
20
  "uclaude": "bin/uclaude.js",
21
- "ucodex": "bin/ucodex.js"
21
+ "ucodex": "bin/ucodex.js",
22
+ "ucode": "bin/ucode.js",
23
+ "ucode-core": "bin/ucode-core.js",
24
+ "ufoo-assistant-agent": "bin/ufoo-assistant-agent.js",
25
+ "ufoo-engine": "bin/ufoo-engine.js"
22
26
  },
23
27
  "files": [
24
28
  "bin/",
25
29
  "src/",
30
+ "online/",
26
31
  "scripts/",
27
32
  "SKILLS/",
28
33
  "modules/",
@@ -35,6 +40,7 @@
35
40
  },
36
41
  "scripts": {
37
42
  "postinstall": "node scripts/postinstall.js",
43
+ "import:pi-mono": "node scripts/import-pi-mono.js",
38
44
  "test": "jest",
39
45
  "test:watch": "jest --watch",
40
46
  "test:coverage": "jest --coverage"
@@ -44,7 +50,10 @@
44
50
  "chalk": "^4.1.2",
45
51
  "commander": "^13.1.0",
46
52
  "gray-matter": "^4.0.3",
47
- "node-pty": "^1.1.0"
53
+ "node-pty": "^1.1.0",
54
+ "ws": "^8.19.0",
55
+ "xterm-addon-serialize": "^0.11.0",
56
+ "xterm-headless": "^5.3.0"
48
57
  },
49
58
  "devDependencies": {
50
59
  "jest": "^30.2.0"
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { spawnSync } = require("child_process");
6
+
7
+ function usage() {
8
+ console.log("Usage: node scripts/import-pi-mono.js <pi-mono-source-path> [--target <target-path>]");
9
+ }
10
+
11
+ function shouldSkip(name = "") {
12
+ return name === ".git" || name === "node_modules";
13
+ }
14
+
15
+ function copyRecursive(source, target) {
16
+ const stat = fs.statSync(source);
17
+ if (stat.isDirectory()) {
18
+ fs.mkdirSync(target, { recursive: true });
19
+ const entries = fs.readdirSync(source);
20
+ for (const entry of entries) {
21
+ if (shouldSkip(entry)) continue;
22
+ copyRecursive(path.join(source, entry), path.join(target, entry));
23
+ }
24
+ return;
25
+ }
26
+ fs.mkdirSync(path.dirname(target), { recursive: true });
27
+ fs.copyFileSync(source, target);
28
+ }
29
+
30
+ function parseArgs(argv = []) {
31
+ const args = Array.isArray(argv) ? argv : [];
32
+ const parsed = {
33
+ source: "",
34
+ target: "",
35
+ help: false,
36
+ };
37
+ for (let i = 0; i < args.length; i += 1) {
38
+ const item = String(args[i] || "").trim();
39
+ if (!item) continue;
40
+ if (item === "--help" || item === "-h") {
41
+ parsed.help = true;
42
+ continue;
43
+ }
44
+ if (item === "--target" || item === "-t") {
45
+ const next = String(args[i + 1] || "").trim();
46
+ if (next) parsed.target = next;
47
+ i += 1;
48
+ continue;
49
+ }
50
+ if (!parsed.source) parsed.source = item;
51
+ }
52
+ return parsed;
53
+ }
54
+
55
+ function readGitValue(sourceRoot = "", gitArgs = []) {
56
+ try {
57
+ const res = spawnSync("git", ["-C", sourceRoot, ...gitArgs], {
58
+ encoding: "utf8",
59
+ stdio: ["ignore", "pipe", "ignore"],
60
+ });
61
+ if (res.error || res.status !== 0) return "";
62
+ return String(res.stdout || "").trim();
63
+ } catch {
64
+ return "";
65
+ }
66
+ }
67
+
68
+ function readGitMetadata(sourceRoot = "") {
69
+ return {
70
+ commit: readGitValue(sourceRoot, ["rev-parse", "HEAD"]),
71
+ branch: readGitValue(sourceRoot, ["rev-parse", "--abbrev-ref", "HEAD"]),
72
+ remote: readGitValue(sourceRoot, ["config", "--get", "remote.origin.url"]),
73
+ };
74
+ }
75
+
76
+ function main() {
77
+ const parsedArgs = parseArgs(process.argv.slice(2));
78
+ if (parsedArgs.help) {
79
+ usage();
80
+ process.exit(0);
81
+ }
82
+ const sourceArg = parsedArgs.source || "";
83
+ if (!sourceArg) {
84
+ usage();
85
+ process.exit(1);
86
+ }
87
+
88
+ const sourceRoot = path.resolve(sourceArg);
89
+ const sourcePackage = path.join(sourceRoot, "package.json");
90
+ if (!fs.existsSync(sourcePackage)) {
91
+ console.error(`Invalid source: missing package.json at ${sourceRoot}`);
92
+ process.exit(2);
93
+ }
94
+
95
+ const repoRoot = path.resolve(__dirname, "..");
96
+ const targetRoot = path.resolve(parsedArgs.target || path.join(repoRoot, "src", "code", "pi-mono"));
97
+ const backupRoot = `${targetRoot}.backup-${Date.now()}`;
98
+
99
+ if (fs.existsSync(targetRoot)) {
100
+ fs.renameSync(targetRoot, backupRoot);
101
+ console.log(`Backed up existing fork to ${backupRoot}`);
102
+ }
103
+
104
+ copyRecursive(sourceRoot, targetRoot);
105
+ const upstream = readGitMetadata(sourceRoot);
106
+
107
+ const metadata = {
108
+ imported_at: new Date().toISOString(),
109
+ source_root: sourceRoot,
110
+ target: targetRoot,
111
+ upstream_commit: upstream.commit,
112
+ upstream_branch: upstream.branch,
113
+ upstream_remote: upstream.remote,
114
+ };
115
+ fs.writeFileSync(
116
+ path.join(targetRoot, ".ufoo-import.json"),
117
+ `${JSON.stringify(metadata, null, 2)}\n`,
118
+ "utf8",
119
+ );
120
+
121
+ console.log(`Imported pi-mono into ${targetRoot}${upstream.commit ? ` @ ${upstream.commit}` : ""}`);
122
+ }
123
+
124
+ main();
@@ -1,59 +1,30 @@
1
1
  /* eslint-disable no-console */
2
2
  const path = require("path");
3
3
  const fs = require("fs");
4
- const { spawnSync } = require("child_process");
5
-
6
- function run(args) {
7
- const bin = path.join(__dirname, "..", "bin", "ufoo.js");
8
- const res = spawnSync(process.execPath, [bin, ...args], {
9
- stdio: "ignore",
10
- });
11
- return res.status === 0;
12
- }
13
-
14
- function tryInstall(args, label) {
15
- try {
16
- run(args);
17
- } catch (err) {
18
- console.warn(`[postinstall] ${label} failed: ${err.message || String(err)}`);
19
- }
20
- }
21
4
 
22
5
  // Fix node-pty spawn-helper permissions on macOS (both arm64 and x64)
23
- function fixNodePtyPermissions() {
24
- const platforms = ["darwin-arm64", "darwin-x64"];
6
+ const platforms = ["darwin-arm64", "darwin-x64"];
25
7
 
26
- for (const platform of platforms) {
27
- try {
28
- const spawnHelperPath = path.join(
29
- __dirname,
30
- "..",
31
- "node_modules",
32
- "node-pty",
33
- "prebuilds",
34
- platform,
35
- "spawn-helper"
36
- );
37
-
38
- if (fs.existsSync(spawnHelperPath)) {
39
- const stats = fs.statSync(spawnHelperPath);
40
- // Check if executable bit is missing
41
- if ((stats.mode & 0o111) === 0) {
42
- fs.chmodSync(spawnHelperPath, 0o755);
43
- console.log(`[postinstall] Fixed node-pty spawn-helper permissions (${platform})`);
44
- }
8
+ for (const platform of platforms) {
9
+ try {
10
+ const spawnHelperPath = path.join(
11
+ __dirname,
12
+ "..",
13
+ "node_modules",
14
+ "node-pty",
15
+ "prebuilds",
16
+ platform,
17
+ "spawn-helper"
18
+ );
19
+
20
+ if (fs.existsSync(spawnHelperPath)) {
21
+ const stats = fs.statSync(spawnHelperPath);
22
+ if ((stats.mode & 0o111) === 0) {
23
+ fs.chmodSync(spawnHelperPath, 0o755);
24
+ console.log(`[postinstall] Fixed node-pty spawn-helper permissions (${platform})`);
45
25
  }
46
- } catch {
47
- // Silently ignore errors - not critical for non-macOS or if node-pty not installed
48
26
  }
27
+ } catch {
28
+ // Silently ignore - not critical for non-macOS or if node-pty not installed
49
29
  }
50
30
  }
51
-
52
- fixNodePtyPermissions();
53
-
54
- const skills = ["ufoo", "ubus", "uctx"];
55
-
56
- for (const skill of skills) {
57
- tryInstall(["skills", "install", skill, "--codex"], `install ${skill} skill (codex)`);
58
- tryInstall(["skills", "install", skill], `install ${skill} skill (claude)`);
59
- }
@@ -0,0 +1,21 @@
1
+ #!/bin/bash
2
+ # Sync SKILLS to .claude/commands for Claude Code slash commands
3
+
4
+ set -e
5
+
6
+ SKILLS_DIR="SKILLS"
7
+ CLAUDE_COMMANDS_DIR=".claude/commands"
8
+
9
+ # Create commands directory if it doesn't exist
10
+ mkdir -p "$CLAUDE_COMMANDS_DIR"
11
+
12
+ # Copy all SKILL.md files to .claude/commands/
13
+ for skill_dir in "$SKILLS_DIR"/*; do
14
+ if [ -d "$skill_dir" ] && [ -f "$skill_dir/SKILL.md" ]; then
15
+ skill_name=$(basename "$skill_dir")
16
+ echo "Syncing $skill_name..."
17
+ cp "$skill_dir/SKILL.md" "$CLAUDE_COMMANDS_DIR/$skill_name.md"
18
+ fi
19
+ done
20
+
21
+ echo "Skills synced to Claude Code commands directory!"