superpowers-zh 1.5.0 → 1.6.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.pi/extensions/superpowers.ts +121 -0
- package/README.md +58 -28
- package/RELEASE-NOTES.zh.md +21 -0
- package/assets/qr-wechat.jpg +0 -0
- package/assets/sponsors/5cookie-code.png +0 -0
- package/bin/superpowers-zh.js +17 -18
- package/docs/README.antigravity.md +7 -7
- package/docs/README.kimi.md +86 -0
- package/docs/README.pi.md +54 -0
- package/docs/README.trae.md +6 -4
- package/gemini-extension.json +1 -1
- package/hooks/session-start +4 -12
- package/package.json +15 -3
- package/skills/brainstorming/SKILL.md +5 -0
- package/skills/brainstorming/scripts/frame-template.html +25 -26
- package/skills/brainstorming/scripts/helper.js +101 -22
- package/skills/brainstorming/scripts/server.cjs +399 -30
- package/skills/brainstorming/scripts/start-server.sh +70 -9
- package/skills/brainstorming/scripts/stop-server.sh +66 -2
- package/skills/chinese-code-review/SKILL.md +5 -0
- package/skills/chinese-commit-conventions/SKILL.md +5 -0
- package/skills/chinese-documentation/SKILL.md +5 -0
- package/skills/chinese-git-workflow/SKILL.md +5 -0
- package/skills/dispatching-parallel-agents/SKILL.md +5 -0
- package/skills/executing-plans/SKILL.md +7 -2
- package/skills/finishing-a-development-branch/SKILL.md +7 -2
- package/skills/mcp-builder/SKILL.md +5 -0
- package/skills/receiving-code-review/SKILL.md +5 -0
- package/skills/requesting-code-review/SKILL.md +5 -0
- package/skills/subagent-driven-development/SKILL.md +5 -0
- package/skills/systematic-debugging/SKILL.md +5 -0
- package/skills/test-driven-development/SKILL.md +5 -0
- package/skills/using-git-worktrees/SKILL.md +14 -24
- package/skills/using-superpowers/SKILL.md +5 -0
- package/skills/using-superpowers/references/pi-tools.md +28 -0
- package/skills/verification-before-completion/SKILL.md +5 -0
- package/skills/workflow-runner/SKILL.md +5 -0
- package/skills/writing-plans/SKILL.md +5 -0
- package/skills/writing-skills/SKILL.md +5 -0
package/gemini-extension.json
CHANGED
package/hooks/session-start
CHANGED
|
@@ -7,13 +7,6 @@ set -euo pipefail
|
|
|
7
7
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
8
|
PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
9
9
|
|
|
10
|
-
# Check if legacy skills directory exists and build warning
|
|
11
|
-
warning_message=""
|
|
12
|
-
legacy_skills_dir="${HOME}/.config/superpowers/skills"
|
|
13
|
-
if [ -d "$legacy_skills_dir" ]; then
|
|
14
|
-
warning_message="\n\n<important-reminder>IN YOUR FIRST REPLY AFTER SEEING THIS MESSAGE YOU MUST TELL THE USER:⚠️ **WARNING:** Superpowers now uses Claude Code's skills system. Custom skills in ~/.config/superpowers/skills will not be read. Move custom skills to ~/.claude/skills instead. To make this message go away, remove ~/.config/superpowers/skills</important-reminder>"
|
|
15
|
-
fi
|
|
16
|
-
|
|
17
10
|
# Read using-superpowers content
|
|
18
11
|
using_superpowers_content=$(cat "${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md" 2>&1 || echo "Error reading using-superpowers skill")
|
|
19
12
|
|
|
@@ -31,8 +24,7 @@ escape_for_json() {
|
|
|
31
24
|
}
|
|
32
25
|
|
|
33
26
|
using_superpowers_escaped=$(escape_for_json "$using_superpowers_content")
|
|
34
|
-
|
|
35
|
-
session_context="<EXTREMELY_IMPORTANT>\nYou have superpowers.\n\n**Below is the full content of your 'superpowers:using-superpowers' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**\n\n${using_superpowers_escaped}\n\n${warning_escaped}\n</EXTREMELY_IMPORTANT>"
|
|
27
|
+
session_context="<EXTREMELY_IMPORTANT>\nYou have superpowers.\n\n**Below is the full content of your 'superpowers:using-superpowers' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**\n\n${using_superpowers_escaped}\n</EXTREMELY_IMPORTANT>"
|
|
36
28
|
|
|
37
29
|
# Output context injection as JSON.
|
|
38
30
|
# Cursor hooks expect additional_context (snake_case).
|
|
@@ -45,13 +37,13 @@ session_context="<EXTREMELY_IMPORTANT>\nYou have superpowers.\n\n**Below is the
|
|
|
45
37
|
# See: https://github.com/obra/superpowers/issues/571
|
|
46
38
|
if [ -n "${CURSOR_PLUGIN_ROOT:-}" ]; then
|
|
47
39
|
# Cursor sets CURSOR_PLUGIN_ROOT (may also set CLAUDE_PLUGIN_ROOT)
|
|
48
|
-
printf '{\n "additional_context": "%s"\n}\n' "$session_context"
|
|
40
|
+
printf '{\n "additional_context": "%s"\n}\n' "$session_context" | cat
|
|
49
41
|
elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -z "${COPILOT_CLI:-}" ]; then
|
|
50
42
|
# Claude Code sets CLAUDE_PLUGIN_ROOT without COPILOT_CLI
|
|
51
|
-
printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context"
|
|
43
|
+
printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context" | cat
|
|
52
44
|
else
|
|
53
45
|
# Copilot CLI (sets COPILOT_CLI=1) or unknown platform — SDK standard format
|
|
54
|
-
printf '{\n "additionalContext": "%s"\n}\n' "$session_context"
|
|
46
|
+
printf '{\n "additionalContext": "%s"\n}\n' "$session_context" | cat
|
|
55
47
|
fi
|
|
56
48
|
|
|
57
49
|
exit 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superpowers-zh",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20.0.0"
|
|
6
6
|
},
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"superpowers-zh": "bin/superpowers-zh.js"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
+
"site": "node site/build.mjs",
|
|
15
|
+
"site:deploy": "node site/build.mjs && wrangler pages deploy site/dist --project-name=superpowers-zh-site",
|
|
14
16
|
"version": "node scripts/sync-plugin-version.js && git add .claude-plugin/plugin.json .claude-plugin/marketplace.json .cursor-plugin/plugin.json .codex-plugin/plugin.json gemini-extension.json"
|
|
15
17
|
},
|
|
16
18
|
"files": [
|
|
@@ -24,6 +26,7 @@
|
|
|
24
26
|
".codex-plugin/",
|
|
25
27
|
".opencode/INSTALL.md",
|
|
26
28
|
".opencode/plugins/",
|
|
29
|
+
".pi/extensions/",
|
|
27
30
|
"CLAUDE.md",
|
|
28
31
|
"GEMINI.md",
|
|
29
32
|
"RELEASE-NOTES.md",
|
|
@@ -60,11 +63,20 @@
|
|
|
60
63
|
"中文",
|
|
61
64
|
"tdd",
|
|
62
65
|
"debugging",
|
|
63
|
-
"code-review"
|
|
66
|
+
"code-review",
|
|
67
|
+
"pi-package"
|
|
64
68
|
],
|
|
69
|
+
"pi": {
|
|
70
|
+
"skills": [
|
|
71
|
+
"./skills"
|
|
72
|
+
],
|
|
73
|
+
"extensions": [
|
|
74
|
+
"./.pi/extensions/superpowers.ts"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
65
77
|
"repository": {
|
|
66
78
|
"type": "git",
|
|
67
|
-
"url": "https://github.com/jnMetaCode/superpowers-zh.git"
|
|
79
|
+
"url": "git+https://github.com/jnMetaCode/superpowers-zh.git"
|
|
68
80
|
},
|
|
69
81
|
"homepage": "https://github.com/jnMetaCode/superpowers-zh",
|
|
70
82
|
"author": "jnMetaCode",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
*
|
|
10
10
|
* This template provides a consistent frame with:
|
|
11
11
|
* - OS-aware light/dark theming
|
|
12
|
-
* -
|
|
12
|
+
* - Header branding and connection status
|
|
13
13
|
* - Scrollable main content area
|
|
14
14
|
* - CSS helpers for common UI patterns
|
|
15
15
|
*
|
|
16
|
-
* Content is injected via placeholder comment in #
|
|
16
|
+
* Content is injected via placeholder comment in #frame-content.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
@@ -63,34 +63,37 @@
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/* ===== FRAME STRUCTURE ===== */
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
border-bottom: 1px solid var(--border);
|
|
73
|
-
flex-shrink: 0;
|
|
66
|
+
.brand { display: flex; align-items: center; min-width: 0; overflow: hidden; color: var(--text-secondary); line-height: 1; }
|
|
67
|
+
.brand a { color: inherit; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; min-width: 0; max-width: 100%; line-height: 1; }
|
|
68
|
+
.brand-copy { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1; transform: translateY(-1px); }
|
|
69
|
+
.brand-logo { display: block; height: 1em; width: auto; max-width: 180px; flex-shrink: 0; filter: invert(1); }
|
|
70
|
+
@media (prefers-color-scheme: dark) {
|
|
71
|
+
.brand-logo { filter: none; }
|
|
74
72
|
}
|
|
75
|
-
.
|
|
76
|
-
.
|
|
77
|
-
.header .status::before { content: ''; width: 6px; height: 6px; background: var(--success); border-radius: 50%; }
|
|
73
|
+
.status { font-size: 0.7rem; color: var(--status-color, var(--success)); display: flex; align-items: center; gap: 0.4rem; justify-self: end; white-space: nowrap; line-height: 1; }
|
|
74
|
+
.status::before { content: ''; width: 6px; height: 6px; background: var(--status-color, var(--success)); border-radius: 50%; }
|
|
78
75
|
|
|
79
76
|
.main { flex: 1; overflow-y: auto; }
|
|
80
|
-
#
|
|
77
|
+
#frame-content { padding: 2rem; min-height: 100%; }
|
|
81
78
|
|
|
82
|
-
.
|
|
79
|
+
.header {
|
|
83
80
|
background: var(--bg-secondary);
|
|
84
|
-
border-
|
|
81
|
+
border-bottom: 1px solid var(--border);
|
|
85
82
|
padding: 0.5rem 1.5rem;
|
|
86
83
|
flex-shrink: 0;
|
|
87
|
-
|
|
84
|
+
display: grid;
|
|
85
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
86
|
+
align-items: center;
|
|
87
|
+
gap: 1rem;
|
|
88
|
+
min-height: 42px;
|
|
88
89
|
}
|
|
89
|
-
.
|
|
90
|
+
.header .brand { justify-self: start; width: 100%; font-size: 0.75rem; line-height: 1; }
|
|
91
|
+
.header .status { grid-column: 2; line-height: 1; }
|
|
92
|
+
.header span {
|
|
90
93
|
font-size: 0.75rem;
|
|
91
94
|
color: var(--text-secondary);
|
|
92
95
|
}
|
|
93
|
-
.
|
|
96
|
+
.header .selected-text {
|
|
94
97
|
color: var(--accent);
|
|
95
98
|
font-weight: 500;
|
|
96
99
|
}
|
|
@@ -196,19 +199,15 @@
|
|
|
196
199
|
</head>
|
|
197
200
|
<body>
|
|
198
201
|
<div class="header">
|
|
199
|
-
|
|
200
|
-
<div class="status">
|
|
202
|
+
<!-- BRANDING -->
|
|
203
|
+
<div class="status">Connecting…</div>
|
|
201
204
|
</div>
|
|
202
205
|
|
|
203
206
|
<div class="main">
|
|
204
|
-
<div id="
|
|
207
|
+
<div id="frame-content">
|
|
205
208
|
<!-- CONTENT -->
|
|
206
209
|
</div>
|
|
207
210
|
</div>
|
|
208
211
|
|
|
209
|
-
<div class="indicator-bar">
|
|
210
|
-
<span id="indicator-text">Click an option above, then return to the terminal</span>
|
|
211
|
-
</div>
|
|
212
|
-
|
|
213
212
|
</body>
|
|
214
213
|
</html>
|
|
@@ -1,26 +1,120 @@
|
|
|
1
1
|
(function() {
|
|
2
|
-
const
|
|
2
|
+
const MIN_RECONNECT_MS = 500;
|
|
3
|
+
const MAX_RECONNECT_MS = 30000;
|
|
4
|
+
const TOMBSTONE_AFTER_MS = 15000; // show the "paused" overlay after this long disconnected
|
|
5
|
+
|
|
6
|
+
// Pure: next backoff delay (doubles, capped). Exported for unit tests.
|
|
7
|
+
function nextReconnectDelay(current, max) {
|
|
8
|
+
return Math.min(current * 2, max);
|
|
9
|
+
}
|
|
10
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
11
|
+
module.exports = { nextReconnectDelay, MIN_RECONNECT_MS, MAX_RECONNECT_MS, TOMBSTONE_AFTER_MS };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Everything below is browser-only; bail out when loaded in Node (tests).
|
|
15
|
+
if (typeof window === 'undefined') return;
|
|
16
|
+
|
|
3
17
|
let ws = null;
|
|
4
18
|
let eventQueue = [];
|
|
19
|
+
let reconnectDelay = MIN_RECONNECT_MS;
|
|
20
|
+
let reconnectTimer = null;
|
|
21
|
+
let disconnectedSince = null;
|
|
22
|
+
let everConnected = false;
|
|
23
|
+
let tombstoneShown = false;
|
|
24
|
+
|
|
25
|
+
function sessionKey() {
|
|
26
|
+
try {
|
|
27
|
+
return window.sessionStorage && window.sessionStorage.getItem('brainstorm-session-key');
|
|
28
|
+
} catch (e) {}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function websocketUrl() {
|
|
33
|
+
const key = sessionKey();
|
|
34
|
+
return 'ws://' + window.location.host + (key ? '/?key=' + encodeURIComponent(key) : '');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function reloadAfterRecovery() {
|
|
38
|
+
const key = sessionKey();
|
|
39
|
+
if (key) {
|
|
40
|
+
window.location.replace('/?key=' + encodeURIComponent(key));
|
|
41
|
+
} else {
|
|
42
|
+
window.location.reload();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Reflect connection state in the frame's status pill (absent on full-doc screens).
|
|
47
|
+
function setStatus(state) {
|
|
48
|
+
const el = document.querySelector('.status');
|
|
49
|
+
if (!el) return;
|
|
50
|
+
const map = {
|
|
51
|
+
connecting: ['Connecting…', 'var(--text-tertiary)'],
|
|
52
|
+
connected: ['Connected', 'var(--success)'],
|
|
53
|
+
reconnecting: ['Reconnecting…', 'var(--warning)'],
|
|
54
|
+
disconnected: ['Disconnected', 'var(--error)']
|
|
55
|
+
};
|
|
56
|
+
const [text, color] = map[state] || map.disconnected;
|
|
57
|
+
el.textContent = text;
|
|
58
|
+
el.style.setProperty('--status-color', color);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Self-styled so it works on framed and full-document screens alike.
|
|
62
|
+
function showTombstone() {
|
|
63
|
+
if (tombstoneShown) return;
|
|
64
|
+
tombstoneShown = true;
|
|
65
|
+
const el = document.createElement('div');
|
|
66
|
+
el.id = 'bs-tombstone';
|
|
67
|
+
el.style.cssText = 'position:fixed;inset:0;z-index:99999;display:flex;' +
|
|
68
|
+
'align-items:center;justify-content:center;padding:2rem;text-align:center;' +
|
|
69
|
+
'background:rgba(20,20,22,0.92);color:#f5f5f7;font-family:system-ui,sans-serif';
|
|
70
|
+
el.innerHTML = '<div style="max-width:480px">' +
|
|
71
|
+
'<h2 style="margin:0 0 .5rem;font-weight:600">Companion paused</h2>' +
|
|
72
|
+
'<p style="margin:0;opacity:.85">This brainstorm companion has stopped. ' +
|
|
73
|
+
'Ask your coding agent to bring it back — this page reconnects automatically.</p></div>';
|
|
74
|
+
if (document.body) document.body.appendChild(el);
|
|
75
|
+
}
|
|
5
76
|
|
|
6
77
|
function connect() {
|
|
7
|
-
|
|
78
|
+
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
|
|
79
|
+
setStatus(everConnected ? 'reconnecting' : 'connecting');
|
|
80
|
+
ws = new WebSocket(websocketUrl());
|
|
8
81
|
|
|
9
82
|
ws.onopen = () => {
|
|
83
|
+
const recovered = tombstoneShown;
|
|
84
|
+
everConnected = true;
|
|
85
|
+
disconnectedSince = null;
|
|
86
|
+
reconnectDelay = MIN_RECONNECT_MS;
|
|
87
|
+
tombstoneShown = false;
|
|
88
|
+
setStatus('connected');
|
|
10
89
|
eventQueue.forEach(e => ws.send(JSON.stringify(e)));
|
|
11
90
|
eventQueue = [];
|
|
91
|
+
// Recovered from a tombstoned outage (e.g. the server restarted on the same
|
|
92
|
+
// port) — reload through the keyed bootstrap when possible so the cookie is
|
|
93
|
+
// refreshed before the visible URL returns to bare /.
|
|
94
|
+
if (recovered) reloadAfterRecovery();
|
|
12
95
|
};
|
|
13
96
|
|
|
14
97
|
ws.onmessage = (msg) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
98
|
+
let data;
|
|
99
|
+
try { data = JSON.parse(msg.data); } catch (e) { return; }
|
|
100
|
+
if (data.type === 'reload') window.location.reload();
|
|
19
101
|
};
|
|
20
102
|
|
|
21
103
|
ws.onclose = () => {
|
|
22
|
-
|
|
104
|
+
ws = null;
|
|
105
|
+
if (disconnectedSince === null) disconnectedSince = Date.now();
|
|
106
|
+
if (Date.now() - disconnectedSince >= TOMBSTONE_AFTER_MS) {
|
|
107
|
+
setStatus('disconnected');
|
|
108
|
+
showTombstone();
|
|
109
|
+
} else {
|
|
110
|
+
setStatus('reconnecting');
|
|
111
|
+
}
|
|
112
|
+
reconnectTimer = setTimeout(connect, reconnectDelay);
|
|
113
|
+
reconnectDelay = nextReconnectDelay(reconnectDelay, MAX_RECONNECT_MS);
|
|
23
114
|
};
|
|
115
|
+
|
|
116
|
+
// Let onclose own reconnection so we don't schedule it twice.
|
|
117
|
+
ws.onerror = () => { try { ws.close(); } catch (e) {} };
|
|
24
118
|
}
|
|
25
119
|
|
|
26
120
|
function sendEvent(event) {
|
|
@@ -44,21 +138,6 @@
|
|
|
44
138
|
id: target.id || null
|
|
45
139
|
});
|
|
46
140
|
|
|
47
|
-
// Update indicator bar (defer so toggleSelect runs first)
|
|
48
|
-
setTimeout(() => {
|
|
49
|
-
const indicator = document.getElementById('indicator-text');
|
|
50
|
-
if (!indicator) return;
|
|
51
|
-
const container = target.closest('.options') || target.closest('.cards');
|
|
52
|
-
const selected = container ? container.querySelectorAll('.selected') : [];
|
|
53
|
-
if (selected.length === 0) {
|
|
54
|
-
indicator.textContent = 'Click an option above, then return to the terminal';
|
|
55
|
-
} else if (selected.length === 1) {
|
|
56
|
-
const label = selected[0].querySelector('h3, .content h3, .card-body h3')?.textContent?.trim() || selected[0].dataset.choice;
|
|
57
|
-
indicator.innerHTML = '<span class="selected-text">' + label + ' selected</span> — return to terminal to continue';
|
|
58
|
-
} else {
|
|
59
|
-
indicator.innerHTML = '<span class="selected-text">' + selected.length + ' selected</span> — return to terminal to continue';
|
|
60
|
-
}
|
|
61
|
-
}, 0);
|
|
62
141
|
});
|
|
63
142
|
|
|
64
143
|
// Frame UI: selection tracking
|