pi-squad 0.6.5 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -2
- package/src/index.ts +11 -137
- package/src/protocol.ts +1 -18
- package/src/scheduler.ts +0 -86
- package/src/skills/squad-architecture/SKILL.md +53 -0
- package/src/skills/squad-backend-dev/SKILL.md +45 -0
- package/src/skills/{collaboration → squad-collaboration}/SKILL.md +1 -1
- package/src/skills/squad-frontend-dev/SKILL.md +51 -0
- package/src/skills/squad-qa-testing/SKILL.md +61 -0
- package/src/skills/squad-security-audit/SKILL.md +43 -0
- package/src/skills/{verification → squad-verification}/SKILL.md +1 -1
- package/src/store.ts +0 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-squad",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Multi-agent collaboration extension for pi — task decomposition, dependency management, parallel execution, TUI panel",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"pi": {
|
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
"src/index.ts"
|
|
9
9
|
],
|
|
10
10
|
"skills": [
|
|
11
|
-
"src/skills/squad-supervisor"
|
|
11
|
+
"src/skills/squad-supervisor",
|
|
12
|
+
"src/skills/squad-backend-dev",
|
|
13
|
+
"src/skills/squad-frontend-dev",
|
|
14
|
+
"src/skills/squad-qa-testing",
|
|
15
|
+
"src/skills/squad-security-audit",
|
|
16
|
+
"src/skills/squad-architecture"
|
|
12
17
|
]
|
|
13
18
|
},
|
|
14
19
|
"files": [
|
package/src/index.ts
CHANGED
|
@@ -352,14 +352,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
352
352
|
switch (event.type) {
|
|
353
353
|
case "squad_completed": {
|
|
354
354
|
const tasks = store.loadAllTasks(squadId);
|
|
355
|
+
const summary = tasks
|
|
356
|
+
.filter((t) => t.status === "done")
|
|
357
|
+
.map((t) => `- ${t.id} (${t.agent}): ${t.output?.slice(0, 150) || "done"}`)
|
|
358
|
+
.join("\n");
|
|
355
359
|
const totalCost = tasks.reduce((sum, t) => sum + t.usage.cost, 0);
|
|
356
360
|
const s = schedulers.get(squadId); if (s) s.updateContext();
|
|
357
|
-
const overview = store.loadOverview(squadId);
|
|
358
361
|
pi.sendMessage({
|
|
359
362
|
customType: "squad-completed",
|
|
360
|
-
content: `[squad] Squad "${squadId}" completed all ${tasks.length} tasks.\n\n
|
|
361
|
-
(overview ? `## Squad Overview\n\n${overview}\n\n` : "") +
|
|
362
|
-
`Total cost: $${totalCost.toFixed(4)}`,
|
|
363
|
+
content: `[squad] Squad "${squadId}" completed all ${tasks.length} tasks.\n\nSummary:\n${summary}\n\nTotal cost: $${totalCost.toFixed(4)}`,
|
|
363
364
|
display: true,
|
|
364
365
|
});
|
|
365
366
|
schedulers.delete(squadId);
|
|
@@ -370,12 +371,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
370
371
|
const tasks = store.loadAllTasks(squadId);
|
|
371
372
|
const failed = tasks.filter((t) => t.status === "failed");
|
|
372
373
|
const done = tasks.filter((t) => t.status === "done");
|
|
373
|
-
const overview = store.loadOverview(squadId);
|
|
374
374
|
pi.sendMessage({
|
|
375
375
|
customType: "squad-failed",
|
|
376
|
-
content: `[squad] Squad "${squadId}" has stalled. ${done.length}/${tasks.length} done, ${failed.length} failed.\
|
|
377
|
-
`Failed: ${failed.map((t) => `${t.id}: ${t.error?.slice(0, 100)}`).join("; ")}` +
|
|
378
|
-
(overview ? `\n\n## Squad Overview\n\n${overview}` : ""),
|
|
376
|
+
content: `[squad] Squad "${squadId}" has stalled. ${done.length}/${tasks.length} done, ${failed.length} failed.\nFailed: ${failed.map((t) => `${t.id}: ${t.error?.slice(0, 100)}`).join("; ")}`,
|
|
379
377
|
display: true,
|
|
380
378
|
}, { triggerTurn: true });
|
|
381
379
|
forceWidgetUpdate();
|
|
@@ -1229,31 +1227,6 @@ async function startSquad(
|
|
|
1229
1227
|
|
|
1230
1228
|
store.saveSquad(squad);
|
|
1231
1229
|
|
|
1232
|
-
// Create initial OVERVIEW.md with squad goal, task plan, and design contract
|
|
1233
|
-
const planLines = [
|
|
1234
|
-
`# Squad: ${params.goal}`,
|
|
1235
|
-
``,
|
|
1236
|
-
`**Created:** ${squad.created}`,
|
|
1237
|
-
`**Tasks:** ${plan.tasks.length}`,
|
|
1238
|
-
``,
|
|
1239
|
-
`## Task Plan`,
|
|
1240
|
-
``,
|
|
1241
|
-
...plan.tasks.map((t) => {
|
|
1242
|
-
const deps = t.depends.length > 0 ? ` (after: ${t.depends.join(", ")})` : "";
|
|
1243
|
-
return `- **${t.id}** → ${t.agent}: ${t.title}${deps}`;
|
|
1244
|
-
}),
|
|
1245
|
-
``,
|
|
1246
|
-
];
|
|
1247
|
-
|
|
1248
|
-
// Extract design contract from task descriptions — API paths, schemas,
|
|
1249
|
-
// ports, file conventions — so parallel agents share a single source of truth.
|
|
1250
|
-
const contractLines = buildDesignContract(plan.tasks, params.goal);
|
|
1251
|
-
if (contractLines.length > 0) {
|
|
1252
|
-
planLines.push(...contractLines);
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
store.appendOverview(squadId, planLines.join("\n"));
|
|
1256
|
-
|
|
1257
1230
|
// Create task files
|
|
1258
1231
|
for (const taskDef of plan.tasks) {
|
|
1259
1232
|
const task: Task = {
|
|
@@ -1301,6 +1274,10 @@ async function startSquad(
|
|
|
1301
1274
|
switch (event.type) {
|
|
1302
1275
|
case "squad_completed": {
|
|
1303
1276
|
const tasks = store.loadAllTasks(squadId);
|
|
1277
|
+
const summary = tasks
|
|
1278
|
+
.filter((t) => t.status === "done")
|
|
1279
|
+
.map((t) => `- ${t.id} (${t.agent}): ${t.output?.slice(0, 150) || "done"}`)
|
|
1280
|
+
.join("\n");
|
|
1304
1281
|
const totalCost = tasks.reduce((sum, t) => sum + t.usage.cost, 0);
|
|
1305
1282
|
|
|
1306
1283
|
// Final context update before clearing scheduler
|
|
@@ -1309,17 +1286,10 @@ async function startSquad(
|
|
|
1309
1286
|
completedSched.updateContext();
|
|
1310
1287
|
}
|
|
1311
1288
|
|
|
1312
|
-
// Load the full overview document — contains the narrative of
|
|
1313
|
-
// each task's output, decisions, issues, and files modified.
|
|
1314
|
-
const overview = store.loadOverview(squadId);
|
|
1315
|
-
|
|
1316
|
-
// Send into LLM context (not display-only) so the main agent
|
|
1317
|
-
// knows exactly what happened. No triggerTurn — user decides
|
|
1318
|
-
// what to do next.
|
|
1319
1289
|
pi.sendMessage({
|
|
1320
1290
|
customType: "squad-completed",
|
|
1321
1291
|
content: `[squad] Squad "${squadId}" completed all ${tasks.length} tasks.\n\n` +
|
|
1322
|
-
|
|
1292
|
+
`Summary:\n${summary}\n\n` +
|
|
1323
1293
|
`Total cost: $${totalCost.toFixed(4)}`,
|
|
1324
1294
|
display: true,
|
|
1325
1295
|
});
|
|
@@ -1334,14 +1304,12 @@ async function startSquad(
|
|
|
1334
1304
|
const tasks = store.loadAllTasks(squadId);
|
|
1335
1305
|
const failed = tasks.filter((t) => t.status === "failed");
|
|
1336
1306
|
const done = tasks.filter((t) => t.status === "done");
|
|
1337
|
-
const overview = store.loadOverview(squadId);
|
|
1338
1307
|
|
|
1339
1308
|
pi.sendMessage({
|
|
1340
1309
|
customType: "squad-failed",
|
|
1341
1310
|
content: `[squad] Squad "${squadId}" has stalled. ` +
|
|
1342
1311
|
`${done.length}/${tasks.length} tasks done, ${failed.length} failed.\n` +
|
|
1343
1312
|
`Failed: ${failed.map((t) => `${t.id}: ${t.error?.slice(0, 100)}`).join("; ")}\n` +
|
|
1344
|
-
(overview ? `\n## Squad Overview\n\n${overview}\n\n` : "\n") +
|
|
1345
1313
|
`Use squad_status for details or squad_modify to adjust.`,
|
|
1346
1314
|
display: true,
|
|
1347
1315
|
}, { triggerTurn: true });
|
|
@@ -1403,98 +1371,4 @@ function getSquadSkillPaths(skillsDir: string): string[] {
|
|
|
1403
1371
|
.filter((dir) => fs.existsSync(path.join(dir, "SKILL.md")));
|
|
1404
1372
|
}
|
|
1405
1373
|
|
|
1406
|
-
/**
|
|
1407
|
-
* Extract a design contract from task descriptions.
|
|
1408
|
-
*
|
|
1409
|
-
* Scans all tasks for API paths, ports, file names, data schemas, and
|
|
1410
|
-
* shared conventions. Produces a "## Design Contract" section in the
|
|
1411
|
-
* OVERVIEW.md so that ALL agents (including parallel ones) share a
|
|
1412
|
-
* single source of truth before they start working.
|
|
1413
|
-
*/
|
|
1414
|
-
function buildDesignContract(
|
|
1415
|
-
tasks: Array<{ id: string; title: string; description: string; agent: string; depends: string[] }>,
|
|
1416
|
-
goal: string,
|
|
1417
|
-
): string[] {
|
|
1418
|
-
const lines: string[] = [];
|
|
1419
|
-
|
|
1420
|
-
// Collect API routes mentioned in any task description
|
|
1421
|
-
const apiRoutes: string[] = [];
|
|
1422
|
-
const ports: string[] = [];
|
|
1423
|
-
const files: string[] = [];
|
|
1424
|
-
const schemas: string[] = [];
|
|
1425
|
-
|
|
1426
|
-
const allText = tasks.map((t) => `${t.title}\n${t.description}`).join("\n") + "\n" + goal;
|
|
1427
|
-
|
|
1428
|
-
// Extract API paths like GET /foo, POST /bar/:id
|
|
1429
|
-
const routePattern = /\b(GET|POST|PUT|PATCH|DELETE|HEAD)\s+(\/[\w\/:]+)/gi;
|
|
1430
|
-
for (const match of allText.matchAll(routePattern)) {
|
|
1431
|
-
const route = `${match[1].toUpperCase()} ${match[2]}`;
|
|
1432
|
-
if (!apiRoutes.includes(route)) apiRoutes.push(route);
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1435
|
-
// Extract port numbers
|
|
1436
|
-
const portPattern = /\b(?:port|PORT|Port)\s*[=:]?\s*(\d{4,5})\b/gi;
|
|
1437
|
-
for (const match of allText.matchAll(portPattern)) {
|
|
1438
|
-
if (!ports.includes(match[1])) ports.push(match[1]);
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
// Extract key file names mentioned (e.g., server.js, index.html)
|
|
1442
|
-
const filePattern = /\b([\w-]+\.(js|ts|json|html|css|sh|mjs|cjs))\b/gi;
|
|
1443
|
-
for (const match of allText.matchAll(filePattern)) {
|
|
1444
|
-
const f = match[1];
|
|
1445
|
-
if (!files.includes(f) && !['package.json'].includes(f)) files.push(f);
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
// Extract data schemas like {field, field, field}
|
|
1449
|
-
const schemaPattern = /\{([a-zA-Z_][\w,\s\[\]?]+)\}/g;
|
|
1450
|
-
for (const match of allText.matchAll(schemaPattern)) {
|
|
1451
|
-
const fields = match[1].trim();
|
|
1452
|
-
if (fields.includes(',') && fields.length > 5 && fields.length < 200) {
|
|
1453
|
-
if (!schemas.includes(fields)) schemas.push(fields);
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
// Only emit a contract section if we found shared design elements
|
|
1458
|
-
if (apiRoutes.length === 0 && ports.length === 0 && schemas.length === 0) {
|
|
1459
|
-
return [];
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
lines.push(`## Design Contract`);
|
|
1463
|
-
lines.push(``);
|
|
1464
|
-
lines.push(`> **All agents MUST follow these specifications.** Do not invent`);
|
|
1465
|
-
lines.push(`> alternative paths, ports, or schemas. If you need to deviate,`);
|
|
1466
|
-
lines.push(`> document the reason in your output.`);
|
|
1467
|
-
lines.push(``);
|
|
1468
|
-
|
|
1469
|
-
if (ports.length > 0) {
|
|
1470
|
-
lines.push(`### Server`);
|
|
1471
|
-
lines.push(`- Port: **${ports.join(", ")}**`);
|
|
1472
|
-
lines.push(``);
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
if (apiRoutes.length > 0) {
|
|
1476
|
-
lines.push(`### API Endpoints`);
|
|
1477
|
-
for (const r of apiRoutes) {
|
|
1478
|
-
lines.push(`- \`${r}\``);
|
|
1479
|
-
}
|
|
1480
|
-
lines.push(``);
|
|
1481
|
-
}
|
|
1482
1374
|
|
|
1483
|
-
if (schemas.length > 0) {
|
|
1484
|
-
lines.push(`### Data Schemas`);
|
|
1485
|
-
for (const s of schemas) {
|
|
1486
|
-
lines.push(`- \`{ ${s} }\``);
|
|
1487
|
-
}
|
|
1488
|
-
lines.push(``);
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
if (files.length > 0) {
|
|
1492
|
-
lines.push(`### Key Files`);
|
|
1493
|
-
for (const f of files) {
|
|
1494
|
-
lines.push(`- \`${f}\``);
|
|
1495
|
-
}
|
|
1496
|
-
lines.push(``);
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
return lines;
|
|
1500
|
-
}
|
package/src/protocol.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import type { AgentDef, KnowledgeEntry, Squad, Task, TaskMessage } from "./types.js";
|
|
14
|
-
import { loadAllKnowledge, loadAllTasks, loadMessages
|
|
14
|
+
import { loadAllKnowledge, loadAllTasks, loadMessages } from "./store.js";
|
|
15
15
|
|
|
16
16
|
// ============================================================================
|
|
17
17
|
// Squad Protocol (injected into every agent)
|
|
@@ -54,9 +54,7 @@ The squad system will detect this and route help.
|
|
|
54
54
|
## Rules
|
|
55
55
|
- Stay focused on YOUR task — don't do work assigned to other agents
|
|
56
56
|
- Read the dependency outputs below — don't redo completed work
|
|
57
|
-
- **Follow the Design Contract in the Squad Progress Document** — use the exact API paths, ports, schemas, and file names specified. Do NOT invent alternatives
|
|
58
57
|
- Check the modified files list — coordinate before editing shared files
|
|
59
|
-
- When creating APIs, clearly document all endpoints, request/response shapes, and status codes in your completion output
|
|
60
58
|
- Ask for help if stuck — don't spin for more than a few minutes
|
|
61
59
|
- Verify your work before claiming done
|
|
62
60
|
`;
|
|
@@ -116,20 +114,6 @@ ${sections.join("\n---\n\n")}
|
|
|
116
114
|
`;
|
|
117
115
|
}
|
|
118
116
|
|
|
119
|
-
// ============================================================================
|
|
120
|
-
// Squad Progress Overview
|
|
121
|
-
// ============================================================================
|
|
122
|
-
|
|
123
|
-
export function buildOverviewSection(squadId: string): string {
|
|
124
|
-
const content = loadOverview(squadId);
|
|
125
|
-
if (!content.trim()) return "";
|
|
126
|
-
|
|
127
|
-
return `# Squad Progress Document
|
|
128
|
-
|
|
129
|
-
${content.trim()}
|
|
130
|
-
`;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
117
|
// ============================================================================
|
|
134
118
|
// Sibling Awareness
|
|
135
119
|
// ============================================================================
|
|
@@ -309,7 +293,6 @@ export function buildAgentSystemPrompt(options: ProtocolBuildOptions): string {
|
|
|
309
293
|
buildTaskSection(task),
|
|
310
294
|
buildReworkContext(task, squadId),
|
|
311
295
|
buildChainContext(task, allTasks, squadId),
|
|
312
|
-
buildOverviewSection(squadId),
|
|
313
296
|
buildSiblingAwareness(task, allTasks, modifiedFiles),
|
|
314
297
|
buildKnowledgeSection(squadId),
|
|
315
298
|
buildQueuedMessages(queuedMessages),
|
package/src/scheduler.ts
CHANGED
|
@@ -463,10 +463,6 @@ export class Scheduler {
|
|
|
463
463
|
text: "Task completed",
|
|
464
464
|
});
|
|
465
465
|
|
|
466
|
-
// Reload task after status update so overview has output, completed time, etc.
|
|
467
|
-
const updatedTask = store.loadTask(this.squadId, taskId) || task;
|
|
468
|
-
this.appendTaskOverview(updatedTask, taskId, messages);
|
|
469
|
-
|
|
470
466
|
this.emit({
|
|
471
467
|
type: "task_completed",
|
|
472
468
|
squadId: this.squadId,
|
|
@@ -943,88 +939,6 @@ export class Scheduler {
|
|
|
943
939
|
return textParts.length > 0 ? textParts.join("\n") : null;
|
|
944
940
|
}
|
|
945
941
|
|
|
946
|
-
// =========================================================================
|
|
947
|
-
// Overview Document — append task summary after completion
|
|
948
|
-
// =========================================================================
|
|
949
|
-
|
|
950
|
-
/**
|
|
951
|
-
* Build a concise markdown summary of a completed task and append it
|
|
952
|
-
* to the squad's OVERVIEW.md. This gives subsequent agents a narrative
|
|
953
|
-
* understanding of what was accomplished, issues hit, and decisions made.
|
|
954
|
-
*/
|
|
955
|
-
private appendTaskOverview(
|
|
956
|
-
task: Task,
|
|
957
|
-
taskId: string,
|
|
958
|
-
messages: import("./types.js").TaskMessage[],
|
|
959
|
-
): void {
|
|
960
|
-
try {
|
|
961
|
-
const lines: string[] = [];
|
|
962
|
-
|
|
963
|
-
// Header
|
|
964
|
-
lines.push(`## ${task.id}: ${task.title}`);
|
|
965
|
-
lines.push(``);
|
|
966
|
-
lines.push(`**Agent:** ${task.agent} | **Status:** ${task.status}`);
|
|
967
|
-
if (task.started && task.completed) {
|
|
968
|
-
const dur = new Date(task.completed).getTime() - new Date(task.started).getTime();
|
|
969
|
-
lines.push(`**Duration:** ${formatElapsed(dur)}`);
|
|
970
|
-
}
|
|
971
|
-
lines.push(``);
|
|
972
|
-
|
|
973
|
-
// Output
|
|
974
|
-
if (task.output) {
|
|
975
|
-
lines.push(`### Output`);
|
|
976
|
-
lines.push(task.output.slice(0, 800));
|
|
977
|
-
if (task.output.length > 800) lines.push(`... (truncated)`);
|
|
978
|
-
lines.push(``);
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// Issues / errors encountered
|
|
982
|
-
const errors = messages.filter((m) => m.type === "error" && m.from !== "system");
|
|
983
|
-
if (errors.length > 0) {
|
|
984
|
-
lines.push(`### Issues Encountered`);
|
|
985
|
-
for (const err of errors.slice(-5)) {
|
|
986
|
-
lines.push(`- ${err.text.split("\n")[0].slice(0, 200)}`);
|
|
987
|
-
}
|
|
988
|
-
lines.push(``);
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
// Decisions — scan agent text for decision-like language
|
|
992
|
-
const decisionPatterns = /\b(decided|chose|approach|instead of|trade-?off|opted|going with|switched to|using .+ because)\b/i;
|
|
993
|
-
const agentTexts = messages.filter((m) => m.from === task.agent && m.type === "text");
|
|
994
|
-
const decisions: string[] = [];
|
|
995
|
-
for (const msg of agentTexts) {
|
|
996
|
-
for (const line of msg.text.split("\n")) {
|
|
997
|
-
if (decisionPatterns.test(line) && line.trim().length > 10) {
|
|
998
|
-
decisions.push(line.trim().slice(0, 200));
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
if (decisions.length > 0) {
|
|
1003
|
-
lines.push(`### Decisions`);
|
|
1004
|
-
for (const d of decisions.slice(-5)) {
|
|
1005
|
-
lines.push(`- ${d}`);
|
|
1006
|
-
}
|
|
1007
|
-
lines.push(``);
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
// Files modified
|
|
1011
|
-
const activity = this.pool.getActivity(taskId);
|
|
1012
|
-
if (activity && activity.modifiedFiles.size > 0) {
|
|
1013
|
-
lines.push(`### Files Modified`);
|
|
1014
|
-
for (const f of Array.from(activity.modifiedFiles).slice(0, 15)) {
|
|
1015
|
-
lines.push(`- ${f}`);
|
|
1016
|
-
}
|
|
1017
|
-
if (activity.modifiedFiles.size > 15) {
|
|
1018
|
-
lines.push(`- ... and ${activity.modifiedFiles.size - 15} more`);
|
|
1019
|
-
}
|
|
1020
|
-
lines.push(``);
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
store.appendOverview(this.squadId, lines.join("\n"));
|
|
1024
|
-
} catch (err) {
|
|
1025
|
-
logError("squad-scheduler", `Failed to append overview for ${taskId}: ${(err as Error).message}`);
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
942
|
}
|
|
1029
943
|
|
|
1030
944
|
function formatElapsed(ms: number): string {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: squad-architecture
|
|
3
|
+
description: >
|
|
4
|
+
System architecture practices — project structure, API contracts, shared types,
|
|
5
|
+
monorepo setup, and technical decision documentation. Use when designing system
|
|
6
|
+
architecture, defining contracts between components, or setting up project structure.
|
|
7
|
+
version: 1.0.0
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Architecture & System Design
|
|
11
|
+
|
|
12
|
+
## Project Structure
|
|
13
|
+
- Define clear boundaries between components (backend, frontend, shared)
|
|
14
|
+
- Use a monorepo with workspaces when frontend and backend share types
|
|
15
|
+
- Create a shared types/constants package that both sides import
|
|
16
|
+
- Document the project structure in a README or ARCHITECTURE.md
|
|
17
|
+
|
|
18
|
+
## API Contract Definition
|
|
19
|
+
When defining API contracts that other agents will implement:
|
|
20
|
+
- List EVERY endpoint with method, path, request body, and response shape
|
|
21
|
+
- Specify exact field names, types, and which fields are optional
|
|
22
|
+
- Define error response format consistently
|
|
23
|
+
- Specify authentication requirements per endpoint
|
|
24
|
+
- Include example request/response pairs
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
```
|
|
28
|
+
POST /api/auth/login
|
|
29
|
+
Request: { email: string, password: string }
|
|
30
|
+
Response: { user: { id, email, name }, accessToken: string, refreshToken: string }
|
|
31
|
+
Errors: 401 { error: "Invalid credentials" }
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Shared Types
|
|
35
|
+
- Define TypeScript interfaces for all data models
|
|
36
|
+
- Include validation schemas (zod) alongside types
|
|
37
|
+
- Export constants (status enums, priority levels, config values)
|
|
38
|
+
- Version the shared package so consumers know when contracts change
|
|
39
|
+
|
|
40
|
+
## Technical Decisions
|
|
41
|
+
Document every significant decision in your output:
|
|
42
|
+
- What was decided and why
|
|
43
|
+
- What alternatives were considered
|
|
44
|
+
- What trade-offs were accepted
|
|
45
|
+
|
|
46
|
+
This helps downstream agents understand the rationale and stay consistent.
|
|
47
|
+
|
|
48
|
+
## Handoff Quality
|
|
49
|
+
Your output is the contract that all other agents build against. Be precise:
|
|
50
|
+
- Don't leave ambiguity in field names or types
|
|
51
|
+
- Specify exact port numbers, file paths, directory structure
|
|
52
|
+
- Include the database schema with column types and constraints
|
|
53
|
+
- List all environment variables needed
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: squad-backend-dev
|
|
3
|
+
description: >
|
|
4
|
+
Backend engineering practices — API design, database patterns, auth implementation,
|
|
5
|
+
input validation, error handling, and security. Use when building APIs, servers,
|
|
6
|
+
databases, or backend services.
|
|
7
|
+
version: 1.0.0
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Backend Development
|
|
11
|
+
|
|
12
|
+
## API Design
|
|
13
|
+
- RESTful conventions: GET (read), POST (create), PUT (replace), PATCH (update), DELETE (remove)
|
|
14
|
+
- Consistent response format: success returns data directly, errors return `{ error: string, code?: string }`
|
|
15
|
+
- Validate all inputs at the boundary (use zod, joi, or manual checks)
|
|
16
|
+
- Paginate list endpoints (limit/offset or cursor-based)
|
|
17
|
+
- Use proper HTTP status codes: 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 500 Server Error
|
|
18
|
+
- Document every endpoint in your completion output (method, path, request/response shape)
|
|
19
|
+
|
|
20
|
+
## Database
|
|
21
|
+
- Use migrations for schema changes (never ALTER in application code)
|
|
22
|
+
- Add indexes for frequently queried columns and foreign keys
|
|
23
|
+
- Use foreign keys with appropriate ON DELETE behavior (CASCADE, SET NULL)
|
|
24
|
+
- Use transactions for multi-step writes
|
|
25
|
+
- Sanitize all user inputs in queries (parameterized queries, never string interpolation)
|
|
26
|
+
|
|
27
|
+
## Authentication
|
|
28
|
+
- Never store passwords in plain text — use bcrypt with sufficient rounds (10+)
|
|
29
|
+
- JWT access tokens: short-lived (15min), stateless verification
|
|
30
|
+
- Refresh tokens: longer-lived (7d), stored server-side, rotated on use
|
|
31
|
+
- Validate tokens on every protected endpoint via middleware
|
|
32
|
+
- Return 401 for invalid/expired tokens, 403 for insufficient permissions
|
|
33
|
+
|
|
34
|
+
## Error Handling
|
|
35
|
+
- Catch errors at route level, don't let unhandled rejections crash the server
|
|
36
|
+
- Log errors with context (request id, user id, endpoint)
|
|
37
|
+
- Never expose stack traces or internal details in API responses
|
|
38
|
+
- Use typed error classes for different error categories
|
|
39
|
+
- Implement graceful shutdown (close DB connections, finish in-flight requests)
|
|
40
|
+
|
|
41
|
+
## Security
|
|
42
|
+
- Rate-limit public endpoints (login, register, password reset)
|
|
43
|
+
- Set security headers (CORS, Helmet for Express)
|
|
44
|
+
- Validate file uploads (type whitelist, size limits)
|
|
45
|
+
- Never log sensitive data (passwords, tokens, PII)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: squad-frontend-dev
|
|
3
|
+
description: >
|
|
4
|
+
Frontend engineering practices — React patterns, CSS/Tailwind, accessibility,
|
|
5
|
+
state management, API integration, and responsive design. Use when building
|
|
6
|
+
UIs, web apps, or frontend components.
|
|
7
|
+
version: 1.0.0
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Frontend Development
|
|
11
|
+
|
|
12
|
+
## React Patterns
|
|
13
|
+
- Use functional components with hooks (no class components)
|
|
14
|
+
- Extract reusable logic into custom hooks (useAuth, useFetch, useDebounce)
|
|
15
|
+
- Handle all UI states explicitly: loading, error, empty, success
|
|
16
|
+
- Use React.memo only for measured performance bottlenecks
|
|
17
|
+
- Prefer composition over prop drilling — use context for cross-cutting concerns
|
|
18
|
+
- Use controlled components for forms, validate before submit
|
|
19
|
+
|
|
20
|
+
## State Management
|
|
21
|
+
- Server state: use React Query / TanStack Query (caching, refetching, optimistic updates)
|
|
22
|
+
- Client state: use useState for local, useContext for shared, useReducer for complex
|
|
23
|
+
- URL state: use router params/search params for bookmarkable state
|
|
24
|
+
- Never duplicate server state in client state — let the query cache be the source of truth
|
|
25
|
+
|
|
26
|
+
## API Integration
|
|
27
|
+
- Create a typed API client module (don't scatter fetch calls across components)
|
|
28
|
+
- Handle 401 responses globally (refresh token, redirect to login)
|
|
29
|
+
- Show loading indicators for any request > 200ms
|
|
30
|
+
- Handle network errors gracefully (offline banner, retry button)
|
|
31
|
+
- Use optimistic updates for snappy UX (revert on error)
|
|
32
|
+
|
|
33
|
+
## CSS / Tailwind
|
|
34
|
+
- Use utility classes, avoid custom CSS when possible
|
|
35
|
+
- Follow the project's design tokens (colors, spacing, typography)
|
|
36
|
+
- Use responsive prefixes: sm:, md:, lg:, xl:
|
|
37
|
+
- Implement dark mode with dark: prefix when required
|
|
38
|
+
- Ensure consistent spacing scale (don't mix arbitrary values)
|
|
39
|
+
|
|
40
|
+
## Accessibility
|
|
41
|
+
- All interactive elements must be keyboard-navigable (tab, enter, escape)
|
|
42
|
+
- Use semantic HTML elements (button, nav, main, section, header, footer)
|
|
43
|
+
- Add aria-labels to icon-only buttons and non-text interactive elements
|
|
44
|
+
- Ensure color contrast meets WCAG AA (4.5:1 for normal text, 3:1 for large)
|
|
45
|
+
- Support screen readers: announce dynamic content changes with aria-live
|
|
46
|
+
|
|
47
|
+
## Build & Performance
|
|
48
|
+
- Verify the build completes without errors before claiming done
|
|
49
|
+
- Code-split routes (React.lazy + Suspense)
|
|
50
|
+
- Optimize images (lazy loading, proper sizing)
|
|
51
|
+
- Minimize bundle size (check for unnecessary dependencies)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: squad-qa-testing
|
|
3
|
+
description: >
|
|
4
|
+
QA and testing practices — test strategy, checklist, evidence requirements,
|
|
5
|
+
verdict format, and rework flow. Use when verifying, testing, or reviewing
|
|
6
|
+
implementations.
|
|
7
|
+
version: 1.0.0
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# QA & Testing
|
|
11
|
+
|
|
12
|
+
## Test Strategy
|
|
13
|
+
1. **Build verification**: Does the code compile/build without errors?
|
|
14
|
+
2. **Smoke test**: Does the app start and respond to basic requests?
|
|
15
|
+
3. **Functional tests**: Do all features work as specified?
|
|
16
|
+
4. **Edge cases**: Invalid inputs, empty states, boundary values
|
|
17
|
+
5. **Integration**: Do components work together (API ↔ frontend, auth flow)?
|
|
18
|
+
|
|
19
|
+
## Before You Start
|
|
20
|
+
- Read the task description and dependency outputs carefully
|
|
21
|
+
- Understand what was built before testing it
|
|
22
|
+
- Start the server/app and verify it's actually running
|
|
23
|
+
- Don't assume anything works — verify everything
|
|
24
|
+
|
|
25
|
+
## Testing Checklist
|
|
26
|
+
- [ ] App builds without errors (tsc, vite build, etc.)
|
|
27
|
+
- [ ] Server starts and responds to health check
|
|
28
|
+
- [ ] All CRUD operations work (create, read, update, delete)
|
|
29
|
+
- [ ] Authentication flow works (register, login, protected routes)
|
|
30
|
+
- [ ] Input validation rejects bad data (missing fields, wrong types)
|
|
31
|
+
- [ ] Error responses have correct HTTP status codes
|
|
32
|
+
- [ ] Frontend renders without console errors
|
|
33
|
+
- [ ] Navigation between pages works
|
|
34
|
+
- [ ] Forms submit correctly and show feedback
|
|
35
|
+
|
|
36
|
+
## Evidence Requirements
|
|
37
|
+
Every claim must have evidence. Don't just say "it works" — show it:
|
|
38
|
+
- **API tests**: Show curl commands and their responses
|
|
39
|
+
- **Build tests**: Show the build command output (exit code 0)
|
|
40
|
+
- **UI tests**: Describe what you see, or use screenshots
|
|
41
|
+
- **Error tests**: Show the error response for invalid input
|
|
42
|
+
|
|
43
|
+
## Verdict Format
|
|
44
|
+
```
|
|
45
|
+
## Verdict: PASS | FAIL
|
|
46
|
+
|
|
47
|
+
### Issues Found
|
|
48
|
+
| # | Issue | Severity | Details |
|
|
49
|
+
|---|-------|----------|---------|
|
|
50
|
+
| 1 | ... | Critical/High/Medium/Low | ... |
|
|
51
|
+
|
|
52
|
+
### Evidence
|
|
53
|
+
[test output, curl commands, screenshots]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Rework Flow
|
|
57
|
+
If issues are found:
|
|
58
|
+
1. Document each issue with severity, location, and reproduction steps
|
|
59
|
+
2. The squad system will create fix tasks automatically
|
|
60
|
+
3. You will re-test after fixes are applied
|
|
61
|
+
4. On re-test: verify ALL previous issues are fixed, not just the latest
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: squad-security-audit
|
|
3
|
+
description: >
|
|
4
|
+
Security audit checklist, vulnerability patterns, and remediation guidance.
|
|
5
|
+
Use when reviewing code for security issues, hardening an application,
|
|
6
|
+
or performing security verification.
|
|
7
|
+
version: 1.0.0
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Security Audit
|
|
11
|
+
|
|
12
|
+
## Checklist
|
|
13
|
+
- [ ] No secrets in source code (API keys, passwords, tokens, connection strings)
|
|
14
|
+
- [ ] All user inputs validated and sanitized
|
|
15
|
+
- [ ] SQL injection prevention (parameterized queries, no string interpolation)
|
|
16
|
+
- [ ] XSS prevention (output encoding, CSP headers, no innerHTML with user data)
|
|
17
|
+
- [ ] Authentication on all protected endpoints
|
|
18
|
+
- [ ] Authorization checks (can THIS user perform THIS action on THIS resource?)
|
|
19
|
+
- [ ] Rate limiting on public endpoints (login, register, password reset)
|
|
20
|
+
- [ ] Secure headers configured (CORS, HSTS, X-Frame-Options, X-Content-Type-Options)
|
|
21
|
+
- [ ] File upload validation (type whitelist, size limits)
|
|
22
|
+
- [ ] Error messages don't leak internal details (stack traces, SQL errors, file paths)
|
|
23
|
+
- [ ] Passwords hashed with bcrypt (not MD5, SHA1, or plain text)
|
|
24
|
+
- [ ] JWT tokens validated on every request (signature, expiry, issuer)
|
|
25
|
+
- [ ] Sensitive data not logged (passwords, tokens, credit cards)
|
|
26
|
+
- [ ] Dependencies checked for known vulnerabilities (npm audit)
|
|
27
|
+
|
|
28
|
+
## Common Vulnerabilities
|
|
29
|
+
- **Broken Access Control**: Missing auth checks, IDOR (accessing other users' data by changing IDs)
|
|
30
|
+
- **Injection**: SQL, NoSQL, command injection via unsanitized inputs
|
|
31
|
+
- **Broken Auth**: Weak passwords allowed, no rate limiting on login, tokens never expire
|
|
32
|
+
- **Security Misconfiguration**: Default credentials, verbose error pages, unnecessary features enabled
|
|
33
|
+
- **Sensitive Data Exposure**: Secrets in client-side code, PII in logs, HTTP instead of HTTPS
|
|
34
|
+
|
|
35
|
+
## Reporting Format
|
|
36
|
+
```
|
|
37
|
+
## Security Finding: [Title]
|
|
38
|
+
**Severity**: Critical | High | Medium | Low
|
|
39
|
+
**Location**: [file:line or endpoint]
|
|
40
|
+
**Description**: What's wrong
|
|
41
|
+
**Impact**: What could happen if exploited
|
|
42
|
+
**Remediation**: How to fix it (specific code change)
|
|
43
|
+
```
|
package/src/store.ts
CHANGED
|
@@ -405,32 +405,6 @@ export function loadAllKnowledge(squadId: string): KnowledgeEntry[] {
|
|
|
405
405
|
].sort((a, b) => a.ts.localeCompare(b.ts));
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
// ============================================================================
|
|
409
|
-
// Overview Document (shared squad summary)
|
|
410
|
-
// ============================================================================
|
|
411
|
-
|
|
412
|
-
export function getOverviewPath(squadId: string): string {
|
|
413
|
-
return path.join(getSquadDir(squadId), "OVERVIEW.md");
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
export function loadOverview(squadId: string): string {
|
|
417
|
-
try {
|
|
418
|
-
return fs.readFileSync(getOverviewPath(squadId), "utf-8");
|
|
419
|
-
} catch {
|
|
420
|
-
return "";
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
export function appendOverview(squadId: string, section: string): void {
|
|
425
|
-
const filePath = getOverviewPath(squadId);
|
|
426
|
-
ensureDir(path.dirname(filePath));
|
|
427
|
-
const existing = loadOverview(squadId);
|
|
428
|
-
const content = existing
|
|
429
|
-
? existing.trimEnd() + "\n\n---\n\n" + section.trim() + "\n"
|
|
430
|
-
: section.trim() + "\n";
|
|
431
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
432
|
-
}
|
|
433
|
-
|
|
434
408
|
// ============================================================================
|
|
435
409
|
// Rework Helpers
|
|
436
410
|
// ============================================================================
|