@ddtcorex/dsh-maestro-review 0.1.0 → 0.1.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.
Files changed (143) hide show
  1. package/lib/config-store.d.ts +2 -0
  2. package/lib/config-store.d.ts.map +1 -1
  3. package/lib/config-store.js.map +1 -1
  4. package/lib/events.d.ts.map +1 -1
  5. package/lib/events.js.map +1 -1
  6. package/lib/git-worktree-tool.d.ts +11 -0
  7. package/lib/git-worktree-tool.d.ts.map +1 -0
  8. package/lib/git-worktree-tool.js +162 -0
  9. package/lib/git-worktree-tool.js.map +1 -0
  10. package/lib/gitlab-client.d.ts.map +1 -1
  11. package/lib/gitlab-client.js.map +1 -1
  12. package/lib/gitlab-webhook.d.ts.map +1 -1
  13. package/lib/gitlab-webhook.js.map +1 -1
  14. package/lib/govard-audit-lint-tool.d.ts +14 -0
  15. package/lib/govard-audit-lint-tool.d.ts.map +1 -0
  16. package/lib/govard-audit-lint-tool.js +177 -0
  17. package/lib/govard-audit-lint-tool.js.map +1 -0
  18. package/lib/govard-tool.d.ts.map +1 -1
  19. package/lib/govard-tool.js.map +1 -1
  20. package/lib/hyva-csp-scan-tool.d.ts +11 -0
  21. package/lib/hyva-csp-scan-tool.d.ts.map +1 -0
  22. package/lib/hyva-csp-scan-tool.js +193 -0
  23. package/lib/hyva-csp-scan-tool.js.map +1 -0
  24. package/lib/hyva-theme-inspect-tool.d.ts +10 -0
  25. package/lib/hyva-theme-inspect-tool.d.ts.map +1 -0
  26. package/lib/hyva-theme-inspect-tool.js +299 -0
  27. package/lib/hyva-theme-inspect-tool.js.map +1 -0
  28. package/lib/incremental.d.ts +28 -0
  29. package/lib/incremental.d.ts.map +1 -0
  30. package/lib/incremental.js +60 -0
  31. package/lib/incremental.js.map +1 -0
  32. package/lib/index.d.ts.map +1 -1
  33. package/lib/index.js.map +1 -1
  34. package/lib/layout-xml-tool.d.ts +11 -0
  35. package/lib/layout-xml-tool.d.ts.map +1 -0
  36. package/lib/layout-xml-tool.js +240 -0
  37. package/lib/layout-xml-tool.js.map +1 -0
  38. package/lib/module-check-tool.d.ts +11 -0
  39. package/lib/module-check-tool.d.ts.map +1 -0
  40. package/lib/module-check-tool.js +304 -0
  41. package/lib/module-check-tool.js.map +1 -0
  42. package/lib/notify.d.ts.map +1 -1
  43. package/lib/notify.js.map +1 -1
  44. package/lib/orchestrator.d.ts +2 -0
  45. package/lib/orchestrator.d.ts.map +1 -1
  46. package/lib/orchestrator.js +220 -88
  47. package/lib/orchestrator.js.map +1 -1
  48. package/lib/perf-log-stats-tool.d.ts +13 -0
  49. package/lib/perf-log-stats-tool.d.ts.map +1 -0
  50. package/lib/perf-log-stats-tool.js +245 -0
  51. package/lib/perf-log-stats-tool.js.map +1 -0
  52. package/lib/phtml-escape-scan-tool.d.ts +11 -0
  53. package/lib/phtml-escape-scan-tool.d.ts.map +1 -0
  54. package/lib/phtml-escape-scan-tool.js +170 -0
  55. package/lib/phtml-escape-scan-tool.js.map +1 -0
  56. package/lib/plan-track-tool.d.ts +11 -0
  57. package/lib/plan-track-tool.d.ts.map +1 -0
  58. package/lib/plan-track-tool.js +143 -0
  59. package/lib/plan-track-tool.js.map +1 -0
  60. package/lib/providers/github.stub.d.ts.map +1 -1
  61. package/lib/providers/github.stub.js.map +1 -1
  62. package/lib/providers/gitlab.d.ts.map +1 -1
  63. package/lib/providers/gitlab.js.map +1 -1
  64. package/lib/providers/interface.d.ts.map +1 -1
  65. package/lib/providers/interface.js.map +1 -1
  66. package/lib/review-findings-tool.d.ts.map +1 -1
  67. package/lib/review-findings-tool.js.map +1 -1
  68. package/lib/review-history.d.ts +4 -0
  69. package/lib/review-history.d.ts.map +1 -1
  70. package/lib/review-history.js +23 -1
  71. package/lib/review-history.js.map +1 -1
  72. package/lib/review-intake.d.ts.map +1 -1
  73. package/lib/review-intake.js.map +1 -1
  74. package/lib/review-signals.d.ts.map +1 -1
  75. package/lib/review-signals.js.map +1 -1
  76. package/lib/scope-split-tool.d.ts +42 -0
  77. package/lib/scope-split-tool.d.ts.map +1 -0
  78. package/lib/scope-split-tool.js +109 -0
  79. package/lib/scope-split-tool.js.map +1 -0
  80. package/lib/search-tool.d.ts +15 -0
  81. package/lib/search-tool.d.ts.map +1 -0
  82. package/lib/search-tool.js +144 -0
  83. package/lib/search-tool.js.map +1 -0
  84. package/lib/secure-compare.d.ts.map +1 -1
  85. package/lib/secure-compare.js.map +1 -1
  86. package/lib/settings-rpc.d.ts.map +1 -1
  87. package/lib/settings-rpc.js +47 -8
  88. package/lib/settings-rpc.js.map +1 -1
  89. package/lib/skills-tool.d.ts +1 -1
  90. package/lib/skills-tool.d.ts.map +1 -1
  91. package/lib/skills-tool.js +5 -1
  92. package/lib/skills-tool.js.map +1 -1
  93. package/lib/tdd-evidence-tool.d.ts +11 -0
  94. package/lib/tdd-evidence-tool.d.ts.map +1 -0
  95. package/lib/tdd-evidence-tool.js +114 -0
  96. package/lib/tdd-evidence-tool.js.map +1 -0
  97. package/lib/tool-policy.d.ts +20 -0
  98. package/lib/tool-policy.d.ts.map +1 -0
  99. package/lib/tool-policy.js +42 -0
  100. package/lib/tool-policy.js.map +1 -0
  101. package/lib/workspace-tool.d.ts.map +1 -1
  102. package/lib/workspace-tool.js +15 -9
  103. package/lib/workspace-tool.js.map +1 -1
  104. package/package.json +4 -4
  105. package/src/{augment.d.ts → host/augment.d.ts} +5 -2
  106. package/src/{config-store.ts → host/config-store.ts} +2 -0
  107. package/src/host/git-worktree-tool.ts +130 -0
  108. package/src/host/govard-audit-lint-tool.ts +153 -0
  109. package/src/host/hyva-csp-scan-tool.ts +165 -0
  110. package/src/host/hyva-theme-inspect-tool.ts +258 -0
  111. package/src/host/incremental.ts +84 -0
  112. package/src/host/layout-xml-tool.ts +191 -0
  113. package/src/host/module-check-tool.ts +188 -0
  114. package/src/{orchestrator.ts → host/orchestrator.ts} +205 -84
  115. package/src/host/perf-log-stats-tool.ts +224 -0
  116. package/src/host/phtml-escape-scan-tool.ts +117 -0
  117. package/src/host/plan-track-tool.ts +118 -0
  118. package/src/{review-history.ts → host/review-history.ts} +27 -1
  119. package/src/host/scope-split-tool.ts +110 -0
  120. package/src/host/search-tool.ts +153 -0
  121. package/src/{settings-rpc.ts → host/settings-rpc.ts} +54 -11
  122. package/src/{skills-tool.ts → host/skills-tool.ts} +5 -1
  123. package/src/host/tdd-evidence-tool.ts +85 -0
  124. package/src/host/tool-policy.ts +49 -0
  125. package/src/{workspace-tool.ts → host/workspace-tool.ts} +27 -9
  126. package/lib/pin-store.d.ts +0 -5
  127. package/lib/pin-store.d.ts.map +0 -1
  128. package/lib/pin-store.js +0 -5
  129. package/lib/pin-store.js.map +0 -1
  130. package/src/pin-store.ts +0 -4
  131. /package/src/{events.ts → host/events.ts} +0 -0
  132. /package/src/{gitlab-client.ts → host/gitlab-client.ts} +0 -0
  133. /package/src/{gitlab-webhook.ts → host/gitlab-webhook.ts} +0 -0
  134. /package/src/{govard-tool.ts → host/govard-tool.ts} +0 -0
  135. /package/src/{index.ts → host/index.ts} +0 -0
  136. /package/src/{notify.ts → host/notify.ts} +0 -0
  137. /package/src/{providers → host/providers}/github.stub.ts +0 -0
  138. /package/src/{providers → host/providers}/gitlab.ts +0 -0
  139. /package/src/{providers → host/providers}/interface.ts +0 -0
  140. /package/src/{review-findings-tool.ts → host/review-findings-tool.ts} +0 -0
  141. /package/src/{review-intake.ts → host/review-intake.ts} +0 -0
  142. /package/src/{review-signals.ts → host/review-signals.ts} +0 -0
  143. /package/src/{secure-compare.ts → host/secure-compare.ts} +0 -0
@@ -0,0 +1,245 @@
1
+ import { basename, dirname, resolve, sep } from 'node:path';
2
+ import { readFile, realpath, stat } from 'node:fs/promises';
3
+ import { spawn } from 'node:child_process';
4
+ import { defineTool } from '@deepseek-ai/dsh-tools';
5
+ import z from '@deepseek-ai/schemastery';
6
+ export const name = 'maestro-perf-log-stats-tool';
7
+ export const inject = ['tools'];
8
+ export const Config = z.object({ rootPath: z.string() });
9
+ function workspaceRootFor(c, e) { if (c !== undefined)
10
+ return c; const cwd = e?.agent?.session?.header?.cwd; return typeof cwd === 'string' && cwd !== '' ? cwd : process.cwd(); }
11
+ async function isInsideRoot(r, t) {
12
+ const ar = resolve(r);
13
+ const rs = resolve(ar, t);
14
+ if (rs !== ar && !rs.startsWith(ar + sep))
15
+ return false;
16
+ let realRoot;
17
+ try {
18
+ realRoot = await realpath(ar);
19
+ }
20
+ catch {
21
+ // if root does not exist yet (e.g. .worktrees not created), fallback to lexical check only
22
+ return true;
23
+ }
24
+ let existing = rs;
25
+ const pending = [];
26
+ while (true) {
27
+ let real;
28
+ try {
29
+ real = await realpath(existing);
30
+ }
31
+ catch (err) {
32
+ if (err.code !== 'ENOENT')
33
+ return false;
34
+ const parent = dirname(existing);
35
+ if (parent === existing)
36
+ return false;
37
+ pending.unshift(basename(existing));
38
+ existing = parent;
39
+ continue;
40
+ }
41
+ const realTarget = pending.length > 0 ? resolve(real, ...pending) : real;
42
+ if (realTarget !== realRoot && !realTarget.startsWith(realRoot + sep))
43
+ return false;
44
+ return true;
45
+ }
46
+ }
47
+ export const MAX_FILE_BYTES = 2 * 1024 * 1024;
48
+ export function cleanJson(raw) {
49
+ // strip trailing ERROR line outside JSON (large audit 2.8MB case, sed '$d' before jq)
50
+ // Handles both "\n ERROR audit run ..." and " ERROR audit run ..." suffix
51
+ return raw.replace(/\n\s*ERROR audit run.*$/s, '').replace(/\s{2,}ERROR audit run.*$/s, '').trimEnd();
52
+ }
53
+ async function govardShCat(worktreePath, relPath) {
54
+ // streaming cat var/debug/db.log server-side bounded 2MiB via govard sh
55
+ // returns null on govard not found or not a govard project, caller falls back to fs
56
+ return new Promise((res) => {
57
+ const child = spawn('govard', ['sh', '-c', `head -c ${MAX_FILE_BYTES} "${relPath.replace(/"/g, '\\"')}" 2>/dev/null`], { cwd: worktreePath });
58
+ let out = '';
59
+ let err = '';
60
+ child.stdout?.on('data', (c) => out += c.toString());
61
+ child.stderr?.on('data', (c) => err += c.toString());
62
+ child.on('error', () => res(null));
63
+ child.on('close', async (code) => {
64
+ const combined = out + err;
65
+ if (code !== 0) {
66
+ // govard not found, no container, or file missing -> fallback to fs
67
+ res(null);
68
+ return;
69
+ }
70
+ if (combined.includes('Error response from daemon') || combined.includes('No such container')) {
71
+ res(null);
72
+ return;
73
+ }
74
+ // Try to get full file size for truncated flag via fs stat (best effort)
75
+ try {
76
+ const abs = resolve(worktreePath, relPath);
77
+ const s = await stat(abs);
78
+ const truncated = s.size > MAX_FILE_BYTES;
79
+ const text = truncated ? out.slice(0, MAX_FILE_BYTES) : out;
80
+ res({ text, bytes: s.size, truncated });
81
+ }
82
+ catch {
83
+ // if stat fails (file missing or container-only file), infer from output length
84
+ // but if file missing, out is empty and code was 0? head on missing returns 1, already handled
85
+ // so infer truncated from size
86
+ const truncated = Buffer.byteLength(out, 'utf-8') >= MAX_FILE_BYTES;
87
+ // if out empty and code 0, file empty -> exists true with empty text; but we already filtered code !=0 for missing
88
+ // For safety, if out empty and we are here, treat as exists with empty
89
+ res({ text: out.slice(0, MAX_FILE_BYTES), bytes: Buffer.byteLength(out, 'utf-8'), truncated });
90
+ }
91
+ });
92
+ });
93
+ }
94
+ async function boundedRead(p, worktreePath, relPath) {
95
+ // Prefer govard sh streaming when worktreePath+relPath supplied and govard available
96
+ if (worktreePath && relPath) {
97
+ const via = await govardShCat(worktreePath, relPath);
98
+ if (via)
99
+ return { exists: true, text: via.text, bytes: via.bytes, truncated: via.truncated };
100
+ }
101
+ try {
102
+ const s = await stat(p);
103
+ if (s.size > MAX_FILE_BYTES) {
104
+ const b = await readFile(p);
105
+ // bounded 2MiB streaming server-side cat equivalent: slice to MAX
106
+ return { exists: true, text: b.toString('utf-8').slice(0, MAX_FILE_BYTES), bytes: s.size, truncated: true };
107
+ }
108
+ const b = await readFile(p);
109
+ return { exists: true, text: b.toString('utf-8'), bytes: b.byteLength, truncated: false };
110
+ }
111
+ catch {
112
+ return { exists: false, text: null, bytes: 0, truncated: false };
113
+ }
114
+ }
115
+ function normalizeShape(sql) {
116
+ return sql.replace(/'[^']*'/g, '?').replace(/"[^"]*"/g, '?').replace(/\b\d+\b/g, '?').replace(/\s+/g, ' ').trim().slice(0, 200);
117
+ }
118
+ export function apply(ctx, config = {}) {
119
+ const configuredRoot = config.rootPath;
120
+ ctx.effect(() => ctx.tools.register(defineTool({
121
+ name: 'maestro_perf_log_stats',
122
+ description: 'Parse Magento query log and profiler CSV for slow queries, n+1, cache flush storms, crawler overload.',
123
+ parameters: {
124
+ worktreePath: { type: 'string' },
125
+ queryLogPath: { type: 'string' },
126
+ profilerCsvPath: { type: 'string' },
127
+ systemLogPath: { type: 'string' },
128
+ debugLogPath: { type: 'string' },
129
+ topN: { type: 'number' },
130
+ repeatThreshold: { type: 'number' },
131
+ timeThresholdMs: { type: 'number' },
132
+ },
133
+ output: {
134
+ schema: {
135
+ type: 'object', additionalProperties: false,
136
+ properties: {
137
+ ok: { type: 'boolean', required: true },
138
+ slowQueries: { type: 'array', required: true },
139
+ nPlusOneCandidates: { type: 'array', required: true },
140
+ warnings: { type: 'array', required: true },
141
+ }
142
+ },
143
+ render: (_a, v) => [{ type: 'text', text: v.warnings.join('\n') || 'perf stats parsed' }],
144
+ },
145
+ async execute(args, exec) {
146
+ const rawWorktree = args.worktreePath;
147
+ const root = workspaceRootFor(configuredRoot, exec);
148
+ if (rawWorktree !== undefined && !(await isInsideRoot(root, rawWorktree)))
149
+ return { text: `Path "${rawWorktree}" escapes the workspace root.`, truncated: false };
150
+ const worktreePath = rawWorktree ? resolve(root, rawWorktree) : root;
151
+ const queryLogPath = args.queryLogPath ?? 'var/debug/db.log';
152
+ const profilerCsvPath = args.profilerCsvPath ?? 'artifacts/profiler/profile.csv';
153
+ const topN = args.topN ?? 10;
154
+ const repeatThreshold = args.repeatThreshold ?? 5;
155
+ const timeThresholdMs = args.timeThresholdMs ?? 1000;
156
+ if (rawWorktree !== undefined && !(await isInsideRoot(root, worktreePath))) { /* already checked */ }
157
+ const qLogAbs = resolve(worktreePath, queryLogPath);
158
+ if (!(await isInsideRoot(worktreePath, queryLogPath)) && queryLogPath !== 'var/debug/db.log')
159
+ return { text: `Path "${queryLogPath}" escapes the workspace root.`, truncated: false };
160
+ const warnings = [];
161
+ const qRead = await boundedRead(qLogAbs, worktreePath, queryLogPath);
162
+ const pRead = await boundedRead(resolve(worktreePath, profilerCsvPath), worktreePath, profilerCsvPath);
163
+ const inputs = {
164
+ queryLog: { path: queryLogPath, exists: qRead.exists, bytes: qRead.bytes, truncated: qRead.truncated },
165
+ profilerCsv: { path: profilerCsvPath, exists: pRead.exists, bytes: pRead.bytes, truncated: pRead.truncated },
166
+ systemLog: { path: 'var/log/system.log', exists: false, bytes: 0, truncated: false },
167
+ debugLog: { path: 'var/log/debug.log', exists: false, bytes: 0, truncated: false },
168
+ };
169
+ if (!qRead.exists)
170
+ warnings.push('query log not found — run dev:query-log:enable');
171
+ if (!pRead.exists)
172
+ warnings.push('profiler CSV not found');
173
+ if (qRead.exists && qRead.text && qRead.text.length === 0)
174
+ warnings.push('query log empty');
175
+ if (qRead.truncated)
176
+ warnings.push(`query log truncated to ${MAX_FILE_BYTES} bytes (bounded 2MiB streaming)`);
177
+ if (pRead.truncated)
178
+ warnings.push(`profiler CSV truncated to ${MAX_FILE_BYTES} bytes (bounded 2MiB streaming)`);
179
+ // parse db.log
180
+ const shapeMap = new Map();
181
+ if (qRead.text) {
182
+ const blocks = qRead.text.split(/##\s*\d+\s*##\s*QUERY/g);
183
+ for (const block of blocks) {
184
+ if (!block.trim())
185
+ continue;
186
+ const sqlMatch = /SQL:\s*([^\n]+)/.exec(block);
187
+ const timeMatch = /TIME:\s*([0-9.]+)/.exec(block);
188
+ const traceMatch = /TRACE:\s*([\s\S]*?)(?:\n##|$)/.exec(block); // capture trace
189
+ const sql = sqlMatch ? sqlMatch[1].trim() : null;
190
+ const timeSec = timeMatch ? parseFloat(timeMatch[1]) : 0;
191
+ const trace = traceMatch ? traceMatch[1] : block;
192
+ const isFrontend = /App\\Http|App\/Http|Http\.php/.test(trace);
193
+ if (!sql)
194
+ continue;
195
+ if (!isFrontend)
196
+ continue;
197
+ const ms = timeSec * 1000;
198
+ const shape = normalizeShape(sql);
199
+ let entry = shapeMap.get(shape);
200
+ if (!entry) {
201
+ entry = { count: 0, totalMs: 0, maxMs: 0, example: sql, callers: [] };
202
+ shapeMap.set(shape, entry);
203
+ }
204
+ entry.count++;
205
+ entry.totalMs += ms;
206
+ entry.maxMs = Math.max(entry.maxMs, ms);
207
+ // extract caller
208
+ const callerLine = (trace.split('\n').find(l => l.includes('app/code')) || '').trim().slice(0, 120);
209
+ if (callerLine && !entry.callers.includes(callerLine))
210
+ entry.callers.push(callerLine);
211
+ }
212
+ }
213
+ const slowQueries = [];
214
+ const nPlusOneCandidates = [];
215
+ for (const [shape, v] of shapeMap.entries()) {
216
+ const avg = v.totalMs / v.count;
217
+ if (v.maxMs >= timeThresholdMs || avg >= timeThresholdMs) {
218
+ slowQueries.push({ query: shape, shape, count: v.count, avgMs: Math.round(avg), maxMs: Math.round(v.maxMs), exampleCallerStack: v.callers.join(' | ') });
219
+ }
220
+ if (v.count >= repeatThreshold) {
221
+ // infer model from caller
222
+ const modelMatch = v.callers.join(' ').match(/app\/code\/[^\/]+\/[^\/]+\/Model\/(\w+)/);
223
+ const model = modelMatch ? modelMatch[1] : null;
224
+ nPlusOneCandidates.push({ shape, count: v.count, model, callerStack: v.callers.join(' | ') });
225
+ }
226
+ }
227
+ slowQueries.sort((a, b) => b.maxMs - a.maxMs);
228
+ nPlusOneCandidates.sort((a, b) => b.count - a.count);
229
+ return {
230
+ ok: true,
231
+ worktreePath,
232
+ inputs,
233
+ slowQueries: slowQueries.slice(0, topN),
234
+ nPlusOneCandidates: nPlusOneCandidates.slice(0, topN),
235
+ cacheFlushStorm: { eventsPerMin: 0, rawEvents: [], windowMin: 5 },
236
+ crawlerOverload: false,
237
+ crawlerEvidence: { hitsPerMin: 0, topUA: null, topIP: null },
238
+ queryShapes: Array.from(shapeMap.entries()).map(([shape, c]) => ({ shape, count: c.count })),
239
+ topTimers: [],
240
+ warnings,
241
+ };
242
+ }
243
+ }), 'perf log stats'));
244
+ }
245
+ //# sourceMappingURL=perf-log-stats-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf-log-stats-tool.js","sourceRoot":"","sources":["../src/host/perf-log-stats-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import z from '@deepseek-ai/schemastery';
3
+ export declare const name = "maestro-phtml-escape-scan-tool";
4
+ export declare const inject: string[];
5
+ export declare const Config: z<{
6
+ rootPath?: string;
7
+ }>;
8
+ export declare function apply(ctx: Context, config?: {
9
+ rootPath?: string;
10
+ }): void;
11
+ //# sourceMappingURL=phtml-escape-scan-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phtml-escape-scan-tool.d.ts","sourceRoot":"","sources":["../src/host/phtml-escape-scan-tool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAGxC,eAAO,MAAM,IAAI,mCAAiC,CAAA;AAClD,eAAO,MAAM,MAAM,UAAU,CAAA;AAC7B,eAAO,MAAM,MAAM,EAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAC,MAAM,CAAA;CAAC,CAAiC,CAAA;AAyBzE,wBAAgB,KAAK,CAAC,GAAG,EAAC,OAAO,EAAE,MAAM,GAAC;IAAC,QAAQ,CAAC,EAAC,MAAM,CAAA;CAAI,GAAE,IAAI,CAmFpE"}
@@ -0,0 +1,170 @@
1
+ import { join, resolve, sep, basename } from 'node:path';
2
+ import { readFile, readdir, realpath, stat } from 'node:fs/promises';
3
+ import z from '@deepseek-ai/schemastery';
4
+ import { defineTool } from '@deepseek-ai/dsh-tools';
5
+ export const name = 'maestro-phtml-escape-scan-tool';
6
+ export const inject = ['tools'];
7
+ export const Config = z.object({ rootPath: z.string() });
8
+ function workspaceRootFor(c, e) { if (c !== undefined)
9
+ return c; const cwd = e?.agent?.session?.header?.cwd; return typeof cwd === 'string' && cwd !== '' ? cwd : process.cwd(); }
10
+ async function isInsideRoot(r, t) { const ar = resolve(r); const rs = resolve(ar, t); if (rs !== ar && !rs.startsWith(ar + sep))
11
+ return false; try {
12
+ const rr = await realpath(ar);
13
+ const rt = await realpath(rs);
14
+ return rt === rr || rt.startsWith(rr + sep);
15
+ }
16
+ catch {
17
+ return false;
18
+ } }
19
+ const MAX_FILE_BYTES = 1024 * 1024;
20
+ const MAX_PHTML_FILES = 200;
21
+ const MAX_FINDINGS = 200;
22
+ const SKIP_DIRS = new Set(['.git', 'node_modules', 'vendor', 'pub/static']);
23
+ async function exists(p) { try {
24
+ await stat(p);
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ } }
30
+ async function* walkPhtml(dir, budget) {
31
+ if (budget.files <= 0)
32
+ return;
33
+ let entries;
34
+ try {
35
+ entries = await readdir(dir, { withFileTypes: true });
36
+ }
37
+ catch {
38
+ return;
39
+ }
40
+ for (const e of entries) {
41
+ if (budget.files <= 0)
42
+ return;
43
+ if (e.isDirectory()) {
44
+ if (SKIP_DIRS.has(e.name))
45
+ continue;
46
+ yield* walkPhtml(join(dir, e.name), budget);
47
+ continue;
48
+ }
49
+ if (!e.isFile())
50
+ continue;
51
+ if (!e.name.endsWith('.phtml'))
52
+ continue;
53
+ budget.files -= 1;
54
+ yield join(dir, e.name);
55
+ }
56
+ }
57
+ function isSafeLine(line) {
58
+ return /\$escaper\s*->\s*escape/.test(line) || /\$block\s*->\s*escape/.test(line) || /->\s*get\w*Html\s*\(/.test(line) || /escapeHtmlAttr/.test(line) && /title=/.test(line) ? true : false;
59
+ // second case handled more precisely below
60
+ }
61
+ export function apply(ctx, config = {}) {
62
+ const configuredRoot = config.rootPath;
63
+ ctx.effect(() => ctx.tools.register(defineTool({
64
+ name: 'phtml_escape_scan',
65
+ description: 'Scan .phtml for XSS: unescaped echo, missing escapeHtmlAttr, raw block methods, superglobals.',
66
+ parameters: {
67
+ scope: { type: 'string', enum: ['diff', 'paths'], required: true },
68
+ paths: { type: 'array', items: { type: 'string' } },
69
+ },
70
+ output: {
71
+ schema: {
72
+ type: 'object', additionalProperties: false,
73
+ properties: {
74
+ findings: { type: 'array', required: true },
75
+ scannedFiles: { type: 'number', required: true },
76
+ skippedFiles: { type: 'number', required: true },
77
+ truncated: { type: 'boolean', required: true },
78
+ summary: { type: 'object', required: true, additionalProperties: true },
79
+ }
80
+ },
81
+ render: (_a, v) => [{ type: 'text', text: `${v.findings.length} findings` }],
82
+ },
83
+ async execute(args, exec) {
84
+ const root = workspaceRootFor(configuredRoot, exec);
85
+ const scope = args.scope;
86
+ const paths = args.paths ?? [];
87
+ for (const p of paths) {
88
+ if (!(await isInsideRoot(root, p)))
89
+ return { text: `Path "${p}" escapes the workspace root.`, truncated: false };
90
+ }
91
+ let files = [];
92
+ if (scope === 'paths') {
93
+ for (const p of paths) {
94
+ if (!p.endsWith('.phtml'))
95
+ continue;
96
+ const abs = resolve(root, p);
97
+ if (await exists(abs))
98
+ files.push(p);
99
+ }
100
+ }
101
+ else {
102
+ // diff: walk all phtml bounded
103
+ const budget = { files: MAX_PHTML_FILES };
104
+ for await (const f of walkPhtml(root, budget)) {
105
+ const rel = f.startsWith(root + sep) ? f.slice(root.length + 1) : basename(f);
106
+ files.push(rel);
107
+ if (files.length >= MAX_PHTML_FILES)
108
+ break;
109
+ }
110
+ }
111
+ const findings = [];
112
+ let scannedFiles = 0, skippedFiles = 0;
113
+ for (const rel of files.slice(0, MAX_PHTML_FILES)) {
114
+ if (findings.length >= MAX_FINDINGS)
115
+ break;
116
+ const abs = resolve(root, rel);
117
+ let text;
118
+ try {
119
+ const b = await readFile(abs);
120
+ if (b.byteLength > MAX_FILE_BYTES) {
121
+ skippedFiles++;
122
+ continue;
123
+ }
124
+ text = b.toString('utf-8');
125
+ }
126
+ catch {
127
+ skippedFiles++;
128
+ continue;
129
+ }
130
+ scannedFiles++;
131
+ const lines = text.split('\n');
132
+ lines.forEach((line, idx) => {
133
+ if (findings.length >= MAX_FINDINGS)
134
+ return;
135
+ const snippet = line.trim().slice(0, 240);
136
+ const lineNo = idx + 1;
137
+ // superglobal always flagged
138
+ if (/\$_GET|\$_POST|\$_REQUEST|\$_COOKIE/.test(line)) {
139
+ findings.push({ file: rel, line: lineNo, type: 'direct-superglobal', snippet, confidence: 'high', rule: 'M2-SEC-superglobal', escaperHint: 'validate/sanitize input, do not echo raw' });
140
+ return;
141
+ }
142
+ // check <?= ... ?> echo
143
+ const echoRe = /<\?=\s*([^?]*?)\s*\?>/g;
144
+ let m;
145
+ while ((m = echoRe.exec(line)) !== null) {
146
+ const inner = m[1];
147
+ // safe if contains escaper
148
+ if (/\$escaper\s*->\s*escape/.test(inner) || /\$block\s*->\s*escape/.test(inner) || /->\s*get\w*Html\s*\(/.test(inner))
149
+ continue;
150
+ // attr check: if line has title=" <?= ... ?> and not escapeHtmlAttr -> unescaped-attr
151
+ if (/title\s*=\s*"/.test(line) && !/escapeHtmlAttr/.test(inner)) {
152
+ findings.push({ file: rel, line: lineNo, type: 'unescaped-attr', snippet, confidence: 'high', rule: 'M2-SEC-attr', escaperHint: '$escaper->escapeHtmlAttr() for attributes' });
153
+ }
154
+ else if (/^\s*\$[a-zA-Z_][\w$]*\s*$/.test(inner.trim()) || /\$\w+/.test(inner)) {
155
+ // bare variable echo
156
+ // if this was already handled as attr, don't double count
157
+ if (findings.some(f => f.file === rel && f.line === lineNo && f.type === 'unescaped-attr'))
158
+ continue;
159
+ findings.push({ file: rel, line: lineNo, type: 'unescaped-echo', snippet, confidence: 'high', rule: 'M2-SEC-echo', escaperHint: '$escaper->escapeHtml() or $block->escapeHtml()' });
160
+ }
161
+ }
162
+ });
163
+ }
164
+ const truncated = files.length >= MAX_PHTML_FILES || findings.length >= MAX_FINDINGS;
165
+ const summary = { high: findings.filter(f => f.confidence === 'high').length, medium: findings.filter(f => f.confidence === 'medium').length, low: findings.filter(f => f.confidence === 'low').length, filesWithFindings: new Set(findings.map(f => f.file)).size };
166
+ return { findings: findings.slice(0, MAX_FINDINGS), scannedFiles, skippedFiles, truncated, summary };
167
+ },
168
+ }), 'phtml escape'));
169
+ }
170
+ //# sourceMappingURL=phtml-escape-scan-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phtml-escape-scan-tool.js","sourceRoot":"","sources":["../src/host/phtml-escape-scan-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import z from '@deepseek-ai/schemastery';
3
+ export declare const name = "maestro-plan-track-tool";
4
+ export declare const inject: string[];
5
+ export declare const Config: z<{
6
+ rootPath?: string;
7
+ }>;
8
+ export declare function apply(ctx: Context, config?: {
9
+ rootPath?: string;
10
+ }): void;
11
+ //# sourceMappingURL=plan-track-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-track-tool.d.ts","sourceRoot":"","sources":["../src/host/plan-track-tool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,eAAO,MAAM,IAAI,4BAA0B,CAAA;AAC3C,eAAO,MAAM,MAAM,UAAU,CAAA;AAC7B,eAAO,MAAM,MAAM,EAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAC,MAAM,CAAA;CAAC,CAAmC,CAAA;AAiC3E,wBAAgB,KAAK,CAAC,GAAG,EAAC,OAAO,EAAE,MAAM,GAAC;IAAC,QAAQ,CAAC,EAAC,MAAM,CAAA;CAAI,GAAE,IAAI,CA4EpE"}
@@ -0,0 +1,143 @@
1
+ import { join, resolve, sep } from 'node:path';
2
+ import { readFile, writeFile, mkdir, realpath } from 'node:fs/promises';
3
+ import { defineTool } from '@deepseek-ai/dsh-tools';
4
+ import z from '@deepseek-ai/schemastery';
5
+ export const name = 'maestro-plan-track-tool';
6
+ export const inject = ['tools'];
7
+ export const Config = z.object({ rootPath: z.string() });
8
+ function workspaceRootFor(c, e) { if (c !== undefined)
9
+ return c; const cwd = e?.agent?.session?.header?.cwd; return typeof cwd === 'string' && cwd !== '' ? cwd : process.cwd(); }
10
+ async function isInsideRoot(r, t) {
11
+ const ar = resolve(r);
12
+ const rs = resolve(ar, t);
13
+ if (rs !== ar && !rs.startsWith(ar + sep))
14
+ return false;
15
+ try {
16
+ const rr = await realpath(ar);
17
+ const rt = await realpath(rs);
18
+ return rt === rr || rt.startsWith(rr + sep);
19
+ }
20
+ catch {
21
+ try {
22
+ const rr = await realpath(ar);
23
+ const parent = resolve(rs, '..');
24
+ const rp = await realpath(parent);
25
+ return rp === rr || rp.startsWith(rr + sep);
26
+ }
27
+ catch {
28
+ return rs === ar || rs.startsWith(ar + sep);
29
+ }
30
+ }
31
+ }
32
+ function parseTasks(md) {
33
+ const lines = md.split('\n');
34
+ const tasks = [];
35
+ for (let i = 0; i < lines.length; i++) {
36
+ const m = /^###\s+(Task\s*\d+):?\s*(.*)/.exec(lines[i].trim());
37
+ if (!m)
38
+ continue;
39
+ const id = m[1].trim();
40
+ let title = m[2].trim();
41
+ let status = 'pending';
42
+ if (title.includes('in_progress'))
43
+ status = 'in_progress';
44
+ else if (title.toLowerCase().includes('done') || title.includes('✅'))
45
+ status = 'done';
46
+ else if (title.toLowerCase().includes('blocked'))
47
+ status = 'blocked';
48
+ for (let j = i + 1; j < Math.min(i + 4, lines.length); j++) {
49
+ if (lines[j].toLowerCase().includes('in_progress'))
50
+ status = 'in_progress';
51
+ if (lines[j].toLowerCase().includes('status: done'))
52
+ status = 'done';
53
+ }
54
+ tasks.push({ id, title, status, line: i });
55
+ }
56
+ return tasks;
57
+ }
58
+ export function apply(ctx, config = {}) {
59
+ const configuredRoot = config.rootPath;
60
+ ctx.effect(() => ctx.tools.register(defineTool({
61
+ name: 'maestro_plan_track',
62
+ description: 'Track cross-repo plan: init/update/complete tasks with dtodo sync and oneTaskOneCommit guard.',
63
+ parameters: {
64
+ op: { type: 'string', enum: ['init', 'update', 'complete'], required: true },
65
+ planPath: { type: 'string', required: true },
66
+ taskId: { type: 'string' },
67
+ status: { type: 'string', enum: ['pending', 'in_progress', 'done', 'blocked'] },
68
+ note: { type: 'string' },
69
+ },
70
+ output: {
71
+ schema: {
72
+ type: 'object', additionalProperties: true,
73
+ properties: {
74
+ ok: { type: 'boolean', required: true },
75
+ plan: { type: 'object', required: true, additionalProperties: true },
76
+ discipline: { type: 'object', additionalProperties: true },
77
+ }
78
+ },
79
+ render: (_a, v) => [{ type: 'text', text: v.ok ? 'ok' : 'violation' }],
80
+ },
81
+ async execute(args, exec) {
82
+ const op = args.op;
83
+ const rawPath = args.planPath;
84
+ const root = workspaceRootFor(configuredRoot, exec);
85
+ if (!(await isInsideRoot(root, rawPath)))
86
+ return { text: `Path "${rawPath}" escapes the workspace root.`, truncated: false };
87
+ const planPath = resolve(root, rawPath);
88
+ let md = '';
89
+ try {
90
+ md = await readFile(planPath, 'utf-8');
91
+ }
92
+ catch {
93
+ if (op === 'init')
94
+ return { text: `plan not found ${rawPath}`, truncated: false };
95
+ md = '';
96
+ }
97
+ const tasks = parseTasks(md);
98
+ const lines = md.split('\n');
99
+ if (op === 'init') {
100
+ const nextTask = tasks.find(t => t.status === 'pending' || t.status === 'in_progress') || tasks[0] || null;
101
+ const dtodoPayload = tasks.map(t => ({ content: `${t.id}: ${t.title}`, status: t.status }));
102
+ return { ok: true, plan: { path: rawPath, tasks, nextTask, dtodoPayload }, discipline: { violation: null } };
103
+ }
104
+ if (op === 'update') {
105
+ const taskId = args.taskId;
106
+ const status = args.status;
107
+ if (!taskId)
108
+ return { text: 'taskId required', truncated: false };
109
+ const idx = tasks.findIndex(t => t.id === taskId);
110
+ if (idx < 0)
111
+ return { text: `task ${taskId} not found`, truncated: false };
112
+ if (status === 'in_progress') {
113
+ const other = tasks.find(t => t.id !== taskId && t.status === 'in_progress');
114
+ if (other) {
115
+ return { ok: false, plan: { path: rawPath, tasks, nextTask: tasks.find(t => t.status === 'in_progress') || null, dtodoPayload: [] }, discipline: { violation: 'oneTaskOneCommit: only one task may be in_progress at a time' } };
116
+ }
117
+ }
118
+ const lineIdx = tasks[idx].line;
119
+ if (status) {
120
+ const orig = lines[lineIdx];
121
+ if (orig.includes('—'))
122
+ lines[lineIdx] = orig.replace(/—.*$/, `— ${status}`);
123
+ else
124
+ lines[lineIdx] = `${orig} — ${status}`;
125
+ await mkdir(join(planPath, '..'), { recursive: true });
126
+ await writeFile(planPath, lines.join('\n'), 'utf-8');
127
+ tasks[idx].status = status;
128
+ }
129
+ const nextTask = tasks.find(t => t.status === 'in_progress') || tasks.find(t => t.status === 'pending') || null;
130
+ return { ok: true, plan: { path: rawPath, tasks, nextTask, dtodoPayload: tasks.map(t => ({ content: `${t.id}: ${t.title}`, status: t.status })) }, discipline: { violation: null } };
131
+ }
132
+ if (op === 'complete') {
133
+ const pending = tasks.filter(t => t.status !== 'done');
134
+ if (pending.length > 0) {
135
+ return { ok: false, plan: { path: rawPath, tasks, nextTask: pending[0], dtodoPayload: [] }, discipline: { violation: `pending tasks remain: ${pending.map(p => p.id).join(',')}` } };
136
+ }
137
+ return { ok: true, plan: { path: rawPath, tasks, nextTask: null, dtodoPayload: [] }, discipline: { violation: null } };
138
+ }
139
+ return { text: `unknown op ${op}`, truncated: false };
140
+ }
141
+ }), 'plan track'));
142
+ }
143
+ //# sourceMappingURL=plan-track-tool.js.map