Compare commits

...

1 Commits

Author SHA1 Message Date
134
e9b7efd256 feat: add Claw product lifecycle shortcuts 2026-07-27 23:16:26 +08:00
2 changed files with 76 additions and 5 deletions

View File

@ -1,6 +1,6 @@
export const CANVAS_SKILL_TAGS_SETTING_KEY = "canvas_skill_tags"; export const CANVAS_SKILL_TAGS_SETTING_KEY = "canvas_skill_tags";
export const CANVAS_SKILL_TAG_ORDER_SETTING_KEY_PREFIX = "canvas_skill_tag_order:"; export const CANVAS_SKILL_TAG_ORDER_SETTING_KEY_PREFIX = "canvas_skill_tag_order:";
export const CANVAS_SKILL_TAGS_CONFIG_VERSION = 5; export const CANVAS_SKILL_TAGS_CONFIG_VERSION = 6;
export const DEFAULT_CANVAS_SKILL_TAGS_TITLE = "Claw 快捷技能"; export const DEFAULT_CANVAS_SKILL_TAGS_TITLE = "Claw 快捷技能";
export type CanvasSkillTagSettings = { export type CanvasSkillTagSettings = {
@ -19,6 +19,27 @@ export type CanvasSkillTag = {
iconUrl?: string; iconUrl?: string;
}; };
export const CANVAS_SKILL_TAGS_V6_ADDITIONS: CanvasSkillTag[] = [
{
id: "product-lifecycle-overview",
name: "产品生命周期总览",
iconName: "layout-grid",
placeholder:
"查看公司产品生命周期总览,分析经营阶段、库存风险、SKU迭代和停产清退",
prompt:
"【前台技能:商品全生命周期】【技能路由:product-lifecycle-archive-db】【技能目录:product-lifecycle-archive】全生命周期产品分析。用途:查看公司内部产品从开款、经营到停产的全生命周期经营总览,包括产品规模、生命周期阶段、SKU 迭代、品类经营、库存周转、停产清退和负责人。进入后直接返回公司产品经营总览,并提供库存风险 TOP10、低效产品结构、品类经营表现、SKU 迭代效果、停产与清退、负责人看板和导出经营盘点表等下一步选择。不要解释成市场类目的引入期/成长期分析,也不要转到市场数据库。",
},
{
id: "new-product-progress",
name: "新品进度",
iconName: "sparkles",
placeholder:
"输入 SPU、SKU、开款编号、商品ID或流程号,查询新品当前节点和审批时间线",
prompt:
"【前台技能:商品全生命周期】【技能路由:product-lifecycle-archive-db】【技能目录:product-lifecycle-archive】新品进度查询。用途:查询单个新品当前审批进度,或汇总全部进行中的新品项目。单品查询时先要求用户输入 SPU、SKU、开款编号、商品 ID 或流程号,返回当前节点、当前处理人、审批时间线和流程表单;用户明确要求全部新品时,返回所有未完结新品、超时节点、当前处理人和进行中明细,并提供查看当前卡点、负责人负载、品类进度等下一步选择。不要转到市场新品趋势或商品策划技能。",
},
];
export const DEFAULT_CANVAS_SKILL_TAGS: CanvasSkillTag[] = [ export const DEFAULT_CANVAS_SKILL_TAGS: CanvasSkillTag[] = [
{ {
id: "video-gateway", id: "video-gateway",
@ -52,6 +73,7 @@ export const DEFAULT_CANVAS_SKILL_TAGS: CanvasSkillTag[] = [
prompt: prompt:
"【技能路由:product-detail-planning-v2】用途:通过可恢复的分步对话完成商品详情页全案策划,覆盖产品事实、竞品、真实评价与问大家、内容策划、视觉策划、详情页提示词、五张主图、主图视频、AI 买家秀,以及最终 HTML 和 17 子表 Excel 交付。触发关键词:商品策划案、详情页策划、完整详情策划、新品详情策划、竞品详情分析、详情页改版、主图策划、SKU图策划、主图视频策划、AI买家秀策划、继续详情策划、新版商品策划案、商品策划案V2。通用“商品策划案”默认进入 V2,只有用户明确要求旧版/V1时才使用兼容流程。公司已有商品可提供 SKU/SPU;新品可以提供产品名、现有资料并上传产品图。每轮只处理当前节点,完整展示当前节点数据、系统最优推荐和可选动作,但不得替用户确认或跨节点推进;图片只证明可见结构,不得自动证明材质、参数、检测或服务承诺。全部必需节点完成后生成 HTML 策划案和固定 17 子表 Excel,并返回查看或下载入口。", "【技能路由:product-detail-planning-v2】用途:通过可恢复的分步对话完成商品详情页全案策划,覆盖产品事实、竞品、真实评价与问大家、内容策划、视觉策划、详情页提示词、五张主图、主图视频、AI 买家秀,以及最终 HTML 和 17 子表 Excel 交付。触发关键词:商品策划案、详情页策划、完整详情策划、新品详情策划、竞品详情分析、详情页改版、主图策划、SKU图策划、主图视频策划、AI买家秀策划、继续详情策划、新版商品策划案、商品策划案V2。通用“商品策划案”默认进入 V2,只有用户明确要求旧版/V1时才使用兼容流程。公司已有商品可提供 SKU/SPU;新品可以提供产品名、现有资料并上传产品图。每轮只处理当前节点,完整展示当前节点数据、系统最优推荐和可选动作,但不得替用户确认或跨节点推进;图片只证明可见结构,不得自动证明材质、参数、检测或服务承诺。全部必需节点完成后生成 HTML 策划案和固定 17 子表 Excel,并返回查看或下载入口。",
}, },
...CANVAS_SKILL_TAGS_V6_ADDITIONS,
{ {
id: "install-media-db", id: "install-media-db",
name: "安装资料", name: "安装资料",
@ -195,6 +217,17 @@ function normalizeId(value: unknown, fallbackIndex: number) {
return id || `tag-${fallbackIndex + 1}`; return id || `tag-${fallbackIndex + 1}`;
} }
function appendMissingCanvasSkillTags(
tags: CanvasSkillTag[],
additions: CanvasSkillTag[]
) {
const existingIds = new Set(tags.map((tag) => tag.id));
return [
...tags,
...additions.filter((tag) => !existingIds.has(tag.id)),
].slice(0, 30);
}
export function normalizeCanvasSkillTags(value: unknown): CanvasSkillTag[] { export function normalizeCanvasSkillTags(value: unknown): CanvasSkillTag[] {
const source = Array.isArray(value) ? value : DEFAULT_CANVAS_SKILL_TAGS; const source = Array.isArray(value) ? value : DEFAULT_CANVAS_SKILL_TAGS;
const usedIds = new Set<string>(); const usedIds = new Set<string>();
@ -236,10 +269,14 @@ export function normalizeCanvasSkillTagSettings(
options: { requireCurrentVersion?: boolean } = {} options: { requireCurrentVersion?: boolean } = {}
): CanvasSkillTagSettings { ): CanvasSkillTagSettings {
if (Array.isArray(value)) { if (Array.isArray(value)) {
const tags = appendMissingCanvasSkillTags(
normalizeCanvasSkillTags(value),
CANVAS_SKILL_TAGS_V6_ADDITIONS
);
return { return {
enabled: true, enabled: true,
title: DEFAULT_CANVAS_SKILL_TAGS_TITLE, title: DEFAULT_CANVAS_SKILL_TAGS_TITLE,
tags: normalizeCanvasSkillTags(value), tags,
version: CANVAS_SKILL_TAGS_CONFIG_VERSION, version: CANVAS_SKILL_TAGS_CONFIG_VERSION,
}; };
} }
@ -249,10 +286,14 @@ export function normalizeCanvasSkillTagSettings(
const version = Number(row.version || 0); const version = Number(row.version || 0);
if (options.requireCurrentVersion && version < CANVAS_SKILL_TAGS_CONFIG_VERSION) { if (options.requireCurrentVersion && version < CANVAS_SKILL_TAGS_CONFIG_VERSION) {
const title = normalizeString(row.title) || DEFAULT_CANVAS_SKILL_TAGS_TITLE; const title = normalizeString(row.title) || DEFAULT_CANVAS_SKILL_TAGS_TITLE;
const tags = appendMissingCanvasSkillTags(
normalizeCanvasSkillTags(row.tags),
CANVAS_SKILL_TAGS_V6_ADDITIONS
);
return { return {
enabled: row.enabled !== false, enabled: row.enabled !== false,
title, title,
tags: normalizeCanvasSkillTags(row.tags), tags,
version: CANVAS_SKILL_TAGS_CONFIG_VERSION, version: CANVAS_SKILL_TAGS_CONFIG_VERSION,
}; };
} }

View File

@ -1,5 +1,7 @@
import { expect, test } from "@playwright/test"; import { expect, test } from "@playwright/test";
import { import {
CANVAS_SKILL_TAGS_CONFIG_VERSION,
CANVAS_SKILL_TAGS_V6_ADDITIONS,
DEFAULT_CANVAS_SKILL_TAGS, DEFAULT_CANVAS_SKILL_TAGS,
normalizeCanvasSkillTagSettings, normalizeCanvasSkillTagSettings,
type CanvasSkillTag, type CanvasSkillTag,
@ -161,7 +163,7 @@ test.describe.serial("/api/admin/settings", () => {
); );
}); });
test("canvas skill tag upgrades preserve admin configured tags without restoring defaults", () => { test("canvas skill tag upgrades preserve custom tags and append new managed skills", () => {
const customTags: CanvasSkillTag[] = [ const customTags: CanvasSkillTag[] = [
{ {
id: "custom-claw-skill", id: "custom-claw-skill",
@ -183,17 +185,45 @@ test.describe.serial("/api/admin/settings", () => {
); );
expect(normalized.title).toBe("试试这些 Skills"); expect(normalized.title).toBe("试试这些 Skills");
expect(normalized.tags).toEqual(customTags); expect(normalized.tags).toEqual([
...customTags,
...CANVAS_SKILL_TAGS_V6_ADDITIONS,
]);
expect( expect(
normalized.tags.some((tag) => tag.id === "promotion-diagnosis-db") normalized.tags.some((tag) => tag.id === "promotion-diagnosis-db")
).toBe(false); ).toBe(false);
}); });
test("current canvas skill tag settings can intentionally remove managed skills", () => {
const customTags: CanvasSkillTag[] = [
{
id: "custom-claw-skill",
name: "自定义技能",
prompt: "只保留后台手动配置的技能。",
},
];
const normalized = normalizeCanvasSkillTagSettings(
{
enabled: true,
title: "Claw 快捷技能",
tags: customTags,
version: CANVAS_SKILL_TAGS_CONFIG_VERSION,
},
{ requireCurrentVersion: true }
);
expect(normalized.tags).toEqual(customTags);
});
test("canvas skill tags still use defaults when no stored setting exists", () => { test("canvas skill tags still use defaults when no stored setting exists", () => {
const normalized = normalizeCanvasSkillTagSettings(null, { const normalized = normalizeCanvasSkillTagSettings(null, {
requireCurrentVersion: true, requireCurrentVersion: true,
}); });
expect(normalized.tags).toEqual(DEFAULT_CANVAS_SKILL_TAGS); expect(normalized.tags).toEqual(DEFAULT_CANVAS_SKILL_TAGS);
expect(normalized.tags.map((tag) => tag.name)).toEqual(
expect.arrayContaining(["产品生命周期总览", "新品进度"])
);
}); });
}); });