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.
- package/bin/opengstack.js +35 -90
- package/package.json +2 -3
- package/scripts/install-skills.js +47 -58
- package/skills/browse/bin/find-browse +21 -0
- package/skills/browse/bin/remote-slug +14 -0
- package/skills/browse/scripts/build-node-server.sh +48 -0
- package/skills/browse/src/activity.ts +208 -0
- package/skills/browse/src/browser-manager.ts +959 -0
- package/skills/browse/src/buffers.ts +137 -0
- package/skills/browse/src/bun-polyfill.cjs +109 -0
- package/skills/browse/src/cli.ts +678 -0
- package/skills/browse/src/commands.ts +128 -0
- package/skills/browse/src/config.ts +150 -0
- package/skills/browse/src/cookie-import-browser.ts +625 -0
- package/skills/browse/src/cookie-picker-routes.ts +230 -0
- package/skills/browse/src/cookie-picker-ui.ts +688 -0
- package/skills/browse/src/find-browse.ts +61 -0
- package/skills/browse/src/meta-commands.ts +550 -0
- package/skills/browse/src/platform.ts +17 -0
- package/skills/browse/src/read-commands.ts +358 -0
- package/skills/browse/src/server.ts +1192 -0
- package/skills/browse/src/sidebar-agent.ts +280 -0
- package/skills/browse/src/sidebar-utils.ts +21 -0
- package/skills/browse/src/snapshot.ts +407 -0
- package/skills/browse/src/url-validation.ts +95 -0
- package/skills/browse/src/write-commands.ts +364 -0
- package/skills/browse/test/activity.test.ts +120 -0
- package/skills/browse/test/adversarial-security.test.ts +32 -0
- package/skills/browse/test/browser-manager-unit.test.ts +17 -0
- package/skills/browse/test/bun-polyfill.test.ts +72 -0
- package/skills/browse/test/commands.test.ts +2075 -0
- package/skills/browse/test/compare-board.test.ts +342 -0
- package/skills/browse/test/config.test.ts +316 -0
- package/skills/browse/test/cookie-import-browser.test.ts +519 -0
- package/skills/browse/test/cookie-picker-routes.test.ts +260 -0
- package/skills/browse/test/file-drop.test.ts +271 -0
- package/skills/browse/test/find-browse.test.ts +50 -0
- package/skills/browse/test/findport.test.ts +191 -0
- package/skills/browse/test/fixtures/basic.html +33 -0
- package/skills/browse/test/fixtures/cursor-interactive.html +22 -0
- package/skills/browse/test/fixtures/dialog.html +15 -0
- package/skills/browse/test/fixtures/empty.html +2 -0
- package/skills/browse/test/fixtures/forms.html +55 -0
- package/skills/browse/test/fixtures/iframe.html +30 -0
- package/skills/browse/test/fixtures/network-idle.html +30 -0
- package/skills/browse/test/fixtures/qa-eval-checkout.html +108 -0
- package/skills/browse/test/fixtures/qa-eval-spa.html +98 -0
- package/skills/browse/test/fixtures/qa-eval.html +51 -0
- package/skills/browse/test/fixtures/responsive.html +49 -0
- package/skills/browse/test/fixtures/snapshot.html +55 -0
- package/skills/browse/test/fixtures/spa.html +24 -0
- package/skills/browse/test/fixtures/states.html +17 -0
- package/skills/browse/test/fixtures/upload.html +25 -0
- package/skills/browse/test/gstack-config.test.ts +138 -0
- package/skills/browse/test/gstack-update-check.test.ts +514 -0
- package/skills/browse/test/handoff.test.ts +235 -0
- package/skills/browse/test/path-validation.test.ts +91 -0
- package/skills/browse/test/platform.test.ts +37 -0
- package/skills/browse/test/server-auth.test.ts +65 -0
- package/skills/browse/test/sidebar-agent-roundtrip.test.ts +226 -0
- package/skills/browse/test/sidebar-agent.test.ts +199 -0
- package/skills/browse/test/sidebar-integration.test.ts +320 -0
- package/skills/browse/test/sidebar-unit.test.ts +96 -0
- package/skills/browse/test/snapshot.test.ts +467 -0
- package/skills/browse/test/state-ttl.test.ts +35 -0
- package/skills/browse/test/test-server.ts +57 -0
- package/skills/browse/test/url-validation.test.ts +72 -0
- package/skills/browse/test/watch.test.ts +129 -0
- package/skills/careful/bin/check-careful.sh +112 -0
- package/skills/cso/ACKNOWLEDGEMENTS.md +14 -0
- package/skills/freeze/bin/check-freeze.sh +79 -0
- package/skills/qa/references/issue-taxonomy.md +85 -0
- package/skills/qa/templates/qa-report-template.md +126 -0
- package/skills/review/TODOS-format.md +62 -0
- package/skills/review/checklist.md +220 -0
- package/skills/review/design-checklist.md +132 -0
- package/skills/review/greptile-triage.md +220 -0
- /package/{autoplan → skills/autoplan}/SKILL.md +0 -0
- /package/{autoplan → skills/autoplan}/SKILL.md.tmpl +0 -0
- /package/{benchmark → skills/benchmark}/SKILL.md +0 -0
- /package/{benchmark → skills/benchmark}/SKILL.md.tmpl +0 -0
- /package/{browse → skills/browse}/SKILL.md +0 -0
- /package/{browse → skills/browse}/SKILL.md.tmpl +0 -0
- /package/{canary → skills/canary}/SKILL.md +0 -0
- /package/{canary → skills/canary}/SKILL.md.tmpl +0 -0
- /package/{careful → skills/careful}/SKILL.md +0 -0
- /package/{careful → skills/careful}/SKILL.md.tmpl +0 -0
- /package/{codex → skills/codex}/SKILL.md +0 -0
- /package/{codex → skills/codex}/SKILL.md.tmpl +0 -0
- /package/{connect-chrome → skills/connect-chrome}/SKILL.md +0 -0
- /package/{connect-chrome → skills/connect-chrome}/SKILL.md.tmpl +0 -0
- /package/{cso → skills/cso}/SKILL.md +0 -0
- /package/{cso → skills/cso}/SKILL.md.tmpl +0 -0
- /package/{design-consultation → skills/design-consultation}/SKILL.md +0 -0
- /package/{design-consultation → skills/design-consultation}/SKILL.md.tmpl +0 -0
- /package/{design-review → skills/design-review}/SKILL.md +0 -0
- /package/{design-review → skills/design-review}/SKILL.md.tmpl +0 -0
- /package/{design-shotgun → skills/design-shotgun}/SKILL.md +0 -0
- /package/{design-shotgun → skills/design-shotgun}/SKILL.md.tmpl +0 -0
- /package/{document-release → skills/document-release}/SKILL.md +0 -0
- /package/{document-release → skills/document-release}/SKILL.md.tmpl +0 -0
- /package/{freeze → skills/freeze}/SKILL.md +0 -0
- /package/{freeze → skills/freeze}/SKILL.md.tmpl +0 -0
- /package/{gstack-upgrade → skills/gstack-upgrade}/SKILL.md +0 -0
- /package/{gstack-upgrade → skills/gstack-upgrade}/SKILL.md.tmpl +0 -0
- /package/{guard → skills/guard}/SKILL.md +0 -0
- /package/{guard → skills/guard}/SKILL.md.tmpl +0 -0
- /package/{investigate → skills/investigate}/SKILL.md +0 -0
- /package/{investigate → skills/investigate}/SKILL.md.tmpl +0 -0
- /package/{land-and-deploy → skills/land-and-deploy}/SKILL.md +0 -0
- /package/{land-and-deploy → skills/land-and-deploy}/SKILL.md.tmpl +0 -0
- /package/{office-hours → skills/office-hours}/SKILL.md +0 -0
- /package/{office-hours → skills/office-hours}/SKILL.md.tmpl +0 -0
- /package/{plan-ceo-review → skills/plan-ceo-review}/SKILL.md +0 -0
- /package/{plan-ceo-review → skills/plan-ceo-review}/SKILL.md.tmpl +0 -0
- /package/{plan-design-review → skills/plan-design-review}/SKILL.md +0 -0
- /package/{plan-design-review → skills/plan-design-review}/SKILL.md.tmpl +0 -0
- /package/{plan-eng-review → skills/plan-eng-review}/SKILL.md +0 -0
- /package/{plan-eng-review → skills/plan-eng-review}/SKILL.md.tmpl +0 -0
- /package/{qa → skills/qa}/SKILL.md +0 -0
- /package/{qa → skills/qa}/SKILL.md.tmpl +0 -0
- /package/{qa-only → skills/qa-only}/SKILL.md +0 -0
- /package/{qa-only → skills/qa-only}/SKILL.md.tmpl +0 -0
- /package/{retro → skills/retro}/SKILL.md +0 -0
- /package/{retro → skills/retro}/SKILL.md.tmpl +0 -0
- /package/{review → skills/review}/SKILL.md +0 -0
- /package/{review → skills/review}/SKILL.md.tmpl +0 -0
- /package/{setup-browser-cookies → skills/setup-browser-cookies}/SKILL.md +0 -0
- /package/{setup-browser-cookies → skills/setup-browser-cookies}/SKILL.md.tmpl +0 -0
- /package/{setup-deploy → skills/setup-deploy}/SKILL.md +0 -0
- /package/{setup-deploy → skills/setup-deploy}/SKILL.md.tmpl +0 -0
- /package/{ship → skills/ship}/SKILL.md +0 -0
- /package/{ship → skills/ship}/SKILL.md.tmpl +0 -0
- /package/{unfreeze → skills/unfreeze}/SKILL.md +0 -0
- /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
|
+

|
|
68
|
+
2. {Action}
|
|
69
|
+

|
|
70
|
+
3. **Observe:** {what goes wrong}
|
|
71
|
+

|
|
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:** 
|
|
85
|
+
**After:** 
|
|
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
|
+
```
|