@jingyi0605/codingns4dsh 0.2.1-beta.1 → 0.2.1-beta.3
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/README.en.md +25 -14
- package/README.md +21 -17
- package/data/build/dist/client/bundle.js +366 -81
- package/data/build/dist/client/bundle.js.map +1 -1
- package/data/build/dist/client/locales/fileWorkspace.d.ts.map +1 -1
- package/data/build/dist/client/locales/fileWorkspace.js +4 -0
- package/data/build/dist/client/locales/fileWorkspace.js.map +1 -1
- package/data/build/dist/client/locales/peerHostWorkspace.d.ts.map +1 -1
- package/data/build/dist/client/locales/peerHostWorkspace.js +16 -2
- package/data/build/dist/client/locales/peerHostWorkspace.js.map +1 -1
- package/data/build/dist/client/peer-host-native-projection.d.ts +4 -3
- package/data/build/dist/client/peer-host-native-projection.d.ts.map +1 -1
- package/data/build/dist/client/peer-host-native-projection.js +7 -6
- package/data/build/dist/client/peer-host-native-projection.js.map +1 -1
- package/data/build/dist/client/peer-host-native-store-projection.js +9 -21
- package/data/build/dist/client/peer-host-native-store-projection.js.map +1 -1
- package/data/build/dist/client/peer-host-workspace-tab.d.ts.map +1 -1
- package/data/build/dist/client/peer-host-workspace-tab.js +139 -10
- package/data/build/dist/client/peer-host-workspace-tab.js.map +1 -1
- package/data/build/dist/client/session-changed-files-view.d.ts.map +1 -1
- package/data/build/dist/client/session-changed-files-view.js +162 -58
- package/data/build/dist/client/session-changed-files-view.js.map +1 -1
- package/data/build/dist/client/skill-command.d.ts +1 -1
- package/data/build/dist/client/skill-command.d.ts.map +1 -1
- package/data/build/dist/client/skill-command.js +25 -2
- package/data/build/dist/client/skill-command.js.map +1 -1
- package/data/build/dist/client/skill-reference-dom.js +1 -1
- package/data/build/dist/client/skill-reference-dom.js.map +1 -1
- package/data/build/dist/client/terminal/styles.js +2 -2
- package/data/build/dist/dsh-capabilities/host/sidebar-session-compat.d.ts +28 -0
- package/data/build/dist/dsh-capabilities/host/sidebar-session-compat.d.ts.map +1 -0
- package/data/build/dist/dsh-capabilities/host/sidebar-session-compat.js +109 -0
- package/data/build/dist/dsh-capabilities/host/sidebar-session-compat.js.map +1 -0
- package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts +25 -3
- package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/acp-cli-driver.js +120 -9
- package/data/build/dist/host/cli-adapters/acp-cli-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/acp-elicitation.d.ts +31 -0
- package/data/build/dist/host/cli-adapters/acp-elicitation.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/acp-elicitation.js +144 -0
- package/data/build/dist/host/cli-adapters/acp-elicitation.js.map +1 -0
- package/data/build/dist/host/cli-adapters/antigravity-driver.d.ts +2 -1
- package/data/build/dist/host/cli-adapters/antigravity-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/antigravity-driver.js +15 -2
- package/data/build/dist/host/cli-adapters/antigravity-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/attachment-utils.d.ts +16 -5
- package/data/build/dist/host/cli-adapters/attachment-utils.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/attachment-utils.js +152 -42
- package/data/build/dist/host/cli-adapters/attachment-utils.js.map +1 -1
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts +23 -8
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/claude-driver.js +227 -70
- package/data/build/dist/host/cli-adapters/claude-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts +7 -1
- package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/codebuddy-driver.js +143 -6
- package/data/build/dist/host/cli-adapters/codebuddy-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/codex-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/codex-driver.js +74 -6
- package/data/build/dist/host/cli-adapters/codex-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/command-code-acp-loader.d.ts +23 -0
- package/data/build/dist/host/cli-adapters/command-code-acp-loader.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/command-code-acp-loader.js +39 -0
- package/data/build/dist/host/cli-adapters/command-code-acp-loader.js.map +1 -0
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +17 -6
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/command-code-driver.js +448 -26
- package/data/build/dist/host/cli-adapters/command-code-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/command-code-subscription.d.ts +1 -1
- package/data/build/dist/host/cli-adapters/command-code-subscription.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/command-code-subscription.js +7 -4
- package/data/build/dist/host/cli-adapters/command-code-subscription.js.map +1 -1
- package/data/build/dist/host/cli-adapters/cursor-driver.d.ts +1 -1
- package/data/build/dist/host/cli-adapters/cursor-driver.js +2 -2
- package/data/build/dist/host/cli-adapters/cursor-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js +1 -1
- package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js.map +1 -1
- package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js +28 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js.map +1 -1
- package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/dsh-tool-history.js +23 -1
- package/data/build/dist/host/cli-adapters/dsh-tool-history.js.map +1 -1
- package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/feature.js +5 -3
- package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
- package/data/build/dist/host/cli-adapters/gemini-driver.d.ts +2 -1
- package/data/build/dist/host/cli-adapters/gemini-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/gemini-driver.js +66 -11
- package/data/build/dist/host/cli-adapters/gemini-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/grok-driver.d.ts +5 -2
- package/data/build/dist/host/cli-adapters/grok-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/grok-driver.js +268 -18
- package/data/build/dist/host/cli-adapters/grok-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/json-rpc-process.js +8 -3
- package/data/build/dist/host/cli-adapters/json-rpc-process.js.map +1 -1
- package/data/build/dist/host/cli-adapters/kimi-driver.d.ts +4 -0
- package/data/build/dist/host/cli-adapters/kimi-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/kimi-driver.js +11 -4
- package/data/build/dist/host/cli-adapters/kimi-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/kiro-driver.js +1 -1
- package/data/build/dist/host/cli-adapters/kiro-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/mcode-driver.d.ts +6 -2
- package/data/build/dist/host/cli-adapters/mcode-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/mcode-driver.js +95 -26
- package/data/build/dist/host/cli-adapters/mcode-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.d.ts +55 -1
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.js +148 -6
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.js.map +1 -1
- package/data/build/dist/host/cli-adapters/native-team-subagent.d.ts +7 -0
- package/data/build/dist/host/cli-adapters/native-team-subagent.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/native-team-subagent.js.map +1 -1
- package/data/build/dist/host/cli-adapters/new-api-subscription.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/new-api-subscription.js +11 -58
- package/data/build/dist/host/cli-adapters/new-api-subscription.js.map +1 -1
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +5 -2
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/opencode-driver.js +136 -18
- package/data/build/dist/host/cli-adapters/opencode-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/pi-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/pi-driver.js +2 -1
- package/data/build/dist/host/cli-adapters/pi-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/process-utils.d.ts +10 -0
- package/data/build/dist/host/cli-adapters/process-utils.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/process-utils.js +20 -0
- package/data/build/dist/host/cli-adapters/process-utils.js.map +1 -1
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +3 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/provider-subscription.js +42 -8
- package/data/build/dist/host/cli-adapters/provider-subscription.js.map +1 -1
- package/data/build/dist/host/cli-adapters/qoder-driver.d.ts +4 -4
- package/data/build/dist/host/cli-adapters/qoder-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/qoder-driver.js +31 -8
- package/data/build/dist/host/cli-adapters/qoder-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/registry.js +5 -3
- package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +2 -9
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js.map +1 -1
- package/data/build/dist/host/cli-adapters/skill-filesystem.d.ts +25 -0
- package/data/build/dist/host/cli-adapters/skill-filesystem.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/skill-filesystem.js +161 -0
- package/data/build/dist/host/cli-adapters/skill-filesystem.js.map +1 -0
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +1 -0
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js +7 -1
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/subagent-tool.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/subagent-tool.js +40 -10
- package/data/build/dist/host/cli-adapters/subagent-tool.js.map +1 -1
- package/data/build/dist/host/cli-adapters/zcode-driver.d.ts +8 -2
- package/data/build/dist/host/cli-adapters/zcode-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/zcode-driver.js +166 -55
- package/data/build/dist/host/cli-adapters/zcode-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/zcode-interactions.d.ts +18 -0
- package/data/build/dist/host/cli-adapters/zcode-interactions.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/zcode-interactions.js +90 -0
- package/data/build/dist/host/cli-adapters/zcode-interactions.js.map +1 -0
- package/data/build/dist/host/cli-adapters/zcode-provider-config.d.ts +26 -0
- package/data/build/dist/host/cli-adapters/zcode-provider-config.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/zcode-provider-config.js +102 -0
- package/data/build/dist/host/cli-adapters/zcode-provider-config.js.map +1 -0
- package/data/build/dist/host/cli-adapters/zcode-subscription.d.ts +2 -7
- package/data/build/dist/host/cli-adapters/zcode-subscription.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/zcode-subscription.js.map +1 -1
- package/data/build/dist/host/cli-adapters/zcode-telemetry.d.ts +17 -0
- package/data/build/dist/host/cli-adapters/zcode-telemetry.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/zcode-telemetry.js +124 -0
- package/data/build/dist/host/cli-adapters/zcode-telemetry.js.map +1 -0
- package/data/build/dist/host/cli-bridge/bridge-holder.d.ts +4 -1
- package/data/build/dist/host/cli-bridge/bridge-holder.d.ts.map +1 -1
- package/data/build/dist/host/cli-bridge/bridge-holder.js.map +1 -1
- package/data/build/dist/host/cli-bridge/bridge-server.d.ts +11 -3
- package/data/build/dist/host/cli-bridge/bridge-server.d.ts.map +1 -1
- package/data/build/dist/host/cli-bridge/bridge-server.js +8 -4
- package/data/build/dist/host/cli-bridge/bridge-server.js.map +1 -1
- package/data/build/dist/host/cli-bridge/command-code-mod.js +18 -2
- package/data/build/dist/host/cli-bridge/command-code-mod.js.map +1 -1
- package/data/build/dist/host/cli-bridge/dispatch.d.ts.map +1 -1
- package/data/build/dist/host/cli-bridge/dispatch.js +30 -5
- package/data/build/dist/host/cli-bridge/dispatch.js.map +1 -1
- package/data/build/dist/host/cli-bridge/injections.d.ts +2 -9
- package/data/build/dist/host/cli-bridge/injections.d.ts.map +1 -1
- package/data/build/dist/host/cli-bridge/injections.js +9 -19
- package/data/build/dist/host/cli-bridge/injections.js.map +1 -1
- package/data/build/dist/host/cli-bridge/mcp-stdio-entry.js +49 -7
- package/data/build/dist/host/cli-bridge/mcp-stdio-entry.js.map +1 -1
- package/data/build/dist/host/features/peer-host.d.ts.map +1 -1
- package/data/build/dist/host/features/peer-host.js +23 -0
- package/data/build/dist/host/features/peer-host.js.map +1 -1
- package/data/build/dist/host/index.d.ts +1 -1
- package/data/build/dist/host/index.d.ts.map +1 -1
- package/data/build/dist/host/index.js +4 -1
- package/data/build/dist/host/index.js.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.d.ts +2 -0
- package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.d.ts.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.js +5 -0
- package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.js.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-remote-summary-source.d.ts.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-remote-summary-source.js +3 -1
- package/data/build/dist/host/modules/peer-host/peer-host-remote-summary-source.js.map +1 -1
- package/data/build/dist/host/rpc.d.ts.map +1 -1
- package/data/build/dist/host/rpc.js +5 -0
- package/data/build/dist/host/rpc.js.map +1 -1
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +4 -0
- package/data/build/dist/shared/contracts/cli-adapter.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/version.d.ts +1 -1
- package/data/build/dist/shared/contracts/version.js +1 -1
- package/package.json +3 -1
- package/profile/README.md +2 -2
- package/profile/package.json +2 -2
- package/profile/version.json +1 -1
- package/version.json +1 -1
|
@@ -6,9 +6,11 @@ import readline from 'node:readline';
|
|
|
6
6
|
import { firstToolText, serializeToolValue } from './tool-observation.js';
|
|
7
7
|
import { usageChunk } from './rpc-driver-utils.js';
|
|
8
8
|
import { commandEnvironment, resolveCommandPath, terminateChildProcess } from './process-utils.js';
|
|
9
|
-
import { promptWithAttachmentPaths } from './attachment-utils.js';
|
|
9
|
+
import { prepareAttachmentPaths, promptWithAttachmentPaths } from './attachment-utils.js';
|
|
10
|
+
import { parseSkillFrontmatter } from './skill-filesystem.js';
|
|
10
11
|
import { commandCodeBridgeArgs, commandCodeBridgeEnvironment } from '../cli-bridge/injections.js';
|
|
11
12
|
import { getSubagentBridge } from '../cli-bridge/bridge-holder.js';
|
|
13
|
+
import { AcpCliDriver } from './acp-cli-driver.js';
|
|
12
14
|
import { CommandCodeHistory, } from './command-code-history.js';
|
|
13
15
|
const WINDOWS = process.platform === 'win32';
|
|
14
16
|
const COMMAND_CODE_BINARIES = WINDOWS
|
|
@@ -156,6 +158,14 @@ function readDeclaredEfforts(homeDirectory) {
|
|
|
156
158
|
const DEFAULT_MAX_TURNS = 500;
|
|
157
159
|
const DEFAULT_AUTO_CONTINUE_ATTEMPTS = 3;
|
|
158
160
|
const AUTO_CONTINUE_PROMPT = '继续';
|
|
161
|
+
/** Command Code ACP 自由文本兼容 loader 的 URL;只注入子进程,不改写安装包。 */
|
|
162
|
+
const COMMAND_CODE_ACP_LOADER_URL = new URL('./command-code-acp-loader.js', import.meta.url).href;
|
|
163
|
+
function commandCodeAcpEnvironment() {
|
|
164
|
+
// file URL 会把空格编码成 %20,避免 Windows 的 NODE_OPTIONS 按空格拆路径。
|
|
165
|
+
const loaderOption = `--loader=${COMMAND_CODE_ACP_LOADER_URL}`;
|
|
166
|
+
const existing = process.env.NODE_OPTIONS?.trim();
|
|
167
|
+
return { NODE_OPTIONS: existing === undefined || existing === '' ? loaderOption : `${existing} ${loaderOption}` };
|
|
168
|
+
}
|
|
159
169
|
/** CLI 在 -p 模式撞到 --max-turns 时的退出码(MAX_TURNS_REACHED)。 */
|
|
160
170
|
const COMMAND_CODE_MAX_TURNS_EXIT_CODE = 8;
|
|
161
171
|
/** 发送 SIGINT 后等待 CLI 自己收尾的时间;超时再强制清理进程树。 */
|
|
@@ -181,12 +191,7 @@ function commandCodePermissionArgs(permission) {
|
|
|
181
191
|
* usage 和原生组件映射全部交给公共消息投影层。
|
|
182
192
|
*/
|
|
183
193
|
export class CommandCodeDriver {
|
|
184
|
-
descriptor
|
|
185
|
-
id: 'command-code',
|
|
186
|
-
name: 'Command Code',
|
|
187
|
-
protocol: 'command',
|
|
188
|
-
capabilities: ['models', 'stream', 'resume', 'interrupt', 'tool-events', 'reasoning', 'usage', 'history'],
|
|
189
|
-
};
|
|
194
|
+
descriptor;
|
|
190
195
|
/** 驱动自己维护 Provider turn 边界,Host 可以把工具边界映射为 DSH step。 */
|
|
191
196
|
supportsSegmentedTurns = true;
|
|
192
197
|
homeDirectory;
|
|
@@ -197,6 +202,11 @@ export class CommandCodeDriver {
|
|
|
197
202
|
autoContinueMaxAttempts;
|
|
198
203
|
autoContinuePrompt;
|
|
199
204
|
history;
|
|
205
|
+
acpDriver;
|
|
206
|
+
respondPermission;
|
|
207
|
+
respondQuestion;
|
|
208
|
+
/** 按规范化 cwd 缓存 Skill 摘要;forceReload 用于响应目录变更。 */
|
|
209
|
+
skillCatalogs = new Map();
|
|
200
210
|
cachedBinary = null;
|
|
201
211
|
cachedEnvironment;
|
|
202
212
|
processes = new Set();
|
|
@@ -211,6 +221,36 @@ export class CommandCodeDriver {
|
|
|
211
221
|
this.autoContinuePrompt = options.autoContinuePrompt?.trim() || AUTO_CONTINUE_PROMPT;
|
|
212
222
|
const readStatus = options.readStatus ?? ((workspacePath) => this.readCommandCodeStatus(workspacePath));
|
|
213
223
|
this.history = new CommandCodeHistory(this.homeDirectory, { readStatus });
|
|
224
|
+
const legacyCapabilities = ['models', 'skills', 'stream', 'resume', 'interrupt', 'tool-events', 'reasoning', 'usage', 'history'];
|
|
225
|
+
const acpCapabilities = [...legacyCapabilities, 'permission', 'questions'];
|
|
226
|
+
this.descriptor = { id: 'command-code', name: 'Command Code', protocol: options.enableAcp === true ? 'acp' : 'command', capabilities: options.enableAcp === true ? acpCapabilities : legacyCapabilities };
|
|
227
|
+
if (options.enableAcp === true) {
|
|
228
|
+
this.acpDriver = new AcpCliDriver({
|
|
229
|
+
binaries: this.binaries,
|
|
230
|
+
spawnSync: this.runSpawnSync,
|
|
231
|
+
spawn: this.runSpawn,
|
|
232
|
+
args: ['acp'],
|
|
233
|
+
buildArgs: (input) => commandCodeAcpArgs(input),
|
|
234
|
+
runtimeModelSelection: false,
|
|
235
|
+
configureSession: configureCommandCodeAcpSession,
|
|
236
|
+
sessionEnvironment: commandCodeAcpEnvironment(),
|
|
237
|
+
id: 'command-code',
|
|
238
|
+
name: 'Command Code',
|
|
239
|
+
capabilities: acpCapabilities,
|
|
240
|
+
readQuestionRequest: readCommandCodeQuestionRequest,
|
|
241
|
+
probeReason: 'Command Code ACP 不公开可安全读取的会话索引',
|
|
242
|
+
});
|
|
243
|
+
this.respondPermission = (sessionId, response) => this.acpDriver.respondPermission(sessionId, response);
|
|
244
|
+
this.respondQuestion = (sessionId, response) => this.acpDriver.respondQuestion(sessionId, response);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
this.acpDriver = undefined;
|
|
248
|
+
// 保持旧实例的运行时能力声明:未启用 ACP 时不暴露这两个可回写接口。
|
|
249
|
+
this.respondPermission = () => { throw new Error('Command Code ACP 未启用'); };
|
|
250
|
+
this.respondQuestion = () => { throw new Error('Command Code ACP 未启用'); };
|
|
251
|
+
delete this.respondPermission;
|
|
252
|
+
delete this.respondQuestion;
|
|
253
|
+
}
|
|
214
254
|
}
|
|
215
255
|
/** 读取 Command Code 原生会话目录,供 Provider History 层使用。 */
|
|
216
256
|
detectSessions(workspacePath) {
|
|
@@ -219,6 +259,43 @@ export class CommandCodeDriver {
|
|
|
219
259
|
detectSessionsDetailed(workspacePath) {
|
|
220
260
|
return this.history.detectSessionsDetailed(workspacePath);
|
|
221
261
|
}
|
|
262
|
+
/** 读取 Command Code 与 `.agents` 兼容目录中的 Skill 摘要,不返回本地路径。 */
|
|
263
|
+
async listSkills(input) {
|
|
264
|
+
if (input.signal?.aborted)
|
|
265
|
+
throw new Error('Skill 目录读取已取消');
|
|
266
|
+
const command = this.cachedBinary ?? (await this.detect()).command;
|
|
267
|
+
if (command === null)
|
|
268
|
+
return [];
|
|
269
|
+
const cwd = resolve(input.cwd ?? process.cwd());
|
|
270
|
+
const entries = this.readSkillCatalog(cwd, input.forceReload === true);
|
|
271
|
+
return entries.map(({ path: _path, ...descriptor }) => descriptor);
|
|
272
|
+
}
|
|
273
|
+
/** 只为显式 `/skill-name` 或 `$skill-name` 下发匹配的 Skill 目录。 */
|
|
274
|
+
async resolveSkillPaths(input) {
|
|
275
|
+
const names = commandCodeSkillNames(input.prompt);
|
|
276
|
+
if (names.length === 0)
|
|
277
|
+
return [];
|
|
278
|
+
if (input.signal?.aborted)
|
|
279
|
+
throw new Error('Skill 目录读取已取消');
|
|
280
|
+
const entries = this.readSkillCatalog(resolve(input.cwd ?? process.cwd()), false);
|
|
281
|
+
const byName = new Map(entries.map((entry) => [entry.name, entry]));
|
|
282
|
+
const paths = [];
|
|
283
|
+
for (const name of names) {
|
|
284
|
+
const entry = byName.get(name);
|
|
285
|
+
if (entry?.enabled !== true || paths.includes(entry.path))
|
|
286
|
+
continue;
|
|
287
|
+
paths.push(entry.path);
|
|
288
|
+
}
|
|
289
|
+
return paths;
|
|
290
|
+
}
|
|
291
|
+
readSkillCatalog(cwd, forceReload) {
|
|
292
|
+
const cached = this.skillCatalogs.get(cwd);
|
|
293
|
+
if (!forceReload && cached !== undefined)
|
|
294
|
+
return cached;
|
|
295
|
+
const entries = scanCommandCodeSkills(cwd, this.homeDirectory);
|
|
296
|
+
this.skillCatalogs.set(cwd, entries);
|
|
297
|
+
return entries;
|
|
298
|
+
}
|
|
222
299
|
startSession(workspacePath, options = {}) {
|
|
223
300
|
return this.history.startSession(workspacePath, options);
|
|
224
301
|
}
|
|
@@ -263,10 +340,8 @@ export class CommandCodeDriver {
|
|
|
263
340
|
const direct = this.detectCommand(command);
|
|
264
341
|
if (direct !== null)
|
|
265
342
|
return direct;
|
|
266
|
-
if (!this.lookupAfterDetectionFailure)
|
|
267
|
-
continue;
|
|
268
343
|
const resolved = resolveCommandPath(command, this.runSpawnSync);
|
|
269
|
-
if (resolved === null)
|
|
344
|
+
if (resolved === null || resolved === command)
|
|
270
345
|
continue;
|
|
271
346
|
const fallback = this.detectCommand(resolved);
|
|
272
347
|
if (fallback !== null)
|
|
@@ -274,9 +349,7 @@ export class CommandCodeDriver {
|
|
|
274
349
|
}
|
|
275
350
|
return { installed: false, version: null, command: null };
|
|
276
351
|
}
|
|
277
|
-
lookupAfterDetectionFailure = false;
|
|
278
352
|
detectCommand(command) {
|
|
279
|
-
this.lookupAfterDetectionFailure = false;
|
|
280
353
|
try {
|
|
281
354
|
const result = this.runSpawnSync(command, ['--version'], { encoding: 'utf8', timeout: 3_000, windowsHide: true, shell: WINDOWS, env: commandEnvironment(command) });
|
|
282
355
|
const output = `${result.stdout ?? ''}${result.stderr ?? ''}`.trim();
|
|
@@ -286,11 +359,9 @@ export class CommandCodeDriver {
|
|
|
286
359
|
this.cachedEnvironment = commandEnvironment(command);
|
|
287
360
|
return { installed: true, version, command };
|
|
288
361
|
}
|
|
289
|
-
this.lookupAfterDetectionFailure = result.status === null;
|
|
290
362
|
}
|
|
291
363
|
catch {
|
|
292
364
|
// PATH 中不存在候选命令属于正常的未安装状态。
|
|
293
|
-
this.lookupAfterDetectionFailure = true;
|
|
294
365
|
}
|
|
295
366
|
return null;
|
|
296
367
|
}
|
|
@@ -364,6 +435,8 @@ export class CommandCodeDriver {
|
|
|
364
435
|
}
|
|
365
436
|
}
|
|
366
437
|
async probeSession(input) {
|
|
438
|
+
if (this.acpDriver !== undefined)
|
|
439
|
+
return this.acpDriver.probeSession(input);
|
|
367
440
|
throwIfCommandCodeProbeAborted(input.signal);
|
|
368
441
|
const providerSessionId = input.providerSessionId?.trim();
|
|
369
442
|
if (!providerSessionId)
|
|
@@ -420,11 +493,30 @@ export class CommandCodeDriver {
|
|
|
420
493
|
return lastFailure ?? { state: 'missing', reason: 'Command Code 原生会话文件不存在', ...(safeRawStoreRef ? { rawStoreRef: safeRawStoreRef } : {}) };
|
|
421
494
|
}
|
|
422
495
|
async *executeTurn(input) {
|
|
496
|
+
const skillPaths = await this.resolveSkillPaths(input);
|
|
497
|
+
const turnInput = skillPaths.length === 0 ? input : { ...input, skillPaths };
|
|
498
|
+
if (this.acpDriver !== undefined) {
|
|
499
|
+
yield* this.acpDriver.executeTurn(turnInput);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
423
502
|
const binary = this.cachedBinary ?? (await this.detect()).command;
|
|
424
503
|
if (binary === null)
|
|
425
504
|
throw new Error('Command Code 未安装');
|
|
426
505
|
const segmented = input.splitToolSteps === true;
|
|
427
|
-
const
|
|
506
|
+
const existing = this.turns.get(input.sessionId);
|
|
507
|
+
const resuming = segmented && input.resumeSegmentedTurn === true && existing !== undefined && !existing.disposed && !existing.finished;
|
|
508
|
+
const prepared = resuming ? undefined : await prepareAttachmentPaths(input);
|
|
509
|
+
let turn;
|
|
510
|
+
try {
|
|
511
|
+
const preparedInput = prepared?.input ?? input;
|
|
512
|
+
turn = segmented ? this.acquireTurn(preparedInput, binary, skillPaths) : this.startTurn(preparedInput, binary, skillPaths);
|
|
513
|
+
if (prepared !== undefined)
|
|
514
|
+
turn.attachmentCleanup = prepared.cleanup;
|
|
515
|
+
}
|
|
516
|
+
catch (error) {
|
|
517
|
+
prepared?.cleanup();
|
|
518
|
+
throw error;
|
|
519
|
+
}
|
|
428
520
|
let suspended = false;
|
|
429
521
|
const onAbort = () => { this.requestGracefulStop(turn); };
|
|
430
522
|
input.signal?.addEventListener('abort', onAbort, { once: true });
|
|
@@ -442,12 +534,14 @@ export class CommandCodeDriver {
|
|
|
442
534
|
}
|
|
443
535
|
}
|
|
444
536
|
dispose() {
|
|
537
|
+
this.acpDriver?.dispose();
|
|
445
538
|
for (const turn of [...this.turns.values()])
|
|
446
539
|
this.disposeTurn(turn);
|
|
447
540
|
this.turns.clear();
|
|
448
541
|
for (const child of this.processes)
|
|
449
542
|
terminateChildProcess(child);
|
|
450
543
|
this.processes.clear();
|
|
544
|
+
this.skillCatalogs.clear();
|
|
451
545
|
this.cachedBinary = null;
|
|
452
546
|
}
|
|
453
547
|
/** 丢弃等待下一个 DSH step 的 `-p` 运行,避免旧进程继续占用新一轮输出。 */
|
|
@@ -457,18 +551,18 @@ export class CommandCodeDriver {
|
|
|
457
551
|
this.disposeTurn(turn);
|
|
458
552
|
}
|
|
459
553
|
/** 只有 Host 显式声明续段时才复用常驻进程;新的用户回合必须重新启动。 */
|
|
460
|
-
acquireTurn(input, binary) {
|
|
554
|
+
acquireTurn(input, binary, skillPaths) {
|
|
461
555
|
const existing = this.turns.get(input.sessionId);
|
|
462
556
|
if (existing !== undefined) {
|
|
463
557
|
if (input.resumeSegmentedTurn === true && !existing.disposed && !existing.finished)
|
|
464
558
|
return existing;
|
|
465
559
|
this.disposeTurn(existing);
|
|
466
560
|
}
|
|
467
|
-
const turn = this.startTurn(input, binary);
|
|
561
|
+
const turn = this.startTurn(input, binary, skillPaths);
|
|
468
562
|
this.turns.set(input.sessionId, turn);
|
|
469
563
|
return turn;
|
|
470
564
|
}
|
|
471
|
-
startTurn(input, binary) {
|
|
565
|
+
startTurn(input, binary, skillPaths) {
|
|
472
566
|
const cwd = input.cwd ?? process.cwd();
|
|
473
567
|
const requestedProviderSessionId = input.providerSessionId?.trim() || undefined;
|
|
474
568
|
// 旧 synthetic ID 可能只对应 checkpoint;回退时必须以当前 DSH 会话 ID
|
|
@@ -497,6 +591,7 @@ export class CommandCodeDriver {
|
|
|
497
591
|
transcriptPath,
|
|
498
592
|
providerSessionId,
|
|
499
593
|
rawStoreRef: canResume ? input.rawStoreRef?.trim() || undefined : transcriptPath,
|
|
594
|
+
skillPaths,
|
|
500
595
|
cwd,
|
|
501
596
|
queue: createEventQueue(),
|
|
502
597
|
currentMessageId: undefined,
|
|
@@ -616,6 +711,8 @@ export class CommandCodeDriver {
|
|
|
616
711
|
if (input.enableAskUserQuestion === true || input.runtimeEnv?.CMD_TOOLS_ASK_USER_QUESTION_ENABLE === 'true') {
|
|
617
712
|
args.push('--tools-enable', 'ask_user_question');
|
|
618
713
|
}
|
|
714
|
+
for (const path of turn.skillPaths)
|
|
715
|
+
args.push('--skill', path);
|
|
619
716
|
// 子代理托管开启时加载桥接 mod:内建 agent 调用会被转投成 DSH 原生子会话。
|
|
620
717
|
args.push(...commandCodeBridgeArgs(input.sessionId));
|
|
621
718
|
for (const directory of new Set((input.attachments ?? []).map((attachment) => dirname(attachment.path))))
|
|
@@ -629,6 +726,10 @@ export class CommandCodeDriver {
|
|
|
629
726
|
}
|
|
630
727
|
/** 中断当前会话并清理 CLI 进程;不存在活动运行时视为幂等成功。 */
|
|
631
728
|
async interrupt(sessionId) {
|
|
729
|
+
if (this.acpDriver !== undefined) {
|
|
730
|
+
await this.acpDriver.interrupt(sessionId);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
632
733
|
const turn = this.turns.get(sessionId);
|
|
633
734
|
if (turn === undefined)
|
|
634
735
|
return;
|
|
@@ -806,6 +907,7 @@ export class CommandCodeDriver {
|
|
|
806
907
|
this.processes.delete(child);
|
|
807
908
|
terminateChildProcess(child);
|
|
808
909
|
}
|
|
910
|
+
turn.attachmentCleanup?.();
|
|
809
911
|
// 首轮 transcript 已经是 canonical 文件,必须保留给下一轮 --resume 及冷恢复。
|
|
810
912
|
}
|
|
811
913
|
}
|
|
@@ -1024,10 +1126,20 @@ function commandCodeEventChunks(event, state) {
|
|
|
1024
1126
|
}
|
|
1025
1127
|
else if (isToolResult(type)) {
|
|
1026
1128
|
// 子代理托管命中时,mod 用 block 结束内建 agent 调用,真正的执行已经发生在
|
|
1027
|
-
// DSH
|
|
1028
|
-
|
|
1029
|
-
const
|
|
1030
|
-
const
|
|
1129
|
+
// DSH 原生子会话里;这里必须投影为桥接返回的 running/completed/failed 状态,
|
|
1130
|
+
// 而不是把后台创建误报为完成或把普通 hook 拦截误报为托管成功。
|
|
1131
|
+
const redirected = type.includes('blocked') ? consumeBridgeRedirect(state, event) : undefined;
|
|
1132
|
+
const failed = redirected === undefined && (type.includes('error') || type.includes('fail') || type.includes('denied') || type.includes('blocked'));
|
|
1133
|
+
// 后台托管的 hook_blocked 只代表“已创建子会话”。如果这里投影成 completed,
|
|
1134
|
+
// 父 Agent 会跳过 wait/read,子会话随后失败也就没有机会回到父会话。
|
|
1135
|
+
const redirectedStatus = redirected === undefined
|
|
1136
|
+
? undefined
|
|
1137
|
+
: redirected.completed === false || redirected.status === 'running'
|
|
1138
|
+
? 'running'
|
|
1139
|
+
: redirected.ok && redirected.status !== 'failed' && redirected.status !== 'interrupted'
|
|
1140
|
+
? 'completed'
|
|
1141
|
+
: 'failed';
|
|
1142
|
+
const tool = readToolChunk(event, redirectedStatus ?? (failed ? 'failed' : 'completed'));
|
|
1031
1143
|
if (tool !== null) {
|
|
1032
1144
|
chunks.push(tool);
|
|
1033
1145
|
// 工具完成后的正文属于下一条 assistant 消息。即使 CLI 没有发送
|
|
@@ -1051,15 +1163,15 @@ function readFinalText(event) {
|
|
|
1051
1163
|
/** 该 blocked 事件是否来自桥接转投;命中后消费一次记录,避免重复投影。 */
|
|
1052
1164
|
function consumeBridgeRedirect(state, event) {
|
|
1053
1165
|
if (state.bridgeSessionId === '')
|
|
1054
|
-
return
|
|
1166
|
+
return undefined;
|
|
1055
1167
|
const callId = firstToolText(event.callId, event.call_id, event.toolCallId, event.tool_call_id, event.toolUseId, event.tool_use_id, event.id);
|
|
1056
1168
|
if (callId === undefined || callId === '')
|
|
1057
|
-
return
|
|
1169
|
+
return undefined;
|
|
1058
1170
|
try {
|
|
1059
|
-
return getSubagentBridge()?.consumeRedirect(state.bridgeSessionId, callId)
|
|
1171
|
+
return getSubagentBridge()?.consumeRedirect(state.bridgeSessionId, callId);
|
|
1060
1172
|
}
|
|
1061
1173
|
catch {
|
|
1062
|
-
return
|
|
1174
|
+
return undefined;
|
|
1063
1175
|
}
|
|
1064
1176
|
}
|
|
1065
1177
|
function beginMessage(state) {
|
|
@@ -1362,6 +1474,200 @@ function workspaceSlug(workspacePath) {
|
|
|
1362
1474
|
.replace(/^-+|-+$/g, '')
|
|
1363
1475
|
.toLowerCase();
|
|
1364
1476
|
}
|
|
1477
|
+
/** 从用户文本提取显式 Skill mention,和 Codex 的 `/name`、`$name` 语义保持一致。 */
|
|
1478
|
+
function commandCodeSkillNames(prompt) {
|
|
1479
|
+
const names = [];
|
|
1480
|
+
const seen = new Set();
|
|
1481
|
+
for (const match of prompt.matchAll(/(?:^|\s)(?:\$|\/)([A-Za-z0-9][A-Za-z0-9._-]*)(?=\s|$)/gu)) {
|
|
1482
|
+
const name = match[1]?.trim();
|
|
1483
|
+
if (name === undefined || name === '' || seen.has(name))
|
|
1484
|
+
continue;
|
|
1485
|
+
seen.add(name);
|
|
1486
|
+
names.push(name);
|
|
1487
|
+
}
|
|
1488
|
+
return names;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* 按 Command Code 的优先级扫描项目级、用户级和额外 Skill 根目录。
|
|
1492
|
+
*
|
|
1493
|
+
* 只把合法的 `SKILL.md` 摘要交给 Client;绝对路径保留在 Host,供显式 mention
|
|
1494
|
+
* 通过 `--skill` 下发。重名时高优先级目录胜出,避免同一 Skill 被重复注入。
|
|
1495
|
+
*/
|
|
1496
|
+
function scanCommandCodeSkills(cwd, homeDirectory) {
|
|
1497
|
+
const projectRoot = commandCodeProjectRoot(cwd);
|
|
1498
|
+
const userHome = basename(homeDirectory) === '.commandcode' ? dirname(homeDirectory) : homeDirectory;
|
|
1499
|
+
const disabledSkills = readCommandCodeDisabledSkills(projectRoot, homeDirectory);
|
|
1500
|
+
const roots = [
|
|
1501
|
+
join(projectRoot, '.commandcode', 'skills'),
|
|
1502
|
+
...commandCodeProjectAgentsSkillRoots(cwd, userHome),
|
|
1503
|
+
join(homeDirectory, 'skills'),
|
|
1504
|
+
join(userHome, '.agents', 'skills'),
|
|
1505
|
+
...readCommandCodeConfiguredSkillRoots(projectRoot, homeDirectory),
|
|
1506
|
+
];
|
|
1507
|
+
const entries = [];
|
|
1508
|
+
const seenNames = new Set();
|
|
1509
|
+
const visitedDirectories = new Set();
|
|
1510
|
+
for (const root of roots)
|
|
1511
|
+
scanCommandCodeSkillRoot(root, entries, seenNames, visitedDirectories, disabledSkills);
|
|
1512
|
+
return entries;
|
|
1513
|
+
}
|
|
1514
|
+
/** Command Code 会从当前目录向上查找项目 `.agents/skills`,最多跨十级。 */
|
|
1515
|
+
function commandCodeProjectAgentsSkillRoots(cwd, userHome) {
|
|
1516
|
+
const roots = [];
|
|
1517
|
+
let current = resolve(cwd);
|
|
1518
|
+
for (let depth = 0; depth <= 10; depth += 1) {
|
|
1519
|
+
if (current === resolve(userHome))
|
|
1520
|
+
break;
|
|
1521
|
+
roots.push(join(current, '.agents', 'skills'));
|
|
1522
|
+
const parent = dirname(current);
|
|
1523
|
+
if (parent === current)
|
|
1524
|
+
break;
|
|
1525
|
+
current = parent;
|
|
1526
|
+
}
|
|
1527
|
+
return roots;
|
|
1528
|
+
}
|
|
1529
|
+
function commandCodeProjectRoot(cwd) {
|
|
1530
|
+
let current = resolve(cwd);
|
|
1531
|
+
const fallback = current;
|
|
1532
|
+
while (true) {
|
|
1533
|
+
if (existsSync(join(current, '.git')))
|
|
1534
|
+
return current;
|
|
1535
|
+
const parent = dirname(current);
|
|
1536
|
+
if (parent === current)
|
|
1537
|
+
return fallback;
|
|
1538
|
+
current = parent;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
function readCommandCodeConfiguredSkillRoots(projectRoot, homeDirectory) {
|
|
1542
|
+
const settingsLayers = [
|
|
1543
|
+
{ path: join(projectRoot, '.commandcode', 'settings.local.json'), base: projectRoot },
|
|
1544
|
+
{ path: join(projectRoot, '.commandcode', 'settings.json'), base: projectRoot },
|
|
1545
|
+
{ path: join(homeDirectory, 'settings.json'), base: basename(homeDirectory) === '.commandcode' ? dirname(homeDirectory) : homeDirectory },
|
|
1546
|
+
];
|
|
1547
|
+
const layer = settingsLayers.find(({ path }) => Array.isArray(readJson(path)?.skills));
|
|
1548
|
+
if (layer === undefined)
|
|
1549
|
+
return [];
|
|
1550
|
+
const settings = readJson(layer.path);
|
|
1551
|
+
if (!Array.isArray(settings?.skills))
|
|
1552
|
+
return [];
|
|
1553
|
+
const roots = [];
|
|
1554
|
+
for (const value of settings.skills) {
|
|
1555
|
+
if (typeof value !== 'string' || value.trim() === '')
|
|
1556
|
+
continue;
|
|
1557
|
+
const configured = value.trim();
|
|
1558
|
+
const userHome = basename(homeDirectory) === '.commandcode' ? dirname(homeDirectory) : homeDirectory;
|
|
1559
|
+
roots.push(configured.startsWith('~/')
|
|
1560
|
+
? resolve(userHome, configured.slice(2))
|
|
1561
|
+
: resolve(layer.base, configured));
|
|
1562
|
+
}
|
|
1563
|
+
return roots;
|
|
1564
|
+
}
|
|
1565
|
+
function scanCommandCodeSkillRoot(root, entries, seenNames, visitedDirectories, disabledSkills) {
|
|
1566
|
+
if (!existsSync(root))
|
|
1567
|
+
return;
|
|
1568
|
+
let canonicalRoot;
|
|
1569
|
+
try {
|
|
1570
|
+
if (!statSync(root).isDirectory())
|
|
1571
|
+
return;
|
|
1572
|
+
canonicalRoot = realpathSync(root);
|
|
1573
|
+
}
|
|
1574
|
+
catch {
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
const walk = (directory, depth) => {
|
|
1578
|
+
if (depth > 32)
|
|
1579
|
+
return;
|
|
1580
|
+
let canonicalDirectory;
|
|
1581
|
+
try {
|
|
1582
|
+
canonicalDirectory = realpathSync(directory);
|
|
1583
|
+
}
|
|
1584
|
+
catch {
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
if (visitedDirectories.has(canonicalDirectory))
|
|
1588
|
+
return;
|
|
1589
|
+
visitedDirectories.add(canonicalDirectory);
|
|
1590
|
+
const ownSkillFile = join(canonicalDirectory, 'SKILL.md');
|
|
1591
|
+
try {
|
|
1592
|
+
if (statSync(ownSkillFile).isFile()) {
|
|
1593
|
+
const entry = parseCommandCodeSkillFile(ownSkillFile);
|
|
1594
|
+
if (entry !== null && !seenNames.has(entry.name)) {
|
|
1595
|
+
seenNames.add(entry.name);
|
|
1596
|
+
entries.push(disabledSkills.has(entry.name) ? { ...entry, enabled: false } : entry);
|
|
1597
|
+
}
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
catch {
|
|
1602
|
+
// 当前目录不是 Skill 目录,继续检查可能的分组子目录。
|
|
1603
|
+
}
|
|
1604
|
+
let children;
|
|
1605
|
+
try {
|
|
1606
|
+
children = readdirSync(canonicalDirectory, { withFileTypes: true });
|
|
1607
|
+
}
|
|
1608
|
+
catch {
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
for (const child of children) {
|
|
1612
|
+
if (child.name === '.git' || child.name === 'node_modules')
|
|
1613
|
+
continue;
|
|
1614
|
+
const childPath = join(canonicalDirectory, child.name);
|
|
1615
|
+
if (child.isDirectory()) {
|
|
1616
|
+
walk(childPath, depth + 1);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
// 首次遍历也使用规范路径,确保额外目录的符号链接遵守去重规则。
|
|
1621
|
+
walk(canonicalRoot, 0);
|
|
1622
|
+
}
|
|
1623
|
+
/** disabledSkills 是用户设置与项目设置的并集,保持 Command Code 的禁用语义。 */
|
|
1624
|
+
function readCommandCodeDisabledSkills(projectRoot, homeDirectory) {
|
|
1625
|
+
const disabled = new Set();
|
|
1626
|
+
const paths = [
|
|
1627
|
+
join(homeDirectory, 'settings.json'),
|
|
1628
|
+
join(projectRoot, '.commandcode', 'settings.json'),
|
|
1629
|
+
join(projectRoot, '.commandcode', 'settings.local.json'),
|
|
1630
|
+
];
|
|
1631
|
+
for (const path of paths) {
|
|
1632
|
+
const settings = readJson(path);
|
|
1633
|
+
if (!Array.isArray(settings?.disabledSkills))
|
|
1634
|
+
continue;
|
|
1635
|
+
for (const value of settings.disabledSkills) {
|
|
1636
|
+
if (typeof value === 'string' && /^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(value.trim()))
|
|
1637
|
+
disabled.add(value.trim());
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
return disabled;
|
|
1641
|
+
}
|
|
1642
|
+
function parseCommandCodeSkillFile(path) {
|
|
1643
|
+
let source;
|
|
1644
|
+
try {
|
|
1645
|
+
source = readFileSync(path, 'utf8');
|
|
1646
|
+
}
|
|
1647
|
+
catch {
|
|
1648
|
+
return null;
|
|
1649
|
+
}
|
|
1650
|
+
const match = source.match(/^(?:\uFEFF)?---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u);
|
|
1651
|
+
if (match === null)
|
|
1652
|
+
return null;
|
|
1653
|
+
const fields = parseSkillFrontmatter(match[1] ?? '');
|
|
1654
|
+
if (fields === null)
|
|
1655
|
+
return null;
|
|
1656
|
+
const name = typeof fields.name === 'string' ? fields.name.trim() : '';
|
|
1657
|
+
const description = typeof fields.description === 'string' ? fields.description.trim() : '';
|
|
1658
|
+
const directoryName = basename(dirname(path));
|
|
1659
|
+
if (name.length > 64 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(name)
|
|
1660
|
+
|| name !== directoryName
|
|
1661
|
+
|| description === '')
|
|
1662
|
+
return null;
|
|
1663
|
+
return {
|
|
1664
|
+
id: name,
|
|
1665
|
+
name,
|
|
1666
|
+
description,
|
|
1667
|
+
enabled: fields['user-invocable'] !== false,
|
|
1668
|
+
path: dirname(path),
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1365
1671
|
function buildMaxTurnsReachedMessage(autoContinueCount, maxTurns) {
|
|
1366
1672
|
const autoContinueSuffix = autoContinueCount > 0
|
|
1367
1673
|
? `,自动续跑 ${autoContinueCount} 次后仍未完成`
|
|
@@ -1519,6 +1825,122 @@ function readJson(path) { if (!existsSync(path))
|
|
|
1519
1825
|
catch {
|
|
1520
1826
|
return null;
|
|
1521
1827
|
} }
|
|
1828
|
+
/** 保留启动选择作为进程复用条件;ACP 的实际会话状态由原生协议显式设置。 */
|
|
1829
|
+
function commandCodeAcpArgs(input) {
|
|
1830
|
+
const args = ['acp', ...commandCodePermissionArgs(input.permission)];
|
|
1831
|
+
if (input.plan === true && !args.includes('--plan'))
|
|
1832
|
+
args.push('--plan');
|
|
1833
|
+
if (input.enableAskUserQuestion === true || input.runtimeEnv?.CMD_TOOLS_ASK_USER_QUESTION_ENABLE === 'true') {
|
|
1834
|
+
args.push('--tools-enable', 'ask_user_question');
|
|
1835
|
+
}
|
|
1836
|
+
for (const path of input.skillPaths ?? [])
|
|
1837
|
+
args.push('--skill', path);
|
|
1838
|
+
if (input.modelId !== undefined && input.modelId !== 'provider-default')
|
|
1839
|
+
args.push('--model', input.modelId);
|
|
1840
|
+
const effort = input.effortId?.trim().toLowerCase();
|
|
1841
|
+
if (effort !== undefined && VALID_EFFORTS.has(effort))
|
|
1842
|
+
args.push('--effort', effort);
|
|
1843
|
+
return args;
|
|
1844
|
+
}
|
|
1845
|
+
/**
|
|
1846
|
+
* Command Code 1.74.3 的 acp 子命令不执行普通 CLI action 的参数初始化。
|
|
1847
|
+
* 仅传 --model/--effort/--permission-mode 会被静默忽略,恢复的会话还会沿用旧模型。
|
|
1848
|
+
* 必须在 prompt 前设置线程状态,且不能吞掉模型或权限设置失败。
|
|
1849
|
+
*/
|
|
1850
|
+
async function configureCommandCodeAcpSession(rpc, sessionId, input) {
|
|
1851
|
+
const requestOptions = { signal: input.signal, killOnAbort: false };
|
|
1852
|
+
if (input.modelId !== undefined && input.modelId !== 'provider-default') {
|
|
1853
|
+
await rpc.request('session/set_model', { sessionId, modelId: input.modelId }, requestOptions);
|
|
1854
|
+
}
|
|
1855
|
+
await rpc.request('session/set_mode', { sessionId, modeId: commandCodeAcpMode(input) }, requestOptions);
|
|
1856
|
+
const effort = input.effortId?.trim().toLowerCase();
|
|
1857
|
+
if (effort !== undefined && VALID_EFFORTS.has(effort)) {
|
|
1858
|
+
await rpc.request('session/set_config_option', { sessionId, configId: 'effort', value: effort }, requestOptions);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
/** ACP 使用权限引擎的模式 ID,与普通 CLI 的 accept-edits/yolo 别名不同。 */
|
|
1862
|
+
function commandCodeAcpMode(input) {
|
|
1863
|
+
const permission = input.permission;
|
|
1864
|
+
if (input.plan === true || permission?.sandboxMode === 'read-only')
|
|
1865
|
+
return 'plan';
|
|
1866
|
+
if (permission?.approvalPolicy !== 'never')
|
|
1867
|
+
return 'default';
|
|
1868
|
+
if (permission.sandboxMode === 'danger-full-access')
|
|
1869
|
+
return 'bypass';
|
|
1870
|
+
if (permission.sandboxMode === 'workspace-write')
|
|
1871
|
+
return 'auto-accept';
|
|
1872
|
+
return 'default';
|
|
1873
|
+
}
|
|
1874
|
+
/** Command Code 将 ask_user_question 编译成 `session/request_permission`。 */
|
|
1875
|
+
function readCommandCodeQuestionRequest(message) {
|
|
1876
|
+
const method = message.method?.toLowerCase();
|
|
1877
|
+
if (method !== 'session/request_permission' || (typeof message.id !== 'string' && typeof message.id !== 'number'))
|
|
1878
|
+
return null;
|
|
1879
|
+
const params = isRecord(message.params) ? message.params : {};
|
|
1880
|
+
const tool = isRecord(params.toolCall) ? params.toolCall : isRecord(params.tool_call) ? params.tool_call : {};
|
|
1881
|
+
const kind = firstToolText(tool.kind, params.kind)?.toLowerCase();
|
|
1882
|
+
const toolName = firstToolText(tool.name, tool.toolName, tool.tool_name);
|
|
1883
|
+
if (kind !== 'other' && toolName !== 'ask_user_question')
|
|
1884
|
+
return null;
|
|
1885
|
+
const rawInput = isRecord(tool.rawInput) ? tool.rawInput : isRecord(tool.raw_input) ? tool.raw_input : {};
|
|
1886
|
+
const questionText = firstToolText(rawInput.question, rawInput.prompt, params.question, params.prompt);
|
|
1887
|
+
const rawOptions = Array.isArray(rawInput.options)
|
|
1888
|
+
? rawInput.options
|
|
1889
|
+
: Array.isArray(params.questionOptions)
|
|
1890
|
+
? params.questionOptions
|
|
1891
|
+
: [];
|
|
1892
|
+
if (questionText === undefined || rawOptions.length === 0)
|
|
1893
|
+
return null;
|
|
1894
|
+
const options = rawOptions.flatMap((rawOption) => {
|
|
1895
|
+
if (typeof rawOption === 'string' && rawOption.trim() !== '')
|
|
1896
|
+
return [{ label: rawOption.trim() }];
|
|
1897
|
+
if (!isRecord(rawOption))
|
|
1898
|
+
return [];
|
|
1899
|
+
const label = firstToolText(rawOption.label, rawOption.name, rawOption.title, rawOption.value);
|
|
1900
|
+
if (label === undefined)
|
|
1901
|
+
return [];
|
|
1902
|
+
const description = firstToolText(rawOption.description, rawOption.detail);
|
|
1903
|
+
return [{ label, ...(description === undefined ? {} : { description }) }];
|
|
1904
|
+
});
|
|
1905
|
+
if (options.length === 0)
|
|
1906
|
+
return null;
|
|
1907
|
+
const protocolOptions = Array.isArray(params.options) ? params.options : [];
|
|
1908
|
+
const optionIds = options.map((_, index) => {
|
|
1909
|
+
const protocolOption = isRecord(protocolOptions[index]) ? protocolOptions[index] : {};
|
|
1910
|
+
return firstToolText(protocolOption.optionId, protocolOption.option_id, protocolOption.id) ?? `option_${index}`;
|
|
1911
|
+
});
|
|
1912
|
+
const requestId = String(message.id);
|
|
1913
|
+
const questionId = `command-code-question-${requestId}`;
|
|
1914
|
+
const header = firstToolText(rawInput.header, params.header);
|
|
1915
|
+
return {
|
|
1916
|
+
requestId,
|
|
1917
|
+
rpcId: message.id,
|
|
1918
|
+
questions: [{
|
|
1919
|
+
id: questionId,
|
|
1920
|
+
question: questionText,
|
|
1921
|
+
...(header === undefined ? {} : { header }),
|
|
1922
|
+
options,
|
|
1923
|
+
}],
|
|
1924
|
+
respond: (response) => {
|
|
1925
|
+
const answer = response.answers.find((item) => item.id === questionId) ?? response.answers[0];
|
|
1926
|
+
const custom = answer?.custom?.trim() ?? '';
|
|
1927
|
+
const selected = answer?.selected[0]?.trim() ?? '';
|
|
1928
|
+
const selectedIndex = options.findIndex((option) => option.label === selected);
|
|
1929
|
+
if (custom !== '') {
|
|
1930
|
+
// `optionId` 使用保留值,未加载兼容桥时会安全地取消,不会误选第一项。
|
|
1931
|
+
// loader 会从 _meta 读取原文,并把它作为 ask_user_question 的工具答案。
|
|
1932
|
+
return {
|
|
1933
|
+
outcome: { outcome: 'selected', optionId: '__codingns_custom__' },
|
|
1934
|
+
answers: [{ questionIndex: 0, selectedOptions: [custom] }],
|
|
1935
|
+
_meta: { 'codingns/questionAnswer': custom },
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
if (selected === '' || selectedIndex < 0)
|
|
1939
|
+
return { outcome: { outcome: 'cancelled' } };
|
|
1940
|
+
return { outcome: { outcome: 'selected', optionId: optionIds[selectedIndex] } };
|
|
1941
|
+
},
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1522
1944
|
function isRecord(value) { return typeof value === 'object' && value !== null && !Array.isArray(value); }
|
|
1523
1945
|
function emptyCatalog() { return { groups: [], currentModel: null, currentEffort: null }; }
|
|
1524
1946
|
//# sourceMappingURL=command-code-driver.js.map
|