@coze-arch/cli 0.0.18 → 0.0.19-beta.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.
- package/lib/__templates__/expo/.coze +1 -0
- package/lib/__templates__/expo/.cozeproj/scripts/validate.sh +8 -0
- package/lib/__templates__/expo/package.json +2 -1
- package/lib/__templates__/nextjs/.coze +1 -0
- package/lib/__templates__/nextjs/package.json +3 -1
- package/lib/__templates__/nextjs/scripts/validate.sh +10 -0
- package/lib/__templates__/nuxt-vue/.coze +1 -0
- package/lib/__templates__/nuxt-vue/eslint.config.mjs +25 -0
- package/lib/__templates__/nuxt-vue/package.json +9 -2
- package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +790 -10
- package/lib/__templates__/nuxt-vue/scripts/validate.sh +10 -0
- package/lib/__templates__/pi-agent/.coze +10 -0
- package/lib/__templates__/pi-agent/AGENTS.md +144 -0
- package/lib/__templates__/pi-agent/README.md +216 -0
- package/lib/__templates__/pi-agent/_gitignore +3 -0
- package/lib/__templates__/pi-agent/_npmrc +23 -0
- package/lib/__templates__/pi-agent/bin/pi-bot.ts +8 -0
- package/lib/__templates__/pi-agent/docs/project-overview.md +374 -0
- package/lib/__templates__/pi-agent/docs/user/getting-started.md +47 -0
- package/lib/__templates__/pi-agent/package.json +63 -0
- package/lib/__templates__/pi-agent/pi-resources/SYSTEM.md +15 -0
- package/lib/__templates__/pi-agent/pi-resources/extensions/preference-memory/index.ts +355 -0
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-asr/SKILL.md +36 -0
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-asr/scripts/asr.mjs +9 -0
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-image-gen/SKILL.md +41 -0
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-image-gen/scripts/gen.mjs +9 -0
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-tts/SKILL.md +85 -0
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-tts/scripts/tts.mjs +9 -0
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-video-gen/SKILL.md +53 -0
- package/lib/__templates__/pi-agent/pi-resources/skills/coze-video-gen/scripts/gen.mjs +9 -0
- package/lib/__templates__/pi-agent/pnpm-lock.yaml +8282 -0
- package/lib/__templates__/pi-agent/scripts/dev.sh +14 -0
- package/lib/__templates__/pi-agent/scripts/prepare.sh +35 -0
- package/lib/__templates__/pi-agent/src/agent.ts +363 -0
- package/lib/__templates__/pi-agent/src/channels/feishu/index.ts +760 -0
- package/lib/__templates__/pi-agent/src/channels/feishu/streaming-card.ts +297 -0
- package/lib/__templates__/pi-agent/src/channels/wechat/index.ts +171 -0
- package/lib/__templates__/pi-agent/src/cli.ts +117 -0
- package/lib/__templates__/pi-agent/src/config.ts +708 -0
- package/lib/__templates__/pi-agent/src/core.ts +218 -0
- package/lib/__templates__/pi-agent/src/dashboard/api/channels.ts +104 -0
- package/lib/__templates__/pi-agent/src/dashboard/api/docs.ts +204 -0
- package/lib/__templates__/pi-agent/src/dashboard/api/models.ts +98 -0
- package/lib/__templates__/pi-agent/src/dashboard/api/overview.ts +33 -0
- package/lib/__templates__/pi-agent/src/dashboard/config-store.ts +64 -0
- package/lib/__templates__/pi-agent/src/dashboard/index.ts +39 -0
- package/lib/__templates__/pi-agent/src/dashboard/server.ts +622 -0
- package/lib/__templates__/pi-agent/src/dashboard/types.ts +25 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/index.html +13 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/postcss.config.cjs +7 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/app-layout.tsx +186 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/page-title.tsx +17 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/alert.tsx +22 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/badge.tsx +25 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/button.tsx +40 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/card.tsx +29 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/input.tsx +18 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/label.tsx +8 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/select.tsx +80 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/components/ui/separator.tsx +23 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/hooks/use-fetch.ts +32 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/hooks/use-local-storage-state.ts +23 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/main.tsx +30 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/channels-page.tsx +188 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/chat-page.tsx +451 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/docs-page.tsx +65 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/models-page.tsx +122 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/pages/overview-page.tsx +134 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/services/chat-ws-service.ts +167 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/styles.css +294 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/src/utils/index.ts +11 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/tsconfig.json +13 -0
- package/lib/__templates__/pi-agent/src/dashboard/web/vite.config.ts +17 -0
- package/lib/__templates__/pi-agent/src/index.ts +123 -0
- package/lib/__templates__/pi-agent/src/pi-resources.ts +125 -0
- package/lib/__templates__/pi-agent/src/session-store.ts +223 -0
- package/lib/__templates__/pi-agent/src/tools/common/format-coze-error.ts +12 -0
- package/lib/__templates__/pi-agent/src/tools/index.ts +2 -0
- package/lib/__templates__/pi-agent/src/tools/web-fetch/index.ts +195 -0
- package/lib/__templates__/pi-agent/src/tools/web-search/index.ts +206 -0
- package/lib/__templates__/pi-agent/template.config.js +45 -0
- package/lib/__templates__/pi-agent/tests/cli.test.ts +136 -0
- package/lib/__templates__/pi-agent/tests/config.test.ts +315 -0
- package/lib/__templates__/pi-agent/tests/dashboard-docs-api.test.ts +125 -0
- package/lib/__templates__/pi-agent/tests/dashboard-models-api.test.ts +171 -0
- package/lib/__templates__/pi-agent/tests/feishu-channel.test.ts +149 -0
- package/lib/__templates__/pi-agent/tests/feishu-streaming-card.test.ts +15 -0
- package/lib/__templates__/pi-agent/tests/pi-resources.test.ts +73 -0
- package/lib/__templates__/pi-agent/tests/preference-memory.test.ts +43 -0
- package/lib/__templates__/pi-agent/tests/session-store.test.ts +61 -0
- package/lib/__templates__/pi-agent/tests/smoke/run-smoke.ts +275 -0
- package/lib/__templates__/pi-agent/tests/web-fetch.test.ts +157 -0
- package/lib/__templates__/pi-agent/tests/web-search.test.ts +208 -0
- package/lib/__templates__/pi-agent/tsconfig.json +21 -0
- package/lib/__templates__/pi-agent/types/larksuiteoapi-node-sdk.d.ts +113 -0
- package/lib/__templates__/taro/.coze +1 -0
- package/lib/__templates__/taro/.cozeproj/scripts/validate.sh +8 -0
- package/lib/__templates__/taro/package.json +1 -1
- package/lib/__templates__/templates.json +24 -0
- package/lib/__templates__/vite/.coze +1 -0
- package/lib/__templates__/vite/package.json +3 -1
- package/lib/__templates__/vite/scripts/validate.sh +10 -0
- package/lib/cli.js +13 -2
- package/package.json +1 -1
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
4
|
+
|
|
5
|
+
const PREFERENCES_RELATIVE_PATH = "preference.md";
|
|
6
|
+
const LONG_TERM_SIGNAL_RE = /(以后|默认|统一|后续|今后|记住|长期|一直|都按这个)/;
|
|
7
|
+
const TEMPORARY_SIGNAL_RE = /(这次|本次|先|暂时|今天|当前)/;
|
|
8
|
+
const SENTENCE_SPLIT_RE = /[\n。!?;;]+/;
|
|
9
|
+
|
|
10
|
+
const SECTION_ORDER = ["回复偏好", "项目约定", "安全约束", "其他偏好"] as const;
|
|
11
|
+
|
|
12
|
+
export type PreferenceSection = (typeof SECTION_ORDER)[number];
|
|
13
|
+
|
|
14
|
+
export type PreferenceRecord = {
|
|
15
|
+
key: string;
|
|
16
|
+
section: PreferenceSection;
|
|
17
|
+
label: string;
|
|
18
|
+
value: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default function preferenceMemoryExtension(pi: ExtensionAPI) {
|
|
22
|
+
pi.on("before_agent_start", async (event, ctx) => {
|
|
23
|
+
const preferencesPath = getPreferencesFilePath(ctx.cwd);
|
|
24
|
+
const content = readInjectablePreferences(preferencesPath);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
systemPrompt: `${event.systemPrompt}
|
|
28
|
+
|
|
29
|
+
## Preference Persistence
|
|
30
|
+
|
|
31
|
+
This runtime can persist a small set of stable user or project preferences to workspace/preference.md.
|
|
32
|
+
Only persist preferences when the user clearly expresses a long-term default or rule, for example using phrases like "以后", "默认", "统一", "后续", or "记住".
|
|
33
|
+
Do not claim that you are unable to persist preferences in this runtime.
|
|
34
|
+
Do not say a preference was saved unless it actually matched the rules and was written.
|
|
35
|
+
|
|
36
|
+
${content ? `The following preferences were already persisted and should be treated as defaults unless the user overrides them for the current request:
|
|
37
|
+
|
|
38
|
+
${content}` : "No preferences have been persisted yet."}`
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
pi.on("agent_end", async (event, ctx) => {
|
|
43
|
+
const userPrompt = extractUserPrompt(event.messages);
|
|
44
|
+
if (!userPrompt) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const extracted = extractPreferencesFromText(userPrompt);
|
|
49
|
+
if (extracted.length === 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const preferencesPath = getPreferencesFilePath(ctx.cwd);
|
|
54
|
+
const current = existsSync(preferencesPath) ? readFileSync(preferencesPath, "utf-8") : "";
|
|
55
|
+
const next = upsertPreferencesDocument(current, extracted);
|
|
56
|
+
if (normalizeDocument(current) === normalizeDocument(next)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
mkdirSync(dirname(preferencesPath), { recursive: true });
|
|
61
|
+
writeFileSync(preferencesPath, next, "utf-8");
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getPreferencesFilePath(cwd: string): string {
|
|
66
|
+
return resolve(cwd, PREFERENCES_RELATIVE_PATH);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function readInjectablePreferences(filePath: string): string | undefined {
|
|
70
|
+
if (!existsSync(filePath)) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const content = readFileSync(filePath, "utf-8").trim();
|
|
75
|
+
if (!content.includes("<!-- pref:")) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return content;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function extractPreferencesFromText(text: string): PreferenceRecord[] {
|
|
83
|
+
const preferences = new Map<string, PreferenceRecord>();
|
|
84
|
+
|
|
85
|
+
for (const sentence of splitIntoSentences(text)) {
|
|
86
|
+
const normalized = sentence.trim();
|
|
87
|
+
if (!normalized) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const hasLongTermSignal = LONG_TERM_SIGNAL_RE.test(normalized);
|
|
92
|
+
const hasTemporaryOnlySignal = TEMPORARY_SIGNAL_RE.test(normalized) && !hasLongTermSignal;
|
|
93
|
+
if (hasTemporaryOnlySignal) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
addPreference(preferences, detectReplyLanguage(normalized, hasLongTermSignal));
|
|
98
|
+
addPreference(preferences, detectReplyStyle(normalized, hasLongTermSignal));
|
|
99
|
+
addPreference(preferences, detectPackageManager(normalized, hasLongTermSignal));
|
|
100
|
+
addPreference(preferences, detectRuntimeResourceDir(normalized, hasLongTermSignal));
|
|
101
|
+
addPreference(preferences, detectDeleteConstraint(normalized, hasLongTermSignal));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Array.from(preferences.values());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function upsertPreferencesDocument(document: string, preferences: PreferenceRecord[]): string {
|
|
108
|
+
const records = parseManagedPreferences(document);
|
|
109
|
+
for (const preference of preferences) {
|
|
110
|
+
records.set(preference.key, preference);
|
|
111
|
+
}
|
|
112
|
+
return renderPreferencesDocument(Array.from(records.values()));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function parseManagedPreferences(document: string): Map<string, PreferenceRecord> {
|
|
116
|
+
const records = new Map<string, PreferenceRecord>();
|
|
117
|
+
const lines = document.split(/\r?\n/);
|
|
118
|
+
|
|
119
|
+
for (const line of lines) {
|
|
120
|
+
const match = line.match(/<!--\s*pref:([a-z0-9-]+)\s*-->\s*([^::]+)[::]\s*(.+)$/i);
|
|
121
|
+
if (!match) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const key = match[1].trim();
|
|
126
|
+
const label = match[2].trim();
|
|
127
|
+
const value = match[3].trim();
|
|
128
|
+
const section = inferSectionFromKey(key);
|
|
129
|
+
if (!section || !label || !value) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
records.set(key, { key, section, label, value });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return records;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function renderPreferencesDocument(preferences: PreferenceRecord[]): string {
|
|
140
|
+
const lines: string[] = [
|
|
141
|
+
"# Preferences",
|
|
142
|
+
"",
|
|
143
|
+
"<!-- Managed by preference-memory extension. -->",
|
|
144
|
+
""
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
const grouped = new Map<PreferenceSection, PreferenceRecord[]>();
|
|
148
|
+
for (const section of SECTION_ORDER) {
|
|
149
|
+
grouped.set(section, []);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const preference of preferences) {
|
|
153
|
+
grouped.get(preference.section)?.push(preference);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (const section of SECTION_ORDER) {
|
|
157
|
+
lines.push(`## ${section}`, "");
|
|
158
|
+
const items = (grouped.get(section) ?? []).sort((left, right) => left.label.localeCompare(right.label));
|
|
159
|
+
for (const item of items) {
|
|
160
|
+
lines.push(`- <!-- pref:${item.key} --> ${item.label}:${item.value}`);
|
|
161
|
+
}
|
|
162
|
+
lines.push("");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return `${trimTrailingBlankLines(lines).join("\n")}\n`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function extractUserPrompt(messages: unknown[]): string | undefined {
|
|
169
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
170
|
+
const text = extractMessageText(messages[index], "user");
|
|
171
|
+
if (text) {
|
|
172
|
+
return text;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function extractMessageText(message: unknown, role: string): string | undefined {
|
|
180
|
+
if (!message || typeof message !== "object") {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if ((message as { role?: unknown }).role !== role) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const content = (message as { content?: unknown }).content;
|
|
189
|
+
if (typeof content === "string") {
|
|
190
|
+
return content.trim() || undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!Array.isArray(content)) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const text = content
|
|
198
|
+
.flatMap((part) => {
|
|
199
|
+
if (!part || typeof part !== "object") {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const typedPart = part as { type?: unknown; text?: unknown };
|
|
204
|
+
return typedPart.type === "text" && typeof typedPart.text === "string" ? [typedPart.text] : [];
|
|
205
|
+
})
|
|
206
|
+
.join("")
|
|
207
|
+
.trim();
|
|
208
|
+
|
|
209
|
+
return text || undefined;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function splitIntoSentences(text: string): string[] {
|
|
213
|
+
return text
|
|
214
|
+
.split(SENTENCE_SPLIT_RE)
|
|
215
|
+
.map((sentence) => sentence.trim())
|
|
216
|
+
.filter(Boolean);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function addPreference(
|
|
220
|
+
records: Map<string, PreferenceRecord>,
|
|
221
|
+
preference: PreferenceRecord | undefined
|
|
222
|
+
): void {
|
|
223
|
+
if (!preference) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
records.set(preference.key, preference);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function detectReplyLanguage(sentence: string, hasLongTermSignal: boolean): PreferenceRecord | undefined {
|
|
231
|
+
if (!hasLongTermSignal) {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!/(回复|回答|输出|沟通|说明)/.test(sentence)) {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (/中文/.test(sentence)) {
|
|
240
|
+
return createPreference("reply-language", "回复偏好", "回复语言", "中文");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (/(英文|英语|english)/i.test(sentence)) {
|
|
244
|
+
return createPreference("reply-language", "回复偏好", "回复语言", "英文");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function detectReplyStyle(sentence: string, hasLongTermSignal: boolean): PreferenceRecord | undefined {
|
|
251
|
+
if (!hasLongTermSignal) {
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!/(回复|回答|输出|风格|说明|解释)/.test(sentence)) {
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (/(简洁|简短|精简)/.test(sentence)) {
|
|
260
|
+
return createPreference("reply-style", "回复偏好", "回复风格", "简洁");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (/(详细|展开|细一点)/.test(sentence)) {
|
|
264
|
+
return createPreference("reply-style", "回复偏好", "回复风格", "详细");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function detectPackageManager(sentence: string, hasLongTermSignal: boolean): PreferenceRecord | undefined {
|
|
271
|
+
if (!hasLongTermSignal) {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const packageManager = sentence.match(/\b(pnpm|npm|yarn|bun)\b/i)?.[1]?.toLowerCase();
|
|
276
|
+
if (!packageManager) {
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!/(统一|默认|以后|后续|项目|依赖|安装)/.test(sentence)) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return createPreference("package-manager", "项目约定", "包管理器", packageManager);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function detectRuntimeResourceDir(sentence: string, hasLongTermSignal: boolean): PreferenceRecord | undefined {
|
|
288
|
+
if (!hasLongTermSignal) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!/(写到|放到|创建在|存到|记录到)/.test(sentence)) {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!/(workspace\/\.pi|\.pi\b)/.test(sentence)) {
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return createPreference("runtime-resource-dir", "项目约定", "运行态资源目录", "workspace/.pi");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function detectDeleteConstraint(sentence: string, hasLongTermSignal: boolean): PreferenceRecord | undefined {
|
|
304
|
+
const hasStrongConstraint = /(没有我确认|未经确认|没确认|记住)/.test(sentence);
|
|
305
|
+
if (!hasLongTermSignal && !hasStrongConstraint) {
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (!/(删|删除|rm)/.test(sentence)) {
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (!/(文件|目录|代码|资源)/.test(sentence)) {
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return createPreference("delete-confirmation", "安全约束", "删除约束", "未经确认不要删除文件");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function createPreference(
|
|
321
|
+
key: string,
|
|
322
|
+
section: PreferenceSection,
|
|
323
|
+
label: string,
|
|
324
|
+
value: string
|
|
325
|
+
): PreferenceRecord {
|
|
326
|
+
return { key, section, label, value };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function inferSectionFromKey(key: string): PreferenceSection | undefined {
|
|
330
|
+
if (key.startsWith("reply-")) {
|
|
331
|
+
return "回复偏好";
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (key === "package-manager" || key === "runtime-resource-dir") {
|
|
335
|
+
return "项目约定";
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (key === "delete-confirmation") {
|
|
339
|
+
return "安全约束";
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return "其他偏好";
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function trimTrailingBlankLines(lines: string[]): string[] {
|
|
346
|
+
const trimmed = [...lines];
|
|
347
|
+
while (trimmed.length > 0 && trimmed[trimmed.length - 1].trim() === "") {
|
|
348
|
+
trimmed.pop();
|
|
349
|
+
}
|
|
350
|
+
return trimmed;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function normalizeDocument(document: string): string {
|
|
354
|
+
return document.replace(/\r\n/g, "\n").trim();
|
|
355
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coze-asr
|
|
3
|
+
description: Convert speech audio to text using Coze ASR. Use when you need to transcribe spoken content from a remote audio URL or a local audio file into plain text.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Coze ASR
|
|
7
|
+
|
|
8
|
+
Transcribe audio from a URL or local file using Coze ASR.
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
node {skillDir}/scripts/asr.mjs --url "https://example.com/audio.mp3"
|
|
14
|
+
node {skillDir}/scripts/asr.mjs --file ./recording.mp3
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Options
|
|
18
|
+
|
|
19
|
+
- `--url <url>`, `-u <url>` remote audio URL
|
|
20
|
+
- `--file <path>`, `-f <path>` local audio file
|
|
21
|
+
|
|
22
|
+
## Behavior
|
|
23
|
+
|
|
24
|
+
- One of `--url` or `--file` is required.
|
|
25
|
+
- If both `--url` and `--file` are provided, the local file input takes precedence.
|
|
26
|
+
- Local files are read and uploaded as base64 audio content.
|
|
27
|
+
- Supported audio formats follow the SDK/API capability: WAV, MP3, OGG Opus, M4A.
|
|
28
|
+
- Recommended limits from the SDK docs: duration up to 2 hours, size up to 100MB.
|
|
29
|
+
- The CLI prints recognized `text`, and may also print `duration` and `segments`.
|
|
30
|
+
- The CLI does not print full `utterances` details or raw response payload.
|
|
31
|
+
- This skill does not expose custom headers such as `--header`, `-H`, or mock mode.
|
|
32
|
+
|
|
33
|
+
## Notes
|
|
34
|
+
|
|
35
|
+
- `{skillDir}` means the directory containing this `SKILL.md`.
|
|
36
|
+
- Local files are read and uploaded as base64 audio content.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import createJiti from "jiti";
|
|
4
|
+
|
|
5
|
+
const jiti = createJiti(import.meta.url);
|
|
6
|
+
const { runAsrCli } = await jiti.import("../../../src/skill-cli.ts");
|
|
7
|
+
|
|
8
|
+
const code = await runAsrCli(process.argv.slice(2), process.env);
|
|
9
|
+
process.exit(code);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coze-image-gen
|
|
3
|
+
description: Generate one or more images from text prompts using Coze image generation. Use when you need to create images from a natural-language prompt, produce multiple image variants, or generate sequential frames from the same prompt.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Coze Image Generation
|
|
7
|
+
|
|
8
|
+
Generate one or more images from a prompt using Coze.
|
|
9
|
+
|
|
10
|
+
This skill runs as a Node CLI wrapper around the backend SDK. Do not use the SDK from client-side code.
|
|
11
|
+
|
|
12
|
+
## Quick start
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
node {skillDir}/scripts/gen.mjs --prompt "A futuristic city at sunset"
|
|
16
|
+
node {skillDir}/scripts/gen.mjs --prompt "A serene mountain landscape" --count 2 --size 4K
|
|
17
|
+
node {skillDir}/scripts/gen.mjs --prompt "A hero's journey through magical lands" --sequential --max-sequential 5
|
|
18
|
+
node {skillDir}/scripts/gen.mjs --prompt "Transform into anime style" --image "https://example.com/input.jpg"
|
|
19
|
+
node {skillDir}/scripts/gen.mjs --prompt "A modern office workspace" --response-format url
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Options
|
|
23
|
+
|
|
24
|
+
- `--prompt <text>` required prompt text
|
|
25
|
+
- `--count <n>` number of independent generation requests, default `1`
|
|
26
|
+
- `--size <size>` image size: `2K`, `4K`, or `WIDTHxHEIGHT`, default `2K`
|
|
27
|
+
- `--image <url>` reference image URL; repeat the flag to pass multiple images
|
|
28
|
+
- `--response-format <format>` only supports `url`
|
|
29
|
+
- `--watermark <true|false>` whether to keep watermark
|
|
30
|
+
- `--optimize-prompt-mode <mode>` prompt optimization mode passed through to the SDK
|
|
31
|
+
- `--sequential` enable sequential image generation
|
|
32
|
+
- `--max-sequential <n>` max sequential frames, default `15`
|
|
33
|
+
- `--header <key:value>` custom HTTP header; repeatable, alias `-H`
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
|
|
37
|
+
- `{skillDir}` means the directory containing this `SKILL.md`.
|
|
38
|
+
- Successful runs print generated URLs directly.
|
|
39
|
+
- Printed image URLs must be kept exactly intact, complete, and accurate. All URL parameters must be preserved without truncation, rewriting, omission, or reordering; in particular, parameters inside the query string such as `sign` must not be dropped, otherwise the image may be inaccessible.
|
|
40
|
+
- Unless the user explicitly asks to download the URL content, only return the complete URL link to the user.
|
|
41
|
+
- This skill does not expose base64 output.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import createJiti from "jiti";
|
|
4
|
+
|
|
5
|
+
const jiti = createJiti(import.meta.url);
|
|
6
|
+
const { runImageCli } = await jiti.import("../../../src/skill-cli.ts");
|
|
7
|
+
|
|
8
|
+
const code = await runImageCli(process.argv.slice(2), process.env);
|
|
9
|
+
process.exit(code);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coze-tts
|
|
3
|
+
description: Convert text to speech using Coze TTS. Use when you need to synthesize spoken audio from one text input or multiple text segments, optionally with a specific speaker, format, sample rate, or speech settings.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Coze TTS
|
|
7
|
+
|
|
8
|
+
Generate speech audio URLs from text using Coze TTS.
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
node {skillDir}/scripts/tts.mjs --text "Hello, welcome to our service"
|
|
14
|
+
node {skillDir}/scripts/tts.mjs --texts "Chapter 1" "Chapter 2" --speaker zh_male_m191_uranus_bigtts
|
|
15
|
+
node {skillDir}/scripts/tts.mjs --text "Fast announcement" --speech-rate 30 --format mp3 --sample-rate 48000
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Options
|
|
19
|
+
|
|
20
|
+
- `--text <text>` single text input. If both `--text` and `--texts` are provided, `--text` takes precedence.
|
|
21
|
+
- `--texts <texts...>` multiple text inputs. Values are read until the next `--flag`.
|
|
22
|
+
- `--speaker <id>` speaker id, default `zh_female_xiaohe_uranus_bigtts`
|
|
23
|
+
- `--format <fmt>` audio format: `mp3`, `pcm`, or `ogg_opus`. Default is SDK default (`mp3`).
|
|
24
|
+
- `--sample-rate <hz>` sample rate. Supported values: `8000`, `16000`, `22050`, `24000`, `32000`, `44100`, `48000`. Default is SDK default (`24000`).
|
|
25
|
+
- `--speech-rate <n>` speech rate adjustment, range `-50` to `100`, default `0`
|
|
26
|
+
- `--loudness-rate <n>` loudness adjustment, range `-50` to `100`, default `0`
|
|
27
|
+
|
|
28
|
+
## Behavior
|
|
29
|
+
|
|
30
|
+
- At least one of `--text` or `--texts` is required.
|
|
31
|
+
- This skill currently supports plain text input only. It does not expose `ssml`, `--header`, `-H`, or `--mock`.
|
|
32
|
+
- The CLI prints one audio URL per generated segment. It does not download audio files locally.
|
|
33
|
+
- Printed audio URLs must be kept exactly intact, complete, and accurate. All URL parameters must be preserved without truncation, rewriting, omission, or reordering; in particular, parameters inside the query string such as `sign` must not be dropped, otherwise the audio may be inaccessible.
|
|
34
|
+
- Unless the user explicitly asks to download the URL content, only return the complete URL link to the user.
|
|
35
|
+
- The CLI does not print `audioSize`, even though the underlying SDK returns it.
|
|
36
|
+
- Invalid ranges or unsupported values are passed through to the SDK and may fail there.
|
|
37
|
+
|
|
38
|
+
## Sample Rates
|
|
39
|
+
|
|
40
|
+
Supported: `8000`, `16000`, `22050`, `24000`, `32000`, `44100`, `48000` Hz
|
|
41
|
+
|
|
42
|
+
- `8000-16000`: Phone quality
|
|
43
|
+
- `22050-24000`: Standard quality (default)
|
|
44
|
+
- `32000-48000`: High quality
|
|
45
|
+
|
|
46
|
+
## Tuning
|
|
47
|
+
|
|
48
|
+
- `speechRate`: range `-50` to `100`, default `0`. Negative values slow speech down, positive values speed it up.
|
|
49
|
+
- `loudnessRate`: range `-50` to `100`, default `0`. Negative values make output quieter, positive values make it louder.
|
|
50
|
+
|
|
51
|
+
## Voices
|
|
52
|
+
|
|
53
|
+
### General
|
|
54
|
+
|
|
55
|
+
- `zh_female_xiaohe_uranus_bigtts` `小荷`: 默认,通用女声
|
|
56
|
+
- `zh_female_vv_uranus_bigtts` `Vivi`: 中英双语女声
|
|
57
|
+
- `zh_male_m191_uranus_bigtts` `云舟`: 男声
|
|
58
|
+
- `zh_male_taocheng_uranus_bigtts` `小天`: 男声
|
|
59
|
+
|
|
60
|
+
### Audiobook / Reading
|
|
61
|
+
|
|
62
|
+
- `zh_female_xueayi_saturn_bigtts` `雪阿姨`: 儿童有声读物女声
|
|
63
|
+
|
|
64
|
+
### Video Dubbing
|
|
65
|
+
|
|
66
|
+
- `zh_male_dayi_saturn_bigtts` `大一`: 男声
|
|
67
|
+
- `zh_female_mizai_saturn_bigtts` `米仔`: 女声
|
|
68
|
+
- `zh_female_jitangnv_saturn_bigtts` `鸡汤女`: 励志女声
|
|
69
|
+
- `zh_female_meilinvyou_saturn_bigtts` `甜美女友`: 甜美女友
|
|
70
|
+
- `zh_female_santongyongns_saturn_bigtts` `三通女声`: 通用流畅女声
|
|
71
|
+
- `zh_male_ruyayichen_saturn_bigtts` `儒雅一尘`: 儒雅男声
|
|
72
|
+
|
|
73
|
+
### Roleplay
|
|
74
|
+
|
|
75
|
+
- `saturn_zh_female_keainvsheng_tob` `可爱女生`: 可爱女生
|
|
76
|
+
- `saturn_zh_female_tiaopigongzhu_tob` `俏皮公主`: 俏皮公主
|
|
77
|
+
- `saturn_zh_male_shuanglangshaonian_tob` `爽朗少年`: 爽朗少年
|
|
78
|
+
- `saturn_zh_male_tiancaitongzhuo_tob` `天才同桌`: 天才同桌
|
|
79
|
+
- `saturn_zh_female_cancan_tob` `灿灿`: 知性灿灿
|
|
80
|
+
|
|
81
|
+
## Notes
|
|
82
|
+
|
|
83
|
+
- `{skillDir}` means the directory containing this `SKILL.md`.
|
|
84
|
+
- The script prints one audio URL per generated segment.
|
|
85
|
+
- The returned URL must be used as-is, in full, and with every parameter preserved exactly, especially query parameters such as `sign`, otherwise the audio may not be accessible.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import createJiti from "jiti";
|
|
4
|
+
|
|
5
|
+
const jiti = createJiti(import.meta.url);
|
|
6
|
+
const { runTtsCli } = await jiti.import("../../../src/skill-cli.ts");
|
|
7
|
+
|
|
8
|
+
const code = await runTtsCli(process.argv.slice(2), process.env);
|
|
9
|
+
process.exit(code);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coze-video-gen
|
|
3
|
+
description: Generate a video from text prompts and/or image inputs using Coze video generation. Use when you need text-to-video, image-to-video, first-frame control, last-frame control, or a returned last-frame image URL.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Coze Video Generation
|
|
7
|
+
|
|
8
|
+
Generate a video URL from text and image inputs using Coze.
|
|
9
|
+
|
|
10
|
+
This skill runs as a Node CLI wrapper around the backend SDK. Do not use the SDK from client-side code.
|
|
11
|
+
|
|
12
|
+
## Quick start
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
node {skillDir}/scripts/gen.mjs --prompt "A serene mountain landscape with flowing clouds"
|
|
16
|
+
node {skillDir}/scripts/gen.mjs --prompt "A cinematic robot walking through neon rain" --duration 6 --ratio 9:16 --resolution 1080p
|
|
17
|
+
node {skillDir}/scripts/gen.mjs --prompt "Animate this concept art" --first-frame "https://example.com/first.png"
|
|
18
|
+
node {skillDir}/scripts/gen.mjs --prompt "Transition from sunrise to night" --first-frame "https://example.com/start.png" --last-frame "https://example.com/end.png" --return-last-frame true
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Options
|
|
22
|
+
|
|
23
|
+
- `--prompt <text>` text prompt
|
|
24
|
+
- `--image <url>` reference image URL; repeat the flag to pass multiple images
|
|
25
|
+
- `--first-frame <url>` first frame image URL
|
|
26
|
+
- `--last-frame <url>` last frame image URL
|
|
27
|
+
- `--model <id>` model id, default SDK model is `doubao-seedance-1-5-pro-251215`
|
|
28
|
+
- `--duration <seconds>` video duration in seconds
|
|
29
|
+
- `--ratio <ratio>` one of `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, `21:9`, `adaptive`
|
|
30
|
+
- `--resolution <value>` one of `480p`, `720p`, `1080p`
|
|
31
|
+
- `--watermark <true|false>` whether to keep watermark
|
|
32
|
+
- `--seed <n>` fixed random seed for reproducibility
|
|
33
|
+
- `--camera-fixed <true|false>` whether to fix the camera
|
|
34
|
+
- `--generate-audio <true|false>` whether to generate synced audio
|
|
35
|
+
- `--return-last-frame <true|false>` whether to return the last-frame image URL
|
|
36
|
+
- `--max-wait-time <seconds>` max synchronous wait time
|
|
37
|
+
- `--callback-url <url>` async callback URL passed through to the SDK
|
|
38
|
+
- `--header <key:value>` custom HTTP header; repeatable, alias `-H`
|
|
39
|
+
|
|
40
|
+
## Behavior
|
|
41
|
+
|
|
42
|
+
- At least one of `--prompt`, `--image`, `--first-frame`, or `--last-frame` is required.
|
|
43
|
+
- Successful runs print `Task ID`, `Status`, `Video URL`, and optionally `Last Frame URL`.
|
|
44
|
+
- The CLI does not download video files locally.
|
|
45
|
+
- Printed video URLs and frame URLs must be kept exactly intact, complete, and accurate. All URL parameters must be preserved without truncation, rewriting, omission, or reordering; in particular, parameters inside the query string such as `sign` must not be dropped, otherwise the asset may be inaccessible.
|
|
46
|
+
- Unless the user explicitly asks to download the URL content, only return the complete URL link to the user.
|
|
47
|
+
|
|
48
|
+
## Notes
|
|
49
|
+
|
|
50
|
+
- `{skillDir}` means the directory containing this `SKILL.md`.
|
|
51
|
+
- The default model `doubao-seedance-1-5-pro-251215` supports text input plus `first_frame` and `last_frame`. Generic `reference_image` inputs from `--image` may be rejected by that model.
|
|
52
|
+
- Only use `--image` when you have selected a model that explicitly supports `reference_image`.
|
|
53
|
+
- The returned video URL is already hosted with a valid expiration period. Unless the user explicitly needs rehosting or download, use it directly.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import createJiti from "jiti";
|
|
4
|
+
|
|
5
|
+
const jiti = createJiti(import.meta.url);
|
|
6
|
+
const { runVideoCli } = await jiti.import("../../../src/skill-cli.ts");
|
|
7
|
+
|
|
8
|
+
const code = await runVideoCli(process.argv.slice(2), process.env);
|
|
9
|
+
process.exit(code);
|