opengstack 0.13.7 → 0.13.9

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 (135) hide show
  1. package/bin/opengstack.js +35 -90
  2. package/package.json +2 -3
  3. package/scripts/install-skills.js +47 -58
  4. package/skills/browse/bin/find-browse +21 -0
  5. package/skills/browse/bin/remote-slug +14 -0
  6. package/skills/browse/scripts/build-node-server.sh +48 -0
  7. package/skills/browse/src/activity.ts +208 -0
  8. package/skills/browse/src/browser-manager.ts +959 -0
  9. package/skills/browse/src/buffers.ts +137 -0
  10. package/skills/browse/src/bun-polyfill.cjs +109 -0
  11. package/skills/browse/src/cli.ts +678 -0
  12. package/skills/browse/src/commands.ts +128 -0
  13. package/skills/browse/src/config.ts +150 -0
  14. package/skills/browse/src/cookie-import-browser.ts +625 -0
  15. package/skills/browse/src/cookie-picker-routes.ts +230 -0
  16. package/skills/browse/src/cookie-picker-ui.ts +688 -0
  17. package/skills/browse/src/find-browse.ts +61 -0
  18. package/skills/browse/src/meta-commands.ts +550 -0
  19. package/skills/browse/src/platform.ts +17 -0
  20. package/skills/browse/src/read-commands.ts +358 -0
  21. package/skills/browse/src/server.ts +1192 -0
  22. package/skills/browse/src/sidebar-agent.ts +280 -0
  23. package/skills/browse/src/sidebar-utils.ts +21 -0
  24. package/skills/browse/src/snapshot.ts +407 -0
  25. package/skills/browse/src/url-validation.ts +95 -0
  26. package/skills/browse/src/write-commands.ts +364 -0
  27. package/skills/browse/test/activity.test.ts +120 -0
  28. package/skills/browse/test/adversarial-security.test.ts +32 -0
  29. package/skills/browse/test/browser-manager-unit.test.ts +17 -0
  30. package/skills/browse/test/bun-polyfill.test.ts +72 -0
  31. package/skills/browse/test/commands.test.ts +2075 -0
  32. package/skills/browse/test/compare-board.test.ts +342 -0
  33. package/skills/browse/test/config.test.ts +316 -0
  34. package/skills/browse/test/cookie-import-browser.test.ts +519 -0
  35. package/skills/browse/test/cookie-picker-routes.test.ts +260 -0
  36. package/skills/browse/test/file-drop.test.ts +271 -0
  37. package/skills/browse/test/find-browse.test.ts +50 -0
  38. package/skills/browse/test/findport.test.ts +191 -0
  39. package/skills/browse/test/fixtures/basic.html +33 -0
  40. package/skills/browse/test/fixtures/cursor-interactive.html +22 -0
  41. package/skills/browse/test/fixtures/dialog.html +15 -0
  42. package/skills/browse/test/fixtures/empty.html +2 -0
  43. package/skills/browse/test/fixtures/forms.html +55 -0
  44. package/skills/browse/test/fixtures/iframe.html +30 -0
  45. package/skills/browse/test/fixtures/network-idle.html +30 -0
  46. package/skills/browse/test/fixtures/qa-eval-checkout.html +108 -0
  47. package/skills/browse/test/fixtures/qa-eval-spa.html +98 -0
  48. package/skills/browse/test/fixtures/qa-eval.html +51 -0
  49. package/skills/browse/test/fixtures/responsive.html +49 -0
  50. package/skills/browse/test/fixtures/snapshot.html +55 -0
  51. package/skills/browse/test/fixtures/spa.html +24 -0
  52. package/skills/browse/test/fixtures/states.html +17 -0
  53. package/skills/browse/test/fixtures/upload.html +25 -0
  54. package/skills/browse/test/gstack-config.test.ts +138 -0
  55. package/skills/browse/test/gstack-update-check.test.ts +514 -0
  56. package/skills/browse/test/handoff.test.ts +235 -0
  57. package/skills/browse/test/path-validation.test.ts +91 -0
  58. package/skills/browse/test/platform.test.ts +37 -0
  59. package/skills/browse/test/server-auth.test.ts +65 -0
  60. package/skills/browse/test/sidebar-agent-roundtrip.test.ts +226 -0
  61. package/skills/browse/test/sidebar-agent.test.ts +199 -0
  62. package/skills/browse/test/sidebar-integration.test.ts +320 -0
  63. package/skills/browse/test/sidebar-unit.test.ts +96 -0
  64. package/skills/browse/test/snapshot.test.ts +467 -0
  65. package/skills/browse/test/state-ttl.test.ts +35 -0
  66. package/skills/browse/test/test-server.ts +57 -0
  67. package/skills/browse/test/url-validation.test.ts +72 -0
  68. package/skills/browse/test/watch.test.ts +129 -0
  69. package/skills/careful/bin/check-careful.sh +112 -0
  70. package/skills/cso/ACKNOWLEDGEMENTS.md +14 -0
  71. package/skills/freeze/bin/check-freeze.sh +79 -0
  72. package/skills/qa/references/issue-taxonomy.md +85 -0
  73. package/skills/qa/templates/qa-report-template.md +126 -0
  74. package/skills/review/TODOS-format.md +62 -0
  75. package/skills/review/checklist.md +220 -0
  76. package/skills/review/design-checklist.md +132 -0
  77. package/skills/review/greptile-triage.md +220 -0
  78. /package/{autoplan → skills/autoplan}/SKILL.md +0 -0
  79. /package/{autoplan → skills/autoplan}/SKILL.md.tmpl +0 -0
  80. /package/{benchmark → skills/benchmark}/SKILL.md +0 -0
  81. /package/{benchmark → skills/benchmark}/SKILL.md.tmpl +0 -0
  82. /package/{browse → skills/browse}/SKILL.md +0 -0
  83. /package/{browse → skills/browse}/SKILL.md.tmpl +0 -0
  84. /package/{canary → skills/canary}/SKILL.md +0 -0
  85. /package/{canary → skills/canary}/SKILL.md.tmpl +0 -0
  86. /package/{careful → skills/careful}/SKILL.md +0 -0
  87. /package/{careful → skills/careful}/SKILL.md.tmpl +0 -0
  88. /package/{codex → skills/codex}/SKILL.md +0 -0
  89. /package/{codex → skills/codex}/SKILL.md.tmpl +0 -0
  90. /package/{connect-chrome → skills/connect-chrome}/SKILL.md +0 -0
  91. /package/{connect-chrome → skills/connect-chrome}/SKILL.md.tmpl +0 -0
  92. /package/{cso → skills/cso}/SKILL.md +0 -0
  93. /package/{cso → skills/cso}/SKILL.md.tmpl +0 -0
  94. /package/{design-consultation → skills/design-consultation}/SKILL.md +0 -0
  95. /package/{design-consultation → skills/design-consultation}/SKILL.md.tmpl +0 -0
  96. /package/{design-review → skills/design-review}/SKILL.md +0 -0
  97. /package/{design-review → skills/design-review}/SKILL.md.tmpl +0 -0
  98. /package/{design-shotgun → skills/design-shotgun}/SKILL.md +0 -0
  99. /package/{design-shotgun → skills/design-shotgun}/SKILL.md.tmpl +0 -0
  100. /package/{document-release → skills/document-release}/SKILL.md +0 -0
  101. /package/{document-release → skills/document-release}/SKILL.md.tmpl +0 -0
  102. /package/{freeze → skills/freeze}/SKILL.md +0 -0
  103. /package/{freeze → skills/freeze}/SKILL.md.tmpl +0 -0
  104. /package/{gstack-upgrade → skills/gstack-upgrade}/SKILL.md +0 -0
  105. /package/{gstack-upgrade → skills/gstack-upgrade}/SKILL.md.tmpl +0 -0
  106. /package/{guard → skills/guard}/SKILL.md +0 -0
  107. /package/{guard → skills/guard}/SKILL.md.tmpl +0 -0
  108. /package/{investigate → skills/investigate}/SKILL.md +0 -0
  109. /package/{investigate → skills/investigate}/SKILL.md.tmpl +0 -0
  110. /package/{land-and-deploy → skills/land-and-deploy}/SKILL.md +0 -0
  111. /package/{land-and-deploy → skills/land-and-deploy}/SKILL.md.tmpl +0 -0
  112. /package/{office-hours → skills/office-hours}/SKILL.md +0 -0
  113. /package/{office-hours → skills/office-hours}/SKILL.md.tmpl +0 -0
  114. /package/{plan-ceo-review → skills/plan-ceo-review}/SKILL.md +0 -0
  115. /package/{plan-ceo-review → skills/plan-ceo-review}/SKILL.md.tmpl +0 -0
  116. /package/{plan-design-review → skills/plan-design-review}/SKILL.md +0 -0
  117. /package/{plan-design-review → skills/plan-design-review}/SKILL.md.tmpl +0 -0
  118. /package/{plan-eng-review → skills/plan-eng-review}/SKILL.md +0 -0
  119. /package/{plan-eng-review → skills/plan-eng-review}/SKILL.md.tmpl +0 -0
  120. /package/{qa → skills/qa}/SKILL.md +0 -0
  121. /package/{qa → skills/qa}/SKILL.md.tmpl +0 -0
  122. /package/{qa-only → skills/qa-only}/SKILL.md +0 -0
  123. /package/{qa-only → skills/qa-only}/SKILL.md.tmpl +0 -0
  124. /package/{retro → skills/retro}/SKILL.md +0 -0
  125. /package/{retro → skills/retro}/SKILL.md.tmpl +0 -0
  126. /package/{review → skills/review}/SKILL.md +0 -0
  127. /package/{review → skills/review}/SKILL.md.tmpl +0 -0
  128. /package/{setup-browser-cookies → skills/setup-browser-cookies}/SKILL.md +0 -0
  129. /package/{setup-browser-cookies → skills/setup-browser-cookies}/SKILL.md.tmpl +0 -0
  130. /package/{setup-deploy → skills/setup-deploy}/SKILL.md +0 -0
  131. /package/{setup-deploy → skills/setup-deploy}/SKILL.md.tmpl +0 -0
  132. /package/{ship → skills/ship}/SKILL.md +0 -0
  133. /package/{ship → skills/ship}/SKILL.md.tmpl +0 -0
  134. /package/{unfreeze → skills/unfreeze}/SKILL.md +0 -0
  135. /package/{unfreeze → skills/unfreeze}/SKILL.md.tmpl +0 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Tests for watch mode state machine in BrowserManager.
3
+ *
4
+ * Pure unit tests — no browser needed. Just instantiate BrowserManager
5
+ * and test the watch state methods (startWatch, stopWatch, addWatchSnapshot,
6
+ * isWatching).
7
+ */
8
+
9
+ import { describe, test, expect } from 'bun:test';
10
+ import { BrowserManager } from '../src/browser-manager';
11
+
12
+ describe('watch mode — state machine', () => {
13
+ test('isWatching returns false by default', () => {
14
+ const bm = new BrowserManager();
15
+ expect(bm.isWatching()).toBe(false);
16
+ });
17
+
18
+ test('startWatch sets isWatching to true', () => {
19
+ const bm = new BrowserManager();
20
+ bm.startWatch();
21
+ expect(bm.isWatching()).toBe(true);
22
+ });
23
+
24
+ test('stopWatch clears isWatching and returns snapshots', () => {
25
+ const bm = new BrowserManager();
26
+ bm.startWatch();
27
+ bm.addWatchSnapshot('snapshot-1');
28
+ bm.addWatchSnapshot('snapshot-2');
29
+
30
+ const result = bm.stopWatch();
31
+ expect(bm.isWatching()).toBe(false);
32
+ expect(result.snapshots).toEqual(['snapshot-1', 'snapshot-2']);
33
+ expect(result.snapshots.length).toBe(2);
34
+ });
35
+
36
+ test('stopWatch returns correct duration (approximately)', async () => {
37
+ const bm = new BrowserManager();
38
+ bm.startWatch();
39
+
40
+ // Wait ~50ms to get a measurable duration
41
+ await new Promise(resolve => setTimeout(resolve, 50));
42
+
43
+ const result = bm.stopWatch();
44
+ // Duration should be at least 40ms (allowing for timer imprecision)
45
+ expect(result.duration).toBeGreaterThanOrEqual(40);
46
+ // And less than 5 seconds (sanity check)
47
+ expect(result.duration).toBeLessThan(5000);
48
+ });
49
+
50
+ test('addWatchSnapshot stores snapshots', () => {
51
+ const bm = new BrowserManager();
52
+ bm.startWatch();
53
+
54
+ bm.addWatchSnapshot('page A content');
55
+ bm.addWatchSnapshot('page B content');
56
+ bm.addWatchSnapshot('page C content');
57
+
58
+ const result = bm.stopWatch();
59
+ expect(result.snapshots.length).toBe(3);
60
+ expect(result.snapshots[0]).toBe('page A content');
61
+ expect(result.snapshots[1]).toBe('page B content');
62
+ expect(result.snapshots[2]).toBe('page C content');
63
+ });
64
+
65
+ test('stopWatch resets snapshots for next cycle', () => {
66
+ const bm = new BrowserManager();
67
+
68
+ // First cycle
69
+ bm.startWatch();
70
+ bm.addWatchSnapshot('first-cycle-snapshot');
71
+ const result1 = bm.stopWatch();
72
+ expect(result1.snapshots.length).toBe(1);
73
+
74
+ // Second cycle — should start fresh
75
+ bm.startWatch();
76
+ const result2 = bm.stopWatch();
77
+ expect(result2.snapshots.length).toBe(0);
78
+ });
79
+
80
+ test('multiple start/stop cycles work correctly', () => {
81
+ const bm = new BrowserManager();
82
+
83
+ // Cycle 1
84
+ bm.startWatch();
85
+ expect(bm.isWatching()).toBe(true);
86
+ bm.addWatchSnapshot('snap-1');
87
+ const r1 = bm.stopWatch();
88
+ expect(bm.isWatching()).toBe(false);
89
+ expect(r1.snapshots).toEqual(['snap-1']);
90
+
91
+ // Cycle 2
92
+ bm.startWatch();
93
+ expect(bm.isWatching()).toBe(true);
94
+ bm.addWatchSnapshot('snap-2a');
95
+ bm.addWatchSnapshot('snap-2b');
96
+ const r2 = bm.stopWatch();
97
+ expect(bm.isWatching()).toBe(false);
98
+ expect(r2.snapshots).toEqual(['snap-2a', 'snap-2b']);
99
+
100
+ // Cycle 3 — no snapshots added
101
+ bm.startWatch();
102
+ expect(bm.isWatching()).toBe(true);
103
+ const r3 = bm.stopWatch();
104
+ expect(bm.isWatching()).toBe(false);
105
+ expect(r3.snapshots).toEqual([]);
106
+ });
107
+
108
+ test('stopWatch clears watchInterval if set', () => {
109
+ const bm = new BrowserManager();
110
+ bm.startWatch();
111
+
112
+ // Simulate an interval being set (as the server does)
113
+ bm.watchInterval = setInterval(() => {}, 100000);
114
+ expect(bm.watchInterval).not.toBeNull();
115
+
116
+ bm.stopWatch();
117
+ expect(bm.watchInterval).toBeNull();
118
+ });
119
+
120
+ test('stopWatch without startWatch returns empty results', () => {
121
+ const bm = new BrowserManager();
122
+
123
+ // Calling stopWatch without startWatch should not throw
124
+ const result = bm.stopWatch();
125
+ expect(result.snapshots).toEqual([]);
126
+ expect(result.duration).toBeLessThanOrEqual(Date.now()); // duration = now - 0
127
+ expect(bm.isWatching()).toBe(false);
128
+ });
129
+ });
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env bash
2
+ # check-careful.sh — PreToolUse hook for /careful skill
3
+ # Reads JSON from stdin, checks Bash command for destructive patterns.
4
+ # Returns {"permissionDecision":"ask","message":"..."} to warn, or {} to allow.
5
+ set -euo pipefail
6
+
7
+ # Read stdin (JSON with tool_input)
8
+ INPUT=$(cat)
9
+
10
+ # Extract the "command" field value from tool_input
11
+ # Try grep/sed first (handles 99% of cases), fall back to Python for escaped quotes
12
+ CMD=$(printf '%s' "$INPUT" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*:[[:space:]]*"//;s/"$//' || true)
13
+
14
+ # Python fallback if grep returned empty (e.g., escaped quotes in command)
15
+ if [ -z "$CMD" ]; then
16
+ CMD=$(printf '%s' "$INPUT" | python3 -c 'import sys,json; print(json.loads(sys.stdin.read()).get("tool_input",{}).get("command",""))' 2>/dev/null || true)
17
+ fi
18
+
19
+ # If we still couldn't extract a command, allow
20
+ if [ -z "$CMD" ]; then
21
+ echo '{}'
22
+ exit 0
23
+ fi
24
+
25
+ # Normalize: lowercase for case-insensitive SQL matching
26
+ CMD_LOWER=$(printf '%s' "$CMD" | tr '[:upper:]' '[:lower:]')
27
+
28
+ # --- Check for safe exceptions (rm -rf of build artifacts) ---
29
+ if printf '%s' "$CMD" | grep -qE 'rm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|--recursive\s+)' 2>/dev/null; then
30
+ SAFE_ONLY=true
31
+ RM_ARGS=$(printf '%s' "$CMD" | sed -E 's/.*rm\s+(-[a-zA-Z]+\s+)*//;s/--recursive\s*//')
32
+ for target in $RM_ARGS; do
33
+ case "$target" in
34
+ */node_modules|node_modules|*/\.next|\.next|*/dist|dist|*/__pycache__|__pycache__|*/\.cache|\.cache|*/build|build|*/\.turbo|\.turbo|*/coverage|coverage)
35
+ ;; # safe target
36
+ -*)
37
+ ;; # flag, skip
38
+ *)
39
+ SAFE_ONLY=false
40
+ break
41
+ ;;
42
+ esac
43
+ done
44
+ if [ "$SAFE_ONLY" = true ]; then
45
+ echo '{}'
46
+ exit 0
47
+ fi
48
+ fi
49
+
50
+ # --- Destructive pattern checks ---
51
+ WARN=""
52
+ PATTERN=""
53
+
54
+ # rm -rf / rm -r / rm --recursive
55
+ if printf '%s' "$CMD" | grep -qE 'rm\s+(-[a-zA-Z]*r|--recursive)' 2>/dev/null; then
56
+ WARN="Destructive: recursive delete (rm -r). This permanently removes files."
57
+ PATTERN="rm_recursive"
58
+ fi
59
+
60
+ # DROP TABLE / DROP DATABASE
61
+ if [ -z "$WARN" ] && printf '%s' "$CMD_LOWER" | grep -qE 'drop\s+(table|database)' 2>/dev/null; then
62
+ WARN="Destructive: SQL DROP detected. This permanently deletes database objects."
63
+ PATTERN="drop_table"
64
+ fi
65
+
66
+ # TRUNCATE
67
+ if [ -z "$WARN" ] && printf '%s' "$CMD_LOWER" | grep -qE '\btruncate\b' 2>/dev/null; then
68
+ WARN="Destructive: SQL TRUNCATE detected. This deletes all rows from a table."
69
+ PATTERN="truncate"
70
+ fi
71
+
72
+ # git push --force / git push -f
73
+ if [ -z "$WARN" ] && printf '%s' "$CMD" | grep -qE 'git\s+push\s+.*(-f\b|--force)' 2>/dev/null; then
74
+ WARN="Destructive: git force-push rewrites remote history. Other contributors may lose work."
75
+ PATTERN="git_force_push"
76
+ fi
77
+
78
+ # git reset --hard
79
+ if [ -z "$WARN" ] && printf '%s' "$CMD" | grep -qE 'git\s+reset\s+--hard' 2>/dev/null; then
80
+ WARN="Destructive: git reset --hard discards all uncommitted changes."
81
+ PATTERN="git_reset_hard"
82
+ fi
83
+
84
+ # git checkout . / git restore .
85
+ if [ -z "$WARN" ] && printf '%s' "$CMD" | grep -qE 'git\s+(checkout|restore)\s+\.' 2>/dev/null; then
86
+ WARN="Destructive: discards all uncommitted changes in the working tree."
87
+ PATTERN="git_discard"
88
+ fi
89
+
90
+ # kubectl delete
91
+ if [ -z "$WARN" ] && printf '%s' "$CMD" | grep -qE 'kubectl\s+delete' 2>/dev/null; then
92
+ WARN="Destructive: kubectl delete removes Kubernetes resources. May impact production."
93
+ PATTERN="kubectl_delete"
94
+ fi
95
+
96
+ # docker rm -f / docker system prune
97
+ if [ -z "$WARN" ] && printf '%s' "$CMD" | grep -qE 'docker\s+(rm\s+-f|system\s+prune)' 2>/dev/null; then
98
+ WARN="Destructive: Docker force-remove or prune. May delete running containers or cached images."
99
+ PATTERN="docker_destructive"
100
+ fi
101
+
102
+ # --- Output ---
103
+ if [ -n "$WARN" ]; then
104
+ # Log hook fire event (pattern name only, never command content)
105
+ mkdir -p ~/.gstack/analytics 2>/dev/null || true
106
+ echo '{"event":"hook_fire","skill":"careful","pattern":"'"$PATTERN"'","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
107
+
108
+ WARN_ESCAPED=$(printf '%s' "$WARN" | sed 's/"/\\"/g')
109
+ printf '{"permissionDecision":"ask","message":"[careful] %s"}\n' "$WARN_ESCAPED"
110
+ else
111
+ echo '{}'
112
+ fi
@@ -0,0 +1,14 @@
1
+ # Acknowledgements
2
+
3
+ /cso v2 was informed by research across the security audit landscape. Credits to:
4
+
5
+ - **[Sentry Security Review](https://github.com/getsentry/skills)** — The confidence-based reporting system (only HIGH confidence findings get reported) and the "research before reporting" methodology (trace data flow, check upstream validation) validated our 8/10 daily confidence gate. TimOnWeb rated it the only security skill worth installing out of 5 tested.
6
+ - **[Trail of Bits Skills](https://github.com/trailofbits/skills)** — The audit-context-building methodology (build a mental model before hunting bugs) directly inspired Phase 0. Their variant analysis concept (found one vuln? Search the whole codebase for the same pattern) inspired Phase 12's variant analysis step.
7
+ - **[Shannon by Keygraph](https://github.com/KeygraphHQ/shannon)** — Autonomous AI pentester achieving 96.15% on the XBOW benchmark (100/104 exploits). Validated that AI can do real security testing, not just checklist scanning. Our Phase 12 active verification is the static-analysis version of what Shannon does live.
8
+ - **[afiqiqmal/claude-security-audit](https://github.com/afiqiqmal/claude-security-audit)** — The AI/LLM-specific security checks (prompt injection, RAG poisoning, tool calling permissions) inspired Phase 7. Their framework-level auto-detection (detecting "Next.js" not just "Node/TypeScript") inspired Phase 0's framework detection step.
9
+ - **[Snyk ToxicSkills Research](https://snyk.io/blog/toxicskills-malicious-ai-agent-skills-clawhub/)** — The finding that 36% of AI agent skills have security flaws and 13.4% are malicious inspired Phase 8 (Skill Supply Chain scanning).
10
+ - **[Daniel Miessler's Personal AI Infrastructure](https://github.com/danielmiessler/Personal_AI_Infrastructure)** — The incident response playbooks and protection file concept informed the remediation and LLM security phases.
11
+ - **[McGo/claude-code-security-audit](https://github.com/McGo/claude-code-security-audit)** — The idea of generating shareable reports and actionable epics informed our report format evolution.
12
+ - **[Claude Code Security Pack](https://dev.to/myougatheaxo/automate-owasp-security-audits-with-claude-code-security-pack-4mah)** — Modular approach (separate /security-audit, /secret-scanner, /deps-check skills) validated that these are distinct concerns. Our unified approach sacrifices modularity for cross-phase reasoning.
13
+ - **[Anthropic Claude Code Security](https://www.anthropic.com/news/claude-code-security)** — Multi-stage verification and confidence scoring validated our parallel finding verification approach. Found 500+ zero-days in open source.
14
+ - **[@gus_argon](https://x.com/gus_aragon/status/2035841289602904360)** — Identified critical v1 blind spots: no stack detection (runs all-language patterns), uses bash grep instead of Claude Code's Grep tool, `| head -20` truncates results silently, and preamble bloat. These directly shaped v2's stack-first approach and Grep tool mandate.
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env bash
2
+ # check-freeze.sh — PreToolUse hook for /freeze skill
3
+ # Reads JSON from stdin, checks if file_path is within the freeze boundary.
4
+ # Returns {"permissionDecision":"deny","message":"..."} to block, or {} to allow.
5
+ set -euo pipefail
6
+
7
+ # Read stdin
8
+ INPUT=$(cat)
9
+
10
+ # Locate the freeze directory state file
11
+ STATE_DIR="${CLAUDE_PLUGIN_DATA:-$HOME/.gstack}"
12
+ FREEZE_FILE="$STATE_DIR/freeze-dir.txt"
13
+
14
+ # If no freeze file exists, allow everything (not yet configured)
15
+ if [ ! -f "$FREEZE_FILE" ]; then
16
+ echo '{}'
17
+ exit 0
18
+ fi
19
+
20
+ FREEZE_DIR=$(tr -d '[:space:]' < "$FREEZE_FILE")
21
+
22
+ # If freeze dir is empty, allow
23
+ if [ -z "$FREEZE_DIR" ]; then
24
+ echo '{}'
25
+ exit 0
26
+ fi
27
+
28
+ # Extract file_path from tool_input JSON
29
+ # Try grep/sed first, fall back to Python for escaped quotes
30
+ FILE_PATH=$(printf '%s' "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*:[[:space:]]*"//;s/"$//' || true)
31
+
32
+ # Python fallback if grep returned empty
33
+ if [ -z "$FILE_PATH" ]; then
34
+ FILE_PATH=$(printf '%s' "$INPUT" | python3 -c 'import sys,json; print(json.loads(sys.stdin.read()).get("tool_input",{}).get("file_path",""))' 2>/dev/null || true)
35
+ fi
36
+
37
+ # If we couldn't extract a file path, allow (don't block on parse failure)
38
+ if [ -z "$FILE_PATH" ]; then
39
+ echo '{}'
40
+ exit 0
41
+ fi
42
+
43
+ # Resolve file_path to absolute if it isn't already
44
+ case "$FILE_PATH" in
45
+ /*) ;; # already absolute
46
+ *)
47
+ FILE_PATH="$(pwd)/$FILE_PATH"
48
+ ;;
49
+ esac
50
+
51
+ # Normalize: remove double slashes and trailing slash
52
+ FILE_PATH=$(printf '%s' "$FILE_PATH" | sed 's|/\+|/|g;s|/$||')
53
+
54
+ # Resolve symlinks and .. sequences (POSIX-portable, works on macOS)
55
+ _resolve_path() {
56
+ local _dir _base
57
+ _dir="$(dirname "$1")"
58
+ _base="$(basename "$1")"
59
+ _dir="$(cd "$_dir" 2>/dev/null && pwd -P || printf '%s' "$_dir")"
60
+ printf '%s/%s' "$_dir" "$_base"
61
+ }
62
+ FILE_PATH=$(_resolve_path "$FILE_PATH")
63
+ FREEZE_DIR=$(_resolve_path "$FREEZE_DIR")
64
+
65
+ # Check: does the file path start with the freeze directory?
66
+ case "$FILE_PATH" in
67
+ "${FREEZE_DIR}/"*|"${FREEZE_DIR}")
68
+ # Inside freeze boundary — allow
69
+ echo '{}'
70
+ ;;
71
+ *)
72
+ # Outside freeze boundary — deny
73
+ # Log hook fire event
74
+ mkdir -p ~/.gstack/analytics 2>/dev/null || true
75
+ echo '{"event":"hook_fire","skill":"freeze","pattern":"boundary_deny","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
76
+
77
+ printf '{"permissionDecision":"deny","message":"[freeze] Blocked: %s is outside the freeze boundary (%s). Only edits within the frozen directory are allowed."}\n' "$FILE_PATH" "$FREEZE_DIR"
78
+ ;;
79
+ esac
@@ -0,0 +1,85 @@
1
+ # QA Issue Taxonomy
2
+
3
+ ## Severity Levels
4
+
5
+ | Severity | Definition | Examples |
6
+ |----------|------------|----------|
7
+ | **critical** | Blocks a core workflow, causes data loss, or crashes the app | Form submit causes error page, checkout flow broken, data deleted without confirmation |
8
+ | **high** | Major feature broken or unusable, no workaround | Search returns wrong results, file upload silently fails, auth redirect loop |
9
+ | **medium** | Feature works but with noticeable problems, workaround exists | Slow page load (>5s), form validation missing but submit still works, layout broken on mobile only |
10
+ | **low** | Minor cosmetic or polish issue | Typo in footer, 1px alignment issue, hover state inconsistent |
11
+
12
+ ## Categories
13
+
14
+ ### 1. Visual/UI
15
+ - Layout breaks (overlapping elements, clipped text, horizontal scrollbar)
16
+ - Broken or missing images
17
+ - Incorrect z-index (elements appearing behind others)
18
+ - Font/color inconsistencies
19
+ - Animation glitches (jank, incomplete transitions)
20
+ - Alignment issues (off-grid, uneven spacing)
21
+ - Dark mode / theme issues
22
+
23
+ ### 2. Functional
24
+ - Broken links (404, wrong destination)
25
+ - Dead buttons (click does nothing)
26
+ - Form validation (missing, wrong, bypassed)
27
+ - Incorrect redirects
28
+ - State not persisting (data lost on refresh, back button)
29
+ - Race conditions (double-submit, stale data)
30
+ - Search returning wrong or no results
31
+
32
+ ### 3. UX
33
+ - Confusing navigation (no breadcrumbs, dead ends)
34
+ - Missing loading indicators (user doesn't know something is happening)
35
+ - Slow interactions (>500ms with no feedback)
36
+ - Unclear error messages ("Something went wrong" with no detail)
37
+ - No confirmation before destructive actions
38
+ - Inconsistent interaction patterns across pages
39
+ - Dead ends (no way back, no next action)
40
+
41
+ ### 4. Content
42
+ - Typos and grammar errors
43
+ - Outdated or incorrect text
44
+ - Placeholder / lorem ipsum text left in
45
+ - Truncated text (cut off without ellipsis or "more")
46
+ - Wrong labels on buttons or form fields
47
+ - Missing or unhelpful empty states
48
+
49
+ ### 5. Performance
50
+ - Slow page loads (>3 seconds)
51
+ - Janky scrolling (dropped frames)
52
+ - Layout shifts (content jumping after load)
53
+ - Excessive network requests (>50 on a single page)
54
+ - Large unoptimized images
55
+ - Blocking JavaScript (page unresponsive during load)
56
+
57
+ ### 6. Console/Errors
58
+ - JavaScript exceptions (uncaught errors)
59
+ - Failed network requests (4xx, 5xx)
60
+ - Deprecation warnings (upcoming breakage)
61
+ - CORS errors
62
+ - Mixed content warnings (HTTP resources on HTTPS)
63
+ - CSP violations
64
+
65
+ ### 7. Accessibility
66
+ - Missing alt text on images
67
+ - Unlabeled form inputs
68
+ - Keyboard navigation broken (can't tab to elements)
69
+ - Focus traps (can't escape a modal or dropdown)
70
+ - Missing or incorrect ARIA attributes
71
+ - Insufficient color contrast
72
+ - Content not reachable by screen reader
73
+
74
+ ## Per-Page Exploration Checklist
75
+
76
+ For each page visited during a QA session:
77
+
78
+ 1. **Visual scan** — Take annotated screenshot (`snapshot -i -a -o`). Look for layout issues, broken images, alignment.
79
+ 2. **Interactive elements** — Click every button, link, and control. Does each do what it says?
80
+ 3. **Forms** — Fill and submit. Test empty submission, invalid data, edge cases (long text, special characters).
81
+ 4. **Navigation** — Check all paths in/out. Breadcrumbs, back button, deep links, mobile menu.
82
+ 5. **States** — Check empty state, loading state, error state, full/overflow state.
83
+ 6. **Console** — Run `console --errors` after interactions. Any new JS errors or failed requests?
84
+ 7. **Responsiveness** — If relevant, check mobile and tablet viewports.
85
+ 8. **Auth boundaries** — What happens when logged out? Different user roles?
@@ -0,0 +1,126 @@
1
+ # QA Report: {APP_NAME}
2
+
3
+ | Field | Value |
4
+ |-------|-------|
5
+ | **Date** | {DATE} |
6
+ | **URL** | {URL} |
7
+ | **Branch** | {BRANCH} |
8
+ | **Commit** | {COMMIT_SHA} ({COMMIT_DATE}) |
9
+ | **PR** | {PR_NUMBER} ({PR_URL}) or "—" |
10
+ | **Tier** | Quick / Standard / Exhaustive |
11
+ | **Scope** | {SCOPE or "Full app"} |
12
+ | **Duration** | {DURATION} |
13
+ | **Pages visited** | {COUNT} |
14
+ | **Screenshots** | {COUNT} |
15
+ | **Framework** | {DETECTED or "Unknown"} |
16
+ | **Index** | [All QA runs](./index.md) |
17
+
18
+ ## Health Score: {SCORE}/100
19
+
20
+ | Category | Score |
21
+ |----------|-------|
22
+ | Console | {0-100} |
23
+ | Links | {0-100} |
24
+ | Visual | {0-100} |
25
+ | Functional | {0-100} |
26
+ | UX | {0-100} |
27
+ | Performance | {0-100} |
28
+ | Accessibility | {0-100} |
29
+
30
+ ## Top 3 Things to Fix
31
+
32
+ 1. **{ISSUE-NNN}: {title}** — {one-line description}
33
+ 2. **{ISSUE-NNN}: {title}** — {one-line description}
34
+ 3. **{ISSUE-NNN}: {title}** — {one-line description}
35
+
36
+ ## Console Health
37
+
38
+ | Error | Count | First seen |
39
+ |-------|-------|------------|
40
+ | {error message} | {N} | {URL} |
41
+
42
+ ## Summary
43
+
44
+ | Severity | Count |
45
+ |----------|-------|
46
+ | Critical | 0 |
47
+ | High | 0 |
48
+ | Medium | 0 |
49
+ | Low | 0 |
50
+ | **Total** | **0** |
51
+
52
+ ## Issues
53
+
54
+ ### ISSUE-001: {Short title}
55
+
56
+ | Field | Value |
57
+ |-------|-------|
58
+ | **Severity** | critical / high / medium / low |
59
+ | **Category** | visual / functional / ux / content / performance / console / accessibility |
60
+ | **URL** | {page URL} |
61
+
62
+ **Description:** {What is wrong, expected vs actual.}
63
+
64
+ **Repro Steps:**
65
+
66
+ 1. Navigate to {URL}
67
+ ![Step 1](screenshots/issue-001-step-1.png)
68
+ 2. {Action}
69
+ ![Step 2](screenshots/issue-001-step-2.png)
70
+ 3. **Observe:** {what goes wrong}
71
+ ![Result](screenshots/issue-001-result.png)
72
+
73
+ ---
74
+
75
+ ## Fixes Applied (if applicable)
76
+
77
+ | Issue | Fix Status | Commit | Files Changed |
78
+ |-------|-----------|--------|---------------|
79
+ | ISSUE-NNN | verified / best-effort / reverted / deferred | {SHA} | {files} |
80
+
81
+ ### Before/After Evidence
82
+
83
+ #### ISSUE-NNN: {title}
84
+ **Before:** ![Before](screenshots/issue-NNN-before.png)
85
+ **After:** ![After](screenshots/issue-NNN-after.png)
86
+
87
+ ---
88
+
89
+ ## Regression Tests
90
+
91
+ | Issue | Test File | Status | Description |
92
+ |-------|-----------|--------|-------------|
93
+ | ISSUE-NNN | path/to/test | committed / deferred / skipped | description |
94
+
95
+ ### Deferred Tests
96
+
97
+ #### ISSUE-NNN: {title}
98
+ **Precondition:** {setup state that triggers the bug}
99
+ **Action:** {what the user does}
100
+ **Expected:** {correct behavior}
101
+ **Why deferred:** {reason}
102
+
103
+ ---
104
+
105
+ ## Ship Readiness
106
+
107
+ | Metric | Value |
108
+ |--------|-------|
109
+ | Health score | {before} → {after} ({delta}) |
110
+ | Issues found | N |
111
+ | Fixes applied | N (verified: X, best-effort: Y, reverted: Z) |
112
+ | Deferred | N |
113
+
114
+ **PR Summary:** "QA found N issues, fixed M, health score X → Y."
115
+
116
+ ---
117
+
118
+ ## Regression (if applicable)
119
+
120
+ | Metric | Baseline | Current | Delta |
121
+ |--------|----------|---------|-------|
122
+ | Health score | {N} | {N} | {+/-N} |
123
+ | Issues | {N} | {N} | {+/-N} |
124
+
125
+ **Fixed since baseline:** {list}
126
+ **New since baseline:** {list}
@@ -0,0 +1,62 @@
1
+ # TODOS.md Format Reference
2
+
3
+ Shared reference for the canonical TODOS.md format. Referenced by `/ship` (Step 5.5) and `/plan-ceo-review` (TODOS.md updates section) to ensure consistent TODO item structure.
4
+
5
+ ---
6
+
7
+ ## File Structure
8
+
9
+ ```markdown
10
+ # TODOS
11
+
12
+ ## <Skill/Component> ← e.g., ## Browse, ## Ship, ## Review, ## Infrastructure
13
+ <items sorted P0 first, then P1, P2, P3, P4>
14
+
15
+ ## Completed
16
+ <finished items with completion annotation>
17
+ ```
18
+
19
+ **Sections:** Organize by skill or component (`## Browse`, `## Ship`, `## Review`, `## QA`, `## Retro`, `## Infrastructure`). Within each section, sort items by priority (P0 at top).
20
+
21
+ ---
22
+
23
+ ## TODO Item Format
24
+
25
+ Each item is an H3 under its section:
26
+
27
+ ```markdown
28
+ ### <Title>
29
+
30
+ **What:** One-line description of the work.
31
+
32
+ **Why:** The concrete problem it solves or value it unlocks.
33
+
34
+ **Context:** Enough detail that someone picking this up in 3 months understands the motivation, the current state, and where to start.
35
+
36
+ **Effort:** S / M / L / XL
37
+ **Priority:** P0 / P1 / P2 / P3 / P4
38
+ **Depends on:** <prerequisites, or "None">
39
+ ```
40
+
41
+ **Required fields:** What, Why, Context, Effort, Priority
42
+ **Optional fields:** Depends on, Blocked by
43
+
44
+ ---
45
+
46
+ ## Priority Definitions
47
+
48
+ - **P0** — Blocking: must be done before next release
49
+ - **P1** — Critical: should be done this cycle
50
+ - **P2** — Important: do when P0/P1 are clear
51
+ - **P3** — Nice-to-have: revisit after adoption/usage data
52
+ - **P4** — Someday: good idea, no urgency
53
+
54
+ ---
55
+
56
+ ## Completed Item Format
57
+
58
+ When an item is completed, move it to the `## Completed` section preserving its original content and appending:
59
+
60
+ ```markdown
61
+ **Completed:** vX.Y.Z (YYYY-MM-DD)
62
+ ```