@kaitranntt/ccs 7.63.1 → 7.64.0-dev.2
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.md +12 -3
- package/dist/api/services/profile-lifecycle-service.js +4 -4
- package/dist/api/services/profile-lifecycle-service.js.map +1 -1
- package/dist/api/services/profile-types.d.ts +17 -0
- package/dist/api/services/profile-types.d.ts.map +1 -1
- package/dist/api/services/profile-writer.d.ts.map +1 -1
- package/dist/api/services/profile-writer.js +3 -5
- package/dist/api/services/profile-writer.js.map +1 -1
- package/dist/ccs.js +82 -15
- package/dist/ccs.js.map +1 -1
- package/dist/cliproxy/accounts/email-account-identity.d.ts +12 -0
- package/dist/cliproxy/accounts/email-account-identity.d.ts.map +1 -0
- package/dist/cliproxy/accounts/email-account-identity.js +124 -0
- package/dist/cliproxy/accounts/email-account-identity.js.map +1 -0
- package/dist/cliproxy/accounts/query.d.ts.map +1 -1
- package/dist/cliproxy/accounts/query.js +15 -8
- package/dist/cliproxy/accounts/query.js.map +1 -1
- package/dist/cliproxy/accounts/registry.d.ts +6 -0
- package/dist/cliproxy/accounts/registry.d.ts.map +1 -1
- package/dist/cliproxy/accounts/registry.js +136 -42
- package/dist/cliproxy/accounts/registry.js.map +1 -1
- package/dist/cliproxy/auth/token-manager.d.ts.map +1 -1
- package/dist/cliproxy/auth/token-manager.js +45 -11
- package/dist/cliproxy/auth/token-manager.js.map +1 -1
- package/dist/cliproxy/executor/env-resolver.d.ts +27 -0
- package/dist/cliproxy/executor/env-resolver.d.ts.map +1 -1
- package/dist/cliproxy/executor/env-resolver.js +87 -3
- package/dist/cliproxy/executor/env-resolver.js.map +1 -1
- package/dist/cliproxy/executor/index.d.ts.map +1 -1
- package/dist/cliproxy/executor/index.js +55 -12
- package/dist/cliproxy/executor/index.js.map +1 -1
- package/dist/cliproxy/model-catalog.d.ts +6 -0
- package/dist/cliproxy/model-catalog.d.ts.map +1 -1
- package/dist/cliproxy/model-catalog.js +38 -1
- package/dist/cliproxy/model-catalog.js.map +1 -1
- package/dist/cliproxy/proxy-config-resolver.d.ts +2 -1
- package/dist/cliproxy/proxy-config-resolver.d.ts.map +1 -1
- package/dist/cliproxy/proxy-config-resolver.js +1 -0
- package/dist/cliproxy/proxy-config-resolver.js.map +1 -1
- package/dist/cliproxy/proxy-target-resolver.d.ts +2 -0
- package/dist/cliproxy/proxy-target-resolver.d.ts.map +1 -1
- package/dist/cliproxy/proxy-target-resolver.js +3 -0
- package/dist/cliproxy/proxy-target-resolver.js.map +1 -1
- package/dist/cliproxy/quota-fetcher-codex.d.ts +0 -3
- package/dist/cliproxy/quota-fetcher-codex.d.ts.map +1 -1
- package/dist/cliproxy/quota-fetcher-codex.js +46 -17
- package/dist/cliproxy/quota-fetcher-codex.js.map +1 -1
- package/dist/cliproxy/remote-auth-fetcher.d.ts.map +1 -1
- package/dist/cliproxy/remote-auth-fetcher.js +89 -8
- package/dist/cliproxy/remote-auth-fetcher.js.map +1 -1
- package/dist/cliproxy/services/variant-settings.d.ts.map +1 -1
- package/dist/cliproxy/services/variant-settings.js +23 -10
- package/dist/cliproxy/services/variant-settings.js.map +1 -1
- package/dist/cliproxy/stats-transformer.d.ts.map +1 -1
- package/dist/cliproxy/stats-transformer.js +26 -3
- package/dist/cliproxy/stats-transformer.js.map +1 -1
- package/dist/cliproxy/types.d.ts +2 -0
- package/dist/cliproxy/types.d.ts.map +1 -1
- package/dist/commands/cliproxy/quota-subcommand.d.ts.map +1 -1
- package/dist/commands/cliproxy/quota-subcommand.js +25 -22
- package/dist/commands/cliproxy/quota-subcommand.js.map +1 -1
- package/dist/commands/cliproxy/variant-subcommand.d.ts.map +1 -1
- package/dist/commands/cliproxy/variant-subcommand.js +14 -6
- package/dist/commands/cliproxy/variant-subcommand.js.map +1 -1
- package/dist/commands/config-image-analysis-command.d.ts.map +1 -1
- package/dist/commands/config-image-analysis-command.js +87 -1
- package/dist/commands/config-image-analysis-command.js.map +1 -1
- package/dist/commands/install-command.d.ts.map +1 -1
- package/dist/commands/install-command.js +8 -2
- package/dist/commands/install-command.js.map +1 -1
- package/dist/config/unified-config-loader.d.ts.map +1 -1
- package/dist/config/unified-config-loader.js +9 -4
- package/dist/config/unified-config-loader.js.map +1 -1
- package/dist/config/unified-config-types.d.ts +4 -0
- package/dist/config/unified-config-types.d.ts.map +1 -1
- package/dist/config/unified-config-types.js +4 -2
- package/dist/config/unified-config-types.js.map +1 -1
- package/dist/copilot/copilot-executor.d.ts +13 -0
- package/dist/copilot/copilot-executor.d.ts.map +1 -1
- package/dist/copilot/copilot-executor.js +63 -4
- package/dist/copilot/copilot-executor.js.map +1 -1
- package/dist/delegation/executor/result-aggregator.d.ts +2 -1
- package/dist/delegation/executor/result-aggregator.d.ts.map +1 -1
- package/dist/delegation/executor/result-aggregator.js +21 -1
- package/dist/delegation/executor/result-aggregator.js.map +1 -1
- package/dist/delegation/executor/types.d.ts +6 -0
- package/dist/delegation/executor/types.d.ts.map +1 -1
- package/dist/delegation/headless-executor.d.ts.map +1 -1
- package/dist/delegation/headless-executor.js +69 -4
- package/dist/delegation/headless-executor.js.map +1 -1
- package/dist/management/checks/image-analysis-check.js +1 -1
- package/dist/management/checks/image-analysis-check.js.map +1 -1
- package/dist/management/instance-manager.d.ts +1 -1
- package/dist/management/instance-manager.d.ts.map +1 -1
- package/dist/management/instance-manager.js +10 -2
- package/dist/management/instance-manager.js.map +1 -1
- package/dist/shared/compatible-cli-contracts.d.ts +4 -0
- package/dist/shared/compatible-cli-contracts.d.ts.map +1 -1
- package/dist/targets/codex-adapter.d.ts.map +1 -1
- package/dist/targets/codex-adapter.js +78 -3
- package/dist/targets/codex-adapter.js.map +1 -1
- package/dist/targets/codex-detector.d.ts.map +1 -1
- package/dist/targets/codex-detector.js +28 -7
- package/dist/targets/codex-detector.js.map +1 -1
- package/dist/types/config.d.ts +5 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/dist/ui/assets/{accounts-DkxZnPJE.js → accounts-BHEYnq6b.js} +1 -1
- package/dist/ui/assets/{alert-dialog-CiYMglgR.js → alert-dialog-D0EFRcfB.js} +1 -1
- package/dist/ui/assets/api-DhM3BYXr.js +4 -0
- package/dist/ui/assets/{auth-section-BMaKBRA_.js → auth-section-DVp8FQGm.js} +1 -1
- package/dist/ui/assets/{backups-section-DOpSADoH.js → backups-section-CRo0NZkA.js} +1 -1
- package/dist/ui/assets/channels-uZ_9CBqO.js +1 -0
- package/dist/ui/assets/checkbox-32DNqW_Q.js +1 -0
- package/dist/ui/assets/{claude-extension-B5RngGem.js → claude-extension-BfXlz5gV.js} +1 -1
- package/dist/ui/assets/cliproxy-DjNY9H-U.js +3 -0
- package/dist/ui/assets/{cliproxy-ai-providers-DVaaS-CT.js → cliproxy-ai-providers-5SHLMHiy.js} +5 -5
- package/dist/ui/assets/cliproxy-control-panel-Zax_m1AC.js +1 -0
- package/dist/ui/assets/codex-CRUSpjsu.js +27 -0
- package/dist/ui/assets/{confirm-dialog-B9vRgowr.js → confirm-dialog-DVf5ZmCZ.js} +1 -1
- package/dist/ui/assets/copilot-BZrihl_Z.js +3 -0
- package/dist/ui/assets/cursor-BP4nbEk_.js +1 -0
- package/dist/ui/assets/{droid-DshEfT1H.js → droid-BG92rdM2.js} +2 -2
- package/dist/ui/assets/globalenv-section-Cf6dKgSf.js +1 -0
- package/dist/ui/assets/{health-CE0VQs6K.js → health-BTy1UZs3.js} +1 -1
- package/dist/ui/assets/icons-CeH5899d.js +1 -0
- package/dist/ui/assets/index-B6SrL1O-.css +1 -0
- package/dist/ui/assets/index-BVeN0dIB.js +1 -0
- package/dist/ui/assets/index-Corv1lSo.js +69 -0
- package/dist/ui/assets/index-DHrTq-0n.js +1 -0
- package/dist/ui/assets/index-DuRYaONg.js +1 -0
- package/dist/ui/assets/index-N2ZSJurX.js +1 -0
- package/dist/ui/assets/index-wg7UtkFv.js +1 -0
- package/dist/ui/assets/{masked-input-B2tcbvAj.js → masked-input-DX9bedLy.js} +1 -1
- package/dist/ui/assets/{proxy-status-widget-BnJD49TF.js → proxy-status-widget-DVDMuZK5.js} +1 -1
- package/dist/ui/assets/{radix-ui-Dt3edmE5.js → radix-ui-C98W0NRG.js} +1 -1
- package/dist/ui/assets/{raw-json-settings-editor-panel-DnUbq1__.js → raw-json-settings-editor-panel-Dkt5E6Z_.js} +1 -1
- package/dist/ui/assets/{searchable-select-ULayr5K1.js → searchable-select-BP3Q1-Yn.js} +1 -1
- package/dist/ui/assets/separator-BLGGUlh9.js +1 -0
- package/dist/ui/assets/shared-G0XRyLig.js +8 -0
- package/dist/ui/assets/{table-E5IxHhrW.js → table-B4lRrWC-.js} +1 -1
- package/dist/ui/assets/tanstack-CfKik0yL.js +4 -0
- package/dist/ui/assets/updates--A2Sdo7N.js +1 -0
- package/dist/ui/index.html +5 -5
- package/dist/utils/claude-config-path.d.ts +2 -0
- package/dist/utils/claude-config-path.d.ts.map +1 -1
- package/dist/utils/claude-config-path.js +6 -1
- package/dist/utils/claude-config-path.js.map +1 -1
- package/dist/utils/hooks/get-image-analysis-hook-env.d.ts +3 -2
- package/dist/utils/hooks/get-image-analysis-hook-env.d.ts.map +1 -1
- package/dist/utils/hooks/get-image-analysis-hook-env.js +15 -6
- package/dist/utils/hooks/get-image-analysis-hook-env.js.map +1 -1
- package/dist/utils/hooks/image-analysis-backend-resolver.d.ts +53 -0
- package/dist/utils/hooks/image-analysis-backend-resolver.d.ts.map +1 -0
- package/dist/utils/hooks/image-analysis-backend-resolver.js +376 -0
- package/dist/utils/hooks/image-analysis-backend-resolver.js.map +1 -0
- package/dist/utils/hooks/image-analysis-runtime-status.d.ts +17 -0
- package/dist/utils/hooks/image-analysis-runtime-status.d.ts.map +1 -0
- package/dist/utils/hooks/image-analysis-runtime-status.js +132 -0
- package/dist/utils/hooks/image-analysis-runtime-status.js.map +1 -0
- package/dist/utils/hooks/image-analyzer-profile-hook-injector.d.ts +6 -5
- package/dist/utils/hooks/image-analyzer-profile-hook-injector.d.ts.map +1 -1
- package/dist/utils/hooks/image-analyzer-profile-hook-injector.js +37 -17
- package/dist/utils/hooks/image-analyzer-profile-hook-injector.js.map +1 -1
- package/dist/utils/hooks/index.d.ts +2 -0
- package/dist/utils/hooks/index.d.ts.map +1 -1
- package/dist/utils/hooks/index.js +8 -1
- package/dist/utils/hooks/index.js.map +1 -1
- package/dist/utils/websearch/claude-tool-args.d.ts +5 -0
- package/dist/utils/websearch/claude-tool-args.d.ts.map +1 -0
- package/dist/utils/websearch/claude-tool-args.js +125 -0
- package/dist/utils/websearch/claude-tool-args.js.map +1 -0
- package/dist/utils/websearch/hook-env.d.ts.map +1 -1
- package/dist/utils/websearch/hook-env.js +8 -0
- package/dist/utils/websearch/hook-env.js.map +1 -1
- package/dist/utils/websearch/hook-installer.d.ts +3 -2
- package/dist/utils/websearch/hook-installer.d.ts.map +1 -1
- package/dist/utils/websearch/hook-installer.js +3 -2
- package/dist/utils/websearch/hook-installer.js.map +1 -1
- package/dist/utils/websearch/index.d.ts +3 -0
- package/dist/utils/websearch/index.d.ts.map +1 -1
- package/dist/utils/websearch/index.js +23 -2
- package/dist/utils/websearch/index.js.map +1 -1
- package/dist/utils/websearch/mcp-installer.d.ts +14 -0
- package/dist/utils/websearch/mcp-installer.d.ts.map +1 -0
- package/dist/utils/websearch/mcp-installer.js +351 -0
- package/dist/utils/websearch/mcp-installer.js.map +1 -0
- package/dist/utils/websearch/profile-hook-injector.d.ts +5 -3
- package/dist/utils/websearch/profile-hook-injector.d.ts.map +1 -1
- package/dist/utils/websearch/profile-hook-injector.js +5 -3
- package/dist/utils/websearch/profile-hook-injector.js.map +1 -1
- package/dist/utils/websearch/status.d.ts.map +1 -1
- package/dist/utils/websearch/status.js +67 -1
- package/dist/utils/websearch/status.js.map +1 -1
- package/dist/utils/websearch/trace.d.ts +23 -0
- package/dist/utils/websearch/trace.d.ts.map +1 -0
- package/dist/utils/websearch/trace.js +206 -0
- package/dist/utils/websearch/trace.js.map +1 -0
- package/dist/utils/websearch-manager.d.ts +11 -11
- package/dist/utils/websearch-manager.d.ts.map +1 -1
- package/dist/utils/websearch-manager.js +32 -17
- package/dist/utils/websearch-manager.js.map +1 -1
- package/dist/web-server/index.d.ts.map +1 -1
- package/dist/web-server/index.js +9 -1
- package/dist/web-server/index.js.map +1 -1
- package/dist/web-server/routes/account-routes.d.ts.map +1 -1
- package/dist/web-server/routes/account-routes.js +2 -1
- package/dist/web-server/routes/account-routes.js.map +1 -1
- package/dist/web-server/routes/cliproxy-auth-routes.d.ts.map +1 -1
- package/dist/web-server/routes/cliproxy-auth-routes.js +1 -1
- package/dist/web-server/routes/cliproxy-auth-routes.js.map +1 -1
- package/dist/web-server/routes/cliproxy-local-proxy.d.ts +20 -0
- package/dist/web-server/routes/cliproxy-local-proxy.d.ts.map +1 -0
- package/dist/web-server/routes/cliproxy-local-proxy.js +117 -0
- package/dist/web-server/routes/cliproxy-local-proxy.js.map +1 -0
- package/dist/web-server/routes/image-analysis-routes.d.ts +3 -0
- package/dist/web-server/routes/image-analysis-routes.d.ts.map +1 -0
- package/dist/web-server/routes/image-analysis-routes.js +362 -0
- package/dist/web-server/routes/image-analysis-routes.js.map +1 -0
- package/dist/web-server/routes/index.d.ts.map +1 -1
- package/dist/web-server/routes/index.js +2 -0
- package/dist/web-server/routes/index.js.map +1 -1
- package/dist/web-server/routes/settings-routes.d.ts.map +1 -1
- package/dist/web-server/routes/settings-routes.js +67 -5
- package/dist/web-server/routes/settings-routes.js.map +1 -1
- package/dist/web-server/services/codex-dashboard-service.d.ts.map +1 -1
- package/dist/web-server/services/codex-dashboard-service.js +27 -8
- package/dist/web-server/services/codex-dashboard-service.js.map +1 -1
- package/lib/hooks/websearch-transformer.cjs +660 -96
- package/lib/mcp/ccs-websearch-server.cjs +339 -0
- package/package.json +3 -2
- package/scripts/github/normalize-ai-review-output.mjs +232 -16
- package/scripts/github/prepare-ai-review-scope.mjs +317 -0
- package/dist/ui/assets/api-DaOtMRT4.js +0 -4
- package/dist/ui/assets/channels-zDFV-BlC.js +0 -1
- package/dist/ui/assets/checkbox-Cb5AZBZL.js +0 -1
- package/dist/ui/assets/cliproxy-VYe0Qov1.js +0 -3
- package/dist/ui/assets/cliproxy-control-panel-FVIQcFti.js +0 -1
- package/dist/ui/assets/codex-D2yIwOs4.js +0 -27
- package/dist/ui/assets/copilot-HvsOp6hu.js +0 -3
- package/dist/ui/assets/cursor-C1XOjAWS.js +0 -1
- package/dist/ui/assets/globalenv-section-CmcMkb6z.js +0 -1
- package/dist/ui/assets/icons-EMBHZkGo.js +0 -1
- package/dist/ui/assets/index-6dNBcNC3.js +0 -1
- package/dist/ui/assets/index-BAuT6yuc.css +0 -1
- package/dist/ui/assets/index-CesVGA6m.js +0 -1
- package/dist/ui/assets/index-CmKclBR1.js +0 -1
- package/dist/ui/assets/index-CmtSgCxo.js +0 -1
- package/dist/ui/assets/index-DAtuJuGe.js +0 -69
- package/dist/ui/assets/separator--ZH5ZM-3.js +0 -1
- package/dist/ui/assets/shared-qizFb9Ye.js +0 -8
- package/dist/ui/assets/switch-DmDIWykO.js +0 -1
- package/dist/ui/assets/tanstack-B8i0evp-.js +0 -4
- package/dist/ui/assets/updates-2Uu4Mgtg.js +0 -1
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getActiveProviderIds,
|
|
5
|
+
getQueryFingerprint,
|
|
6
|
+
getSkipReason,
|
|
7
|
+
hasAnyActiveProviders,
|
|
8
|
+
runLocalWebSearch,
|
|
9
|
+
shouldSkipHook,
|
|
10
|
+
traceWebSearchEvent,
|
|
11
|
+
} = require('../hooks/websearch-transformer.cjs');
|
|
12
|
+
|
|
13
|
+
const PROTOCOL_VERSION = '2024-11-05';
|
|
14
|
+
const SERVER_NAME = 'ccs-websearch';
|
|
15
|
+
const SERVER_VERSION = '1.0.0';
|
|
16
|
+
const TOOL_NAME = 'WebSearch';
|
|
17
|
+
const TOOL_ALIASES = ['search'];
|
|
18
|
+
const TOOL_DESCRIPTION =
|
|
19
|
+
'Third-party WebSearch replacement for CCS-managed Claude launches. Use this instead of Bash/curl/http fetches for web lookups. Provider order: Exa, Tavily, Brave Search, DuckDuckGo, then optional legacy CLI fallback.';
|
|
20
|
+
|
|
21
|
+
function isSupportedToolName(name) {
|
|
22
|
+
return name === TOOL_NAME || TOOL_ALIASES.includes(name);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let inputBuffer = Buffer.alloc(0);
|
|
26
|
+
const sessionState = {
|
|
27
|
+
initializeCount: 0,
|
|
28
|
+
toolsListCount: 0,
|
|
29
|
+
exposed: false,
|
|
30
|
+
toolCalls: 0,
|
|
31
|
+
};
|
|
32
|
+
let sessionSummaryWritten = false;
|
|
33
|
+
|
|
34
|
+
function shouldExposeTools() {
|
|
35
|
+
return !shouldSkipHook() && hasAnyActiveProviders();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getTools() {
|
|
39
|
+
if (!shouldExposeTools()) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
name: TOOL_NAME,
|
|
46
|
+
description: TOOL_DESCRIPTION,
|
|
47
|
+
inputSchema: {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
query: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description:
|
|
53
|
+
'Web query to resolve through CCS providers. Prefer this tool over ad hoc Bash/curl lookups when you need current web information.',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
required: ['query'],
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function writeMessage(message) {
|
|
64
|
+
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function writeResponse(id, result) {
|
|
68
|
+
writeMessage({
|
|
69
|
+
jsonrpc: '2.0',
|
|
70
|
+
id,
|
|
71
|
+
result,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function writeError(id, code, message) {
|
|
76
|
+
writeMessage({
|
|
77
|
+
jsonrpc: '2.0',
|
|
78
|
+
id,
|
|
79
|
+
error: {
|
|
80
|
+
code,
|
|
81
|
+
message,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function handleToolCall(message) {
|
|
87
|
+
const id = message.id;
|
|
88
|
+
const params = message.params || {};
|
|
89
|
+
const toolArgs = params.arguments || {};
|
|
90
|
+
const toolName = params.name || '<missing>';
|
|
91
|
+
const query = typeof toolArgs.query === 'string' ? toolArgs.query.trim() : '';
|
|
92
|
+
const fingerprint = getQueryFingerprint(query);
|
|
93
|
+
|
|
94
|
+
if (!isSupportedToolName(toolName)) {
|
|
95
|
+
traceWebSearchEvent('mcp_tool_call_rejected', {
|
|
96
|
+
source: 'mcp',
|
|
97
|
+
reason: 'unknown_tool',
|
|
98
|
+
toolName,
|
|
99
|
+
});
|
|
100
|
+
writeError(id, -32602, `Unknown tool: ${toolName}`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
sessionState.toolCalls += 1;
|
|
105
|
+
traceWebSearchEvent('mcp_tool_call_received', {
|
|
106
|
+
source: 'mcp',
|
|
107
|
+
toolName,
|
|
108
|
+
...fingerprint,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (!shouldExposeTools()) {
|
|
112
|
+
traceWebSearchEvent('mcp_tool_call_unavailable', {
|
|
113
|
+
source: 'mcp',
|
|
114
|
+
toolName,
|
|
115
|
+
exposed: false,
|
|
116
|
+
skipReason: getSkipReason(),
|
|
117
|
+
activeProviderIds: getActiveProviderIds(),
|
|
118
|
+
...fingerprint,
|
|
119
|
+
});
|
|
120
|
+
writeResponse(id, {
|
|
121
|
+
content: [
|
|
122
|
+
{
|
|
123
|
+
type: 'text',
|
|
124
|
+
text: 'CCS WebSearch is unavailable for this profile or no providers are ready.',
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
isError: true,
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!query) {
|
|
133
|
+
traceWebSearchEvent('mcp_tool_call_rejected', {
|
|
134
|
+
source: 'mcp',
|
|
135
|
+
reason: 'empty_query',
|
|
136
|
+
toolName,
|
|
137
|
+
});
|
|
138
|
+
writeError(id, -32602, `Tool "${TOOL_NAME}" requires a non-empty string query.`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const result = await runLocalWebSearch(query);
|
|
143
|
+
if (result.success) {
|
|
144
|
+
traceWebSearchEvent('mcp_tool_call_result', {
|
|
145
|
+
source: 'mcp',
|
|
146
|
+
toolName,
|
|
147
|
+
success: true,
|
|
148
|
+
providerId: result.providerId,
|
|
149
|
+
providerName: result.providerName,
|
|
150
|
+
...fingerprint,
|
|
151
|
+
});
|
|
152
|
+
writeResponse(id, {
|
|
153
|
+
content: [{ type: 'text', text: result.content }],
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
traceWebSearchEvent('mcp_tool_call_result', {
|
|
159
|
+
source: 'mcp',
|
|
160
|
+
toolName,
|
|
161
|
+
success: false,
|
|
162
|
+
noActiveProviders: Boolean(result.noActiveProviders),
|
|
163
|
+
errorCount: result.errors.length,
|
|
164
|
+
...fingerprint,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const errorDetail =
|
|
168
|
+
result.noActiveProviders || result.errors.length === 0
|
|
169
|
+
? 'No active WebSearch providers are ready.'
|
|
170
|
+
: result.errors.map((entry) => `${entry.provider}: ${entry.error}`).join(' | ');
|
|
171
|
+
|
|
172
|
+
writeResponse(id, {
|
|
173
|
+
content: [
|
|
174
|
+
{
|
|
175
|
+
type: 'text',
|
|
176
|
+
text: `CCS local WebSearch failed for "${query}". ${errorDetail}`,
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
isError: true,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function handleMessage(message) {
|
|
184
|
+
if (!message || message.jsonrpc !== '2.0' || typeof message.method !== 'string') {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
switch (message.method) {
|
|
189
|
+
case 'initialize':
|
|
190
|
+
sessionState.initializeCount += 1;
|
|
191
|
+
sessionState.exposed = sessionState.exposed || shouldExposeTools();
|
|
192
|
+
traceWebSearchEvent('mcp_initialize', {
|
|
193
|
+
source: 'mcp',
|
|
194
|
+
exposed: shouldExposeTools(),
|
|
195
|
+
skipReason: getSkipReason(),
|
|
196
|
+
activeProviderIds: getActiveProviderIds(),
|
|
197
|
+
});
|
|
198
|
+
writeResponse(message.id, {
|
|
199
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
200
|
+
capabilities: {
|
|
201
|
+
tools: {},
|
|
202
|
+
},
|
|
203
|
+
serverInfo: {
|
|
204
|
+
name: SERVER_NAME,
|
|
205
|
+
version: SERVER_VERSION,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
case 'notifications/initialized':
|
|
210
|
+
return;
|
|
211
|
+
case 'ping':
|
|
212
|
+
writeResponse(message.id, {});
|
|
213
|
+
return;
|
|
214
|
+
case 'tools/list':
|
|
215
|
+
sessionState.toolsListCount += 1;
|
|
216
|
+
{
|
|
217
|
+
const tools = getTools();
|
|
218
|
+
const exposed = tools.length > 0;
|
|
219
|
+
sessionState.exposed = sessionState.exposed || exposed;
|
|
220
|
+
traceWebSearchEvent('mcp_tools_list', {
|
|
221
|
+
source: 'mcp',
|
|
222
|
+
exposed,
|
|
223
|
+
toolNames: tools.map((tool) => tool.name),
|
|
224
|
+
activeProviderIds: getActiveProviderIds(),
|
|
225
|
+
skipReason: getSkipReason(),
|
|
226
|
+
});
|
|
227
|
+
writeResponse(message.id, { tools });
|
|
228
|
+
}
|
|
229
|
+
return;
|
|
230
|
+
case 'tools/call':
|
|
231
|
+
await handleToolCall(message);
|
|
232
|
+
return;
|
|
233
|
+
default:
|
|
234
|
+
if (message.id !== undefined) {
|
|
235
|
+
writeError(message.id, -32601, `Method not found: ${message.method}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function writeSessionSummary(exitCodeOrSignal) {
|
|
241
|
+
if (sessionSummaryWritten) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
sessionSummaryWritten = true;
|
|
246
|
+
traceWebSearchEvent('mcp_session_summary', {
|
|
247
|
+
source: 'mcp',
|
|
248
|
+
initializeCount: sessionState.initializeCount,
|
|
249
|
+
toolsListCount: sessionState.toolsListCount,
|
|
250
|
+
exposed: sessionState.exposed,
|
|
251
|
+
toolCalls: sessionState.toolCalls,
|
|
252
|
+
calledWebSearch: sessionState.toolCalls > 0,
|
|
253
|
+
likelyBypassed: sessionState.exposed && sessionState.toolCalls === 0 ? 'unknown' : false,
|
|
254
|
+
activeProviderIds: getActiveProviderIds(),
|
|
255
|
+
skipReason: getSkipReason(),
|
|
256
|
+
exitCode: typeof exitCodeOrSignal === 'number' ? exitCodeOrSignal : null,
|
|
257
|
+
exitSignal: typeof exitCodeOrSignal === 'string' ? exitCodeOrSignal : null,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function parseMessages() {
|
|
262
|
+
while (true) {
|
|
263
|
+
let body;
|
|
264
|
+
const startsWithLegacyHeaders = inputBuffer
|
|
265
|
+
.slice(0, Math.min(inputBuffer.length, 32))
|
|
266
|
+
.toString('utf8')
|
|
267
|
+
.toLowerCase()
|
|
268
|
+
.startsWith('content-length:');
|
|
269
|
+
|
|
270
|
+
if (startsWithLegacyHeaders) {
|
|
271
|
+
const headerEnd = inputBuffer.indexOf('\r\n\r\n');
|
|
272
|
+
if (headerEnd === -1) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const headerText = inputBuffer.slice(0, headerEnd).toString('utf8');
|
|
277
|
+
const contentLengthMatch = headerText.match(/content-length:\s*(\d+)/i);
|
|
278
|
+
if (!contentLengthMatch) {
|
|
279
|
+
inputBuffer = Buffer.alloc(0);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const contentLength = Number.parseInt(contentLengthMatch[1], 10);
|
|
284
|
+
const messageEnd = headerEnd + 4 + contentLength;
|
|
285
|
+
if (inputBuffer.length < messageEnd) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
body = inputBuffer.slice(headerEnd + 4, messageEnd).toString('utf8');
|
|
290
|
+
inputBuffer = inputBuffer.slice(messageEnd);
|
|
291
|
+
} else {
|
|
292
|
+
const newlineIndex = inputBuffer.indexOf('\n');
|
|
293
|
+
if (newlineIndex === -1) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
body = inputBuffer.slice(0, newlineIndex).toString('utf8').replace(/\r$/, '').trim();
|
|
298
|
+
inputBuffer = inputBuffer.slice(newlineIndex + 1);
|
|
299
|
+
if (!body) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let message;
|
|
305
|
+
try {
|
|
306
|
+
message = JSON.parse(body);
|
|
307
|
+
} catch {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
Promise.resolve(handleMessage(message)).catch((error) => {
|
|
312
|
+
if (message && message.id !== undefined) {
|
|
313
|
+
writeError(message.id, -32603, (error && error.message) || 'Internal error');
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
process.stdin.on('data', (chunk) => {
|
|
320
|
+
inputBuffer = Buffer.concat([inputBuffer, chunk]);
|
|
321
|
+
parseMessages();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
process.stdin.on('error', () => {
|
|
325
|
+
process.exit(0);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
process.on('exit', (code) => {
|
|
329
|
+
writeSessionSummary(code);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
['SIGINT', 'SIGTERM', 'SIGHUP'].forEach((signal) => {
|
|
333
|
+
process.on(signal, () => {
|
|
334
|
+
writeSessionSummary(signal);
|
|
335
|
+
process.exit(0);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
process.stdin.resume();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaitranntt/ccs",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.64.0-dev.2",
|
|
4
4
|
"description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -81,12 +81,13 @@
|
|
|
81
81
|
"test:native": "bash tests/native/unix/edge-cases.sh",
|
|
82
82
|
"test:e2e": "bun test tests/e2e/ --bail --timeout 60000",
|
|
83
83
|
"report:hardening": "node scripts/hardening-inventory.js",
|
|
84
|
-
"dev": "bun run build:server &&
|
|
84
|
+
"dev": "bun run build:server && node dist/ccs.js config --dev",
|
|
85
85
|
"dev:symlink": "bash scripts/dev-symlink.sh",
|
|
86
86
|
"dev:unlink": "bash scripts/dev-symlink.sh --restore",
|
|
87
87
|
"ui:build": "cd ui && bun run build",
|
|
88
88
|
"ui:preview": "cd ui && bun run preview",
|
|
89
89
|
"ui:validate": "cd ui && bun run validate",
|
|
90
|
+
"prepack": "bun run build:all",
|
|
90
91
|
"prepare": "husky",
|
|
91
92
|
"postinstall": "node scripts/postinstall.js"
|
|
92
93
|
},
|
|
@@ -21,6 +21,12 @@ const STATUS_LABELS = {
|
|
|
21
21
|
na: 'N/A',
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
const REVIEW_MODE_DETAILS = {
|
|
25
|
+
fast: 'diff-focused bounded review',
|
|
26
|
+
triage: 'hotspot-based bounded review (non-exhaustive)',
|
|
27
|
+
deep: 'expanded surrounding-code review',
|
|
28
|
+
};
|
|
29
|
+
|
|
24
30
|
const RENDERER_OWNED_MARKUP_PATTERNS = [
|
|
25
31
|
{ pattern: /^#{1,6}\s/u, reason: 'markdown heading' },
|
|
26
32
|
{ pattern: /^\s*Verdict\s*:/iu, reason: 'verdict label' },
|
|
@@ -44,6 +50,171 @@ function renderCode(value) {
|
|
|
44
50
|
return `${fence}${text}${fence}`;
|
|
45
51
|
}
|
|
46
52
|
|
|
53
|
+
function parsePositiveInteger(value) {
|
|
54
|
+
if (value === null || value === undefined || value === '') {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const parsed = typeof value === 'number' ? value : Number.parseInt(cleanText(value), 10);
|
|
59
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeReviewMode(value) {
|
|
63
|
+
const mode = cleanText(value).toLowerCase();
|
|
64
|
+
return REVIEW_MODE_DETAILS[mode] ? mode : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizeRenderingMetadata(raw) {
|
|
68
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const mode = normalizeReviewMode(raw.mode);
|
|
73
|
+
const maxTurns = parsePositiveInteger(raw.maxTurns);
|
|
74
|
+
const timeoutMinutes = parsePositiveInteger(raw.timeoutMinutes);
|
|
75
|
+
const timeoutSeconds = parsePositiveInteger(raw.timeoutSeconds);
|
|
76
|
+
const selectedFiles = parsePositiveInteger(raw.selectedFiles);
|
|
77
|
+
const reviewableFiles = parsePositiveInteger(raw.reviewableFiles);
|
|
78
|
+
const selectedChanges = parsePositiveInteger(raw.selectedChanges);
|
|
79
|
+
const reviewableChanges = parsePositiveInteger(raw.reviewableChanges);
|
|
80
|
+
const scopeLabel = cleanText(raw.scopeLabel).toLowerCase();
|
|
81
|
+
const metadata = {};
|
|
82
|
+
|
|
83
|
+
if (mode) metadata.mode = mode;
|
|
84
|
+
if (maxTurns) metadata.maxTurns = maxTurns;
|
|
85
|
+
if (timeoutMinutes) metadata.timeoutMinutes = timeoutMinutes;
|
|
86
|
+
if (timeoutSeconds) metadata.timeoutSeconds = timeoutSeconds;
|
|
87
|
+
if (selectedFiles) metadata.selectedFiles = selectedFiles;
|
|
88
|
+
if (reviewableFiles) metadata.reviewableFiles = reviewableFiles;
|
|
89
|
+
if (selectedChanges) metadata.selectedChanges = selectedChanges;
|
|
90
|
+
if (reviewableChanges) metadata.reviewableChanges = reviewableChanges;
|
|
91
|
+
if (scopeLabel === 'reviewable files' || scopeLabel === 'changed files') metadata.scopeLabel = scopeLabel;
|
|
92
|
+
|
|
93
|
+
return metadata;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function mergeRenderingMetadata(...sources) {
|
|
97
|
+
const merged = {};
|
|
98
|
+
for (const source of sources) {
|
|
99
|
+
Object.assign(merged, normalizeRenderingMetadata(source));
|
|
100
|
+
}
|
|
101
|
+
return merged;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function formatTurnBudget(rendering) {
|
|
105
|
+
return typeof rendering.maxTurns === 'number' ? `${rendering.maxTurns} turns` : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function formatTimeBudget(rendering) {
|
|
109
|
+
if (typeof rendering.timeoutMinutes === 'number') {
|
|
110
|
+
return `${rendering.timeoutMinutes} minute${rendering.timeoutMinutes === 1 ? '' : 's'}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (typeof rendering.timeoutSeconds === 'number') {
|
|
114
|
+
return `${rendering.timeoutSeconds} second${rendering.timeoutSeconds === 1 ? '' : 's'}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function formatCombinedBudget(rendering) {
|
|
121
|
+
const parts = [formatTurnBudget(rendering), formatTimeBudget(rendering)].filter(Boolean);
|
|
122
|
+
return parts.length > 0 ? parts.join(' / ') : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function formatScopeSummary(rendering) {
|
|
126
|
+
if (
|
|
127
|
+
typeof rendering.selectedFiles !== 'number' ||
|
|
128
|
+
typeof rendering.reviewableFiles !== 'number'
|
|
129
|
+
) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const scopeLabel = rendering.scopeLabel || 'reviewable files';
|
|
134
|
+
const fileScope = `${rendering.selectedFiles}/${rendering.reviewableFiles} ${scopeLabel}`;
|
|
135
|
+
if (
|
|
136
|
+
typeof rendering.selectedChanges === 'number' &&
|
|
137
|
+
typeof rendering.reviewableChanges === 'number'
|
|
138
|
+
) {
|
|
139
|
+
const changeLabel = scopeLabel === 'reviewable files' ? 'reviewable changed lines' : 'changed lines';
|
|
140
|
+
return `${fileScope}; ${rendering.selectedChanges}/${rendering.reviewableChanges} ${changeLabel}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return fileScope;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function formatReviewContext(rendering) {
|
|
147
|
+
const parts = [];
|
|
148
|
+
|
|
149
|
+
if (rendering.mode) {
|
|
150
|
+
parts.push(`mode ${renderCode(rendering.mode)}`);
|
|
151
|
+
parts.push(REVIEW_MODE_DETAILS[rendering.mode]);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const scopeSummary = formatScopeSummary(rendering);
|
|
155
|
+
if (scopeSummary) {
|
|
156
|
+
parts.push(`scope ${scopeSummary}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const turnBudget = formatTurnBudget(rendering);
|
|
160
|
+
if (turnBudget) {
|
|
161
|
+
parts.push(`turn budget ${turnBudget}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const timeBudget = formatTimeBudget(rendering);
|
|
165
|
+
if (timeBudget) {
|
|
166
|
+
parts.push(`workflow cap ${timeBudget}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (parts.length === 0) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return `> 🧭 Review context: ${parts.join('; ')}.`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function classifyFallbackReason(reason) {
|
|
177
|
+
const normalized = cleanText(reason).toLowerCase();
|
|
178
|
+
if (!normalized || normalized === 'missing structured output') {
|
|
179
|
+
return 'missing';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (normalized === 'structured output is not valid json') {
|
|
183
|
+
return 'invalid_json';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return 'invalid_fields';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function describeIncompleteOutcome({ reason, rendering, turnsUsed, status }) {
|
|
190
|
+
const reviewLabel = rendering.mode ? `${renderCode(rendering.mode)} review` : 'bounded review';
|
|
191
|
+
const turnBudget = formatTurnBudget(rendering);
|
|
192
|
+
const timeBudget = formatTimeBudget(rendering);
|
|
193
|
+
const combinedBudget = formatCombinedBudget(rendering);
|
|
194
|
+
const exhaustedTurnBudget =
|
|
195
|
+
typeof turnsUsed === 'number' &&
|
|
196
|
+
typeof rendering.maxTurns === 'number' &&
|
|
197
|
+
turnsUsed >= rendering.maxTurns;
|
|
198
|
+
|
|
199
|
+
if (status === 'cancelled' && timeBudget) {
|
|
200
|
+
return `The ${reviewLabel} hit the workflow runtime cap before it produced validated structured output. The run stayed bounded to ${timeBudget}.`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (exhaustedTurnBudget) {
|
|
204
|
+
return `The ${reviewLabel} reached its ${rendering.maxTurns}-turn runtime budget before it produced validated structured output.`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (combinedBudget && classifyFallbackReason(reason) === 'missing') {
|
|
208
|
+
return `The ${reviewLabel} ended before it could produce validated structured output within the available ${combinedBudget} runtime budget.`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (classifyFallbackReason(reason) === 'missing' || classifyFallbackReason(reason) === 'invalid_json') {
|
|
212
|
+
return `The ${reviewLabel} ended without validated structured output, so the normalizer published the safe fallback comment instead.`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return `The ${reviewLabel} returned incomplete structured data, so the normalizer published the safe fallback comment instead.`;
|
|
216
|
+
}
|
|
217
|
+
|
|
47
218
|
function validatePlainTextField(fieldName, value) {
|
|
48
219
|
const text = cleanText(value);
|
|
49
220
|
if (!text) {
|
|
@@ -155,6 +326,8 @@ export function normalizeStructuredOutput(raw) {
|
|
|
155
326
|
const strengths = normalizeStringList('strengths', parsed.strengths);
|
|
156
327
|
if (!strengths.ok) return strengths;
|
|
157
328
|
|
|
329
|
+
const rendering = normalizeRenderingMetadata(parsed.rendering);
|
|
330
|
+
|
|
158
331
|
if (!ASSESSMENTS[overallAssessment] || findings === null) {
|
|
159
332
|
return { ok: false, reason: 'structured output is missing required review fields' };
|
|
160
333
|
}
|
|
@@ -203,19 +376,22 @@ export function normalizeStructuredOutput(raw) {
|
|
|
203
376
|
});
|
|
204
377
|
}
|
|
205
378
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
informational: informational.value,
|
|
216
|
-
strengths: strengths.value,
|
|
217
|
-
},
|
|
379
|
+
const value = {
|
|
380
|
+
summary: summary.value,
|
|
381
|
+
findings: normalizedFindings,
|
|
382
|
+
overallAssessment,
|
|
383
|
+
overallRationale: overallRationale.value,
|
|
384
|
+
securityChecklist: securityChecklist.value,
|
|
385
|
+
ccsCompliance: ccsCompliance.value,
|
|
386
|
+
informational: informational.value,
|
|
387
|
+
strengths: strengths.value,
|
|
218
388
|
};
|
|
389
|
+
|
|
390
|
+
if (Object.keys(rendering).length > 0) {
|
|
391
|
+
value.rendering = rendering;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return { ok: true, value };
|
|
219
395
|
}
|
|
220
396
|
|
|
221
397
|
function renderChecklistTable(title, labelHeader, labelKey, rows) {
|
|
@@ -233,8 +409,14 @@ function renderBulletSection(title, items) {
|
|
|
233
409
|
return ['', title, ...items.map((item) => `- ${escapeMarkdownText(item)}`)];
|
|
234
410
|
}
|
|
235
411
|
|
|
236
|
-
export function renderStructuredReview(review, { model }) {
|
|
412
|
+
export function renderStructuredReview(review, { model, rendering: renderOptions } = {}) {
|
|
413
|
+
const rendering = mergeRenderingMetadata(review?.rendering, renderOptions);
|
|
237
414
|
const lines = ['### 📋 Summary', '', escapeMarkdownText(review.summary), '', '### 🔍 Findings'];
|
|
415
|
+
const reviewContext = formatReviewContext(rendering);
|
|
416
|
+
|
|
417
|
+
if (reviewContext) {
|
|
418
|
+
lines.splice(4, 0, reviewContext, '');
|
|
419
|
+
}
|
|
238
420
|
|
|
239
421
|
if (review.findings.length === 0) {
|
|
240
422
|
lines.push('No confirmed issues found after reviewing the diff and surrounding code.');
|
|
@@ -273,15 +455,35 @@ export function renderStructuredReview(review, { model }) {
|
|
|
273
455
|
return lines.join('\n');
|
|
274
456
|
}
|
|
275
457
|
|
|
276
|
-
export function renderIncompleteReview({
|
|
458
|
+
export function renderIncompleteReview({
|
|
459
|
+
model,
|
|
460
|
+
reason,
|
|
461
|
+
runUrl,
|
|
462
|
+
runtimeTools,
|
|
463
|
+
turnsUsed,
|
|
464
|
+
rendering: renderOptions,
|
|
465
|
+
status,
|
|
466
|
+
}) {
|
|
467
|
+
const rendering = mergeRenderingMetadata(renderOptions);
|
|
277
468
|
const lines = [
|
|
278
469
|
'### ⚠️ AI Review Incomplete',
|
|
279
470
|
'',
|
|
280
471
|
'Claude did not return validated structured review output, so this workflow did not publish raw scratch text.',
|
|
281
472
|
'',
|
|
282
|
-
`-
|
|
473
|
+
`- Outcome: ${describeIncompleteOutcome({ reason, rendering, turnsUsed, status })}`,
|
|
283
474
|
];
|
|
284
475
|
|
|
476
|
+
if (rendering.mode) {
|
|
477
|
+
lines.push(`- Review mode: ${renderCode(rendering.mode)} (${escapeMarkdownText(REVIEW_MODE_DETAILS[rendering.mode])})`);
|
|
478
|
+
}
|
|
479
|
+
const scopeSummary = formatScopeSummary(rendering);
|
|
480
|
+
if (scopeSummary) {
|
|
481
|
+
lines.push(`- Review scope: ${escapeMarkdownText(scopeSummary)}`);
|
|
482
|
+
}
|
|
483
|
+
const runtimeBudget = formatCombinedBudget(rendering);
|
|
484
|
+
if (runtimeBudget) {
|
|
485
|
+
lines.push(`- Runtime budget: ${escapeMarkdownText(runtimeBudget)}`);
|
|
486
|
+
}
|
|
285
487
|
if (runtimeTools?.length) {
|
|
286
488
|
lines.push(`- Runtime tools: ${runtimeTools.map(renderCode).join(', ')}`);
|
|
287
489
|
}
|
|
@@ -299,14 +501,28 @@ export function writeReviewFromEnv(env = process.env) {
|
|
|
299
501
|
const runUrl = env.AI_REVIEW_RUN_URL || '#';
|
|
300
502
|
const validation = normalizeStructuredOutput(env.AI_REVIEW_STRUCTURED_OUTPUT);
|
|
301
503
|
const metadata = readExecutionMetadata(env.AI_REVIEW_EXECUTION_FILE);
|
|
504
|
+
const status = cleanText(env.AI_REVIEW_STATUS).toLowerCase() || null;
|
|
505
|
+
const rendering = normalizeRenderingMetadata({
|
|
506
|
+
mode: env.AI_REVIEW_MODE,
|
|
507
|
+
selectedFiles: env.AI_REVIEW_SELECTED_FILES,
|
|
508
|
+
reviewableFiles: env.AI_REVIEW_REVIEWABLE_FILES,
|
|
509
|
+
selectedChanges: env.AI_REVIEW_SELECTED_CHANGES,
|
|
510
|
+
reviewableChanges: env.AI_REVIEW_REVIEWABLE_CHANGES,
|
|
511
|
+
scopeLabel: env.AI_REVIEW_SCOPE_LABEL,
|
|
512
|
+
maxTurns: env.AI_REVIEW_MAX_TURNS,
|
|
513
|
+
timeoutMinutes: env.AI_REVIEW_TIMEOUT_MINUTES ?? env.AI_REVIEW_TIMEOUT_MINUTES_BUDGET,
|
|
514
|
+
timeoutSeconds: env.AI_REVIEW_TIMEOUT_SECONDS ?? env.AI_REVIEW_TIMEOUT_SEC,
|
|
515
|
+
});
|
|
302
516
|
const content = validation.ok
|
|
303
|
-
? renderStructuredReview(validation.value, { model })
|
|
517
|
+
? renderStructuredReview(validation.value, { model, rendering })
|
|
304
518
|
: renderIncompleteReview({
|
|
305
519
|
model,
|
|
306
520
|
reason: validation.reason,
|
|
307
521
|
runUrl,
|
|
308
522
|
runtimeTools: metadata.runtimeTools,
|
|
309
523
|
turnsUsed: metadata.turnsUsed,
|
|
524
|
+
rendering,
|
|
525
|
+
status,
|
|
310
526
|
});
|
|
311
527
|
|
|
312
528
|
fs.mkdirSync(path.dirname(outputFile), { recursive: true });
|