mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-21 23:04:41 +00:00
feat(coding-agent): add --no-extensions flag to disable extension discovery
Adds --no-extensions CLI flag that skips automatic extension discovery while still allowing explicit -e paths. Three modes now available: 1. Default: auto-discover + any -e additions 2. --no-extensions: no extensions at all 3. --no-extensions -e foo.js: only load explicit extensions Useful for debugging or running subagent instances without auto-discovered extensions. closes #524
This commit is contained in:
commit
7f3fa417c4
6 changed files with 71 additions and 6 deletions
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- `--no-extensions` flag to disable extension discovery while still allowing explicit `-e` paths ([#524](https://github.com/badlogic/pi-mono/pull/524) by [@cv](https://github.com/cv))
|
||||||
- SDK: `InteractiveMode`, `runPrintMode()`, `runRpcMode()` exported for building custom run modes. See `docs/sdk.md`.
|
- SDK: `InteractiveMode`, `runPrintMode()`, `runRpcMode()` exported for building custom run modes. See `docs/sdk.md`.
|
||||||
- `PI_SKIP_VERSION_CHECK` environment variable to disable new version notifications at startup ([#549](https://github.com/badlogic/pi-mono/pull/549) by [@aos](https://github.com/aos))
|
- `PI_SKIP_VERSION_CHECK` environment variable to disable new version notifications at startup ([#549](https://github.com/badlogic/pi-mono/pull/549) by [@aos](https://github.com/aos))
|
||||||
- `thinkingBudgets` setting to customize token budgets per thinking level for token-based providers ([#529](https://github.com/badlogic/pi-mono/pull/529) by [@melihmucuk](https://github.com/melihmucuk))
|
- `thinkingBudgets` setting to customize token budgets per thinking level for token-based providers ([#529](https://github.com/badlogic/pi-mono/pull/529) by [@melihmucuk](https://github.com/melihmucuk))
|
||||||
|
|
|
||||||
|
|
@ -1125,6 +1125,7 @@ pi [options] [@files...] [messages...]
|
||||||
| `--tools <tools>` | Comma-separated tool list (default: `read,bash,edit,write`) |
|
| `--tools <tools>` | Comma-separated tool list (default: `read,bash,edit,write`) |
|
||||||
| `--thinking <level>` | Thinking level: `off`, `minimal`, `low`, `medium`, `high` |
|
| `--thinking <level>` | Thinking level: `off`, `minimal`, `low`, `medium`, `high` |
|
||||||
| `--extension <path>`, `-e` | Load an extension file (can be used multiple times) |
|
| `--extension <path>`, `-e` | Load an extension file (can be used multiple times) |
|
||||||
|
| `--no-extensions` | Disable extension discovery (explicit `-e` paths still work) |
|
||||||
| `--no-skills` | Disable skills discovery and loading |
|
| `--no-skills` | Disable skills discovery and loading |
|
||||||
| `--skills <patterns>` | Comma-separated glob patterns to filter skills (e.g., `git-*,docker`) |
|
| `--skills <patterns>` | Comma-separated glob patterns to filter skills (e.g., `git-*,docker`) |
|
||||||
| `--export <file> [output]` | Export session to HTML |
|
| `--export <file> [output]` | Export session to HTML |
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ export interface Args {
|
||||||
models?: string[];
|
models?: string[];
|
||||||
tools?: ToolName[];
|
tools?: ToolName[];
|
||||||
extensions?: string[];
|
extensions?: string[];
|
||||||
|
noExtensions?: boolean;
|
||||||
print?: boolean;
|
print?: boolean;
|
||||||
export?: string;
|
export?: string;
|
||||||
noSkills?: boolean;
|
noSkills?: boolean;
|
||||||
|
|
@ -116,6 +117,8 @@ export function parseArgs(args: string[], extensionFlags?: Map<string, { type: "
|
||||||
} else if ((arg === "--extension" || arg === "-e") && i + 1 < args.length) {
|
} else if ((arg === "--extension" || arg === "-e") && i + 1 < args.length) {
|
||||||
result.extensions = result.extensions ?? [];
|
result.extensions = result.extensions ?? [];
|
||||||
result.extensions.push(args[++i]);
|
result.extensions.push(args[++i]);
|
||||||
|
} else if (arg === "--no-extensions") {
|
||||||
|
result.noExtensions = true;
|
||||||
} else if (arg === "--no-skills") {
|
} else if (arg === "--no-skills") {
|
||||||
result.noSkills = true;
|
result.noSkills = true;
|
||||||
} else if (arg === "--skills" && i + 1 < args.length) {
|
} else if (arg === "--skills" && i + 1 < args.length) {
|
||||||
|
|
@ -175,6 +178,7 @@ ${chalk.bold("Options:")}
|
||||||
Available: read, bash, edit, write, grep, find, ls
|
Available: read, bash, edit, write, grep, find, ls
|
||||||
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
|
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
|
||||||
--extension, -e <path> Load an extension file (can be used multiple times)
|
--extension, -e <path> Load an extension file (can be used multiple times)
|
||||||
|
--no-extensions Disable extensions discovery and loading
|
||||||
--no-skills Disable skills discovery and loading
|
--no-skills Disable skills discovery and loading
|
||||||
--skills <patterns> Comma-separated glob patterns to filter skills (e.g., git-*,docker)
|
--skills <patterns> Comma-separated glob patterns to filter skills (e.g., git-*,docker)
|
||||||
--export <file> Export session file to HTML and exit
|
--export <file> Export session file to HTML and exit
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import { selectSession } from "./cli/session-picker.js";
|
||||||
import { CONFIG_DIR_NAME, getAgentDir, getModelsPath, VERSION } from "./config.js";
|
import { CONFIG_DIR_NAME, getAgentDir, getModelsPath, VERSION } from "./config.js";
|
||||||
import { createEventBus } from "./core/event-bus.js";
|
import { createEventBus } from "./core/event-bus.js";
|
||||||
import { exportFromFile } from "./core/export-html/index.js";
|
import { exportFromFile } from "./core/export-html/index.js";
|
||||||
import { discoverAndLoadExtensions, type LoadExtensionsResult } from "./core/extensions/index.js";
|
import { discoverAndLoadExtensions, type LoadExtensionsResult, loadExtensions } from "./core/extensions/index.js";
|
||||||
import type { ModelRegistry } from "./core/model-registry.js";
|
import type { ModelRegistry } from "./core/model-registry.js";
|
||||||
import { resolveModelScope, type ScopedModel } from "./core/model-resolver.js";
|
import { resolveModelScope, type ScopedModel } from "./core/model-resolver.js";
|
||||||
import { type CreateAgentSessionOptions, createAgentSession, discoverAuthStorage, discoverModels } from "./core/sdk.js";
|
import { type CreateAgentSessionOptions, createAgentSession, discoverAuthStorage, discoverModels } from "./core/sdk.js";
|
||||||
|
|
@ -209,16 +209,25 @@ export async function main(args: string[]) {
|
||||||
const firstPass = parseArgs(args);
|
const firstPass = parseArgs(args);
|
||||||
time("parseArgs-firstPass");
|
time("parseArgs-firstPass");
|
||||||
|
|
||||||
// Early load extensions to discover their CLI flags
|
// Early load extensions to discover their CLI flags (unless --no-extensions)
|
||||||
const cwd = process.cwd();
|
const cwd = process.cwd();
|
||||||
const agentDir = getAgentDir();
|
const agentDir = getAgentDir();
|
||||||
const eventBus = createEventBus();
|
const eventBus = createEventBus();
|
||||||
const settingsManager = SettingsManager.create(cwd);
|
const settingsManager = SettingsManager.create(cwd);
|
||||||
time("SettingsManager.create");
|
time("SettingsManager.create");
|
||||||
// Merge CLI --extension args with settings.json extensions
|
|
||||||
const extensionPaths = [...settingsManager.getExtensionPaths(), ...(firstPass.extensions ?? [])];
|
let extensionsResult: LoadExtensionsResult;
|
||||||
const extensionsResult = await discoverAndLoadExtensions(extensionPaths, cwd, agentDir, eventBus);
|
if (firstPass.noExtensions) {
|
||||||
time("discoverExtensionFlags");
|
// --no-extensions disables discovery, but explicit -e flags still work
|
||||||
|
const explicitPaths = firstPass.extensions ?? [];
|
||||||
|
extensionsResult = await loadExtensions(explicitPaths, cwd, eventBus);
|
||||||
|
time("loadExtensions");
|
||||||
|
} else {
|
||||||
|
// Merge CLI --extension args with settings.json extensions
|
||||||
|
const extensionPaths = [...settingsManager.getExtensionPaths(), ...(firstPass.extensions ?? [])];
|
||||||
|
extensionsResult = await discoverAndLoadExtensions(extensionPaths, cwd, agentDir, eventBus);
|
||||||
|
time("discoverExtensionFlags");
|
||||||
|
}
|
||||||
|
|
||||||
// Collect all extension flags
|
// Collect all extension flags
|
||||||
const extensionFlags = new Map<string, { type: "boolean" | "string" }>();
|
const extensionFlags = new Map<string, { type: "boolean" | "string" }>();
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,26 @@ describe("parseArgs", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("--no-extensions flag", () => {
|
||||||
|
test("parses --no-extensions flag", () => {
|
||||||
|
const result = parseArgs(["--no-extensions"]);
|
||||||
|
expect(result.noExtensions).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses --no-extensions with explicit -e flags", () => {
|
||||||
|
const result = parseArgs(["--no-extensions", "-e", "foo.ts", "-e", "bar.ts"]);
|
||||||
|
expect(result.noExtensions).toBe(true);
|
||||||
|
expect(result.extensions).toEqual(["foo.ts", "bar.ts"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("--no-skills flag", () => {
|
||||||
|
test("parses --no-skills flag", () => {
|
||||||
|
const result = parseArgs(["--no-skills"]);
|
||||||
|
expect(result.noSkills).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("messages and file args", () => {
|
describe("messages and file args", () => {
|
||||||
test("parses plain text messages", () => {
|
test("parses plain text messages", () => {
|
||||||
const result = parseArgs(["hello", "world"]);
|
const result = parseArgs(["hello", "world"]);
|
||||||
|
|
|
||||||
|
|
@ -443,4 +443,34 @@ describe("extensions discovery", () => {
|
||||||
expect(result.extensions).toHaveLength(1);
|
expect(result.extensions).toHaveLength(1);
|
||||||
expect(result.extensions[0].flags.has("--my-flag")).toBe(true);
|
expect(result.extensions[0].flags.has("--my-flag")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("loadExtensions only loads explicit paths without discovery", async () => {
|
||||||
|
// Create discoverable extensions (would be found by discoverAndLoadExtensions)
|
||||||
|
fs.writeFileSync(path.join(extensionsDir, "discovered.ts"), extensionCodeWithTool("discovered"));
|
||||||
|
|
||||||
|
// Create explicit extension outside discovery path
|
||||||
|
const explicitPath = path.join(tempDir, "explicit.ts");
|
||||||
|
fs.writeFileSync(explicitPath, extensionCodeWithTool("explicit"));
|
||||||
|
|
||||||
|
// Use loadExtensions directly to skip discovery
|
||||||
|
const { loadExtensions } = await import("../src/core/extensions/loader.js");
|
||||||
|
const result = await loadExtensions([explicitPath], tempDir);
|
||||||
|
|
||||||
|
expect(result.errors).toHaveLength(0);
|
||||||
|
expect(result.extensions).toHaveLength(1);
|
||||||
|
expect(result.extensions[0].tools.has("explicit")).toBe(true);
|
||||||
|
expect(result.extensions[0].tools.has("discovered")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("loadExtensions with no paths loads nothing", async () => {
|
||||||
|
// Create discoverable extensions (would be found by discoverAndLoadExtensions)
|
||||||
|
fs.writeFileSync(path.join(extensionsDir, "discovered.ts"), extensionCode);
|
||||||
|
|
||||||
|
// Use loadExtensions directly with empty paths
|
||||||
|
const { loadExtensions } = await import("../src/core/extensions/loader.js");
|
||||||
|
const result = await loadExtensions([], tempDir);
|
||||||
|
|
||||||
|
expect(result.errors).toHaveLength(0);
|
||||||
|
expect(result.extensions).toHaveLength(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue