agents-gitflow-guard 0.0.18 → 0.0.20
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 +90 -42
- package/README.zh.md +90 -42
- package/lib/cli.mjs +377 -4
- package/lib/index.mjs +1 -1
- package/lib/{src-BQYC4N6b.mjs → src-DPJRoEJq.mjs} +216 -28
- package/package.json +7 -3
package/lib/cli.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { _ as resolveLocale, d as gitRunner, f as loadConfig, h as makeT, i as formatDeny, l as currentBranch, o as stateDir, p as roleMatches, r as evaluateCommand, u as findRepoRoot, v as classify } from "./src-
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import {
|
|
1
|
+
import { _ as resolveLocale, d as gitRunner, f as loadConfig, h as makeT, i as formatDeny, l as currentBranch, o as stateDir, p as roleMatches, r as evaluateCommand, u as findRepoRoot, v as classify } from "./src-DPJRoEJq.mjs";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { createInterface } from "node:readline";
|
|
4
6
|
//#region src/platform.ts
|
|
5
7
|
function str(v) {
|
|
6
8
|
return typeof v === "string" ? v : "";
|
|
@@ -84,6 +86,213 @@ function encodeDeny(platform, reason) {
|
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
//#endregion
|
|
89
|
+
//#region src/wire.ts
|
|
90
|
+
const CLIENTS = [
|
|
91
|
+
"dsh",
|
|
92
|
+
"claude",
|
|
93
|
+
"codex",
|
|
94
|
+
"opencode",
|
|
95
|
+
"antigravity",
|
|
96
|
+
"pi"
|
|
97
|
+
];
|
|
98
|
+
function isWireClient(v) {
|
|
99
|
+
return CLIENTS.includes(v);
|
|
100
|
+
}
|
|
101
|
+
/** 各客户端的 hook 落位规格(dsh/pi 无 hook 文件, 仅输出接入引导) */
|
|
102
|
+
const WIRE_CLIENTS = [
|
|
103
|
+
{
|
|
104
|
+
client: "claude",
|
|
105
|
+
projectPath: ".claude/settings.json",
|
|
106
|
+
globalPath: () => join(homedir(), ".claude", "settings.json")
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
client: "codex",
|
|
110
|
+
projectPath: ".codex/hooks.json",
|
|
111
|
+
globalPath: () => join(homedir(), ".codex", "hooks.json")
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
client: "opencode",
|
|
115
|
+
projectPath: ".opencode/hook/hooks.yaml",
|
|
116
|
+
globalPath: () => join(homedir(), ".config", "opencode", "hook", "hooks.yaml")
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
client: "antigravity",
|
|
120
|
+
projectPath: ".agents/hooks.json",
|
|
121
|
+
globalPath: () => join(homedir(), ".gemini", "config", "hooks.json"),
|
|
122
|
+
experimental: true
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
client: "dsh",
|
|
126
|
+
projectPath: "",
|
|
127
|
+
globalPath: () => ""
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
client: "pi",
|
|
131
|
+
projectPath: "",
|
|
132
|
+
globalPath: () => ""
|
|
133
|
+
}
|
|
134
|
+
];
|
|
135
|
+
/** 各 stdin-hook 客户端的 hook 命令(与 references/*.md 逐一对应; codex/antigravity 用相对 bin/...) */
|
|
136
|
+
const COMMANDS = {
|
|
137
|
+
claude: "node ${CLAUDE_PROJECT_DIR}/bin/gitflow-guard.mjs check --platform claude",
|
|
138
|
+
codex: "node bin/gitflow-guard.mjs check --platform codex",
|
|
139
|
+
opencode: "node \"$OPENCODE_PROJECT_DIR/bin/gitflow-guard.mjs\" check --platform opencode",
|
|
140
|
+
antigravity: "node bin/gitflow-guard.mjs check --platform antigravity"
|
|
141
|
+
};
|
|
142
|
+
/** OpenCode YAML 模板(顶层 hooks: + 语义 id gitflow-guard) */
|
|
143
|
+
const OPENCODE_TEMPLATE = [
|
|
144
|
+
"hooks:",
|
|
145
|
+
" - id: gitflow-guard",
|
|
146
|
+
" event: tool.before.bash",
|
|
147
|
+
" actions:",
|
|
148
|
+
" - bash: |",
|
|
149
|
+
` ${COMMANDS.opencode}`
|
|
150
|
+
].join("\n");
|
|
151
|
+
const YAML_ID_GUARD = /^\s*- id: gitflow-guard\s*$/m;
|
|
152
|
+
const YAML_ID_ANY = /^\s*- id:/m;
|
|
153
|
+
/** 读取文本文件; 缺失返回 null(其余异常也视为缺失, 决策保守) */
|
|
154
|
+
async function readText(path) {
|
|
155
|
+
try {
|
|
156
|
+
return await readFile(path, "utf8");
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function writeText(path, content) {
|
|
162
|
+
await mkdir(dirname(path), { recursive: true });
|
|
163
|
+
await writeFile(path, content, "utf8");
|
|
164
|
+
}
|
|
165
|
+
/** JSON 递归搜索: 是否已含该命令(任意形状, 幂等判重) */
|
|
166
|
+
function jsonContains(obj, needle) {
|
|
167
|
+
if (typeof obj === "string") return obj === needle;
|
|
168
|
+
if (Array.isArray(obj)) return obj.some((x) => jsonContains(x, needle));
|
|
169
|
+
if (obj !== null && typeof obj === "object") return Object.values(obj).some((x) => jsonContains(x, needle));
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
function parseJsonOrThrow(path, raw) {
|
|
173
|
+
try {
|
|
174
|
+
const parsed = JSON.parse(raw);
|
|
175
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("not an object");
|
|
176
|
+
return parsed;
|
|
177
|
+
} catch {
|
|
178
|
+
throw new Error(`invalid JSON in ${path} — refusing to modify it`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async function writeJson(path, obj) {
|
|
182
|
+
await writeText(path, `${JSON.stringify(obj, null, 2)}\n`);
|
|
183
|
+
}
|
|
184
|
+
/** JSON 客户端(claude/codex/antigravity)新增 hook 条目; 非破坏性合并, 同命令已存在则跳过 */
|
|
185
|
+
async function addJsonEntry(path, client, dryRun) {
|
|
186
|
+
const cmd = COMMANDS[client];
|
|
187
|
+
const raw = await readText(path);
|
|
188
|
+
const obj = raw === null ? {} : parseJsonOrThrow(path, raw);
|
|
189
|
+
if (jsonContains(obj, cmd)) return "exists";
|
|
190
|
+
const entry = client === "antigravity" ? {
|
|
191
|
+
matcher: "run_command",
|
|
192
|
+
hooks: [{
|
|
193
|
+
type: "command",
|
|
194
|
+
command: cmd
|
|
195
|
+
}]
|
|
196
|
+
} : {
|
|
197
|
+
matcher: client === "codex" ? "^Bash$" : "Bash",
|
|
198
|
+
hooks: [{
|
|
199
|
+
type: "command",
|
|
200
|
+
command: cmd
|
|
201
|
+
}]
|
|
202
|
+
};
|
|
203
|
+
if (client === "antigravity") {
|
|
204
|
+
const block = obj["gitflow-guard"] ??= { PreToolUse: [] };
|
|
205
|
+
if (!Array.isArray(block.PreToolUse)) throw new Error(`invalid ${path}: gitflow-guard.PreToolUse must be an array`);
|
|
206
|
+
block.PreToolUse.push(entry);
|
|
207
|
+
} else {
|
|
208
|
+
const hooksObj = obj["hooks"] ??= {};
|
|
209
|
+
const arr = hooksObj["PreToolUse"] ??= [];
|
|
210
|
+
if (!Array.isArray(arr)) throw new Error(`invalid ${path}: hooks.PreToolUse must be an array`);
|
|
211
|
+
arr.push(entry);
|
|
212
|
+
}
|
|
213
|
+
if (!dryRun) await writeJson(path, obj);
|
|
214
|
+
return "added";
|
|
215
|
+
}
|
|
216
|
+
/** JSON 客户端移除本插件条目; 不动其他内容 */
|
|
217
|
+
async function removeJsonEntry(path, client, dryRun) {
|
|
218
|
+
const cmd = COMMANDS[client];
|
|
219
|
+
const raw = await readText(path);
|
|
220
|
+
if (raw === null) return "absent";
|
|
221
|
+
const obj = parseJsonOrThrow(path, raw);
|
|
222
|
+
if (!jsonContains(obj, cmd)) return "absent";
|
|
223
|
+
if (client === "antigravity") delete obj["gitflow-guard"];
|
|
224
|
+
else {
|
|
225
|
+
const hooksObj = obj["hooks"];
|
|
226
|
+
const arr = hooksObj?.["PreToolUse"];
|
|
227
|
+
if (Array.isArray(arr)) {
|
|
228
|
+
const rest = arr.filter((e) => !(e?.hooks ?? []).some((h) => h?.command === cmd));
|
|
229
|
+
if (rest.length === 0) delete hooksObj["PreToolUse"];
|
|
230
|
+
else hooksObj["PreToolUse"] = rest;
|
|
231
|
+
if (hooksObj && Object.keys(hooksObj).length === 0) delete obj["hooks"];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (!dryRun) await writeJson(path, obj);
|
|
235
|
+
return "removed";
|
|
236
|
+
}
|
|
237
|
+
/** OpenCode YAML: hooks: 列表按语义 id gitflow-guard 判重/落位 */
|
|
238
|
+
async function addYamlEntry(path, dryRun) {
|
|
239
|
+
const raw = await readText(path);
|
|
240
|
+
if (raw !== null) {
|
|
241
|
+
if (YAML_ID_GUARD.test(raw)) return "exists";
|
|
242
|
+
const lines = raw.split("\n");
|
|
243
|
+
const hooksIdx = lines.findIndex((l) => /^hooks:\s*$/.test(l));
|
|
244
|
+
const block = OPENCODE_TEMPLATE.split("\n").slice(1);
|
|
245
|
+
if (hooksIdx === -1) {
|
|
246
|
+
const joined = [
|
|
247
|
+
...lines,
|
|
248
|
+
"",
|
|
249
|
+
...block
|
|
250
|
+
].join("\n");
|
|
251
|
+
if (!dryRun) await writeText(path, joined);
|
|
252
|
+
return "added";
|
|
253
|
+
}
|
|
254
|
+
lines.splice(hooksIdx + 1, 0, ...block);
|
|
255
|
+
if (!dryRun) await writeText(path, lines.join("\n"));
|
|
256
|
+
return "added";
|
|
257
|
+
}
|
|
258
|
+
if (!dryRun) await writeText(path, OPENCODE_TEMPLATE);
|
|
259
|
+
return "added";
|
|
260
|
+
}
|
|
261
|
+
/** OpenCode YAML: 移除 gitflow-guard 块; 若列表清空则连顶层 hooks: 一并清理 */
|
|
262
|
+
async function removeYamlEntry(path, dryRun) {
|
|
263
|
+
const raw = await readText(path);
|
|
264
|
+
if (raw === null) return "absent";
|
|
265
|
+
if (!YAML_ID_GUARD.test(raw)) return "absent";
|
|
266
|
+
const lines = raw.split("\n");
|
|
267
|
+
const start = lines.findIndex((l) => YAML_ID_GUARD.test(l));
|
|
268
|
+
let end = lines.length;
|
|
269
|
+
for (let i = start + 1; i < lines.length; i++) if (YAML_ID_ANY.test(lines[i])) {
|
|
270
|
+
end = i;
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
let rest = [...lines.slice(0, start), ...lines.slice(end)];
|
|
274
|
+
if (!rest.some((l) => YAML_ID_ANY.test(l))) rest = rest.filter((l) => !/^hooks:\s*$/.test(l));
|
|
275
|
+
const text = rest.join("\n");
|
|
276
|
+
if (!dryRun) await writeText(path, text);
|
|
277
|
+
return "removed";
|
|
278
|
+
}
|
|
279
|
+
/** 执行一次 wire 落位/移除/预览; dsh/pi 由上层直接短路, 不进这里 */
|
|
280
|
+
async function applyWire(client, path, unwire, dryRun) {
|
|
281
|
+
if (client === "opencode") return unwire ? removeYamlEntry(path, dryRun) : addYamlEntry(path, dryRun);
|
|
282
|
+
return unwire ? removeJsonEntry(path, client, dryRun) : addJsonEntry(path, client, dryRun);
|
|
283
|
+
}
|
|
284
|
+
/** 只读探测: 该配置文件是否已含本插件 hook(status 的接线提示用) */
|
|
285
|
+
async function isWired(client, path) {
|
|
286
|
+
const raw = await readText(path);
|
|
287
|
+
if (raw === null) return false;
|
|
288
|
+
if (client === "opencode") return YAML_ID_GUARD.test(raw);
|
|
289
|
+
try {
|
|
290
|
+
return jsonContains(JSON.parse(raw), COMMANDS[client]);
|
|
291
|
+
} catch {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
//#endregion
|
|
87
296
|
//#region src/cli.ts
|
|
88
297
|
function parseFlags(argv) {
|
|
89
298
|
const flags = {};
|
|
@@ -95,11 +304,18 @@ function parseFlags(argv) {
|
|
|
95
304
|
else if (a === "--platform") flags.platform = next();
|
|
96
305
|
else if (a === "--command") flags.command = next();
|
|
97
306
|
else if (a === "--locale") flags.locale = next();
|
|
307
|
+
else if (a === "--client") flags.client = next();
|
|
308
|
+
else if (a === "--global") flags.global = true;
|
|
309
|
+
else if (a === "--project") flags.project = true;
|
|
310
|
+
else if (a === "--unwire") flags.unwire = true;
|
|
311
|
+
else if (a === "--dry-run") flags.dryRun = true;
|
|
312
|
+
else if (a === "--yes") flags.yes = true;
|
|
98
313
|
else if (a.startsWith("--repo=")) flags.repo = a.slice(7);
|
|
99
314
|
else if (a.startsWith("--lines=")) flags.lines = Number(a.slice(8));
|
|
100
315
|
else if (a.startsWith("--platform=")) flags.platform = a.slice(11);
|
|
101
316
|
else if (a.startsWith("--command=")) flags.command = a.slice(10);
|
|
102
317
|
else if (a.startsWith("--locale=")) flags.locale = a.slice(9);
|
|
318
|
+
else if (a.startsWith("--client=")) flags.client = a.slice(9);
|
|
103
319
|
}
|
|
104
320
|
return flags;
|
|
105
321
|
}
|
|
@@ -131,6 +347,8 @@ async function main(argv, opts = {}) {
|
|
|
131
347
|
if (cmd === "status") return await status(flags, runner);
|
|
132
348
|
if (cmd === "audit") return await audit(flags, runner);
|
|
133
349
|
if (cmd === "check") return await check(flags);
|
|
350
|
+
if (cmd === "wire") return await wire(flags, runner);
|
|
351
|
+
if (cmd === "setup") return await setup(flags, runner);
|
|
134
352
|
const t = makeT(await resolveFrameworkLocale(flags, runner));
|
|
135
353
|
console.error(`${t("cli.unknownCommand", { cmd: cmd ?? "" })}\n\n${t("usage.text")}`);
|
|
136
354
|
return 1;
|
|
@@ -145,7 +363,8 @@ async function status(flags, runner) {
|
|
|
145
363
|
console.error(makeT(resolveLocale(flags.locale))("cli.cannotLocate"));
|
|
146
364
|
return 1;
|
|
147
365
|
}
|
|
148
|
-
const
|
|
366
|
+
const loaded = await loadConfig(repoRoot);
|
|
367
|
+
const { config, errors, warnings } = loaded;
|
|
149
368
|
const enabled = config?.enabled === true;
|
|
150
369
|
const t = makeT(cliLocale(flags, config?.locale));
|
|
151
370
|
console.log(t("cli.statusTitle", { repo: repoRoot }));
|
|
@@ -173,6 +392,10 @@ async function status(flags, runner) {
|
|
|
173
392
|
merge: c.branches.production.mergeBy || "user"
|
|
174
393
|
}));
|
|
175
394
|
if (c.branches.archive) console.log(t("cli.statusArchive", { list: c.branches.archive.branches.join(", ") }));
|
|
395
|
+
if (loaded.usingDefaults) {
|
|
396
|
+
console.log(t("cli.statusUsingDefaults"));
|
|
397
|
+
console.log(t("cli.statusMainProtected"));
|
|
398
|
+
}
|
|
176
399
|
console.log(t("cli.statusCurrentBranch", { branch: branch ?? t("cli.statusUnknownBranch") }));
|
|
177
400
|
const r = await runner.run([
|
|
178
401
|
"for-each-ref",
|
|
@@ -190,6 +413,15 @@ async function status(flags, runner) {
|
|
|
190
413
|
};
|
|
191
414
|
console.log(t("cli.statusLocalBranches"));
|
|
192
415
|
for (const b of localBranches) console.log(` ${b} → ${classifyBranch(b)}`);
|
|
416
|
+
const hints = [];
|
|
417
|
+
for (const spec of WIRE_CLIENTS) {
|
|
418
|
+
if (spec.client === "dsh" || spec.client === "pi") continue;
|
|
419
|
+
if (!await isWired(spec.client, join(repoRoot, spec.projectPath))) hints.push(spec.client);
|
|
420
|
+
}
|
|
421
|
+
if (hints.length > 0) {
|
|
422
|
+
console.log(t("cli.statusWireHints"));
|
|
423
|
+
for (const c of hints) console.log(t("cli.statusWireHint", { client: c }));
|
|
424
|
+
}
|
|
193
425
|
return 0;
|
|
194
426
|
}
|
|
195
427
|
async function audit(flags, runner) {
|
|
@@ -214,6 +446,147 @@ async function audit(flags, runner) {
|
|
|
214
446
|
}
|
|
215
447
|
return 0;
|
|
216
448
|
}
|
|
449
|
+
/** 交互提问(仅 TTY 可用); 返回小写化、去空格的答案 */
|
|
450
|
+
function askLine(question) {
|
|
451
|
+
return new Promise((resolve) => {
|
|
452
|
+
const rl = createInterface({
|
|
453
|
+
input: process.stdin,
|
|
454
|
+
output: process.stdout
|
|
455
|
+
});
|
|
456
|
+
rl.question(question, (ans) => {
|
|
457
|
+
rl.close();
|
|
458
|
+
resolve(ans.trim().toLowerCase());
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
/** 作用域解析: 显式旗标 > 交互询问(仅 TTY) > 非交互默认 project(安全) */
|
|
463
|
+
async function resolveScope(flags, t) {
|
|
464
|
+
if (flags.global) return "global";
|
|
465
|
+
if (flags.project) return "project";
|
|
466
|
+
if (process.stdin.isTTY) {
|
|
467
|
+
const ans = await askLine(t("cli.wireScopeAsk"));
|
|
468
|
+
if (ans === "project" || ans === "p") return "project";
|
|
469
|
+
if (ans === "global" || ans === "g") return "global";
|
|
470
|
+
console.log(t("cli.wireScopeInvalid"));
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
return "project";
|
|
474
|
+
}
|
|
475
|
+
/** wire/setup 共用落位核心: dsh/pi 只打印引导; 其余客户端读取/合并/写入对应配置文件(非破坏性) */
|
|
476
|
+
async function wireCore(client, scope, opts, t) {
|
|
477
|
+
const spec = WIRE_CLIENTS.find((s) => s.client === client);
|
|
478
|
+
if (client === "dsh") {
|
|
479
|
+
console.log(t("cli.wireDshGuide"));
|
|
480
|
+
return 0;
|
|
481
|
+
}
|
|
482
|
+
if (client === "pi") {
|
|
483
|
+
console.log(t("cli.wirePiGuide"));
|
|
484
|
+
return 0;
|
|
485
|
+
}
|
|
486
|
+
if (spec.experimental) console.log(t("cli.wireExperimental", { client }));
|
|
487
|
+
const path = scope === "project" ? join(opts.repoRoot, spec.projectPath) : spec.globalPath();
|
|
488
|
+
console.log(t("cli.wireTarget", {
|
|
489
|
+
client,
|
|
490
|
+
path
|
|
491
|
+
}));
|
|
492
|
+
if (opts.dryRun) {
|
|
493
|
+
const res = await applyWire(client, path, !!opts.unwire, true);
|
|
494
|
+
if (res === "added") console.log(t("cli.wireDryRunAdd", {
|
|
495
|
+
client,
|
|
496
|
+
path
|
|
497
|
+
}));
|
|
498
|
+
else if (res === "removed") console.log(t("cli.wireDryRunRemove", {
|
|
499
|
+
client,
|
|
500
|
+
path
|
|
501
|
+
}));
|
|
502
|
+
else console.log(t("cli.wireDryRunNoOp", {
|
|
503
|
+
client,
|
|
504
|
+
path
|
|
505
|
+
}));
|
|
506
|
+
return 0;
|
|
507
|
+
}
|
|
508
|
+
if (!opts.yes) {
|
|
509
|
+
if (scope === "global" && !process.stdin.isTTY) {
|
|
510
|
+
console.error(t("cli.wireRefuseGlobal"));
|
|
511
|
+
return 1;
|
|
512
|
+
}
|
|
513
|
+
if (process.stdin.isTTY) {
|
|
514
|
+
const ans = await askLine(t("cli.wireConfirmWrite", { path }));
|
|
515
|
+
if (ans !== "y" && ans !== "yes") return 1;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
const res = await applyWire(client, path, !!opts.unwire, false);
|
|
519
|
+
if (res === "added") console.log(t("cli.wireCreated", {
|
|
520
|
+
client,
|
|
521
|
+
path
|
|
522
|
+
}));
|
|
523
|
+
else if (res === "exists") console.log(t("cli.wireAlready", {
|
|
524
|
+
client,
|
|
525
|
+
path
|
|
526
|
+
}));
|
|
527
|
+
else if (res === "removed") console.log(t("cli.wireRemoved", {
|
|
528
|
+
client,
|
|
529
|
+
path
|
|
530
|
+
}));
|
|
531
|
+
else console.log(t("cli.wireNotWired", {
|
|
532
|
+
client,
|
|
533
|
+
path
|
|
534
|
+
}));
|
|
535
|
+
return 0;
|
|
536
|
+
}
|
|
537
|
+
/** wire: 单客户端非交互/半交互落位(--client 必填; 作用域默认交互询问) */
|
|
538
|
+
async function wire(flags, runner) {
|
|
539
|
+
const t = makeT(await resolveFrameworkLocale(flags, runner));
|
|
540
|
+
const client = (flags.client ?? "").toLowerCase();
|
|
541
|
+
if (!isWireClient(client)) {
|
|
542
|
+
console.error(t("cli.wireUnknownClient", { client }));
|
|
543
|
+
return 1;
|
|
544
|
+
}
|
|
545
|
+
const scope = await resolveScope(flags, t);
|
|
546
|
+
if (!scope) return 1;
|
|
547
|
+
let repoRoot = null;
|
|
548
|
+
if (scope === "project") {
|
|
549
|
+
repoRoot = await resolveRepo(flags, runner);
|
|
550
|
+
if (!repoRoot) {
|
|
551
|
+
console.error(t("cli.wireNeedRepo"));
|
|
552
|
+
return 1;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return wireCore(client, scope, {
|
|
556
|
+
unwire: flags.unwire,
|
|
557
|
+
dryRun: flags.dryRun,
|
|
558
|
+
yes: flags.yes,
|
|
559
|
+
repoRoot
|
|
560
|
+
}, t);
|
|
561
|
+
}
|
|
562
|
+
/** setup: 交互向导(客户端 → 作用域 → 确认), 安装后一步式接线; 非交互终端拒绝并指路 wire */
|
|
563
|
+
async function setup(flags, runner) {
|
|
564
|
+
const t = makeT(await resolveFrameworkLocale(flags, runner));
|
|
565
|
+
if (!process.stdin.isTTY) {
|
|
566
|
+
console.error(t("cli.setupNoTty"));
|
|
567
|
+
return 1;
|
|
568
|
+
}
|
|
569
|
+
console.log(t("cli.setupIntro"));
|
|
570
|
+
const client = (await askLine(t("cli.setupClientAsk"))).trim().toLowerCase();
|
|
571
|
+
if (!isWireClient(client)) {
|
|
572
|
+
console.error(t("cli.setupClientInvalid"));
|
|
573
|
+
return 1;
|
|
574
|
+
}
|
|
575
|
+
const scope = await resolveScope(flags, t);
|
|
576
|
+
if (!scope) return 1;
|
|
577
|
+
let repoRoot = null;
|
|
578
|
+
if (scope === "project") {
|
|
579
|
+
repoRoot = await resolveRepo(flags, runner);
|
|
580
|
+
if (!repoRoot) {
|
|
581
|
+
console.error(t("cli.wireNeedRepo"));
|
|
582
|
+
return 1;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return wireCore(client, scope, {
|
|
586
|
+
yes: flags.yes,
|
|
587
|
+
repoRoot
|
|
588
|
+
}, t);
|
|
589
|
+
}
|
|
217
590
|
function readStdin() {
|
|
218
591
|
return new Promise((resolve) => {
|
|
219
592
|
let data = "";
|
package/lib/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as name, c as createPiExtension, g as registerLocale, i as formatDeny, m as MESSAGE_KEYS, n as apply, o as stateDir, r as evaluateCommand, s as userStateRoot, t as appendAudit } from "./src-
|
|
1
|
+
import { a as name, c as createPiExtension, g as registerLocale, i as formatDeny, m as MESSAGE_KEYS, n as apply, o as stateDir, r as evaluateCommand, s as userStateRoot, t as appendAudit } from "./src-DPJRoEJq.mjs";
|
|
2
2
|
export { MESSAGE_KEYS, appendAudit, apply, createPiExtension, evaluateCommand, formatDeny, name, registerLocale, stateDir, userStateRoot };
|