@oh-my-pi/pi-coding-agent 13.1.2 → 13.2.1
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 +31 -0
- package/package.json +7 -7
- package/scripts/format-prompts.ts +33 -14
- package/src/async/job-manager.ts +43 -1
- package/src/capability/index.ts +1 -2
- package/src/capability/tool.ts +1 -1
- package/src/cli/args.ts +1 -2
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/file-processor.ts +1 -2
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/jupyter-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +1 -1
- package/src/cli/stats-cli.ts +1 -2
- package/src/cli/update-cli.ts +1 -2
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/launch.ts +2 -1
- package/src/commit/agentic/agent.ts +2 -1
- package/src/commit/agentic/index.ts +1 -2
- package/src/commit/agentic/prompts/system.md +3 -3
- package/src/commit/agentic/tools/propose-changelog.ts +30 -19
- package/src/commit/changelog/generate.ts +16 -6
- package/src/commit/changelog/index.ts +2 -1
- package/src/commit/pipeline.ts +1 -2
- package/src/commit/prompts/reduce-system.md +1 -1
- package/src/commit/types.ts +10 -1
- package/src/config/keybindings.ts +1 -2
- package/src/config/model-registry.ts +1 -1
- package/src/config/prompt-templates.ts +14 -2
- package/src/config/settings-schema.ts +10 -0
- package/src/config/settings.ts +25 -2
- package/src/config.ts +1 -2
- package/src/debug/index.ts +1 -1
- package/src/debug/report-bundle.ts +1 -2
- package/src/debug/system-info.ts +1 -2
- package/src/discovery/agents.ts +2 -2
- package/src/discovery/builtin.ts +24 -14
- package/src/discovery/claude-plugins.ts +3 -2
- package/src/discovery/claude.ts +9 -9
- package/src/discovery/codex.ts +3 -3
- package/src/discovery/cursor.ts +5 -4
- package/src/discovery/gemini.ts +5 -5
- package/src/discovery/helpers.ts +47 -69
- package/src/discovery/mcp-json.ts +3 -3
- package/src/discovery/opencode.ts +7 -8
- package/src/discovery/ssh.ts +3 -3
- package/src/discovery/vscode.ts +3 -2
- package/src/discovery/windsurf.ts +3 -2
- package/src/exa/company.ts +1 -1
- package/src/exa/factory.ts +1 -6
- package/src/exa/linkedin.ts +1 -1
- package/src/exa/mcp-client.ts +19 -8
- package/src/exa/search.ts +2 -2
- package/src/exa/types.ts +3 -3
- package/src/exec/bash-executor.ts +2 -1
- package/src/exec/non-interactive-env.ts +43 -0
- package/src/export/custom-share.ts +1 -1
- package/src/export/html/index.ts +1 -2
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/plugins/installer.ts +1 -2
- package/src/extensibility/plugins/loader.ts +1 -2
- package/src/extensibility/plugins/manager.ts +3 -2
- package/src/extensibility/skills.ts +59 -115
- package/src/index.ts +1 -3
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/ipy/executor.ts +1 -2
- package/src/ipy/gateway-coordinator.ts +1 -2
- package/src/ipy/modules.ts +1 -1
- package/src/ipy/runtime.ts +1 -3
- package/src/main.ts +1 -2
- package/src/mcp/config.ts +1 -1
- package/src/mcp/transports/stdio.ts +1 -2
- package/src/memories/index.ts +1 -2
- package/src/modes/components/diff.ts +49 -19
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +8 -2
- package/src/modes/components/footer.ts +1 -2
- package/src/modes/components/status-line/segments.ts +1 -2
- package/src/modes/components/tool-execution.ts +3 -10
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/command-controller.ts +1 -2
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/controllers/ssh-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +2 -3
- package/src/modes/shared.ts +1 -2
- package/src/modes/theme/theme.ts +1 -2
- package/src/patch/index.ts +1 -25
- package/src/prompts/agents/designer.md +7 -10
- package/src/prompts/agents/explore.md +15 -23
- package/src/prompts/agents/init.md +23 -23
- package/src/prompts/agents/plan.md +14 -77
- package/src/prompts/agents/reviewer.md +6 -5
- package/src/prompts/agents/task.md +13 -11
- package/src/prompts/compaction/branch-summary.md +3 -3
- package/src/prompts/compaction/compaction-short-summary.md +7 -7
- package/src/prompts/compaction/compaction-summary-context.md +1 -1
- package/src/prompts/compaction/compaction-summary.md +5 -5
- package/src/prompts/compaction/compaction-turn-prefix.md +3 -3
- package/src/prompts/compaction/compaction-update-summary.md +11 -11
- package/src/prompts/memories/consolidation.md +5 -5
- package/src/prompts/memories/read-path.md +6 -6
- package/src/prompts/memories/stage_one_input.md +1 -1
- package/src/prompts/memories/stage_one_system.md +5 -5
- package/src/prompts/review-request.md +4 -4
- package/src/prompts/system/agent-creation-architect.md +17 -17
- package/src/prompts/system/agent-creation-user.md +2 -2
- package/src/prompts/system/custom-system-prompt.md +4 -4
- package/src/prompts/system/plan-mode-active.md +20 -20
- package/src/prompts/system/plan-mode-approved.md +7 -7
- package/src/prompts/system/plan-mode-reference.md +2 -2
- package/src/prompts/system/plan-mode-subagent.md +8 -8
- package/src/prompts/system/subagent-submit-reminder.md +5 -5
- package/src/prompts/system/subagent-system-prompt.md +29 -22
- package/src/prompts/system/subagent-user-prompt.md +7 -3
- package/src/prompts/system/summarization-system.md +1 -1
- package/src/prompts/system/system-prompt.md +201 -226
- package/src/prompts/system/title-system.md +2 -2
- package/src/prompts/system/ttsr-interrupt.md +1 -1
- package/src/prompts/system/web-search.md +16 -16
- package/src/prompts/tools/ask.md +1 -3
- package/src/prompts/tools/await.md +2 -4
- package/src/prompts/tools/bash.md +5 -7
- package/src/prompts/tools/browser.md +4 -6
- package/src/prompts/tools/calculator.md +1 -3
- package/src/prompts/tools/cancel-job.md +2 -4
- package/src/prompts/tools/exit-plan-mode.md +7 -7
- package/src/prompts/tools/fetch.md +0 -2
- package/src/prompts/tools/find.md +3 -5
- package/src/prompts/tools/gemini-image.md +6 -22
- package/src/prompts/tools/grep.md +4 -6
- package/src/prompts/tools/hashline.md +12 -15
- package/src/prompts/tools/lsp.md +1 -3
- package/src/prompts/tools/patch.md +7 -9
- package/src/prompts/tools/python.md +10 -14
- package/src/prompts/tools/read.md +0 -2
- package/src/prompts/tools/replace.md +5 -7
- package/src/prompts/tools/ssh.md +3 -5
- package/src/prompts/tools/task.md +6 -8
- package/src/prompts/tools/todo-write.md +7 -9
- package/src/prompts/tools/web-search.md +3 -5
- package/src/prompts/tools/write.md +3 -5
- package/src/sdk.ts +1 -2
- package/src/session/agent-session.ts +10 -26
- package/src/session/agent-storage.ts +1 -2
- package/src/session/history-storage.ts +1 -2
- package/src/session/session-manager.ts +10 -2
- package/src/ssh/connection-manager.ts +11 -2
- package/src/ssh/sshfs-mount.ts +7 -1
- package/src/system-prompt.ts +25 -103
- package/src/task/agents.ts +1 -1
- package/src/task/worktree.ts +1 -2
- package/src/tools/ask.ts +0 -1
- package/src/tools/await-tool.ts +6 -3
- package/src/tools/bash-interactive.ts +2 -45
- package/src/tools/bash.ts +5 -5
- package/src/tools/browser.ts +1 -2
- package/src/tools/gemini-image.ts +8 -28
- package/src/tools/json-tree.ts +2 -1
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +1 -2
- package/src/tools/render-utils.ts +5 -2
- package/src/tools/todo-write.ts +11 -9
- package/src/utils/tools-manager.ts +1 -2
- package/src/web/scrapers/artifacthub.ts +2 -1
- package/src/web/scrapers/aur.ts +2 -1
- package/src/web/scrapers/biorxiv.ts +2 -1
- package/src/web/scrapers/bluesky.ts +2 -1
- package/src/web/scrapers/chocolatey.ts +2 -1
- package/src/web/scrapers/cisa-kev.ts +2 -1
- package/src/web/scrapers/clojars.ts +2 -1
- package/src/web/scrapers/coingecko.ts +2 -1
- package/src/web/scrapers/crates-io.ts +2 -1
- package/src/web/scrapers/crossref.ts +2 -1
- package/src/web/scrapers/discogs.ts +3 -1
- package/src/web/scrapers/discourse.ts +2 -1
- package/src/web/scrapers/dockerhub.ts +2 -1
- package/src/web/scrapers/fdroid.ts +2 -1
- package/src/web/scrapers/firefox-addons.ts +2 -1
- package/src/web/scrapers/flathub.ts +2 -1
- package/src/web/scrapers/gitlab.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +2 -1
- package/src/web/scrapers/hackage.ts +2 -1
- package/src/web/scrapers/hackernews.ts +2 -1
- package/src/web/scrapers/hex.ts +2 -1
- package/src/web/scrapers/huggingface.ts +2 -1
- package/src/web/scrapers/jetbrains-marketplace.ts +2 -1
- package/src/web/scrapers/lemmy.ts +2 -1
- package/src/web/scrapers/lobsters.ts +2 -1
- package/src/web/scrapers/mastodon.ts +2 -1
- package/src/web/scrapers/maven.ts +2 -1
- package/src/web/scrapers/mdn.ts +2 -1
- package/src/web/scrapers/metacpan.ts +2 -1
- package/src/web/scrapers/musicbrainz.ts +3 -1
- package/src/web/scrapers/npm.ts +2 -1
- package/src/web/scrapers/nuget.ts +2 -1
- package/src/web/scrapers/nvd.ts +2 -1
- package/src/web/scrapers/ollama.ts +2 -1
- package/src/web/scrapers/open-vsx.ts +2 -1
- package/src/web/scrapers/opencorporates.ts +2 -1
- package/src/web/scrapers/openlibrary.ts +2 -1
- package/src/web/scrapers/orcid.ts +3 -1
- package/src/web/scrapers/osv.ts +2 -1
- package/src/web/scrapers/packagist.ts +2 -1
- package/src/web/scrapers/pub-dev.ts +2 -1
- package/src/web/scrapers/pubmed.ts +2 -1
- package/src/web/scrapers/pypi.ts +2 -1
- package/src/web/scrapers/rawg.ts +2 -8
- package/src/web/scrapers/reddit.ts +2 -1
- package/src/web/scrapers/repology.ts +2 -1
- package/src/web/scrapers/rfc.ts +2 -1
- package/src/web/scrapers/rubygems.ts +2 -1
- package/src/web/scrapers/searchcode.ts +2 -1
- package/src/web/scrapers/sec-edgar.ts +2 -1
- package/src/web/scrapers/semantic-scholar.ts +2 -1
- package/src/web/scrapers/snapcraft.ts +2 -1
- package/src/web/scrapers/sourcegraph.ts +2 -1
- package/src/web/scrapers/spdx.ts +2 -1
- package/src/web/scrapers/stackoverflow.ts +2 -1
- package/src/web/scrapers/terraform.ts +2 -1
- package/src/web/scrapers/types.ts +0 -11
- package/src/web/scrapers/vimeo.ts +2 -1
- package/src/web/scrapers/vscode-marketplace.ts +2 -1
- package/src/web/scrapers/w3c.ts +2 -1
- package/src/web/scrapers/wikidata.ts +2 -1
- package/src/web/search/index.ts +10 -14
- package/src/web/search/provider.ts +2 -2
- package/src/web/search/providers/codex.ts +1 -2
- package/src/web/search/providers/exa.ts +1 -6
- package/src/web/search/providers/gemini.ts +1 -1
- package/src/web/search/providers/perplexity.ts +1 -2
- package/src/web/search/providers/utils.ts +1 -1
package/src/discovery/helpers.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
1
2
|
import * as path from "node:path";
|
|
2
3
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
3
4
|
import { FileType, glob } from "@oh-my-pi/pi-natives";
|
|
4
|
-
import { CONFIG_DIR_NAME } from "@oh-my-pi/pi-utils
|
|
5
|
+
import { CONFIG_DIR_NAME, tryParseJson } from "@oh-my-pi/pi-utils";
|
|
5
6
|
import { readFile } from "../capability/fs";
|
|
6
7
|
import { parseRuleConditionAndScope, type Rule, type RuleFrontmatter } from "../capability/rule";
|
|
7
8
|
import type { Skill, SkillFrontmatter } from "../capability/skill";
|
|
@@ -268,68 +269,56 @@ async function globIf(
|
|
|
268
269
|
}
|
|
269
270
|
}
|
|
270
271
|
|
|
271
|
-
export
|
|
272
|
+
export interface ScanSkillsFromDirOptions {
|
|
273
|
+
dir: string;
|
|
274
|
+
providerId: string;
|
|
275
|
+
level: "user" | "project";
|
|
276
|
+
requireDescription?: boolean;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export async function scanSkillsFromDir(
|
|
272
280
|
_ctx: LoadContext,
|
|
273
|
-
options:
|
|
274
|
-
dir: string;
|
|
275
|
-
providerId: string;
|
|
276
|
-
level: "user" | "project";
|
|
277
|
-
requireDescription?: boolean;
|
|
278
|
-
},
|
|
281
|
+
options: ScanSkillsFromDirOptions,
|
|
279
282
|
): Promise<LoadResult<Skill>> {
|
|
280
283
|
const items: Skill[] = [];
|
|
281
284
|
const warnings: string[] = [];
|
|
282
285
|
const { dir, level, providerId, requireDescription = false } = options;
|
|
283
|
-
// Use native glob to find all SKILL.md files one level deep
|
|
284
|
-
// Pattern */SKILL.md matches <dir>/<subdir>/SKILL.md
|
|
285
|
-
const discoveredMatches = new Set<string>();
|
|
286
|
-
for (const match of await globIf(dir, "*/SKILL.md", FileType.File)) {
|
|
287
|
-
discoveredMatches.add(match.path);
|
|
288
|
-
}
|
|
289
|
-
for (const match of await globIf(dir, "*", FileType.Dir, false)) {
|
|
290
|
-
const skillRelPath = `${match.path}/SKILL.md`;
|
|
291
|
-
const content = await readFile(path.join(dir, skillRelPath));
|
|
292
|
-
if (content !== null) {
|
|
293
|
-
discoveredMatches.add(skillRelPath);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
const matches = [...discoveredMatches].map(path => ({ path }));
|
|
297
|
-
if (matches.length === 0) {
|
|
298
|
-
return { items, warnings };
|
|
299
|
-
}
|
|
300
286
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const content = await readFile(
|
|
306
|
-
if (!content)
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
const { frontmatter, body } = parseFrontmatter(content, { source: skillFile });
|
|
287
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
288
|
+
|
|
289
|
+
const loadSkill = async (skillPath: string) => {
|
|
290
|
+
try {
|
|
291
|
+
const content = await readFile(skillPath);
|
|
292
|
+
if (!content) return;
|
|
293
|
+
const { frontmatter, body } = parseFrontmatter(content, { source: skillPath });
|
|
310
294
|
if (requireDescription && !frontmatter.description) {
|
|
311
|
-
return
|
|
295
|
+
return;
|
|
312
296
|
}
|
|
297
|
+
const skillDirName = path.basename(path.dirname(skillPath));
|
|
298
|
+
items.push({
|
|
299
|
+
name: (frontmatter.name as string) || skillDirName,
|
|
300
|
+
path: skillPath,
|
|
301
|
+
content: body,
|
|
302
|
+
frontmatter: frontmatter as SkillFrontmatter,
|
|
303
|
+
level,
|
|
304
|
+
_source: createSourceMeta(providerId, skillPath, level),
|
|
305
|
+
});
|
|
306
|
+
} catch {
|
|
307
|
+
warnings.push(`Failed to read skill file: ${skillPath}`);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
313
310
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
level,
|
|
323
|
-
_source: createSourceMeta(providerId, skillFile, level),
|
|
324
|
-
},
|
|
325
|
-
warning: null as string | null,
|
|
326
|
-
};
|
|
327
|
-
}),
|
|
328
|
-
);
|
|
329
|
-
for (const result of results) {
|
|
330
|
-
if (result.warning) warnings.push(result.warning);
|
|
331
|
-
if (result.item) items.push(result.item);
|
|
311
|
+
const work = [];
|
|
312
|
+
for (const entry of entries) {
|
|
313
|
+
if (entry.name.startsWith(".")) continue;
|
|
314
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
315
|
+
const skillPath = path.join(dir, entry.name, "SKILL.md");
|
|
316
|
+
if (fs.existsSync(skillPath)) {
|
|
317
|
+
work.push(loadSkill(skillPath));
|
|
318
|
+
}
|
|
332
319
|
}
|
|
320
|
+
await Promise.all(work);
|
|
321
|
+
|
|
333
322
|
return { items, warnings };
|
|
334
323
|
}
|
|
335
324
|
|
|
@@ -337,7 +326,7 @@ export async function loadSkillsFromDir(
|
|
|
337
326
|
* Expand environment variables in a string.
|
|
338
327
|
* Supports ${VAR} and ${VAR:-default} syntax.
|
|
339
328
|
*/
|
|
340
|
-
|
|
329
|
+
function expandEnvVars(value: string, extraEnv?: Record<string, string>): string {
|
|
341
330
|
return value.replace(/\$\{([^}:]+)(?::-([^}]*))?\}/g, (_, varName: string, defaultValue?: string) => {
|
|
342
331
|
const envValue = extraEnv?.[varName] ?? Bun.env[varName];
|
|
343
332
|
if (envValue !== undefined) return envValue;
|
|
@@ -447,17 +436,6 @@ export async function loadFilesFromDir<T>(
|
|
|
447
436
|
return { items, warnings };
|
|
448
437
|
}
|
|
449
438
|
|
|
450
|
-
/**
|
|
451
|
-
* Parse JSON safely.
|
|
452
|
-
*/
|
|
453
|
-
export function parseJSON<T>(content: string): T | null {
|
|
454
|
-
try {
|
|
455
|
-
return JSON.parse(content) as T;
|
|
456
|
-
} catch {
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
439
|
/**
|
|
462
440
|
* Calculate depth of target directory relative to current working directory.
|
|
463
441
|
* Depth is the number of directory levels from cwd to target.
|
|
@@ -480,7 +458,7 @@ async function readExtensionModuleManifest(
|
|
|
480
458
|
const content = await readFile(packageJsonPath);
|
|
481
459
|
if (!content) return null;
|
|
482
460
|
|
|
483
|
-
const pkg =
|
|
461
|
+
const pkg = tryParseJson<{ omp?: ExtensionModuleManifest; pi?: ExtensionModuleManifest }>(content);
|
|
484
462
|
const manifest = pkg?.omp ?? pkg?.pi;
|
|
485
463
|
if (manifest && typeof manifest === "object") {
|
|
486
464
|
return manifest;
|
|
@@ -506,9 +484,9 @@ export async function discoverExtensionModulePaths(_ctx: LoadContext, dir: strin
|
|
|
506
484
|
// 1. Direct *.ts or *.js files
|
|
507
485
|
globIf(dir, "*.{ts,js}", FileType.File, false),
|
|
508
486
|
// 2. Subdirectory index files
|
|
509
|
-
globIf(dir, "*/index.{ts,js}", FileType.File),
|
|
487
|
+
globIf(dir, "*/index.{ts,js}", FileType.File, false),
|
|
510
488
|
// 3. Subdirectory package.json files
|
|
511
|
-
globIf(dir, "*/package.json", FileType.File),
|
|
489
|
+
globIf(dir, "*/package.json", FileType.File, false),
|
|
512
490
|
]);
|
|
513
491
|
|
|
514
492
|
// Process direct files
|
|
@@ -617,7 +595,7 @@ export interface ClaudePluginRoot {
|
|
|
617
595
|
* Parse Claude Code installed_plugins.json content.
|
|
618
596
|
*/
|
|
619
597
|
export function parseClaudePluginsRegistry(content: string): ClaudePluginsRegistry | null {
|
|
620
|
-
const data =
|
|
598
|
+
const data = tryParseJson<ClaudePluginsRegistry>(content);
|
|
621
599
|
if (!data || typeof data !== "object") return null;
|
|
622
600
|
if (
|
|
623
601
|
typeof data.version !== "number" ||
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
* Priority: 5 (low, as this is a fallback after tool-specific providers)
|
|
8
8
|
*/
|
|
9
9
|
import * as path from "node:path";
|
|
10
|
-
import { logger } from "@oh-my-pi/pi-utils";
|
|
10
|
+
import { logger, tryParseJson } from "@oh-my-pi/pi-utils";
|
|
11
11
|
import { registerProvider } from "../capability";
|
|
12
12
|
import { readFile } from "../capability/fs";
|
|
13
13
|
import { type MCPServer, mcpCapability } from "../capability/mcp";
|
|
14
14
|
import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
|
|
15
|
-
import { createSourceMeta, expandEnvVarsDeep
|
|
15
|
+
import { createSourceMeta, expandEnvVarsDeep } from "./helpers";
|
|
16
16
|
|
|
17
17
|
const PROVIDER_ID = "mcp-json";
|
|
18
18
|
const DISPLAY_NAME = "MCP Config";
|
|
@@ -115,7 +115,7 @@ async function loadMCPJsonFile(
|
|
|
115
115
|
return { items, warnings };
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
const config =
|
|
118
|
+
const config = tryParseJson<MCPConfigFile>(content);
|
|
119
119
|
if (!config) {
|
|
120
120
|
warnings.push(`Failed to parse JSON in ${path}`);
|
|
121
121
|
return { items, warnings };
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Priority: 55 (tool-specific provider)
|
|
17
17
|
*/
|
|
18
18
|
import * as path from "node:path";
|
|
19
|
-
import { logger } from "@oh-my-pi/pi-utils";
|
|
19
|
+
import { logger, tryParseJson } from "@oh-my-pi/pi-utils";
|
|
20
20
|
import { registerProvider } from "../capability";
|
|
21
21
|
import { type ContextFile, contextFileCapability } from "../capability/context-file";
|
|
22
22
|
import { type ExtensionModule, extensionModuleCapability } from "../capability/extension-module";
|
|
@@ -35,8 +35,7 @@ import {
|
|
|
35
35
|
getProjectPath,
|
|
36
36
|
getUserPath,
|
|
37
37
|
loadFilesFromDir,
|
|
38
|
-
|
|
39
|
-
parseJSON,
|
|
38
|
+
scanSkillsFromDir,
|
|
40
39
|
} from "./helpers";
|
|
41
40
|
|
|
42
41
|
const PROVIDER_ID = "opencode";
|
|
@@ -51,7 +50,7 @@ async function loadJsonConfig(configPath: string): Promise<Record<string, unknow
|
|
|
51
50
|
const content = await readFile(configPath);
|
|
52
51
|
if (!content) return null;
|
|
53
52
|
|
|
54
|
-
const parsed =
|
|
53
|
+
const parsed = tryParseJson<Record<string, unknown>>(content);
|
|
55
54
|
if (!parsed) {
|
|
56
55
|
logger.warn("Failed to parse OpenCode JSON config", { path: configPath });
|
|
57
56
|
return null;
|
|
@@ -190,7 +189,7 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
190
189
|
|
|
191
190
|
if (userSkillsDir) {
|
|
192
191
|
promises.push(
|
|
193
|
-
|
|
192
|
+
scanSkillsFromDir(ctx, {
|
|
194
193
|
dir: userSkillsDir,
|
|
195
194
|
providerId: PROVIDER_ID,
|
|
196
195
|
level: "user",
|
|
@@ -200,7 +199,7 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
200
199
|
|
|
201
200
|
if (projectSkillsDir) {
|
|
202
201
|
promises.push(
|
|
203
|
-
|
|
202
|
+
scanSkillsFromDir(ctx, {
|
|
204
203
|
dir: projectSkillsDir,
|
|
205
204
|
providerId: PROVIDER_ID,
|
|
206
205
|
level: "project",
|
|
@@ -307,7 +306,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
307
306
|
if (userConfigPath) {
|
|
308
307
|
const content = await readFile(userConfigPath);
|
|
309
308
|
if (content) {
|
|
310
|
-
const parsed =
|
|
309
|
+
const parsed = tryParseJson<Record<string, unknown>>(content);
|
|
311
310
|
if (parsed) {
|
|
312
311
|
items.push({
|
|
313
312
|
path: userConfigPath,
|
|
@@ -325,7 +324,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
325
324
|
const projectConfigPath = path.join(ctx.cwd, "opencode.json");
|
|
326
325
|
const content = await readFile(projectConfigPath);
|
|
327
326
|
if (content) {
|
|
328
|
-
const parsed =
|
|
327
|
+
const parsed = tryParseJson<Record<string, unknown>>(content);
|
|
329
328
|
if (parsed) {
|
|
330
329
|
items.push({
|
|
331
330
|
path: projectConfigPath,
|
package/src/discovery/ssh.ts
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* Priority: 5 (low, project/user config discovery)
|
|
6
6
|
*/
|
|
7
7
|
import * as path from "node:path";
|
|
8
|
-
import { getSSHConfigPath } from "@oh-my-pi/pi-utils
|
|
8
|
+
import { getSSHConfigPath, tryParseJson } from "@oh-my-pi/pi-utils";
|
|
9
9
|
import { registerProvider } from "../capability";
|
|
10
10
|
import { readFile } from "../capability/fs";
|
|
11
11
|
import { type SSHHost, sshCapability } from "../capability/ssh";
|
|
12
12
|
import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
|
|
13
13
|
import { expandTilde } from "../tools/path-utils";
|
|
14
|
-
import { createSourceMeta, expandEnvVarsDeep
|
|
14
|
+
import { createSourceMeta, expandEnvVarsDeep } from "./helpers";
|
|
15
15
|
|
|
16
16
|
const PROVIDER_ID = "ssh-json";
|
|
17
17
|
const DISPLAY_NAME = "SSH Config";
|
|
@@ -95,7 +95,7 @@ async function loadSshJsonFile(
|
|
|
95
95
|
if (content === null) {
|
|
96
96
|
return { items, warnings };
|
|
97
97
|
}
|
|
98
|
-
const parsed =
|
|
98
|
+
const parsed = tryParseJson<SSHConfigFile>(content);
|
|
99
99
|
if (!parsed) {
|
|
100
100
|
warnings.push(`Failed to parse JSON in ${filePath}`);
|
|
101
101
|
return { items, warnings };
|
package/src/discovery/vscode.ts
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* Loads config from `.vscode` directory (project-only).
|
|
5
5
|
* Supports MCP server discovery from `mcp.json` with nested `mcp.servers` structure.
|
|
6
6
|
*/
|
|
7
|
+
import { tryParseJson } from "@oh-my-pi/pi-utils";
|
|
7
8
|
import { registerProvider } from "../capability";
|
|
8
9
|
import { readFile } from "../capability/fs";
|
|
9
10
|
import { type MCPServer, mcpCapability } from "../capability/mcp";
|
|
10
11
|
import type { LoadContext, LoadResult } from "../capability/types";
|
|
11
|
-
import { createSourceMeta, expandEnvVarsDeep, getProjectPath
|
|
12
|
+
import { createSourceMeta, expandEnvVarsDeep, getProjectPath } from "./helpers";
|
|
12
13
|
|
|
13
14
|
const PROVIDER_ID = "vscode";
|
|
14
15
|
const DISPLAY_NAME = "VS Code";
|
|
@@ -57,7 +58,7 @@ async function loadMCPConfig(
|
|
|
57
58
|
return { items, warnings };
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
const parsed =
|
|
61
|
+
const parsed = tryParseJson<{ mcp?: { servers?: Record<string, unknown> } }>(content);
|
|
61
62
|
if (!parsed) {
|
|
62
63
|
warnings.push(`Invalid JSON in ${path}`);
|
|
63
64
|
return { items, warnings };
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* - Rules from .windsurf/rules/*.md and ~/.codeium/windsurf/memories/global_rules.md
|
|
11
11
|
* - Legacy .windsurfrules file
|
|
12
12
|
*/
|
|
13
|
+
|
|
14
|
+
import { tryParseJson } from "@oh-my-pi/pi-utils";
|
|
13
15
|
import { registerProvider } from "../capability";
|
|
14
16
|
import { readFile } from "../capability/fs";
|
|
15
17
|
import { type MCPServer, mcpCapability } from "../capability/mcp";
|
|
@@ -22,7 +24,6 @@ import {
|
|
|
22
24
|
getProjectPath,
|
|
23
25
|
getUserPath,
|
|
24
26
|
loadFilesFromDir,
|
|
25
|
-
parseJSON,
|
|
26
27
|
} from "./helpers";
|
|
27
28
|
|
|
28
29
|
const PROVIDER_ID = "windsurf";
|
|
@@ -78,7 +79,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
78
79
|
for (const { content, path, scope } of configs) {
|
|
79
80
|
if (!content || !path) continue;
|
|
80
81
|
|
|
81
|
-
const config =
|
|
82
|
+
const config = tryParseJson<{ mcpServers?: Record<string, unknown> }>(content);
|
|
82
83
|
if (!config?.mcpServers) continue;
|
|
83
84
|
|
|
84
85
|
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
package/src/exa/company.ts
CHANGED
package/src/exa/factory.ts
CHANGED
|
@@ -31,12 +31,7 @@ export function createExaTool(
|
|
|
31
31
|
async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
|
|
32
32
|
try {
|
|
33
33
|
const apiKey = await findApiKey();
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
|
|
37
|
-
details: { error: "EXA_API_KEY not found", toolName: name },
|
|
38
|
-
};
|
|
39
|
-
}
|
|
34
|
+
// Exa MCP endpoint is publicly accessible; API key is optional
|
|
40
35
|
const args = transformParams ? transformParams(params as Record<string, unknown>) : params;
|
|
41
36
|
const response = await callExaTool(mcpToolName, args, apiKey);
|
|
42
37
|
|
package/src/exa/linkedin.ts
CHANGED
package/src/exa/mcp-client.ts
CHANGED
|
@@ -17,8 +17,11 @@ export function findApiKey(): string | null {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/** Fetch available tools from Exa MCP */
|
|
20
|
-
export async function fetchExaTools(apiKey: string, toolNames: string[]): Promise<MCPTool[]> {
|
|
21
|
-
const
|
|
20
|
+
export async function fetchExaTools(apiKey: string | null, toolNames: string[]): Promise<MCPTool[]> {
|
|
21
|
+
const params = new URLSearchParams();
|
|
22
|
+
if (apiKey) params.set("exaApiKey", apiKey);
|
|
23
|
+
params.set("toolNames", toolNames.join(","));
|
|
24
|
+
const url = `https://mcp.exa.ai/mcp?${params.toString()}`;
|
|
22
25
|
const response = (await callMCP(url, "tools/list")) as MCPToolsResponse;
|
|
23
26
|
|
|
24
27
|
if (response.error) {
|
|
@@ -43,8 +46,15 @@ export async function fetchWebsetsTools(apiKey: string): Promise<MCPTool[]> {
|
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
/** Call a tool on Exa MCP (simplified: toolName as first arg for easier use) */
|
|
46
|
-
export async function callExaTool(
|
|
47
|
-
|
|
49
|
+
export async function callExaTool(
|
|
50
|
+
toolName: string,
|
|
51
|
+
args: Record<string, unknown>,
|
|
52
|
+
apiKey: string | null,
|
|
53
|
+
): Promise<unknown> {
|
|
54
|
+
const params = new URLSearchParams();
|
|
55
|
+
if (apiKey) params.set("exaApiKey", apiKey);
|
|
56
|
+
params.set("tools", toolName);
|
|
57
|
+
const url = `https://mcp.exa.ai/mcp?${params.toString()}`;
|
|
48
58
|
const response = (await callMCP(url, "tools/call", {
|
|
49
59
|
name: toolName,
|
|
50
60
|
arguments: args,
|
|
@@ -174,15 +184,16 @@ export class MCPWrappedTool implements CustomTool<TSchema, ExaRenderDetails> {
|
|
|
174
184
|
): Promise<CustomToolResult<ExaRenderDetails>> {
|
|
175
185
|
try {
|
|
176
186
|
const apiKey = await findApiKey();
|
|
177
|
-
|
|
187
|
+
// Websets tools require an API key; basic Exa MCP tools work without one
|
|
188
|
+
if (!apiKey && this.config.isWebsetsTool) {
|
|
178
189
|
return {
|
|
179
|
-
content: [{ type: "text" as const, text: "Error: EXA_API_KEY
|
|
180
|
-
details: { error: "EXA_API_KEY
|
|
190
|
+
content: [{ type: "text" as const, text: "Error: EXA_API_KEY required for Websets tools" }],
|
|
191
|
+
details: { error: "EXA_API_KEY required for Websets tools", toolName: this.config.name },
|
|
181
192
|
};
|
|
182
193
|
}
|
|
183
194
|
|
|
184
195
|
const response = this.config.isWebsetsTool
|
|
185
|
-
? await callWebsetsTool(apiKey
|
|
196
|
+
? await callWebsetsTool(apiKey!, this.config.mcpToolName, params as Record<string, unknown>)
|
|
186
197
|
: await callExaTool(this.config.mcpToolName, params as Record<string, unknown>, apiKey);
|
|
187
198
|
|
|
188
199
|
if (isSearchResponse(response)) {
|
package/src/exa/search.ts
CHANGED
|
@@ -143,7 +143,7 @@ Similar parameters to exa_search, optimized for research depth.`,
|
|
|
143
143
|
),
|
|
144
144
|
}),
|
|
145
145
|
"web_search_exa",
|
|
146
|
-
{ transformParams: params => ({ ...params, type: "
|
|
146
|
+
{ transformParams: params => ({ ...params, type: "auto" }) },
|
|
147
147
|
);
|
|
148
148
|
|
|
149
149
|
/** exa_search_code - Code-focused search */
|
|
@@ -195,7 +195,7 @@ Parameters:
|
|
|
195
195
|
}),
|
|
196
196
|
),
|
|
197
197
|
}),
|
|
198
|
-
"
|
|
198
|
+
"crawling_exa",
|
|
199
199
|
);
|
|
200
200
|
|
|
201
201
|
export const searchTools: CustomTool<any, ExaRenderDetails>[] = [
|
package/src/exa/types.ts
CHANGED
|
@@ -122,11 +122,11 @@ export const EXA_TOOL_MAPPINGS = {
|
|
|
122
122
|
// Search tools
|
|
123
123
|
web_search_exa: "exa_search",
|
|
124
124
|
get_code_context_exa: "exa_search_code",
|
|
125
|
-
|
|
125
|
+
crawling_exa: "exa_crawl",
|
|
126
126
|
// LinkedIn
|
|
127
|
-
|
|
127
|
+
linkedin_search_exa: "exa_linkedin",
|
|
128
128
|
// Company
|
|
129
|
-
|
|
129
|
+
company_research_exa: "exa_company",
|
|
130
130
|
// Researcher
|
|
131
131
|
deep_researcher_start: "exa_researcher_start",
|
|
132
132
|
deep_researcher_check: "exa_researcher_poll",
|
|
@@ -7,6 +7,7 @@ import { Shell } from "@oh-my-pi/pi-natives";
|
|
|
7
7
|
import { Settings } from "../config/settings";
|
|
8
8
|
import { OutputSink } from "../session/streaming-output";
|
|
9
9
|
import { getOrCreateSnapshot } from "../utils/shell-snapshot";
|
|
10
|
+
import { NON_INTERACTIVE_ENV } from "./non-interactive-env";
|
|
10
11
|
|
|
11
12
|
export interface BashExecutorOptions {
|
|
12
13
|
cwd?: string;
|
|
@@ -97,7 +98,7 @@ export async function executeBash(command: string, options?: BashExecutorOptions
|
|
|
97
98
|
{
|
|
98
99
|
command: finalCommand,
|
|
99
100
|
cwd: options?.cwd,
|
|
100
|
-
env: options?.env,
|
|
101
|
+
env: options?.env ? { ...NON_INTERACTIVE_ENV, ...options.env } : NON_INTERACTIVE_ENV,
|
|
101
102
|
timeoutMs: options?.timeout,
|
|
102
103
|
signal,
|
|
103
104
|
},
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const NON_INTERACTIVE_ENV: Readonly<Record<string, string>> = {
|
|
2
|
+
// Disable pagers so commands don't block on interactive views.
|
|
3
|
+
PAGER: "cat",
|
|
4
|
+
GIT_PAGER: "cat",
|
|
5
|
+
MANPAGER: "cat",
|
|
6
|
+
SYSTEMD_PAGER: "cat",
|
|
7
|
+
BAT_PAGER: "cat",
|
|
8
|
+
DELTA_PAGER: "cat",
|
|
9
|
+
GH_PAGER: "cat",
|
|
10
|
+
GLAB_PAGER: "cat",
|
|
11
|
+
PSQL_PAGER: "cat",
|
|
12
|
+
MYSQL_PAGER: "cat",
|
|
13
|
+
AWS_PAGER: "",
|
|
14
|
+
HOMEBREW_PAGER: "cat",
|
|
15
|
+
LESS: "FRX",
|
|
16
|
+
// Disable editor and terminal credential prompts.
|
|
17
|
+
GIT_EDITOR: "true",
|
|
18
|
+
VISUAL: "true",
|
|
19
|
+
EDITOR: "true",
|
|
20
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
21
|
+
SSH_ASKPASS: "/usr/bin/false",
|
|
22
|
+
CI: "1",
|
|
23
|
+
// Package manager defaults for unattended execution.
|
|
24
|
+
npm_config_yes: "true",
|
|
25
|
+
npm_config_update_notifier: "false",
|
|
26
|
+
npm_config_fund: "false",
|
|
27
|
+
npm_config_audit: "false",
|
|
28
|
+
npm_config_progress: "false",
|
|
29
|
+
PNPM_DISABLE_SELF_UPDATE_CHECK: "true",
|
|
30
|
+
PNPM_UPDATE_NOTIFIER: "false",
|
|
31
|
+
YARN_ENABLE_TELEMETRY: "0",
|
|
32
|
+
YARN_ENABLE_PROGRESS_BARS: "0",
|
|
33
|
+
// Cross-language/tooling non-interactive defaults.
|
|
34
|
+
CARGO_TERM_PROGRESS_WHEN: "never",
|
|
35
|
+
DEBIAN_FRONTEND: "noninteractive",
|
|
36
|
+
PIP_NO_INPUT: "1",
|
|
37
|
+
PIP_DISABLE_PIP_VERSION_CHECK: "1",
|
|
38
|
+
TF_INPUT: "0",
|
|
39
|
+
TF_IN_AUTOMATION: "1",
|
|
40
|
+
GH_PROMPT_DISABLED: "1",
|
|
41
|
+
COMPOSER_NO_INTERACTION: "1",
|
|
42
|
+
CLOUDSDK_CORE_DISABLE_PROMPTS: "1",
|
|
43
|
+
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
-
import { getAgentDir } from "@oh-my-pi/pi-utils
|
|
9
|
+
import { getAgentDir } from "@oh-my-pi/pi-utils";
|
|
10
10
|
|
|
11
11
|
export interface CustomShareResult {
|
|
12
12
|
/** URL to display/open (optional - script may handle everything itself) */
|
package/src/export/html/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import type { AgentState } from "@oh-my-pi/pi-agent-core";
|
|
3
|
-
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
|
3
|
+
import { APP_NAME, isEnoent } from "@oh-my-pi/pi-utils";
|
|
5
4
|
import { getResolvedThemeColors, getThemeExportColors } from "../../modes/theme/theme";
|
|
6
5
|
import { type SessionEntry, type SessionHeader, SessionManager } from "../../session/session-manager";
|
|
7
6
|
// Pre-generated template (created by scripts/generate-template.ts at publish time)
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import * as piCodingAgent from "@oh-my-pi/pi-coding-agent";
|
|
10
|
-
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
11
|
-
import { getAgentDir, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
10
|
+
import { getAgentDir, getProjectDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
12
11
|
import * as typebox from "@sinclair/typebox";
|
|
13
12
|
import { getConfigDirs } from "../../config";
|
|
14
13
|
import { execCommand } from "../../exec/exec";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import { getAgentDir, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
3
|
+
import { getAgentDir, getProjectDir, isEnoent } from "@oh-my-pi/pi-utils";
|
|
5
4
|
import { extractPackageName } from "./parser";
|
|
6
5
|
import type { InstalledPlugin } from "./types";
|
|
7
6
|
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
-
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
10
|
-
import { getPluginsLockfile, getPluginsNodeModules, getPluginsPackageJson } from "@oh-my-pi/pi-utils/dirs";
|
|
9
|
+
import { getPluginsLockfile, getPluginsNodeModules, getPluginsPackageJson, isEnoent } from "@oh-my-pi/pi-utils";
|
|
11
10
|
import { getConfigDirPaths } from "../../config";
|
|
12
11
|
import type { InstalledPlugin, PluginManifest, PluginRuntimeConfig, ProjectPluginOverrides } from "./types";
|
|
13
12
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
4
3
|
import {
|
|
5
4
|
getPluginsDir,
|
|
6
5
|
getPluginsLockfile,
|
|
@@ -8,7 +7,9 @@ import {
|
|
|
8
7
|
getPluginsPackageJson,
|
|
9
8
|
getProjectDir,
|
|
10
9
|
getProjectPluginOverridesPath,
|
|
11
|
-
|
|
10
|
+
isEnoent,
|
|
11
|
+
logger,
|
|
12
|
+
} from "@oh-my-pi/pi-utils";
|
|
12
13
|
import { extractPackageName, parsePluginSpec } from "./parser";
|
|
13
14
|
import type {
|
|
14
15
|
DoctorCheck,
|