@kolisachint/hoocode-agent 0.5.49 → 0.5.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -6
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-frontmatter.d.ts +5 -0
- package/dist/core/agent-frontmatter.d.ts.map +1 -1
- package/dist/core/agent-frontmatter.js +10 -7
- package/dist/core/agent-frontmatter.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +1 -1
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.js +2 -22
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/plugins/authoring.d.ts.map +1 -1
- package/dist/core/extensions/plugins/authoring.js +1 -1
- package/dist/core/extensions/plugins/authoring.js.map +1 -1
- package/dist/core/extensions/plugins/formats/shared.d.ts +1 -1
- package/dist/core/extensions/plugins/formats/shared.d.ts.map +1 -1
- package/dist/core/extensions/plugins/formats/shared.js +1 -1
- package/dist/core/extensions/plugins/formats/shared.js.map +1 -1
- package/dist/core/extensions/plugins/formats/types.d.ts +1 -1
- package/dist/core/extensions/plugins/formats/types.d.ts.map +1 -1
- package/dist/core/extensions/plugins/formats/types.js.map +1 -1
- package/dist/core/extensions/types.d.ts +3 -33
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +0 -9
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/external-tools.d.ts +3 -3
- package/dist/core/external-tools.d.ts.map +1 -1
- package/dist/core/external-tools.js +5 -5
- package/dist/core/external-tools.js.map +1 -1
- package/dist/core/light.d.ts +2 -2
- package/dist/core/light.d.ts.map +1 -1
- package/dist/core/light.js +2 -2
- package/dist/core/light.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +4 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/self-docs.d.ts +1 -1
- package/dist/core/self-docs.d.ts.map +1 -1
- package/dist/core/self-docs.js +1 -1
- package/dist/core/self-docs.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -1
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +9 -26
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +326 -101
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/fd-utils.d.ts +3 -17
- package/dist/core/tools/fd-utils.d.ts.map +1 -1
- package/dist/core/tools/fd-utils.js +3 -38
- package/dist/core/tools/fd-utils.js.map +1 -1
- package/dist/core/tools/index.d.ts +0 -12
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +2 -11
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/native-search.d.ts +0 -15
- package/dist/core/tools/native-search.d.ts.map +1 -1
- package/dist/core/tools/native-search.js +0 -87
- package/dist/core/tools/native-search.js.map +1 -1
- package/dist/core/tools/propose-plugin.d.ts.map +1 -1
- package/dist/core/tools/propose-plugin.js +1 -1
- package/dist/core/tools/propose-plugin.js.map +1 -1
- package/dist/core/tools/read-dedup.d.ts +14 -1
- package/dist/core/tools/read-dedup.d.ts.map +1 -1
- package/dist/core/tools/read-dedup.js +53 -1
- package/dist/core/tools/read-dedup.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +17 -5
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/search.d.ts +3 -2
- package/dist/core/tools/search.d.ts.map +1 -1
- package/dist/core/tools/search.js +7 -6
- package/dist/core/tools/search.js.map +1 -1
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +1 -1
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/core/warm-subagent-pool.d.ts.map +1 -1
- package/dist/core/warm-subagent-pool.js +1 -1
- package/dist/core/warm-subagent-pool.js.map +1 -1
- package/dist/extensions/core/permission-gate.d.ts.map +1 -1
- package/dist/extensions/core/permission-gate.js +23 -5
- package/dist/extensions/core/permission-gate.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/init-templates.generated.d.ts.map +1 -1
- package/dist/init-templates.generated.js +10 -10
- package/dist/init-templates.generated.js.map +1 -1
- package/dist/modes/interactive/components/tool-chain-summary.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-chain-summary.js +1 -1
- package/dist/modes/interactive/components/tool-chain-summary.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +4 -13
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +2 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +1 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/extensions.md +2 -4
- package/docs/modes.md +1 -1
- package/docs/quickstart.md +1 -1
- package/docs/sdk.md +8 -10
- package/docs/settings.md +2 -2
- package/docs/usage.md +3 -3
- package/examples/agents/fork-reviewer.md +1 -1
- package/examples/agents/orchestrator.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +1 -188
- package/examples/extensions/plan-mode/README.md +1 -1
- package/examples/extensions/plan-mode/index.ts +2 -2
- package/examples/extensions/preset.ts +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/truncated-tool.ts +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/05-tools.ts +3 -3
- package/package.json +4 -4
- package/templates/agents/code-review.md +1 -1
- package/templates/agents/explore.md +2 -2
- package/templates/agents/general-purpose.md +1 -1
- package/templates/agents/plan.md +2 -2
- package/templates/agents/security-review.md +1 -1
- package/templates/modes/ask/system.md +1 -1
- package/templates/modes/debug/system.md +1 -1
- package/templates/skills/artifact-design/SKILL.md +1 -1
- package/templates/skills/canvas-design/SKILL.md +1 -1
- package/templates/skills/plugin-authoring/SKILL.md +1 -1
- package/dist/core/tools/find.d.ts +0 -40
- package/dist/core/tools/find.d.ts.map +0 -1
- package/dist/core/tools/find.js +0 -364
- package/dist/core/tools/find.js.map +0 -1
- package/dist/core/tools/grep.d.ts +0 -37
- package/dist/core/tools/grep.d.ts.map +0 -1
- package/dist/core/tools/grep.js +0 -414
- package/dist/core/tools/grep.js.map +0 -1
- package/dist/core/tools/ls.d.ts +0 -49
- package/dist/core/tools/ls.d.ts.map +0 -1
- package/dist/core/tools/ls.js +0 -211
- package/dist/core/tools/ls.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-search.d.ts","sourceRoot":"","sources":["../../../src/core/tools/native-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAQH,KAAK,SAAS,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAUjC,MAAM,WAAW,cAAc;IAC9B,qBAAqB;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,CAAC;CAChB;AAiCD,MAAM,WAAW,WAAW;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,iEAAiE;IACjE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,cAAc,EAAE,CA8DrF;AA0DD,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,SAAS,CAAC;IAChB,2EAA2E;IAC3E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAkB1E;AAED,UAAU,eAAe;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CAC7D;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;CAC3B;AAeD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+DjG;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C","sourcesContent":["/**\n * Native (pure-JS) fallbacks for the `find` and `grep` tools.\n *\n * The tools normally shell out to `fd` / `rg`, which are downloaded on demand\n * (see tools-manager.ts). In restricted environments those binaries may be\n * neither on PATH nor downloadable, and `HOOCODE_NATIVE_SEARCH=1` can also force\n * this path. Rather than failing the tool and asking the model to fall back to\n * `bash`, these functions reproduce the essential behaviour in JS so search\n * degrades automatically:\n *\n * - hierarchical `.gitignore` handling (each `.gitignore` is scoped to its own\n * subtree, matching fd's `--no-require-git` behaviour and issue #3303),\n * - hidden files included (like `fd --hidden` / `rg --hidden`),\n * - `.git` always skipped; `node_modules` skipped for `find` (mirrors the\n * tool's built-in excludes) but left to `.gitignore` for `grep` (like rg).\n *\n * These are best-effort approximations, not byte-for-byte fd/rg parity: globs\n * are matched with `minimatch` and patterns with JS `RegExp`, and only\n * `.gitignore` files are honoured (not `.ignore` or global excludes).\n */\n\nimport { readdirSync, readFileSync, statSync } from \"fs\";\nimport ignore, { type Ignore } from \"ignore\";\nimport { minimatch } from \"minimatch\";\nimport path from \"path\";\nimport { toPosixPath } from \"./fd-utils.js\";\n\ntype EntryType = \"f\" | \"d\" | \"l\";\n\n/** Hard cap on entries enumerated during a single walk, so a pathological tree\n * can never hang the fallback. Well above any tool's own result limit. */\nconst MAX_ENTRIES = 200_000;\n\n/** Files larger than this are skipped by the grep fallback (rg streams; we read\n * whole files, so we guard against loading huge blobs into memory). */\nconst MAX_GREP_FILE_BYTES = 20 * 1024 * 1024;\n\nexport interface CollectedEntry {\n\t/** Absolute path. */\n\tabs: string;\n\t/** POSIX path relative to the walk root. */\n\trel: string;\n\ttype: EntryType;\n}\n\n/** A `.gitignore` matcher scoped to the subtree rooted at `baseDir`. */\ninterface GitignoreMatcher {\n\tbaseDir: string;\n\tig: Ignore;\n}\n\nfunction loadGitignore(dir: string): Ignore | undefined {\n\tlet content: string;\n\ttry {\n\t\tcontent = readFileSync(path.join(dir, \".gitignore\"), \"utf-8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn ignore().add(content);\n}\n\n/**\n * Whether `absPath` is ignored by any applicable `.gitignore`. Each matcher only\n * applies to paths inside its `baseDir`, and the path is tested relative to that\n * base — so `a/.gitignore` scopes to `a/` and its descendants but never `b/`.\n */\nfunction isGitIgnored(absPath: string, isDir: boolean, matchers: GitignoreMatcher[]): boolean {\n\tfor (const m of matchers) {\n\t\tconst rel = path.relative(m.baseDir, absPath);\n\t\tif (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) continue;\n\t\tconst relPosix = toPosixPath(rel) + (isDir ? \"/\" : \"\");\n\t\tif (m.ig.ignores(relPosix)) return true;\n\t}\n\treturn false;\n}\n\nexport interface WalkOptions {\n\t/** Max entry depth relative to root; direct children are depth 1. */\n\tmaxDepth?: number;\n\tsignal?: AbortSignal;\n\t/** Directory names to never descend into. Defaults to `.git`. */\n\talwaysSkipDirs?: Set<string>;\n}\n\n/**\n * Walk `root` depth-first, returning every entry not excluded by `.gitignore`\n * or an always-skip directory. Symlinks are reported but never followed (avoids\n * cycles). Enumeration stops at {@link MAX_ENTRIES}.\n */\nexport function collectEntries(root: string, opts: WalkOptions = {}): CollectedEntry[] {\n\tconst out: CollectedEntry[] = [];\n\tconst alwaysSkip = opts.alwaysSkipDirs ?? new Set([\".git\"]);\n\n\tconst rootMatchers: GitignoreMatcher[] = [];\n\tconst rootIg = loadGitignore(root);\n\tif (rootIg) rootMatchers.push({ baseDir: root, ig: rootIg });\n\n\t// `depth` is the depth of `dir`; its direct children are at depth + 1.\n\tconst stack: Array<{ dir: string; depth: number; matchers: GitignoreMatcher[] }> = [\n\t\t{ dir: root, depth: 0, matchers: rootMatchers },\n\t];\n\n\twhile (stack.length > 0) {\n\t\tif (out.length >= MAX_ENTRIES || opts.signal?.aborted) break;\n\t\tconst { dir, depth, matchers } = stack.pop()!;\n\n\t\tlet dirents: import(\"fs\").Dirent[];\n\t\ttry {\n\t\t\tdirents = readdirSync(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\tcontinue; // unreadable directory — skip rather than abort the whole walk\n\t\t}\n\n\t\tconst entryDepth = depth + 1;\n\t\tfor (const dirent of dirents) {\n\t\t\tif (out.length >= MAX_ENTRIES) break;\n\n\t\t\tconst name = dirent.name;\n\t\t\tconst abs = path.join(dir, name);\n\n\t\t\tlet type: EntryType;\n\t\t\tlet isDir = false;\n\t\t\tif (dirent.isSymbolicLink()) {\n\t\t\t\ttype = \"l\";\n\t\t\t} else if (dirent.isDirectory()) {\n\t\t\t\ttype = \"d\";\n\t\t\t\tisDir = true;\n\t\t\t} else if (dirent.isFile()) {\n\t\t\t\ttype = \"f\";\n\t\t\t} else {\n\t\t\t\tcontinue; // sockets, fifos, block devices, …\n\t\t\t}\n\n\t\t\tif (isDir && alwaysSkip.has(name)) continue;\n\t\t\tif (isGitIgnored(abs, isDir, matchers)) continue;\n\n\t\t\tif (opts.maxDepth === undefined || entryDepth <= opts.maxDepth) {\n\t\t\t\tout.push({ abs, rel: toPosixPath(path.relative(root, abs)), type });\n\t\t\t}\n\n\t\t\t// Descend only into real directories, and only if their children can\n\t\t\t// still be within the depth budget.\n\t\t\tif (isDir && (opts.maxDepth === undefined || entryDepth < opts.maxDepth)) {\n\t\t\t\tconst childIg = loadGitignore(abs);\n\t\t\t\tconst nextMatchers = childIg ? [...matchers, { baseDir: abs, ig: childIg }] : matchers;\n\t\t\t\tstack.push({ dir: abs, depth: entryDepth, matchers: nextMatchers });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out;\n}\n\n/**\n * Reject globs fd's parser would reject. minimatch silently treats an unclosed\n * `[` or `{` as literal text, which made the fallback resolve \"no results\"\n * where the fd path errors — same call, different outcome. Error messages\n * mirror fd/globset so callers (and tests) can match either path.\n */\nfunction validateGlob(pattern: string): void {\n\tlet inClass = false;\n\tlet braceDepth = 0;\n\tfor (let i = 0; i < pattern.length; i++) {\n\t\tconst c = pattern[i];\n\t\tif (c === \"\\\\\") {\n\t\t\ti++; // skip escaped char\n\t\t} else if (inClass) {\n\t\t\tif (c === \"]\") inClass = false;\n\t\t} else if (c === \"[\") {\n\t\t\tinClass = true;\n\t\t} else if (c === \"{\") {\n\t\t\tbraceDepth++;\n\t\t} else if (c === \"}\") {\n\t\t\tif (braceDepth > 0) braceDepth--;\n\t\t}\n\t}\n\tif (inClass) {\n\t\tthrow new Error(`error parsing glob '${pattern}': unclosed character class; missing ']'`);\n\t}\n\tif (braceDepth > 0) {\n\t\tthrow new Error(`error parsing glob '${pattern}': unclosed alternate group; missing '}'`);\n\t}\n}\n\n/** fd smart-case: a pattern without uppercase letters matches case-insensitively. */\nfunction isSmartCaseInsensitive(pattern: string): boolean {\n\treturn !/[A-Z]/.test(pattern);\n}\n\n/**\n * Match a `find` glob against a POSIX relative path, mirroring fd's semantics:\n * a slashless pattern matches the basename at any depth; a pattern containing a\n * slash matches the full path and is anchored anywhere in the tree. Case\n * sensitivity is smart-case, like fd.\n */\nfunction matchesFindPattern(relPosix: string, pattern: string): boolean {\n\tconst nocase = isSmartCaseInsensitive(pattern);\n\tif (pattern.includes(\"/\")) {\n\t\tlet p = pattern;\n\t\tif (p.startsWith(\"/\")) {\n\t\t\tp = p.slice(1); // leading slash anchors to root; rel paths have no leading slash\n\t\t} else if (!p.startsWith(\"**/\") && p !== \"**\") {\n\t\t\tp = `**/${p}`;\n\t\t}\n\t\treturn minimatch(relPosix, p, { dot: true, nocase });\n\t}\n\treturn minimatch(relPosix, pattern, { dot: true, matchBase: true, nocase });\n}\n\nexport interface NativeFindOptions {\n\tpatterns: string[];\n\ttype: EntryType;\n\t/** Extra exclusion globs (already includes node_modules/.git for find). */\n\texcludeGlobs: string[];\n\tmaxDepth?: number;\n\talwaysSkipDirs?: Set<string>;\n\tsignal?: AbortSignal;\n}\n\n/**\n * Native replacement for the fd-backed search. Returns POSIX paths relative to\n * `root`, with a trailing slash on directories (like fd), unsorted/undeduped —\n * the caller applies its own dedupe/sort/limit.\n */\nexport function nativeFind(root: string, opts: NativeFindOptions): string[] {\n\tfor (const pattern of opts.patterns) validateGlob(pattern);\n\tfor (const glob of opts.excludeGlobs) validateGlob(glob);\n\n\tconst entries = collectEntries(root, {\n\t\tmaxDepth: opts.maxDepth,\n\t\tsignal: opts.signal,\n\t\talwaysSkipDirs: opts.alwaysSkipDirs,\n\t});\n\n\tconst results: string[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type !== opts.type) continue;\n\t\tif (opts.excludeGlobs.some((g) => minimatch(entry.rel, g, { dot: true }))) continue;\n\t\tif (!opts.patterns.some((pat) => matchesFindPattern(entry.rel, pat))) continue;\n\t\tresults.push(entry.type === \"d\" ? `${entry.rel}/` : entry.rel);\n\t}\n\treturn results;\n}\n\ninterface NativeGrepMatch {\n\tfilePath: string;\n\tlineNumber: number;\n\tlineText: string;\n}\n\nexport interface NativeGrepOptions {\n\tpattern: string;\n\t/** True when `root` is a directory; false when it is a single file. */\n\tisDirectory: boolean;\n\tignoreCase?: boolean;\n\tliteral?: boolean;\n\t/** Optional glob filter applied to file paths (like rg --glob). */\n\tglob?: string;\n\t/** Stop after this many matches. */\n\tlimit: number;\n\tsignal?: AbortSignal;\n\treadFile: (absolutePath: string) => Promise<string> | string;\n}\n\nexport interface NativeGrepResult {\n\tmatches: NativeGrepMatch[];\n\tmatchLimitReached: boolean;\n}\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Cheap binary-file heuristic: a NUL byte in the first chunk. rg skips these. */\nfunction looksBinary(content: string): boolean {\n\tconst sampleLength = Math.min(content.length, 8192);\n\tfor (let i = 0; i < sampleLength; i++) {\n\t\tif (content.charCodeAt(i) === 0) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Native replacement for the rg-backed content search. Collects up to `limit`\n * matches as `{ filePath, lineNumber, lineText }`, the same shape the tool's\n * formatter already consumes from rg's JSON output.\n *\n * Throws an Error tagged `invalidRegex` when a non-literal pattern is not a\n * valid JS regex, so the caller can surface the same \"pass literal: true\" hint.\n */\nexport async function nativeGrep(root: string, opts: NativeGrepOptions): Promise<NativeGrepResult> {\n\tlet regex: RegExp;\n\tconst flags = opts.ignoreCase ? \"i\" : \"\";\n\tif (opts.literal) {\n\t\tregex = new RegExp(escapeRegExp(opts.pattern), flags);\n\t} else {\n\t\ttry {\n\t\t\tregex = new RegExp(opts.pattern, flags);\n\t\t} catch (e) {\n\t\t\tconst error = new Error(e instanceof Error ? e.message : String(e)) as Error & { invalidRegex?: boolean };\n\t\t\terror.invalidRegex = true;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tlet files: string[];\n\tif (!opts.isDirectory) {\n\t\tfiles = [root];\n\t} else {\n\t\tconst entries = collectEntries(root, { signal: opts.signal });\n\t\tfiles = entries\n\t\t\t.filter((e) => e.type === \"f\")\n\t\t\t.filter((e) => {\n\t\t\t\tif (!opts.glob) return true;\n\t\t\t\treturn minimatch(e.rel, opts.glob, { dot: true, matchBase: !opts.glob.includes(\"/\") });\n\t\t\t})\n\t\t\t.map((e) => e.abs);\n\t}\n\n\tconst matches: NativeGrepMatch[] = [];\n\tlet matchLimitReached = false;\n\n\tfor (const filePath of files) {\n\t\tif (opts.signal?.aborted || matches.length >= opts.limit) break;\n\n\t\ttry {\n\t\t\tif (statSync(filePath).size > MAX_GREP_FILE_BYTES) continue;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet content: string;\n\t\ttry {\n\t\t\tcontent = await opts.readFile(filePath);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (looksBinary(content)) continue;\n\n\t\tconst lines = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").split(\"\\n\");\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t// New RegExp per file with no /g flag → test() is stateless across lines.\n\t\t\tif (regex.test(lines[i])) {\n\t\t\t\tmatches.push({ filePath, lineNumber: i + 1, lineText: lines[i] });\n\t\t\t\tif (matches.length >= opts.limit) {\n\t\t\t\t\tmatchLimitReached = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { matches, matchLimitReached };\n}\n\n/** Whether the native search path is forced regardless of fd/rg availability. */\nexport function isNativeSearchForced(): boolean {\n\treturn process.env.HOOCODE_NATIVE_SEARCH === \"1\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"native-search.d.ts","sourceRoot":"","sources":["../../../src/core/tools/native-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAQH,KAAK,SAAS,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAUjC,MAAM,WAAW,cAAc;IAC9B,qBAAqB;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,CAAC;CAChB;AAiCD,MAAM,WAAW,WAAW;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,iEAAiE;IACjE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,cAAc,EAAE,CA8DrF;AAED,UAAU,eAAe;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CAC7D;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;CAC3B;AAeD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+DjG;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C","sourcesContent":["/**\n * Native (pure-JS) fallbacks for the `find` and `grep` tools.\n *\n * The tools normally shell out to `fd` / `rg`, which are downloaded on demand\n * (see tools-manager.ts). In restricted environments those binaries may be\n * neither on PATH nor downloadable, and `HOOCODE_NATIVE_SEARCH=1` can also force\n * this path. Rather than failing the tool and asking the model to fall back to\n * `bash`, these functions reproduce the essential behaviour in JS so search\n * degrades automatically:\n *\n * - hierarchical `.gitignore` handling (each `.gitignore` is scoped to its own\n * subtree, matching fd's `--no-require-git` behaviour and issue #3303),\n * - hidden files included (like `fd --hidden` / `rg --hidden`),\n * - `.git` always skipped; `node_modules` skipped for `find` (mirrors the\n * tool's built-in excludes) but left to `.gitignore` for `grep` (like rg).\n *\n * These are best-effort approximations, not byte-for-byte fd/rg parity: globs\n * are matched with `minimatch` and patterns with JS `RegExp`, and only\n * `.gitignore` files are honoured (not `.ignore` or global excludes).\n */\n\nimport { readdirSync, readFileSync, statSync } from \"fs\";\nimport ignore, { type Ignore } from \"ignore\";\nimport { minimatch } from \"minimatch\";\nimport path from \"path\";\nimport { toPosixPath } from \"./fd-utils.js\";\n\ntype EntryType = \"f\" | \"d\" | \"l\";\n\n/** Hard cap on entries enumerated during a single walk, so a pathological tree\n * can never hang the fallback. Well above any tool's own result limit. */\nconst MAX_ENTRIES = 200_000;\n\n/** Files larger than this are skipped by the grep fallback (rg streams; we read\n * whole files, so we guard against loading huge blobs into memory). */\nconst MAX_GREP_FILE_BYTES = 20 * 1024 * 1024;\n\nexport interface CollectedEntry {\n\t/** Absolute path. */\n\tabs: string;\n\t/** POSIX path relative to the walk root. */\n\trel: string;\n\ttype: EntryType;\n}\n\n/** A `.gitignore` matcher scoped to the subtree rooted at `baseDir`. */\ninterface GitignoreMatcher {\n\tbaseDir: string;\n\tig: Ignore;\n}\n\nfunction loadGitignore(dir: string): Ignore | undefined {\n\tlet content: string;\n\ttry {\n\t\tcontent = readFileSync(path.join(dir, \".gitignore\"), \"utf-8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn ignore().add(content);\n}\n\n/**\n * Whether `absPath` is ignored by any applicable `.gitignore`. Each matcher only\n * applies to paths inside its `baseDir`, and the path is tested relative to that\n * base — so `a/.gitignore` scopes to `a/` and its descendants but never `b/`.\n */\nfunction isGitIgnored(absPath: string, isDir: boolean, matchers: GitignoreMatcher[]): boolean {\n\tfor (const m of matchers) {\n\t\tconst rel = path.relative(m.baseDir, absPath);\n\t\tif (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) continue;\n\t\tconst relPosix = toPosixPath(rel) + (isDir ? \"/\" : \"\");\n\t\tif (m.ig.ignores(relPosix)) return true;\n\t}\n\treturn false;\n}\n\nexport interface WalkOptions {\n\t/** Max entry depth relative to root; direct children are depth 1. */\n\tmaxDepth?: number;\n\tsignal?: AbortSignal;\n\t/** Directory names to never descend into. Defaults to `.git`. */\n\talwaysSkipDirs?: Set<string>;\n}\n\n/**\n * Walk `root` depth-first, returning every entry not excluded by `.gitignore`\n * or an always-skip directory. Symlinks are reported but never followed (avoids\n * cycles). Enumeration stops at {@link MAX_ENTRIES}.\n */\nexport function collectEntries(root: string, opts: WalkOptions = {}): CollectedEntry[] {\n\tconst out: CollectedEntry[] = [];\n\tconst alwaysSkip = opts.alwaysSkipDirs ?? new Set([\".git\"]);\n\n\tconst rootMatchers: GitignoreMatcher[] = [];\n\tconst rootIg = loadGitignore(root);\n\tif (rootIg) rootMatchers.push({ baseDir: root, ig: rootIg });\n\n\t// `depth` is the depth of `dir`; its direct children are at depth + 1.\n\tconst stack: Array<{ dir: string; depth: number; matchers: GitignoreMatcher[] }> = [\n\t\t{ dir: root, depth: 0, matchers: rootMatchers },\n\t];\n\n\twhile (stack.length > 0) {\n\t\tif (out.length >= MAX_ENTRIES || opts.signal?.aborted) break;\n\t\tconst { dir, depth, matchers } = stack.pop()!;\n\n\t\tlet dirents: import(\"fs\").Dirent[];\n\t\ttry {\n\t\t\tdirents = readdirSync(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\tcontinue; // unreadable directory — skip rather than abort the whole walk\n\t\t}\n\n\t\tconst entryDepth = depth + 1;\n\t\tfor (const dirent of dirents) {\n\t\t\tif (out.length >= MAX_ENTRIES) break;\n\n\t\t\tconst name = dirent.name;\n\t\t\tconst abs = path.join(dir, name);\n\n\t\t\tlet type: EntryType;\n\t\t\tlet isDir = false;\n\t\t\tif (dirent.isSymbolicLink()) {\n\t\t\t\ttype = \"l\";\n\t\t\t} else if (dirent.isDirectory()) {\n\t\t\t\ttype = \"d\";\n\t\t\t\tisDir = true;\n\t\t\t} else if (dirent.isFile()) {\n\t\t\t\ttype = \"f\";\n\t\t\t} else {\n\t\t\t\tcontinue; // sockets, fifos, block devices, …\n\t\t\t}\n\n\t\t\tif (isDir && alwaysSkip.has(name)) continue;\n\t\t\tif (isGitIgnored(abs, isDir, matchers)) continue;\n\n\t\t\tif (opts.maxDepth === undefined || entryDepth <= opts.maxDepth) {\n\t\t\t\tout.push({ abs, rel: toPosixPath(path.relative(root, abs)), type });\n\t\t\t}\n\n\t\t\t// Descend only into real directories, and only if their children can\n\t\t\t// still be within the depth budget.\n\t\t\tif (isDir && (opts.maxDepth === undefined || entryDepth < opts.maxDepth)) {\n\t\t\t\tconst childIg = loadGitignore(abs);\n\t\t\t\tconst nextMatchers = childIg ? [...matchers, { baseDir: abs, ig: childIg }] : matchers;\n\t\t\t\tstack.push({ dir: abs, depth: entryDepth, matchers: nextMatchers });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out;\n}\n\ninterface NativeGrepMatch {\n\tfilePath: string;\n\tlineNumber: number;\n\tlineText: string;\n}\n\nexport interface NativeGrepOptions {\n\tpattern: string;\n\t/** True when `root` is a directory; false when it is a single file. */\n\tisDirectory: boolean;\n\tignoreCase?: boolean;\n\tliteral?: boolean;\n\t/** Optional glob filter applied to file paths (like rg --glob). */\n\tglob?: string;\n\t/** Stop after this many matches. */\n\tlimit: number;\n\tsignal?: AbortSignal;\n\treadFile: (absolutePath: string) => Promise<string> | string;\n}\n\nexport interface NativeGrepResult {\n\tmatches: NativeGrepMatch[];\n\tmatchLimitReached: boolean;\n}\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Cheap binary-file heuristic: a NUL byte in the first chunk. rg skips these. */\nfunction looksBinary(content: string): boolean {\n\tconst sampleLength = Math.min(content.length, 8192);\n\tfor (let i = 0; i < sampleLength; i++) {\n\t\tif (content.charCodeAt(i) === 0) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Native replacement for the rg-backed content search. Collects up to `limit`\n * matches as `{ filePath, lineNumber, lineText }`, the same shape the tool's\n * formatter already consumes from rg's JSON output.\n *\n * Throws an Error tagged `invalidRegex` when a non-literal pattern is not a\n * valid JS regex, so the caller can surface the same \"pass literal: true\" hint.\n */\nexport async function nativeGrep(root: string, opts: NativeGrepOptions): Promise<NativeGrepResult> {\n\tlet regex: RegExp;\n\tconst flags = opts.ignoreCase ? \"i\" : \"\";\n\tif (opts.literal) {\n\t\tregex = new RegExp(escapeRegExp(opts.pattern), flags);\n\t} else {\n\t\ttry {\n\t\t\tregex = new RegExp(opts.pattern, flags);\n\t\t} catch (e) {\n\t\t\tconst error = new Error(e instanceof Error ? e.message : String(e)) as Error & { invalidRegex?: boolean };\n\t\t\terror.invalidRegex = true;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tlet files: string[];\n\tif (!opts.isDirectory) {\n\t\tfiles = [root];\n\t} else {\n\t\tconst entries = collectEntries(root, { signal: opts.signal });\n\t\tfiles = entries\n\t\t\t.filter((e) => e.type === \"f\")\n\t\t\t.filter((e) => {\n\t\t\t\tif (!opts.glob) return true;\n\t\t\t\treturn minimatch(e.rel, opts.glob, { dot: true, matchBase: !opts.glob.includes(\"/\") });\n\t\t\t})\n\t\t\t.map((e) => e.abs);\n\t}\n\n\tconst matches: NativeGrepMatch[] = [];\n\tlet matchLimitReached = false;\n\n\tfor (const filePath of files) {\n\t\tif (opts.signal?.aborted || matches.length >= opts.limit) break;\n\n\t\ttry {\n\t\t\tif (statSync(filePath).size > MAX_GREP_FILE_BYTES) continue;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet content: string;\n\t\ttry {\n\t\t\tcontent = await opts.readFile(filePath);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (looksBinary(content)) continue;\n\n\t\tconst lines = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").split(\"\\n\");\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t// New RegExp per file with no /g flag → test() is stateless across lines.\n\t\t\tif (regex.test(lines[i])) {\n\t\t\t\tmatches.push({ filePath, lineNumber: i + 1, lineText: lines[i] });\n\t\t\t\tif (matches.length >= opts.limit) {\n\t\t\t\t\tmatchLimitReached = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { matches, matchLimitReached };\n}\n\n/** Whether the native search path is forced regardless of fd/rg availability. */\nexport function isNativeSearchForced(): boolean {\n\treturn process.env.HOOCODE_NATIVE_SEARCH === \"1\";\n}\n"]}
|
|
@@ -121,93 +121,6 @@ export function collectEntries(root, opts = {}) {
|
|
|
121
121
|
}
|
|
122
122
|
return out;
|
|
123
123
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Reject globs fd's parser would reject. minimatch silently treats an unclosed
|
|
126
|
-
* `[` or `{` as literal text, which made the fallback resolve "no results"
|
|
127
|
-
* where the fd path errors — same call, different outcome. Error messages
|
|
128
|
-
* mirror fd/globset so callers (and tests) can match either path.
|
|
129
|
-
*/
|
|
130
|
-
function validateGlob(pattern) {
|
|
131
|
-
let inClass = false;
|
|
132
|
-
let braceDepth = 0;
|
|
133
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
134
|
-
const c = pattern[i];
|
|
135
|
-
if (c === "\\") {
|
|
136
|
-
i++; // skip escaped char
|
|
137
|
-
}
|
|
138
|
-
else if (inClass) {
|
|
139
|
-
if (c === "]")
|
|
140
|
-
inClass = false;
|
|
141
|
-
}
|
|
142
|
-
else if (c === "[") {
|
|
143
|
-
inClass = true;
|
|
144
|
-
}
|
|
145
|
-
else if (c === "{") {
|
|
146
|
-
braceDepth++;
|
|
147
|
-
}
|
|
148
|
-
else if (c === "}") {
|
|
149
|
-
if (braceDepth > 0)
|
|
150
|
-
braceDepth--;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (inClass) {
|
|
154
|
-
throw new Error(`error parsing glob '${pattern}': unclosed character class; missing ']'`);
|
|
155
|
-
}
|
|
156
|
-
if (braceDepth > 0) {
|
|
157
|
-
throw new Error(`error parsing glob '${pattern}': unclosed alternate group; missing '}'`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
/** fd smart-case: a pattern without uppercase letters matches case-insensitively. */
|
|
161
|
-
function isSmartCaseInsensitive(pattern) {
|
|
162
|
-
return !/[A-Z]/.test(pattern);
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Match a `find` glob against a POSIX relative path, mirroring fd's semantics:
|
|
166
|
-
* a slashless pattern matches the basename at any depth; a pattern containing a
|
|
167
|
-
* slash matches the full path and is anchored anywhere in the tree. Case
|
|
168
|
-
* sensitivity is smart-case, like fd.
|
|
169
|
-
*/
|
|
170
|
-
function matchesFindPattern(relPosix, pattern) {
|
|
171
|
-
const nocase = isSmartCaseInsensitive(pattern);
|
|
172
|
-
if (pattern.includes("/")) {
|
|
173
|
-
let p = pattern;
|
|
174
|
-
if (p.startsWith("/")) {
|
|
175
|
-
p = p.slice(1); // leading slash anchors to root; rel paths have no leading slash
|
|
176
|
-
}
|
|
177
|
-
else if (!p.startsWith("**/") && p !== "**") {
|
|
178
|
-
p = `**/${p}`;
|
|
179
|
-
}
|
|
180
|
-
return minimatch(relPosix, p, { dot: true, nocase });
|
|
181
|
-
}
|
|
182
|
-
return minimatch(relPosix, pattern, { dot: true, matchBase: true, nocase });
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Native replacement for the fd-backed search. Returns POSIX paths relative to
|
|
186
|
-
* `root`, with a trailing slash on directories (like fd), unsorted/undeduped —
|
|
187
|
-
* the caller applies its own dedupe/sort/limit.
|
|
188
|
-
*/
|
|
189
|
-
export function nativeFind(root, opts) {
|
|
190
|
-
for (const pattern of opts.patterns)
|
|
191
|
-
validateGlob(pattern);
|
|
192
|
-
for (const glob of opts.excludeGlobs)
|
|
193
|
-
validateGlob(glob);
|
|
194
|
-
const entries = collectEntries(root, {
|
|
195
|
-
maxDepth: opts.maxDepth,
|
|
196
|
-
signal: opts.signal,
|
|
197
|
-
alwaysSkipDirs: opts.alwaysSkipDirs,
|
|
198
|
-
});
|
|
199
|
-
const results = [];
|
|
200
|
-
for (const entry of entries) {
|
|
201
|
-
if (entry.type !== opts.type)
|
|
202
|
-
continue;
|
|
203
|
-
if (opts.excludeGlobs.some((g) => minimatch(entry.rel, g, { dot: true })))
|
|
204
|
-
continue;
|
|
205
|
-
if (!opts.patterns.some((pat) => matchesFindPattern(entry.rel, pat)))
|
|
206
|
-
continue;
|
|
207
|
-
results.push(entry.type === "d" ? `${entry.rel}/` : entry.rel);
|
|
208
|
-
}
|
|
209
|
-
return results;
|
|
210
|
-
}
|
|
211
124
|
function escapeRegExp(value) {
|
|
212
125
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
213
126
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-search.js","sourceRoot":"","sources":["../../../src/core/tools/native-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACzD,OAAO,MAAuB,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAI5C;2EAC2E;AAC3E,MAAM,WAAW,GAAG,OAAO,CAAC;AAE5B;wEACwE;AACxE,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAgB7C,SAAS,aAAa,CAAC,GAAW,EAAsB;IACvD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACJ,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAAA,CAC7B;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,OAAe,EAAE,KAAc,EAAE,QAA4B,EAAW;IAC7F,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9C,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACzE,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;IACzC,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAUD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAI,GAAgB,EAAE,EAAoB;IACtF,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAE5D,MAAM,YAAY,GAAuB,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,MAAM;QAAE,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAE7D,uEAAuE;IACvE,MAAM,KAAK,GAAwE;QAClF,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE;KAC/C,CAAC;IAEF,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,IAAI,GAAG,CAAC,MAAM,IAAI,WAAW,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;YAAE,MAAM;QAC7D,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;QAE9C,IAAI,OAA8B,CAAC;QACnC,IAAI,CAAC;YACJ,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACR,SAAS,CAAC,iEAA+D;QAC1E,CAAC;QAED,MAAM,UAAU,GAAG,KAAK,GAAG,CAAC,CAAC;QAC7B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC9B,IAAI,GAAG,CAAC,MAAM,IAAI,WAAW;gBAAE,MAAM;YAErC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAEjC,IAAI,IAAe,CAAC;YACpB,IAAI,KAAK,GAAG,KAAK,CAAC;YAClB,IAAI,MAAM,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC7B,IAAI,GAAG,GAAG,CAAC;YACZ,CAAC;iBAAM,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;gBACjC,IAAI,GAAG,GAAG,CAAC;gBACX,KAAK,GAAG,IAAI,CAAC;YACd,CAAC;iBAAM,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC5B,IAAI,GAAG,GAAG,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACP,SAAS,CAAC,qCAAmC;YAC9C,CAAC;YAED,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YAC5C,IAAI,YAAY,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC;gBAAE,SAAS;YAEjD,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,UAAU,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAChE,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;YACrE,CAAC;YAED,qEAAqE;YACrE,oCAAoC;YACpC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1E,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;gBACnC,MAAM,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;gBACvF,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC;YACrE,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,GAAG,CAAC;AAAA,CACX;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,OAAe,EAAQ;IAC5C,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAChB,CAAC,EAAE,CAAC,CAAC,oBAAoB;QAC1B,CAAC;aAAM,IAAI,OAAO,EAAE,CAAC;YACpB,IAAI,CAAC,KAAK,GAAG;gBAAE,OAAO,GAAG,KAAK,CAAC;QAChC,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACtB,OAAO,GAAG,IAAI,CAAC;QAChB,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACtB,UAAU,EAAE,CAAC;QACd,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACtB,IAAI,UAAU,GAAG,CAAC;gBAAE,UAAU,EAAE,CAAC;QAClC,CAAC;IACF,CAAC;IACD,IAAI,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,uBAAuB,OAAO,0CAA0C,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,uBAAuB,OAAO,0CAA0C,CAAC,CAAC;IAC3F,CAAC;AAAA,CACD;AAED,qFAAqF;AACrF,SAAS,sBAAsB,CAAC,OAAe,EAAW;IACzD,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAAA,CAC9B;AAED;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,QAAgB,EAAE,OAAe,EAAW;IACvE,MAAM,MAAM,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,OAAO,CAAC;QAChB,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,iEAAiE;QAClF,CAAC;aAAM,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/C,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC;QACf,CAAC;QACD,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAAA,CAC5E;AAYD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,IAAuB,EAAY;IAC3E,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ;QAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IAC3D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,YAAY;QAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAEzD,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,EAAE;QACpC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,cAAc,EAAE,IAAI,CAAC,cAAc;KACnC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;YAAE,SAAS;QACvC,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAAE,SAAS;QACpF,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAAE,SAAS;QAC/E,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,OAAO,CAAC;AAAA,CACf;AA2BD,SAAS,YAAY,CAAC,KAAa,EAAU;IAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAAA,CACpD;AAED,kFAAkF;AAClF,SAAS,WAAW,CAAC,OAAe,EAAW;IAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;IAC9C,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,IAAuB,EAA6B;IAClG,IAAI,KAAa,CAAC;IAClB,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAClB,KAAK,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;IACvD,CAAC;SAAM,CAAC;QACP,IAAI,CAAC;YACJ,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAuC,CAAC;YAC1G,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;YAC1B,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;IAED,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACvB,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;SAAM,CAAC;QACP,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9D,KAAK,GAAG,OAAO;aACb,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC;aAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YACd,IAAI,CAAC,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YAC5B,OAAO,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAAA,CACvF,CAAC;aACD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAE9B,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC9B,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK;YAAE,MAAM;QAEhE,IAAI,CAAC;YACJ,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,GAAG,mBAAmB;gBAAE,SAAS;QAC7D,CAAC;QAAC,MAAM,CAAC;YACR,SAAS;QACV,CAAC;QAED,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACJ,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACR,SAAS;QACV,CAAC;QACD,IAAI,WAAW,CAAC,OAAO,CAAC;YAAE,SAAS;QAEnC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,4EAA0E;YAC1E,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAClE,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;oBAClC,iBAAiB,GAAG,IAAI,CAAC;oBACzB,MAAM;gBACP,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAAA,CACtC;AAED,iFAAiF;AACjF,MAAM,UAAU,oBAAoB,GAAY;IAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG,CAAC;AAAA,CACjD","sourcesContent":["/**\n * Native (pure-JS) fallbacks for the `find` and `grep` tools.\n *\n * The tools normally shell out to `fd` / `rg`, which are downloaded on demand\n * (see tools-manager.ts). In restricted environments those binaries may be\n * neither on PATH nor downloadable, and `HOOCODE_NATIVE_SEARCH=1` can also force\n * this path. Rather than failing the tool and asking the model to fall back to\n * `bash`, these functions reproduce the essential behaviour in JS so search\n * degrades automatically:\n *\n * - hierarchical `.gitignore` handling (each `.gitignore` is scoped to its own\n * subtree, matching fd's `--no-require-git` behaviour and issue #3303),\n * - hidden files included (like `fd --hidden` / `rg --hidden`),\n * - `.git` always skipped; `node_modules` skipped for `find` (mirrors the\n * tool's built-in excludes) but left to `.gitignore` for `grep` (like rg).\n *\n * These are best-effort approximations, not byte-for-byte fd/rg parity: globs\n * are matched with `minimatch` and patterns with JS `RegExp`, and only\n * `.gitignore` files are honoured (not `.ignore` or global excludes).\n */\n\nimport { readdirSync, readFileSync, statSync } from \"fs\";\nimport ignore, { type Ignore } from \"ignore\";\nimport { minimatch } from \"minimatch\";\nimport path from \"path\";\nimport { toPosixPath } from \"./fd-utils.js\";\n\ntype EntryType = \"f\" | \"d\" | \"l\";\n\n/** Hard cap on entries enumerated during a single walk, so a pathological tree\n * can never hang the fallback. Well above any tool's own result limit. */\nconst MAX_ENTRIES = 200_000;\n\n/** Files larger than this are skipped by the grep fallback (rg streams; we read\n * whole files, so we guard against loading huge blobs into memory). */\nconst MAX_GREP_FILE_BYTES = 20 * 1024 * 1024;\n\nexport interface CollectedEntry {\n\t/** Absolute path. */\n\tabs: string;\n\t/** POSIX path relative to the walk root. */\n\trel: string;\n\ttype: EntryType;\n}\n\n/** A `.gitignore` matcher scoped to the subtree rooted at `baseDir`. */\ninterface GitignoreMatcher {\n\tbaseDir: string;\n\tig: Ignore;\n}\n\nfunction loadGitignore(dir: string): Ignore | undefined {\n\tlet content: string;\n\ttry {\n\t\tcontent = readFileSync(path.join(dir, \".gitignore\"), \"utf-8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn ignore().add(content);\n}\n\n/**\n * Whether `absPath` is ignored by any applicable `.gitignore`. Each matcher only\n * applies to paths inside its `baseDir`, and the path is tested relative to that\n * base — so `a/.gitignore` scopes to `a/` and its descendants but never `b/`.\n */\nfunction isGitIgnored(absPath: string, isDir: boolean, matchers: GitignoreMatcher[]): boolean {\n\tfor (const m of matchers) {\n\t\tconst rel = path.relative(m.baseDir, absPath);\n\t\tif (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) continue;\n\t\tconst relPosix = toPosixPath(rel) + (isDir ? \"/\" : \"\");\n\t\tif (m.ig.ignores(relPosix)) return true;\n\t}\n\treturn false;\n}\n\nexport interface WalkOptions {\n\t/** Max entry depth relative to root; direct children are depth 1. */\n\tmaxDepth?: number;\n\tsignal?: AbortSignal;\n\t/** Directory names to never descend into. Defaults to `.git`. */\n\talwaysSkipDirs?: Set<string>;\n}\n\n/**\n * Walk `root` depth-first, returning every entry not excluded by `.gitignore`\n * or an always-skip directory. Symlinks are reported but never followed (avoids\n * cycles). Enumeration stops at {@link MAX_ENTRIES}.\n */\nexport function collectEntries(root: string, opts: WalkOptions = {}): CollectedEntry[] {\n\tconst out: CollectedEntry[] = [];\n\tconst alwaysSkip = opts.alwaysSkipDirs ?? new Set([\".git\"]);\n\n\tconst rootMatchers: GitignoreMatcher[] = [];\n\tconst rootIg = loadGitignore(root);\n\tif (rootIg) rootMatchers.push({ baseDir: root, ig: rootIg });\n\n\t// `depth` is the depth of `dir`; its direct children are at depth + 1.\n\tconst stack: Array<{ dir: string; depth: number; matchers: GitignoreMatcher[] }> = [\n\t\t{ dir: root, depth: 0, matchers: rootMatchers },\n\t];\n\n\twhile (stack.length > 0) {\n\t\tif (out.length >= MAX_ENTRIES || opts.signal?.aborted) break;\n\t\tconst { dir, depth, matchers } = stack.pop()!;\n\n\t\tlet dirents: import(\"fs\").Dirent[];\n\t\ttry {\n\t\t\tdirents = readdirSync(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\tcontinue; // unreadable directory — skip rather than abort the whole walk\n\t\t}\n\n\t\tconst entryDepth = depth + 1;\n\t\tfor (const dirent of dirents) {\n\t\t\tif (out.length >= MAX_ENTRIES) break;\n\n\t\t\tconst name = dirent.name;\n\t\t\tconst abs = path.join(dir, name);\n\n\t\t\tlet type: EntryType;\n\t\t\tlet isDir = false;\n\t\t\tif (dirent.isSymbolicLink()) {\n\t\t\t\ttype = \"l\";\n\t\t\t} else if (dirent.isDirectory()) {\n\t\t\t\ttype = \"d\";\n\t\t\t\tisDir = true;\n\t\t\t} else if (dirent.isFile()) {\n\t\t\t\ttype = \"f\";\n\t\t\t} else {\n\t\t\t\tcontinue; // sockets, fifos, block devices, …\n\t\t\t}\n\n\t\t\tif (isDir && alwaysSkip.has(name)) continue;\n\t\t\tif (isGitIgnored(abs, isDir, matchers)) continue;\n\n\t\t\tif (opts.maxDepth === undefined || entryDepth <= opts.maxDepth) {\n\t\t\t\tout.push({ abs, rel: toPosixPath(path.relative(root, abs)), type });\n\t\t\t}\n\n\t\t\t// Descend only into real directories, and only if their children can\n\t\t\t// still be within the depth budget.\n\t\t\tif (isDir && (opts.maxDepth === undefined || entryDepth < opts.maxDepth)) {\n\t\t\t\tconst childIg = loadGitignore(abs);\n\t\t\t\tconst nextMatchers = childIg ? [...matchers, { baseDir: abs, ig: childIg }] : matchers;\n\t\t\t\tstack.push({ dir: abs, depth: entryDepth, matchers: nextMatchers });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out;\n}\n\n/**\n * Reject globs fd's parser would reject. minimatch silently treats an unclosed\n * `[` or `{` as literal text, which made the fallback resolve \"no results\"\n * where the fd path errors — same call, different outcome. Error messages\n * mirror fd/globset so callers (and tests) can match either path.\n */\nfunction validateGlob(pattern: string): void {\n\tlet inClass = false;\n\tlet braceDepth = 0;\n\tfor (let i = 0; i < pattern.length; i++) {\n\t\tconst c = pattern[i];\n\t\tif (c === \"\\\\\") {\n\t\t\ti++; // skip escaped char\n\t\t} else if (inClass) {\n\t\t\tif (c === \"]\") inClass = false;\n\t\t} else if (c === \"[\") {\n\t\t\tinClass = true;\n\t\t} else if (c === \"{\") {\n\t\t\tbraceDepth++;\n\t\t} else if (c === \"}\") {\n\t\t\tif (braceDepth > 0) braceDepth--;\n\t\t}\n\t}\n\tif (inClass) {\n\t\tthrow new Error(`error parsing glob '${pattern}': unclosed character class; missing ']'`);\n\t}\n\tif (braceDepth > 0) {\n\t\tthrow new Error(`error parsing glob '${pattern}': unclosed alternate group; missing '}'`);\n\t}\n}\n\n/** fd smart-case: a pattern without uppercase letters matches case-insensitively. */\nfunction isSmartCaseInsensitive(pattern: string): boolean {\n\treturn !/[A-Z]/.test(pattern);\n}\n\n/**\n * Match a `find` glob against a POSIX relative path, mirroring fd's semantics:\n * a slashless pattern matches the basename at any depth; a pattern containing a\n * slash matches the full path and is anchored anywhere in the tree. Case\n * sensitivity is smart-case, like fd.\n */\nfunction matchesFindPattern(relPosix: string, pattern: string): boolean {\n\tconst nocase = isSmartCaseInsensitive(pattern);\n\tif (pattern.includes(\"/\")) {\n\t\tlet p = pattern;\n\t\tif (p.startsWith(\"/\")) {\n\t\t\tp = p.slice(1); // leading slash anchors to root; rel paths have no leading slash\n\t\t} else if (!p.startsWith(\"**/\") && p !== \"**\") {\n\t\t\tp = `**/${p}`;\n\t\t}\n\t\treturn minimatch(relPosix, p, { dot: true, nocase });\n\t}\n\treturn minimatch(relPosix, pattern, { dot: true, matchBase: true, nocase });\n}\n\nexport interface NativeFindOptions {\n\tpatterns: string[];\n\ttype: EntryType;\n\t/** Extra exclusion globs (already includes node_modules/.git for find). */\n\texcludeGlobs: string[];\n\tmaxDepth?: number;\n\talwaysSkipDirs?: Set<string>;\n\tsignal?: AbortSignal;\n}\n\n/**\n * Native replacement for the fd-backed search. Returns POSIX paths relative to\n * `root`, with a trailing slash on directories (like fd), unsorted/undeduped —\n * the caller applies its own dedupe/sort/limit.\n */\nexport function nativeFind(root: string, opts: NativeFindOptions): string[] {\n\tfor (const pattern of opts.patterns) validateGlob(pattern);\n\tfor (const glob of opts.excludeGlobs) validateGlob(glob);\n\n\tconst entries = collectEntries(root, {\n\t\tmaxDepth: opts.maxDepth,\n\t\tsignal: opts.signal,\n\t\talwaysSkipDirs: opts.alwaysSkipDirs,\n\t});\n\n\tconst results: string[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type !== opts.type) continue;\n\t\tif (opts.excludeGlobs.some((g) => minimatch(entry.rel, g, { dot: true }))) continue;\n\t\tif (!opts.patterns.some((pat) => matchesFindPattern(entry.rel, pat))) continue;\n\t\tresults.push(entry.type === \"d\" ? `${entry.rel}/` : entry.rel);\n\t}\n\treturn results;\n}\n\ninterface NativeGrepMatch {\n\tfilePath: string;\n\tlineNumber: number;\n\tlineText: string;\n}\n\nexport interface NativeGrepOptions {\n\tpattern: string;\n\t/** True when `root` is a directory; false when it is a single file. */\n\tisDirectory: boolean;\n\tignoreCase?: boolean;\n\tliteral?: boolean;\n\t/** Optional glob filter applied to file paths (like rg --glob). */\n\tglob?: string;\n\t/** Stop after this many matches. */\n\tlimit: number;\n\tsignal?: AbortSignal;\n\treadFile: (absolutePath: string) => Promise<string> | string;\n}\n\nexport interface NativeGrepResult {\n\tmatches: NativeGrepMatch[];\n\tmatchLimitReached: boolean;\n}\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Cheap binary-file heuristic: a NUL byte in the first chunk. rg skips these. */\nfunction looksBinary(content: string): boolean {\n\tconst sampleLength = Math.min(content.length, 8192);\n\tfor (let i = 0; i < sampleLength; i++) {\n\t\tif (content.charCodeAt(i) === 0) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Native replacement for the rg-backed content search. Collects up to `limit`\n * matches as `{ filePath, lineNumber, lineText }`, the same shape the tool's\n * formatter already consumes from rg's JSON output.\n *\n * Throws an Error tagged `invalidRegex` when a non-literal pattern is not a\n * valid JS regex, so the caller can surface the same \"pass literal: true\" hint.\n */\nexport async function nativeGrep(root: string, opts: NativeGrepOptions): Promise<NativeGrepResult> {\n\tlet regex: RegExp;\n\tconst flags = opts.ignoreCase ? \"i\" : \"\";\n\tif (opts.literal) {\n\t\tregex = new RegExp(escapeRegExp(opts.pattern), flags);\n\t} else {\n\t\ttry {\n\t\t\tregex = new RegExp(opts.pattern, flags);\n\t\t} catch (e) {\n\t\t\tconst error = new Error(e instanceof Error ? e.message : String(e)) as Error & { invalidRegex?: boolean };\n\t\t\terror.invalidRegex = true;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tlet files: string[];\n\tif (!opts.isDirectory) {\n\t\tfiles = [root];\n\t} else {\n\t\tconst entries = collectEntries(root, { signal: opts.signal });\n\t\tfiles = entries\n\t\t\t.filter((e) => e.type === \"f\")\n\t\t\t.filter((e) => {\n\t\t\t\tif (!opts.glob) return true;\n\t\t\t\treturn minimatch(e.rel, opts.glob, { dot: true, matchBase: !opts.glob.includes(\"/\") });\n\t\t\t})\n\t\t\t.map((e) => e.abs);\n\t}\n\n\tconst matches: NativeGrepMatch[] = [];\n\tlet matchLimitReached = false;\n\n\tfor (const filePath of files) {\n\t\tif (opts.signal?.aborted || matches.length >= opts.limit) break;\n\n\t\ttry {\n\t\t\tif (statSync(filePath).size > MAX_GREP_FILE_BYTES) continue;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet content: string;\n\t\ttry {\n\t\t\tcontent = await opts.readFile(filePath);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (looksBinary(content)) continue;\n\n\t\tconst lines = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").split(\"\\n\");\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t// New RegExp per file with no /g flag → test() is stateless across lines.\n\t\t\tif (regex.test(lines[i])) {\n\t\t\t\tmatches.push({ filePath, lineNumber: i + 1, lineText: lines[i] });\n\t\t\t\tif (matches.length >= opts.limit) {\n\t\t\t\t\tmatchLimitReached = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { matches, matchLimitReached };\n}\n\n/** Whether the native search path is forced regardless of fd/rg availability. */\nexport function isNativeSearchForced(): boolean {\n\treturn process.env.HOOCODE_NATIVE_SEARCH === \"1\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"native-search.js","sourceRoot":"","sources":["../../../src/core/tools/native-search.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Native (pure-JS) fallbacks for the `find` and `grep` tools.\n *\n * The tools normally shell out to `fd` / `rg`, which are downloaded on demand\n * (see tools-manager.ts). In restricted environments those binaries may be\n * neither on PATH nor downloadable, and `HOOCODE_NATIVE_SEARCH=1` can also force\n * this path. Rather than failing the tool and asking the model to fall back to\n * `bash`, these functions reproduce the essential behaviour in JS so search\n * degrades automatically:\n *\n * - hierarchical `.gitignore` handling (each `.gitignore` is scoped to its own\n * subtree, matching fd's `--no-require-git` behaviour and issue #3303),\n * - hidden files included (like `fd --hidden` / `rg --hidden`),\n * - `.git` always skipped; `node_modules` skipped for `find` (mirrors the\n * tool's built-in excludes) but left to `.gitignore` for `grep` (like rg).\n *\n * These are best-effort approximations, not byte-for-byte fd/rg parity: globs\n * are matched with `minimatch` and patterns with JS `RegExp`, and only\n * `.gitignore` files are honoured (not `.ignore` or global excludes).\n */\n\nimport { readdirSync, readFileSync, statSync } from \"fs\";\nimport ignore, { type Ignore } from \"ignore\";\nimport { minimatch } from \"minimatch\";\nimport path from \"path\";\nimport { toPosixPath } from \"./fd-utils.js\";\n\ntype EntryType = \"f\" | \"d\" | \"l\";\n\n/** Hard cap on entries enumerated during a single walk, so a pathological tree\n * can never hang the fallback. Well above any tool's own result limit. */\nconst MAX_ENTRIES = 200_000;\n\n/** Files larger than this are skipped by the grep fallback (rg streams; we read\n * whole files, so we guard against loading huge blobs into memory). */\nconst MAX_GREP_FILE_BYTES = 20 * 1024 * 1024;\n\nexport interface CollectedEntry {\n\t/** Absolute path. */\n\tabs: string;\n\t/** POSIX path relative to the walk root. */\n\trel: string;\n\ttype: EntryType;\n}\n\n/** A `.gitignore` matcher scoped to the subtree rooted at `baseDir`. */\ninterface GitignoreMatcher {\n\tbaseDir: string;\n\tig: Ignore;\n}\n\nfunction loadGitignore(dir: string): Ignore | undefined {\n\tlet content: string;\n\ttry {\n\t\tcontent = readFileSync(path.join(dir, \".gitignore\"), \"utf-8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn ignore().add(content);\n}\n\n/**\n * Whether `absPath` is ignored by any applicable `.gitignore`. Each matcher only\n * applies to paths inside its `baseDir`, and the path is tested relative to that\n * base — so `a/.gitignore` scopes to `a/` and its descendants but never `b/`.\n */\nfunction isGitIgnored(absPath: string, isDir: boolean, matchers: GitignoreMatcher[]): boolean {\n\tfor (const m of matchers) {\n\t\tconst rel = path.relative(m.baseDir, absPath);\n\t\tif (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) continue;\n\t\tconst relPosix = toPosixPath(rel) + (isDir ? \"/\" : \"\");\n\t\tif (m.ig.ignores(relPosix)) return true;\n\t}\n\treturn false;\n}\n\nexport interface WalkOptions {\n\t/** Max entry depth relative to root; direct children are depth 1. */\n\tmaxDepth?: number;\n\tsignal?: AbortSignal;\n\t/** Directory names to never descend into. Defaults to `.git`. */\n\talwaysSkipDirs?: Set<string>;\n}\n\n/**\n * Walk `root` depth-first, returning every entry not excluded by `.gitignore`\n * or an always-skip directory. Symlinks are reported but never followed (avoids\n * cycles). Enumeration stops at {@link MAX_ENTRIES}.\n */\nexport function collectEntries(root: string, opts: WalkOptions = {}): CollectedEntry[] {\n\tconst out: CollectedEntry[] = [];\n\tconst alwaysSkip = opts.alwaysSkipDirs ?? new Set([\".git\"]);\n\n\tconst rootMatchers: GitignoreMatcher[] = [];\n\tconst rootIg = loadGitignore(root);\n\tif (rootIg) rootMatchers.push({ baseDir: root, ig: rootIg });\n\n\t// `depth` is the depth of `dir`; its direct children are at depth + 1.\n\tconst stack: Array<{ dir: string; depth: number; matchers: GitignoreMatcher[] }> = [\n\t\t{ dir: root, depth: 0, matchers: rootMatchers },\n\t];\n\n\twhile (stack.length > 0) {\n\t\tif (out.length >= MAX_ENTRIES || opts.signal?.aborted) break;\n\t\tconst { dir, depth, matchers } = stack.pop()!;\n\n\t\tlet dirents: import(\"fs\").Dirent[];\n\t\ttry {\n\t\t\tdirents = readdirSync(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\tcontinue; // unreadable directory — skip rather than abort the whole walk\n\t\t}\n\n\t\tconst entryDepth = depth + 1;\n\t\tfor (const dirent of dirents) {\n\t\t\tif (out.length >= MAX_ENTRIES) break;\n\n\t\t\tconst name = dirent.name;\n\t\t\tconst abs = path.join(dir, name);\n\n\t\t\tlet type: EntryType;\n\t\t\tlet isDir = false;\n\t\t\tif (dirent.isSymbolicLink()) {\n\t\t\t\ttype = \"l\";\n\t\t\t} else if (dirent.isDirectory()) {\n\t\t\t\ttype = \"d\";\n\t\t\t\tisDir = true;\n\t\t\t} else if (dirent.isFile()) {\n\t\t\t\ttype = \"f\";\n\t\t\t} else {\n\t\t\t\tcontinue; // sockets, fifos, block devices, …\n\t\t\t}\n\n\t\t\tif (isDir && alwaysSkip.has(name)) continue;\n\t\t\tif (isGitIgnored(abs, isDir, matchers)) continue;\n\n\t\t\tif (opts.maxDepth === undefined || entryDepth <= opts.maxDepth) {\n\t\t\t\tout.push({ abs, rel: toPosixPath(path.relative(root, abs)), type });\n\t\t\t}\n\n\t\t\t// Descend only into real directories, and only if their children can\n\t\t\t// still be within the depth budget.\n\t\t\tif (isDir && (opts.maxDepth === undefined || entryDepth < opts.maxDepth)) {\n\t\t\t\tconst childIg = loadGitignore(abs);\n\t\t\t\tconst nextMatchers = childIg ? [...matchers, { baseDir: abs, ig: childIg }] : matchers;\n\t\t\t\tstack.push({ dir: abs, depth: entryDepth, matchers: nextMatchers });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out;\n}\n\ninterface NativeGrepMatch {\n\tfilePath: string;\n\tlineNumber: number;\n\tlineText: string;\n}\n\nexport interface NativeGrepOptions {\n\tpattern: string;\n\t/** True when `root` is a directory; false when it is a single file. */\n\tisDirectory: boolean;\n\tignoreCase?: boolean;\n\tliteral?: boolean;\n\t/** Optional glob filter applied to file paths (like rg --glob). */\n\tglob?: string;\n\t/** Stop after this many matches. */\n\tlimit: number;\n\tsignal?: AbortSignal;\n\treadFile: (absolutePath: string) => Promise<string> | string;\n}\n\nexport interface NativeGrepResult {\n\tmatches: NativeGrepMatch[];\n\tmatchLimitReached: boolean;\n}\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Cheap binary-file heuristic: a NUL byte in the first chunk. rg skips these. */\nfunction looksBinary(content: string): boolean {\n\tconst sampleLength = Math.min(content.length, 8192);\n\tfor (let i = 0; i < sampleLength; i++) {\n\t\tif (content.charCodeAt(i) === 0) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Native replacement for the rg-backed content search. Collects up to `limit`\n * matches as `{ filePath, lineNumber, lineText }`, the same shape the tool's\n * formatter already consumes from rg's JSON output.\n *\n * Throws an Error tagged `invalidRegex` when a non-literal pattern is not a\n * valid JS regex, so the caller can surface the same \"pass literal: true\" hint.\n */\nexport async function nativeGrep(root: string, opts: NativeGrepOptions): Promise<NativeGrepResult> {\n\tlet regex: RegExp;\n\tconst flags = opts.ignoreCase ? \"i\" : \"\";\n\tif (opts.literal) {\n\t\tregex = new RegExp(escapeRegExp(opts.pattern), flags);\n\t} else {\n\t\ttry {\n\t\t\tregex = new RegExp(opts.pattern, flags);\n\t\t} catch (e) {\n\t\t\tconst error = new Error(e instanceof Error ? e.message : String(e)) as Error & { invalidRegex?: boolean };\n\t\t\terror.invalidRegex = true;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tlet files: string[];\n\tif (!opts.isDirectory) {\n\t\tfiles = [root];\n\t} else {\n\t\tconst entries = collectEntries(root, { signal: opts.signal });\n\t\tfiles = entries\n\t\t\t.filter((e) => e.type === \"f\")\n\t\t\t.filter((e) => {\n\t\t\t\tif (!opts.glob) return true;\n\t\t\t\treturn minimatch(e.rel, opts.glob, { dot: true, matchBase: !opts.glob.includes(\"/\") });\n\t\t\t})\n\t\t\t.map((e) => e.abs);\n\t}\n\n\tconst matches: NativeGrepMatch[] = [];\n\tlet matchLimitReached = false;\n\n\tfor (const filePath of files) {\n\t\tif (opts.signal?.aborted || matches.length >= opts.limit) break;\n\n\t\ttry {\n\t\t\tif (statSync(filePath).size > MAX_GREP_FILE_BYTES) continue;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet content: string;\n\t\ttry {\n\t\t\tcontent = await opts.readFile(filePath);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (looksBinary(content)) continue;\n\n\t\tconst lines = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").split(\"\\n\");\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t// New RegExp per file with no /g flag → test() is stateless across lines.\n\t\t\tif (regex.test(lines[i])) {\n\t\t\t\tmatches.push({ filePath, lineNumber: i + 1, lineText: lines[i] });\n\t\t\t\tif (matches.length >= opts.limit) {\n\t\t\t\t\tmatchLimitReached = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { matches, matchLimitReached };\n}\n\n/** Whether the native search path is forced regardless of fd/rg availability. */\nexport function isNativeSearchForced(): boolean {\n\treturn process.env.HOOCODE_NATIVE_SEARCH === \"1\";\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"propose-plugin.d.ts","sourceRoot":"","sources":["../../../src/core/tools/propose-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAoBH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAsPzE,wBAAgB,iCAAiC,IAAI,cAAc,CAkFlE;AASD,wBAAgB,gCAAgC,IAAI,cAAc,CA+DjE;AAmCD,wBAAgB,0CAA0C,IAAI,cAAc,CAiE3E;AAED,qFAAqF;AACrF,wBAAgB,kCAAkC,IAAI,cAAc,EAAE,CAMrE","sourcesContent":["/**\n * Capability authoring tools (spec §3), refactored to a single risk-gated path.\n *\n * ProposePlugin author a NEW plugin from any capability mix — skills,\n * commands, subagents, hooks, MCP servers. The risk gate is\n * *computed from content*, not pre-declared by tool choice:\n * passive content (skills, commands, read-only subagents) is\n * authored autonomously; executable content (hooks, MCP servers,\n * mutating/high-privilege subagents) auto-triggers a \"show the\n * code + tool grant → human confirms → activate\" gate in the\n * same call. A mixed plugin (skill + hook) is authored in one\n * call, and a hook can never be mis-routed through a \"passive\"\n * tool because the gate keys off what the draft contains.\n * UpdatePlugin merge inline-authored capabilities into an EXISTING local\n * plugin. Nothing is fetched from a remote, so the supply-chain\n * \"benign v1 → hostile v2\" risk that keeps a marketplace\n * UpdatePlugin out of the model's hands does not apply here;\n * executable additions still pass through the same confirm gate.\n *\n * Both author into `.agents/plugins/<id>/` in the requested vendor layouts\n * (Claude Code + GitHub Copilot by default) via the format registry, so results\n * are proper, publishable plugins that round-trip through parsePluginDir.\n *\n * Privilege-amplification guardrail: an authored subagent may never carry a\n * plugin-system (capability-acquisition) tool in its allowlist — enforced in\n * both tools — so a low-trust authored agent cannot bootstrap privilege.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport {\n\tclassifyAllowlist,\n\tgetPlugin,\n\tisAuthoredPlugin,\n\tmergePluginDraft,\n\tpluginExists,\n\tpromoteDraft,\n\tremoveFromPlugin,\n\tresolvePluginPlatforms,\n\twritePluginDraft,\n} from \"../extensions/plugins/authoring.js\";\nimport type { PluginPlatform } from \"../extensions/plugins/formats/platform-targets.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"../extensions/plugins/formats/types.js\";\nimport { formatGateFindings, runStaticGates, withFindings } from \"../extensions/plugins/gates.js\";\nimport { discardDraftDir } from \"../extensions/plugins/locations.js\";\nimport { runSmokeGate } from \"../extensions/plugins/smoke.js\";\nimport type { ExtensionContext } from \"../extensions/types.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tPROPOSE_PLUGIN_TOOL_NAME,\n\tREMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\tUPDATE_PLUGIN_TOOL_NAME,\n} from \"./plugin-tool-names.js\";\n\nconst skillSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Skill name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line trigger description (kept lazy in context).\" })),\n\t\tbody: Type.String({ description: \"SKILL.md instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst commandSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Command name (invoked as /name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line description.\" })),\n\t\tbody: Type.String({ description: \"Prompt template body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst subagentSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Subagent name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"When to dispatch this subagent.\" })),\n\t\ttools: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Comma-separated allowed-tools, e.g. 'read, grep, glob'. Read-only grants are autonomous; \" +\n\t\t\t\t\t\"mutating/exec/network grants (Bash, Write, Edit, MCP) or '*' require human confirmation. Omit for none.\",\n\t\t\t}),\n\t\t),\n\t\tmodel: Type.Optional(Type.String({ description: \"Model override, or 'inherit'.\" })),\n\t\tbody: Type.String({ description: \"System-prompt / instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst hookSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ...\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Regex matched against the tool name. Empty/'*' = all.\" })),\n\t\tcommand: Type.String({ description: \"Shell command to run on the event.\" }),\n\t\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst mcpServerSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"MCP server name.\" }),\n\t\tcommand: Type.String({ description: \"Executable to launch the server.\" }),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Command arguments.\" })),\n\t\tenv: Type.Optional(Type.Record(Type.String(), Type.String(), { description: \"Environment variables.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\n/** The capability params shared by ProposePlugin (create) and UpdatePlugin (merge). */\nconst capabilityProps = {\n\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\tskills: Type.Optional(Type.Array(skillSchema)),\n\tcommands: Type.Optional(Type.Array(commandSchema)),\n\tsubagents: Type.Optional(\n\t\tType.Array(subagentSchema, {\n\t\t\tdescription: \"Subagents. Read-only allowlists are autonomous; mutating ones trigger human confirmation.\",\n\t\t}),\n\t),\n\thooks: Type.Optional(Type.Array(hookSchema, { description: \"Shell hooks (executable — trigger confirmation).\" })),\n\tmcpServers: Type.Optional(\n\t\tType.Array(mcpServerSchema, { description: \"MCP servers (executable — trigger confirmation).\" }),\n\t),\n} as const;\n\n/** Union of every capability a draft can carry (used to build a draft and to classify risk). */\ninterface CapabilityInput {\n\tdescription?: string;\n\tversion?: string;\n\tskills?: Static<typeof skillSchema>[];\n\tcommands?: Static<typeof commandSchema>[];\n\tsubagents?: Static<typeof subagentSchema>[];\n\thooks?: Static<typeof hookSchema>[];\n\tmcpServers?: Static<typeof mcpServerSchema>[];\n}\n\nfunction resolvePlatforms(): PluginPlatform[] {\n\t// No model-facing platform selection: the human picks the target ecosystem\n\t// with --platform, defaulting to claude. A plugin is a distribution unit, so\n\t// there is no vendor-neutral option here. See resolvePluginPlatforms.\n\treturn resolvePluginPlatforms();\n}\n\nfunction draftFrom(id: string, params: CapabilityInput, platforms: MarketplacePlatform[]): PluginDraft {\n\treturn {\n\t\tid,\n\t\tversion: params.version,\n\t\tdescription: params.description,\n\t\tsupportPlatform: platforms,\n\t\tskills: params.skills,\n\t\tcommands: params.commands,\n\t\tagents: params.subagents,\n\t\thooks: params.hooks,\n\t\tmcpServers: params.mcpServers,\n\t};\n}\n\n/** Total capabilities carried by the draft (empty arrays count as nothing). */\nfunction capabilityCount(params: CapabilityInput): number {\n\treturn (\n\t\t(params.skills?.length ?? 0) +\n\t\t(params.commands?.length ?? 0) +\n\t\t(params.subagents?.length ?? 0) +\n\t\t(params.hooks?.length ?? 0) +\n\t\t(params.mcpServers?.length ?? 0)\n\t);\n}\n\n/** The subagents whose allowlist makes them mutating/high-privilege (need the confirm gate). */\nfunction mutatingSubagents(params: CapabilityInput): Static<typeof subagentSchema>[] {\n\treturn (params.subagents ?? []).filter((sa) => classifyAllowlist(sa.tools).risk === \"mutating\");\n}\n\n/** True when the draft carries anything executable — hooks, MCP servers, or a mutating subagent. */\nfunction hasExecutable(params: CapabilityInput): boolean {\n\treturn (\n\t\t(params.hooks?.length ?? 0) > 0 || (params.mcpServers?.length ?? 0) > 0 || mutatingSubagents(params).length > 0\n\t);\n}\n\n/** Reject if any subagent carries a plugin-system tool (privilege-amplification guardrail). Returns the message, or null. */\nfunction guardrailViolation(params: CapabilityInput): string | null {\n\tfor (const sa of params.subagents ?? []) {\n\t\tconst cls = classifyAllowlist(sa.tools);\n\t\tif (cls.pluginTools.length > 0) {\n\t\t\treturn (\n\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\"\n\t\t\t);\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Build the human-facing review text: the executable code and every mutating tool grant. */\nfunction buildReview(id: string, params: CapabilityInput): string {\n\tconst lines: string[] = [`Plugin \"${id}\" wants to install executable capabilities:`];\n\tfor (const h of params.hooks ?? []) {\n\t\tlines.push(` hook [${h.event}${h.matcher ? ` matcher=${h.matcher}` : \"\"}]: ${h.command}`);\n\t}\n\tfor (const s of params.mcpServers ?? []) {\n\t\tlines.push(` mcp server \"${s.name}\": ${s.command}${s.args?.length ? ` ${s.args.join(\" \")}` : \"\"}`);\n\t}\n\tfor (const sa of mutatingSubagents(params)) {\n\t\tlines.push(` subagent \"${sa.name}\" tools: ${sa.tools ?? \"(none)\"} (${classifyAllowlist(sa.tools).reason})`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nfunction summarizeWrite(\n\tid: string,\n\tplatforms: MarketplacePlatform[],\n\tfiles: string[],\n\tdest: string,\n\tverb: string,\n): string {\n\treturn (\n\t\t`${verb} plugin \"${id}\" (${platforms.join(\", \")}) with ${files.length} file(s) at ${dest}:\\n` +\n\t\tfiles.map((f) => ` ${f}`).join(\"\\n\") +\n\t\t`\\nRemove it with UninstallPlugin.`\n\t);\n}\n\ninterface AuthorPluginDetails {\n\tid: string;\n\tauthored: boolean;\n\t/** Whether an executable-capability confirmation gate ran (and was accepted). */\n\tconfirmed?: boolean;\n}\n\nfunction reject(\n\tid: string,\n\tmessage: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: AuthorPluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false } };\n}\n\n/**\n * Run the shared \"executable capabilities → show → confirm\" gate. Returns:\n * - `{ ok: true }` when there is nothing executable, or the human confirmed;\n * - a tool result (authored:false) when there is no UI to confirm on, or the\n * human declined.\n */\nasync function passExecutableGate(\n\tid: string,\n\tparams: CapabilityInput,\n\tctx: ExtensionContext,\n\tevidence?: string,\n): Promise<{ ok: true; gated: boolean } | { ok: false; result: ReturnType<typeof reject> }> {\n\tif (!hasExecutable(params)) return { ok: true, gated: false };\n\n\t// The gate results go in the prompt, not just the tool result. A human shown a\n\t// bare shell command has no basis to approve it; \"ran the checks, here is what\n\t// they found\" is what makes the confirmation a decision rather than a formality.\n\tconst review = evidence ? `${buildReview(id, params)}\\n\\n${evidence}` : buildReview(id, params);\n\tctx.ui.notify(review, \"warning\");\n\tif (!ctx.hasUI) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: reject(\n\t\t\t\tid,\n\t\t\t\t\"Authoring executable capabilities requires human confirmation, which is unavailable in this mode. \" +\n\t\t\t\t\t`Not activated.\\n${review}`,\n\t\t\t),\n\t\t};\n\t}\n\tconst confirmed = await ctx.ui.confirm(\n\t\t`Author executable plugin \"${id}\"?`,\n\t\t`${review}\\n\\nThis installs and can run the code above. Activate it?`,\n\t);\n\tif (!confirmed) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Declined — plugin \"${id}\" was not authored.` }],\n\t\t\t\tdetails: { id, authored: false, confirmed: false },\n\t\t\t},\n\t\t};\n\t}\n\treturn { ok: true, gated: true };\n}\n\n// ── ProposePlugin (create) ────────────────────────────────────────────────────\n\nconst proposeParams = Type.Object(\n\t{ id: Type.String({ description: \"Plugin id (directory + manifest name).\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createProposePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeParams, AuthorPluginDetails>({\n\t\tname: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a NEW portable, reusable plugin to fill a capability gap when no marketplace plugin fits. Accepts any \" +\n\t\t\t\"capability mix — skills, slash commands, subagents, hooks, MCP servers. Authored as one self-contained \" +\n\t\t\t\"artifact usable across sessions and projects. Passive content (skills, commands, read-only \" +\n\t\t\t\"subagents) is authored autonomously; executable content (hooks, MCP servers, mutating subagents) is shown \" +\n\t\t\t\"and requires human confirmation before it activates. To change an existing plugin, use UpdatePlugin.\",\n\t\tpromptSnippet:\n\t\t\t\"Author a new portable, reusable plugin to fill a capability gap (passive is autonomous; executable asks to confirm).\",\n\t\t// Trimmed to the trigger and the two hard prohibitions. The craft guidance —\n\t\t// portability rules, capability-not-task naming, the one-call shape — was\n\t\t// ~600 tok/turn of always-on prose restating a how-to. It is the\n\t\t// `plugin-authoring` skill in the bundled marketplace now, read on demand.\n\t\tpromptGuidelines: [\n\t\t\t\"Sense reusability proactively: a multi-step recipe you'd plausibly repeat (or repeated twice in one session) that SearchPlugins does not already cover is worth authoring. Read the plugin-authoring skill first if it is available; it decides naming, portability and shape.\",\n\t\t\t\"Announce what you authored and why — passive content activates without asking, so the user learns it exists only if you say so.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t\t\"Publishing to a marketplace stays a human action — never do it autonomously.\",\n\t\t],\n\t\tparameters: proposeParams,\n\t\tasync execute(_id, params: Static<typeof proposeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tif (capabilityCount(params) === 0) {\n\t\t\t\treturn reject(params.id, \"Nothing to author. Provide skills, commands, subagents, hooks, or mcpServers.\");\n\t\t\t}\n\n\t\t\tconst platforms = resolvePlatforms();\n\t\t\t// Scoped to the target platform's home: the same id on another platform is\n\t\t\t// a separate ecosystem artifact, not a duplicate.\n\t\t\tif (pluginExists(params.id, platforms)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`A ${platforms.join(\"/\")} plugin named \"${params.id}\" already exists. ` +\n\t\t\t\t\t\t\"Use UpdatePlugin to change it, or pick another id.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Emit to an ephemeral draft first: the gates need something real to run\n\t\t\t// against, and a production home is live (Claude Code loads\n\t\t\t// ~/.claude/skills on its next session), so nothing may land there until\n\t\t\t// the checks pass and — for executable content — the human agrees.\n\t\t\tconst draft = writePluginDraft(draftFrom(params.id, params, platforms), platforms, { promote: false });\n\t\t\tlet evaluation = runStaticGates(draft.dest, { platform: platforms[0] });\n\t\t\t// G3 only for executable content, and only once the cheap gates are\n\t\t\t// happy: no point running a hook whose command G2 already rejected.\n\t\t\tif (evaluation.ok && evaluation.plugin && hasExecutable(params)) {\n\t\t\t\tevaluation = withFindings(evaluation, await runSmokeGate(evaluation.plugin));\n\t\t\t}\n\t\t\tif (!evaluation.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored — it failed its checks.\\n` +\n\t\t\t\t\t\t`${formatGateFindings(evaluation)}\\nFix the issues and try again.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx, formatGateFindings(evaluation));\n\t\t\tif (!gate.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn gate.result;\n\t\t\t}\n\n\t\t\tconst dest = promoteDraft(draft.dest, params.id, platforms);\n\t\t\t// Passive capabilities activate live — usable on the very next model request,\n\t\t\t// this same turn; hooks/MCP servers activate via the reload once the turn ends.\n\t\t\tconst activation = ctx.activatePlugin(dest);\n\t\t\tconst text =\n\t\t\t\t`${summarizeWrite(params.id, platforms, draft.files, dest, \"Authored\")}\\n` +\n\t\t\t\t`${formatGateFindings(evaluation)}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Authored plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── UpdatePlugin (merge into an existing local plugin) ─────────────────────────\n\nconst updateParams = Type.Object(\n\t{ id: Type.String({ description: \"Id of the existing local plugin to update.\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createUpdatePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof updateParams, AuthorPluginDetails>({\n\t\tname: UPDATE_PLUGIN_TOOL_NAME,\n\t\tlabel: UPDATE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Merge inline-authored capabilities into an EXISTING locally AUTHORED plugin (marketplace-installed plugins \" +\n\t\t\t\"are refused). Skills/commands/subagents are added or replaced by name; hooks and MCP servers are unioned \" +\n\t\t\t\"with what's already there; metadata is overwritten only where you supply it. Additive only — remove a \" +\n\t\t\t\"capability with RemovePluginCapability. Nothing is fetched from a remote. Keep additions as portable as \" +\n\t\t\t\"the original, and in its existing layout. Passive additions apply autonomously; executable additions (hooks, MCP \" +\n\t\t\t\"servers, mutating subagents) require human confirmation. Use ProposePlugin to create.\",\n\t\tpromptSnippet:\n\t\t\t\"Add/replace capabilities in a portable plugin you authored (additive; executable additions ask to confirm).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Hooks cannot be modified in place: they have no name, so supplying a changed command ADDS a second hook alongside the old one (both fire). To change a hook, RemovePluginCapability the old one first, then add the new one here.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t],\n\t\tparameters: updateParams,\n\t\tasync execute(_id, params: Static<typeof updateParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`No plugin named \"${params.id}\" is installed. Use ProposePlugin to create it first.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Authored-only: marketplace installs land in the same directory but don't\n\t\t\t// round-trip losslessly through our emitters (see mergePluginDraft).\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"UpdatePlugin only modifies locally authored plugins — updating a marketplace plugin is a human \" +\n\t\t\t\t\t\t\"action (uninstall it and install a newer version instead).\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (capabilityCount(params) === 0 && !params.version && !params.description) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t\"Nothing to update. Provide skills, commands, subagents, hooks, mcpServers, or metadata.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Gate on the DELTA only — existing executables aren't re-confirmed.\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx);\n\t\t\tif (!gate.ok) return gate.result;\n\n\t\t\t// No model-facing platform selection: a merge keeps the plugin's existing\n\t\t\t// layout (mergePluginDraft defaults to existing.supportPlatform).\n\t\t\tconst result = mergePluginDraft(ctx.cwd, params.id, draftFrom(params.id, params, existing.supportPlatform));\n\t\t\tconst platforms = result.plugin?.supportPlatform ?? existing.supportPlatform;\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest, \"Updated\")}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Updated plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── RemovePluginCapability (subtract from an authored plugin) ─────────────────\n\nconst hookRemovalSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event of the hook(s) to remove, e.g. PreToolUse.\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this matcher.\" })),\n\t\tcommand: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this command.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst removeParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Id of the authored plugin to remove capabilities from.\" }),\n\t\tskills: Type.Optional(Type.Array(Type.String(), { description: \"Skill names to remove.\" })),\n\t\tcommands: Type.Optional(Type.Array(Type.String(), { description: \"Command names to remove.\" })),\n\t\tsubagents: Type.Optional(Type.Array(Type.String(), { description: \"Subagent names to remove.\" })),\n\t\tmcpServers: Type.Optional(Type.Array(Type.String(), { description: \"MCP server names to remove.\" })),\n\t\thooks: Type.Optional(\n\t\t\tType.Array(hookRemovalSchema, {\n\t\t\t\tdescription: \"Hooks to remove, matched by event and narrowed by matcher/command when provided.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\ninterface RemovePluginCapabilityDetails {\n\tid: string;\n\tremoved: string[];\n\tmissing: string[];\n}\n\nexport function createRemovePluginCapabilityToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof removeParams, RemovePluginCapabilityDetails>({\n\t\tname: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tlabel: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Remove named capabilities from a locally AUTHORED plugin — skills, commands, subagents, and MCP servers by \" +\n\t\t\t\"name; hooks by event (narrowed by matcher/command). The subtractive half of UpdatePlugin. Removal is \" +\n\t\t\t\"low-risk and autonomous (deleting capabilities cannot execute code). To remove the whole plugin, use \" +\n\t\t\t\"UninstallPlugin; marketplace-installed plugins are refused here.\",\n\t\tpromptSnippet: \"Remove capabilities from a plugin you authored (low risk; autonomous).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Removal runs autonomously (the low-risk direction) — announce what you removed and why.\",\n\t\t\t\"To CHANGE a hook (hooks have no name to replace by): RemovePluginCapability the old hook, then UpdatePlugin the new one (which asks for confirmation).\",\n\t\t],\n\t\tparameters: removeParams,\n\t\tasync execute(_id, params: Static<typeof removeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst noDetails = (msg: string) => ({\n\t\t\t\tcontent: [{ type: \"text\" as const, text: msg }],\n\t\t\t\tdetails: { id: params.id, removed: [], missing: [] },\n\t\t\t});\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn noDetails(`No plugin named \"${params.id}\" is installed.`);\n\t\t\t}\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn noDetails(\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"RemovePluginCapability only edits locally authored plugins — use UninstallPlugin to remove it entirely.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst requested =\n\t\t\t\t(params.skills?.length ?? 0) +\n\t\t\t\t(params.commands?.length ?? 0) +\n\t\t\t\t(params.subagents?.length ?? 0) +\n\t\t\t\t(params.mcpServers?.length ?? 0) +\n\t\t\t\t(params.hooks?.length ?? 0);\n\t\t\tif (requested === 0) {\n\t\t\t\treturn noDetails(\"Nothing to remove. Name skills, commands, subagents, mcpServers, or hooks.\");\n\t\t\t}\n\n\t\t\tconst result = removeFromPlugin(ctx.cwd, params.id, {\n\t\t\t\tskills: params.skills,\n\t\t\t\tcommands: params.commands,\n\t\t\t\tsubagents: params.subagents,\n\t\t\t\tmcpServers: params.mcpServers,\n\t\t\t\thooks: params.hooks,\n\t\t\t});\n\t\t\tconst lines: string[] = [];\n\t\t\tif (result.removed.length > 0) {\n\t\t\t\tlines.push(`Removed from plugin \"${params.id}\":`, ...result.removed.map((r) => ` ${r}`));\n\t\t\t}\n\t\t\tif (result.missing.length > 0) {\n\t\t\t\tlines.push(`Not found (nothing removed):`, ...result.missing.map((m) => ` ${m}`));\n\t\t\t}\n\t\t\tconst text = lines.join(\"\\n\");\n\t\t\t// Removal takes effect through the reload path, same as UninstallPlugin.\n\t\t\tif (result.removed.length > 0) ctx.requestReloadWhenIdle();\n\t\t\tctx.ui.notify(text, result.removed.length > 0 ? \"info\" : \"warning\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, removed: result.removed, missing: result.missing },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** All three authoring tool definitions, for registration on the top-level agent. */\nexport function createProposePluginToolDefinitions(): ToolDefinition[] {\n\treturn [\n\t\tcreateProposePluginToolDefinition(),\n\t\tcreateUpdatePluginToolDefinition(),\n\t\tcreateRemovePluginCapabilityToolDefinition(),\n\t];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"propose-plugin.d.ts","sourceRoot":"","sources":["../../../src/core/tools/propose-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAoBH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAsPzE,wBAAgB,iCAAiC,IAAI,cAAc,CAkFlE;AASD,wBAAgB,gCAAgC,IAAI,cAAc,CA+DjE;AAmCD,wBAAgB,0CAA0C,IAAI,cAAc,CAiE3E;AAED,qFAAqF;AACrF,wBAAgB,kCAAkC,IAAI,cAAc,EAAE,CAMrE","sourcesContent":["/**\n * Capability authoring tools (spec §3), refactored to a single risk-gated path.\n *\n * ProposePlugin author a NEW plugin from any capability mix — skills,\n * commands, subagents, hooks, MCP servers. The risk gate is\n * *computed from content*, not pre-declared by tool choice:\n * passive content (skills, commands, read-only subagents) is\n * authored autonomously; executable content (hooks, MCP servers,\n * mutating/high-privilege subagents) auto-triggers a \"show the\n * code + tool grant → human confirms → activate\" gate in the\n * same call. A mixed plugin (skill + hook) is authored in one\n * call, and a hook can never be mis-routed through a \"passive\"\n * tool because the gate keys off what the draft contains.\n * UpdatePlugin merge inline-authored capabilities into an EXISTING local\n * plugin. Nothing is fetched from a remote, so the supply-chain\n * \"benign v1 → hostile v2\" risk that keeps a marketplace\n * UpdatePlugin out of the model's hands does not apply here;\n * executable additions still pass through the same confirm gate.\n *\n * Both author into `.agents/plugins/<id>/` in the requested vendor layouts\n * (Claude Code + GitHub Copilot by default) via the format registry, so results\n * are proper, publishable plugins that round-trip through parsePluginDir.\n *\n * Privilege-amplification guardrail: an authored subagent may never carry a\n * plugin-system (capability-acquisition) tool in its allowlist — enforced in\n * both tools — so a low-trust authored agent cannot bootstrap privilege.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport {\n\tclassifyAllowlist,\n\tgetPlugin,\n\tisAuthoredPlugin,\n\tmergePluginDraft,\n\tpluginExists,\n\tpromoteDraft,\n\tremoveFromPlugin,\n\tresolvePluginPlatforms,\n\twritePluginDraft,\n} from \"../extensions/plugins/authoring.js\";\nimport type { PluginPlatform } from \"../extensions/plugins/formats/platform-targets.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"../extensions/plugins/formats/types.js\";\nimport { formatGateFindings, runStaticGates, withFindings } from \"../extensions/plugins/gates.js\";\nimport { discardDraftDir } from \"../extensions/plugins/locations.js\";\nimport { runSmokeGate } from \"../extensions/plugins/smoke.js\";\nimport type { ExtensionContext } from \"../extensions/types.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tPROPOSE_PLUGIN_TOOL_NAME,\n\tREMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\tUPDATE_PLUGIN_TOOL_NAME,\n} from \"./plugin-tool-names.js\";\n\nconst skillSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Skill name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line trigger description (kept lazy in context).\" })),\n\t\tbody: Type.String({ description: \"SKILL.md instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst commandSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Command name (invoked as /name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line description.\" })),\n\t\tbody: Type.String({ description: \"Prompt template body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst subagentSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Subagent name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"When to dispatch this subagent.\" })),\n\t\ttools: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Comma-separated allowed-tools, e.g. 'read, search'. Read-only grants are autonomous; \" +\n\t\t\t\t\t\"mutating/exec/network grants (Bash, Write, Edit, MCP) or '*' require human confirmation. Omit for none.\",\n\t\t\t}),\n\t\t),\n\t\tmodel: Type.Optional(Type.String({ description: \"Model override, or 'inherit'.\" })),\n\t\tbody: Type.String({ description: \"System-prompt / instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst hookSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ...\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Regex matched against the tool name. Empty/'*' = all.\" })),\n\t\tcommand: Type.String({ description: \"Shell command to run on the event.\" }),\n\t\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst mcpServerSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"MCP server name.\" }),\n\t\tcommand: Type.String({ description: \"Executable to launch the server.\" }),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Command arguments.\" })),\n\t\tenv: Type.Optional(Type.Record(Type.String(), Type.String(), { description: \"Environment variables.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\n/** The capability params shared by ProposePlugin (create) and UpdatePlugin (merge). */\nconst capabilityProps = {\n\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\tskills: Type.Optional(Type.Array(skillSchema)),\n\tcommands: Type.Optional(Type.Array(commandSchema)),\n\tsubagents: Type.Optional(\n\t\tType.Array(subagentSchema, {\n\t\t\tdescription: \"Subagents. Read-only allowlists are autonomous; mutating ones trigger human confirmation.\",\n\t\t}),\n\t),\n\thooks: Type.Optional(Type.Array(hookSchema, { description: \"Shell hooks (executable — trigger confirmation).\" })),\n\tmcpServers: Type.Optional(\n\t\tType.Array(mcpServerSchema, { description: \"MCP servers (executable — trigger confirmation).\" }),\n\t),\n} as const;\n\n/** Union of every capability a draft can carry (used to build a draft and to classify risk). */\ninterface CapabilityInput {\n\tdescription?: string;\n\tversion?: string;\n\tskills?: Static<typeof skillSchema>[];\n\tcommands?: Static<typeof commandSchema>[];\n\tsubagents?: Static<typeof subagentSchema>[];\n\thooks?: Static<typeof hookSchema>[];\n\tmcpServers?: Static<typeof mcpServerSchema>[];\n}\n\nfunction resolvePlatforms(): PluginPlatform[] {\n\t// No model-facing platform selection: the human picks the target ecosystem\n\t// with --platform, defaulting to claude. A plugin is a distribution unit, so\n\t// there is no vendor-neutral option here. See resolvePluginPlatforms.\n\treturn resolvePluginPlatforms();\n}\n\nfunction draftFrom(id: string, params: CapabilityInput, platforms: MarketplacePlatform[]): PluginDraft {\n\treturn {\n\t\tid,\n\t\tversion: params.version,\n\t\tdescription: params.description,\n\t\tsupportPlatform: platforms,\n\t\tskills: params.skills,\n\t\tcommands: params.commands,\n\t\tagents: params.subagents,\n\t\thooks: params.hooks,\n\t\tmcpServers: params.mcpServers,\n\t};\n}\n\n/** Total capabilities carried by the draft (empty arrays count as nothing). */\nfunction capabilityCount(params: CapabilityInput): number {\n\treturn (\n\t\t(params.skills?.length ?? 0) +\n\t\t(params.commands?.length ?? 0) +\n\t\t(params.subagents?.length ?? 0) +\n\t\t(params.hooks?.length ?? 0) +\n\t\t(params.mcpServers?.length ?? 0)\n\t);\n}\n\n/** The subagents whose allowlist makes them mutating/high-privilege (need the confirm gate). */\nfunction mutatingSubagents(params: CapabilityInput): Static<typeof subagentSchema>[] {\n\treturn (params.subagents ?? []).filter((sa) => classifyAllowlist(sa.tools).risk === \"mutating\");\n}\n\n/** True when the draft carries anything executable — hooks, MCP servers, or a mutating subagent. */\nfunction hasExecutable(params: CapabilityInput): boolean {\n\treturn (\n\t\t(params.hooks?.length ?? 0) > 0 || (params.mcpServers?.length ?? 0) > 0 || mutatingSubagents(params).length > 0\n\t);\n}\n\n/** Reject if any subagent carries a plugin-system tool (privilege-amplification guardrail). Returns the message, or null. */\nfunction guardrailViolation(params: CapabilityInput): string | null {\n\tfor (const sa of params.subagents ?? []) {\n\t\tconst cls = classifyAllowlist(sa.tools);\n\t\tif (cls.pluginTools.length > 0) {\n\t\t\treturn (\n\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\"\n\t\t\t);\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Build the human-facing review text: the executable code and every mutating tool grant. */\nfunction buildReview(id: string, params: CapabilityInput): string {\n\tconst lines: string[] = [`Plugin \"${id}\" wants to install executable capabilities:`];\n\tfor (const h of params.hooks ?? []) {\n\t\tlines.push(` hook [${h.event}${h.matcher ? ` matcher=${h.matcher}` : \"\"}]: ${h.command}`);\n\t}\n\tfor (const s of params.mcpServers ?? []) {\n\t\tlines.push(` mcp server \"${s.name}\": ${s.command}${s.args?.length ? ` ${s.args.join(\" \")}` : \"\"}`);\n\t}\n\tfor (const sa of mutatingSubagents(params)) {\n\t\tlines.push(` subagent \"${sa.name}\" tools: ${sa.tools ?? \"(none)\"} (${classifyAllowlist(sa.tools).reason})`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nfunction summarizeWrite(\n\tid: string,\n\tplatforms: MarketplacePlatform[],\n\tfiles: string[],\n\tdest: string,\n\tverb: string,\n): string {\n\treturn (\n\t\t`${verb} plugin \"${id}\" (${platforms.join(\", \")}) with ${files.length} file(s) at ${dest}:\\n` +\n\t\tfiles.map((f) => ` ${f}`).join(\"\\n\") +\n\t\t`\\nRemove it with UninstallPlugin.`\n\t);\n}\n\ninterface AuthorPluginDetails {\n\tid: string;\n\tauthored: boolean;\n\t/** Whether an executable-capability confirmation gate ran (and was accepted). */\n\tconfirmed?: boolean;\n}\n\nfunction reject(\n\tid: string,\n\tmessage: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: AuthorPluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false } };\n}\n\n/**\n * Run the shared \"executable capabilities → show → confirm\" gate. Returns:\n * - `{ ok: true }` when there is nothing executable, or the human confirmed;\n * - a tool result (authored:false) when there is no UI to confirm on, or the\n * human declined.\n */\nasync function passExecutableGate(\n\tid: string,\n\tparams: CapabilityInput,\n\tctx: ExtensionContext,\n\tevidence?: string,\n): Promise<{ ok: true; gated: boolean } | { ok: false; result: ReturnType<typeof reject> }> {\n\tif (!hasExecutable(params)) return { ok: true, gated: false };\n\n\t// The gate results go in the prompt, not just the tool result. A human shown a\n\t// bare shell command has no basis to approve it; \"ran the checks, here is what\n\t// they found\" is what makes the confirmation a decision rather than a formality.\n\tconst review = evidence ? `${buildReview(id, params)}\\n\\n${evidence}` : buildReview(id, params);\n\tctx.ui.notify(review, \"warning\");\n\tif (!ctx.hasUI) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: reject(\n\t\t\t\tid,\n\t\t\t\t\"Authoring executable capabilities requires human confirmation, which is unavailable in this mode. \" +\n\t\t\t\t\t`Not activated.\\n${review}`,\n\t\t\t),\n\t\t};\n\t}\n\tconst confirmed = await ctx.ui.confirm(\n\t\t`Author executable plugin \"${id}\"?`,\n\t\t`${review}\\n\\nThis installs and can run the code above. Activate it?`,\n\t);\n\tif (!confirmed) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Declined — plugin \"${id}\" was not authored.` }],\n\t\t\t\tdetails: { id, authored: false, confirmed: false },\n\t\t\t},\n\t\t};\n\t}\n\treturn { ok: true, gated: true };\n}\n\n// ── ProposePlugin (create) ────────────────────────────────────────────────────\n\nconst proposeParams = Type.Object(\n\t{ id: Type.String({ description: \"Plugin id (directory + manifest name).\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createProposePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeParams, AuthorPluginDetails>({\n\t\tname: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a NEW portable, reusable plugin to fill a capability gap when no marketplace plugin fits. Accepts any \" +\n\t\t\t\"capability mix — skills, slash commands, subagents, hooks, MCP servers. Authored as one self-contained \" +\n\t\t\t\"artifact usable across sessions and projects. Passive content (skills, commands, read-only \" +\n\t\t\t\"subagents) is authored autonomously; executable content (hooks, MCP servers, mutating subagents) is shown \" +\n\t\t\t\"and requires human confirmation before it activates. To change an existing plugin, use UpdatePlugin.\",\n\t\tpromptSnippet:\n\t\t\t\"Author a new portable, reusable plugin to fill a capability gap (passive is autonomous; executable asks to confirm).\",\n\t\t// Trimmed to the trigger and the two hard prohibitions. The craft guidance —\n\t\t// portability rules, capability-not-task naming, the one-call shape — was\n\t\t// ~600 tok/turn of always-on prose restating a how-to. It is the\n\t\t// `plugin-authoring` skill in the bundled marketplace now, read on demand.\n\t\tpromptGuidelines: [\n\t\t\t\"Sense reusability proactively: a multi-step recipe you'd plausibly repeat (or repeated twice in one session) that SearchPlugins does not already cover is worth authoring. Read the plugin-authoring skill first if it is available; it decides naming, portability and shape.\",\n\t\t\t\"Announce what you authored and why — passive content activates without asking, so the user learns it exists only if you say so.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t\t\"Publishing to a marketplace stays a human action — never do it autonomously.\",\n\t\t],\n\t\tparameters: proposeParams,\n\t\tasync execute(_id, params: Static<typeof proposeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tif (capabilityCount(params) === 0) {\n\t\t\t\treturn reject(params.id, \"Nothing to author. Provide skills, commands, subagents, hooks, or mcpServers.\");\n\t\t\t}\n\n\t\t\tconst platforms = resolvePlatforms();\n\t\t\t// Scoped to the target platform's home: the same id on another platform is\n\t\t\t// a separate ecosystem artifact, not a duplicate.\n\t\t\tif (pluginExists(params.id, platforms)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`A ${platforms.join(\"/\")} plugin named \"${params.id}\" already exists. ` +\n\t\t\t\t\t\t\"Use UpdatePlugin to change it, or pick another id.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Emit to an ephemeral draft first: the gates need something real to run\n\t\t\t// against, and a production home is live (Claude Code loads\n\t\t\t// ~/.claude/skills on its next session), so nothing may land there until\n\t\t\t// the checks pass and — for executable content — the human agrees.\n\t\t\tconst draft = writePluginDraft(draftFrom(params.id, params, platforms), platforms, { promote: false });\n\t\t\tlet evaluation = runStaticGates(draft.dest, { platform: platforms[0] });\n\t\t\t// G3 only for executable content, and only once the cheap gates are\n\t\t\t// happy: no point running a hook whose command G2 already rejected.\n\t\t\tif (evaluation.ok && evaluation.plugin && hasExecutable(params)) {\n\t\t\t\tevaluation = withFindings(evaluation, await runSmokeGate(evaluation.plugin));\n\t\t\t}\n\t\t\tif (!evaluation.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored — it failed its checks.\\n` +\n\t\t\t\t\t\t`${formatGateFindings(evaluation)}\\nFix the issues and try again.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx, formatGateFindings(evaluation));\n\t\t\tif (!gate.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn gate.result;\n\t\t\t}\n\n\t\t\tconst dest = promoteDraft(draft.dest, params.id, platforms);\n\t\t\t// Passive capabilities activate live — usable on the very next model request,\n\t\t\t// this same turn; hooks/MCP servers activate via the reload once the turn ends.\n\t\t\tconst activation = ctx.activatePlugin(dest);\n\t\t\tconst text =\n\t\t\t\t`${summarizeWrite(params.id, platforms, draft.files, dest, \"Authored\")}\\n` +\n\t\t\t\t`${formatGateFindings(evaluation)}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Authored plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── UpdatePlugin (merge into an existing local plugin) ─────────────────────────\n\nconst updateParams = Type.Object(\n\t{ id: Type.String({ description: \"Id of the existing local plugin to update.\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createUpdatePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof updateParams, AuthorPluginDetails>({\n\t\tname: UPDATE_PLUGIN_TOOL_NAME,\n\t\tlabel: UPDATE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Merge inline-authored capabilities into an EXISTING locally AUTHORED plugin (marketplace-installed plugins \" +\n\t\t\t\"are refused). Skills/commands/subagents are added or replaced by name; hooks and MCP servers are unioned \" +\n\t\t\t\"with what's already there; metadata is overwritten only where you supply it. Additive only — remove a \" +\n\t\t\t\"capability with RemovePluginCapability. Nothing is fetched from a remote. Keep additions as portable as \" +\n\t\t\t\"the original, and in its existing layout. Passive additions apply autonomously; executable additions (hooks, MCP \" +\n\t\t\t\"servers, mutating subagents) require human confirmation. Use ProposePlugin to create.\",\n\t\tpromptSnippet:\n\t\t\t\"Add/replace capabilities in a portable plugin you authored (additive; executable additions ask to confirm).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Hooks cannot be modified in place: they have no name, so supplying a changed command ADDS a second hook alongside the old one (both fire). To change a hook, RemovePluginCapability the old one first, then add the new one here.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t],\n\t\tparameters: updateParams,\n\t\tasync execute(_id, params: Static<typeof updateParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`No plugin named \"${params.id}\" is installed. Use ProposePlugin to create it first.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Authored-only: marketplace installs land in the same directory but don't\n\t\t\t// round-trip losslessly through our emitters (see mergePluginDraft).\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"UpdatePlugin only modifies locally authored plugins — updating a marketplace plugin is a human \" +\n\t\t\t\t\t\t\"action (uninstall it and install a newer version instead).\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (capabilityCount(params) === 0 && !params.version && !params.description) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t\"Nothing to update. Provide skills, commands, subagents, hooks, mcpServers, or metadata.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Gate on the DELTA only — existing executables aren't re-confirmed.\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx);\n\t\t\tif (!gate.ok) return gate.result;\n\n\t\t\t// No model-facing platform selection: a merge keeps the plugin's existing\n\t\t\t// layout (mergePluginDraft defaults to existing.supportPlatform).\n\t\t\tconst result = mergePluginDraft(ctx.cwd, params.id, draftFrom(params.id, params, existing.supportPlatform));\n\t\t\tconst platforms = result.plugin?.supportPlatform ?? existing.supportPlatform;\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest, \"Updated\")}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Updated plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── RemovePluginCapability (subtract from an authored plugin) ─────────────────\n\nconst hookRemovalSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event of the hook(s) to remove, e.g. PreToolUse.\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this matcher.\" })),\n\t\tcommand: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this command.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst removeParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Id of the authored plugin to remove capabilities from.\" }),\n\t\tskills: Type.Optional(Type.Array(Type.String(), { description: \"Skill names to remove.\" })),\n\t\tcommands: Type.Optional(Type.Array(Type.String(), { description: \"Command names to remove.\" })),\n\t\tsubagents: Type.Optional(Type.Array(Type.String(), { description: \"Subagent names to remove.\" })),\n\t\tmcpServers: Type.Optional(Type.Array(Type.String(), { description: \"MCP server names to remove.\" })),\n\t\thooks: Type.Optional(\n\t\t\tType.Array(hookRemovalSchema, {\n\t\t\t\tdescription: \"Hooks to remove, matched by event and narrowed by matcher/command when provided.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\ninterface RemovePluginCapabilityDetails {\n\tid: string;\n\tremoved: string[];\n\tmissing: string[];\n}\n\nexport function createRemovePluginCapabilityToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof removeParams, RemovePluginCapabilityDetails>({\n\t\tname: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tlabel: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Remove named capabilities from a locally AUTHORED plugin — skills, commands, subagents, and MCP servers by \" +\n\t\t\t\"name; hooks by event (narrowed by matcher/command). The subtractive half of UpdatePlugin. Removal is \" +\n\t\t\t\"low-risk and autonomous (deleting capabilities cannot execute code). To remove the whole plugin, use \" +\n\t\t\t\"UninstallPlugin; marketplace-installed plugins are refused here.\",\n\t\tpromptSnippet: \"Remove capabilities from a plugin you authored (low risk; autonomous).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Removal runs autonomously (the low-risk direction) — announce what you removed and why.\",\n\t\t\t\"To CHANGE a hook (hooks have no name to replace by): RemovePluginCapability the old hook, then UpdatePlugin the new one (which asks for confirmation).\",\n\t\t],\n\t\tparameters: removeParams,\n\t\tasync execute(_id, params: Static<typeof removeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst noDetails = (msg: string) => ({\n\t\t\t\tcontent: [{ type: \"text\" as const, text: msg }],\n\t\t\t\tdetails: { id: params.id, removed: [], missing: [] },\n\t\t\t});\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn noDetails(`No plugin named \"${params.id}\" is installed.`);\n\t\t\t}\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn noDetails(\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"RemovePluginCapability only edits locally authored plugins — use UninstallPlugin to remove it entirely.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst requested =\n\t\t\t\t(params.skills?.length ?? 0) +\n\t\t\t\t(params.commands?.length ?? 0) +\n\t\t\t\t(params.subagents?.length ?? 0) +\n\t\t\t\t(params.mcpServers?.length ?? 0) +\n\t\t\t\t(params.hooks?.length ?? 0);\n\t\t\tif (requested === 0) {\n\t\t\t\treturn noDetails(\"Nothing to remove. Name skills, commands, subagents, mcpServers, or hooks.\");\n\t\t\t}\n\n\t\t\tconst result = removeFromPlugin(ctx.cwd, params.id, {\n\t\t\t\tskills: params.skills,\n\t\t\t\tcommands: params.commands,\n\t\t\t\tsubagents: params.subagents,\n\t\t\t\tmcpServers: params.mcpServers,\n\t\t\t\thooks: params.hooks,\n\t\t\t});\n\t\t\tconst lines: string[] = [];\n\t\t\tif (result.removed.length > 0) {\n\t\t\t\tlines.push(`Removed from plugin \"${params.id}\":`, ...result.removed.map((r) => ` ${r}`));\n\t\t\t}\n\t\t\tif (result.missing.length > 0) {\n\t\t\t\tlines.push(`Not found (nothing removed):`, ...result.missing.map((m) => ` ${m}`));\n\t\t\t}\n\t\t\tconst text = lines.join(\"\\n\");\n\t\t\t// Removal takes effect through the reload path, same as UninstallPlugin.\n\t\t\tif (result.removed.length > 0) ctx.requestReloadWhenIdle();\n\t\t\tctx.ui.notify(text, result.removed.length > 0 ? \"info\" : \"warning\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, removed: result.removed, missing: result.missing },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** All three authoring tool definitions, for registration on the top-level agent. */\nexport function createProposePluginToolDefinitions(): ToolDefinition[] {\n\treturn [\n\t\tcreateProposePluginToolDefinition(),\n\t\tcreateUpdatePluginToolDefinition(),\n\t\tcreateRemovePluginCapabilityToolDefinition(),\n\t];\n}\n"]}
|
|
@@ -46,7 +46,7 @@ const subagentSchema = Type.Object({
|
|
|
46
46
|
name: Type.String({ description: "Subagent name." }),
|
|
47
47
|
description: Type.Optional(Type.String({ description: "When to dispatch this subagent." })),
|
|
48
48
|
tools: Type.Optional(Type.String({
|
|
49
|
-
description: "Comma-separated allowed-tools, e.g. 'read,
|
|
49
|
+
description: "Comma-separated allowed-tools, e.g. 'read, search'. Read-only grants are autonomous; " +
|
|
50
50
|
"mutating/exec/network grants (Bash, Write, Edit, MCP) or '*' require human confirmation. Omit for none.",
|
|
51
51
|
})),
|
|
52
52
|
model: Type.Optional(Type.String({ description: "Model override, or 'inherit'." })),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"propose-plugin.js","sourceRoot":"","sources":["../../../src/core/tools/propose-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability authoring tools (spec §3), refactored to a single risk-gated path.\n *\n * ProposePlugin author a NEW plugin from any capability mix — skills,\n * commands, subagents, hooks, MCP servers. The risk gate is\n * *computed from content*, not pre-declared by tool choice:\n * passive content (skills, commands, read-only subagents) is\n * authored autonomously; executable content (hooks, MCP servers,\n * mutating/high-privilege subagents) auto-triggers a \"show the\n * code + tool grant → human confirms → activate\" gate in the\n * same call. A mixed plugin (skill + hook) is authored in one\n * call, and a hook can never be mis-routed through a \"passive\"\n * tool because the gate keys off what the draft contains.\n * UpdatePlugin merge inline-authored capabilities into an EXISTING local\n * plugin. Nothing is fetched from a remote, so the supply-chain\n * \"benign v1 → hostile v2\" risk that keeps a marketplace\n * UpdatePlugin out of the model's hands does not apply here;\n * executable additions still pass through the same confirm gate.\n *\n * Both author into `.agents/plugins/<id>/` in the requested vendor layouts\n * (Claude Code + GitHub Copilot by default) via the format registry, so results\n * are proper, publishable plugins that round-trip through parsePluginDir.\n *\n * Privilege-amplification guardrail: an authored subagent may never carry a\n * plugin-system (capability-acquisition) tool in its allowlist — enforced in\n * both tools — so a low-trust authored agent cannot bootstrap privilege.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport {\n\tclassifyAllowlist,\n\tgetPlugin,\n\tisAuthoredPlugin,\n\tmergePluginDraft,\n\tpluginExists,\n\tpromoteDraft,\n\tremoveFromPlugin,\n\tresolvePluginPlatforms,\n\twritePluginDraft,\n} from \"../extensions/plugins/authoring.js\";\nimport type { PluginPlatform } from \"../extensions/plugins/formats/platform-targets.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"../extensions/plugins/formats/types.js\";\nimport { formatGateFindings, runStaticGates, withFindings } from \"../extensions/plugins/gates.js\";\nimport { discardDraftDir } from \"../extensions/plugins/locations.js\";\nimport { runSmokeGate } from \"../extensions/plugins/smoke.js\";\nimport type { ExtensionContext } from \"../extensions/types.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tPROPOSE_PLUGIN_TOOL_NAME,\n\tREMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\tUPDATE_PLUGIN_TOOL_NAME,\n} from \"./plugin-tool-names.js\";\n\nconst skillSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Skill name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line trigger description (kept lazy in context).\" })),\n\t\tbody: Type.String({ description: \"SKILL.md instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst commandSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Command name (invoked as /name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line description.\" })),\n\t\tbody: Type.String({ description: \"Prompt template body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst subagentSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Subagent name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"When to dispatch this subagent.\" })),\n\t\ttools: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Comma-separated allowed-tools, e.g. 'read, grep, glob'. Read-only grants are autonomous; \" +\n\t\t\t\t\t\"mutating/exec/network grants (Bash, Write, Edit, MCP) or '*' require human confirmation. Omit for none.\",\n\t\t\t}),\n\t\t),\n\t\tmodel: Type.Optional(Type.String({ description: \"Model override, or 'inherit'.\" })),\n\t\tbody: Type.String({ description: \"System-prompt / instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst hookSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ...\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Regex matched against the tool name. Empty/'*' = all.\" })),\n\t\tcommand: Type.String({ description: \"Shell command to run on the event.\" }),\n\t\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst mcpServerSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"MCP server name.\" }),\n\t\tcommand: Type.String({ description: \"Executable to launch the server.\" }),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Command arguments.\" })),\n\t\tenv: Type.Optional(Type.Record(Type.String(), Type.String(), { description: \"Environment variables.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\n/** The capability params shared by ProposePlugin (create) and UpdatePlugin (merge). */\nconst capabilityProps = {\n\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\tskills: Type.Optional(Type.Array(skillSchema)),\n\tcommands: Type.Optional(Type.Array(commandSchema)),\n\tsubagents: Type.Optional(\n\t\tType.Array(subagentSchema, {\n\t\t\tdescription: \"Subagents. Read-only allowlists are autonomous; mutating ones trigger human confirmation.\",\n\t\t}),\n\t),\n\thooks: Type.Optional(Type.Array(hookSchema, { description: \"Shell hooks (executable — trigger confirmation).\" })),\n\tmcpServers: Type.Optional(\n\t\tType.Array(mcpServerSchema, { description: \"MCP servers (executable — trigger confirmation).\" }),\n\t),\n} as const;\n\n/** Union of every capability a draft can carry (used to build a draft and to classify risk). */\ninterface CapabilityInput {\n\tdescription?: string;\n\tversion?: string;\n\tskills?: Static<typeof skillSchema>[];\n\tcommands?: Static<typeof commandSchema>[];\n\tsubagents?: Static<typeof subagentSchema>[];\n\thooks?: Static<typeof hookSchema>[];\n\tmcpServers?: Static<typeof mcpServerSchema>[];\n}\n\nfunction resolvePlatforms(): PluginPlatform[] {\n\t// No model-facing platform selection: the human picks the target ecosystem\n\t// with --platform, defaulting to claude. A plugin is a distribution unit, so\n\t// there is no vendor-neutral option here. See resolvePluginPlatforms.\n\treturn resolvePluginPlatforms();\n}\n\nfunction draftFrom(id: string, params: CapabilityInput, platforms: MarketplacePlatform[]): PluginDraft {\n\treturn {\n\t\tid,\n\t\tversion: params.version,\n\t\tdescription: params.description,\n\t\tsupportPlatform: platforms,\n\t\tskills: params.skills,\n\t\tcommands: params.commands,\n\t\tagents: params.subagents,\n\t\thooks: params.hooks,\n\t\tmcpServers: params.mcpServers,\n\t};\n}\n\n/** Total capabilities carried by the draft (empty arrays count as nothing). */\nfunction capabilityCount(params: CapabilityInput): number {\n\treturn (\n\t\t(params.skills?.length ?? 0) +\n\t\t(params.commands?.length ?? 0) +\n\t\t(params.subagents?.length ?? 0) +\n\t\t(params.hooks?.length ?? 0) +\n\t\t(params.mcpServers?.length ?? 0)\n\t);\n}\n\n/** The subagents whose allowlist makes them mutating/high-privilege (need the confirm gate). */\nfunction mutatingSubagents(params: CapabilityInput): Static<typeof subagentSchema>[] {\n\treturn (params.subagents ?? []).filter((sa) => classifyAllowlist(sa.tools).risk === \"mutating\");\n}\n\n/** True when the draft carries anything executable — hooks, MCP servers, or a mutating subagent. */\nfunction hasExecutable(params: CapabilityInput): boolean {\n\treturn (\n\t\t(params.hooks?.length ?? 0) > 0 || (params.mcpServers?.length ?? 0) > 0 || mutatingSubagents(params).length > 0\n\t);\n}\n\n/** Reject if any subagent carries a plugin-system tool (privilege-amplification guardrail). Returns the message, or null. */\nfunction guardrailViolation(params: CapabilityInput): string | null {\n\tfor (const sa of params.subagents ?? []) {\n\t\tconst cls = classifyAllowlist(sa.tools);\n\t\tif (cls.pluginTools.length > 0) {\n\t\t\treturn (\n\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\"\n\t\t\t);\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Build the human-facing review text: the executable code and every mutating tool grant. */\nfunction buildReview(id: string, params: CapabilityInput): string {\n\tconst lines: string[] = [`Plugin \"${id}\" wants to install executable capabilities:`];\n\tfor (const h of params.hooks ?? []) {\n\t\tlines.push(` hook [${h.event}${h.matcher ? ` matcher=${h.matcher}` : \"\"}]: ${h.command}`);\n\t}\n\tfor (const s of params.mcpServers ?? []) {\n\t\tlines.push(` mcp server \"${s.name}\": ${s.command}${s.args?.length ? ` ${s.args.join(\" \")}` : \"\"}`);\n\t}\n\tfor (const sa of mutatingSubagents(params)) {\n\t\tlines.push(` subagent \"${sa.name}\" tools: ${sa.tools ?? \"(none)\"} (${classifyAllowlist(sa.tools).reason})`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nfunction summarizeWrite(\n\tid: string,\n\tplatforms: MarketplacePlatform[],\n\tfiles: string[],\n\tdest: string,\n\tverb: string,\n): string {\n\treturn (\n\t\t`${verb} plugin \"${id}\" (${platforms.join(\", \")}) with ${files.length} file(s) at ${dest}:\\n` +\n\t\tfiles.map((f) => ` ${f}`).join(\"\\n\") +\n\t\t`\\nRemove it with UninstallPlugin.`\n\t);\n}\n\ninterface AuthorPluginDetails {\n\tid: string;\n\tauthored: boolean;\n\t/** Whether an executable-capability confirmation gate ran (and was accepted). */\n\tconfirmed?: boolean;\n}\n\nfunction reject(\n\tid: string,\n\tmessage: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: AuthorPluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false } };\n}\n\n/**\n * Run the shared \"executable capabilities → show → confirm\" gate. Returns:\n * - `{ ok: true }` when there is nothing executable, or the human confirmed;\n * - a tool result (authored:false) when there is no UI to confirm on, or the\n * human declined.\n */\nasync function passExecutableGate(\n\tid: string,\n\tparams: CapabilityInput,\n\tctx: ExtensionContext,\n\tevidence?: string,\n): Promise<{ ok: true; gated: boolean } | { ok: false; result: ReturnType<typeof reject> }> {\n\tif (!hasExecutable(params)) return { ok: true, gated: false };\n\n\t// The gate results go in the prompt, not just the tool result. A human shown a\n\t// bare shell command has no basis to approve it; \"ran the checks, here is what\n\t// they found\" is what makes the confirmation a decision rather than a formality.\n\tconst review = evidence ? `${buildReview(id, params)}\\n\\n${evidence}` : buildReview(id, params);\n\tctx.ui.notify(review, \"warning\");\n\tif (!ctx.hasUI) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: reject(\n\t\t\t\tid,\n\t\t\t\t\"Authoring executable capabilities requires human confirmation, which is unavailable in this mode. \" +\n\t\t\t\t\t`Not activated.\\n${review}`,\n\t\t\t),\n\t\t};\n\t}\n\tconst confirmed = await ctx.ui.confirm(\n\t\t`Author executable plugin \"${id}\"?`,\n\t\t`${review}\\n\\nThis installs and can run the code above. Activate it?`,\n\t);\n\tif (!confirmed) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Declined — plugin \"${id}\" was not authored.` }],\n\t\t\t\tdetails: { id, authored: false, confirmed: false },\n\t\t\t},\n\t\t};\n\t}\n\treturn { ok: true, gated: true };\n}\n\n// ── ProposePlugin (create) ────────────────────────────────────────────────────\n\nconst proposeParams = Type.Object(\n\t{ id: Type.String({ description: \"Plugin id (directory + manifest name).\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createProposePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeParams, AuthorPluginDetails>({\n\t\tname: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a NEW portable, reusable plugin to fill a capability gap when no marketplace plugin fits. Accepts any \" +\n\t\t\t\"capability mix — skills, slash commands, subagents, hooks, MCP servers. Authored as one self-contained \" +\n\t\t\t\"artifact usable across sessions and projects. Passive content (skills, commands, read-only \" +\n\t\t\t\"subagents) is authored autonomously; executable content (hooks, MCP servers, mutating subagents) is shown \" +\n\t\t\t\"and requires human confirmation before it activates. To change an existing plugin, use UpdatePlugin.\",\n\t\tpromptSnippet:\n\t\t\t\"Author a new portable, reusable plugin to fill a capability gap (passive is autonomous; executable asks to confirm).\",\n\t\t// Trimmed to the trigger and the two hard prohibitions. The craft guidance —\n\t\t// portability rules, capability-not-task naming, the one-call shape — was\n\t\t// ~600 tok/turn of always-on prose restating a how-to. It is the\n\t\t// `plugin-authoring` skill in the bundled marketplace now, read on demand.\n\t\tpromptGuidelines: [\n\t\t\t\"Sense reusability proactively: a multi-step recipe you'd plausibly repeat (or repeated twice in one session) that SearchPlugins does not already cover is worth authoring. Read the plugin-authoring skill first if it is available; it decides naming, portability and shape.\",\n\t\t\t\"Announce what you authored and why — passive content activates without asking, so the user learns it exists only if you say so.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t\t\"Publishing to a marketplace stays a human action — never do it autonomously.\",\n\t\t],\n\t\tparameters: proposeParams,\n\t\tasync execute(_id, params: Static<typeof proposeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tif (capabilityCount(params) === 0) {\n\t\t\t\treturn reject(params.id, \"Nothing to author. Provide skills, commands, subagents, hooks, or mcpServers.\");\n\t\t\t}\n\n\t\t\tconst platforms = resolvePlatforms();\n\t\t\t// Scoped to the target platform's home: the same id on another platform is\n\t\t\t// a separate ecosystem artifact, not a duplicate.\n\t\t\tif (pluginExists(params.id, platforms)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`A ${platforms.join(\"/\")} plugin named \"${params.id}\" already exists. ` +\n\t\t\t\t\t\t\"Use UpdatePlugin to change it, or pick another id.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Emit to an ephemeral draft first: the gates need something real to run\n\t\t\t// against, and a production home is live (Claude Code loads\n\t\t\t// ~/.claude/skills on its next session), so nothing may land there until\n\t\t\t// the checks pass and — for executable content — the human agrees.\n\t\t\tconst draft = writePluginDraft(draftFrom(params.id, params, platforms), platforms, { promote: false });\n\t\t\tlet evaluation = runStaticGates(draft.dest, { platform: platforms[0] });\n\t\t\t// G3 only for executable content, and only once the cheap gates are\n\t\t\t// happy: no point running a hook whose command G2 already rejected.\n\t\t\tif (evaluation.ok && evaluation.plugin && hasExecutable(params)) {\n\t\t\t\tevaluation = withFindings(evaluation, await runSmokeGate(evaluation.plugin));\n\t\t\t}\n\t\t\tif (!evaluation.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored — it failed its checks.\\n` +\n\t\t\t\t\t\t`${formatGateFindings(evaluation)}\\nFix the issues and try again.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx, formatGateFindings(evaluation));\n\t\t\tif (!gate.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn gate.result;\n\t\t\t}\n\n\t\t\tconst dest = promoteDraft(draft.dest, params.id, platforms);\n\t\t\t// Passive capabilities activate live — usable on the very next model request,\n\t\t\t// this same turn; hooks/MCP servers activate via the reload once the turn ends.\n\t\t\tconst activation = ctx.activatePlugin(dest);\n\t\t\tconst text =\n\t\t\t\t`${summarizeWrite(params.id, platforms, draft.files, dest, \"Authored\")}\\n` +\n\t\t\t\t`${formatGateFindings(evaluation)}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Authored plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── UpdatePlugin (merge into an existing local plugin) ─────────────────────────\n\nconst updateParams = Type.Object(\n\t{ id: Type.String({ description: \"Id of the existing local plugin to update.\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createUpdatePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof updateParams, AuthorPluginDetails>({\n\t\tname: UPDATE_PLUGIN_TOOL_NAME,\n\t\tlabel: UPDATE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Merge inline-authored capabilities into an EXISTING locally AUTHORED plugin (marketplace-installed plugins \" +\n\t\t\t\"are refused). Skills/commands/subagents are added or replaced by name; hooks and MCP servers are unioned \" +\n\t\t\t\"with what's already there; metadata is overwritten only where you supply it. Additive only — remove a \" +\n\t\t\t\"capability with RemovePluginCapability. Nothing is fetched from a remote. Keep additions as portable as \" +\n\t\t\t\"the original, and in its existing layout. Passive additions apply autonomously; executable additions (hooks, MCP \" +\n\t\t\t\"servers, mutating subagents) require human confirmation. Use ProposePlugin to create.\",\n\t\tpromptSnippet:\n\t\t\t\"Add/replace capabilities in a portable plugin you authored (additive; executable additions ask to confirm).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Hooks cannot be modified in place: they have no name, so supplying a changed command ADDS a second hook alongside the old one (both fire). To change a hook, RemovePluginCapability the old one first, then add the new one here.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t],\n\t\tparameters: updateParams,\n\t\tasync execute(_id, params: Static<typeof updateParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`No plugin named \"${params.id}\" is installed. Use ProposePlugin to create it first.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Authored-only: marketplace installs land in the same directory but don't\n\t\t\t// round-trip losslessly through our emitters (see mergePluginDraft).\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"UpdatePlugin only modifies locally authored plugins — updating a marketplace plugin is a human \" +\n\t\t\t\t\t\t\"action (uninstall it and install a newer version instead).\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (capabilityCount(params) === 0 && !params.version && !params.description) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t\"Nothing to update. Provide skills, commands, subagents, hooks, mcpServers, or metadata.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Gate on the DELTA only — existing executables aren't re-confirmed.\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx);\n\t\t\tif (!gate.ok) return gate.result;\n\n\t\t\t// No model-facing platform selection: a merge keeps the plugin's existing\n\t\t\t// layout (mergePluginDraft defaults to existing.supportPlatform).\n\t\t\tconst result = mergePluginDraft(ctx.cwd, params.id, draftFrom(params.id, params, existing.supportPlatform));\n\t\t\tconst platforms = result.plugin?.supportPlatform ?? existing.supportPlatform;\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest, \"Updated\")}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Updated plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── RemovePluginCapability (subtract from an authored plugin) ─────────────────\n\nconst hookRemovalSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event of the hook(s) to remove, e.g. PreToolUse.\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this matcher.\" })),\n\t\tcommand: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this command.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst removeParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Id of the authored plugin to remove capabilities from.\" }),\n\t\tskills: Type.Optional(Type.Array(Type.String(), { description: \"Skill names to remove.\" })),\n\t\tcommands: Type.Optional(Type.Array(Type.String(), { description: \"Command names to remove.\" })),\n\t\tsubagents: Type.Optional(Type.Array(Type.String(), { description: \"Subagent names to remove.\" })),\n\t\tmcpServers: Type.Optional(Type.Array(Type.String(), { description: \"MCP server names to remove.\" })),\n\t\thooks: Type.Optional(\n\t\t\tType.Array(hookRemovalSchema, {\n\t\t\t\tdescription: \"Hooks to remove, matched by event and narrowed by matcher/command when provided.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\ninterface RemovePluginCapabilityDetails {\n\tid: string;\n\tremoved: string[];\n\tmissing: string[];\n}\n\nexport function createRemovePluginCapabilityToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof removeParams, RemovePluginCapabilityDetails>({\n\t\tname: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tlabel: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Remove named capabilities from a locally AUTHORED plugin — skills, commands, subagents, and MCP servers by \" +\n\t\t\t\"name; hooks by event (narrowed by matcher/command). The subtractive half of UpdatePlugin. Removal is \" +\n\t\t\t\"low-risk and autonomous (deleting capabilities cannot execute code). To remove the whole plugin, use \" +\n\t\t\t\"UninstallPlugin; marketplace-installed plugins are refused here.\",\n\t\tpromptSnippet: \"Remove capabilities from a plugin you authored (low risk; autonomous).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Removal runs autonomously (the low-risk direction) — announce what you removed and why.\",\n\t\t\t\"To CHANGE a hook (hooks have no name to replace by): RemovePluginCapability the old hook, then UpdatePlugin the new one (which asks for confirmation).\",\n\t\t],\n\t\tparameters: removeParams,\n\t\tasync execute(_id, params: Static<typeof removeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst noDetails = (msg: string) => ({\n\t\t\t\tcontent: [{ type: \"text\" as const, text: msg }],\n\t\t\t\tdetails: { id: params.id, removed: [], missing: [] },\n\t\t\t});\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn noDetails(`No plugin named \"${params.id}\" is installed.`);\n\t\t\t}\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn noDetails(\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"RemovePluginCapability only edits locally authored plugins — use UninstallPlugin to remove it entirely.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst requested =\n\t\t\t\t(params.skills?.length ?? 0) +\n\t\t\t\t(params.commands?.length ?? 0) +\n\t\t\t\t(params.subagents?.length ?? 0) +\n\t\t\t\t(params.mcpServers?.length ?? 0) +\n\t\t\t\t(params.hooks?.length ?? 0);\n\t\t\tif (requested === 0) {\n\t\t\t\treturn noDetails(\"Nothing to remove. Name skills, commands, subagents, mcpServers, or hooks.\");\n\t\t\t}\n\n\t\t\tconst result = removeFromPlugin(ctx.cwd, params.id, {\n\t\t\t\tskills: params.skills,\n\t\t\t\tcommands: params.commands,\n\t\t\t\tsubagents: params.subagents,\n\t\t\t\tmcpServers: params.mcpServers,\n\t\t\t\thooks: params.hooks,\n\t\t\t});\n\t\t\tconst lines: string[] = [];\n\t\t\tif (result.removed.length > 0) {\n\t\t\t\tlines.push(`Removed from plugin \"${params.id}\":`, ...result.removed.map((r) => ` ${r}`));\n\t\t\t}\n\t\t\tif (result.missing.length > 0) {\n\t\t\t\tlines.push(`Not found (nothing removed):`, ...result.missing.map((m) => ` ${m}`));\n\t\t\t}\n\t\t\tconst text = lines.join(\"\\n\");\n\t\t\t// Removal takes effect through the reload path, same as UninstallPlugin.\n\t\t\tif (result.removed.length > 0) ctx.requestReloadWhenIdle();\n\t\t\tctx.ui.notify(text, result.removed.length > 0 ? \"info\" : \"warning\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, removed: result.removed, missing: result.missing },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** All three authoring tool definitions, for registration on the top-level agent. */\nexport function createProposePluginToolDefinitions(): ToolDefinition[] {\n\treturn [\n\t\tcreateProposePluginToolDefinition(),\n\t\tcreateUpdatePluginToolDefinition(),\n\t\tcreateRemovePluginCapabilityToolDefinition(),\n\t];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"propose-plugin.js","sourceRoot":"","sources":["../../../src/core/tools/propose-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability authoring tools (spec §3), refactored to a single risk-gated path.\n *\n * ProposePlugin author a NEW plugin from any capability mix — skills,\n * commands, subagents, hooks, MCP servers. The risk gate is\n * *computed from content*, not pre-declared by tool choice:\n * passive content (skills, commands, read-only subagents) is\n * authored autonomously; executable content (hooks, MCP servers,\n * mutating/high-privilege subagents) auto-triggers a \"show the\n * code + tool grant → human confirms → activate\" gate in the\n * same call. A mixed plugin (skill + hook) is authored in one\n * call, and a hook can never be mis-routed through a \"passive\"\n * tool because the gate keys off what the draft contains.\n * UpdatePlugin merge inline-authored capabilities into an EXISTING local\n * plugin. Nothing is fetched from a remote, so the supply-chain\n * \"benign v1 → hostile v2\" risk that keeps a marketplace\n * UpdatePlugin out of the model's hands does not apply here;\n * executable additions still pass through the same confirm gate.\n *\n * Both author into `.agents/plugins/<id>/` in the requested vendor layouts\n * (Claude Code + GitHub Copilot by default) via the format registry, so results\n * are proper, publishable plugins that round-trip through parsePluginDir.\n *\n * Privilege-amplification guardrail: an authored subagent may never carry a\n * plugin-system (capability-acquisition) tool in its allowlist — enforced in\n * both tools — so a low-trust authored agent cannot bootstrap privilege.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport {\n\tclassifyAllowlist,\n\tgetPlugin,\n\tisAuthoredPlugin,\n\tmergePluginDraft,\n\tpluginExists,\n\tpromoteDraft,\n\tremoveFromPlugin,\n\tresolvePluginPlatforms,\n\twritePluginDraft,\n} from \"../extensions/plugins/authoring.js\";\nimport type { PluginPlatform } from \"../extensions/plugins/formats/platform-targets.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"../extensions/plugins/formats/types.js\";\nimport { formatGateFindings, runStaticGates, withFindings } from \"../extensions/plugins/gates.js\";\nimport { discardDraftDir } from \"../extensions/plugins/locations.js\";\nimport { runSmokeGate } from \"../extensions/plugins/smoke.js\";\nimport type { ExtensionContext } from \"../extensions/types.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tPROPOSE_PLUGIN_TOOL_NAME,\n\tREMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\tUPDATE_PLUGIN_TOOL_NAME,\n} from \"./plugin-tool-names.js\";\n\nconst skillSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Skill name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line trigger description (kept lazy in context).\" })),\n\t\tbody: Type.String({ description: \"SKILL.md instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst commandSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Command name (invoked as /name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line description.\" })),\n\t\tbody: Type.String({ description: \"Prompt template body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst subagentSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Subagent name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"When to dispatch this subagent.\" })),\n\t\ttools: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Comma-separated allowed-tools, e.g. 'read, search'. Read-only grants are autonomous; \" +\n\t\t\t\t\t\"mutating/exec/network grants (Bash, Write, Edit, MCP) or '*' require human confirmation. Omit for none.\",\n\t\t\t}),\n\t\t),\n\t\tmodel: Type.Optional(Type.String({ description: \"Model override, or 'inherit'.\" })),\n\t\tbody: Type.String({ description: \"System-prompt / instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst hookSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ...\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Regex matched against the tool name. Empty/'*' = all.\" })),\n\t\tcommand: Type.String({ description: \"Shell command to run on the event.\" }),\n\t\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst mcpServerSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"MCP server name.\" }),\n\t\tcommand: Type.String({ description: \"Executable to launch the server.\" }),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Command arguments.\" })),\n\t\tenv: Type.Optional(Type.Record(Type.String(), Type.String(), { description: \"Environment variables.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\n/** The capability params shared by ProposePlugin (create) and UpdatePlugin (merge). */\nconst capabilityProps = {\n\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\tskills: Type.Optional(Type.Array(skillSchema)),\n\tcommands: Type.Optional(Type.Array(commandSchema)),\n\tsubagents: Type.Optional(\n\t\tType.Array(subagentSchema, {\n\t\t\tdescription: \"Subagents. Read-only allowlists are autonomous; mutating ones trigger human confirmation.\",\n\t\t}),\n\t),\n\thooks: Type.Optional(Type.Array(hookSchema, { description: \"Shell hooks (executable — trigger confirmation).\" })),\n\tmcpServers: Type.Optional(\n\t\tType.Array(mcpServerSchema, { description: \"MCP servers (executable — trigger confirmation).\" }),\n\t),\n} as const;\n\n/** Union of every capability a draft can carry (used to build a draft and to classify risk). */\ninterface CapabilityInput {\n\tdescription?: string;\n\tversion?: string;\n\tskills?: Static<typeof skillSchema>[];\n\tcommands?: Static<typeof commandSchema>[];\n\tsubagents?: Static<typeof subagentSchema>[];\n\thooks?: Static<typeof hookSchema>[];\n\tmcpServers?: Static<typeof mcpServerSchema>[];\n}\n\nfunction resolvePlatforms(): PluginPlatform[] {\n\t// No model-facing platform selection: the human picks the target ecosystem\n\t// with --platform, defaulting to claude. A plugin is a distribution unit, so\n\t// there is no vendor-neutral option here. See resolvePluginPlatforms.\n\treturn resolvePluginPlatforms();\n}\n\nfunction draftFrom(id: string, params: CapabilityInput, platforms: MarketplacePlatform[]): PluginDraft {\n\treturn {\n\t\tid,\n\t\tversion: params.version,\n\t\tdescription: params.description,\n\t\tsupportPlatform: platforms,\n\t\tskills: params.skills,\n\t\tcommands: params.commands,\n\t\tagents: params.subagents,\n\t\thooks: params.hooks,\n\t\tmcpServers: params.mcpServers,\n\t};\n}\n\n/** Total capabilities carried by the draft (empty arrays count as nothing). */\nfunction capabilityCount(params: CapabilityInput): number {\n\treturn (\n\t\t(params.skills?.length ?? 0) +\n\t\t(params.commands?.length ?? 0) +\n\t\t(params.subagents?.length ?? 0) +\n\t\t(params.hooks?.length ?? 0) +\n\t\t(params.mcpServers?.length ?? 0)\n\t);\n}\n\n/** The subagents whose allowlist makes them mutating/high-privilege (need the confirm gate). */\nfunction mutatingSubagents(params: CapabilityInput): Static<typeof subagentSchema>[] {\n\treturn (params.subagents ?? []).filter((sa) => classifyAllowlist(sa.tools).risk === \"mutating\");\n}\n\n/** True when the draft carries anything executable — hooks, MCP servers, or a mutating subagent. */\nfunction hasExecutable(params: CapabilityInput): boolean {\n\treturn (\n\t\t(params.hooks?.length ?? 0) > 0 || (params.mcpServers?.length ?? 0) > 0 || mutatingSubagents(params).length > 0\n\t);\n}\n\n/** Reject if any subagent carries a plugin-system tool (privilege-amplification guardrail). Returns the message, or null. */\nfunction guardrailViolation(params: CapabilityInput): string | null {\n\tfor (const sa of params.subagents ?? []) {\n\t\tconst cls = classifyAllowlist(sa.tools);\n\t\tif (cls.pluginTools.length > 0) {\n\t\t\treturn (\n\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\"\n\t\t\t);\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Build the human-facing review text: the executable code and every mutating tool grant. */\nfunction buildReview(id: string, params: CapabilityInput): string {\n\tconst lines: string[] = [`Plugin \"${id}\" wants to install executable capabilities:`];\n\tfor (const h of params.hooks ?? []) {\n\t\tlines.push(` hook [${h.event}${h.matcher ? ` matcher=${h.matcher}` : \"\"}]: ${h.command}`);\n\t}\n\tfor (const s of params.mcpServers ?? []) {\n\t\tlines.push(` mcp server \"${s.name}\": ${s.command}${s.args?.length ? ` ${s.args.join(\" \")}` : \"\"}`);\n\t}\n\tfor (const sa of mutatingSubagents(params)) {\n\t\tlines.push(` subagent \"${sa.name}\" tools: ${sa.tools ?? \"(none)\"} (${classifyAllowlist(sa.tools).reason})`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nfunction summarizeWrite(\n\tid: string,\n\tplatforms: MarketplacePlatform[],\n\tfiles: string[],\n\tdest: string,\n\tverb: string,\n): string {\n\treturn (\n\t\t`${verb} plugin \"${id}\" (${platforms.join(\", \")}) with ${files.length} file(s) at ${dest}:\\n` +\n\t\tfiles.map((f) => ` ${f}`).join(\"\\n\") +\n\t\t`\\nRemove it with UninstallPlugin.`\n\t);\n}\n\ninterface AuthorPluginDetails {\n\tid: string;\n\tauthored: boolean;\n\t/** Whether an executable-capability confirmation gate ran (and was accepted). */\n\tconfirmed?: boolean;\n}\n\nfunction reject(\n\tid: string,\n\tmessage: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: AuthorPluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false } };\n}\n\n/**\n * Run the shared \"executable capabilities → show → confirm\" gate. Returns:\n * - `{ ok: true }` when there is nothing executable, or the human confirmed;\n * - a tool result (authored:false) when there is no UI to confirm on, or the\n * human declined.\n */\nasync function passExecutableGate(\n\tid: string,\n\tparams: CapabilityInput,\n\tctx: ExtensionContext,\n\tevidence?: string,\n): Promise<{ ok: true; gated: boolean } | { ok: false; result: ReturnType<typeof reject> }> {\n\tif (!hasExecutable(params)) return { ok: true, gated: false };\n\n\t// The gate results go in the prompt, not just the tool result. A human shown a\n\t// bare shell command has no basis to approve it; \"ran the checks, here is what\n\t// they found\" is what makes the confirmation a decision rather than a formality.\n\tconst review = evidence ? `${buildReview(id, params)}\\n\\n${evidence}` : buildReview(id, params);\n\tctx.ui.notify(review, \"warning\");\n\tif (!ctx.hasUI) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: reject(\n\t\t\t\tid,\n\t\t\t\t\"Authoring executable capabilities requires human confirmation, which is unavailable in this mode. \" +\n\t\t\t\t\t`Not activated.\\n${review}`,\n\t\t\t),\n\t\t};\n\t}\n\tconst confirmed = await ctx.ui.confirm(\n\t\t`Author executable plugin \"${id}\"?`,\n\t\t`${review}\\n\\nThis installs and can run the code above. Activate it?`,\n\t);\n\tif (!confirmed) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Declined — plugin \"${id}\" was not authored.` }],\n\t\t\t\tdetails: { id, authored: false, confirmed: false },\n\t\t\t},\n\t\t};\n\t}\n\treturn { ok: true, gated: true };\n}\n\n// ── ProposePlugin (create) ────────────────────────────────────────────────────\n\nconst proposeParams = Type.Object(\n\t{ id: Type.String({ description: \"Plugin id (directory + manifest name).\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createProposePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeParams, AuthorPluginDetails>({\n\t\tname: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a NEW portable, reusable plugin to fill a capability gap when no marketplace plugin fits. Accepts any \" +\n\t\t\t\"capability mix — skills, slash commands, subagents, hooks, MCP servers. Authored as one self-contained \" +\n\t\t\t\"artifact usable across sessions and projects. Passive content (skills, commands, read-only \" +\n\t\t\t\"subagents) is authored autonomously; executable content (hooks, MCP servers, mutating subagents) is shown \" +\n\t\t\t\"and requires human confirmation before it activates. To change an existing plugin, use UpdatePlugin.\",\n\t\tpromptSnippet:\n\t\t\t\"Author a new portable, reusable plugin to fill a capability gap (passive is autonomous; executable asks to confirm).\",\n\t\t// Trimmed to the trigger and the two hard prohibitions. The craft guidance —\n\t\t// portability rules, capability-not-task naming, the one-call shape — was\n\t\t// ~600 tok/turn of always-on prose restating a how-to. It is the\n\t\t// `plugin-authoring` skill in the bundled marketplace now, read on demand.\n\t\tpromptGuidelines: [\n\t\t\t\"Sense reusability proactively: a multi-step recipe you'd plausibly repeat (or repeated twice in one session) that SearchPlugins does not already cover is worth authoring. Read the plugin-authoring skill first if it is available; it decides naming, portability and shape.\",\n\t\t\t\"Announce what you authored and why — passive content activates without asking, so the user learns it exists only if you say so.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t\t\"Publishing to a marketplace stays a human action — never do it autonomously.\",\n\t\t],\n\t\tparameters: proposeParams,\n\t\tasync execute(_id, params: Static<typeof proposeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tif (capabilityCount(params) === 0) {\n\t\t\t\treturn reject(params.id, \"Nothing to author. Provide skills, commands, subagents, hooks, or mcpServers.\");\n\t\t\t}\n\n\t\t\tconst platforms = resolvePlatforms();\n\t\t\t// Scoped to the target platform's home: the same id on another platform is\n\t\t\t// a separate ecosystem artifact, not a duplicate.\n\t\t\tif (pluginExists(params.id, platforms)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`A ${platforms.join(\"/\")} plugin named \"${params.id}\" already exists. ` +\n\t\t\t\t\t\t\"Use UpdatePlugin to change it, or pick another id.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Emit to an ephemeral draft first: the gates need something real to run\n\t\t\t// against, and a production home is live (Claude Code loads\n\t\t\t// ~/.claude/skills on its next session), so nothing may land there until\n\t\t\t// the checks pass and — for executable content — the human agrees.\n\t\t\tconst draft = writePluginDraft(draftFrom(params.id, params, platforms), platforms, { promote: false });\n\t\t\tlet evaluation = runStaticGates(draft.dest, { platform: platforms[0] });\n\t\t\t// G3 only for executable content, and only once the cheap gates are\n\t\t\t// happy: no point running a hook whose command G2 already rejected.\n\t\t\tif (evaluation.ok && evaluation.plugin && hasExecutable(params)) {\n\t\t\t\tevaluation = withFindings(evaluation, await runSmokeGate(evaluation.plugin));\n\t\t\t}\n\t\t\tif (!evaluation.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored — it failed its checks.\\n` +\n\t\t\t\t\t\t`${formatGateFindings(evaluation)}\\nFix the issues and try again.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx, formatGateFindings(evaluation));\n\t\t\tif (!gate.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn gate.result;\n\t\t\t}\n\n\t\t\tconst dest = promoteDraft(draft.dest, params.id, platforms);\n\t\t\t// Passive capabilities activate live — usable on the very next model request,\n\t\t\t// this same turn; hooks/MCP servers activate via the reload once the turn ends.\n\t\t\tconst activation = ctx.activatePlugin(dest);\n\t\t\tconst text =\n\t\t\t\t`${summarizeWrite(params.id, platforms, draft.files, dest, \"Authored\")}\\n` +\n\t\t\t\t`${formatGateFindings(evaluation)}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Authored plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── UpdatePlugin (merge into an existing local plugin) ─────────────────────────\n\nconst updateParams = Type.Object(\n\t{ id: Type.String({ description: \"Id of the existing local plugin to update.\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createUpdatePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof updateParams, AuthorPluginDetails>({\n\t\tname: UPDATE_PLUGIN_TOOL_NAME,\n\t\tlabel: UPDATE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Merge inline-authored capabilities into an EXISTING locally AUTHORED plugin (marketplace-installed plugins \" +\n\t\t\t\"are refused). Skills/commands/subagents are added or replaced by name; hooks and MCP servers are unioned \" +\n\t\t\t\"with what's already there; metadata is overwritten only where you supply it. Additive only — remove a \" +\n\t\t\t\"capability with RemovePluginCapability. Nothing is fetched from a remote. Keep additions as portable as \" +\n\t\t\t\"the original, and in its existing layout. Passive additions apply autonomously; executable additions (hooks, MCP \" +\n\t\t\t\"servers, mutating subagents) require human confirmation. Use ProposePlugin to create.\",\n\t\tpromptSnippet:\n\t\t\t\"Add/replace capabilities in a portable plugin you authored (additive; executable additions ask to confirm).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Hooks cannot be modified in place: they have no name, so supplying a changed command ADDS a second hook alongside the old one (both fire). To change a hook, RemovePluginCapability the old one first, then add the new one here.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t],\n\t\tparameters: updateParams,\n\t\tasync execute(_id, params: Static<typeof updateParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`No plugin named \"${params.id}\" is installed. Use ProposePlugin to create it first.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Authored-only: marketplace installs land in the same directory but don't\n\t\t\t// round-trip losslessly through our emitters (see mergePluginDraft).\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"UpdatePlugin only modifies locally authored plugins — updating a marketplace plugin is a human \" +\n\t\t\t\t\t\t\"action (uninstall it and install a newer version instead).\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (capabilityCount(params) === 0 && !params.version && !params.description) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t\"Nothing to update. Provide skills, commands, subagents, hooks, mcpServers, or metadata.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Gate on the DELTA only — existing executables aren't re-confirmed.\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx);\n\t\t\tif (!gate.ok) return gate.result;\n\n\t\t\t// No model-facing platform selection: a merge keeps the plugin's existing\n\t\t\t// layout (mergePluginDraft defaults to existing.supportPlatform).\n\t\t\tconst result = mergePluginDraft(ctx.cwd, params.id, draftFrom(params.id, params, existing.supportPlatform));\n\t\t\tconst platforms = result.plugin?.supportPlatform ?? existing.supportPlatform;\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest, \"Updated\")}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Updated plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── RemovePluginCapability (subtract from an authored plugin) ─────────────────\n\nconst hookRemovalSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event of the hook(s) to remove, e.g. PreToolUse.\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this matcher.\" })),\n\t\tcommand: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this command.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst removeParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Id of the authored plugin to remove capabilities from.\" }),\n\t\tskills: Type.Optional(Type.Array(Type.String(), { description: \"Skill names to remove.\" })),\n\t\tcommands: Type.Optional(Type.Array(Type.String(), { description: \"Command names to remove.\" })),\n\t\tsubagents: Type.Optional(Type.Array(Type.String(), { description: \"Subagent names to remove.\" })),\n\t\tmcpServers: Type.Optional(Type.Array(Type.String(), { description: \"MCP server names to remove.\" })),\n\t\thooks: Type.Optional(\n\t\t\tType.Array(hookRemovalSchema, {\n\t\t\t\tdescription: \"Hooks to remove, matched by event and narrowed by matcher/command when provided.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\ninterface RemovePluginCapabilityDetails {\n\tid: string;\n\tremoved: string[];\n\tmissing: string[];\n}\n\nexport function createRemovePluginCapabilityToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof removeParams, RemovePluginCapabilityDetails>({\n\t\tname: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tlabel: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Remove named capabilities from a locally AUTHORED plugin — skills, commands, subagents, and MCP servers by \" +\n\t\t\t\"name; hooks by event (narrowed by matcher/command). The subtractive half of UpdatePlugin. Removal is \" +\n\t\t\t\"low-risk and autonomous (deleting capabilities cannot execute code). To remove the whole plugin, use \" +\n\t\t\t\"UninstallPlugin; marketplace-installed plugins are refused here.\",\n\t\tpromptSnippet: \"Remove capabilities from a plugin you authored (low risk; autonomous).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Removal runs autonomously (the low-risk direction) — announce what you removed and why.\",\n\t\t\t\"To CHANGE a hook (hooks have no name to replace by): RemovePluginCapability the old hook, then UpdatePlugin the new one (which asks for confirmation).\",\n\t\t],\n\t\tparameters: removeParams,\n\t\tasync execute(_id, params: Static<typeof removeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst noDetails = (msg: string) => ({\n\t\t\t\tcontent: [{ type: \"text\" as const, text: msg }],\n\t\t\t\tdetails: { id: params.id, removed: [], missing: [] },\n\t\t\t});\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn noDetails(`No plugin named \"${params.id}\" is installed.`);\n\t\t\t}\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn noDetails(\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"RemovePluginCapability only edits locally authored plugins — use UninstallPlugin to remove it entirely.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst requested =\n\t\t\t\t(params.skills?.length ?? 0) +\n\t\t\t\t(params.commands?.length ?? 0) +\n\t\t\t\t(params.subagents?.length ?? 0) +\n\t\t\t\t(params.mcpServers?.length ?? 0) +\n\t\t\t\t(params.hooks?.length ?? 0);\n\t\t\tif (requested === 0) {\n\t\t\t\treturn noDetails(\"Nothing to remove. Name skills, commands, subagents, mcpServers, or hooks.\");\n\t\t\t}\n\n\t\t\tconst result = removeFromPlugin(ctx.cwd, params.id, {\n\t\t\t\tskills: params.skills,\n\t\t\t\tcommands: params.commands,\n\t\t\t\tsubagents: params.subagents,\n\t\t\t\tmcpServers: params.mcpServers,\n\t\t\t\thooks: params.hooks,\n\t\t\t});\n\t\t\tconst lines: string[] = [];\n\t\t\tif (result.removed.length > 0) {\n\t\t\t\tlines.push(`Removed from plugin \"${params.id}\":`, ...result.removed.map((r) => ` ${r}`));\n\t\t\t}\n\t\t\tif (result.missing.length > 0) {\n\t\t\t\tlines.push(`Not found (nothing removed):`, ...result.missing.map((m) => ` ${m}`));\n\t\t\t}\n\t\t\tconst text = lines.join(\"\\n\");\n\t\t\t// Removal takes effect through the reload path, same as UninstallPlugin.\n\t\t\tif (result.removed.length > 0) ctx.requestReloadWhenIdle();\n\t\t\tctx.ui.notify(text, result.removed.length > 0 ? \"info\" : \"warning\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, removed: result.removed, missing: result.missing },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** All three authoring tool definitions, for registration on the top-level agent. */\nexport function createProposePluginToolDefinitions(): ToolDefinition[] {\n\treturn [\n\t\tcreateProposePluginToolDefinition(),\n\t\tcreateUpdatePluginToolDefinition(),\n\t\tcreateRemovePluginCapabilityToolDefinition(),\n\t];\n}\n"]}
|