@fieldwangai/agentflow 0.1.34 → 0.1.35

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.
@@ -711,6 +711,14 @@ function truncateComposerLine(s) {
711
711
  return t.slice(0, COMPOSER_STATUS_MAX - 1) + "…";
712
712
  }
713
713
 
714
+ const RAW_TRACE_MAX_CHARS = 4096;
715
+
716
+ function rawTraceText(value) {
717
+ const text = typeof value === "string" ? value : JSON.stringify(value);
718
+ if (!text) return "";
719
+ return text.length > RAW_TRACE_MAX_CHARS ? text.slice(0, RAW_TRACE_MAX_CHARS) + "\n...[truncated]" : text;
720
+ }
721
+
714
722
  function normalizeStreamTextChunk(t) {
715
723
  if (!t || typeof t !== "string") return "";
716
724
  return t.replace(/\\n/g, "\n").replace(/\\t/g, "\t");
@@ -728,6 +736,7 @@ function extractCursorStreamNlText(event) {
728
736
  }
729
737
  if (typeof event.text === "string" && event.text.trim()) return normalizeStreamTextChunk(event.text);
730
738
  if (typeof event.thinking === "string" && event.thinking.trim()) return normalizeStreamTextChunk(event.thinking);
739
+ if (typeof event.delta === "string" && event.delta.trim()) return normalizeStreamTextChunk(event.delta);
731
740
  return "";
732
741
  }
733
742
 
@@ -864,6 +873,7 @@ export function runCursorAgentWithPrompt(cliWorkspace, promptText, options = {})
864
873
  for (const line of lines) {
865
874
  try {
866
875
  const event = JSON.parse(line);
876
+ emit({ type: "raw", source: "cursor", stream: "stdout", eventType: event?.type || "unknown", text: rawTraceText(event) });
867
877
  if (event.type === "assistant" && event.message?.content) {
868
878
  const text = extractCursorStreamNlText(event);
869
879
  if (text) {
@@ -903,6 +913,7 @@ export function runCursorAgentWithPrompt(cliWorkspace, promptText, options = {})
903
913
  emit({ type: "status", line: `${t("runner.event_label")}: ${event.type ?? "unknown"}` });
904
914
  }
905
915
  } catch (_) {
916
+ emit({ type: "raw", source: "cursor", stream: "stdout", eventType: "line", text: rawTraceText(line) });
906
917
  if (line.includes('"type":"tool_call"') || line.includes('"type": "tool_call"')) {
907
918
  let subtype = "?";
908
919
  try {
@@ -1017,6 +1028,7 @@ export function runOpenCodeAgentWithPrompt(cliWorkspace, promptText, options = {
1017
1028
  const line = outBuf.slice(0, idx);
1018
1029
  outBuf = outBuf.slice(idx + 1);
1019
1030
  if (line) {
1031
+ emit({ type: "raw", source: "opencode", stream: "stdout", eventType: "line", text: rawTraceText(line) });
1020
1032
  tryEmitOpenCodeLineAsNatural(line, emit);
1021
1033
  emit({ type: "status", line: `[stdout] ${truncateComposerLine(line)}` });
1022
1034
  }
@@ -1032,6 +1044,7 @@ export function runOpenCodeAgentWithPrompt(cliWorkspace, promptText, options = {
1032
1044
  const line = errBuf.slice(0, idx);
1033
1045
  errBuf = errBuf.slice(idx + 1);
1034
1046
  if (line) {
1047
+ emit({ type: "raw", source: "opencode", stream: "stderr", eventType: "line", text: rawTraceText(line) });
1035
1048
  tryEmitOpenCodeLineAsNatural(line, emit);
1036
1049
  emit({ type: "status", line: `[stderr] ${truncateComposerLine(line)}` });
1037
1050
  }
@@ -1051,10 +1064,12 @@ export function runOpenCodeAgentWithPrompt(cliWorkspace, promptText, options = {
1051
1064
  child.stderr.removeAllListeners();
1052
1065
  child.removeAllListeners();
1053
1066
  if (outBuf.trim()) {
1067
+ emit({ type: "raw", source: "opencode", stream: "stdout", eventType: "tail", text: rawTraceText(outBuf.trim()) });
1054
1068
  tryEmitOpenCodeLineAsNatural(outBuf.trim(), emit);
1055
1069
  emit({ type: "status", line: truncateComposerLine(outBuf.trim()) });
1056
1070
  }
1057
1071
  if (errBuf.trim()) {
1072
+ emit({ type: "raw", source: "opencode", stream: "stderr", eventType: "tail", text: rawTraceText(errBuf.trim()) });
1058
1073
  tryEmitOpenCodeLineAsNatural(errBuf.trim(), emit);
1059
1074
  emit({ type: "status", line: `[opencode_stderr] ${truncateComposerLine(errBuf.trim())}` });
1060
1075
  }
@@ -1151,6 +1166,7 @@ export function runClaudeCodeAgentWithPrompt(cliWorkspace, promptText, options =
1151
1166
  for (const line of lines) {
1152
1167
  try {
1153
1168
  const event = JSON.parse(line);
1169
+ emit({ type: "raw", source: "claude-code", stream: "stdout", eventType: event?.type || "unknown", text: rawTraceText(event) });
1154
1170
  if (event.type === "assistant" && event.message && Array.isArray(event.message.content)) {
1155
1171
  for (const block of event.message.content) {
1156
1172
  if (!block || typeof block !== "object") continue;
@@ -1196,6 +1212,7 @@ export function runClaudeCodeAgentWithPrompt(cliWorkspace, promptText, options =
1196
1212
  emit({ type: "status", line: `${t("runner.event_label")}: ${event.type ?? "unknown"}` });
1197
1213
  }
1198
1214
  } catch (_) {
1215
+ emit({ type: "raw", source: "claude-code", stream: "stdout", eventType: "line", text: rawTraceText(line) });
1199
1216
  emit({ type: "status", line: truncateComposerLine(line) });
1200
1217
  }
1201
1218
  }
@@ -106,6 +106,7 @@ export function buildScriptContentBlockForInstances(flowYamlAbs, instanceIds) {
106
106
  * @param {string} [opts.modelKey]
107
107
  * @param {boolean} [opts.force]
108
108
  * @param {(ev: object) => void} [opts.onStreamEvent]
109
+ * @param {(subtype: string, toolName: string) => void} [opts.onToolCall]
109
110
  * @returns {{ child: import('child_process').ChildProcess, finished: Promise<void> }}
110
111
  */
111
112
  export function startComposerAgent(opts) {
@@ -123,6 +124,7 @@ export function startComposerAgent(opts) {
123
124
 
124
125
  const common = {
125
126
  onStreamEvent: opts.onStreamEvent,
127
+ onToolCall: opts.onToolCall,
126
128
  force: Boolean(opts.force),
127
129
  env,
128
130
  };
@@ -13,7 +13,11 @@
13
13
  */
14
14
  import fs from "fs";
15
15
  import path from "path";
16
- import { listSkills as registryListSkills, readSkillDetail as registryReadSkillDetail } from "./skill-registry.mjs";
16
+ import {
17
+ listSkills as registryListSkills,
18
+ listUniqueSkills as registryListUniqueSkills,
19
+ readSkillDetail as registryReadSkillDetail,
20
+ } from "./skill-registry.mjs";
17
21
 
18
22
  // ─── 意图模式定义 ─────────────────────────────────────────────────────────
19
23
 
@@ -126,7 +130,7 @@ function readFileCached(absPath) {
126
130
  }
127
131
 
128
132
  export function listComposerSkills(packageRoot, workspaceRoot) {
129
- return registryListSkills(packageRoot, workspaceRoot).map(({ body, content, ...skill }) => skill);
133
+ return registryListUniqueSkills(packageRoot, workspaceRoot).map(({ body, content, ...skill }) => skill);
130
134
  }
131
135
 
132
136
  export function readComposerSkillDetail(packageRoot, workspaceRoot, keyOrName) {
@@ -140,9 +144,24 @@ export function loadResourcesForSkillKeys(skillKeys, packageRoot, workspaceRoot)
140
144
  const wanted = new Set(skillKeys.map((x) => String(x || "").trim()).filter(Boolean));
141
145
  if (wanted.size === 0) return { skills: [], references: [], skillsHint: "", hasContext: false };
142
146
 
143
- const skills = [];
144
- for (const item of registryListSkills(packageRoot, workspaceRoot)) {
147
+ const exactByKey = new Map(registryListSkills(packageRoot, workspaceRoot).map((item) => [item.key, item]));
148
+ const candidateItems = [];
149
+ const seenKeys = new Set();
150
+ for (const item of registryListUniqueSkills(packageRoot, workspaceRoot)) {
145
151
  if (!wanted.has(item.key) && !wanted.has(item.name)) continue;
152
+ candidateItems.push(item);
153
+ seenKeys.add(item.key);
154
+ }
155
+ for (const key of wanted) {
156
+ const exact = exactByKey.get(key);
157
+ if (exact && !seenKeys.has(exact.key)) {
158
+ candidateItems.push(exact);
159
+ seenKeys.add(exact.key);
160
+ }
161
+ }
162
+
163
+ const skills = [];
164
+ for (const item of candidateItems) {
146
165
  skills.push({
147
166
  id: item.name,
148
167
  content: item.body,
@@ -5,6 +5,15 @@ import yaml from "js-yaml";
5
5
 
6
6
  const fileCache = new Map();
7
7
  const CACHE_TTL_MS = 60_000;
8
+ const SOURCE_PRIORITY = new Map([
9
+ ["workspace-agents", 100],
10
+ ["workspace-codex", 95],
11
+ ["workspace-cursor", 90],
12
+ ["builtin", 80],
13
+ ["global-agents", 70],
14
+ ["global-codex", 65],
15
+ ["global-cursor", 60],
16
+ ]);
8
17
 
9
18
  function readFileCached(absPath) {
10
19
  const now = Date.now();
@@ -62,7 +71,15 @@ export function listSkillFiles(dir) {
62
71
  try {
63
72
  if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return [];
64
73
  return fs.readdirSync(dir, { withFileTypes: true })
65
- .filter((entry) => entry.isDirectory())
74
+ .filter((entry) => {
75
+ if (entry.isDirectory()) return true;
76
+ if (!entry.isSymbolicLink()) return false;
77
+ try {
78
+ return fs.statSync(path.join(dir, entry.name)).isDirectory();
79
+ } catch {
80
+ return false;
81
+ }
82
+ })
66
83
  .map((entry) => path.join(dir, entry.name, "SKILL.md"))
67
84
  .filter((skillPath) => fs.existsSync(skillPath));
68
85
  } catch {
@@ -132,14 +149,41 @@ export function listSkillsFromSources(sources, opts = {}) {
132
149
  return { skills, warnings };
133
150
  }
134
151
 
152
+ function skillPriority(skill) {
153
+ return SOURCE_PRIORITY.get(String(skill?.source || "")) ?? 0;
154
+ }
155
+
156
+ export function dedupeSkillsByName(skills) {
157
+ const byName = new Map();
158
+ for (const skill of Array.isArray(skills) ? skills : []) {
159
+ const name = String(skill?.name || skill?.id || "").trim();
160
+ if (!name) continue;
161
+ const existing = byName.get(name);
162
+ if (!existing || skillPriority(skill) > skillPriority(existing)) {
163
+ byName.set(name, skill);
164
+ }
165
+ }
166
+ return Array.from(byName.values()).sort((a, b) => {
167
+ const bySource = String(a.sourceLabel || "").localeCompare(String(b.sourceLabel || ""));
168
+ if (bySource !== 0) return bySource;
169
+ return String(a.name || "").localeCompare(String(b.name || ""));
170
+ });
171
+ }
172
+
135
173
  export function listSkills(packageRoot, workspaceRoot, opts = {}) {
136
174
  return listSkillsFromSources(defaultSkillSources(packageRoot, workspaceRoot), opts).skills;
137
175
  }
138
176
 
177
+ export function listUniqueSkills(packageRoot, workspaceRoot, opts = {}) {
178
+ return dedupeSkillsByName(listSkills(packageRoot, workspaceRoot, opts));
179
+ }
180
+
139
181
  export function readSkillDetail(packageRoot, workspaceRoot, keyOrName) {
140
182
  const wanted = String(keyOrName || "").trim();
141
183
  if (!wanted) return null;
142
- const item = listSkills(packageRoot, workspaceRoot).find((skill) => skill.key === wanted || skill.name === wanted);
184
+ const all = listSkills(packageRoot, workspaceRoot);
185
+ const item = all.find((skill) => skill.key === wanted)
186
+ || dedupeSkillsByName(all).find((skill) => skill.name === wanted);
143
187
  if (!item) return null;
144
188
  return item;
145
189
  }