@co0ontty/wand 2.4.3 → 2.4.4
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/dist/build-info.json +3 -3
- package/dist/git-quick-commit.d.ts +6 -2
- package/dist/git-quick-commit.js +8 -3
- package/dist/path-repair.d.ts +3 -2
- package/dist/path-repair.js +20 -14
- package/dist/server-session-routes.js +7 -8
- package/dist/session-ai-context.d.ts +15 -0
- package/dist/session-ai-context.js +36 -0
- package/dist/storage.js +6 -1
- package/dist/web-ui/content/scripts.js +38 -38
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +3 -3
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-07-
|
|
4
|
-
"version": "2.4.
|
|
2
|
+
"commit": "c470535a559107d4b5f27a9cc7c7a4e7b057352e",
|
|
3
|
+
"builtAt": "2026-07-10T13:26:51.037Z",
|
|
4
|
+
"version": "2.4.4",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
|
@@ -11,7 +11,7 @@ interface QuickCommitOptions {
|
|
|
11
11
|
autoMessage: boolean;
|
|
12
12
|
customMessage?: string;
|
|
13
13
|
tag?: string;
|
|
14
|
-
/** When `tag` is empty, ask
|
|
14
|
+
/** When `tag` is empty, ask the session provider to generate one based on the diff + commit message. */
|
|
15
15
|
autoTag?: boolean;
|
|
16
16
|
push?: boolean;
|
|
17
17
|
/**
|
|
@@ -39,7 +39,11 @@ export declare function generateCommitMessageOnly(cwd: string, language: string,
|
|
|
39
39
|
interface TagHeadOptions {
|
|
40
40
|
cwd: string;
|
|
41
41
|
language: string;
|
|
42
|
-
|
|
42
|
+
provider?: SessionProvider;
|
|
43
|
+
model?: string | null;
|
|
44
|
+
thinkingEffort?: SessionSnapshot["thinkingEffort"];
|
|
45
|
+
inheritEnv?: boolean;
|
|
46
|
+
/** Explicit tag name. If empty and `autoTag` is true, ask the session provider to generate one. */
|
|
43
47
|
tag?: string;
|
|
44
48
|
autoTag?: boolean;
|
|
45
49
|
/** Push only this tag to its upstream remote after creating it. */
|
package/dist/git-quick-commit.js
CHANGED
|
@@ -492,7 +492,7 @@ export async function generateCommitMessageOnly(cwd, language, ai = {}) {
|
|
|
492
492
|
return generateCommitMessageWithTag(cwd, language, ai);
|
|
493
493
|
}
|
|
494
494
|
/**
|
|
495
|
-
* Ask
|
|
495
|
+
* Ask the session provider for a single tag string. Called from `runQuickCommit` after the commit has
|
|
496
496
|
* already landed, so we look at `git show HEAD` and use `HEAD~1` for the previous tag.
|
|
497
497
|
*/
|
|
498
498
|
async function generateTagAfterCommit(cwd, language, commitMessage, ai = {}) {
|
|
@@ -613,7 +613,12 @@ export async function runTagHead(opts) {
|
|
|
613
613
|
catch {
|
|
614
614
|
headSubject = "";
|
|
615
615
|
}
|
|
616
|
-
tagName = await generateTagAfterCommit(cwd, language, headSubject || ""
|
|
616
|
+
tagName = await generateTagAfterCommit(cwd, language, headSubject || "", {
|
|
617
|
+
provider: opts.provider,
|
|
618
|
+
model: opts.model,
|
|
619
|
+
thinkingEffort: opts.thinkingEffort,
|
|
620
|
+
inheritEnv: opts.inheritEnv,
|
|
621
|
+
});
|
|
617
622
|
}
|
|
618
623
|
if (!tagName) {
|
|
619
624
|
throw new QuickCommitError("请填写 tag 名称,或开启 AI 生成。", "EMPTY_TAG");
|
|
@@ -1096,7 +1101,7 @@ export async function runQuickCommit(opts) {
|
|
|
1096
1101
|
catch {
|
|
1097
1102
|
commitHash = "";
|
|
1098
1103
|
}
|
|
1099
|
-
// Tag: explicit `tag` wins; if empty + autoTag, ask
|
|
1104
|
+
// Tag: explicit `tag` wins; if empty + autoTag, ask the session provider; otherwise skip.
|
|
1100
1105
|
let tagName = (tag || "").trim();
|
|
1101
1106
|
if (!tagName && autoTag) {
|
|
1102
1107
|
tagName = await generateTagAfterCommit(cwd, language, message, ai);
|
package/dist/path-repair.d.ts
CHANGED
|
@@ -58,8 +58,9 @@ export declare function repairRuntimePath(): PathRepairResult;
|
|
|
58
58
|
*
|
|
59
59
|
* 行为:
|
|
60
60
|
* - 跑 `${shell} -l -c '...'` 拉 $PATH 和 command -v claude/codex(4s 超时)
|
|
61
|
-
* -
|
|
62
|
-
*
|
|
61
|
+
* - 按 login shell 的 PATH 顺序重排 process.env.PATH,再把仅存在于 service 的
|
|
62
|
+
* 目录追加回去。不能只前插新增目录:同一个 CLI 同时装在 nvm 和 Homebrew 时,
|
|
63
|
+
* 两个目录本来都存在,旧实现不会重排,structured 与 PTY 会命中不同版本。
|
|
63
64
|
* - 失败时静默走同步那一版结果
|
|
64
65
|
*
|
|
65
66
|
* 接受一个已经跑过同步阶段的 result,在上面 mutate。
|
package/dist/path-repair.js
CHANGED
|
@@ -153,8 +153,9 @@ export function repairRuntimePath() {
|
|
|
153
153
|
*
|
|
154
154
|
* 行为:
|
|
155
155
|
* - 跑 `${shell} -l -c '...'` 拉 $PATH 和 command -v claude/codex(4s 超时)
|
|
156
|
-
* -
|
|
157
|
-
*
|
|
156
|
+
* - 按 login shell 的 PATH 顺序重排 process.env.PATH,再把仅存在于 service 的
|
|
157
|
+
* 目录追加回去。不能只前插新增目录:同一个 CLI 同时装在 nvm 和 Homebrew 时,
|
|
158
|
+
* 两个目录本来都存在,旧实现不会重排,structured 与 PTY 会命中不同版本。
|
|
158
159
|
* - 失败时静默走同步那一版结果
|
|
159
160
|
*
|
|
160
161
|
* 接受一个已经跑过同步阶段的 result,在上面 mutate。
|
|
@@ -184,10 +185,11 @@ export async function deepRepairRuntimePath(result, opts = {}) {
|
|
|
184
185
|
return result;
|
|
185
186
|
}
|
|
186
187
|
const delim = path.delimiter;
|
|
187
|
-
const
|
|
188
|
+
const currentSegments = (process.env.PATH ?? "")
|
|
188
189
|
.split(delim)
|
|
189
190
|
.map((seg) => seg.trim())
|
|
190
|
-
.filter((seg) => seg.length > 0)
|
|
191
|
+
.filter((seg) => seg.length > 0);
|
|
192
|
+
const existing = new Set(currentSegments);
|
|
191
193
|
// login shell 报告的所有 PATH 段 + claude/codex 解析出的目录都纳入候选。
|
|
192
194
|
const fromShell = [];
|
|
193
195
|
for (const seg of probe.path.split(delim).map((s) => s.trim()).filter(Boolean)) {
|
|
@@ -197,9 +199,10 @@ export async function deepRepairRuntimePath(result, opts = {}) {
|
|
|
197
199
|
fromShell.push(path.dirname(probe.claude));
|
|
198
200
|
if (probe.codex)
|
|
199
201
|
fromShell.push(path.dirname(probe.codex));
|
|
200
|
-
const
|
|
202
|
+
const preferred = [];
|
|
203
|
+
const preferredSet = new Set();
|
|
201
204
|
for (const dir of fromShell) {
|
|
202
|
-
if (!dir ||
|
|
205
|
+
if (!dir || preferredSet.has(dir))
|
|
203
206
|
continue;
|
|
204
207
|
let ok = false;
|
|
205
208
|
try {
|
|
@@ -210,19 +213,22 @@ export async function deepRepairRuntimePath(result, opts = {}) {
|
|
|
210
213
|
}
|
|
211
214
|
if (!ok)
|
|
212
215
|
continue;
|
|
213
|
-
|
|
214
|
-
|
|
216
|
+
preferredSet.add(dir);
|
|
217
|
+
preferred.push(dir);
|
|
218
|
+
}
|
|
219
|
+
const additions = preferred.filter((dir) => !existing.has(dir));
|
|
220
|
+
const serviceOnly = currentSegments.filter((dir) => !preferredSet.has(dir));
|
|
221
|
+
const reordered = [...preferred, ...serviceOnly];
|
|
222
|
+
const nextPath = reordered.join(delim);
|
|
223
|
+
if (nextPath && nextPath !== (process.env.PATH ?? "")) {
|
|
224
|
+
process.env.PATH = nextPath;
|
|
215
225
|
}
|
|
216
226
|
if (additions.length > 0) {
|
|
217
|
-
// 前插:login shell 的 PATH 优先级比 unit 写死的高,让 claude 解析到用户期望的版本。
|
|
218
|
-
const prefix = additions.join(delim);
|
|
219
|
-
const currentPath = process.env.PATH ?? "";
|
|
220
|
-
process.env.PATH = currentPath ? `${prefix}${delim}${currentPath}` : prefix;
|
|
221
227
|
result.added.push(...additions);
|
|
222
228
|
}
|
|
223
229
|
result.finalPath = process.env.PATH ?? "";
|
|
224
|
-
// 修复完再 probe 一次,让 resolved
|
|
225
|
-
//
|
|
230
|
+
// 修复完再 probe 一次,让 resolved 字段反映最终能找到的位置;目录即使原本已在
|
|
231
|
+
// service PATH 中,也可能因本次重排而从旧的 nvm 副本切换到 Homebrew 副本。
|
|
226
232
|
result.resolved = probeCommands();
|
|
227
233
|
result.deepProbe = "success";
|
|
228
234
|
return result;
|
|
@@ -4,6 +4,7 @@ import { getDefaultModelForProvider, normalizeMode } from "./config.js";
|
|
|
4
4
|
import { blockWindowMessagesForTransport, sliceTurnBlocksForTransport, truncateMessagesForTransport, windowMessagesForTransport } from "./message-truncator.js";
|
|
5
5
|
import { checkSessionWorktreeMergeability, cleanupSessionWorktree, getWorktreeMergeErrorCode, mergeSessionWorktree, WorktreeMergeError } from "./git-worktree.js";
|
|
6
6
|
import { resolveSessionCwd } from "./session-cwd.js";
|
|
7
|
+
import { resolveSessionAiContext } from "./session-ai-context.js";
|
|
7
8
|
import { getGitStatus, QuickCommitError, runQuickCommitWithFallback, runTagHead, runPush, generateCommitMessageOnly, } from "./git-quick-commit.js";
|
|
8
9
|
import { getErrorMessage } from "./error-utils.js";
|
|
9
10
|
export { getErrorMessage };
|
|
@@ -537,13 +538,11 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
537
538
|
}
|
|
538
539
|
const body = (req.body ?? {});
|
|
539
540
|
try {
|
|
541
|
+
const ai = resolveSessionAiContext(snapshot, config);
|
|
540
542
|
const result = await runQuickCommitWithFallback({
|
|
541
543
|
cwd: snapshot.cwd,
|
|
542
544
|
language: config.language ?? "",
|
|
543
|
-
|
|
544
|
-
model: snapshot.selectedModel ?? snapshot.structuredState?.model ?? getDefaultModelForProvider(config, snapshot.provider),
|
|
545
|
-
thinkingEffort: snapshot.thinkingEffort ?? config.defaultThinkingEffort,
|
|
546
|
-
inheritEnv: config.inheritEnv,
|
|
545
|
+
...ai,
|
|
547
546
|
autoMessage: body.autoMessage !== false,
|
|
548
547
|
customMessage: typeof body.customMessage === "string" ? body.customMessage : undefined,
|
|
549
548
|
tag: typeof body.tag === "string" ? body.tag : undefined,
|
|
@@ -573,11 +572,9 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
573
572
|
return;
|
|
574
573
|
}
|
|
575
574
|
try {
|
|
575
|
+
const ai = resolveSessionAiContext(snapshot, config);
|
|
576
576
|
const result = await generateCommitMessageOnly(snapshot.cwd, config.language ?? "", {
|
|
577
|
-
|
|
578
|
-
model: snapshot.selectedModel ?? snapshot.structuredState?.model ?? getDefaultModelForProvider(config, snapshot.provider),
|
|
579
|
-
thinkingEffort: snapshot.thinkingEffort ?? config.defaultThinkingEffort,
|
|
580
|
-
inheritEnv: config.inheritEnv,
|
|
577
|
+
...ai,
|
|
581
578
|
});
|
|
582
579
|
res.json(result);
|
|
583
580
|
}
|
|
@@ -601,9 +598,11 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
601
598
|
}
|
|
602
599
|
const body = (req.body ?? {});
|
|
603
600
|
try {
|
|
601
|
+
const ai = resolveSessionAiContext(snapshot, config);
|
|
604
602
|
const result = await runTagHead({
|
|
605
603
|
cwd: snapshot.cwd,
|
|
606
604
|
language: config.language ?? "",
|
|
605
|
+
...ai,
|
|
607
606
|
tag: typeof body.tag === "string" ? body.tag : undefined,
|
|
608
607
|
autoTag: !!body.autoTag,
|
|
609
608
|
push: !!body.push,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SessionProvider, SessionSnapshot, WandConfig } from "./types.js";
|
|
2
|
+
export interface SessionAiContext {
|
|
3
|
+
provider: SessionProvider;
|
|
4
|
+
model?: string;
|
|
5
|
+
thinkingEffort: SessionSnapshot["thinkingEffort"];
|
|
6
|
+
inheritEnv?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the provider from every representation used by current and legacy
|
|
10
|
+
* sessions. Older persisted sessions may not have the top-level provider, but
|
|
11
|
+
* still identify Codex through structuredState, runner, or command.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveSessionProvider(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command">): SessionProvider;
|
|
14
|
+
/** Build the provider-specific settings used by session-adjacent AI actions. */
|
|
15
|
+
export declare function resolveSessionAiContext(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command" | "selectedModel" | "thinkingEffort">, config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultThinkingEffort" | "inheritEnv">): SessionAiContext;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getDefaultModelForProvider } from "./config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the provider from every representation used by current and legacy
|
|
4
|
+
* sessions. Older persisted sessions may not have the top-level provider, but
|
|
5
|
+
* still identify Codex through structuredState, runner, or command.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveSessionProvider(snapshot) {
|
|
8
|
+
if (snapshot.provider === "claude" || snapshot.provider === "codex") {
|
|
9
|
+
return snapshot.provider;
|
|
10
|
+
}
|
|
11
|
+
if (snapshot.structuredState?.provider === "claude" || snapshot.structuredState?.provider === "codex") {
|
|
12
|
+
return snapshot.structuredState.provider;
|
|
13
|
+
}
|
|
14
|
+
const runner = snapshot.runner ?? snapshot.structuredState?.runner;
|
|
15
|
+
if (runner === "codex-cli-exec")
|
|
16
|
+
return "codex";
|
|
17
|
+
if (runner === "claude-cli" || runner === "claude-cli-print" || runner === "claude-sdk")
|
|
18
|
+
return "claude";
|
|
19
|
+
return /^codex\b/i.test(snapshot.command.trim()) ? "codex" : "claude";
|
|
20
|
+
}
|
|
21
|
+
function normalizeModel(value) {
|
|
22
|
+
const model = value?.trim();
|
|
23
|
+
return model && model !== "default" ? model : undefined;
|
|
24
|
+
}
|
|
25
|
+
/** Build the provider-specific settings used by session-adjacent AI actions. */
|
|
26
|
+
export function resolveSessionAiContext(snapshot, config) {
|
|
27
|
+
const provider = resolveSessionProvider(snapshot);
|
|
28
|
+
const sessionModel = normalizeModel(snapshot.selectedModel) ?? normalizeModel(snapshot.structuredState?.model);
|
|
29
|
+
const defaultModel = normalizeModel(getDefaultModelForProvider(config, provider));
|
|
30
|
+
return {
|
|
31
|
+
provider,
|
|
32
|
+
model: sessionModel ?? defaultModel,
|
|
33
|
+
thinkingEffort: snapshot.thinkingEffort ?? config.defaultThinkingEffort,
|
|
34
|
+
inheritEnv: config.inheritEnv,
|
|
35
|
+
};
|
|
36
|
+
}
|
package/dist/storage.js
CHANGED
|
@@ -24,7 +24,12 @@ function inferSessionProvider(row) {
|
|
|
24
24
|
if (row.runner === "claude-cli" || row.runner === "claude-cli-print") {
|
|
25
25
|
return "claude";
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
if (row.runner === "codex-cli-exec") {
|
|
28
|
+
return "codex";
|
|
29
|
+
}
|
|
30
|
+
if (/^codex\b/i.test(row.command.trim()))
|
|
31
|
+
return "codex";
|
|
32
|
+
return /^claude\b/i.test(row.command.trim()) ? "claude" : undefined;
|
|
28
33
|
}
|
|
29
34
|
function parseWorktreeInfo(raw) {
|
|
30
35
|
const parsed = safeJsonParse(raw);
|