@gempack/squad-mcp 0.6.5 → 0.8.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.
Files changed (132) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +11 -6
  3. package/CHANGELOG.md +62 -0
  4. package/INSTALL.md +37 -22
  5. package/README.md +54 -18
  6. package/agents/code-explorer.md +77 -0
  7. package/agents/product-owner.md +10 -1
  8. package/agents/senior-architect.md +12 -0
  9. package/agents/senior-dba.md +15 -1
  10. package/agents/senior-dev-reviewer.md +101 -30
  11. package/agents/senior-dev-security.md +13 -0
  12. package/agents/senior-developer.md +15 -0
  13. package/agents/senior-qa.md +14 -1
  14. package/agents/tech-lead-consolidator.md +10 -0
  15. package/agents/tech-lead-planner.md +17 -0
  16. package/commands/brainstorm.md +12 -2
  17. package/commands/implement.md +32 -0
  18. package/commands/next.md +24 -0
  19. package/commands/question.md +20 -0
  20. package/commands/review.md +30 -0
  21. package/commands/task.md +29 -0
  22. package/commands/tasks.md +21 -0
  23. package/dist/config/ownership-matrix.d.ts +1 -1
  24. package/dist/config/ownership-matrix.js +21 -20
  25. package/dist/config/ownership-matrix.js.map +1 -1
  26. package/dist/config/squad-yaml.d.ts +1 -1
  27. package/dist/config/squad-yaml.js +4 -8
  28. package/dist/config/squad-yaml.js.map +1 -1
  29. package/dist/errors.js.map +1 -1
  30. package/dist/exec/git.d.ts +1 -1
  31. package/dist/exec/git.js +0 -0
  32. package/dist/exec/git.js.map +1 -1
  33. package/dist/format/pr-review.js +1 -3
  34. package/dist/format/pr-review.js.map +1 -1
  35. package/dist/index.js +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/learning/format.js +1 -5
  38. package/dist/learning/format.js.map +1 -1
  39. package/dist/learning/store.d.ts +1 -1
  40. package/dist/learning/store.js +90 -17
  41. package/dist/learning/store.js.map +1 -1
  42. package/dist/observability/logger.d.ts +2 -2
  43. package/dist/observability/logger.js +20 -20
  44. package/dist/observability/logger.js.map +1 -1
  45. package/dist/prompts/registry.js.map +1 -1
  46. package/dist/resources/agent-loader.js +1 -0
  47. package/dist/resources/agent-loader.js.map +1 -1
  48. package/dist/resources/registry.js +28 -28
  49. package/dist/tasks/select.js.map +1 -1
  50. package/dist/tasks/store.d.ts +2 -2
  51. package/dist/tasks/store.js +50 -12
  52. package/dist/tasks/store.js.map +1 -1
  53. package/dist/tools/_shared/schemas.d.ts +21 -0
  54. package/dist/tools/_shared/schemas.js +25 -0
  55. package/dist/tools/_shared/schemas.js.map +1 -0
  56. package/dist/tools/agents.d.ts +3 -3
  57. package/dist/tools/agents.js +9 -9
  58. package/dist/tools/agents.js.map +1 -1
  59. package/dist/tools/classify-work-type.d.ts +5 -5
  60. package/dist/tools/classify-work-type.js +0 -0
  61. package/dist/tools/classify-work-type.js.map +1 -1
  62. package/dist/tools/compose-advisory-bundle.d.ts +8 -0
  63. package/dist/tools/compose-advisory-bundle.js +12 -14
  64. package/dist/tools/compose-advisory-bundle.js.map +1 -1
  65. package/dist/tools/compose-prd-parse.js.map +1 -1
  66. package/dist/tools/compose-squad-workflow.d.ts +30 -1
  67. package/dist/tools/compose-squad-workflow.js +0 -0
  68. package/dist/tools/compose-squad-workflow.js.map +1 -1
  69. package/dist/tools/consolidate.js +1 -3
  70. package/dist/tools/consolidate.js.map +1 -1
  71. package/dist/tools/detect-changed-files.d.ts +5 -6
  72. package/dist/tools/detect-changed-files.js +0 -0
  73. package/dist/tools/detect-changed-files.js.map +1 -1
  74. package/dist/tools/list-tasks.js +1 -8
  75. package/dist/tools/list-tasks.js.map +1 -1
  76. package/dist/tools/mode/exec-mode.d.ts +124 -0
  77. package/dist/tools/mode/exec-mode.js +153 -0
  78. package/dist/tools/mode/exec-mode.js.map +1 -0
  79. package/dist/tools/next-task.js +1 -8
  80. package/dist/tools/next-task.js.map +1 -1
  81. package/dist/tools/read-learnings.js +3 -5
  82. package/dist/tools/read-learnings.js.map +1 -1
  83. package/dist/tools/read-squad-config.js +1 -1
  84. package/dist/tools/read-squad-config.js.map +1 -1
  85. package/dist/tools/record-learning.d.ts +1 -1
  86. package/dist/tools/record-learning.js +1 -1
  87. package/dist/tools/record-tasks.js.map +1 -1
  88. package/dist/tools/registry.js +2 -4
  89. package/dist/tools/registry.js.map +1 -1
  90. package/dist/tools/score-risk.d.ts +3 -3
  91. package/dist/tools/score-risk.js +15 -15
  92. package/dist/tools/score-rubric.js.map +1 -1
  93. package/dist/tools/select-squad.d.ts +5 -5
  94. package/dist/tools/select-squad.js +0 -0
  95. package/dist/tools/select-squad.js.map +1 -1
  96. package/dist/tools/slice-files-for-task.js.map +1 -1
  97. package/dist/tools/slice-files.d.ts +2 -2
  98. package/dist/tools/slice-files.js +0 -0
  99. package/dist/tools/slice-files.js.map +1 -1
  100. package/dist/tools/update-task-status.js +1 -8
  101. package/dist/tools/update-task-status.js.map +1 -1
  102. package/dist/tools/validate-plan-text.d.ts +3 -3
  103. package/dist/tools/validate-plan-text.js +0 -0
  104. package/dist/tools/validate-plan-text.js.map +1 -1
  105. package/dist/util/file-lock.d.ts +10 -0
  106. package/dist/util/file-lock.js +102 -0
  107. package/dist/util/file-lock.js.map +1 -0
  108. package/dist/util/override-allowlist.d.ts +4 -4
  109. package/dist/util/override-allowlist.js +36 -27
  110. package/dist/util/override-allowlist.js.map +1 -1
  111. package/dist/util/path-internal.js +10 -8
  112. package/dist/util/path-internal.js.map +1 -1
  113. package/dist/util/path-safety.d.ts +15 -0
  114. package/dist/util/path-safety.js +47 -13
  115. package/dist/util/path-safety.js.map +1 -1
  116. package/package.json +13 -2
  117. package/shared/Skill-Squad-Dev.md +46 -35
  118. package/shared/Skill-Squad-Review.md +64 -41
  119. package/shared/_Severity-and-Ownership.md +6 -6
  120. package/skills/brainstorm/SKILL.md +50 -37
  121. package/skills/commit-suggest/SKILL.md +32 -14
  122. package/skills/question/SKILL.md +110 -0
  123. package/skills/squad/SKILL.md +70 -26
  124. package/tools/_tasks-io.mjs +25 -16
  125. package/tools/list-tasks.mjs +1 -4
  126. package/tools/next-task.mjs +4 -13
  127. package/tools/post-review.mjs +20 -30
  128. package/tools/record-learning.mjs +8 -11
  129. package/tools/record-tasks.mjs +2 -9
  130. package/tools/update-task-status.mjs +2 -9
  131. package/commands/squad-review.md +0 -20
  132. package/commands/squad.md +0 -22
@@ -0,0 +1,10 @@
1
+ export interface FileLockOptions {
2
+ /** Total wall-clock budget to acquire the lock (ms). */
3
+ timeoutMs?: number;
4
+ }
5
+ /**
6
+ * Acquire the lock for `targetPath` (operates on `${targetPath}.lock`),
7
+ * run `fn`, then release. The lock file is removed in a finally so a
8
+ * thrown body still cleans up.
9
+ */
10
+ export declare function withFileLock<T>(targetPath: string, fn: () => Promise<T>, opts?: FileLockOptions): Promise<T>;
@@ -0,0 +1,102 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ /**
4
+ * Cross-process advisory lock implemented via O_EXCL on a sibling `.lock`
5
+ * file. Used to serialise read-modify-write on `.squad/tasks.json` and
6
+ * appends to `.squad/learnings.jsonl` when multiple MCP server processes
7
+ * share a workspace (a common case — two Claude clients open in the same
8
+ * repo).
9
+ *
10
+ * Limitations:
11
+ * - Not a kernel-enforced lock. Cooperative — every writer must use this.
12
+ * - A crash mid-section can leave a stale lock. After STALE_AFTER_MS the
13
+ * lock is force-acquired (one unlink, then retry once). Mtime is the
14
+ * staleness signal; we do NOT trust pid inside the file because PIDs
15
+ * can be reused across containers.
16
+ * - Single-host. NFS rename + flock semantics are weaker; not in scope.
17
+ */
18
+ const DEFAULT_TIMEOUT_MS = 5_000;
19
+ const DEFAULT_RETRY_BASE_MS = 15;
20
+ const DEFAULT_RETRY_MAX_MS = 75;
21
+ const STALE_AFTER_MS = 30_000;
22
+ async function sleep(ms) {
23
+ await new Promise((r) => setTimeout(r, ms));
24
+ }
25
+ async function tryAcquire(lockPath) {
26
+ try {
27
+ const fh = await fs.open(lockPath, "wx");
28
+ // The body is for human inspection only; logic depends only on existence + mtime.
29
+ await fh.writeFile(`${process.pid}\t${Date.now()}\n`, { encoding: "utf8" });
30
+ await fh.close();
31
+ return true;
32
+ }
33
+ catch (err) {
34
+ const code = err.code;
35
+ if (code === "EEXIST")
36
+ return false;
37
+ throw err;
38
+ }
39
+ }
40
+ async function isStale(lockPath) {
41
+ try {
42
+ const stat = await fs.stat(lockPath);
43
+ return Date.now() - stat.mtimeMs > STALE_AFTER_MS;
44
+ }
45
+ catch {
46
+ // Lock vanished — treat as stale; the next tryAcquire will create it.
47
+ return true;
48
+ }
49
+ }
50
+ /**
51
+ * Acquire the lock for `targetPath` (operates on `${targetPath}.lock`),
52
+ * run `fn`, then release. The lock file is removed in a finally so a
53
+ * thrown body still cleans up.
54
+ */
55
+ export async function withFileLock(targetPath, fn, opts = {}) {
56
+ const lockPath = `${targetPath}.lock`;
57
+ // The lock lives alongside the target. The body may be creating the target
58
+ // for the first time so the parent directory may not exist yet — mkdir is
59
+ // cheap and idempotent.
60
+ await fs.mkdir(path.dirname(lockPath), { recursive: true });
61
+ const deadline = Date.now() + (opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
62
+ let attempt = 0;
63
+ let acquired = false;
64
+ while (!acquired) {
65
+ if (await tryAcquire(lockPath)) {
66
+ acquired = true;
67
+ break;
68
+ }
69
+ if (Date.now() > deadline) {
70
+ // Last chance: if the existing lock looks stale, force-acquire.
71
+ if (await isStale(lockPath)) {
72
+ try {
73
+ await fs.unlink(lockPath);
74
+ }
75
+ catch {
76
+ // Another holder may have just removed it themselves — retry once.
77
+ }
78
+ if (await tryAcquire(lockPath)) {
79
+ acquired = true;
80
+ break;
81
+ }
82
+ }
83
+ throw new Error(`file-lock: timed out acquiring ${lockPath} after ${opts.timeoutMs ?? DEFAULT_TIMEOUT_MS}ms`);
84
+ }
85
+ attempt += 1;
86
+ // Jittered backoff to avoid lockstep contention between processes.
87
+ const base = Math.min(DEFAULT_RETRY_BASE_MS * attempt, DEFAULT_RETRY_MAX_MS);
88
+ await sleep(base + Math.floor(Math.random() * 10));
89
+ }
90
+ try {
91
+ return await fn();
92
+ }
93
+ finally {
94
+ try {
95
+ await fs.unlink(lockPath);
96
+ }
97
+ catch {
98
+ // Best-effort. If a stale-recovery removed it concurrently, that's fine.
99
+ }
100
+ }
101
+ }
102
+ //# sourceMappingURL=file-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../../src/util/file-lock.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { SquadError } from '../errors.js';
1
+ import { SquadError } from "../errors.js";
2
2
  /**
3
3
  * Validates that a directory chosen as the agent-override location lives under
4
4
  * a known user-controlled prefix, and that per-file resolutions inside it stay
@@ -14,16 +14,16 @@ import { SquadError } from '../errors.js';
14
14
  * `realpath()` and `fs.readFile()`. Acceptable for a single-user dev tool — an
15
15
  * attacker with write access to a user-allowlisted root has already won.
16
16
  */
17
- export type OverrideRejectionReason = 'malformed' | 'not_absolute' | 'unc_or_device_namespace' | 'outside_allowlist' | 'symlink_escape';
17
+ export type OverrideRejectionReason = "malformed" | "not_absolute" | "unc_or_device_namespace" | "outside_allowlist" | "symlink_escape";
18
18
  export interface AllowlistRoot {
19
- source: 'home' | 'appdata' | 'localappdata' | 'xdg_config_home' | 'cwd';
19
+ source: "home" | "appdata" | "localappdata" | "xdg_config_home" | "cwd";
20
20
  lexical: string;
21
21
  real: string;
22
22
  }
23
23
  export interface ValidationOk {
24
24
  ok: true;
25
25
  resolvedPath: string;
26
- allowlistMatch: AllowlistRoot['source'] | 'unsafe_override';
26
+ allowlistMatch: AllowlistRoot["source"] | "unsafe_override";
27
27
  unsafeOverride: boolean;
28
28
  }
29
29
  export interface ValidationFail {
@@ -1,8 +1,8 @@
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';
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 === '1';
15
+ return process.env.SQUAD_AGENTS_ALLOW_UNSAFE === "1";
16
16
  }
17
17
  function isUncOrDeviceNamespace(absolute) {
18
- if (process.platform !== 'win32')
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: 'home', raw: os.homedir() },
35
- { source: 'cwd', raw: process.cwd() },
34
+ { source: "home", raw: os.homedir() },
35
+ { source: "cwd", raw: process.cwd() },
36
36
  ];
37
- if (process.platform === 'win32') {
38
- candidates.push({ source: 'appdata', raw: process.env.APPDATA });
39
- candidates.push({ source: 'localappdata', raw: process.env.LOCALAPPDATA });
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: 'xdg_config_home', raw: process.env.XDG_CONFIG_HOME });
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 === '' || 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('..' + path.sep))
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 (err) {
107
- return { ok: false, reason: 'malformed', rejectedPath: rawDir };
106
+ catch {
107
+ return { ok: false, reason: "malformed", rejectedPath: rawDir };
108
108
  }
109
109
  if (!path.isAbsolute(rawDir)) {
110
- return { ok: false, reason: 'not_absolute', rejectedPath: rawDir };
110
+ return { ok: false, reason: "not_absolute", rejectedPath: rawDir };
111
111
  }
112
112
  if (isUncOrDeviceNamespace(rawDir)) {
113
- return { ok: false, reason: 'unc_or_device_namespace', rejectedPath: rawDir };
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 ? 'symlink_escape' : 'outside_allowlist';
132
+ const reason = lexicalMatchExists
133
+ ? "symlink_escape"
134
+ : "outside_allowlist";
133
135
  if (unsafe) {
134
- return { ok: true, resolvedPath: real, allowlistMatch: 'unsafe_override', unsafeOverride: true };
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('OVERRIDE_REJECTED', `override directory rejected: ${fail.reason}`, {
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 === '..' || normalized.startsWith('..' + path.sep) || normalized.includes(path.sep + '..' + path.sep)) {
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 'node:fs';
2
- import { SquadError } from '../errors.js';
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('\0')) {
9
- throw new SquadError('PATH_INVALID', 'file path contains NUL byte', { file });
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('PATH_INVALID', 'file path starts with ~ (tilde expansion not supported)', { file });
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(':', 2);
14
+ const adsIndex = file.indexOf(":", 2);
15
15
  if (adsIndex !== -1) {
16
- throw new SquadError('PATH_INVALID', 'file path contains ADS marker (:) after drive letter', { file });
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,CAAC,cAAc,EAAE,yDAAyD,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5G,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,EAAE,IAAI,EAAE,CAAC,CAAC;IACzG,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"}
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
  *
@@ -1,11 +1,39 @@
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';
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('PATH_REQUIRES_WORKSPACE', 'absolute or traversal-bearing path requires workspace_root', { file });
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('PATH_INVALID', 'workspace_root must be absolute', { workspaceRoot });
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) || lexicalRel === '..' || lexicalRel.startsWith('..' + path.sep)) {
41
- throw new SquadError('PATH_TRAVERSAL_DENIED', 'path escapes workspace_root (lexical)', { file });
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 === '..' || realRel.startsWith('..' + path.sep)) {
55
- throw new SquadError('PATH_TRAVERSAL_DENIED', 'path escapes workspace_root (after realpath)', { file });
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, 'r');
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('utf8'),
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":"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"}
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.6.5",
3
+ "version": "0.8.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
- "lint": "tsc --noEmit",
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
  }