@bradygaster/squad-cli 0.9.1 → 0.9.2-insider.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/README.md +329 -329
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +10 -10
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/config.d.ts +12 -0
  6. package/dist/cli/commands/config.d.ts.map +1 -0
  7. package/dist/cli/commands/config.js +157 -0
  8. package/dist/cli/commands/config.js.map +1 -0
  9. package/dist/cli/commands/consult.d.ts.map +1 -1
  10. package/dist/cli/commands/consult.js +9 -4
  11. package/dist/cli/commands/consult.js.map +1 -1
  12. package/dist/cli/commands/copilot.d.ts.map +1 -1
  13. package/dist/cli/commands/copilot.js +8 -7
  14. package/dist/cli/commands/copilot.js.map +1 -1
  15. package/dist/cli/commands/doctor.d.ts.map +1 -1
  16. package/dist/cli/commands/doctor.js +50 -17
  17. package/dist/cli/commands/doctor.js.map +1 -1
  18. package/dist/cli/commands/economy.d.ts.map +1 -1
  19. package/dist/cli/commands/economy.js +3 -2
  20. package/dist/cli/commands/economy.js.map +1 -1
  21. package/dist/cli/commands/export.d.ts.map +1 -1
  22. package/dist/cli/commands/export.js +22 -16
  23. package/dist/cli/commands/export.js.map +1 -1
  24. package/dist/cli/commands/extract.d.ts.map +1 -1
  25. package/dist/cli/commands/extract.js +14 -10
  26. package/dist/cli/commands/extract.js.map +1 -1
  27. package/dist/cli/commands/import.d.ts.map +1 -1
  28. package/dist/cli/commands/import.js +21 -18
  29. package/dist/cli/commands/import.js.map +1 -1
  30. package/dist/cli/commands/init-remote.d.ts.map +1 -1
  31. package/dist/cli/commands/init-remote.js +7 -6
  32. package/dist/cli/commands/init-remote.js.map +1 -1
  33. package/dist/cli/commands/link.d.ts.map +1 -1
  34. package/dist/cli/commands/link.js +11 -10
  35. package/dist/cli/commands/link.js.map +1 -1
  36. package/dist/cli/commands/migrate.d.ts.map +1 -1
  37. package/dist/cli/commands/migrate.js +19 -18
  38. package/dist/cli/commands/migrate.js.map +1 -1
  39. package/dist/cli/commands/personal.d.ts.map +1 -1
  40. package/dist/cli/commands/personal.js +57 -65
  41. package/dist/cli/commands/personal.js.map +1 -1
  42. package/dist/cli/commands/plugin.d.ts.map +1 -1
  43. package/dist/cli/commands/plugin.js +8 -7
  44. package/dist/cli/commands/plugin.js.map +1 -1
  45. package/dist/cli/commands/rc.d.ts.map +1 -1
  46. package/dist/cli/commands/rc.js +19 -12
  47. package/dist/cli/commands/rc.js.map +1 -1
  48. package/dist/cli/commands/schedule.d.ts.map +1 -1
  49. package/dist/cli/commands/schedule.js +6 -5
  50. package/dist/cli/commands/schedule.js.map +1 -1
  51. package/dist/cli/commands/start.d.ts.map +1 -1
  52. package/dist/cli/commands/start.js +18 -11
  53. package/dist/cli/commands/start.js.map +1 -1
  54. package/dist/cli/commands/streams.d.ts.map +1 -1
  55. package/dist/cli/commands/streams.js +3 -2
  56. package/dist/cli/commands/streams.js.map +1 -1
  57. package/dist/cli/commands/upstream.d.ts.map +1 -1
  58. package/dist/cli/commands/upstream.js +23 -19
  59. package/dist/cli/commands/upstream.js.map +1 -1
  60. package/dist/cli/commands/watch/capabilities/board.d.ts +22 -0
  61. package/dist/cli/commands/watch/capabilities/board.d.ts.map +1 -0
  62. package/dist/cli/commands/watch/capabilities/board.js +121 -0
  63. package/dist/cli/commands/watch/capabilities/board.js.map +1 -0
  64. package/dist/cli/commands/watch/capabilities/budget-check.d.ts +29 -0
  65. package/dist/cli/commands/watch/capabilities/budget-check.d.ts.map +1 -0
  66. package/dist/cli/commands/watch/capabilities/budget-check.js +38 -0
  67. package/dist/cli/commands/watch/capabilities/budget-check.js.map +1 -0
  68. package/dist/cli/commands/watch/capabilities/circuit-breaker.d.ts +52 -0
  69. package/dist/cli/commands/watch/capabilities/circuit-breaker.d.ts.map +1 -0
  70. package/dist/cli/commands/watch/capabilities/circuit-breaker.js +152 -0
  71. package/dist/cli/commands/watch/capabilities/circuit-breaker.js.map +1 -0
  72. package/dist/cli/commands/watch/capabilities/decision-hygiene.d.ts +14 -0
  73. package/dist/cli/commands/watch/capabilities/decision-hygiene.d.ts.map +1 -0
  74. package/dist/cli/commands/watch/capabilities/decision-hygiene.js +72 -0
  75. package/dist/cli/commands/watch/capabilities/decision-hygiene.js.map +1 -0
  76. package/dist/cli/commands/watch/capabilities/execute.d.ts +33 -0
  77. package/dist/cli/commands/watch/capabilities/execute.d.ts.map +1 -0
  78. package/dist/cli/commands/watch/capabilities/execute.js +156 -0
  79. package/dist/cli/commands/watch/capabilities/execute.js.map +1 -0
  80. package/dist/cli/commands/watch/capabilities/health-check.d.ts +29 -0
  81. package/dist/cli/commands/watch/capabilities/health-check.d.ts.map +1 -0
  82. package/dist/cli/commands/watch/capabilities/health-check.js +139 -0
  83. package/dist/cli/commands/watch/capabilities/health-check.js.map +1 -0
  84. package/dist/cli/commands/watch/capabilities/heartbeat.d.ts +48 -0
  85. package/dist/cli/commands/watch/capabilities/heartbeat.d.ts.map +1 -0
  86. package/dist/cli/commands/watch/capabilities/heartbeat.js +115 -0
  87. package/dist/cli/commands/watch/capabilities/heartbeat.js.map +1 -0
  88. package/dist/cli/commands/watch/capabilities/index.d.ts +9 -0
  89. package/dist/cli/commands/watch/capabilities/index.d.ts.map +1 -0
  90. package/dist/cli/commands/watch/capabilities/index.js +40 -0
  91. package/dist/cli/commands/watch/capabilities/index.js.map +1 -0
  92. package/dist/cli/commands/watch/capabilities/lockfile.d.ts +30 -0
  93. package/dist/cli/commands/watch/capabilities/lockfile.d.ts.map +1 -0
  94. package/dist/cli/commands/watch/capabilities/lockfile.js +100 -0
  95. package/dist/cli/commands/watch/capabilities/lockfile.js.map +1 -0
  96. package/dist/cli/commands/watch/capabilities/machine-capabilities.d.ts +30 -0
  97. package/dist/cli/commands/watch/capabilities/machine-capabilities.d.ts.map +1 -0
  98. package/dist/cli/commands/watch/capabilities/machine-capabilities.js +103 -0
  99. package/dist/cli/commands/watch/capabilities/machine-capabilities.js.map +1 -0
  100. package/dist/cli/commands/watch/capabilities/monitor-email.d.ts +14 -0
  101. package/dist/cli/commands/watch/capabilities/monitor-email.d.ts.map +1 -0
  102. package/dist/cli/commands/watch/capabilities/monitor-email.js +54 -0
  103. package/dist/cli/commands/watch/capabilities/monitor-email.js.map +1 -0
  104. package/dist/cli/commands/watch/capabilities/monitor-teams.d.ts +14 -0
  105. package/dist/cli/commands/watch/capabilities/monitor-teams.d.ts.map +1 -0
  106. package/dist/cli/commands/watch/capabilities/monitor-teams.js +55 -0
  107. package/dist/cli/commands/watch/capabilities/monitor-teams.js.map +1 -0
  108. package/dist/cli/commands/watch/capabilities/post-failure.d.ts +19 -0
  109. package/dist/cli/commands/watch/capabilities/post-failure.d.ts.map +1 -0
  110. package/dist/cli/commands/watch/capabilities/post-failure.js +58 -0
  111. package/dist/cli/commands/watch/capabilities/post-failure.js.map +1 -0
  112. package/dist/cli/commands/watch/capabilities/priority.d.ts +59 -0
  113. package/dist/cli/commands/watch/capabilities/priority.d.ts.map +1 -0
  114. package/dist/cli/commands/watch/capabilities/priority.js +101 -0
  115. package/dist/cli/commands/watch/capabilities/priority.js.map +1 -0
  116. package/dist/cli/commands/watch/capabilities/rate-pool.d.ts +67 -0
  117. package/dist/cli/commands/watch/capabilities/rate-pool.d.ts.map +1 -0
  118. package/dist/cli/commands/watch/capabilities/rate-pool.js +187 -0
  119. package/dist/cli/commands/watch/capabilities/rate-pool.js.map +1 -0
  120. package/dist/cli/commands/watch/capabilities/retro.d.ts +14 -0
  121. package/dist/cli/commands/watch/capabilities/retro.d.ts.map +1 -0
  122. package/dist/cli/commands/watch/capabilities/retro.js +81 -0
  123. package/dist/cli/commands/watch/capabilities/retro.js.map +1 -0
  124. package/dist/cli/commands/watch/capabilities/self-pull.d.ts +14 -0
  125. package/dist/cli/commands/watch/capabilities/self-pull.d.ts.map +1 -0
  126. package/dist/cli/commands/watch/capabilities/self-pull.js +33 -0
  127. package/dist/cli/commands/watch/capabilities/self-pull.js.map +1 -0
  128. package/dist/cli/commands/watch/capabilities/stale-reclaim.d.ts +23 -0
  129. package/dist/cli/commands/watch/capabilities/stale-reclaim.d.ts.map +1 -0
  130. package/dist/cli/commands/watch/capabilities/stale-reclaim.js +87 -0
  131. package/dist/cli/commands/watch/capabilities/stale-reclaim.js.map +1 -0
  132. package/dist/cli/commands/watch/capabilities/two-pass.d.ts +14 -0
  133. package/dist/cli/commands/watch/capabilities/two-pass.d.ts.map +1 -0
  134. package/dist/cli/commands/watch/capabilities/two-pass.js +66 -0
  135. package/dist/cli/commands/watch/capabilities/two-pass.js.map +1 -0
  136. package/dist/cli/commands/watch/capabilities/wave-dispatch.d.ts +14 -0
  137. package/dist/cli/commands/watch/capabilities/wave-dispatch.d.ts.map +1 -0
  138. package/dist/cli/commands/watch/capabilities/wave-dispatch.js +117 -0
  139. package/dist/cli/commands/watch/capabilities/wave-dispatch.js.map +1 -0
  140. package/dist/cli/commands/watch/capabilities/webhook-alerts.d.ts +29 -0
  141. package/dist/cli/commands/watch/capabilities/webhook-alerts.d.ts.map +1 -0
  142. package/dist/cli/commands/watch/capabilities/webhook-alerts.js +114 -0
  143. package/dist/cli/commands/watch/capabilities/webhook-alerts.js.map +1 -0
  144. package/dist/cli/commands/watch/config.d.ts +40 -0
  145. package/dist/cli/commands/watch/config.d.ts.map +1 -0
  146. package/dist/cli/commands/watch/config.js +129 -0
  147. package/dist/cli/commands/watch/config.js.map +1 -0
  148. package/dist/cli/commands/watch/index.d.ts +109 -0
  149. package/dist/cli/commands/watch/index.d.ts.map +1 -0
  150. package/dist/cli/commands/watch/index.js +757 -0
  151. package/dist/cli/commands/watch/index.js.map +1 -0
  152. package/dist/cli/commands/watch/registry.d.ts +19 -0
  153. package/dist/cli/commands/watch/registry.d.ts.map +1 -0
  154. package/dist/cli/commands/watch/registry.js +28 -0
  155. package/dist/cli/commands/watch/registry.js.map +1 -0
  156. package/dist/cli/commands/watch/types.d.ts +57 -0
  157. package/dist/cli/commands/watch/types.d.ts.map +1 -0
  158. package/dist/cli/commands/watch/types.js +8 -0
  159. package/dist/cli/commands/watch/types.js.map +1 -0
  160. package/dist/cli/core/cast.d.ts.map +1 -1
  161. package/dist/cli/core/cast.js +15 -19
  162. package/dist/cli/core/cast.js.map +1 -1
  163. package/dist/cli/core/detect-squad-dir.d.ts.map +1 -1
  164. package/dist/cli/core/detect-squad-dir.js +12 -10
  165. package/dist/cli/core/detect-squad-dir.js.map +1 -1
  166. package/dist/cli/core/email-scrub.d.ts.map +1 -1
  167. package/dist/cli/core/email-scrub.js +12 -11
  168. package/dist/cli/core/email-scrub.js.map +1 -1
  169. package/dist/cli/core/gh-cli.d.ts +13 -0
  170. package/dist/cli/core/gh-cli.d.ts.map +1 -1
  171. package/dist/cli/core/gh-cli.js +24 -0
  172. package/dist/cli/core/gh-cli.js.map +1 -1
  173. package/dist/cli/core/init.d.ts +2 -0
  174. package/dist/cli/core/init.d.ts.map +1 -1
  175. package/dist/cli/core/init.js +22 -5
  176. package/dist/cli/core/init.js.map +1 -1
  177. package/dist/cli/core/migrate-directory.d.ts.map +1 -1
  178. package/dist/cli/core/migrate-directory.js +14 -13
  179. package/dist/cli/core/migrate-directory.js.map +1 -1
  180. package/dist/cli/core/migrations.d.ts.map +1 -1
  181. package/dist/cli/core/migrations.js +22 -8
  182. package/dist/cli/core/migrations.js.map +1 -1
  183. package/dist/cli/core/nap.d.ts.map +1 -1
  184. package/dist/cli/core/nap.js +116 -49
  185. package/dist/cli/core/nap.js.map +1 -1
  186. package/dist/cli/core/project-type.d.ts.map +1 -1
  187. package/dist/cli/core/project-type.js +11 -10
  188. package/dist/cli/core/project-type.js.map +1 -1
  189. package/dist/cli/core/team-md.d.ts.map +1 -1
  190. package/dist/cli/core/team-md.js +43 -38
  191. package/dist/cli/core/team-md.js.map +1 -1
  192. package/dist/cli/core/templates.d.ts.map +1 -1
  193. package/dist/cli/core/templates.js +4 -3
  194. package/dist/cli/core/templates.js.map +1 -1
  195. package/dist/cli/core/upgrade.d.ts.map +1 -1
  196. package/dist/cli/core/upgrade.js +68 -55
  197. package/dist/cli/core/upgrade.js.map +1 -1
  198. package/dist/cli/core/version.d.ts.map +1 -1
  199. package/dist/cli/core/version.js +8 -7
  200. package/dist/cli/core/version.js.map +1 -1
  201. package/dist/cli/index.d.ts +1 -1
  202. package/dist/cli/index.d.ts.map +1 -1
  203. package/dist/cli/index.js +1 -1
  204. package/dist/cli/index.js.map +1 -1
  205. package/dist/cli/self-update.d.ts.map +1 -1
  206. package/dist/cli/self-update.js +7 -4
  207. package/dist/cli/self-update.js.map +1 -1
  208. package/dist/cli/shell/agent-name-parser.d.ts +16 -0
  209. package/dist/cli/shell/agent-name-parser.d.ts.map +1 -0
  210. package/dist/cli/shell/agent-name-parser.js +54 -0
  211. package/dist/cli/shell/agent-name-parser.js.map +1 -0
  212. package/dist/cli/shell/commands.d.ts.map +1 -1
  213. package/dist/cli/shell/commands.js +4 -3
  214. package/dist/cli/shell/commands.js.map +1 -1
  215. package/dist/cli/shell/coordinator.d.ts +4 -1
  216. package/dist/cli/shell/coordinator.d.ts.map +1 -1
  217. package/dist/cli/shell/coordinator.js +29 -26
  218. package/dist/cli/shell/coordinator.js.map +1 -1
  219. package/dist/cli/shell/index.d.ts.map +1 -1
  220. package/dist/cli/shell/index.js +33 -35
  221. package/dist/cli/shell/index.js.map +1 -1
  222. package/dist/cli/shell/lifecycle.d.ts +13 -2
  223. package/dist/cli/shell/lifecycle.d.ts.map +1 -1
  224. package/dist/cli/shell/lifecycle.js +26 -13
  225. package/dist/cli/shell/lifecycle.js.map +1 -1
  226. package/dist/cli/shell/session-store.d.ts.map +1 -1
  227. package/dist/cli/shell/session-store.js +16 -12
  228. package/dist/cli/shell/session-store.js.map +1 -1
  229. package/dist/cli/shell/spawn.d.ts +4 -1
  230. package/dist/cli/shell/spawn.d.ts.map +1 -1
  231. package/dist/cli/shell/spawn.js +28 -10
  232. package/dist/cli/shell/spawn.js.map +1 -1
  233. package/dist/cli-entry.js +136 -12
  234. package/dist/cli-entry.js.map +1 -1
  235. package/package.json +8 -4
  236. package/scripts/patch-esm-imports.mjs +105 -105
  237. package/scripts/patch-ink-rendering.mjs +115 -115
  238. package/templates/casting/Futurama.json +9 -9
  239. package/templates/casting-history.json +4 -4
  240. package/templates/casting-policy.json +37 -37
  241. package/templates/casting-reference.md +104 -104
  242. package/templates/casting-registry.json +3 -3
  243. package/templates/ceremonies.md +41 -41
  244. package/templates/charter.md +53 -53
  245. package/templates/constraint-tracking.md +38 -38
  246. package/templates/cooperative-rate-limiting.md +229 -229
  247. package/templates/copilot-instructions.md +46 -46
  248. package/templates/history.md +10 -10
  249. package/templates/identity/now.md +9 -9
  250. package/templates/identity/wisdom.md +15 -15
  251. package/templates/issue-lifecycle.md +412 -412
  252. package/templates/keda-scaler.md +164 -164
  253. package/templates/machine-capabilities.md +74 -74
  254. package/templates/mcp-config.md +90 -90
  255. package/templates/multi-agent-format.md +28 -28
  256. package/templates/orchestration-log.md +27 -27
  257. package/templates/plugin-marketplace.md +49 -49
  258. package/templates/ralph-circuit-breaker.md +313 -313
  259. package/templates/raw-agent-output.md +37 -37
  260. package/templates/roster.md +60 -60
  261. package/templates/routing.md +39 -39
  262. package/templates/run-output.md +50 -50
  263. package/templates/scribe-charter.md +123 -119
  264. package/templates/skill.md +24 -24
  265. package/templates/skills/agent-collaboration/SKILL.md +42 -42
  266. package/templates/skills/agent-conduct/SKILL.md +24 -24
  267. package/templates/skills/architectural-proposals/SKILL.md +151 -151
  268. package/templates/skills/ci-validation-gates/SKILL.md +84 -84
  269. package/templates/skills/cli-wiring/SKILL.md +47 -47
  270. package/templates/skills/client-compatibility/SKILL.md +89 -89
  271. package/templates/skills/cross-machine-coordination/SKILL.md +434 -0
  272. package/templates/skills/cross-squad/SKILL.md +114 -114
  273. package/templates/skills/distributed-mesh/SKILL.md +287 -287
  274. package/templates/skills/distributed-mesh/mesh.json.example +30 -30
  275. package/templates/skills/distributed-mesh/sync-mesh.ps1 +111 -111
  276. package/templates/skills/distributed-mesh/sync-mesh.sh +104 -104
  277. package/templates/skills/docs-standards/SKILL.md +71 -71
  278. package/templates/skills/economy-mode/SKILL.md +114 -114
  279. package/templates/skills/error-recovery/SKILL.md +99 -0
  280. package/templates/skills/external-comms/SKILL.md +329 -329
  281. package/templates/skills/gh-auth-isolation/SKILL.md +183 -183
  282. package/templates/skills/git-workflow/SKILL.md +204 -204
  283. package/templates/skills/github-multi-account/SKILL.md +95 -95
  284. package/templates/skills/history-hygiene/SKILL.md +36 -36
  285. package/templates/skills/humanizer/SKILL.md +105 -105
  286. package/templates/skills/init-mode/SKILL.md +102 -102
  287. package/templates/skills/iterative-retrieval/SKILL.md +165 -0
  288. package/templates/skills/model-selection/SKILL.md +117 -117
  289. package/templates/skills/nap/SKILL.md +24 -24
  290. package/templates/skills/notification-routing/SKILL.md +105 -0
  291. package/templates/skills/personal-squad/SKILL.md +57 -57
  292. package/templates/skills/pr-screenshots/SKILL.md +149 -0
  293. package/templates/skills/ralph-two-pass-scan/SKILL.md +35 -0
  294. package/templates/skills/reflect/SKILL.md +229 -0
  295. package/templates/skills/release-process/SKILL.md +131 -423
  296. package/templates/skills/reskill/SKILL.md +92 -92
  297. package/templates/skills/retro-enforcement/SKILL.md +148 -0
  298. package/templates/skills/reviewer-protocol/SKILL.md +79 -79
  299. package/templates/skills/secret-handling/SKILL.md +200 -200
  300. package/templates/skills/session-recovery/SKILL.md +155 -155
  301. package/templates/skills/squad-conventions/SKILL.md +69 -69
  302. package/templates/skills/test-discipline/SKILL.md +37 -37
  303. package/templates/skills/tiered-memory/SKILL.md +234 -0
  304. package/templates/skills/windows-compatibility/SKILL.md +98 -74
  305. package/templates/{squad.agent.md → squad.agent.md.template} +1316 -1287
  306. package/templates/workflows/squad-ci.yml +24 -24
  307. package/templates/workflows/squad-docs.yml +54 -54
  308. package/templates/workflows/squad-heartbeat.yml +0 -4
  309. package/templates/workflows/squad-insider-release.yml +61 -61
  310. package/templates/workflows/squad-issue-assign.yml +161 -161
  311. package/templates/workflows/squad-label-enforce.yml +181 -181
  312. package/templates/workflows/squad-preview.yml +55 -55
  313. package/templates/workflows/squad-promote.yml +120 -120
  314. package/templates/workflows/squad-release.yml +77 -77
  315. package/templates/workflows/squad-triage.yml +260 -260
  316. package/templates/workflows/sync-squad-labels.yml +169 -169
  317. package/dist/cli/commands/watch.d.ts +0 -18
  318. package/dist/cli/commands/watch.d.ts.map +0 -1
  319. package/dist/cli/commands/watch.js +0 -306
  320. package/dist/cli/commands/watch.js.map +0 -1
@@ -1,260 +1,260 @@
1
- name: Squad Triage
2
-
3
- on:
4
- issues:
5
- types: [labeled]
6
-
7
- permissions:
8
- issues: write
9
- contents: read
10
-
11
- jobs:
12
- triage:
13
- if: github.event.label.name == 'squad'
14
- runs-on: ubuntu-latest
15
- steps:
16
- - uses: actions/checkout@v4
17
-
18
- - name: Triage issue via Lead agent
19
- uses: actions/github-script@v7
20
- with:
21
- script: |
22
- const fs = require('fs');
23
- const issue = context.payload.issue;
24
-
25
- // Read team roster — check .squad/ first, fall back to .ai-team/
26
- let teamFile = '.squad/team.md';
27
- if (!fs.existsSync(teamFile)) {
28
- teamFile = '.ai-team/team.md';
29
- }
30
- if (!fs.existsSync(teamFile)) {
31
- core.warning('No .squad/team.md or .ai-team/team.md found — cannot triage');
32
- return;
33
- }
34
-
35
- const content = fs.readFileSync(teamFile, 'utf8');
36
- const lines = content.split('\n');
37
-
38
- // Check if @copilot is on the team
39
- const hasCopilot = content.includes('🤖 Coding Agent');
40
- const copilotAutoAssign = content.includes('<!-- copilot-auto-assign: true -->');
41
-
42
- // Parse @copilot capability profile
43
- let goodFitKeywords = [];
44
- let needsReviewKeywords = [];
45
- let notSuitableKeywords = [];
46
-
47
- if (hasCopilot) {
48
- // Extract capability tiers from team.md
49
- const goodFitMatch = content.match(/🟢\s*Good fit[^:]*:\s*(.+)/i);
50
- const needsReviewMatch = content.match(/🟡\s*Needs review[^:]*:\s*(.+)/i);
51
- const notSuitableMatch = content.match(/🔴\s*Not suitable[^:]*:\s*(.+)/i);
52
-
53
- if (goodFitMatch) {
54
- goodFitKeywords = goodFitMatch[1].toLowerCase().split(',').map(s => s.trim());
55
- } else {
56
- goodFitKeywords = ['bug fix', 'test coverage', 'lint', 'format', 'dependency update', 'small feature', 'scaffolding', 'doc fix', 'documentation'];
57
- }
58
- if (needsReviewMatch) {
59
- needsReviewKeywords = needsReviewMatch[1].toLowerCase().split(',').map(s => s.trim());
60
- } else {
61
- needsReviewKeywords = ['medium feature', 'refactoring', 'api endpoint', 'migration'];
62
- }
63
- if (notSuitableMatch) {
64
- notSuitableKeywords = notSuitableMatch[1].toLowerCase().split(',').map(s => s.trim());
65
- } else {
66
- notSuitableKeywords = ['architecture', 'system design', 'security', 'auth', 'encryption', 'performance'];
67
- }
68
- }
69
-
70
- const members = [];
71
- let inMembersTable = false;
72
- for (const line of lines) {
73
- if (line.match(/^##\s+(Members|Team Roster)/i)) {
74
- inMembersTable = true;
75
- continue;
76
- }
77
- if (inMembersTable && line.startsWith('## ')) {
78
- break;
79
- }
80
- if (inMembersTable && line.startsWith('|') && !line.includes('---') && !line.includes('Name')) {
81
- const cells = line.split('|').map(c => c.trim()).filter(Boolean);
82
- if (cells.length >= 2 && cells[0] !== 'Scribe') {
83
- members.push({
84
- name: cells[0],
85
- role: cells[1]
86
- });
87
- }
88
- }
89
- }
90
-
91
- // Read routing rules — check .squad/ first, fall back to .ai-team/
92
- let routingFile = '.squad/routing.md';
93
- if (!fs.existsSync(routingFile)) {
94
- routingFile = '.ai-team/routing.md';
95
- }
96
- let routingContent = '';
97
- if (fs.existsSync(routingFile)) {
98
- routingContent = fs.readFileSync(routingFile, 'utf8');
99
- }
100
-
101
- // Find the Lead
102
- const lead = members.find(m =>
103
- m.role.toLowerCase().includes('lead') ||
104
- m.role.toLowerCase().includes('architect') ||
105
- m.role.toLowerCase().includes('coordinator')
106
- );
107
-
108
- if (!lead) {
109
- core.warning('No Lead role found in team roster — cannot triage');
110
- return;
111
- }
112
-
113
- // Build triage context
114
- const memberList = members.map(m =>
115
- `- **${m.name}** (${m.role}) → label: \`squad:${m.name.toLowerCase()}\``
116
- ).join('\n');
117
-
118
- // Determine best assignee based on issue content and routing
119
- const issueText = `${issue.title}\n${issue.body || ''}`.toLowerCase();
120
-
121
- let assignedMember = null;
122
- let triageReason = '';
123
- let copilotTier = null;
124
-
125
- // First, evaluate @copilot fit if enabled
126
- if (hasCopilot) {
127
- const isNotSuitable = notSuitableKeywords.some(kw => issueText.includes(kw));
128
- const isGoodFit = !isNotSuitable && goodFitKeywords.some(kw => issueText.includes(kw));
129
- const isNeedsReview = !isNotSuitable && !isGoodFit && needsReviewKeywords.some(kw => issueText.includes(kw));
130
-
131
- if (isGoodFit) {
132
- copilotTier = 'good-fit';
133
- assignedMember = { name: '@copilot', role: 'Coding Agent' };
134
- triageReason = '🟢 Good fit for @copilot — matches capability profile';
135
- } else if (isNeedsReview) {
136
- copilotTier = 'needs-review';
137
- assignedMember = { name: '@copilot', role: 'Coding Agent' };
138
- triageReason = '🟡 Routing to @copilot (needs review) — a squad member should review the PR';
139
- } else if (isNotSuitable) {
140
- copilotTier = 'not-suitable';
141
- // Fall through to normal routing
142
- }
143
- }
144
-
145
- // If not routed to @copilot, use keyword-based routing
146
- if (!assignedMember) {
147
- for (const member of members) {
148
- const role = member.role.toLowerCase();
149
- if ((role.includes('frontend') || role.includes('ui')) &&
150
- (issueText.includes('ui') || issueText.includes('frontend') ||
151
- issueText.includes('css') || issueText.includes('component') ||
152
- issueText.includes('button') || issueText.includes('page') ||
153
- issueText.includes('layout') || issueText.includes('design'))) {
154
- assignedMember = member;
155
- triageReason = 'Issue relates to frontend/UI work';
156
- break;
157
- }
158
- if ((role.includes('backend') || role.includes('api') || role.includes('server')) &&
159
- (issueText.includes('api') || issueText.includes('backend') ||
160
- issueText.includes('database') || issueText.includes('endpoint') ||
161
- issueText.includes('server') || issueText.includes('auth'))) {
162
- assignedMember = member;
163
- triageReason = 'Issue relates to backend/API work';
164
- break;
165
- }
166
- if ((role.includes('test') || role.includes('qa') || role.includes('quality')) &&
167
- (issueText.includes('test') || issueText.includes('bug') ||
168
- issueText.includes('fix') || issueText.includes('regression') ||
169
- issueText.includes('coverage'))) {
170
- assignedMember = member;
171
- triageReason = 'Issue relates to testing/quality work';
172
- break;
173
- }
174
- if ((role.includes('devops') || role.includes('infra') || role.includes('ops')) &&
175
- (issueText.includes('deploy') || issueText.includes('ci') ||
176
- issueText.includes('pipeline') || issueText.includes('docker') ||
177
- issueText.includes('infrastructure'))) {
178
- assignedMember = member;
179
- triageReason = 'Issue relates to DevOps/infrastructure work';
180
- break;
181
- }
182
- }
183
- }
184
-
185
- // Default to Lead if no routing match
186
- if (!assignedMember) {
187
- assignedMember = lead;
188
- triageReason = 'No specific domain match — assigned to Lead for further analysis';
189
- }
190
-
191
- const isCopilot = assignedMember.name === '@copilot';
192
- const assignLabel = isCopilot ? 'squad:copilot' : `squad:${assignedMember.name.toLowerCase()}`;
193
-
194
- // Add the member-specific label
195
- await github.rest.issues.addLabels({
196
- owner: context.repo.owner,
197
- repo: context.repo.repo,
198
- issue_number: issue.number,
199
- labels: [assignLabel]
200
- });
201
-
202
- // Apply default triage verdict
203
- await github.rest.issues.addLabels({
204
- owner: context.repo.owner,
205
- repo: context.repo.repo,
206
- issue_number: issue.number,
207
- labels: ['go:needs-research']
208
- });
209
-
210
- // Auto-assign @copilot if enabled
211
- if (isCopilot && copilotAutoAssign) {
212
- try {
213
- await github.rest.issues.addAssignees({
214
- owner: context.repo.owner,
215
- repo: context.repo.repo,
216
- issue_number: issue.number,
217
- assignees: ['copilot']
218
- });
219
- } catch (err) {
220
- core.warning(`Could not auto-assign @copilot: ${err.message}`);
221
- }
222
- }
223
-
224
- // Build copilot evaluation note
225
- let copilotNote = '';
226
- if (hasCopilot && !isCopilot) {
227
- if (copilotTier === 'not-suitable') {
228
- copilotNote = `\n\n**@copilot evaluation:** 🔴 Not suitable — issue involves work outside the coding agent's capability profile.`;
229
- } else {
230
- copilotNote = `\n\n**@copilot evaluation:** No strong capability match — routed to squad member.`;
231
- }
232
- }
233
-
234
- // Post triage comment
235
- const comment = [
236
- `### 🏗️ Squad Triage — ${lead.name} (${lead.role})`,
237
- '',
238
- `**Issue:** #${issue.number} — ${issue.title}`,
239
- `**Assigned to:** ${assignedMember.name} (${assignedMember.role})`,
240
- `**Reason:** ${triageReason}`,
241
- copilotTier === 'needs-review' ? `\n⚠️ **PR review recommended** — a squad member should review @copilot's work on this one.` : '',
242
- copilotNote,
243
- '',
244
- `---`,
245
- '',
246
- `**Team roster:**`,
247
- memberList,
248
- hasCopilot ? `- **@copilot** (Coding Agent) → label: \`squad:copilot\`` : '',
249
- '',
250
- `> To reassign, remove the current \`squad:*\` label and add the correct one.`,
251
- ].filter(Boolean).join('\n');
252
-
253
- await github.rest.issues.createComment({
254
- owner: context.repo.owner,
255
- repo: context.repo.repo,
256
- issue_number: issue.number,
257
- body: comment
258
- });
259
-
260
- core.info(`Triaged issue #${issue.number} → ${assignedMember.name} (${assignLabel})`);
1
+ name: Squad Triage
2
+
3
+ on:
4
+ issues:
5
+ types: [labeled]
6
+
7
+ permissions:
8
+ issues: write
9
+ contents: read
10
+
11
+ jobs:
12
+ triage:
13
+ if: github.event.label.name == 'squad'
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Triage issue via Lead agent
19
+ uses: actions/github-script@v7
20
+ with:
21
+ script: |
22
+ const fs = require('fs');
23
+ const issue = context.payload.issue;
24
+
25
+ // Read team roster — check .squad/ first, fall back to .ai-team/
26
+ let teamFile = '.squad/team.md';
27
+ if (!fs.existsSync(teamFile)) {
28
+ teamFile = '.ai-team/team.md';
29
+ }
30
+ if (!fs.existsSync(teamFile)) {
31
+ core.warning('No .squad/team.md or .ai-team/team.md found — cannot triage');
32
+ return;
33
+ }
34
+
35
+ const content = fs.readFileSync(teamFile, 'utf8');
36
+ const lines = content.split('\n');
37
+
38
+ // Check if @copilot is on the team
39
+ const hasCopilot = content.includes('🤖 Coding Agent');
40
+ const copilotAutoAssign = content.includes('<!-- copilot-auto-assign: true -->');
41
+
42
+ // Parse @copilot capability profile
43
+ let goodFitKeywords = [];
44
+ let needsReviewKeywords = [];
45
+ let notSuitableKeywords = [];
46
+
47
+ if (hasCopilot) {
48
+ // Extract capability tiers from team.md
49
+ const goodFitMatch = content.match(/🟢\s*Good fit[^:]*:\s*(.+)/i);
50
+ const needsReviewMatch = content.match(/🟡\s*Needs review[^:]*:\s*(.+)/i);
51
+ const notSuitableMatch = content.match(/🔴\s*Not suitable[^:]*:\s*(.+)/i);
52
+
53
+ if (goodFitMatch) {
54
+ goodFitKeywords = goodFitMatch[1].toLowerCase().split(',').map(s => s.trim());
55
+ } else {
56
+ goodFitKeywords = ['bug fix', 'test coverage', 'lint', 'format', 'dependency update', 'small feature', 'scaffolding', 'doc fix', 'documentation'];
57
+ }
58
+ if (needsReviewMatch) {
59
+ needsReviewKeywords = needsReviewMatch[1].toLowerCase().split(',').map(s => s.trim());
60
+ } else {
61
+ needsReviewKeywords = ['medium feature', 'refactoring', 'api endpoint', 'migration'];
62
+ }
63
+ if (notSuitableMatch) {
64
+ notSuitableKeywords = notSuitableMatch[1].toLowerCase().split(',').map(s => s.trim());
65
+ } else {
66
+ notSuitableKeywords = ['architecture', 'system design', 'security', 'auth', 'encryption', 'performance'];
67
+ }
68
+ }
69
+
70
+ const members = [];
71
+ let inMembersTable = false;
72
+ for (const line of lines) {
73
+ if (line.match(/^##\s+(Members|Team Roster)/i)) {
74
+ inMembersTable = true;
75
+ continue;
76
+ }
77
+ if (inMembersTable && line.startsWith('## ')) {
78
+ break;
79
+ }
80
+ if (inMembersTable && line.startsWith('|') && !line.includes('---') && !line.includes('Name')) {
81
+ const cells = line.split('|').map(c => c.trim()).filter(Boolean);
82
+ if (cells.length >= 2 && cells[0] !== 'Scribe') {
83
+ members.push({
84
+ name: cells[0],
85
+ role: cells[1]
86
+ });
87
+ }
88
+ }
89
+ }
90
+
91
+ // Read routing rules — check .squad/ first, fall back to .ai-team/
92
+ let routingFile = '.squad/routing.md';
93
+ if (!fs.existsSync(routingFile)) {
94
+ routingFile = '.ai-team/routing.md';
95
+ }
96
+ let routingContent = '';
97
+ if (fs.existsSync(routingFile)) {
98
+ routingContent = fs.readFileSync(routingFile, 'utf8');
99
+ }
100
+
101
+ // Find the Lead
102
+ const lead = members.find(m =>
103
+ m.role.toLowerCase().includes('lead') ||
104
+ m.role.toLowerCase().includes('architect') ||
105
+ m.role.toLowerCase().includes('coordinator')
106
+ );
107
+
108
+ if (!lead) {
109
+ core.warning('No Lead role found in team roster — cannot triage');
110
+ return;
111
+ }
112
+
113
+ // Build triage context
114
+ const memberList = members.map(m =>
115
+ `- **${m.name}** (${m.role}) → label: \`squad:${m.name.toLowerCase()}\``
116
+ ).join('\n');
117
+
118
+ // Determine best assignee based on issue content and routing
119
+ const issueText = `${issue.title}\n${issue.body || ''}`.toLowerCase();
120
+
121
+ let assignedMember = null;
122
+ let triageReason = '';
123
+ let copilotTier = null;
124
+
125
+ // First, evaluate @copilot fit if enabled
126
+ if (hasCopilot) {
127
+ const isNotSuitable = notSuitableKeywords.some(kw => issueText.includes(kw));
128
+ const isGoodFit = !isNotSuitable && goodFitKeywords.some(kw => issueText.includes(kw));
129
+ const isNeedsReview = !isNotSuitable && !isGoodFit && needsReviewKeywords.some(kw => issueText.includes(kw));
130
+
131
+ if (isGoodFit) {
132
+ copilotTier = 'good-fit';
133
+ assignedMember = { name: '@copilot', role: 'Coding Agent' };
134
+ triageReason = '🟢 Good fit for @copilot — matches capability profile';
135
+ } else if (isNeedsReview) {
136
+ copilotTier = 'needs-review';
137
+ assignedMember = { name: '@copilot', role: 'Coding Agent' };
138
+ triageReason = '🟡 Routing to @copilot (needs review) — a squad member should review the PR';
139
+ } else if (isNotSuitable) {
140
+ copilotTier = 'not-suitable';
141
+ // Fall through to normal routing
142
+ }
143
+ }
144
+
145
+ // If not routed to @copilot, use keyword-based routing
146
+ if (!assignedMember) {
147
+ for (const member of members) {
148
+ const role = member.role.toLowerCase();
149
+ if ((role.includes('frontend') || role.includes('ui')) &&
150
+ (issueText.includes('ui') || issueText.includes('frontend') ||
151
+ issueText.includes('css') || issueText.includes('component') ||
152
+ issueText.includes('button') || issueText.includes('page') ||
153
+ issueText.includes('layout') || issueText.includes('design'))) {
154
+ assignedMember = member;
155
+ triageReason = 'Issue relates to frontend/UI work';
156
+ break;
157
+ }
158
+ if ((role.includes('backend') || role.includes('api') || role.includes('server')) &&
159
+ (issueText.includes('api') || issueText.includes('backend') ||
160
+ issueText.includes('database') || issueText.includes('endpoint') ||
161
+ issueText.includes('server') || issueText.includes('auth'))) {
162
+ assignedMember = member;
163
+ triageReason = 'Issue relates to backend/API work';
164
+ break;
165
+ }
166
+ if ((role.includes('test') || role.includes('qa') || role.includes('quality')) &&
167
+ (issueText.includes('test') || issueText.includes('bug') ||
168
+ issueText.includes('fix') || issueText.includes('regression') ||
169
+ issueText.includes('coverage'))) {
170
+ assignedMember = member;
171
+ triageReason = 'Issue relates to testing/quality work';
172
+ break;
173
+ }
174
+ if ((role.includes('devops') || role.includes('infra') || role.includes('ops')) &&
175
+ (issueText.includes('deploy') || issueText.includes('ci') ||
176
+ issueText.includes('pipeline') || issueText.includes('docker') ||
177
+ issueText.includes('infrastructure'))) {
178
+ assignedMember = member;
179
+ triageReason = 'Issue relates to DevOps/infrastructure work';
180
+ break;
181
+ }
182
+ }
183
+ }
184
+
185
+ // Default to Lead if no routing match
186
+ if (!assignedMember) {
187
+ assignedMember = lead;
188
+ triageReason = 'No specific domain match — assigned to Lead for further analysis';
189
+ }
190
+
191
+ const isCopilot = assignedMember.name === '@copilot';
192
+ const assignLabel = isCopilot ? 'squad:copilot' : `squad:${assignedMember.name.toLowerCase()}`;
193
+
194
+ // Add the member-specific label
195
+ await github.rest.issues.addLabels({
196
+ owner: context.repo.owner,
197
+ repo: context.repo.repo,
198
+ issue_number: issue.number,
199
+ labels: [assignLabel]
200
+ });
201
+
202
+ // Apply default triage verdict
203
+ await github.rest.issues.addLabels({
204
+ owner: context.repo.owner,
205
+ repo: context.repo.repo,
206
+ issue_number: issue.number,
207
+ labels: ['go:needs-research']
208
+ });
209
+
210
+ // Auto-assign @copilot if enabled
211
+ if (isCopilot && copilotAutoAssign) {
212
+ try {
213
+ await github.rest.issues.addAssignees({
214
+ owner: context.repo.owner,
215
+ repo: context.repo.repo,
216
+ issue_number: issue.number,
217
+ assignees: ['copilot']
218
+ });
219
+ } catch (err) {
220
+ core.warning(`Could not auto-assign @copilot: ${err.message}`);
221
+ }
222
+ }
223
+
224
+ // Build copilot evaluation note
225
+ let copilotNote = '';
226
+ if (hasCopilot && !isCopilot) {
227
+ if (copilotTier === 'not-suitable') {
228
+ copilotNote = `\n\n**@copilot evaluation:** 🔴 Not suitable — issue involves work outside the coding agent's capability profile.`;
229
+ } else {
230
+ copilotNote = `\n\n**@copilot evaluation:** No strong capability match — routed to squad member.`;
231
+ }
232
+ }
233
+
234
+ // Post triage comment
235
+ const comment = [
236
+ `### 🏗️ Squad Triage — ${lead.name} (${lead.role})`,
237
+ '',
238
+ `**Issue:** #${issue.number} — ${issue.title}`,
239
+ `**Assigned to:** ${assignedMember.name} (${assignedMember.role})`,
240
+ `**Reason:** ${triageReason}`,
241
+ copilotTier === 'needs-review' ? `\n⚠️ **PR review recommended** — a squad member should review @copilot's work on this one.` : '',
242
+ copilotNote,
243
+ '',
244
+ `---`,
245
+ '',
246
+ `**Team roster:**`,
247
+ memberList,
248
+ hasCopilot ? `- **@copilot** (Coding Agent) → label: \`squad:copilot\`` : '',
249
+ '',
250
+ `> To reassign, remove the current \`squad:*\` label and add the correct one.`,
251
+ ].filter(Boolean).join('\n');
252
+
253
+ await github.rest.issues.createComment({
254
+ owner: context.repo.owner,
255
+ repo: context.repo.repo,
256
+ issue_number: issue.number,
257
+ body: comment
258
+ });
259
+
260
+ core.info(`Triaged issue #${issue.number} → ${assignedMember.name} (${assignLabel})`);