shipwright-cli 2.3.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +3 -3
  2. package/dashboard/public/index.html +1 -1
  3. package/dashboard/src/core/api.test.ts +362 -0
  4. package/dashboard/src/core/router.test.ts +266 -0
  5. package/dashboard/src/core/state.test.ts +235 -0
  6. package/dashboard/src/core/ws.test.ts +216 -0
  7. package/dashboard/src/design/icons.test.ts +105 -0
  8. package/dashboard/src/design/tokens.test.ts +204 -0
  9. package/dashboard/tsconfig.json +1 -1
  10. package/dashboard/vitest.config.ts +27 -0
  11. package/package.json +10 -3
  12. package/scripts/lib/pipeline-stages.sh +59 -0
  13. package/scripts/sw +1 -1
  14. package/scripts/sw-activity.sh +1 -1
  15. package/scripts/sw-adaptive.sh +1 -1
  16. package/scripts/sw-adversarial.sh +1 -1
  17. package/scripts/sw-architecture-enforcer.sh +1 -1
  18. package/scripts/sw-auth.sh +1 -1
  19. package/scripts/sw-autonomous.sh +230 -13
  20. package/scripts/sw-changelog.sh +1 -1
  21. package/scripts/sw-checkpoint.sh +1 -1
  22. package/scripts/sw-ci.sh +1 -1
  23. package/scripts/sw-cleanup.sh +1 -1
  24. package/scripts/sw-code-review.sh +1 -1
  25. package/scripts/sw-connect.sh +1 -1
  26. package/scripts/sw-context.sh +1 -1
  27. package/scripts/sw-cost.sh +1 -1
  28. package/scripts/sw-daemon.sh +1 -1
  29. package/scripts/sw-dashboard.sh +1 -1
  30. package/scripts/sw-db.sh +1 -1
  31. package/scripts/sw-decompose.sh +1 -1
  32. package/scripts/sw-deps.sh +1 -1
  33. package/scripts/sw-developer-simulation.sh +1 -1
  34. package/scripts/sw-discovery.sh +1 -1
  35. package/scripts/sw-doc-fleet.sh +1 -1
  36. package/scripts/sw-docs-agent.sh +1 -1
  37. package/scripts/sw-docs.sh +1 -1
  38. package/scripts/sw-doctor.sh +1 -1
  39. package/scripts/sw-dora.sh +1 -1
  40. package/scripts/sw-durable.sh +1 -1
  41. package/scripts/sw-e2e-orchestrator.sh +1 -1
  42. package/scripts/sw-eventbus.sh +1 -1
  43. package/scripts/sw-feedback.sh +1 -1
  44. package/scripts/sw-fix.sh +1 -1
  45. package/scripts/sw-fleet-discover.sh +1 -1
  46. package/scripts/sw-fleet-viz.sh +1 -1
  47. package/scripts/sw-fleet.sh +1 -1
  48. package/scripts/sw-github-app.sh +1 -1
  49. package/scripts/sw-github-checks.sh +1 -1
  50. package/scripts/sw-github-deploy.sh +1 -1
  51. package/scripts/sw-github-graphql.sh +1 -1
  52. package/scripts/sw-guild.sh +1 -1
  53. package/scripts/sw-heartbeat.sh +1 -1
  54. package/scripts/sw-hygiene.sh +1 -1
  55. package/scripts/sw-incident.sh +1 -1
  56. package/scripts/sw-init.sh +1 -1
  57. package/scripts/sw-instrument.sh +1 -1
  58. package/scripts/sw-intelligence.sh +1 -1
  59. package/scripts/sw-jira.sh +1 -1
  60. package/scripts/sw-launchd.sh +1 -1
  61. package/scripts/sw-linear.sh +1 -1
  62. package/scripts/sw-logs.sh +1 -1
  63. package/scripts/sw-loop.sh +1 -1
  64. package/scripts/sw-memory.sh +1 -1
  65. package/scripts/sw-mission-control.sh +1 -1
  66. package/scripts/sw-model-router.sh +1 -1
  67. package/scripts/sw-otel.sh +1 -1
  68. package/scripts/sw-oversight.sh +1 -1
  69. package/scripts/sw-pipeline-composer.sh +1 -1
  70. package/scripts/sw-pipeline-vitals.sh +1 -1
  71. package/scripts/sw-pipeline.sh +1 -1
  72. package/scripts/sw-pm.sh +1 -1
  73. package/scripts/sw-pr-lifecycle.sh +1 -1
  74. package/scripts/sw-predictive.sh +1 -1
  75. package/scripts/sw-prep.sh +1 -1
  76. package/scripts/sw-ps.sh +1 -1
  77. package/scripts/sw-public-dashboard.sh +1 -1
  78. package/scripts/sw-quality.sh +1 -1
  79. package/scripts/sw-reaper.sh +1 -1
  80. package/scripts/sw-regression.sh +1 -1
  81. package/scripts/sw-release-manager.sh +1 -1
  82. package/scripts/sw-release.sh +1 -1
  83. package/scripts/sw-remote.sh +1 -1
  84. package/scripts/sw-replay.sh +1 -1
  85. package/scripts/sw-retro.sh +4 -1
  86. package/scripts/sw-scale.sh +1 -1
  87. package/scripts/sw-security-audit.sh +1 -1
  88. package/scripts/sw-self-optimize.sh +99 -1
  89. package/scripts/sw-session.sh +1 -1
  90. package/scripts/sw-setup.sh +1 -1
  91. package/scripts/sw-standup.sh +1 -1
  92. package/scripts/sw-status.sh +1 -1
  93. package/scripts/sw-strategic.sh +1 -1
  94. package/scripts/sw-stream.sh +1 -1
  95. package/scripts/sw-swarm.sh +1 -1
  96. package/scripts/sw-team-stages.sh +1 -1
  97. package/scripts/sw-templates.sh +1 -1
  98. package/scripts/sw-testgen.sh +1 -1
  99. package/scripts/sw-tmux-pipeline.sh +1 -1
  100. package/scripts/sw-tmux.sh +1 -1
  101. package/scripts/sw-trace.sh +1 -1
  102. package/scripts/sw-tracker.sh +1 -1
  103. package/scripts/sw-triage.sh +198 -11
  104. package/scripts/sw-upgrade.sh +1 -1
  105. package/scripts/sw-ux.sh +1 -1
  106. package/scripts/sw-webhook.sh +1 -1
  107. package/scripts/sw-widgets.sh +1 -1
  108. package/scripts/sw-worktree.sh +1 -1
@@ -0,0 +1,204 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ colors,
4
+ fonts,
5
+ typeScale,
6
+ spacing,
7
+ radius,
8
+ shadows,
9
+ duration,
10
+ easing,
11
+ zIndex,
12
+ STAGES,
13
+ STAGE_SHORT,
14
+ STAGE_COLORS,
15
+ STAGE_HEX,
16
+ } from "./tokens";
17
+ import type { StageName } from "./tokens";
18
+
19
+ describe("Design Tokens", () => {
20
+ describe("colors", () => {
21
+ it("has all background colors", () => {
22
+ expect(colors.bg).toHaveProperty("abyss");
23
+ expect(colors.bg).toHaveProperty("deep");
24
+ expect(colors.bg).toHaveProperty("ocean");
25
+ expect(colors.bg).toHaveProperty("surface");
26
+ expect(colors.bg).toHaveProperty("foam");
27
+ });
28
+
29
+ it("has accent colors as valid hex/rgba", () => {
30
+ expect(colors.accent.cyan).toMatch(/^#[0-9a-f]{6}$/i);
31
+ expect(colors.accent.purple).toMatch(/^#[0-9a-f]{6}$/i);
32
+ expect(colors.accent.blue).toMatch(/^#[0-9a-f]{6}$/i);
33
+ expect(colors.accent.cyanGlow).toMatch(/^rgba\(/);
34
+ });
35
+
36
+ it("has semantic colors", () => {
37
+ expect(colors.semantic.success).toMatch(/^#/);
38
+ expect(colors.semantic.warning).toMatch(/^#/);
39
+ expect(colors.semantic.error).toMatch(/^#/);
40
+ });
41
+
42
+ it("has text colors", () => {
43
+ expect(colors.text.primary).toMatch(/^#/);
44
+ expect(colors.text.secondary).toMatch(/^#/);
45
+ expect(colors.text.muted).toMatch(/^#/);
46
+ });
47
+ });
48
+
49
+ describe("fonts", () => {
50
+ it("defines display, body, and mono font stacks", () => {
51
+ expect(fonts.display).toContain("serif");
52
+ expect(fonts.body).toContain("sans-serif");
53
+ expect(fonts.mono).toContain("monospace");
54
+ });
55
+ });
56
+
57
+ describe("typeScale", () => {
58
+ it("has all scale levels", () => {
59
+ const levels = [
60
+ "display",
61
+ "heading",
62
+ "title",
63
+ "body",
64
+ "caption",
65
+ "tiny",
66
+ "mono",
67
+ "monoSm",
68
+ ];
69
+ for (const level of levels) {
70
+ const entry = typeScale[level as keyof typeof typeScale];
71
+ expect(entry).toHaveProperty("size");
72
+ expect(entry).toHaveProperty("weight");
73
+ expect(entry).toHaveProperty("family");
74
+ expect(entry.size).toBeGreaterThan(0);
75
+ }
76
+ });
77
+
78
+ it("sizes are ordered display > heading > title > body > caption > tiny", () => {
79
+ expect(typeScale.display.size).toBeGreaterThan(typeScale.heading.size);
80
+ expect(typeScale.heading.size).toBeGreaterThan(typeScale.title.size);
81
+ expect(typeScale.title.size).toBeGreaterThan(typeScale.body.size);
82
+ expect(typeScale.body.size).toBeGreaterThan(typeScale.caption.size);
83
+ expect(typeScale.caption.size).toBeGreaterThan(typeScale.tiny.size);
84
+ });
85
+ });
86
+
87
+ describe("spacing", () => {
88
+ it("defines a spacing scale", () => {
89
+ expect(spacing[0]).toBe(0);
90
+ expect(spacing[1]).toBe(4);
91
+ expect(spacing[2]).toBe(8);
92
+ expect(spacing[4]).toBe(16);
93
+ expect(spacing[8]).toBe(32);
94
+ });
95
+
96
+ it("has increasing values", () => {
97
+ const keys = Object.keys(spacing)
98
+ .map(Number)
99
+ .sort((a, b) => a - b);
100
+ for (let i = 1; i < keys.length; i++) {
101
+ expect(spacing[keys[i]]).toBeGreaterThanOrEqual(spacing[keys[i - 1]]);
102
+ }
103
+ });
104
+ });
105
+
106
+ describe("radius", () => {
107
+ it("has sm < md < lg < xl < full", () => {
108
+ expect(radius.sm).toBeLessThan(radius.md);
109
+ expect(radius.md).toBeLessThan(radius.lg);
110
+ expect(radius.lg).toBeLessThan(radius.xl);
111
+ expect(radius.xl).toBeLessThan(radius.full);
112
+ });
113
+ });
114
+
115
+ describe("shadows", () => {
116
+ it("has glow shadows for each semantic color", () => {
117
+ expect(shadows.glow.cyan).toContain("rgba");
118
+ expect(shadows.glow.purple).toContain("rgba");
119
+ expect(shadows.glow.success).toContain("rgba");
120
+ expect(shadows.glow.error).toContain("rgba");
121
+ });
122
+
123
+ it("has an elevated shadow", () => {
124
+ expect(shadows.elevated).toContain("rgba");
125
+ });
126
+ });
127
+
128
+ describe("duration", () => {
129
+ it("has increasing animation durations", () => {
130
+ expect(duration.fast).toBeLessThan(duration.base);
131
+ expect(duration.base).toBeLessThan(duration.slow);
132
+ expect(duration.slow).toBeLessThan(duration.glacial);
133
+ });
134
+ });
135
+
136
+ describe("easing", () => {
137
+ it("defines easing curves", () => {
138
+ expect(easing.default).toBe("ease");
139
+ expect(easing.smooth).toContain("cubic-bezier");
140
+ expect(easing.spring).toContain("cubic-bezier");
141
+ });
142
+ });
143
+
144
+ describe("zIndex", () => {
145
+ it("has increasing z-index values", () => {
146
+ expect(zIndex.base).toBeLessThan(zIndex.dropdown);
147
+ expect(zIndex.dropdown).toBeLessThan(zIndex.sticky);
148
+ expect(zIndex.sticky).toBeLessThan(zIndex.overlay);
149
+ expect(zIndex.overlay).toBeLessThan(zIndex.modal);
150
+ expect(zIndex.modal).toBeLessThan(zIndex.toast);
151
+ });
152
+ });
153
+
154
+ describe("STAGES", () => {
155
+ it("defines the pipeline stage sequence", () => {
156
+ expect(STAGES).toHaveLength(11);
157
+ expect(STAGES[0]).toBe("intake");
158
+ expect(STAGES[STAGES.length - 1]).toBe("monitor");
159
+ });
160
+
161
+ it("includes all expected stages", () => {
162
+ const expected = [
163
+ "intake",
164
+ "plan",
165
+ "design",
166
+ "build",
167
+ "test",
168
+ "review",
169
+ "compound_quality",
170
+ "pr",
171
+ "merge",
172
+ "deploy",
173
+ "monitor",
174
+ ];
175
+ expect([...STAGES]).toEqual(expected);
176
+ });
177
+ });
178
+
179
+ describe("STAGE_SHORT", () => {
180
+ it("maps every stage to a short code", () => {
181
+ for (const stage of STAGES) {
182
+ expect(STAGE_SHORT[stage]).toBeDefined();
183
+ expect(STAGE_SHORT[stage].length).toBeLessThanOrEqual(3);
184
+ }
185
+ });
186
+ });
187
+
188
+ describe("STAGE_COLORS", () => {
189
+ it("has a color class for each stage", () => {
190
+ expect(STAGE_COLORS).toHaveLength(STAGES.length);
191
+ for (const cls of STAGE_COLORS) {
192
+ expect(cls).toMatch(/^c-/);
193
+ }
194
+ });
195
+ });
196
+
197
+ describe("STAGE_HEX", () => {
198
+ it("maps every stage to a hex color", () => {
199
+ for (const stage of STAGES) {
200
+ expect(STAGE_HEX[stage]).toMatch(/^#[0-9a-f]{6}$/i);
201
+ }
202
+ });
203
+ });
204
+ });
@@ -17,5 +17,5 @@
17
17
  "types": ["bun-types"]
18
18
  },
19
19
  "include": ["src/**/*.ts"],
20
- "exclude": ["node_modules", "public/dist"]
20
+ "exclude": ["node_modules", "public/dist", "src/**/*.test.ts"]
21
21
  }
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "happy-dom",
6
+ root: "./dashboard",
7
+ include: ["src/**/*.test.ts"],
8
+ globals: true,
9
+ coverage: {
10
+ provider: "v8",
11
+ reporter: ["text", "json-summary"],
12
+ include: ["src/**/*.ts"],
13
+ exclude: ["src/**/*.test.ts", "src/types/**"],
14
+ thresholds: {
15
+ statements: 60,
16
+ branches: 50,
17
+ functions: 60,
18
+ lines: 60,
19
+ },
20
+ },
21
+ },
22
+ resolve: {
23
+ alias: {
24
+ "@": "./dashboard/src",
25
+ },
26
+ },
27
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipwright-cli",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "Orchestrate autonomous Claude Code agent teams in tmux",
5
5
  "bin": {
6
6
  "shipwright": "./scripts/sw",
@@ -33,7 +33,10 @@
33
33
  "dashboard:build": "bun build dashboard/src/main.ts --target=browser --outdir=dashboard/public/dist --sourcemap=linked",
34
34
  "dashboard:watch": "bun build dashboard/src/main.ts --target=browser --outdir=dashboard/public/dist --sourcemap=linked --watch",
35
35
  "dashboard:prod": "bun build dashboard/src/main.ts --target=browser --outdir=dashboard/public/dist --minify",
36
- "test": "bash scripts/sw-agi-roadmap-test.sh && bash scripts/sw-activity-test.sh && bash scripts/sw-adaptive-test.sh && bash scripts/sw-adversarial-test.sh && bash scripts/sw-architecture-enforcer-test.sh && bash scripts/sw-auth-test.sh && bash scripts/sw-autonomous-test.sh && bash scripts/sw-changelog-test.sh && bash scripts/sw-checkpoint-test.sh && bash scripts/sw-ci-test.sh && bash scripts/sw-cleanup-test.sh && bash scripts/sw-code-review-test.sh && bash scripts/sw-connect-test.sh && bash scripts/sw-context-test.sh && bash scripts/sw-cost-test.sh && bash scripts/sw-daemon-test.sh && bash scripts/sw-dashboard-test.sh && bash scripts/sw-db-test.sh && bash scripts/sw-decompose-test.sh && bash scripts/sw-deps-test.sh && bash scripts/sw-developer-simulation-test.sh && bash scripts/sw-discovery-test.sh && bash scripts/sw-doc-fleet-test.sh && bash scripts/sw-docs-agent-test.sh && bash scripts/sw-docs-test.sh && bash scripts/sw-doctor-test.sh && bash scripts/sw-dora-test.sh && bash scripts/sw-durable-test.sh && bash scripts/sw-e2e-orchestrator-test.sh && bash scripts/sw-eventbus-test.sh && bash scripts/sw-feedback-test.sh && bash scripts/sw-fix-test.sh && bash scripts/sw-fleet-discover-test.sh && bash scripts/sw-fleet-test.sh && bash scripts/sw-fleet-viz-test.sh && bash scripts/sw-frontier-test.sh && bash scripts/sw-github-app-test.sh && bash scripts/sw-github-checks-test.sh && bash scripts/sw-github-deploy-test.sh && bash scripts/sw-github-graphql-test.sh && bash scripts/sw-guild-test.sh && bash scripts/sw-heartbeat-test.sh && bash scripts/sw-hygiene-test.sh && bash scripts/sw-incident-test.sh && bash scripts/sw-init-test.sh && bash scripts/sw-instrument-test.sh && bash scripts/sw-intelligence-test.sh && bash scripts/sw-jira-test.sh && bash scripts/sw-launchd-test.sh && bash scripts/sw-linear-test.sh && bash scripts/sw-logs-test.sh && bash scripts/sw-loop-test.sh && bash scripts/sw-memory-test.sh && bash scripts/sw-mission-control-test.sh && bash scripts/sw-model-router-test.sh && bash scripts/sw-otel-test.sh && bash scripts/sw-oversight-test.sh && bash scripts/sw-patrol-meta-test.sh && bash scripts/sw-pipeline-composer-test.sh && bash scripts/sw-pipeline-test.sh && bash scripts/sw-pipeline-vitals-test.sh && bash scripts/sw-pm-test.sh && bash scripts/sw-pr-lifecycle-test.sh && bash scripts/sw-predictive-test.sh && bash scripts/sw-prep-test.sh && bash scripts/sw-ps-test.sh && bash scripts/sw-public-dashboard-test.sh && bash scripts/sw-quality-test.sh && bash scripts/sw-reaper-test.sh && bash scripts/sw-recruit-test.sh && bash scripts/sw-regression-test.sh && bash scripts/sw-release-manager-test.sh && bash scripts/sw-release-test.sh && bash scripts/sw-remote-test.sh && bash scripts/sw-replay-test.sh && bash scripts/sw-retro-test.sh && bash scripts/sw-scale-test.sh && bash scripts/sw-security-audit-test.sh && bash scripts/sw-self-optimize-test.sh && bash scripts/sw-session-test.sh && bash scripts/sw-setup-test.sh && bash scripts/sw-standup-test.sh && bash scripts/sw-status-test.sh && bash scripts/sw-strategic-test.sh && bash scripts/sw-stream-test.sh && bash scripts/sw-swarm-test.sh && bash scripts/sw-team-stages-test.sh && bash scripts/sw-templates-test.sh && bash scripts/sw-testgen-test.sh && bash scripts/sw-tmux-pipeline-test.sh && bash scripts/sw-tmux-test.sh && bash scripts/sw-trace-test.sh && bash scripts/sw-tracker-test.sh && bash scripts/sw-triage-test.sh && bash scripts/sw-upgrade-test.sh && bash scripts/sw-ux-test.sh && bash scripts/sw-webhook-test.sh && bash scripts/sw-widgets-test.sh && bash scripts/sw-worktree-test.sh && bash scripts/sw-policy-e2e-test.sh && bash scripts/sw-e2e-smoke-test.sh",
36
+ "dashboard:test": "vitest run --config dashboard/vitest.config.ts",
37
+ "dashboard:test:watch": "vitest --config dashboard/vitest.config.ts",
38
+ "dashboard:test:coverage": "vitest run --config dashboard/vitest.config.ts --coverage",
39
+ "test": "bash scripts/sw-agi-roadmap-test.sh && bash scripts/sw-activity-test.sh && bash scripts/sw-adaptive-test.sh && bash scripts/sw-adversarial-test.sh && bash scripts/sw-architecture-enforcer-test.sh && bash scripts/sw-auth-test.sh && bash scripts/sw-autonomous-test.sh && bash scripts/sw-changelog-test.sh && bash scripts/sw-checkpoint-test.sh && bash scripts/sw-ci-test.sh && bash scripts/sw-cleanup-test.sh && bash scripts/sw-code-review-test.sh && bash scripts/sw-connect-test.sh && bash scripts/sw-context-test.sh && bash scripts/sw-cost-test.sh && bash scripts/sw-daemon-test.sh && bash scripts/sw-dashboard-test.sh && bash scripts/sw-db-test.sh && bash scripts/sw-decompose-test.sh && bash scripts/sw-deps-test.sh && bash scripts/sw-developer-simulation-test.sh && bash scripts/sw-discovery-test.sh && bash scripts/sw-doc-fleet-test.sh && bash scripts/sw-docs-agent-test.sh && bash scripts/sw-docs-test.sh && bash scripts/sw-doctor-test.sh && bash scripts/sw-dora-test.sh && bash scripts/sw-durable-test.sh && bash scripts/sw-e2e-orchestrator-test.sh && bash scripts/sw-eventbus-test.sh && bash scripts/sw-feedback-test.sh && bash scripts/sw-fix-test.sh && bash scripts/sw-fleet-discover-test.sh && bash scripts/sw-fleet-test.sh && bash scripts/sw-fleet-viz-test.sh && bash scripts/sw-frontier-test.sh && bash scripts/sw-github-app-test.sh && bash scripts/sw-github-checks-test.sh && bash scripts/sw-github-deploy-test.sh && bash scripts/sw-github-graphql-test.sh && bash scripts/sw-guild-test.sh && bash scripts/sw-heartbeat-test.sh && bash scripts/sw-hygiene-test.sh && bash scripts/sw-incident-test.sh && bash scripts/sw-init-test.sh && bash scripts/sw-instrument-test.sh && bash scripts/sw-intelligence-test.sh && bash scripts/sw-jira-test.sh && bash scripts/sw-launchd-test.sh && bash scripts/sw-linear-test.sh && bash scripts/sw-logs-test.sh && bash scripts/sw-loop-test.sh && bash scripts/sw-memory-test.sh && bash scripts/sw-mission-control-test.sh && bash scripts/sw-model-router-test.sh && bash scripts/sw-otel-test.sh && bash scripts/sw-oversight-test.sh && bash scripts/sw-patrol-meta-test.sh && bash scripts/sw-pipeline-composer-test.sh && bash scripts/sw-pipeline-test.sh && bash scripts/sw-pipeline-vitals-test.sh && bash scripts/sw-pm-test.sh && bash scripts/sw-pr-lifecycle-test.sh && bash scripts/sw-predictive-test.sh && bash scripts/sw-prep-test.sh && bash scripts/sw-ps-test.sh && bash scripts/sw-public-dashboard-test.sh && bash scripts/sw-quality-test.sh && bash scripts/sw-reaper-test.sh && bash scripts/sw-recruit-test.sh && bash scripts/sw-regression-test.sh && bash scripts/sw-release-manager-test.sh && bash scripts/sw-release-test.sh && bash scripts/sw-remote-test.sh && bash scripts/sw-replay-test.sh && bash scripts/sw-retro-test.sh && bash scripts/sw-scale-test.sh && bash scripts/sw-security-audit-test.sh && bash scripts/sw-self-optimize-test.sh && bash scripts/sw-session-test.sh && bash scripts/sw-setup-test.sh && bash scripts/sw-standup-test.sh && bash scripts/sw-status-test.sh && bash scripts/sw-strategic-test.sh && bash scripts/sw-stream-test.sh && bash scripts/sw-swarm-test.sh && bash scripts/sw-team-stages-test.sh && bash scripts/sw-templates-test.sh && bash scripts/sw-testgen-test.sh && bash scripts/sw-tmux-pipeline-test.sh && bash scripts/sw-tmux-test.sh && bash scripts/sw-trace-test.sh && bash scripts/sw-tracker-test.sh && bash scripts/sw-triage-test.sh && bash scripts/sw-upgrade-test.sh && bash scripts/sw-ux-test.sh && bash scripts/sw-webhook-test.sh && bash scripts/sw-widgets-test.sh && bash scripts/sw-worktree-test.sh && bash scripts/sw-policy-e2e-test.sh && bash scripts/sw-e2e-smoke-test.sh && bash scripts/sw-dashboard-e2e-test.sh",
37
40
  "test:smoke": "bash scripts/sw-e2e-smoke-test.sh",
38
41
  "test:integration": "bash scripts/sw-e2e-integration-test.sh"
39
42
  },
@@ -57,7 +60,11 @@
57
60
  "node": ">=20"
58
61
  },
59
62
  "devDependencies": {
63
+ "@testing-library/dom": "^10.4.1",
60
64
  "bun-types": "^1.3.9",
61
- "typescript": "^5.9.3"
65
+ "happy-dom": "^20.6.1",
66
+ "jsdom": "^28.1.0",
67
+ "typescript": "^5.9.3",
68
+ "vitest": "^4.0.18"
62
69
  }
63
70
  }
@@ -1761,6 +1761,60 @@ stage_merge() {
1761
1761
  return 0
1762
1762
  fi
1763
1763
 
1764
+ # ── Oversight gate: merge block on verdict (diff + review criticals + goal) ──
1765
+ if [[ -x "$SCRIPT_DIR/sw-oversight.sh" ]] && [[ "${SKIP_GATES:-false}" != "true" ]]; then
1766
+ local merge_diff_file="${ARTIFACTS_DIR}/review-diff.patch"
1767
+ local merge_review_file="${ARTIFACTS_DIR}/review.md"
1768
+ if [[ ! -s "$merge_diff_file" ]]; then
1769
+ git diff "${BASE_BRANCH}...${GIT_BRANCH}" > "$merge_diff_file" 2>/dev/null || \
1770
+ git diff HEAD~5 > "$merge_diff_file" 2>/dev/null || true
1771
+ fi
1772
+ if [[ -s "$merge_diff_file" ]]; then
1773
+ local _merge_critical _merge_sec _merge_blocking _merge_reject
1774
+ _merge_critical=$(grep -ciE '\*\*\[?Critical\]?\*\*' "$merge_review_file" 2>/dev/null || echo "0")
1775
+ _merge_sec=$(grep -ciE '\*\*\[?Security\]?\*\*' "$merge_review_file" 2>/dev/null || echo "0")
1776
+ _merge_blocking=$((${_merge_critical:-0} + ${_merge_sec:-0}))
1777
+ [[ "$_merge_blocking" -gt 0 ]] && _merge_reject="Review found ${_merge_blocking} critical/security issue(s)"
1778
+ if ! bash "$SCRIPT_DIR/sw-oversight.sh" gate --diff "$merge_diff_file" --description "${GOAL:-Pipeline merge}" --reject-if "${_merge_reject:-}" >/dev/null 2>&1; then
1779
+ error "Oversight gate rejected — blocking merge"
1780
+ emit_event "merge.oversight_blocked" "issue=${ISSUE_NUMBER:-0}"
1781
+ log_stage "merge" "BLOCKED: oversight gate rejected"
1782
+ return 1
1783
+ fi
1784
+ fi
1785
+ fi
1786
+
1787
+ # ── Approval gates: block if merge requires approval and pending for this issue ──
1788
+ local ag_file="${HOME}/.shipwright/approval-gates.json"
1789
+ if [[ -f "$ag_file" ]] && [[ "${SKIP_GATES:-false}" != "true" ]]; then
1790
+ local ag_enabled ag_stages ag_pending_merge ag_issue_num
1791
+ ag_enabled=$(jq -r '.enabled // false' "$ag_file" 2>/dev/null || echo "false")
1792
+ ag_stages=$(jq -r '.stages // [] | if type == "array" then .[] else empty end' "$ag_file" 2>/dev/null || true)
1793
+ ag_issue_num=$(echo "${ISSUE_NUMBER:-0}" | awk '{print $1+0}')
1794
+ if [[ "$ag_enabled" == "true" ]] && echo "$ag_stages" | grep -qx "merge" 2>/dev/null; then
1795
+ local ha_file="${ARTIFACTS_DIR}/human-approval.txt"
1796
+ local ha_approved="false"
1797
+ if [[ -f "$ha_file" ]]; then
1798
+ ha_approved=$(jq -r --arg stage "merge" 'select(.stage == $stage) | .approved // false' "$ha_file" 2>/dev/null || echo "false")
1799
+ fi
1800
+ if [[ "$ha_approved" != "true" ]]; then
1801
+ ag_pending_merge=$(jq -r --argjson issue "$ag_issue_num" --arg stage "merge" \
1802
+ '[.pending[]? | select(.issue == $issue and .stage == $stage)] | length' "$ag_file" 2>/dev/null || echo "0")
1803
+ if [[ "${ag_pending_merge:-0}" -eq 0 ]]; then
1804
+ local req_at tmp_ag
1805
+ req_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || true)
1806
+ tmp_ag=$(mktemp "${HOME}/.shipwright/approval-gates.json.XXXXXX" 2>/dev/null || mktemp)
1807
+ jq --argjson issue "$ag_issue_num" --arg stage "merge" --arg requested "${req_at}" \
1808
+ '.pending += [{"issue": $issue, "stage": $stage, "requested_at": $requested}]' "$ag_file" > "$tmp_ag" 2>/dev/null && mv "$tmp_ag" "$ag_file" || rm -f "$tmp_ag"
1809
+ fi
1810
+ info "Merge requires approval — awaiting human approval via dashboard"
1811
+ emit_event "merge.approval_pending" "issue=${ISSUE_NUMBER:-0}"
1812
+ log_stage "merge" "BLOCKED: approval gate pending"
1813
+ return 1
1814
+ fi
1815
+ fi
1816
+ fi
1817
+
1764
1818
  # ── Branch Protection Check ──
1765
1819
  if type gh_branch_protection &>/dev/null 2>&1 && [[ -n "$REPO_OWNER" && -n "$REPO_NAME" ]]; then
1766
1820
  local protection_json
@@ -2472,6 +2526,11 @@ _Created automatically by \`shipwright pipeline\` monitor stage_" 2>/dev/null ||
2472
2526
 
2473
2527
  success "Post-deploy monitoring clean (${total_errors} errors in ${duration_minutes}m)"
2474
2528
 
2529
+ # Proactive feedback collection: always collect deploy logs for trend analysis
2530
+ if [[ -f "$deploy_log_file" ]] && [[ -s "$deploy_log_file" ]] && [[ -x "$SCRIPT_DIR/sw-feedback.sh" ]]; then
2531
+ (cd "$PROJECT_ROOT" && ARTIFACTS_DIR="$ARTIFACTS_DIR" bash "$SCRIPT_DIR/sw-feedback.sh" collect "$deploy_log_file" 2>/dev/null) || true
2532
+ fi
2533
+
2475
2534
  if [[ -n "$ISSUE_NUMBER" ]]; then
2476
2535
  gh_comment_issue "$ISSUE_NUMBER" "✅ **Post-deploy monitoring passed** — ${duration_minutes}m, ${total_errors} errors" 2>/dev/null || true
2477
2536
  fi
package/scripts/sw CHANGED
@@ -5,7 +5,7 @@
5
5
  # ╚═══════════════════════════════════════════════════════════════════════════╝
6
6
  set -euo pipefail
7
7
 
8
- VERSION="2.3.0"
8
+ VERSION="2.3.1"
9
9
 
10
10
  # Resolve symlinks (required for npm global install where bin/ symlinks to node_modules/)
11
11
  SOURCE="${BASH_SOURCE[0]}"
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.3.0"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.3.0"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.3.0"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.3.0"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.3.0"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12