slashdev 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CLAUDE.md +1 -1
  2. package/claude-skills/hormozi/SKILL.md +133 -0
  3. package/claude-skills/hormozi/references/core-four-leads.md +195 -0
  4. package/claude-skills/hormozi/references/critique-rubric.md +132 -0
  5. package/claude-skills/hormozi/references/enhancements.md +161 -0
  6. package/claude-skills/hormozi/references/examples.md +208 -0
  7. package/claude-skills/hormozi/references/grand-slam-offer.md +105 -0
  8. package/claude-skills/hormozi/references/lead-magnets.md +115 -0
  9. package/claude-skills/hormozi/references/problem-solution-stack.md +125 -0
  10. package/claude-skills/hormozi/references/value-equation.md +105 -0
  11. package/hooks/post-response.sh +62 -1
  12. package/hooks/prompt-submit.sh +42 -0
  13. package/package.json +1 -1
  14. package/src/commands/setup-internals.js +22 -0
  15. package/vendor/gstack/.env.example +0 -5
  16. package/vendor/gstack/autoplan/SKILL.md +0 -1116
  17. package/vendor/gstack/browse/SKILL.md +0 -538
  18. package/vendor/gstack/canary/SKILL.md +0 -587
  19. package/vendor/gstack/careful/SKILL.md +0 -59
  20. package/vendor/gstack/codex/SKILL.md +0 -862
  21. package/vendor/gstack/connect-chrome/SKILL.md +0 -549
  22. package/vendor/gstack/cso/ACKNOWLEDGEMENTS.md +0 -14
  23. package/vendor/gstack/cso/SKILL.md +0 -929
  24. package/vendor/gstack/design-consultation/SKILL.md +0 -962
  25. package/vendor/gstack/design-review/SKILL.md +0 -1314
  26. package/vendor/gstack/design-shotgun/SKILL.md +0 -730
  27. package/vendor/gstack/document-release/SKILL.md +0 -718
  28. package/vendor/gstack/freeze/SKILL.md +0 -82
  29. package/vendor/gstack/gstack-upgrade/SKILL.md +0 -232
  30. package/vendor/gstack/guard/SKILL.md +0 -82
  31. package/vendor/gstack/investigate/SKILL.md +0 -504
  32. package/vendor/gstack/land-and-deploy/SKILL.md +0 -1367
  33. package/vendor/gstack/office-hours/SKILL.md +0 -1317
  34. package/vendor/gstack/plan-ceo-review/SKILL.md +0 -1537
  35. package/vendor/gstack/plan-design-review/SKILL.md +0 -1227
  36. package/vendor/gstack/plan-eng-review/SKILL.md +0 -1120
  37. package/vendor/gstack/qa/SKILL.md +0 -1136
  38. package/vendor/gstack/qa/references/issue-taxonomy.md +0 -85
  39. package/vendor/gstack/qa/templates/qa-report-template.md +0 -126
  40. package/vendor/gstack/qa-only/SKILL.md +0 -726
  41. package/vendor/gstack/retro/SKILL.md +0 -1197
  42. package/vendor/gstack/review/SKILL.md +0 -1138
  43. package/vendor/gstack/review/TODOS-format.md +0 -62
  44. package/vendor/gstack/review/checklist.md +0 -220
  45. package/vendor/gstack/review/design-checklist.md +0 -132
  46. package/vendor/gstack/review/greptile-triage.md +0 -220
  47. package/vendor/gstack/setup-browser-cookies/SKILL.md +0 -348
  48. package/vendor/gstack/setup-deploy/SKILL.md +0 -528
  49. package/vendor/gstack/ship/SKILL.md +0 -1931
  50. package/vendor/gstack/unfreeze/SKILL.md +0 -40
@@ -3,6 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const https = require('https');
5
5
  const path = require('path');
6
+ const { execFileSync } = require('child_process');
6
7
 
7
8
  // Debug log
8
9
  const DEBUG_LOG = path.join(require('os').homedir(), '.slashdev', 'hook-debug.log');
@@ -181,6 +182,54 @@ try {
181
182
  debug(`Conversation type derivation error: ${e.message}`);
182
183
  }
183
184
 
185
+ // v2: was this typed by a person? Claude Code also records background-task
186
+ // notifications, messages from other sessions, skill loads and IDE
187
+ // context as user turns; headless runs (claude -p) are scripts.
188
+ let source = 'human';
189
+ try {
190
+ const head = lastPrompt.trimStart().slice(0, 80).toLowerCase();
191
+ const entrypoint = String(process.env.CLAUDE_CODE_ENTRYPOINT || '');
192
+ if (!lastPrompt.trim()) source = 'unknown';
193
+ else if (entrypoint.startsWith('sdk')) source = 'scripted';
194
+ else if (head.startsWith('<task-notification>')) source = 'task_notification';
195
+ else if (head.startsWith('another claude session sent')) source = 'session_message';
196
+ else if (head.startsWith('base directory for this skill')) source = 'skill';
197
+ else if (head.startsWith('<ide_') || head.startsWith('<system-reminder>')) source = 'ide_context';
198
+ } catch (e) {
199
+ debug(`Source classification error: ${e.message}`);
200
+ }
201
+
202
+ // v2: when the prompt was sent (stamped by prompt-submit.sh).
203
+ let promptSubmittedAt = null;
204
+ try {
205
+ const safeId = String(sessionId).replace(/[^A-Za-z0-9_-]/g, '');
206
+ const stamp = path.join(require('os').homedir(), '.slashdev', 'sessions', `${safeId}.json`);
207
+ if (safeId && fs.existsSync(stamp)) {
208
+ promptSubmittedAt = JSON.parse(fs.readFileSync(stamp, 'utf8')).submitted_at || null;
209
+ }
210
+ } catch (e) {
211
+ debug(`Prompt stamp read error: ${e.message}`);
212
+ }
213
+
214
+ // v2: repo, branch and commit, to tie a session to what it shipped.
215
+ // The remote URL is reduced to owner/repo so no credentials leave.
216
+ let repo = null;
217
+ let branch = null;
218
+ let commitSha = null;
219
+ try {
220
+ const cwd = hookData.cwd;
221
+ if (cwd && fs.existsSync(cwd)) {
222
+ const git = (args) => execFileSync('git', ['-C', cwd, ...args], { encoding: 'utf8', timeout: 1500, stdio: ['ignore', 'pipe', 'ignore'] }).trim();
223
+ branch = git(['rev-parse', '--abbrev-ref', 'HEAD']) || null;
224
+ commitSha = git(['rev-parse', 'HEAD']) || null;
225
+ const remote = git(['config', '--get', 'remote.origin.url']);
226
+ const m = remote.match(/[:/]([^/:]+\/[^/]+?)(?:\.git)?\/?$/);
227
+ repo = m ? m[1] : null;
228
+ }
229
+ } catch (e) {
230
+ // Not a git repo, or git missing: leave them null.
231
+ }
232
+
184
233
  const payload = JSON.stringify({
185
234
  engineer,
186
235
  session_id: sessionId,
@@ -192,9 +241,21 @@ try {
192
241
  conversation_type: conversationType,
193
242
  model: model,
194
243
  message_count: messageCount,
244
+ source,
245
+ prompt_submitted_at: promptSubmittedAt,
246
+ repo,
247
+ branch,
248
+ commit_sha: commitSha,
249
+ cli_version: config.packageVersion || null,
195
250
  });
196
251
 
197
- debug(`Sending: engineer=${engineer}, session=${sessionId.substring(0, 12)}, prompt_len=${lastPrompt.length}, response_len=${lastResponse.length}, tools=[${toolsUsed.join(',')}], skills=[${skillsUsed.join(',')}], project=${projectName}, type=${conversationType}, model=${model}, messages=${messageCount}`);
252
+ debug(`Sending: engineer=${engineer}, session=${sessionId.substring(0, 12)}, prompt_len=${lastPrompt.length}, response_len=${lastResponse.length}, source=${source}, submitted=${promptSubmittedAt}, repo=${repo}, branch=${branch}, tools=[${toolsUsed.join(',')}], skills=[${skillsUsed.join(',')}], project=${projectName}, type=${conversationType}, model=${model}, messages=${messageCount}`);
253
+
254
+ // Dry run (testing): log the payload, send nothing.
255
+ if (process.env.SLASHDEV_LOG_DRY === '1') {
256
+ debug(`DRY payload: ${payload}`);
257
+ process.exit(0);
258
+ }
198
259
 
199
260
  // POST to API
200
261
  const url = new URL('https://v1api.slashdev.io/api/logs');
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Claude Code UserPromptSubmit hook: note when a prompt was sent, so the Stop
4
+ // hook (post-response.sh) can log the time a person asked as well as the time
5
+ // Claude finished. Local only — no network, no output — and it never blocks
6
+ // a prompt: any error just exits 0.
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ try {
13
+ let input = '';
14
+ process.stdin.setEncoding('utf8');
15
+ process.stdin.on('data', (chunk) => { input += chunk; });
16
+ process.stdin.on('end', () => {
17
+ try {
18
+ const hookData = input ? JSON.parse(input) : {};
19
+ const sessionId = String(hookData.session_id || '').replace(/[^A-Za-z0-9_-]/g, '');
20
+ if (!sessionId) process.exit(0);
21
+
22
+ const dir = path.join(os.homedir(), '.slashdev', 'sessions');
23
+ fs.mkdirSync(dir, { recursive: true });
24
+ fs.writeFileSync(
25
+ path.join(dir, `${sessionId}.json`),
26
+ JSON.stringify({ submitted_at: new Date().toISOString() })
27
+ );
28
+
29
+ // Housekeeping: drop stamps older than two days.
30
+ const cutoff = Date.now() - 2 * 24 * 60 * 60 * 1000;
31
+ for (const name of fs.readdirSync(dir)) {
32
+ try {
33
+ const file = path.join(dir, name);
34
+ if (fs.statSync(file).mtimeMs < cutoff) fs.unlinkSync(file);
35
+ } catch (_) { /* ignore */ }
36
+ }
37
+ } catch (_) { /* never block a prompt */ }
38
+ process.exit(0);
39
+ });
40
+ } catch (_) {
41
+ process.exit(0);
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slashdev",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Skills & workflows for AI agents in the Slashdev ecosystem",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -119,6 +119,28 @@ export function installHooks() {
119
119
  settings.hooks.Stop = stopHooks;
120
120
  }
121
121
 
122
+ // Register UserPromptSubmit hook: stamps when each prompt was sent, so the
123
+ // Stop hook can log it (local only, never blocks a prompt).
124
+ const submitCommand = join(CLAUDE_HOOKS_DIR, 'prompt-submit.sh');
125
+ if (existsSync(submitCommand)) {
126
+ const submitHooks = settings.hooks.UserPromptSubmit || [];
127
+ const submitRegistered = submitHooks.some(
128
+ (entry) => Array.isArray(entry.hooks) && entry.hooks.some((h) => h.command && h.command.includes('prompt-submit.sh'))
129
+ );
130
+
131
+ if (!submitRegistered) {
132
+ submitHooks.push({
133
+ hooks: [
134
+ {
135
+ type: 'command',
136
+ command: submitCommand,
137
+ },
138
+ ],
139
+ });
140
+ settings.hooks.UserPromptSubmit = submitHooks;
141
+ }
142
+ }
143
+
122
144
  // Register PreToolUse hook for the install guard (Bash tool only).
123
145
  const guardCommand = join(CLAUDE_HOOKS_DIR, 'pre-install-guard.sh');
124
146
  if (existsSync(guardCommand)) {
@@ -1,5 +0,0 @@
1
- # Copy to .env and fill in values
2
- # bun auto-loads .env — no dotenv needed
3
-
4
- # Required for LLM-as-judge evals (bun run test:eval)
5
- ANTHROPIC_API_KEY=sk-ant-your-key-here