@oh-my-pi/pi-coding-agent 13.2.0 → 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 +26 -0
- package/package.json +7 -7
- package/scripts/format-prompts.ts +33 -14
- package/src/capability/index.ts +1 -2
- 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.ts +9 -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 +8 -9
- package/src/discovery/claude-plugins.ts +2 -2
- package/src/discovery/claude.ts +7 -7
- 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/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/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/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/debug/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import * as fs from "node:fs/promises";
|
|
|
7
7
|
import * as url from "node:url";
|
|
8
8
|
import { getWorkProfile } from "@oh-my-pi/pi-natives";
|
|
9
9
|
import { Container, Loader, type SelectItem, SelectList, Spacer, Text } from "@oh-my-pi/pi-tui";
|
|
10
|
-
import { getSessionsDir } from "@oh-my-pi/pi-utils
|
|
10
|
+
import { getSessionsDir } from "@oh-my-pi/pi-utils";
|
|
11
11
|
import { DynamicBorder } from "../modes/components/dynamic-border";
|
|
12
12
|
import { getSelectListTheme, getSymbolTheme, theme } from "../modes/theme/theme";
|
|
13
13
|
import type { InteractiveModeContext } from "../modes/types";
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
import * as fs from "node:fs/promises";
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import type { WorkProfile } from "@oh-my-pi/pi-natives";
|
|
9
|
-
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
10
|
-
import { APP_NAME, getLogPath, getLogsDir, getReportsDir } from "@oh-my-pi/pi-utils/dirs";
|
|
9
|
+
import { APP_NAME, getLogPath, getLogsDir, getReportsDir, isEnoent } from "@oh-my-pi/pi-utils";
|
|
11
10
|
import type { CpuProfile, HeapSnapshot } from "./profiler";
|
|
12
11
|
import { collectSystemInfo, sanitizeEnv } from "./system-info";
|
|
13
12
|
|
package/src/debug/system-info.ts
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import * as os from "node:os";
|
|
6
|
-
import { formatBytes } from "@oh-my-pi/pi-utils";
|
|
7
|
-
import { getProjectDir, VERSION } from "@oh-my-pi/pi-utils/dirs";
|
|
6
|
+
import { formatBytes, getProjectDir, VERSION } from "@oh-my-pi/pi-utils";
|
|
8
7
|
|
|
9
8
|
export interface SystemInfo {
|
|
10
9
|
os: string;
|
package/src/discovery/agents.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { type Skill, skillCapability } from "../capability/skill";
|
|
|
13
13
|
import { type SlashCommand, slashCommandCapability } from "../capability/slash-command";
|
|
14
14
|
import { type SystemPrompt, systemPromptCapability } from "../capability/system-prompt";
|
|
15
15
|
import type { LoadContext, LoadResult } from "../capability/types";
|
|
16
|
-
import { buildRuleFromMarkdown, createSourceMeta, loadFilesFromDir,
|
|
16
|
+
import { buildRuleFromMarkdown, createSourceMeta, loadFilesFromDir, scanSkillsFromDir } from "./helpers";
|
|
17
17
|
|
|
18
18
|
const PROVIDER_ID = "agents";
|
|
19
19
|
const DISPLAY_NAME = "Agents (standard)";
|
|
@@ -28,7 +28,7 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
28
28
|
const items: Skill[] = [];
|
|
29
29
|
const warnings: string[] = [];
|
|
30
30
|
for (const userSkillsDir of getUserAgentPathCandidates(ctx, "skills")) {
|
|
31
|
-
const result = await
|
|
31
|
+
const result = await scanSkillsFromDir(ctx, {
|
|
32
32
|
dir: userSkillsDir,
|
|
33
33
|
providerId: PROVIDER_ID,
|
|
34
34
|
level: "user",
|
package/src/discovery/builtin.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Primary provider for OMP native configs. Supports all capabilities.
|
|
5
5
|
*/
|
|
6
6
|
import * as path from "node:path";
|
|
7
|
-
import { logger } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import { logger, tryParseJson } from "@oh-my-pi/pi-utils";
|
|
8
8
|
import { registerProvider } from "../capability";
|
|
9
9
|
import { type ContextFile, contextFileCapability } from "../capability/context-file";
|
|
10
10
|
import { type Extension, type ExtensionManifest, extensionCapability } from "../capability/extension";
|
|
@@ -30,9 +30,8 @@ import {
|
|
|
30
30
|
expandEnvVarsDeep,
|
|
31
31
|
getExtensionNameFromPath,
|
|
32
32
|
loadFilesFromDir,
|
|
33
|
-
loadSkillsFromDir,
|
|
34
|
-
parseJSON,
|
|
35
33
|
SOURCE_PATHS,
|
|
34
|
+
scanSkillsFromDir,
|
|
36
35
|
} from "./helpers";
|
|
37
36
|
|
|
38
37
|
const PROVIDER_ID = "native";
|
|
@@ -98,7 +97,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
98
97
|
|
|
99
98
|
const parseMcpServers = (content: string, path: string, level: "user" | "project"): MCPServer[] => {
|
|
100
99
|
const result: MCPServer[] = [];
|
|
101
|
-
const data =
|
|
100
|
+
const data = tryParseJson<{ mcpServers?: Record<string, unknown> }>(content);
|
|
102
101
|
if (!data?.mcpServers) return result;
|
|
103
102
|
|
|
104
103
|
const expanded = expandEnvVarsDeep(data.mcpServers);
|
|
@@ -245,7 +244,7 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
245
244
|
const configDirs = await getConfigDirs(ctx);
|
|
246
245
|
const results = await Promise.all(
|
|
247
246
|
configDirs.map(({ dir, level }) =>
|
|
248
|
-
|
|
247
|
+
scanSkillsFromDir(ctx, {
|
|
249
248
|
dir: path.join(dir, "skills"),
|
|
250
249
|
providerId: PROVIDER_ID,
|
|
251
250
|
level,
|
|
@@ -404,7 +403,7 @@ async function loadExtensionModules(ctx: LoadContext): Promise<LoadResult<Extens
|
|
|
404
403
|
if (!settingsContent) continue;
|
|
405
404
|
|
|
406
405
|
const settingsPath = path.join(dir, "settings.json");
|
|
407
|
-
const settingsData =
|
|
406
|
+
const settingsData = tryParseJson<{ extensions?: unknown }>(settingsContent);
|
|
408
407
|
const extensions = settingsData?.extensions;
|
|
409
408
|
if (!Array.isArray(extensions)) continue;
|
|
410
409
|
|
|
@@ -508,7 +507,7 @@ async function loadExtensions(ctx: LoadContext): Promise<LoadResult<Extension>>
|
|
|
508
507
|
if (!content) continue;
|
|
509
508
|
|
|
510
509
|
const { extDir, manifestPath, entryName, level } = manifestCandidates[i];
|
|
511
|
-
const manifest =
|
|
510
|
+
const manifest = tryParseJson<ExtensionManifest>(content);
|
|
512
511
|
if (!manifest) {
|
|
513
512
|
warnings.push(`Failed to parse ${manifestPath}`);
|
|
514
513
|
continue;
|
|
@@ -655,7 +654,7 @@ async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
|
|
|
655
654
|
extensions: ["json", "md", "ts", "js", "sh", "bash", "py"],
|
|
656
655
|
transform: (name, content, path, source) => {
|
|
657
656
|
if (name.endsWith(".json")) {
|
|
658
|
-
const data =
|
|
657
|
+
const data = tryParseJson<{ name?: string; description?: string }>(content);
|
|
659
658
|
const toolName = data?.name || name.replace(/\.json$/, "");
|
|
660
659
|
const description =
|
|
661
660
|
typeof data?.description === "string" && data.description.trim()
|
|
@@ -754,7 +753,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
754
753
|
const content = await readFile(settingsPath);
|
|
755
754
|
if (!content) continue;
|
|
756
755
|
|
|
757
|
-
const data =
|
|
756
|
+
const data = tryParseJson<Record<string, unknown>>(content);
|
|
758
757
|
if (!data) {
|
|
759
758
|
warnings.push(`Failed to parse ${settingsPath}`);
|
|
760
759
|
continue;
|
|
@@ -11,7 +11,7 @@ import { type Skill, skillCapability } from "../capability/skill";
|
|
|
11
11
|
import { type SlashCommand, slashCommandCapability } from "../capability/slash-command";
|
|
12
12
|
import { type CustomTool, toolCapability } from "../capability/tool";
|
|
13
13
|
import type { LoadContext, LoadResult } from "../capability/types";
|
|
14
|
-
import { type ClaudePluginRoot, listClaudePluginRoots, loadFilesFromDir,
|
|
14
|
+
import { type ClaudePluginRoot, listClaudePluginRoots, loadFilesFromDir, scanSkillsFromDir } from "./helpers";
|
|
15
15
|
|
|
16
16
|
const PROVIDER_ID = "claude-plugins";
|
|
17
17
|
const DISPLAY_NAME = "Claude Code Marketplace";
|
|
@@ -31,7 +31,7 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
31
31
|
const results = await Promise.all(
|
|
32
32
|
roots.map(async root => {
|
|
33
33
|
const skillsDir = path.join(root.path, "skills");
|
|
34
|
-
return
|
|
34
|
+
return scanSkillsFromDir(ctx, {
|
|
35
35
|
dir: skillsDir,
|
|
36
36
|
providerId: PROVIDER_ID,
|
|
37
37
|
level: root.scope,
|
package/src/discovery/claude.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Priority: 80 (tool-specific, below builtin but above shared standards)
|
|
6
6
|
*/
|
|
7
7
|
import * as path from "node:path";
|
|
8
|
+
import { tryParseJson } from "@oh-my-pi/pi-utils";
|
|
8
9
|
import { registerProvider } from "../capability";
|
|
9
10
|
import { type ContextFile, contextFileCapability } from "../capability/context-file";
|
|
10
11
|
import { type ExtensionModule, extensionModuleCapability } from "../capability/extension-module";
|
|
@@ -24,8 +25,7 @@ import {
|
|
|
24
25
|
expandEnvVarsDeep,
|
|
25
26
|
getExtensionNameFromPath,
|
|
26
27
|
loadFilesFromDir,
|
|
27
|
-
|
|
28
|
-
parseJSON,
|
|
28
|
+
scanSkillsFromDir,
|
|
29
29
|
} from "./helpers";
|
|
30
30
|
|
|
31
31
|
const PROVIDER_ID = "claude";
|
|
@@ -77,7 +77,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
77
77
|
|
|
78
78
|
const parseMcpServers = (content: string | null, path: string, level: "user" | "project"): MCPServer[] => {
|
|
79
79
|
if (!content) return [];
|
|
80
|
-
const json =
|
|
80
|
+
const json = tryParseJson<{ mcpServers?: Record<string, unknown> }>(content);
|
|
81
81
|
if (!json?.mcpServers) return [];
|
|
82
82
|
|
|
83
83
|
const mcpServers = expandEnvVarsDeep(json.mcpServers);
|
|
@@ -163,8 +163,8 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
163
163
|
const projectSkillsDir = path.join(getProjectClaude(ctx), "skills");
|
|
164
164
|
|
|
165
165
|
const results = await Promise.all([
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
scanSkillsFromDir(ctx, { dir: userSkillsDir, providerId: PROVIDER_ID, level: "user" }),
|
|
167
|
+
scanSkillsFromDir(ctx, { dir: projectSkillsDir, providerId: PROVIDER_ID, level: "project" }),
|
|
168
168
|
]);
|
|
169
169
|
|
|
170
170
|
return {
|
|
@@ -396,7 +396,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
396
396
|
|
|
397
397
|
const userContent = await readFile(userSettingsJson);
|
|
398
398
|
if (userContent) {
|
|
399
|
-
const data =
|
|
399
|
+
const data = tryParseJson<Record<string, unknown>>(userContent);
|
|
400
400
|
if (data) {
|
|
401
401
|
items.push({
|
|
402
402
|
path: userSettingsJson,
|
|
@@ -413,7 +413,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
413
413
|
const projectSettingsJson = path.join(projectBase, "settings.json");
|
|
414
414
|
const projectContent = await readFile(projectSettingsJson);
|
|
415
415
|
if (projectContent) {
|
|
416
|
-
const data =
|
|
416
|
+
const data = tryParseJson<Record<string, unknown>>(projectContent);
|
|
417
417
|
if (data) {
|
|
418
418
|
items.push({
|
|
419
419
|
path: projectSettingsJson,
|
package/src/discovery/codex.ts
CHANGED
|
@@ -35,8 +35,8 @@ import {
|
|
|
35
35
|
discoverExtensionModulePaths,
|
|
36
36
|
getExtensionNameFromPath,
|
|
37
37
|
loadFilesFromDir,
|
|
38
|
-
loadSkillsFromDir,
|
|
39
38
|
SOURCE_PATHS,
|
|
39
|
+
scanSkillsFromDir,
|
|
40
40
|
} from "./helpers";
|
|
41
41
|
|
|
42
42
|
const PROVIDER_ID = "codex";
|
|
@@ -214,12 +214,12 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
214
214
|
const projectSkillsDir = path.join(codexDir, "skills");
|
|
215
215
|
|
|
216
216
|
const results = await Promise.all([
|
|
217
|
-
|
|
217
|
+
scanSkillsFromDir(ctx, {
|
|
218
218
|
dir: userSkillsDir,
|
|
219
219
|
providerId: PROVIDER_ID,
|
|
220
220
|
level: "user",
|
|
221
221
|
}),
|
|
222
|
-
|
|
222
|
+
scanSkillsFromDir(ctx, {
|
|
223
223
|
dir: projectSkillsDir,
|
|
224
224
|
providerId: PROVIDER_ID,
|
|
225
225
|
level: "project",
|
package/src/discovery/cursor.ts
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* - rules: From rules/*.mdc files with MDC frontmatter (description, globs, alwaysApply)
|
|
14
14
|
* - settings: From settings.json if present
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
import { tryParseJson } from "@oh-my-pi/pi-utils";
|
|
16
18
|
import { registerProvider } from "../capability";
|
|
17
19
|
import { readFile } from "../capability/fs";
|
|
18
20
|
import { type MCPServer, mcpCapability } from "../capability/mcp";
|
|
@@ -28,7 +30,6 @@ import {
|
|
|
28
30
|
getProjectPath,
|
|
29
31
|
getUserPath,
|
|
30
32
|
loadFilesFromDir,
|
|
31
|
-
parseJSON,
|
|
32
33
|
} from "./helpers";
|
|
33
34
|
|
|
34
35
|
const PROVIDER_ID = "cursor";
|
|
@@ -46,7 +47,7 @@ function parseMCPServers(
|
|
|
46
47
|
): { items: MCPServer[]; warning?: string } {
|
|
47
48
|
const items: MCPServer[] = [];
|
|
48
49
|
|
|
49
|
-
const parsed =
|
|
50
|
+
const parsed = tryParseJson<{ mcpServers?: Record<string, unknown> }>(content);
|
|
50
51
|
if (!parsed?.mcpServers) {
|
|
51
52
|
return { items, warning: `${path}: missing or invalid 'mcpServers' key` };
|
|
52
53
|
}
|
|
@@ -158,7 +159,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
158
159
|
const projectContentPromise = projectPath ? readFile(projectPath) : Promise.resolve(null);
|
|
159
160
|
|
|
160
161
|
if (userContent && userPath) {
|
|
161
|
-
const parsed =
|
|
162
|
+
const parsed = tryParseJson<Record<string, unknown>>(userContent);
|
|
162
163
|
if (parsed) {
|
|
163
164
|
items.push({
|
|
164
165
|
path: userPath,
|
|
@@ -173,7 +174,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
173
174
|
|
|
174
175
|
const projectContent = await projectContentPromise;
|
|
175
176
|
if (projectContent && projectPath) {
|
|
176
|
-
const parsed =
|
|
177
|
+
const parsed = tryParseJson<Record<string, unknown>>(projectContent);
|
|
177
178
|
if (parsed) {
|
|
178
179
|
items.push({
|
|
179
180
|
path: projectPath,
|
package/src/discovery/gemini.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* - settings: From settings.json
|
|
17
17
|
*/
|
|
18
18
|
import * as path from "node:path";
|
|
19
|
+
import { tryParseJson } from "@oh-my-pi/pi-utils";
|
|
19
20
|
import { registerProvider } from "../capability";
|
|
20
21
|
import { type ContextFile, contextFileCapability } from "../capability/context-file";
|
|
21
22
|
import { type Extension, type ExtensionManifest, extensionCapability } from "../capability/extension";
|
|
@@ -33,7 +34,6 @@ import {
|
|
|
33
34
|
getExtensionNameFromPath,
|
|
34
35
|
getProjectPath,
|
|
35
36
|
getUserPath,
|
|
36
|
-
parseJSON,
|
|
37
37
|
} from "./helpers";
|
|
38
38
|
|
|
39
39
|
const PROVIDER_ID = "gemini";
|
|
@@ -80,7 +80,7 @@ async function loadMCPFromSettings(
|
|
|
80
80
|
return { items, warnings };
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const parsed =
|
|
83
|
+
const parsed = tryParseJson<{ mcpServers?: Record<string, unknown> }>(content);
|
|
84
84
|
if (!parsed) {
|
|
85
85
|
warnings.push(`Invalid JSON in ${path}`);
|
|
86
86
|
return { items, warnings };
|
|
@@ -206,7 +206,7 @@ async function loadExtensionsFromDir(extensionsDir: string, level: "user" | "pro
|
|
|
206
206
|
for (const { entry, extPath, manifestPath, content } of results) {
|
|
207
207
|
if (!content) continue;
|
|
208
208
|
|
|
209
|
-
const manifest =
|
|
209
|
+
const manifest = tryParseJson<ExtensionManifest>(content);
|
|
210
210
|
if (!manifest) {
|
|
211
211
|
warnings.push(`Invalid JSON in ${manifestPath}`);
|
|
212
212
|
continue;
|
|
@@ -268,7 +268,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
268
268
|
if (userPath) {
|
|
269
269
|
const content = await readFile(userPath);
|
|
270
270
|
if (content) {
|
|
271
|
-
const parsed =
|
|
271
|
+
const parsed = tryParseJson<Record<string, unknown>>(content);
|
|
272
272
|
if (parsed) {
|
|
273
273
|
items.push({
|
|
274
274
|
path: userPath,
|
|
@@ -287,7 +287,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
287
287
|
if (projectPath) {
|
|
288
288
|
const content = await readFile(projectPath);
|
|
289
289
|
if (content) {
|
|
290
|
-
const parsed =
|
|
290
|
+
const parsed = tryParseJson<Record<string, unknown>>(content);
|
|
291
291
|
if (parsed) {
|
|
292
292
|
items.push({
|
|
293
293
|
path: projectPath,
|
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