@co0ontty/wand 4.11.2 → 4.12.0

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "commit": "26afd2798da3b5ba08d434492ac457a4959ee69a",
3
- "builtAt": "2026-07-17T10:32:08.535Z",
4
- "version": "4.11.2",
2
+ "commit": "c1870fda254528fbd1c34b8907f4f1be5a229c54",
3
+ "builtAt": "2026-07-17T11:06:16.718Z",
4
+ "version": "4.12.0",
5
5
  "channel": "stable"
6
6
  }
@@ -1,5 +1,5 @@
1
1
  import { GitStatusResult, PushResult, QuickCommitResult, SessionProvider, SessionSnapshot, TagHeadResult } from "./types.js";
2
- export type QuickCommitErrorCode = "CWD_MISSING" | "NO_CWD" | "NOT_A_GIT_REPO" | "NO_COMMIT" | "NOTHING_TO_COMMIT" | "NOTHING_TO_PUSH" | "EMPTY_MESSAGE" | "EMPTY_TAG" | "EMPTY_AI_MESSAGE" | "INVALID_AI_TAG" | "TAG_EXISTS" | "GIT_ADD_FAILED" | "GIT_DIFF_FAILED" | "GIT_COMMIT_FAILED" | "GIT_TAG_FAILED" | "CLAUDE_CLI_MISSING" | "CLAUDE_CLI_FAILED" | "CLAUDE_TIMEOUT";
2
+ export type QuickCommitErrorCode = "CWD_MISSING" | "NO_CWD" | "NOT_A_GIT_REPO" | "NO_COMMIT" | "NOTHING_TO_COMMIT" | "NOTHING_TO_PUSH" | "EMPTY_MESSAGE" | "EMPTY_TAG" | "EMPTY_AI_MESSAGE" | "AI_FALLBACK_FAILED" | "INVALID_AI_TAG" | "TAG_EXISTS" | "GIT_ADD_FAILED" | "GIT_DIFF_FAILED" | "GIT_COMMIT_FAILED" | "GIT_TAG_FAILED" | "CLAUDE_CLI_MISSING" | "CLAUDE_CLI_FAILED" | "CLAUDE_TIMEOUT";
3
3
  export declare function getGitStatus(cwd: string): GitStatusResult;
4
4
  /** Non-blocking status implementation used by HTTP routes. */
5
5
  export declare function getGitStatusAsync(cwd: string): Promise<GitStatusResult>;
@@ -10,6 +10,8 @@ interface QuickCommitOptions {
10
10
  model?: string | null;
11
11
  thinkingEffort?: SessionSnapshot["thinkingEffort"];
12
12
  inheritEnv?: boolean;
13
+ /** Direct API to try when the selected Commit source is a CLI. */
14
+ fallbackSystemAi?: import("./types.js").SystemAiConfig;
13
15
  systemAi?: import("./types.js").SystemAiConfig;
14
16
  autoMessage: boolean;
15
17
  customMessage?: string;
@@ -28,6 +30,7 @@ interface QuickCommitAiOptions {
28
30
  model?: string | null;
29
31
  thinkingEffort?: SessionSnapshot["thinkingEffort"];
30
32
  inheritEnv?: boolean;
33
+ fallbackSystemAi?: import("./types.js").SystemAiConfig;
31
34
  systemAi?: import("./types.js").SystemAiConfig;
32
35
  }
33
36
  export declare class QuickCommitError extends Error {
@@ -47,6 +50,8 @@ interface TagHeadOptions {
47
50
  model?: string | null;
48
51
  thinkingEffort?: SessionSnapshot["thinkingEffort"];
49
52
  inheritEnv?: boolean;
53
+ fallbackSystemAi?: import("./types.js").SystemAiConfig;
54
+ systemAi?: import("./types.js").SystemAiConfig;
50
55
  /** Explicit tag name. If empty and `autoTag` is true, ask the session provider to generate one. */
51
56
  tag?: string;
52
57
  autoTag?: boolean;
@@ -605,9 +605,7 @@ async function callOpenCodeText(prompt, cwd, opts) {
605
605
  throw new QuickCommitError("OpenCode 返回了空的 commit message。", "EMPTY_AI_MESSAGE");
606
606
  return text;
607
607
  }
608
- async function callAiText(prompt, cwd, language, opts) {
609
- if (opts.systemAi?.enabled)
610
- return callSystemAiText(prompt, opts.systemAi);
608
+ async function callCliAiText(prompt, cwd, language, opts) {
611
609
  const provider = normalizeProvider(opts.provider);
612
610
  if (provider === "codex") {
613
611
  return callCodexText(prompt, cwd, opts);
@@ -616,6 +614,49 @@ async function callAiText(prompt, cwd, language, opts) {
616
614
  return callOpenCodeText(prompt, cwd, opts);
617
615
  return callClaudeText(prompt, cwd, language, opts.model);
618
616
  }
617
+ async function callDirectApiText(prompt, systemAi) {
618
+ const text = await callSystemAiText(prompt, systemAi);
619
+ if (!text.trim()) {
620
+ throw new QuickCommitError("直连 API 返回了空结果。", "EMPTY_AI_MESSAGE");
621
+ }
622
+ return text;
623
+ }
624
+ function aiFallbackFailed(primary, primaryError, fallbackError) {
625
+ const fallback = primary === "直连 API" ? "CLI" : "直连 API";
626
+ return new QuickCommitError(`${primary} 与 ${fallback} 均失败:${getGitErrorMessage(primaryError)};${getGitErrorMessage(fallbackError)}`, "AI_FALLBACK_FAILED");
627
+ }
628
+ async function callAiText(prompt, cwd, language, opts) {
629
+ if (opts.systemAi?.enabled) {
630
+ try {
631
+ return await callDirectApiText(prompt, opts.systemAi);
632
+ }
633
+ catch (apiError) {
634
+ try {
635
+ // The user selected the direct API first. If it is unavailable or
636
+ // empty, retry this exact request through their selected CLI.
637
+ return await callCliAiText(prompt, cwd, language, opts);
638
+ }
639
+ catch (cliError) {
640
+ throw aiFallbackFailed("直连 API", apiError, cliError);
641
+ }
642
+ }
643
+ }
644
+ try {
645
+ return await callCliAiText(prompt, cwd, language, opts);
646
+ }
647
+ catch (cliError) {
648
+ if (!opts.fallbackSystemAi?.enabled)
649
+ throw cliError;
650
+ try {
651
+ // The user selected the CLI first. A complete direct-API profile is only
652
+ // provided here as the reciprocal fallback, never as a second CLI try.
653
+ return await callDirectApiText(prompt, opts.fallbackSystemAi);
654
+ }
655
+ catch (apiError) {
656
+ throw aiFallbackFailed("CLI", cliError, apiError);
657
+ }
658
+ }
659
+ }
619
660
  async function collectStagedDiff(cwd) {
620
661
  let diff;
621
662
  try {
@@ -854,6 +895,8 @@ export async function runTagHead(opts) {
854
895
  model: opts.model,
855
896
  thinkingEffort: opts.thinkingEffort,
856
897
  inheritEnv: opts.inheritEnv,
898
+ fallbackSystemAi: opts.fallbackSystemAi,
899
+ systemAi: opts.systemAi,
857
900
  });
858
901
  }
859
902
  if (!tagName) {
@@ -1246,11 +1289,7 @@ async function runQuickCommitFallbackCli(opts, priorError) {
1246
1289
  pushed: false,
1247
1290
  };
1248
1291
  }
1249
- function shouldFallbackToCli(error, opts) {
1250
- // An explicit direct-API selection is a hard boundary: never execute a local
1251
- // provider CLI (especially the permission-bypassing fallback) behind it.
1252
- if (opts.systemAi?.enabled)
1253
- return false;
1292
+ function shouldFallbackToCli(error) {
1254
1293
  return ![
1255
1294
  "CWD_MISSING",
1256
1295
  "NO_CWD",
@@ -1260,6 +1299,7 @@ function shouldFallbackToCli(error, opts) {
1260
1299
  "NOTHING_TO_PUSH",
1261
1300
  "EMPTY_MESSAGE",
1262
1301
  "EMPTY_TAG",
1302
+ "AI_FALLBACK_FAILED",
1263
1303
  "TAG_EXISTS",
1264
1304
  ].includes(error.code);
1265
1305
  }
@@ -1268,7 +1308,7 @@ export async function runQuickCommitWithFallback(opts) {
1268
1308
  return await runQuickCommit(opts);
1269
1309
  }
1270
1310
  catch (error) {
1271
- if (error instanceof QuickCommitError && shouldFallbackToCli(error, opts)) {
1311
+ if (error instanceof QuickCommitError && shouldFallbackToCli(error)) {
1272
1312
  return runQuickCommitFallbackCli(opts, error.message);
1273
1313
  }
1274
1314
  throw error;
@@ -1281,6 +1321,7 @@ export async function runQuickCommit(opts) {
1281
1321
  model: opts.model,
1282
1322
  thinkingEffort: opts.thinkingEffort,
1283
1323
  inheritEnv: opts.inheritEnv,
1324
+ fallbackSystemAi: opts.fallbackSystemAi,
1284
1325
  systemAi: opts.systemAi,
1285
1326
  };
1286
1327
  await assertGitWorkTreeAsync(cwd);
@@ -4,6 +4,8 @@ export interface SessionAiContext {
4
4
  model?: string;
5
5
  thinkingEffort: SessionSnapshot["thinkingEffort"];
6
6
  inheritEnv?: boolean;
7
+ /** Direct API to try when Commit is configured to prefer its CLI. */
8
+ fallbackSystemAi?: SystemAiConfig;
7
9
  systemAi?: SystemAiConfig;
8
10
  }
9
11
  /**
@@ -44,6 +44,11 @@ function normalizeModel(value) {
44
44
  const model = value?.trim();
45
45
  return model && model !== "default" ? model : undefined;
46
46
  }
47
+ function usableSystemAi(config) {
48
+ if (!config.baseUrl.trim() || !config.apiKey.trim() || !config.model.trim())
49
+ return undefined;
50
+ return { ...config, enabled: true };
51
+ }
47
52
  /** Build the provider-specific settings used by session-adjacent AI actions. */
48
53
  export function resolveSessionAiContext(snapshot, config) {
49
54
  const provider = resolveSessionProvider(snapshot);
@@ -66,10 +71,13 @@ export function resolveCommitAiContext(snapshot, config) {
66
71
  apiKey: "",
67
72
  model: "",
68
73
  };
74
+ const readyDirectApi = usableSystemAi(directApi);
69
75
  return {
70
76
  ...sessionContext,
71
77
  provider: config.commitCli === "codex" || config.commitCli === "opencode" ? config.commitCli : "claude",
72
78
  model: normalizeModel(config.commitModel),
73
- ...(config.commitAiSource === "api" ? { systemAi: { ...directApi, enabled: true } } : {}),
79
+ ...(config.commitAiSource === "api"
80
+ ? { systemAi: { ...directApi, enabled: true } }
81
+ : readyDirectApi ? { fallbackSystemAi: readyDirectApi } : {}),
74
82
  };
75
83
  }