u-foo 2.1.0 → 2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u-foo",
3
- "version": "2.1.0",
3
+ "version": "2.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",
@@ -26,6 +26,16 @@ function hasMetaCommandArgs(args = []) {
26
26
  return hasArg(args, ["-h", "--help", "-v", "--version"]);
27
27
  }
28
28
 
29
+ function readOptionalFile(filePath) {
30
+ const target = asTrimmedString(filePath);
31
+ if (!target) return "";
32
+ try {
33
+ return fs.readFileSync(target, "utf8");
34
+ } catch {
35
+ return "";
36
+ }
37
+ }
38
+
29
39
  /**
30
40
  * Load the team activity timeline for prompt injection.
31
41
  * The daemon syncs manual inputs every ~30s; bus messages are appended in real-time.
@@ -67,6 +77,11 @@ function defaultBootstrapFile(projectRoot, agentType = "") {
67
77
  return path.join(getUfooPaths(projectRoot).agentDir, safeAgentType, "default-bootstrap.md");
68
78
  }
69
79
 
80
+ function mergedBootstrapFile(projectRoot, agentType = "") {
81
+ const safeAgentType = asTrimmedString(agentType).replace(/[^a-zA-Z0-9._-]/g, "-") || "agent";
82
+ return path.join(getUfooPaths(projectRoot).agentDir, safeAgentType, "merged-bootstrap.md");
83
+ }
84
+
70
85
  function prepareDefaultBootstrapFile({
71
86
  projectRoot,
72
87
  agentType = "",
@@ -80,6 +95,83 @@ function prepareDefaultBootstrapFile({
80
95
  return { ok: true, file };
81
96
  }
82
97
 
98
+ function mergePromptSegments(...segments) {
99
+ return segments
100
+ .map((item) => String(item || "").trim())
101
+ .filter(Boolean)
102
+ .join("\n\n");
103
+ }
104
+
105
+ function mergeClaudePromptArgs({
106
+ projectRoot,
107
+ agentType = "claude-code",
108
+ args = [],
109
+ bootstrapText = "",
110
+ } = {}) {
111
+ const currentArgs = Array.isArray(args) ? args.slice() : [];
112
+ for (let index = 0; index < currentArgs.length; index += 1) {
113
+ const item = asTrimmedString(currentArgs[index]);
114
+ if (!item) continue;
115
+
116
+ if (item === "--append-system-prompt") {
117
+ const existingFile = asTrimmedString(currentArgs[index + 1]);
118
+ const mergedText = mergePromptSegments(readOptionalFile(existingFile), bootstrapText);
119
+ const prepared = prepareDefaultBootstrapFile({
120
+ projectRoot,
121
+ agentType,
122
+ targetFile: mergedBootstrapFile(projectRoot, agentType),
123
+ promptText: mergedText,
124
+ });
125
+ currentArgs[index + 1] = prepared.file;
126
+ return { args: currentArgs, file: prepared.file, promptText: mergedText };
127
+ }
128
+
129
+ if (item.startsWith("--append-system-prompt=")) {
130
+ const existingFile = item.slice("--append-system-prompt=".length);
131
+ const mergedText = mergePromptSegments(readOptionalFile(existingFile), bootstrapText);
132
+ const prepared = prepareDefaultBootstrapFile({
133
+ projectRoot,
134
+ agentType,
135
+ targetFile: mergedBootstrapFile(projectRoot, agentType),
136
+ promptText: mergedText,
137
+ });
138
+ currentArgs[index] = `--append-system-prompt=${prepared.file}`;
139
+ return { args: currentArgs, file: prepared.file, promptText: mergedText };
140
+ }
141
+
142
+ if (item === "--system-prompt") {
143
+ const existingPrompt = String(currentArgs[index + 1] || "");
144
+ currentArgs[index + 1] = mergePromptSegments(existingPrompt, bootstrapText);
145
+ return { args: currentArgs, file: "", promptText: String(currentArgs[index + 1] || "") };
146
+ }
147
+
148
+ if (item.startsWith("--system-prompt=")) {
149
+ const existingPrompt = item.slice("--system-prompt=".length);
150
+ const mergedText = mergePromptSegments(existingPrompt, bootstrapText);
151
+ currentArgs[index] = `--system-prompt=${mergedText}`;
152
+ return { args: currentArgs, file: "", promptText: mergedText };
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+
158
+ function mergeCodexPromptArgs({ args = [], bootstrapText = "" } = {}) {
159
+ const currentArgs = Array.isArray(args) ? args.slice() : [];
160
+ const lastIndex = currentArgs.length - 1;
161
+ if (lastIndex < 0) return null;
162
+ const lastItem = asTrimmedString(currentArgs[lastIndex]);
163
+ const promptIndex = lastItem && !lastItem.startsWith("-") ? lastIndex : -1;
164
+
165
+ if (promptIndex < 0) return null;
166
+
167
+ currentArgs[promptIndex] = mergePromptSegments(bootstrapText, currentArgs[promptIndex]);
168
+ return {
169
+ args: currentArgs,
170
+ promptText: String(currentArgs[promptIndex] || ""),
171
+ promptIndex,
172
+ };
173
+ }
174
+
83
175
  function resolveDefaultManualBootstrap({
84
176
  projectRoot,
85
177
  agentType = "",
@@ -98,10 +190,22 @@ function resolveDefaultManualBootstrap({
98
190
  }
99
191
 
100
192
  if (normalizedAgent === "claude-code") {
101
- if (hasArg(currentArgs, ["--append-system-prompt", "--system-prompt"])) {
102
- return { args: currentArgs, env: {}, mode: "skip" };
103
- }
104
193
  const promptText = buildDefaultStartupBootstrapPrompt({ agentType: normalizedAgent, projectRoot });
194
+ const merged = mergeClaudePromptArgs({
195
+ projectRoot,
196
+ agentType: normalizedAgent,
197
+ args: currentArgs,
198
+ bootstrapText: promptText,
199
+ });
200
+ if (merged) {
201
+ return {
202
+ args: merged.args,
203
+ env: {},
204
+ mode: "merged-system-prompt",
205
+ file: merged.file,
206
+ promptText: merged.promptText,
207
+ };
208
+ }
105
209
  const prepared = prepareDefaultBootstrapFile({
106
210
  projectRoot,
107
211
  agentType: normalizedAgent,
@@ -117,10 +221,29 @@ function resolveDefaultManualBootstrap({
117
221
  }
118
222
 
119
223
  if (normalizedAgent === "codex") {
224
+ const promptText = buildDefaultStartupBootstrapPrompt({ agentType: normalizedAgent, projectRoot });
225
+ const merged = mergeCodexPromptArgs({
226
+ args: currentArgs,
227
+ bootstrapText: promptText,
228
+ });
229
+ if (merged) {
230
+ return {
231
+ args: merged.args,
232
+ env: {},
233
+ mode: "initial-prompt-arg",
234
+ promptText: merged.promptText,
235
+ };
236
+ }
120
237
  if (currentArgs.length > 0) {
121
- return { args: currentArgs, env: {}, mode: "skip" };
238
+ return {
239
+ args: currentArgs,
240
+ env: {
241
+ UFOO_STARTUP_BOOTSTRAP_TEXT: promptText,
242
+ },
243
+ mode: "post-launch-inject",
244
+ promptText,
245
+ };
122
246
  }
123
- const promptText = buildDefaultStartupBootstrapPrompt({ agentType: normalizedAgent, projectRoot });
124
247
  return {
125
248
  args: currentArgs,
126
249
  env: {