@aexol/spectral 0.9.129 → 0.9.130
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/dist/extensions/code-order/analyzer.d.ts +47 -0
- package/dist/extensions/code-order/analyzer.d.ts.map +1 -0
- package/dist/extensions/code-order/analyzer.js +315 -0
- package/dist/extensions/code-order/heuristics.d.ts +37 -0
- package/dist/extensions/code-order/heuristics.d.ts.map +1 -0
- package/dist/extensions/code-order/heuristics.js +188 -0
- package/dist/extensions/code-order/index.d.ts +15 -0
- package/dist/extensions/code-order/index.d.ts.map +1 -0
- package/dist/extensions/code-order/index.js +76 -0
- package/dist/extensions/code-order/report.d.ts +14 -0
- package/dist/extensions/code-order/report.d.ts.map +1 -0
- package/dist/extensions/code-order/report.js +140 -0
- package/dist/extensions/code-order/types.d.ts +63 -0
- package/dist/extensions/code-order/types.d.ts.map +1 -0
- package/dist/extensions/code-order/types.js +7 -0
- package/dist/mcp/ui-stream-types.d.ts +2 -2
- package/dist/relay/dispatcher.d.ts.map +1 -1
- package/dist/relay/dispatcher.js +5 -4
- package/dist/sdk/coding-agent/core/extensions/native-extensions.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/extensions/native-extensions.js +11 -0
- package/dist/sdk/coding-agent/core/system-prompt.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/system-prompt.js +2 -0
- package/dist/sdk/coding-agent/core/tools/bash-blocklist.d.ts +42 -0
- package/dist/sdk/coding-agent/core/tools/bash-blocklist.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/tools/bash-blocklist.js +141 -0
- package/dist/sdk/coding-agent/core/tools/bash.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/bash.js +5 -0
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +6 -3
- package/dist/server/error-humanizer.d.ts +31 -0
- package/dist/server/error-humanizer.d.ts.map +1 -0
- package/dist/server/error-humanizer.js +77 -0
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +7 -6
- package/package.json +1 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Order — native spectral extension.
|
|
3
|
+
*
|
|
4
|
+
* Registers a single tool, `code_order_report`, that scans the current
|
|
5
|
+
* project's directory tree and returns a Markdown cleanup report: oversized
|
|
6
|
+
* files, deep nesting, empty folders/files, duplicate sibling names and
|
|
7
|
+
* single-child folders. Language-agnostic, read-only, zero configuration.
|
|
8
|
+
*
|
|
9
|
+
* The report is input for the agent — it deduces what to split or flatten
|
|
10
|
+
* outside of this extension. Tree-like structure is enforced separately via
|
|
11
|
+
* the system prompt guidelines (see system-prompt.ts).
|
|
12
|
+
*/
|
|
13
|
+
import { resolve } from "node:path";
|
|
14
|
+
import { walkTree, computeMetrics } from "./analyzer.js";
|
|
15
|
+
import { runAllHeuristics } from "./heuristics.js";
|
|
16
|
+
import { renderReport } from "./report.js";
|
|
17
|
+
export default async function codeOrderExtension(ext) {
|
|
18
|
+
const tool = {
|
|
19
|
+
name: "code_order_report",
|
|
20
|
+
label: "Code Order Report",
|
|
21
|
+
description: "Analyze the current project's folder structure and file sizes, " +
|
|
22
|
+
"then generate a cleanup report with actionable findings: " +
|
|
23
|
+
"oversized files, deep nesting, empty folders, duplicate sibling names, single-child folders. " +
|
|
24
|
+
"Read-only, zero config. Use this to get input data before reorganizing code.",
|
|
25
|
+
promptSnippet: "`code_order_report` — analyze folder structure & sizes, generate cleanup report",
|
|
26
|
+
parameters: {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
path: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Directory to analyze. Default: current working directory.",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
async execute(_toolCallId, params) {
|
|
36
|
+
const rawPath = params.path ?? process.cwd();
|
|
37
|
+
const root = resolve(rawPath);
|
|
38
|
+
try {
|
|
39
|
+
const { tree, allFiles, allFolders, maxDepth, fileCount } = await walkTree(root);
|
|
40
|
+
const metrics = computeMetrics(allFiles, allFolders, maxDepth);
|
|
41
|
+
const issues = runAllHeuristics(tree, allFiles, allFolders, metrics);
|
|
42
|
+
const result = {
|
|
43
|
+
root,
|
|
44
|
+
generatedAt: new Date().toISOString(),
|
|
45
|
+
metrics,
|
|
46
|
+
issues,
|
|
47
|
+
};
|
|
48
|
+
const report = renderReport(result);
|
|
49
|
+
const summary = `Analyzed ${fileCount.toLocaleString()} files across ` +
|
|
50
|
+
`${metrics.totalFolders.toLocaleString()} folders ` +
|
|
51
|
+
`(${(metrics.totalSize / (1024 * 1024)).toFixed(1)} MB). ` +
|
|
52
|
+
`Found ${issues.length} structural issue(s).`;
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: "text", text: report }],
|
|
55
|
+
details: {
|
|
56
|
+
summary,
|
|
57
|
+
fileCount,
|
|
58
|
+
folderCount: metrics.totalFolders,
|
|
59
|
+
totalSize: metrics.totalSize,
|
|
60
|
+
issueCount: issues.length,
|
|
61
|
+
root,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: "text", text: `❌ Code Order analysis failed: ${msg}` }],
|
|
69
|
+
details: { isError: true, error: msg, root },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
ext.registerTool(tool);
|
|
75
|
+
process.stderr.write("[code-order] Registered code_order_report tool.\n");
|
|
76
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Order — Markdown report generator.
|
|
3
|
+
*
|
|
4
|
+
* Turns an {@link AnalysisResult} into a human/agent-readable Markdown report
|
|
5
|
+
* with a summary, prioritized issues, top largest files, and the extension
|
|
6
|
+
* distribution. The report is returned to the session so the LLM can deduce
|
|
7
|
+
* what to reorganize next.
|
|
8
|
+
*/
|
|
9
|
+
import type { AnalysisResult } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Render the full cleanup report as Markdown.
|
|
12
|
+
*/
|
|
13
|
+
export declare function renderReport(result: AnalysisResult): string;
|
|
14
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/extensions/code-order/report.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyB,MAAM,YAAY,CAAC;AAgDxE;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA0F3D"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Order — Markdown report generator.
|
|
3
|
+
*
|
|
4
|
+
* Turns an {@link AnalysisResult} into a human/agent-readable Markdown report
|
|
5
|
+
* with a summary, prioritized issues, top largest files, and the extension
|
|
6
|
+
* distribution. The report is returned to the session so the LLM can deduce
|
|
7
|
+
* what to reorganize next.
|
|
8
|
+
*/
|
|
9
|
+
function formatBytes(n) {
|
|
10
|
+
if (n < 1024)
|
|
11
|
+
return `${n} B`;
|
|
12
|
+
if (n < 1024 * 1024)
|
|
13
|
+
return `${(n / 1024).toFixed(1)} KB`;
|
|
14
|
+
if (n < 1024 * 1024 * 1024)
|
|
15
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
16
|
+
return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
17
|
+
}
|
|
18
|
+
function severityEmoji(sev) {
|
|
19
|
+
switch (sev) {
|
|
20
|
+
case "critical":
|
|
21
|
+
return "🔴";
|
|
22
|
+
case "warning":
|
|
23
|
+
return "🟡";
|
|
24
|
+
default:
|
|
25
|
+
return "ℹ️";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function groupByType(issues) {
|
|
29
|
+
const out = new Map();
|
|
30
|
+
for (const i of issues) {
|
|
31
|
+
const arr = out.get(i.type) ?? [];
|
|
32
|
+
arr.push(i);
|
|
33
|
+
out.set(i.type, arr);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
const ISSUE_TITLES = {
|
|
38
|
+
oversized_file: "Oversized files",
|
|
39
|
+
deep_nesting: "Deep nesting",
|
|
40
|
+
empty_folder: "Empty folders",
|
|
41
|
+
empty_file: "Empty files",
|
|
42
|
+
duplicate_names: "Duplicate sibling folder names",
|
|
43
|
+
single_child_folder: "Single-child folders",
|
|
44
|
+
};
|
|
45
|
+
const ISSUE_ORDER = [
|
|
46
|
+
"oversized_file",
|
|
47
|
+
"deep_nesting",
|
|
48
|
+
"duplicate_names",
|
|
49
|
+
"empty_folder",
|
|
50
|
+
"empty_file",
|
|
51
|
+
"single_child_folder",
|
|
52
|
+
];
|
|
53
|
+
/**
|
|
54
|
+
* Render the full cleanup report as Markdown.
|
|
55
|
+
*/
|
|
56
|
+
export function renderReport(result) {
|
|
57
|
+
const { root, generatedAt, metrics, issues } = result;
|
|
58
|
+
const lines = [];
|
|
59
|
+
lines.push(`# Code Order Report — ${root}`);
|
|
60
|
+
lines.push("");
|
|
61
|
+
lines.push(`_Generated: ${generatedAt}_`);
|
|
62
|
+
lines.push("");
|
|
63
|
+
// Summary
|
|
64
|
+
lines.push("## Summary");
|
|
65
|
+
lines.push("");
|
|
66
|
+
lines.push(`**${metrics.totalFiles.toLocaleString()} files** | ` +
|
|
67
|
+
`**${metrics.totalFolders.toLocaleString()} folders** | ` +
|
|
68
|
+
`**${formatBytes(metrics.totalSize)} total** | ` +
|
|
69
|
+
`max depth ${metrics.maxDepth}`);
|
|
70
|
+
lines.push("");
|
|
71
|
+
// Issues
|
|
72
|
+
if (issues.length === 0) {
|
|
73
|
+
lines.push("## Issues");
|
|
74
|
+
lines.push("");
|
|
75
|
+
lines.push("✅ No structural issues detected. The project looks tidy.");
|
|
76
|
+
lines.push("");
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const grouped = groupByType(issues);
|
|
80
|
+
const counts = { critical: 0, warning: 0, info: 0 };
|
|
81
|
+
for (const i of issues)
|
|
82
|
+
counts[i.severity]++;
|
|
83
|
+
lines.push(`## Issues (${issues.length})`);
|
|
84
|
+
lines.push("");
|
|
85
|
+
lines.push(`**${counts.critical} critical** · **${counts.warning} warnings** · **${counts.info} info**`);
|
|
86
|
+
lines.push("");
|
|
87
|
+
for (const type of ISSUE_ORDER) {
|
|
88
|
+
const group = grouped.get(type);
|
|
89
|
+
if (!group || group.length === 0)
|
|
90
|
+
continue;
|
|
91
|
+
const sev = group[0].severity;
|
|
92
|
+
lines.push(`### ${severityEmoji(sev)} ${ISSUE_TITLES[type]} (${group.length})`);
|
|
93
|
+
lines.push("");
|
|
94
|
+
if (type === "oversized_file") {
|
|
95
|
+
lines.push("| Size | Path | Suggestion |");
|
|
96
|
+
lines.push("|------|------|------------|");
|
|
97
|
+
for (const i of group) {
|
|
98
|
+
const size = i.message.match(/is ([\d.]+ [KMG]?B)/)?.[1] ?? "?";
|
|
99
|
+
lines.push(`| ${size} | \`${i.path}\` | ${i.suggestion ?? ""} |`);
|
|
100
|
+
}
|
|
101
|
+
lines.push("");
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
for (const i of group) {
|
|
105
|
+
lines.push(`- \`${i.path}\` — ${i.message}`);
|
|
106
|
+
if (i.suggestion)
|
|
107
|
+
lines.push(` - ${i.suggestion}`);
|
|
108
|
+
}
|
|
109
|
+
lines.push("");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Top largest files
|
|
114
|
+
if (metrics.topLargestFiles.length > 0) {
|
|
115
|
+
lines.push(`## Top ${metrics.topLargestFiles.length} largest files`);
|
|
116
|
+
lines.push("");
|
|
117
|
+
lines.push("| Size | Path |");
|
|
118
|
+
lines.push("|------|------|");
|
|
119
|
+
for (const f of metrics.topLargestFiles) {
|
|
120
|
+
lines.push(`| ${formatBytes(f.size)} | \`${f.path}\` |`);
|
|
121
|
+
}
|
|
122
|
+
lines.push("");
|
|
123
|
+
}
|
|
124
|
+
// Extension distribution
|
|
125
|
+
if (metrics.extensions.length > 0) {
|
|
126
|
+
lines.push("## Extension distribution");
|
|
127
|
+
lines.push("");
|
|
128
|
+
lines.push("| Extension | Files | Total size |");
|
|
129
|
+
lines.push("|-----------|-------|------------|");
|
|
130
|
+
for (const e of metrics.extensions.slice(0, 20)) {
|
|
131
|
+
lines.push(`| .${e.extension} | ${e.count} | ${formatBytes(e.size)} |`);
|
|
132
|
+
}
|
|
133
|
+
if (metrics.extensions.length > 20) {
|
|
134
|
+
lines.push(`| … | … | … |`);
|
|
135
|
+
lines.push(`| _total_ | ${metrics.totalFiles} | ${formatBytes(metrics.totalSize)} |`);
|
|
136
|
+
}
|
|
137
|
+
lines.push("");
|
|
138
|
+
}
|
|
139
|
+
return lines.join("\n");
|
|
140
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Order — shared types.
|
|
3
|
+
*
|
|
4
|
+
* Tree-like representation of a scanned directory. Every node is either a file
|
|
5
|
+
* or a folder; folders aggregate size/file/folder counts from their children.
|
|
6
|
+
*/
|
|
7
|
+
export interface FileNode {
|
|
8
|
+
readonly path: string;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly size: number;
|
|
11
|
+
/** Lowercase extension without the leading dot. Empty string when none. */
|
|
12
|
+
readonly extension: string;
|
|
13
|
+
readonly isFile: true;
|
|
14
|
+
readonly mtime: number;
|
|
15
|
+
}
|
|
16
|
+
export interface FolderNode {
|
|
17
|
+
path: string;
|
|
18
|
+
name: string;
|
|
19
|
+
isFile: false;
|
|
20
|
+
totalSize: number;
|
|
21
|
+
fileCount: number;
|
|
22
|
+
folderCount: number;
|
|
23
|
+
maxDepth: number;
|
|
24
|
+
children: TreeNode[];
|
|
25
|
+
}
|
|
26
|
+
export type TreeNode = FileNode | FolderNode;
|
|
27
|
+
export type IssueType = "oversized_file" | "deep_nesting" | "empty_folder" | "empty_file" | "duplicate_names" | "single_child_folder";
|
|
28
|
+
export type IssueSeverity = "info" | "warning" | "critical";
|
|
29
|
+
export interface Issue {
|
|
30
|
+
type: IssueType;
|
|
31
|
+
severity: IssueSeverity;
|
|
32
|
+
path: string;
|
|
33
|
+
message: string;
|
|
34
|
+
suggestion?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ExtensionEntry {
|
|
37
|
+
extension: string;
|
|
38
|
+
count: number;
|
|
39
|
+
size: number;
|
|
40
|
+
}
|
|
41
|
+
export interface ProjectMetrics {
|
|
42
|
+
totalFiles: number;
|
|
43
|
+
totalFolders: number;
|
|
44
|
+
totalSize: number;
|
|
45
|
+
maxDepth: number;
|
|
46
|
+
avgFileSize: number;
|
|
47
|
+
medianFileSize: number;
|
|
48
|
+
extensions: ExtensionEntry[];
|
|
49
|
+
topLargestFiles: FileNode[];
|
|
50
|
+
emptyFolders: string[];
|
|
51
|
+
emptyFiles: string[];
|
|
52
|
+
}
|
|
53
|
+
export interface AnalysisResult {
|
|
54
|
+
root: string;
|
|
55
|
+
generatedAt: string;
|
|
56
|
+
metrics: ProjectMetrics;
|
|
57
|
+
issues: Issue[];
|
|
58
|
+
}
|
|
59
|
+
export interface AnalysisOptions {
|
|
60
|
+
/** Absolute directory to analyze. Defaults to process.cwd(). */
|
|
61
|
+
root: string;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/extensions/code-order/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE7C,MAAM,MAAM,SAAS,GAClB,gBAAgB,GAChB,cAAc,GACd,cAAc,GACd,YAAY,GACZ,iBAAiB,GACjB,qBAAqB,CAAC;AAEzB,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;AAE5D,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,eAAe,EAAE,QAAQ,EAAE,CAAC;IAC5B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,KAAK,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC/B,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;CACb"}
|
|
@@ -43,7 +43,7 @@ export declare const visualizationStreamEnvelopeSchema: z.ZodObject<{
|
|
|
43
43
|
streamId: string;
|
|
44
44
|
sequence: number;
|
|
45
45
|
frameType: "patch" | "checkpoint" | "final";
|
|
46
|
-
phase: "
|
|
46
|
+
phase: "structure" | "settled" | "shell" | "narrative" | "detail";
|
|
47
47
|
message?: string | undefined;
|
|
48
48
|
spec?: Record<string, unknown> | undefined;
|
|
49
49
|
checkpoint?: Record<string, unknown> | undefined;
|
|
@@ -52,7 +52,7 @@ export declare const visualizationStreamEnvelopeSchema: z.ZodObject<{
|
|
|
52
52
|
streamId: string;
|
|
53
53
|
sequence: number;
|
|
54
54
|
frameType: "patch" | "checkpoint" | "final";
|
|
55
|
-
phase: "
|
|
55
|
+
phase: "structure" | "settled" | "shell" | "narrative" | "detail";
|
|
56
56
|
message?: string | undefined;
|
|
57
57
|
spec?: Record<string, unknown> | undefined;
|
|
58
58
|
checkpoint?: Record<string, unknown> | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/relay/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAQH,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACf,MAAM,uBAAuB,CAAC;AAK/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAgDzD,OAAO,KAAK,EACV,oBAAoB,EACpB,UAAU,EACX,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/relay/dispatcher.ts"],"names":[],"mappings":"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"}
|
package/dist/relay/dispatcher.js
CHANGED
|
@@ -58,6 +58,7 @@ import { handleGetAgentSettings, handleListAgents, handlePutAgentSettings, } fro
|
|
|
58
58
|
import { handleGetSettings, handlePutSettings, } from "../server/handlers/settings.js";
|
|
59
59
|
import { handleGetPromptMutationSettings, handlePutPromptMutationSettings, } from "../server/handlers/prompt-mutation-settings.js";
|
|
60
60
|
import { shutdownState } from "../server/shutdown.js";
|
|
61
|
+
import { humanizeProviderError } from "../server/error-humanizer.js";
|
|
61
62
|
import { handleAutoResearch } from "./auto-research.js";
|
|
62
63
|
import { handleAutoOptimize } from "./auto-optimizer.js";
|
|
63
64
|
/**
|
|
@@ -1212,7 +1213,7 @@ export function handleClientMessage(frame, deps) {
|
|
|
1212
1213
|
relay.send({
|
|
1213
1214
|
kind: "ws_event",
|
|
1214
1215
|
sessionId,
|
|
1215
|
-
event: { type: "error", message:
|
|
1216
|
+
event: { type: "error", message: humanizeProviderError(err) },
|
|
1216
1217
|
});
|
|
1217
1218
|
return;
|
|
1218
1219
|
}
|
|
@@ -1250,7 +1251,7 @@ export function handleClientMessage(frame, deps) {
|
|
|
1250
1251
|
relay.send({
|
|
1251
1252
|
kind: "ws_event",
|
|
1252
1253
|
sessionId,
|
|
1253
|
-
event: { type: "error", message:
|
|
1254
|
+
event: { type: "error", message: humanizeProviderError(err) },
|
|
1254
1255
|
});
|
|
1255
1256
|
});
|
|
1256
1257
|
}
|
|
@@ -1311,7 +1312,7 @@ export function handleSubscribe(frame, deps) {
|
|
|
1311
1312
|
relay.send({
|
|
1312
1313
|
kind: "ws_event",
|
|
1313
1314
|
sessionId,
|
|
1314
|
-
event: { type: "error", message:
|
|
1315
|
+
event: { type: "error", message: humanizeProviderError(err) },
|
|
1315
1316
|
});
|
|
1316
1317
|
return;
|
|
1317
1318
|
}
|
|
@@ -1342,7 +1343,7 @@ export function handleSubscribe(frame, deps) {
|
|
|
1342
1343
|
relay.send({
|
|
1343
1344
|
kind: "ws_event",
|
|
1344
1345
|
sessionId,
|
|
1345
|
-
event: { type: "error", message:
|
|
1346
|
+
event: { type: "error", message: humanizeProviderError(err) },
|
|
1346
1347
|
});
|
|
1347
1348
|
});
|
|
1348
1349
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-extensions.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/extensions/native-extensions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,aAAa,CAAC;IAChD;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,cAAc,EAAE,OAAO,CAAC;IACxB,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"native-extensions.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/extensions/native-extensions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,aAAa,CAAC;IAChD;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,cAAc,EAAE,OAAO,CAAC;IACxB,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,uBAAuB,EAsItD,CAAC;AAEF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAElF;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAI1G;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;IAClG,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC,CAUD"}
|
|
@@ -129,6 +129,17 @@ export const NATIVE_EXTENSIONS = [
|
|
|
129
129
|
defaultEnabled: true,
|
|
130
130
|
tags: ["screenshot", "desktop", "vision", "screen"],
|
|
131
131
|
},
|
|
132
|
+
{
|
|
133
|
+
id: "code-order",
|
|
134
|
+
label: "Code Order",
|
|
135
|
+
description: "Analyzes folder structure and file sizes, then generates a cleanup report " +
|
|
136
|
+
"(oversized files, deep nesting, empty folders, duplicates, single-child folders). " +
|
|
137
|
+
"Language-agnostic, read-only, zero config. Input for reorganizing code.",
|
|
138
|
+
category: "automation",
|
|
139
|
+
entryPath: "src/extensions/code-order/index.ts",
|
|
140
|
+
defaultEnabled: true,
|
|
141
|
+
tags: ["structure", "analysis", "cleanup", "tree", "audit"],
|
|
142
|
+
},
|
|
132
143
|
];
|
|
133
144
|
/**
|
|
134
145
|
* Look up a native extension by id. Returns undefined for unknown ids.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../../../src/sdk/coding-agent/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,wBAAwB;IACxC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,yBAAyB;IACzB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../../../src/sdk/coding-agent/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,wBAAwB;IACxC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,yBAAyB;IACzB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CA4K3E"}
|
|
@@ -86,6 +86,8 @@ export function buildSystemPrompt(options) {
|
|
|
86
86
|
addGuideline(normalized);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
addGuideline("When creating new files, maintain a tree-like directory structure. Place files in semantically meaningful subdirectories (e.g. `handlers/auth/`, `utils/string/`) rather than dumping them at the project root or in shallow flat folders. Group related files into dedicated folders, prefer `feature/module/file.ts` over `feature-file.ts` at root. Avoid creating files directly in root unless they are truly project-wide (e.g. config, package.json). Keep folder depth reasonable (max 5–6 levels) but prefer nesting over flattening — tree-like structure improves LLM navigability and agentic coding efficiency.");
|
|
90
|
+
addGuideline("When editing code, first inspect nearby files and existing patterns; follow the codebase’s conventions and libraries");
|
|
89
91
|
addGuideline("When editing code, first inspect nearby files and existing patterns; follow the codebase’s conventions and libraries");
|
|
90
92
|
// Always include these
|
|
91
93
|
if (hasSubagent) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash command blocklist for filesystem-wide scans.
|
|
3
|
+
*
|
|
4
|
+
* Blocks recursive scans originating from the filesystem root `/` or other
|
|
5
|
+
* system directories (e.g. `/etc`, `/home`, `/usr`, `/var`) because they can
|
|
6
|
+
* take a very long time and are almost never the intended action inside a
|
|
7
|
+
* workspace-scoped bash tool. Scoped scans (e.g. `find .`, `grep -r src/`,
|
|
8
|
+
* `ls -R src/`) remain allowed.
|
|
9
|
+
*
|
|
10
|
+
* This is intentionally a regex-based heuristic, not a full shell parser. It
|
|
11
|
+
* targets the common scan shapes; exotic obfuscation is out of scope.
|
|
12
|
+
*/
|
|
13
|
+
/** Result of a blocklist check. */
|
|
14
|
+
export interface BashBlocklistResult {
|
|
15
|
+
/** Whether the command should be blocked. */
|
|
16
|
+
blocked: boolean;
|
|
17
|
+
/** Human-readable reason (empty when not blocked). */
|
|
18
|
+
reason: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Default, reusable explanation returned to the LLM when a command is blocked.
|
|
22
|
+
* Phrased to steer the model toward a scoped alternative.
|
|
23
|
+
*/
|
|
24
|
+
export declare const BASH_BLOCKLIST_REASON: string;
|
|
25
|
+
/**
|
|
26
|
+
* Check a bash command against the filesystem-scan blocklist.
|
|
27
|
+
*
|
|
28
|
+
* @param command The raw command string passed to the bash tool (after any
|
|
29
|
+
* command prefix has been applied).
|
|
30
|
+
* @returns `{ blocked: true, reason }` when the command matches a blocked
|
|
31
|
+
* pattern, otherwise `{ blocked: false, reason: "" }`.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* checkBashBlocklist("find /") // { blocked: true, ... }
|
|
35
|
+
* checkBashBlocklist("find / -name foo") // { blocked: true, ... }
|
|
36
|
+
* checkBashBlocklist("find .") // { blocked: false, reason: "" }
|
|
37
|
+
* checkBashBlocklist("grep -r /") // { blocked: true, ... }
|
|
38
|
+
* checkBashBlocklist("grep -r src/") // { blocked: false, reason: "" }
|
|
39
|
+
* checkBashBlocklist("cat /etc/hosts") // { blocked: false, reason: "" }
|
|
40
|
+
*/
|
|
41
|
+
export declare function checkBashBlocklist(command: string): BashBlocklistResult;
|
|
42
|
+
//# sourceMappingURL=bash-blocklist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-blocklist.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/tools/bash-blocklist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,mCAAmC;AACnC,MAAM,WAAW,mBAAmB;IACnC,6CAA6C;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;CACf;AA0GD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAGgD,CAAC;AAEnF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAevE"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash command blocklist for filesystem-wide scans.
|
|
3
|
+
*
|
|
4
|
+
* Blocks recursive scans originating from the filesystem root `/` or other
|
|
5
|
+
* system directories (e.g. `/etc`, `/home`, `/usr`, `/var`) because they can
|
|
6
|
+
* take a very long time and are almost never the intended action inside a
|
|
7
|
+
* workspace-scoped bash tool. Scoped scans (e.g. `find .`, `grep -r src/`,
|
|
8
|
+
* `ls -R src/`) remain allowed.
|
|
9
|
+
*
|
|
10
|
+
* This is intentionally a regex-based heuristic, not a full shell parser. It
|
|
11
|
+
* targets the common scan shapes; exotic obfuscation is out of scope.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Absolute system paths that a workspace bash tool should never recursively
|
|
15
|
+
* scan. Each entry is matched as a literal path token (anchored or
|
|
16
|
+
* space/quote-delimited) and is also the prefix of a directory subtree.
|
|
17
|
+
*
|
|
18
|
+
* Notes:
|
|
19
|
+
* - `/` is the root.
|
|
20
|
+
* - We deliberately exclude `/tmp` and `/Users` style home roots here; they
|
|
21
|
+
* are handled by the recursive-root rules where relevant, but blocking every
|
|
22
|
+
* `/Users/...` access would also block legitimate `cat /Users/.../file`
|
|
23
|
+
* reads. We only block *recursive scans* against `/`, not arbitrary reads.
|
|
24
|
+
*/
|
|
25
|
+
const SYSTEM_SCAN_ROOTS = [
|
|
26
|
+
"/",
|
|
27
|
+
"/etc",
|
|
28
|
+
"/home",
|
|
29
|
+
"/usr",
|
|
30
|
+
"/var",
|
|
31
|
+
"/bin",
|
|
32
|
+
"/sbin",
|
|
33
|
+
"/lib",
|
|
34
|
+
"/lib64",
|
|
35
|
+
"/opt",
|
|
36
|
+
"/root",
|
|
37
|
+
"/sys",
|
|
38
|
+
"/proc",
|
|
39
|
+
"/dev",
|
|
40
|
+
"/boot",
|
|
41
|
+
"/srv",
|
|
42
|
+
"/mnt",
|
|
43
|
+
"/media",
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* Build an alternation of system roots, escaping regex metacharacters. `/` has
|
|
47
|
+
* no special regex meaning so it does not need escaping, but we escape it for
|
|
48
|
+
* safety/generality. Longest entries first so `/etc` matches before `/`.
|
|
49
|
+
*/
|
|
50
|
+
const ROOTS_ALT = SYSTEM_SCAN_ROOTS.slice()
|
|
51
|
+
.sort((a, b) => b.length - a.length)
|
|
52
|
+
.map((r) => r.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
53
|
+
.join("|");
|
|
54
|
+
/**
|
|
55
|
+
* Match a command token that starts with one of the system roots.
|
|
56
|
+
*
|
|
57
|
+
* The token is anchored by start-of-string, a preceding space, or an opening
|
|
58
|
+
* quote, and extends to the next whitespace or end-of-string. This prevents
|
|
59
|
+
* false positives like `cat /etc/hosts` being treated as a scan of `/etc`,
|
|
60
|
+
* because we only apply this pattern to recursive-scan commands (find/grep -r
|
|
61
|
+
* / ls -R) below.
|
|
62
|
+
*
|
|
63
|
+
* Captured group 1 is the matched root path token.
|
|
64
|
+
*/
|
|
65
|
+
const SYSTEM_ROOT_TOKEN = new RegExp(`(?:^|\\s|['"])(?<root>${ROOTS_ALT})[^\\s'"]*(?=\\s|$|['"])`);
|
|
66
|
+
/**
|
|
67
|
+
* `find <root>` — `find` invoked with a system root as one of its path
|
|
68
|
+
* arguments. We match `find` followed by optional flags/options and then a
|
|
69
|
+
* system-root token anywhere in the argument list.
|
|
70
|
+
*
|
|
71
|
+
* `find` is inherently recursive, so any `find /` (or `find /etc ...`) is a
|
|
72
|
+
* filesystem scan. Scoped forms (`find .`, `find src/`, `find ./packages`)
|
|
73
|
+
* are not matched because their path tokens are not system roots.
|
|
74
|
+
*/
|
|
75
|
+
const FIND_SYSTEM_ROOT = new RegExp(
|
|
76
|
+
// `find` as its own token, followed by anything, then a system-root token.
|
|
77
|
+
`(?:^|[\\s;&|]+)(?:sudo\\s+)?find\\b.*${SYSTEM_ROOT_TOKEN.source}`);
|
|
78
|
+
/**
|
|
79
|
+
* Recursive grep targeting a system root. Requires a recursive flag
|
|
80
|
+
* (`-r`, `-R`, `--recursive`, or a combined short flag containing `r`/`R`
|
|
81
|
+
* such as `-rn`, `-RI`, `-rnI`) and a system-root path token.
|
|
82
|
+
*
|
|
83
|
+
* Supports `grep`, `ggrep`, `egrep`, `fgrep`, `rg` (ripgrep), and `ack`.
|
|
84
|
+
*/
|
|
85
|
+
const GREP_SYSTEM_ROOT = new RegExp(`(?:^|[\\s;&|]+)(?:sudo\\s+)?(?:g?grep|egrep|fgrep)\\b` +
|
|
86
|
+
// require a recursive flag somewhere before/after — combined short flags
|
|
87
|
+
// like -rn, -RI are covered by [-][A-Za-z]*[rR][A-Za-z]*
|
|
88
|
+
`.*(?:-r\\b|-R\\b|--recursive\\b|-[A-Za-z]*[rR][A-Za-z]*)` +
|
|
89
|
+
`.*${SYSTEM_ROOT_TOKEN.source}`);
|
|
90
|
+
/**
|
|
91
|
+
* Recursive `ls` targeting a system root. `ls` is only a scan when `-R`
|
|
92
|
+
* (or a combined short flag containing `R`, e.g. `-laR`, `-AR`) is present
|
|
93
|
+
* AND the target is a system root.
|
|
94
|
+
*/
|
|
95
|
+
const LS_SYSTEM_ROOT = new RegExp(`(?:^|[\\s;&|]+)(?:sudo\\s+)?ls\\b` +
|
|
96
|
+
`.*(?:-R\\b|--recursive\\b|-[A-Za-z]*R[A-Za-z]*)` +
|
|
97
|
+
`.*${SYSTEM_ROOT_TOKEN.source}`);
|
|
98
|
+
const BLOCKED_PATTERNS = [
|
|
99
|
+
{ name: "find on system root", pattern: FIND_SYSTEM_ROOT },
|
|
100
|
+
{ name: "recursive grep on system root", pattern: GREP_SYSTEM_ROOT },
|
|
101
|
+
{ name: "recursive ls on system root", pattern: LS_SYSTEM_ROOT },
|
|
102
|
+
];
|
|
103
|
+
/**
|
|
104
|
+
* Default, reusable explanation returned to the LLM when a command is blocked.
|
|
105
|
+
* Phrased to steer the model toward a scoped alternative.
|
|
106
|
+
*/
|
|
107
|
+
export const BASH_BLOCKLIST_REASON = "Command blocked: a filesystem-wide scan starting from a system root ('/' or a system directory such as /etc, /usr, /home) was detected. " +
|
|
108
|
+
"Such scans can take a very long time. Use the find/grep/ls tools with a scoped path within the workspace " +
|
|
109
|
+
"(e.g. '.', 'src/', './packages') instead of scanning '/' or system directories.";
|
|
110
|
+
/**
|
|
111
|
+
* Check a bash command against the filesystem-scan blocklist.
|
|
112
|
+
*
|
|
113
|
+
* @param command The raw command string passed to the bash tool (after any
|
|
114
|
+
* command prefix has been applied).
|
|
115
|
+
* @returns `{ blocked: true, reason }` when the command matches a blocked
|
|
116
|
+
* pattern, otherwise `{ blocked: false, reason: "" }`.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* checkBashBlocklist("find /") // { blocked: true, ... }
|
|
120
|
+
* checkBashBlocklist("find / -name foo") // { blocked: true, ... }
|
|
121
|
+
* checkBashBlocklist("find .") // { blocked: false, reason: "" }
|
|
122
|
+
* checkBashBlocklist("grep -r /") // { blocked: true, ... }
|
|
123
|
+
* checkBashBlocklist("grep -r src/") // { blocked: false, reason: "" }
|
|
124
|
+
* checkBashBlocklist("cat /etc/hosts") // { blocked: false, reason: "" }
|
|
125
|
+
*/
|
|
126
|
+
export function checkBashBlocklist(command) {
|
|
127
|
+
if (typeof command !== "string" || command.length === 0) {
|
|
128
|
+
return { blocked: false, reason: "" };
|
|
129
|
+
}
|
|
130
|
+
// Strip quoted substrings before matching so that system-root tokens that
|
|
131
|
+
// appear only inside a quoted option value (e.g. `find . -name '/etc/hosts'`)
|
|
132
|
+
// do not false-positive. A real scan target like `find /` or `grep -r /etc`
|
|
133
|
+
// is an unquoted path token and survives this stripping.
|
|
134
|
+
const normalized = command.replace(/'[^']*'|"[^"]*"/g, "");
|
|
135
|
+
for (const { pattern } of BLOCKED_PATTERNS) {
|
|
136
|
+
if (pattern.test(normalized)) {
|
|
137
|
+
return { blocked: true, reason: BASH_BLOCKLIST_REASON };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { blocked: false, reason: "" };
|
|
141
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/tools/bash.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAU5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/tools/bash.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAU5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAK7D,OAAO,EAAoD,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAExG,QAAA,MAAM,UAAU;;;EAGd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;OAMG;IACH,IAAI,EAAE,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KACxB,KACG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1C;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAkF1F;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;AAO5E,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,aAAa,CAAC;CAC1B;AAyFD,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CA0IhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG"}
|