@gempack/squad-mcp 0.3.1 → 0.5.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.
@@ -0,0 +1,191 @@
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
+ let allowlistCache = null;
7
+ /**
8
+ * Test-only: clears the memoized allowlist so env-var changes take effect.
9
+ * Production code should never call this.
10
+ */
11
+ export function __resetOverrideAllowlistCache() {
12
+ allowlistCache = null;
13
+ }
14
+ function isUnsafeOverrideEnabled() {
15
+ return process.env.SQUAD_AGENTS_ALLOW_UNSAFE === '1';
16
+ }
17
+ function isUncOrDeviceNamespace(absolute) {
18
+ if (process.platform !== 'win32')
19
+ return false;
20
+ if (absolute.startsWith('\\\\?\\'))
21
+ return true;
22
+ if (absolute.startsWith('\\\\.\\'))
23
+ return true;
24
+ if (absolute.startsWith('\\\\'))
25
+ return true;
26
+ return false;
27
+ }
28
+ async function buildAllowlist() {
29
+ if (allowlistCache !== null)
30
+ return allowlistCache;
31
+ const roots = [];
32
+ const seen = new Set();
33
+ const candidates = [
34
+ { source: 'home', raw: os.homedir() },
35
+ { source: 'cwd', raw: process.cwd() },
36
+ ];
37
+ if (process.platform === 'win32') {
38
+ candidates.push({ source: 'appdata', raw: process.env.APPDATA });
39
+ candidates.push({ source: 'localappdata', raw: process.env.LOCALAPPDATA });
40
+ }
41
+ else {
42
+ candidates.push({ source: 'xdg_config_home', raw: process.env.XDG_CONFIG_HOME });
43
+ }
44
+ for (const c of candidates) {
45
+ if (!c.raw)
46
+ continue;
47
+ if (!path.isAbsolute(c.raw))
48
+ continue;
49
+ try {
50
+ rejectIfMalformed(c.raw);
51
+ }
52
+ catch {
53
+ // hostile env var (e.g. APPDATA injected with NUL) — silently skip from allowlist
54
+ continue;
55
+ }
56
+ if (isUncOrDeviceNamespace(c.raw))
57
+ continue;
58
+ const lexical = path.normalize(c.raw);
59
+ const real = await realpathOrSelf(lexical);
60
+ if (seen.has(real))
61
+ continue;
62
+ seen.add(real);
63
+ roots.push({ source: c.source, lexical, real });
64
+ }
65
+ allowlistCache = roots;
66
+ return roots;
67
+ }
68
+ function isInsideRoot(candidate, root) {
69
+ if (candidate === root)
70
+ return true;
71
+ const rel = path.relative(root, candidate);
72
+ if (rel === '' || rel === '.')
73
+ return true;
74
+ if (path.isAbsolute(rel))
75
+ return false;
76
+ if (rel === '..')
77
+ return false;
78
+ if (rel.startsWith('..' + path.sep))
79
+ return false;
80
+ return true;
81
+ }
82
+ function findAllowlistMatch(candidateLexical, candidateReal, roots) {
83
+ // Lexical AND realpath both must be inside the same allowlist entry.
84
+ // (Either alone is a known bypass: a symlinked-out lexical-allowed path,
85
+ // or a realpath-allowed path that lexically points elsewhere.)
86
+ for (const r of roots) {
87
+ if (isInsideRoot(candidateLexical, r.lexical) && isInsideRoot(candidateReal, r.real)) {
88
+ return r;
89
+ }
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Validate an override directory.
95
+ *
96
+ * Returns the resolved (realpath) directory on success. Throws `OVERRIDE_REJECTED`
97
+ * for policy violations (UNC, malformed, not absolute, outside allowlist, symlink
98
+ * escape) UNLESS `SQUAD_AGENTS_ALLOW_UNSAFE=1` is set, in which case the violation
99
+ * is bypassed (still rejects malformed inputs hard — those are not policy choices).
100
+ */
101
+ export async function validateOverrideDir(rawDir) {
102
+ // Hard rejections — never bypassed by the escape hatch.
103
+ try {
104
+ rejectIfMalformed(rawDir);
105
+ }
106
+ catch (err) {
107
+ return { ok: false, reason: 'malformed', rejectedPath: rawDir };
108
+ }
109
+ if (!path.isAbsolute(rawDir)) {
110
+ return { ok: false, reason: 'not_absolute', rejectedPath: rawDir };
111
+ }
112
+ if (isUncOrDeviceNamespace(rawDir)) {
113
+ return { ok: false, reason: 'unc_or_device_namespace', rejectedPath: rawDir };
114
+ }
115
+ const lexical = path.normalize(rawDir);
116
+ const real = await realpathOrSelf(lexical);
117
+ const unsafe = isUnsafeOverrideEnabled();
118
+ const roots = await buildAllowlist();
119
+ const match = findAllowlistMatch(lexical, real, roots);
120
+ if (match) {
121
+ return { ok: true, resolvedPath: real, allowlistMatch: match.source, unsafeOverride: false };
122
+ }
123
+ // Distinguish lexical-only escape (likely symlink) from outright outside-allowlist.
124
+ // If lexical matches some root but realpath does not, the user did `ln -s /tmp/x ~/.squad`.
125
+ let lexicalMatchExists = false;
126
+ for (const r of roots) {
127
+ if (isInsideRoot(lexical, r.lexical)) {
128
+ lexicalMatchExists = true;
129
+ break;
130
+ }
131
+ }
132
+ const reason = lexicalMatchExists ? 'symlink_escape' : 'outside_allowlist';
133
+ if (unsafe) {
134
+ return { ok: true, resolvedPath: real, allowlistMatch: 'unsafe_override', unsafeOverride: true };
135
+ }
136
+ return { ok: false, reason, rejectedPath: rawDir };
137
+ }
138
+ /**
139
+ * Convert a `ValidationFail` into a structured `OVERRIDE_REJECTED` error.
140
+ * Caller decides whether to throw or downgrade to a warn-and-fallback.
141
+ */
142
+ export function rejectionToError(fail, allowlistSize) {
143
+ return new SquadError('OVERRIDE_REJECTED', `override directory rejected: ${fail.reason}`, {
144
+ reason: fail.reason,
145
+ path: fail.rejectedPath,
146
+ allowlist_size: allowlistSize,
147
+ });
148
+ }
149
+ export async function getAllowlistSize() {
150
+ const roots = await buildAllowlist();
151
+ return roots.length;
152
+ }
153
+ /**
154
+ * Validate that a candidate file path inside a previously-validated override
155
+ * directory does not escape (after symlink resolution).
156
+ *
157
+ * Returns the file's realpath on success, or `null` if the file does not exist
158
+ * or escapes the directory. Per-file escape is NOT a policy-level error — the
159
+ * caller falls back to embedded for that file only and continues.
160
+ */
161
+ export async function validateOverrideFile(validatedDirReal, fileName) {
162
+ try {
163
+ rejectIfMalformed(fileName);
164
+ }
165
+ catch {
166
+ return null;
167
+ }
168
+ // Lexical: file name must not contain traversal segments.
169
+ const normalized = path.normalize(fileName);
170
+ if (path.isAbsolute(normalized))
171
+ return null;
172
+ if (normalized === '..' || normalized.startsWith('..' + path.sep) || normalized.includes(path.sep + '..' + path.sep)) {
173
+ return null;
174
+ }
175
+ const candidate = path.resolve(validatedDirReal, normalized);
176
+ // Lexical containment.
177
+ if (!isInsideRoot(candidate, validatedDirReal))
178
+ return null;
179
+ // Existence check before realpath.
180
+ try {
181
+ await fs.access(candidate);
182
+ }
183
+ catch {
184
+ return null;
185
+ }
186
+ const real = await realpathOrSelf(candidate);
187
+ if (!isInsideRoot(real, validatedDirReal))
188
+ return null;
189
+ return real;
190
+ }
191
+ //# sourceMappingURL=override-allowlist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"override-allowlist.js","sourceRoot":"","sources":["../../src/util/override-allowlist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA8CvE,IAAI,cAAc,GAA2B,IAAI,CAAC;AAElD;;;GAGG;AACH,MAAM,UAAU,6BAA6B;IAC3C,cAAc,GAAG,IAAI,CAAC;AACxB,CAAC;AAED,SAAS,uBAAuB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG,CAAC;AACvD,CAAC;AAED,SAAS,sBAAsB,CAAC,QAAgB;IAC9C,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,IAAI,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,cAAc;IAC3B,IAAI,cAAc,KAAK,IAAI;QAAE,OAAO,cAAc,CAAC;IACnD,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,MAAM,UAAU,GAAmE;QACjF,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE;QACrC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE;KACtC,CAAC;IACF,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACjE,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;IAC7E,CAAC;SAAM,CAAC;QACN,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,CAAC,GAAG;YAAE,SAAS;QACrB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;YAAE,SAAS;QACtC,IAAI,CAAC;YACH,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,kFAAkF;YAClF,SAAS;QACX,CAAC;QACD,IAAI,sBAAsB,CAAC,CAAC,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAC7B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,cAAc,GAAG,KAAK,CAAC;IACvB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,SAAiB,EAAE,IAAY;IACnD,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC3C,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAClD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,kBAAkB,CACzB,gBAAwB,EACxB,aAAqB,EACrB,KAAsB;IAEtB,qEAAqE;IACrE,yEAAyE;IACzE,+DAA+D;IAC/D,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,YAAY,CAAC,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACrF,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAc;IACtD,wDAAwD;IACxD,IAAI,CAAC;QACH,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IAClE,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IACrE,CAAC;IAED,IAAI,sBAAsB,CAAC,MAAM,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IAChF,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;IAE3C,MAAM,MAAM,GAAG,uBAAuB,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,cAAc,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAEvD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;IAC/F,CAAC;IAED,oFAAoF;IACpF,4FAA4F;IAC5F,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,kBAAkB,GAAG,IAAI,CAAC;YAC1B,MAAM;QACR,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAA4B,kBAAkB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,mBAAmB,CAAC;IAEpG,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;IACnG,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAoB,EAAE,aAAqB;IAC1E,OAAO,IAAI,UAAU,CAAC,mBAAmB,EAAE,gCAAgC,IAAI,CAAC,MAAM,EAAE,EAAE;QACxF,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI,EAAE,IAAI,CAAC,YAAY;QACvB,cAAc,EAAE,aAAa;KAC9B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,MAAM,KAAK,GAAG,MAAM,cAAc,EAAE,CAAC;IACrC,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,gBAAwB,EAAE,QAAgB;IACnF,IAAI,CAAC;QACH,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,0DAA0D;IAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5C,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;IAE7D,uBAAuB;IACvB,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IAE5D,mCAAmC;IACnC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Internal helpers shared between path-safety.ts and override-allowlist.ts.
3
+ * Exported only for intra-`util/` reuse; do not import from outside src/util/.
4
+ */
5
+ export declare function rejectIfMalformed(file: string): void;
6
+ export declare function realpathOrSelf(p: string): Promise<string>;
@@ -0,0 +1,27 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { SquadError } from '../errors.js';
3
+ /**
4
+ * Internal helpers shared between path-safety.ts and override-allowlist.ts.
5
+ * Exported only for intra-`util/` reuse; do not import from outside src/util/.
6
+ */
7
+ export function rejectIfMalformed(file) {
8
+ if (file.includes('\0')) {
9
+ throw new SquadError('PATH_INVALID', 'file path contains NUL byte', { file });
10
+ }
11
+ if (file.startsWith('~')) {
12
+ throw new SquadError('PATH_INVALID', 'file path starts with ~ (tilde expansion not supported)', { file });
13
+ }
14
+ const adsIndex = file.indexOf(':', 2);
15
+ if (adsIndex !== -1) {
16
+ throw new SquadError('PATH_INVALID', 'file path contains ADS marker (:) after drive letter', { file });
17
+ }
18
+ }
19
+ export async function realpathOrSelf(p) {
20
+ try {
21
+ return await fs.realpath(p);
22
+ }
23
+ catch {
24
+ return p;
25
+ }
26
+ }
27
+ //# sourceMappingURL=path-internal.js.map
@@ -0,0 +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"}
Binary file
@@ -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":"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,EAAE,aAAa,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACrD,IAAI,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACrD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,QAAQ,GAAG,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC;QAChD,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IAE5D,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACzD,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACjG,MAAM,IAAI,UAAU,CAAC,uBAAuB,EAAE,uCAAuC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACnG,CAAC;IAED,IAAI,eAAe,GAAG,KAAK,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC9B,eAAe,GAAG,IAAI,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;IAC5D,CAAC;IAED,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,aAAa,GAAG,MAAM,cAAc,CAAC,YAAY,CAAC,CAAC;QACzD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QACvD,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACxF,MAAM,IAAI,UAAU,CAAC,uBAAuB,EAAE,8CAA8C,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1G,CAAC;QACD,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAOD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe;IAC/C,IAAI,EAAE,CAAC;IACP,IAAI,CAAC;QACH,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC1D,OAAO;YACL,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YACjD,SAAS,EAAE,SAAS,KAAK,SAAS;SACnC,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gempack/squad-mcp",
3
- "version": "0.3.1",
3
+ "version": "0.5.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",
@@ -47,6 +47,7 @@
47
47
  "skills",
48
48
  ".claude-plugin",
49
49
  "README.md",
50
+ "INSTALL.md",
50
51
  "LICENSE",
51
52
  "NOTICE",
52
53
  "CHANGELOG.md"
@@ -0,0 +1,284 @@
1
+ ---
2
+ name: brainstorm
3
+ description: Collaborative brainstorm and research skill. Takes a problem, decision, or implementation topic; runs deep web research in parallel; spawns specialist agents for multi-domain perspectives; synthesizes findings into an options matrix with pros/cons/risks/sources and a recommendation. Output is a decision aid, NOT code. Use this BEFORE /squad to decide what to build; use /squad after to implement. Trigger when the user types /brainstorm or asks to "brainstorm", "research approaches", "explore options", "help me think through", "what does the industry use", or "best practices for".
4
+ ---
5
+
6
+ # Skill: Brainstorm
7
+
8
+ ## Objective
9
+ Help the user think through a problem, decision, or implementation idea by running parallel web research (market patterns, best practices, pitfalls, examples) and gathering specialist agent perspectives, then synthesizing the findings into a structured options matrix with a recommendation. This skill is exploratory — it does not write code, run tests, or modify the repo.
10
+
11
+ Position in the workflow:
12
+ - **`/brainstorm`** → decide what to build (this skill)
13
+ - **`/squad`** → implement what was decided
14
+ - **`/squad-review`** → review what was implemented
15
+
16
+ ## Skill Name
17
+ `/brainstorm`
18
+
19
+ ## Inviolable Rules
20
+
21
+ 1. **No code implementation.** This skill produces a brainstorm report. It must not edit files, run scripts, run tests, or modify any persistent state.
22
+ 2. **No `git commit`, `git push`, or any state-mutating git command.** Read-only git is fine (`git log`, `git status`, `git diff` for context).
23
+ 3. **Cite sources.** Every market claim, best practice, statistic, or "industry uses X" assertion must link to the URL it came from. Unsourced claims are not allowed.
24
+ 4. **Multiple options.** Always present at least two alternatives with explicit pros/cons. Never single-answer. The user is brainstorming, not asking for a verdict.
25
+ 5. **Honest gaps.** When research is incomplete or a decision needs more input, surface it explicitly under "Open questions" — do not paper over.
26
+ 6. **No AI attribution in any artifact produced.** Consistent with the global commit-authorship rule: if the brainstorm output ever gets pasted into a commit, doc, or message, it must not carry `Co-Authored-By: Claude / Anthropic / AI / Generated with [...]` lines.
27
+
28
+ ## Inputs
29
+
30
+ The skill takes one required argument (the topic) and optional flags:
31
+
32
+ | Param | Default | Description |
33
+ |-------|---------|-------------|
34
+ | `<topic>` | required | Free-form text describing the problem, decision, or idea to brainstorm |
35
+ | `--depth <level>` | `medium` | `quick` (3 web queries, 1 agent), `medium` (6 queries, 2-3 agents), `deep` (10+ queries, 4 agents + tech-lead) |
36
+ | `--no-web` | off | Skip web research entirely. Agents-only mode. Use when offline or when the topic is purely internal-codebase. |
37
+ | `--focus <domain>` | auto | Force a domain bias: `frontend`, `backend`, `infra`, `data`, `security`, `business`, `mobile`. Auto-detection scans the topic text for keywords. |
38
+ | `--sources <N>` | 5 | Cap on web sources cited per section. Avoids dump of every result. |
39
+
40
+ ## Step 1: Topic Understanding
41
+
42
+ Read the user's prompt and extract:
43
+ - **Problem/decision**: what is being decided? Phrase it as a question.
44
+ - **Constraints**: tech stack, team size, scale, budget, timeline (if mentioned or inferable from `git log` / `package.json` / `README`).
45
+ - **Existing context**: scan the current repo for related code, prior decisions in `CHANGELOG.md` or ADRs (`docs/adr/`, `architecture/`).
46
+ - **Domain(s)**: classify into one or more of `frontend / backend / infra / data / security / business / mobile`.
47
+ - **What "done" looks like**: what would satisfy the user — a single recommendation, multiple paths to consider, a comparison table, a risk inventory?
48
+
49
+ If the topic is ambiguous, ask **one** clarifying question before proceeding. Do not ask a list of questions; pick the most load-bearing one.
50
+
51
+ ## Step 2: Research Plan
52
+
53
+ Build a research plan with:
54
+
55
+ ### Web queries (skip if `--no-web`)
56
+
57
+ Construct 3-10 targeted queries (count from `--depth`). Use the **current year** in queries that benefit from recency:
58
+
59
+ - `{topic} best practices {year}`
60
+ - `{topic} {dominant_stack} examples`
61
+ - `{topic} alternatives comparison`
62
+ - `{topic} common pitfalls`
63
+ - `{topic} performance` / `security` / `scalability` / `cost`
64
+ - `{topic} case study {year}` (for industry examples)
65
+ - `{topic} open source` (if implementation references would help)
66
+ - `{topic} vs {known_alternative}` (if a comparison is implicit)
67
+
68
+ Avoid:
69
+ - Generic queries like "{topic}" alone (returns marketing pages).
70
+ - Queries that the user can find better via their internal docs (e.g., proprietary product internals).
71
+
72
+ ### Agents
73
+
74
+ Pick agents based on detected domains. For `--depth quick`: pick the single most relevant. For `medium`: 2-3. For `deep`: 4 + tech-lead. Mapping:
75
+
76
+ | Domain | Primary agent |
77
+ |--------|---------------|
78
+ | frontend | senior-developer (UX/perf perspective) |
79
+ | backend | senior-developer + senior-architect |
80
+ | infra | senior-architect + senior-dev-security |
81
+ | data | senior-dba + senior-architect |
82
+ | security | senior-dev-security + senior-architect |
83
+ | business | product-owner |
84
+ | testing | senior-qa |
85
+ | code quality | senior-dev-reviewer |
86
+
87
+ `tech-lead` is included only at `--depth deep` (or whenever 3+ agents participate, to consolidate).
88
+
89
+ ## Step 3: Parallel Research and Agent Spawn
90
+
91
+ Run web queries and agent invocations **in parallel** in a single message:
92
+ - One `WebSearch` tool call per query.
93
+ - One `Agent` tool call per specialist.
94
+
95
+ Per-agent prompt template:
96
+
97
+ ```
98
+ You are participating in a brainstorm — pre-implementation thinking.
99
+
100
+ ## Topic
101
+ {topic restated}
102
+
103
+ ## What we know so far
104
+ {problem framing, constraints, existing context}
105
+
106
+ ## Your perspective
107
+ As {agent role}, contribute:
108
+ 1. The 1-3 approaches you would consider, with one-line pros/cons each.
109
+ 2. Domain-specific risks the user should weigh.
110
+ 3. Open questions that need answers before deciding.
111
+ 4. (Optional) One concrete example from your experience or prior projects.
112
+
113
+ Format: at most 400 words. Bullet points fine. Do NOT produce a full review template.
114
+ Do NOT recommend code changes — this is exploration, not implementation.
115
+ If you do not have enough context to contribute meaningfully, say so explicitly.
116
+ ```
117
+
118
+ ## Step 4: Findings Synthesis
119
+
120
+ Aggregate web findings and agent perspectives into:
121
+
122
+ ### Market research section
123
+ Group findings by category. **Cite every claim.** Example:
124
+
125
+ ```
126
+ ### What the industry does
127
+ - Stripe and Block use a "saga" pattern for cross-service refund flows — [Stripe Engineering blog](url), [Square's saga implementation](url).
128
+ - 7 of the top 10 fintech APIs (per State of API 2026) implement idempotency keys via request headers — [State of API 2026](url).
129
+
130
+ ### Best practices
131
+ - Always include a `request_id` in idempotency keys to disambiguate retries — [GitHub's idempotency guide](url).
132
+
133
+ ### Pitfalls / anti-patterns
134
+ - Don't use the database PK as an idempotency key — collisions across retries break replays — [Postgres weekly issue 543](url).
135
+ ```
136
+
137
+ ### Options matrix
138
+
139
+ Build a table of **3-5 alternatives**. Columns:
140
+
141
+ | # | Approach | How it works | Pros | Cons | Risk | Best when |
142
+ |---|----------|--------------|------|------|------|-----------|
143
+
144
+ Each row is one viable path. "Approach" is short (3-6 words). "How it works" is one sentence. Pros/cons are bullet-style condensed.
145
+
146
+ ### Agent perspectives
147
+
148
+ One collapsible section per agent that participated:
149
+
150
+ ```
151
+ <details>
152
+ <summary>senior-architect</summary>
153
+ {their perspective bullet-pointed}
154
+ </details>
155
+ ```
156
+
157
+ ## Step 5: Tech-Lead Recommendation
158
+
159
+ If `--depth deep` (or 3+ agents participated), spawn the `tech-lead` agent with:
160
+
161
+ ```
162
+ You are consolidating a brainstorm. Pick one option and justify.
163
+
164
+ ## Topic
165
+ {topic}
166
+
167
+ ## Options matrix
168
+ {the matrix from step 4}
169
+
170
+ ## Web findings summary
171
+ {condensed market research, with sources}
172
+
173
+ ## Specialist perspectives
174
+ {condensed bullets from each agent}
175
+
176
+ ## Your task
177
+ 1. Pick ONE option from the matrix as the recommendation.
178
+ 2. Explain in 3-5 sentences why this option, with the trade-offs you accepted.
179
+ 3. List the top 2-3 open questions that must be answered before implementation begins.
180
+ 4. Suggest the immediate next step (e.g., spike, prototype, more research, /squad implement).
181
+
182
+ Format: at most 400 words. No long template. No scorecard.
183
+ ```
184
+
185
+ For `quick` and `medium` depth, the synthesizing skill itself produces the recommendation directly (no separate tech-lead spawn).
186
+
187
+ ## Step 6: Delivery
188
+
189
+ Output in this format:
190
+
191
+ ```
192
+ # Brainstorm: {short topic}
193
+
194
+ ## Topic
195
+ {problem framing in 1-2 sentences}
196
+
197
+ ## Context I gathered
198
+ - {key fact 1 from repo / git / user prompt}
199
+ - {key fact 2}
200
+
201
+ ## Market research
202
+
203
+ ### What the industry does
204
+ - {finding} — [source title](url)
205
+ - {finding} — [source title](url)
206
+
207
+ ### Best practices
208
+ - {practice} — [source](url)
209
+
210
+ ### Pitfalls / anti-patterns
211
+ - {pitfall} — [source](url)
212
+
213
+ ## Options matrix
214
+
215
+ | # | Approach | How it works | Pros | Cons | Risk | Best when |
216
+ |---|----------|--------------|------|------|------|-----------|
217
+ | A | ... | ... | ... | ... | Low | small scale, low traffic |
218
+ | B | ... | ... | ... | ... | Med | growth phase |
219
+ | C | ... | ... | ... | ... | High | enterprise / regulated |
220
+
221
+ ## Agent perspectives
222
+
223
+ <details><summary>senior-architect</summary>{view}</details>
224
+ <details><summary>senior-developer</summary>{view}</details>
225
+
226
+ ## Recommendation
227
+ **Option {letter}** — {one-paragraph justification including the trade-offs accepted}.
228
+
229
+ ## Open questions
230
+ - {gap 1 — needs decision or more research}
231
+ - {gap 2}
232
+ - {gap 3}
233
+
234
+ ## Next steps
235
+ - `/squad implement {selected option}` to execute
236
+ - `/brainstorm --focus {domain} {sub-topic}` to deep-dive on a specific concern
237
+ - Spike / prototype: {1-2 line description if appropriate}
238
+ - Continue research on: {gap}
239
+
240
+ Sources used:
241
+ - [Title 1](url)
242
+ - [Title 2](url)
243
+ - ...
244
+ ```
245
+
246
+ If `--no-web` was passed, omit "Market research" section and replace with a one-line note: `Web research disabled — agents-only brainstorm.`
247
+
248
+ If the user passed `--depth quick`, output is condensed: skip "Agent perspectives" details, drop the matrix to 2-3 options, and replace the recommendation paragraph with one sentence.
249
+
250
+ ## Edge Cases
251
+
252
+ - **Topic is too vague** ("help me think about scaling") → ask one clarifying question first; do not run research blindly.
253
+ - **Topic is purely internal** (only repo-specific, no public reference) → suggest `--no-web` and note that web research is unlikely to add value.
254
+ - **Topic touches a regulated domain** (PCI, HIPAA, GDPR, SOX) → flag the regulatory angle in the Open questions section even if the user did not mention it. Do not produce legal/compliance advice — point at the right specialists/docs.
255
+ - **Web search returns thin results** → state honestly: "Web research surfaced limited material; the recommendation leans on agent perspectives and codebase context." Do not invent citations.
256
+ - **Agent reports "not enough context"** → record it and proceed; do not retry with more context just to force an opinion.
257
+ - **The user wants implementation, not brainstorm** → redirect: "This sounds like a `/squad` task. `/brainstorm` is for pre-implementation exploration."
258
+
259
+ ## Boundaries
260
+
261
+ - This skill never edits files.
262
+ - This skill never runs state-mutating git commands.
263
+ - This skill never claims authority for legal/regulatory/compliance verdicts — it points at sources and specialists.
264
+ - This skill never invents URLs or sources. If unsure, omit the citation and note the gap.
265
+ - This skill produces text only.
266
+
267
+ ## Considerations
268
+
269
+ ### Cost vs depth
270
+ - `quick`: ~3 web queries + 1 agent. Roughly 5-10K tokens. Useful for quick reality-checks.
271
+ - `medium` (default): ~6 queries + 2-3 agents. ~20-40K tokens. Useful for genuine option exploration.
272
+ - `deep`: ~10+ queries + 4 agents + tech-lead. ~60-100K tokens. Useful for high-stakes decisions where multiple stakeholders need to align.
273
+
274
+ ### When to use vs alternatives
275
+ - Use `/brainstorm` when: deciding *what* to build, comparing approaches, scanning industry, exploring a problem space.
276
+ - Use `/squad` when: you've decided and want to implement.
277
+ - Use `/squad-review` when: implementation is done and you want a multi-perspective review.
278
+ - Use `WebSearch` directly when: you need one specific answer, not a brainstorm framing.
279
+
280
+ ### Sources reliability
281
+ Prefer (in this order): official docs, recognized engineering blogs (e.g., Stripe, AWS, Cloudflare, Google Cloud, Microsoft, Netflix Tech Blog), academic / standards bodies, recognized newsletters (Pragmatic Engineer, Increment), GitHub READMEs of widely-adopted libraries, conference talks. Avoid: SEO listicles, vendor-marketing pieces masquerading as articles, AI-generated content farms.
282
+
283
+ ### Output format consistency
284
+ Always close with a "Next steps" block and a flat list of all sources used. The Next steps block is the bridge from brainstorm to action — never omit it.