@gajae-code/coding-agent 0.18.12 → 0.19.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 +467 -0
- package/dist/types/capability/rule.d.ts +7 -2
- package/dist/types/cli/append-system-prompt.d.ts +6 -0
- package/dist/types/cli/args.d.ts +7 -2
- package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
- package/dist/types/cli/customize-doctor.d.ts +4 -3
- package/dist/types/cli/public-command-errors.d.ts +1 -0
- package/dist/types/cli/root-flags.d.ts +0 -12
- package/dist/types/cli/update-cli.d.ts +7 -2
- package/dist/types/cli-main.d.ts +0 -6
- package/dist/types/commands/launch.d.ts +0 -6
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +8 -0
- package/dist/types/commit/map-reduce/index.d.ts +4 -4
- package/dist/types/config/autorouting-tier-map.d.ts +23 -1
- package/dist/types/config/config-hot-reload.d.ts +5 -0
- package/dist/types/config/file-lock.d.ts +44 -2
- package/dist/types/config/model-profile-activation.d.ts +19 -5
- package/dist/types/config/model-registry.d.ts +16 -3
- package/dist/types/config/model-resolver.d.ts +5 -4
- package/dist/types/config/settings-schema.d.ts +11 -1
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/customization/import.d.ts +2 -0
- package/dist/types/customization/types.d.ts +1 -1
- package/dist/types/dap/session.d.ts +11 -2
- package/dist/types/discovery/helpers.d.ts +7 -2
- package/dist/types/edit/index.d.ts +5 -0
- package/dist/types/edit/modes/patch.d.ts +9 -0
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/export/ttsr.d.ts +19 -0
- package/dist/types/extensibility/extensions/types.d.ts +5 -1
- package/dist/types/extensibility/slash-commands.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/content.d.ts +5 -6
- package/dist/types/internal-urls/types.d.ts +2 -0
- package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
- package/dist/types/main.d.ts +23 -18
- package/dist/types/migrate/action-planner.d.ts +10 -1
- package/dist/types/migrate/adapters/codex.d.ts +1 -1
- package/dist/types/migrate/adapters/index.d.ts +35 -3
- package/dist/types/migrate/hook-module.d.ts +23 -0
- package/dist/types/migrate/report.d.ts +2 -1
- package/dist/types/migrate/types.d.ts +69 -2
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +43 -0
- package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
- package/dist/types/modes/utils/host-status.d.ts +15 -11
- package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
- package/dist/types/sdk/broker/broker-exit.d.ts +1 -1
- package/dist/types/sdk/broker/broker.d.ts +22 -1
- package/dist/types/sdk/broker/ensure.d.ts +5 -2
- package/dist/types/sdk/broker/hop.d.ts +9 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
- package/dist/types/sdk/broker/session-index.d.ts +2 -0
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/index.d.ts +21 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
- package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
- package/dist/types/sdk/host/control/operations.d.ts +5 -1
- package/dist/types/sdk/host/events.d.ts +4 -0
- package/dist/types/sdk/host/host.d.ts +2 -1
- package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
- package/dist/types/sdk/host/session-runtime.d.ts +8 -8
- package/dist/types/sdk/host/surface-policy.d.ts +2 -1
- package/dist/types/sdk/lifecycle/service.d.ts +12 -0
- package/dist/types/sdk/prompt-status.d.ts +11 -4
- package/dist/types/session/agent-session.d.ts +50 -8
- package/dist/types/session/session-manager.d.ts +16 -0
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +13 -1
- package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
- package/dist/types/ssh/ssh-executor.d.ts +8 -1
- package/dist/types/system-prompt.d.ts +3 -2
- package/dist/types/task/role-body.d.ts +11 -0
- package/dist/types/tools/awaiting-input.d.ts +28 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +6 -0
- package/dist/types/tools/bash-skill-urls.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +7 -0
- package/dist/types/tools/descriptors.d.ts +13 -2
- package/dist/types/tools/fetch.d.ts +6 -0
- package/dist/types/tools/irc.d.ts +5 -3
- package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/utils/file-mentions.d.ts +5 -0
- package/dist/types/utils/image-limits.d.ts +7 -0
- package/dist/types/utils/image-loading.d.ts +0 -1
- package/dist/types/utils/open.d.ts +0 -1
- package/dist/types/utils/pasted-image-loading.d.ts +1 -5
- package/dist/types/utils/prompt-suggestion.d.ts +2 -2
- package/dist/types/utils/shell-snapshot.d.ts +2 -0
- package/dist/types/utils/title-generator.d.ts +13 -3
- package/dist/types/web/search/providers/codex.d.ts +6 -7
- package/package.json +9 -8
- package/scripts/acp-conformance-agent.ts +12 -2
- package/scripts/generate-sdk-operation-inventory.ts +16 -0
- package/src/capability/fs.ts +31 -3
- package/src/capability/rule.ts +11 -2
- package/src/cli/append-system-prompt.ts +12 -0
- package/src/cli/args.ts +11 -7
- package/src/cli/customize-doctor-precedence.ts +31 -0
- package/src/cli/customize-doctor.ts +49 -39
- package/src/cli/fast-help.ts +0 -2
- package/src/cli/migrate-cli.ts +14 -3
- package/src/cli/public-command-errors.ts +2 -0
- package/src/cli/root-flags.ts +0 -2
- package/src/cli/update-cli.ts +247 -6
- package/src/commands/harness.ts +25 -15
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +81 -11
- package/src/commit/agentic/agent.ts +6 -6
- package/src/commit/analysis/conventional.ts +14 -5
- package/src/commit/map-reduce/index.ts +7 -7
- package/src/commit/pipeline.ts +12 -12
- package/src/config/autorouting-tier-map.ts +20 -1
- package/src/config/config-file.ts +106 -3
- package/src/config/config-hot-reload.ts +8 -0
- package/src/config/file-lock.ts +1431 -304
- package/src/config/keybindings.ts +154 -7
- package/src/config/model-profile-activation.ts +177 -39
- package/src/config/model-registry.ts +380 -55
- package/src/config/model-resolver.ts +49 -21
- package/src/config/settings-schema.ts +13 -1
- package/src/config/settings.ts +2 -0
- package/src/coordinator-mcp/server.ts +37 -2
- package/src/crash/sanitize.ts +3 -1
- package/src/customization/import.ts +276 -7
- package/src/customization/inventory.ts +24 -8
- package/src/customization/types.ts +1 -0
- package/src/dap/client.ts +3 -2
- package/src/dap/session.ts +161 -36
- package/src/debug/crash-diagnostics.ts +87 -5
- package/src/debug/report-bundle.ts +71 -2
- package/src/discovery/builtin.ts +4 -3
- package/src/discovery/helpers.ts +24 -14
- package/src/edit/index.ts +191 -5
- package/src/edit/modes/patch.ts +27 -0
- package/src/eval/py/runtime.ts +44 -4
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/export/html/index.ts +60 -20
- package/src/export/ttsr.ts +176 -1
- package/src/extensibility/extensions/loader.ts +2 -0
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
- package/src/extensibility/extensions/types.ts +5 -1
- package/src/extensibility/hooks/loader.ts +10 -2
- package/src/extensibility/plugins/marketplace/manager.ts +36 -10
- package/src/extensibility/slash-commands.ts +5 -0
- package/src/gjc-runtime/gc-runtime.ts +4 -1
- package/src/gjc-runtime/launch-tmux.ts +23 -13
- package/src/gjc-runtime/ralplan-current-final.ts +294 -0
- package/src/gjc-runtime/ralplan-runtime.ts +95 -1
- package/src/gjc-runtime/state-writer.ts +109 -5
- package/src/gjc-runtime/tmux-sessions.ts +93 -13
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
- package/src/gjc-runtime/ultragoal-guard.ts +9 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
- package/src/harness-control-plane/finalize.ts +11 -0
- package/src/harness-control-plane/receipt-spool.ts +105 -28
- package/src/harness-control-plane/storage.ts +124 -6
- package/src/hindsight/content.ts +116 -16
- package/src/hindsight/mental-models.ts +22 -10
- package/src/hindsight/state.ts +7 -4
- package/src/hooks/native-skill-hook.ts +3 -1
- package/src/internal-urls/artifact-protocol.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +14 -14
- package/src/internal-urls/local-protocol.ts +28 -3
- package/src/internal-urls/types.ts +2 -0
- package/src/lsp/client.ts +2 -1
- package/src/lsp/clients/biome-client.ts +2 -0
- package/src/lsp/clients/swiftlint-client.ts +2 -0
- package/src/main.ts +330 -92
- package/src/memories/index.ts +25 -2
- package/src/migrate/action-planner.ts +517 -2
- package/src/migrate/adapters/claude-code.ts +141 -3
- package/src/migrate/adapters/codex.ts +57 -5
- package/src/migrate/adapters/index.ts +240 -5
- package/src/migrate/adapters/opencode.ts +4 -0
- package/src/migrate/executor.ts +141 -3
- package/src/migrate/hook-module.ts +193 -0
- package/src/migrate/report.ts +45 -1
- package/src/migrate/types.ts +73 -3
- package/src/modes/acp/acp-agent.ts +10 -1
- package/src/modes/components/agent-dashboard.ts +20 -4
- package/src/modes/components/customization/customization-dashboard.ts +7 -0
- package/src/modes/components/customization/import-wizard.ts +29 -3
- package/src/modes/components/model-selector.ts +157 -103
- package/src/modes/components/welcome.ts +3 -1
- package/src/modes/controllers/command-controller.ts +271 -16
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +31 -1
- package/src/modes/controllers/goal-mode-controller.ts +79 -10
- package/src/modes/controllers/input-controller.ts +2 -0
- package/src/modes/controllers/plan-mode-controller.ts +49 -4
- package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
- package/src/modes/controllers/selector-controller.ts +14 -1
- package/src/modes/interactive-mode.ts +55 -4
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +5 -1
- package/src/modes/utils/host-status.ts +21 -10
- package/src/prompts/agent-fragments/browser-aside.md +2 -2
- package/src/prompts/system/irc-incoming.md +3 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/runtime-mcp/manager.ts +8 -3
- package/src/runtime-mcp/project-host-values.ts +12 -8
- package/src/runtime-mcp/tool-bridge.ts +68 -4
- package/src/runtime-mcp/transports/stdio.ts +2 -1
- package/src/sdk/broker/broker-exit.ts +2 -0
- package/src/sdk/broker/broker.ts +143 -4
- package/src/sdk/broker/ensure.ts +74 -6
- package/src/sdk/broker/hop.ts +13 -1
- package/src/sdk/broker/lifecycle-ledger.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +102 -6
- package/src/sdk/broker/session-index.ts +29 -4
- package/src/sdk/broker/transport.ts +17 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +408 -14
- package/src/sdk/bus/session-control.ts +2 -0
- package/src/sdk/bus/slack-daemon.ts +105 -9
- package/src/sdk/bus/telegram-daemon-contract.ts +7 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -2
- package/src/sdk/bus/threaded-render.ts +20 -1
- package/src/sdk/cli/session-cli.ts +2 -0
- package/src/sdk/host/control/dispatch.ts +13 -1
- package/src/sdk/host/control/operations.ts +10 -1
- package/src/sdk/host/events.ts +67 -4
- package/src/sdk/host/host.ts +8 -2
- package/src/sdk/host/prompt-image-upload.ts +438 -0
- package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
- package/src/sdk/host/session-runtime.test.ts +1537 -24
- package/src/sdk/host/session-runtime.ts +332 -36
- package/src/sdk/host/surface-policy.ts +4 -0
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
- package/src/sdk/host/websocket-transport.ts +19 -7
- package/src/sdk/lifecycle/service.ts +14 -1
- package/src/sdk/prompt-status.ts +6 -4
- package/src/sdk/protocol/adapter-validation.ts +28 -1
- package/src/sdk/protocol/operation-inventory.generated.json +160 -0
- package/src/sdk/protocol/operation-registry.ts +33 -3
- package/src/sdk/session.ts +123 -36
- package/src/session/agent-session.ts +1614 -329
- package/src/session/history-storage.ts +182 -12
- package/src/session/internal/managed-session-scope.ts +47 -18
- package/src/session/internal/managed-session-storage.ts +71 -1
- package/src/session/session-manager.ts +90 -5
- package/src/session-import/redact.ts +53 -2
- package/src/skill-state/workflow-mutation-guard.ts +1498 -18
- package/src/slash-commands/builtin-registry.ts +6 -3
- package/src/slash-commands/helpers/mcp.ts +11 -3
- package/src/ssh/ssh-executor.ts +9 -2
- package/src/stt/recorder.ts +26 -9
- package/src/system-prompt.ts +61 -16
- package/src/task/executor.ts +12 -8
- package/src/task/role-body.ts +84 -0
- package/src/task/token-log-mode.test.ts +162 -0
- package/src/task/token-log.ts +91 -2
- package/src/tools/archive-reader.ts +650 -37
- package/src/tools/ask.ts +1 -0
- package/src/tools/ast-grep.ts +7 -2
- package/src/tools/auto-generated-guard.ts +12 -3
- package/src/tools/awaiting-input.ts +114 -0
- package/src/tools/bash-allowed-prefixes.ts +61 -1
- package/src/tools/bash-skill-urls.ts +58 -5
- package/src/tools/bash.ts +99 -26
- package/src/tools/browser/tab-worker.ts +39 -13
- package/src/tools/browser.ts +17 -7
- package/src/tools/cron.ts +10 -1
- package/src/tools/debug.ts +9 -2
- package/src/tools/descriptors.test.ts +4 -4
- package/src/tools/descriptors.ts +16 -4
- package/src/tools/eval.ts +15 -0
- package/src/tools/fetch.ts +90 -2
- package/src/tools/gh.ts +27 -12
- package/src/tools/image-gen.ts +20 -6
- package/src/tools/irc.ts +5 -3
- package/src/tools/monitor.ts +9 -1
- package/src/tools/output-meta.ts +51 -3
- package/src/tools/read.ts +9 -2
- package/src/tools/recipe/runner.ts +23 -12
- package/src/tools/render-utils.ts +2 -2
- package/src/tools/search.ts +3 -2
- package/src/tools/ssh.ts +274 -8
- package/src/tools/subagent.ts +10 -4
- package/src/tools/tmux-self-injection-guard.ts +3301 -122
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/ultragoal-ask-guard.ts +13 -1
- package/src/tools/write.ts +7 -0
- package/src/tui/tree-list.ts +15 -1
- package/src/utils/commit-message-generator.ts +3 -2
- package/src/utils/external-editor.ts +21 -7
- package/src/utils/file-mentions.ts +46 -6
- package/src/utils/git.ts +38 -7
- package/src/utils/image-limits.ts +8 -0
- package/src/utils/image-loading.ts +1 -1
- package/src/utils/open.ts +73 -4
- package/src/utils/pasted-image-loading.ts +9 -8
- package/src/utils/prompt-suggestion.ts +5 -4
- package/src/utils/shell-snapshot.ts +9 -1
- package/src/utils/title-generator.ts +65 -20
- package/src/web/search/index.ts +2 -1
- package/src/web/search/providers/codex.ts +29 -32
|
@@ -1,19 +1,123 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Claude Code adapter: reads
|
|
3
|
-
* and the selected scope's `CLAUDE.md`.
|
|
2
|
+
* Claude Code adapter: reads MCP servers, user skills, selected-scope commands,
|
|
3
|
+
* selected-scope subagents, and the selected scope's `CLAUDE.md`.
|
|
4
4
|
*/
|
|
5
5
|
import * as path from "node:path";
|
|
6
|
-
import
|
|
6
|
+
import { isSupportedHookEvent, SUPPORTED_HOOK_EVENTS } from "../hook-module";
|
|
7
|
+
import type { AdapterResult, HookCandidate, ManualAction, McpCandidate } from "../types";
|
|
7
8
|
import {
|
|
8
9
|
type Adapter,
|
|
9
10
|
type AdapterOptions,
|
|
10
11
|
collectInstructionFile,
|
|
12
|
+
collectMarkdownAgents,
|
|
13
|
+
collectMarkdownCommands,
|
|
11
14
|
collectSkillDir,
|
|
12
15
|
parseSourceJson,
|
|
16
|
+
readOptionalJson,
|
|
13
17
|
readSourceText,
|
|
14
18
|
} from "./index";
|
|
15
19
|
|
|
16
20
|
const SOURCE = "claude-code" as const;
|
|
21
|
+
const MAX_LISTED_KEYS = 8;
|
|
22
|
+
|
|
23
|
+
function listKeys(keys: string[]): string {
|
|
24
|
+
if (keys.length <= MAX_LISTED_KEYS) return keys.join(", ");
|
|
25
|
+
return `${keys.slice(0, MAX_LISTED_KEYS).join(", ")}, +${keys.length - MAX_LISTED_KEYS} more`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function stringArray(value: unknown): string[] {
|
|
29
|
+
return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Flatten a `hooks` object into `{event, matcher?, command}` candidates. */
|
|
33
|
+
function collectHookCommands(hooks: Record<string, unknown>): HookCandidate[] {
|
|
34
|
+
const candidates: HookCandidate[] = [];
|
|
35
|
+
for (const [event, matchers] of Object.entries(hooks)) {
|
|
36
|
+
if (!Array.isArray(matchers)) continue;
|
|
37
|
+
for (const matcher of matchers) {
|
|
38
|
+
if (!matcher || typeof matcher !== "object") continue;
|
|
39
|
+
const record = matcher as Record<string, unknown>;
|
|
40
|
+
const matcherValue = typeof record.matcher === "string" ? record.matcher.trim() : "";
|
|
41
|
+
const inner = record.hooks;
|
|
42
|
+
if (!Array.isArray(inner)) continue;
|
|
43
|
+
for (const hook of inner) {
|
|
44
|
+
if (!hook || typeof hook !== "object") continue;
|
|
45
|
+
const command = (hook as Record<string, unknown>).command;
|
|
46
|
+
if (typeof command !== "string" || !command.trim()) continue;
|
|
47
|
+
candidates.push({
|
|
48
|
+
source: SOURCE,
|
|
49
|
+
event,
|
|
50
|
+
...(matcherValue ? { matcher: matcherValue } : {}),
|
|
51
|
+
command,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return candidates;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Describe Claude Code settings that gjc cannot import. These are reported
|
|
61
|
+
* rather than translated: gjc hooks are TypeScript modules, model ids are
|
|
62
|
+
* provider-specific, and an approval policy that changed meaning silently would
|
|
63
|
+
* be a security regression. Environment values are never read.
|
|
64
|
+
*/
|
|
65
|
+
function describeSettings(data: Record<string, unknown>, label: string): ManualAction[] {
|
|
66
|
+
const actions: ManualAction[] = [];
|
|
67
|
+
const hooks = data.hooks;
|
|
68
|
+
if (hooks && typeof hooks === "object" && !Array.isArray(hooks)) {
|
|
69
|
+
const byEvent = new Map<string, number>();
|
|
70
|
+
for (const candidate of collectHookCommands(hooks as Record<string, unknown>)) {
|
|
71
|
+
if (isSupportedHookEvent(candidate.event)) continue;
|
|
72
|
+
byEvent.set(candidate.event, (byEvent.get(candidate.event) ?? 0) + 1);
|
|
73
|
+
}
|
|
74
|
+
for (const [event, commands] of [...byEvent.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
75
|
+
actions.push({
|
|
76
|
+
source: SOURCE,
|
|
77
|
+
area: `${label}: hooks.${event}`,
|
|
78
|
+
detail: `${commands} shell command${commands === 1 ? "" : "s"} run on ${event}, which has no faithful gjc equivalent; port ${
|
|
79
|
+
commands === 1 ? "it" : "them"
|
|
80
|
+
} by hand to a .gjc/hooks module. Importable events: ${SUPPORTED_HOOK_EVENTS.join(", ")}.`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const model = data.model;
|
|
85
|
+
if (typeof model === "string" && model.trim()) {
|
|
86
|
+
actions.push({
|
|
87
|
+
source: SOURCE,
|
|
88
|
+
area: `${label}: model`,
|
|
89
|
+
detail: `model "${model}" is provider-specific and was not imported; select it with gjc config or --model.`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const permissions = data.permissions;
|
|
93
|
+
if (permissions && typeof permissions === "object" && !Array.isArray(permissions)) {
|
|
94
|
+
const record = permissions as Record<string, unknown>;
|
|
95
|
+
const allow = stringArray(record.allow).length;
|
|
96
|
+
const deny = stringArray(record.deny).length;
|
|
97
|
+
const ask = stringArray(record.ask).length;
|
|
98
|
+
if (allow + deny + ask > 0) {
|
|
99
|
+
actions.push({
|
|
100
|
+
source: SOURCE,
|
|
101
|
+
area: `${label}: permissions`,
|
|
102
|
+
detail: `${allow} allow, ${deny} deny, ${ask} ask rule${allow + deny + ask === 1 ? "" : "s"} have no gjc equivalent and were not imported; re-express the approvals you still need in gjc's tool settings.${
|
|
103
|
+
deny > 0 ? " Deny rules are the security-relevant ones — review them first." : ""
|
|
104
|
+
}`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const env = data.env;
|
|
109
|
+
if (env && typeof env === "object" && !Array.isArray(env)) {
|
|
110
|
+
const keys = Object.keys(env as Record<string, unknown>).sort();
|
|
111
|
+
if (keys.length > 0) {
|
|
112
|
+
actions.push({
|
|
113
|
+
source: SOURCE,
|
|
114
|
+
area: `${label}: env`,
|
|
115
|
+
detail: `${keys.length} variable${keys.length === 1 ? "" : "s"} (${listKeys(keys)}) were not imported and their values were not read; export them in your shell or project environment instead.`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return actions;
|
|
120
|
+
}
|
|
17
121
|
|
|
18
122
|
export const claudeCodeAdapter: Adapter = {
|
|
19
123
|
source: SOURCE,
|
|
@@ -21,8 +125,12 @@ export const claudeCodeAdapter: Adapter = {
|
|
|
21
125
|
const result: AdapterResult = {
|
|
22
126
|
mcpCandidates: [],
|
|
23
127
|
skillCandidates: [],
|
|
128
|
+
slashCommandCandidates: [],
|
|
129
|
+
agentCandidates: [],
|
|
130
|
+
hookCandidates: [],
|
|
24
131
|
instructionCandidates: [],
|
|
25
132
|
diagnostics: [],
|
|
133
|
+
manualActions: [],
|
|
26
134
|
};
|
|
27
135
|
|
|
28
136
|
const configPath = path.join(homeDir, ".claude.json");
|
|
@@ -47,12 +155,42 @@ export const claudeCodeAdapter: Adapter = {
|
|
|
47
155
|
result.skillCandidates.push(...skills.candidates);
|
|
48
156
|
result.diagnostics.push(...skills.diagnostics);
|
|
49
157
|
|
|
158
|
+
const commandsDir =
|
|
159
|
+
scope === "project" ? path.join(cwd, ".claude", "commands") : path.join(homeDir, ".claude", "commands");
|
|
160
|
+
const commands = await collectMarkdownCommands(commandsDir, SOURCE, { recursive: true });
|
|
161
|
+
result.slashCommandCandidates.push(...commands.candidates);
|
|
162
|
+
result.diagnostics.push(...commands.diagnostics);
|
|
163
|
+
|
|
164
|
+
const agentsDir =
|
|
165
|
+
scope === "project" ? path.join(cwd, ".claude", "agents") : path.join(homeDir, ".claude", "agents");
|
|
166
|
+
const agents = await collectMarkdownAgents(agentsDir, SOURCE);
|
|
167
|
+
result.agentCandidates.push(...agents.candidates);
|
|
168
|
+
result.diagnostics.push(...agents.diagnostics);
|
|
169
|
+
|
|
50
170
|
const instructionsPath =
|
|
51
171
|
scope === "project" ? path.join(cwd, "CLAUDE.md") : path.join(homeDir, ".claude", "CLAUDE.md");
|
|
52
172
|
const instructions = await collectInstructionFile(instructionsPath, SOURCE);
|
|
53
173
|
result.instructionCandidates.push(...instructions.candidates);
|
|
54
174
|
result.diagnostics.push(...instructions.diagnostics);
|
|
55
175
|
|
|
176
|
+
const settingsFiles =
|
|
177
|
+
scope === "project"
|
|
178
|
+
? [path.join(cwd, ".claude", "settings.json"), path.join(cwd, ".claude", "settings.local.json")]
|
|
179
|
+
: [path.join(homeDir, ".claude", "settings.json")];
|
|
180
|
+
for (const file of settingsFiles) {
|
|
181
|
+
const settings = await readOptionalJson(file, SOURCE, "source");
|
|
182
|
+
if ("absent" in settings) continue;
|
|
183
|
+
if ("diagnostic" in settings) {
|
|
184
|
+
result.diagnostics.push(settings.diagnostic);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const settingsHooks = settings.data.hooks;
|
|
188
|
+
if (settingsHooks && typeof settingsHooks === "object" && !Array.isArray(settingsHooks)) {
|
|
189
|
+
result.hookCandidates.push(...collectHookCommands(settingsHooks as Record<string, unknown>));
|
|
190
|
+
}
|
|
191
|
+
result.manualActions.push(...describeSettings(settings.data, path.basename(file)));
|
|
192
|
+
}
|
|
193
|
+
|
|
56
194
|
return result;
|
|
57
195
|
},
|
|
58
196
|
};
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Codex adapter: reads `~/.codex/config.toml` ([mcp_servers]),
|
|
2
|
+
* Codex adapter: reads `~/.codex/config.toml` ([mcp_servers]), user prompts,
|
|
3
3
|
* and user-level `~/.codex/AGENTS.md` instructions. Project AGENTS.md is native GJC input.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import { TOML } from "bun";
|
|
8
|
-
import type { AdapterResult, McpCandidate, SourceDiagnostic } from "../types";
|
|
8
|
+
import type { AdapterResult, ManualAction, McpCandidate, SourceDiagnostic } from "../types";
|
|
9
9
|
import {
|
|
10
10
|
type Adapter,
|
|
11
11
|
type AdapterOptions,
|
|
12
12
|
collectInstructionFile,
|
|
13
|
-
|
|
13
|
+
collectMarkdownCommands,
|
|
14
14
|
readSourceText,
|
|
15
15
|
} from "./index";
|
|
16
16
|
|
|
@@ -45,14 +45,63 @@ function parseToml(
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Describe `config.toml` settings gjc cannot import. Approvals and sandbox mode
|
|
50
|
+
* change what the agent may do, so they are reported for the user to re-express
|
|
51
|
+
* rather than translated. Environment variable names are listed; values are not.
|
|
52
|
+
*/
|
|
53
|
+
function describeCodexSettings(data: Record<string, unknown>): ManualAction[] {
|
|
54
|
+
const actions: ManualAction[] = [];
|
|
55
|
+
const model = data.model;
|
|
56
|
+
if (typeof model === "string" && model.trim()) {
|
|
57
|
+
actions.push({
|
|
58
|
+
source: SOURCE,
|
|
59
|
+
area: "config.toml: model",
|
|
60
|
+
detail: `model "${model}" is provider-specific and was not imported; select it with gjc config or --model.`,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
for (const key of ["approval_policy", "sandbox_mode"] as const) {
|
|
64
|
+
const value = data[key];
|
|
65
|
+
if (typeof value !== "string" || !value.trim()) continue;
|
|
66
|
+
actions.push({
|
|
67
|
+
source: SOURCE,
|
|
68
|
+
area: `config.toml: ${key}`,
|
|
69
|
+
detail: `"${value}" has no gjc equivalent and was not imported; review gjc's tool approval and sandbox settings.`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const policy = data.shell_environment_policy;
|
|
73
|
+
if (policy && typeof policy === "object" && !Array.isArray(policy)) {
|
|
74
|
+
const record = policy as Record<string, unknown>;
|
|
75
|
+
const setKeys = record.set && typeof record.set === "object" ? Object.keys(record.set as object).sort() : [];
|
|
76
|
+
const unsetKeys = Array.isArray(record.unset)
|
|
77
|
+
? record.unset.filter((key): key is string => typeof key === "string").sort()
|
|
78
|
+
: [];
|
|
79
|
+
if (setKeys.length > 0 || unsetKeys.length > 0) {
|
|
80
|
+
const parts: string[] = [];
|
|
81
|
+
if (setKeys.length > 0) parts.push(`sets ${setKeys.join(", ")}`);
|
|
82
|
+
if (unsetKeys.length > 0) parts.push(`unsets ${unsetKeys.join(", ")}`);
|
|
83
|
+
actions.push({
|
|
84
|
+
source: SOURCE,
|
|
85
|
+
area: "config.toml: shell_environment_policy",
|
|
86
|
+
detail: `shell_environment_policy ${parts.join(" and ")}; variable names were reported without values and nothing was imported.`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return actions;
|
|
91
|
+
}
|
|
92
|
+
|
|
48
93
|
export const codexAdapter: Adapter = {
|
|
49
94
|
source: SOURCE,
|
|
50
95
|
async collect({ homeDir, scope }: AdapterOptions): Promise<AdapterResult> {
|
|
51
96
|
const result: AdapterResult = {
|
|
52
97
|
mcpCandidates: [],
|
|
53
98
|
skillCandidates: [],
|
|
99
|
+
slashCommandCandidates: [],
|
|
100
|
+
agentCandidates: [],
|
|
54
101
|
instructionCandidates: [],
|
|
102
|
+
hookCandidates: [],
|
|
55
103
|
diagnostics: [],
|
|
104
|
+
manualActions: [],
|
|
56
105
|
};
|
|
57
106
|
|
|
58
107
|
const configPath = path.join(homeDir, ".codex", "config.toml");
|
|
@@ -70,11 +119,14 @@ export const codexAdapter: Adapter = {
|
|
|
70
119
|
result.mcpCandidates.push({ source: SOURCE, name, raw } satisfies McpCandidate);
|
|
71
120
|
}
|
|
72
121
|
}
|
|
122
|
+
result.manualActions.push(...describeCodexSettings(parsed.data));
|
|
73
123
|
}
|
|
74
124
|
}
|
|
75
125
|
|
|
76
|
-
const prompts = await
|
|
77
|
-
result.
|
|
126
|
+
const prompts = await collectMarkdownCommands(path.join(homeDir, ".codex", "prompts"), SOURCE);
|
|
127
|
+
result.slashCommandCandidates.push(
|
|
128
|
+
...prompts.candidates.map(candidate => ({ ...candidate, name: `prompts:${candidate.name}` })),
|
|
129
|
+
);
|
|
78
130
|
result.diagnostics.push(...prompts.diagnostics);
|
|
79
131
|
|
|
80
132
|
if (scope === "user") {
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Source adapters for `gjc migrate`.
|
|
3
3
|
*
|
|
4
|
-
* Each adapter returns normalized
|
|
4
|
+
* Each adapter returns normalized MCP/skill/command candidates, selected-scope
|
|
5
5
|
* instruction candidates, and source-level diagnostics. Adapters never connect to
|
|
6
6
|
* anything.
|
|
7
7
|
*/
|
|
8
8
|
import * as fs from "node:fs/promises";
|
|
9
9
|
import * as path from "node:path";
|
|
10
|
-
import { isEnoent } from "@gajae-code/utils";
|
|
10
|
+
import { isEnoent, parseFrontmatter } from "@gajae-code/utils";
|
|
11
|
+
import { YAML } from "bun";
|
|
11
12
|
import { normalizeSkill } from "../skill-normalizer";
|
|
12
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
AdapterResult,
|
|
15
|
+
AgentCandidate,
|
|
16
|
+
InstructionCandidate,
|
|
17
|
+
MigrateSource,
|
|
18
|
+
SkillCandidate,
|
|
19
|
+
SlashCommandCandidate,
|
|
20
|
+
SourceDiagnostic,
|
|
21
|
+
} from "../types";
|
|
13
22
|
import { claudeCodeAdapter } from "./claude-code";
|
|
14
23
|
import { codexAdapter } from "./codex";
|
|
15
24
|
import { opencodeAdapter } from "./opencode";
|
|
@@ -17,9 +26,9 @@ import { opencodeAdapter } from "./opencode";
|
|
|
17
26
|
export interface AdapterOptions {
|
|
18
27
|
/** Home directory root; overridable for tests. */
|
|
19
28
|
homeDir: string;
|
|
20
|
-
/** Source scope for instructions
|
|
29
|
+
/** Source scope for instructions and Claude Code commands. */
|
|
21
30
|
scope: "project" | "user";
|
|
22
|
-
/** Project root used when importing project instructions. */
|
|
31
|
+
/** Project root used when importing project instructions and commands. */
|
|
23
32
|
cwd: string;
|
|
24
33
|
}
|
|
25
34
|
|
|
@@ -76,6 +85,32 @@ export async function readSourceText(
|
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Read an optional settings file. A missing file is expected (most setups do not
|
|
90
|
+
* have every settings file), so unlike `readSourceText` it produces no diagnostic.
|
|
91
|
+
*/
|
|
92
|
+
export async function readOptionalJson(
|
|
93
|
+
filePath: string,
|
|
94
|
+
source: MigrateSource,
|
|
95
|
+
type: SourceDiagnostic["type"],
|
|
96
|
+
): Promise<{ data: Record<string, unknown> } | { diagnostic: SourceDiagnostic } | { absent: true }> {
|
|
97
|
+
let text: string;
|
|
98
|
+
try {
|
|
99
|
+
text = await fs.readFile(filePath, "utf-8");
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (isEnoent(error)) return { absent: true };
|
|
102
|
+
return {
|
|
103
|
+
diagnostic: {
|
|
104
|
+
source,
|
|
105
|
+
type,
|
|
106
|
+
status: "failed_io",
|
|
107
|
+
message: `failed to read ${filePath}: ${(error as Error).message}`,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return parseSourceJson(text, filePath, source, type);
|
|
112
|
+
}
|
|
113
|
+
|
|
79
114
|
/** Parse JSON text, classifying parse errors into a `failed_invalid_source` diagnostic. */
|
|
80
115
|
export function parseSourceJson(
|
|
81
116
|
text: string,
|
|
@@ -214,6 +249,206 @@ export async function collectMarkdownPrompts(
|
|
|
214
249
|
return { candidates, diagnostics };
|
|
215
250
|
}
|
|
216
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Foreign subagent tools map onto the GJC tool surfaces that carry the same
|
|
254
|
+
* capability. Anything without an equivalent is dropped from the tool list (and
|
|
255
|
+
* reported) rather than written into an agent that cannot be loaded.
|
|
256
|
+
*/
|
|
257
|
+
const AGENT_TOOL_EQUIVALENTS: Readonly<Record<string, string | undefined>> = {
|
|
258
|
+
read: "read",
|
|
259
|
+
write: "edit",
|
|
260
|
+
edit: "edit",
|
|
261
|
+
multiedit: "edit",
|
|
262
|
+
notebookedit: undefined,
|
|
263
|
+
bash: "bash",
|
|
264
|
+
bashoutput: undefined,
|
|
265
|
+
killbash: undefined,
|
|
266
|
+
grep: "grep",
|
|
267
|
+
glob: "glob",
|
|
268
|
+
ls: undefined,
|
|
269
|
+
task: "task",
|
|
270
|
+
agent: "task",
|
|
271
|
+
webfetch: "web_search",
|
|
272
|
+
websearch: "web_search",
|
|
273
|
+
todowrite: undefined,
|
|
274
|
+
todoread: undefined,
|
|
275
|
+
exitplanmode: undefined,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/** Bundled agent names an imported agent must never shadow. */
|
|
279
|
+
export const PROTECTED_AGENT_NAMES: ReadonlySet<string> = new Set(["executor", "architect", "planner", "critic"]);
|
|
280
|
+
|
|
281
|
+
const AGENT_NAME_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
282
|
+
|
|
283
|
+
function importedAgentTools(raw: unknown, warnings: string[]): string[] | undefined {
|
|
284
|
+
const list =
|
|
285
|
+
typeof raw === "string"
|
|
286
|
+
? raw.split(",")
|
|
287
|
+
: Array.isArray(raw)
|
|
288
|
+
? raw.filter((entry): entry is string => typeof entry === "string")
|
|
289
|
+
: [];
|
|
290
|
+
const mapped: string[] = [];
|
|
291
|
+
for (const entry of list) {
|
|
292
|
+
const key = entry.trim().toLowerCase();
|
|
293
|
+
if (!key) continue;
|
|
294
|
+
if (!(key in AGENT_TOOL_EQUIVALENTS)) {
|
|
295
|
+
warnings.push(`dropped unsupported agent tool "${entry.trim()}"`);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
const equivalent = AGENT_TOOL_EQUIVALENTS[key];
|
|
299
|
+
if (!equivalent) {
|
|
300
|
+
warnings.push(`dropped agent tool "${entry.trim()}" (no GJC equivalent)`);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (!mapped.includes(equivalent)) mapped.push(equivalent);
|
|
304
|
+
}
|
|
305
|
+
return mapped.length > 0 ? mapped : undefined;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Collect subagents from a flat `<agentsDir>/*.md` directory. Frontmatter is
|
|
310
|
+
* rewritten so the loaded name matches the destination file name, and tool lists
|
|
311
|
+
* are translated to GJC tools.
|
|
312
|
+
*/
|
|
313
|
+
export async function collectMarkdownAgents(
|
|
314
|
+
dir: string,
|
|
315
|
+
source: MigrateSource,
|
|
316
|
+
): Promise<{ candidates: AgentCandidate[]; diagnostics: SourceDiagnostic[] }> {
|
|
317
|
+
const candidates: AgentCandidate[] = [];
|
|
318
|
+
const diagnostics: SourceDiagnostic[] = [];
|
|
319
|
+
const entries = await fs.readdir(dir, { withFileTypes: true }).catch((error: unknown) => {
|
|
320
|
+
if (isEnoent(error)) {
|
|
321
|
+
diagnostics.push({
|
|
322
|
+
source,
|
|
323
|
+
type: "agent",
|
|
324
|
+
status: "skipped_absent_source",
|
|
325
|
+
message: `no agents dir at ${dir}`,
|
|
326
|
+
});
|
|
327
|
+
} else {
|
|
328
|
+
diagnostics.push({
|
|
329
|
+
source,
|
|
330
|
+
type: "agent",
|
|
331
|
+
status: "failed_io",
|
|
332
|
+
message: `failed to read ${dir}: ${(error as Error).message}`,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
return undefined;
|
|
336
|
+
});
|
|
337
|
+
if (!entries) return { candidates, diagnostics };
|
|
338
|
+
|
|
339
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
340
|
+
if (entry.name.startsWith(".")) continue;
|
|
341
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
342
|
+
const entryPath = path.join(dir, entry.name);
|
|
343
|
+
const read = await readSourceText(entryPath, source, "agent");
|
|
344
|
+
if ("diagnostic" in read) {
|
|
345
|
+
diagnostics.push(read.diagnostic);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const warnings: string[] = [];
|
|
350
|
+
const { frontmatter, body } = parseFrontmatter(read.text, { location: entryPath, level: "off" });
|
|
351
|
+
const rawName = typeof frontmatter.name === "string" ? frontmatter.name.trim() : entry.name.slice(0, -3);
|
|
352
|
+
const description = typeof frontmatter.description === "string" ? frontmatter.description.trim() : "";
|
|
353
|
+
if (!AGENT_NAME_SEGMENT.test(rawName)) {
|
|
354
|
+
diagnostics.push({
|
|
355
|
+
source,
|
|
356
|
+
type: "agent",
|
|
357
|
+
status: "failed_invalid_source",
|
|
358
|
+
message: `unsupported agent name "${rawName}" in ${entry.name}; names must use letters, numbers, dots, underscores, or hyphens`,
|
|
359
|
+
});
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (!description || !body.trim()) {
|
|
363
|
+
diagnostics.push({
|
|
364
|
+
source,
|
|
365
|
+
type: "agent",
|
|
366
|
+
status: "failed_invalid_source",
|
|
367
|
+
message: `agent ${entry.name} needs both a description and a prompt body`,
|
|
368
|
+
});
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const tools = importedAgentTools(frontmatter.tools, warnings);
|
|
373
|
+
const normalized: Record<string, unknown> = { name: rawName, description, ...(tools ? { tools } : {}) };
|
|
374
|
+
const yaml = YAML.stringify(normalized).trimEnd();
|
|
375
|
+
candidates.push({
|
|
376
|
+
source,
|
|
377
|
+
name: rawName,
|
|
378
|
+
content: `---\n${yaml}\n---\n\n${body.trim()}\n`,
|
|
379
|
+
warnings,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
return { candidates, diagnostics };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Collect Markdown slash commands. Nested command directories are represented
|
|
387
|
+
* with colon-separated names, matching Claude Code's `/group:command` syntax.
|
|
388
|
+
*/
|
|
389
|
+
export async function collectMarkdownCommands(
|
|
390
|
+
dir: string,
|
|
391
|
+
source: MigrateSource,
|
|
392
|
+
options: { recursive?: boolean } = {},
|
|
393
|
+
): Promise<{ candidates: SlashCommandCandidate[]; diagnostics: SourceDiagnostic[] }> {
|
|
394
|
+
const candidates: SlashCommandCandidate[] = [];
|
|
395
|
+
const diagnostics: SourceDiagnostic[] = [];
|
|
396
|
+
const validNameSegment = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
397
|
+
|
|
398
|
+
async function walk(currentDir: string, parentSegments: string[], root = false): Promise<void> {
|
|
399
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true }).catch((error: unknown) => {
|
|
400
|
+
if (root && isEnoent(error)) {
|
|
401
|
+
diagnostics.push({
|
|
402
|
+
source,
|
|
403
|
+
type: "command",
|
|
404
|
+
status: "skipped_absent_source",
|
|
405
|
+
message: `no commands dir at ${dir}`,
|
|
406
|
+
});
|
|
407
|
+
} else if (!isEnoent(error)) {
|
|
408
|
+
diagnostics.push({
|
|
409
|
+
source,
|
|
410
|
+
type: "command",
|
|
411
|
+
status: "failed_io",
|
|
412
|
+
message: `failed to read ${currentDir}: ${(error as Error).message}`,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
return undefined;
|
|
416
|
+
});
|
|
417
|
+
if (!entries) return;
|
|
418
|
+
|
|
419
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
420
|
+
if (entry.name.startsWith(".")) continue;
|
|
421
|
+
const entryPath = path.join(currentDir, entry.name);
|
|
422
|
+
if (entry.isDirectory()) {
|
|
423
|
+
if (options.recursive) await walk(entryPath, [...parentSegments, entry.name]);
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
427
|
+
|
|
428
|
+
const nameParts = [...parentSegments, entry.name.slice(0, -3)];
|
|
429
|
+
if (!nameParts.every(part => validNameSegment.test(part))) {
|
|
430
|
+
diagnostics.push({
|
|
431
|
+
source,
|
|
432
|
+
type: "command",
|
|
433
|
+
status: "failed_invalid_source",
|
|
434
|
+
message: `unsupported slash-command path ${path.relative(dir, entryPath)}; names must use letters, numbers, dots, underscores, or hyphens`,
|
|
435
|
+
});
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const read = await readSourceText(entryPath, source, "command");
|
|
440
|
+
if ("diagnostic" in read) {
|
|
441
|
+
diagnostics.push(read.diagnostic);
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
candidates.push({ source, name: nameParts.join(":"), content: read.text });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
await walk(dir, [], true);
|
|
449
|
+
return { candidates, diagnostics };
|
|
450
|
+
}
|
|
451
|
+
|
|
217
452
|
/**
|
|
218
453
|
* Recursively collect skill candidates from any `**/SKILL.md` under `root`.
|
|
219
454
|
* The slug derives from the directory that directly contains the `SKILL.md`.
|
|
@@ -21,8 +21,12 @@ export const opencodeAdapter: Adapter = {
|
|
|
21
21
|
const result: AdapterResult = {
|
|
22
22
|
mcpCandidates: [],
|
|
23
23
|
skillCandidates: [],
|
|
24
|
+
slashCommandCandidates: [],
|
|
25
|
+
agentCandidates: [],
|
|
24
26
|
instructionCandidates: [],
|
|
27
|
+
hookCandidates: [],
|
|
25
28
|
diagnostics: [],
|
|
29
|
+
manualActions: [],
|
|
26
30
|
};
|
|
27
31
|
const baseDir = path.join(homeDir, ".config", "opencode");
|
|
28
32
|
|