mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
feat(pi-worktree): 新增斜杠命令 git worktree 管理扩展
提供 /wt(别名 /worktree)一键拆分/合并: - /wt new 建 worktree 后经 SessionManager.forkFrom + switchSession 迁移会话 cwd,历史上下文保留;合并/切换同理 - /wt merge 先确认再 rebase 到来源分支、默认 --ff-only(可选 --squash),来源改动用 stash 兜底,冲突可中止或保留后用 /wt merge --continue / --abort 处理,永不 push - 来源 checkout 的 .pi/worktree.json 声明环境复刻(copy/link/env/ postCreate),确认框先展示计划再执行;postCreate 仅在项目已信任 时以 argv 形式 execFile,失败可 /wt reopen 增量重跑 - 登记文件 <agentDir>/pi-worktree/state.json 原子写入并加锁 串行化;会话切换后向模型注入 cwd 变更提示;未被 .gitignore 覆盖的软链/环境文件写入 .git/info/exclude 防止误提交 - 仅注册用户显式触发的斜杠命令,不暴露模型可调用工具 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
f237025cf9
commit
97c2e75dfe
@@ -0,0 +1,64 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtemp } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
import { argumentCompletions } from "../index.ts";
|
||||
import { addEntry, updateState, type WorktreeEntry } from "../src/state.ts";
|
||||
|
||||
function entry(overrides: Partial<WorktreeEntry> = {}): WorktreeEntry {
|
||||
return {
|
||||
slug: "demo",
|
||||
path: "/nonexistent/demo",
|
||||
branch: "wt/demo",
|
||||
sourcePath: "/nonexistent/source",
|
||||
sourceBranch: "main",
|
||||
sourceSha: "0".repeat(40),
|
||||
createdAt: new Date().toISOString(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test("first-token completion returns subcommand names", async () => {
|
||||
const items = await argumentCompletions("mer");
|
||||
assert.ok(items?.some((item) => item.value === "merge"));
|
||||
});
|
||||
|
||||
test("merge flag completions expand to the full argument line", async () => {
|
||||
const items = await argumentCompletions("merge --c");
|
||||
assert.deepEqual(
|
||||
items?.map((item) => ({ value: item.value, label: item.label })),
|
||||
[{ value: "merge --continue", label: "--continue" }],
|
||||
);
|
||||
});
|
||||
|
||||
test("bare -- after merge offers all four flags as full lines", async () => {
|
||||
const items = await argumentCompletions("merge --");
|
||||
assert.equal(items?.length, 4);
|
||||
for (const item of items ?? []) {
|
||||
assert.ok(item.value.startsWith("merge --"), item.value);
|
||||
}
|
||||
});
|
||||
|
||||
test("slug completions keep the subcommand in the replacement value", async (t) => {
|
||||
const agentDir = await mkdtemp(join(tmpdir(), "pi-worktree-agent-"));
|
||||
const previous = process.env.PI_CODING_AGENT_DIR;
|
||||
process.env.PI_CODING_AGENT_DIR = agentDir;
|
||||
t.after(() => {
|
||||
if (previous === undefined) delete process.env.PI_CODING_AGENT_DIR;
|
||||
else process.env.PI_CODING_AGENT_DIR = previous;
|
||||
});
|
||||
await updateState((s) => addEntry(s, entry()));
|
||||
|
||||
const switchItems = await argumentCompletions("switch ");
|
||||
const values = switchItems?.map((item) => item.value) ?? [];
|
||||
assert.ok(values.includes("switch main"), JSON.stringify(values));
|
||||
assert.ok(values.includes("switch demo"), JSON.stringify(values));
|
||||
|
||||
const removeItems = await argumentCompletions("remove d");
|
||||
assert.deepEqual(
|
||||
removeItems?.map((item) => ({ value: item.value, label: item.label })),
|
||||
[{ value: "remove demo", label: "demo" }],
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user