shipwright-cli 2.0.0 → 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 +160 -72
- package/completions/_shipwright +59 -7
- package/completions/shipwright.bash +24 -4
- package/completions/shipwright.fish +80 -2
- package/dashboard/server.ts +208 -0
- package/docs/tmux-research/TMUX-ARCHITECTURE.md +567 -0
- package/docs/tmux-research/TMUX-AUDIT.md +925 -0
- package/docs/tmux-research/TMUX-BEST-PRACTICES-2025-2026.md +829 -0
- package/docs/tmux-research/TMUX-QUICK-REFERENCE.md +543 -0
- package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +438 -0
- package/package.json +2 -2
- package/scripts/lib/helpers.sh +7 -0
- package/scripts/sw +116 -2
- package/scripts/sw-activity.sh +1 -1
- package/scripts/sw-adaptive.sh +1 -1
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +1 -1
- package/scripts/sw-autonomous.sh +128 -38
- package/scripts/sw-changelog.sh +1 -1
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +1 -1
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +62 -1
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +1 -1
- package/scripts/sw-cost.sh +44 -3
- package/scripts/sw-daemon.sh +155 -27
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +958 -118
- package/scripts/sw-decompose.sh +1 -1
- package/scripts/sw-deps.sh +1 -1
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +1 -1
- package/scripts/sw-docs-agent.sh +1 -1
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +49 -1
- package/scripts/sw-dora.sh +1 -1
- package/scripts/sw-durable.sh +1 -1
- package/scripts/sw-e2e-orchestrator.sh +1 -1
- package/scripts/sw-eventbus.sh +1 -1
- package/scripts/sw-feedback.sh +23 -15
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +1 -1
- package/scripts/sw-fleet-viz.sh +1 -1
- package/scripts/sw-fleet.sh +1 -1
- package/scripts/sw-github-app.sh +1 -1
- package/scripts/sw-github-checks.sh +4 -4
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +1 -1
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +1 -1
- package/scripts/sw-incident.sh +45 -6
- package/scripts/sw-init.sh +150 -24
- package/scripts/sw-instrument.sh +1 -1
- package/scripts/sw-intelligence.sh +1 -1
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +1 -1
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +204 -19
- package/scripts/sw-memory.sh +18 -1
- package/scripts/sw-mission-control.sh +1 -1
- package/scripts/sw-model-router.sh +1 -1
- package/scripts/sw-otel.sh +1 -1
- package/scripts/sw-oversight.sh +76 -1
- package/scripts/sw-pipeline-composer.sh +1 -1
- package/scripts/sw-pipeline-vitals.sh +1 -1
- package/scripts/sw-pipeline.sh +261 -12
- package/scripts/sw-pm.sh +70 -5
- package/scripts/sw-pr-lifecycle.sh +1 -1
- package/scripts/sw-predictive.sh +8 -1
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +1 -1
- package/scripts/sw-quality.sh +1 -1
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +1853 -178
- package/scripts/sw-regression.sh +1 -1
- package/scripts/sw-release-manager.sh +1 -1
- package/scripts/sw-release.sh +1 -1
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +1 -1
- package/scripts/sw-retro.sh +1 -1
- package/scripts/sw-scale.sh +1 -1
- package/scripts/sw-security-audit.sh +1 -1
- package/scripts/sw-self-optimize.sh +1 -1
- package/scripts/sw-session.sh +1 -1
- package/scripts/sw-setup.sh +263 -127
- package/scripts/sw-standup.sh +1 -1
- package/scripts/sw-status.sh +44 -2
- package/scripts/sw-strategic.sh +189 -41
- package/scripts/sw-stream.sh +1 -1
- package/scripts/sw-swarm.sh +42 -5
- package/scripts/sw-team-stages.sh +1 -1
- package/scripts/sw-templates.sh +4 -4
- package/scripts/sw-testgen.sh +66 -15
- package/scripts/sw-tmux-pipeline.sh +1 -1
- package/scripts/sw-tmux-role-color.sh +58 -0
- package/scripts/sw-tmux-status.sh +128 -0
- package/scripts/sw-tmux.sh +1 -1
- package/scripts/sw-trace.sh +1 -1
- package/scripts/sw-tracker.sh +1 -1
- package/scripts/sw-triage.sh +61 -37
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +1 -1
- package/scripts/sw-webhook.sh +1 -1
- package/scripts/sw-widgets.sh +1 -1
- package/scripts/sw-worktree.sh +1 -1
- package/templates/pipelines/autonomous.json +2 -2
- package/tmux/shipwright-overlay.conf +35 -17
- package/tmux/tmux.conf +23 -21
package/scripts/sw-auth.sh
CHANGED
package/scripts/sw-autonomous.sh
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
9
9
|
|
|
10
|
-
VERSION="2.
|
|
10
|
+
VERSION="2.1.0"
|
|
11
11
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
12
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
13
13
|
|
|
@@ -203,31 +203,17 @@ run_analysis_cycle() {
|
|
|
203
203
|
if command -v claude &>/dev/null; then
|
|
204
204
|
info "Running codebase analysis with Claude..."
|
|
205
205
|
|
|
206
|
-
claude
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
- Priority: critical/high/medium/low
|
|
218
|
-
- Effort estimate: S/M/L (small/medium/large)
|
|
219
|
-
- Labels: e.g. "bug", "performance", "test", "docs", "security", "refactor", "self-improvement"
|
|
220
|
-
|
|
221
|
-
Output as JSON array of findings with fields:
|
|
222
|
-
{
|
|
223
|
-
"title": "...",
|
|
224
|
-
"description": "...",
|
|
225
|
-
"priority": "high",
|
|
226
|
-
"effort": "M",
|
|
227
|
-
"labels": ["..."],
|
|
228
|
-
"category": "bug|performance|test|docs|security|refactor|self-improvement"
|
|
229
|
-
}
|
|
230
|
-
ANALYSIS_PROMPT
|
|
206
|
+
claude -p 'You are Shipwright'"'"'s autonomous PM. Analyze this repository for:
|
|
207
|
+
1. Bugs & Issues: Missing error handling, potential crashes, edge cases
|
|
208
|
+
2. Performance: Bottlenecks, n+1 queries, memory leaks, unnecessary work
|
|
209
|
+
3. Missing Tests: Uncovered code paths, critical scenarios
|
|
210
|
+
4. Stale Documentation: Outdated guides, missing API docs
|
|
211
|
+
5. Security: Input validation, injection risks, credential leaks
|
|
212
|
+
6. Code Quality: Dead code, refactoring opportunities, tech debt
|
|
213
|
+
7. Self-Improvement: How Shipwright itself could be enhanced
|
|
214
|
+
|
|
215
|
+
For each finding, output JSON with fields: title, description, priority (critical/high/medium/low), effort (S/M/L), labels (array), category.
|
|
216
|
+
Output ONLY a JSON array, no other text.' --max-turns 3 > "$findings" 2>/dev/null || true
|
|
231
217
|
|
|
232
218
|
else
|
|
233
219
|
warn "Claude CLI not available, using static heuristics..."
|
|
@@ -289,8 +275,9 @@ create_issue_from_finding() {
|
|
|
289
275
|
# Add shipwright label to auto-feed daemon
|
|
290
276
|
local all_labels="shipwright,$labels"
|
|
291
277
|
|
|
292
|
-
# Create GitHub issue
|
|
293
|
-
|
|
278
|
+
# Create GitHub issue; capture URL and parse issue number
|
|
279
|
+
local create_out
|
|
280
|
+
create_out=$(gh issue create \
|
|
294
281
|
--title "$title" \
|
|
295
282
|
--body "$description
|
|
296
283
|
|
|
@@ -301,14 +288,83 @@ create_issue_from_finding() {
|
|
|
301
288
|
- Created: \`$(now_iso)\`
|
|
302
289
|
- By: Autonomous loop (sw-autonomous.sh)
|
|
303
290
|
" \
|
|
304
|
-
--label "$all_labels" 2>/dev/null
|
|
305
|
-
|
|
306
|
-
return 0 || \
|
|
307
|
-
warn "Failed to create issue: $title" && \
|
|
291
|
+
--label "$all_labels" 2>/dev/null) || {
|
|
292
|
+
warn "Failed to create issue: $title"
|
|
308
293
|
return 1
|
|
294
|
+
}
|
|
295
|
+
success "Created issue: $title"
|
|
296
|
+
local issue_num
|
|
297
|
+
issue_num=$(echo "$create_out" | sed -n 's|.*/issues/\([0-9]*\)|\1|p')
|
|
298
|
+
[[ -n "$issue_num" ]] && echo "$issue_num"
|
|
299
|
+
return 0
|
|
309
300
|
}
|
|
310
301
|
|
|
311
302
|
# ─── Issue Processing from Analysis ────────────────────────────────────────
|
|
303
|
+
# Trigger pipeline for a finding issue (daemon will also pick it up; this runs immediately)
|
|
304
|
+
trigger_pipeline_for_finding() {
|
|
305
|
+
local issue_num="$1"
|
|
306
|
+
local title="$2"
|
|
307
|
+
if [[ -z "$issue_num" || ! "$issue_num" =~ ^[0-9]+$ ]]; then
|
|
308
|
+
return 0
|
|
309
|
+
fi
|
|
310
|
+
if [[ ! -x "$SCRIPT_DIR/sw-pipeline.sh" ]]; then
|
|
311
|
+
return 0
|
|
312
|
+
fi
|
|
313
|
+
info "Triggering pipeline for finding issue #${issue_num}: $title"
|
|
314
|
+
(cd "$REPO_DIR" && export REPO_DIR SCRIPT_DIR && bash "$SCRIPT_DIR/sw-pipeline.sh" start --issue "$issue_num" 2>/dev/null) &
|
|
315
|
+
emit_event "autonomous.pipeline_triggered" "issue=$issue_num" "title=$title"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
# Record finding outcome for learning (which findings led to successful fixes)
|
|
319
|
+
record_finding_pending() {
|
|
320
|
+
local issue_num="$1"
|
|
321
|
+
local finding_title="$2"
|
|
322
|
+
ensure_state_dir
|
|
323
|
+
local state_file="${STATE_DIR}/state.json"
|
|
324
|
+
local pending_file="${STATE_DIR}/pending_findings.jsonl"
|
|
325
|
+
[[ ! -f "$pending_file" ]] && touch "$pending_file"
|
|
326
|
+
echo "{\"issue_number\":$issue_num,\"finding_title\":\"${finding_title//\"/\\\"}\",\"created_at\":\"$(now_iso)\",\"outcome\":\"pending\"}" >> "$pending_file"
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
# Update outcomes for pending findings (called at start of each cycle)
|
|
330
|
+
update_finding_outcomes() {
|
|
331
|
+
[[ "$NO_GITHUB" == "true" ]] && return 0
|
|
332
|
+
local pending_file="${STATE_DIR}/pending_findings.jsonl"
|
|
333
|
+
[[ ! -f "$pending_file" ]] && return 0
|
|
334
|
+
local updated_file
|
|
335
|
+
updated_file=$(mktemp "${TMPDIR:-/tmp}/sw-autonomous-pending.XXXXXX")
|
|
336
|
+
while IFS= read -r line; do
|
|
337
|
+
[[ -z "$line" ]] && continue
|
|
338
|
+
local issue_num outcome
|
|
339
|
+
issue_num=$(echo "$line" | jq -r '.issue_number // empty')
|
|
340
|
+
outcome=$(echo "$line" | jq -r '.outcome // "pending"')
|
|
341
|
+
if [[ "$outcome" != "pending" || -z "$issue_num" ]]; then
|
|
342
|
+
echo "$line" >> "$updated_file"
|
|
343
|
+
continue
|
|
344
|
+
fi
|
|
345
|
+
local state
|
|
346
|
+
state=$(gh issue view "$issue_num" --json state 2>/dev/null | jq -r '.state // "OPEN"') || state="OPEN"
|
|
347
|
+
if [[ "$state" != "CLOSED" ]]; then
|
|
348
|
+
echo "$line" >> "$updated_file"
|
|
349
|
+
continue
|
|
350
|
+
fi
|
|
351
|
+
local merged=""
|
|
352
|
+
local merged_pipeline merged_daemon
|
|
353
|
+
merged_pipeline=$(gh pr list --head "pipeline/issue-${issue_num}" --json state -q '.[0].state' 2>/dev/null || echo "")
|
|
354
|
+
merged_daemon=$(gh pr list --head "daemon/issue-${issue_num}" --json state -q '.[0].state' 2>/dev/null || echo "")
|
|
355
|
+
[[ "$merged_pipeline" == "MERGED" || "$merged_daemon" == "MERGED" ]] && merged="MERGED"
|
|
356
|
+
if [[ "$merged" == "MERGED" ]]; then
|
|
357
|
+
outcome="success"
|
|
358
|
+
increment_counter "issues_completed"
|
|
359
|
+
emit_event "autonomous.finding_success" "issue=$issue_num"
|
|
360
|
+
else
|
|
361
|
+
outcome="failure"
|
|
362
|
+
emit_event "autonomous.finding_failure" "issue=$issue_num"
|
|
363
|
+
fi
|
|
364
|
+
echo "$line" | jq --arg o "$outcome" '.outcome = $o' >> "$updated_file"
|
|
365
|
+
done < "$pending_file"
|
|
366
|
+
mv "$updated_file" "$pending_file"
|
|
367
|
+
}
|
|
312
368
|
|
|
313
369
|
process_findings() {
|
|
314
370
|
local findings_file="$1"
|
|
@@ -320,7 +376,7 @@ process_findings() {
|
|
|
320
376
|
local created=0
|
|
321
377
|
local total=0
|
|
322
378
|
|
|
323
|
-
# Parse findings and create issues
|
|
379
|
+
# Parse findings and create issues; trigger pipeline for each; record for outcome tracking
|
|
324
380
|
while IFS= read -r finding; do
|
|
325
381
|
[[ -z "$finding" ]] && continue
|
|
326
382
|
|
|
@@ -344,14 +400,18 @@ process_findings() {
|
|
|
344
400
|
labels="${category}${labels:+,$labels}"
|
|
345
401
|
fi
|
|
346
402
|
|
|
347
|
-
|
|
403
|
+
local issue_num
|
|
404
|
+
issue_num=$(create_issue_from_finding "$title" "$description" "$priority" "$effort" "$labels")
|
|
405
|
+
if [[ $? -eq 0 && -n "$issue_num" ]]; then
|
|
348
406
|
created=$((created + 1))
|
|
349
407
|
increment_counter "issues_created"
|
|
350
|
-
emit_event "autonomous.issue_created" "title=$title" "priority=$priority" "effort=$effort"
|
|
408
|
+
emit_event "autonomous.issue_created" "title=$title" "priority=$priority" "effort=$effort" "issue=$issue_num"
|
|
409
|
+
trigger_pipeline_for_finding "$issue_num" "$title"
|
|
410
|
+
record_finding_pending "$issue_num" "$title"
|
|
351
411
|
fi
|
|
352
412
|
done < <(jq -c '.[]' "$findings_file" 2>/dev/null)
|
|
353
413
|
|
|
354
|
-
info "Created $created of $total findings as issues"
|
|
414
|
+
info "Created $created of $total findings as issues (pipelines triggered)"
|
|
355
415
|
echo "$created"
|
|
356
416
|
}
|
|
357
417
|
|
|
@@ -452,6 +512,29 @@ show_history() {
|
|
|
452
512
|
|
|
453
513
|
# ─── Loop Control ──────────────────────────────────────────────────────────
|
|
454
514
|
|
|
515
|
+
# Scheduler: run cycles at interval (real scheduler instead of manual cycle only)
|
|
516
|
+
run_scheduler() {
|
|
517
|
+
ensure_state_dir
|
|
518
|
+
init_state
|
|
519
|
+
update_state "status" "running"
|
|
520
|
+
info "Autonomous scheduler started (cycle every $(get_config "cycle_interval_minutes" "60") minutes)"
|
|
521
|
+
emit_event "autonomous.scheduler_started"
|
|
522
|
+
|
|
523
|
+
while true; do
|
|
524
|
+
local status
|
|
525
|
+
status=$(read_state | jq -r '.status // "running"')
|
|
526
|
+
if [[ "$status" != "running" ]]; then
|
|
527
|
+
info "Status is ${status} — exiting scheduler"
|
|
528
|
+
break
|
|
529
|
+
fi
|
|
530
|
+
run_single_cycle || true
|
|
531
|
+
local interval_mins
|
|
532
|
+
interval_mins=$(get_config "cycle_interval_minutes" "60")
|
|
533
|
+
info "Next cycle in ${interval_mins} minutes"
|
|
534
|
+
sleep $((interval_mins * 60))
|
|
535
|
+
done
|
|
536
|
+
}
|
|
537
|
+
|
|
455
538
|
start_loop() {
|
|
456
539
|
ensure_state_dir
|
|
457
540
|
init_state
|
|
@@ -491,6 +574,9 @@ run_single_cycle() {
|
|
|
491
574
|
ensure_state_dir
|
|
492
575
|
update_state "status" "analyzing"
|
|
493
576
|
|
|
577
|
+
# Update outcomes for pending findings (which led to successful fixes)
|
|
578
|
+
update_finding_outcomes
|
|
579
|
+
|
|
494
580
|
info "Running single analysis cycle..."
|
|
495
581
|
|
|
496
582
|
# Step 1: Analysis
|
|
@@ -574,11 +660,12 @@ USAGE
|
|
|
574
660
|
sw autonomous <command> [options]
|
|
575
661
|
|
|
576
662
|
COMMANDS
|
|
577
|
-
start
|
|
663
|
+
start Set status to running (use with external scheduler)
|
|
664
|
+
run Run scheduler: cycle every N minutes until stopped
|
|
578
665
|
stop Stop the loop gracefully
|
|
579
666
|
pause Pause without losing state
|
|
580
667
|
resume Resume from pause
|
|
581
|
-
cycle Run one analysis cycle manually
|
|
668
|
+
cycle Run one analysis cycle manually (create issues + trigger pipelines)
|
|
582
669
|
status Show loop status, recent cycles, issue creation stats
|
|
583
670
|
config [show|set] Show or set configuration
|
|
584
671
|
history Show past cycles and their outcomes
|
|
@@ -611,6 +698,9 @@ main() {
|
|
|
611
698
|
start)
|
|
612
699
|
start_loop
|
|
613
700
|
;;
|
|
701
|
+
run)
|
|
702
|
+
run_scheduler
|
|
703
|
+
;;
|
|
614
704
|
stop)
|
|
615
705
|
stop_loop
|
|
616
706
|
;;
|
package/scripts/sw-changelog.sh
CHANGED
package/scripts/sw-checkpoint.sh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
10
10
|
|
|
11
|
-
VERSION="2.
|
|
11
|
+
VERSION="2.1.0"
|
|
12
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
13
|
|
|
14
14
|
# ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
|
package/scripts/sw-ci.sh
CHANGED
package/scripts/sw-cleanup.sh
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# ║ Default: dry-run (shows what would be cleaned). ║
|
|
6
6
|
# ║ Use --force to actually kill sessions and remove files. ║
|
|
7
7
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
8
|
-
VERSION="2.
|
|
8
|
+
VERSION="2.1.0"
|
|
9
9
|
set -euo pipefail
|
|
10
10
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
11
11
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="2.
|
|
9
|
+
VERSION="2.1.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -382,6 +382,50 @@ auto_fix() {
|
|
|
382
382
|
echo "$fixed"
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
+
# ─── Claude-powered semantic review (logic, race conditions, API usage, requirements) ──
|
|
386
|
+
run_claude_semantic_review() {
|
|
387
|
+
local diff_content="$1"
|
|
388
|
+
local requirements="${2:-}"
|
|
389
|
+
[[ -z "$diff_content" ]] && return 0
|
|
390
|
+
if ! command -v claude &>/dev/null; then
|
|
391
|
+
return 0
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
local prompt="You are a senior code reviewer. Review this git diff for semantic issues (not just style).
|
|
395
|
+
|
|
396
|
+
Focus on:
|
|
397
|
+
1. Logic errors and edge cases (off-by-one, null/empty handling, wrong conditions)
|
|
398
|
+
2. Race conditions and concurrency issues (shared state, ordering, locks)
|
|
399
|
+
3. Incorrect or unsafe API usage (wrong arguments, missing error handling, deprecated APIs)
|
|
400
|
+
4. Security issues (injection, auth bypass, sensitive data exposure)
|
|
401
|
+
5. Requirements alignment: does the change match the intended behavior?
|
|
402
|
+
|
|
403
|
+
For each issue use this format on its own line:
|
|
404
|
+
- **[SEVERITY]** file:line — brief description
|
|
405
|
+
|
|
406
|
+
Severity: Critical, Bug, Security, Warning, Suggestion.
|
|
407
|
+
If no issues found, reply with exactly: Review clean — no semantic issues found.
|
|
408
|
+
|
|
409
|
+
## Diff
|
|
410
|
+
${diff_content}
|
|
411
|
+
"
|
|
412
|
+
[[ -n "$requirements" ]] && prompt="${prompt}
|
|
413
|
+
|
|
414
|
+
## Requirements / intended behavior
|
|
415
|
+
${requirements}
|
|
416
|
+
"
|
|
417
|
+
|
|
418
|
+
local claude_out
|
|
419
|
+
claude_out=$(claude -p "$prompt" --max-turns 3 2>/dev/null || true)
|
|
420
|
+
[[ -z "$claude_out" ]] && return 0
|
|
421
|
+
|
|
422
|
+
if echo "$claude_out" | grep -qi "Review clean — no semantic issues found"; then
|
|
423
|
+
return 0
|
|
424
|
+
fi
|
|
425
|
+
echo "$claude_out" | grep -oE '\*\*\[?(Critical|Bug|Security|Warning|Suggestion)\]?\*\*[^—]*—[^$]+' 2>/dev/null || \
|
|
426
|
+
echo "$claude_out" | grep -oE '-\s+\*\*[^*]+\*\*[^\n]+' 2>/dev/null || true
|
|
427
|
+
}
|
|
428
|
+
|
|
385
429
|
# ─── Review Subcommand ───────────────────────────────────────────────────────
|
|
386
430
|
|
|
387
431
|
review_changes() {
|
|
@@ -410,6 +454,23 @@ review_changes() {
|
|
|
410
454
|
|
|
411
455
|
[[ ${#changed_files[@]} -eq 0 ]] && { success "No changes to review"; return 0; }
|
|
412
456
|
|
|
457
|
+
# Claude-powered semantic review (logic, race conditions, API usage) when available
|
|
458
|
+
local diff_content
|
|
459
|
+
if [[ "$review_scope" == "staged" ]]; then
|
|
460
|
+
diff_content=$(cd "$REPO_DIR" && git diff --cached 2>/dev/null || true)
|
|
461
|
+
else
|
|
462
|
+
diff_content=$(cd "$REPO_DIR" && git diff main...HEAD 2>/dev/null || true)
|
|
463
|
+
fi
|
|
464
|
+
local semantic_issues=()
|
|
465
|
+
if [[ -n "$diff_content" ]] && command -v claude &>/dev/null; then
|
|
466
|
+
info "Running Claude semantic review (logic, race conditions, API usage)..."
|
|
467
|
+
mapfile -t semantic_issues < <(run_claude_semantic_review "$diff_content" "${REVIEW_REQUIREMENTS:-}" || true)
|
|
468
|
+
if [[ ${#semantic_issues[@]} -gt 0 ]]; then
|
|
469
|
+
total_issues=$((total_issues + ${#semantic_issues[@]}))
|
|
470
|
+
review_output=$(echo "$review_output" | jq --argjson arr "$(printf '%s\n' "${semantic_issues[@]}" | jq -R . | jq -s .)" '.semantic_findings = $arr' 2>/dev/null || echo "$review_output")
|
|
471
|
+
fi
|
|
472
|
+
fi
|
|
473
|
+
|
|
413
474
|
for file in "${changed_files[@]}"; do
|
|
414
475
|
local file_path="${REPO_DIR}/${file}"
|
|
415
476
|
[[ ! -f "$file_path" ]] && continue
|
package/scripts/sw-connect.sh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
10
10
|
|
|
11
|
-
VERSION="2.
|
|
11
|
+
VERSION="2.1.0"
|
|
12
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
13
|
|
|
14
14
|
# ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
|
package/scripts/sw-context.sh
CHANGED
package/scripts/sw-cost.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="2.
|
|
9
|
+
VERSION="2.1.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -70,6 +70,12 @@ COST_DIR="${HOME}/.shipwright"
|
|
|
70
70
|
COST_FILE="${COST_DIR}/costs.json"
|
|
71
71
|
BUDGET_FILE="${COST_DIR}/budget.json"
|
|
72
72
|
|
|
73
|
+
# Source sw-db.sh for SQLite cost functions (if available)
|
|
74
|
+
_COST_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
75
|
+
if [[ -f "$_COST_SCRIPT_DIR/sw-db.sh" ]]; then
|
|
76
|
+
source "$_COST_SCRIPT_DIR/sw-db.sh" 2>/dev/null || true
|
|
77
|
+
fi
|
|
78
|
+
|
|
73
79
|
ensure_cost_dir() {
|
|
74
80
|
mkdir -p "$COST_DIR"
|
|
75
81
|
[[ -f "$COST_FILE" ]] || echo '{"entries":[],"summary":{}}' > "$COST_FILE"
|
|
@@ -143,6 +149,7 @@ cost_calculate() {
|
|
|
143
149
|
|
|
144
150
|
# cost_record <input_tokens> <output_tokens> <model> <stage> [issue]
|
|
145
151
|
# Records a cost entry to the cost file and events log.
|
|
152
|
+
# Tries SQLite first, always writes to JSON for backward compat.
|
|
146
153
|
cost_record() {
|
|
147
154
|
local input_tokens="${1:-0}"
|
|
148
155
|
local output_tokens="${2:-0}"
|
|
@@ -155,6 +162,12 @@ cost_record() {
|
|
|
155
162
|
local cost_usd
|
|
156
163
|
cost_usd=$(cost_calculate "$input_tokens" "$output_tokens" "$model")
|
|
157
164
|
|
|
165
|
+
# Try SQLite first
|
|
166
|
+
if type db_record_cost &>/dev/null; then
|
|
167
|
+
db_record_cost "$input_tokens" "$output_tokens" "$model" "$stage" "$cost_usd" "$issue" 2>/dev/null || true
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# Always write to JSON (dual-write period)
|
|
158
171
|
(
|
|
159
172
|
if command -v flock &>/dev/null; then
|
|
160
173
|
flock -w 10 200 2>/dev/null || { warn "Cost lock timeout"; }
|
|
@@ -197,9 +210,20 @@ cost_check_budget() {
|
|
|
197
210
|
|
|
198
211
|
ensure_cost_dir
|
|
199
212
|
|
|
213
|
+
# Try DB for budget info
|
|
200
214
|
local budget_enabled budget_usd
|
|
201
|
-
|
|
202
|
-
|
|
215
|
+
if type db_get_budget &>/dev/null && type db_available &>/dev/null && db_available 2>/dev/null; then
|
|
216
|
+
local db_budget
|
|
217
|
+
db_budget=$(db_get_budget 2>/dev/null || true)
|
|
218
|
+
if [[ -n "$db_budget" ]]; then
|
|
219
|
+
budget_enabled=$(echo "$db_budget" | cut -d'|' -f2)
|
|
220
|
+
budget_usd=$(echo "$db_budget" | cut -d'|' -f1)
|
|
221
|
+
[[ "$budget_enabled" == "1" ]] && budget_enabled="true"
|
|
222
|
+
fi
|
|
223
|
+
fi
|
|
224
|
+
# Fallback to JSON
|
|
225
|
+
budget_enabled="${budget_enabled:-$(jq -r '.enabled' "$BUDGET_FILE" 2>/dev/null || echo "false")}"
|
|
226
|
+
budget_usd="${budget_usd:-$(jq -r '.daily_budget_usd' "$BUDGET_FILE" 2>/dev/null || echo "0")}"
|
|
203
227
|
|
|
204
228
|
if [[ "$budget_enabled" != "true" || "$budget_usd" == "0" ]]; then
|
|
205
229
|
return 0
|
|
@@ -244,6 +268,17 @@ cost_check_budget() {
|
|
|
244
268
|
cost_remaining_budget() {
|
|
245
269
|
ensure_cost_dir
|
|
246
270
|
|
|
271
|
+
# Try DB for remaining budget (single query)
|
|
272
|
+
if type db_remaining_budget &>/dev/null && type db_available &>/dev/null && db_available 2>/dev/null; then
|
|
273
|
+
local db_result
|
|
274
|
+
db_result=$(db_remaining_budget 2>/dev/null || true)
|
|
275
|
+
if [[ -n "$db_result" ]]; then
|
|
276
|
+
echo "$db_result"
|
|
277
|
+
return 0
|
|
278
|
+
fi
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
# Fallback to JSON
|
|
247
282
|
local budget_enabled budget_usd
|
|
248
283
|
budget_enabled=$(jq -r '.enabled' "$BUDGET_FILE" 2>/dev/null || echo "false")
|
|
249
284
|
budget_usd=$(jq -r '.daily_budget_usd' "$BUDGET_FILE" 2>/dev/null || echo "0")
|
|
@@ -792,6 +827,12 @@ budget_set() {
|
|
|
792
827
|
|
|
793
828
|
ensure_cost_dir
|
|
794
829
|
|
|
830
|
+
# Write to DB if available
|
|
831
|
+
if type db_set_budget &>/dev/null; then
|
|
832
|
+
db_set_budget "$amount" 2>/dev/null || true
|
|
833
|
+
fi
|
|
834
|
+
|
|
835
|
+
# Always write to JSON (dual-write)
|
|
795
836
|
local tmp_file
|
|
796
837
|
tmp_file=$(mktemp)
|
|
797
838
|
jq --arg amt "$amount" \
|