@aswless_854771076/ai_short_studio_cli 0.1.33 → 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.
- package/README.md +6 -1
- package/dist/commands/canvas/node/types.js +1 -1
- package/dist/commands/skill/install.d.ts +2 -0
- package/dist/commands/skill/install.js +12 -3
- package/dist/commands/skill/path.d.ts +4 -0
- package/dist/commands/skill/path.js +13 -2
- package/dist/commands/skill/status.d.ts +2 -0
- package/dist/commands/skill/status.js +13 -3
- package/dist/skill/command-flags.d.ts +6 -2
- package/dist/skill/command-flags.js +9 -4
- package/dist/skill/skill-manager.d.ts +105 -1
- package/dist/skill/skill-manager.js +73 -33
- package/package.json +1 -1
- package/scripts/postinstall.mjs +16 -4
- package/skills/short-drama/LICENSE +21 -0
- package/skills/short-drama/README.md +508 -0
- package/skills/short-drama/SKILL.md +525 -0
- package/skills/short-drama/references/compliance-checklist.md +295 -0
- package/skills/short-drama/references/genre-guide.md +195 -0
- package/skills/short-drama/references/hook-design.md +310 -0
- package/skills/short-drama/references/opening-rules.md +205 -0
- package/skills/short-drama/references/paywall-design.md +220 -0
- package/skills/short-drama/references/rhythm-curve.md +263 -0
- package/skills/short-drama/references/satisfaction-matrix.md +319 -0
- package/skills/short-drama/references/villain-design.md +319 -0
- package/skills/using-vvicat-ai-short-studio-cli/SKILL.md +11 -5
- package/skills/using-vvicat-ai-short-studio-cli/references/commands.md +4 -4
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ ai-short-studio canvas node types --json
|
|
|
13
13
|
|
|
14
14
|
npm 包内置 `using-vvicat-ai-short-studio-cli` Agent Skill。安装或升级 CLI 时,`postinstall` 会自动把该 Skill 安装或更新到 Codex;用户修改过或不由 CLI 管理的副本不会被覆盖。设置 `VVICAT_SKIP_SKILL_INSTALL=1` 可关闭自动安装。
|
|
15
15
|
|
|
16
|
+
npm 安装或升级现在会同时安装 `using-vvicat-ai-short-studio-cli` 和 `short-drama`。`skill install/status/update` 默认管理全部内置 Skill,也可用 `--skill short-drama` 只管理短剧 Skill;某个 Skill 被用户修改时会独立跳过,不影响另一个 Skill。创作剧本使用 `short-drama`,操作 VVICAT 使用主 CLI Skill。
|
|
17
|
+
|
|
16
18
|
邮箱注册和登录从 stdin 读取密码:
|
|
17
19
|
|
|
18
20
|
```bash
|
|
@@ -92,6 +94,8 @@ CLI 通过 `/api/v1/bootstrap` 完成版本、连通性与 Supabase 公共配置
|
|
|
92
94
|
|
|
93
95
|
`storyboard-image` schema 现在暴露节点级 `gridSize`。CLI 继续按实时 schema 通用写入,配套 Skill 会分析每个镜头的动作、运镜、多人调度和连续性难度,向用户确认后逐镜设置;未单独设置或连接旧服务时回退到项目格数,系统默认为 6 格。
|
|
94
96
|
|
|
97
|
+
`character-visual-design` schema 暴露 `candidateCount`,范围为 1~3,默认值为 1。CLI 按实时 schema 校验和写入;多个视觉描述仍分别进入独立三视图和图片资产链。
|
|
98
|
+
|
|
95
99
|
`canvas settings set` 支持项目级 `imageResolution`、`imageQuality`、`videoResolution`,未覆盖的图片、图片编辑和视频节点会继承这些默认值。候选值来自 `settings get` 对应字段的实时 `options`;节点仅在需要偏离项目默认时,按当前模型 `capabilities` 写入 `resolution`、`quality` 覆盖。
|
|
96
100
|
|
|
97
101
|
项目输出语言通过 `ai-short-studio canvas settings set --project <PROJECT_ID> --field outputLanguage --value en` 设置,也可用 `null` 恢复自动。语言变化只把相关下游节点标为 stale,不删除历史资产或自动生成。交付时只下载每镜 selected 原始 MP4,按 `S001.mp4` 顺序命名,并用 manifest 记录镜号、节点、资产、版本和文件名;字幕单独交付,不烧录进原始视频。
|
|
@@ -114,9 +118,10 @@ CLI 通过 `/api/v1/bootstrap` 完成版本、连通性与 Supabase 公共配置
|
|
|
114
118
|
ai-short-studio skill install --target codex
|
|
115
119
|
ai-short-studio skill status --target codex
|
|
116
120
|
ai-short-studio skill update --target codex
|
|
121
|
+
ai-short-studio skill update --target codex --skill short-drama --json
|
|
117
122
|
```
|
|
118
123
|
|
|
119
|
-
手动恢复或安装到其他 Agent 时,可使用 `--target agents
|
|
124
|
+
手动恢复或安装到其他 Agent 时,可使用 `--target agents`;自定义全部内置 Skills 的父目录使用 `--root <path>`。旧版 `--directory <path>` 保留为兼容入口,只把主 CLI Skill 精确安装到该目录。自动安装对应使用 `VVICAT_SKILLS_ROOT`;旧变量 `VVICAT_SKILL_DIRECTORY` 仍保持主 Skill 精确目录语义。CLI 会记录版本和 SHA-256;已安装 Skill 被用户修改后,更新默认停止,只有显式 `--force` 才覆盖。自动安装可通过 `VVICAT_SKIP_SKILL_INSTALL=1` 跳过。
|
|
120
125
|
|
|
121
126
|
## 发布
|
|
122
127
|
|
|
@@ -2,7 +2,7 @@ import { Args } from '@oclif/core';
|
|
|
2
2
|
import { BaseCommand } from '../../../base-command.js';
|
|
3
3
|
import { commandApi } from '../../../command-helpers.js';
|
|
4
4
|
export default class CanvasNodeTypes extends BaseCommand {
|
|
5
|
-
static description = '
|
|
5
|
+
static description = '读取服务端实时画布节点类型、默认配置与 schema';
|
|
6
6
|
static args = { kind: Args.string() };
|
|
7
7
|
static flags = BaseCommand.baseFlags;
|
|
8
8
|
async run() { const { args, flags } = await this.parse(CanvasNodeTypes); const { api } = await commandApi(flags); const catalog = await api.nodeTypes(); this.print(args.kind ? catalog.nodeTypes.find((item) => item.kind === args.kind) ?? null : catalog); }
|
|
@@ -4,6 +4,8 @@ export default class SkillInstall extends BaseCommand {
|
|
|
4
4
|
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
5
5
|
target: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
6
|
directory: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
root: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
skill: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
9
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
10
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
11
|
locale: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../base-command.js';
|
|
3
|
-
import { installSkill } from '../../skill/skill-manager.js';
|
|
4
|
-
import {
|
|
3
|
+
import { installSkill, installSkills, withLegacySkillFields } from '../../skill/skill-manager.js';
|
|
4
|
+
import { resolveSkillsRoot, selectedSkillNames, skillFlags } from '../../skill/command-flags.js';
|
|
5
5
|
export default class SkillInstall extends BaseCommand {
|
|
6
6
|
static flags = { ...BaseCommand.baseFlags, ...skillFlags, force: Flags.boolean({ description: '覆盖非托管或已修改的 Skill' }) };
|
|
7
|
-
async run() {
|
|
7
|
+
async run() {
|
|
8
|
+
const { flags } = await this.parse(SkillInstall);
|
|
9
|
+
if (flags.directory) {
|
|
10
|
+
this.print(await installSkill({ target: flags.directory, force: flags.force }));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
this.print(withLegacySkillFields(await installSkills({
|
|
14
|
+
root: resolveSkillsRoot(flags), skills: selectedSkillNames(flags.skill), force: flags.force,
|
|
15
|
+
})));
|
|
16
|
+
}
|
|
8
17
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command.js';
|
|
2
2
|
export default class SkillPath extends BaseCommand {
|
|
3
3
|
static flags: {
|
|
4
|
+
target: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
5
|
+
directory: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
root: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
skill: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
4
8
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
5
9
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
6
10
|
locale: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
import { join } from 'node:path';
|
|
2
3
|
import { bundledSkillPath } from '../../skill/skill-manager.js';
|
|
4
|
+
import { resolveSkillsRoot, selectedSkillNames, skillFlags } from '../../skill/command-flags.js';
|
|
3
5
|
export default class SkillPath extends BaseCommand {
|
|
4
|
-
static flags = { ...BaseCommand.baseFlags };
|
|
5
|
-
async run() {
|
|
6
|
+
static flags = { ...BaseCommand.baseFlags, ...skillFlags };
|
|
7
|
+
async run() {
|
|
8
|
+
const { flags } = await this.parse(SkillPath);
|
|
9
|
+
if (flags.directory) {
|
|
10
|
+
this.print({ path: bundledSkillPath(), target: flags.directory });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const root = resolveSkillsRoot(flags);
|
|
14
|
+
const skills = selectedSkillNames(flags.skill).map((name) => ({ name, path: join(root, name), bundledPath: bundledSkillPath(name) }));
|
|
15
|
+
this.print({ path: skills[0]?.bundledPath ?? bundledSkillPath(), root, skills });
|
|
16
|
+
}
|
|
6
17
|
}
|
|
@@ -3,6 +3,8 @@ export default class SkillStatus extends BaseCommand {
|
|
|
3
3
|
static flags: {
|
|
4
4
|
target: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
5
5
|
directory: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
root: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
skill: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
8
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
9
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
10
|
locale: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command.js';
|
|
2
|
-
import { skillStatus } from '../../skill/skill-manager.js';
|
|
3
|
-
import {
|
|
2
|
+
import { skillStatus, skillStatuses, withLegacySkillFields } from '../../skill/skill-manager.js';
|
|
3
|
+
import { resolveSkillsRoot, selectedSkillNames, skillFlags } from '../../skill/command-flags.js';
|
|
4
4
|
export default class SkillStatus extends BaseCommand {
|
|
5
5
|
static flags = { ...BaseCommand.baseFlags, ...skillFlags };
|
|
6
|
-
async run() {
|
|
6
|
+
async run() {
|
|
7
|
+
const { flags } = await this.parse(SkillStatus);
|
|
8
|
+
if (flags.directory) {
|
|
9
|
+
this.print(await skillStatus(flags.directory));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const result = await skillStatuses(resolveSkillsRoot(flags));
|
|
13
|
+
const selected = new Set(selectedSkillNames(flags.skill));
|
|
14
|
+
const skills = result.skills.filter((item) => selected.has(item.name));
|
|
15
|
+
this.print(withLegacySkillFields({ ...result, current: skills.every((item) => item.current), skills }));
|
|
16
|
+
}
|
|
7
17
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { type BundledSkillName } from './skill-manager.js';
|
|
1
2
|
export declare const skillFlags: {
|
|
2
3
|
target: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
3
4
|
directory: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
5
|
+
root: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
skill: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
4
7
|
};
|
|
5
|
-
export declare function
|
|
8
|
+
export declare function resolveSkillsRoot(flags: {
|
|
6
9
|
target: string;
|
|
7
|
-
|
|
10
|
+
root?: string;
|
|
8
11
|
}): string;
|
|
12
|
+
export declare function selectedSkillNames(skill: string): readonly BundledSkillName[];
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
|
-
import {
|
|
2
|
+
import { BUNDLED_SKILL_NAMES, defaultSkillsRoot, normalizeSkillsRoot, } from './skill-manager.js';
|
|
3
3
|
export const skillFlags = {
|
|
4
4
|
target: Flags.string({ options: ['codex', 'agents'], default: 'codex', description: 'Agent 类型' }),
|
|
5
|
-
directory: Flags.string({ description: '
|
|
5
|
+
directory: Flags.string({ description: '兼容旧版:主 Skill 的精确安装目录', exclusive: ['root'] }),
|
|
6
|
+
root: Flags.string({ description: '全部内置 Skills 的根目录', exclusive: ['directory'] }),
|
|
7
|
+
skill: Flags.string({ options: ['all', ...BUNDLED_SKILL_NAMES], default: 'all', description: '要管理的内置 Skill' }),
|
|
6
8
|
};
|
|
7
|
-
export function
|
|
8
|
-
return flags.
|
|
9
|
+
export function resolveSkillsRoot(flags) {
|
|
10
|
+
return normalizeSkillsRoot(flags.root || defaultSkillsRoot(flags.target));
|
|
11
|
+
}
|
|
12
|
+
export function selectedSkillNames(skill) {
|
|
13
|
+
return skill === 'all' ? BUNDLED_SKILL_NAMES : [skill];
|
|
9
14
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
export declare const BUNDLED_SKILL_NAMES: readonly ["using-vvicat-ai-short-studio-cli", "short-drama"];
|
|
2
|
+
export type BundledSkillName = typeof BUNDLED_SKILL_NAMES[number];
|
|
1
3
|
export type SkillTarget = 'codex' | 'agents';
|
|
2
|
-
export declare function bundledSkillPath(): string;
|
|
4
|
+
export declare function bundledSkillPath(name?: BundledSkillName): string;
|
|
5
|
+
export declare function defaultSkillsRoot(target: SkillTarget): string;
|
|
3
6
|
export declare function defaultSkillTarget(target: SkillTarget): string;
|
|
7
|
+
export declare function normalizeSkillsRoot(path: string): string;
|
|
4
8
|
export declare function skillStatus(target: string): Promise<{
|
|
9
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
5
10
|
target: string;
|
|
6
11
|
installed: boolean;
|
|
7
12
|
managed: boolean;
|
|
@@ -10,10 +15,99 @@ export declare function skillStatus(target: string): Promise<{
|
|
|
10
15
|
installedVersion: string | null;
|
|
11
16
|
modified: boolean;
|
|
12
17
|
}>;
|
|
18
|
+
export declare function installSkills(input: {
|
|
19
|
+
root: string;
|
|
20
|
+
skills?: readonly BundledSkillName[];
|
|
21
|
+
force?: boolean;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
root: string;
|
|
24
|
+
current: boolean;
|
|
25
|
+
skills: ({
|
|
26
|
+
action: "unchanged";
|
|
27
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
28
|
+
target: string;
|
|
29
|
+
installed: boolean;
|
|
30
|
+
managed: boolean;
|
|
31
|
+
current: boolean;
|
|
32
|
+
packageVersion: string;
|
|
33
|
+
installedVersion: string | null;
|
|
34
|
+
modified: boolean;
|
|
35
|
+
} | {
|
|
36
|
+
action: "skipped";
|
|
37
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
38
|
+
target: string;
|
|
39
|
+
installed: boolean;
|
|
40
|
+
managed: boolean;
|
|
41
|
+
current: boolean;
|
|
42
|
+
packageVersion: string;
|
|
43
|
+
installedVersion: string | null;
|
|
44
|
+
modified: boolean;
|
|
45
|
+
} | {
|
|
46
|
+
action: "updated" | "installed";
|
|
47
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
48
|
+
target: string;
|
|
49
|
+
installed: boolean;
|
|
50
|
+
managed: boolean;
|
|
51
|
+
current: boolean;
|
|
52
|
+
packageVersion: string;
|
|
53
|
+
installedVersion: string | null;
|
|
54
|
+
modified: boolean;
|
|
55
|
+
})[];
|
|
56
|
+
}>;
|
|
57
|
+
export declare function autoInstallSkills(root: string): Promise<{
|
|
58
|
+
root: string;
|
|
59
|
+
current: boolean;
|
|
60
|
+
skills: ({
|
|
61
|
+
action: "unchanged";
|
|
62
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
63
|
+
target: string;
|
|
64
|
+
installed: boolean;
|
|
65
|
+
managed: boolean;
|
|
66
|
+
current: boolean;
|
|
67
|
+
packageVersion: string;
|
|
68
|
+
installedVersion: string | null;
|
|
69
|
+
modified: boolean;
|
|
70
|
+
} | {
|
|
71
|
+
action: "skipped";
|
|
72
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
73
|
+
target: string;
|
|
74
|
+
installed: boolean;
|
|
75
|
+
managed: boolean;
|
|
76
|
+
current: boolean;
|
|
77
|
+
packageVersion: string;
|
|
78
|
+
installedVersion: string | null;
|
|
79
|
+
modified: boolean;
|
|
80
|
+
} | {
|
|
81
|
+
action: "updated" | "installed";
|
|
82
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
83
|
+
target: string;
|
|
84
|
+
installed: boolean;
|
|
85
|
+
managed: boolean;
|
|
86
|
+
current: boolean;
|
|
87
|
+
packageVersion: string;
|
|
88
|
+
installedVersion: string | null;
|
|
89
|
+
modified: boolean;
|
|
90
|
+
})[];
|
|
91
|
+
}>;
|
|
92
|
+
export declare function skillStatuses(rootInput: string): Promise<{
|
|
93
|
+
root: string;
|
|
94
|
+
current: boolean;
|
|
95
|
+
skills: {
|
|
96
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
97
|
+
target: string;
|
|
98
|
+
installed: boolean;
|
|
99
|
+
managed: boolean;
|
|
100
|
+
current: boolean;
|
|
101
|
+
packageVersion: string;
|
|
102
|
+
installedVersion: string | null;
|
|
103
|
+
modified: boolean;
|
|
104
|
+
}[];
|
|
105
|
+
}>;
|
|
13
106
|
export declare function installSkill(input: {
|
|
14
107
|
target: string;
|
|
15
108
|
force?: boolean;
|
|
16
109
|
}): Promise<{
|
|
110
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
17
111
|
target: string;
|
|
18
112
|
installed: boolean;
|
|
19
113
|
managed: boolean;
|
|
@@ -23,6 +117,7 @@ export declare function installSkill(input: {
|
|
|
23
117
|
modified: boolean;
|
|
24
118
|
}>;
|
|
25
119
|
export declare function autoInstallSkill(target: string): Promise<{
|
|
120
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
26
121
|
target: string;
|
|
27
122
|
installed: boolean;
|
|
28
123
|
managed: boolean;
|
|
@@ -32,6 +127,7 @@ export declare function autoInstallSkill(target: string): Promise<{
|
|
|
32
127
|
modified: boolean;
|
|
33
128
|
action: "unchanged";
|
|
34
129
|
} | {
|
|
130
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
35
131
|
target: string;
|
|
36
132
|
installed: boolean;
|
|
37
133
|
managed: boolean;
|
|
@@ -41,6 +137,7 @@ export declare function autoInstallSkill(target: string): Promise<{
|
|
|
41
137
|
modified: boolean;
|
|
42
138
|
action: "skipped";
|
|
43
139
|
} | {
|
|
140
|
+
name: "using-vvicat-ai-short-studio-cli" | "short-drama";
|
|
44
141
|
target: string;
|
|
45
142
|
installed: boolean;
|
|
46
143
|
managed: boolean;
|
|
@@ -50,3 +147,10 @@ export declare function autoInstallSkill(target: string): Promise<{
|
|
|
50
147
|
modified: boolean;
|
|
51
148
|
action: "updated" | "installed";
|
|
52
149
|
}>;
|
|
150
|
+
export declare function withLegacySkillFields<T extends {
|
|
151
|
+
root: string;
|
|
152
|
+
current: boolean;
|
|
153
|
+
skills: Array<Record<string, unknown> & {
|
|
154
|
+
current: boolean;
|
|
155
|
+
}>;
|
|
156
|
+
}>(result: T): Record<string, unknown>;
|
|
@@ -1,72 +1,112 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { cp, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
import { dirname, join, relative } from 'node:path';
|
|
4
|
+
import { basename, dirname, join, relative } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
-
const
|
|
6
|
+
export const BUNDLED_SKILL_NAMES = ['using-vvicat-ai-short-studio-cli', 'short-drama'];
|
|
7
7
|
const PACKAGE_JSON = fileURLToPath(new URL('../../package.json', import.meta.url));
|
|
8
8
|
const MANIFEST = '.vvicat-skill.json';
|
|
9
|
-
export function bundledSkillPath() {
|
|
10
|
-
return
|
|
9
|
+
export function bundledSkillPath(name = BUNDLED_SKILL_NAMES[0]) {
|
|
10
|
+
return fileURLToPath(new URL(`../../skills/${name}`, import.meta.url));
|
|
11
|
+
}
|
|
12
|
+
export function defaultSkillsRoot(target) {
|
|
13
|
+
return join(homedir(), target === 'codex' ? '.codex' : '.agents', 'skills');
|
|
11
14
|
}
|
|
12
15
|
export function defaultSkillTarget(target) {
|
|
13
|
-
return join(
|
|
16
|
+
return join(defaultSkillsRoot(target), BUNDLED_SKILL_NAMES[0]);
|
|
14
17
|
}
|
|
15
|
-
export
|
|
18
|
+
export function normalizeSkillsRoot(path) {
|
|
19
|
+
return BUNDLED_SKILL_NAMES.includes(basename(path)) ? dirname(path) : path;
|
|
20
|
+
}
|
|
21
|
+
async function statusFor(name, target) {
|
|
16
22
|
const packageVersion = await readPackageVersion();
|
|
17
|
-
const bundledChecksum = await directoryChecksum(
|
|
23
|
+
const bundledChecksum = await directoryChecksum(bundledSkillPath(name));
|
|
18
24
|
const installed = await exists(target);
|
|
19
25
|
const manifest = installed ? await readManifest(target) : null;
|
|
20
26
|
const installedChecksum = installed ? await directoryChecksum(target) : null;
|
|
21
27
|
return {
|
|
22
|
-
target,
|
|
23
|
-
installed,
|
|
24
|
-
managed: Boolean(manifest),
|
|
28
|
+
name, target, installed, managed: Boolean(manifest),
|
|
25
29
|
current: Boolean(manifest && manifest.packageVersion === packageVersion && installedChecksum === bundledChecksum),
|
|
26
|
-
packageVersion,
|
|
27
|
-
installedVersion: manifest?.packageVersion ?? null,
|
|
30
|
+
packageVersion, installedVersion: manifest?.packageVersion ?? null,
|
|
28
31
|
modified: Boolean(manifest && installedChecksum !== manifest.checksum),
|
|
29
32
|
};
|
|
30
33
|
}
|
|
31
|
-
export async function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (current.modified)
|
|
37
|
-
throw new Error('已安装 Skill 已被修改;使用 --force 才能替换');
|
|
38
|
-
}
|
|
39
|
-
const checksum = await directoryChecksum(BUNDLED_SKILL);
|
|
34
|
+
export async function skillStatus(target) {
|
|
35
|
+
return statusFor(BUNDLED_SKILL_NAMES[0], target);
|
|
36
|
+
}
|
|
37
|
+
async function installOne(name, target) {
|
|
38
|
+
const checksum = await directoryChecksum(bundledSkillPath(name));
|
|
40
39
|
const packageVersion = await readPackageVersion();
|
|
41
|
-
const temporary = `${
|
|
42
|
-
const backup = `${
|
|
43
|
-
await mkdir(dirname(
|
|
40
|
+
const temporary = `${target}.tmp-${process.pid}`;
|
|
41
|
+
const backup = `${target}.backup-${process.pid}`;
|
|
42
|
+
await mkdir(dirname(target), { recursive: true });
|
|
44
43
|
await rm(temporary, { recursive: true, force: true });
|
|
45
|
-
await cp(
|
|
44
|
+
await cp(bundledSkillPath(name), temporary, { recursive: true });
|
|
46
45
|
await writeFile(join(temporary, MANIFEST), `${JSON.stringify({ packageVersion, checksum }, null, 2)}\n`, 'utf8');
|
|
47
|
-
const hadTarget = await exists(
|
|
46
|
+
const hadTarget = await exists(target);
|
|
48
47
|
try {
|
|
49
48
|
if (hadTarget)
|
|
50
|
-
await rename(
|
|
51
|
-
await rename(temporary,
|
|
49
|
+
await rename(target, backup);
|
|
50
|
+
await rename(temporary, target);
|
|
52
51
|
if (hadTarget)
|
|
53
52
|
await rm(backup, { recursive: true, force: true });
|
|
54
53
|
}
|
|
55
54
|
catch (error) {
|
|
56
55
|
await rm(temporary, { recursive: true, force: true });
|
|
57
|
-
if (hadTarget && await exists(backup) && !await exists(
|
|
58
|
-
await rename(backup,
|
|
56
|
+
if (hadTarget && await exists(backup) && !await exists(target))
|
|
57
|
+
await rename(backup, target);
|
|
59
58
|
throw error;
|
|
60
59
|
}
|
|
61
|
-
return
|
|
60
|
+
return statusFor(name, target);
|
|
61
|
+
}
|
|
62
|
+
export async function installSkills(input) {
|
|
63
|
+
const root = normalizeSkillsRoot(input.root);
|
|
64
|
+
const results = [];
|
|
65
|
+
for (const name of input.skills ?? BUNDLED_SKILL_NAMES) {
|
|
66
|
+
const target = join(root, name);
|
|
67
|
+
const current = await statusFor(name, target);
|
|
68
|
+
if (current.current) {
|
|
69
|
+
results.push({ ...current, action: 'unchanged' });
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (current.installed && !input.force && (!current.managed || current.modified)) {
|
|
73
|
+
results.push({ ...current, action: 'skipped' });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
results.push({ ...await installOne(name, target), action: current.installed ? 'updated' : 'installed' });
|
|
77
|
+
}
|
|
78
|
+
return { root, current: results.every((item) => item.current), skills: results };
|
|
79
|
+
}
|
|
80
|
+
export function autoInstallSkills(root) {
|
|
81
|
+
return installSkills({ root });
|
|
82
|
+
}
|
|
83
|
+
export async function skillStatuses(rootInput) {
|
|
84
|
+
const root = normalizeSkillsRoot(rootInput);
|
|
85
|
+
const skills = await Promise.all(BUNDLED_SKILL_NAMES.map((name) => statusFor(name, join(root, name))));
|
|
86
|
+
return { root, current: skills.every((item) => item.current), skills };
|
|
87
|
+
}
|
|
88
|
+
export async function installSkill(input) {
|
|
89
|
+
const name = BUNDLED_SKILL_NAMES[0];
|
|
90
|
+
const current = await statusFor(name, input.target);
|
|
91
|
+
if (current.installed && !input.force) {
|
|
92
|
+
if (!current.managed)
|
|
93
|
+
throw new Error('目标目录不是 CLI 管理的 Skill;使用 --force 才能替换');
|
|
94
|
+
if (current.modified)
|
|
95
|
+
throw new Error('已安装 Skill 已被修改;使用 --force 才能替换');
|
|
96
|
+
}
|
|
97
|
+
return installOne(name, input.target);
|
|
62
98
|
}
|
|
63
99
|
export async function autoInstallSkill(target) {
|
|
64
|
-
const
|
|
100
|
+
const name = BUNDLED_SKILL_NAMES[0];
|
|
101
|
+
const current = await statusFor(name, target);
|
|
65
102
|
if (current.current)
|
|
66
103
|
return { action: 'unchanged', ...current };
|
|
67
104
|
if (current.installed && (!current.managed || current.modified))
|
|
68
105
|
return { action: 'skipped', ...current };
|
|
69
|
-
return { action: current.installed ? 'updated' : 'installed', ...await
|
|
106
|
+
return { action: current.installed ? 'updated' : 'installed', ...await installOne(name, target) };
|
|
107
|
+
}
|
|
108
|
+
export function withLegacySkillFields(result) {
|
|
109
|
+
return { ...result.skills[0], root: result.root, allCurrent: result.current, skills: result.skills };
|
|
70
110
|
}
|
|
71
111
|
async function directoryChecksum(root) {
|
|
72
112
|
const files = await filesBelow(root);
|
package/package.json
CHANGED
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
1
|
+
import { dirname, resolve } from 'node:path'
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
|
|
4
4
|
const packageRoot = fileURLToPath(new URL('../', import.meta.url))
|
|
@@ -11,9 +11,21 @@ export function shouldAutoInstallSkill(environment = process.env) {
|
|
|
11
11
|
export async function main(environment = process.env) {
|
|
12
12
|
if (!shouldAutoInstallSkill(environment)) return
|
|
13
13
|
try {
|
|
14
|
-
const { autoInstallSkill,
|
|
15
|
-
const result =
|
|
16
|
-
|
|
14
|
+
const { autoInstallSkill, autoInstallSkills, defaultSkillsRoot, installSkills } = await import('../dist/skill/skill-manager.js')
|
|
15
|
+
const result = environment.VVICAT_SKILL_DIRECTORY
|
|
16
|
+
? {
|
|
17
|
+
skills: [
|
|
18
|
+
await autoInstallSkill(environment.VVICAT_SKILL_DIRECTORY),
|
|
19
|
+
...(await installSkills({
|
|
20
|
+
root: dirname(environment.VVICAT_SKILL_DIRECTORY),
|
|
21
|
+
skills: ['short-drama'],
|
|
22
|
+
})).skills,
|
|
23
|
+
],
|
|
24
|
+
}
|
|
25
|
+
: await autoInstallSkills(environment.VVICAT_SKILLS_ROOT || defaultSkillsRoot('codex'))
|
|
26
|
+
for (const skill of result.skills) {
|
|
27
|
+
process.stdout.write(`VVICAT Skill ${skill.name} ${skill.action}: ${skill.target}\n`)
|
|
28
|
+
}
|
|
17
29
|
} catch (error) {
|
|
18
30
|
process.stderr.write(`VVICAT CLI 已安装,Skill 自动安装已跳过:${error instanceof Error ? error.message : String(error)}\n`)
|
|
19
31
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 0xsline
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|