fix(extensions): route print mode through buildResourceLoader
Print mode was constructing DefaultResourceLoader directly, which
bypassed the GSD extension registry filter and let disabled bundled
extensions leak through. With the community @0xkobold/pi-ollama
installed, every `gsd -p` invocation printed an /ollama command
conflict because the bundled ollama extension (explicitly disabled
in ~/.gsd/extensions/registry.json) was still being loaded.
- Add extension-manifest.json for the bundled ollama extension so the
registry's id-keyed disable entry can actually target it.
- Extend buildResourceLoader() with an options bag for print-mode
callers (additionalExtensionPaths, appendSystemPrompt).
- Switch print mode to buildResourceLoader() so the registry filter
(extensionPathsTransform) runs in both TUI and print paths.
Also fix a stderr leak in the GSD codebase-generator: execSync("git
ls-files") was inheriting stderr to the parent, so running gsd from a
non-repo cwd (e.g. $HOME) printed "fatal: not a git repository" before
the catch silently returned []. Pipe stderr so it lands in the thrown
Error instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2f2f1845f7
commit
6612456934
4 changed files with 58 additions and 7 deletions
|
|
@ -544,8 +544,12 @@ if (isPrintMode) {
|
|||
exitIfManagedResourcesAreNewer(agentDir)
|
||||
initResources(agentDir)
|
||||
markStartup('initResources')
|
||||
const resourceLoader = new DefaultResourceLoader({
|
||||
agentDir,
|
||||
// Route print mode through buildResourceLoader so the GSD extension registry
|
||||
// filter (extensionPathsTransform) is applied consistently with TUI mode.
|
||||
// Constructing DefaultResourceLoader directly bypassed the filter and let
|
||||
// disabled bundled extensions (e.g. `ollama` superseded by `@0xkobold/pi-ollama`)
|
||||
// leak through and emit `/ollama` command conflicts on every print invocation.
|
||||
const resourceLoader = buildResourceLoader(agentDir, {
|
||||
additionalExtensionPaths: cliFlags.extensions.length > 0 ? cliFlags.extensions : undefined,
|
||||
appendSystemPrompt,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { DefaultResourceLoader, sortExtensionPaths } from '@gsd/pi-coding-agent'
|
||||
if (process.env.GSD_DEBUG_EXTENSIONS) process.stderr.write("[gsd-debug] resource-loader.ts loaded\n")
|
||||
import { createHash } from 'node:crypto'
|
||||
import { homedir } from 'node:os'
|
||||
import { chmodSync, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, openSync, closeSync, readFileSync, readlinkSync, readdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs'
|
||||
|
|
@ -730,7 +731,22 @@ function getBundledExtensionKeys(): Set<string> {
|
|||
return _bundledExtensionKeys
|
||||
}
|
||||
|
||||
export function buildResourceLoader(agentDir: string): DefaultResourceLoader {
|
||||
/**
|
||||
* Optional overrides passed through to DefaultResourceLoader. Print mode
|
||||
* needs these — it used to construct DefaultResourceLoader directly, which
|
||||
* bypassed buildResourceLoader's extensionPathsTransform (= the GSD registry
|
||||
* filter) and let disabled bundled extensions like `ollama` leak through and
|
||||
* conflict with community replacements such as `@0xkobold/pi-ollama`.
|
||||
*/
|
||||
export interface BuildResourceLoaderOptions {
|
||||
additionalExtensionPaths?: string[]
|
||||
appendSystemPrompt?: string
|
||||
}
|
||||
|
||||
export function buildResourceLoader(
|
||||
agentDir: string,
|
||||
options: BuildResourceLoaderOptions = {},
|
||||
): DefaultResourceLoader {
|
||||
const registry = loadRegistry()
|
||||
const piAgentDir = join(homedir(), '.pi', 'agent')
|
||||
const piExtensionsDir = join(piAgentDir, 'extensions')
|
||||
|
|
@ -743,19 +759,30 @@ export function buildResourceLoader(agentDir: string): DefaultResourceLoader {
|
|||
return isExtensionEnabled(registry, manifest.id)
|
||||
})
|
||||
|
||||
// Print-mode callers pass their own additional extension paths (e.g. --extension
|
||||
// flags). Non-print mode uses the implicit pi-extensions discovery above.
|
||||
const additionalExtensionPaths =
|
||||
options.additionalExtensionPaths && options.additionalExtensionPaths.length > 0
|
||||
? options.additionalExtensionPaths
|
||||
: piExtensionPaths
|
||||
|
||||
return new DefaultResourceLoader({
|
||||
agentDir,
|
||||
additionalExtensionPaths: piExtensionPaths,
|
||||
additionalExtensionPaths,
|
||||
appendSystemPrompt: options.appendSystemPrompt,
|
||||
bundledExtensionKeys: bundledKeys,
|
||||
extensionPathsTransform: (paths: string[]) => {
|
||||
// 1. Filter community extensions through the GSD registry
|
||||
// Filter community + bundled extensions through the GSD registry so
|
||||
// explicitly-disabled entries (e.g. bundled `ollama` superseded by
|
||||
// `@0xkobold/pi-ollama`) never reach the runtime and trigger command
|
||||
// conflicts.
|
||||
const filteredPaths = paths.filter((entryPath) => {
|
||||
const manifest = readManifestFromEntryPath(entryPath)
|
||||
if (!manifest) return true // no manifest = always load
|
||||
return isExtensionEnabled(registry, manifest.id)
|
||||
})
|
||||
|
||||
// 2. Sort in topological dependency order
|
||||
// Sort in topological dependency order
|
||||
const { sortedPaths, warnings } = sortExtensionPaths(filteredPaths)
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -199,7 +199,16 @@ function shouldExclude(filePath: string, excludes: string[]): boolean {
|
|||
|
||||
function lsFiles(basePath: string): string[] {
|
||||
try {
|
||||
const result = execSync("git ls-files", { cwd: basePath, encoding: "utf-8", timeout: 10000 });
|
||||
// stdio: "pipe" captures stderr into the thrown Error instead of
|
||||
// inheriting it to the parent. Without it, running gsd from a non-repo
|
||||
// cwd (e.g. `$HOME`) leaks a "fatal: not a git repository" line to the
|
||||
// user's terminal before the catch silently falls through to [].
|
||||
const result = execSync("git ls-files", {
|
||||
cwd: basePath,
|
||||
encoding: "utf-8",
|
||||
timeout: 10000,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
return result.split("\n").filter(Boolean);
|
||||
} catch {
|
||||
return [];
|
||||
|
|
|
|||
11
src/resources/extensions/ollama/extension-manifest.json
Normal file
11
src/resources/extensions/ollama/extension-manifest.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"id": "ollama",
|
||||
"name": "Ollama",
|
||||
"version": "1.0.0",
|
||||
"description": "Local Ollama model discovery and /ollama command",
|
||||
"tier": "bundled",
|
||||
"requires": { "platform": ">=2.29.0" },
|
||||
"provides": {
|
||||
"commands": ["ollama"]
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue