@aswless_854771076/ai_short_studio_cli 0.1.33 → 0.1.34
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 +4 -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 +10 -4
- 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
|
|
@@ -114,9 +116,10 @@ CLI 通过 `/api/v1/bootstrap` 完成版本、连通性与 Supabase 公共配置
|
|
|
114
116
|
ai-short-studio skill install --target codex
|
|
115
117
|
ai-short-studio skill status --target codex
|
|
116
118
|
ai-short-studio skill update --target codex
|
|
119
|
+
ai-short-studio skill update --target codex --skill short-drama --json
|
|
117
120
|
```
|
|
118
121
|
|
|
119
|
-
手动恢复或安装到其他 Agent 时,可使用 `--target agents
|
|
122
|
+
手动恢复或安装到其他 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
123
|
|
|
121
124
|
## 发布
|
|
122
125
|
|
|
@@ -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.
|