fix(coding-agent): remove process-cwd tool singletons and use tool-name allowlists
- switch SDK/CLI tool selection to name-based allowlists - apply allowlists across built-in, extension, and SDK tools - remove ambient process.cwd defaults from core tooling and resource helpers - update tests, examples, and TUI callers for explicit cwd plumbing - add regression coverage for extension tool filtering closes #3452 closes #2835
This commit is contained in:
@@ -265,11 +265,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||
private basePath: string;
|
||||
private fdPath: string | null;
|
||||
|
||||
constructor(
|
||||
commands: (SlashCommand | AutocompleteItem)[] = [],
|
||||
basePath: string = process.cwd(),
|
||||
fdPath: string | null = null,
|
||||
) {
|
||||
constructor(commands: (SlashCommand | AutocompleteItem)[] = [], basePath: string, fdPath: string | null = null) {
|
||||
this.commands = commands;
|
||||
this.basePath = basePath;
|
||||
this.fdPath = fdPath;
|
||||
|
||||
@@ -20,6 +20,7 @@ export class Loader extends Text {
|
||||
private currentFrame = 0;
|
||||
private intervalId: NodeJS.Timeout | null = null;
|
||||
private ui: TUI | null = null;
|
||||
private renderIndicatorVerbatim = false;
|
||||
|
||||
constructor(
|
||||
ui: TUI,
|
||||
@@ -55,7 +56,8 @@ export class Loader extends Text {
|
||||
}
|
||||
|
||||
setIndicator(indicator?: LoaderIndicatorOptions): void {
|
||||
this.frames = indicator?.frames ? [...indicator.frames] : [...DEFAULT_FRAMES];
|
||||
this.renderIndicatorVerbatim = indicator !== undefined;
|
||||
this.frames = indicator?.frames !== undefined ? [...indicator.frames] : [...DEFAULT_FRAMES];
|
||||
this.intervalMs = indicator?.intervalMs && indicator.intervalMs > 0 ? indicator.intervalMs : DEFAULT_INTERVAL_MS;
|
||||
this.currentFrame = 0;
|
||||
this.start();
|
||||
@@ -74,7 +76,8 @@ export class Loader extends Text {
|
||||
|
||||
private updateDisplay(): void {
|
||||
const frame = this.frames[this.currentFrame] ?? "";
|
||||
const indicator = frame.length > 0 ? `${this.spinnerColorFn(frame)} ` : "";
|
||||
const renderedFrame = this.renderIndicatorVerbatim ? frame : this.spinnerColorFn(frame);
|
||||
const indicator = frame.length > 0 ? `${renderedFrame} ` : "";
|
||||
this.setText(`${indicator}${this.messageColorFn(this.message)}`);
|
||||
if (this.ui) {
|
||||
this.ui.requestRender();
|
||||
|
||||
@@ -2476,14 +2476,17 @@ describe("Editor component", () => {
|
||||
|
||||
it("awaits async slash command argument completions", async () => {
|
||||
const editor = new Editor(createTestTUI(), defaultEditorTheme);
|
||||
const provider = new CombinedAutocompleteProvider([
|
||||
{
|
||||
name: "load-skills",
|
||||
description: "Load skills",
|
||||
getArgumentCompletions: async (prefix) =>
|
||||
prefix.startsWith("s") ? [{ value: "skill-a", label: "skill-a" }] : null,
|
||||
},
|
||||
]);
|
||||
const provider = new CombinedAutocompleteProvider(
|
||||
[
|
||||
{
|
||||
name: "load-skills",
|
||||
description: "Load skills",
|
||||
getArgumentCompletions: async (prefix) =>
|
||||
prefix.startsWith("s") ? [{ value: "skill-a", label: "skill-a" }] : null,
|
||||
},
|
||||
],
|
||||
process.cwd(),
|
||||
);
|
||||
editor.setAutocompleteProvider(provider);
|
||||
editor.setText("/load-skills ");
|
||||
|
||||
@@ -2498,15 +2501,18 @@ describe("Editor component", () => {
|
||||
|
||||
it("ignores invalid slash command argument completion results", async () => {
|
||||
const editor = new Editor(createTestTUI(), defaultEditorTheme);
|
||||
const provider = new CombinedAutocompleteProvider([
|
||||
{
|
||||
name: "load-skills",
|
||||
description: "Load skills",
|
||||
getArgumentCompletions: (() => "not-an-array") as unknown as (
|
||||
argumentPrefix: string,
|
||||
) => Promise<{ value: string; label: string }[] | null>,
|
||||
},
|
||||
]);
|
||||
const provider = new CombinedAutocompleteProvider(
|
||||
[
|
||||
{
|
||||
name: "load-skills",
|
||||
description: "Load skills",
|
||||
getArgumentCompletions: (() => "not-an-array") as unknown as (
|
||||
argumentPrefix: string,
|
||||
) => Promise<{ value: string; label: string }[] | null>,
|
||||
},
|
||||
],
|
||||
process.cwd(),
|
||||
);
|
||||
editor.setAutocompleteProvider(provider);
|
||||
editor.setText("/load-skills ");
|
||||
|
||||
@@ -2518,14 +2524,17 @@ describe("Editor component", () => {
|
||||
|
||||
it("does not show argument completions when command has no argument completer", async () => {
|
||||
const editor = new Editor(createTestTUI(), defaultEditorTheme);
|
||||
const provider = new CombinedAutocompleteProvider([
|
||||
{ name: "help", description: "Show help" },
|
||||
{
|
||||
name: "model",
|
||||
description: "Switch model",
|
||||
getArgumentCompletions: () => [{ value: "claude-opus", label: "claude-opus" }],
|
||||
},
|
||||
]);
|
||||
const provider = new CombinedAutocompleteProvider(
|
||||
[
|
||||
{ name: "help", description: "Show help" },
|
||||
{
|
||||
name: "model",
|
||||
description: "Switch model",
|
||||
getArgumentCompletions: () => [{ value: "claude-opus", label: "claude-opus" }],
|
||||
},
|
||||
],
|
||||
process.cwd(),
|
||||
);
|
||||
editor.setAutocompleteProvider(provider);
|
||||
|
||||
editor.handleInput("/");
|
||||
|
||||
Reference in New Issue
Block a user