qualia-framework 2.1.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/README.md +50 -0
- package/bin/cli.js +519 -0
- package/framework/agents/architecture-strategist.md +53 -0
- package/framework/agents/backend-agent.md +150 -0
- package/framework/agents/code-simplicity-reviewer.md +86 -0
- package/framework/agents/frontend-agent.md +111 -0
- package/framework/agents/kieran-typescript-reviewer.md +96 -0
- package/framework/agents/performance-oracle.md +111 -0
- package/framework/agents/qualia-codebase-mapper.md +760 -0
- package/framework/agents/qualia-debugger.md +1203 -0
- package/framework/agents/qualia-executor.md +881 -0
- package/framework/agents/qualia-integration-checker.md +423 -0
- package/framework/agents/qualia-phase-researcher.md +453 -0
- package/framework/agents/qualia-plan-checker.md +699 -0
- package/framework/agents/qualia-planner.md +1241 -0
- package/framework/agents/qualia-project-researcher.md +602 -0
- package/framework/agents/qualia-research-synthesizer.md +236 -0
- package/framework/agents/qualia-roadmapper.md +605 -0
- package/framework/agents/qualia-verifier.md +685 -0
- package/framework/agents/team-orchestrator.md +228 -0
- package/framework/agents/teams/full-stack-team.md +48 -0
- package/framework/agents/teams/optimize-team.md +53 -0
- package/framework/agents/teams/review-team.md +62 -0
- package/framework/agents/teams/ship-team.md +86 -0
- package/framework/agents/test-agent.md +182 -0
- package/framework/askpass.sh +2 -0
- package/framework/commands/design.md +53 -0
- package/framework/commands/quick-db.md +22 -0
- package/framework/config/retention.json +35 -0
- package/framework/core/PRINCIPLES.md +77 -0
- package/framework/hooks/auto-format.sh +45 -0
- package/framework/hooks/block-env-edit.sh +42 -0
- package/framework/hooks/branch-guard.sh +46 -0
- package/framework/hooks/confirm-delete.sh +56 -0
- package/framework/hooks/migration-validate.sh +68 -0
- package/framework/hooks/notification-speak.sh +15 -0
- package/framework/hooks/pre-commit.sh +80 -0
- package/framework/hooks/pre-compact.sh +55 -0
- package/framework/hooks/pre-deploy-gate.sh +151 -0
- package/framework/hooks/qualia-colors.sh +32 -0
- package/framework/hooks/retention-cleanup.sh +43 -0
- package/framework/hooks/save-session-state.sh +153 -0
- package/framework/hooks/session-context-loader.sh +28 -0
- package/framework/hooks/session-learn.sh +30 -0
- package/framework/knowledge/claudecode-bible.md +1384 -0
- package/framework/knowledge/client-prefs.md +22 -0
- package/framework/knowledge/common-fixes.md +25 -0
- package/framework/knowledge/deployment-map.md +35 -0
- package/framework/knowledge/email-signature.html +1 -0
- package/framework/knowledge/employees.md +8 -0
- package/framework/knowledge/learned-patterns.md +51 -0
- package/framework/knowledge/optimization-research-2026.md +137 -0
- package/framework/knowledge/qualia-context.md +67 -0
- package/framework/knowledge/supabase-patterns.md +50 -0
- package/framework/knowledge/voice-agent-patterns.md +46 -0
- package/framework/qualia-engine/VERSION +1 -0
- package/framework/qualia-engine/bin/qualia-tools.js +2160 -0
- package/framework/qualia-engine/bin/qualia-tools.test.js +1054 -0
- package/framework/qualia-engine/references/checkpoints.md +775 -0
- package/framework/qualia-engine/references/continuation-format.md +249 -0
- package/framework/qualia-engine/references/decimal-phase-calculation.md +65 -0
- package/framework/qualia-engine/references/design-quality.md +56 -0
- package/framework/qualia-engine/references/git-integration.md +254 -0
- package/framework/qualia-engine/references/git-planning-commit.md +50 -0
- package/framework/qualia-engine/references/model-profile-resolution.md +32 -0
- package/framework/qualia-engine/references/model-profiles.md +73 -0
- package/framework/qualia-engine/references/phase-argument-parsing.md +61 -0
- package/framework/qualia-engine/references/planning-config.md +195 -0
- package/framework/qualia-engine/references/questioning.md +141 -0
- package/framework/qualia-engine/references/tdd.md +263 -0
- package/framework/qualia-engine/references/ui-brand.md +160 -0
- package/framework/qualia-engine/references/verification-patterns.md +612 -0
- package/framework/qualia-engine/templates/DEBUG.md +159 -0
- package/framework/qualia-engine/templates/DESIGN.md +81 -0
- package/framework/qualia-engine/templates/UAT.md +247 -0
- package/framework/qualia-engine/templates/codebase/architecture.md +255 -0
- package/framework/qualia-engine/templates/codebase/concerns.md +310 -0
- package/framework/qualia-engine/templates/codebase/conventions.md +307 -0
- package/framework/qualia-engine/templates/codebase/integrations.md +280 -0
- package/framework/qualia-engine/templates/codebase/stack.md +186 -0
- package/framework/qualia-engine/templates/codebase/structure.md +285 -0
- package/framework/qualia-engine/templates/codebase/testing.md +480 -0
- package/framework/qualia-engine/templates/config.json +35 -0
- package/framework/qualia-engine/templates/context.md +283 -0
- package/framework/qualia-engine/templates/continue-here.md +78 -0
- package/framework/qualia-engine/templates/debug-subagent-prompt.md +91 -0
- package/framework/qualia-engine/templates/discovery.md +146 -0
- package/framework/qualia-engine/templates/milestone-archive.md +123 -0
- package/framework/qualia-engine/templates/milestone.md +115 -0
- package/framework/qualia-engine/templates/phase-prompt.md +567 -0
- package/framework/qualia-engine/templates/planner-subagent-prompt.md +117 -0
- package/framework/qualia-engine/templates/project.md +184 -0
- package/framework/qualia-engine/templates/projects/ai-agent.md +156 -0
- package/framework/qualia-engine/templates/projects/mobile-app.md +181 -0
- package/framework/qualia-engine/templates/projects/voice-agent.md +134 -0
- package/framework/qualia-engine/templates/projects/website.md +137 -0
- package/framework/qualia-engine/templates/requirements.md +231 -0
- package/framework/qualia-engine/templates/research-project/ARCHITECTURE.md +204 -0
- package/framework/qualia-engine/templates/research-project/FEATURES.md +147 -0
- package/framework/qualia-engine/templates/research-project/PITFALLS.md +200 -0
- package/framework/qualia-engine/templates/research-project/STACK.md +120 -0
- package/framework/qualia-engine/templates/research-project/SUMMARY.md +170 -0
- package/framework/qualia-engine/templates/research.md +552 -0
- package/framework/qualia-engine/templates/roadmap.md +202 -0
- package/framework/qualia-engine/templates/state.md +176 -0
- package/framework/qualia-engine/templates/summary-complex.md +59 -0
- package/framework/qualia-engine/templates/summary-minimal.md +41 -0
- package/framework/qualia-engine/templates/summary-standard.md +48 -0
- package/framework/qualia-engine/templates/summary.md +246 -0
- package/framework/qualia-engine/templates/user-setup.md +311 -0
- package/framework/qualia-engine/templates/verification-report.md +322 -0
- package/framework/qualia-engine/workflows/add-phase.md +179 -0
- package/framework/qualia-engine/workflows/add-todo.md +157 -0
- package/framework/qualia-engine/workflows/audit-milestone.md +241 -0
- package/framework/qualia-engine/workflows/check-todos.md +176 -0
- package/framework/qualia-engine/workflows/complete-milestone.md +858 -0
- package/framework/qualia-engine/workflows/diagnose-issues.md +219 -0
- package/framework/qualia-engine/workflows/discovery-phase.md +289 -0
- package/framework/qualia-engine/workflows/discuss-phase.md +534 -0
- package/framework/qualia-engine/workflows/execute-phase.md +559 -0
- package/framework/qualia-engine/workflows/execute-plan.md +438 -0
- package/framework/qualia-engine/workflows/help.md +470 -0
- package/framework/qualia-engine/workflows/insert-phase.md +220 -0
- package/framework/qualia-engine/workflows/list-phase-assumptions.md +178 -0
- package/framework/qualia-engine/workflows/map-codebase.md +327 -0
- package/framework/qualia-engine/workflows/new-milestone.md +363 -0
- package/framework/qualia-engine/workflows/new-project.md +1037 -0
- package/framework/qualia-engine/workflows/pause-work.md +122 -0
- package/framework/qualia-engine/workflows/plan-milestone-gaps.md +256 -0
- package/framework/qualia-engine/workflows/plan-phase.md +422 -0
- package/framework/qualia-engine/workflows/progress.md +354 -0
- package/framework/qualia-engine/workflows/quick.md +252 -0
- package/framework/qualia-engine/workflows/remove-phase.md +326 -0
- package/framework/qualia-engine/workflows/research-phase.md +74 -0
- package/framework/qualia-engine/workflows/resume-project.md +306 -0
- package/framework/qualia-engine/workflows/set-profile.md +80 -0
- package/framework/qualia-engine/workflows/settings.md +145 -0
- package/framework/qualia-engine/workflows/transition.md +556 -0
- package/framework/qualia-engine/workflows/update.md +197 -0
- package/framework/qualia-engine/workflows/verify-phase.md +195 -0
- package/framework/qualia-engine/workflows/verify-work.md +625 -0
- package/framework/rules/context7.md +11 -0
- package/framework/rules/deployment.md +29 -0
- package/framework/rules/frontend.md +33 -0
- package/framework/rules/security.md +12 -0
- package/framework/rules/speed.md +20 -0
- package/framework/scripts/__pycache__/say.cpython-314.pyc +0 -0
- package/framework/scripts/apply-retention.sh +120 -0
- package/framework/scripts/bootstrap-pop-os.sh +354 -0
- package/framework/scripts/claude-voice +13 -0
- package/framework/scripts/cleanup.sh +131 -0
- package/framework/scripts/cowork-mode.sh +141 -0
- package/framework/scripts/generate-project-claude-md.sh +153 -0
- package/framework/scripts/load-test-webhook.js +172 -0
- package/framework/scripts/say.py +236 -0
- package/framework/scripts/showcase-video-recorder/ffmpeg-builder.js +167 -0
- package/framework/scripts/showcase-video-recorder/playwright-helpers.js +216 -0
- package/framework/scripts/speak.py +55 -0
- package/framework/scripts/speak.sh +18 -0
- package/framework/scripts/status.sh +138 -0
- package/framework/scripts/sync-to-framework.sh +65 -0
- package/framework/scripts/voice-hotkey.py +227 -0
- package/framework/scripts/voice-input.sh +51 -0
- package/framework/skills/animate/SKILL.md +202 -0
- package/framework/skills/bolder/SKILL.md +144 -0
- package/framework/skills/browser-qa/SKILL.md +536 -0
- package/framework/skills/clarify/SKILL.md +179 -0
- package/framework/skills/colorize/SKILL.md +170 -0
- package/framework/skills/critique/SKILL.md +126 -0
- package/framework/skills/deep-research/SKILL.md +271 -0
- package/framework/skills/delight/SKILL.md +329 -0
- package/framework/skills/deploy/SKILL.md +261 -0
- package/framework/skills/deploy-verify/SKILL.md +377 -0
- package/framework/skills/deploy-verify/scripts/canary-check.sh +206 -0
- package/framework/skills/deploy-verify/scripts/check-console-errors.js +147 -0
- package/framework/skills/deploy-verify/scripts/check-cwv.js +139 -0
- package/framework/skills/deploy-verify/scripts/project-detect.sh +84 -0
- package/framework/skills/deploy-verify/scripts/verify.sh +548 -0
- package/framework/skills/design-quieter/SKILL.md +130 -0
- package/framework/skills/distill/SKILL.md +149 -0
- package/framework/skills/docs-lookup/SKILL.md +78 -0
- package/framework/skills/fcm-notifications/SKILL.md +125 -0
- package/framework/skills/financial-ledger/SKILL.md +1039 -0
- package/framework/skills/frontend-master/NOTICE.md +4 -0
- package/framework/skills/frontend-master/SKILL.md +127 -0
- package/framework/skills/frontend-master/reference/color-and-contrast.md +132 -0
- package/framework/skills/frontend-master/reference/interaction-design.md +123 -0
- package/framework/skills/frontend-master/reference/motion-design.md +99 -0
- package/framework/skills/frontend-master/reference/responsive-design.md +114 -0
- package/framework/skills/frontend-master/reference/spatial-design.md +100 -0
- package/framework/skills/frontend-master/reference/typography.md +131 -0
- package/framework/skills/frontend-master/reference/ux-writing.md +107 -0
- package/framework/skills/harden/SKILL.md +357 -0
- package/framework/skills/i18n-rtl/SKILL.md +752 -0
- package/framework/skills/learn/SKILL.md +71 -0
- package/framework/skills/memory/SKILL.md +50 -0
- package/framework/skills/mobile-expo/SKILL.md +864 -0
- package/framework/skills/mobile-expo/references/store-checklist.md +550 -0
- package/framework/skills/nestjs-backend/README.md +73 -0
- package/framework/skills/nestjs-backend/SKILL.md +446 -0
- package/framework/skills/nestjs-backend/references/templates.md +1173 -0
- package/framework/skills/normalize/SKILL.md +79 -0
- package/framework/skills/onboard/SKILL.md +242 -0
- package/framework/skills/polish/SKILL.md +209 -0
- package/framework/skills/pr/SKILL.md +66 -0
- package/framework/skills/qualia/SKILL.md +153 -0
- package/framework/skills/qualia-add-todo/SKILL.md +68 -0
- package/framework/skills/qualia-audit-milestone/SKILL.md +92 -0
- package/framework/skills/qualia-check-todos/SKILL.md +55 -0
- package/framework/skills/qualia-complete-milestone/SKILL.md +108 -0
- package/framework/skills/qualia-debug/SKILL.md +149 -0
- package/framework/skills/qualia-design/SKILL.md +203 -0
- package/framework/skills/qualia-discuss-phase/SKILL.md +72 -0
- package/framework/skills/qualia-execute-phase/SKILL.md +86 -0
- package/framework/skills/qualia-help/SKILL.md +67 -0
- package/framework/skills/qualia-idk/SKILL.md +352 -0
- package/framework/skills/qualia-list-phase-assumptions/SKILL.md +67 -0
- package/framework/skills/qualia-new-milestone/SKILL.md +72 -0
- package/framework/skills/qualia-new-project/SKILL.md +92 -0
- package/framework/skills/qualia-optimize/SKILL.md +417 -0
- package/framework/skills/qualia-pause-work/SKILL.md +96 -0
- package/framework/skills/qualia-plan-milestone-gaps/SKILL.md +57 -0
- package/framework/skills/qualia-plan-phase/SKILL.md +101 -0
- package/framework/skills/qualia-progress/SKILL.md +53 -0
- package/framework/skills/qualia-quick/SKILL.md +89 -0
- package/framework/skills/qualia-research-phase/SKILL.md +88 -0
- package/framework/skills/qualia-resume-work/SKILL.md +62 -0
- package/framework/skills/qualia-review/SKILL.md +263 -0
- package/framework/skills/qualia-start/SKILL.md +182 -0
- package/framework/skills/qualia-verify-work/SKILL.md +105 -0
- package/framework/skills/qualia-workflow/SKILL.md +130 -0
- package/framework/skills/rag/SKILL.md +750 -0
- package/framework/skills/responsive/SKILL.md +231 -0
- package/framework/skills/retro/SKILL.md +284 -0
- package/framework/skills/sakani-conventions/SKILL.md +136 -0
- package/framework/skills/sakani-conventions/evals/evals.json +23 -0
- package/framework/skills/sakani-conventions/references/entities.md +365 -0
- package/framework/skills/sakani-conventions/references/error-codes.md +95 -0
- package/framework/skills/seo-master/SKILL.md +490 -0
- package/framework/skills/seo-master/references/checklist.md +199 -0
- package/framework/skills/seo-master/references/structured-data.md +609 -0
- package/framework/skills/ship/SKILL.md +202 -0
- package/framework/skills/stack-researcher/SKILL.md +215 -0
- package/framework/skills/status/SKILL.md +154 -0
- package/framework/skills/status/scripts/health-check.sh +562 -0
- package/framework/skills/subscription-payments/SKILL.md +250 -0
- package/framework/skills/supabase/SKILL.md +973 -0
- package/framework/skills/supabase/references/templates.md +159 -0
- package/framework/skills/team/SKILL.md +67 -0
- package/framework/skills/test-runner/SKILL.md +202 -0
- package/framework/skills/voice-agent/SKILL.md +407 -0
- package/framework/skills/zoho-workflow/SKILL.md +51 -0
- package/framework/statusline-command.sh +117 -0
- package/package.json +24 -0
- package/profiles/fawzi.json +16 -0
- package/profiles/hasan.json +16 -0
- package/profiles/moayad.json +16 -0
- package/templates/CLAUDE-owner.md +52 -0
- package/templates/CLAUDE.md.hbs +58 -0
- package/templates/env.claude.template +12 -0
- package/templates/settings.json +141 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* FFmpeg Command Builder for Showcase Videos
|
|
4
|
+
* Bundled with showcase-video-recorder skill
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node ffmpeg-builder.js <input.webm> <output.mp4> [scenes.json]
|
|
8
|
+
*
|
|
9
|
+
* If scenes.json not provided, reads from stdin.
|
|
10
|
+
* scenes.json format: [{ "name": "landing", "time": 0, "speed": 1.0 }, ...]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const TARGET_DURATION = 60; // seconds
|
|
17
|
+
|
|
18
|
+
function buildFfmpegCommand(inputFile, outputFile, scenes) {
|
|
19
|
+
// Validate scenes
|
|
20
|
+
if (!scenes || scenes.length < 2) {
|
|
21
|
+
throw new Error('Need at least 2 scene markers (start + end)');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Ensure scenes are sorted by time
|
|
25
|
+
scenes.sort((a, b) => a.time - b.time);
|
|
26
|
+
|
|
27
|
+
// Build segments (each scene runs until the next scene starts)
|
|
28
|
+
const segments = [];
|
|
29
|
+
for (let i = 0; i < scenes.length - 1; i++) {
|
|
30
|
+
const raw = scenes[i + 1].time - scenes[i].time;
|
|
31
|
+
const speed = scenes[i].speed || 1.3; // default speed if not specified
|
|
32
|
+
const final = raw / speed;
|
|
33
|
+
segments.push({
|
|
34
|
+
name: scenes[i].name,
|
|
35
|
+
start: scenes[i].time,
|
|
36
|
+
end: scenes[i + 1].time,
|
|
37
|
+
raw,
|
|
38
|
+
speed,
|
|
39
|
+
final,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Calculate total
|
|
44
|
+
const totalFinal = segments.reduce((sum, s) => sum + s.final, 0);
|
|
45
|
+
|
|
46
|
+
// Print segment breakdown
|
|
47
|
+
console.log('=== SEGMENT BREAKDOWN ===');
|
|
48
|
+
for (const s of segments) {
|
|
49
|
+
console.log(` ${s.name}: ${s.raw.toFixed(1)}s raw -> ${s.final.toFixed(1)}s @ ${s.speed}x`);
|
|
50
|
+
}
|
|
51
|
+
console.log(` TOTAL: ${totalFinal.toFixed(1)}s (target: ${TARGET_DURATION}s)`);
|
|
52
|
+
|
|
53
|
+
if (totalFinal < 45 || totalFinal > 75) {
|
|
54
|
+
console.warn(`\n WARNING: Total duration ${totalFinal.toFixed(1)}s is outside 45-75s range.`);
|
|
55
|
+
console.warn(' Adjust speeds and re-run.\n');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Build ffmpeg filter_complex
|
|
59
|
+
const N = segments.length;
|
|
60
|
+
const splitOutputs = segments.map((_, i) => `[s${i + 1}]`).join('');
|
|
61
|
+
const trimFilters = segments.map((s, i) =>
|
|
62
|
+
`[s${i + 1}]trim=start=${s.start.toFixed(3)}:end=${s.end.toFixed(3)},setpts=(PTS-STARTPTS)/${s.speed}[v${i + 1}]`
|
|
63
|
+
).join(';\n ');
|
|
64
|
+
const concatInputs = segments.map((_, i) => `[v${i + 1}]`).join('');
|
|
65
|
+
|
|
66
|
+
const cmd = `ffmpeg -i "${inputFile}" \\
|
|
67
|
+
-filter_complex "
|
|
68
|
+
[0:v]split=${N}${splitOutputs};
|
|
69
|
+
${trimFilters};
|
|
70
|
+
${concatInputs}concat=n=${N}:v=1:a=0[out]
|
|
71
|
+
" \\
|
|
72
|
+
-map "[out]" \\
|
|
73
|
+
-c:v libx264 -preset medium -crf 20 \\
|
|
74
|
+
-pix_fmt yuv420p -r 30 -s 1920x1080 \\
|
|
75
|
+
-movflags +faststart \\
|
|
76
|
+
"${outputFile}"`;
|
|
77
|
+
|
|
78
|
+
return { cmd, segments, totalFinal };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function autoTuneSpeeds(scenes, targetDuration = TARGET_DURATION) {
|
|
82
|
+
const scenesCopy = scenes.map(s => ({ ...s }));
|
|
83
|
+
|
|
84
|
+
// Default all to 1.3x if no speed set
|
|
85
|
+
for (const s of scenesCopy) {
|
|
86
|
+
if (!s.speed) s.speed = 1.3;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Calculate initial total
|
|
90
|
+
let total = 0;
|
|
91
|
+
for (let i = 0; i < scenesCopy.length - 1; i++) {
|
|
92
|
+
const raw = scenesCopy[i + 1].time - scenesCopy[i].time;
|
|
93
|
+
total += raw / scenesCopy[i].speed;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Iteratively adjust
|
|
97
|
+
const maxIterations = 20;
|
|
98
|
+
for (let iter = 0; iter < maxIterations; iter++) {
|
|
99
|
+
const ratio = total / targetDuration;
|
|
100
|
+
if (Math.abs(ratio - 1.0) < 0.05) break; // Within 5% of target
|
|
101
|
+
|
|
102
|
+
// Scale all speeds proportionally
|
|
103
|
+
for (const s of scenesCopy) {
|
|
104
|
+
s.speed = Math.min(4.0, Math.max(0.8, s.speed * ratio));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Recalculate
|
|
108
|
+
total = 0;
|
|
109
|
+
for (let i = 0; i < scenesCopy.length - 1; i++) {
|
|
110
|
+
const raw = scenesCopy[i + 1].time - scenesCopy[i].time;
|
|
111
|
+
total += raw / scenesCopy[i].speed;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Round speeds to 1 decimal
|
|
116
|
+
for (const s of scenesCopy) {
|
|
117
|
+
s.speed = Math.round(s.speed * 10) / 10;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return scenesCopy;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// CLI entry point
|
|
124
|
+
if (require.main === module) {
|
|
125
|
+
const args = process.argv.slice(2);
|
|
126
|
+
|
|
127
|
+
if (args.length < 2) {
|
|
128
|
+
console.log('Usage: node ffmpeg-builder.js <input.webm> <output.mp4> [scenes.json]');
|
|
129
|
+
console.log('\nscenes.json example:');
|
|
130
|
+
console.log(JSON.stringify([
|
|
131
|
+
{ name: 'landing', time: 0, speed: 1.0 },
|
|
132
|
+
{ name: 'dashboard', time: 8, speed: 1.5 },
|
|
133
|
+
{ name: 'feature-a', time: 20, speed: 1.2 },
|
|
134
|
+
{ name: 'end', time: 80 },
|
|
135
|
+
], null, 2));
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const [inputFile, outputFile, scenesFile] = args;
|
|
140
|
+
|
|
141
|
+
let scenes;
|
|
142
|
+
if (scenesFile) {
|
|
143
|
+
scenes = JSON.parse(fs.readFileSync(scenesFile, 'utf-8'));
|
|
144
|
+
} else {
|
|
145
|
+
// Read from stdin
|
|
146
|
+
console.log('Paste scene timestamps JSON (Ctrl+D when done):');
|
|
147
|
+
const input = fs.readFileSync('/dev/stdin', 'utf-8');
|
|
148
|
+
scenes = JSON.parse(input);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Auto-tune if speeds not all defined
|
|
152
|
+
const hasAllSpeeds = scenes.every(s => s.speed !== undefined);
|
|
153
|
+
if (!hasAllSpeeds) {
|
|
154
|
+
console.log('Auto-tuning speeds to hit ~60s...');
|
|
155
|
+
scenes = autoTuneSpeeds(scenes);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const { cmd, totalFinal } = buildFfmpegCommand(inputFile, outputFile, scenes);
|
|
159
|
+
|
|
160
|
+
console.log('\n=== FFMPEG COMMAND ===');
|
|
161
|
+
console.log(cmd);
|
|
162
|
+
|
|
163
|
+
console.log(`\nEstimated output: ${totalFinal.toFixed(1)}s`);
|
|
164
|
+
console.log('Run the command above to process the video.');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = { buildFfmpegCommand, autoTuneSpeeds };
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright Recording Helpers
|
|
3
|
+
* Bundled with showcase-video-recorder skill
|
|
4
|
+
*
|
|
5
|
+
* Usage: const helpers = require('./playwright-helpers');
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Mask all input fields to hide credentials.
|
|
10
|
+
* Call BEFORE submitting login forms.
|
|
11
|
+
*/
|
|
12
|
+
async function maskInputs(page) {
|
|
13
|
+
await page.evaluate(() => {
|
|
14
|
+
document.querySelectorAll('input[type="password"], input[type="email"], input[type="text"]')
|
|
15
|
+
.forEach(el => {
|
|
16
|
+
el.style.webkitTextSecurity = 'disc';
|
|
17
|
+
el.style.color = 'transparent';
|
|
18
|
+
el.style.textShadow = '0 0 8px rgba(255,255,255,0.5)';
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Smooth scroll with cubic ease-in-out.
|
|
25
|
+
* @param {import('playwright').Page} page
|
|
26
|
+
* @param {number} targetY - Target scroll position in pixels
|
|
27
|
+
* @param {number} duration - Animation duration in ms (default 1500)
|
|
28
|
+
*/
|
|
29
|
+
async function smoothScroll(page, targetY, duration = 1500) {
|
|
30
|
+
await page.evaluate(async ({ targetY, duration }) => {
|
|
31
|
+
const startY = window.scrollY;
|
|
32
|
+
const distance = targetY - startY;
|
|
33
|
+
const startTime = performance.now();
|
|
34
|
+
return new Promise(resolve => {
|
|
35
|
+
function step(currentTime) {
|
|
36
|
+
const elapsed = currentTime - startTime;
|
|
37
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
38
|
+
// Cubic ease-in-out
|
|
39
|
+
const ease = progress < 0.5
|
|
40
|
+
? 4 * progress * progress * progress
|
|
41
|
+
: 1 - Math.pow(-2 * progress + 2, 3) / 2;
|
|
42
|
+
window.scrollTo(0, startY + distance * ease);
|
|
43
|
+
if (progress < 1) requestAnimationFrame(step);
|
|
44
|
+
else resolve();
|
|
45
|
+
}
|
|
46
|
+
requestAnimationFrame(step);
|
|
47
|
+
});
|
|
48
|
+
}, { targetY, duration });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Click a button by its accessible name, with fallback to text content.
|
|
53
|
+
* @returns {boolean} true if clicked, false if not found
|
|
54
|
+
*/
|
|
55
|
+
async function clickButton(page, text, timeout = 5000) {
|
|
56
|
+
try {
|
|
57
|
+
const btn = page.getByRole('button', { name: text });
|
|
58
|
+
await btn.waitFor({ state: 'visible', timeout });
|
|
59
|
+
await btn.scrollIntoViewIfNeeded();
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
await btn.click();
|
|
62
|
+
return true;
|
|
63
|
+
} catch {
|
|
64
|
+
try {
|
|
65
|
+
const fallback = page.locator(`text="${text}"`).first();
|
|
66
|
+
await fallback.waitFor({ state: 'visible', timeout: 3000 });
|
|
67
|
+
await fallback.click();
|
|
68
|
+
return true;
|
|
69
|
+
} catch {
|
|
70
|
+
console.log(`[WARN] Button not found: "${text}" — skipping`);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Type text with natural human-like timing variation.
|
|
78
|
+
* @param {number} baseDelay - Base delay between keystrokes in ms
|
|
79
|
+
*/
|
|
80
|
+
async function naturalType(page, selector, text, baseDelay = 80) {
|
|
81
|
+
const el = page.locator(selector).first();
|
|
82
|
+
await el.waitFor({ state: 'visible', timeout: 5000 });
|
|
83
|
+
await el.click();
|
|
84
|
+
await page.waitForTimeout(200);
|
|
85
|
+
for (const char of text) {
|
|
86
|
+
await el.type(char, { delay: 0 });
|
|
87
|
+
const delay = baseDelay + Math.random() * 60 - 30;
|
|
88
|
+
await page.waitForTimeout(Math.max(30, delay));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Add a custom cursor highlight ring that follows mouse movement.
|
|
94
|
+
* Teal border (#00FFD1) with purple click feedback (#8B5CF6).
|
|
95
|
+
* Must be called after every page navigation (ring is lost on navigation).
|
|
96
|
+
*/
|
|
97
|
+
async function addCursorHighlight(page) {
|
|
98
|
+
await page.evaluate(() => {
|
|
99
|
+
// Remove existing ring if present
|
|
100
|
+
const existing = document.getElementById('cursor-highlight');
|
|
101
|
+
if (existing) existing.remove();
|
|
102
|
+
|
|
103
|
+
const ring = document.createElement('div');
|
|
104
|
+
ring.id = 'cursor-highlight';
|
|
105
|
+
ring.style.cssText = `
|
|
106
|
+
position: fixed; width: 28px; height: 28px;
|
|
107
|
+
border: 2px solid #00FFD1; border-radius: 50%;
|
|
108
|
+
pointer-events: none; z-index: 999999;
|
|
109
|
+
transform: translate(-50%, -50%);
|
|
110
|
+
transition: all 0.15s ease-out;
|
|
111
|
+
box-shadow: 0 0 10px rgba(0,255,209,0.3);
|
|
112
|
+
`;
|
|
113
|
+
document.body.appendChild(ring);
|
|
114
|
+
|
|
115
|
+
document.addEventListener('mousemove', e => {
|
|
116
|
+
ring.style.left = e.clientX + 'px';
|
|
117
|
+
ring.style.top = e.clientY + 'px';
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
document.addEventListener('mousedown', () => {
|
|
121
|
+
ring.style.borderColor = '#8B5CF6';
|
|
122
|
+
ring.style.transform = 'translate(-50%, -50%) scale(0.8)';
|
|
123
|
+
ring.style.boxShadow = '0 0 20px rgba(139,92,246,0.5)';
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
document.addEventListener('mouseup', () => {
|
|
127
|
+
ring.style.borderColor = '#00FFD1';
|
|
128
|
+
ring.style.transform = 'translate(-50%, -50%) scale(1)';
|
|
129
|
+
ring.style.boxShadow = '0 0 10px rgba(0,255,209,0.3)';
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Click a navigation link by its text content.
|
|
136
|
+
* Waits for network idle after navigation.
|
|
137
|
+
*/
|
|
138
|
+
async function clickNavLink(page, text, timeout = 8000) {
|
|
139
|
+
try {
|
|
140
|
+
const link = page.locator(`a:has-text("${text}")`).first();
|
|
141
|
+
await link.waitFor({ state: 'visible', timeout: 5000 });
|
|
142
|
+
await link.click();
|
|
143
|
+
await page.waitForLoadState('networkidle', { timeout });
|
|
144
|
+
await page.waitForTimeout(1500);
|
|
145
|
+
return true;
|
|
146
|
+
} catch {
|
|
147
|
+
console.log(`[WARN] Nav link not found: "${text}" — skipping`);
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Hover over an element to trigger hover states for visual effect.
|
|
154
|
+
*/
|
|
155
|
+
async function hoverElement(page, selector, duration = 1000) {
|
|
156
|
+
try {
|
|
157
|
+
const el = page.locator(selector).first();
|
|
158
|
+
await el.waitFor({ state: 'visible', timeout: 3000 });
|
|
159
|
+
await el.hover();
|
|
160
|
+
await page.waitForTimeout(duration);
|
|
161
|
+
return true;
|
|
162
|
+
} catch {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Scene timestamp tracker for ffmpeg post-processing.
|
|
169
|
+
*/
|
|
170
|
+
class SceneTracker {
|
|
171
|
+
constructor() {
|
|
172
|
+
this.scenes = [];
|
|
173
|
+
this.startTime = null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
start() {
|
|
177
|
+
this.startTime = Date.now();
|
|
178
|
+
this.mark('start');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
mark(name) {
|
|
182
|
+
if (!this.startTime) this.startTime = Date.now();
|
|
183
|
+
const elapsed = (Date.now() - this.startTime) / 1000;
|
|
184
|
+
this.scenes.push({ name, time: elapsed });
|
|
185
|
+
console.log(`[SCENE] ${name} @ ${elapsed.toFixed(1)}s`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
getScenes() {
|
|
189
|
+
return this.scenes;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
printSummary() {
|
|
193
|
+
console.log('\n=== SCENE TIMESTAMPS ===');
|
|
194
|
+
console.log(JSON.stringify(this.scenes, null, 2));
|
|
195
|
+
|
|
196
|
+
// Calculate raw durations
|
|
197
|
+
console.log('\n=== SEGMENT DURATIONS ===');
|
|
198
|
+
for (let i = 0; i < this.scenes.length - 1; i++) {
|
|
199
|
+
const raw = this.scenes[i + 1].time - this.scenes[i].time;
|
|
200
|
+
console.log(` ${this.scenes[i].name}: ${raw.toFixed(1)}s`);
|
|
201
|
+
}
|
|
202
|
+
const total = this.scenes[this.scenes.length - 1].time - this.scenes[0].time;
|
|
203
|
+
console.log(` TOTAL RAW: ${total.toFixed(1)}s`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = {
|
|
208
|
+
maskInputs,
|
|
209
|
+
smoothScroll,
|
|
210
|
+
clickButton,
|
|
211
|
+
naturalType,
|
|
212
|
+
addCursorHighlight,
|
|
213
|
+
clickNavLink,
|
|
214
|
+
hoverElement,
|
|
215
|
+
SceneTracker,
|
|
216
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""ElevenLabs TTS - pipe text to this script to hear it spoken."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import tempfile
|
|
8
|
+
|
|
9
|
+
from elevenlabs import ElevenLabs
|
|
10
|
+
|
|
11
|
+
API_KEY = os.environ.get("ELEVEN_API_KEY")
|
|
12
|
+
VOICE_ID = os.environ.get("ELEVEN_VOICE_ID", "PB6BdkFkZLbI39GHdnbQ")
|
|
13
|
+
|
|
14
|
+
if not API_KEY:
|
|
15
|
+
sys.exit(0)
|
|
16
|
+
|
|
17
|
+
def speak(text: str):
|
|
18
|
+
if not text.strip():
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
client = ElevenLabs(api_key=API_KEY)
|
|
22
|
+
|
|
23
|
+
audio = client.text_to_speech.convert(
|
|
24
|
+
voice_id=VOICE_ID,
|
|
25
|
+
text=text,
|
|
26
|
+
model_id="eleven_turbo_v2_5",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Write to temp file and play
|
|
30
|
+
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
|
|
31
|
+
for chunk in audio:
|
|
32
|
+
f.write(chunk)
|
|
33
|
+
f.flush()
|
|
34
|
+
|
|
35
|
+
# Play with mpv, ffplay, or paplay
|
|
36
|
+
for cmd in [
|
|
37
|
+
["mpv", "--no-video", "--really-quiet", f.name],
|
|
38
|
+
["ffplay", "-nodisp", "-autoexit", "-loglevel", "quiet", f.name],
|
|
39
|
+
["paplay", f.name],
|
|
40
|
+
]:
|
|
41
|
+
try:
|
|
42
|
+
subprocess.run(cmd, check=True)
|
|
43
|
+
break
|
|
44
|
+
except FileNotFoundError:
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
os.unlink(f.name)
|
|
48
|
+
|
|
49
|
+
if __name__ == "__main__":
|
|
50
|
+
if len(sys.argv) > 1:
|
|
51
|
+
text = " ".join(sys.argv[1:])
|
|
52
|
+
else:
|
|
53
|
+
text = sys.stdin.read()
|
|
54
|
+
|
|
55
|
+
speak(text)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Quick TTS wrapper for hooks
|
|
3
|
+
# Requires ELEVEN_API_KEY env var (set in shell profile or .env.claude)
|
|
4
|
+
# Cross-platform: detects Windows venv path vs Unix venv path
|
|
5
|
+
export PYTHONWARNINGS="ignore::UserWarning"
|
|
6
|
+
export ELEVEN_VOICE_ID="${ELEVEN_VOICE_ID:-PB6BdkFkZLbI39GHdnbQ}"
|
|
7
|
+
|
|
8
|
+
TEXT="${1:-}"
|
|
9
|
+
if [ -n "$TEXT" ] && [ -n "$ELEVEN_API_KEY" ]; then
|
|
10
|
+
# Cross-platform venv python detection
|
|
11
|
+
VENV_PYTHON="$HOME/.claude/venv/bin/python"
|
|
12
|
+
if [ ! -f "$VENV_PYTHON" ]; then
|
|
13
|
+
VENV_PYTHON="$HOME/.claude/venv/Scripts/python"
|
|
14
|
+
fi
|
|
15
|
+
if [ -f "$VENV_PYTHON" ]; then
|
|
16
|
+
echo "$TEXT" | "$VENV_PYTHON" "$HOME/.claude/scripts/speak.py" &
|
|
17
|
+
fi
|
|
18
|
+
fi
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# GSD Status Dashboard - Quick project state overview
|
|
3
|
+
# Usage: status.sh [project_path]
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
PROJECT_DIR="${1:-.}"
|
|
8
|
+
cd "$PROJECT_DIR"
|
|
9
|
+
|
|
10
|
+
# Colors
|
|
11
|
+
RED='\033[0;31m'
|
|
12
|
+
GREEN='\033[0;32m'
|
|
13
|
+
YELLOW='\033[0;33m'
|
|
14
|
+
BLUE='\033[0;34m'
|
|
15
|
+
CYAN='\033[0;36m'
|
|
16
|
+
BOLD='\033[1m'
|
|
17
|
+
DIM='\033[2m'
|
|
18
|
+
NC='\033[0m' # No Color
|
|
19
|
+
|
|
20
|
+
# Header
|
|
21
|
+
echo ""
|
|
22
|
+
echo -e "${BOLD}═══════════════════════════════════════════════════════════${NC}"
|
|
23
|
+
echo -e "${BOLD} GSD STATUS: $(basename "$PWD")${NC}"
|
|
24
|
+
echo -e "${BOLD}═══════════════════════════════════════════════════════════${NC}"
|
|
25
|
+
|
|
26
|
+
# Check if GSD project
|
|
27
|
+
if [ ! -d ".planning" ]; then
|
|
28
|
+
echo -e "\n${YELLOW}⚠ No .planning/ directory - not a GSD project${NC}"
|
|
29
|
+
echo -e "${DIM}Run /gsd:new-project to initialize${NC}\n"
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Current Phase from STATE.md
|
|
34
|
+
echo -e "\n${CYAN}▸ CURRENT POSITION${NC}"
|
|
35
|
+
if [ -f ".planning/STATE.md" ]; then
|
|
36
|
+
# Extract current phase line
|
|
37
|
+
PHASE_LINE=$(grep -E "^(current_phase|## Current|Phase:)" .planning/STATE.md 2>/dev/null | head -1 || echo "")
|
|
38
|
+
if [ -n "$PHASE_LINE" ]; then
|
|
39
|
+
echo -e " $PHASE_LINE"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Extract status
|
|
43
|
+
STATUS_LINE=$(grep -E "^(status:|Status:)" .planning/STATE.md 2>/dev/null | head -1 || echo "")
|
|
44
|
+
if [ -n "$STATUS_LINE" ]; then
|
|
45
|
+
echo -e " $STATUS_LINE"
|
|
46
|
+
fi
|
|
47
|
+
else
|
|
48
|
+
echo -e " ${YELLOW}No STATE.md found${NC}"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Phase Overview from ROADMAP.md
|
|
52
|
+
echo -e "\n${CYAN}▸ PHASES${NC}"
|
|
53
|
+
if [ -f ".planning/ROADMAP.md" ]; then
|
|
54
|
+
# Count phases and show completion
|
|
55
|
+
TOTAL_PHASES=$(grep -cE "^## Phase [0-9]" .planning/ROADMAP.md 2>/dev/null || echo "0")
|
|
56
|
+
COMPLETE_PHASES=$(grep -cE "\[x\]|✓|complete" .planning/ROADMAP.md 2>/dev/null || echo "0")
|
|
57
|
+
echo -e " Progress: ${GREEN}$COMPLETE_PHASES${NC}/${TOTAL_PHASES} phases"
|
|
58
|
+
|
|
59
|
+
# List phases
|
|
60
|
+
grep -E "^## Phase [0-9]" .planning/ROADMAP.md 2>/dev/null | head -10 | while read -r line; do
|
|
61
|
+
echo -e " ${DIM}$line${NC}"
|
|
62
|
+
done
|
|
63
|
+
else
|
|
64
|
+
echo -e " ${YELLOW}No ROADMAP.md found${NC}"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Plans in current phase
|
|
68
|
+
echo -e "\n${CYAN}▸ PLANS${NC}"
|
|
69
|
+
if [ -d ".planning/phases" ]; then
|
|
70
|
+
# Find most recent phase directory
|
|
71
|
+
LATEST_PHASE=$(ls -d .planning/phases/*/ 2>/dev/null | sort -V | tail -1)
|
|
72
|
+
if [ -n "$LATEST_PHASE" ]; then
|
|
73
|
+
PHASE_NAME=$(basename "$LATEST_PHASE")
|
|
74
|
+
TOTAL_PLANS=$(ls -1 "$LATEST_PHASE"*-PLAN.md 2>/dev/null | wc -l | tr -d ' ')
|
|
75
|
+
COMPLETE_PLANS=$(ls -1 "$LATEST_PHASE"*-SUMMARY.md 2>/dev/null | wc -l | tr -d ' ')
|
|
76
|
+
echo -e " ${BOLD}$PHASE_NAME${NC}: ${GREEN}$COMPLETE_PLANS${NC}/${TOTAL_PLANS} plans complete"
|
|
77
|
+
|
|
78
|
+
# List incomplete plans
|
|
79
|
+
for plan in "$LATEST_PHASE"*-PLAN.md; do
|
|
80
|
+
if [ -f "$plan" ]; then
|
|
81
|
+
PLAN_NAME=$(basename "$plan" -PLAN.md)
|
|
82
|
+
SUMMARY="${plan/-PLAN.md/-SUMMARY.md}"
|
|
83
|
+
if [ -f "$SUMMARY" ]; then
|
|
84
|
+
echo -e " ${GREEN}✓${NC} $PLAN_NAME"
|
|
85
|
+
else
|
|
86
|
+
echo -e " ${YELLOW}○${NC} $PLAN_NAME ${DIM}(pending)${NC}"
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
done 2>/dev/null
|
|
90
|
+
fi
|
|
91
|
+
else
|
|
92
|
+
echo -e " ${YELLOW}No phases directory${NC}"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Blockers from STATE.md
|
|
96
|
+
echo -e "\n${CYAN}▸ BLOCKERS${NC}"
|
|
97
|
+
if [ -f ".planning/STATE.md" ]; then
|
|
98
|
+
BLOCKERS=$(sed -n '/## Blockers/,/^##/p' .planning/STATE.md 2>/dev/null | grep -E "^-" | head -5)
|
|
99
|
+
if [ -n "$BLOCKERS" ]; then
|
|
100
|
+
echo -e "${RED}$BLOCKERS${NC}"
|
|
101
|
+
else
|
|
102
|
+
echo -e " ${GREEN}None${NC}"
|
|
103
|
+
fi
|
|
104
|
+
else
|
|
105
|
+
echo -e " ${DIM}Unknown${NC}"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Git status (brief)
|
|
109
|
+
echo -e "\n${CYAN}▸ GIT${NC}"
|
|
110
|
+
if git rev-parse --git-dir > /dev/null 2>&1; then
|
|
111
|
+
BRANCH=$(git branch --show-current 2>/dev/null || echo "detached")
|
|
112
|
+
CHANGES=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
|
|
113
|
+
COMMITS_AHEAD=$(git rev-list --count @{u}..HEAD 2>/dev/null || echo "?")
|
|
114
|
+
echo -e " Branch: ${BOLD}$BRANCH${NC}"
|
|
115
|
+
echo -e " Uncommitted: $CHANGES files"
|
|
116
|
+
[ "$COMMITS_AHEAD" != "?" ] && echo -e " Ahead of remote: $COMMITS_AHEAD commits"
|
|
117
|
+
else
|
|
118
|
+
echo -e " ${YELLOW}Not a git repo${NC}"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# Suggested next action
|
|
122
|
+
echo -e "\n${CYAN}▸ NEXT ACTION${NC}"
|
|
123
|
+
if [ -f ".planning/STATE.md" ]; then
|
|
124
|
+
# Simple heuristic based on state
|
|
125
|
+
if grep -qE "needs.*(plan|planning)" .planning/STATE.md 2>/dev/null; then
|
|
126
|
+
echo -e " ${BOLD}/gsd:plan-phase${NC}"
|
|
127
|
+
elif grep -qE "needs.*(exec|execution)" .planning/STATE.md 2>/dev/null; then
|
|
128
|
+
echo -e " ${BOLD}/gsd:execute-phase${NC}"
|
|
129
|
+
elif grep -qE "needs.*(verify|verification)" .planning/STATE.md 2>/dev/null; then
|
|
130
|
+
echo -e " ${BOLD}/gsd:verify-work${NC}"
|
|
131
|
+
else
|
|
132
|
+
echo -e " ${BOLD}/gsd:progress${NC} ${DIM}(check state)${NC}"
|
|
133
|
+
fi
|
|
134
|
+
else
|
|
135
|
+
echo -e " ${BOLD}/gsd:new-project${NC}"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
echo -e "\n${DIM}─────────────────────────────────────────────────────────────${NC}\n"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Sync ~/.claude/ framework files → ~/Projects/qualia-framework/framework/
|
|
3
|
+
# Run this after making changes to ~/.claude/ to prepare for distribution
|
|
4
|
+
# Usage: ~/.claude/scripts/sync-to-framework.sh
|
|
5
|
+
|
|
6
|
+
source "$HOME/.claude/hooks/qualia-colors.sh"
|
|
7
|
+
|
|
8
|
+
SRC="$HOME/.claude"
|
|
9
|
+
DEST="$HOME/Projects/qualia-framework/framework"
|
|
10
|
+
|
|
11
|
+
if [ ! -d "$DEST" ]; then
|
|
12
|
+
printf "${Q_FAIL} ✗ Framework repo not found at ~/Projects/qualia-framework${Q_RESET}\n"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
q_header "SYNC TO FRAMEWORK REPO"
|
|
17
|
+
|
|
18
|
+
# Sync framework components (these are the distributable parts)
|
|
19
|
+
COMPONENTS=(
|
|
20
|
+
"skills"
|
|
21
|
+
"agents"
|
|
22
|
+
"hooks"
|
|
23
|
+
"rules"
|
|
24
|
+
"knowledge"
|
|
25
|
+
"qualia-engine"
|
|
26
|
+
"scripts"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
for comp in "${COMPONENTS[@]}"; do
|
|
30
|
+
if [ -d "$SRC/$comp" ]; then
|
|
31
|
+
rsync -a --delete \
|
|
32
|
+
--exclude="node_modules" \
|
|
33
|
+
--exclude=".DS_Store" \
|
|
34
|
+
--exclude="retros/" \
|
|
35
|
+
--exclude="project-notes/" \
|
|
36
|
+
--exclude="session-digest.md" \
|
|
37
|
+
"$SRC/$comp/" "$DEST/$comp/"
|
|
38
|
+
COUNT=$(find "$DEST/$comp" -type f | wc -l)
|
|
39
|
+
q_pass "$comp ($COUNT files)"
|
|
40
|
+
fi
|
|
41
|
+
done
|
|
42
|
+
|
|
43
|
+
# Sync individual files
|
|
44
|
+
cp "$SRC/CLAUDE.md" "$DEST/../templates/CLAUDE-owner.md" 2>/dev/null && q_pass "CLAUDE.md → templates/CLAUDE-owner.md"
|
|
45
|
+
cp "$SRC/statusline-command.sh" "$DEST/statusline-command.sh" 2>/dev/null && q_pass "statusline-command.sh"
|
|
46
|
+
cp "$SRC/askpass.sh" "$DEST/askpass.sh" 2>/dev/null && q_pass "askpass.sh"
|
|
47
|
+
cp "$SRC/install.sh" "$DEST/../install.sh" 2>/dev/null && q_pass "install.sh"
|
|
48
|
+
cp "$SRC/install.ps1" "$DEST/../install.ps1" 2>/dev/null && q_pass "install.ps1"
|
|
49
|
+
|
|
50
|
+
# Don't sync: secrets, session data, history, plugins, caches, personal memories
|
|
51
|
+
# Those stay local to each machine
|
|
52
|
+
|
|
53
|
+
# Show what changed in the repo
|
|
54
|
+
echo ""
|
|
55
|
+
cd "$HOME/Projects/qualia-framework"
|
|
56
|
+
CHANGED=$(git status --porcelain | wc -l)
|
|
57
|
+
if [ "$CHANGED" -gt 0 ]; then
|
|
58
|
+
q_warn "$CHANGED files changed"
|
|
59
|
+
git status --short | head -20
|
|
60
|
+
echo ""
|
|
61
|
+
printf "${Q_DIM} Ready to commit. Run:${Q_RESET}\n"
|
|
62
|
+
printf "${Q_TEAL} cd ~/Projects/qualia-framework && git add -A && git commit -m 'chore: sync framework updates' && git push${Q_RESET}\n"
|
|
63
|
+
else
|
|
64
|
+
q_pass "Already up to date"
|
|
65
|
+
fi
|