u-foo 1.0.3 → 1.1.9

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 (179) hide show
  1. package/README.md +110 -11
  2. package/README.zh-CN.md +9 -7
  3. package/SKILLS/ufoo/SKILL.md +132 -0
  4. package/SKILLS/uinit/SKILL.md +78 -0
  5. package/SKILLS/ustatus/SKILL.md +36 -0
  6. package/bin/uclaude.js +13 -0
  7. package/bin/ucode-core.js +15 -0
  8. package/bin/ucode.js +125 -0
  9. package/bin/ucodex.js +13 -0
  10. package/bin/ufoo +9 -31
  11. package/bin/ufoo-assistant-agent.js +5 -0
  12. package/bin/ufoo-engine.js +25 -0
  13. package/bin/ufoo.js +17 -0
  14. package/modules/AGENTS.template.md +29 -11
  15. package/modules/bus/README.md +33 -25
  16. package/modules/bus/SKILLS/ubus/SKILL.md +19 -8
  17. package/modules/context/README.md +18 -40
  18. package/modules/context/SKILLS/uctx/SKILL.md +63 -1
  19. package/modules/online/SKILLS/ufoo-online/SKILL.md +144 -0
  20. package/package.json +25 -4
  21. package/scripts/import-pi-mono.js +124 -0
  22. package/scripts/postinstall.js +30 -0
  23. package/scripts/sync-claude-skills.sh +21 -0
  24. package/src/agent/cliRunner.js +554 -33
  25. package/src/agent/internalRunner.js +150 -56
  26. package/src/agent/launcher.js +754 -0
  27. package/src/agent/normalizeOutput.js +1 -1
  28. package/src/agent/notifier.js +340 -0
  29. package/src/agent/ptyRunner.js +847 -0
  30. package/src/agent/ptyWrapper.js +379 -0
  31. package/src/agent/readyDetector.js +175 -0
  32. package/src/agent/ucode.js +443 -0
  33. package/src/agent/ucodeBootstrap.js +113 -0
  34. package/src/agent/ucodeBuild.js +67 -0
  35. package/src/agent/ucodeDoctor.js +184 -0
  36. package/src/agent/ucodeRuntimeConfig.js +129 -0
  37. package/src/agent/ufooAgent.js +46 -42
  38. package/src/assistant/agent.js +260 -0
  39. package/src/assistant/bridge.js +172 -0
  40. package/src/assistant/engine.js +252 -0
  41. package/src/assistant/stdio.js +58 -0
  42. package/src/assistant/ufooEngineCli.js +306 -0
  43. package/src/bus/activate.js +172 -0
  44. package/src/bus/daemon.js +436 -0
  45. package/src/bus/index.js +842 -0
  46. package/src/bus/inject.js +315 -0
  47. package/src/bus/message.js +430 -0
  48. package/src/bus/nickname.js +88 -0
  49. package/src/bus/queue.js +136 -0
  50. package/src/bus/shake.js +26 -0
  51. package/src/bus/store.js +189 -0
  52. package/src/bus/subscriber.js +312 -0
  53. package/src/bus/utils.js +363 -0
  54. package/src/chat/agentBar.js +117 -0
  55. package/src/chat/agentDirectory.js +88 -0
  56. package/src/chat/agentSockets.js +225 -0
  57. package/src/chat/agentViewController.js +298 -0
  58. package/src/chat/chatLogController.js +115 -0
  59. package/src/chat/commandExecutor.js +700 -0
  60. package/src/chat/commands.js +132 -0
  61. package/src/chat/completionController.js +414 -0
  62. package/src/chat/cronScheduler.js +160 -0
  63. package/src/chat/daemonConnection.js +166 -0
  64. package/src/chat/daemonCoordinator.js +64 -0
  65. package/src/chat/daemonMessageRouter.js +257 -0
  66. package/src/chat/daemonReconnect.js +41 -0
  67. package/src/chat/daemonTransport.js +36 -0
  68. package/src/chat/daemonTransportDefaults.js +10 -0
  69. package/src/chat/dashboardKeyController.js +480 -0
  70. package/src/chat/dashboardView.js +154 -0
  71. package/src/chat/index.js +1011 -1392
  72. package/src/chat/inputHistoryController.js +105 -0
  73. package/src/chat/inputListenerController.js +304 -0
  74. package/src/chat/inputMath.js +104 -0
  75. package/src/chat/inputSubmitHandler.js +171 -0
  76. package/src/chat/layout.js +165 -0
  77. package/src/chat/pasteController.js +81 -0
  78. package/src/chat/rawKeyMap.js +42 -0
  79. package/src/chat/settingsController.js +132 -0
  80. package/src/chat/statusLineController.js +177 -0
  81. package/src/chat/streamTracker.js +138 -0
  82. package/src/chat/text.js +70 -0
  83. package/src/chat/transport.js +61 -0
  84. package/src/cli/busCoreCommands.js +59 -0
  85. package/src/cli/ctxCoreCommands.js +199 -0
  86. package/src/cli/onlineCoreCommands.js +379 -0
  87. package/src/cli.js +1162 -96
  88. package/src/code/README.md +29 -0
  89. package/src/code/UCODE_PROMPT.md +32 -0
  90. package/src/code/agent.js +1651 -0
  91. package/src/code/cli.js +158 -0
  92. package/src/code/config +0 -0
  93. package/src/code/dispatch.js +42 -0
  94. package/src/code/index.js +70 -0
  95. package/src/code/nativeRunner.js +1213 -0
  96. package/src/code/runtime.js +154 -0
  97. package/src/code/sessionStore.js +162 -0
  98. package/src/code/taskDecomposer.js +269 -0
  99. package/src/code/tools/bash.js +53 -0
  100. package/src/code/tools/common.js +42 -0
  101. package/src/code/tools/edit.js +70 -0
  102. package/src/code/tools/read.js +44 -0
  103. package/src/code/tools/write.js +35 -0
  104. package/src/code/tui.js +1580 -0
  105. package/src/config.js +56 -3
  106. package/src/context/decisions.js +324 -0
  107. package/src/context/doctor.js +183 -0
  108. package/src/context/index.js +55 -0
  109. package/src/context/sync.js +127 -0
  110. package/src/daemon/agentProcessManager.js +74 -0
  111. package/src/daemon/cronOps.js +241 -0
  112. package/src/daemon/index.js +998 -170
  113. package/src/daemon/ipcServer.js +99 -0
  114. package/src/daemon/ops.js +630 -48
  115. package/src/daemon/promptLoop.js +319 -0
  116. package/src/daemon/promptRequest.js +101 -0
  117. package/src/daemon/providerSessions.js +306 -0
  118. package/src/daemon/reporting.js +90 -0
  119. package/src/daemon/run.js +31 -1
  120. package/src/daemon/status.js +48 -8
  121. package/src/doctor/index.js +50 -0
  122. package/src/init/index.js +318 -0
  123. package/src/online/bridge.js +663 -0
  124. package/src/online/client.js +245 -0
  125. package/src/online/runner.js +253 -0
  126. package/src/online/server.js +992 -0
  127. package/src/online/tokens.js +103 -0
  128. package/src/report/store.js +331 -0
  129. package/src/shared/eventContract.js +35 -0
  130. package/src/shared/ptySocketContract.js +21 -0
  131. package/src/skills/index.js +159 -0
  132. package/src/status/index.js +285 -0
  133. package/src/terminal/adapterContract.js +87 -0
  134. package/src/terminal/adapterRouter.js +84 -0
  135. package/src/terminal/adapters/externalAdapter.js +14 -0
  136. package/src/terminal/adapters/internalAdapter.js +13 -0
  137. package/src/terminal/adapters/internalPtyAdapter.js +42 -0
  138. package/src/terminal/adapters/internalQueueAdapter.js +37 -0
  139. package/src/terminal/adapters/terminalAdapter.js +31 -0
  140. package/src/terminal/adapters/tmuxAdapter.js +30 -0
  141. package/src/terminal/detect.js +64 -0
  142. package/src/terminal/index.js +8 -0
  143. package/src/terminal/iterm2.js +126 -0
  144. package/src/ufoo/agentsStore.js +107 -0
  145. package/src/ufoo/paths.js +46 -0
  146. package/src/utils/banner.js +76 -0
  147. package/bin/uclaude +0 -65
  148. package/bin/ucodex +0 -65
  149. package/modules/bus/scripts/bus-alert.sh +0 -185
  150. package/modules/bus/scripts/bus-listen.sh +0 -117
  151. package/modules/context/ASSUMPTIONS.md +0 -7
  152. package/modules/context/CONSTRAINTS.md +0 -7
  153. package/modules/context/CONTEXT-STRUCTURE.md +0 -49
  154. package/modules/context/DECISION-PROTOCOL.md +0 -62
  155. package/modules/context/HANDOFF.md +0 -33
  156. package/modules/context/RULES.md +0 -15
  157. package/modules/context/SKILLS/README.md +0 -14
  158. package/modules/context/SYSTEM.md +0 -18
  159. package/modules/context/TEMPLATES/assumptions.md +0 -4
  160. package/modules/context/TEMPLATES/constraints.md +0 -4
  161. package/modules/context/TEMPLATES/decision.md +0 -16
  162. package/modules/context/TEMPLATES/project-context-readme.md +0 -6
  163. package/modules/context/TEMPLATES/system.md +0 -3
  164. package/modules/context/TEMPLATES/terminology.md +0 -4
  165. package/modules/context/TERMINOLOGY.md +0 -10
  166. package/scripts/banner.sh +0 -89
  167. package/scripts/bus-alert.sh +0 -6
  168. package/scripts/bus-autotrigger.sh +0 -6
  169. package/scripts/bus-daemon.sh +0 -231
  170. package/scripts/bus-inject.sh +0 -144
  171. package/scripts/bus-listen.sh +0 -6
  172. package/scripts/bus.sh +0 -984
  173. package/scripts/context-decisions.sh +0 -167
  174. package/scripts/context-doctor.sh +0 -72
  175. package/scripts/context-lint.sh +0 -110
  176. package/scripts/doctor.sh +0 -22
  177. package/scripts/init.sh +0 -247
  178. package/scripts/skills.sh +0 -113
  179. package/scripts/status.sh +0 -125
@@ -0,0 +1,443 @@
1
+ const { loadConfig } = require("../config");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+
5
+ function bundledModuleRoots() {
6
+ const repoRoot = path.join(__dirname, "..", "..");
7
+ return [
8
+ path.join(repoRoot, "src", "code"),
9
+ ];
10
+ }
11
+
12
+ function resolveFirstExisting(paths = []) {
13
+ for (const candidate of paths) {
14
+ if (!candidate) continue;
15
+ try {
16
+ if (fs.existsSync(candidate)) return candidate;
17
+ } catch {
18
+ // ignore
19
+ }
20
+ }
21
+ return "";
22
+ }
23
+
24
+ function defaultBundledCoreRoot() {
25
+ const root = path.join(__dirname, "..", "code");
26
+ const agentEntry = path.join(root, "agent.js");
27
+ if (resolveFirstExisting([agentEntry])) return root;
28
+ return root;
29
+ }
30
+
31
+ function defaultBundledPromptFile() {
32
+ const moduleRoots = bundledModuleRoots();
33
+ const candidates = moduleRoots.map((root) => path.join(root, "UCODE_PROMPT.md"));
34
+ return resolveFirstExisting(candidates) || candidates[0];
35
+ }
36
+
37
+ function isWindowsPlatform() {
38
+ return process.platform === "win32";
39
+ }
40
+
41
+ function canExecutePath(filePath = "") {
42
+ const target = String(filePath || "").trim();
43
+ if (!target) return false;
44
+ try {
45
+ const stat = fs.statSync(target);
46
+ if (!stat.isFile()) return false;
47
+ if (isWindowsPlatform()) return true;
48
+ fs.accessSync(target, fs.constants.X_OK);
49
+ return true;
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ function isReadableFile(filePath = "") {
56
+ const target = String(filePath || "").trim();
57
+ if (!target) return false;
58
+ try {
59
+ return fs.statSync(target).isFile();
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ function resolveExecutableFromPath(command = "", env = process.env) {
66
+ const text = String(command || "").trim();
67
+ if (!text) return "";
68
+ if (path.isAbsolute(text) || text.includes("/") || text.includes("\\")) {
69
+ return canExecutePath(text) ? path.resolve(text) : "";
70
+ }
71
+
72
+ const pathText = String((env && env.PATH) || process.env.PATH || "").trim();
73
+ if (!pathText) return "";
74
+ const dirs = pathText.split(path.delimiter).map((item) => String(item || "").trim()).filter(Boolean);
75
+ if (dirs.length === 0) return "";
76
+
77
+ const hasExplicitExt = /\.[a-zA-Z0-9]+$/.test(text);
78
+ const exts = isWindowsPlatform()
79
+ ? String((env && env.PATHEXT) || process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM")
80
+ .split(";")
81
+ .map((item) => item.trim())
82
+ .filter(Boolean)
83
+ : [""];
84
+ const suffixes = hasExplicitExt ? [""] : exts;
85
+
86
+ for (const dir of dirs) {
87
+ for (const ext of suffixes) {
88
+ const candidate = path.join(dir, `${text}${ext}`);
89
+ if (canExecutePath(candidate)) return candidate;
90
+ }
91
+ }
92
+ return "";
93
+ }
94
+
95
+ function tokenizeCommand(raw = "") {
96
+ const text = String(raw || "");
97
+ const tokens = [];
98
+ let current = "";
99
+ let quote = "";
100
+
101
+ for (let i = 0; i < text.length; i += 1) {
102
+ const ch = text[i];
103
+ if (quote) {
104
+ if (quote === "\"") {
105
+ if (ch === "\\") {
106
+ if (i + 1 < text.length) {
107
+ const next = text[i + 1];
108
+ if (next === "\"" || next === "\\") {
109
+ current += next;
110
+ i += 1;
111
+ continue;
112
+ }
113
+ }
114
+ current += "\\";
115
+ continue;
116
+ }
117
+ } else if (quote === "'" && ch === "\\") {
118
+ current += "\\";
119
+ continue;
120
+ }
121
+ if (ch === quote) {
122
+ quote = "";
123
+ continue;
124
+ }
125
+ current += ch;
126
+ continue;
127
+ }
128
+
129
+ if (ch === "'" || ch === "\"") {
130
+ quote = ch;
131
+ continue;
132
+ }
133
+ if (ch === "\\") {
134
+ if (i + 1 < text.length) {
135
+ const next = text[i + 1];
136
+ if (/\s/.test(next) || next === "'" || next === "\"" || next === "\\") {
137
+ current += next;
138
+ i += 1;
139
+ } else {
140
+ current += "\\";
141
+ }
142
+ } else {
143
+ current += "\\";
144
+ }
145
+ continue;
146
+ }
147
+ if (/\s/.test(ch)) {
148
+ if (current) {
149
+ tokens.push(current);
150
+ current = "";
151
+ }
152
+ continue;
153
+ }
154
+ current += ch;
155
+ }
156
+
157
+ if (quote) {
158
+ return String(raw || "").trim().split(/\s+/).filter(Boolean);
159
+ }
160
+ if (current) tokens.push(current);
161
+ return tokens;
162
+ }
163
+
164
+ function splitCommand(raw, fallback = "pi") {
165
+ const text = String(raw || "").trim();
166
+ if (!text) return { command: fallback, args: [] };
167
+ const parts = tokenizeCommand(text);
168
+ if (parts.length === 0) return { command: fallback, args: [] };
169
+ return { command: parts[0], args: parts.slice(1) };
170
+ }
171
+
172
+ function hasAnyArg(args = [], names = []) {
173
+ if (!Array.isArray(args) || args.length === 0) return false;
174
+ const flags = new Set((Array.isArray(names) ? names : []).filter(Boolean));
175
+ return args.some((arg) => {
176
+ const text = String(arg || "").trim();
177
+ if (!text) return false;
178
+ if (flags.has(text)) return true;
179
+ const eqIdx = text.indexOf("=");
180
+ if (eqIdx <= 0) return false;
181
+ const key = text.slice(0, eqIdx).trim();
182
+ return flags.has(key);
183
+ });
184
+ }
185
+
186
+ function pickBinEntry(binField = {}) {
187
+ if (typeof binField === "string" && binField.trim()) {
188
+ return binField.trim();
189
+ }
190
+ if (!binField || typeof binField !== "object") return "";
191
+ const entries = Object.entries(binField)
192
+ .filter(([, value]) => typeof value === "string" && value.trim());
193
+ if (entries.length === 0) return "";
194
+ const preferred = entries.find(([name]) => /^(ucode|core|cli)$/i.test(String(name)));
195
+ if (preferred) return preferred[1].trim();
196
+ return entries[0][1].trim();
197
+ }
198
+
199
+ function normalizeAppendSystemPromptMode(value = "") {
200
+ const text = String(value || "").trim().toLowerCase();
201
+ if (text === "always" || text === "force" || text === "on" || text === "1" || text === "true") return "always";
202
+ if (text === "never" || text === "off" || text === "0" || text === "false" || text === "disable") return "never";
203
+ return "auto";
204
+ }
205
+
206
+ function isLikelyPiCoreCommand(command = "", args = []) {
207
+ const cmdText = String(command || "").trim();
208
+ const cmdBase = path.basename(cmdText).toLowerCase();
209
+ if (cmdBase === "ucode" || cmdBase === "ucode.exe") return true;
210
+ if (cmdBase === "ucode-core" || cmdBase === "ucode-core.exe") return true;
211
+
212
+ const joined = [cmdText, ...(Array.isArray(args) ? args : [])]
213
+ .map((part) => String(part || "").toLowerCase())
214
+ .join(" ");
215
+ if (!joined) return false;
216
+ if (joined.includes("ucode-core")) return true;
217
+ if (joined.includes("/src/code/agent.js")) return true;
218
+ if (joined.includes("\\src\\code\\agent.js")) return true;
219
+ return false;
220
+ }
221
+
222
+ function readLastArgValue(args = [], flag = "") {
223
+ if (!Array.isArray(args) || !flag) return "";
224
+ let value = "";
225
+ for (let i = 0; i < args.length; i += 1) {
226
+ const item = String(args[i] || "").trim();
227
+ if (!item) continue;
228
+ if (item === flag) {
229
+ const next = String(args[i + 1] || "").trim();
230
+ if (next) value = next;
231
+ i += 1;
232
+ continue;
233
+ }
234
+ if (item.startsWith(`${flag}=`)) {
235
+ const inlineValue = item.slice(flag.length + 1).trim();
236
+ if (inlineValue) value = inlineValue;
237
+ }
238
+ }
239
+ return value;
240
+ }
241
+
242
+ function resolveCoreFromPath(coreRoot = "") {
243
+ const requestedRoot = String(coreRoot || "").trim();
244
+ if (!requestedRoot) return null;
245
+ let stat;
246
+ try {
247
+ stat = fs.statSync(requestedRoot);
248
+ } catch {
249
+ return null;
250
+ }
251
+ if (!stat.isDirectory()) return null;
252
+
253
+ const candidates = [
254
+ requestedRoot,
255
+ path.join(requestedRoot, "packages", "coding-agent"),
256
+ ];
257
+
258
+ for (const root of candidates) {
259
+ const packageFile = path.join(root, "package.json");
260
+ let pkg = null;
261
+ try {
262
+ pkg = JSON.parse(fs.readFileSync(packageFile, "utf8"));
263
+ } catch {
264
+ continue;
265
+ }
266
+ const binRel = pickBinEntry(pkg && pkg.bin ? pkg.bin : {});
267
+ if (!binRel) continue;
268
+ const binAbs = path.resolve(root, binRel);
269
+ if (!fs.existsSync(binAbs)) continue;
270
+ return {
271
+ command: process.execPath,
272
+ args: [binAbs],
273
+ root,
274
+ };
275
+ }
276
+ return null;
277
+ }
278
+
279
+ function resolveCandidateCoreRoot({
280
+ env = process.env,
281
+ config = {},
282
+ } = {}) {
283
+ // Native-only mode: external pi-mono path is no longer used as launch fallback.
284
+ // Keep function for compatibility with older diagnostic surfaces.
285
+ void env;
286
+ void config;
287
+ return null;
288
+ }
289
+
290
+ function resolveNativeFallbackCommand({ env = process.env } = {}) {
291
+ const candidates = [
292
+ path.resolve(__dirname, "..", "code", "agent.js"),
293
+ path.resolve(__dirname, "..", "..", "bin", "ucode-core.js"),
294
+ ];
295
+ for (const entry of candidates) {
296
+ try {
297
+ if (isReadableFile(entry)) {
298
+ if (entry.endsWith("agent.js")) {
299
+ return {
300
+ command: process.execPath,
301
+ args: [entry],
302
+ root: path.resolve(__dirname, "..", "code"),
303
+ kind: "native",
304
+ available: true,
305
+ resolvedPath: entry,
306
+ };
307
+ }
308
+ return {
309
+ command: process.execPath,
310
+ args: [entry, "agent"],
311
+ root: path.resolve(__dirname, "..", "code"),
312
+ kind: "native",
313
+ available: true,
314
+ resolvedPath: entry,
315
+ };
316
+ }
317
+ } catch {
318
+ // ignore
319
+ }
320
+ }
321
+ const resolvedCommand = resolveExecutableFromPath("ucode-core", env);
322
+ if (resolvedCommand) {
323
+ return {
324
+ command: "ucode-core",
325
+ args: ["agent"],
326
+ root: "",
327
+ kind: "native",
328
+ available: true,
329
+ resolvedPath: resolvedCommand,
330
+ };
331
+ }
332
+ return {
333
+ command: "ucode-core",
334
+ args: ["agent"],
335
+ root: "",
336
+ kind: "native",
337
+ available: false,
338
+ resolvedPath: "",
339
+ missingReason: "src/code/agent.js not found and ucode-core is not available on PATH",
340
+ };
341
+ }
342
+
343
+ function resolveUcodeLaunch({
344
+ argv = [],
345
+ env = process.env,
346
+ cwd = process.cwd(),
347
+ loadConfigImpl = loadConfig,
348
+ } = {}) {
349
+ const config = loadConfigImpl(cwd);
350
+ const configuredProvider = String(
351
+ env.UFOO_UCODE_PROVIDER
352
+ || config.ucodeProvider
353
+ || ""
354
+ ).trim();
355
+ const configuredModel = String(
356
+ env.UFOO_UCODE_MODEL
357
+ || config.ucodeModel
358
+ || ""
359
+ ).trim();
360
+
361
+ const nativeCore = resolveNativeFallbackCommand({ env });
362
+ const command = nativeCore.command;
363
+ const baseArgs = Array.isArray(nativeCore.args) ? nativeCore.args.slice() : [];
364
+ const passthrough = Array.isArray(argv) ? argv.slice() : [];
365
+ const finalArgs = [...baseArgs, ...passthrough];
366
+ const hasProviderArg = hasAnyArg(finalArgs, ["--provider"]);
367
+ const hasModelArg = hasAnyArg(finalArgs, ["--model"]);
368
+ if (!hasProviderArg && configuredProvider) finalArgs.push("--provider", configuredProvider);
369
+ if (!hasModelArg && configuredModel) finalArgs.push("--model", configuredModel);
370
+ const promptFile = String(
371
+ env.UFOO_UCODE_PROMPT_FILE
372
+ || config.ucodePromptFile
373
+ || defaultBundledPromptFile()
374
+ ).trim();
375
+ const bootstrapFile = String(
376
+ env.UFOO_UCODE_BOOTSTRAP_FILE
377
+ || config.ucodeBootstrapFile
378
+ || path.join(cwd || process.cwd(), ".ufoo", "agent", "ucode", "bootstrap.md")
379
+ ).trim();
380
+ const appendSystemPrompt = String(
381
+ env.UFOO_UCODE_APPEND_SYSTEM_PROMPT
382
+ || config.ucodeAppendSystemPrompt
383
+ || bootstrapFile
384
+ ).trim();
385
+ const appendSystemPromptMode = normalizeAppendSystemPromptMode(
386
+ env.UFOO_UCODE_APPEND_SYSTEM_PROMPT_MODE
387
+ || config.ucodeAppendSystemPromptMode
388
+ || "auto"
389
+ );
390
+ const hasSystemPromptArg = hasAnyArg(finalArgs, ["--system-prompt", "--append-system-prompt"]);
391
+ const appendSupported = appendSystemPromptMode === "always"
392
+ || (
393
+ appendSystemPromptMode === "auto"
394
+ && (
395
+ nativeCore.kind === "native"
396
+ || isLikelyPiCoreCommand(command, finalArgs)
397
+ )
398
+ );
399
+ if (!hasSystemPromptArg && appendSystemPrompt && appendSystemPromptMode !== "never" && appendSupported) {
400
+ finalArgs.push("--append-system-prompt", appendSystemPrompt);
401
+ }
402
+ const effectiveProvider = readLastArgValue(finalArgs, "--provider");
403
+ const effectiveModel = readLastArgValue(finalArgs, "--model");
404
+
405
+ return {
406
+ agentType: "ufoo-code",
407
+ command,
408
+ args: finalArgs,
409
+ env: {
410
+ UFOO_UCODE_PROMPT_FILE: promptFile,
411
+ UFOO_UCODE_PROJECT_ROOT: String(cwd || process.cwd()),
412
+ UFOO_UCODE_MODE: "coding-agent",
413
+ UFOO_UCODE_PROTOCOL_VERSION: "1",
414
+ UFOO_UCODE_PROVIDER: effectiveProvider,
415
+ UFOO_UCODE_MODEL: effectiveModel,
416
+ UFOO_UCODE_CORE_ROOT: nativeCore.root || "",
417
+ UFOO_UCODE_CORE_KIND: "native",
418
+ UFOO_UCODE_CORE_AVAILABLE: nativeCore.available === false ? "0" : "1",
419
+ UFOO_UCODE_BOOTSTRAP_FILE: bootstrapFile,
420
+ UFOO_UCODE_APPEND_SYSTEM_PROMPT: appendSystemPrompt,
421
+ UFOO_UCODE_APPEND_SYSTEM_PROMPT_MODE: appendSystemPromptMode,
422
+ },
423
+ };
424
+ }
425
+
426
+ module.exports = {
427
+ bundledModuleRoots,
428
+ defaultBundledCoreRoot,
429
+ defaultBundledPromptFile,
430
+ tokenizeCommand,
431
+ splitCommand,
432
+ hasAnyArg,
433
+ pickBinEntry,
434
+ normalizeAppendSystemPromptMode,
435
+ isLikelyPiCoreCommand,
436
+ readLastArgValue,
437
+ resolveCoreFromPath,
438
+ resolveCandidateCoreRoot,
439
+ canExecutePath,
440
+ resolveExecutableFromPath,
441
+ resolveNativeFallbackCommand,
442
+ resolveUcodeLaunch,
443
+ };
@@ -0,0 +1,113 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { getUfooPaths } = require("../ufoo/paths");
4
+
5
+ function readFileSafe(filePath = "") {
6
+ if (!filePath) return "";
7
+ try {
8
+ return fs.readFileSync(filePath, "utf8");
9
+ } catch {
10
+ return "";
11
+ }
12
+ }
13
+
14
+ function resolveProjectRules(projectRoot = "") {
15
+ const rules = [];
16
+ const agentsFile = path.join(projectRoot, "AGENTS.md");
17
+ const claudeFile = path.join(projectRoot, "CLAUDE.md");
18
+
19
+ if (fs.existsSync(agentsFile)) {
20
+ rules.push({ path: agentsFile, content: readFileSafe(agentsFile) });
21
+ } else if (fs.existsSync(claudeFile)) {
22
+ rules.push({ path: claudeFile, content: readFileSafe(claudeFile) });
23
+ }
24
+ return rules.filter((item) => item.content.trim());
25
+ }
26
+
27
+ function defaultBootstrapPath(projectRoot = "") {
28
+ const dir = path.join(getUfooPaths(projectRoot).agentDir, "ucode");
29
+ return path.join(dir, "bootstrap.md");
30
+ }
31
+
32
+ function buildBootstrapContent({
33
+ projectRoot = "",
34
+ promptFile = "",
35
+ promptText = "",
36
+ rules = [],
37
+ } = {}) {
38
+ const lines = [];
39
+ lines.push("# ucode Bootstrap");
40
+ lines.push("");
41
+ lines.push(`Generated at: ${new Date().toISOString()}`);
42
+ lines.push(`Project root: ${projectRoot}`);
43
+ lines.push("");
44
+ if (promptFile) {
45
+ lines.push(`Source prompt: ${promptFile}`);
46
+ lines.push("");
47
+ }
48
+
49
+ if (promptText.trim()) {
50
+ lines.push("## Core Prompt");
51
+ lines.push("");
52
+ lines.push(promptText.trim());
53
+ lines.push("");
54
+ }
55
+
56
+ if (rules.length > 0) {
57
+ lines.push("## Project Rules");
58
+ lines.push("");
59
+ for (const rule of rules) {
60
+ lines.push(`### ${rule.path}`);
61
+ lines.push("");
62
+ lines.push(rule.content.trim());
63
+ lines.push("");
64
+ }
65
+ }
66
+
67
+ if (rules.length === 0) {
68
+ lines.push("## Project Rules");
69
+ lines.push("");
70
+ lines.push("No AGENTS.md/CLAUDE.md rules detected.");
71
+ lines.push("");
72
+ }
73
+
74
+ return `${lines.join("\n")}\n`;
75
+ }
76
+
77
+ function prepareUcodeBootstrap({
78
+ projectRoot = process.cwd(),
79
+ promptFile = "",
80
+ targetFile = "",
81
+ } = {}) {
82
+ const resolvedProjectRoot = path.resolve(projectRoot);
83
+ const resolvedPrompt = String(promptFile || "").trim();
84
+ const resolvedTarget = String(targetFile || "").trim() || defaultBootstrapPath(resolvedProjectRoot);
85
+
86
+ const promptText = readFileSafe(resolvedPrompt);
87
+ const rules = resolveProjectRules(resolvedProjectRoot);
88
+ const content = buildBootstrapContent({
89
+ projectRoot: resolvedProjectRoot,
90
+ promptFile: resolvedPrompt,
91
+ promptText,
92
+ rules,
93
+ });
94
+
95
+ fs.mkdirSync(path.dirname(resolvedTarget), { recursive: true });
96
+ fs.writeFileSync(resolvedTarget, content, "utf8");
97
+
98
+ return {
99
+ ok: true,
100
+ file: resolvedTarget,
101
+ promptFile: resolvedPrompt,
102
+ hasPrompt: Boolean(promptText.trim()),
103
+ rulesCount: rules.length,
104
+ };
105
+ }
106
+
107
+ module.exports = {
108
+ readFileSafe,
109
+ resolveProjectRules,
110
+ defaultBootstrapPath,
111
+ buildBootstrapContent,
112
+ prepareUcodeBootstrap,
113
+ };
@@ -0,0 +1,67 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { loadConfig } = require("../config");
4
+ const {
5
+ resolveNativeFallbackCommand,
6
+ } = require("./ucode");
7
+
8
+ function resolveCoreRoot({
9
+ env = process.env,
10
+ config = {},
11
+ } = {}) {
12
+ void env;
13
+ void config;
14
+ const native = resolveNativeFallbackCommand();
15
+ return native.root || path.resolve(__dirname, "..", "code");
16
+ }
17
+
18
+ function inspectUcodeBuildSetup({
19
+ projectRoot = process.cwd(),
20
+ env = process.env,
21
+ loadConfigImpl = loadConfig,
22
+ } = {}) {
23
+ const root = path.resolve(projectRoot);
24
+ const config = loadConfigImpl(root);
25
+ const coreRoot = resolveCoreRoot({ env, config });
26
+ const native = resolveNativeFallbackCommand();
27
+ const workspaceRoot = root;
28
+ const distCliPath = Array.isArray(native.args) && native.args[0] ? path.resolve(native.args[0]) : "";
29
+ const distCliExists = Boolean(distCliPath && fs.existsSync(distCliPath));
30
+ const nodeModulesPath = "";
31
+ return {
32
+ projectRoot: root,
33
+ coreRoot,
34
+ workspaceRoot,
35
+ distCliPath,
36
+ distCliExists,
37
+ nodeModulesPath,
38
+ nodeModulesExists: Boolean(nodeModulesPath && fs.existsSync(nodeModulesPath)),
39
+ };
40
+ }
41
+
42
+ function buildUcodeCore({
43
+ projectRoot = process.cwd(),
44
+ env = process.env,
45
+ loadConfigImpl = loadConfig,
46
+ installIfMissing = true,
47
+ stdio = "inherit",
48
+ } = {}) {
49
+ const before = inspectUcodeBuildSetup({
50
+ projectRoot,
51
+ env,
52
+ loadConfigImpl,
53
+ });
54
+ void installIfMissing;
55
+ void stdio;
56
+ void env;
57
+ if (!before.distCliExists) {
58
+ throw new Error(`ucode native core entry missing: ${before.distCliPath || "(unknown)"}`);
59
+ }
60
+ return { ...before, steps: ["native-check"] };
61
+ }
62
+
63
+ module.exports = {
64
+ inspectUcodeBuildSetup,
65
+ buildUcodeCore,
66
+ resolveCoreRoot,
67
+ };