@gempack/squad-mcp 0.6.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +6 -3
- package/CHANGELOG.md +37 -0
- package/INSTALL.md +15 -0
- package/README.md +32 -0
- package/agents/product-owner.md +9 -0
- package/agents/senior-architect.md +12 -0
- package/agents/senior-dba.md +15 -1
- package/agents/senior-dev-reviewer.md +100 -29
- package/agents/senior-dev-security.md +13 -0
- package/agents/senior-developer.md +15 -0
- package/agents/senior-qa.md +13 -0
- package/agents/tech-lead-consolidator.md +10 -0
- package/agents/tech-lead-planner.md +9 -0
- package/commands/squad-next.md +24 -0
- package/commands/squad-task.md +29 -0
- package/commands/squad-tasks.md +21 -0
- package/dist/config/ownership-matrix.js +4 -20
- package/dist/config/ownership-matrix.js.map +1 -1
- package/dist/config/squad-yaml.js +3 -7
- package/dist/config/squad-yaml.js.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/exec/git.d.ts +1 -1
- package/dist/exec/git.js +0 -0
- package/dist/exec/git.js.map +1 -1
- package/dist/format/pr-review.js +1 -3
- package/dist/format/pr-review.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/learning/format.js +1 -5
- package/dist/learning/format.js.map +1 -1
- package/dist/learning/store.js +89 -16
- package/dist/learning/store.js.map +1 -1
- package/dist/observability/logger.d.ts +2 -2
- package/dist/observability/logger.js +20 -20
- package/dist/observability/logger.js.map +1 -1
- package/dist/prompts/registry.js.map +1 -1
- package/dist/resources/agent-loader.js.map +1 -1
- package/dist/resources/registry.js +28 -28
- package/dist/tasks/select.js.map +1 -1
- package/dist/tasks/store.js +49 -11
- package/dist/tasks/store.js.map +1 -1
- package/dist/tools/_shared/schemas.d.ts +21 -0
- package/dist/tools/_shared/schemas.js +25 -0
- package/dist/tools/_shared/schemas.js.map +1 -0
- package/dist/tools/agents.d.ts +3 -3
- package/dist/tools/agents.js +9 -9
- package/dist/tools/agents.js.map +1 -1
- package/dist/tools/classify-work-type.d.ts +5 -5
- package/dist/tools/classify-work-type.js +0 -0
- package/dist/tools/classify-work-type.js.map +1 -1
- package/dist/tools/compose-advisory-bundle.js +4 -14
- package/dist/tools/compose-advisory-bundle.js.map +1 -1
- package/dist/tools/compose-prd-parse.js.map +1 -1
- package/dist/tools/compose-squad-workflow.js +0 -0
- package/dist/tools/compose-squad-workflow.js.map +1 -1
- package/dist/tools/consolidate.js +1 -3
- package/dist/tools/consolidate.js.map +1 -1
- package/dist/tools/detect-changed-files.d.ts +5 -6
- package/dist/tools/detect-changed-files.js +0 -0
- package/dist/tools/detect-changed-files.js.map +1 -1
- package/dist/tools/list-tasks.js +1 -8
- package/dist/tools/list-tasks.js.map +1 -1
- package/dist/tools/next-task.js +1 -8
- package/dist/tools/next-task.js.map +1 -1
- package/dist/tools/read-learnings.js +2 -4
- package/dist/tools/read-learnings.js.map +1 -1
- package/dist/tools/read-squad-config.js +1 -1
- package/dist/tools/read-squad-config.js.map +1 -1
- package/dist/tools/record-tasks.js.map +1 -1
- package/dist/tools/registry.js +2 -4
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/score-risk.d.ts +3 -3
- package/dist/tools/score-risk.js +15 -15
- package/dist/tools/score-rubric.js.map +1 -1
- package/dist/tools/select-squad.d.ts +5 -5
- package/dist/tools/select-squad.js +0 -0
- package/dist/tools/select-squad.js.map +1 -1
- package/dist/tools/slice-files-for-task.js.map +1 -1
- package/dist/tools/slice-files.d.ts +2 -2
- package/dist/tools/slice-files.js +0 -0
- package/dist/tools/slice-files.js.map +1 -1
- package/dist/tools/update-task-status.js +1 -8
- package/dist/tools/update-task-status.js.map +1 -1
- package/dist/tools/validate-plan-text.d.ts +3 -3
- package/dist/tools/validate-plan-text.js +0 -0
- package/dist/tools/validate-plan-text.js.map +1 -1
- package/dist/util/file-lock.d.ts +10 -0
- package/dist/util/file-lock.js +102 -0
- package/dist/util/file-lock.js.map +1 -0
- package/dist/util/override-allowlist.d.ts +4 -4
- package/dist/util/override-allowlist.js +36 -27
- package/dist/util/override-allowlist.js.map +1 -1
- package/dist/util/path-internal.js +10 -8
- package/dist/util/path-internal.js.map +1 -1
- package/dist/util/path-safety.d.ts +15 -0
- package/dist/util/path-safety.js +47 -13
- package/dist/util/path-safety.js.map +1 -1
- package/package.json +13 -2
- package/shared/Skill-Squad-Dev.md +38 -27
- package/shared/Skill-Squad-Review.md +49 -26
- package/shared/_Severity-and-Ownership.md +6 -6
- package/skills/brainstorm/SKILL.md +31 -20
- package/skills/commit-suggest/SKILL.md +32 -14
- package/tools/_tasks-io.mjs +25 -16
- package/tools/list-tasks.mjs +1 -4
- package/tools/next-task.mjs +4 -13
- package/tools/post-review.mjs +20 -30
- package/tools/record-learning.mjs +8 -11
- package/tools/record-tasks.mjs +2 -9
- package/tools/update-task-status.mjs +2 -9
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { promises as fs } from
|
|
2
|
-
import path from
|
|
3
|
-
import os from
|
|
4
|
-
import { SquadError } from
|
|
5
|
-
import { rejectIfMalformed, realpathOrSelf } from
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { SquadError } from "../errors.js";
|
|
5
|
+
import { rejectIfMalformed, realpathOrSelf } from "./path-internal.js";
|
|
6
6
|
let allowlistCache = null;
|
|
7
7
|
/**
|
|
8
8
|
* Test-only: clears the memoized allowlist so env-var changes take effect.
|
|
@@ -12,16 +12,16 @@ export function __resetOverrideAllowlistCache() {
|
|
|
12
12
|
allowlistCache = null;
|
|
13
13
|
}
|
|
14
14
|
function isUnsafeOverrideEnabled() {
|
|
15
|
-
return process.env.SQUAD_AGENTS_ALLOW_UNSAFE ===
|
|
15
|
+
return process.env.SQUAD_AGENTS_ALLOW_UNSAFE === "1";
|
|
16
16
|
}
|
|
17
17
|
function isUncOrDeviceNamespace(absolute) {
|
|
18
|
-
if (process.platform !==
|
|
18
|
+
if (process.platform !== "win32")
|
|
19
19
|
return false;
|
|
20
|
-
if (absolute.startsWith(
|
|
20
|
+
if (absolute.startsWith("\\\\?\\"))
|
|
21
21
|
return true;
|
|
22
|
-
if (absolute.startsWith(
|
|
22
|
+
if (absolute.startsWith("\\\\.\\"))
|
|
23
23
|
return true;
|
|
24
|
-
if (absolute.startsWith(
|
|
24
|
+
if (absolute.startsWith("\\\\"))
|
|
25
25
|
return true;
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
@@ -31,15 +31,15 @@ async function buildAllowlist() {
|
|
|
31
31
|
const roots = [];
|
|
32
32
|
const seen = new Set();
|
|
33
33
|
const candidates = [
|
|
34
|
-
{ source:
|
|
35
|
-
{ source:
|
|
34
|
+
{ source: "home", raw: os.homedir() },
|
|
35
|
+
{ source: "cwd", raw: process.cwd() },
|
|
36
36
|
];
|
|
37
|
-
if (process.platform ===
|
|
38
|
-
candidates.push({ source:
|
|
39
|
-
candidates.push({ source:
|
|
37
|
+
if (process.platform === "win32") {
|
|
38
|
+
candidates.push({ source: "appdata", raw: process.env.APPDATA });
|
|
39
|
+
candidates.push({ source: "localappdata", raw: process.env.LOCALAPPDATA });
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
candidates.push({ source:
|
|
42
|
+
candidates.push({ source: "xdg_config_home", raw: process.env.XDG_CONFIG_HOME });
|
|
43
43
|
}
|
|
44
44
|
for (const c of candidates) {
|
|
45
45
|
if (!c.raw)
|
|
@@ -69,13 +69,13 @@ function isInsideRoot(candidate, root) {
|
|
|
69
69
|
if (candidate === root)
|
|
70
70
|
return true;
|
|
71
71
|
const rel = path.relative(root, candidate);
|
|
72
|
-
if (rel ===
|
|
72
|
+
if (rel === "" || rel === ".")
|
|
73
73
|
return true;
|
|
74
74
|
if (path.isAbsolute(rel))
|
|
75
75
|
return false;
|
|
76
|
-
if (rel ===
|
|
76
|
+
if (rel === "..")
|
|
77
77
|
return false;
|
|
78
|
-
if (rel.startsWith(
|
|
78
|
+
if (rel.startsWith(".." + path.sep))
|
|
79
79
|
return false;
|
|
80
80
|
return true;
|
|
81
81
|
}
|
|
@@ -103,14 +103,14 @@ export async function validateOverrideDir(rawDir) {
|
|
|
103
103
|
try {
|
|
104
104
|
rejectIfMalformed(rawDir);
|
|
105
105
|
}
|
|
106
|
-
catch
|
|
107
|
-
return { ok: false, reason:
|
|
106
|
+
catch {
|
|
107
|
+
return { ok: false, reason: "malformed", rejectedPath: rawDir };
|
|
108
108
|
}
|
|
109
109
|
if (!path.isAbsolute(rawDir)) {
|
|
110
|
-
return { ok: false, reason:
|
|
110
|
+
return { ok: false, reason: "not_absolute", rejectedPath: rawDir };
|
|
111
111
|
}
|
|
112
112
|
if (isUncOrDeviceNamespace(rawDir)) {
|
|
113
|
-
return { ok: false, reason:
|
|
113
|
+
return { ok: false, reason: "unc_or_device_namespace", rejectedPath: rawDir };
|
|
114
114
|
}
|
|
115
115
|
const lexical = path.normalize(rawDir);
|
|
116
116
|
const real = await realpathOrSelf(lexical);
|
|
@@ -129,9 +129,16 @@ export async function validateOverrideDir(rawDir) {
|
|
|
129
129
|
break;
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
-
const reason = lexicalMatchExists
|
|
132
|
+
const reason = lexicalMatchExists
|
|
133
|
+
? "symlink_escape"
|
|
134
|
+
: "outside_allowlist";
|
|
133
135
|
if (unsafe) {
|
|
134
|
-
return {
|
|
136
|
+
return {
|
|
137
|
+
ok: true,
|
|
138
|
+
resolvedPath: real,
|
|
139
|
+
allowlistMatch: "unsafe_override",
|
|
140
|
+
unsafeOverride: true,
|
|
141
|
+
};
|
|
135
142
|
}
|
|
136
143
|
return { ok: false, reason, rejectedPath: rawDir };
|
|
137
144
|
}
|
|
@@ -140,7 +147,7 @@ export async function validateOverrideDir(rawDir) {
|
|
|
140
147
|
* Caller decides whether to throw or downgrade to a warn-and-fallback.
|
|
141
148
|
*/
|
|
142
149
|
export function rejectionToError(fail, allowlistSize) {
|
|
143
|
-
return new SquadError(
|
|
150
|
+
return new SquadError("OVERRIDE_REJECTED", `override directory rejected: ${fail.reason}`, {
|
|
144
151
|
reason: fail.reason,
|
|
145
152
|
path: fail.rejectedPath,
|
|
146
153
|
allowlist_size: allowlistSize,
|
|
@@ -169,7 +176,9 @@ export async function validateOverrideFile(validatedDirReal, fileName) {
|
|
|
169
176
|
const normalized = path.normalize(fileName);
|
|
170
177
|
if (path.isAbsolute(normalized))
|
|
171
178
|
return null;
|
|
172
|
-
if (normalized ===
|
|
179
|
+
if (normalized === ".." ||
|
|
180
|
+
normalized.startsWith(".." + path.sep) ||
|
|
181
|
+
normalized.includes(path.sep + ".." + path.sep)) {
|
|
173
182
|
return null;
|
|
174
183
|
}
|
|
175
184
|
const candidate = path.resolve(validatedDirReal, normalized);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"override-allowlist.js","sourceRoot":"","sources":["../../src/util/override-allowlist.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"override-allowlist.js","sourceRoot":"","sources":["../../src/util/override-allowlist.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import { promises as fs } from
|
|
2
|
-
import { SquadError } from
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import { SquadError } from "../errors.js";
|
|
3
3
|
/**
|
|
4
4
|
* Internal helpers shared between path-safety.ts and override-allowlist.ts.
|
|
5
5
|
* Exported only for intra-`util/` reuse; do not import from outside src/util/.
|
|
6
6
|
*/
|
|
7
7
|
export function rejectIfMalformed(file) {
|
|
8
|
-
if (file.includes(
|
|
9
|
-
throw new SquadError(
|
|
8
|
+
if (file.includes("\0")) {
|
|
9
|
+
throw new SquadError("PATH_INVALID", "file path contains NUL byte", { file });
|
|
10
10
|
}
|
|
11
|
-
if (file.startsWith(
|
|
12
|
-
throw new SquadError(
|
|
11
|
+
if (file.startsWith("~")) {
|
|
12
|
+
throw new SquadError("PATH_INVALID", "file path starts with ~ (tilde expansion not supported)", { file });
|
|
13
13
|
}
|
|
14
|
-
const adsIndex = file.indexOf(
|
|
14
|
+
const adsIndex = file.indexOf(":", 2);
|
|
15
15
|
if (adsIndex !== -1) {
|
|
16
|
-
throw new SquadError(
|
|
16
|
+
throw new SquadError("PATH_INVALID", "file path contains ADS marker (:) after drive letter", {
|
|
17
|
+
file,
|
|
18
|
+
});
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
21
|
export async function realpathOrSelf(p) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-internal.js","sourceRoot":"","sources":["../../src/util/path-internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C;;;GAGG;AAEH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,UAAU,CAAC,cAAc,EAAE,6BAA6B,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,UAAU,
|
|
1
|
+
{"version":3,"file":"path-internal.js","sourceRoot":"","sources":["../../src/util/path-internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C;;;GAGG;AAEH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,UAAU,CAAC,cAAc,EAAE,6BAA6B,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,UAAU,CAClB,cAAc,EACd,yDAAyD,EACzD,EAAE,IAAI,EAAE,CACT,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACtC,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,UAAU,CAAC,cAAc,EAAE,sDAAsD,EAAE;YAC3F,IAAI;SACL,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,CAAS;IAC5C,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC"}
|
|
@@ -3,6 +3,21 @@ export interface SafePathContext {
|
|
|
3
3
|
rootRealCache: Map<string, string>;
|
|
4
4
|
}
|
|
5
5
|
export declare function createSafePathContext(): SafePathContext;
|
|
6
|
+
/**
|
|
7
|
+
* Lexical-only containment check for a config-supplied relative path
|
|
8
|
+
* (e.g. `.squad.yaml` `learnings.path` / `tasks.path`).
|
|
9
|
+
*
|
|
10
|
+
* Sync; no filesystem access. Throws PATH_TRAVERSAL_DENIED if `configuredPath`
|
|
11
|
+
* is absolute, or escapes `workspaceRoot` after lexical normalization. Used at
|
|
12
|
+
* the boundary where the LLM-controllable config first becomes a real fs path
|
|
13
|
+
* — without this, `.squad.yaml` with `learnings.path: ../../etc/whatever` gives
|
|
14
|
+
* an arbitrary-write primitive (CWE-22).
|
|
15
|
+
*
|
|
16
|
+
* Does NOT resolve symlinks. The config itself lives inside the workspace, so
|
|
17
|
+
* TOCTOU symlink swap is not in this gateway's threat model — the writer side
|
|
18
|
+
* (resolveSafePath) handles that for the data write path.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ensureRelativeInsideRoot(workspaceRoot: string, configuredPath: string, settingName: string): void;
|
|
6
21
|
/**
|
|
7
22
|
* Resolve a user-supplied file path safely against a workspace root.
|
|
8
23
|
*
|
package/dist/util/path-safety.js
CHANGED
|
@@ -1,11 +1,39 @@
|
|
|
1
|
-
import { promises as fs } from
|
|
2
|
-
import path from
|
|
3
|
-
import { SquadError } from
|
|
4
|
-
import { rejectIfMalformed, realpathOrSelf } from
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { SquadError } from "../errors.js";
|
|
4
|
+
import { rejectIfMalformed, realpathOrSelf } from "./path-internal.js";
|
|
5
5
|
export const MAX_BYTES = 16_384;
|
|
6
6
|
export function createSafePathContext() {
|
|
7
7
|
return { rootRealCache: new Map() };
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Lexical-only containment check for a config-supplied relative path
|
|
11
|
+
* (e.g. `.squad.yaml` `learnings.path` / `tasks.path`).
|
|
12
|
+
*
|
|
13
|
+
* Sync; no filesystem access. Throws PATH_TRAVERSAL_DENIED if `configuredPath`
|
|
14
|
+
* is absolute, or escapes `workspaceRoot` after lexical normalization. Used at
|
|
15
|
+
* the boundary where the LLM-controllable config first becomes a real fs path
|
|
16
|
+
* — without this, `.squad.yaml` with `learnings.path: ../../etc/whatever` gives
|
|
17
|
+
* an arbitrary-write primitive (CWE-22).
|
|
18
|
+
*
|
|
19
|
+
* Does NOT resolve symlinks. The config itself lives inside the workspace, so
|
|
20
|
+
* TOCTOU symlink swap is not in this gateway's threat model — the writer side
|
|
21
|
+
* (resolveSafePath) handles that for the data write path.
|
|
22
|
+
*/
|
|
23
|
+
export function ensureRelativeInsideRoot(workspaceRoot, configuredPath, settingName) {
|
|
24
|
+
if (path.isAbsolute(configuredPath)) {
|
|
25
|
+
throw new SquadError("PATH_TRAVERSAL_DENIED", `${settingName} must be a workspace-relative path, not absolute`, { setting: settingName, configuredPath });
|
|
26
|
+
}
|
|
27
|
+
const rootAbs = path.resolve(workspaceRoot);
|
|
28
|
+
const candidateAbs = path.resolve(rootAbs, configuredPath);
|
|
29
|
+
const rel = path.relative(rootAbs, candidateAbs);
|
|
30
|
+
if (path.isAbsolute(rel) || rel === ".." || rel.startsWith(".." + path.sep)) {
|
|
31
|
+
throw new SquadError("PATH_TRAVERSAL_DENIED", `${settingName} escapes workspace_root`, {
|
|
32
|
+
setting: settingName,
|
|
33
|
+
configuredPath,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
9
37
|
/**
|
|
10
38
|
* Resolve a user-supplied file path safely against a workspace root.
|
|
11
39
|
*
|
|
@@ -20,13 +48,15 @@ export function createSafePathContext() {
|
|
|
20
48
|
export async function resolveSafePath(workspaceRoot, file, ctx) {
|
|
21
49
|
rejectIfMalformed(file);
|
|
22
50
|
if (workspaceRoot === undefined) {
|
|
23
|
-
if (path.isAbsolute(file) || file.includes(
|
|
24
|
-
throw new SquadError(
|
|
51
|
+
if (path.isAbsolute(file) || file.includes("..")) {
|
|
52
|
+
throw new SquadError("PATH_REQUIRES_WORKSPACE", "absolute or traversal-bearing path requires workspace_root", { file });
|
|
25
53
|
}
|
|
26
54
|
return file;
|
|
27
55
|
}
|
|
28
56
|
if (!path.isAbsolute(workspaceRoot)) {
|
|
29
|
-
throw new SquadError(
|
|
57
|
+
throw new SquadError("PATH_INVALID", "workspace_root must be absolute", {
|
|
58
|
+
workspaceRoot,
|
|
59
|
+
});
|
|
30
60
|
}
|
|
31
61
|
const rootNormalized = path.normalize(workspaceRoot);
|
|
32
62
|
let rootReal = ctx.rootRealCache.get(rootNormalized);
|
|
@@ -37,8 +67,12 @@ export async function resolveSafePath(workspaceRoot, file, ctx) {
|
|
|
37
67
|
const fileNormalized = path.normalize(file);
|
|
38
68
|
const candidateAbs = path.resolve(rootReal, fileNormalized);
|
|
39
69
|
const lexicalRel = path.relative(rootReal, candidateAbs);
|
|
40
|
-
if (path.isAbsolute(lexicalRel) ||
|
|
41
|
-
|
|
70
|
+
if (path.isAbsolute(lexicalRel) ||
|
|
71
|
+
lexicalRel === ".." ||
|
|
72
|
+
lexicalRel.startsWith(".." + path.sep)) {
|
|
73
|
+
throw new SquadError("PATH_TRAVERSAL_DENIED", "path escapes workspace_root (lexical)", {
|
|
74
|
+
file,
|
|
75
|
+
});
|
|
42
76
|
}
|
|
43
77
|
let candidateExists = false;
|
|
44
78
|
try {
|
|
@@ -51,8 +85,8 @@ export async function resolveSafePath(workspaceRoot, file, ctx) {
|
|
|
51
85
|
if (candidateExists) {
|
|
52
86
|
const candidateReal = await realpathOrSelf(candidateAbs);
|
|
53
87
|
const realRel = path.relative(rootReal, candidateReal);
|
|
54
|
-
if (path.isAbsolute(realRel) || realRel ===
|
|
55
|
-
throw new SquadError(
|
|
88
|
+
if (path.isAbsolute(realRel) || realRel === ".." || realRel.startsWith(".." + path.sep)) {
|
|
89
|
+
throw new SquadError("PATH_TRAVERSAL_DENIED", "path escapes workspace_root (after realpath)", { file });
|
|
56
90
|
}
|
|
57
91
|
return candidateReal;
|
|
58
92
|
}
|
|
@@ -67,7 +101,7 @@ export async function resolveSafePath(workspaceRoot, file, ctx) {
|
|
|
67
101
|
export async function readSnippet(absPath) {
|
|
68
102
|
let fh;
|
|
69
103
|
try {
|
|
70
|
-
fh = await fs.open(absPath,
|
|
104
|
+
fh = await fs.open(absPath, "r");
|
|
71
105
|
}
|
|
72
106
|
catch {
|
|
73
107
|
return null;
|
|
@@ -76,7 +110,7 @@ export async function readSnippet(absPath) {
|
|
|
76
110
|
const buf = Buffer.alloc(MAX_BYTES);
|
|
77
111
|
const { bytesRead } = await fh.read(buf, 0, MAX_BYTES, 0);
|
|
78
112
|
return {
|
|
79
|
-
content: buf.slice(0, bytesRead).toString(
|
|
113
|
+
content: buf.slice(0, bytesRead).toString("utf8"),
|
|
80
114
|
truncated: bytesRead === MAX_BYTES,
|
|
81
115
|
};
|
|
82
116
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-safety.js","sourceRoot":"","sources":["../../src/util/path-safety.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEvE,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC;AAMhC,MAAM,UAAU,qBAAqB;IACnC,OAAO,EAAE,aAAa,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;AACtC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,aAAiC,EACjC,IAAY,EACZ,GAAoB;IAEpB,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAExB,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,UAAU,CAClB,yBAAyB,EACzB,4DAA4D,EAC5D,EAAE,IAAI,EAAE,CACT,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,UAAU,CAAC,cAAc,EAAE,iCAAiC,EAAE,
|
|
1
|
+
{"version":3,"file":"path-safety.js","sourceRoot":"","sources":["../../src/util/path-safety.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gempack/squad-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "MCP server for the squad-dev workflow: classification, risk scoring, agent selection, advisory orchestration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,7 +37,12 @@
|
|
|
37
37
|
"dev": "tsx src/index.ts",
|
|
38
38
|
"test": "vitest run",
|
|
39
39
|
"test:watch": "vitest",
|
|
40
|
-
"
|
|
40
|
+
"test:coverage": "vitest run --coverage",
|
|
41
|
+
"smoke": "node tests/smoke.mjs",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"lint": "tsc --noEmit && eslint .",
|
|
44
|
+
"format": "prettier --write .",
|
|
45
|
+
"format:check": "prettier --check .",
|
|
41
46
|
"prepublishOnly": "npm run build"
|
|
42
47
|
},
|
|
43
48
|
"files": [
|
|
@@ -75,8 +80,14 @@
|
|
|
75
80
|
"devDependencies": {
|
|
76
81
|
"@types/js-yaml": "^4.0.9",
|
|
77
82
|
"@types/node": "^22.0.0",
|
|
83
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
84
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
85
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
86
|
+
"eslint": "^9.0.0",
|
|
87
|
+
"prettier": "^3.3.0",
|
|
78
88
|
"tsx": "^4.19.0",
|
|
79
89
|
"typescript": "^5.6.0",
|
|
90
|
+
"typescript-eslint": "^8.0.0",
|
|
80
91
|
"vitest": "^2.1.0"
|
|
81
92
|
}
|
|
82
93
|
}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
# Skill: Squad Dev
|
|
2
2
|
|
|
3
3
|
## Objective
|
|
4
|
+
|
|
4
5
|
Development skill that takes a user prompt, builds an implementation plan, runs gated advisory with specialized agents, implements, and consolidates via TechLead-Consolidator. Codex is optional (`/squad --codex`) and may be auto-suggested when the plan is high-risk.
|
|
5
6
|
|
|
6
7
|
## Skill Name
|
|
8
|
+
|
|
7
9
|
`/squad`
|
|
8
10
|
|
|
9
11
|
## Agent Registry
|
|
10
12
|
|
|
11
|
-
| subagent_type
|
|
12
|
-
|
|
|
13
|
-
| `po`
|
|
14
|
-
| `tech-lead-planner`
|
|
15
|
-
| `tech-lead-consolidator`
|
|
16
|
-
| `senior-architect`
|
|
17
|
-
| `senior-dba`
|
|
18
|
-
| `senior-developer`
|
|
19
|
-
| `senior-dev-reviewer`
|
|
20
|
-
| `senior-dev-security`
|
|
21
|
-
| `senior-qa`
|
|
13
|
+
| subagent_type | Purpose |
|
|
14
|
+
| ------------------------ | -------------------------------------------- |
|
|
15
|
+
| `po` | Business value, UX, requirements fit |
|
|
16
|
+
| `tech-lead-planner` | Pre-implementation trade-offs and viability |
|
|
17
|
+
| `tech-lead-consolidator` | Post-implementation final verdict |
|
|
18
|
+
| `senior-architect` | Boundaries, DI, scalability |
|
|
19
|
+
| `senior-dba` | Queries, migrations, EF, cache |
|
|
20
|
+
| `senior-developer` | Correctness, robustness, APIs, observability |
|
|
21
|
+
| `senior-dev-reviewer` | Readability, idioms, naming |
|
|
22
|
+
| `senior-dev-security` | OWASP, authz, sensitive data |
|
|
23
|
+
| `senior-qa` | Test coverage, strategy, reliability |
|
|
22
24
|
|
|
23
25
|
## General Flow
|
|
24
26
|
|
|
@@ -86,11 +88,13 @@ Summary + modified files + tests + validations + rollback plan + next steps.
|
|
|
86
88
|
## Phase Details
|
|
87
89
|
|
|
88
90
|
### Phase 0 — Pre-Check
|
|
91
|
+
|
|
89
92
|
1. Run `git status`; capture current branch.
|
|
90
93
|
2. If uncommitted, unrelated changes are present, warn and ask the user before proceeding.
|
|
91
94
|
3. Record starting SHA for the delivery report.
|
|
92
95
|
|
|
93
96
|
### Phase 1 — Understanding and Risk
|
|
97
|
+
|
|
94
98
|
- Read `$ARGUMENTS`; detect `--codex`.
|
|
95
99
|
- Classify type: Feature / Bug Fix / Refactor / Performance / Security / Business Rule.
|
|
96
100
|
- Explore the codebase (Glob, Grep, Read) to locate the affected area and patterns.
|
|
@@ -188,6 +192,7 @@ Be direct. If the plan is good, say so. Do not invent problems.
|
|
|
188
192
|
Absorb relevant suggestions and show an adjusted-plan diff summary.
|
|
189
193
|
|
|
190
194
|
### Phase 4 — Gate 1: User Approval
|
|
195
|
+
|
|
191
196
|
Present the final plan and wait for explicit approval. Do not proceed without it.
|
|
192
197
|
|
|
193
198
|
### Phase 5 — Advisory Squad
|
|
@@ -204,6 +209,7 @@ Present the final plan and wait for explicit approval. Do not proceed without it
|
|
|
204
209
|
| Business Rule | po, senior-developer, senior-qa | +senior-dba if data-bound |
|
|
205
210
|
|
|
206
211
|
**Hard conditionals based on touched files:**
|
|
212
|
+
|
|
207
213
|
- Query / Migration / EF / Cache → `senior-dba` required
|
|
208
214
|
- DI / Boundaries / new project or module → `senior-architect` required
|
|
209
215
|
- Endpoint / Auth / Middleware → `senior-dev-security` required
|
|
@@ -237,13 +243,16 @@ Stay inside your ownership. Forward anything outside your scope.
|
|
|
237
243
|
Send all selected agents in a single message with multiple tool calls so they run in parallel.
|
|
238
244
|
|
|
239
245
|
### Phase 6 — Gate 2: Blocker Halt
|
|
246
|
+
|
|
240
247
|
- Any advisory Blocker → HALT. Surface blockers and ask the user how to proceed (revise plan, accept risk, abort).
|
|
241
248
|
- Major/Minor → proceed, capturing them as acceptance criteria for implementation.
|
|
242
249
|
|
|
243
250
|
### Phase 7 — Escalation Round (optional)
|
|
251
|
+
|
|
244
252
|
If an advisory agent forwarded a Blocker/Major to an agent that was not selected, spawn that missing agent with only that forwarded item (not a full review).
|
|
245
253
|
|
|
246
254
|
### Phase 8 — Implementation
|
|
255
|
+
|
|
247
256
|
- Follow the plan and advisory acceptance criteria.
|
|
248
257
|
- Read → Edit/Write each file. Respect project patterns.
|
|
249
258
|
- Method names in English. No emojis.
|
|
@@ -285,6 +294,7 @@ Be direct. If it is good, say so. Do not request cosmetic refactors.
|
|
|
285
294
|
Spawn `tech-lead-consolidator` with every advisory report and the delivered delta. Consolidator produces the final verdict and rollback plan.
|
|
286
295
|
|
|
287
296
|
### Phase 11 — Gate 3: Reject Loop (max 2 iterations)
|
|
297
|
+
|
|
288
298
|
- APPROVED / CHANGES REQUIRED (non-blocker) → apply fixes, then deliver.
|
|
289
299
|
- REJECTED → apply the fix list, re-run affected agents on the delta, re-consolidate.
|
|
290
300
|
- After 2 iterations, stop and hand the situation to the user.
|
|
@@ -338,13 +348,13 @@ Spawn `tech-lead-consolidator` with every advisory report and the delivered delt
|
|
|
338
348
|
|
|
339
349
|
## Skill Parameters
|
|
340
350
|
|
|
341
|
-
| Parameter | Type | Default | Description
|
|
342
|
-
|
|
343
|
-
| --codex | flag | off | Enable Codex for plan validation and implementation review
|
|
351
|
+
| Parameter | Type | Default | Description |
|
|
352
|
+
| --------- | ------ | ------- | ---------------------------------------------------------------------------------- |
|
|
353
|
+
| --codex | flag | off | Enable Codex for plan validation and implementation review |
|
|
344
354
|
| --quick | flag | off | Quick mode (see below). Trades depth for speed. Mutually exclusive with `--codex`. |
|
|
345
|
-
| squad | string | auto | Specific squad or "auto" for detection
|
|
346
|
-
| plan-only | bool | false | Build the plan only, do not execute
|
|
347
|
-
| verbose | bool | false | Show individual agent reports inline
|
|
355
|
+
| squad | string | auto | Specific squad or "auto" for detection |
|
|
356
|
+
| plan-only | bool | false | Build the plan only, do not execute |
|
|
357
|
+
| verbose | bool | false | Show individual agent reports inline |
|
|
348
358
|
|
|
349
359
|
## Quick Mode (`--quick`)
|
|
350
360
|
|
|
@@ -352,16 +362,16 @@ Reduced agent set, terse prompts, condensed delivery. Suitable for small fixes,
|
|
|
352
362
|
|
|
353
363
|
Phase deltas vs. normal mode:
|
|
354
364
|
|
|
355
|
-
| Phase
|
|
356
|
-
|
|
357
|
-
| Plan
|
|
358
|
-
| Codex plan validation
|
|
359
|
-
| User approval gate
|
|
360
|
-
| Squad
|
|
361
|
-
| Agent prompts
|
|
362
|
-
| Codex review
|
|
363
|
-
| Tech-lead consolidator | Always
|
|
364
|
-
| Delivery
|
|
365
|
+
| Phase | Normal | Quick |
|
|
366
|
+
| ---------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
367
|
+
| Plan | Full plan with risks/decisions/tests | Condensed: objective, files, top 1-2 risks. Skip alternatives table. |
|
|
368
|
+
| Codex plan validation | Opt-in via `--codex` | Force-disabled. `--quick --codex` is rejected. |
|
|
369
|
+
| User approval gate | Always required | Auto-proceed when risk Low AND scope ≤3 files AND no security/data path. Otherwise still gates. |
|
|
370
|
+
| Squad | Auto-detect 3-7 specialists + tech-lead | Hard cap: 1 specialist + tech-lead. Specialist = work-type primary. |
|
|
371
|
+
| Agent prompts | Full template | "Flag only Blocker/Major. ≤200 words. No long template. If clean, reply 'No issues in scope.'" |
|
|
372
|
+
| Codex review | Opt-in | Skipped (force-disabled with `--quick`) |
|
|
373
|
+
| Tech-lead consolidator | Always | Skipped when zero Blocker/Major from specialist |
|
|
374
|
+
| Delivery | Full report | Condensed: objective, files, tests, residual risks |
|
|
365
375
|
|
|
366
376
|
Critical-change auto-fallback: if scope touches `auth`, `crypto`, `permissions`, `Program.cs`, `Startup.cs`, migrations, EF mappings, or `appsettings`, fall back to normal mode with warning `Quick mode disabled — change touches security/data layer. Running full workflow.`.
|
|
367
377
|
|
|
@@ -387,6 +397,7 @@ Critical-change auto-fallback: if scope touches `auth`, `crypto`, `permissions`,
|
|
|
387
397
|
```
|
|
388
398
|
|
|
389
399
|
## Inviolable Rules
|
|
400
|
+
|
|
390
401
|
1. Every implementation starts from a plan (approved explicitly, or auto-proceeded under `--quick` when risk Low).
|
|
391
402
|
2. Codex only runs with user consent (flag or confirmed auto-suggestion). Never combined with `--quick`.
|
|
392
403
|
3. TechLead-Consolidator always delivers the final verdict in normal mode. Under `--quick`, skipped only when the specialist reports zero Blocker/Major findings.
|