@oh-my-pi/pi-coding-agent 0.1.0
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 +1629 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/config-usage.md +113 -0
- package/docs/custom-tools.md +541 -0
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +867 -0
- package/docs/rpc.md +1040 -0
- package/docs/sdk.md +994 -0
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +240 -0
- package/docs/skills.md +290 -0
- package/docs/theme.md +670 -0
- package/docs/tree.md +197 -0
- package/docs/tui.md +341 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +124 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/question/index.ts +84 -0
- package/examples/custom-tools/subagent/README.md +172 -0
- package/examples/custom-tools/subagent/agents/planner.md +37 -0
- package/examples/custom-tools/subagent/agents/scout.md +50 -0
- package/examples/custom-tools/subagent/agents/worker.md +24 -0
- package/examples/custom-tools/subagent/agents.ts +156 -0
- package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
- package/examples/custom-tools/subagent/commands/implement.md +10 -0
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
- package/examples/custom-tools/subagent/index.ts +1002 -0
- package/examples/custom-tools/todo/index.ts +212 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +49 -0
- package/examples/hooks/confirm-destructive.ts +59 -0
- package/examples/hooks/custom-compaction.ts +116 -0
- package/examples/hooks/dirty-repo-guard.ts +52 -0
- package/examples/hooks/file-trigger.ts +41 -0
- package/examples/hooks/git-checkpoint.ts +53 -0
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +34 -0
- package/examples/hooks/protected-paths.ts +30 -0
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +90 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +42 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +95 -0
- package/examples/sdk/README.md +154 -0
- package/package.json +89 -0
- package/src/bun-imports.d.ts +16 -0
- package/src/capability/context-file.ts +40 -0
- package/src/capability/extension.ts +48 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +616 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +52 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule.ts +56 -0
- package/src/capability/settings.ts +35 -0
- package/src/capability/skill.ts +49 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/system-prompt.ts +35 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +166 -0
- package/src/cli/args.ts +259 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +661 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli/update-cli.ts +274 -0
- package/src/cli.ts +10 -0
- package/src/config.ts +391 -0
- package/src/core/agent-session.ts +2178 -0
- package/src/core/auth-storage.ts +258 -0
- package/src/core/bash-executor.ts +197 -0
- package/src/core/compaction/branch-summarization.ts +315 -0
- package/src/core/compaction/compaction.ts +664 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +153 -0
- package/src/core/custom-commands/bundled/review/index.ts +156 -0
- package/src/core/custom-commands/index.ts +15 -0
- package/src/core/custom-commands/loader.ts +226 -0
- package/src/core/custom-commands/types.ts +112 -0
- package/src/core/custom-tools/index.ts +22 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +185 -0
- package/src/core/custom-tools/wrapper.ts +29 -0
- package/src/core/exec.ts +139 -0
- package/src/core/export-html/index.ts +159 -0
- package/src/core/export-html/template.css +774 -0
- package/src/core/export-html/template.generated.ts +2 -0
- package/src/core/export-html/template.html +45 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/template.macro.ts +24 -0
- package/src/core/file-mentions.ts +54 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +288 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +98 -0
- package/src/core/hooks/types.ts +770 -0
- package/src/core/index.ts +53 -0
- package/src/core/logger.ts +112 -0
- package/src/core/mcp/client.ts +185 -0
- package/src/core/mcp/config.ts +248 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +99 -0
- package/src/core/mcp/manager.ts +235 -0
- package/src/core/mcp/tool-bridge.ts +156 -0
- package/src/core/mcp/transports/http.ts +316 -0
- package/src/core/mcp/transports/index.ts +6 -0
- package/src/core/mcp/transports/stdio.ts +252 -0
- package/src/core/mcp/types.ts +228 -0
- package/src/core/messages.ts +211 -0
- package/src/core/model-registry.ts +334 -0
- package/src/core/model-resolver.ts +494 -0
- package/src/core/plugins/doctor.ts +67 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +339 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +37 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +900 -0
- package/src/core/session-manager.ts +1837 -0
- package/src/core/settings-manager.ts +860 -0
- package/src/core/skills.ts +352 -0
- package/src/core/slash-commands.ts +132 -0
- package/src/core/system-prompt.ts +442 -0
- package/src/core/timings.ts +25 -0
- package/src/core/title-generator.ts +110 -0
- package/src/core/tools/ask.ts +193 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +91 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +487 -0
- package/src/core/tools/edit.ts +140 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +63 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +200 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +338 -0
- package/src/core/tools/exa/types.ts +167 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +244 -0
- package/src/core/tools/grep.ts +584 -0
- package/src/core/tools/index.ts +283 -0
- package/src/core/tools/ls.ts +142 -0
- package/src/core/tools/lsp/client.ts +767 -0
- package/src/core/tools/lsp/clients/biome-client.ts +207 -0
- package/src/core/tools/lsp/clients/index.ts +49 -0
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
- package/src/core/tools/lsp/config.ts +845 -0
- package/src/core/tools/lsp/edits.ts +110 -0
- package/src/core/tools/lsp/index.ts +1364 -0
- package/src/core/tools/lsp/render.ts +560 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +495 -0
- package/src/core/tools/lsp/utils.ts +526 -0
- package/src/core/tools/notebook.ts +182 -0
- package/src/core/tools/output.ts +198 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +507 -0
- package/src/core/tools/renderers.ts +820 -0
- package/src/core/tools/review.ts +275 -0
- package/src/core/tools/rulebook.ts +124 -0
- package/src/core/tools/task/agents.ts +158 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/commands.ts +157 -0
- package/src/core/tools/task/discovery.ts +217 -0
- package/src/core/tools/task/executor.ts +531 -0
- package/src/core/tools/task/index.ts +548 -0
- package/src/core/tools/task/model-resolver.ts +176 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +502 -0
- package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
- package/src/core/tools/task/types.ts +142 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2511 -0
- package/src/core/tools/web-search/auth.ts +199 -0
- package/src/core/tools/web-search/index.ts +583 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +196 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +372 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +63 -0
- package/src/core/ttsr.ts +211 -0
- package/src/core/utils.ts +187 -0
- package/src/discovery/agents-md.ts +75 -0
- package/src/discovery/builtin.ts +647 -0
- package/src/discovery/claude.ts +623 -0
- package/src/discovery/cline.ts +104 -0
- package/src/discovery/codex.ts +571 -0
- package/src/discovery/cursor.ts +266 -0
- package/src/discovery/gemini.ts +368 -0
- package/src/discovery/github.ts +120 -0
- package/src/discovery/helpers.test.ts +127 -0
- package/src/discovery/helpers.ts +249 -0
- package/src/discovery/index.ts +84 -0
- package/src/discovery/mcp-json.ts +127 -0
- package/src/discovery/vscode.ts +99 -0
- package/src/discovery/windsurf.ts +219 -0
- package/src/index.ts +192 -0
- package/src/main.ts +507 -0
- package/src/migrations.ts +156 -0
- package/src/modes/cleanup.ts +23 -0
- package/src/modes/index.ts +48 -0
- package/src/modes/interactive/components/armin.ts +382 -0
- package/src/modes/interactive/components/assistant-message.ts +86 -0
- package/src/modes/interactive/components/bash-execution.ts +199 -0
- package/src/modes/interactive/components/bordered-loader.ts +41 -0
- package/src/modes/interactive/components/branch-summary-message.ts +42 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
- package/src/modes/interactive/components/custom-editor.ts +122 -0
- package/src/modes/interactive/components/diff.ts +147 -0
- package/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +296 -0
- package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
- package/src/modes/interactive/components/extensions/index.ts +9 -0
- package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
- package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
- package/src/modes/interactive/components/extensions/types.ts +191 -0
- package/src/modes/interactive/components/hook-editor.ts +117 -0
- package/src/modes/interactive/components/hook-input.ts +64 -0
- package/src/modes/interactive/components/hook-message.ts +96 -0
- package/src/modes/interactive/components/hook-selector.ts +91 -0
- package/src/modes/interactive/components/model-selector.ts +560 -0
- package/src/modes/interactive/components/oauth-selector.ts +136 -0
- package/src/modes/interactive/components/plugin-settings.ts +481 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +220 -0
- package/src/modes/interactive/components/settings-defs.ts +597 -0
- package/src/modes/interactive/components/settings-selector.ts +545 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/status-line/index.ts +4 -0
- package/src/modes/interactive/components/status-line/presets.ts +94 -0
- package/src/modes/interactive/components/status-line/segments.ts +350 -0
- package/src/modes/interactive/components/status-line/separators.ts +55 -0
- package/src/modes/interactive/components/status-line/types.ts +81 -0
- package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
- package/src/modes/interactive/components/status-line.ts +384 -0
- package/src/modes/interactive/components/theme-selector.ts +62 -0
- package/src/modes/interactive/components/thinking-selector.ts +64 -0
- package/src/modes/interactive/components/tool-execution.ts +946 -0
- package/src/modes/interactive/components/tree-selector.ts +877 -0
- package/src/modes/interactive/components/ttsr-notification.ts +82 -0
- package/src/modes/interactive/components/user-message-selector.ts +159 -0
- package/src/modes/interactive/components/user-message.ts +18 -0
- package/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/src/modes/interactive/components/welcome.ts +228 -0
- package/src/modes/interactive/interactive-mode.ts +2669 -0
- package/src/modes/interactive/theme/dark.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
- package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
- package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
- package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
- package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
- package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
- package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
- package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
- package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
- package/src/modes/interactive/theme/defaults/index.ts +67 -0
- package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
- package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
- package/src/modes/interactive/theme/defaults/light-github.json +114 -0
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
- package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
- package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
- package/src/modes/interactive/theme/defaults/light-one.json +105 -0
- package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
- package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
- package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
- package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
- package/src/modes/interactive/theme/light.json +99 -0
- package/src/modes/interactive/theme/theme-schema.json +424 -0
- package/src/modes/interactive/theme/theme.ts +2211 -0
- package/src/modes/print-mode.ts +163 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +494 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/prompts/architect-plan.md +10 -0
- package/src/prompts/branch-summary-preamble.md +3 -0
- package/src/prompts/branch-summary.md +28 -0
- package/src/prompts/browser.md +71 -0
- package/src/prompts/compaction-summary.md +34 -0
- package/src/prompts/compaction-turn-prefix.md +16 -0
- package/src/prompts/compaction-update-summary.md +41 -0
- package/src/prompts/explore.md +82 -0
- package/src/prompts/implement-with-critic.md +11 -0
- package/src/prompts/implement.md +11 -0
- package/src/prompts/init.md +30 -0
- package/src/prompts/plan.md +54 -0
- package/src/prompts/reviewer.md +81 -0
- package/src/prompts/summarization-system.md +3 -0
- package/src/prompts/system-prompt.md +27 -0
- package/src/prompts/task.md +56 -0
- package/src/prompts/title-system.md +8 -0
- package/src/prompts/tools/ask.md +24 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/edit.md +9 -0
- package/src/prompts/tools/find.md +6 -0
- package/src/prompts/tools/grep.md +12 -0
- package/src/prompts/tools/lsp.md +14 -0
- package/src/prompts/tools/output.md +23 -0
- package/src/prompts/tools/read.md +25 -0
- package/src/prompts/tools/web-fetch.md +8 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +10 -0
- package/src/utils/changelog.ts +99 -0
- package/src/utils/clipboard.ts +265 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/shell-snapshot.ts +218 -0
- package/src/utils/shell.ts +364 -0
- package/src/utils/tools-manager.ts +265 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow commands for orchestrating multi-agent workflows.
|
|
3
|
+
*
|
|
4
|
+
* Commands are embedded at build time via Bun's import with { type: "text" }.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { type SlashCommand, slashCommandCapability } from "../../../capability/slash-command";
|
|
9
|
+
import { loadSync } from "../../../discovery";
|
|
10
|
+
|
|
11
|
+
// Embed command markdown files at build time
|
|
12
|
+
import architectPlanMd from "../../../prompts/architect-plan.md" with { type: "text" };
|
|
13
|
+
import implementMd from "../../../prompts/implement.md" with { type: "text" };
|
|
14
|
+
import implementWithCriticMd from "../../../prompts/implement-with-critic.md" with { type: "text" };
|
|
15
|
+
|
|
16
|
+
const EMBEDDED_COMMANDS: { name: string; content: string }[] = [
|
|
17
|
+
{ name: "architect-plan.md", content: architectPlanMd },
|
|
18
|
+
{ name: "implement-with-critic.md", content: implementWithCriticMd },
|
|
19
|
+
{ name: "implement.md", content: implementMd },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/** Workflow command definition */
|
|
23
|
+
export interface WorkflowCommand {
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
instructions: string;
|
|
27
|
+
source: "bundled" | "user" | "project";
|
|
28
|
+
filePath: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse YAML frontmatter from markdown content.
|
|
33
|
+
*/
|
|
34
|
+
function parseFrontmatter(content: string): { frontmatter: Record<string, string>; body: string } {
|
|
35
|
+
const frontmatter: Record<string, string> = {};
|
|
36
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
37
|
+
|
|
38
|
+
if (!normalized.startsWith("---")) {
|
|
39
|
+
return { frontmatter, body: normalized };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const endIndex = normalized.indexOf("\n---", 3);
|
|
43
|
+
if (endIndex === -1) {
|
|
44
|
+
return { frontmatter, body: normalized };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const frontmatterBlock = normalized.slice(4, endIndex);
|
|
48
|
+
const body = normalized.slice(endIndex + 4).trim();
|
|
49
|
+
|
|
50
|
+
for (const line of frontmatterBlock.split("\n")) {
|
|
51
|
+
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
52
|
+
if (match) {
|
|
53
|
+
let value = match[2].trim();
|
|
54
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
55
|
+
value = value.slice(1, -1);
|
|
56
|
+
}
|
|
57
|
+
frontmatter[match[1]] = value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { frontmatter, body };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Cache for bundled commands */
|
|
65
|
+
let bundledCommandsCache: WorkflowCommand[] | null = null;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Load all bundled commands from embedded content.
|
|
69
|
+
*/
|
|
70
|
+
export function loadBundledCommands(): WorkflowCommand[] {
|
|
71
|
+
if (bundledCommandsCache !== null) {
|
|
72
|
+
return bundledCommandsCache;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const commands: WorkflowCommand[] = [];
|
|
76
|
+
|
|
77
|
+
for (const { name, content } of EMBEDDED_COMMANDS) {
|
|
78
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
79
|
+
const cmdName = name.replace(/\.md$/, "");
|
|
80
|
+
|
|
81
|
+
commands.push({
|
|
82
|
+
name: cmdName,
|
|
83
|
+
description: frontmatter.description || "",
|
|
84
|
+
instructions: body,
|
|
85
|
+
source: "bundled",
|
|
86
|
+
filePath: `embedded:${name}`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
bundledCommandsCache = commands;
|
|
91
|
+
return commands;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Discover all available commands.
|
|
96
|
+
*
|
|
97
|
+
* Precedence (highest wins): .omp > .pi > .claude (project before user), then bundled
|
|
98
|
+
*/
|
|
99
|
+
export function discoverCommands(cwd: string): WorkflowCommand[] {
|
|
100
|
+
const resolvedCwd = path.resolve(cwd);
|
|
101
|
+
|
|
102
|
+
// Load slash commands from capability API
|
|
103
|
+
const result = loadSync<SlashCommand>(slashCommandCapability.id, { cwd: resolvedCwd });
|
|
104
|
+
|
|
105
|
+
const commands: WorkflowCommand[] = [];
|
|
106
|
+
const seen = new Set<string>();
|
|
107
|
+
|
|
108
|
+
// Convert SlashCommand to WorkflowCommand format
|
|
109
|
+
for (const cmd of result.items) {
|
|
110
|
+
if (seen.has(cmd.name)) continue;
|
|
111
|
+
|
|
112
|
+
const { frontmatter, body } = parseFrontmatter(cmd.content);
|
|
113
|
+
|
|
114
|
+
// Map capability levels to WorkflowCommand source
|
|
115
|
+
const source: "bundled" | "user" | "project" = cmd.level === "native" ? "bundled" : cmd.level;
|
|
116
|
+
|
|
117
|
+
commands.push({
|
|
118
|
+
name: cmd.name,
|
|
119
|
+
description: frontmatter.description || "",
|
|
120
|
+
instructions: body,
|
|
121
|
+
source,
|
|
122
|
+
filePath: cmd.path,
|
|
123
|
+
});
|
|
124
|
+
seen.add(cmd.name);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Add bundled commands if not already present
|
|
128
|
+
for (const cmd of loadBundledCommands()) {
|
|
129
|
+
if (seen.has(cmd.name)) continue;
|
|
130
|
+
commands.push(cmd);
|
|
131
|
+
seen.add(cmd.name);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return commands;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get a command by name.
|
|
139
|
+
*/
|
|
140
|
+
export function getCommand(commands: WorkflowCommand[], name: string): WorkflowCommand | undefined {
|
|
141
|
+
return commands.find((c) => c.name === name);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Expand command instructions with task input.
|
|
146
|
+
* Replaces $@ with the provided input.
|
|
147
|
+
*/
|
|
148
|
+
export function expandCommand(command: WorkflowCommand, input: string): string {
|
|
149
|
+
return command.instructions.replace(/\$@/g, input);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Clear the bundled commands cache (for testing).
|
|
154
|
+
*/
|
|
155
|
+
export function clearBundledCommandsCache(): void {
|
|
156
|
+
bundledCommandsCache = null;
|
|
157
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent discovery from filesystem.
|
|
3
|
+
*
|
|
4
|
+
* Discovers agent definitions from:
|
|
5
|
+
* - ~/.omp/agent/agents/*.md (user-level, primary)
|
|
6
|
+
* - ~/.pi/agent/agents/*.md (user-level, legacy)
|
|
7
|
+
* - ~/.claude/agents/*.md (user-level, legacy)
|
|
8
|
+
* - .omp/agents/*.md (project-level, primary)
|
|
9
|
+
* - .pi/agents/*.md (project-level, legacy)
|
|
10
|
+
* - .claude/agents/*.md (project-level, legacy)
|
|
11
|
+
*
|
|
12
|
+
* Agent files use markdown with YAML frontmatter.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as fs from "node:fs";
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
import { findAllNearestProjectConfigDirs, getConfigDirs } from "../../../config";
|
|
18
|
+
import { loadBundledAgents } from "./agents";
|
|
19
|
+
import type { AgentDefinition, AgentSource } from "./types";
|
|
20
|
+
|
|
21
|
+
/** Result of agent discovery */
|
|
22
|
+
export interface DiscoveryResult {
|
|
23
|
+
agents: AgentDefinition[];
|
|
24
|
+
projectAgentsDir: string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse YAML frontmatter from markdown content.
|
|
29
|
+
*/
|
|
30
|
+
function parseFrontmatter(content: string): { frontmatter: Record<string, string>; body: string } {
|
|
31
|
+
const frontmatter: Record<string, string> = {};
|
|
32
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
33
|
+
|
|
34
|
+
if (!normalized.startsWith("---")) {
|
|
35
|
+
return { frontmatter, body: normalized };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const endIndex = normalized.indexOf("\n---", 3);
|
|
39
|
+
if (endIndex === -1) {
|
|
40
|
+
return { frontmatter, body: normalized };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const frontmatterBlock = normalized.slice(4, endIndex);
|
|
44
|
+
const body = normalized.slice(endIndex + 4).trim();
|
|
45
|
+
|
|
46
|
+
for (const line of frontmatterBlock.split("\n")) {
|
|
47
|
+
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
48
|
+
if (match) {
|
|
49
|
+
let value = match[2].trim();
|
|
50
|
+
// Strip quotes
|
|
51
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
52
|
+
value = value.slice(1, -1);
|
|
53
|
+
}
|
|
54
|
+
frontmatter[match[1]] = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { frontmatter, body };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Load agents from a directory.
|
|
63
|
+
*/
|
|
64
|
+
function loadAgentsFromDir(dir: string, source: AgentSource): AgentDefinition[] {
|
|
65
|
+
const agents: AgentDefinition[] = [];
|
|
66
|
+
|
|
67
|
+
if (!fs.existsSync(dir)) {
|
|
68
|
+
return agents;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let entries: fs.Dirent[];
|
|
72
|
+
try {
|
|
73
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
74
|
+
} catch {
|
|
75
|
+
return agents;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const entry of entries) {
|
|
79
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
80
|
+
|
|
81
|
+
const filePath = path.resolve(dir, entry.name);
|
|
82
|
+
|
|
83
|
+
// Handle both regular files and symlinks
|
|
84
|
+
try {
|
|
85
|
+
if (!fs.statSync(filePath).isFile()) continue;
|
|
86
|
+
} catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let content: string;
|
|
91
|
+
try {
|
|
92
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
93
|
+
} catch {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
98
|
+
|
|
99
|
+
// Require name and description
|
|
100
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const tools = frontmatter.tools
|
|
105
|
+
?.split(",")
|
|
106
|
+
.map((t) => t.trim())
|
|
107
|
+
.filter(Boolean);
|
|
108
|
+
|
|
109
|
+
// Parse spawns field
|
|
110
|
+
let spawns: string[] | "*" | undefined;
|
|
111
|
+
if (frontmatter.spawns !== undefined) {
|
|
112
|
+
const spawnsRaw = frontmatter.spawns.trim();
|
|
113
|
+
if (spawnsRaw === "*") {
|
|
114
|
+
spawns = "*";
|
|
115
|
+
} else if (spawnsRaw) {
|
|
116
|
+
spawns = spawnsRaw
|
|
117
|
+
.split(",")
|
|
118
|
+
.map((s) => s.trim())
|
|
119
|
+
.filter(Boolean);
|
|
120
|
+
if (spawns.length === 0) spawns = undefined;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Backward compat: infer spawns: "*" when tools includes "task"
|
|
125
|
+
if (spawns === undefined && tools?.includes("task")) {
|
|
126
|
+
spawns = "*";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const recursive =
|
|
130
|
+
frontmatter.recursive === undefined
|
|
131
|
+
? undefined
|
|
132
|
+
: frontmatter.recursive === "true" || frontmatter.recursive === "1";
|
|
133
|
+
|
|
134
|
+
agents.push({
|
|
135
|
+
name: frontmatter.name,
|
|
136
|
+
description: frontmatter.description,
|
|
137
|
+
tools: tools && tools.length > 0 ? tools : undefined,
|
|
138
|
+
spawns,
|
|
139
|
+
model: frontmatter.model,
|
|
140
|
+
recursive,
|
|
141
|
+
systemPrompt: body,
|
|
142
|
+
source,
|
|
143
|
+
filePath,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return agents;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Discover agents from filesystem and merge with bundled agents.
|
|
152
|
+
*
|
|
153
|
+
* Precedence (highest wins): .omp > .pi > .claude (project before user), then bundled
|
|
154
|
+
*
|
|
155
|
+
* @param cwd - Current working directory for project agent discovery
|
|
156
|
+
*/
|
|
157
|
+
export function discoverAgents(cwd: string): DiscoveryResult {
|
|
158
|
+
const resolvedCwd = path.resolve(cwd);
|
|
159
|
+
const agentSources = Array.from(new Set(getConfigDirs("", { project: false }).map((entry) => entry.source)));
|
|
160
|
+
|
|
161
|
+
// Get user directories (priority order: .omp, .pi, .claude, ...)
|
|
162
|
+
const userDirs = getConfigDirs("agents", { project: false })
|
|
163
|
+
.filter((entry) => agentSources.includes(entry.source))
|
|
164
|
+
.map((entry) => ({
|
|
165
|
+
...entry,
|
|
166
|
+
path: path.resolve(entry.path),
|
|
167
|
+
}));
|
|
168
|
+
|
|
169
|
+
// Get project directories by walking up from cwd (priority order)
|
|
170
|
+
const projectDirs = findAllNearestProjectConfigDirs("agents", resolvedCwd)
|
|
171
|
+
.filter((entry) => agentSources.includes(entry.source))
|
|
172
|
+
.map((entry) => ({
|
|
173
|
+
...entry,
|
|
174
|
+
path: path.resolve(entry.path),
|
|
175
|
+
}));
|
|
176
|
+
|
|
177
|
+
const orderedSources = agentSources.filter(
|
|
178
|
+
(source) =>
|
|
179
|
+
userDirs.some((entry) => entry.source === source) || projectDirs.some((entry) => entry.source === source),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const orderedDirs: Array<{ dir: string; source: AgentSource }> = [];
|
|
183
|
+
for (const source of orderedSources) {
|
|
184
|
+
const project = projectDirs.find((entry) => entry.source === source);
|
|
185
|
+
if (project) orderedDirs.push({ dir: project.path, source: "project" });
|
|
186
|
+
const user = userDirs.find((entry) => entry.source === source);
|
|
187
|
+
if (user) orderedDirs.push({ dir: user.path, source: "user" });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const agents: AgentDefinition[] = [];
|
|
191
|
+
const seen = new Set<string>();
|
|
192
|
+
|
|
193
|
+
for (const { dir, source } of orderedDirs) {
|
|
194
|
+
for (const agent of loadAgentsFromDir(dir, source)) {
|
|
195
|
+
if (seen.has(agent.name)) continue;
|
|
196
|
+
agents.push(agent);
|
|
197
|
+
seen.add(agent.name);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
for (const agent of loadBundledAgents()) {
|
|
202
|
+
if (seen.has(agent.name)) continue;
|
|
203
|
+
agents.push(agent);
|
|
204
|
+
seen.add(agent.name);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const projectAgentsDir = projectDirs.length > 0 ? projectDirs[0].path : null;
|
|
208
|
+
|
|
209
|
+
return { agents, projectAgentsDir };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get an agent by name from discovered agents.
|
|
214
|
+
*/
|
|
215
|
+
export function getAgent(agents: AgentDefinition[], name: string): AgentDefinition | undefined {
|
|
216
|
+
return agents.find((a) => a.name === name);
|
|
217
|
+
}
|