@gempack/squad-mcp 0.3.1
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 +20 -0
- package/.claude-plugin/plugin.json +20 -0
- package/CHANGELOG.md +282 -0
- package/LICENSE +201 -0
- package/NOTICE +11 -0
- package/README.md +164 -0
- package/agents/PO.md +84 -0
- package/agents/Senior-Architect.md +121 -0
- package/agents/Senior-DBA.md +137 -0
- package/agents/Senior-Dev-Reviewer.md +104 -0
- package/agents/Senior-Dev-Security.md +134 -0
- package/agents/Senior-Developer.md +180 -0
- package/agents/Senior-QA.md +146 -0
- package/agents/Skill-Squad-Dev.md +369 -0
- package/agents/Skill-Squad-Review.md +267 -0
- package/agents/TechLead-Consolidator.md +117 -0
- package/agents/TechLead-Planner.md +90 -0
- package/agents/_Severity-and-Ownership.md +68 -0
- package/commands/squad-review.md +68 -0
- package/commands/squad.md +81 -0
- package/dist/config/ownership-matrix.d.ts +48 -0
- package/dist/config/ownership-matrix.js +197 -0
- package/dist/config/ownership-matrix.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.js +14 -0
- package/dist/errors.js.map +1 -0
- package/dist/exec/git.d.ts +17 -0
- package/dist/exec/git.js +0 -0
- package/dist/exec/git.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/observability/logger.d.ts +23 -0
- package/dist/observability/logger.js +93 -0
- package/dist/observability/logger.js.map +1 -0
- package/dist/prompts/registry.d.ts +21 -0
- package/dist/prompts/registry.js +183 -0
- package/dist/prompts/registry.js.map +1 -0
- package/dist/resources/agent-loader.d.ts +20 -0
- package/dist/resources/agent-loader.js +122 -0
- package/dist/resources/agent-loader.js.map +1 -0
- package/dist/resources/registry.d.ts +13 -0
- package/dist/resources/registry.js +67 -0
- package/dist/resources/registry.js.map +1 -0
- package/dist/tools/agents.d.ts +22 -0
- package/dist/tools/agents.js +32 -0
- package/dist/tools/agents.js.map +1 -0
- package/dist/tools/classify-work-type.d.ts +28 -0
- package/dist/tools/classify-work-type.js +0 -0
- package/dist/tools/classify-work-type.js.map +1 -0
- package/dist/tools/compose-advisory-bundle.d.ts +75 -0
- package/dist/tools/compose-advisory-bundle.js +68 -0
- package/dist/tools/compose-advisory-bundle.js.map +1 -0
- package/dist/tools/compose-squad-workflow.d.ts +84 -0
- package/dist/tools/compose-squad-workflow.js +0 -0
- package/dist/tools/compose-squad-workflow.js.map +1 -0
- package/dist/tools/consolidate.d.ts +97 -0
- package/dist/tools/consolidate.js +75 -0
- package/dist/tools/consolidate.js.map +1 -0
- package/dist/tools/detect-changed-files.d.ts +35 -0
- package/dist/tools/detect-changed-files.js +0 -0
- package/dist/tools/detect-changed-files.js.map +1 -0
- package/dist/tools/registry.d.ts +26 -0
- package/dist/tools/registry.js +169 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/score-risk.d.ts +38 -0
- package/dist/tools/score-risk.js +34 -0
- package/dist/tools/score-risk.js.map +1 -0
- package/dist/tools/select-squad.d.ts +46 -0
- package/dist/tools/select-squad.js +0 -0
- package/dist/tools/select-squad.js.map +1 -0
- package/dist/tools/slice-files.d.ts +34 -0
- package/dist/tools/slice-files.js +0 -0
- package/dist/tools/slice-files.js.map +1 -0
- package/dist/tools/validate-plan-text.d.ts +24 -0
- package/dist/tools/validate-plan-text.js +0 -0
- package/dist/tools/validate-plan-text.js.map +1 -0
- package/dist/util/path-safety.d.ts +28 -0
- package/dist/util/path-safety.js +0 -0
- package/dist/util/path-safety.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { AGENTS } from '../config/ownership-matrix.js';
|
|
2
|
+
const orchestration = {
|
|
3
|
+
name: 'squad_orchestration',
|
|
4
|
+
description: 'Full squad-dev flow guide. Walks the host LLM through Phase 0–12 of the squad workflow.',
|
|
5
|
+
arguments: [
|
|
6
|
+
{ name: 'user_prompt', description: 'The user task description', required: true },
|
|
7
|
+
{ name: 'codex', description: 'Whether Codex review is enabled (true/false)', required: false },
|
|
8
|
+
],
|
|
9
|
+
build: (args) => {
|
|
10
|
+
const codex = args.codex === 'true';
|
|
11
|
+
const text = `You are orchestrating the squad-dev workflow.
|
|
12
|
+
|
|
13
|
+
User request:
|
|
14
|
+
${args.user_prompt}
|
|
15
|
+
|
|
16
|
+
Follow this flow:
|
|
17
|
+
|
|
18
|
+
Phase 0 — Pre-Check
|
|
19
|
+
- Run git status, capture branch.
|
|
20
|
+
- Warn if uncommitted unrelated changes.
|
|
21
|
+
|
|
22
|
+
Phase 1 — Understanding and Risk
|
|
23
|
+
- Classify type: Feature / Bug Fix / Refactor / Performance / Security / Business Rule.
|
|
24
|
+
- Detect signals (auth, money, migration, files_count, new_module, api_change).
|
|
25
|
+
- Call tool \`score_risk\` with the signals.
|
|
26
|
+
|
|
27
|
+
Phase 2 — Plan + Planner in Parallel
|
|
28
|
+
- Build the implementation plan.
|
|
29
|
+
- Call \`get_agent_definition\` for tech-lead-planner and run it in parallel with plan creation.
|
|
30
|
+
- Absorb planner adjustments before showing the plan.
|
|
31
|
+
|
|
32
|
+
Phase 3 — Codex Plan Review (optional)
|
|
33
|
+
- Codex enabled this run: ${codex}.
|
|
34
|
+
- If High risk and Codex disabled, ask user for confirmation.
|
|
35
|
+
|
|
36
|
+
Phase 4 — Gate 1: User Approval
|
|
37
|
+
- Wait for explicit approval before any implementation.
|
|
38
|
+
|
|
39
|
+
Phase 5 — Advisory Squad
|
|
40
|
+
- Call \`select_squad\` with work_type and changed files.
|
|
41
|
+
- For each selected agent, call \`slice_files_for_agent\` to get the relevant slice.
|
|
42
|
+
- Spawn each advisory agent in parallel using its definition + sliced context.
|
|
43
|
+
|
|
44
|
+
Phase 6 — Gate 2: Blocker Halt
|
|
45
|
+
- Any Blocker in any report → halt and ask user.
|
|
46
|
+
|
|
47
|
+
Phase 7 — Escalation Round
|
|
48
|
+
- If reports forward items to non-selected agents, spawn those agents only for forwarded items.
|
|
49
|
+
|
|
50
|
+
Phase 8 — Implementation
|
|
51
|
+
- Implement guided by advisory acceptance criteria.
|
|
52
|
+
- Method names in English. No emojis.
|
|
53
|
+
|
|
54
|
+
Phase 9 — Codex Implementation Review (optional, only if codex=${codex}).
|
|
55
|
+
|
|
56
|
+
Phase 10 — TechLead-Consolidator
|
|
57
|
+
- Call \`apply_consolidation_rules\` with all reports.
|
|
58
|
+
- Spawn tech-lead-consolidator with the consolidation output.
|
|
59
|
+
|
|
60
|
+
Phase 11 — Gate 3: Reject Loop (max 2 iterations)
|
|
61
|
+
|
|
62
|
+
Phase 12 — Delivery
|
|
63
|
+
- Summary + modified files + tests + validations + rollback plan + next steps.
|
|
64
|
+
|
|
65
|
+
Inviolable rules:
|
|
66
|
+
1. Implementation only after approved plan.
|
|
67
|
+
2. Codex requires explicit user consent.
|
|
68
|
+
3. TechLead-Consolidator delivers the final verdict.
|
|
69
|
+
4. Advisory agents assess; the orchestrator implements.
|
|
70
|
+
5. Method names in English. No emojis.
|
|
71
|
+
6. Never run commit or push.`;
|
|
72
|
+
return {
|
|
73
|
+
description: 'Squad-dev orchestration guide',
|
|
74
|
+
messages: [{ role: 'user', content: { type: 'text', text } }],
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const advisory = {
|
|
79
|
+
name: 'agent_advisory',
|
|
80
|
+
description: 'Sliced advisory prompt for one agent. Use after select_squad and slice_files_for_agent.',
|
|
81
|
+
arguments: [
|
|
82
|
+
{ name: 'agent', description: 'Agent name (po, senior-dba, etc.)', required: true },
|
|
83
|
+
{ name: 'plan', description: 'The approved implementation plan', required: true },
|
|
84
|
+
{ name: 'slice', description: 'Files and snippets relevant to the agent ownership', required: true },
|
|
85
|
+
],
|
|
86
|
+
build: (args) => {
|
|
87
|
+
const agentName = args.agent;
|
|
88
|
+
const def = AGENTS[agentName];
|
|
89
|
+
if (!def)
|
|
90
|
+
throw new Error(`Unknown agent: ${args.agent}`);
|
|
91
|
+
const text = `You are part of a squad-dev advisory round.
|
|
92
|
+
|
|
93
|
+
Role: ${def.role}
|
|
94
|
+
Ownership: ${def.owns.join(', ')}
|
|
95
|
+
|
|
96
|
+
Approved Plan:
|
|
97
|
+
${args.plan}
|
|
98
|
+
|
|
99
|
+
Your Slice:
|
|
100
|
+
${args.slice}
|
|
101
|
+
|
|
102
|
+
Tasks:
|
|
103
|
+
1. Does the plan make sense in your area of expertise?
|
|
104
|
+
2. What domain-specific risks do you see?
|
|
105
|
+
3. What must the implementation take care of?
|
|
106
|
+
4. Define acceptance criteria the implementation must meet in your domain.
|
|
107
|
+
|
|
108
|
+
Use severity labels: Blocker, Major, Minor, Suggestion.
|
|
109
|
+
Stay strictly inside your ownership. Forward anything outside scope using \`forwarded_to: <agent>\`.
|
|
110
|
+
|
|
111
|
+
Every report must end with:
|
|
112
|
+
### Assumptions and Limitations
|
|
113
|
+
- What was assumed due to missing context
|
|
114
|
+
- What could not be validated from the diff alone
|
|
115
|
+
- Information that would need confirmation`;
|
|
116
|
+
return {
|
|
117
|
+
description: `Advisory prompt for ${def.role}`,
|
|
118
|
+
messages: [{ role: 'user', content: { type: 'text', text } }],
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
const consolidator = {
|
|
123
|
+
name: 'consolidator',
|
|
124
|
+
description: 'Consolidator prompt. Use after collecting all advisory reports.',
|
|
125
|
+
arguments: [
|
|
126
|
+
{ name: 'reports', description: 'JSON array of advisory reports', required: true },
|
|
127
|
+
{ name: 'rules_output', description: 'Output of apply_consolidation_rules tool', required: true },
|
|
128
|
+
{ name: 'delta', description: 'Implemented diff summary', required: true },
|
|
129
|
+
],
|
|
130
|
+
build: (args) => {
|
|
131
|
+
const text = `You are the TechLead-Consolidator.
|
|
132
|
+
|
|
133
|
+
Advisory reports:
|
|
134
|
+
${args.reports}
|
|
135
|
+
|
|
136
|
+
Deterministic rules output:
|
|
137
|
+
${args.rules_output}
|
|
138
|
+
|
|
139
|
+
Implementation delta:
|
|
140
|
+
${args.delta}
|
|
141
|
+
|
|
142
|
+
Produce the final verdict (APPROVED / CHANGES_REQUIRED / REJECTED) with:
|
|
143
|
+
1. Justification per blocker/major/minor.
|
|
144
|
+
2. Arbitration of any conflicting recommendations.
|
|
145
|
+
3. Risks the user should know about (residual).
|
|
146
|
+
4. Rollback plan: commands, flags, data steps.
|
|
147
|
+
5. Next steps (manual migration, env config, etc.) if any.
|
|
148
|
+
|
|
149
|
+
Inviolable rules:
|
|
150
|
+
- Any Blocker → REJECTED.
|
|
151
|
+
- Major without justification → REJECTED.
|
|
152
|
+
- Conflicting advice → arbitrate and justify.
|
|
153
|
+
- Agent that did not report → record as "Not evaluated" and assess gap risk.`;
|
|
154
|
+
return {
|
|
155
|
+
description: 'Final verdict consolidation prompt',
|
|
156
|
+
messages: [{ role: 'user', content: { type: 'text', text } }],
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
const prompts = new Map([
|
|
161
|
+
[orchestration.name, orchestration],
|
|
162
|
+
[advisory.name, advisory],
|
|
163
|
+
[consolidator.name, consolidator],
|
|
164
|
+
]);
|
|
165
|
+
export function listPrompts() {
|
|
166
|
+
return Array.from(prompts.values()).map((p) => ({
|
|
167
|
+
name: p.name,
|
|
168
|
+
description: p.description,
|
|
169
|
+
arguments: p.arguments,
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
export function getPrompt(name, args) {
|
|
173
|
+
const p = prompts.get(name);
|
|
174
|
+
if (!p)
|
|
175
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
176
|
+
for (const arg of p.arguments) {
|
|
177
|
+
if (arg.required && !(arg.name in args)) {
|
|
178
|
+
throw new Error(`Missing required argument: ${arg.name}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return p.build(args);
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/prompts/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type AgentName } from '../config/ownership-matrix.js';
|
|
2
|
+
export declare const SHARED_FILES: string[];
|
|
3
|
+
export declare function getLocalDir(): string;
|
|
4
|
+
export declare function getEmbeddedDir(): string;
|
|
5
|
+
export declare function resolveAgentFile(name: AgentName): Promise<string>;
|
|
6
|
+
export declare function resolveSharedFile(file: string): Promise<string>;
|
|
7
|
+
export declare function readAgentDefinition(name: AgentName): Promise<string>;
|
|
8
|
+
export declare function listAvailableAgents(): Promise<{
|
|
9
|
+
name: AgentName;
|
|
10
|
+
role: string;
|
|
11
|
+
owns: string[];
|
|
12
|
+
conventions: string[];
|
|
13
|
+
file: string;
|
|
14
|
+
overridden: boolean;
|
|
15
|
+
}[]>;
|
|
16
|
+
export declare function initLocalConfig(force?: boolean): Promise<{
|
|
17
|
+
created: string[];
|
|
18
|
+
skipped: string[];
|
|
19
|
+
dir: string;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { AGENTS } from '../config/ownership-matrix.js';
|
|
6
|
+
import { SquadError } from '../errors.js';
|
|
7
|
+
import { logger } from '../observability/logger.js';
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const AGENT_FILE_MAP = {
|
|
10
|
+
po: 'PO.md',
|
|
11
|
+
'tech-lead-planner': 'TechLead-Planner.md',
|
|
12
|
+
'tech-lead-consolidator': 'TechLead-Consolidator.md',
|
|
13
|
+
'senior-architect': 'Senior-Architect.md',
|
|
14
|
+
'senior-dba': 'Senior-DBA.md',
|
|
15
|
+
'senior-developer': 'Senior-Developer.md',
|
|
16
|
+
'senior-dev-reviewer': 'Senior-Dev-Reviewer.md',
|
|
17
|
+
'senior-dev-security': 'Senior-Dev-Security.md',
|
|
18
|
+
'senior-qa': 'Senior-QA.md',
|
|
19
|
+
};
|
|
20
|
+
export const SHARED_FILES = ['_Severity-and-Ownership.md', 'Skill-Squad-Dev.md', 'Skill-Squad-Review.md'];
|
|
21
|
+
function defaultLocalDir() {
|
|
22
|
+
if (process.platform === 'win32') {
|
|
23
|
+
const appdata = process.env.APPDATA ?? path.join(os.homedir(), 'AppData', 'Roaming');
|
|
24
|
+
return path.join(appdata, 'squad-mcp', 'agents');
|
|
25
|
+
}
|
|
26
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), '.config');
|
|
27
|
+
return path.join(xdg, 'squad-mcp', 'agents');
|
|
28
|
+
}
|
|
29
|
+
export function getLocalDir() {
|
|
30
|
+
return process.env.SQUAD_AGENTS_DIR ?? defaultLocalDir();
|
|
31
|
+
}
|
|
32
|
+
export function getEmbeddedDir() {
|
|
33
|
+
return path.resolve(__dirname, '..', '..', 'agents');
|
|
34
|
+
}
|
|
35
|
+
async function exists(p) {
|
|
36
|
+
try {
|
|
37
|
+
await fs.access(p);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let embeddedAsserted = false;
|
|
45
|
+
let overrideWarnEmitted = false;
|
|
46
|
+
let overrideActiveAnnounced = false;
|
|
47
|
+
async function ensureEmbeddedDir() {
|
|
48
|
+
if (embeddedAsserted)
|
|
49
|
+
return;
|
|
50
|
+
const dir = getEmbeddedDir();
|
|
51
|
+
if (!(await exists(dir))) {
|
|
52
|
+
throw new SquadError('AGENT_DIR_MISSING', `embedded agents directory missing at ${dir}`);
|
|
53
|
+
}
|
|
54
|
+
embeddedAsserted = true;
|
|
55
|
+
}
|
|
56
|
+
async function announceOverrideOnce() {
|
|
57
|
+
if (overrideActiveAnnounced)
|
|
58
|
+
return;
|
|
59
|
+
const localDir = getLocalDir();
|
|
60
|
+
if (process.env.SQUAD_AGENTS_DIR && (await exists(localDir))) {
|
|
61
|
+
logger.info('agent override active', { details: { local_dir_present: true } });
|
|
62
|
+
}
|
|
63
|
+
else if (process.env.SQUAD_AGENTS_DIR && !overrideWarnEmitted) {
|
|
64
|
+
logger.warn('SQUAD_AGENTS_DIR set but directory not found; falling back to embedded defaults');
|
|
65
|
+
overrideWarnEmitted = true;
|
|
66
|
+
}
|
|
67
|
+
overrideActiveAnnounced = true;
|
|
68
|
+
}
|
|
69
|
+
export async function resolveAgentFile(name) {
|
|
70
|
+
await ensureEmbeddedDir();
|
|
71
|
+
await announceOverrideOnce();
|
|
72
|
+
const file = AGENT_FILE_MAP[name];
|
|
73
|
+
const local = path.join(getLocalDir(), file);
|
|
74
|
+
if (await exists(local))
|
|
75
|
+
return local;
|
|
76
|
+
return path.join(getEmbeddedDir(), file);
|
|
77
|
+
}
|
|
78
|
+
export async function resolveSharedFile(file) {
|
|
79
|
+
await ensureEmbeddedDir();
|
|
80
|
+
await announceOverrideOnce();
|
|
81
|
+
const local = path.join(getLocalDir(), file);
|
|
82
|
+
if (await exists(local))
|
|
83
|
+
return local;
|
|
84
|
+
return path.join(getEmbeddedDir(), file);
|
|
85
|
+
}
|
|
86
|
+
export async function readAgentDefinition(name) {
|
|
87
|
+
const filePath = await resolveAgentFile(name);
|
|
88
|
+
return fs.readFile(filePath, 'utf8');
|
|
89
|
+
}
|
|
90
|
+
export async function listAvailableAgents() {
|
|
91
|
+
const dir = getLocalDir();
|
|
92
|
+
const localExists = await exists(dir);
|
|
93
|
+
return Object.values(AGENTS).map((a) => ({
|
|
94
|
+
name: a.name,
|
|
95
|
+
role: a.role,
|
|
96
|
+
owns: a.owns,
|
|
97
|
+
conventions: a.conventions,
|
|
98
|
+
file: AGENT_FILE_MAP[a.name],
|
|
99
|
+
overridden: localExists,
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
export async function initLocalConfig(force = false) {
|
|
103
|
+
await ensureEmbeddedDir();
|
|
104
|
+
const dir = getLocalDir();
|
|
105
|
+
await fs.mkdir(dir, { recursive: true });
|
|
106
|
+
const created = [];
|
|
107
|
+
const skipped = [];
|
|
108
|
+
// SECURITY: file names come from hardcoded constants only; never accept user-supplied names here.
|
|
109
|
+
const sources = [...Object.values(AGENT_FILE_MAP), ...SHARED_FILES];
|
|
110
|
+
for (const file of sources) {
|
|
111
|
+
const dst = path.join(dir, file);
|
|
112
|
+
if ((await exists(dst)) && !force) {
|
|
113
|
+
skipped.push(file);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const src = path.join(getEmbeddedDir(), file);
|
|
117
|
+
await fs.copyFile(src, dst);
|
|
118
|
+
created.push(file);
|
|
119
|
+
}
|
|
120
|
+
return { created, skipped, dir };
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=agent-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-loader.js","sourceRoot":"","sources":["../../src/resources/agent-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function listResources(): Promise<{
|
|
2
|
+
uri: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
mimeType: string;
|
|
6
|
+
}[]>;
|
|
7
|
+
export declare function readResource(uri: string): Promise<{
|
|
8
|
+
contents: {
|
|
9
|
+
uri: string;
|
|
10
|
+
mimeType: string;
|
|
11
|
+
text: string;
|
|
12
|
+
}[];
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { AGENTS } from '../config/ownership-matrix.js';
|
|
3
|
+
import { resolveAgentFile, resolveSharedFile, SHARED_FILES } from './agent-loader.js';
|
|
4
|
+
import { SquadError } from '../errors.js';
|
|
5
|
+
import { logger, newRequestId } from '../observability/logger.js';
|
|
6
|
+
export async function listResources() {
|
|
7
|
+
const agentResources = Object.values(AGENTS).map((a) => ({
|
|
8
|
+
uri: `agent://${a.name}`,
|
|
9
|
+
name: a.name,
|
|
10
|
+
description: a.role,
|
|
11
|
+
mimeType: 'text/markdown',
|
|
12
|
+
}));
|
|
13
|
+
const sharedResources = SHARED_FILES.map((f) => ({
|
|
14
|
+
uri: `severity://${f.replace(/\.md$/, '').toLowerCase()}`,
|
|
15
|
+
name: f,
|
|
16
|
+
description: 'Shared squad reference document',
|
|
17
|
+
mimeType: 'text/markdown',
|
|
18
|
+
}));
|
|
19
|
+
return [...agentResources, ...sharedResources];
|
|
20
|
+
}
|
|
21
|
+
export async function readResource(uri) {
|
|
22
|
+
const requestId = newRequestId();
|
|
23
|
+
const started = Date.now();
|
|
24
|
+
try {
|
|
25
|
+
if (uri.startsWith('agent://')) {
|
|
26
|
+
const name = uri.replace('agent://', '');
|
|
27
|
+
if (!(name in AGENTS)) {
|
|
28
|
+
throw new SquadError('UNKNOWN_AGENT', `unknown agent: ${name}`, { uri });
|
|
29
|
+
}
|
|
30
|
+
const filePath = await resolveAgentFile(name);
|
|
31
|
+
const text = await fs.readFile(filePath, 'utf8');
|
|
32
|
+
logger.debug('resource ok', {
|
|
33
|
+
request_id: requestId,
|
|
34
|
+
outcome: 'success',
|
|
35
|
+
duration_ms: Date.now() - started,
|
|
36
|
+
details: { scheme: 'agent', name },
|
|
37
|
+
});
|
|
38
|
+
return { contents: [{ uri, mimeType: 'text/markdown', text }] };
|
|
39
|
+
}
|
|
40
|
+
if (uri.startsWith('severity://')) {
|
|
41
|
+
const slug = uri.replace('severity://', '');
|
|
42
|
+
const file = SHARED_FILES.find((f) => f.replace(/\.md$/, '').toLowerCase() === slug);
|
|
43
|
+
if (!file) {
|
|
44
|
+
throw new SquadError('UNKNOWN_AGENT', `unknown shared resource: ${uri}`, { uri });
|
|
45
|
+
}
|
|
46
|
+
const filePath = await resolveSharedFile(file);
|
|
47
|
+
const text = await fs.readFile(filePath, 'utf8');
|
|
48
|
+
logger.debug('resource ok', {
|
|
49
|
+
request_id: requestId,
|
|
50
|
+
outcome: 'success',
|
|
51
|
+
duration_ms: Date.now() - started,
|
|
52
|
+
details: { scheme: 'severity', slug },
|
|
53
|
+
});
|
|
54
|
+
return { contents: [{ uri, mimeType: 'text/markdown', text }] };
|
|
55
|
+
}
|
|
56
|
+
throw new SquadError('UNKNOWN_AGENT', `unsupported URI scheme: ${uri}`, { uri });
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
logger.warn('resource error', {
|
|
60
|
+
request_id: requestId,
|
|
61
|
+
duration_ms: Date.now() - started,
|
|
62
|
+
details: { uri, message: err instanceof Error ? err.message : String(err) },
|
|
63
|
+
});
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/resources/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolDef } from './registry.js';
|
|
3
|
+
import { type AgentName } from '../config/ownership-matrix.js';
|
|
4
|
+
declare const listSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
5
|
+
export declare const listAgentsTool: ToolDef<typeof listSchema>;
|
|
6
|
+
declare const getSchema: z.ZodObject<{
|
|
7
|
+
name: z.ZodEnum<[AgentName, ...AgentName[]]>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
name: AgentName;
|
|
10
|
+
}, {
|
|
11
|
+
name: AgentName;
|
|
12
|
+
}>;
|
|
13
|
+
export declare const getAgentDefinitionTool: ToolDef<typeof getSchema>;
|
|
14
|
+
declare const initSchema: z.ZodObject<{
|
|
15
|
+
force: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
force: boolean;
|
|
18
|
+
}, {
|
|
19
|
+
force?: boolean | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const initLocalConfigTool: ToolDef<typeof initSchema>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { AGENTS } from '../config/ownership-matrix.js';
|
|
3
|
+
import { listAvailableAgents, readAgentDefinition, initLocalConfig, getLocalDir } from '../resources/agent-loader.js';
|
|
4
|
+
const listSchema = z.object({});
|
|
5
|
+
export const listAgentsTool = {
|
|
6
|
+
name: 'list_agents',
|
|
7
|
+
description: 'List all configured agents with their roles, ownership, and naming conventions.',
|
|
8
|
+
schema: listSchema,
|
|
9
|
+
handler: async () => ({ agents: await listAvailableAgents(), local_dir: getLocalDir() }),
|
|
10
|
+
};
|
|
11
|
+
const getSchema = z.object({
|
|
12
|
+
name: z.enum(Object.keys(AGENTS)),
|
|
13
|
+
});
|
|
14
|
+
export const getAgentDefinitionTool = {
|
|
15
|
+
name: 'get_agent_definition',
|
|
16
|
+
description: 'Return the full markdown system prompt for a given agent. Resolves from local override → embedded default.',
|
|
17
|
+
schema: getSchema,
|
|
18
|
+
handler: async ({ name }) => ({
|
|
19
|
+
name,
|
|
20
|
+
definition: await readAgentDefinition(name),
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
const initSchema = z.object({
|
|
24
|
+
force: z.boolean().optional().default(false),
|
|
25
|
+
});
|
|
26
|
+
export const initLocalConfigTool = {
|
|
27
|
+
name: 'init_local_config',
|
|
28
|
+
description: 'Copy embedded agent defaults to the local override directory ($SQUAD_AGENTS_DIR or %APPDATA%/squad-mcp/agents). Files locally edited override the bundled versions.',
|
|
29
|
+
schema: initSchema,
|
|
30
|
+
handler: async ({ force }) => initLocalConfig(force),
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/tools/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,MAAM,EAAkB,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEtH,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAEhC,MAAM,CAAC,MAAM,cAAc,GAA+B;IACxD,IAAI,EAAE,aAAa;IACnB,WAAW,EAAE,iFAAiF;IAC9F,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,mBAAmB,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,CAAC;CACzF,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACzB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAgC,CAAC;CACjE,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAA8B;IAC/D,IAAI,EAAE,sBAAsB;IAC5B,WAAW,EAAE,4GAA4G;IACzH,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5B,IAAI;QACJ,UAAU,EAAE,MAAM,mBAAmB,CAAC,IAAI,CAAC;KAC5C,CAAC;CACH,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CAC7C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAA+B;IAC7D,IAAI,EAAE,mBAAmB;IACzB,WAAW,EACT,qKAAqK;IACvK,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC;CACrD,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolDef } from './registry.js';
|
|
3
|
+
import type { WorkType } from '../config/ownership-matrix.js';
|
|
4
|
+
declare const schema: z.ZodObject<{
|
|
5
|
+
user_prompt: z.ZodEffects<z.ZodString, string, string>;
|
|
6
|
+
files: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
files: string[];
|
|
9
|
+
user_prompt: string;
|
|
10
|
+
}, {
|
|
11
|
+
user_prompt: string;
|
|
12
|
+
files?: string[] | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
type Input = z.infer<typeof schema>;
|
|
15
|
+
export type Confidence = 'Low' | 'Medium' | 'High';
|
|
16
|
+
export interface ClassifyOutput {
|
|
17
|
+
work_type: WorkType;
|
|
18
|
+
confidence: Confidence;
|
|
19
|
+
signals_detected: {
|
|
20
|
+
source: 'prompt' | 'path';
|
|
21
|
+
signal: string;
|
|
22
|
+
weight: number;
|
|
23
|
+
}[];
|
|
24
|
+
scores: Record<WorkType, number>;
|
|
25
|
+
}
|
|
26
|
+
export declare function classifyWorkType(input: Input): ClassifyOutput;
|
|
27
|
+
export declare const classifyWorkTypeTool: ToolDef<typeof schema>;
|
|
28
|
+
export {};
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify-work-type.js","sourceRoot":"","sources":["../../src/tools/classify-work-type.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolDef } from './registry.js';
|
|
3
|
+
import { type ComposeWorkflowOutput } from './compose-squad-workflow.js';
|
|
4
|
+
import { type SliceOutput } from './slice-files.js';
|
|
5
|
+
import { type ValidatePlanOutput } from './validate-plan-text.js';
|
|
6
|
+
declare const schema: z.ZodObject<{
|
|
7
|
+
workspace_root: z.ZodEffects<z.ZodString, string, string>;
|
|
8
|
+
user_prompt: z.ZodEffects<z.ZodString, string, string>;
|
|
9
|
+
plan: z.ZodString;
|
|
10
|
+
base_ref: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
11
|
+
staged_only: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
12
|
+
read_content: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
13
|
+
force_work_type: z.ZodOptional<z.ZodEnum<["Feature", "Bug Fix", "Refactor", "Performance", "Security", "Business Rule"]>>;
|
|
14
|
+
force_agents: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEnum<[import("../config/ownership-matrix.js").AgentName, ...import("../config/ownership-matrix.js").AgentName[]]>, "many">>>;
|
|
15
|
+
risk_signals: z.ZodOptional<z.ZodObject<{
|
|
16
|
+
touches_auth: z.ZodOptional<z.ZodBoolean>;
|
|
17
|
+
touches_money: z.ZodOptional<z.ZodBoolean>;
|
|
18
|
+
touches_migration: z.ZodOptional<z.ZodBoolean>;
|
|
19
|
+
new_module: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
api_contract_change: z.ZodOptional<z.ZodBoolean>;
|
|
21
|
+
}, "strip", z.ZodTypeAny, {
|
|
22
|
+
touches_auth?: boolean | undefined;
|
|
23
|
+
touches_money?: boolean | undefined;
|
|
24
|
+
touches_migration?: boolean | undefined;
|
|
25
|
+
new_module?: boolean | undefined;
|
|
26
|
+
api_contract_change?: boolean | undefined;
|
|
27
|
+
}, {
|
|
28
|
+
touches_auth?: boolean | undefined;
|
|
29
|
+
touches_money?: boolean | undefined;
|
|
30
|
+
touches_migration?: boolean | undefined;
|
|
31
|
+
new_module?: boolean | undefined;
|
|
32
|
+
api_contract_change?: boolean | undefined;
|
|
33
|
+
}>>;
|
|
34
|
+
}, "strip", z.ZodTypeAny, {
|
|
35
|
+
read_content: boolean;
|
|
36
|
+
force_agents: import("../config/ownership-matrix.js").AgentName[];
|
|
37
|
+
workspace_root: string;
|
|
38
|
+
user_prompt: string;
|
|
39
|
+
staged_only: boolean;
|
|
40
|
+
plan: string;
|
|
41
|
+
base_ref?: string | undefined;
|
|
42
|
+
force_work_type?: "Feature" | "Bug Fix" | "Refactor" | "Performance" | "Security" | "Business Rule" | undefined;
|
|
43
|
+
risk_signals?: {
|
|
44
|
+
touches_auth?: boolean | undefined;
|
|
45
|
+
touches_money?: boolean | undefined;
|
|
46
|
+
touches_migration?: boolean | undefined;
|
|
47
|
+
new_module?: boolean | undefined;
|
|
48
|
+
api_contract_change?: boolean | undefined;
|
|
49
|
+
} | undefined;
|
|
50
|
+
}, {
|
|
51
|
+
workspace_root: string;
|
|
52
|
+
user_prompt: string;
|
|
53
|
+
plan: string;
|
|
54
|
+
read_content?: boolean | undefined;
|
|
55
|
+
force_agents?: import("../config/ownership-matrix.js").AgentName[] | undefined;
|
|
56
|
+
base_ref?: string | undefined;
|
|
57
|
+
staged_only?: boolean | undefined;
|
|
58
|
+
force_work_type?: "Feature" | "Bug Fix" | "Refactor" | "Performance" | "Security" | "Business Rule" | undefined;
|
|
59
|
+
risk_signals?: {
|
|
60
|
+
touches_auth?: boolean | undefined;
|
|
61
|
+
touches_money?: boolean | undefined;
|
|
62
|
+
touches_migration?: boolean | undefined;
|
|
63
|
+
new_module?: boolean | undefined;
|
|
64
|
+
api_contract_change?: boolean | undefined;
|
|
65
|
+
} | undefined;
|
|
66
|
+
}>;
|
|
67
|
+
type Input = z.infer<typeof schema>;
|
|
68
|
+
export interface AdvisoryBundleOutput {
|
|
69
|
+
workflow: ComposeWorkflowOutput;
|
|
70
|
+
slices_by_agent: Record<string, SliceOutput>;
|
|
71
|
+
plan_validation: ValidatePlanOutput;
|
|
72
|
+
}
|
|
73
|
+
export declare function composeAdvisoryBundle(input: Input): Promise<AdvisoryBundleOutput>;
|
|
74
|
+
export declare const composeAdvisoryBundleTool: ToolDef<typeof schema>;
|
|
75
|
+
export {};
|