@agimon-ai/doompi-team 0.0.1-alpha.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/LICENSE +25 -0
- package/README.md +46 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.cjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.mjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.cjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.mjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateParam.cjs +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateParam.mjs +1 -0
- package/dist/_virtual/_rolldown/runtime.cjs +1 -0
- package/dist/_virtual/_rolldown/runtime.mjs +1 -0
- package/dist/agents/discovery.cjs +2 -0
- package/dist/agents/discovery.cjs.map +1 -0
- package/dist/agents/discovery.mjs +2 -0
- package/dist/agents/discovery.mjs.map +1 -0
- package/dist/agents/frontmatter.cjs +10 -0
- package/dist/agents/frontmatter.cjs.map +1 -0
- package/dist/agents/frontmatter.mjs +10 -0
- package/dist/agents/frontmatter.mjs.map +1 -0
- package/dist/agents/loader.cjs +2 -0
- package/dist/agents/loader.cjs.map +1 -0
- package/dist/agents/loader.mjs +2 -0
- package/dist/agents/loader.mjs.map +1 -0
- package/dist/agents/memory.cjs +3 -0
- package/dist/agents/memory.cjs.map +1 -0
- package/dist/agents/memory.mjs +3 -0
- package/dist/agents/memory.mjs.map +1 -0
- package/dist/agents/projectRoot.cjs +2 -0
- package/dist/agents/projectRoot.cjs.map +1 -0
- package/dist/agents/projectRoot.mjs +2 -0
- package/dist/agents/projectRoot.mjs.map +1 -0
- package/dist/agents/settings.cjs +2 -0
- package/dist/agents/settings.cjs.map +1 -0
- package/dist/agents/settings.mjs +2 -0
- package/dist/agents/settings.mjs.map +1 -0
- package/dist/agents/skills.cjs +0 -0
- package/dist/agents/skills.cjs.map +1 -0
- package/dist/agents/skills.mjs +5 -0
- package/dist/agents/skills.mjs.map +1 -0
- package/dist/agents/types.d.cts +121 -0
- package/dist/agents/types.d.cts.map +1 -0
- package/dist/agents/types.d.mts +121 -0
- package/dist/agents/types.d.mts.map +1 -0
- package/dist/api/backgroundWork.cjs +1 -0
- package/dist/api/backgroundWork.d.cts +2 -0
- package/dist/api/backgroundWork.d.mts +2 -0
- package/dist/api/backgroundWork.mjs +1 -0
- package/dist/api/capabilityCeiling.cjs +1 -0
- package/dist/api/capabilityCeiling.d.cts +3 -0
- package/dist/api/capabilityCeiling.d.mts +3 -0
- package/dist/api/capabilityCeiling.mjs +2 -0
- package/dist/api/capabilityCeiling.mjs.map +1 -0
- package/dist/api/delegation.cjs +1 -0
- package/dist/api/delegation.d.cts +2 -0
- package/dist/api/delegation.d.mts +2 -0
- package/dist/api/delegation.mjs +1 -0
- package/dist/api/teamSnapshot.cjs +1 -0
- package/dist/api/teamSnapshot.d.cts +2 -0
- package/dist/api/teamSnapshot.d.mts +2 -0
- package/dist/api/teamSnapshot.mjs +2 -0
- package/dist/api/teamSnapshot.mjs.map +1 -0
- package/dist/container/bindOrRebind.cjs +2 -0
- package/dist/container/bindOrRebind.cjs.map +1 -0
- package/dist/container/bindOrRebind.mjs +2 -0
- package/dist/container/bindOrRebind.mjs.map +1 -0
- package/dist/container/globalBridge.cjs +2 -0
- package/dist/container/globalBridge.cjs.map +1 -0
- package/dist/container/globalBridge.d.cts +19 -0
- package/dist/container/globalBridge.d.cts.map +1 -0
- package/dist/container/globalBridge.d.mts +19 -0
- package/dist/container/globalBridge.d.mts.map +1 -0
- package/dist/container/globalBridge.mjs +2 -0
- package/dist/container/globalBridge.mjs.map +1 -0
- package/dist/container/index.cjs +2 -0
- package/dist/container/index.cjs.map +1 -0
- package/dist/container/index.d.cts +38 -0
- package/dist/container/index.d.cts.map +1 -0
- package/dist/container/index.d.mts +38 -0
- package/dist/container/index.d.mts.map +1 -0
- package/dist/container/index.mjs +2 -0
- package/dist/container/index.mjs.map +1 -0
- package/dist/container/modules/agents.cjs +2 -0
- package/dist/container/modules/agents.cjs.map +1 -0
- package/dist/container/modules/agents.d.cts +1 -0
- package/dist/container/modules/agents.d.mts +1 -0
- package/dist/container/modules/agents.mjs +2 -0
- package/dist/container/modules/agents.mjs.map +1 -0
- package/dist/container/modules/core.cjs +2 -0
- package/dist/container/modules/core.cjs.map +1 -0
- package/dist/container/modules/core.d.cts +1 -0
- package/dist/container/modules/core.d.mts +1 -0
- package/dist/container/modules/core.mjs +2 -0
- package/dist/container/modules/core.mjs.map +1 -0
- package/dist/container/modules/extensions.cjs +2 -0
- package/dist/container/modules/extensions.cjs.map +1 -0
- package/dist/container/modules/extensions.mjs +2 -0
- package/dist/container/modules/extensions.mjs.map +1 -0
- package/dist/container/modules/intercom.cjs +2 -0
- package/dist/container/modules/intercom.cjs.map +1 -0
- package/dist/container/modules/intercom.mjs +2 -0
- package/dist/container/modules/intercom.mjs.map +1 -0
- package/dist/container/modules/runs.cjs +2 -0
- package/dist/container/modules/runs.cjs.map +1 -0
- package/dist/container/modules/runs.mjs +2 -0
- package/dist/container/modules/runs.mjs.map +1 -0
- package/dist/container/types.cjs +2 -0
- package/dist/container/types.cjs.map +1 -0
- package/dist/container/types.d.cts +45 -0
- package/dist/container/types.d.cts.map +1 -0
- package/dist/container/types.d.mts +45 -0
- package/dist/container/types.d.mts.map +1 -0
- package/dist/container/types.mjs +2 -0
- package/dist/container/types.mjs.map +1 -0
- package/dist/env.cjs +2 -0
- package/dist/env.cjs.map +1 -0
- package/dist/env.d.cts +65 -0
- package/dist/env.d.cts.map +1 -0
- package/dist/env.d.mts +65 -0
- package/dist/env.d.mts.map +1 -0
- package/dist/env.mjs +2 -0
- package/dist/env.mjs.map +1 -0
- package/dist/extension.cjs +2 -0
- package/dist/extension.cjs.map +1 -0
- package/dist/extension.mjs +2 -0
- package/dist/extension.mjs.map +1 -0
- package/dist/extensions/config.cjs +2 -0
- package/dist/extensions/config.cjs.map +1 -0
- package/dist/extensions/config.mjs +2 -0
- package/dist/extensions/config.mjs.map +1 -0
- package/dist/extensions/delegationBridge.cjs +2 -0
- package/dist/extensions/delegationBridge.cjs.map +1 -0
- package/dist/extensions/delegationBridge.mjs +2 -0
- package/dist/extensions/delegationBridge.mjs.map +1 -0
- package/dist/extensions/doom.cjs +2 -0
- package/dist/extensions/doom.cjs.map +1 -0
- package/dist/extensions/doom.d.cts +8 -0
- package/dist/extensions/doom.d.cts.map +1 -0
- package/dist/extensions/doom.d.mts +8 -0
- package/dist/extensions/doom.d.mts.map +1 -0
- package/dist/extensions/doom.mjs +2 -0
- package/dist/extensions/doom.mjs.map +1 -0
- package/dist/extensions/fablePlanBridge.cjs +2 -0
- package/dist/extensions/fablePlanBridge.cjs.map +1 -0
- package/dist/extensions/fablePlanBridge.mjs +2 -0
- package/dist/extensions/fablePlanBridge.mjs.map +1 -0
- package/dist/extensions/managementActions.cjs +2 -0
- package/dist/extensions/managementActions.cjs.map +1 -0
- package/dist/extensions/managementActions.mjs +2 -0
- package/dist/extensions/managementActions.mjs.map +1 -0
- package/dist/extensions/operationJournal.cjs +2 -0
- package/dist/extensions/operationJournal.cjs.map +1 -0
- package/dist/extensions/operationJournal.mjs +2 -0
- package/dist/extensions/operationJournal.mjs.map +1 -0
- package/dist/extensions/orchestratorPrompt.cjs +35 -0
- package/dist/extensions/orchestratorPrompt.cjs.map +1 -0
- package/dist/extensions/orchestratorPrompt.mjs +35 -0
- package/dist/extensions/orchestratorPrompt.mjs.map +1 -0
- package/dist/extensions/pi.cjs +2 -0
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.cts +2 -0
- package/dist/extensions/pi.d.mts +2 -0
- package/dist/extensions/pi.mjs +2 -0
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/extensions/schemas.cjs +1 -0
- package/dist/extensions/schemas.mjs +2 -0
- package/dist/extensions/schemas.mjs.map +1 -0
- package/dist/extensions/spawnPlan.cjs +3 -0
- package/dist/extensions/spawnPlan.cjs.map +1 -0
- package/dist/extensions/spawnPlan.mjs +3 -0
- package/dist/extensions/spawnPlan.mjs.map +1 -0
- package/dist/extensions/standard.cjs +2 -0
- package/dist/extensions/standard.cjs.map +1 -0
- package/dist/extensions/standard.d.cts +8 -0
- package/dist/extensions/standard.d.cts.map +1 -0
- package/dist/extensions/standard.d.mts +8 -0
- package/dist/extensions/standard.d.mts.map +1 -0
- package/dist/extensions/standard.mjs +2 -0
- package/dist/extensions/standard.mjs.map +1 -0
- package/dist/extensions/statusViews.cjs +7 -0
- package/dist/extensions/statusViews.cjs.map +1 -0
- package/dist/extensions/statusViews.mjs +7 -0
- package/dist/extensions/statusViews.mjs.map +1 -0
- package/dist/extensions/subagentPromptRuntime.cjs +18 -0
- package/dist/extensions/subagentPromptRuntime.cjs.map +1 -0
- package/dist/extensions/subagentPromptRuntime.d.cts +58 -0
- package/dist/extensions/subagentPromptRuntime.d.cts.map +1 -0
- package/dist/extensions/subagentPromptRuntime.d.mts +58 -0
- package/dist/extensions/subagentPromptRuntime.d.mts.map +1 -0
- package/dist/extensions/subagentPromptRuntime.mjs +18 -0
- package/dist/extensions/subagentPromptRuntime.mjs.map +1 -0
- package/dist/extensions/subagentPromptRuntimeEntry.cjs +2 -0
- package/dist/extensions/subagentPromptRuntimeEntry.cjs.map +1 -0
- package/dist/extensions/subagentPromptRuntimeEntry.d.cts +2 -0
- package/dist/extensions/subagentPromptRuntimeEntry.d.mts +2 -0
- package/dist/extensions/subagentPromptRuntimeEntry.mjs +2 -0
- package/dist/extensions/subagentPromptRuntimeEntry.mjs.map +1 -0
- package/dist/extensions/subagentTool.cjs +7 -0
- package/dist/extensions/subagentTool.cjs.map +1 -0
- package/dist/extensions/subagentTool.mjs +7 -0
- package/dist/extensions/subagentTool.mjs.map +1 -0
- package/dist/extensions/toolDescription.cjs +2 -0
- package/dist/extensions/toolDescription.cjs.map +1 -0
- package/dist/extensions/toolDescription.mjs +2 -0
- package/dist/extensions/toolDescription.mjs.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +34 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +34 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/intercom/intercomBridge.cjs +2 -0
- package/dist/intercom/intercomBridge.cjs.map +1 -0
- package/dist/intercom/intercomBridge.mjs +2 -0
- package/dist/intercom/intercomBridge.mjs.map +1 -0
- package/dist/intercom/nativeTeamChannel.cjs +5 -0
- package/dist/intercom/nativeTeamChannel.cjs.map +1 -0
- package/dist/intercom/nativeTeamChannel.d.cts +22 -0
- package/dist/intercom/nativeTeamChannel.d.cts.map +1 -0
- package/dist/intercom/nativeTeamChannel.d.mts +22 -0
- package/dist/intercom/nativeTeamChannel.d.mts.map +1 -0
- package/dist/intercom/nativeTeamChannel.mjs +5 -0
- package/dist/intercom/nativeTeamChannel.mjs.map +1 -0
- package/dist/intercom/supervisorControlChannel.cjs +2 -0
- package/dist/intercom/supervisorControlChannel.cjs.map +1 -0
- package/dist/intercom/supervisorControlChannel.mjs +2 -0
- package/dist/intercom/supervisorControlChannel.mjs.map +1 -0
- package/dist/protocol/backgroundWork.cjs +2 -0
- package/dist/protocol/backgroundWork.cjs.map +1 -0
- package/dist/protocol/backgroundWork.d.cts +52 -0
- package/dist/protocol/backgroundWork.d.cts.map +1 -0
- package/dist/protocol/backgroundWork.d.mts +52 -0
- package/dist/protocol/backgroundWork.d.mts.map +1 -0
- package/dist/protocol/backgroundWork.mjs +2 -0
- package/dist/protocol/backgroundWork.mjs.map +1 -0
- package/dist/protocol/capabilityCeiling.cjs +2 -0
- package/dist/protocol/capabilityCeiling.cjs.map +1 -0
- package/dist/protocol/capabilityCeiling.d.cts +51 -0
- package/dist/protocol/capabilityCeiling.d.cts.map +1 -0
- package/dist/protocol/capabilityCeiling.d.mts +51 -0
- package/dist/protocol/capabilityCeiling.d.mts.map +1 -0
- package/dist/protocol/capabilityCeiling.mjs +2 -0
- package/dist/protocol/capabilityCeiling.mjs.map +1 -0
- package/dist/protocol/delegation.cjs +2 -0
- package/dist/protocol/delegation.cjs.map +1 -0
- package/dist/protocol/delegation.d.cts +269 -0
- package/dist/protocol/delegation.d.cts.map +1 -0
- package/dist/protocol/delegation.d.mts +269 -0
- package/dist/protocol/delegation.d.mts.map +1 -0
- package/dist/protocol/delegation.mjs +2 -0
- package/dist/protocol/delegation.mjs.map +1 -0
- package/dist/runs/background/asyncExecution.cjs +2 -0
- package/dist/runs/background/asyncExecution.cjs.map +1 -0
- package/dist/runs/background/asyncExecution.mjs +2 -0
- package/dist/runs/background/asyncExecution.mjs.map +1 -0
- package/dist/runs/background/asyncJobTracker.cjs +2 -0
- package/dist/runs/background/asyncJobTracker.cjs.map +1 -0
- package/dist/runs/background/asyncJobTracker.mjs +2 -0
- package/dist/runs/background/asyncJobTracker.mjs.map +1 -0
- package/dist/runs/background/cliRunnerEntry.cjs +2 -0
- package/dist/runs/background/cliRunnerEntry.cjs.map +1 -0
- package/dist/runs/background/cliRunnerEntry.d.cts +1 -0
- package/dist/runs/background/cliRunnerEntry.d.mts +1 -0
- package/dist/runs/background/cliRunnerEntry.mjs +2 -0
- package/dist/runs/background/cliRunnerEntry.mjs.map +1 -0
- package/dist/runs/background/completionBatcher.cjs +2 -0
- package/dist/runs/background/completionBatcher.cjs.map +1 -0
- package/dist/runs/background/completionBatcher.mjs +2 -0
- package/dist/runs/background/completionBatcher.mjs.map +1 -0
- package/dist/runs/background/notify.cjs +4 -0
- package/dist/runs/background/notify.cjs.map +1 -0
- package/dist/runs/background/notify.mjs +4 -0
- package/dist/runs/background/notify.mjs.map +1 -0
- package/dist/runs/background/parentWatchdog.cjs +2 -0
- package/dist/runs/background/parentWatchdog.cjs.map +1 -0
- package/dist/runs/background/parentWatchdog.mjs +2 -0
- package/dist/runs/background/parentWatchdog.mjs.map +1 -0
- package/dist/runs/background/processTerminal.cjs +2 -0
- package/dist/runs/background/processTerminal.cjs.map +1 -0
- package/dist/runs/background/processTerminal.mjs +2 -0
- package/dist/runs/background/processTerminal.mjs.map +1 -0
- package/dist/runs/background/resultWatcher.cjs +2 -0
- package/dist/runs/background/resultWatcher.cjs.map +1 -0
- package/dist/runs/background/resultWatcher.mjs +2 -0
- package/dist/runs/background/resultWatcher.mjs.map +1 -0
- package/dist/runs/background/runIdResolver.cjs +2 -0
- package/dist/runs/background/runIdResolver.cjs.map +1 -0
- package/dist/runs/background/runIdResolver.mjs +2 -0
- package/dist/runs/background/runIdResolver.mjs.map +1 -0
- package/dist/runs/background/runnerBootstrap.cjs +2 -0
- package/dist/runs/background/runnerBootstrap.cjs.map +1 -0
- package/dist/runs/background/runnerBootstrap.mjs +2 -0
- package/dist/runs/background/runnerBootstrap.mjs.map +1 -0
- package/dist/runs/background/runnerExecution.cjs +2 -0
- package/dist/runs/background/runnerExecution.cjs.map +1 -0
- package/dist/runs/background/runnerExecution.mjs +2 -0
- package/dist/runs/background/runnerExecution.mjs.map +1 -0
- package/dist/runs/background/runnerReporting.cjs +3 -0
- package/dist/runs/background/runnerReporting.cjs.map +1 -0
- package/dist/runs/background/runnerReporting.mjs +3 -0
- package/dist/runs/background/runnerReporting.mjs.map +1 -0
- package/dist/runs/background/runnerTeamMembership.cjs +2 -0
- package/dist/runs/background/runnerTeamMembership.cjs.map +1 -0
- package/dist/runs/background/runnerTeamMembership.mjs +2 -0
- package/dist/runs/background/runnerTeamMembership.mjs.map +1 -0
- package/dist/runs/background/spawnHandshake.cjs +2 -0
- package/dist/runs/background/spawnHandshake.cjs.map +1 -0
- package/dist/runs/background/spawnHandshake.mjs +2 -0
- package/dist/runs/background/spawnHandshake.mjs.map +1 -0
- package/dist/runs/background/staleRunReconciler.cjs +2 -0
- package/dist/runs/background/staleRunReconciler.cjs.map +1 -0
- package/dist/runs/background/staleRunReconciler.mjs +2 -0
- package/dist/runs/background/staleRunReconciler.mjs.map +1 -0
- package/dist/runs/background/statusReader.cjs +2 -0
- package/dist/runs/background/statusReader.cjs.map +1 -0
- package/dist/runs/background/statusReader.mjs +2 -0
- package/dist/runs/background/statusReader.mjs.map +1 -0
- package/dist/runs/background/statusWriter.cjs +2 -0
- package/dist/runs/background/statusWriter.cjs.map +1 -0
- package/dist/runs/background/statusWriter.mjs +2 -0
- package/dist/runs/background/statusWriter.mjs.map +1 -0
- package/dist/runs/background/subagentWait.cjs +2 -0
- package/dist/runs/background/subagentWait.cjs.map +1 -0
- package/dist/runs/background/subagentWait.mjs +2 -0
- package/dist/runs/background/subagentWait.mjs.map +1 -0
- package/dist/runs/background/terminalPersistence.cjs +2 -0
- package/dist/runs/background/terminalPersistence.cjs.map +1 -0
- package/dist/runs/background/terminalPersistence.mjs +2 -0
- package/dist/runs/background/terminalPersistence.mjs.map +1 -0
- package/dist/runs/registry/processGroup.cjs +2 -0
- package/dist/runs/registry/processGroup.cjs.map +1 -0
- package/dist/runs/registry/processGroup.mjs +2 -0
- package/dist/runs/registry/processGroup.mjs.map +1 -0
- package/dist/runs/registry/runRegistry.cjs +2 -0
- package/dist/runs/registry/runRegistry.cjs.map +1 -0
- package/dist/runs/registry/runRegistry.mjs +2 -0
- package/dist/runs/registry/runRegistry.mjs.map +1 -0
- package/dist/runs/registry/sessionLifecycle.cjs +2 -0
- package/dist/runs/registry/sessionLifecycle.cjs.map +1 -0
- package/dist/runs/registry/sessionLifecycle.mjs +2 -0
- package/dist/runs/registry/sessionLifecycle.mjs.map +1 -0
- package/dist/runs/registry/suspendedRuns.cjs +4 -0
- package/dist/runs/registry/suspendedRuns.cjs.map +1 -0
- package/dist/runs/registry/suspendedRuns.mjs +4 -0
- package/dist/runs/registry/suspendedRuns.mjs.map +1 -0
- package/dist/runs/sdkRunnerEntry.cjs +2 -0
- package/dist/runs/sdkRunnerEntry.cjs.map +1 -0
- package/dist/runs/sdkRunnerEntry.d.cts +1 -0
- package/dist/runs/sdkRunnerEntry.d.mts +1 -0
- package/dist/runs/sdkRunnerEntry.mjs +2 -0
- package/dist/runs/sdkRunnerEntry.mjs.map +1 -0
- package/dist/runs/shared/claudeFableProfile.cjs +4 -0
- package/dist/runs/shared/claudeFableProfile.cjs.map +1 -0
- package/dist/runs/shared/claudeFableProfile.mjs +4 -0
- package/dist/runs/shared/claudeFableProfile.mjs.map +1 -0
- package/dist/runs/shared/depthGuard.cjs +2 -0
- package/dist/runs/shared/depthGuard.cjs.map +1 -0
- package/dist/runs/shared/depthGuard.mjs +2 -0
- package/dist/runs/shared/depthGuard.mjs.map +1 -0
- package/dist/runs/shared/mcpDirectToolAllowlist.cjs +2 -0
- package/dist/runs/shared/mcpDirectToolAllowlist.cjs.map +1 -0
- package/dist/runs/shared/mcpDirectToolAllowlist.mjs +2 -0
- package/dist/runs/shared/mcpDirectToolAllowlist.mjs.map +1 -0
- package/dist/runs/shared/modelFallback.cjs +2 -0
- package/dist/runs/shared/modelFallback.cjs.map +1 -0
- package/dist/runs/shared/modelFallback.mjs +2 -0
- package/dist/runs/shared/modelFallback.mjs.map +1 -0
- package/dist/runs/shared/modelScope.cjs +2 -0
- package/dist/runs/shared/modelScope.cjs.map +1 -0
- package/dist/runs/shared/modelScope.mjs +2 -0
- package/dist/runs/shared/modelScope.mjs.map +1 -0
- package/dist/runs/shared/nestedPath.cjs +2 -0
- package/dist/runs/shared/nestedPath.cjs.map +1 -0
- package/dist/runs/shared/nestedPath.mjs +2 -0
- package/dist/runs/shared/nestedPath.mjs.map +1 -0
- package/dist/runs/shared/piArgs.cjs +2 -0
- package/dist/runs/shared/piArgs.cjs.map +1 -0
- package/dist/runs/shared/piArgs.mjs +2 -0
- package/dist/runs/shared/piArgs.mjs.map +1 -0
- package/dist/runs/shared/piSpawn.cjs +2 -0
- package/dist/runs/shared/piSpawn.cjs.map +1 -0
- package/dist/runs/shared/piSpawn.mjs +2 -0
- package/dist/runs/shared/piSpawn.mjs.map +1 -0
- package/dist/runs/shared/runWithConcurrency.cjs +2 -0
- package/dist/runs/shared/runWithConcurrency.cjs.map +1 -0
- package/dist/runs/shared/runWithConcurrency.mjs +2 -0
- package/dist/runs/shared/runWithConcurrency.mjs.map +1 -0
- package/dist/runs/shared/runtimeRegistry.cjs +2 -0
- package/dist/runs/shared/runtimeRegistry.cjs.map +1 -0
- package/dist/runs/shared/runtimeRegistry.mjs +2 -0
- package/dist/runs/shared/runtimeRegistry.mjs.map +1 -0
- package/dist/runs/shared/structuredOutput.cjs +2 -0
- package/dist/runs/shared/structuredOutput.cjs.map +1 -0
- package/dist/runs/shared/structuredOutput.mjs +2 -0
- package/dist/runs/shared/structuredOutput.mjs.map +1 -0
- package/dist/runs/shared/toolAvailability.cjs +2 -0
- package/dist/runs/shared/toolAvailability.cjs.map +1 -0
- package/dist/runs/shared/toolAvailability.mjs +2 -0
- package/dist/runs/shared/toolAvailability.mjs.map +1 -0
- package/dist/runs/shared/toolBudget.cjs +2 -0
- package/dist/runs/shared/toolBudget.cjs.map +1 -0
- package/dist/runs/shared/toolBudget.mjs +2 -0
- package/dist/runs/shared/toolBudget.mjs.map +1 -0
- package/dist/shared/artifacts.cjs +2 -0
- package/dist/shared/artifacts.cjs.map +1 -0
- package/dist/shared/artifacts.mjs +2 -0
- package/dist/shared/artifacts.mjs.map +1 -0
- package/dist/shared/atomicJson.cjs +2 -0
- package/dist/shared/atomicJson.cjs.map +1 -0
- package/dist/shared/atomicJson.mjs +2 -0
- package/dist/shared/atomicJson.mjs.map +1 -0
- package/dist/shared/boundedKeySet.cjs +2 -0
- package/dist/shared/boundedKeySet.cjs.map +1 -0
- package/dist/shared/boundedKeySet.mjs +2 -0
- package/dist/shared/boundedKeySet.mjs.map +1 -0
- package/dist/shared/childTranscript.cjs +5 -0
- package/dist/shared/childTranscript.cjs.map +1 -0
- package/dist/shared/childTranscript.mjs +5 -0
- package/dist/shared/childTranscript.mjs.map +1 -0
- package/dist/shared/configDir.cjs +2 -0
- package/dist/shared/configDir.cjs.map +1 -0
- package/dist/shared/configDir.mjs +2 -0
- package/dist/shared/configDir.mjs.map +1 -0
- package/dist/shared/errors.cjs +2 -0
- package/dist/shared/errors.cjs.map +1 -0
- package/dist/shared/errors.mjs +2 -0
- package/dist/shared/errors.mjs.map +1 -0
- package/dist/shared/fileSystemRetry.cjs +2 -0
- package/dist/shared/fileSystemRetry.cjs.map +1 -0
- package/dist/shared/fileSystemRetry.mjs +2 -0
- package/dist/shared/fileSystemRetry.mjs.map +1 -0
- package/dist/shared/formatters.cjs +2 -0
- package/dist/shared/formatters.cjs.map +1 -0
- package/dist/shared/formatters.mjs +2 -0
- package/dist/shared/formatters.mjs.map +1 -0
- package/dist/shared/lruCache.cjs +2 -0
- package/dist/shared/lruCache.cjs.map +1 -0
- package/dist/shared/lruCache.mjs +2 -0
- package/dist/shared/lruCache.mjs.map +1 -0
- package/dist/shared/modelInfo.cjs +2 -0
- package/dist/shared/modelInfo.cjs.map +1 -0
- package/dist/shared/modelInfo.mjs +2 -0
- package/dist/shared/modelInfo.mjs.map +1 -0
- package/dist/shared/modelResolution.cjs +2 -0
- package/dist/shared/modelResolution.cjs.map +1 -0
- package/dist/shared/modelResolution.mjs +2 -0
- package/dist/shared/modelResolution.mjs.map +1 -0
- package/dist/shared/paths.cjs +2 -0
- package/dist/shared/paths.cjs.map +1 -0
- package/dist/shared/paths.mjs +2 -0
- package/dist/shared/paths.mjs.map +1 -0
- package/dist/shared/pollScheduler.cjs +2 -0
- package/dist/shared/pollScheduler.cjs.map +1 -0
- package/dist/shared/pollScheduler.mjs +2 -0
- package/dist/shared/pollScheduler.mjs.map +1 -0
- package/dist/shared/scopeOwner.cjs +2 -0
- package/dist/shared/scopeOwner.cjs.map +1 -0
- package/dist/shared/scopeOwner.mjs +2 -0
- package/dist/shared/scopeOwner.mjs.map +1 -0
- package/dist/shared/versioned.cjs +2 -0
- package/dist/shared/versioned.cjs.map +1 -0
- package/dist/shared/versioned.mjs +2 -0
- package/dist/shared/versioned.mjs.map +1 -0
- package/dist/slash/chainExpression.cjs +2 -0
- package/dist/slash/chainExpression.cjs.map +1 -0
- package/dist/slash/chainExpression.mjs +2 -0
- package/dist/slash/chainExpression.mjs.map +1 -0
- package/dist/slash/doctor.cjs +5 -0
- package/dist/slash/doctor.cjs.map +1 -0
- package/dist/slash/doctor.mjs +5 -0
- package/dist/slash/doctor.mjs.map +1 -0
- package/dist/slash/slashCommands.cjs +4 -0
- package/dist/slash/slashCommands.cjs.map +1 -0
- package/dist/slash/slashCommands.mjs +4 -0
- package/dist/slash/slashCommands.mjs.map +1 -0
- package/dist/slash/spawnRequestMapping.cjs +2 -0
- package/dist/slash/spawnRequestMapping.cjs.map +1 -0
- package/dist/slash/spawnRequestMapping.mjs +2 -0
- package/dist/slash/spawnRequestMapping.mjs.map +1 -0
- package/dist/slash/subagentLaunch.cjs +2 -0
- package/dist/slash/subagentLaunch.cjs.map +1 -0
- package/dist/slash/subagentLaunch.mjs +2 -0
- package/dist/slash/subagentLaunch.mjs.map +1 -0
- package/dist/tui/agentCatalog.cjs +2 -0
- package/dist/tui/agentCatalog.cjs.map +1 -0
- package/dist/tui/agentCatalog.mjs +2 -0
- package/dist/tui/agentCatalog.mjs.map +1 -0
- package/dist/tui/agentResourceProjection.cjs +2 -0
- package/dist/tui/agentResourceProjection.cjs.map +1 -0
- package/dist/tui/agentResourceProjection.mjs +2 -0
- package/dist/tui/agentResourceProjection.mjs.map +1 -0
- package/dist/tui/completionNotice.cjs +5 -0
- package/dist/tui/completionNotice.cjs.map +1 -0
- package/dist/tui/completionNotice.mjs +5 -0
- package/dist/tui/completionNotice.mjs.map +1 -0
- package/dist/tui/fleet.cjs +3 -0
- package/dist/tui/fleet.cjs.map +1 -0
- package/dist/tui/fleet.mjs +3 -0
- package/dist/tui/fleet.mjs.map +1 -0
- package/dist/tui/fleetAgentView.cjs +2 -0
- package/dist/tui/fleetAgentView.cjs.map +1 -0
- package/dist/tui/fleetAgentView.mjs +2 -0
- package/dist/tui/fleetAgentView.mjs.map +1 -0
- package/dist/tui/fleetStatus.cjs +2 -0
- package/dist/tui/fleetStatus.cjs.map +1 -0
- package/dist/tui/fleetStatus.mjs +2 -0
- package/dist/tui/fleetStatus.mjs.map +1 -0
- package/dist/tui/fleetTranscript.cjs +5 -0
- package/dist/tui/fleetTranscript.cjs.map +1 -0
- package/dist/tui/fleetTranscript.mjs +5 -0
- package/dist/tui/fleetTranscript.mjs.map +1 -0
- package/dist/tui/register.cjs +2 -0
- package/dist/tui/register.cjs.map +1 -0
- package/dist/tui/register.mjs +2 -0
- package/dist/tui/register.mjs.map +1 -0
- package/dist/types.d.cts +59 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +59 -0
- package/dist/types.d.mts.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectRoot.mjs","names":[],"sources":["../../src/agents/projectRoot.ts"],"sourcesContent":["/**\n * Locating the project root that agent configuration hangs off.\n *\n * A project root is any ancestor of the working directory that carries a config\n * directory or a legacy `.agents` directory. There can be several: a package\n * inside a monorepo and the monorepo itself both qualify. Which one wins is a\n * setting, because both answers are legitimate. Teams that keep one shared set\n * of agents at the repository root want `git-root`, teams that give each package\n * its own want `nearest`, and neither can be inferred from the tree alone.\n *\n * DESIGN PATTERNS:\n * - Candidates are collected once, nearest first, then a policy picks from them.\n * Separating collection from policy is what lets `git-root` be answered\n * without a second walk\n * - Path derivation goes through `shared/configDir.ts`, so a rebranded build\n * that renames its config directory needs no change here\n *\n * AVOID:\n * - Hardcoding the config directory name; it is discovered, not assumed\n * - Treating the nearest candidate as the project root without reading the\n * setting first\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { getAgentDir, getProjectConfigDir, readSettingsFileStrict } from '../shared/configDir.ts';\n\nconst SETTINGS_FILE_NAME = 'settings.json';\nconst GIT_DIR_NAME = '.git';\n/**\n * Bare `.agents`. Two roles, one name: under a project root it is the legacy\n * location that predates the config directory, under the home directory it is\n * the current one. Both are read.\n */\nconst DOT_AGENTS_DIR_NAME = '.agents';\n/** Agents inside a config directory, project-level or user-level. */\nconst AGENTS_DIR_NAME = 'agents';\nconst SUBAGENTS_SETTINGS_KEY = 'subagents';\nconst PROJECT_ROOT_RESOLUTION_KEY = 'projectRootResolution';\n\n/**\n * Which candidate root wins when several ancestors qualify.\n *\n * `nearest` stops at the first one found walking up; `git-root` prefers the\n * candidate that is also the enclosing git repository root.\n */\nexport type ProjectRootResolution = 'nearest' | 'git-root';\n\n/** Whether a path exists and is a directory. Any failure reads as \"no\". */\nexport function isDirectory(dirPath: string): boolean {\n try {\n return fs.statSync(dirPath).isDirectory();\n } catch {\n // A missing path, a broken symlink, or a permission error all mean \"not usable\".\n return false;\n }\n}\n\n/** True when `dir` carries either marker that makes it a project root. */\nexport function isProjectRootCandidate(dir: string): boolean {\n return isDirectory(getProjectConfigDir(dir)) || isDirectory(path.join(dir, DOT_AGENTS_DIR_NAME));\n}\n\n/** Every project root above `cwd`, nearest first. */\nexport function findProjectRootCandidates(cwd: string): string[] {\n const roots: string[] = [];\n let currentDir = cwd;\n while (true) {\n if (isProjectRootCandidate(currentDir)) roots.push(currentDir);\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) return roots;\n currentDir = parentDir;\n }\n}\n\n/**\n * Nearest ancestor containing `.git`.\n *\n * Existence is enough: a worktree or submodule has a `.git` file rather than a\n * directory, and both are real repository roots.\n */\nexport function findNearestGitRoot(cwd: string): string | null {\n let currentDir = cwd;\n while (true) {\n if (fs.existsSync(path.join(currentDir, GIT_DIR_NAME))) return currentDir;\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) return null;\n currentDir = parentDir;\n }\n}\n\n/**\n * Read a candidate's declared resolution policy.\n *\n * Deliberately throws on an unrecognised value rather than falling back: a typo\n * here silently changes which agents the whole session sees.\n */\nexport function readProjectRootResolution(projectRoot: string): ProjectRootResolution | undefined {\n const settingsPath = path.join(getProjectConfigDir(projectRoot), SETTINGS_FILE_NAME);\n if (!fs.existsSync(settingsPath)) return undefined;\n\n const settings = readSettingsFileStrict(settingsPath);\n const subagents = settings[SUBAGENTS_SETTINGS_KEY];\n if (!subagents || typeof subagents !== 'object' || Array.isArray(subagents)) return undefined;\n\n const value = (subagents as Record<string, unknown>)[PROJECT_ROOT_RESOLUTION_KEY];\n if (value === undefined) return undefined;\n if (value === 'nearest' || value === 'git-root') return value;\n throw new Error(\n `Subagent settings in '${settingsPath}' have invalid '${PROJECT_ROOT_RESOLUTION_KEY}'; expected 'nearest' or 'git-root'.`,\n );\n}\n\n/** The closest project root above `cwd`, ignoring any resolution policy. */\nexport function findNearestProjectRoot(cwd: string): string | null {\n return findProjectRootCandidates(cwd)[0] ?? null;\n}\n\n/**\n * The project root to use, honouring the resolution policy.\n *\n * The policy is read from the nearest candidate first so a package can opt out\n * of its monorepo's shared agents, and from the git root second so a repository\n * can claim its packages without editing each one.\n */\nexport function findConfiguredProjectRoot(cwd: string): string | null {\n const candidates = findProjectRootCandidates(cwd);\n const nearestRoot = candidates[0];\n if (!nearestRoot) return null;\n\n const nearestMode = readProjectRootResolution(nearestRoot);\n if (nearestMode === 'nearest') return nearestRoot;\n\n const gitRoot = findNearestGitRoot(cwd);\n const gitProjectRoot = gitRoot\n ? candidates.find((candidate) => path.resolve(candidate) === path.resolve(gitRoot))\n : undefined;\n if (gitProjectRoot && (nearestMode === 'git-root' || readProjectRootResolution(gitProjectRoot) === 'git-root')) {\n return gitProjectRoot;\n }\n\n return nearestRoot;\n}\n\n/**\n * User-level agent directories, in load order.\n *\n * Two locations, not one: agents used to live in the config directory and now\n * also live in `~/.agents`, and both are still read so an existing setup keeps\n * working after the move. Returned unfiltered because the caller reads each\n * directory anyway and a missing one costs nothing.\n */\nexport function userAgentDirs(): string[] {\n return [path.join(getAgentDir(), AGENTS_DIR_NAME), path.join(os.homedir(), DOT_AGENTS_DIR_NAME)];\n}\n\n/** The user-level settings file. Always resolvable, unlike the project one. */\nexport function getUserAgentSettingsPath(): string {\n return path.join(getAgentDir(), SETTINGS_FILE_NAME);\n}\n\n/** The project settings file, or null when `cwd` sits under no project root. */\nexport function getProjectAgentSettingsPath(cwd: string): string | null {\n const projectRoot = findConfiguredProjectRoot(cwd);\n return projectRoot ? path.join(getProjectConfigDir(projectRoot), SETTINGS_FILE_NAME) : null;\n}\n\n/**\n * Project agent directories for `cwd`.\n *\n * `readDirs` holds only directories that exist, so discovery never stats twice,\n * and lists the legacy location first so a project mid-migration still resolves.\n * `preferredDir` is where a newly created agent should be written, and is\n * returned whether or not it exists yet.\n */\nexport function resolveNearestProjectAgentDirs(cwd: string): { readDirs: string[]; preferredDir: string | null } {\n const projectRoot = findConfiguredProjectRoot(cwd);\n if (!projectRoot) return { readDirs: [], preferredDir: null };\n\n const legacyDir = path.join(projectRoot, DOT_AGENTS_DIR_NAME);\n const preferredDir = path.join(getProjectConfigDir(projectRoot), AGENTS_DIR_NAME);\n const readDirs: string[] = [];\n if (isDirectory(legacyDir)) readDirs.push(legacyDir);\n if (isDirectory(preferredDir)) readDirs.push(preferredDir);\n\n return { readDirs, preferredDir };\n}\n"],"mappings":"4QAkDA,SAAgB,EAAY,EAA0B,CACpD,GAAI,CACF,OAAO,EAAG,SAAS,EAAQ,CAAC,aAAa,MACnC,CAEN,MAAO,IAKX,SAAgB,EAAuB,EAAsB,CAC3D,OAAO,EAAY,EAAoB,EAAI,CAAC,EAAI,EAAY,EAAK,KAAK,EAAK,EAAoB,CAAC,CAIlG,SAAgB,EAA0B,EAAuB,CAC/D,IAAM,EAAkB,EAAE,CACtB,EAAa,EACjB,OAAa,CACP,EAAuB,EAAW,EAAE,EAAM,KAAK,EAAW,CAE9D,IAAM,EAAY,EAAK,QAAQ,EAAW,CAC1C,GAAI,IAAc,EAAY,OAAO,EACrC,EAAa,GAUjB,SAAgB,EAAmB,EAA4B,CAC7D,IAAI,EAAa,EACjB,OAAa,CACX,GAAI,EAAG,WAAW,EAAK,KAAK,EAAY,EAAa,CAAC,CAAE,OAAO,EAE/D,IAAM,EAAY,EAAK,QAAQ,EAAW,CAC1C,GAAI,IAAc,EAAY,OAAO,KACrC,EAAa,GAUjB,SAAgB,EAA0B,EAAwD,CAChG,IAAM,EAAe,EAAK,KAAK,EAAoB,EAAY,CAAE,EAAmB,CACpF,GAAI,CAAC,EAAG,WAAW,EAAa,CAAE,OAGlC,IAAM,EADW,EAAuB,EACd,CAAC,GAC3B,GAAI,CAAC,GAAa,OAAO,GAAc,UAAY,MAAM,QAAQ,EAAU,CAAE,OAE7E,IAAM,EAAS,EAAsC,GACjD,OAAU,IAAA,GACd,IAAI,IAAU,WAAa,IAAU,WAAY,OAAO,EACxD,MAAU,MACR,yBAAyB,EAAa,kBAAkB,EAA4B,sCACrF,EAeH,SAAgB,EAA0B,EAA4B,CACpE,IAAM,EAAa,EAA0B,EAAI,CAC3C,EAAc,EAAW,GAC/B,GAAI,CAAC,EAAa,OAAO,KAEzB,IAAM,EAAc,EAA0B,EAAY,CAC1D,GAAI,IAAgB,UAAW,OAAO,EAEtC,IAAM,EAAU,EAAmB,EAAI,CACjC,EAAiB,EACnB,EAAW,KAAM,GAAc,EAAK,QAAQ,EAAU,GAAK,EAAK,QAAQ,EAAQ,CAAC,CACjF,IAAA,GAKJ,OAJI,IAAmB,IAAgB,YAAc,EAA0B,EAAe,GAAK,YAC1F,EAGF,EAWT,SAAgB,GAA0B,CACxC,MAAO,CAAC,EAAK,KAAK,GAAa,CAAE,EAAgB,CAAE,EAAK,KAAK,EAAG,SAAS,CAAE,EAAoB,CAAC,CAIlG,SAAgB,GAAmC,CACjD,OAAO,EAAK,KAAK,GAAa,CAAE,EAAmB,CAIrD,SAAgB,EAA4B,EAA4B,CACtE,IAAM,EAAc,EAA0B,EAAI,CAClD,OAAO,EAAc,EAAK,KAAK,EAAoB,EAAY,CAAE,EAAmB,CAAG,KAWzF,SAAgB,EAA+B,EAAkE,CAC/G,IAAM,EAAc,EAA0B,EAAI,CAClD,GAAI,CAAC,EAAa,MAAO,CAAE,SAAU,EAAE,CAAE,aAAc,KAAM,CAE7D,IAAM,EAAY,EAAK,KAAK,EAAa,EAAoB,CACvD,EAAe,EAAK,KAAK,EAAoB,EAAY,CAAE,EAAgB,CAC3E,EAAqB,EAAE,CAI7B,OAHI,EAAY,EAAU,EAAE,EAAS,KAAK,EAAU,CAChD,EAAY,EAAa,EAAE,EAAS,KAAK,EAAa,CAEnD,CAAE,WAAU,eAAc,8BAlK+D,CAE5F,EAAqB,gBACrB,EAAe,OAMf,EAAsB,UAEtB,EAAkB,SAClB,EAAyB,YACzB,EAA8B"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../shared/configDir.cjs`);require(`../shared/atomicJson.cjs`);let n=require(`node:fs`);n=e.__toESM(n,1);const r={overrides:{}},i=new WeakMap;function a(e,t){i.set(e,t)}function o(e,...t){let n=i.get(e);return n?t.some(e=>n.has(e)):!1}function s(e,t){let n=i.get(e);return n&&i.set(t,n),t}function c(e){let t=[],n=[];for(let r of e??[])r.startsWith(`mcp:`)?t.push(r.slice(4)):n.push(r);return{...e===void 0?{}:{tools:n},...t.length>0?{mcpDirectTools:t}:{}}}function l(e,t){let n=e[t];if(!(!n||typeof n!=`object`||Array.isArray(n)))return n}function u(e,t){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Subagent settings in '${t}' have invalid 'modelScope'; expected an object.`);let n=e,r={};if(`enforce`in n){if(typeof n.enforce!=`boolean`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.enforce'; expected a boolean.`);r.enforce=n.enforce}if(`allow`in n){if(!Array.isArray(n.allow))throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let e=[];for(let r of n.allow){if(typeof r!=`string`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let n=r.trim();n&&e.push(n)}if(e.length===0)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`);r.allow=e}if(r.enforce===!0&&(!r.allow||r.allow.length===0))throw Error(`Subagent settings in '${t}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`);return Object.keys(r).length>0?r:void 0}function d(e,t){if(e===void 0)return;if(e===!1)return!1;if(!Array.isArray(e))throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let n=[];for(let r of e){if(typeof r!=`string`)throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let e=r.trim();e&&n.push(e)}return n}function f(e,t,n){if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Builtin override '${e}' in '${n}' must be an object.`);let r=t,i={};if(`model`in r)if(typeof r.model==`string`||r.model===!1)i.model=r.model;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'model'; expected a string or false.`);if(`thinking`in r)if(typeof r.thinking==`string`||r.thinking===!1)i.thinking=r.thinking;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'thinking'; expected a string or false.`);if(`systemPromptMode`in r)if(r.systemPromptMode===`append`||r.systemPromptMode===`replace`)i.systemPromptMode=r.systemPromptMode;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`);if(`inheritProjectContext`in r)if(typeof r.inheritProjectContext==`boolean`)i.inheritProjectContext=r.inheritProjectContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritProjectContext'; expected a boolean.`);if(`inheritSkills`in r)if(typeof r.inheritSkills==`boolean`)i.inheritSkills=r.inheritSkills;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritSkills'; expected a boolean.`);if(`defaultContext`in r)if(r.defaultContext===`fresh`||r.defaultContext===`fork`||r.defaultContext===!1)i.defaultContext=r.defaultContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`);if(`disabled`in r)if(typeof r.disabled==`boolean`)i.disabled=r.disabled;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'disabled'; expected a boolean.`);if(`completionGuard`in r)if(typeof r.completionGuard==`boolean`)i.completionGuard=r.completionGuard;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'completionGuard'; expected a boolean.`);if(`toolBudget`in r){let t=p(r.toolBudget,e,n);t!==void 0&&(i.toolBudget=t)}if(`systemPrompt`in r)if(typeof r.systemPrompt==`string`)i.systemPrompt=r.systemPrompt;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPrompt'; expected a string.`);let a=t=>d(r[t],{filePath:n,name:e,field:t}),o=a(`fallbackModels`);o!==void 0&&(i.fallbackModels=o);let s=a(`skills`);s!==void 0&&(i.skills=s);let c=a(`tools`);c!==void 0&&(i.tools=c);let l=a(`extensions`);l!==void 0&&(i.extensions=l);let u=a(`subagentOnlyExtensions`);return u!==void 0&&(i.subagentOnlyExtensions=u),Object.keys(i).length>0?i:void 0}function p(e,t,n){if(e===!1)return!1;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget'; expected an object or false.`);let r=e;if(typeof r.hard!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.hard'; expected a number.`);let i={hard:r.hard};if(`soft`in r){if(typeof r.soft!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.soft'; expected a number.`);i.soft=r.soft}if(`block`in r)if(r.block===`*`)i.block=`*`;else if(Array.isArray(r.block)&&r.block.every(e=>typeof e==`string`))i.block=[...r.block];else throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`);return i}function m(e){if(!e)return r;let n=l(t.readSettingsFileStrict(e),`subagents`);if(!n)return r;let i;if(`disableBuiltins`in n)if(typeof n.disableBuiltins==`boolean`)i=n.disableBuiltins;else throw Error(`Subagent settings in '${e}' have invalid 'disableBuiltins'; expected a boolean.`);let a;if(`disableThinking`in n)if(typeof n.disableThinking==`boolean`)a=n.disableThinking;else throw Error(`Subagent settings in '${e}' have invalid 'disableThinking'; expected a boolean.`);let o;if(`defaultModel`in n)if(typeof n.defaultModel==`string`&&n.defaultModel.trim())o=n.defaultModel.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultModel'; expected a non-empty string.`);let s;if(`defaultThinking`in n)if(typeof n.defaultThinking==`string`&&n.defaultThinking.trim())s=n.defaultThinking.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultThinking'; expected a non-empty string.`);let c;if(`defaultExtensions`in n){let t=n.defaultExtensions;if(!Array.isArray(t)||t.some(e=>typeof e!=`string`||!e.trim()))throw Error(`Subagent settings in '${e}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`);c=t.map(e=>String(e).trim())}let d=u(n.modelScope,e),p={},m=l(n,`agentOverrides`);if(m)for(let[t,n]of Object.entries(m)){let r=f(t,n,e);r&&(p[t]=r)}return{overrides:p,defaultModel:o,defaultThinking:s,defaultExtensions:c,disableBuiltins:i,disableThinking:a,modelScope:d}}function h(e,t,n,r){return r&&t.defaultModel!==void 0?{type:`subagents.defaultModel`,scope:`project`,path:r,model:t.defaultModel}:e.defaultModel===void 0?void 0:{type:`subagents.defaultModel`,scope:`user`,path:n,model:e.defaultModel}}function g(e,t){return t?e.map(e=>e.model===void 0?s(e,{...e,model:t.model,modelSource:t}):e):e}function _(e,t){return t===void 0?e:e.map(e=>e.fallbackModels===void 0?s(e,{...e,fallbackModels:[...t]}):e)}function v(e,t,n){return n&&t.defaultThinking!==void 0?t.defaultThinking:e.defaultThinking}function y(e,t){return t===void 0?e:e.map(e=>e.thinking===void 0?s(e,{...e,thinking:t}):e)}function b(e,t,n){return n&&t.defaultExtensions!==void 0?t.defaultExtensions:e.defaultExtensions}function x(e,t){return t===void 0?e:e.map(e=>e.extensions===void 0?s(e,{...e,extensions:[...t],extensionsFromDefault:!0}):e)}function S(e,t,n,r,i){return x(y(_(g(e,t),i),n),r)}function C(e){return{model:e.model,fallbackModels:e.fallbackModels?[...e.fallbackModels]:void 0,thinking:e.thinking,systemPromptMode:e.systemPromptMode,inheritProjectContext:e.inheritProjectContext,inheritSkills:e.inheritSkills,defaultContext:e.defaultContext,disabled:e.disabled,systemPrompt:e.systemPrompt,skills:e.skills?[...e.skills]:void 0,skillPath:e.skillPath?[...e.skillPath]:void 0,tools:e.tools?[...e.tools]:void 0,mcpDirectTools:e.mcpDirectTools?[...e.mcpDirectTools]:void 0,extensions:e.extensionsFromDefault?void 0:e.extensions?[...e.extensions]:void 0,subagentOnlyExtensions:e.subagentOnlyExtensions?[...e.subagentOnlyExtensions]:void 0,completionGuard:e.completionGuard,toolBudget:e.toolBudget}}function w(e,t,n){let r,i=!1,a=()=>(r??={...e},r),l=(t,n,r)=>{o(e,...n)||(a()[t]=r,i=!0)};if(t.model!==void 0&&l(`model`,[`model`],t.model===!1?void 0:t.model),t.fallbackModels!==void 0&&l(`fallbackModels`,[`fallbackModels`],t.fallbackModels===!1?void 0:[...t.fallbackModels]),t.thinking!==void 0&&l(`thinking`,[`thinking`],t.thinking===!1?void 0:t.thinking),t.systemPromptMode!==void 0&&l(`systemPromptMode`,[`systemPromptMode`],t.systemPromptMode),t.inheritProjectContext!==void 0&&l(`inheritProjectContext`,[`inheritProjectContext`],t.inheritProjectContext),t.inheritSkills!==void 0&&l(`inheritSkills`,[`inheritSkills`],t.inheritSkills),t.defaultContext!==void 0&&l(`defaultContext`,[`defaultContext`],t.defaultContext===!1?void 0:t.defaultContext),t.disabled!==void 0&&e.disabled===void 0&&(a().disabled=t.disabled,i=!0),t.skills!==void 0&&l(`skills`,[`skill`,`skills`],t.skills===!1?void 0:[...t.skills]),t.tools!==void 0&&!o(e,`tools`)){let{tools:e,mcpDirectTools:n}=c(t.tools===!1?[]:t.tools),r=a();r.tools=e,r.mcpDirectTools=n,i=!0}return t.extensions!==void 0&&l(`extensions`,[`extensions`],t.extensions===!1?void 0:[...t.extensions]),t.subagentOnlyExtensions!==void 0&&l(`subagentOnlyExtensions`,[`subagentOnlyExtensions`],t.subagentOnlyExtensions===!1?void 0:[...t.subagentOnlyExtensions]),t.completionGuard!==void 0&&l(`completionGuard`,[`completionGuard`],t.completionGuard),t.toolBudget!==void 0&&l(`toolBudget`,[`toolBudget`],t.toolBudget===!1?void 0:t.toolBudget),!i||!r?e:(r.override={...n,base:C(e)},s(e,r))}function T(e,t,n,r,i){return e.map(e=>{let a=n.overrides[e.name];if(a&&i)return w(e,a,{scope:`project`,path:i});let o=t.overrides[e.name];return o?w(e,o,{scope:`user`,path:r}):e})}exports.EMPTY_SUBAGENT_SETTINGS=r,exports.applyCustomAgentOverrides=T,exports.applySubagentDefaults=S,exports.readSubagentSettings=m,exports.resolveSubagentDefaultExtensions=b,exports.resolveSubagentDefaultModel=h,exports.resolveSubagentDefaultThinking=v,exports.setAgentFrontmatterFields=a;
|
|
2
|
+
//# sourceMappingURL=settings.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.cjs","names":["readSettingsFileStrict"],"sources":["../../src/agents/settings.ts"],"sourcesContent":["/**\n * Agent settings: reading them, and applying what they say to discovered agents.\n *\n * Two things live here because they are the same contract seen from both ends.\n * Reading turns untyped JSON on disk into settings; applying folds those\n * settings over the agents discovery found. The precedence is fixed: project\n * settings beat user settings, an explicit per-agent override beats a bulk\n * switch, and for a custom agent the agent's own frontmatter beats both.\n *\n * That last rule is why builtins and custom agents take different paths.\n * Overriding a builtin replaces values outright, because a builtin has no file\n * the user could have edited instead. Overriding a custom agent only fills\n * fields its frontmatter left unset, so a settings default never silently\n * contradicts what the author wrote in the file.\n *\n * DESIGN PATTERNS:\n * - Reading is strict and throws with the offending path and field. A settings\n * file that is quietly half-ignored is worse than one that refuses to load,\n * because the user sees agents behaving in ways their config does not explain\n * - Applying is pure and copy-on-write. Every `apply*` returns new configs and\n * never mutates its input, so a cached discovery result cannot be corrupted\n * by a later pass over it\n * - Overriding a builtin snapshots the values it replaced into `override.base`,\n * which is what makes removing the override restore the builtin exactly\n *\n * AVOID:\n * - Trusting any shape read off disk without a real guard\n * - Mutating an `AgentConfig` in place\n * - Writing a settings file non-atomically; it holds the user's overrides\n */\n\nimport * as fs from 'node:fs';\nimport { writeAtomicJson } from '../shared/atomicJson.ts';\nimport { readSettingsFileStrict } from '../shared/configDir.ts';\nimport type { ModelScopeConfig, ToolBudgetConfig } from '../types.ts';\nimport { getProjectAgentSettingsPath, getUserAgentSettingsPath } from './projectRoot.ts';\nexport { readSettingsFileStrict } from '../shared/configDir.ts';\nimport type {\n AgentConfig,\n AgentModelSourceInfo,\n BuiltinAgentOverrideBase,\n BuiltinAgentOverrideConfig,\n SubagentSettings,\n} from './types.ts';\n\nconst SUBAGENTS_SETTINGS_KEY = 'subagents';\nconst AGENT_OVERRIDES_KEY = 'agentOverrides';\n/** Prefix marking a tool the child calls on an MCP server directly. */\nconst MCP_DIRECT_TOOL_PREFIX = 'mcp:';\n\n/** Which settings file an override came from, without the value snapshot. */\ntype OverrideScopeMeta = { scope: 'user' | 'project'; path: string };\n\n/**\n * The snapshot of a builtin's pre-override values.\n *\n * Widens the contract with `mcpDirectTools` because a builtin's tool list is\n * stored split in `AgentConfig` and must round-trip through the snapshot to be\n * restorable. See the report note on `BuiltinAgentOverrideBase`.\n */\nexport interface BuiltinOverrideSnapshot extends BuiltinAgentOverrideBase {\n mcpDirectTools?: string[];\n}\n\n/**\n * Returned whenever there is nothing to read.\n *\n * Shared rather than freshly built per call because it is returned on the hot\n * discovery path for every session with no project settings file.\n */\nexport const EMPTY_SUBAGENT_SETTINGS: SubagentSettings = { overrides: {} };\n\n/**\n * Which fields of a config came from its file's frontmatter.\n *\n * Keyed by the config object rather than by agent name because configs are\n * copied on write: each `apply*` pass produces new objects, and the new one\n * inherits the entry so later passes still know what the author declared.\n * A WeakMap means a discarded config's entry is collectable with it.\n */\nconst agentFrontmatterFields = new WeakMap<AgentConfig, Set<string>>();\n\n/** Record which fields a freshly parsed config took from frontmatter. */\nexport function setAgentFrontmatterFields(agent: AgentConfig, fields: Set<string>): void {\n agentFrontmatterFields.set(agent, fields);\n}\n\n/** True when the agent's file declared any of `fields` in its frontmatter. */\nexport function agentHasFrontmatterField(agent: AgentConfig, ...fields: string[]): boolean {\n const frontmatterFields = agentFrontmatterFields.get(agent);\n return frontmatterFields ? fields.some((field) => frontmatterFields.has(field)) : false;\n}\n\n/** Carry the frontmatter record onto a copy produced by an `apply*` pass. */\nfunction inheritFrontmatterFields(source: AgentConfig, next: AgentConfig): AgentConfig {\n const frontmatterFields = agentFrontmatterFields.get(source);\n if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);\n return next;\n}\n\n/** Split a flat tool list into host tools and direct MCP tools. */\nfunction splitToolList(rawTools: string[] | undefined): { tools?: string[]; mcpDirectTools?: string[] } {\n const mcpDirectTools: string[] = [];\n const tools: string[] = [];\n for (const tool of rawTools ?? []) {\n if (tool.startsWith(MCP_DIRECT_TOOL_PREFIX)) mcpDirectTools.push(tool.slice(MCP_DIRECT_TOOL_PREFIX.length));\n else tools.push(tool);\n }\n return {\n ...(rawTools !== undefined ? { tools } : {}),\n ...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),\n };\n}\n\n/** Inverse of `splitToolList`, for writing a tool list back to settings. */\nfunction joinToolList(config: { tools?: string[]; mcpDirectTools?: string[] }): string[] | undefined {\n const joined = [\n ...(config.tools ?? []),\n ...(config.mcpDirectTools ?? []).map((tool) => `${MCP_DIRECT_TOOL_PREFIX}${tool}`),\n ];\n return joined.length > 0 ? joined : undefined;\n}\n\n/** Order-sensitive equality; a reordered tool list is a real change. */\nfunction arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n// ============================================================================\n// Reading\n// ============================================================================\n\n/**\n * Write a settings file atomically.\n *\n * This file holds every override the user configured. A partial write from a\n * crash mid-save would be read back as malformed on the next start and, because\n * reading is strict, would refuse to load at all.\n */\nexport function writeSettingsFile(filePath: string, settings: Record<string, unknown>): void {\n writeAtomicJson(filePath, settings);\n}\n\n/** Read a settings object at `key`, or undefined when it is absent or not an object. */\nfunction readObjectField(source: Record<string, unknown>, key: string): Record<string, unknown> | undefined {\n const value = source[key];\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\n/** Copy a settings object at `key`, or start a fresh one when it is absent. */\nfunction cloneObjectField(source: Record<string, unknown>, key: string): Record<string, unknown> {\n // Spreading undefined is already a no-op, so no empty-object fallback is needed.\n return { ...readObjectField(source, key) };\n}\n\n/**\n * Parse the model-scope block.\n *\n * Lives here rather than beside the scope enforcement logic because parsing is\n * a settings concern and the agents domain may not reach into the runs domain.\n */\nfunction parseModelScopeConfig(value: unknown, filePath: string): ModelScopeConfig | undefined {\n if (value === undefined) return undefined;\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'modelScope'; expected an object.`);\n }\n\n const input = value as Record<string, unknown>;\n const config: ModelScopeConfig = {};\n\n if ('enforce' in input) {\n if (typeof input.enforce !== 'boolean') {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'modelScope.enforce'; expected a boolean.`);\n }\n config.enforce = input.enforce;\n }\n\n if ('allow' in input) {\n if (!Array.isArray(input.allow)) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected an array of strings.`,\n );\n }\n const allow: string[] = [];\n for (const entry of input.allow) {\n if (typeof entry !== 'string') {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected an array of strings.`,\n );\n }\n const trimmed = entry.trim();\n if (trimmed) allow.push(trimmed);\n }\n if (allow.length === 0) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`,\n );\n }\n config.allow = allow;\n }\n\n // Enforcing against an empty allow list would reject every model, so it is\n // rejected here rather than at the point it would start failing runs.\n if (config.enforce === true && (!config.allow || config.allow.length === 0)) {\n throw new Error(\n `Subagent settings in '${filePath}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`,\n );\n }\n\n return Object.keys(config).length > 0 ? config : undefined;\n}\n\n/**\n * Parse a string-array override field, which may also be `false` to clear it.\n *\n * Empty entries are dropped rather than rejected: trailing commas in a hand\n * edited list are common and unambiguous.\n */\nexport function parseOverrideStringArrayOrFalse(\n value: unknown,\n meta: { filePath: string; name: string; field: string },\n): string[] | false | undefined {\n if (value === undefined) return undefined;\n if (value === false) return false;\n if (!Array.isArray(value)) {\n throw new Error(\n `Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`,\n );\n }\n\n const items: string[] = [];\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new Error(\n `Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`,\n );\n }\n const trimmed = item.trim();\n if (trimmed) items.push(trimmed);\n }\n return items;\n}\n\n/**\n * Parse one entry of `subagents.agentOverrides`.\n *\n * Returns undefined for an entry that declared nothing, so an empty object in\n * the file does not register as an override and mark the agent as overridden.\n */\nexport function parseBuiltinOverrideEntry(\n name: string,\n value: unknown,\n filePath: string,\n): BuiltinAgentOverrideConfig | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`Builtin override '${name}' in '${filePath}' must be an object.`);\n }\n\n const input = value as Record<string, unknown>;\n const override: BuiltinAgentOverrideConfig = {};\n\n if ('model' in input) {\n if (typeof input.model === 'string' || input.model === false) override.model = input.model;\n else\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);\n }\n\n if ('thinking' in input) {\n if (typeof input.thinking === 'string' || input.thinking === false) override.thinking = input.thinking;\n else\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'thinking'; expected a string or false.`,\n );\n }\n\n if ('systemPromptMode' in input) {\n if (input.systemPromptMode === 'append' || input.systemPromptMode === 'replace') {\n override.systemPromptMode = input.systemPromptMode;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`,\n );\n }\n }\n\n if ('inheritProjectContext' in input) {\n if (typeof input.inheritProjectContext === 'boolean') {\n override.inheritProjectContext = input.inheritProjectContext;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'inheritProjectContext'; expected a boolean.`,\n );\n }\n }\n\n if ('inheritSkills' in input) {\n if (typeof input.inheritSkills === 'boolean') {\n override.inheritSkills = input.inheritSkills;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritSkills'; expected a boolean.`);\n }\n }\n\n if ('defaultContext' in input) {\n if (input.defaultContext === 'fresh' || input.defaultContext === 'fork' || input.defaultContext === false) {\n override.defaultContext = input.defaultContext;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`,\n );\n }\n }\n\n if ('disabled' in input) {\n if (typeof input.disabled === 'boolean') {\n override.disabled = input.disabled;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'disabled'; expected a boolean.`);\n }\n }\n\n if ('completionGuard' in input) {\n if (typeof input.completionGuard === 'boolean') {\n override.completionGuard = input.completionGuard;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'completionGuard'; expected a boolean.`);\n }\n }\n\n if ('toolBudget' in input) {\n const toolBudget = parseToolBudgetOverride(input.toolBudget, name, filePath);\n if (toolBudget !== undefined) override.toolBudget = toolBudget;\n }\n\n if ('systemPrompt' in input) {\n if (typeof input.systemPrompt === 'string') override.systemPrompt = input.systemPrompt;\n else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPrompt'; expected a string.`);\n }\n\n const parseArrayField = (field: string): string[] | false | undefined =>\n parseOverrideStringArrayOrFalse(input[field], { filePath, name, field });\n\n const fallbackModels = parseArrayField('fallbackModels');\n if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;\n const skills = parseArrayField('skills');\n if (skills !== undefined) override.skills = skills;\n const tools = parseArrayField('tools');\n if (tools !== undefined) override.tools = tools;\n const extensions = parseArrayField('extensions');\n if (extensions !== undefined) override.extensions = extensions;\n const subagentOnlyExtensions = parseArrayField('subagentOnlyExtensions');\n if (subagentOnlyExtensions !== undefined) override.subagentOnlyExtensions = subagentOnlyExtensions;\n\n return Object.keys(override).length > 0 ? override : undefined;\n}\n\n/**\n * Parse a `toolBudget` override.\n *\n * `hard` is required by the contract, so an object without a usable one is\n * rejected here instead of producing a budget that never blocks.\n */\nfunction parseToolBudgetOverride(value: unknown, name: string, filePath: string): ToolBudgetConfig | false | undefined {\n if (value === false) return false;\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'toolBudget'; expected an object or false.`,\n );\n }\n\n const input = value as Record<string, unknown>;\n if (typeof input.hard !== 'number') {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.hard'; expected a number.`);\n }\n\n const budget: ToolBudgetConfig = { hard: input.hard };\n if ('soft' in input) {\n if (typeof input.soft !== 'number') {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.soft'; expected a number.`);\n }\n budget.soft = input.soft;\n }\n if ('block' in input) {\n if (input.block === '*') {\n budget.block = '*';\n } else if (Array.isArray(input.block) && input.block.every((entry) => typeof entry === 'string')) {\n budget.block = [...input.block];\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`,\n );\n }\n }\n return budget;\n}\n\n/** Read and validate one settings file's `subagents` block. */\nexport function readSubagentSettings(filePath: string | null): SubagentSettings {\n if (!filePath) return EMPTY_SUBAGENT_SETTINGS;\n const settings = readSettingsFileStrict(filePath);\n const subagentsObject = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagentsObject) return EMPTY_SUBAGENT_SETTINGS;\n\n let disableBuiltins: boolean | undefined;\n if ('disableBuiltins' in subagentsObject) {\n if (typeof subagentsObject.disableBuiltins === 'boolean') {\n disableBuiltins = subagentsObject.disableBuiltins;\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'disableBuiltins'; expected a boolean.`);\n }\n }\n\n let disableThinking: boolean | undefined;\n if ('disableThinking' in subagentsObject) {\n if (typeof subagentsObject.disableThinking === 'boolean') {\n disableThinking = subagentsObject.disableThinking;\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'disableThinking'; expected a boolean.`);\n }\n }\n\n let defaultModel: string | undefined;\n if ('defaultModel' in subagentsObject) {\n if (typeof subagentsObject.defaultModel === 'string' && subagentsObject.defaultModel.trim()) {\n defaultModel = subagentsObject.defaultModel.trim();\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultModel'; expected a non-empty string.`);\n }\n }\n\n let defaultThinking: string | undefined;\n if ('defaultThinking' in subagentsObject) {\n if (typeof subagentsObject.defaultThinking === 'string' && subagentsObject.defaultThinking.trim()) {\n defaultThinking = subagentsObject.defaultThinking.trim();\n } else {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'defaultThinking'; expected a non-empty string.`,\n );\n }\n }\n\n let defaultExtensions: string[] | undefined;\n if ('defaultExtensions' in subagentsObject) {\n const raw = subagentsObject.defaultExtensions;\n if (!Array.isArray(raw) || raw.some((item) => typeof item !== 'string' || !item.trim())) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`,\n );\n }\n defaultExtensions = raw.map((item) => String(item).trim());\n }\n\n const modelScope = parseModelScopeConfig(subagentsObject.modelScope, filePath);\n\n const overrides: Record<string, BuiltinAgentOverrideConfig> = {};\n const agentOverrides = readObjectField(subagentsObject, AGENT_OVERRIDES_KEY);\n if (agentOverrides) {\n for (const [name, value] of Object.entries(agentOverrides)) {\n const override = parseBuiltinOverrideEntry(name, value, filePath);\n if (override) overrides[name] = override;\n }\n }\n\n return { overrides, defaultModel, defaultThinking, defaultExtensions, disableBuiltins, disableThinking, modelScope };\n}\n\n// ============================================================================\n// Settings-level defaults\n// ============================================================================\n\n/** Which settings file supplies `defaultModel`, project winning over user. */\nexport function resolveSubagentDefaultModel(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentModelSourceInfo | undefined {\n if (projectSettingsPath && projectSettings.defaultModel !== undefined) {\n return {\n type: 'subagents.defaultModel',\n scope: 'project',\n path: projectSettingsPath,\n model: projectSettings.defaultModel,\n };\n }\n return userSettings.defaultModel !== undefined\n ? { type: 'subagents.defaultModel', scope: 'user', path: userSettingsPath, model: userSettings.defaultModel }\n : undefined;\n}\n\n/**\n * Fill in the default model for agents that declared none.\n *\n * `modelSource` is recorded alongside so callers can tell an inherited model\n * from a requested one, which decides how hard model-scope enforcement bites.\n */\nexport function applySubagentDefaultModel(\n agents: AgentConfig[],\n defaultModel: AgentModelSourceInfo | undefined,\n): AgentConfig[] {\n if (!defaultModel) return agents;\n return agents.map((agent) => {\n if (agent.model !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, model: defaultModel.model, modelSource: defaultModel });\n });\n}\n\n/** Fill in fallback models only when the agent declared no fallback policy. */\nexport function applySubagentDefaultFallbackModels(\n agents: AgentConfig[],\n fallbackModels: string[] | undefined,\n): AgentConfig[] {\n if (fallbackModels === undefined) return agents;\n return agents.map((agent) => {\n if (agent.fallbackModels !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, fallbackModels: [...fallbackModels] });\n });\n}\n\nexport function resolveSubagentDefaultThinking(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n projectSettingsPath: string | null,\n): string | undefined {\n if (projectSettingsPath && projectSettings.defaultThinking !== undefined) return projectSettings.defaultThinking;\n return userSettings.defaultThinking;\n}\n\nexport function applySubagentDefaultThinking(\n agents: AgentConfig[],\n defaultThinking: string | undefined,\n): AgentConfig[] {\n if (defaultThinking === undefined) return agents;\n return agents.map((agent) => {\n if (agent.thinking !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, thinking: defaultThinking });\n });\n}\n\nexport function resolveSubagentDefaultExtensions(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n projectSettingsPath: string | null,\n): string[] | undefined {\n if (projectSettingsPath && projectSettings.defaultExtensions !== undefined) return projectSettings.defaultExtensions;\n return userSettings.defaultExtensions;\n}\n\n/**\n * Fill in default extensions for agents that declared none.\n *\n * The `extensionsFromDefault` flag matters when the agent is later overridden:\n * a snapshot must not record a settings default as if the builtin had declared\n * it, or removing the override would bake the default in permanently.\n */\nexport function applySubagentDefaultExtensions(\n agents: AgentConfig[],\n defaultExtensions: string[] | undefined,\n): AgentConfig[] {\n if (defaultExtensions === undefined) return agents;\n return agents.map((agent) => {\n if (agent.extensions !== undefined) return agent;\n return inheritFrontmatterFields(agent, {\n ...agent,\n extensions: [...defaultExtensions],\n extensionsFromDefault: true,\n });\n });\n}\n\nexport function applySubagentDefaults(\n agents: AgentConfig[],\n defaultModel: AgentModelSourceInfo | undefined,\n defaultThinking: string | undefined,\n defaultExtensions: string[] | undefined,\n defaultFallbackModels?: string[],\n): AgentConfig[] {\n return applySubagentDefaultExtensions(\n applySubagentDefaultThinking(\n applySubagentDefaultFallbackModels(applySubagentDefaultModel(agents, defaultModel), defaultFallbackModels),\n defaultThinking,\n ),\n defaultExtensions,\n );\n}\n\n// ============================================================================\n// Override snapshots\n// ============================================================================\n\n/**\n * Snapshot the fields an override may replace, so it can be undone.\n *\n * `extensions` is omitted when it came from a settings default rather than the\n * agent itself; restoring it would turn a default into a permanent value.\n */\nexport function cloneOverrideBase(agent: AgentConfig): BuiltinOverrideSnapshot {\n return {\n model: agent.model,\n fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,\n thinking: agent.thinking,\n systemPromptMode: agent.systemPromptMode,\n inheritProjectContext: agent.inheritProjectContext,\n inheritSkills: agent.inheritSkills,\n defaultContext: agent.defaultContext,\n disabled: agent.disabled,\n systemPrompt: agent.systemPrompt,\n skills: agent.skills ? [...agent.skills] : undefined,\n skillPath: agent.skillPath ? [...agent.skillPath] : undefined,\n tools: agent.tools ? [...agent.tools] : undefined,\n mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined,\n extensions: agent.extensionsFromDefault ? undefined : agent.extensions ? [...agent.extensions] : undefined,\n subagentOnlyExtensions: agent.subagentOnlyExtensions ? [...agent.subagentOnlyExtensions] : undefined,\n completionGuard: agent.completionGuard,\n toolBudget: agent.toolBudget,\n };\n}\n\n/**\n * Deep-copy an override before it is written to a settings file.\n *\n * Only keys the caller actually set are copied, because an explicit `undefined`\n * would serialise the key away and read back as \"not overridden\".\n */\nexport function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {\n return {\n ...(override.model !== undefined ? { model: override.model } : {}),\n ...(override.fallbackModels !== undefined\n ? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }\n : {}),\n ...(override.thinking !== undefined ? { thinking: override.thinking } : {}),\n ...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),\n ...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),\n ...(override.inheritSkills !== undefined ? { inheritSkills: override.inheritSkills } : {}),\n ...(override.defaultContext !== undefined ? { defaultContext: override.defaultContext } : {}),\n ...(override.disabled !== undefined ? { disabled: override.disabled } : {}),\n ...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),\n ...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),\n ...(override.tools !== undefined ? { tools: override.tools === false ? false : [...override.tools] } : {}),\n ...(override.extensions !== undefined\n ? { extensions: override.extensions === false ? false : [...override.extensions] }\n : {}),\n ...(override.subagentOnlyExtensions !== undefined\n ? {\n subagentOnlyExtensions:\n override.subagentOnlyExtensions === false ? false : [...override.subagentOnlyExtensions],\n }\n : {}),\n ...(override.completionGuard !== undefined ? { completionGuard: override.completionGuard } : {}),\n ...(override.toolBudget !== undefined\n ? {\n toolBudget:\n override.toolBudget === false\n ? false\n : {\n ...override.toolBudget,\n ...(Array.isArray(override.toolBudget.block) ? { block: [...override.toolBudget.block] } : {}),\n },\n }\n : {}),\n };\n}\n\n// ============================================================================\n// Applying overrides\n// ============================================================================\n\n/**\n * Replace a builtin's values with an override's.\n *\n * `false` clears a field rather than setting it to false; that distinction is\n * the whole reason the stored shape differs from the snapshot shape.\n */\nexport function applyBuiltinOverride(\n agent: AgentConfig,\n override: BuiltinAgentOverrideConfig,\n meta: OverrideScopeMeta,\n): AgentConfig {\n const next: AgentConfig = { ...agent, override: { ...meta, base: cloneOverrideBase(agent) } };\n\n if (override.model !== undefined) next.model = override.model === false ? undefined : override.model;\n if (override.fallbackModels !== undefined) {\n next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];\n }\n if (override.thinking !== undefined) next.thinking = override.thinking === false ? undefined : override.thinking;\n if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;\n if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;\n if (override.inheritSkills !== undefined) next.inheritSkills = override.inheritSkills;\n if (override.defaultContext !== undefined) {\n next.defaultContext = override.defaultContext === false ? undefined : override.defaultContext;\n }\n if (override.disabled !== undefined) next.disabled = override.disabled;\n if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;\n if (override.skills !== undefined) next.skills = override.skills === false ? undefined : [...override.skills];\n if (override.tools !== undefined) {\n const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);\n next.tools = tools;\n next.mcpDirectTools = mcpDirectTools;\n }\n if (override.extensions !== undefined) {\n next.extensions = override.extensions === false ? undefined : [...override.extensions];\n }\n if (override.subagentOnlyExtensions !== undefined) {\n next.subagentOnlyExtensions =\n override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions];\n }\n if (override.completionGuard !== undefined) next.completionGuard = override.completionGuard;\n if (override.toolBudget !== undefined) {\n next.toolBudget = override.toolBudget === false ? undefined : override.toolBudget;\n }\n\n return next;\n}\n\n/**\n * Strip an agent's thinking level for the bulk `disableThinking` switch.\n *\n * An existing `override` is kept rather than replaced, so a per-agent override\n * remains the recorded reason the agent differs from its builtin definition.\n */\nexport function clearBuiltinThinking(agent: AgentConfig, meta: OverrideScopeMeta): AgentConfig {\n if (agent.thinking === undefined) return agent;\n return { ...agent, thinking: undefined, override: agent.override ?? { ...meta, base: cloneOverrideBase(agent) } };\n}\n\n/**\n * Apply user and project settings to the builtin agents.\n *\n * Precedence per agent: a project override, then the project bulk disable, then\n * a user override, then the user bulk disable. A project file that sets\n * `disableBuiltins` at all (including to false) shadows the user's switch, so a\n * project can re-enable builtins its user settings turned off.\n */\nexport function applyBuiltinOverrides(\n builtinAgents: AgentConfig[],\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentConfig[] {\n const projectBulkDisabled = projectSettings.disableBuiltins === true && projectSettingsPath !== null;\n const userBulkDisabled = projectSettings.disableBuiltins === undefined && userSettings.disableBuiltins === true;\n const projectThinkingConfigured = projectSettings.disableThinking !== undefined && projectSettingsPath !== null;\n const disableThinking = projectThinkingConfigured\n ? projectSettings.disableThinking === true\n : userSettings.disableThinking === true;\n const disableThinkingMeta: OverrideScopeMeta =\n projectThinkingConfigured && projectSettingsPath\n ? { scope: 'project', path: projectSettingsPath }\n : { scope: 'user', path: userSettingsPath };\n\n // A per-agent thinking override is more specific than the bulk switch, so it\n // survives it; everything else is cleared.\n const applyGlobalThinking = (agent: AgentConfig, hasExplicitThinkingOverride: boolean): AgentConfig => {\n if (!disableThinking || hasExplicitThinkingOverride) return agent;\n return clearBuiltinThinking(agent, disableThinkingMeta);\n };\n\n return builtinAgents.map((agent) => {\n const projectOverride = projectSettings.overrides[agent.name];\n if (projectOverride && projectSettingsPath) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, projectOverride, { scope: 'project', path: projectSettingsPath }),\n projectOverride.thinking !== undefined,\n );\n }\n\n if (projectBulkDisabled && projectSettingsPath) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, { disabled: true }, { scope: 'project', path: projectSettingsPath }),\n false,\n );\n }\n\n const userOverride = userSettings.overrides[agent.name];\n if (userOverride) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, userOverride, { scope: 'user', path: userSettingsPath }),\n !projectThinkingConfigured && userOverride.thinking !== undefined,\n );\n }\n\n if (userBulkDisabled) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, { disabled: true }, { scope: 'user', path: userSettingsPath }),\n false,\n );\n }\n\n return applyGlobalThinking(agent, false);\n });\n}\n\n/**\n * Apply an override to a custom agent, filling only unset fields.\n *\n * A custom agent has a file the user could have edited, so a settings value\n * must never contradict its frontmatter. Nothing is copied and no `override`\n * is recorded unless at least one field was actually filled, which keeps agents\n * that declared everything from being reported as overridden.\n */\nexport function applyCustomAgentOverride(\n agent: AgentConfig,\n override: BuiltinAgentOverrideConfig,\n meta: OverrideScopeMeta,\n): AgentConfig {\n let next: AgentConfig | undefined;\n let anyFilled = false;\n\n const mutable = (): AgentConfig => {\n next ??= { ...agent };\n return next;\n };\n\n const fill = <K extends keyof AgentConfig>(field: K, frontmatterFields: string[], value: AgentConfig[K]): void => {\n if (agentHasFrontmatterField(agent, ...frontmatterFields)) return;\n mutable()[field] = value;\n anyFilled = true;\n };\n\n if (override.model !== undefined) {\n fill('model', ['model'], override.model === false ? undefined : override.model);\n }\n if (override.fallbackModels !== undefined) {\n fill(\n 'fallbackModels',\n ['fallbackModels'],\n override.fallbackModels === false ? undefined : [...override.fallbackModels],\n );\n }\n if (override.thinking !== undefined) {\n fill('thinking', ['thinking'], override.thinking === false ? undefined : override.thinking);\n }\n if (override.systemPromptMode !== undefined) {\n fill('systemPromptMode', ['systemPromptMode'], override.systemPromptMode);\n }\n if (override.inheritProjectContext !== undefined) {\n fill('inheritProjectContext', ['inheritProjectContext'], override.inheritProjectContext);\n }\n if (override.inheritSkills !== undefined) {\n fill('inheritSkills', ['inheritSkills'], override.inheritSkills);\n }\n if (override.defaultContext !== undefined) {\n fill('defaultContext', ['defaultContext'], override.defaultContext === false ? undefined : override.defaultContext);\n }\n if (override.disabled !== undefined && agent.disabled === undefined) {\n mutable().disabled = override.disabled;\n anyFilled = true;\n }\n if (override.skills !== undefined) {\n fill('skills', ['skill', 'skills'], override.skills === false ? undefined : [...override.skills]);\n }\n if (override.tools !== undefined && !agentHasFrontmatterField(agent, 'tools')) {\n const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);\n const target = mutable();\n target.tools = tools;\n target.mcpDirectTools = mcpDirectTools;\n anyFilled = true;\n }\n if (override.extensions !== undefined) {\n fill('extensions', ['extensions'], override.extensions === false ? undefined : [...override.extensions]);\n }\n if (override.subagentOnlyExtensions !== undefined) {\n fill(\n 'subagentOnlyExtensions',\n ['subagentOnlyExtensions'],\n override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions],\n );\n }\n if (override.completionGuard !== undefined) {\n fill('completionGuard', ['completionGuard'], override.completionGuard);\n }\n if (override.toolBudget !== undefined) {\n fill('toolBudget', ['toolBudget'], override.toolBudget === false ? undefined : override.toolBudget);\n }\n\n if (!anyFilled || !next) return agent;\n next.override = { ...meta, base: cloneOverrideBase(agent) };\n return inheritFrontmatterFields(agent, next);\n}\n\nexport function applyCustomAgentOverrides(\n agents: AgentConfig[],\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentConfig[] {\n return agents.map((agent) => {\n const projectOverride = projectSettings.overrides[agent.name];\n if (projectOverride && projectSettingsPath) {\n return applyCustomAgentOverride(agent, projectOverride, { scope: 'project', path: projectSettingsPath });\n }\n\n const userOverride = userSettings.overrides[agent.name];\n if (userOverride) {\n return applyCustomAgentOverride(agent, userOverride, { scope: 'user', path: userSettingsPath });\n }\n\n return agent;\n });\n}\n\n/**\n * Diff an edited agent against its pre-override snapshot.\n *\n * Only changed fields are written, so a settings file records the user's edits\n * rather than a full copy of the builtin that would drift as the builtin does.\n * Returns undefined when nothing changed, which callers treat as \"remove the\n * override\" rather than \"write an empty one\".\n */\nexport function buildBuiltinOverrideConfig(\n base: BuiltinOverrideSnapshot,\n draft: Pick<\n AgentConfig,\n | 'model'\n | 'fallbackModels'\n | 'thinking'\n | 'systemPromptMode'\n | 'inheritProjectContext'\n | 'inheritSkills'\n | 'defaultContext'\n | 'disabled'\n | 'systemPrompt'\n | 'skills'\n | 'tools'\n | 'mcpDirectTools'\n | 'extensions'\n | 'subagentOnlyExtensions'\n | 'completionGuard'\n | 'toolBudget'\n >,\n): BuiltinAgentOverrideConfig | undefined {\n const override: BuiltinAgentOverrideConfig = {};\n\n if (draft.model !== base.model) override.model = draft.model ?? false;\n if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) {\n override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;\n }\n if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;\n if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;\n if (draft.inheritProjectContext !== base.inheritProjectContext) {\n override.inheritProjectContext = draft.inheritProjectContext;\n }\n if (draft.inheritSkills !== base.inheritSkills) override.inheritSkills = draft.inheritSkills;\n if (draft.defaultContext !== base.defaultContext) override.defaultContext = draft.defaultContext ?? false;\n if (draft.disabled !== base.disabled) override.disabled = draft.disabled ?? false;\n if (draft.systemPrompt !== base.systemPrompt) override.systemPrompt = draft.systemPrompt;\n if (!arraysEqual(draft.skills, base.skills)) override.skills = draft.skills ? [...draft.skills] : false;\n\n const baseTools = joinToolList(base);\n const draftTools = joinToolList(draft);\n if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;\n if (!arraysEqual(draft.extensions, base.extensions)) {\n override.extensions = draft.extensions ? [...draft.extensions] : false;\n }\n if (!arraysEqual(draft.subagentOnlyExtensions, base.subagentOnlyExtensions)) {\n override.subagentOnlyExtensions = draft.subagentOnlyExtensions ? [...draft.subagentOnlyExtensions] : false;\n }\n // Compared as booleans because an unset guard and an enabled one behave the\n // same, so only a switch to or from explicitly disabled is a real change.\n if ((draft.completionGuard !== false) !== (base.completionGuard !== false)) {\n override.completionGuard = draft.completionGuard !== false;\n }\n if (JSON.stringify(draft.toolBudget) !== JSON.stringify(base.toolBudget)) {\n override.toolBudget = draft.toolBudget ?? false;\n }\n\n return Object.keys(override).length > 0 ? override : undefined;\n}\n\n// ============================================================================\n// Persisting overrides\n// ============================================================================\n\nconst NO_PROJECT_ROOT_MESSAGE = 'Project override is not available here. No project config root was found.';\n\n/** Resolve the settings file for a scope, or throw when there is no project. */\nfunction requireSettingsPath(cwd: string, scope: 'user' | 'project'): string {\n const filePath = scope === 'project' ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();\n if (!filePath) throw new Error(NO_PROJECT_ROOT_MESSAGE);\n return filePath;\n}\n\n/** Write an override, replacing any existing entry for the agent wholesale. */\nexport function saveBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n override: BuiltinAgentOverrideConfig,\n): string {\n const filePath = requireSettingsPath(cwd, scope);\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = cloneObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n const agentOverrides = cloneObjectField(subagents, AGENT_OVERRIDES_KEY);\n\n agentOverrides[name] = cloneOverrideValue(override);\n subagents[AGENT_OVERRIDES_KEY] = agentOverrides;\n settings[SUBAGENTS_SETTINGS_KEY] = subagents;\n writeSettingsFile(filePath, settings);\n return filePath;\n}\n\n/**\n * Drop an agent's whole override entry.\n *\n * Emptied containers are deleted rather than left behind, so removing the last\n * override leaves the settings file as it was before any were added.\n */\nexport function removeBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n): { path: string; removed: boolean } {\n const filePath = requireSettingsPath(cwd, scope);\n if (!fs.existsSync(filePath)) return { path: filePath, removed: false };\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagents) return { path: filePath, removed: false };\n const agentOverrides = readObjectField(subagents, AGENT_OVERRIDES_KEY);\n if (!agentOverrides) return { path: filePath, removed: false };\n if (!Object.hasOwn(agentOverrides, name)) return { path: filePath, removed: false };\n\n const nextSubagents = { ...subagents };\n const nextOverrides = { ...agentOverrides };\n delete nextOverrides[name];\n if (Object.keys(nextOverrides).length > 0) nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n else delete nextSubagents[AGENT_OVERRIDES_KEY];\n\n if (Object.keys(nextSubagents).length > 0) settings[SUBAGENTS_SETTINGS_KEY] = nextSubagents;\n else delete settings[SUBAGENTS_SETTINGS_KEY];\n\n writeSettingsFile(filePath, settings);\n return { path: filePath, removed: true };\n}\n\n/** Merge fields into an agent's existing override, leaving the rest in place. */\nexport function mergeBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n fields: BuiltinAgentOverrideConfig,\n): string {\n const filePath = requireSettingsPath(cwd, scope);\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = cloneObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n const agentOverrides = cloneObjectField(subagents, AGENT_OVERRIDES_KEY);\n\n const existing = readObjectField(agentOverrides, name) ?? {};\n agentOverrides[name] = { ...existing, ...cloneOverrideValue(fields) };\n subagents[AGENT_OVERRIDES_KEY] = agentOverrides;\n settings[SUBAGENTS_SETTINGS_KEY] = subagents;\n writeSettingsFile(filePath, settings);\n return filePath;\n}\n\n/** Remove named fields from an agent's override, dropping it once empty. */\nexport function removeBuiltinAgentOverrideFields(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n fields: string[],\n): { path: string; removed: boolean } {\n const filePath = requireSettingsPath(cwd, scope);\n if (!fs.existsSync(filePath)) return { path: filePath, removed: false };\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagents) return { path: filePath, removed: false };\n const agentOverrides = readObjectField(subagents, AGENT_OVERRIDES_KEY);\n if (!agentOverrides) return { path: filePath, removed: false };\n const entry = readObjectField(agentOverrides, name);\n if (!entry) return { path: filePath, removed: false };\n\n const nextEntry: Record<string, unknown> = { ...entry };\n let removed = false;\n for (const field of fields) {\n if (Object.hasOwn(nextEntry, field)) {\n delete nextEntry[field];\n removed = true;\n }\n }\n if (!removed) return { path: filePath, removed: false };\n\n const nextSubagents = { ...subagents };\n const nextOverrides = { ...agentOverrides };\n if (Object.keys(nextEntry).length > 0) {\n nextOverrides[name] = nextEntry;\n nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n } else {\n delete nextOverrides[name];\n if (Object.keys(nextOverrides).length > 0) nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n else delete nextSubagents[AGENT_OVERRIDES_KEY];\n }\n if (Object.keys(nextSubagents).length > 0) settings[SUBAGENTS_SETTINGS_KEY] = nextSubagents;\n else delete settings[SUBAGENTS_SETTINGS_KEY];\n\n writeSettingsFile(filePath, settings);\n return { path: filePath, removed: true };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import{init_configDir as t,readSettingsFileStrict as n}from"../shared/configDir.mjs";import{init_atomicJson as r}from"../shared/atomicJson.mjs";function i(e,t){k.set(e,t)}function a(e,...t){let n=k.get(e);return n?t.some(e=>n.has(e)):!1}function o(e,t){let n=k.get(e);return n&&k.set(t,n),t}function s(e){let t=[],n=[];for(let r of e??[])r.startsWith(D)?t.push(r.slice(4)):n.push(r);return{...e===void 0?{}:{tools:n},...t.length>0?{mcpDirectTools:t}:{}}}function c(e,t){let n=e[t];if(!(!n||typeof n!=`object`||Array.isArray(n)))return n}function l(e,t){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Subagent settings in '${t}' have invalid 'modelScope'; expected an object.`);let n=e,r={};if(`enforce`in n){if(typeof n.enforce!=`boolean`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.enforce'; expected a boolean.`);r.enforce=n.enforce}if(`allow`in n){if(!Array.isArray(n.allow))throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let e=[];for(let r of n.allow){if(typeof r!=`string`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let n=r.trim();n&&e.push(n)}if(e.length===0)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`);r.allow=e}if(r.enforce===!0&&(!r.allow||r.allow.length===0))throw Error(`Subagent settings in '${t}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`);return Object.keys(r).length>0?r:void 0}function u(e,t){if(e===void 0)return;if(e===!1)return!1;if(!Array.isArray(e))throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let n=[];for(let r of e){if(typeof r!=`string`)throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let e=r.trim();e&&n.push(e)}return n}function d(e,t,n){if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Builtin override '${e}' in '${n}' must be an object.`);let r=t,i={};if(`model`in r)if(typeof r.model==`string`||r.model===!1)i.model=r.model;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'model'; expected a string or false.`);if(`thinking`in r)if(typeof r.thinking==`string`||r.thinking===!1)i.thinking=r.thinking;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'thinking'; expected a string or false.`);if(`systemPromptMode`in r)if(r.systemPromptMode===`append`||r.systemPromptMode===`replace`)i.systemPromptMode=r.systemPromptMode;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`);if(`inheritProjectContext`in r)if(typeof r.inheritProjectContext==`boolean`)i.inheritProjectContext=r.inheritProjectContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritProjectContext'; expected a boolean.`);if(`inheritSkills`in r)if(typeof r.inheritSkills==`boolean`)i.inheritSkills=r.inheritSkills;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritSkills'; expected a boolean.`);if(`defaultContext`in r)if(r.defaultContext===`fresh`||r.defaultContext===`fork`||r.defaultContext===!1)i.defaultContext=r.defaultContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`);if(`disabled`in r)if(typeof r.disabled==`boolean`)i.disabled=r.disabled;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'disabled'; expected a boolean.`);if(`completionGuard`in r)if(typeof r.completionGuard==`boolean`)i.completionGuard=r.completionGuard;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'completionGuard'; expected a boolean.`);if(`toolBudget`in r){let t=f(r.toolBudget,e,n);t!==void 0&&(i.toolBudget=t)}if(`systemPrompt`in r)if(typeof r.systemPrompt==`string`)i.systemPrompt=r.systemPrompt;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPrompt'; expected a string.`);let a=t=>u(r[t],{filePath:n,name:e,field:t}),o=a(`fallbackModels`);o!==void 0&&(i.fallbackModels=o);let s=a(`skills`);s!==void 0&&(i.skills=s);let c=a(`tools`);c!==void 0&&(i.tools=c);let l=a(`extensions`);l!==void 0&&(i.extensions=l);let d=a(`subagentOnlyExtensions`);return d!==void 0&&(i.subagentOnlyExtensions=d),Object.keys(i).length>0?i:void 0}function f(e,t,n){if(e===!1)return!1;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget'; expected an object or false.`);let r=e;if(typeof r.hard!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.hard'; expected a number.`);let i={hard:r.hard};if(`soft`in r){if(typeof r.soft!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.soft'; expected a number.`);i.soft=r.soft}if(`block`in r)if(r.block===`*`)i.block=`*`;else if(Array.isArray(r.block)&&r.block.every(e=>typeof e==`string`))i.block=[...r.block];else throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`);return i}function p(e){if(!e)return O;let t=c(n(e),T);if(!t)return O;let r;if(`disableBuiltins`in t)if(typeof t.disableBuiltins==`boolean`)r=t.disableBuiltins;else throw Error(`Subagent settings in '${e}' have invalid 'disableBuiltins'; expected a boolean.`);let i;if(`disableThinking`in t)if(typeof t.disableThinking==`boolean`)i=t.disableThinking;else throw Error(`Subagent settings in '${e}' have invalid 'disableThinking'; expected a boolean.`);let a;if(`defaultModel`in t)if(typeof t.defaultModel==`string`&&t.defaultModel.trim())a=t.defaultModel.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultModel'; expected a non-empty string.`);let o;if(`defaultThinking`in t)if(typeof t.defaultThinking==`string`&&t.defaultThinking.trim())o=t.defaultThinking.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultThinking'; expected a non-empty string.`);let s;if(`defaultExtensions`in t){let n=t.defaultExtensions;if(!Array.isArray(n)||n.some(e=>typeof e!=`string`||!e.trim()))throw Error(`Subagent settings in '${e}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`);s=n.map(e=>String(e).trim())}let u=l(t.modelScope,e),f={},p=c(t,E);if(p)for(let[t,n]of Object.entries(p)){let r=d(t,n,e);r&&(f[t]=r)}return{overrides:f,defaultModel:a,defaultThinking:o,defaultExtensions:s,disableBuiltins:r,disableThinking:i,modelScope:u}}function m(e,t,n,r){return r&&t.defaultModel!==void 0?{type:`subagents.defaultModel`,scope:`project`,path:r,model:t.defaultModel}:e.defaultModel===void 0?void 0:{type:`subagents.defaultModel`,scope:`user`,path:n,model:e.defaultModel}}function h(e,t){return t?e.map(e=>e.model===void 0?o(e,{...e,model:t.model,modelSource:t}):e):e}function g(e,t){return t===void 0?e:e.map(e=>e.fallbackModels===void 0?o(e,{...e,fallbackModels:[...t]}):e)}function _(e,t,n){return n&&t.defaultThinking!==void 0?t.defaultThinking:e.defaultThinking}function v(e,t){return t===void 0?e:e.map(e=>e.thinking===void 0?o(e,{...e,thinking:t}):e)}function y(e,t,n){return n&&t.defaultExtensions!==void 0?t.defaultExtensions:e.defaultExtensions}function b(e,t){return t===void 0?e:e.map(e=>e.extensions===void 0?o(e,{...e,extensions:[...t],extensionsFromDefault:!0}):e)}function x(e,t,n,r,i){return b(v(g(h(e,t),i),n),r)}function S(e){return{model:e.model,fallbackModels:e.fallbackModels?[...e.fallbackModels]:void 0,thinking:e.thinking,systemPromptMode:e.systemPromptMode,inheritProjectContext:e.inheritProjectContext,inheritSkills:e.inheritSkills,defaultContext:e.defaultContext,disabled:e.disabled,systemPrompt:e.systemPrompt,skills:e.skills?[...e.skills]:void 0,skillPath:e.skillPath?[...e.skillPath]:void 0,tools:e.tools?[...e.tools]:void 0,mcpDirectTools:e.mcpDirectTools?[...e.mcpDirectTools]:void 0,extensions:e.extensionsFromDefault?void 0:e.extensions?[...e.extensions]:void 0,subagentOnlyExtensions:e.subagentOnlyExtensions?[...e.subagentOnlyExtensions]:void 0,completionGuard:e.completionGuard,toolBudget:e.toolBudget}}function C(e,t,n){let r,i=!1,c=()=>(r??={...e},r),l=(t,n,r)=>{a(e,...n)||(c()[t]=r,i=!0)};if(t.model!==void 0&&l(`model`,[`model`],t.model===!1?void 0:t.model),t.fallbackModels!==void 0&&l(`fallbackModels`,[`fallbackModels`],t.fallbackModels===!1?void 0:[...t.fallbackModels]),t.thinking!==void 0&&l(`thinking`,[`thinking`],t.thinking===!1?void 0:t.thinking),t.systemPromptMode!==void 0&&l(`systemPromptMode`,[`systemPromptMode`],t.systemPromptMode),t.inheritProjectContext!==void 0&&l(`inheritProjectContext`,[`inheritProjectContext`],t.inheritProjectContext),t.inheritSkills!==void 0&&l(`inheritSkills`,[`inheritSkills`],t.inheritSkills),t.defaultContext!==void 0&&l(`defaultContext`,[`defaultContext`],t.defaultContext===!1?void 0:t.defaultContext),t.disabled!==void 0&&e.disabled===void 0&&(c().disabled=t.disabled,i=!0),t.skills!==void 0&&l(`skills`,[`skill`,`skills`],t.skills===!1?void 0:[...t.skills]),t.tools!==void 0&&!a(e,`tools`)){let{tools:e,mcpDirectTools:n}=s(t.tools===!1?[]:t.tools),r=c();r.tools=e,r.mcpDirectTools=n,i=!0}return t.extensions!==void 0&&l(`extensions`,[`extensions`],t.extensions===!1?void 0:[...t.extensions]),t.subagentOnlyExtensions!==void 0&&l(`subagentOnlyExtensions`,[`subagentOnlyExtensions`],t.subagentOnlyExtensions===!1?void 0:[...t.subagentOnlyExtensions]),t.completionGuard!==void 0&&l(`completionGuard`,[`completionGuard`],t.completionGuard),t.toolBudget!==void 0&&l(`toolBudget`,[`toolBudget`],t.toolBudget===!1?void 0:t.toolBudget),!i||!r?e:(r.override={...n,base:S(e)},o(e,r))}function w(e,t,n,r,i){return e.map(e=>{let a=n.overrides[e.name];if(a&&i)return C(e,a,{scope:`project`,path:i});let o=t.overrides[e.name];return o?C(e,o,{scope:`user`,path:r}):e})}var T,E,D,O,k,A=e((()=>{r(),t(),T=`subagents`,E=`agentOverrides`,D=`mcp:`,O={overrides:{}},k=new WeakMap}));A();export{O as EMPTY_SUBAGENT_SETTINGS,w as applyCustomAgentOverrides,x as applySubagentDefaults,A as init_settings,p as readSubagentSettings,y as resolveSubagentDefaultExtensions,m as resolveSubagentDefaultModel,_ as resolveSubagentDefaultThinking,i as setAgentFrontmatterFields};
|
|
2
|
+
//# sourceMappingURL=settings.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.mjs","names":[],"sources":["../../src/agents/settings.ts"],"sourcesContent":["/**\n * Agent settings: reading them, and applying what they say to discovered agents.\n *\n * Two things live here because they are the same contract seen from both ends.\n * Reading turns untyped JSON on disk into settings; applying folds those\n * settings over the agents discovery found. The precedence is fixed: project\n * settings beat user settings, an explicit per-agent override beats a bulk\n * switch, and for a custom agent the agent's own frontmatter beats both.\n *\n * That last rule is why builtins and custom agents take different paths.\n * Overriding a builtin replaces values outright, because a builtin has no file\n * the user could have edited instead. Overriding a custom agent only fills\n * fields its frontmatter left unset, so a settings default never silently\n * contradicts what the author wrote in the file.\n *\n * DESIGN PATTERNS:\n * - Reading is strict and throws with the offending path and field. A settings\n * file that is quietly half-ignored is worse than one that refuses to load,\n * because the user sees agents behaving in ways their config does not explain\n * - Applying is pure and copy-on-write. Every `apply*` returns new configs and\n * never mutates its input, so a cached discovery result cannot be corrupted\n * by a later pass over it\n * - Overriding a builtin snapshots the values it replaced into `override.base`,\n * which is what makes removing the override restore the builtin exactly\n *\n * AVOID:\n * - Trusting any shape read off disk without a real guard\n * - Mutating an `AgentConfig` in place\n * - Writing a settings file non-atomically; it holds the user's overrides\n */\n\nimport * as fs from 'node:fs';\nimport { writeAtomicJson } from '../shared/atomicJson.ts';\nimport { readSettingsFileStrict } from '../shared/configDir.ts';\nimport type { ModelScopeConfig, ToolBudgetConfig } from '../types.ts';\nimport { getProjectAgentSettingsPath, getUserAgentSettingsPath } from './projectRoot.ts';\nexport { readSettingsFileStrict } from '../shared/configDir.ts';\nimport type {\n AgentConfig,\n AgentModelSourceInfo,\n BuiltinAgentOverrideBase,\n BuiltinAgentOverrideConfig,\n SubagentSettings,\n} from './types.ts';\n\nconst SUBAGENTS_SETTINGS_KEY = 'subagents';\nconst AGENT_OVERRIDES_KEY = 'agentOverrides';\n/** Prefix marking a tool the child calls on an MCP server directly. */\nconst MCP_DIRECT_TOOL_PREFIX = 'mcp:';\n\n/** Which settings file an override came from, without the value snapshot. */\ntype OverrideScopeMeta = { scope: 'user' | 'project'; path: string };\n\n/**\n * The snapshot of a builtin's pre-override values.\n *\n * Widens the contract with `mcpDirectTools` because a builtin's tool list is\n * stored split in `AgentConfig` and must round-trip through the snapshot to be\n * restorable. See the report note on `BuiltinAgentOverrideBase`.\n */\nexport interface BuiltinOverrideSnapshot extends BuiltinAgentOverrideBase {\n mcpDirectTools?: string[];\n}\n\n/**\n * Returned whenever there is nothing to read.\n *\n * Shared rather than freshly built per call because it is returned on the hot\n * discovery path for every session with no project settings file.\n */\nexport const EMPTY_SUBAGENT_SETTINGS: SubagentSettings = { overrides: {} };\n\n/**\n * Which fields of a config came from its file's frontmatter.\n *\n * Keyed by the config object rather than by agent name because configs are\n * copied on write: each `apply*` pass produces new objects, and the new one\n * inherits the entry so later passes still know what the author declared.\n * A WeakMap means a discarded config's entry is collectable with it.\n */\nconst agentFrontmatterFields = new WeakMap<AgentConfig, Set<string>>();\n\n/** Record which fields a freshly parsed config took from frontmatter. */\nexport function setAgentFrontmatterFields(agent: AgentConfig, fields: Set<string>): void {\n agentFrontmatterFields.set(agent, fields);\n}\n\n/** True when the agent's file declared any of `fields` in its frontmatter. */\nexport function agentHasFrontmatterField(agent: AgentConfig, ...fields: string[]): boolean {\n const frontmatterFields = agentFrontmatterFields.get(agent);\n return frontmatterFields ? fields.some((field) => frontmatterFields.has(field)) : false;\n}\n\n/** Carry the frontmatter record onto a copy produced by an `apply*` pass. */\nfunction inheritFrontmatterFields(source: AgentConfig, next: AgentConfig): AgentConfig {\n const frontmatterFields = agentFrontmatterFields.get(source);\n if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);\n return next;\n}\n\n/** Split a flat tool list into host tools and direct MCP tools. */\nfunction splitToolList(rawTools: string[] | undefined): { tools?: string[]; mcpDirectTools?: string[] } {\n const mcpDirectTools: string[] = [];\n const tools: string[] = [];\n for (const tool of rawTools ?? []) {\n if (tool.startsWith(MCP_DIRECT_TOOL_PREFIX)) mcpDirectTools.push(tool.slice(MCP_DIRECT_TOOL_PREFIX.length));\n else tools.push(tool);\n }\n return {\n ...(rawTools !== undefined ? { tools } : {}),\n ...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),\n };\n}\n\n/** Inverse of `splitToolList`, for writing a tool list back to settings. */\nfunction joinToolList(config: { tools?: string[]; mcpDirectTools?: string[] }): string[] | undefined {\n const joined = [\n ...(config.tools ?? []),\n ...(config.mcpDirectTools ?? []).map((tool) => `${MCP_DIRECT_TOOL_PREFIX}${tool}`),\n ];\n return joined.length > 0 ? joined : undefined;\n}\n\n/** Order-sensitive equality; a reordered tool list is a real change. */\nfunction arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n// ============================================================================\n// Reading\n// ============================================================================\n\n/**\n * Write a settings file atomically.\n *\n * This file holds every override the user configured. A partial write from a\n * crash mid-save would be read back as malformed on the next start and, because\n * reading is strict, would refuse to load at all.\n */\nexport function writeSettingsFile(filePath: string, settings: Record<string, unknown>): void {\n writeAtomicJson(filePath, settings);\n}\n\n/** Read a settings object at `key`, or undefined when it is absent or not an object. */\nfunction readObjectField(source: Record<string, unknown>, key: string): Record<string, unknown> | undefined {\n const value = source[key];\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\n/** Copy a settings object at `key`, or start a fresh one when it is absent. */\nfunction cloneObjectField(source: Record<string, unknown>, key: string): Record<string, unknown> {\n // Spreading undefined is already a no-op, so no empty-object fallback is needed.\n return { ...readObjectField(source, key) };\n}\n\n/**\n * Parse the model-scope block.\n *\n * Lives here rather than beside the scope enforcement logic because parsing is\n * a settings concern and the agents domain may not reach into the runs domain.\n */\nfunction parseModelScopeConfig(value: unknown, filePath: string): ModelScopeConfig | undefined {\n if (value === undefined) return undefined;\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'modelScope'; expected an object.`);\n }\n\n const input = value as Record<string, unknown>;\n const config: ModelScopeConfig = {};\n\n if ('enforce' in input) {\n if (typeof input.enforce !== 'boolean') {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'modelScope.enforce'; expected a boolean.`);\n }\n config.enforce = input.enforce;\n }\n\n if ('allow' in input) {\n if (!Array.isArray(input.allow)) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected an array of strings.`,\n );\n }\n const allow: string[] = [];\n for (const entry of input.allow) {\n if (typeof entry !== 'string') {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected an array of strings.`,\n );\n }\n const trimmed = entry.trim();\n if (trimmed) allow.push(trimmed);\n }\n if (allow.length === 0) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`,\n );\n }\n config.allow = allow;\n }\n\n // Enforcing against an empty allow list would reject every model, so it is\n // rejected here rather than at the point it would start failing runs.\n if (config.enforce === true && (!config.allow || config.allow.length === 0)) {\n throw new Error(\n `Subagent settings in '${filePath}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`,\n );\n }\n\n return Object.keys(config).length > 0 ? config : undefined;\n}\n\n/**\n * Parse a string-array override field, which may also be `false` to clear it.\n *\n * Empty entries are dropped rather than rejected: trailing commas in a hand\n * edited list are common and unambiguous.\n */\nexport function parseOverrideStringArrayOrFalse(\n value: unknown,\n meta: { filePath: string; name: string; field: string },\n): string[] | false | undefined {\n if (value === undefined) return undefined;\n if (value === false) return false;\n if (!Array.isArray(value)) {\n throw new Error(\n `Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`,\n );\n }\n\n const items: string[] = [];\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new Error(\n `Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`,\n );\n }\n const trimmed = item.trim();\n if (trimmed) items.push(trimmed);\n }\n return items;\n}\n\n/**\n * Parse one entry of `subagents.agentOverrides`.\n *\n * Returns undefined for an entry that declared nothing, so an empty object in\n * the file does not register as an override and mark the agent as overridden.\n */\nexport function parseBuiltinOverrideEntry(\n name: string,\n value: unknown,\n filePath: string,\n): BuiltinAgentOverrideConfig | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`Builtin override '${name}' in '${filePath}' must be an object.`);\n }\n\n const input = value as Record<string, unknown>;\n const override: BuiltinAgentOverrideConfig = {};\n\n if ('model' in input) {\n if (typeof input.model === 'string' || input.model === false) override.model = input.model;\n else\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);\n }\n\n if ('thinking' in input) {\n if (typeof input.thinking === 'string' || input.thinking === false) override.thinking = input.thinking;\n else\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'thinking'; expected a string or false.`,\n );\n }\n\n if ('systemPromptMode' in input) {\n if (input.systemPromptMode === 'append' || input.systemPromptMode === 'replace') {\n override.systemPromptMode = input.systemPromptMode;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`,\n );\n }\n }\n\n if ('inheritProjectContext' in input) {\n if (typeof input.inheritProjectContext === 'boolean') {\n override.inheritProjectContext = input.inheritProjectContext;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'inheritProjectContext'; expected a boolean.`,\n );\n }\n }\n\n if ('inheritSkills' in input) {\n if (typeof input.inheritSkills === 'boolean') {\n override.inheritSkills = input.inheritSkills;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritSkills'; expected a boolean.`);\n }\n }\n\n if ('defaultContext' in input) {\n if (input.defaultContext === 'fresh' || input.defaultContext === 'fork' || input.defaultContext === false) {\n override.defaultContext = input.defaultContext;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`,\n );\n }\n }\n\n if ('disabled' in input) {\n if (typeof input.disabled === 'boolean') {\n override.disabled = input.disabled;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'disabled'; expected a boolean.`);\n }\n }\n\n if ('completionGuard' in input) {\n if (typeof input.completionGuard === 'boolean') {\n override.completionGuard = input.completionGuard;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'completionGuard'; expected a boolean.`);\n }\n }\n\n if ('toolBudget' in input) {\n const toolBudget = parseToolBudgetOverride(input.toolBudget, name, filePath);\n if (toolBudget !== undefined) override.toolBudget = toolBudget;\n }\n\n if ('systemPrompt' in input) {\n if (typeof input.systemPrompt === 'string') override.systemPrompt = input.systemPrompt;\n else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPrompt'; expected a string.`);\n }\n\n const parseArrayField = (field: string): string[] | false | undefined =>\n parseOverrideStringArrayOrFalse(input[field], { filePath, name, field });\n\n const fallbackModels = parseArrayField('fallbackModels');\n if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;\n const skills = parseArrayField('skills');\n if (skills !== undefined) override.skills = skills;\n const tools = parseArrayField('tools');\n if (tools !== undefined) override.tools = tools;\n const extensions = parseArrayField('extensions');\n if (extensions !== undefined) override.extensions = extensions;\n const subagentOnlyExtensions = parseArrayField('subagentOnlyExtensions');\n if (subagentOnlyExtensions !== undefined) override.subagentOnlyExtensions = subagentOnlyExtensions;\n\n return Object.keys(override).length > 0 ? override : undefined;\n}\n\n/**\n * Parse a `toolBudget` override.\n *\n * `hard` is required by the contract, so an object without a usable one is\n * rejected here instead of producing a budget that never blocks.\n */\nfunction parseToolBudgetOverride(value: unknown, name: string, filePath: string): ToolBudgetConfig | false | undefined {\n if (value === false) return false;\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'toolBudget'; expected an object or false.`,\n );\n }\n\n const input = value as Record<string, unknown>;\n if (typeof input.hard !== 'number') {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.hard'; expected a number.`);\n }\n\n const budget: ToolBudgetConfig = { hard: input.hard };\n if ('soft' in input) {\n if (typeof input.soft !== 'number') {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.soft'; expected a number.`);\n }\n budget.soft = input.soft;\n }\n if ('block' in input) {\n if (input.block === '*') {\n budget.block = '*';\n } else if (Array.isArray(input.block) && input.block.every((entry) => typeof entry === 'string')) {\n budget.block = [...input.block];\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`,\n );\n }\n }\n return budget;\n}\n\n/** Read and validate one settings file's `subagents` block. */\nexport function readSubagentSettings(filePath: string | null): SubagentSettings {\n if (!filePath) return EMPTY_SUBAGENT_SETTINGS;\n const settings = readSettingsFileStrict(filePath);\n const subagentsObject = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagentsObject) return EMPTY_SUBAGENT_SETTINGS;\n\n let disableBuiltins: boolean | undefined;\n if ('disableBuiltins' in subagentsObject) {\n if (typeof subagentsObject.disableBuiltins === 'boolean') {\n disableBuiltins = subagentsObject.disableBuiltins;\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'disableBuiltins'; expected a boolean.`);\n }\n }\n\n let disableThinking: boolean | undefined;\n if ('disableThinking' in subagentsObject) {\n if (typeof subagentsObject.disableThinking === 'boolean') {\n disableThinking = subagentsObject.disableThinking;\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'disableThinking'; expected a boolean.`);\n }\n }\n\n let defaultModel: string | undefined;\n if ('defaultModel' in subagentsObject) {\n if (typeof subagentsObject.defaultModel === 'string' && subagentsObject.defaultModel.trim()) {\n defaultModel = subagentsObject.defaultModel.trim();\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultModel'; expected a non-empty string.`);\n }\n }\n\n let defaultThinking: string | undefined;\n if ('defaultThinking' in subagentsObject) {\n if (typeof subagentsObject.defaultThinking === 'string' && subagentsObject.defaultThinking.trim()) {\n defaultThinking = subagentsObject.defaultThinking.trim();\n } else {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'defaultThinking'; expected a non-empty string.`,\n );\n }\n }\n\n let defaultExtensions: string[] | undefined;\n if ('defaultExtensions' in subagentsObject) {\n const raw = subagentsObject.defaultExtensions;\n if (!Array.isArray(raw) || raw.some((item) => typeof item !== 'string' || !item.trim())) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`,\n );\n }\n defaultExtensions = raw.map((item) => String(item).trim());\n }\n\n const modelScope = parseModelScopeConfig(subagentsObject.modelScope, filePath);\n\n const overrides: Record<string, BuiltinAgentOverrideConfig> = {};\n const agentOverrides = readObjectField(subagentsObject, AGENT_OVERRIDES_KEY);\n if (agentOverrides) {\n for (const [name, value] of Object.entries(agentOverrides)) {\n const override = parseBuiltinOverrideEntry(name, value, filePath);\n if (override) overrides[name] = override;\n }\n }\n\n return { overrides, defaultModel, defaultThinking, defaultExtensions, disableBuiltins, disableThinking, modelScope };\n}\n\n// ============================================================================\n// Settings-level defaults\n// ============================================================================\n\n/** Which settings file supplies `defaultModel`, project winning over user. */\nexport function resolveSubagentDefaultModel(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentModelSourceInfo | undefined {\n if (projectSettingsPath && projectSettings.defaultModel !== undefined) {\n return {\n type: 'subagents.defaultModel',\n scope: 'project',\n path: projectSettingsPath,\n model: projectSettings.defaultModel,\n };\n }\n return userSettings.defaultModel !== undefined\n ? { type: 'subagents.defaultModel', scope: 'user', path: userSettingsPath, model: userSettings.defaultModel }\n : undefined;\n}\n\n/**\n * Fill in the default model for agents that declared none.\n *\n * `modelSource` is recorded alongside so callers can tell an inherited model\n * from a requested one, which decides how hard model-scope enforcement bites.\n */\nexport function applySubagentDefaultModel(\n agents: AgentConfig[],\n defaultModel: AgentModelSourceInfo | undefined,\n): AgentConfig[] {\n if (!defaultModel) return agents;\n return agents.map((agent) => {\n if (agent.model !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, model: defaultModel.model, modelSource: defaultModel });\n });\n}\n\n/** Fill in fallback models only when the agent declared no fallback policy. */\nexport function applySubagentDefaultFallbackModels(\n agents: AgentConfig[],\n fallbackModels: string[] | undefined,\n): AgentConfig[] {\n if (fallbackModels === undefined) return agents;\n return agents.map((agent) => {\n if (agent.fallbackModels !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, fallbackModels: [...fallbackModels] });\n });\n}\n\nexport function resolveSubagentDefaultThinking(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n projectSettingsPath: string | null,\n): string | undefined {\n if (projectSettingsPath && projectSettings.defaultThinking !== undefined) return projectSettings.defaultThinking;\n return userSettings.defaultThinking;\n}\n\nexport function applySubagentDefaultThinking(\n agents: AgentConfig[],\n defaultThinking: string | undefined,\n): AgentConfig[] {\n if (defaultThinking === undefined) return agents;\n return agents.map((agent) => {\n if (agent.thinking !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, thinking: defaultThinking });\n });\n}\n\nexport function resolveSubagentDefaultExtensions(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n projectSettingsPath: string | null,\n): string[] | undefined {\n if (projectSettingsPath && projectSettings.defaultExtensions !== undefined) return projectSettings.defaultExtensions;\n return userSettings.defaultExtensions;\n}\n\n/**\n * Fill in default extensions for agents that declared none.\n *\n * The `extensionsFromDefault` flag matters when the agent is later overridden:\n * a snapshot must not record a settings default as if the builtin had declared\n * it, or removing the override would bake the default in permanently.\n */\nexport function applySubagentDefaultExtensions(\n agents: AgentConfig[],\n defaultExtensions: string[] | undefined,\n): AgentConfig[] {\n if (defaultExtensions === undefined) return agents;\n return agents.map((agent) => {\n if (agent.extensions !== undefined) return agent;\n return inheritFrontmatterFields(agent, {\n ...agent,\n extensions: [...defaultExtensions],\n extensionsFromDefault: true,\n });\n });\n}\n\nexport function applySubagentDefaults(\n agents: AgentConfig[],\n defaultModel: AgentModelSourceInfo | undefined,\n defaultThinking: string | undefined,\n defaultExtensions: string[] | undefined,\n defaultFallbackModels?: string[],\n): AgentConfig[] {\n return applySubagentDefaultExtensions(\n applySubagentDefaultThinking(\n applySubagentDefaultFallbackModels(applySubagentDefaultModel(agents, defaultModel), defaultFallbackModels),\n defaultThinking,\n ),\n defaultExtensions,\n );\n}\n\n// ============================================================================\n// Override snapshots\n// ============================================================================\n\n/**\n * Snapshot the fields an override may replace, so it can be undone.\n *\n * `extensions` is omitted when it came from a settings default rather than the\n * agent itself; restoring it would turn a default into a permanent value.\n */\nexport function cloneOverrideBase(agent: AgentConfig): BuiltinOverrideSnapshot {\n return {\n model: agent.model,\n fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,\n thinking: agent.thinking,\n systemPromptMode: agent.systemPromptMode,\n inheritProjectContext: agent.inheritProjectContext,\n inheritSkills: agent.inheritSkills,\n defaultContext: agent.defaultContext,\n disabled: agent.disabled,\n systemPrompt: agent.systemPrompt,\n skills: agent.skills ? [...agent.skills] : undefined,\n skillPath: agent.skillPath ? [...agent.skillPath] : undefined,\n tools: agent.tools ? [...agent.tools] : undefined,\n mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined,\n extensions: agent.extensionsFromDefault ? undefined : agent.extensions ? [...agent.extensions] : undefined,\n subagentOnlyExtensions: agent.subagentOnlyExtensions ? [...agent.subagentOnlyExtensions] : undefined,\n completionGuard: agent.completionGuard,\n toolBudget: agent.toolBudget,\n };\n}\n\n/**\n * Deep-copy an override before it is written to a settings file.\n *\n * Only keys the caller actually set are copied, because an explicit `undefined`\n * would serialise the key away and read back as \"not overridden\".\n */\nexport function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {\n return {\n ...(override.model !== undefined ? { model: override.model } : {}),\n ...(override.fallbackModels !== undefined\n ? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }\n : {}),\n ...(override.thinking !== undefined ? { thinking: override.thinking } : {}),\n ...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),\n ...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),\n ...(override.inheritSkills !== undefined ? { inheritSkills: override.inheritSkills } : {}),\n ...(override.defaultContext !== undefined ? { defaultContext: override.defaultContext } : {}),\n ...(override.disabled !== undefined ? { disabled: override.disabled } : {}),\n ...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),\n ...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),\n ...(override.tools !== undefined ? { tools: override.tools === false ? false : [...override.tools] } : {}),\n ...(override.extensions !== undefined\n ? { extensions: override.extensions === false ? false : [...override.extensions] }\n : {}),\n ...(override.subagentOnlyExtensions !== undefined\n ? {\n subagentOnlyExtensions:\n override.subagentOnlyExtensions === false ? false : [...override.subagentOnlyExtensions],\n }\n : {}),\n ...(override.completionGuard !== undefined ? { completionGuard: override.completionGuard } : {}),\n ...(override.toolBudget !== undefined\n ? {\n toolBudget:\n override.toolBudget === false\n ? false\n : {\n ...override.toolBudget,\n ...(Array.isArray(override.toolBudget.block) ? { block: [...override.toolBudget.block] } : {}),\n },\n }\n : {}),\n };\n}\n\n// ============================================================================\n// Applying overrides\n// ============================================================================\n\n/**\n * Replace a builtin's values with an override's.\n *\n * `false` clears a field rather than setting it to false; that distinction is\n * the whole reason the stored shape differs from the snapshot shape.\n */\nexport function applyBuiltinOverride(\n agent: AgentConfig,\n override: BuiltinAgentOverrideConfig,\n meta: OverrideScopeMeta,\n): AgentConfig {\n const next: AgentConfig = { ...agent, override: { ...meta, base: cloneOverrideBase(agent) } };\n\n if (override.model !== undefined) next.model = override.model === false ? undefined : override.model;\n if (override.fallbackModels !== undefined) {\n next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];\n }\n if (override.thinking !== undefined) next.thinking = override.thinking === false ? undefined : override.thinking;\n if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;\n if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;\n if (override.inheritSkills !== undefined) next.inheritSkills = override.inheritSkills;\n if (override.defaultContext !== undefined) {\n next.defaultContext = override.defaultContext === false ? undefined : override.defaultContext;\n }\n if (override.disabled !== undefined) next.disabled = override.disabled;\n if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;\n if (override.skills !== undefined) next.skills = override.skills === false ? undefined : [...override.skills];\n if (override.tools !== undefined) {\n const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);\n next.tools = tools;\n next.mcpDirectTools = mcpDirectTools;\n }\n if (override.extensions !== undefined) {\n next.extensions = override.extensions === false ? undefined : [...override.extensions];\n }\n if (override.subagentOnlyExtensions !== undefined) {\n next.subagentOnlyExtensions =\n override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions];\n }\n if (override.completionGuard !== undefined) next.completionGuard = override.completionGuard;\n if (override.toolBudget !== undefined) {\n next.toolBudget = override.toolBudget === false ? undefined : override.toolBudget;\n }\n\n return next;\n}\n\n/**\n * Strip an agent's thinking level for the bulk `disableThinking` switch.\n *\n * An existing `override` is kept rather than replaced, so a per-agent override\n * remains the recorded reason the agent differs from its builtin definition.\n */\nexport function clearBuiltinThinking(agent: AgentConfig, meta: OverrideScopeMeta): AgentConfig {\n if (agent.thinking === undefined) return agent;\n return { ...agent, thinking: undefined, override: agent.override ?? { ...meta, base: cloneOverrideBase(agent) } };\n}\n\n/**\n * Apply user and project settings to the builtin agents.\n *\n * Precedence per agent: a project override, then the project bulk disable, then\n * a user override, then the user bulk disable. A project file that sets\n * `disableBuiltins` at all (including to false) shadows the user's switch, so a\n * project can re-enable builtins its user settings turned off.\n */\nexport function applyBuiltinOverrides(\n builtinAgents: AgentConfig[],\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentConfig[] {\n const projectBulkDisabled = projectSettings.disableBuiltins === true && projectSettingsPath !== null;\n const userBulkDisabled = projectSettings.disableBuiltins === undefined && userSettings.disableBuiltins === true;\n const projectThinkingConfigured = projectSettings.disableThinking !== undefined && projectSettingsPath !== null;\n const disableThinking = projectThinkingConfigured\n ? projectSettings.disableThinking === true\n : userSettings.disableThinking === true;\n const disableThinkingMeta: OverrideScopeMeta =\n projectThinkingConfigured && projectSettingsPath\n ? { scope: 'project', path: projectSettingsPath }\n : { scope: 'user', path: userSettingsPath };\n\n // A per-agent thinking override is more specific than the bulk switch, so it\n // survives it; everything else is cleared.\n const applyGlobalThinking = (agent: AgentConfig, hasExplicitThinkingOverride: boolean): AgentConfig => {\n if (!disableThinking || hasExplicitThinkingOverride) return agent;\n return clearBuiltinThinking(agent, disableThinkingMeta);\n };\n\n return builtinAgents.map((agent) => {\n const projectOverride = projectSettings.overrides[agent.name];\n if (projectOverride && projectSettingsPath) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, projectOverride, { scope: 'project', path: projectSettingsPath }),\n projectOverride.thinking !== undefined,\n );\n }\n\n if (projectBulkDisabled && projectSettingsPath) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, { disabled: true }, { scope: 'project', path: projectSettingsPath }),\n false,\n );\n }\n\n const userOverride = userSettings.overrides[agent.name];\n if (userOverride) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, userOverride, { scope: 'user', path: userSettingsPath }),\n !projectThinkingConfigured && userOverride.thinking !== undefined,\n );\n }\n\n if (userBulkDisabled) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, { disabled: true }, { scope: 'user', path: userSettingsPath }),\n false,\n );\n }\n\n return applyGlobalThinking(agent, false);\n });\n}\n\n/**\n * Apply an override to a custom agent, filling only unset fields.\n *\n * A custom agent has a file the user could have edited, so a settings value\n * must never contradict its frontmatter. Nothing is copied and no `override`\n * is recorded unless at least one field was actually filled, which keeps agents\n * that declared everything from being reported as overridden.\n */\nexport function applyCustomAgentOverride(\n agent: AgentConfig,\n override: BuiltinAgentOverrideConfig,\n meta: OverrideScopeMeta,\n): AgentConfig {\n let next: AgentConfig | undefined;\n let anyFilled = false;\n\n const mutable = (): AgentConfig => {\n next ??= { ...agent };\n return next;\n };\n\n const fill = <K extends keyof AgentConfig>(field: K, frontmatterFields: string[], value: AgentConfig[K]): void => {\n if (agentHasFrontmatterField(agent, ...frontmatterFields)) return;\n mutable()[field] = value;\n anyFilled = true;\n };\n\n if (override.model !== undefined) {\n fill('model', ['model'], override.model === false ? undefined : override.model);\n }\n if (override.fallbackModels !== undefined) {\n fill(\n 'fallbackModels',\n ['fallbackModels'],\n override.fallbackModels === false ? undefined : [...override.fallbackModels],\n );\n }\n if (override.thinking !== undefined) {\n fill('thinking', ['thinking'], override.thinking === false ? undefined : override.thinking);\n }\n if (override.systemPromptMode !== undefined) {\n fill('systemPromptMode', ['systemPromptMode'], override.systemPromptMode);\n }\n if (override.inheritProjectContext !== undefined) {\n fill('inheritProjectContext', ['inheritProjectContext'], override.inheritProjectContext);\n }\n if (override.inheritSkills !== undefined) {\n fill('inheritSkills', ['inheritSkills'], override.inheritSkills);\n }\n if (override.defaultContext !== undefined) {\n fill('defaultContext', ['defaultContext'], override.defaultContext === false ? undefined : override.defaultContext);\n }\n if (override.disabled !== undefined && agent.disabled === undefined) {\n mutable().disabled = override.disabled;\n anyFilled = true;\n }\n if (override.skills !== undefined) {\n fill('skills', ['skill', 'skills'], override.skills === false ? undefined : [...override.skills]);\n }\n if (override.tools !== undefined && !agentHasFrontmatterField(agent, 'tools')) {\n const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);\n const target = mutable();\n target.tools = tools;\n target.mcpDirectTools = mcpDirectTools;\n anyFilled = true;\n }\n if (override.extensions !== undefined) {\n fill('extensions', ['extensions'], override.extensions === false ? undefined : [...override.extensions]);\n }\n if (override.subagentOnlyExtensions !== undefined) {\n fill(\n 'subagentOnlyExtensions',\n ['subagentOnlyExtensions'],\n override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions],\n );\n }\n if (override.completionGuard !== undefined) {\n fill('completionGuard', ['completionGuard'], override.completionGuard);\n }\n if (override.toolBudget !== undefined) {\n fill('toolBudget', ['toolBudget'], override.toolBudget === false ? undefined : override.toolBudget);\n }\n\n if (!anyFilled || !next) return agent;\n next.override = { ...meta, base: cloneOverrideBase(agent) };\n return inheritFrontmatterFields(agent, next);\n}\n\nexport function applyCustomAgentOverrides(\n agents: AgentConfig[],\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentConfig[] {\n return agents.map((agent) => {\n const projectOverride = projectSettings.overrides[agent.name];\n if (projectOverride && projectSettingsPath) {\n return applyCustomAgentOverride(agent, projectOverride, { scope: 'project', path: projectSettingsPath });\n }\n\n const userOverride = userSettings.overrides[agent.name];\n if (userOverride) {\n return applyCustomAgentOverride(agent, userOverride, { scope: 'user', path: userSettingsPath });\n }\n\n return agent;\n });\n}\n\n/**\n * Diff an edited agent against its pre-override snapshot.\n *\n * Only changed fields are written, so a settings file records the user's edits\n * rather than a full copy of the builtin that would drift as the builtin does.\n * Returns undefined when nothing changed, which callers treat as \"remove the\n * override\" rather than \"write an empty one\".\n */\nexport function buildBuiltinOverrideConfig(\n base: BuiltinOverrideSnapshot,\n draft: Pick<\n AgentConfig,\n | 'model'\n | 'fallbackModels'\n | 'thinking'\n | 'systemPromptMode'\n | 'inheritProjectContext'\n | 'inheritSkills'\n | 'defaultContext'\n | 'disabled'\n | 'systemPrompt'\n | 'skills'\n | 'tools'\n | 'mcpDirectTools'\n | 'extensions'\n | 'subagentOnlyExtensions'\n | 'completionGuard'\n | 'toolBudget'\n >,\n): BuiltinAgentOverrideConfig | undefined {\n const override: BuiltinAgentOverrideConfig = {};\n\n if (draft.model !== base.model) override.model = draft.model ?? false;\n if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) {\n override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;\n }\n if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;\n if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;\n if (draft.inheritProjectContext !== base.inheritProjectContext) {\n override.inheritProjectContext = draft.inheritProjectContext;\n }\n if (draft.inheritSkills !== base.inheritSkills) override.inheritSkills = draft.inheritSkills;\n if (draft.defaultContext !== base.defaultContext) override.defaultContext = draft.defaultContext ?? false;\n if (draft.disabled !== base.disabled) override.disabled = draft.disabled ?? false;\n if (draft.systemPrompt !== base.systemPrompt) override.systemPrompt = draft.systemPrompt;\n if (!arraysEqual(draft.skills, base.skills)) override.skills = draft.skills ? [...draft.skills] : false;\n\n const baseTools = joinToolList(base);\n const draftTools = joinToolList(draft);\n if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;\n if (!arraysEqual(draft.extensions, base.extensions)) {\n override.extensions = draft.extensions ? [...draft.extensions] : false;\n }\n if (!arraysEqual(draft.subagentOnlyExtensions, base.subagentOnlyExtensions)) {\n override.subagentOnlyExtensions = draft.subagentOnlyExtensions ? [...draft.subagentOnlyExtensions] : false;\n }\n // Compared as booleans because an unset guard and an enabled one behave the\n // same, so only a switch to or from explicitly disabled is a real change.\n if ((draft.completionGuard !== false) !== (base.completionGuard !== false)) {\n override.completionGuard = draft.completionGuard !== false;\n }\n if (JSON.stringify(draft.toolBudget) !== JSON.stringify(base.toolBudget)) {\n override.toolBudget = draft.toolBudget ?? false;\n }\n\n return Object.keys(override).length > 0 ? override : undefined;\n}\n\n// ============================================================================\n// Persisting overrides\n// ============================================================================\n\nconst NO_PROJECT_ROOT_MESSAGE = 'Project override is not available here. No project config root was found.';\n\n/** Resolve the settings file for a scope, or throw when there is no project. */\nfunction requireSettingsPath(cwd: string, scope: 'user' | 'project'): string {\n const filePath = scope === 'project' ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();\n if (!filePath) throw new Error(NO_PROJECT_ROOT_MESSAGE);\n return filePath;\n}\n\n/** Write an override, replacing any existing entry for the agent wholesale. */\nexport function saveBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n override: BuiltinAgentOverrideConfig,\n): string {\n const filePath = requireSettingsPath(cwd, scope);\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = cloneObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n const agentOverrides = cloneObjectField(subagents, AGENT_OVERRIDES_KEY);\n\n agentOverrides[name] = cloneOverrideValue(override);\n subagents[AGENT_OVERRIDES_KEY] = agentOverrides;\n settings[SUBAGENTS_SETTINGS_KEY] = subagents;\n writeSettingsFile(filePath, settings);\n return filePath;\n}\n\n/**\n * Drop an agent's whole override entry.\n *\n * Emptied containers are deleted rather than left behind, so removing the last\n * override leaves the settings file as it was before any were added.\n */\nexport function removeBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n): { path: string; removed: boolean } {\n const filePath = requireSettingsPath(cwd, scope);\n if (!fs.existsSync(filePath)) return { path: filePath, removed: false };\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagents) return { path: filePath, removed: false };\n const agentOverrides = readObjectField(subagents, AGENT_OVERRIDES_KEY);\n if (!agentOverrides) return { path: filePath, removed: false };\n if (!Object.hasOwn(agentOverrides, name)) return { path: filePath, removed: false };\n\n const nextSubagents = { ...subagents };\n const nextOverrides = { ...agentOverrides };\n delete nextOverrides[name];\n if (Object.keys(nextOverrides).length > 0) nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n else delete nextSubagents[AGENT_OVERRIDES_KEY];\n\n if (Object.keys(nextSubagents).length > 0) settings[SUBAGENTS_SETTINGS_KEY] = nextSubagents;\n else delete settings[SUBAGENTS_SETTINGS_KEY];\n\n writeSettingsFile(filePath, settings);\n return { path: filePath, removed: true };\n}\n\n/** Merge fields into an agent's existing override, leaving the rest in place. */\nexport function mergeBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n fields: BuiltinAgentOverrideConfig,\n): string {\n const filePath = requireSettingsPath(cwd, scope);\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = cloneObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n const agentOverrides = cloneObjectField(subagents, AGENT_OVERRIDES_KEY);\n\n const existing = readObjectField(agentOverrides, name) ?? {};\n agentOverrides[name] = { ...existing, ...cloneOverrideValue(fields) };\n subagents[AGENT_OVERRIDES_KEY] = agentOverrides;\n settings[SUBAGENTS_SETTINGS_KEY] = subagents;\n writeSettingsFile(filePath, settings);\n return filePath;\n}\n\n/** Remove named fields from an agent's override, dropping it once empty. */\nexport function removeBuiltinAgentOverrideFields(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n fields: string[],\n): { path: string; removed: boolean } {\n const filePath = requireSettingsPath(cwd, scope);\n if (!fs.existsSync(filePath)) return { path: filePath, removed: false };\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagents) return { path: filePath, removed: false };\n const agentOverrides = readObjectField(subagents, AGENT_OVERRIDES_KEY);\n if (!agentOverrides) return { path: filePath, removed: false };\n const entry = readObjectField(agentOverrides, name);\n if (!entry) return { path: filePath, removed: false };\n\n const nextEntry: Record<string, unknown> = { ...entry };\n let removed = false;\n for (const field of fields) {\n if (Object.hasOwn(nextEntry, field)) {\n delete nextEntry[field];\n removed = true;\n }\n }\n if (!removed) return { path: filePath, removed: false };\n\n const nextSubagents = { ...subagents };\n const nextOverrides = { ...agentOverrides };\n if (Object.keys(nextEntry).length > 0) {\n nextOverrides[name] = nextEntry;\n nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n } else {\n delete nextOverrides[name];\n if (Object.keys(nextOverrides).length > 0) nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n else delete nextSubagents[AGENT_OVERRIDES_KEY];\n }\n if (Object.keys(nextSubagents).length > 0) settings[SUBAGENTS_SETTINGS_KEY] = nextSubagents;\n else delete settings[SUBAGENTS_SETTINGS_KEY];\n\n writeSettingsFile(filePath, settings);\n return { path: filePath, removed: true };\n}\n"],"mappings":"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"}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.cjs","names":["fs","path","getProjectConfigDir","os","LruCache","getAgentDir"],"sources":["../../src/agents/skills.ts"],"sourcesContent":["/**\n * Resolving skill files by name and rendering them into a child system prompt.\n *\n * A skill is a markdown file, either `<dir>/SKILL.md` or a bare `<name>.md`,\n * found by sweeping a fixed list of search roots. The same name can appear in\n * several roots, so the sweep records where each hit came from and the highest\n * priority source wins.\n *\n * WHY THIS IS A SERVICE AND NOT A MODULE OF FUNCTIONS:\n * Resolution walks every skill root and reads the frontmatter of every markdown\n * file it finds. Prompt assembly asks for the catalogue on each launch and\n * completion asks on each keystroke, so the answers have to be cached, and a\n * cache needs an owner that can be invalidated and swapped in a test.\n *\n * DESIGN PATTERNS:\n * - Two caches with different lifetimes. Listing answers \"does this name exist\n * and where\", reading answers \"what does it say\"; the first goes stale on any\n * directory change, the second only when one file's mtime moves\n * - Both are bounded LRUs. The predecessor held the listing in a single mutable\n * slot keyed by cwd, which thrashed the moment two working directories were in\n * play, and evicted parsed files in insertion order regardless of use\n * - Every filesystem call is best-effort. A skill root that cannot be read is\n * skipped, never fatal: a missing skill surfaces to the caller as `missing`\n * - Symlinked and repeated directories are visited at most once per priority, so\n * a cycle in the skill tree terminates\n *\n * AVOID:\n * - Walking the skill roots on a completion path without going through the cache\n * - Deriving a config directory here instead of asking `configDir.ts`\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { injectable } from 'inversify';\n\nimport { EXTRA_SKILL_DIRS_ENV } from '../env.ts';\nimport { getAgentDir, getProjectConfigDir } from '../shared/configDir.ts';\nimport { LruCache } from '../shared/lruCache.ts';\n\n/**\n * Where a skill file was found.\n *\n * Deliberately not `AgentSource`: a skill can also come from a settings list or\n * from an extension directory, neither of which can hold an agent definition.\n */\nexport type SkillSource =\n | 'project'\n | 'user'\n | 'project-settings'\n | 'user-settings'\n | 'extension'\n | 'builtin'\n | 'unknown';\n\nexport interface ResolvedSkill {\n name: string;\n path: string;\n content: string;\n description?: string;\n source: SkillSource;\n}\n\nexport interface SkillLocation {\n path: string;\n source: SkillSource;\n}\n\nexport interface SkillResolution {\n resolved: ResolvedSkill[];\n missing: string[];\n}\n\nexport interface DiscoveredSkill {\n name: string;\n source: SkillSource;\n description?: string;\n}\n\n/**\n * Discovery and reading of skill files.\n *\n * Behind an interface for the same reason agent discovery is: the results are\n * cached and invalidated, and callers on the completion path must be able to ask\n * repeatedly without paying for a directory walk each time.\n */\nexport interface ISkillDiscoveryService {\n /** Locate one skill by name, or undefined when no root holds it. */\n resolveSkillPath(skillName: string, cwd: string): SkillLocation | undefined;\n /** Read the named skills, reporting any that could not be resolved. */\n resolveSkills(skillNames: string[], cwd: string, localSkillPaths?: string[], localBaseDir?: string): SkillResolution;\n /** Resolve against `primaryCwd`, retrying whatever is missing in `fallbackCwd`. */\n resolveSkillsWithFallback(\n skillNames: string[],\n primaryCwd: string,\n fallbackCwd?: string,\n localSkillPaths?: string[],\n localBaseDir?: string,\n ): SkillResolution;\n /** Every skill visible from `cwd`, sorted by name. */\n discoverAvailableSkills(cwd: string): DiscoveredSkill[];\n /** Drop cached results. Call after writing or removing a skill file. */\n invalidate(): void;\n}\n\n/** One entry in a listing sweep: located, but not yet read. */\ninterface CachedSkillEntry {\n name: string;\n filePath: string;\n source: SkillSource;\n description?: string;\n /** Discovery order, used to break ties between equal-priority sources. */\n order: number;\n}\n\ninterface SkillFileCacheEntry {\n mtimeMs: number;\n skill: ResolvedSkill;\n}\n\ninterface SkillListingCacheEntry {\n skills: CachedSkillEntry[];\n expiresAt: number;\n}\n\ninterface SkillSearchPath {\n path: string;\n source: SkillSource;\n}\n\n/** Roughly the number of distinct skills one session touches. */\nconst SKILL_FILE_CACHE_CAPACITY = 50;\n/** One entry per (cwd, agentDir) pair; a session rarely spans more than a few. */\nconst SKILL_LISTING_CACHE_CAPACITY = 8;\n/**\n * How long a listing stays valid.\n *\n * Short enough that a skill added mid-session appears without a restart, long\n * enough that a burst of completions does not re-walk the roots per keystroke.\n */\nconst SKILL_LISTING_TTL_MS = 5_000;\n\n/** Cache keys join two paths, so use a separator that cannot occur in either. */\nconst CACHE_KEY_SEPARATOR = '\\0';\n\n/**\n * The orchestration skill, excluded from every listing and injection.\n *\n * A subagent already has the orchestration tools wired in, so handing it the\n * skill that explains how to reach for them wastes context and invites a child\n * to fan out again. The name is the published skill's, not this package's.\n */\nconst SUBAGENT_ORCHESTRATION_SKILL = 'pi-subagents';\n\nconst SKILL_FILE_NAME = 'SKILL.md';\nconst MARKDOWN_EXTENSION = '.md';\nconst FRONTMATTER_FENCE = '---';\nconst NODE_MODULES_DIR = 'node_modules';\nconst HOME_ALIAS_PREFIX = '~/';\n\n/** Sources a listing can report, most authoritative first. */\nconst SOURCE_PRIORITY: Record<SkillSource, number> = {\n project: 700,\n 'project-settings': 650,\n user: 300,\n 'user-settings': 250,\n extension: 150,\n builtin: 100,\n unknown: 0,\n};\n\nfunction priorityOf(source: SkillSource): number {\n return SOURCE_PRIORITY[source] ?? SOURCE_PRIORITY.unknown;\n}\n\n/** Index of the first character after a closing frontmatter fence, or -1. */\nfunction findFrontmatterEnd(normalized: string): number {\n if (!normalized.startsWith(FRONTMATTER_FENCE)) return -1;\n return normalized.indexOf(`\\n${FRONTMATTER_FENCE}`, FRONTMATTER_FENCE.length);\n}\n\nexport function stripSkillFrontmatter(content: string): string {\n const normalized = content.replace(/\\r\\n/g, '\\n');\n const endIndex = findFrontmatterEnd(normalized);\n if (endIndex === -1) return normalized;\n return normalized.slice(endIndex + 1 + FRONTMATTER_FENCE.length).trim();\n}\n\nexport function parseSkillDescription(content: string): string | undefined {\n const normalized = content.replace(/\\r\\n/g, '\\n');\n const endIndex = findFrontmatterEnd(normalized);\n if (endIndex === -1) return undefined;\n\n const frontmatter = normalized.slice(FRONTMATTER_FENCE.length, endIndex).trim();\n const match = frontmatter.match(/^description:\\s*(.+)$/m);\n return match?.[1]?.trim().replace(/^['\"]|['\"]$/g, '');\n}\n\nfunction maybeReadSkillDescription(filePath: string): string | undefined {\n try {\n return parseSkillDescription(fs.readFileSync(filePath, 'utf-8'));\n } catch {\n // The description is optional metadata; an unreadable file still lists by name.\n return undefined;\n }\n}\n\nfunction isWithinPath(filePath: string, dir: string): boolean {\n const relative = path.relative(dir, filePath);\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\n/** Read a JSON file that is allowed to be absent, but not allowed to be corrupt. */\nfunction readOptionalJsonFile(filePath: string, label: string): unknown {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n return parsed;\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code?: unknown }).code : undefined;\n if (code === 'ENOENT') return null;\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to read ${label} '${filePath}': ${message}`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n}\n\n/** Extract the string entries of a settings file's `skills` array. */\nfunction readSettingsSkillList(settings: unknown): string[] {\n if (typeof settings !== 'object' || settings === null || Array.isArray(settings)) return [];\n const skills = (settings as { skills?: unknown }).skills;\n if (!Array.isArray(skills)) return [];\n return skills.filter((entry): entry is string => typeof entry === 'string');\n}\n\nfunction collectSettingsSkillPaths(cwd: string, agentDir: string): SkillSearchPath[] {\n const results: SkillSearchPath[] = [];\n const projectConfigDir = getProjectConfigDir(cwd);\n const settingsFiles = [\n { file: path.join(projectConfigDir, 'settings.json'), base: projectConfigDir, source: 'project-settings' as const },\n { file: path.join(agentDir, 'settings.json'), base: agentDir, source: 'user-settings' as const },\n ];\n\n for (const { file, base, source } of settingsFiles) {\n for (const entry of readSettingsSkillList(readOptionalJsonFile(file, 'skills settings file'))) {\n let resolved = entry;\n if (resolved.startsWith(HOME_ALIAS_PREFIX)) {\n resolved = path.join(os.homedir(), resolved.slice(HOME_ALIAS_PREFIX.length));\n } else if (!path.isAbsolute(resolved)) {\n resolved = path.resolve(base, resolved);\n }\n results.push({ path: resolved, source });\n }\n }\n\n return results;\n}\n\nfunction extraSkillPaths(): SkillSearchPath[] {\n return (process.env[EXTRA_SKILL_DIRS_ENV] ?? '')\n .split(path.delimiter)\n .map((directory) => directory.trim())\n .filter(Boolean)\n .map((directory) => ({ path: directory, source: 'extension' as const }));\n}\n\nfunction buildSkillPaths(cwd: string, agentDir: string): SkillSearchPath[] {\n const projectConfigDir = getProjectConfigDir(cwd);\n return [\n ...extraSkillPaths(),\n { path: path.join(projectConfigDir, 'skills'), source: 'project' },\n { path: path.join(cwd, '.agents', 'skills'), source: 'project' },\n { path: path.join(agentDir, 'skills'), source: 'user' },\n { path: path.join(os.homedir(), '.agents', 'skills'), source: 'user' },\n ...collectSettingsSkillPaths(cwd, agentDir),\n ];\n}\n\n/** Classify a hit that arrived without a source hint, by which root contains it. */\nfunction inferSkillSource(filePath: string, cwd: string, agentDir: string, sourceHint?: SkillSource): SkillSource {\n if (sourceHint) return sourceHint;\n\n const projectConfigRoot = path.resolve(getProjectConfigDir(cwd));\n const projectSkillsRoot = path.resolve(projectConfigRoot, 'skills');\n const projectAgentsRoot = path.resolve(cwd, '.agents');\n const userSkillsRoot = path.resolve(agentDir, 'skills');\n const userAgentRoot = path.resolve(agentDir);\n const userAgentsRoot = path.resolve(os.homedir(), '.agents');\n\n if (isWithinPath(filePath, projectSkillsRoot) || isWithinPath(filePath, projectAgentsRoot)) return 'project';\n if (isWithinPath(filePath, projectConfigRoot)) return 'project-settings';\n\n if (isWithinPath(filePath, userSkillsRoot) || isWithinPath(filePath, userAgentsRoot)) return 'user';\n if (isWithinPath(filePath, userAgentRoot)) return 'user-settings';\n\n return 'unknown';\n}\n\nfunction chooseHigherPrioritySkill(\n existing: CachedSkillEntry | undefined,\n candidate: CachedSkillEntry,\n): CachedSkillEntry {\n if (!existing) return candidate;\n const existingPriority = priorityOf(existing.source);\n const candidatePriority = priorityOf(candidate.source);\n if (candidatePriority > existingPriority) return candidate;\n if (candidatePriority < existingPriority) return existing;\n return candidate.order < existing.order ? candidate : existing;\n}\n\nfunction isMarkdownFile(fileName: string): boolean {\n return fileName.toLowerCase().endsWith(MARKDOWN_EXTENSION);\n}\n\n/** Directory names never worth descending into while hunting for skills. */\nfunction shouldSkipDirectory(name: string): boolean {\n return name.startsWith('.') || name === NODE_MODULES_DIR;\n}\n\nfunction statOrUndefined(target: string): fs.Stats | undefined {\n try {\n return fs.statSync(target);\n } catch {\n // A dangling symlink or a path removed mid-sweep is simply not a skill.\n return undefined;\n }\n}\n\nfunction readDirOrEmpty(dirPath: string): fs.Dirent[] {\n try {\n return fs.readdirSync(dirPath, { withFileTypes: true });\n } catch {\n // An unreadable root must not abort the sweep over the remaining roots.\n return [];\n }\n}\n\n/**\n * Sweep the given roots for skill files.\n *\n * Pure with respect to the caches, which is what lets it serve both the cached\n * catalogue and an agent's own uncached `skillPath` list.\n */\nfunction collectFilesystemSkills(cwd: string, agentDir: string, skillPaths: SkillSearchPath[]): CachedSkillEntry[] {\n const entries: CachedSkillEntry[] = [];\n const seen = new Map<string, number>();\n const visitedDirectories = new Map<string, number>();\n let order = 0;\n\n const pushEntry = (name: string, filePath: string, sourceHint?: SkillSource) => {\n const resolvedFile = path.resolve(filePath);\n if (!fs.existsSync(resolvedFile)) return;\n const source = inferSkillSource(resolvedFile, cwd, agentDir, sourceHint);\n const existingIndex = seen.get(resolvedFile);\n if (existingIndex !== undefined) {\n const existing = entries[existingIndex];\n if (existing && priorityOf(source) > priorityOf(existing.source)) {\n entries[existingIndex] = {\n ...existing,\n name,\n source,\n description: maybeReadSkillDescription(resolvedFile),\n };\n }\n return;\n }\n seen.set(resolvedFile, entries.length);\n entries.push({\n name,\n filePath: resolvedFile,\n source,\n description: maybeReadSkillDescription(resolvedFile),\n order: order++,\n });\n };\n\n /** False when this directory was already walked at an equal or better priority. */\n const markDirectoryVisited = (dirPath: string, sourceHint?: SkillSource): boolean => {\n let resolvedDir: string;\n try {\n resolvedDir = fs.realpathSync(dirPath);\n } catch {\n // Symlink resolution is only needed to detect cycles; the lexical path is\n // a sound fallback that at worst walks one directory twice.\n resolvedDir = path.resolve(dirPath);\n }\n const priority = sourceHint ? priorityOf(sourceHint) : SOURCE_PRIORITY.unknown;\n const previousPriority = visitedDirectories.get(resolvedDir);\n if (previousPriority !== undefined && previousPriority >= priority) return false;\n visitedDirectories.set(resolvedDir, priority);\n return true;\n };\n\n const walkSkillDirectories = (dirPath: string, sourceHint?: SkillSource) => {\n if (!markDirectoryVisited(dirPath, sourceHint)) return;\n\n // A directory holding SKILL.md is itself the skill; do not descend further.\n const skillFile = path.join(dirPath, SKILL_FILE_NAME);\n if (fs.existsSync(skillFile)) {\n pushEntry(path.basename(dirPath), skillFile, sourceHint);\n return;\n }\n\n for (const entry of readDirOrEmpty(dirPath)) {\n if (shouldSkipDirectory(entry.name)) continue;\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n\n const entryPath = path.join(dirPath, entry.name);\n if (statOrUndefined(entryPath)?.isDirectory()) walkSkillDirectories(entryPath, sourceHint);\n }\n };\n\n for (const skillPath of skillPaths) {\n if (!fs.existsSync(skillPath.path)) continue;\n\n const stat = statOrUndefined(skillPath.path);\n if (!stat) continue;\n\n // A root may point straight at a markdown file rather than a directory.\n if (stat.isFile()) {\n const fileName = path.basename(skillPath.path);\n if (!isMarkdownFile(fileName)) continue;\n const skillName =\n fileName.toLowerCase() === SKILL_FILE_NAME.toLowerCase()\n ? path.basename(path.dirname(skillPath.path))\n : path.basename(fileName, path.extname(fileName));\n pushEntry(skillName, skillPath.path, skillPath.source);\n continue;\n }\n\n if (!stat.isDirectory()) continue;\n\n const rootSkillFile = path.join(skillPath.path, SKILL_FILE_NAME);\n if (fs.existsSync(rootSkillFile)) {\n pushEntry(path.basename(skillPath.path), rootSkillFile, skillPath.source);\n continue;\n }\n\n markDirectoryVisited(skillPath.path, skillPath.source);\n\n for (const child of readDirOrEmpty(skillPath.path)) {\n if (child.name.startsWith('.')) continue;\n const childPath = path.join(skillPath.path, child.name);\n if (child.isDirectory() || child.isSymbolicLink()) {\n if (shouldSkipDirectory(child.name)) continue;\n if (statOrUndefined(childPath)?.isDirectory()) walkSkillDirectories(childPath, skillPath.source);\n continue;\n }\n if (child.isFile() && isMarkdownFile(child.name)) {\n pushEntry(path.basename(child.name, path.extname(child.name)), childPath, skillPath.source);\n }\n }\n }\n\n return entries;\n}\n\n@injectable()\nexport class SkillDiscoveryService implements ISkillDiscoveryService {\n /** Parsed skill bodies, keyed by resolved file path and validated by mtime. */\n private readonly fileCache = new LruCache<string, SkillFileCacheEntry>(SKILL_FILE_CACHE_CAPACITY);\n /** Located-but-unread catalogues, keyed by the (cwd, agentDir) pair. */\n private readonly listingCache = new LruCache<string, SkillListingCacheEntry>(SKILL_LISTING_CACHE_CAPACITY);\n\n /**\n * Seams for tests, as protected members rather than constructor arguments.\n *\n * The container resolves this service by type, and inversify cannot supply a\n * primitive like a TTL, so configuration cannot arrive through the\n * constructor. The repo convention is also not to abstract the clock, so a\n * test subclass overrides these instead.\n */\n protected readonly listingTtlMs: number = SKILL_LISTING_TTL_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n resolveSkillPath(skillName: string, cwd: string): SkillLocation | undefined {\n const skill = this.listSkills(cwd).find((candidate) => candidate.name === skillName);\n if (!skill) return undefined;\n return { path: skill.filePath, source: skill.source };\n }\n\n resolveSkills(skillNames: string[], cwd: string, localSkillPaths?: string[], localBaseDir?: string): SkillResolution {\n const resolved: ResolvedSkill[] = [];\n const missing: string[] = [];\n const localByName = localSkillPaths?.length\n ? collectLocalSkills(cwd, localSkillPaths, localBaseDir)\n : new Map<string, CachedSkillEntry>();\n\n for (const name of skillNames) {\n const trimmed = name.trim();\n if (!trimmed) continue;\n if (trimmed === SUBAGENT_ORCHESTRATION_SKILL) {\n missing.push(trimmed);\n continue;\n }\n\n // An agent's own skillPath outranks anything found in the shared roots.\n const local = localByName.get(trimmed);\n let skill = local ? this.readSkill(trimmed, local.filePath, local.source) : undefined;\n if (!skill) {\n const location = this.resolveSkillPath(trimmed, cwd);\n if (location) skill = this.readSkill(trimmed, location.path, location.source);\n }\n if (skill) resolved.push(skill);\n else missing.push(trimmed);\n }\n\n return { resolved, missing };\n }\n\n /**\n * Resolve against `primaryCwd`, then retry whatever is still missing elsewhere.\n *\n * A child launched into a worktree still needs the skills configured in the\n * repository it was launched from.\n */\n resolveSkillsWithFallback(\n skillNames: string[],\n primaryCwd: string,\n fallbackCwd?: string,\n localSkillPaths?: string[],\n localBaseDir?: string,\n ): SkillResolution {\n const primary = this.resolveSkills(skillNames, primaryCwd, localSkillPaths, localBaseDir);\n if (!fallbackCwd || primary.missing.length === 0) return primary;\n if (path.resolve(primaryCwd) === path.resolve(fallbackCwd)) return primary;\n\n const fallback = this.resolveSkills(primary.missing, fallbackCwd);\n return {\n resolved: [...primary.resolved, ...fallback.resolved],\n missing: fallback.missing,\n };\n }\n\n discoverAvailableSkills(cwd: string): DiscoveredSkill[] {\n return this.listSkills(cwd)\n .filter((skill) => skill.name !== SUBAGENT_ORCHESTRATION_SKILL)\n .map((skill) => ({\n name: skill.name,\n source: skill.source,\n description: skill.description,\n }))\n .sort((a, b) => a.name.localeCompare(b.name));\n }\n\n invalidate(): void {\n this.fileCache.clear();\n this.listingCache.clear();\n }\n\n /** The catalogue visible from `cwd`, sweeping the roots only when stale. */\n private listSkills(cwd: string): CachedSkillEntry[] {\n const timestamp = this.now();\n const agentDir = getAgentDir();\n const key = `${cwd}${CACHE_KEY_SEPARATOR}${agentDir}`;\n\n const cached = this.listingCache.get(key);\n if (cached && cached.expiresAt > timestamp) return cached.skills;\n\n const loaded = collectFilesystemSkills(cwd, agentDir, buildSkillPaths(cwd, agentDir));\n const dedupedByName = new Map<string, CachedSkillEntry>();\n for (const entry of loaded) {\n dedupedByName.set(entry.name, chooseHigherPrioritySkill(dedupedByName.get(entry.name), entry));\n }\n\n const skills = [...dedupedByName.values()].sort((a, b) => a.order - b.order);\n this.listingCache.set(key, { skills, expiresAt: timestamp + this.listingTtlMs });\n return skills;\n }\n\n /** Read one skill file, reusing the cached parse while its mtime holds. */\n private readSkill(skillName: string, skillPath: string, source: SkillSource): ResolvedSkill | undefined {\n try {\n const stat = fs.statSync(skillPath);\n const cached = this.fileCache.get(skillPath);\n if (cached && cached.mtimeMs === stat.mtimeMs) return cached.skill;\n\n const raw = fs.readFileSync(skillPath, 'utf-8');\n const skill: ResolvedSkill = {\n name: skillName,\n path: skillPath,\n content: stripSkillFrontmatter(raw),\n description: parseSkillDescription(raw),\n source,\n };\n\n this.fileCache.set(skillPath, { mtimeMs: stat.mtimeMs, skill });\n return skill;\n } catch {\n // Treat an unreadable skill file as unresolved so the caller reports it\n // missing rather than injecting a half-formed block into a child prompt.\n return undefined;\n }\n }\n}\n\n/**\n * Index skill files supplied by an agent's own `skillPath`, keyed by name.\n *\n * Deliberately uncached: these paths are per-agent rather than per-directory, so\n * they would pollute a catalogue cache keyed on cwd.\n */\nfunction collectLocalSkills(\n cwd: string,\n localSkillPaths: string[],\n localBaseDir?: string,\n): Map<string, CachedSkillEntry> {\n const localByName = new Map<string, CachedSkillEntry>();\n const localEntries = collectFilesystemSkills(\n cwd,\n getAgentDir(),\n localSkillPaths.map((entry) => ({\n path: path.resolve(localBaseDir ?? cwd, entry),\n source: 'unknown' as const,\n })),\n );\n for (const entry of localEntries) {\n if (!localByName.has(entry.name)) localByName.set(entry.name, entry);\n }\n return localByName;\n}\n\nfunction escapeXmlText(value: string): string {\n return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n}\n\n/**\n * Render the skill catalogue for a child system prompt.\n *\n * Only names, descriptions and locations are injected. The child reads a skill\n * when its description matches the task, which keeps the prompt small no matter\n * how large the configured skill bodies are.\n */\nexport function buildSkillInjection(skills: ResolvedSkill[]): string {\n if (skills.length === 0) return '';\n\n const lines = [\n 'The following configured skills are available to this subagent.',\n \"Use the read tool to load a skill's file when the task matches its description.\",\n 'When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.',\n '',\n '<available_skills>',\n ];\n for (const skill of skills) {\n lines.push(' <skill>');\n lines.push(` <name>${escapeXmlText(skill.name)}</name>`);\n lines.push(` <description>${escapeXmlText(skill.description ?? '')}</description>`);\n lines.push(` <location>${escapeXmlText(skill.path)}</location>`);\n lines.push(' </skill>');\n }\n lines.push('</available_skills>');\n return lines.join('\\n');\n}\n\nfunction dedupeSkillNames(names: string[]): string[] {\n return [...new Set(names.map((entry) => entry.trim()).filter((entry) => entry.length > 0))];\n}\n\n/**\n * Normalise the `skills` tool parameter into a name list.\n *\n * `false` means \"no skills at all\" and `undefined` means \"use the agent's own\n * configuration\", which is why they are distinct return values rather than both\n * collapsing to an empty array.\n */\nexport function normalizeSkillInput(input: string | string[] | boolean | undefined): string[] | false | undefined {\n if (input === false) return false;\n if (input === true || input === undefined) return undefined;\n if (Array.isArray(input)) return dedupeSkillNames(input);\n\n // Models sometimes serialise the parameter as a JSON string instead of a native\n // array. Splitting '[\"a\",\"b\"]' on commas would embed brackets and quotes into\n // the names, and resolution would then fail with no obvious cause.\n const trimmed = input.trim();\n if (trimmed.startsWith('[')) {\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (Array.isArray(parsed)) {\n return dedupeSkillNames(parsed.filter((entry): entry is string => typeof entry === 'string'));\n }\n } catch {\n // Not valid JSON after all; fall through to the comma-separated reading.\n }\n }\n return dedupeSkillNames(input.split(','));\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import{LruCache as t,init_lruCache as n}from"../shared/lruCache.mjs";import{getAgentDir as r,getProjectConfigDir as i,init_configDir as a}from"../shared/configDir.mjs";import{__decorate as o,init_decorate as s}from"../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.mjs";import{init_env as c}from"../env.mjs";import*as l from"node:fs";import*as u from"node:path";import{injectable as d}from"inversify";import*as f from"node:os";function p(e){return W[e]??W.unknown}function m(e){return e.startsWith(V)?e.indexOf(`\n${V}`,3):-1}function h(e){let t=e.replace(/\r\n/g,`
|
|
2
|
+
`),n=m(t);return n===-1?t:t.slice(n+1+3).trim()}function g(e){let t=e.replace(/\r\n/g,`
|
|
3
|
+
`),n=m(t);if(n!==-1)return t.slice(3,n).trim().match(/^description:\s*(.+)$/m)?.[1]?.trim().replace(/^['"]|['"]$/g,``)}function _(e){try{return g(l.readFileSync(e,`utf-8`))}catch{return}}function v(e,t){let n=u.relative(t,e);return n===``||!n.startsWith(`..`)&&!u.isAbsolute(n)}function y(e,t){try{return JSON.parse(l.readFileSync(e,`utf-8`))}catch(n){if((typeof n==`object`&&n&&`code`in n?n.code:void 0)===`ENOENT`)return null;let r=n instanceof Error?n.message:String(n);throw Error(`Failed to read ${t} '${e}': ${r}`,{cause:n instanceof Error?n:void 0})}}function b(e){if(typeof e!=`object`||!e||Array.isArray(e))return[];let t=e.skills;return Array.isArray(t)?t.filter(e=>typeof e==`string`):[]}function x(e,t){let n=[],r=i(e),a=[{file:u.join(r,`settings.json`),base:r,source:`project-settings`},{file:u.join(t,`settings.json`),base:t,source:`user-settings`}];for(let{file:e,base:t,source:r}of a)for(let i of b(y(e,`skills settings file`))){let e=i;e.startsWith(U)?e=u.join(f.homedir(),e.slice(2)):u.isAbsolute(e)||(e=u.resolve(t,e)),n.push({path:e,source:r})}return n}function S(){return(process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS??``).split(u.delimiter).map(e=>e.trim()).filter(Boolean).map(e=>({path:e,source:`extension`}))}function C(e,t){let n=i(e);return[...S(),{path:u.join(n,`skills`),source:`project`},{path:u.join(e,`.agents`,`skills`),source:`project`},{path:u.join(t,`skills`),source:`user`},{path:u.join(f.homedir(),`.agents`,`skills`),source:`user`},...x(e,t)]}function w(e,t,n,r){if(r)return r;let a=u.resolve(i(t)),o=u.resolve(a,`skills`),s=u.resolve(t,`.agents`),c=u.resolve(n,`skills`),l=u.resolve(n),d=u.resolve(f.homedir(),`.agents`);return v(e,o)||v(e,s)?`project`:v(e,a)?`project-settings`:v(e,c)||v(e,d)?`user`:v(e,l)?`user-settings`:`unknown`}function T(e,t){if(!e)return t;let n=p(e.source),r=p(t.source);return r>n?t:r<n?e:t.order<e.order?t:e}function E(e){return e.toLowerCase().endsWith(B)}function D(e){return e.startsWith(`.`)||e===H}function O(e){try{return l.statSync(e)}catch{return}}function k(e){try{return l.readdirSync(e,{withFileTypes:!0})}catch{return[]}}function A(e,t,n){let r=[],i=new Map,a=new Map,o=0,s=(n,a,s)=>{let c=u.resolve(a);if(!l.existsSync(c))return;let d=w(c,e,t,s),f=i.get(c);if(f!==void 0){let e=r[f];e&&p(d)>p(e.source)&&(r[f]={...e,name:n,source:d,description:_(c)});return}i.set(c,r.length),r.push({name:n,filePath:c,source:d,description:_(c),order:o++})},c=(e,t)=>{let n;try{n=l.realpathSync(e)}catch{n=u.resolve(e)}let r=t?p(t):W.unknown,i=a.get(n);return i!==void 0&&i>=r?!1:(a.set(n,r),!0)},d=(e,t)=>{if(!c(e,t))return;let n=u.join(e,z);if(l.existsSync(n)){s(u.basename(e),n,t);return}for(let n of k(e)){if(D(n.name)||!n.isDirectory()&&!n.isSymbolicLink())continue;let r=u.join(e,n.name);O(r)?.isDirectory()&&d(r,t)}};for(let e of n){if(!l.existsSync(e.path))continue;let t=O(e.path);if(!t)continue;if(t.isFile()){let t=u.basename(e.path);if(!E(t))continue;s(t.toLowerCase()===z.toLowerCase()?u.basename(u.dirname(e.path)):u.basename(t,u.extname(t)),e.path,e.source);continue}if(!t.isDirectory())continue;let n=u.join(e.path,z);if(l.existsSync(n)){s(u.basename(e.path),n,e.source);continue}c(e.path,e.source);for(let t of k(e.path)){if(t.name.startsWith(`.`))continue;let n=u.join(e.path,t.name);if(t.isDirectory()||t.isSymbolicLink()){if(D(t.name))continue;O(n)?.isDirectory()&&d(n,e.source);continue}t.isFile()&&E(t.name)&&s(u.basename(t.name,u.extname(t.name)),n,e.source)}}return r}function j(e,t,n){let i=new Map,a=A(e,r(),t.map(t=>({path:u.resolve(n??e,t),source:`unknown`})));for(let e of a)i.has(e.name)||i.set(e.name,e);return i}function M(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`)}function N(e){if(e.length===0)return``;let t=[`The following configured skills are available to this subagent.`,`Use the read tool to load a skill's file when the task matches its description.`,`When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.`,``,`<available_skills>`];for(let n of e)t.push(` <skill>`),t.push(` <name>${M(n.name)}</name>`),t.push(` <description>${M(n.description??``)}</description>`),t.push(` <location>${M(n.path)}</location>`),t.push(` </skill>`);return t.push(`</available_skills>`),t.join(`
|
|
4
|
+
`)}var P,F,I,L,R,z,B,V,H,U,W,G,K=e((()=>{c(),a(),n(),s(),P=50,F=8,I=5e3,L=`\0`,R=`pi-subagents`,z=`SKILL.md`,B=`.md`,V=`---`,H=`node_modules`,U=`~/`,W={project:700,"project-settings":650,user:300,"user-settings":250,extension:150,builtin:100,unknown:0},G=class{fileCache=new t(P);listingCache=new t(F);listingTtlMs=I;now(){return Date.now()}resolveSkillPath(e,t){let n=this.listSkills(t).find(t=>t.name===e);if(n)return{path:n.filePath,source:n.source}}resolveSkills(e,t,n,r){let i=[],a=[],o=n?.length?j(t,n,r):new Map;for(let n of e){let e=n.trim();if(!e)continue;if(e===R){a.push(e);continue}let r=o.get(e),s=r?this.readSkill(e,r.filePath,r.source):void 0;if(!s){let n=this.resolveSkillPath(e,t);n&&(s=this.readSkill(e,n.path,n.source))}s?i.push(s):a.push(e)}return{resolved:i,missing:a}}resolveSkillsWithFallback(e,t,n,r,i){let a=this.resolveSkills(e,t,r,i);if(!n||a.missing.length===0||u.resolve(t)===u.resolve(n))return a;let o=this.resolveSkills(a.missing,n);return{resolved:[...a.resolved,...o.resolved],missing:o.missing}}discoverAvailableSkills(e){return this.listSkills(e).filter(e=>e.name!==R).map(e=>({name:e.name,source:e.source,description:e.description})).sort((e,t)=>e.name.localeCompare(t.name))}invalidate(){this.fileCache.clear(),this.listingCache.clear()}listSkills(e){let t=this.now(),n=r(),i=`${e}${L}${n}`,a=this.listingCache.get(i);if(a&&a.expiresAt>t)return a.skills;let o=A(e,n,C(e,n)),s=new Map;for(let e of o)s.set(e.name,T(s.get(e.name),e));let c=[...s.values()].sort((e,t)=>e.order-t.order);return this.listingCache.set(i,{skills:c,expiresAt:t+this.listingTtlMs}),c}readSkill(e,t,n){try{let r=l.statSync(t),i=this.fileCache.get(t);if(i&&i.mtimeMs===r.mtimeMs)return i.skill;let a=l.readFileSync(t,`utf-8`),o={name:e,path:t,content:h(a),description:g(a),source:n};return this.fileCache.set(t,{mtimeMs:r.mtimeMs,skill:o}),o}catch{return}}},G=o([d()],G)}));K();export{G as SkillDiscoveryService,N as buildSkillInjection,K as init_skills};
|
|
5
|
+
//# sourceMappingURL=skills.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.mjs","names":[],"sources":["../../src/agents/skills.ts"],"sourcesContent":["/**\n * Resolving skill files by name and rendering them into a child system prompt.\n *\n * A skill is a markdown file, either `<dir>/SKILL.md` or a bare `<name>.md`,\n * found by sweeping a fixed list of search roots. The same name can appear in\n * several roots, so the sweep records where each hit came from and the highest\n * priority source wins.\n *\n * WHY THIS IS A SERVICE AND NOT A MODULE OF FUNCTIONS:\n * Resolution walks every skill root and reads the frontmatter of every markdown\n * file it finds. Prompt assembly asks for the catalogue on each launch and\n * completion asks on each keystroke, so the answers have to be cached, and a\n * cache needs an owner that can be invalidated and swapped in a test.\n *\n * DESIGN PATTERNS:\n * - Two caches with different lifetimes. Listing answers \"does this name exist\n * and where\", reading answers \"what does it say\"; the first goes stale on any\n * directory change, the second only when one file's mtime moves\n * - Both are bounded LRUs. The predecessor held the listing in a single mutable\n * slot keyed by cwd, which thrashed the moment two working directories were in\n * play, and evicted parsed files in insertion order regardless of use\n * - Every filesystem call is best-effort. A skill root that cannot be read is\n * skipped, never fatal: a missing skill surfaces to the caller as `missing`\n * - Symlinked and repeated directories are visited at most once per priority, so\n * a cycle in the skill tree terminates\n *\n * AVOID:\n * - Walking the skill roots on a completion path without going through the cache\n * - Deriving a config directory here instead of asking `configDir.ts`\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { injectable } from 'inversify';\n\nimport { EXTRA_SKILL_DIRS_ENV } from '../env.ts';\nimport { getAgentDir, getProjectConfigDir } from '../shared/configDir.ts';\nimport { LruCache } from '../shared/lruCache.ts';\n\n/**\n * Where a skill file was found.\n *\n * Deliberately not `AgentSource`: a skill can also come from a settings list or\n * from an extension directory, neither of which can hold an agent definition.\n */\nexport type SkillSource =\n | 'project'\n | 'user'\n | 'project-settings'\n | 'user-settings'\n | 'extension'\n | 'builtin'\n | 'unknown';\n\nexport interface ResolvedSkill {\n name: string;\n path: string;\n content: string;\n description?: string;\n source: SkillSource;\n}\n\nexport interface SkillLocation {\n path: string;\n source: SkillSource;\n}\n\nexport interface SkillResolution {\n resolved: ResolvedSkill[];\n missing: string[];\n}\n\nexport interface DiscoveredSkill {\n name: string;\n source: SkillSource;\n description?: string;\n}\n\n/**\n * Discovery and reading of skill files.\n *\n * Behind an interface for the same reason agent discovery is: the results are\n * cached and invalidated, and callers on the completion path must be able to ask\n * repeatedly without paying for a directory walk each time.\n */\nexport interface ISkillDiscoveryService {\n /** Locate one skill by name, or undefined when no root holds it. */\n resolveSkillPath(skillName: string, cwd: string): SkillLocation | undefined;\n /** Read the named skills, reporting any that could not be resolved. */\n resolveSkills(skillNames: string[], cwd: string, localSkillPaths?: string[], localBaseDir?: string): SkillResolution;\n /** Resolve against `primaryCwd`, retrying whatever is missing in `fallbackCwd`. */\n resolveSkillsWithFallback(\n skillNames: string[],\n primaryCwd: string,\n fallbackCwd?: string,\n localSkillPaths?: string[],\n localBaseDir?: string,\n ): SkillResolution;\n /** Every skill visible from `cwd`, sorted by name. */\n discoverAvailableSkills(cwd: string): DiscoveredSkill[];\n /** Drop cached results. Call after writing or removing a skill file. */\n invalidate(): void;\n}\n\n/** One entry in a listing sweep: located, but not yet read. */\ninterface CachedSkillEntry {\n name: string;\n filePath: string;\n source: SkillSource;\n description?: string;\n /** Discovery order, used to break ties between equal-priority sources. */\n order: number;\n}\n\ninterface SkillFileCacheEntry {\n mtimeMs: number;\n skill: ResolvedSkill;\n}\n\ninterface SkillListingCacheEntry {\n skills: CachedSkillEntry[];\n expiresAt: number;\n}\n\ninterface SkillSearchPath {\n path: string;\n source: SkillSource;\n}\n\n/** Roughly the number of distinct skills one session touches. */\nconst SKILL_FILE_CACHE_CAPACITY = 50;\n/** One entry per (cwd, agentDir) pair; a session rarely spans more than a few. */\nconst SKILL_LISTING_CACHE_CAPACITY = 8;\n/**\n * How long a listing stays valid.\n *\n * Short enough that a skill added mid-session appears without a restart, long\n * enough that a burst of completions does not re-walk the roots per keystroke.\n */\nconst SKILL_LISTING_TTL_MS = 5_000;\n\n/** Cache keys join two paths, so use a separator that cannot occur in either. */\nconst CACHE_KEY_SEPARATOR = '\\0';\n\n/**\n * The orchestration skill, excluded from every listing and injection.\n *\n * A subagent already has the orchestration tools wired in, so handing it the\n * skill that explains how to reach for them wastes context and invites a child\n * to fan out again. The name is the published skill's, not this package's.\n */\nconst SUBAGENT_ORCHESTRATION_SKILL = 'pi-subagents';\n\nconst SKILL_FILE_NAME = 'SKILL.md';\nconst MARKDOWN_EXTENSION = '.md';\nconst FRONTMATTER_FENCE = '---';\nconst NODE_MODULES_DIR = 'node_modules';\nconst HOME_ALIAS_PREFIX = '~/';\n\n/** Sources a listing can report, most authoritative first. */\nconst SOURCE_PRIORITY: Record<SkillSource, number> = {\n project: 700,\n 'project-settings': 650,\n user: 300,\n 'user-settings': 250,\n extension: 150,\n builtin: 100,\n unknown: 0,\n};\n\nfunction priorityOf(source: SkillSource): number {\n return SOURCE_PRIORITY[source] ?? SOURCE_PRIORITY.unknown;\n}\n\n/** Index of the first character after a closing frontmatter fence, or -1. */\nfunction findFrontmatterEnd(normalized: string): number {\n if (!normalized.startsWith(FRONTMATTER_FENCE)) return -1;\n return normalized.indexOf(`\\n${FRONTMATTER_FENCE}`, FRONTMATTER_FENCE.length);\n}\n\nexport function stripSkillFrontmatter(content: string): string {\n const normalized = content.replace(/\\r\\n/g, '\\n');\n const endIndex = findFrontmatterEnd(normalized);\n if (endIndex === -1) return normalized;\n return normalized.slice(endIndex + 1 + FRONTMATTER_FENCE.length).trim();\n}\n\nexport function parseSkillDescription(content: string): string | undefined {\n const normalized = content.replace(/\\r\\n/g, '\\n');\n const endIndex = findFrontmatterEnd(normalized);\n if (endIndex === -1) return undefined;\n\n const frontmatter = normalized.slice(FRONTMATTER_FENCE.length, endIndex).trim();\n const match = frontmatter.match(/^description:\\s*(.+)$/m);\n return match?.[1]?.trim().replace(/^['\"]|['\"]$/g, '');\n}\n\nfunction maybeReadSkillDescription(filePath: string): string | undefined {\n try {\n return parseSkillDescription(fs.readFileSync(filePath, 'utf-8'));\n } catch {\n // The description is optional metadata; an unreadable file still lists by name.\n return undefined;\n }\n}\n\nfunction isWithinPath(filePath: string, dir: string): boolean {\n const relative = path.relative(dir, filePath);\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\n/** Read a JSON file that is allowed to be absent, but not allowed to be corrupt. */\nfunction readOptionalJsonFile(filePath: string, label: string): unknown {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n return parsed;\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code?: unknown }).code : undefined;\n if (code === 'ENOENT') return null;\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to read ${label} '${filePath}': ${message}`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n}\n\n/** Extract the string entries of a settings file's `skills` array. */\nfunction readSettingsSkillList(settings: unknown): string[] {\n if (typeof settings !== 'object' || settings === null || Array.isArray(settings)) return [];\n const skills = (settings as { skills?: unknown }).skills;\n if (!Array.isArray(skills)) return [];\n return skills.filter((entry): entry is string => typeof entry === 'string');\n}\n\nfunction collectSettingsSkillPaths(cwd: string, agentDir: string): SkillSearchPath[] {\n const results: SkillSearchPath[] = [];\n const projectConfigDir = getProjectConfigDir(cwd);\n const settingsFiles = [\n { file: path.join(projectConfigDir, 'settings.json'), base: projectConfigDir, source: 'project-settings' as const },\n { file: path.join(agentDir, 'settings.json'), base: agentDir, source: 'user-settings' as const },\n ];\n\n for (const { file, base, source } of settingsFiles) {\n for (const entry of readSettingsSkillList(readOptionalJsonFile(file, 'skills settings file'))) {\n let resolved = entry;\n if (resolved.startsWith(HOME_ALIAS_PREFIX)) {\n resolved = path.join(os.homedir(), resolved.slice(HOME_ALIAS_PREFIX.length));\n } else if (!path.isAbsolute(resolved)) {\n resolved = path.resolve(base, resolved);\n }\n results.push({ path: resolved, source });\n }\n }\n\n return results;\n}\n\nfunction extraSkillPaths(): SkillSearchPath[] {\n return (process.env[EXTRA_SKILL_DIRS_ENV] ?? '')\n .split(path.delimiter)\n .map((directory) => directory.trim())\n .filter(Boolean)\n .map((directory) => ({ path: directory, source: 'extension' as const }));\n}\n\nfunction buildSkillPaths(cwd: string, agentDir: string): SkillSearchPath[] {\n const projectConfigDir = getProjectConfigDir(cwd);\n return [\n ...extraSkillPaths(),\n { path: path.join(projectConfigDir, 'skills'), source: 'project' },\n { path: path.join(cwd, '.agents', 'skills'), source: 'project' },\n { path: path.join(agentDir, 'skills'), source: 'user' },\n { path: path.join(os.homedir(), '.agents', 'skills'), source: 'user' },\n ...collectSettingsSkillPaths(cwd, agentDir),\n ];\n}\n\n/** Classify a hit that arrived without a source hint, by which root contains it. */\nfunction inferSkillSource(filePath: string, cwd: string, agentDir: string, sourceHint?: SkillSource): SkillSource {\n if (sourceHint) return sourceHint;\n\n const projectConfigRoot = path.resolve(getProjectConfigDir(cwd));\n const projectSkillsRoot = path.resolve(projectConfigRoot, 'skills');\n const projectAgentsRoot = path.resolve(cwd, '.agents');\n const userSkillsRoot = path.resolve(agentDir, 'skills');\n const userAgentRoot = path.resolve(agentDir);\n const userAgentsRoot = path.resolve(os.homedir(), '.agents');\n\n if (isWithinPath(filePath, projectSkillsRoot) || isWithinPath(filePath, projectAgentsRoot)) return 'project';\n if (isWithinPath(filePath, projectConfigRoot)) return 'project-settings';\n\n if (isWithinPath(filePath, userSkillsRoot) || isWithinPath(filePath, userAgentsRoot)) return 'user';\n if (isWithinPath(filePath, userAgentRoot)) return 'user-settings';\n\n return 'unknown';\n}\n\nfunction chooseHigherPrioritySkill(\n existing: CachedSkillEntry | undefined,\n candidate: CachedSkillEntry,\n): CachedSkillEntry {\n if (!existing) return candidate;\n const existingPriority = priorityOf(existing.source);\n const candidatePriority = priorityOf(candidate.source);\n if (candidatePriority > existingPriority) return candidate;\n if (candidatePriority < existingPriority) return existing;\n return candidate.order < existing.order ? candidate : existing;\n}\n\nfunction isMarkdownFile(fileName: string): boolean {\n return fileName.toLowerCase().endsWith(MARKDOWN_EXTENSION);\n}\n\n/** Directory names never worth descending into while hunting for skills. */\nfunction shouldSkipDirectory(name: string): boolean {\n return name.startsWith('.') || name === NODE_MODULES_DIR;\n}\n\nfunction statOrUndefined(target: string): fs.Stats | undefined {\n try {\n return fs.statSync(target);\n } catch {\n // A dangling symlink or a path removed mid-sweep is simply not a skill.\n return undefined;\n }\n}\n\nfunction readDirOrEmpty(dirPath: string): fs.Dirent[] {\n try {\n return fs.readdirSync(dirPath, { withFileTypes: true });\n } catch {\n // An unreadable root must not abort the sweep over the remaining roots.\n return [];\n }\n}\n\n/**\n * Sweep the given roots for skill files.\n *\n * Pure with respect to the caches, which is what lets it serve both the cached\n * catalogue and an agent's own uncached `skillPath` list.\n */\nfunction collectFilesystemSkills(cwd: string, agentDir: string, skillPaths: SkillSearchPath[]): CachedSkillEntry[] {\n const entries: CachedSkillEntry[] = [];\n const seen = new Map<string, number>();\n const visitedDirectories = new Map<string, number>();\n let order = 0;\n\n const pushEntry = (name: string, filePath: string, sourceHint?: SkillSource) => {\n const resolvedFile = path.resolve(filePath);\n if (!fs.existsSync(resolvedFile)) return;\n const source = inferSkillSource(resolvedFile, cwd, agentDir, sourceHint);\n const existingIndex = seen.get(resolvedFile);\n if (existingIndex !== undefined) {\n const existing = entries[existingIndex];\n if (existing && priorityOf(source) > priorityOf(existing.source)) {\n entries[existingIndex] = {\n ...existing,\n name,\n source,\n description: maybeReadSkillDescription(resolvedFile),\n };\n }\n return;\n }\n seen.set(resolvedFile, entries.length);\n entries.push({\n name,\n filePath: resolvedFile,\n source,\n description: maybeReadSkillDescription(resolvedFile),\n order: order++,\n });\n };\n\n /** False when this directory was already walked at an equal or better priority. */\n const markDirectoryVisited = (dirPath: string, sourceHint?: SkillSource): boolean => {\n let resolvedDir: string;\n try {\n resolvedDir = fs.realpathSync(dirPath);\n } catch {\n // Symlink resolution is only needed to detect cycles; the lexical path is\n // a sound fallback that at worst walks one directory twice.\n resolvedDir = path.resolve(dirPath);\n }\n const priority = sourceHint ? priorityOf(sourceHint) : SOURCE_PRIORITY.unknown;\n const previousPriority = visitedDirectories.get(resolvedDir);\n if (previousPriority !== undefined && previousPriority >= priority) return false;\n visitedDirectories.set(resolvedDir, priority);\n return true;\n };\n\n const walkSkillDirectories = (dirPath: string, sourceHint?: SkillSource) => {\n if (!markDirectoryVisited(dirPath, sourceHint)) return;\n\n // A directory holding SKILL.md is itself the skill; do not descend further.\n const skillFile = path.join(dirPath, SKILL_FILE_NAME);\n if (fs.existsSync(skillFile)) {\n pushEntry(path.basename(dirPath), skillFile, sourceHint);\n return;\n }\n\n for (const entry of readDirOrEmpty(dirPath)) {\n if (shouldSkipDirectory(entry.name)) continue;\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n\n const entryPath = path.join(dirPath, entry.name);\n if (statOrUndefined(entryPath)?.isDirectory()) walkSkillDirectories(entryPath, sourceHint);\n }\n };\n\n for (const skillPath of skillPaths) {\n if (!fs.existsSync(skillPath.path)) continue;\n\n const stat = statOrUndefined(skillPath.path);\n if (!stat) continue;\n\n // A root may point straight at a markdown file rather than a directory.\n if (stat.isFile()) {\n const fileName = path.basename(skillPath.path);\n if (!isMarkdownFile(fileName)) continue;\n const skillName =\n fileName.toLowerCase() === SKILL_FILE_NAME.toLowerCase()\n ? path.basename(path.dirname(skillPath.path))\n : path.basename(fileName, path.extname(fileName));\n pushEntry(skillName, skillPath.path, skillPath.source);\n continue;\n }\n\n if (!stat.isDirectory()) continue;\n\n const rootSkillFile = path.join(skillPath.path, SKILL_FILE_NAME);\n if (fs.existsSync(rootSkillFile)) {\n pushEntry(path.basename(skillPath.path), rootSkillFile, skillPath.source);\n continue;\n }\n\n markDirectoryVisited(skillPath.path, skillPath.source);\n\n for (const child of readDirOrEmpty(skillPath.path)) {\n if (child.name.startsWith('.')) continue;\n const childPath = path.join(skillPath.path, child.name);\n if (child.isDirectory() || child.isSymbolicLink()) {\n if (shouldSkipDirectory(child.name)) continue;\n if (statOrUndefined(childPath)?.isDirectory()) walkSkillDirectories(childPath, skillPath.source);\n continue;\n }\n if (child.isFile() && isMarkdownFile(child.name)) {\n pushEntry(path.basename(child.name, path.extname(child.name)), childPath, skillPath.source);\n }\n }\n }\n\n return entries;\n}\n\n@injectable()\nexport class SkillDiscoveryService implements ISkillDiscoveryService {\n /** Parsed skill bodies, keyed by resolved file path and validated by mtime. */\n private readonly fileCache = new LruCache<string, SkillFileCacheEntry>(SKILL_FILE_CACHE_CAPACITY);\n /** Located-but-unread catalogues, keyed by the (cwd, agentDir) pair. */\n private readonly listingCache = new LruCache<string, SkillListingCacheEntry>(SKILL_LISTING_CACHE_CAPACITY);\n\n /**\n * Seams for tests, as protected members rather than constructor arguments.\n *\n * The container resolves this service by type, and inversify cannot supply a\n * primitive like a TTL, so configuration cannot arrive through the\n * constructor. The repo convention is also not to abstract the clock, so a\n * test subclass overrides these instead.\n */\n protected readonly listingTtlMs: number = SKILL_LISTING_TTL_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n resolveSkillPath(skillName: string, cwd: string): SkillLocation | undefined {\n const skill = this.listSkills(cwd).find((candidate) => candidate.name === skillName);\n if (!skill) return undefined;\n return { path: skill.filePath, source: skill.source };\n }\n\n resolveSkills(skillNames: string[], cwd: string, localSkillPaths?: string[], localBaseDir?: string): SkillResolution {\n const resolved: ResolvedSkill[] = [];\n const missing: string[] = [];\n const localByName = localSkillPaths?.length\n ? collectLocalSkills(cwd, localSkillPaths, localBaseDir)\n : new Map<string, CachedSkillEntry>();\n\n for (const name of skillNames) {\n const trimmed = name.trim();\n if (!trimmed) continue;\n if (trimmed === SUBAGENT_ORCHESTRATION_SKILL) {\n missing.push(trimmed);\n continue;\n }\n\n // An agent's own skillPath outranks anything found in the shared roots.\n const local = localByName.get(trimmed);\n let skill = local ? this.readSkill(trimmed, local.filePath, local.source) : undefined;\n if (!skill) {\n const location = this.resolveSkillPath(trimmed, cwd);\n if (location) skill = this.readSkill(trimmed, location.path, location.source);\n }\n if (skill) resolved.push(skill);\n else missing.push(trimmed);\n }\n\n return { resolved, missing };\n }\n\n /**\n * Resolve against `primaryCwd`, then retry whatever is still missing elsewhere.\n *\n * A child launched into a worktree still needs the skills configured in the\n * repository it was launched from.\n */\n resolveSkillsWithFallback(\n skillNames: string[],\n primaryCwd: string,\n fallbackCwd?: string,\n localSkillPaths?: string[],\n localBaseDir?: string,\n ): SkillResolution {\n const primary = this.resolveSkills(skillNames, primaryCwd, localSkillPaths, localBaseDir);\n if (!fallbackCwd || primary.missing.length === 0) return primary;\n if (path.resolve(primaryCwd) === path.resolve(fallbackCwd)) return primary;\n\n const fallback = this.resolveSkills(primary.missing, fallbackCwd);\n return {\n resolved: [...primary.resolved, ...fallback.resolved],\n missing: fallback.missing,\n };\n }\n\n discoverAvailableSkills(cwd: string): DiscoveredSkill[] {\n return this.listSkills(cwd)\n .filter((skill) => skill.name !== SUBAGENT_ORCHESTRATION_SKILL)\n .map((skill) => ({\n name: skill.name,\n source: skill.source,\n description: skill.description,\n }))\n .sort((a, b) => a.name.localeCompare(b.name));\n }\n\n invalidate(): void {\n this.fileCache.clear();\n this.listingCache.clear();\n }\n\n /** The catalogue visible from `cwd`, sweeping the roots only when stale. */\n private listSkills(cwd: string): CachedSkillEntry[] {\n const timestamp = this.now();\n const agentDir = getAgentDir();\n const key = `${cwd}${CACHE_KEY_SEPARATOR}${agentDir}`;\n\n const cached = this.listingCache.get(key);\n if (cached && cached.expiresAt > timestamp) return cached.skills;\n\n const loaded = collectFilesystemSkills(cwd, agentDir, buildSkillPaths(cwd, agentDir));\n const dedupedByName = new Map<string, CachedSkillEntry>();\n for (const entry of loaded) {\n dedupedByName.set(entry.name, chooseHigherPrioritySkill(dedupedByName.get(entry.name), entry));\n }\n\n const skills = [...dedupedByName.values()].sort((a, b) => a.order - b.order);\n this.listingCache.set(key, { skills, expiresAt: timestamp + this.listingTtlMs });\n return skills;\n }\n\n /** Read one skill file, reusing the cached parse while its mtime holds. */\n private readSkill(skillName: string, skillPath: string, source: SkillSource): ResolvedSkill | undefined {\n try {\n const stat = fs.statSync(skillPath);\n const cached = this.fileCache.get(skillPath);\n if (cached && cached.mtimeMs === stat.mtimeMs) return cached.skill;\n\n const raw = fs.readFileSync(skillPath, 'utf-8');\n const skill: ResolvedSkill = {\n name: skillName,\n path: skillPath,\n content: stripSkillFrontmatter(raw),\n description: parseSkillDescription(raw),\n source,\n };\n\n this.fileCache.set(skillPath, { mtimeMs: stat.mtimeMs, skill });\n return skill;\n } catch {\n // Treat an unreadable skill file as unresolved so the caller reports it\n // missing rather than injecting a half-formed block into a child prompt.\n return undefined;\n }\n }\n}\n\n/**\n * Index skill files supplied by an agent's own `skillPath`, keyed by name.\n *\n * Deliberately uncached: these paths are per-agent rather than per-directory, so\n * they would pollute a catalogue cache keyed on cwd.\n */\nfunction collectLocalSkills(\n cwd: string,\n localSkillPaths: string[],\n localBaseDir?: string,\n): Map<string, CachedSkillEntry> {\n const localByName = new Map<string, CachedSkillEntry>();\n const localEntries = collectFilesystemSkills(\n cwd,\n getAgentDir(),\n localSkillPaths.map((entry) => ({\n path: path.resolve(localBaseDir ?? cwd, entry),\n source: 'unknown' as const,\n })),\n );\n for (const entry of localEntries) {\n if (!localByName.has(entry.name)) localByName.set(entry.name, entry);\n }\n return localByName;\n}\n\nfunction escapeXmlText(value: string): string {\n return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n}\n\n/**\n * Render the skill catalogue for a child system prompt.\n *\n * Only names, descriptions and locations are injected. The child reads a skill\n * when its description matches the task, which keeps the prompt small no matter\n * how large the configured skill bodies are.\n */\nexport function buildSkillInjection(skills: ResolvedSkill[]): string {\n if (skills.length === 0) return '';\n\n const lines = [\n 'The following configured skills are available to this subagent.',\n \"Use the read tool to load a skill's file when the task matches its description.\",\n 'When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.',\n '',\n '<available_skills>',\n ];\n for (const skill of skills) {\n lines.push(' <skill>');\n lines.push(` <name>${escapeXmlText(skill.name)}</name>`);\n lines.push(` <description>${escapeXmlText(skill.description ?? '')}</description>`);\n lines.push(` <location>${escapeXmlText(skill.path)}</location>`);\n lines.push(' </skill>');\n }\n lines.push('</available_skills>');\n return lines.join('\\n');\n}\n\nfunction dedupeSkillNames(names: string[]): string[] {\n return [...new Set(names.map((entry) => entry.trim()).filter((entry) => entry.length > 0))];\n}\n\n/**\n * Normalise the `skills` tool parameter into a name list.\n *\n * `false` means \"no skills at all\" and `undefined` means \"use the agent's own\n * configuration\", which is why they are distinct return values rather than both\n * collapsing to an empty array.\n */\nexport function normalizeSkillInput(input: string | string[] | boolean | undefined): string[] | false | undefined {\n if (input === false) return false;\n if (input === true || input === undefined) return undefined;\n if (Array.isArray(input)) return dedupeSkillNames(input);\n\n // Models sometimes serialise the parameter as a JSON string instead of a native\n // array. Splitting '[\"a\",\"b\"]' on commas would embed brackets and quotes into\n // the names, and resolution would then fail with no obvious cause.\n const trimmed = input.trim();\n if (trimmed.startsWith('[')) {\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (Array.isArray(parsed)) {\n return dedupeSkillNames(parsed.filter((entry): entry is string => typeof entry === 'string'));\n }\n } catch {\n // Not valid JSON after all; fall through to the comma-separated reading.\n }\n }\n return dedupeSkillNames(input.split(','));\n}\n"],"mappings":"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"}
|