@gitruck/cli 0.1.0 → 0.1.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/README.md +9 -13
- package/dist/index.js +134 -106
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,26 +30,21 @@
|
|
|
30
30
|
需要 Node.js ≥ 20.6(`node -v` 查看)。
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
# 1)
|
|
34
|
-
npm i -g @gitruck/cli
|
|
35
|
-
#
|
|
33
|
+
# 1) 一条命令装全:命令行 gtrk + /gtrk-oralcut skill + 配置(填 API Key、自动扫剪映目录)
|
|
34
|
+
npm i -g @gitruck/cli && gtrk install
|
|
35
|
+
# 或免全局安装直接用:npx @gitruck/cli install
|
|
36
36
|
|
|
37
|
-
# 2)
|
|
38
|
-
gtrk init
|
|
39
|
-
|
|
40
|
-
# 3) 剪一条(剪完自动打开产物目录)
|
|
37
|
+
# 2) 剪一条(剪完自动打开产物目录)
|
|
41
38
|
gtrk oralcut "D:/素材/某选题-原始口播.mp4" --script "D:/素材/某选题-文字稿.txt"
|
|
42
39
|
```
|
|
43
40
|
|
|
44
|
-
>
|
|
41
|
+
> 只想配置、不装 skill:用 `gtrk init`。本地开发:`cd gtrk-cli && bun install && bun run src/index.ts <命令>`。
|
|
45
42
|
|
|
46
43
|
产物目录形如 `<毛片名>-video-project-<YYMMDD-HHMMSS>/`,内含 `gtrk/`、`jianying/`、`xml/` 三端工程。
|
|
47
44
|
|
|
48
45
|
## 给 AI Agent 用
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
gtrk skills install # 把 /gtrk-oralcut 装进 ~/.claude/skills
|
|
52
|
-
```
|
|
47
|
+
`gtrk install` 已经把 `/gtrk-oralcut` skill 装进 `~/.claude/skills`(单独装用 `gtrk skills install`)。
|
|
53
48
|
|
|
54
49
|
然后在 Claude Code 里直接说「**帮我把这条口播剪一版**」或打 `/gtrk-oralcut`,agent 会问清毛片 / 文稿 / 节奏,调 `gtrk oralcut --json` 跑通闭环、验证产物、把三端打开方式回给你。完整可移植 playbook 见 [`AGENT.md`](./AGENT.md)。
|
|
55
50
|
|
|
@@ -106,9 +101,10 @@ gtrk init --api-key <KEY> --jianying-draft-dir auto -y
|
|
|
106
101
|
|
|
107
102
|
### 其它
|
|
108
103
|
|
|
109
|
-
- `gtrk
|
|
104
|
+
- `gtrk install [--api-key … -y --skills-dir …]` — 一条命令装全(skill + 配置 + 体检),对标飞书 `lark-cli install`。
|
|
105
|
+
- `gtrk init [--api-key … --api-base … --jianying-draft-dir … -y]` — 仅配置(交互 / 非交互)。
|
|
110
106
|
- `gtrk doctor` — 体检。
|
|
111
|
-
- `gtrk skills install [--dir <skills 目录>]` —
|
|
107
|
+
- `gtrk skills install [--dir <skills 目录>]` — 单独安装 agent skill。
|
|
112
108
|
|
|
113
109
|
---
|
|
114
110
|
|
package/dist/index.js
CHANGED
|
@@ -1888,22 +1888,101 @@ var {
|
|
|
1888
1888
|
Help
|
|
1889
1889
|
} = import__.default;
|
|
1890
1890
|
|
|
1891
|
+
// src/index.ts
|
|
1892
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
1893
|
+
import { join as join8 } from "node:path";
|
|
1894
|
+
|
|
1895
|
+
// src/lib/paths.ts
|
|
1896
|
+
import { dirname, join } from "node:path";
|
|
1897
|
+
import { fileURLToPath } from "node:url";
|
|
1898
|
+
import { existsSync } from "node:fs";
|
|
1899
|
+
function packageRoot() {
|
|
1900
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
1901
|
+
for (let i = 0;i < 8; i++) {
|
|
1902
|
+
if (existsSync(join(dir, "package.json")))
|
|
1903
|
+
return dir;
|
|
1904
|
+
const parent = dirname(dir);
|
|
1905
|
+
if (parent === dir)
|
|
1906
|
+
break;
|
|
1907
|
+
dir = parent;
|
|
1908
|
+
}
|
|
1909
|
+
return dir;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// src/commands/skills.ts
|
|
1913
|
+
import { homedir } from "node:os";
|
|
1914
|
+
import { join as join2 } from "node:path";
|
|
1915
|
+
import { existsSync as existsSync2, mkdirSync, copyFileSync } from "node:fs";
|
|
1916
|
+
|
|
1917
|
+
// src/lib/log.ts
|
|
1918
|
+
var c = {
|
|
1919
|
+
dim: (s) => `\x1B[2m${s}\x1B[0m`,
|
|
1920
|
+
cyan: (s) => `\x1B[36m${s}\x1B[0m`,
|
|
1921
|
+
green: (s) => `\x1B[32m${s}\x1B[0m`,
|
|
1922
|
+
yellow: (s) => `\x1B[33m${s}\x1B[0m`,
|
|
1923
|
+
red: (s) => `\x1B[31m${s}\x1B[0m`
|
|
1924
|
+
};
|
|
1925
|
+
var humanOut = process.stdout;
|
|
1926
|
+
function routeLogsToStderr() {
|
|
1927
|
+
humanOut = process.stderr;
|
|
1928
|
+
}
|
|
1929
|
+
var line = (s) => humanOut.write(`${s}
|
|
1930
|
+
`);
|
|
1931
|
+
var log = {
|
|
1932
|
+
step: (msg) => line(c.cyan(msg)),
|
|
1933
|
+
info: (msg) => line(c.dim(` ${msg}`)),
|
|
1934
|
+
ok: (msg) => line(c.green(`✅ ${msg}`)),
|
|
1935
|
+
warn: (msg) => line(c.yellow(`⚠️ ${msg}`)),
|
|
1936
|
+
err: (msg) => process.stderr.write(`${c.red(`❌ ${msg}`)}
|
|
1937
|
+
`),
|
|
1938
|
+
tick: (msg) => humanOut.write(`\r ${msg}\x1B[K`),
|
|
1939
|
+
tickEnd: () => humanOut.write(`
|
|
1940
|
+
`)
|
|
1941
|
+
};
|
|
1942
|
+
|
|
1943
|
+
// src/commands/skills.ts
|
|
1944
|
+
var SKILL_NAME = "gtrk-oralcut";
|
|
1945
|
+
var SRC = join2(packageRoot(), "skills", SKILL_NAME, "SKILL.md");
|
|
1946
|
+
function installSkill(opts = {}) {
|
|
1947
|
+
if (!existsSync2(SRC)) {
|
|
1948
|
+
log.warn(`找不到打包的 skill 源:${SRC}(跳过 skill 安装,不影响命令行)`);
|
|
1949
|
+
return false;
|
|
1950
|
+
}
|
|
1951
|
+
const dest = join2(opts.dir ?? join2(homedir(), ".claude", "skills"), SKILL_NAME);
|
|
1952
|
+
try {
|
|
1953
|
+
mkdirSync(dest, { recursive: true });
|
|
1954
|
+
copyFileSync(SRC, join2(dest, "SKILL.md"));
|
|
1955
|
+
log.ok(`已安装 /${SKILL_NAME} → ${join2(dest, "SKILL.md")}`);
|
|
1956
|
+
log.info("在 Claude Code 里打 /gtrk-oralcut,或直接说「帮我剪个口播」即可触发(可能需重载会话)。");
|
|
1957
|
+
return true;
|
|
1958
|
+
} catch (e) {
|
|
1959
|
+
log.warn(`skill 安装失败(不影响命令行使用):${e instanceof Error ? e.message : String(e)}`);
|
|
1960
|
+
return false;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
function registerSkills(program2) {
|
|
1964
|
+
const skills = program2.command("skills").description("管理 agent skill(安装到 Claude Code)");
|
|
1965
|
+
skills.command("install").description(`把 /${SKILL_NAME} 安装到 ~/.claude/skills(对标飞书 skills add)`).option("--dir <dir>", "自定义 skills 目录(缺省 ~/.claude/skills)").action((opts) => {
|
|
1966
|
+
installSkill({ dir: opts.dir });
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1891
1970
|
// src/commands/init.ts
|
|
1892
|
-
import { join as
|
|
1893
|
-
import { existsSync as
|
|
1971
|
+
import { join as join5, resolve as resolve2 } from "node:path";
|
|
1972
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
1894
1973
|
|
|
1895
1974
|
// src/lib/user-config.ts
|
|
1896
|
-
import { homedir } from "node:os";
|
|
1897
|
-
import { join } from "node:path";
|
|
1898
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
1975
|
+
import { homedir as homedir2 } from "node:os";
|
|
1976
|
+
import { join as join3 } from "node:path";
|
|
1977
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "node:fs";
|
|
1899
1978
|
var DEFAULT_API_BASE = "https://api.ai-mcn.tv:10000";
|
|
1900
|
-
var DIR =
|
|
1901
|
-
var FILE =
|
|
1979
|
+
var DIR = join3(homedir2(), ".gtrk-cli");
|
|
1980
|
+
var FILE = join3(DIR, "config.json");
|
|
1902
1981
|
function configPath() {
|
|
1903
1982
|
return FILE;
|
|
1904
1983
|
}
|
|
1905
1984
|
function readUserConfig() {
|
|
1906
|
-
if (!
|
|
1985
|
+
if (!existsSync3(FILE))
|
|
1907
1986
|
return {};
|
|
1908
1987
|
try {
|
|
1909
1988
|
return JSON.parse(readFileSync(FILE, "utf8"));
|
|
@@ -1912,29 +1991,29 @@ function readUserConfig() {
|
|
|
1912
1991
|
}
|
|
1913
1992
|
}
|
|
1914
1993
|
function writeUserConfig(patch) {
|
|
1915
|
-
|
|
1994
|
+
mkdirSync2(DIR, { recursive: true });
|
|
1916
1995
|
const merged = { ...readUserConfig(), ...patch };
|
|
1917
1996
|
writeFileSync(FILE, JSON.stringify(merged, null, 2));
|
|
1918
1997
|
}
|
|
1919
1998
|
|
|
1920
1999
|
// src/lib/jianying.ts
|
|
1921
|
-
import { join as
|
|
1922
|
-
import { existsSync as
|
|
2000
|
+
import { join as join4, resolve } from "node:path";
|
|
2001
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
1923
2002
|
function probeJianyingDraftDir() {
|
|
1924
2003
|
const local = process.env.LOCALAPPDATA;
|
|
1925
2004
|
if (!local)
|
|
1926
2005
|
return;
|
|
1927
2006
|
const candidates = [
|
|
1928
|
-
|
|
1929
|
-
|
|
2007
|
+
join4(local, "JianyingPro", "User Data", "Projects", "com.lveditor.draft"),
|
|
2008
|
+
join4(local, "CapCut", "User Data", "Projects", "com.lveditor.draft")
|
|
1930
2009
|
];
|
|
1931
|
-
return candidates.find((p) =>
|
|
2010
|
+
return candidates.find((p) => existsSync4(p));
|
|
1932
2011
|
}
|
|
1933
2012
|
function resolveJianyingDraftDir(opt) {
|
|
1934
2013
|
if (opt && opt !== "auto")
|
|
1935
2014
|
return resolve(opt);
|
|
1936
2015
|
const saved = readUserConfig().jianyingDraftDir;
|
|
1937
|
-
if (saved &&
|
|
2016
|
+
if (saved && existsSync4(saved))
|
|
1938
2017
|
return saved;
|
|
1939
2018
|
return probeJianyingDraftDir();
|
|
1940
2019
|
}
|
|
@@ -1957,27 +2036,10 @@ function launch(cmd) {
|
|
|
1957
2036
|
} catch {}
|
|
1958
2037
|
}
|
|
1959
2038
|
|
|
1960
|
-
// src/lib/paths.ts
|
|
1961
|
-
import { dirname, join as join3 } from "node:path";
|
|
1962
|
-
import { fileURLToPath } from "node:url";
|
|
1963
|
-
import { existsSync as existsSync3 } from "node:fs";
|
|
1964
|
-
function packageRoot() {
|
|
1965
|
-
let dir = dirname(fileURLToPath(import.meta.url));
|
|
1966
|
-
for (let i = 0;i < 8; i++) {
|
|
1967
|
-
if (existsSync3(join3(dir, "package.json")))
|
|
1968
|
-
return dir;
|
|
1969
|
-
const parent = dirname(dir);
|
|
1970
|
-
if (parent === dir)
|
|
1971
|
-
break;
|
|
1972
|
-
dir = parent;
|
|
1973
|
-
}
|
|
1974
|
-
return dir;
|
|
1975
|
-
}
|
|
1976
|
-
|
|
1977
2039
|
// src/lib/prompt.ts
|
|
1978
2040
|
import { stdin, stdout } from "node:process";
|
|
1979
2041
|
import { spawnSync } from "node:child_process";
|
|
1980
|
-
var
|
|
2042
|
+
var c2 = {
|
|
1981
2043
|
dim: (s) => `\x1B[2m${s}\x1B[0m`,
|
|
1982
2044
|
cyan: (s) => `\x1B[36m${s}\x1B[0m`
|
|
1983
2045
|
};
|
|
@@ -2002,8 +2064,8 @@ function readClipboard() {
|
|
|
2002
2064
|
}
|
|
2003
2065
|
function ask(message, opts = {}) {
|
|
2004
2066
|
return new Promise((resolve2) => {
|
|
2005
|
-
const hint = opts.defaultValue ?
|
|
2006
|
-
stdout.write(`${
|
|
2067
|
+
const hint = opts.defaultValue ? c2.dim(` (${opts.defaultValue})`) : "";
|
|
2068
|
+
stdout.write(`${c2.cyan("?")} ${message}${hint} `);
|
|
2007
2069
|
let value = "";
|
|
2008
2070
|
const echo = (s) => stdout.write(opts.mask ? "•".repeat([...s].length) : s);
|
|
2009
2071
|
const wasRaw = stdin.isRaw ?? false;
|
|
@@ -2060,14 +2122,14 @@ function promptSecret(message) {
|
|
|
2060
2122
|
return ask(message, { mask: true });
|
|
2061
2123
|
}
|
|
2062
2124
|
async function promptConfirm(message, defaultYes = true) {
|
|
2063
|
-
const a = (await ask(`${message} ${
|
|
2125
|
+
const a = (await ask(`${message} ${c2.dim(defaultYes ? "[Y/n]" : "[y/N]")}`)).trim().toLowerCase();
|
|
2064
2126
|
if (!a)
|
|
2065
2127
|
return defaultYes;
|
|
2066
2128
|
return a === "y" || a === "yes";
|
|
2067
2129
|
}
|
|
2068
2130
|
|
|
2069
2131
|
// src/commands/doctor.ts
|
|
2070
|
-
import { existsSync as
|
|
2132
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
2071
2133
|
function registerDoctor(program2) {
|
|
2072
2134
|
program2.command("doctor").description("体检:配置 / 云端连通 / 剪映目录 / 运行时是否就绪").action(async () => {
|
|
2073
2135
|
await runDoctor();
|
|
@@ -2116,7 +2178,7 @@ async function runDoctor() {
|
|
|
2116
2178
|
}
|
|
2117
2179
|
rows.push({ name: "云端连通 + 鉴权", status: apiStatus, detail: apiDetail });
|
|
2118
2180
|
const draftDir = resolveJianyingDraftDir(undefined);
|
|
2119
|
-
const draftOk = !!draftDir &&
|
|
2181
|
+
const draftOk = !!draftDir && existsSync5(draftDir);
|
|
2120
2182
|
rows.push({
|
|
2121
2183
|
name: "剪映草稿目录",
|
|
2122
2184
|
status: draftOk ? "ok" : "warn",
|
|
@@ -2124,8 +2186,8 @@ async function runDoctor() {
|
|
|
2124
2186
|
});
|
|
2125
2187
|
rows.push({
|
|
2126
2188
|
name: "配置文件",
|
|
2127
|
-
status:
|
|
2128
|
-
detail:
|
|
2189
|
+
status: existsSync5(configPath()) ? "ok" : "warn",
|
|
2190
|
+
detail: existsSync5(configPath()) ? configPath() : `未生成 —— 跑 gtrk init(${configPath()})`
|
|
2129
2191
|
});
|
|
2130
2192
|
console.log(`
|
|
2131
2193
|
gtrk 体检:
|
|
@@ -2143,34 +2205,8 @@ gtrk 体检:
|
|
|
2143
2205
|
return !failed;
|
|
2144
2206
|
}
|
|
2145
2207
|
|
|
2146
|
-
// src/lib/log.ts
|
|
2147
|
-
var c2 = {
|
|
2148
|
-
dim: (s) => `\x1B[2m${s}\x1B[0m`,
|
|
2149
|
-
cyan: (s) => `\x1B[36m${s}\x1B[0m`,
|
|
2150
|
-
green: (s) => `\x1B[32m${s}\x1B[0m`,
|
|
2151
|
-
yellow: (s) => `\x1B[33m${s}\x1B[0m`,
|
|
2152
|
-
red: (s) => `\x1B[31m${s}\x1B[0m`
|
|
2153
|
-
};
|
|
2154
|
-
var humanOut = process.stdout;
|
|
2155
|
-
function routeLogsToStderr() {
|
|
2156
|
-
humanOut = process.stderr;
|
|
2157
|
-
}
|
|
2158
|
-
var line = (s) => humanOut.write(`${s}
|
|
2159
|
-
`);
|
|
2160
|
-
var log = {
|
|
2161
|
-
step: (msg) => line(c2.cyan(msg)),
|
|
2162
|
-
info: (msg) => line(c2.dim(` ${msg}`)),
|
|
2163
|
-
ok: (msg) => line(c2.green(`✅ ${msg}`)),
|
|
2164
|
-
warn: (msg) => line(c2.yellow(`⚠️ ${msg}`)),
|
|
2165
|
-
err: (msg) => process.stderr.write(`${c2.red(`❌ ${msg}`)}
|
|
2166
|
-
`),
|
|
2167
|
-
tick: (msg) => humanOut.write(`\r ${msg}\x1B[K`),
|
|
2168
|
-
tickEnd: () => humanOut.write(`
|
|
2169
|
-
`)
|
|
2170
|
-
};
|
|
2171
|
-
|
|
2172
2208
|
// src/commands/init.ts
|
|
2173
|
-
var GUIDE_IMAGE =
|
|
2209
|
+
var GUIDE_IMAGE = join5(packageRoot(), "assets", "jianying-draft-path.png");
|
|
2174
2210
|
function registerInit(program2) {
|
|
2175
2211
|
program2.command("init").description("一次性配置:API Key + 剪映草稿目录(之后所有命令免重复配置)").option("--api-key <key>", "非交互:直接指定 API Key").option("--api-base <url>", "非交互:指定 API 根地址(缺省用默认生产地址)").option("--jianying-draft-dir <dir>", "非交互:剪映草稿目录(传 auto 则自动探测)").option("-y, --yes", "非交互:用传入值 + 自动探测,不弹任何提示").action(runInit);
|
|
2176
2212
|
}
|
|
@@ -2205,7 +2241,7 @@ async function runInit(opts) {
|
|
|
2205
2241
|
openFile(GUIDE_IMAGE);
|
|
2206
2242
|
const manual = (await promptText("剪映草稿根目录(…\\com.lveditor.draft),留空跳过:")).trim();
|
|
2207
2243
|
if (manual) {
|
|
2208
|
-
if (
|
|
2244
|
+
if (existsSync6(manual))
|
|
2209
2245
|
jianyingDraftDir = resolve2(manual);
|
|
2210
2246
|
else
|
|
2211
2247
|
log.warn(`目录不存在,已跳过:${manual}`);
|
|
@@ -2254,10 +2290,20 @@ async function runInitNonInteractive(opts) {
|
|
|
2254
2290
|
await runDoctor();
|
|
2255
2291
|
}
|
|
2256
2292
|
|
|
2293
|
+
// src/commands/install.ts
|
|
2294
|
+
function registerInstall(program2) {
|
|
2295
|
+
program2.command("install").description("一条命令装全:安装 /gtrk-oralcut skill + 配置(对标飞书 lark-cli install)").option("--api-key <key>", "非交互:直接指定 API Key").option("--api-base <url>", "非交互:指定 API 根地址").option("--jianying-draft-dir <dir>", "非交互:剪映草稿目录(传 auto 则自动探测)").option("--skills-dir <dir>", "自定义 skills 目录(缺省 ~/.claude/skills)").option("-y, --yes", "非交互:用传入值 + 自动探测,不弹任何提示").action(async (opts) => {
|
|
2296
|
+
log.step("① 安装 agent skill…");
|
|
2297
|
+
installSkill({ dir: opts.skillsDir });
|
|
2298
|
+
log.step("② 配置 + 体检…");
|
|
2299
|
+
await runInit(opts);
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2257
2303
|
// src/commands/oralcut.ts
|
|
2258
|
-
import { resolve as resolve3, join as
|
|
2304
|
+
import { resolve as resolve3, join as join7, dirname as dirname2, basename as basename2, extname } from "node:path";
|
|
2259
2305
|
import { mkdir as mkdir2, cp, readFile as readFile2 } from "node:fs/promises";
|
|
2260
|
-
import { existsSync as
|
|
2306
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
2261
2307
|
|
|
2262
2308
|
// src/lib/config.ts
|
|
2263
2309
|
function loadConfig() {
|
|
@@ -2357,18 +2403,18 @@ async function download(url, dest) {
|
|
|
2357
2403
|
}
|
|
2358
2404
|
|
|
2359
2405
|
// src/lib/upload-cache.ts
|
|
2360
|
-
import { homedir as
|
|
2361
|
-
import { join as
|
|
2406
|
+
import { homedir as homedir3 } from "node:os";
|
|
2407
|
+
import { join as join6 } from "node:path";
|
|
2362
2408
|
import { stat, mkdir, readFile, writeFile as writeFile2 } from "node:fs/promises";
|
|
2363
|
-
import { existsSync as
|
|
2364
|
-
var CACHE_DIR =
|
|
2365
|
-
var CACHE_FILE =
|
|
2409
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
2410
|
+
var CACHE_DIR = join6(homedir3(), ".gtrk-cli");
|
|
2411
|
+
var CACHE_FILE = join6(CACHE_DIR, "upload-cache.json");
|
|
2366
2412
|
async function fingerprint(path) {
|
|
2367
2413
|
const s = await stat(path);
|
|
2368
2414
|
return `${s.size}:${Math.round(s.mtimeMs)}`;
|
|
2369
2415
|
}
|
|
2370
2416
|
async function load() {
|
|
2371
|
-
if (!
|
|
2417
|
+
if (!existsSync7(CACHE_FILE))
|
|
2372
2418
|
return {};
|
|
2373
2419
|
try {
|
|
2374
2420
|
return JSON.parse(await readFile(CACHE_FILE, "utf8"));
|
|
@@ -2439,17 +2485,17 @@ async function runOralCut(input, opts) {
|
|
|
2439
2485
|
routeLogsToStderr();
|
|
2440
2486
|
const cfg = loadConfig();
|
|
2441
2487
|
const inputAbs = resolve3(input);
|
|
2442
|
-
if (!
|
|
2488
|
+
if (!existsSync8(inputAbs))
|
|
2443
2489
|
throw new Error(`毛片不存在:${inputAbs}`);
|
|
2444
2490
|
const projName = basename2(inputAbs, extname(inputAbs));
|
|
2445
2491
|
const formats = opts.formats.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2446
2492
|
const wantJianying = formats.some((f) => f === "jianying" || f === "capcut");
|
|
2447
|
-
const outDir = resolve3(opts.out ??
|
|
2493
|
+
const outDir = resolve3(opts.out ?? join7(dirname2(inputAbs), `${projName}-video-project-${timestamp()}`));
|
|
2448
2494
|
await mkdir2(outDir, { recursive: true });
|
|
2449
2495
|
let scriptPath = opts.script ? resolve3(opts.script) : undefined;
|
|
2450
2496
|
if (!scriptPath) {
|
|
2451
|
-
const sibling =
|
|
2452
|
-
if (
|
|
2497
|
+
const sibling = join7(dirname2(inputAbs), `${projName}.txt`);
|
|
2498
|
+
if (existsSync8(sibling)) {
|
|
2453
2499
|
scriptPath = sibling;
|
|
2454
2500
|
log.info(`自动识别到同名文稿:${sibling}(按有稿剪辑;不想用就改名或显式 --script)`);
|
|
2455
2501
|
}
|
|
@@ -2508,9 +2554,9 @@ async function runOralCut(input, opts) {
|
|
|
2508
2554
|
const byFormat = {};
|
|
2509
2555
|
for (const f of files) {
|
|
2510
2556
|
const base = baseFormat(f.format);
|
|
2511
|
-
const fmtDir =
|
|
2557
|
+
const fmtDir = join7(outDir, base);
|
|
2512
2558
|
await mkdir2(fmtDir, { recursive: true });
|
|
2513
|
-
const dest =
|
|
2559
|
+
const dest = join7(fmtDir, f.filename);
|
|
2514
2560
|
await download(f.download_url, dest);
|
|
2515
2561
|
(byFormat[base] ??= []).push(dest);
|
|
2516
2562
|
log.info(`${FORMAT_META[base]?.label ?? f.format} ← ${f.filename}`);
|
|
@@ -2520,16 +2566,16 @@ async function runOralCut(input, opts) {
|
|
|
2520
2566
|
}
|
|
2521
2567
|
let jianyingDraftPath;
|
|
2522
2568
|
if (byFormat.jianying && draftDir) {
|
|
2523
|
-
jianyingDraftPath =
|
|
2569
|
+
jianyingDraftPath = join7(draftDir, basename2(outDir));
|
|
2524
2570
|
await mkdir2(jianyingDraftPath, { recursive: true });
|
|
2525
|
-
await cp(
|
|
2571
|
+
await cp(join7(outDir, "jianying"), jianyingDraftPath, { recursive: true });
|
|
2526
2572
|
log.info(`剪映草稿已落到:${jianyingDraftPath}`);
|
|
2527
2573
|
}
|
|
2528
2574
|
if (!opts.json) {
|
|
2529
2575
|
log.step("⑤ 三方打开(产物已就位,按需自取):");
|
|
2530
2576
|
for (const base of Object.keys(byFormat)) {
|
|
2531
2577
|
const meta = FORMAT_META[base];
|
|
2532
|
-
const target = base === "jianying" ? jianyingDraftPath ??
|
|
2578
|
+
const target = base === "jianying" ? jianyingDraftPath ?? join7(outDir, "jianying") : byFormat[base][0];
|
|
2533
2579
|
console.log(` • ${meta?.label ?? base}:${meta?.openHint(target) ?? target}`);
|
|
2534
2580
|
}
|
|
2535
2581
|
}
|
|
@@ -2552,32 +2598,14 @@ async function runOralCut(input, opts) {
|
|
|
2552
2598
|
}
|
|
2553
2599
|
}
|
|
2554
2600
|
|
|
2555
|
-
// src/commands/skills.ts
|
|
2556
|
-
import { homedir as homedir3 } from "node:os";
|
|
2557
|
-
import { join as join7 } from "node:path";
|
|
2558
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync2, copyFileSync } from "node:fs";
|
|
2559
|
-
var SKILL_NAME = "gtrk-oralcut";
|
|
2560
|
-
var SRC = join7(packageRoot(), "skills", SKILL_NAME, "SKILL.md");
|
|
2561
|
-
function registerSkills(program2) {
|
|
2562
|
-
const skills = program2.command("skills").description("管理 agent skill(安装到 Claude Code)");
|
|
2563
|
-
skills.command("install").description(`把 /${SKILL_NAME} 安装到 ~/.claude/skills(对标飞书 skills add)`).option("--dir <dir>", "自定义 skills 目录(缺省 ~/.claude/skills)").action((opts) => {
|
|
2564
|
-
if (!existsSync8(SRC))
|
|
2565
|
-
throw new Error(`找不到打包的 skill 源:${SRC}`);
|
|
2566
|
-
const base = opts.dir ?? join7(homedir3(), ".claude", "skills");
|
|
2567
|
-
const dest = join7(base, SKILL_NAME);
|
|
2568
|
-
mkdirSync2(dest, { recursive: true });
|
|
2569
|
-
copyFileSync(SRC, join7(dest, "SKILL.md"));
|
|
2570
|
-
console.log(`✅ 已安装 /${SKILL_NAME} → ${join7(dest, "SKILL.md")}`);
|
|
2571
|
-
console.log(" 在 Claude Code 里打 /gtrk-oralcut,或直接说「帮我剪个口播」即可触发(可能需重载会话)。");
|
|
2572
|
-
});
|
|
2573
|
-
}
|
|
2574
|
-
|
|
2575
2601
|
// src/index.ts
|
|
2576
2602
|
try {
|
|
2577
2603
|
process.loadEnvFile?.();
|
|
2578
2604
|
} catch {}
|
|
2605
|
+
var { version } = JSON.parse(readFileSync2(join8(packageRoot(), "package.json"), "utf8"));
|
|
2579
2606
|
var program2 = new Command;
|
|
2580
|
-
program2.name("gtrk").description("同合云成片流水线 CLI —— agent 驱动云端任务、产物拉回本地、三方工程文件(客户端/剪映/PR)互通").version(
|
|
2607
|
+
program2.name("gtrk").description("同合云成片流水线 CLI —— agent 驱动云端任务、产物拉回本地、三方工程文件(客户端/剪映/PR)互通").version(version);
|
|
2608
|
+
registerInstall(program2);
|
|
2581
2609
|
registerInit(program2);
|
|
2582
2610
|
registerOralCut(program2);
|
|
2583
2611
|
registerDoctor(program2);
|