@jspg-ai/coding-bb 0.0.1 → 0.0.2-beta.23

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 (145) hide show
  1. package/cbb/bin/cbb.js +6 -0
  2. package/cbb/bin/cbbm.js +6 -0
  3. package/cbb/dev-standards/rules/cbb-ai-behavior.md +103 -103
  4. package/cbb/dev-standards/rules/cbb-priority.md +55 -55
  5. package/cbb/lib/install/claude-code.js +2 -0
  6. package/cbb/lib/install/init.js +496 -290
  7. package/cbb/lib/install/opencode.js +31 -0
  8. package/cbb/lib/install/qoder.js +2 -0
  9. package/cbb/lib/install/workspaces.js +53 -13
  10. package/cbb/lib/openspec/index.js +3 -3
  11. package/cbb/lib/superpowers/index.js +1 -1
  12. package/cbb/lib/utils/check-update.js +16 -14
  13. package/cbb/lib/utils/gitignore.js +2 -1
  14. package/cbb/lib/utils/output.js +92 -4
  15. package/cbb/lib/utils/version.js +2 -31
  16. package/cbb/lib/wiki/cli.js +25 -25
  17. package/cbb/lib/wiki/index.js +16 -16
  18. package/cbb/tools/cbb-design-to-wiki/SKILL.md +14 -14
  19. package/cbb/tools/cbb-wiki-ops/SKILL.md +22 -22
  20. package/cbb/worktrees/_shared/scripts/find-target-worktree.js +2 -2
  21. package/cbb/worktrees/_shared/scripts/find-workspace-root.js +7 -7
  22. package/cbb/worktrees/_shared/scripts/push-core.js +18 -0
  23. package/cbb/worktrees/commands/{close.md → worktree-close.md} +8 -8
  24. package/cbb/worktrees/commands/{init.md → worktree-init.md} +9 -9
  25. package/cbb/worktrees/commands/{push.md → worktree-push.md} +3 -3
  26. package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/SKILL.md +20 -20
  27. package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/check-env.js +4 -4
  28. package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/check-unarchived.js +1 -1
  29. package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/delete-branches.js +3 -3
  30. package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/discover-apps.js +6 -6
  31. package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-close}/scripts/find-target-worktree.js +2 -2
  32. package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-close}/scripts/find-workspace-root.js +7 -7
  33. package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/remove-worktrees.js +4 -4
  34. package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/safety-check.js +4 -4
  35. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/SKILL.md +81 -107
  36. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/auto-open.js +1 -1
  37. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/check-env-deep.js +4 -4
  38. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/check-env.js +6 -6
  39. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/check-repos.js +4 -4
  40. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/create-branches.js +4 -4
  41. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/create-worktrees.js +8 -7
  42. package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-init}/scripts/find-workspace-root.js +7 -7
  43. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/install-ai.js +33 -33
  44. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/parse-config.js +7 -7
  45. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/push-branches.js +9 -7
  46. package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-init}/scripts/push-core.js +18 -0
  47. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/sync-repos.js +10 -12
  48. package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/update-gitignore.js +19 -6
  49. package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/SKILL.md +16 -16
  50. package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/commit-worktrees.js +2 -2
  51. package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/find-target-worktree.js +2 -2
  52. package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-push}/scripts/find-workspace-root.js +7 -7
  53. package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/push-core.js +18 -0
  54. package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/push-worktrees.js +7 -5
  55. package/config/workspace-config.sample.json +18 -0
  56. package/config/workspaces.json +11 -6
  57. package/openspec/.version +6 -6
  58. package/openspec/commands/apply.md +7 -7
  59. package/openspec/commands/archive.md +7 -7
  60. package/openspec/commands/bulk-archive.md +7 -7
  61. package/openspec/commands/continue.md +7 -7
  62. package/openspec/commands/explore.md +7 -7
  63. package/openspec/commands/ff.md +7 -7
  64. package/openspec/commands/new.md +7 -7
  65. package/openspec/commands/onboard.md +7 -7
  66. package/openspec/commands/propose.md +7 -7
  67. package/openspec/commands/sync.md +7 -7
  68. package/openspec/commands/update.md +7 -7
  69. package/openspec/commands/verify.md +7 -7
  70. package/openspec/skills/openspec-apply-change/SKILL.md +187 -187
  71. package/openspec/skills/openspec-archive-change/SKILL.md +181 -181
  72. package/openspec/skills/openspec-bulk-archive-change/SKILL.md +338 -338
  73. package/openspec/skills/openspec-continue-change/SKILL.md +117 -117
  74. package/openspec/skills/openspec-explore/SKILL.md +342 -342
  75. package/openspec/skills/openspec-ff-change/SKILL.md +116 -116
  76. package/openspec/skills/openspec-new-change/SKILL.md +76 -76
  77. package/openspec/skills/openspec-onboard/SKILL.md +560 -560
  78. package/openspec/skills/openspec-propose/SKILL.md +162 -162
  79. package/openspec/skills/openspec-sync-specs/SKILL.md +261 -261
  80. package/openspec/skills/openspec-update-change/SKILL.md +90 -90
  81. package/openspec/skills/openspec-verify-change/SKILL.md +174 -174
  82. package/package.json +4 -3
  83. package/superpowers/.version +6 -6
  84. package/superpowers/skills/brainstorming/SKILL.md +250 -250
  85. package/superpowers/skills/brainstorming/scripts/helper.js +167 -167
  86. package/superpowers/skills/brainstorming/scripts/start-server.sh +209 -209
  87. package/superpowers/skills/brainstorming/scripts/stop-server.sh +120 -120
  88. package/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +49 -49
  89. package/superpowers/skills/brainstorming/visual-companion.md +299 -299
  90. package/superpowers/skills/dispatching-parallel-agents/SKILL.md +167 -167
  91. package/superpowers/skills/executing-plans/SKILL.md +64 -64
  92. package/superpowers/skills/finishing-a-development-branch/SKILL.md +225 -225
  93. package/superpowers/skills/receiving-code-review/SKILL.md +205 -205
  94. package/superpowers/skills/requesting-code-review/SKILL.md +95 -95
  95. package/superpowers/skills/requesting-code-review/code-reviewer.md +181 -181
  96. package/superpowers/skills/subagent-driven-development/SKILL.md +568 -568
  97. package/superpowers/skills/subagent-driven-development/implementer-prompt.md +154 -154
  98. package/superpowers/skills/subagent-driven-development/re-review-prompt.md +115 -115
  99. package/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md +207 -207
  100. package/superpowers/skills/systematic-debugging/CREATION-LOG.md +119 -119
  101. package/superpowers/skills/systematic-debugging/SKILL.md +283 -283
  102. package/superpowers/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
  103. package/superpowers/skills/systematic-debugging/condition-based-waiting.md +115 -115
  104. package/superpowers/skills/systematic-debugging/defense-in-depth.md +122 -122
  105. package/superpowers/skills/systematic-debugging/find-polluter.sh +72 -72
  106. package/superpowers/skills/systematic-debugging/root-cause-tracing.md +169 -169
  107. package/superpowers/skills/systematic-debugging/test-academic.md +14 -14
  108. package/superpowers/skills/systematic-debugging/test-pressure-1.md +58 -58
  109. package/superpowers/skills/systematic-debugging/test-pressure-2.md +68 -68
  110. package/superpowers/skills/systematic-debugging/test-pressure-3.md +69 -69
  111. package/superpowers/skills/test-driven-development/SKILL.md +320 -320
  112. package/superpowers/skills/test-driven-development/writing-good-tests.md +198 -198
  113. package/superpowers/skills/using-git-worktrees/SKILL.md +167 -167
  114. package/superpowers/skills/using-superpowers/SKILL.md +63 -63
  115. package/superpowers/skills/using-superpowers/references/antigravity-tools.md +23 -23
  116. package/superpowers/skills/using-superpowers/references/codex-tools.md +108 -108
  117. package/superpowers/skills/using-superpowers/references/gemini-tools.md +63 -63
  118. package/superpowers/skills/using-superpowers/references/hermes-tools.md +56 -56
  119. package/superpowers/skills/using-superpowers/references/pi-tools.md +16 -16
  120. package/superpowers/skills/verification-before-completion/SKILL.md +120 -120
  121. package/superpowers/skills/writing-plans/SKILL.md +171 -171
  122. package/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +49 -49
  123. package/superpowers/skills/writing-skills/SKILL.md +679 -679
  124. package/superpowers/skills/writing-skills/anthropic-best-practices.md +1150 -1150
  125. package/superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -189
  126. package/superpowers/skills/writing-skills/persuasion-principles.md +187 -187
  127. package/superpowers/skills/writing-skills/render-graphs.js +169 -169
  128. package/superpowers/skills/writing-skills/testing-skills-with-subagents.md +384 -384
  129. package/cbb/worktrees/commands/extend.md +0 -65
  130. package/cbb/worktrees/skills/openspec-close-worktree/scripts/find-target-worktree.js +0 -96
  131. package/cbb/worktrees/skills/openspec-extend-worktree/SKILL.md +0 -390
  132. package/cbb/worktrees/skills/openspec-extend-worktree/scripts/check-env.js +0 -95
  133. package/cbb/worktrees/skills/openspec-extend-worktree/scripts/check-repos.js +0 -98
  134. package/cbb/worktrees/skills/openspec-extend-worktree/scripts/create-branches-and-worktrees.js +0 -135
  135. package/cbb/worktrees/skills/openspec-extend-worktree/scripts/install-ai.js +0 -150
  136. package/cbb/worktrees/skills/openspec-extend-worktree/scripts/list-available-apps.js +0 -88
  137. package/cbb/worktrees/skills/openspec-extend-worktree/scripts/sync-repos.js +0 -93
  138. package/cbb/worktrees/skills/openspec-init-worktree/scripts/find-workspace-root.js +0 -67
  139. package/cbb/worktrees/skills/openspec-init-worktree/scripts/generate-app-options.js +0 -93
  140. package/cbb/worktrees/skills/openspec-init-worktree/scripts/push-core.js +0 -136
  141. package/cbb/worktrees/skills/openspec-push-worktrees/scripts/silence-popup.js +0 -23
  142. package/config/config.sample.json +0 -16
  143. /package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/silence-popup.js +0 -0
  144. /package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-init}/scripts/silence-popup.js +0 -0
  145. /package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-push}/scripts/silence-popup.js +0 -0
@@ -1,209 +1,209 @@
1
- #!/usr/bin/env bash
2
- # Start the brainstorm server and output connection info
3
- # Usage: start-server.sh [--project-dir <path>] [--host <bind-host>] [--url-host <display-host>] [--foreground] [--background]
4
- #
5
- # Starts server on a random high port, outputs JSON with URL.
6
- # Each session gets its own directory to avoid conflicts.
7
- #
8
- # Options:
9
- # --project-dir <path> Store session files under <path>/.superpowers/brainstorm/
10
- # instead of /tmp. Files persist after server stops.
11
- # --host <bind-host> Host/interface to bind (default: 127.0.0.1).
12
- # Use 0.0.0.0 in remote/containerized environments.
13
- # --url-host <host> Hostname shown in returned URL JSON.
14
- # --idle-timeout-minutes <n> Shut down after n minutes idle (default 240 = 4h).
15
- # --open Auto-open the browser on the first screen (use only
16
- # after the user approves the visual companion).
17
- # --foreground Run server in the current terminal (no backgrounding).
18
- # --background Force background mode (overrides Codex auto-foreground).
19
-
20
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
21
-
22
- # Parse arguments
23
- PROJECT_DIR=""
24
- FOREGROUND="false"
25
- FORCE_BACKGROUND="false"
26
- BIND_HOST="127.0.0.1"
27
- URL_HOST=""
28
- IDLE_TIMEOUT_MINUTES=""
29
- while [[ $# -gt 0 ]]; do
30
- case "$1" in
31
- --project-dir)
32
- PROJECT_DIR="$2"
33
- shift 2
34
- ;;
35
- --host)
36
- BIND_HOST="$2"
37
- shift 2
38
- ;;
39
- --url-host)
40
- URL_HOST="$2"
41
- shift 2
42
- ;;
43
- --idle-timeout-minutes)
44
- IDLE_TIMEOUT_MINUTES="$2"
45
- shift 2
46
- ;;
47
- --open)
48
- export BRAINSTORM_OPEN=1
49
- shift
50
- ;;
51
- --foreground|--no-daemon)
52
- FOREGROUND="true"
53
- shift
54
- ;;
55
- --background|--daemon)
56
- FORCE_BACKGROUND="true"
57
- shift
58
- ;;
59
- *)
60
- echo "{\"error\": \"Unknown argument: $1\"}"
61
- exit 1
62
- ;;
63
- esac
64
- done
65
-
66
- if [[ -z "$URL_HOST" ]]; then
67
- if [[ "$BIND_HOST" == "127.0.0.1" || "$BIND_HOST" == "localhost" ]]; then
68
- URL_HOST="localhost"
69
- else
70
- URL_HOST="$BIND_HOST"
71
- fi
72
- fi
73
-
74
- if [[ -n "$IDLE_TIMEOUT_MINUTES" ]]; then
75
- if ! [[ "$IDLE_TIMEOUT_MINUTES" =~ ^[0-9]+$ ]] || [[ "$IDLE_TIMEOUT_MINUTES" -lt 1 ]]; then
76
- echo "{\"error\": \"--idle-timeout-minutes must be a positive integer\"}"
77
- exit 1
78
- fi
79
- export BRAINSTORM_IDLE_TIMEOUT_MS=$(( IDLE_TIMEOUT_MINUTES * 60 * 1000 ))
80
- fi
81
-
82
- is_windows_like_shell() {
83
- case "${OSTYPE:-}" in
84
- msys*|cygwin*|mingw*) return 0 ;;
85
- esac
86
- if [[ -n "${MSYSTEM:-}" ]]; then
87
- return 0
88
- fi
89
- local uname_s
90
- uname_s="$(uname -s 2>/dev/null || true)"
91
- case "$uname_s" in
92
- MSYS*|MINGW*|CYGWIN*) return 0 ;;
93
- esac
94
- return 1
95
- }
96
-
97
- # Some environments reap detached/background processes. Auto-foreground when detected.
98
- if [[ -n "${CODEX_CI:-}" && "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
99
- FOREGROUND="true"
100
- fi
101
-
102
- # Windows/Git Bash reaps nohup background processes. Auto-foreground when detected.
103
- if [[ "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
104
- if is_windows_like_shell; then
105
- FOREGROUND="true"
106
- fi
107
- fi
108
-
109
- # Session files (server.log, server-info, .last-token) embed the session key —
110
- # keep everything this script and the server create owner-only.
111
- umask 077
112
-
113
- # Generate unique session directory
114
- SESSION_ID="$$-$(date +%s)"
115
-
116
- if [[ -n "$PROJECT_DIR" ]]; then
117
- SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
118
- # Persist the bound port and key per project so a restart reuses them and an
119
- # already-open browser tab reconnects to the same URL with a valid cookie.
120
- export BRAINSTORM_PORT_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-port"
121
- export BRAINSTORM_TOKEN_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-token"
122
- else
123
- SESSION_DIR="/tmp/brainstorm-${SESSION_ID}"
124
- fi
125
-
126
- STATE_DIR="${SESSION_DIR}/state"
127
- PID_FILE="${STATE_DIR}/server.pid"
128
- LOG_FILE="${STATE_DIR}/server.log"
129
- SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
130
-
131
- # Create fresh session directory with content and state peers
132
- mkdir -p "${SESSION_DIR}/content" "$STATE_DIR"
133
-
134
- SERVER_ID=""
135
- if [[ -r /dev/urandom ]]; then
136
- SERVER_ID="$(od -An -N24 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n' || true)"
137
- fi
138
- if ! [[ "$SERVER_ID" =~ ^[A-Za-z0-9_-]{32,64}$ ]]; then
139
- SERVER_ID="$(printf '%08x%08x%08x%08x' "$$" "$(date +%s)" "${RANDOM:-0}" "${RANDOM:-0}")"
140
- fi
141
- printf '%s\n' "$SERVER_ID" > "$SERVER_ID_FILE"
142
- chmod 600 "$SERVER_ID_FILE" 2>/dev/null || true
143
-
144
- # Kill any existing server
145
- if [[ -f "$PID_FILE" ]]; then
146
- old_pid=$(cat "$PID_FILE")
147
- kill "$old_pid" 2>/dev/null
148
- rm -f "$PID_FILE"
149
- fi
150
-
151
- cd "$SCRIPT_DIR" || exit 1
152
-
153
- # Resolve the harness PID (grandparent of this script).
154
- # $PPID is the ephemeral shell the harness spawned to run us — it dies
155
- # when this script exits. The harness itself is $PPID's parent.
156
- OWNER_PID="$(ps -o ppid= -p "$PPID" 2>/dev/null | tr -d ' ')"
157
- if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then
158
- OWNER_PID="$PPID"
159
- fi
160
-
161
- # Windows/MSYS2: Node.js cannot see POSIX PIDs from the MSYS2 namespace.
162
- # Passing a PID node cannot verify causes server to log owner-pid-invalid
163
- # and self-terminate at the 60-second lifecycle check. Clear it so the
164
- # watchdog is disabled and the idle timeout becomes the only shutdown trigger.
165
- if is_windows_like_shell; then
166
- OWNER_PID=""
167
- fi
168
-
169
- # Foreground mode for environments that reap detached/background processes.
170
- if [[ "$FOREGROUND" == "true" ]]; then
171
- env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" &
172
- SERVER_PID=$!
173
- echo "$SERVER_PID" > "$PID_FILE"
174
- wait "$SERVER_PID"
175
- exit $?
176
- fi
177
-
178
- # Start server, capturing output to log file
179
- # Use nohup to survive shell exit; disown to remove from job table
180
- nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" > "$LOG_FILE" 2>&1 &
181
- SERVER_PID=$!
182
- disown "$SERVER_PID" 2>/dev/null
183
- echo "$SERVER_PID" > "$PID_FILE"
184
-
185
- # Wait for server-started message (check log file)
186
- for _ in {1..50}; do
187
- if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
188
- # Verify server is still alive after a short window (catches process reapers)
189
- alive="true"
190
- for _ in {1..20}; do
191
- if ! kill -0 "$SERVER_PID" 2>/dev/null; then
192
- alive="false"
193
- break
194
- fi
195
- sleep 0.1
196
- done
197
- if [[ "$alive" != "true" ]]; then
198
- echo "{\"error\": \"Server started but was killed. Retry in a persistent terminal with: $SCRIPT_DIR/start-server.sh${PROJECT_DIR:+ --project-dir $PROJECT_DIR} --host $BIND_HOST --url-host $URL_HOST --foreground\"}"
199
- exit 1
200
- fi
201
- grep "server-started" "$LOG_FILE" | head -1
202
- exit 0
203
- fi
204
- sleep 0.1
205
- done
206
-
207
- # Timeout - server didn't start
208
- echo '{"error": "Server failed to start within 5 seconds"}'
209
- exit 1
1
+ #!/usr/bin/env bash
2
+ # Start the brainstorm server and output connection info
3
+ # Usage: start-server.sh [--project-dir <path>] [--host <bind-host>] [--url-host <display-host>] [--foreground] [--background]
4
+ #
5
+ # Starts server on a random high port, outputs JSON with URL.
6
+ # Each session gets its own directory to avoid conflicts.
7
+ #
8
+ # Options:
9
+ # --project-dir <path> Store session files under <path>/.superpowers/brainstorm/
10
+ # instead of /tmp. Files persist after server stops.
11
+ # --host <bind-host> Host/interface to bind (default: 127.0.0.1).
12
+ # Use 0.0.0.0 in remote/containerized environments.
13
+ # --url-host <host> Hostname shown in returned URL JSON.
14
+ # --idle-timeout-minutes <n> Shut down after n minutes idle (default 240 = 4h).
15
+ # --open Auto-open the browser on the first screen (use only
16
+ # after the user approves the visual companion).
17
+ # --foreground Run server in the current terminal (no backgrounding).
18
+ # --background Force background mode (overrides Codex auto-foreground).
19
+
20
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
21
+
22
+ # Parse arguments
23
+ PROJECT_DIR=""
24
+ FOREGROUND="false"
25
+ FORCE_BACKGROUND="false"
26
+ BIND_HOST="127.0.0.1"
27
+ URL_HOST=""
28
+ IDLE_TIMEOUT_MINUTES=""
29
+ while [[ $# -gt 0 ]]; do
30
+ case "$1" in
31
+ --project-dir)
32
+ PROJECT_DIR="$2"
33
+ shift 2
34
+ ;;
35
+ --host)
36
+ BIND_HOST="$2"
37
+ shift 2
38
+ ;;
39
+ --url-host)
40
+ URL_HOST="$2"
41
+ shift 2
42
+ ;;
43
+ --idle-timeout-minutes)
44
+ IDLE_TIMEOUT_MINUTES="$2"
45
+ shift 2
46
+ ;;
47
+ --open)
48
+ export BRAINSTORM_OPEN=1
49
+ shift
50
+ ;;
51
+ --foreground|--no-daemon)
52
+ FOREGROUND="true"
53
+ shift
54
+ ;;
55
+ --background|--daemon)
56
+ FORCE_BACKGROUND="true"
57
+ shift
58
+ ;;
59
+ *)
60
+ echo "{\"error\": \"Unknown argument: $1\"}"
61
+ exit 1
62
+ ;;
63
+ esac
64
+ done
65
+
66
+ if [[ -z "$URL_HOST" ]]; then
67
+ if [[ "$BIND_HOST" == "127.0.0.1" || "$BIND_HOST" == "localhost" ]]; then
68
+ URL_HOST="localhost"
69
+ else
70
+ URL_HOST="$BIND_HOST"
71
+ fi
72
+ fi
73
+
74
+ if [[ -n "$IDLE_TIMEOUT_MINUTES" ]]; then
75
+ if ! [[ "$IDLE_TIMEOUT_MINUTES" =~ ^[0-9]+$ ]] || [[ "$IDLE_TIMEOUT_MINUTES" -lt 1 ]]; then
76
+ echo "{\"error\": \"--idle-timeout-minutes must be a positive integer\"}"
77
+ exit 1
78
+ fi
79
+ export BRAINSTORM_IDLE_TIMEOUT_MS=$(( IDLE_TIMEOUT_MINUTES * 60 * 1000 ))
80
+ fi
81
+
82
+ is_windows_like_shell() {
83
+ case "${OSTYPE:-}" in
84
+ msys*|cygwin*|mingw*) return 0 ;;
85
+ esac
86
+ if [[ -n "${MSYSTEM:-}" ]]; then
87
+ return 0
88
+ fi
89
+ local uname_s
90
+ uname_s="$(uname -s 2>/dev/null || true)"
91
+ case "$uname_s" in
92
+ MSYS*|MINGW*|CYGWIN*) return 0 ;;
93
+ esac
94
+ return 1
95
+ }
96
+
97
+ # Some environments reap detached/background processes. Auto-foreground when detected.
98
+ if [[ -n "${CODEX_CI:-}" && "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
99
+ FOREGROUND="true"
100
+ fi
101
+
102
+ # Windows/Git Bash reaps nohup background processes. Auto-foreground when detected.
103
+ if [[ "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
104
+ if is_windows_like_shell; then
105
+ FOREGROUND="true"
106
+ fi
107
+ fi
108
+
109
+ # Session files (server.log, server-info, .last-token) embed the session key —
110
+ # keep everything this script and the server create owner-only.
111
+ umask 077
112
+
113
+ # Generate unique session directory
114
+ SESSION_ID="$$-$(date +%s)"
115
+
116
+ if [[ -n "$PROJECT_DIR" ]]; then
117
+ SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
118
+ # Persist the bound port and key per project so a restart reuses them and an
119
+ # already-open browser tab reconnects to the same URL with a valid cookie.
120
+ export BRAINSTORM_PORT_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-port"
121
+ export BRAINSTORM_TOKEN_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-token"
122
+ else
123
+ SESSION_DIR="/tmp/brainstorm-${SESSION_ID}"
124
+ fi
125
+
126
+ STATE_DIR="${SESSION_DIR}/state"
127
+ PID_FILE="${STATE_DIR}/server.pid"
128
+ LOG_FILE="${STATE_DIR}/server.log"
129
+ SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
130
+
131
+ # Create fresh session directory with content and state peers
132
+ mkdir -p "${SESSION_DIR}/content" "$STATE_DIR"
133
+
134
+ SERVER_ID=""
135
+ if [[ -r /dev/urandom ]]; then
136
+ SERVER_ID="$(od -An -N24 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n' || true)"
137
+ fi
138
+ if ! [[ "$SERVER_ID" =~ ^[A-Za-z0-9_-]{32,64}$ ]]; then
139
+ SERVER_ID="$(printf '%08x%08x%08x%08x' "$$" "$(date +%s)" "${RANDOM:-0}" "${RANDOM:-0}")"
140
+ fi
141
+ printf '%s\n' "$SERVER_ID" > "$SERVER_ID_FILE"
142
+ chmod 600 "$SERVER_ID_FILE" 2>/dev/null || true
143
+
144
+ # Kill any existing server
145
+ if [[ -f "$PID_FILE" ]]; then
146
+ old_pid=$(cat "$PID_FILE")
147
+ kill "$old_pid" 2>/dev/null
148
+ rm -f "$PID_FILE"
149
+ fi
150
+
151
+ cd "$SCRIPT_DIR" || exit 1
152
+
153
+ # Resolve the harness PID (grandparent of this script).
154
+ # $PPID is the ephemeral shell the harness spawned to run us — it dies
155
+ # when this script exits. The harness itself is $PPID's parent.
156
+ OWNER_PID="$(ps -o ppid= -p "$PPID" 2>/dev/null | tr -d ' ')"
157
+ if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then
158
+ OWNER_PID="$PPID"
159
+ fi
160
+
161
+ # Windows/MSYS2: Node.js cannot see POSIX PIDs from the MSYS2 namespace.
162
+ # Passing a PID node cannot verify causes server to log owner-pid-invalid
163
+ # and self-terminate at the 60-second lifecycle check. Clear it so the
164
+ # watchdog is disabled and the idle timeout becomes the only shutdown trigger.
165
+ if is_windows_like_shell; then
166
+ OWNER_PID=""
167
+ fi
168
+
169
+ # Foreground mode for environments that reap detached/background processes.
170
+ if [[ "$FOREGROUND" == "true" ]]; then
171
+ env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" &
172
+ SERVER_PID=$!
173
+ echo "$SERVER_PID" > "$PID_FILE"
174
+ wait "$SERVER_PID"
175
+ exit $?
176
+ fi
177
+
178
+ # Start server, capturing output to log file
179
+ # Use nohup to survive shell exit; disown to remove from job table
180
+ nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" > "$LOG_FILE" 2>&1 &
181
+ SERVER_PID=$!
182
+ disown "$SERVER_PID" 2>/dev/null
183
+ echo "$SERVER_PID" > "$PID_FILE"
184
+
185
+ # Wait for server-started message (check log file)
186
+ for _ in {1..50}; do
187
+ if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
188
+ # Verify server is still alive after a short window (catches process reapers)
189
+ alive="true"
190
+ for _ in {1..20}; do
191
+ if ! kill -0 "$SERVER_PID" 2>/dev/null; then
192
+ alive="false"
193
+ break
194
+ fi
195
+ sleep 0.1
196
+ done
197
+ if [[ "$alive" != "true" ]]; then
198
+ echo "{\"error\": \"Server started but was killed. Retry in a persistent terminal with: $SCRIPT_DIR/start-server.sh${PROJECT_DIR:+ --project-dir $PROJECT_DIR} --host $BIND_HOST --url-host $URL_HOST --foreground\"}"
199
+ exit 1
200
+ fi
201
+ grep "server-started" "$LOG_FILE" | head -1
202
+ exit 0
203
+ fi
204
+ sleep 0.1
205
+ done
206
+
207
+ # Timeout - server didn't start
208
+ echo '{"error": "Server failed to start within 5 seconds"}'
209
+ exit 1
@@ -1,120 +1,120 @@
1
- #!/usr/bin/env bash
2
- # Stop the brainstorm server and clean up
3
- # Usage: stop-server.sh <session_dir>
4
- #
5
- # Kills the server process. Only deletes session directory if it's
6
- # under /tmp (ephemeral). Persistent directories (.superpowers/) are
7
- # kept so mockups can be reviewed later.
8
-
9
- SESSION_DIR="$1"
10
-
11
- if [[ -z "$SESSION_DIR" ]]; then
12
- echo '{"error": "Usage: stop-server.sh <session_dir>"}'
13
- exit 1
14
- fi
15
-
16
- STATE_DIR="${SESSION_DIR}/state"
17
- PID_FILE="${STATE_DIR}/server.pid"
18
- SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
19
-
20
- mark_stopped() {
21
- local reason="$1"
22
- rm -f "${STATE_DIR}/server-info"
23
- printf '{"reason":"%s","timestamp":%s}\n' "$reason" "$(date +%s)" > "${STATE_DIR}/server-stopped"
24
- }
25
-
26
- read_expected_server_id() {
27
- [[ -f "$SERVER_ID_FILE" ]] || return 1
28
- local id
29
- id="$(tr -d '\r\n' < "$SERVER_ID_FILE" 2>/dev/null || true)"
30
- [[ "$id" =~ ^[A-Za-z0-9_-]{32,64}$ ]] || return 1
31
- printf '%s\n' "$id"
32
- }
33
-
34
- command_line_for_pid() {
35
- local pid="$1"
36
- if [[ -r "/proc/$pid/cmdline" ]]; then
37
- tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null || true
38
- return 0
39
- fi
40
- ps -ww -p "$pid" -o command= 2>/dev/null || ps -f -p "$pid" 2>/dev/null | sed '1d' || true
41
- }
42
-
43
- command_has_server_id() {
44
- local pid="$1"
45
- local expected="$2"
46
- local expected_arg="--brainstorm-server-id=$expected"
47
- if [[ -r "/proc/$pid/cmdline" ]]; then
48
- local arg
49
- while IFS= read -r -d '' arg || [[ -n "$arg" ]]; do
50
- [[ "$arg" == "$expected_arg" ]] && return 0
51
- done < "/proc/$pid/cmdline"
52
- return 1
53
- fi
54
- local command_line
55
- command_line="$(command_line_for_pid "$pid")"
56
- [[ -n "$command_line" ]] || return 1
57
- case " $command_line " in
58
- *" $expected_arg "*) return 0 ;;
59
- *) return 1 ;;
60
- esac
61
- }
62
-
63
- # Confirm a PID has this session's per-start instance id, not just a familiar
64
- # process name. Ambiguous or legacy metadata fails closed as stale_pid.
65
- is_brainstorm_server() {
66
- kill -0 "$1" 2>/dev/null || return 1
67
- local expected_id
68
- expected_id="$(read_expected_server_id)" || return 1
69
- command_has_server_id "$1" "$expected_id" || return 1
70
- return 0
71
- }
72
-
73
- if [[ -f "$PID_FILE" ]]; then
74
- pid=$(cat "$PID_FILE")
75
-
76
- # Refuse to signal a PID we can't prove is our server. A stale pid file may
77
- # point at an unrelated process after a reboot/PID wraparound.
78
- if ! is_brainstorm_server "$pid"; then
79
- rm -f "$PID_FILE" "$SERVER_ID_FILE"
80
- mark_stopped "stale_pid"
81
- echo '{"status": "stale_pid"}'
82
- exit 0
83
- fi
84
-
85
- # Try to stop gracefully, fallback to force if still alive
86
- kill "$pid" 2>/dev/null || true
87
-
88
- # Wait for graceful shutdown (up to ~2s)
89
- for _ in {1..20}; do
90
- if ! kill -0 "$pid" 2>/dev/null; then
91
- break
92
- fi
93
- sleep 0.1
94
- done
95
-
96
- # If still running, escalate to SIGKILL
97
- if kill -0 "$pid" 2>/dev/null; then
98
- kill -9 "$pid" 2>/dev/null || true
99
-
100
- # Give SIGKILL a moment to take effect
101
- sleep 0.1
102
- fi
103
-
104
- if kill -0 "$pid" 2>/dev/null; then
105
- echo '{"status": "failed", "error": "process still running"}'
106
- exit 1
107
- fi
108
-
109
- rm -f "$PID_FILE" "$SERVER_ID_FILE" "${STATE_DIR}/server.log"
110
- mark_stopped "stop-server.sh"
111
-
112
- # Only delete ephemeral /tmp directories
113
- if [[ "$SESSION_DIR" == /tmp/* ]]; then
114
- rm -rf "$SESSION_DIR"
115
- fi
116
-
117
- echo '{"status": "stopped"}'
118
- else
119
- echo '{"status": "not_running"}'
120
- fi
1
+ #!/usr/bin/env bash
2
+ # Stop the brainstorm server and clean up
3
+ # Usage: stop-server.sh <session_dir>
4
+ #
5
+ # Kills the server process. Only deletes session directory if it's
6
+ # under /tmp (ephemeral). Persistent directories (.superpowers/) are
7
+ # kept so mockups can be reviewed later.
8
+
9
+ SESSION_DIR="$1"
10
+
11
+ if [[ -z "$SESSION_DIR" ]]; then
12
+ echo '{"error": "Usage: stop-server.sh <session_dir>"}'
13
+ exit 1
14
+ fi
15
+
16
+ STATE_DIR="${SESSION_DIR}/state"
17
+ PID_FILE="${STATE_DIR}/server.pid"
18
+ SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
19
+
20
+ mark_stopped() {
21
+ local reason="$1"
22
+ rm -f "${STATE_DIR}/server-info"
23
+ printf '{"reason":"%s","timestamp":%s}\n' "$reason" "$(date +%s)" > "${STATE_DIR}/server-stopped"
24
+ }
25
+
26
+ read_expected_server_id() {
27
+ [[ -f "$SERVER_ID_FILE" ]] || return 1
28
+ local id
29
+ id="$(tr -d '\r\n' < "$SERVER_ID_FILE" 2>/dev/null || true)"
30
+ [[ "$id" =~ ^[A-Za-z0-9_-]{32,64}$ ]] || return 1
31
+ printf '%s\n' "$id"
32
+ }
33
+
34
+ command_line_for_pid() {
35
+ local pid="$1"
36
+ if [[ -r "/proc/$pid/cmdline" ]]; then
37
+ tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null || true
38
+ return 0
39
+ fi
40
+ ps -ww -p "$pid" -o command= 2>/dev/null || ps -f -p "$pid" 2>/dev/null | sed '1d' || true
41
+ }
42
+
43
+ command_has_server_id() {
44
+ local pid="$1"
45
+ local expected="$2"
46
+ local expected_arg="--brainstorm-server-id=$expected"
47
+ if [[ -r "/proc/$pid/cmdline" ]]; then
48
+ local arg
49
+ while IFS= read -r -d '' arg || [[ -n "$arg" ]]; do
50
+ [[ "$arg" == "$expected_arg" ]] && return 0
51
+ done < "/proc/$pid/cmdline"
52
+ return 1
53
+ fi
54
+ local command_line
55
+ command_line="$(command_line_for_pid "$pid")"
56
+ [[ -n "$command_line" ]] || return 1
57
+ case " $command_line " in
58
+ *" $expected_arg "*) return 0 ;;
59
+ *) return 1 ;;
60
+ esac
61
+ }
62
+
63
+ # Confirm a PID has this session's per-start instance id, not just a familiar
64
+ # process name. Ambiguous or legacy metadata fails closed as stale_pid.
65
+ is_brainstorm_server() {
66
+ kill -0 "$1" 2>/dev/null || return 1
67
+ local expected_id
68
+ expected_id="$(read_expected_server_id)" || return 1
69
+ command_has_server_id "$1" "$expected_id" || return 1
70
+ return 0
71
+ }
72
+
73
+ if [[ -f "$PID_FILE" ]]; then
74
+ pid=$(cat "$PID_FILE")
75
+
76
+ # Refuse to signal a PID we can't prove is our server. A stale pid file may
77
+ # point at an unrelated process after a reboot/PID wraparound.
78
+ if ! is_brainstorm_server "$pid"; then
79
+ rm -f "$PID_FILE" "$SERVER_ID_FILE"
80
+ mark_stopped "stale_pid"
81
+ echo '{"status": "stale_pid"}'
82
+ exit 0
83
+ fi
84
+
85
+ # Try to stop gracefully, fallback to force if still alive
86
+ kill "$pid" 2>/dev/null || true
87
+
88
+ # Wait for graceful shutdown (up to ~2s)
89
+ for _ in {1..20}; do
90
+ if ! kill -0 "$pid" 2>/dev/null; then
91
+ break
92
+ fi
93
+ sleep 0.1
94
+ done
95
+
96
+ # If still running, escalate to SIGKILL
97
+ if kill -0 "$pid" 2>/dev/null; then
98
+ kill -9 "$pid" 2>/dev/null || true
99
+
100
+ # Give SIGKILL a moment to take effect
101
+ sleep 0.1
102
+ fi
103
+
104
+ if kill -0 "$pid" 2>/dev/null; then
105
+ echo '{"status": "failed", "error": "process still running"}'
106
+ exit 1
107
+ fi
108
+
109
+ rm -f "$PID_FILE" "$SERVER_ID_FILE" "${STATE_DIR}/server.log"
110
+ mark_stopped "stop-server.sh"
111
+
112
+ # Only delete ephemeral /tmp directories
113
+ if [[ "$SESSION_DIR" == /tmp/* ]]; then
114
+ rm -rf "$SESSION_DIR"
115
+ fi
116
+
117
+ echo '{"status": "stopped"}'
118
+ else
119
+ echo '{"status": "not_running"}'
120
+ fi