@bradygaster/squad-sdk 0.8.20 → 0.8.21

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 (105) hide show
  1. package/README.md +296 -296
  2. package/dist/adapter/client.js +1 -1
  3. package/dist/adapter/client.js.map +1 -1
  4. package/dist/agents/charter-compiler.d.ts +4 -0
  5. package/dist/agents/charter-compiler.d.ts.map +1 -1
  6. package/dist/agents/charter-compiler.js +8 -0
  7. package/dist/agents/charter-compiler.js.map +1 -1
  8. package/dist/agents/history-shadow.js +30 -30
  9. package/dist/agents/index.js +1 -1
  10. package/dist/agents/index.js.map +1 -1
  11. package/dist/agents/lifecycle.js +1 -1
  12. package/dist/agents/lifecycle.js.map +1 -1
  13. package/dist/build/github-dist.js +42 -42
  14. package/dist/builders/index.d.ts +156 -0
  15. package/dist/builders/index.d.ts.map +1 -0
  16. package/dist/builders/index.js +404 -0
  17. package/dist/builders/index.js.map +1 -0
  18. package/dist/builders/types.d.ts +187 -0
  19. package/dist/builders/types.d.ts.map +1 -0
  20. package/dist/builders/types.js +12 -0
  21. package/dist/builders/types.js.map +1 -0
  22. package/dist/config/init.d.ts +5 -21
  23. package/dist/config/init.d.ts.map +1 -1
  24. package/dist/config/init.js +270 -182
  25. package/dist/config/init.js.map +1 -1
  26. package/dist/coordinator/coordinator.js +1 -1
  27. package/dist/coordinator/coordinator.js.map +1 -1
  28. package/dist/coordinator/index.js +1 -1
  29. package/dist/coordinator/index.js.map +1 -1
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +3 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/runtime/otel-api.d.ts +38 -0
  35. package/dist/runtime/otel-api.d.ts.map +1 -0
  36. package/dist/runtime/otel-api.js +94 -0
  37. package/dist/runtime/otel-api.js.map +1 -0
  38. package/dist/runtime/otel-bridge.js +1 -1
  39. package/dist/runtime/otel-bridge.js.map +1 -1
  40. package/dist/runtime/otel.d.ts +1 -1
  41. package/dist/runtime/otel.d.ts.map +1 -1
  42. package/dist/runtime/otel.js +28 -12
  43. package/dist/runtime/otel.js.map +1 -1
  44. package/dist/runtime/squad-observer.js +1 -1
  45. package/dist/runtime/squad-observer.js.map +1 -1
  46. package/dist/sharing/consult.js +78 -78
  47. package/dist/streams/filter.d.ts +33 -0
  48. package/dist/streams/filter.d.ts.map +1 -0
  49. package/dist/streams/filter.js +29 -0
  50. package/dist/streams/filter.js.map +1 -0
  51. package/dist/streams/index.d.ts +9 -0
  52. package/dist/streams/index.d.ts.map +1 -0
  53. package/dist/streams/index.js +9 -0
  54. package/dist/streams/index.js.map +1 -0
  55. package/dist/streams/resolver.d.ts +40 -0
  56. package/dist/streams/resolver.d.ts.map +1 -0
  57. package/dist/streams/resolver.js +162 -0
  58. package/dist/streams/resolver.js.map +1 -0
  59. package/dist/streams/types.d.ts +44 -0
  60. package/dist/streams/types.d.ts.map +1 -0
  61. package/dist/streams/types.js +10 -0
  62. package/dist/streams/types.js.map +1 -0
  63. package/dist/tools/index.js +1 -1
  64. package/dist/tools/index.js.map +1 -1
  65. package/dist/types.d.ts +20 -0
  66. package/dist/types.d.ts.map +1 -1
  67. package/package.json +12 -11
  68. package/templates/casting-history.json +4 -4
  69. package/templates/casting-policy.json +35 -35
  70. package/templates/casting-registry.json +3 -3
  71. package/templates/ceremonies.md +41 -41
  72. package/templates/charter.md +53 -53
  73. package/templates/constraint-tracking.md +38 -38
  74. package/templates/copilot-instructions.md +46 -46
  75. package/templates/history.md +10 -10
  76. package/templates/identity/now.md +9 -9
  77. package/templates/identity/wisdom.md +15 -15
  78. package/templates/mcp-config.md +98 -98
  79. package/templates/multi-agent-format.md +28 -28
  80. package/templates/orchestration-log.md +27 -27
  81. package/templates/plugin-marketplace.md +49 -49
  82. package/templates/raw-agent-output.md +37 -37
  83. package/templates/roster.md +60 -60
  84. package/templates/routing.md +54 -54
  85. package/templates/run-output.md +50 -50
  86. package/templates/scribe-charter.md +119 -119
  87. package/templates/skill.md +24 -24
  88. package/templates/skills/project-conventions/SKILL.md +56 -56
  89. package/templates/squad.agent.md +1146 -1146
  90. package/templates/workflows/squad-ci.yml +24 -24
  91. package/templates/workflows/squad-docs.yml +50 -50
  92. package/templates/workflows/squad-heartbeat.yml +316 -316
  93. package/templates/workflows/squad-insider-release.yml +61 -61
  94. package/templates/workflows/squad-issue-assign.yml +161 -161
  95. package/templates/workflows/squad-label-enforce.yml +181 -181
  96. package/templates/workflows/squad-preview.yml +55 -55
  97. package/templates/workflows/squad-promote.yml +120 -120
  98. package/templates/workflows/squad-release.yml +77 -77
  99. package/templates/workflows/squad-triage.yml +260 -260
  100. package/templates/workflows/sync-squad-labels.yml +169 -169
  101. package/dist/runtime/event-bus-otel-bridge.d.ts +0 -19
  102. package/dist/runtime/event-bus-otel-bridge.d.ts.map +0 -1
  103. package/dist/runtime/event-bus-otel-bridge.js +0 -61
  104. package/dist/runtime/event-bus-otel-bridge.js.map +0 -1
  105. package/templates/workflows/squad-main-guard.yml +0 -129
@@ -1,169 +1,169 @@
1
- name: Sync Squad Labels
2
-
3
- on:
4
- push:
5
- paths:
6
- - '.squad/team.md'
7
- - '.ai-team/team.md'
8
- workflow_dispatch:
9
-
10
- permissions:
11
- issues: write
12
- contents: read
13
-
14
- jobs:
15
- sync-labels:
16
- runs-on: ubuntu-latest
17
- steps:
18
- - uses: actions/checkout@v4
19
-
20
- - name: Parse roster and sync labels
21
- uses: actions/github-script@v7
22
- with:
23
- script: |
24
- const fs = require('fs');
25
- let teamFile = '.squad/team.md';
26
- if (!fs.existsSync(teamFile)) {
27
- teamFile = '.ai-team/team.md';
28
- }
29
-
30
- if (!fs.existsSync(teamFile)) {
31
- core.info('No .squad/team.md or .ai-team/team.md found — skipping label sync');
32
- return;
33
- }
34
-
35
- const content = fs.readFileSync(teamFile, 'utf8');
36
- const lines = content.split('\n');
37
-
38
- // Parse the Members table for agent names
39
- const members = [];
40
- let inMembersTable = false;
41
- for (const line of lines) {
42
- if (line.match(/^##\s+(Members|Team Roster)/i)) {
43
- inMembersTable = true;
44
- continue;
45
- }
46
- if (inMembersTable && line.startsWith('## ')) {
47
- break;
48
- }
49
- if (inMembersTable && line.startsWith('|') && !line.includes('---') && !line.includes('Name')) {
50
- const cells = line.split('|').map(c => c.trim()).filter(Boolean);
51
- if (cells.length >= 2 && cells[0] !== 'Scribe') {
52
- members.push({
53
- name: cells[0],
54
- role: cells[1]
55
- });
56
- }
57
- }
58
- }
59
-
60
- core.info(`Found ${members.length} squad members: ${members.map(m => m.name).join(', ')}`);
61
-
62
- // Check if @copilot is on the team
63
- const hasCopilot = content.includes('🤖 Coding Agent');
64
-
65
- // Define label color palette for squad labels
66
- const SQUAD_COLOR = '9B8FCC';
67
- const MEMBER_COLOR = '9B8FCC';
68
- const COPILOT_COLOR = '10b981';
69
-
70
- // Define go: and release: labels (static)
71
- const GO_LABELS = [
72
- { name: 'go:yes', color: '0E8A16', description: 'Ready to implement' },
73
- { name: 'go:no', color: 'B60205', description: 'Not pursuing' },
74
- { name: 'go:needs-research', color: 'FBCA04', description: 'Needs investigation' }
75
- ];
76
-
77
- const RELEASE_LABELS = [
78
- { name: 'release:v0.4.0', color: '6B8EB5', description: 'Targeted for v0.4.0' },
79
- { name: 'release:v0.5.0', color: '6B8EB5', description: 'Targeted for v0.5.0' },
80
- { name: 'release:v0.6.0', color: '8B7DB5', description: 'Targeted for v0.6.0' },
81
- { name: 'release:v1.0.0', color: '8B7DB5', description: 'Targeted for v1.0.0' },
82
- { name: 'release:backlog', color: 'D4E5F7', description: 'Not yet targeted' }
83
- ];
84
-
85
- const TYPE_LABELS = [
86
- { name: 'type:feature', color: 'DDD1F2', description: 'New capability' },
87
- { name: 'type:bug', color: 'FF0422', description: 'Something broken' },
88
- { name: 'type:spike', color: 'F2DDD4', description: 'Research/investigation — produces a plan, not code' },
89
- { name: 'type:docs', color: 'D4E5F7', description: 'Documentation work' },
90
- { name: 'type:chore', color: 'D4E5F7', description: 'Maintenance, refactoring, cleanup' },
91
- { name: 'type:epic', color: 'CC4455', description: 'Parent issue that decomposes into sub-issues' }
92
- ];
93
-
94
- // High-signal labels — these MUST visually dominate all others
95
- const SIGNAL_LABELS = [
96
- { name: 'bug', color: 'FF0422', description: 'Something isn\'t working' },
97
- { name: 'feedback', color: '00E5FF', description: 'User feedback — high signal, needs attention' }
98
- ];
99
-
100
- const PRIORITY_LABELS = [
101
- { name: 'priority:p0', color: 'B60205', description: 'Blocking release' },
102
- { name: 'priority:p1', color: 'D93F0B', description: 'This sprint' },
103
- { name: 'priority:p2', color: 'FBCA04', description: 'Next sprint' }
104
- ];
105
-
106
- // Ensure the base "squad" triage label exists
107
- const labels = [
108
- { name: 'squad', color: SQUAD_COLOR, description: 'Squad triage inbox — Lead will assign to a member' }
109
- ];
110
-
111
- for (const member of members) {
112
- labels.push({
113
- name: `squad:${member.name.toLowerCase()}`,
114
- color: MEMBER_COLOR,
115
- description: `Assigned to ${member.name} (${member.role})`
116
- });
117
- }
118
-
119
- // Add @copilot label if coding agent is on the team
120
- if (hasCopilot) {
121
- labels.push({
122
- name: 'squad:copilot',
123
- color: COPILOT_COLOR,
124
- description: 'Assigned to @copilot (Coding Agent) for autonomous work'
125
- });
126
- }
127
-
128
- // Add go:, release:, type:, priority:, and high-signal labels
129
- labels.push(...GO_LABELS);
130
- labels.push(...RELEASE_LABELS);
131
- labels.push(...TYPE_LABELS);
132
- labels.push(...PRIORITY_LABELS);
133
- labels.push(...SIGNAL_LABELS);
134
-
135
- // Sync labels (create or update)
136
- for (const label of labels) {
137
- try {
138
- await github.rest.issues.getLabel({
139
- owner: context.repo.owner,
140
- repo: context.repo.repo,
141
- name: label.name
142
- });
143
- // Label exists — update it
144
- await github.rest.issues.updateLabel({
145
- owner: context.repo.owner,
146
- repo: context.repo.repo,
147
- name: label.name,
148
- color: label.color,
149
- description: label.description
150
- });
151
- core.info(`Updated label: ${label.name}`);
152
- } catch (err) {
153
- if (err.status === 404) {
154
- // Label doesn't exist — create it
155
- await github.rest.issues.createLabel({
156
- owner: context.repo.owner,
157
- repo: context.repo.repo,
158
- name: label.name,
159
- color: label.color,
160
- description: label.description
161
- });
162
- core.info(`Created label: ${label.name}`);
163
- } else {
164
- throw err;
165
- }
166
- }
167
- }
168
-
169
- core.info(`Label sync complete: ${labels.length} labels synced`);
1
+ name: Sync Squad Labels
2
+
3
+ on:
4
+ push:
5
+ paths:
6
+ - '.squad/team.md'
7
+ - '.ai-team/team.md'
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ issues: write
12
+ contents: read
13
+
14
+ jobs:
15
+ sync-labels:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Parse roster and sync labels
21
+ uses: actions/github-script@v7
22
+ with:
23
+ script: |
24
+ const fs = require('fs');
25
+ let teamFile = '.squad/team.md';
26
+ if (!fs.existsSync(teamFile)) {
27
+ teamFile = '.ai-team/team.md';
28
+ }
29
+
30
+ if (!fs.existsSync(teamFile)) {
31
+ core.info('No .squad/team.md or .ai-team/team.md found — skipping label sync');
32
+ return;
33
+ }
34
+
35
+ const content = fs.readFileSync(teamFile, 'utf8');
36
+ const lines = content.split('\n');
37
+
38
+ // Parse the Members table for agent names
39
+ const members = [];
40
+ let inMembersTable = false;
41
+ for (const line of lines) {
42
+ if (line.match(/^##\s+(Members|Team Roster)/i)) {
43
+ inMembersTable = true;
44
+ continue;
45
+ }
46
+ if (inMembersTable && line.startsWith('## ')) {
47
+ break;
48
+ }
49
+ if (inMembersTable && line.startsWith('|') && !line.includes('---') && !line.includes('Name')) {
50
+ const cells = line.split('|').map(c => c.trim()).filter(Boolean);
51
+ if (cells.length >= 2 && cells[0] !== 'Scribe') {
52
+ members.push({
53
+ name: cells[0],
54
+ role: cells[1]
55
+ });
56
+ }
57
+ }
58
+ }
59
+
60
+ core.info(`Found ${members.length} squad members: ${members.map(m => m.name).join(', ')}`);
61
+
62
+ // Check if @copilot is on the team
63
+ const hasCopilot = content.includes('🤖 Coding Agent');
64
+
65
+ // Define label color palette for squad labels
66
+ const SQUAD_COLOR = '9B8FCC';
67
+ const MEMBER_COLOR = '9B8FCC';
68
+ const COPILOT_COLOR = '10b981';
69
+
70
+ // Define go: and release: labels (static)
71
+ const GO_LABELS = [
72
+ { name: 'go:yes', color: '0E8A16', description: 'Ready to implement' },
73
+ { name: 'go:no', color: 'B60205', description: 'Not pursuing' },
74
+ { name: 'go:needs-research', color: 'FBCA04', description: 'Needs investigation' }
75
+ ];
76
+
77
+ const RELEASE_LABELS = [
78
+ { name: 'release:v0.4.0', color: '6B8EB5', description: 'Targeted for v0.4.0' },
79
+ { name: 'release:v0.5.0', color: '6B8EB5', description: 'Targeted for v0.5.0' },
80
+ { name: 'release:v0.6.0', color: '8B7DB5', description: 'Targeted for v0.6.0' },
81
+ { name: 'release:v1.0.0', color: '8B7DB5', description: 'Targeted for v1.0.0' },
82
+ { name: 'release:backlog', color: 'D4E5F7', description: 'Not yet targeted' }
83
+ ];
84
+
85
+ const TYPE_LABELS = [
86
+ { name: 'type:feature', color: 'DDD1F2', description: 'New capability' },
87
+ { name: 'type:bug', color: 'FF0422', description: 'Something broken' },
88
+ { name: 'type:spike', color: 'F2DDD4', description: 'Research/investigation — produces a plan, not code' },
89
+ { name: 'type:docs', color: 'D4E5F7', description: 'Documentation work' },
90
+ { name: 'type:chore', color: 'D4E5F7', description: 'Maintenance, refactoring, cleanup' },
91
+ { name: 'type:epic', color: 'CC4455', description: 'Parent issue that decomposes into sub-issues' }
92
+ ];
93
+
94
+ // High-signal labels — these MUST visually dominate all others
95
+ const SIGNAL_LABELS = [
96
+ { name: 'bug', color: 'FF0422', description: 'Something isn\'t working' },
97
+ { name: 'feedback', color: '00E5FF', description: 'User feedback — high signal, needs attention' }
98
+ ];
99
+
100
+ const PRIORITY_LABELS = [
101
+ { name: 'priority:p0', color: 'B60205', description: 'Blocking release' },
102
+ { name: 'priority:p1', color: 'D93F0B', description: 'This sprint' },
103
+ { name: 'priority:p2', color: 'FBCA04', description: 'Next sprint' }
104
+ ];
105
+
106
+ // Ensure the base "squad" triage label exists
107
+ const labels = [
108
+ { name: 'squad', color: SQUAD_COLOR, description: 'Squad triage inbox — Lead will assign to a member' }
109
+ ];
110
+
111
+ for (const member of members) {
112
+ labels.push({
113
+ name: `squad:${member.name.toLowerCase()}`,
114
+ color: MEMBER_COLOR,
115
+ description: `Assigned to ${member.name} (${member.role})`
116
+ });
117
+ }
118
+
119
+ // Add @copilot label if coding agent is on the team
120
+ if (hasCopilot) {
121
+ labels.push({
122
+ name: 'squad:copilot',
123
+ color: COPILOT_COLOR,
124
+ description: 'Assigned to @copilot (Coding Agent) for autonomous work'
125
+ });
126
+ }
127
+
128
+ // Add go:, release:, type:, priority:, and high-signal labels
129
+ labels.push(...GO_LABELS);
130
+ labels.push(...RELEASE_LABELS);
131
+ labels.push(...TYPE_LABELS);
132
+ labels.push(...PRIORITY_LABELS);
133
+ labels.push(...SIGNAL_LABELS);
134
+
135
+ // Sync labels (create or update)
136
+ for (const label of labels) {
137
+ try {
138
+ await github.rest.issues.getLabel({
139
+ owner: context.repo.owner,
140
+ repo: context.repo.repo,
141
+ name: label.name
142
+ });
143
+ // Label exists — update it
144
+ await github.rest.issues.updateLabel({
145
+ owner: context.repo.owner,
146
+ repo: context.repo.repo,
147
+ name: label.name,
148
+ color: label.color,
149
+ description: label.description
150
+ });
151
+ core.info(`Updated label: ${label.name}`);
152
+ } catch (err) {
153
+ if (err.status === 404) {
154
+ // Label doesn't exist — create it
155
+ await github.rest.issues.createLabel({
156
+ owner: context.repo.owner,
157
+ repo: context.repo.repo,
158
+ name: label.name,
159
+ color: label.color,
160
+ description: label.description
161
+ });
162
+ core.info(`Created label: ${label.name}`);
163
+ } else {
164
+ throw err;
165
+ }
166
+ }
167
+ }
168
+
169
+ core.info(`Label sync complete: ${labels.length} labels synced`);
@@ -1,19 +0,0 @@
1
- /**
2
- * EventBus → OTel Span Bridge (Issue #304)
3
- *
4
- * Subscribes to EventBus events and creates OpenTelemetry spans for each.
5
- * Complements the existing otel-bridge.ts which handles TelemetryEvent
6
- * (dot-separated names like squad.init). This bridge handles runtime
7
- * SquadEvent types (colon-separated like session:created).
8
- *
9
- * @module runtime/event-bus-otel-bridge
10
- */
11
- import type { EventBus, UnsubscribeFn } from './event-bus.js';
12
- /**
13
- * Attach an OTel span bridge to an EventBus.
14
- * Every event emitted on the bus produces a corresponding OTel span.
15
- *
16
- * @returns Unsubscribe function to detach the bridge.
17
- */
18
- export declare function attachOTelBridge(bus: EventBus): UnsubscribeFn;
19
- //# sourceMappingURL=event-bus-otel-bridge.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-bus-otel-bridge.d.ts","sourceRoot":"","sources":["../../src/runtime/event-bus-otel-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAc,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG1E;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,aAAa,CAuC7D"}
@@ -1,61 +0,0 @@
1
- /**
2
- * EventBus → OTel Span Bridge (Issue #304)
3
- *
4
- * Subscribes to EventBus events and creates OpenTelemetry spans for each.
5
- * Complements the existing otel-bridge.ts which handles TelemetryEvent
6
- * (dot-separated names like squad.init). This bridge handles runtime
7
- * SquadEvent types (colon-separated like session:created).
8
- *
9
- * @module runtime/event-bus-otel-bridge
10
- */
11
- import { SpanStatusCode } from '@opentelemetry/api';
12
- import { getTracer } from './otel.js';
13
- import { isSquadEventOfType } from './event-payloads.js';
14
- /**
15
- * Attach an OTel span bridge to an EventBus.
16
- * Every event emitted on the bus produces a corresponding OTel span.
17
- *
18
- * @returns Unsubscribe function to detach the bridge.
19
- */
20
- export function attachOTelBridge(bus) {
21
- return bus.subscribeAll((event) => {
22
- const tracer = getTracer('squad-sdk');
23
- const spanName = `squad.event.${event.type.replace(':', '.')}`;
24
- const attrs = {
25
- 'squad.event.type': event.type,
26
- };
27
- if (event.sessionId)
28
- attrs['squad.session.id'] = event.sessionId;
29
- if (event.agentName)
30
- attrs['squad.agent.name'] = event.agentName;
31
- // Extract payload attributes for known event types
32
- if (isSquadEventOfType(event, 'session:error')) {
33
- attrs['squad.error'] = event.payload.error;
34
- attrs['squad.error.agent'] = event.payload.agentName;
35
- }
36
- else if (isSquadEventOfType(event, 'session:tool_call')) {
37
- attrs['squad.tool.name'] = event.payload.toolName;
38
- if (event.payload.resultType) {
39
- attrs['squad.tool.result'] = event.payload.resultType;
40
- }
41
- }
42
- else if (isSquadEventOfType(event, 'coordinator:routing')) {
43
- attrs['squad.routing.phase'] = event.payload.phase;
44
- }
45
- else if (isSquadEventOfType(event, 'agent:milestone')) {
46
- attrs['squad.milestone.event'] = event.payload.event;
47
- attrs['squad.milestone.agent'] = event.payload.agentName;
48
- }
49
- else if (isSquadEventOfType(event, 'pool:health')) {
50
- attrs['squad.pool.active'] = event.payload.activeSessions;
51
- attrs['squad.pool.available'] = event.payload.availableSlots;
52
- attrs['squad.pool.queued'] = event.payload.queuedRequests;
53
- }
54
- const span = tracer.startSpan(spanName, { attributes: attrs });
55
- if (event.type === 'session:error') {
56
- span.setStatus({ code: SpanStatusCode.ERROR });
57
- }
58
- span.end();
59
- });
60
- }
61
- //# sourceMappingURL=event-bus-otel-bridge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-bus-otel-bridge.js","sourceRoot":"","sources":["../../src/runtime/event-bus-otel-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAa;IAC5C,OAAO,GAAG,CAAC,YAAY,CAAC,CAAC,KAAiB,EAAE,EAAE;QAC5C,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,eAAe,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;QAE/D,MAAM,KAAK,GAA8C;YACvD,kBAAkB,EAAE,KAAK,CAAC,IAAI;SAC/B,CAAC;QACF,IAAI,KAAK,CAAC,SAAS;YAAE,KAAK,CAAC,kBAAkB,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC;QACjE,IAAI,KAAK,CAAC,SAAS;YAAE,KAAK,CAAC,kBAAkB,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC;QAEjE,mDAAmD;QACnD,IAAI,kBAAkB,CAAC,KAAK,EAAE,eAAe,CAAC,EAAE,CAAC;YAC/C,KAAK,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAC3C,KAAK,CAAC,mBAAmB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;QACvD,CAAC;aAAM,IAAI,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,CAAC,EAAE,CAAC;YAC1D,KAAK,CAAC,iBAAiB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YAClD,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;gBAC7B,KAAK,CAAC,mBAAmB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;YACxD,CAAC;QACH,CAAC;aAAM,IAAI,kBAAkB,CAAC,KAAK,EAAE,qBAAqB,CAAC,EAAE,CAAC;YAC5D,KAAK,CAAC,qBAAqB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrD,CAAC;aAAM,IAAI,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,CAAC,EAAE,CAAC;YACxD,KAAK,CAAC,uBAAuB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACrD,KAAK,CAAC,uBAAuB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;QAC3D,CAAC;aAAM,IAAI,kBAAkB,CAAC,KAAK,EAAE,aAAa,CAAC,EAAE,CAAC;YACpD,KAAK,CAAC,mBAAmB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;YAC1D,KAAK,CAAC,sBAAsB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;YAC7D,KAAK,CAAC,mBAAmB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;QAC5D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QAE/D,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACnC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,CAAC,GAAG,EAAE,CAAC;IACb,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1,129 +0,0 @@
1
- name: Squad Protected Branch Guard
2
-
3
- on:
4
- pull_request:
5
- branches: [main, preview, insider]
6
- types: [opened, synchronize, reopened]
7
- push:
8
- branches: [main, preview, insider]
9
-
10
- permissions:
11
- contents: read
12
- pull-requests: read
13
-
14
- jobs:
15
- guard:
16
- runs-on: ubuntu-latest
17
- steps:
18
- - uses: actions/checkout@v4
19
-
20
- - name: Check for forbidden paths
21
- uses: actions/github-script@v7
22
- with:
23
- script: |
24
- // Fetch all files changed - handles both PR and push events
25
- let files = [];
26
-
27
- if (context.eventName === 'pull_request') {
28
- // PR event: use pulls.listFiles API
29
- let page = 1;
30
- while (true) {
31
- const resp = await github.rest.pulls.listFiles({
32
- owner: context.repo.owner,
33
- repo: context.repo.repo,
34
- pull_number: context.payload.pull_request.number,
35
- per_page: 100,
36
- page
37
- });
38
- files.push(...resp.data);
39
- if (resp.data.length < 100) break;
40
- page++;
41
- }
42
- } else if (context.eventName === 'push') {
43
- // Push event: compare against base branch
44
- const base = context.payload.before;
45
- const head = context.payload.after;
46
-
47
- // If this is not a force push and base exists, compare commits
48
- if (base && base !== '0000000000000000000000000000000000000000') {
49
- const comparison = await github.rest.repos.compareCommits({
50
- owner: context.repo.owner,
51
- repo: context.repo.repo,
52
- base,
53
- head
54
- });
55
- files = comparison.data.files || [];
56
- } else {
57
- // Force push or initial commit: list all files in the current tree
58
- core.info('Force push detected or initial commit, checking tree state');
59
- const { data: tree } = await github.rest.git.getTree({
60
- owner: context.repo.owner,
61
- repo: context.repo.repo,
62
- tree_sha: head,
63
- recursive: 'true'
64
- });
65
- files = tree.tree
66
- .filter(item => item.type === 'blob')
67
- .map(item => ({ filename: item.path, status: 'added' }));
68
- }
69
- }
70
-
71
- // Check each file against forbidden path rules
72
- // Allow removals — deleting forbidden files from protected branches is fine
73
- const forbidden = files
74
- .filter(f => f.status !== 'removed')
75
- .map(f => f.filename)
76
- .filter(f => {
77
- // .ai-team/** and .squad/** — ALL team state files, zero exceptions
78
- if (f === '.ai-team' || f.startsWith('.ai-team/') || f === '.squad' || f.startsWith('.squad/')) return true;
79
- // .ai-team-templates/** — Squad's own templates, stay on dev
80
- if (f === '.ai-team-templates' || f.startsWith('.ai-team-templates/')) return true;
81
- // team-docs/** — ALL internal team docs, zero exceptions
82
- if (f.startsWith('team-docs/')) return true;
83
- // docs/proposals/** — internal design proposals, stay on dev
84
- if (f.startsWith('docs/proposals/')) return true;
85
- return false;
86
- });
87
-
88
- if (forbidden.length === 0) {
89
- core.info('✅ No forbidden paths found in PR — all clear.');
90
- return;
91
- }
92
-
93
- // Build a clear, actionable error message
94
- const lines = [
95
- '## 🚫 Forbidden files detected in PR to main',
96
- '',
97
- 'The following files must NOT be merged into `main`.',
98
- '`.ai-team/` and `.squad/` are runtime team state — they belong on dev branches only.',
99
- '`.ai-team-templates/` is Squad\'s internal planning — it belongs on dev branches only.',
100
- '`team-docs/` is internal team content — it belongs on dev branches only.',
101
- '`docs/proposals/` is internal design proposals — it belongs on dev branches only.',
102
- '',
103
- '### Forbidden files found:',
104
- '',
105
- ...forbidden.map(f => `- \`${f}\``),
106
- '',
107
- '### How to fix:',
108
- '',
109
- '```bash',
110
- '# Remove tracked .ai-team/ files (keeps local copies):',
111
- 'git rm --cached -r .ai-team/',
112
- '',
113
- '# Remove tracked .squad/ files (keeps local copies):',
114
- 'git rm --cached -r .squad/',
115
- '',
116
- '# Remove tracked team-docs/ files:',
117
- 'git rm --cached -r team-docs/',
118
- '',
119
- '# Commit the removal and push:',
120
- 'git commit -m "chore: remove forbidden paths from PR"',
121
- 'git push',
122
- '```',
123
- '',
124
- '> ⚠️ `.ai-team/` and `.squad/` are committed on `dev` and feature branches by design.',
125
- '> The guard workflow is the enforcement mechanism that keeps these files off `main` and `preview`.',
126
- '> `git rm --cached` untracks them from this PR without deleting your local copies.',
127
- ];
128
-
129
- core.setFailed(lines.join('\n'));