feat(sproutclaw): modularize webui, extensions, and agent config layout
Some checks failed
CI / build-check-test (push) Has been cancelled
Some checks failed
CI / build-check-test (push) Has been cancelled
Restructure local extensions into per-feature directories, split WebUI into backend modules with slash commands and systemd support, and track prompts/skills under .pi/agent for portable Gitea deployment. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -287,6 +287,8 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
) => (InteractiveMode as any).prototype.getCompactNonPackageExtensionLabel.call(fakeThis, p, index, allPaths),
|
||||
getCompactExtensionLabels: (extensions: ExtensionFixture[]) =>
|
||||
(InteractiveMode as any).prototype.getCompactExtensionLabels.call(fakeThis, extensions),
|
||||
splitExtensionsByOrigin: (extensions: ExtensionFixture[]) =>
|
||||
(InteractiveMode as any).prototype.splitExtensionsByOrigin.call(fakeThis, extensions),
|
||||
formatDiagnostics: () => "diagnostics",
|
||||
getBuiltInCommandConflictDiagnostics: () => [],
|
||||
};
|
||||
@@ -415,7 +417,7 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
const output = renderAll(fakeThis.chatContainer);
|
||||
expect(output).toContain("[Skills]");
|
||||
expect(output).toContain("[技能]");
|
||||
expect(output).toContain("commit");
|
||||
expect(output).not.toContain("resource-list");
|
||||
});
|
||||
@@ -432,7 +434,7 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
const output = renderAll(fakeThis.chatContainer);
|
||||
expect(output).toContain("[Skills]");
|
||||
expect(output).toContain("[技能]");
|
||||
expect(output).toContain("resource-list");
|
||||
expect(output).not.toContain("commit");
|
||||
});
|
||||
@@ -450,7 +452,7 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
const output = renderAll(fakeThis.chatContainer);
|
||||
expect(output).toContain("[Skills]");
|
||||
expect(output).toContain("[技能]");
|
||||
expect(output).toContain("resource-list");
|
||||
expect(output).not.toContain("commit");
|
||||
});
|
||||
@@ -466,8 +468,9 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
const output = renderAll(fakeThis.chatContainer);
|
||||
expect(output).toContain("[Extensions]");
|
||||
expect(output).toContain("answer.ts, btw.ts");
|
||||
expect(output).toContain("[本地扩展]");
|
||||
expect(output).toContain("answer.ts");
|
||||
expect(output).toContain("btw.ts");
|
||||
expect(output).not.toContain("extensions/answer.ts");
|
||||
});
|
||||
|
||||
@@ -483,8 +486,12 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
@scope/pi-scoped, answer.ts, cli-extension.ts, HazAT/pi-interactive-subagents, HazAT/pi-interactive-subagents:subagents, local-index, pi-markdown-preview, user-index"`);
|
||||
"[本地扩展] 4
|
||||
answer.ts cli-extension.ts local-index user-index
|
||||
|
||||
[npm 扩展] 4
|
||||
@scope/pi-scoped HazAT/pi-interactive-subagents HazAT/pi-interactive-subagents:subagents pi-markdown-preview"
|
||||
`);
|
||||
});
|
||||
|
||||
test("adds more parent folders until local extension labels are unique", () => {
|
||||
@@ -529,8 +536,9 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
alpha/one, beta/one, gamma/one"`);
|
||||
"[本地扩展] 3
|
||||
alpha/one beta/one gamma/one"
|
||||
`);
|
||||
});
|
||||
|
||||
test("strips index.ts from local extension label, showing parent dir", () => {
|
||||
@@ -557,8 +565,9 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
plan-mode"`);
|
||||
"[本地扩展] 1
|
||||
plan-mode"
|
||||
`);
|
||||
});
|
||||
|
||||
test("strips index.js from local extension label, showing parent dir", () => {
|
||||
@@ -585,8 +594,9 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
plan-mode"`);
|
||||
"[本地扩展] 1
|
||||
plan-mode"
|
||||
`);
|
||||
});
|
||||
|
||||
test("mixed single-file and subdirectory index.ts extensions strip index.ts", () => {
|
||||
@@ -622,8 +632,9 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
plan-mode, webfetch.ts"`);
|
||||
"[本地扩展] 2
|
||||
plan-mode webfetch.ts"
|
||||
`);
|
||||
});
|
||||
|
||||
test("multiple index.ts with unique parent dirs need no disambiguation", () => {
|
||||
@@ -659,8 +670,9 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
bar, foo"`);
|
||||
"[本地扩展] 2
|
||||
bar foo"
|
||||
`);
|
||||
});
|
||||
|
||||
test("multiple index.ts with same parent dir name disambiguated with grandparent", () => {
|
||||
@@ -696,8 +708,9 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
alpha/tools, beta/tools"`);
|
||||
"[本地扩展] 2
|
||||
alpha/tools beta/tools"
|
||||
`);
|
||||
});
|
||||
|
||||
test("non-index file in subdirectory stays as filename", () => {
|
||||
@@ -724,8 +737,9 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
main.ts"`);
|
||||
"[本地扩展] 1
|
||||
main.ts"
|
||||
`);
|
||||
});
|
||||
|
||||
test("package extensions still strip index.ts correctly (regression guard)", () => {
|
||||
@@ -752,8 +766,9 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
pi-markdown-preview"`);
|
||||
"[npm 扩展] 1
|
||||
pi-markdown-preview"
|
||||
`);
|
||||
});
|
||||
test("captures mixed extension layouts in expanded output", () => {
|
||||
const fakeThis = createShowLoadedResourcesThis({
|
||||
@@ -768,21 +783,25 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
expect(normalizeRenderedOutput(fakeThis.chatContainer)).toMatchInlineSnapshot(`
|
||||
"[Extensions]
|
||||
project
|
||||
/tmp/project/.pi/extensions/answer.ts
|
||||
/tmp/project/.pi/extensions/local-index
|
||||
git:github.com/HazAT/pi-interactive-subagents
|
||||
extensions
|
||||
extensions/subagents
|
||||
npm:@scope/pi-scoped
|
||||
extensions
|
||||
npm:pi-markdown-preview
|
||||
extensions
|
||||
user
|
||||
/tmp/agent/extensions/user-index
|
||||
path
|
||||
/tmp/temp/cli-extension.ts"`);
|
||||
"[本地扩展] 4
|
||||
project
|
||||
/tmp/project/.pi/extensions/answer.ts
|
||||
/tmp/project/.pi/extensions/local-index
|
||||
user
|
||||
/tmp/agent/extensions/user-index
|
||||
path
|
||||
/tmp/temp/cli-extension.ts
|
||||
|
||||
[npm 扩展] 4
|
||||
project
|
||||
git:github.com/HazAT/pi-interactive-subagents
|
||||
extensions
|
||||
extensions/subagents
|
||||
npm:@scope/pi-scoped
|
||||
extensions
|
||||
npm:pi-markdown-preview
|
||||
extensions"
|
||||
`);
|
||||
});
|
||||
|
||||
test("shows context paths relative to cwd while preserving full external paths", () => {
|
||||
@@ -799,8 +818,10 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
const output = renderAll(fakeThis.chatContainer).replace(/\\/g, "/");
|
||||
expect(output).toContain("[Context]");
|
||||
expect(output).toContain("~/.pi/agent/AGENTS.md, AGENTS.md");
|
||||
expect(output).toContain("[系统设定]");
|
||||
expect(output).toContain("~/.pi/agent/AGENTS.md");
|
||||
expect(output).toContain("AGENTS.md");
|
||||
expect(output).not.toContain("~/.pi/agent/AGENTS.md, AGENTS.md");
|
||||
expect(output).not.toContain(`${cwd.replace(/\\/g, "/")}/AGENTS.md`);
|
||||
});
|
||||
|
||||
@@ -819,7 +840,7 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
});
|
||||
|
||||
const output = renderAll(fakeThis.chatContainer).replace(/\\/g, "/");
|
||||
expect(output).toContain("[Context]");
|
||||
expect(output).toContain("[系统设定]");
|
||||
expect(output).toContain("~/.pi/agent/AGENTS.md");
|
||||
expect(output).toContain("~/Development/pi-mono/AGENTS.md");
|
||||
expect(output).not.toContain("~/.pi/agent/AGENTS.md, AGENTS.md");
|
||||
@@ -854,6 +875,6 @@ describe("InteractiveMode.showLoadedResources", () => {
|
||||
|
||||
const output = renderAll(fakeThis.chatContainer);
|
||||
expect(output).toContain("[Skill conflicts]");
|
||||
expect(output).not.toContain("[Skills]");
|
||||
expect(output).not.toContain("[技能]");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user