salmon-loop 0.2.3 → 0.2.16

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.
Files changed (234) hide show
  1. package/dist/cli/argv/headless-detection.js +27 -0
  2. package/dist/cli/chat-flow.js +11 -0
  3. package/dist/cli/chat.js +161 -24
  4. package/dist/cli/commands/chat.js +30 -24
  5. package/dist/cli/commands/context.js +15 -3
  6. package/dist/cli/commands/flow-mode.js +63 -0
  7. package/dist/cli/commands/help-format.js +12 -0
  8. package/dist/cli/commands/registry.js +6 -7
  9. package/dist/cli/commands/run/benchmark-artifacts.js +41 -0
  10. package/dist/cli/commands/run/config-resolution.js +30 -24
  11. package/dist/cli/commands/run/early-errors.js +23 -0
  12. package/dist/cli/commands/run/handler.js +131 -44
  13. package/dist/cli/commands/run/headless-error-writer.js +8 -0
  14. package/dist/cli/commands/run/loop-params.js +3 -0
  15. package/dist/cli/commands/run/mode.js +2 -5
  16. package/dist/cli/commands/run/parse-options.js +18 -2
  17. package/dist/cli/commands/run/persist-session.js +10 -1
  18. package/dist/cli/commands/run/preflight.js +10 -0
  19. package/dist/cli/commands/run/reporter-factory.js +4 -0
  20. package/dist/cli/commands/run/runtime-llm.js +38 -11
  21. package/dist/cli/commands/run/runtime-options.js +2 -2
  22. package/dist/cli/commands/run/validate-options.js +0 -5
  23. package/dist/cli/commands/run/verbose.js +2 -7
  24. package/dist/cli/commands/serve.js +117 -90
  25. package/dist/cli/commands/tool-names.js +78 -78
  26. package/dist/cli/headless/anthropic-stream-normalized-encoder.js +6 -1
  27. package/dist/cli/headless/json-protocol.js +37 -0
  28. package/dist/cli/headless/native-stream-normalized-encoder.js +6 -1
  29. package/dist/cli/headless/protocol-metadata.js +22 -0
  30. package/dist/cli/headless/stream-json-protocol.js +34 -1
  31. package/dist/cli/index.js +6 -4
  32. package/dist/cli/locales/en.js +32 -6
  33. package/dist/cli/program-bootstrap.js +14 -4
  34. package/dist/cli/program-commands.js +9 -1
  35. package/dist/cli/program-options.js +1 -0
  36. package/dist/cli/reporters/anthropic-stream.js +7 -1
  37. package/dist/cli/reporters/json.js +4 -0
  38. package/dist/cli/reporters/stream-json.js +17 -2
  39. package/dist/cli/run-cli.js +5 -3
  40. package/dist/cli/slash/runtime.js +30 -15
  41. package/dist/cli/ui/components/CommandInput.js +7 -3
  42. package/dist/cli/ui/components/CommandSuggestionList.js +1 -1
  43. package/dist/cli/utils/command-option-source.js +13 -0
  44. package/dist/cli/utils/output-format.js +6 -0
  45. package/dist/cli/utils/resolve-cli-config.js +98 -0
  46. package/dist/cli/utils/verbose-level.js +8 -0
  47. package/dist/cli/utils/verify-resolver.js +8 -4
  48. package/dist/cli/utils/worktree-prepare-resolver.js +7 -3
  49. package/dist/core/adapters/fs/file-adapter.js +6 -0
  50. package/dist/core/adapters/fs/filesystem.js +2 -1
  51. package/dist/core/adapters/git/git-adapter.js +78 -1
  52. package/dist/core/benchmark/patch-artifact.js +124 -0
  53. package/dist/core/benchmark/swe-bench.js +25 -0
  54. package/dist/core/config/load.js +39 -18
  55. package/dist/core/config/merge.js +27 -0
  56. package/dist/core/config/paths.js +24 -5
  57. package/dist/core/config/resolve-llm.js +12 -0
  58. package/dist/core/config/resolve.js +7 -5
  59. package/dist/core/config/resolvers/server.js +0 -6
  60. package/dist/core/config/validate.js +94 -21
  61. package/dist/core/context/gatherers/metadata-gatherer.js +1 -0
  62. package/dist/core/context/gatherers/ripgrep-gatherer.js +84 -2
  63. package/dist/core/context/keywords.js +18 -4
  64. package/dist/core/context/service-deps.js +2 -2
  65. package/dist/core/context/service.js +8 -0
  66. package/dist/core/context/steps/context-gather.js +38 -0
  67. package/dist/core/context/summarization/summarizer.js +55 -12
  68. package/dist/core/context/targeting/target-resolver.js +4 -4
  69. package/dist/core/extensions/index.js +23 -5
  70. package/dist/core/extensions/paths.js +31 -0
  71. package/dist/core/extensions/schemas.js +8 -5
  72. package/dist/core/facades/cli-chat.js +6 -2
  73. package/dist/core/facades/cli-command-chat.js +2 -1
  74. package/dist/core/facades/cli-command-tool-names.js +2 -0
  75. package/dist/core/facades/cli-context.js +1 -0
  76. package/dist/core/facades/cli-observability.js +1 -1
  77. package/dist/core/facades/cli-run-handler.js +4 -2
  78. package/dist/core/facades/cli-run-persist-session.js +1 -0
  79. package/dist/core/facades/cli-serve.js +2 -4
  80. package/dist/core/facades/cli-utils-worktree.js +1 -1
  81. package/dist/core/failure/diagnostics.js +53 -1
  82. package/dist/core/grizzco/dsl/llm-strategy.js +4 -1
  83. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +67 -9
  84. package/dist/core/grizzco/engine/pipeline/pipeline.js +6 -2
  85. package/dist/core/grizzco/engine/transaction/attempt-failure.js +90 -15
  86. package/dist/core/grizzco/engine/transaction/report-mapper.js +17 -3
  87. package/dist/core/grizzco/engine/transaction/transaction-runner.js +173 -7
  88. package/dist/core/grizzco/flows/AutopilotFlow.js +18 -0
  89. package/dist/core/grizzco/flows/flow-dispatch.js +11 -0
  90. package/dist/core/grizzco/steps/answer.js +13 -14
  91. package/dist/core/grizzco/steps/autopilot.js +396 -0
  92. package/dist/core/grizzco/steps/cache-sharing.js +29 -0
  93. package/dist/core/grizzco/steps/explore.js +37 -21
  94. package/dist/core/grizzco/steps/generateReview.js +2 -5
  95. package/dist/core/grizzco/steps/patch/apply-check.js +10 -0
  96. package/dist/core/grizzco/steps/patch/diff-normalization.js +70 -0
  97. package/dist/core/grizzco/steps/patch/diff-salvage.js +46 -0
  98. package/dist/core/grizzco/steps/patch/prompt-input.js +42 -0
  99. package/dist/core/grizzco/steps/patch.js +105 -146
  100. package/dist/core/grizzco/steps/plan.js +101 -25
  101. package/dist/core/grizzco/steps/preflight.js +5 -3
  102. package/dist/core/grizzco/steps/request-assembly.js +78 -0
  103. package/dist/core/grizzco/steps/research.js +39 -36
  104. package/dist/core/grizzco/steps/tool-runtime.js +47 -0
  105. package/dist/core/grizzco/steps/verify-shared.js +23 -0
  106. package/dist/core/grizzco/steps/verify.js +13 -21
  107. package/dist/core/intent/chat-intent.js +0 -4
  108. package/dist/core/llm/ai-sdk/chat-executor.js +2 -0
  109. package/dist/core/llm/ai-sdk/high-level-phase-specs.js +63 -0
  110. package/dist/core/llm/ai-sdk/message-mapper.js +40 -10
  111. package/dist/core/llm/ai-sdk/provider-factory.js +14 -0
  112. package/dist/core/llm/ai-sdk/request-params.js +74 -1
  113. package/dist/core/llm/ai-sdk/result-mapper.js +16 -0
  114. package/dist/core/llm/ai-sdk.js +112 -27
  115. package/dist/core/llm/capabilities.js +12 -0
  116. package/dist/core/llm/contracts/repair.js +36 -30
  117. package/dist/core/llm/errors.js +83 -2
  118. package/dist/core/llm/message-composition.js +7 -22
  119. package/dist/core/llm/phase-router.js +29 -10
  120. package/dist/core/llm/redact.js +28 -3
  121. package/dist/core/llm/registry.js +2 -0
  122. package/dist/core/llm/request-augmentation.js +55 -0
  123. package/dist/core/llm/request-envelope.js +334 -0
  124. package/dist/core/llm/shared-request-assembly.js +35 -0
  125. package/dist/core/llm/stream-utils.js +13 -4
  126. package/dist/core/llm/utils.js +18 -29
  127. package/dist/core/memory/relevant-retrieval.js +144 -0
  128. package/dist/core/observability/logger.js +11 -2
  129. package/dist/core/patch/diff.js +1 -0
  130. package/dist/core/prompts/registry.js +39 -2
  131. package/dist/core/prompts/runtime.js +50 -12
  132. package/dist/core/prompts/templates/phases/patch_user.hbs +2 -5
  133. package/dist/core/prompts/templates/phases/research_user.hbs +11 -0
  134. package/dist/core/prompts/templates/phases/review_user.hbs +3 -0
  135. package/dist/core/prompts/templates/system/answer_system.hbs +5 -0
  136. package/dist/core/prompts/templates/system/autopilot_system.hbs +11 -0
  137. package/dist/core/prompts/templates/system/explore_system.hbs +14 -23
  138. package/dist/core/prompts/templates/system/main_system.hbs +4 -16
  139. package/dist/core/prompts/templates/system/patch_system.hbs +39 -8
  140. package/dist/core/prompts/templates/system/plan_system.hbs +86 -1
  141. package/dist/core/prompts/templates/system/research_system.hbs +2 -0
  142. package/dist/core/protocols/a2a/agent-card.js +3 -2
  143. package/dist/core/protocols/a2a/sdk/executor.js +8 -6
  144. package/dist/core/protocols/a2a/sdk/server.js +0 -1
  145. package/dist/core/protocols/acp/formal-agent.js +221 -55
  146. package/dist/core/protocols/acp/handlers.js +5 -1
  147. package/dist/core/protocols/acp/permission-provider.js +21 -1
  148. package/dist/core/protocols/shared/execution-request.js +24 -0
  149. package/dist/core/protocols/shared/flow-mode-mapping.js +23 -0
  150. package/dist/core/public-capabilities/flow-mode-metadata.js +39 -0
  151. package/dist/core/public-capabilities/projections.js +29 -0
  152. package/dist/core/public-capabilities/registry.js +26 -0
  153. package/dist/core/public-capabilities/types.js +2 -0
  154. package/dist/core/runtime/agent-server-runtime.js +47 -43
  155. package/dist/core/runtime/execution-profile.js +67 -0
  156. package/dist/core/session/artifact-state.js +160 -0
  157. package/dist/core/session/compaction/index.js +183 -0
  158. package/dist/core/session/compaction/microcompact.js +78 -0
  159. package/dist/core/session/compaction/tracking.js +48 -0
  160. package/dist/core/session/compaction/types.js +11 -0
  161. package/dist/core/session/compression.js +12 -4
  162. package/dist/core/session/manager.js +247 -10
  163. package/dist/core/session/pruning-strategy.js +55 -9
  164. package/dist/core/session/replacement-preview-provider.js +24 -0
  165. package/dist/core/session/replacement-state.js +131 -0
  166. package/dist/core/session/resume-repair/pipeline.js +79 -0
  167. package/dist/core/session/resume-repair/stages/load-raw-archive-state.js +40 -0
  168. package/dist/core/session/resume-repair/stages/reattach-runtime-state.js +8 -0
  169. package/dist/core/session/resume-repair/stages/recover-orphaned-branches.js +10 -0
  170. package/dist/core/session/resume-repair/stages/relink-boundary-and-tail.js +36 -0
  171. package/dist/core/session/resume-repair/stages/replay-startup-hooks.js +23 -0
  172. package/dist/core/session/resume-repair/stages/rescue-stale-metadata.js +17 -0
  173. package/dist/core/session/resume-repair/types.js +2 -0
  174. package/dist/core/session/summary-sync.js +164 -13
  175. package/dist/core/session/token-tracker.js +6 -0
  176. package/dist/core/skills/audit.js +34 -0
  177. package/dist/core/skills/bridge.js +84 -7
  178. package/dist/core/skills/discovery.js +94 -0
  179. package/dist/core/skills/feature-flags.js +52 -0
  180. package/dist/core/skills/index.js +1 -1
  181. package/dist/core/skills/loader.js +195 -20
  182. package/dist/core/skills/parser.js +296 -24
  183. package/dist/core/skills/permissions.js +117 -0
  184. package/dist/core/skills/runtime/MicroTaskRunner.js +10 -4
  185. package/dist/core/skills/runtime/SkillRunner.js +240 -61
  186. package/dist/core/strata/layers/shadow-driver/shadow-driver.js +37 -7
  187. package/dist/core/strata/layers/worktree.js +70 -13
  188. package/dist/core/strata/runtime/synchronizer.js +29 -2
  189. package/dist/core/streaming/stream-assembler.js +75 -31
  190. package/dist/core/sub-agent/context-snapshot.js +156 -0
  191. package/dist/core/sub-agent/core/loop.js +1 -1
  192. package/dist/core/sub-agent/core/manager.js +119 -20
  193. package/dist/core/sub-agent/dispatch-policy.js +29 -0
  194. package/dist/core/sub-agent/prefix-consistency.js +48 -0
  195. package/dist/core/sub-agent/registry-defaults.js +4 -0
  196. package/dist/core/sub-agent/tools/task-spawn.js +79 -2
  197. package/dist/core/sub-agent/types.js +134 -5
  198. package/dist/core/tools/audit.js +13 -4
  199. package/dist/core/tools/builtin/ast-grep.js +1 -1
  200. package/dist/core/tools/builtin/ast.js +1 -1
  201. package/dist/core/tools/builtin/benchmark.js +360 -0
  202. package/dist/core/tools/builtin/code-search/backends/rg.js +2 -1
  203. package/dist/core/tools/builtin/code-search/executor.js +6 -1
  204. package/dist/core/tools/builtin/code-search/spec.js +26 -2
  205. package/dist/core/tools/builtin/fs.js +256 -23
  206. package/dist/core/tools/builtin/git.js +2 -2
  207. package/dist/core/tools/builtin/index.js +51 -2
  208. package/dist/core/tools/builtin/interaction.js +8 -1
  209. package/dist/core/tools/builtin/plan.js +37 -15
  210. package/dist/core/tools/builtin/shell.js +1 -1
  211. package/dist/core/tools/loader.js +39 -16
  212. package/dist/core/tools/mapper.js +17 -3
  213. package/dist/core/tools/parallel/scheduler.js +35 -4
  214. package/dist/core/tools/permissions/permission-rules.js +5 -10
  215. package/dist/core/tools/policy.js +6 -1
  216. package/dist/core/tools/recoverable-tool-errors.js +10 -0
  217. package/dist/core/tools/router.js +24 -6
  218. package/dist/core/tools/session.js +458 -48
  219. package/dist/core/tools/tool-visibility.js +62 -0
  220. package/dist/core/tools/types.js +9 -1
  221. package/dist/core/types/execution.js +4 -0
  222. package/dist/core/types/flow-mode.js +8 -0
  223. package/dist/core/utils/path.js +52 -0
  224. package/dist/core/verification/runner.js +4 -1
  225. package/dist/interfaces/cli/task-runner.js +4 -3
  226. package/dist/languages/typescript/index.js +4 -1
  227. package/dist/locales/en.js +87 -2
  228. package/dist/utils/eol.js +1 -1
  229. package/package.json +15 -8
  230. package/scripts/fix-es-abstract-compat.js +77 -0
  231. package/dist/core/runtime/fastify-server-bundle.js +0 -26
  232. package/dist/core/runtime/sidecar-fastify-plugin.js +0 -35
  233. package/dist/core/runtime/sidecar-paths.js +0 -47
  234. package/dist/core/runtime/sidecar-route-catalog.js +0 -103
@@ -1,5 +1,32 @@
1
1
  import { createPhaseRoutingLlm, createRuntimeLlm, EXECUTION_PHASES, getLogger, Phase, } from '../../../core/facades/cli-run-runtime-llm.js';
2
2
  import { text } from '../../locales/index.js';
3
+ function runtimeWarningMessage(code, params) {
4
+ if (code === 'API_KEY_MISSING')
5
+ return text.cli.apiKeyMissing;
6
+ if (code === 'PROVIDER_NOT_SUPPORTED') {
7
+ return text.cli.providerNotSupported(String(params.llmType));
8
+ }
9
+ if (code === 'CLIENT_PACKAGE_NOT_SUPPORTED') {
10
+ return text.cli.clientPackageNotSupported(String(params.clientPackage || ''));
11
+ }
12
+ return code;
13
+ }
14
+ function toHeadlessWarning(code, params) {
15
+ if (code === 'API_KEY_MISSING') {
16
+ return {
17
+ code: 'LLM_CREDENTIAL_MISSING',
18
+ message: 'LLM credential not configured; using StubLLM. Configure provider credentials to use a real LLM.',
19
+ source: 'llm.runtime',
20
+ severity: 'warning',
21
+ };
22
+ }
23
+ return {
24
+ code,
25
+ message: runtimeWarningMessage(code, params).replace(/^\[WARN\]\s*/, ''),
26
+ source: 'llm.runtime',
27
+ severity: 'warning',
28
+ };
29
+ }
3
30
  export function createRuntimeLlmAndWarn(params) {
4
31
  const runtimeLlm = createRuntimeLlm(params.llmConfig, {
5
32
  langfuseEnabled: params.langfuseEnabled,
@@ -8,7 +35,7 @@ export function createRuntimeLlmAndWarn(params) {
8
35
  const phaseToProviderModel = params.llmConfig?.routing?.phaseToProviderModel;
9
36
  const phaseLlms = {};
10
37
  if (phaseToProviderModel && typeof phaseToProviderModel === 'object') {
11
- const validPhases = new Set([...EXECUTION_PHASES, Phase.SLASH]);
38
+ const validPhases = new Set([...EXECUTION_PHASES, Phase.SLASH, Phase.AUTOPILOT]);
12
39
  for (const [phase, target] of Object.entries(phaseToProviderModel)) {
13
40
  if (!validPhases.has(phase))
14
41
  continue;
@@ -29,6 +56,7 @@ export function createRuntimeLlmAndWarn(params) {
29
56
  selectedModelId: target.model?.id,
30
57
  selectedModelSlot: target.model?.slot || 'default',
31
58
  },
59
+ capabilities: target.capabilities,
32
60
  };
33
61
  const created = createRuntimeLlm(perPhaseConfig, { langfuseEnabled: params.langfuseEnabled });
34
62
  warnings.push(...created.warnings);
@@ -40,17 +68,16 @@ export function createRuntimeLlmAndWarn(params) {
40
68
  : runtimeLlm.llm;
41
69
  const llmType = params.llmConfig?.type;
42
70
  const clientPackage = params.llmConfig?.clientPackage;
43
- for (const w of Array.from(new Set(warnings))) {
44
- if (w === 'API_KEY_MISSING') {
45
- getLogger().warn(text.cli.apiKeyMissing);
46
- }
47
- else if (w === 'PROVIDER_NOT_SUPPORTED') {
48
- getLogger().warn(text.cli.providerNotSupported(String(llmType)));
49
- }
50
- else if (w === 'CLIENT_PACKAGE_NOT_SUPPORTED') {
51
- getLogger().warn(text.cli.clientPackageNotSupported(String(clientPackage || '')));
71
+ const uniqueWarnings = Array.from(new Set(warnings));
72
+ for (const w of uniqueWarnings) {
73
+ if (!params.headlessOutput) {
74
+ getLogger().warn(runtimeWarningMessage(w, { llmType, clientPackage }));
52
75
  }
53
76
  }
54
- return { llm, warnings: Array.from(new Set(warnings)) };
77
+ return {
78
+ llm,
79
+ warnings: uniqueWarnings,
80
+ headlessWarnings: uniqueWarnings.map((w) => toHeadlessWarning(w, { llmType, clientPackage })),
81
+ };
55
82
  }
56
83
  //# sourceMappingURL=runtime-llm.js.map
@@ -23,8 +23,8 @@ export async function resolveRunRuntimeOptions(params) {
23
23
  if (wantPartialMessages && !llmOutput.kinds.includes('plan')) {
24
24
  llmOutput.kinds.push('plan');
25
25
  }
26
- const effectiveVerify = await resolveVerifyOption(params.repoPath, params.cliOptions.verify, params.resolvedConfig.verify.command);
27
- const effectiveWorktreePrepare = await resolveWorktreePrepareOption(params.repoPath, params.cliOptions.checkpointStrategy, params.cliOptions.worktreePrepare);
26
+ const effectiveVerify = await resolveVerifyOption(params.repoPath, params.cliOptions.verify, params.resolvedConfig.verify.command, { quiet: params.headlessOutput });
27
+ const effectiveWorktreePrepare = await resolveWorktreePrepareOption(params.repoPath, params.cliOptions.checkpointStrategy, params.cliOptions.worktreePrepare, { quiet: params.headlessOutput });
28
28
  return { ok: true, llmOutput, effectiveVerify, effectiveWorktreePrepare };
29
29
  }
30
30
  //# sourceMappingURL=runtime-options.js.map
@@ -1,8 +1,3 @@
1
- export function resolveOutputFormat(raw) {
2
- if (raw === 'text' || raw === 'stream-json' || raw === 'json')
3
- return raw;
4
- return undefined;
5
- }
6
1
  export function validateRunCommandOptions(params) {
7
2
  const { parsed } = params;
8
3
  if (parsed.explicitInstruction && parsed.printInstruction) {
@@ -1,12 +1,7 @@
1
1
  import { getLogger } from '../../../core/facades/cli-observability.js';
2
2
  import { text } from '../../locales/index.js';
3
- export function resolveVerboseLevel(raw) {
4
- if (raw === true)
5
- return 'basic';
6
- if (typeof raw === 'string')
7
- return raw;
8
- return undefined;
9
- }
3
+ import { resolveVerboseLevel } from '../../utils/verbose-level.js';
4
+ export { resolveVerboseLevel };
10
5
  export function logRunVerboseSummary(params) {
11
6
  if (!params.verboseLevel)
12
7
  return;
@@ -1,8 +1,12 @@
1
- import { buildA2AAgentCard, buildSidecarRouteDescriptors, createAcpFormalAgent, createAgentServerRuntime, createInteractionFacade, createSalmonTaskExecutor, createTaskEventBus, createPluginRegistry, createPromptRegistry, defaultSidecarRouteCatalog, defaultPathAdapter, getSidecarListenOptions, getUserAcpSessionStorePath, GitSnapshotCheckpointService, getLogger, mkdir, PlainReporter, PluginLoader, resolveConfig, resolveExtensions, runSalmonLoop, setPluginRegistry, setPromptRegistry, startAcpStdioServer, StderrReporter, } from '../../core/facades/cli-serve.js';
1
+ import { normalizePermissionMode } from '../../core/config/index.js';
2
+ import { buildA2AAgentCard, createAcpFormalAgent, createAgentServerRuntime, createInteractionFacade, createSalmonTaskExecutor, createTaskEventBus, createPluginRegistry, createPromptRegistry, getUserAcpSessionStorePath, GitSnapshotCheckpointService, getLogger, PlainReporter, PluginLoader, resolveExtensions, resolveExecutionProfile, runSalmonLoop, setPluginRegistry, setPromptRegistry, startAcpStdioServer, StderrReporter, } from '../../core/facades/cli-serve.js';
3
+ import { selectPublicCapabilitiesForSurface, toA2APublicSkills, } from '../../core/public-capabilities/projections.js';
4
+ import { buildPublicCapabilityRegistry } from '../../core/public-capabilities/registry.js';
2
5
  import { createTerminalAuthorizationProvider } from '../authorization/provider.js';
3
6
  import { text } from '../locales/index.js';
4
- import { resolveAuditScope } from '../utils/audit-scope.js';
7
+ import { getOptionValueSourceWithGlobalFallback } from '../utils/command-option-source.js';
5
8
  import { createOutcomeReporter } from '../utils/outcome-reporter.js';
9
+ import { resolveCliConfig } from '../utils/resolve-cli-config.js';
6
10
  import { createRuntimeLlmAndWarn } from './run/runtime-llm.js';
7
11
  function parsePort(value, fallback) {
8
12
  if (value === undefined || value === null || value === '')
@@ -10,21 +14,44 @@ function parsePort(value, fallback) {
10
14
  const parsed = Number(value);
11
15
  return Number.isFinite(parsed) ? parsed : Number.NaN;
12
16
  }
13
- function buildSidecarHandlers(deps) {
17
+ function resolveDefaultAcpPermissionPolicy(permissionMode) {
18
+ return permissionMode === 'yolo' ? 'allow_all' : 'ask';
19
+ }
20
+ function resolveServePermissionMode({ command, allOptions, rawConfiguredPermissionMode, flowMode, }) {
21
+ const permissionModeOptionSource = getOptionValueSourceWithGlobalFallback(command, 'mode');
22
+ const configuredPermissionMode = normalizePermissionMode(rawConfiguredPermissionMode);
23
+ const rawPermissionMode = (permissionModeOptionSource === 'cli' ? allOptions.mode : undefined) ??
24
+ configuredPermissionMode ??
25
+ resolveExecutionProfile(flowMode).defaultPermissionMode ??
26
+ 'interactive';
27
+ const permissionMode = normalizePermissionMode(rawPermissionMode);
28
+ if (!permissionMode) {
29
+ getLogger().error(`Invalid --mode "${String(rawPermissionMode)}". Expected "interactive" or "yolo".`, true);
30
+ process.exit(1);
31
+ }
32
+ return permissionMode;
33
+ }
34
+ function buildServeLoopExecutionDefaults(mode) {
35
+ const profile = resolveExecutionProfile(mode);
36
+ const strategy = profile.defaultCheckpointStrategy ?? 'worktree';
14
37
  return {
15
- health: async () => Response.json({ status: 'ok' }),
16
- status: async () => Response.json({ state: 'idle' }),
17
- info: async () => Response.json({
18
- name: deps.name,
19
- version: deps.version,
20
- capabilities: deps.capabilities,
21
- }),
22
- abort: async () => new Response('Abort not implemented for this runtime', { status: 501 }),
23
- workspace_files: async () => new Response('Workspace file access not enabled', { status: 501 }),
24
- logs_stream: async () => new Response('Log streaming not enabled', { status: 501 }),
25
- config_patch: async () => new Response('Config patch not enabled', { status: 501 }),
38
+ strategy,
39
+ applyBackOnDirty: strategy === 'worktree' ? '3way' : undefined,
40
+ environmentMode: 'strict',
26
41
  };
27
42
  }
43
+ function registerServeShutdown({ message, closeRuntime, closeAcpStdio, }) {
44
+ process.once('SIGINT', async () => {
45
+ getLogger().info(message);
46
+ try {
47
+ await closeRuntime?.();
48
+ }
49
+ finally {
50
+ closeAcpStdio?.();
51
+ process.exit(0);
52
+ }
53
+ });
54
+ }
28
55
  export function registerServeCommands(program) {
29
56
  const serve = program
30
57
  .command('serve')
@@ -33,8 +60,6 @@ export function registerServeCommands(program) {
33
60
  .option('--a2a-port <port>', text.cli.a2aPortOption)
34
61
  .option('--a2a-token <token>', text.cli.a2aTokenOption, (value, previous) => previous.concat([value]), [])
35
62
  .option('--no-acp-stdio', text.cli.acpStdioDisableOption)
36
- .option('--sidecar-socket <path>', text.cli.sidecarSocketOption)
37
- .option('--sidecar-allow-conditional', text.cli.sidecarAllowConditionalOption)
38
63
  .option('--no-color', text.cli.noColorOption)
39
64
  .action(handleServeCommand);
40
65
  serve
@@ -45,18 +70,27 @@ export function registerServeCommands(program) {
45
70
  }
46
71
  export async function handleServeCommand(_options, command) {
47
72
  const allOptions = command.optsWithGlobals();
48
- const defaultRepoPath = defaultPathAdapter.resolve(allOptions.repo || process.cwd());
49
- const resolvedConfig = await resolveConfig({ repoRoot: defaultRepoPath });
50
- const serverConfig = resolvedConfig.server;
51
- const auditScopeResolution = resolveAuditScope({
52
- cliValue: allOptions.auditScope,
53
- configValue: resolvedConfig.observability.audit.scope,
73
+ const configResult = await resolveCliConfig({
74
+ repo: allOptions.repo,
75
+ cwd: process.cwd(),
76
+ configPath: allOptions.config,
77
+ enableConfigFile: allOptions.configFile !== false,
78
+ auditScope: allOptions.auditScope,
79
+ logMode: allOptions.logMode,
54
80
  });
55
- if (!auditScopeResolution.ok) {
56
- getLogger().error(text.cli.invalidAuditScope(auditScopeResolution.invalid), true);
81
+ if (!configResult.ok) {
82
+ getLogger().error(configResult.message, true);
57
83
  process.exit(1);
58
84
  }
59
- const auditScope = auditScopeResolution.value;
85
+ const { resolvedConfig, auditScope, repoPath: defaultRepoPath } = configResult;
86
+ const defaultFlowMode = 'autopilot';
87
+ const defaultPermissionMode = resolveServePermissionMode({
88
+ command,
89
+ allOptions,
90
+ rawConfiguredPermissionMode: resolvedConfig.raw?.mode,
91
+ flowMode: defaultFlowMode,
92
+ });
93
+ const serverConfig = resolvedConfig.server;
60
94
  const rawA2aHost = allOptions.a2aHost ?? serverConfig?.a2a?.host;
61
95
  const a2aHost = String(rawA2aHost ?? '127.0.0.1');
62
96
  const rawA2aPort = allOptions.a2aPort ?? serverConfig?.a2a?.port;
@@ -69,13 +103,6 @@ export async function handleServeCommand(_options, command) {
69
103
  if (acpStdioEnabled) {
70
104
  getLogger().setReporter(allOptions.color === false ? new StderrReporter() : new PlainReporter());
71
105
  }
72
- const sidecarListen = typeof allOptions.sidecarSocket === 'string' && allOptions.sidecarSocket.length > 0
73
- ? { type: 'pipe', path: allOptions.sidecarSocket }
74
- : getSidecarListenOptions();
75
- const allowConditional = allOptions.sidecarAllowConditional ?? serverConfig?.sidecar?.allowConditional ?? false;
76
- if (sidecarListen.type === 'pipe' && !sidecarListen.path.startsWith('\\\\.\\pipe\\')) {
77
- await mkdir(defaultPathAdapter.dirname(sidecarListen.path), { recursive: true });
78
- }
79
106
  const languagePlugins = createPluginRegistry();
80
107
  setPluginRegistry(languagePlugins);
81
108
  setPromptRegistry(createPromptRegistry());
@@ -99,20 +126,27 @@ export async function handleServeCommand(_options, command) {
99
126
  const executor = createSalmonTaskExecutor({
100
127
  runLoop: async ({ instruction, mode, repoPath, onEvent, signal, authorizationProvider, authorizationMode, fileSystemOverride, }) => {
101
128
  const effectiveRepoPath = repoPath ?? defaultRepoPath;
129
+ const flowMode = mode;
130
+ const executionDefaults = buildServeLoopExecutionDefaults(flowMode);
131
+ const permissionMode = resolveServePermissionMode({
132
+ command,
133
+ allOptions,
134
+ rawConfiguredPermissionMode: resolvedConfig.raw?.mode,
135
+ flowMode,
136
+ });
102
137
  return await runSalmonLoop({
103
138
  instruction,
104
139
  repoPath: effectiveRepoPath,
105
140
  llm,
106
- mode: mode,
141
+ mode: flowMode,
107
142
  verify: resolvedConfig.verify.command,
108
- strategy: 'worktree',
109
- applyBackOnDirty: '3way',
110
- environmentMode: 'strict',
143
+ ...executionDefaults,
111
144
  llmOutput: resolvedConfig.llmOutput,
112
145
  outcomeReporter,
113
146
  langfuseSessionId: resolvedConfig.observability.langfuse.sessionId,
114
147
  langfuseUserId: resolvedConfig.observability.langfuse.userId,
115
148
  auditScope,
149
+ permissionMode,
116
150
  languagePlugins,
117
151
  fileSystemOverride,
118
152
  authorizationProvider: authorizationProvider ?? defaultAuthorizationProvider,
@@ -154,27 +188,20 @@ export async function handleServeCommand(_options, command) {
154
188
  next();
155
189
  }
156
190
  : undefined;
157
- const capabilities = [{ id: 'patch', title: 'Patch code' }];
191
+ const a2aPublicCapabilities = selectPublicCapabilitiesForSurface('a2a', buildPublicCapabilityRegistry());
192
+ const a2aSkills = toA2APublicSkills(a2aPublicCapabilities);
158
193
  const agentCard = buildA2AAgentCard({
159
194
  name: 'salmon-loop',
160
195
  url: `http://${a2aHost}:${a2aPort}`,
161
- capabilities,
196
+ capabilities: a2aSkills,
162
197
  security: authTokens.length > 0 ? [{ type: 'http', scheme: 'bearer' }] : [],
163
198
  });
164
- const sidecarRoutes = buildSidecarRouteDescriptors({
165
- strict: true,
166
- catalog: defaultSidecarRouteCatalog,
167
- handlers: buildSidecarHandlers({
168
- name: 'salmon-loop',
169
- version: '0.2.0',
170
- capabilities,
171
- }),
172
- });
173
199
  if (acpStdioEnabled) {
174
200
  startAcpStdioServer((conn) => createAcpFormalAgent({
175
201
  conn,
176
202
  agentInfo: { name: 'salmon-loop', version: '0.2.0' },
177
- defaultModeId: resolvedConfig.permissionMode,
203
+ defaultModeId: 'autopilot',
204
+ defaultPermissionPolicy: resolveDefaultAcpPermissionPolicy(defaultPermissionMode),
178
205
  checkpointReader: {
179
206
  listBySession: async ({ repoPath, sessionId, limit }) => await checkpointService.list({ repoPath, sessionId, limit }),
180
207
  getById: async ({ repoPath, checkpointId }) => (await checkpointService.loadWithStatus({ repoPath, checkpointId })).handle,
@@ -189,47 +216,48 @@ export async function handleServeCommand(_options, command) {
189
216
  eventBus: sharedEventBus,
190
217
  }));
191
218
  getLogger().info(text.cli.acpStdioStarted('n/a (stdio)'));
192
- // Handle SIGINT for graceful shutdown
193
- process.on('SIGINT', () => {
194
- getLogger().info('Received SIGINT, shutting down ACP server...');
195
- process.stdin.destroy();
196
- process.exit(0);
197
- });
198
219
  }
199
- const fastify = (await import('fastify')).default;
200
220
  const runtime = createAgentServerRuntime({
201
- createFastify: () => fastify(),
202
221
  a2a: {
203
222
  buildAgentCard: () => agentCard,
204
223
  executeTask: executor.execute,
205
224
  eventBus: sharedEventBus,
206
225
  authMiddleware,
207
226
  },
208
- sidecar: {
209
- routes: sidecarRoutes,
210
- allowConditional,
211
- },
212
227
  listen: {
213
228
  a2a: { host: a2aHost, port: a2aPort },
214
- sidecar: sidecarListen,
215
229
  },
216
- a2aBaseUrl: `http://${a2aHost}:${a2aPort}`,
217
230
  });
218
- // Handle SIGINT for graceful shutdown
219
- process.on('SIGINT', () => {
220
- getLogger().info('Received SIGINT, shutting down server...');
221
- runtime.close().then(() => process.exit(0));
231
+ registerServeShutdown({
232
+ message: 'Received SIGINT, shutting down server...',
233
+ closeRuntime: () => runtime.close(),
234
+ closeAcpStdio: acpStdioEnabled ? () => void process.stdin.destroy() : undefined,
222
235
  });
223
236
  await runtime.start();
224
- const sidecarAddress = sidecarListen.type === 'tcp'
225
- ? `http://${sidecarListen.host}:${sidecarListen.port}`
226
- : sidecarListen.path;
227
- getLogger().success(text.cli.serveStarted(a2aHost, a2aPort, sidecarAddress));
237
+ getLogger().success(text.cli.serveStarted(a2aHost, a2aPort));
228
238
  }
229
239
  export async function handleServeAcpCommand(_options, command) {
230
240
  const allOptions = command.optsWithGlobals();
231
- const defaultRepoPath = defaultPathAdapter.resolve(allOptions.repo || process.cwd());
232
- const resolvedConfig = await resolveConfig({ repoRoot: defaultRepoPath });
241
+ const configResult = await resolveCliConfig({
242
+ repo: allOptions.repo,
243
+ cwd: process.cwd(),
244
+ configPath: allOptions.config,
245
+ enableConfigFile: allOptions.configFile !== false,
246
+ auditScope: allOptions.auditScope,
247
+ logMode: allOptions.logMode,
248
+ });
249
+ if (!configResult.ok) {
250
+ getLogger().error(configResult.message, true);
251
+ process.exit(1);
252
+ }
253
+ const { resolvedConfig, auditScope, repoPath: defaultRepoPath } = configResult;
254
+ const defaultFlowMode = 'autopilot';
255
+ const defaultPermissionMode = resolveServePermissionMode({
256
+ command,
257
+ allOptions,
258
+ rawConfiguredPermissionMode: resolvedConfig.raw?.mode,
259
+ flowMode: defaultFlowMode,
260
+ });
233
261
  getLogger().setReporter(allOptions.color === false ? new StderrReporter() : new PlainReporter());
234
262
  const languagePlugins = createPluginRegistry();
235
263
  setPluginRegistry(languagePlugins);
@@ -240,14 +268,6 @@ export async function handleServeAcpCommand(_options, command) {
240
268
  llmConfig: resolvedConfig.llm,
241
269
  langfuseEnabled: resolvedConfig.observability.langfuse.enabled,
242
270
  });
243
- const auditScopeResolution = resolveAuditScope({
244
- cliValue: allOptions.auditScope,
245
- configValue: resolvedConfig.observability.audit.scope,
246
- });
247
- if (!auditScopeResolution.ok) {
248
- getLogger().error(text.cli.invalidAuditScope(auditScopeResolution.invalid), true);
249
- process.exit(1);
250
- }
251
271
  const outcomeReporter = createOutcomeReporter({
252
272
  enabled: resolvedConfig.observability.langfuse.outcome,
253
273
  endpoint: resolvedConfig.observability.langfuse.endpoint,
@@ -262,20 +282,27 @@ export async function handleServeAcpCommand(_options, command) {
262
282
  const executor = createSalmonTaskExecutor({
263
283
  runLoop: async ({ instruction, mode, repoPath, onEvent, signal, authorizationProvider, authorizationMode, }) => {
264
284
  const effectiveRepoPath = repoPath ?? defaultRepoPath;
285
+ const flowMode = mode;
286
+ const executionDefaults = buildServeLoopExecutionDefaults(flowMode);
287
+ const permissionMode = resolveServePermissionMode({
288
+ command,
289
+ allOptions,
290
+ rawConfiguredPermissionMode: resolvedConfig.raw?.mode,
291
+ flowMode,
292
+ });
265
293
  return await runSalmonLoop({
266
294
  instruction,
267
295
  repoPath: effectiveRepoPath,
268
296
  llm,
269
- mode: mode,
297
+ mode: flowMode,
270
298
  verify: resolvedConfig.verify.command,
271
- strategy: 'worktree',
272
- applyBackOnDirty: '3way',
273
- environmentMode: 'strict',
299
+ ...executionDefaults,
274
300
  llmOutput: resolvedConfig.llmOutput,
275
301
  outcomeReporter,
276
302
  langfuseSessionId: resolvedConfig.observability.langfuse.sessionId,
277
303
  langfuseUserId: resolvedConfig.observability.langfuse.userId,
278
- auditScope: auditScopeResolution.value,
304
+ auditScope,
305
+ permissionMode,
279
306
  languagePlugins,
280
307
  authorizationProvider: authorizationProvider ?? defaultAuthorizationProvider,
281
308
  authorizationMode,
@@ -299,7 +326,8 @@ export async function handleServeAcpCommand(_options, command) {
299
326
  startAcpStdioServer((conn) => createAcpFormalAgent({
300
327
  conn,
301
328
  agentInfo: { name: 'salmon-loop', version: '0.2.0' },
302
- defaultModeId: resolvedConfig.permissionMode,
329
+ defaultModeId: 'autopilot',
330
+ defaultPermissionPolicy: resolveDefaultAcpPermissionPolicy(defaultPermissionMode),
303
331
  checkpointReader: {
304
332
  listBySession: async ({ repoPath, sessionId, limit }) => await checkpointService.list({ repoPath, sessionId, limit }),
305
333
  getById: async ({ repoPath, checkpointId }) => (await checkpointService.loadWithStatus({ repoPath, checkpointId })).handle,
@@ -314,10 +342,9 @@ export async function handleServeAcpCommand(_options, command) {
314
342
  eventBus: sharedEventBus,
315
343
  }));
316
344
  getLogger().info(text.cli.acpStdioStarted('n/a (stdio)'));
317
- process.on('SIGINT', () => {
318
- getLogger().info('Received SIGINT, shutting down ACP server...');
319
- process.stdin.destroy();
320
- process.exit(0);
345
+ registerServeShutdown({
346
+ message: 'Received SIGINT, shutting down ACP server...',
347
+ closeAcpStdio: () => void process.stdin.destroy(),
321
348
  });
322
349
  }
323
350
  //# sourceMappingURL=serve.js.map
@@ -1,7 +1,7 @@
1
1
  import * as os from 'os';
2
2
  import * as path from 'path';
3
- import { getLogger, registerAllBuiltins, skillToToolSpec, SkillParser, ToolRegistry, } from '../../core/facades/cli-command-tool-names.js';
4
- import { existsSync, readFileUtf8, readFileUtf8Sync, readdirDirents, readdirDirentsSync, safePathJoin, stat, statSync, } from '../utils/safe-fs.js';
3
+ import { getLogger, registerAllBuiltins, resolveExtensions, skillToToolSpec, SkillLoader, ToolRegistry, } from '../../core/facades/cli-command-tool-names.js';
4
+ import { stat, statSync } from '../utils/safe-fs.js';
5
5
  const VALID_SIDE_EFFECTS = new Set([
6
6
  'none',
7
7
  'fs_read',
@@ -11,40 +11,23 @@ const VALID_SIDE_EFFECTS = new Set([
11
11
  'git_read',
12
12
  'git_write',
13
13
  ]);
14
+ // Cache key includes repoRoot. Extensions config is not part of the key because
15
+ // tool-names is used for tab completion where extensions may not be available.
14
16
  const toolNameCache = new Map();
15
- async function loadSkillsFromPath(root) {
16
- if (!existsSync(root))
17
- return [];
18
- const entries = await readdirDirents(root, root);
19
- const skills = [];
20
- for (const entry of entries) {
21
- const skillFile = entry.isDirectory()
22
- ? safePathJoin(root, entry.name, 'SKILL.md')
23
- : entry.name.endsWith('.md')
24
- ? safePathJoin(root, entry.name)
25
- : null;
26
- if (!skillFile || !existsSync(skillFile, root))
27
- continue;
28
- try {
29
- const content = await readFileUtf8(skillFile, root);
30
- skills.push(SkillParser.parse(content, skillFile));
31
- }
32
- catch (error) {
33
- getLogger().warn(`Failed to load skill at ${skillFile}: ${error instanceof Error ? error.message : String(error)}`);
34
- }
35
- }
36
- return skills;
37
- }
38
- function getSkillSearchPaths(repoRoot) {
39
- return [
40
- path.join(os.homedir(), '.claude/skills'),
41
- path.join(repoRoot, '.salmonloop/skills'),
42
- path.join(repoRoot, '.claude/skills'),
17
+ /**
18
+ * Compute a cache-invalidation signature based on mtime of all skill search
19
+ * paths that SkillLoader would scan. Mirrors the strict search order.
20
+ */
21
+ async function computeSkillSignature(repoRoot, extraPaths = []) {
22
+ const searchPaths = [
23
+ ...extraPaths,
24
+ path.join(repoRoot, '.salmonloop', 'skills'),
25
+ path.join(repoRoot, '.agents', 'skills'),
26
+ path.join(os.homedir(), '.salmonloop', 'skills'),
27
+ path.join(os.homedir(), '.agents', 'skills'),
43
28
  ];
44
- }
45
- async function computeSkillSignature(repoRoot) {
46
29
  const parts = [];
47
- for (const searchPath of getSkillSearchPaths(repoRoot)) {
30
+ for (const searchPath of searchPaths) {
48
31
  try {
49
32
  const stats = await stat(searchPath);
50
33
  parts.push(`${searchPath}:${stats.mtimeMs}`);
@@ -56,8 +39,14 @@ async function computeSkillSignature(repoRoot) {
56
39
  return parts.join('|');
57
40
  }
58
41
  function computeSkillSignatureSync(repoRoot) {
42
+ const searchPaths = [
43
+ path.join(repoRoot, '.salmonloop', 'skills'),
44
+ path.join(repoRoot, '.agents', 'skills'),
45
+ path.join(os.homedir(), '.salmonloop', 'skills'),
46
+ path.join(os.homedir(), '.agents', 'skills'),
47
+ ];
59
48
  const parts = [];
60
- for (const searchPath of getSkillSearchPaths(repoRoot)) {
49
+ for (const searchPath of searchPaths) {
61
50
  try {
62
51
  const stats = statSync(searchPath);
63
52
  parts.push(`${searchPath}:${stats.mtimeMs}`);
@@ -68,52 +57,63 @@ function computeSkillSignatureSync(repoRoot) {
68
57
  }
69
58
  return parts.join('|');
70
59
  }
71
- function loadSkillsFromPathSync(root) {
72
- if (!existsSync(root))
73
- return [];
74
- const entries = readdirDirentsSync(root, root);
75
- const skills = [];
76
- for (const entry of entries) {
77
- const skillFile = entry.isDirectory()
78
- ? safePathJoin(root, entry.name, 'SKILL.md')
79
- : entry.name.endsWith('.md')
80
- ? safePathJoin(root, entry.name)
81
- : null;
82
- if (!skillFile || !existsSync(skillFile, root))
83
- continue;
84
- try {
85
- const content = readFileUtf8Sync(skillFile, root);
86
- skills.push(SkillParser.parse(content, skillFile));
87
- }
88
- catch (error) {
89
- getLogger().warn(`Failed to load skill at ${skillFile}: ${error instanceof Error ? error.message : String(error)}`);
90
- }
91
- }
92
- return skills;
93
- }
60
+ /**
61
+ * Get all known tool names including skills.
62
+ *
63
+ * Uses `resolveExtensions` to obtain the same SkillLoader configuration
64
+ * (extraPaths) as the main runtime, so that
65
+ * tool-name discovery and actual runtime skill availability stay consistent.
66
+ */
94
67
  export async function getKnownToolNames(repoRoot) {
95
- const signature = await computeSkillSignature(repoRoot);
68
+ // Resolve extensions to get the same loader config as the runtime
69
+ let skillDiscovery = {};
70
+ try {
71
+ const { resolved } = await resolveExtensions({ repoRoot });
72
+ skillDiscovery = resolved.skillDiscovery;
73
+ }
74
+ catch {
75
+ // Extensions config unavailable — fall back to loader defaults
76
+ }
77
+ const extraPaths = skillDiscovery.paths ?? [];
78
+ const signature = await computeSkillSignature(repoRoot, extraPaths);
96
79
  const cached = toolNameCache.get(repoRoot);
97
80
  if (cached && cached.signature === signature)
98
81
  return cached.names;
99
82
  const registry = new ToolRegistry();
100
83
  registerAllBuiltins(registry);
101
- for (const searchPath of getSkillSearchPaths(repoRoot)) {
102
- const skills = await loadSkillsFromPath(searchPath);
103
- for (const skill of skills) {
104
- try {
105
- registry.register(skillToToolSpec(skill));
106
- }
107
- catch (error) {
108
- const label = skill.metadata?.name || skill.id;
109
- getLogger().warn(`Failed to register skill ${label}: ${error instanceof Error ? error.message : String(error)}`);
110
- }
84
+ const routerBox = { router: null };
85
+ const skillLoader = new SkillLoader({
86
+ repoRoot,
87
+ extraPaths,
88
+ });
89
+ const catalog = await skillLoader.loadCatalog();
90
+ for (const entry of catalog) {
91
+ try {
92
+ // Name-only registration: executor is never called, so null router is safe.
93
+ // Use catalog entry for lightweight registration (progressive disclosure).
94
+ registry.register(skillToToolSpec({ entry, loader: skillLoader }, routerBox));
95
+ }
96
+ catch (error) {
97
+ const label = entry.name || entry.id;
98
+ getLogger().warn(`Failed to register skill ${label}: ${error instanceof Error ? error.message : String(error)}`);
111
99
  }
112
100
  }
113
101
  const names = new Set(registry.listAll().map((spec) => spec.name));
114
102
  toolNameCache.set(repoRoot, { names, signature });
115
103
  return names;
116
104
  }
105
+ /**
106
+ * Synchronous variant for tab completion only.
107
+ *
108
+ * @nonAuthoritative This returns a best-effort approximation of known tool
109
+ * names using loader defaults (no extensions resolution, since
110
+ * resolveExtensions is async). The result may differ from the actual runtime
111
+ * tool set when custom `skills.json` discovery paths are configured.
112
+ *
113
+ * DO NOT use this for security decisions (allowlist enforcement, permission
114
+ * checks). Use the async {@link getKnownToolNames} instead, which resolves
115
+ * extensions for full consistency with the runtime.
116
+ */
117
117
  export function getKnownToolNamesSync(repoRoot) {
118
118
  const signature = computeSkillSignatureSync(repoRoot);
119
119
  const cached = toolNameCache.get(repoRoot);
@@ -121,16 +121,16 @@ export function getKnownToolNamesSync(repoRoot) {
121
121
  return cached.names;
122
122
  const registry = new ToolRegistry();
123
123
  registerAllBuiltins(registry);
124
- for (const searchPath of getSkillSearchPaths(repoRoot)) {
125
- const skills = loadSkillsFromPathSync(searchPath);
126
- for (const skill of skills) {
127
- try {
128
- registry.register(skillToToolSpec(skill));
129
- }
130
- catch (error) {
131
- const label = skill.metadata?.name || skill.id;
132
- getLogger().warn(`Failed to register skill ${label}: ${error instanceof Error ? error.message : String(error)}`);
133
- }
124
+ const routerBox = { router: null };
125
+ const skillLoader = new SkillLoader({ repoRoot });
126
+ const skills = skillLoader.initializeSync();
127
+ for (const skill of skills) {
128
+ try {
129
+ registry.register(skillToToolSpec(skill, routerBox));
130
+ }
131
+ catch (error) {
132
+ const label = skill.metadata?.name || skill.id;
133
+ getLogger().warn(`Failed to register skill ${label}: ${error instanceof Error ? error.message : String(error)}`);
134
134
  }
135
135
  }
136
136
  const names = new Set(registry.listAll().map((spec) => spec.name));