shipwright-cli 3.0.0 → 3.2.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 +21 -7
- package/completions/_shipwright +247 -93
- package/completions/shipwright.bash +69 -15
- package/completions/shipwright.fish +309 -41
- package/config/decision-tiers.json +55 -0
- package/config/defaults.json +25 -2
- package/config/event-schema.json +142 -5
- package/config/policy.json +8 -0
- package/dashboard/public/index.html +6 -0
- package/dashboard/public/styles.css +76 -0
- package/dashboard/server.ts +51 -0
- package/dashboard/src/core/api.ts +5 -0
- package/dashboard/src/types/api.ts +10 -0
- package/dashboard/src/views/metrics.ts +69 -1
- package/package.json +3 -3
- package/scripts/lib/architecture.sh +2 -1
- package/scripts/lib/bootstrap.sh +0 -0
- package/scripts/lib/config.sh +0 -0
- package/scripts/lib/daemon-adaptive.sh +4 -2
- package/scripts/lib/daemon-dispatch.sh +24 -1
- package/scripts/lib/daemon-failure.sh +0 -0
- package/scripts/lib/daemon-health.sh +0 -0
- package/scripts/lib/daemon-patrol.sh +42 -7
- package/scripts/lib/daemon-poll.sh +17 -0
- package/scripts/lib/daemon-state.sh +17 -0
- package/scripts/lib/daemon-triage.sh +1 -1
- package/scripts/lib/decide-autonomy.sh +295 -0
- package/scripts/lib/decide-scoring.sh +228 -0
- package/scripts/lib/decide-signals.sh +462 -0
- package/scripts/lib/fleet-failover.sh +0 -0
- package/scripts/lib/helpers.sh +19 -18
- package/scripts/lib/pipeline-detection.sh +1 -1
- package/scripts/lib/pipeline-github.sh +0 -0
- package/scripts/lib/pipeline-intelligence.sh +23 -4
- package/scripts/lib/pipeline-quality-checks.sh +11 -6
- package/scripts/lib/pipeline-quality.sh +0 -0
- package/scripts/lib/pipeline-stages.sh +330 -33
- package/scripts/lib/pipeline-state.sh +14 -0
- package/scripts/lib/policy.sh +0 -0
- package/scripts/lib/test-helpers.sh +0 -0
- package/scripts/postinstall.mjs +75 -1
- package/scripts/signals/example-collector.sh +36 -0
- package/scripts/sw +8 -4
- package/scripts/sw-activity.sh +1 -7
- package/scripts/sw-adaptive.sh +7 -7
- 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 +1 -1
- package/scripts/sw-changelog.sh +1 -1
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +11 -6
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +36 -17
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +1 -1
- package/scripts/sw-cost.sh +71 -5
- package/scripts/sw-daemon.sh +6 -3
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +53 -38
- package/scripts/sw-decide.sh +685 -0
- 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 +80 -4
- package/scripts/sw-doc-fleet.sh +1 -1
- package/scripts/sw-docs-agent.sh +1 -1
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +1 -1
- package/scripts/sw-dora.sh +1 -1
- package/scripts/sw-durable.sh +9 -5
- package/scripts/sw-e2e-orchestrator.sh +1 -1
- package/scripts/sw-eventbus.sh +7 -4
- package/scripts/sw-evidence.sh +1 -1
- package/scripts/sw-feedback.sh +1 -1
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +1 -1
- package/scripts/sw-fleet-viz.sh +6 -4
- package/scripts/sw-fleet.sh +1 -1
- package/scripts/sw-github-app.sh +3 -2
- package/scripts/sw-github-checks.sh +1 -1
- 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 +5 -3
- package/scripts/sw-incident.sh +9 -5
- package/scripts/sw-init.sh +1 -1
- package/scripts/sw-instrument.sh +1 -1
- package/scripts/sw-intelligence.sh +11 -6
- 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 +338 -32
- package/scripts/sw-memory.sh +23 -6
- package/scripts/sw-mission-control.sh +1 -1
- package/scripts/sw-model-router.sh +3 -2
- package/scripts/sw-otel.sh +8 -4
- package/scripts/sw-oversight.sh +1 -1
- package/scripts/sw-pipeline-composer.sh +3 -1
- package/scripts/sw-pipeline-vitals.sh +11 -6
- package/scripts/sw-pipeline.sh +92 -8
- package/scripts/sw-pm.sh +5 -4
- package/scripts/sw-pr-lifecycle.sh +7 -4
- package/scripts/sw-predictive.sh +11 -5
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +3 -2
- package/scripts/sw-quality.sh +21 -10
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +1 -1
- 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-review-rerun.sh +1 -1
- package/scripts/sw-scale.sh +69 -11
- package/scripts/sw-security-audit.sh +1 -1
- package/scripts/sw-self-optimize.sh +168 -4
- package/scripts/sw-session.sh +3 -3
- package/scripts/sw-setup.sh +1 -1
- package/scripts/sw-standup.sh +1 -1
- package/scripts/sw-status.sh +1 -1
- package/scripts/sw-strategic.sh +11 -6
- package/scripts/sw-stream.sh +7 -4
- package/scripts/sw-swarm.sh +3 -2
- package/scripts/sw-team-stages.sh +1 -1
- package/scripts/sw-templates.sh +3 -3
- package/scripts/sw-testgen.sh +11 -6
- package/scripts/sw-tmux-pipeline.sh +1 -1
- package/scripts/sw-tmux.sh +35 -1
- package/scripts/sw-trace.sh +1 -1
- package/scripts/sw-tracker.sh +1 -1
- package/scripts/sw-triage.sh +7 -7
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +1 -1
- package/scripts/sw-webhook.sh +3 -2
- package/scripts/sw-widgets.sh +7 -4
- package/scripts/sw-worktree.sh +1 -1
- package/scripts/update-homebrew-sha.sh +21 -15
package/scripts/postinstall.mjs
CHANGED
|
@@ -11,8 +11,11 @@ import {
|
|
|
11
11
|
readFileSync,
|
|
12
12
|
writeFileSync,
|
|
13
13
|
appendFileSync,
|
|
14
|
+
chmodSync,
|
|
15
|
+
readdirSync,
|
|
14
16
|
} from "fs";
|
|
15
|
-
import { join } from "path";
|
|
17
|
+
import { join, basename } from "path";
|
|
18
|
+
import { execSync } from "child_process";
|
|
16
19
|
|
|
17
20
|
const HOME = process.env.HOME || process.env.USERPROFILE;
|
|
18
21
|
const PKG_DIR = join(import.meta.dirname, "..");
|
|
@@ -130,6 +133,77 @@ try {
|
|
|
130
133
|
success("Migrated legacy config (originals preserved)");
|
|
131
134
|
}
|
|
132
135
|
|
|
136
|
+
// Set executable bits on all scripts (npm strips them on some platforms)
|
|
137
|
+
const scriptsDir = join(PKG_DIR, "scripts");
|
|
138
|
+
if (existsSync(scriptsDir)) {
|
|
139
|
+
let madeExecutable = 0;
|
|
140
|
+
for (const file of readdirSync(scriptsDir)) {
|
|
141
|
+
const fp = join(scriptsDir, file);
|
|
142
|
+
try {
|
|
143
|
+
chmodSync(fp, 0o755);
|
|
144
|
+
madeExecutable++;
|
|
145
|
+
} catch (_) {
|
|
146
|
+
// skip non-files
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const libDir = join(scriptsDir, "lib");
|
|
150
|
+
if (existsSync(libDir)) {
|
|
151
|
+
for (const file of readdirSync(libDir)) {
|
|
152
|
+
try {
|
|
153
|
+
chmodSync(join(libDir, file), 0o755);
|
|
154
|
+
madeExecutable++;
|
|
155
|
+
} catch (_) {}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
success(`Set executable bits on ${madeExecutable} scripts`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Install shell completions for the user's current shell
|
|
162
|
+
const completionsDir = join(PKG_DIR, "completions");
|
|
163
|
+
if (existsSync(completionsDir)) {
|
|
164
|
+
const shell = basename(process.env.SHELL || "/bin/bash");
|
|
165
|
+
try {
|
|
166
|
+
if (shell === "bash") {
|
|
167
|
+
const dest =
|
|
168
|
+
process.env.BASH_COMPLETION_USER_DIR ||
|
|
169
|
+
join(
|
|
170
|
+
process.env.XDG_DATA_HOME || join(HOME, ".local", "share"),
|
|
171
|
+
"bash-completion",
|
|
172
|
+
"completions",
|
|
173
|
+
);
|
|
174
|
+
ensureDir(dest);
|
|
175
|
+
cpSync(
|
|
176
|
+
join(completionsDir, "shipwright.bash"),
|
|
177
|
+
join(dest, "shipwright"),
|
|
178
|
+
);
|
|
179
|
+
cpSync(join(completionsDir, "shipwright.bash"), join(dest, "sw"));
|
|
180
|
+
success(`Installed bash completions to ${dest}`);
|
|
181
|
+
} else if (shell === "zsh") {
|
|
182
|
+
const dest = join(HOME, ".zfunc");
|
|
183
|
+
ensureDir(dest);
|
|
184
|
+
cpSync(join(completionsDir, "_shipwright"), join(dest, "_shipwright"));
|
|
185
|
+
cpSync(join(completionsDir, "_shipwright"), join(dest, "_sw"));
|
|
186
|
+
success(`Installed zsh completions to ${dest}`);
|
|
187
|
+
} else if (shell === "fish") {
|
|
188
|
+
const dest = join(
|
|
189
|
+
process.env.XDG_CONFIG_HOME || join(HOME, ".config"),
|
|
190
|
+
"fish",
|
|
191
|
+
"completions",
|
|
192
|
+
);
|
|
193
|
+
ensureDir(dest);
|
|
194
|
+
cpSync(
|
|
195
|
+
join(completionsDir, "shipwright.fish"),
|
|
196
|
+
join(dest, "shipwright.fish"),
|
|
197
|
+
);
|
|
198
|
+
cpSync(join(completionsDir, "shipwright.fish"), join(dest, "sw.fish"));
|
|
199
|
+
success(`Installed fish completions to ${dest}`);
|
|
200
|
+
}
|
|
201
|
+
} catch (e) {
|
|
202
|
+
warn(`Could not auto-install completions: ${e.message}`);
|
|
203
|
+
info(`Run: shipwright init (or: bash scripts/install-completions.sh)`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
133
207
|
// Print success banner
|
|
134
208
|
console.log();
|
|
135
209
|
console.log(`${GREEN}${BOLD}Shipwright CLI installed!${RESET} Next steps:`);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ Example External Signal Collector for Shipwright Decision Engine ║
|
|
4
|
+
# ║ Place custom collectors in scripts/signals/ — they're auto-discovered ║
|
|
5
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
#
|
|
7
|
+
# Output: one JSON candidate per line (JSONL).
|
|
8
|
+
# Required fields: id, signal, category, title, description, risk_score,
|
|
9
|
+
# confidence, dedup_key
|
|
10
|
+
# Optional fields: evidence (object)
|
|
11
|
+
#
|
|
12
|
+
# The decision engine collects output from all scripts/signals/*.sh files,
|
|
13
|
+
# validates each line as JSON, and includes valid candidates in the scoring
|
|
14
|
+
# pipeline.
|
|
15
|
+
#
|
|
16
|
+
# Categories (determines autonomy tier):
|
|
17
|
+
# auto: deps_patch, deps_minor, security_patch, test_coverage,
|
|
18
|
+
# doc_sync, dead_code
|
|
19
|
+
# propose: refactor_hotspot, architecture_drift, performance_regression,
|
|
20
|
+
# deps_major, security_critical, recurring_failure, dora_regression
|
|
21
|
+
# draft: new_feature, breaking_change, business_logic, api_change,
|
|
22
|
+
# data_model_change
|
|
23
|
+
#
|
|
24
|
+
# Example: detect a custom condition and emit a candidate
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
set -euo pipefail
|
|
28
|
+
|
|
29
|
+
# Example: check if a TODO count exceeds a threshold
|
|
30
|
+
TODO_COUNT=$(grep -r "TODO" --include="*.ts" --include="*.js" --include="*.sh" . 2>/dev/null | wc -l | tr -d ' ' || echo "0")
|
|
31
|
+
|
|
32
|
+
if [[ "${TODO_COUNT:-0}" -gt 50 ]]; then
|
|
33
|
+
cat <<EOF
|
|
34
|
+
{"id":"custom-todo-cleanup","signal":"custom","category":"dead_code","title":"Clean up ${TODO_COUNT} TODOs","description":"Codebase has ${TODO_COUNT} TODO comments — consider a cleanup sprint","evidence":{"todo_count":${TODO_COUNT}},"risk_score":15,"confidence":"0.70","dedup_key":"custom:todo:cleanup"}
|
|
35
|
+
EOF
|
|
36
|
+
fi
|
package/scripts/sw
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
|
-
VERSION="3.
|
|
8
|
+
VERSION="3.2.0"
|
|
9
9
|
|
|
10
10
|
# Resolve symlinks (required for npm global install where bin/ symlinks to node_modules/)
|
|
11
11
|
SOURCE="${BASH_SOURCE[0]}"
|
|
@@ -193,7 +193,8 @@ route_quality() {
|
|
|
193
193
|
security-audit|audit) exec "$SCRIPT_DIR/sw-security-audit.sh" "$@" ;;
|
|
194
194
|
testgen) exec "$SCRIPT_DIR/sw-testgen.sh" "$@" ;;
|
|
195
195
|
hygiene) exec "$SCRIPT_DIR/sw-hygiene.sh" "$@" ;;
|
|
196
|
-
|
|
196
|
+
validate|gate) exec "$SCRIPT_DIR/sw-quality.sh" "$@" ;;
|
|
197
|
+
help|*) echo "Usage: shipwright quality {code-review|security-audit|testgen|hygiene|validate}"; exit 1 ;;
|
|
197
198
|
esac
|
|
198
199
|
}
|
|
199
200
|
|
|
@@ -530,14 +531,17 @@ main() {
|
|
|
530
531
|
evidence|ev)
|
|
531
532
|
exec "$SCRIPT_DIR/sw-evidence.sh" "$@"
|
|
532
533
|
;;
|
|
534
|
+
decide)
|
|
535
|
+
exec "$SCRIPT_DIR/sw-decide.sh" "$@"
|
|
536
|
+
;;
|
|
533
537
|
otel)
|
|
534
538
|
exec "$SCRIPT_DIR/sw-otel.sh" "$@"
|
|
535
539
|
;;
|
|
536
540
|
triage)
|
|
537
541
|
exec "$SCRIPT_DIR/sw-triage.sh" "$@"
|
|
538
542
|
;;
|
|
539
|
-
|
|
540
|
-
exec "$SCRIPT_DIR/sw-
|
|
543
|
+
pipeline-composer|composer)
|
|
544
|
+
exec "$SCRIPT_DIR/sw-pipeline-composer.sh" "$@"
|
|
541
545
|
;;
|
|
542
546
|
oversight)
|
|
543
547
|
exec "$SCRIPT_DIR/sw-oversight.sh" "$@"
|
package/scripts/sw-activity.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="3.
|
|
9
|
+
VERSION="3.2.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
@@ -332,7 +332,6 @@ cmd_stats() {
|
|
|
332
332
|
local end_time=""
|
|
333
333
|
|
|
334
334
|
# Read directly to avoid subshell issues
|
|
335
|
-
echo "DEBUG: Starting read loop..." >&2
|
|
336
335
|
while IFS= read -r line; do
|
|
337
336
|
[ -z "$line" ] && continue
|
|
338
337
|
|
|
@@ -363,14 +362,9 @@ cmd_stats() {
|
|
|
363
362
|
end_time="$ts"
|
|
364
363
|
done < <(grep -v '^$' "$EVENTS_FILE" 2>/dev/null)
|
|
365
364
|
|
|
366
|
-
echo "DEBUG: Read complete, total=$total_events" >&2
|
|
367
|
-
echo "DEBUG: agents_seen length: ${#agents_seen}" >&2
|
|
368
365
|
local unique_agents
|
|
369
|
-
echo "DEBUG: About to compute unique..." >&2
|
|
370
366
|
unique_agents=$(sort -u <<< "$agents_seen" | grep -v '^$' | wc -l | tr -d ' ')
|
|
371
|
-
echo "DEBUG: unique_agents=$unique_agents" >&2
|
|
372
367
|
|
|
373
|
-
echo "DEBUG: About to print results..." >&2
|
|
374
368
|
printf "${BOLD}Total Events:${RESET} %d\n" "$total_events"
|
|
375
369
|
printf "${BOLD}Commits:${RESET} %d\n" "$commits"
|
|
376
370
|
printf "${BOLD}Tests:${RESET} %d\n" "$tests"
|
package/scripts/sw-adaptive.sh
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
3
|
# ║ shipwright adaptive — data-driven pipeline tuning ║
|
|
4
|
-
# ║ Replace
|
|
4
|
+
# ║ Replace static defaults with learned values from historical runs ║
|
|
5
5
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="3.
|
|
9
|
+
VERSION="3.2.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
@@ -712,10 +712,10 @@ cmd_compare() {
|
|
|
712
712
|
esac
|
|
713
713
|
done
|
|
714
714
|
|
|
715
|
-
info "Learned vs
|
|
715
|
+
info "Learned vs Default Values for ${CYAN}${repo}${RESET}"
|
|
716
716
|
echo ""
|
|
717
717
|
|
|
718
|
-
printf "%-25s %-15s %-15s %-15s\n" "Metric" "
|
|
718
|
+
printf "%-25s %-15s %-15s %-15s\n" "Metric" "Default" "Learned" "Difference"
|
|
719
719
|
printf "%s\n" "$(printf '%.0s─' {1..70})"
|
|
720
720
|
|
|
721
721
|
# Timeout
|
|
@@ -841,7 +841,7 @@ ${BOLD}USAGE${RESET}
|
|
|
841
841
|
|
|
842
842
|
${BOLD}SUBCOMMANDS${RESET}
|
|
843
843
|
${CYAN}get${RESET} <metric> [--stage S] [--repo R] [--complexity C] [--default V]
|
|
844
|
-
Return adaptive value for a metric (replaces
|
|
844
|
+
Return adaptive value for a metric (replaces static defaults)
|
|
845
845
|
Metrics: timeout, iterations, model, team_size, template, poll_interval,
|
|
846
846
|
retry_limit, quality_threshold, coverage_min
|
|
847
847
|
|
|
@@ -852,7 +852,7 @@ ${BOLD}SUBCOMMANDS${RESET}
|
|
|
852
852
|
Rebuild models from events.jsonl (run after significant pipeline activity)
|
|
853
853
|
|
|
854
854
|
${CYAN}compare${RESET} [--repo REPO]
|
|
855
|
-
Side-by-side table: learned vs
|
|
855
|
+
Side-by-side table: learned vs default values
|
|
856
856
|
|
|
857
857
|
${CYAN}recommend${RESET} --issue N [--repo REPO]
|
|
858
858
|
Full JSON recommendation for an issue (template, model, team_size, etc.)
|
|
@@ -876,7 +876,7 @@ ${BOLD}EXAMPLES${RESET}
|
|
|
876
876
|
# Get complete recommendation for issue #42
|
|
877
877
|
sw adaptive recommend --issue 42
|
|
878
878
|
|
|
879
|
-
# Compare learned vs
|
|
879
|
+
# Compare learned vs defaults
|
|
880
880
|
sw adaptive compare
|
|
881
881
|
|
|
882
882
|
${BOLD}STORAGE${RESET}
|
package/scripts/sw-auth.sh
CHANGED
package/scripts/sw-autonomous.sh
CHANGED
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="3.
|
|
11
|
+
VERSION="3.2.0"
|
|
12
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
13
13
|
|
|
14
14
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
package/scripts/sw-ci.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="3.
|
|
9
|
+
VERSION="3.2.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -227,15 +227,20 @@ cmd_analyze() {
|
|
|
227
227
|
info "Analyzing workflow efficiency"
|
|
228
228
|
|
|
229
229
|
local job_count step_count matrix_enabled
|
|
230
|
-
job_count=$(grep -c "^ [a-z_-]*:" "$workflow_file" 2>/dev/null ||
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
job_count=$(grep -c "^ [a-z_-]*:" "$workflow_file" 2>/dev/null || true)
|
|
231
|
+
job_count="${job_count:-0}"
|
|
232
|
+
step_count=$(grep -c " - name:" "$workflow_file" 2>/dev/null || true)
|
|
233
|
+
step_count="${step_count:-0}"
|
|
234
|
+
matrix_enabled=$(grep -c "matrix:" "$workflow_file" 2>/dev/null || true)
|
|
235
|
+
matrix_enabled="${matrix_enabled:-0}"
|
|
233
236
|
|
|
234
237
|
local has_cache
|
|
235
|
-
has_cache=$(grep -c "actions/cache" "$workflow_file" 2>/dev/null ||
|
|
238
|
+
has_cache=$(grep -c "actions/cache" "$workflow_file" 2>/dev/null || true)
|
|
239
|
+
has_cache="${has_cache:-0}"
|
|
236
240
|
|
|
237
241
|
local has_timeout
|
|
238
|
-
has_timeout=$(grep -c "timeout-minutes:" "$workflow_file" 2>/dev/null ||
|
|
242
|
+
has_timeout=$(grep -c "timeout-minutes:" "$workflow_file" 2>/dev/null || true)
|
|
243
|
+
has_timeout="${has_timeout:-0}"
|
|
239
244
|
|
|
240
245
|
echo ""
|
|
241
246
|
echo -e "${BOLD}Workflow Analysis: $(basename "$workflow_file")${RESET}"
|
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="3.
|
|
8
|
+
VERSION="3.2.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="3.
|
|
9
|
+
VERSION="3.2.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -72,7 +72,8 @@ detect_code_smells() {
|
|
|
72
72
|
|
|
73
73
|
# Check 1: Long functions (>60 lines in bash)
|
|
74
74
|
local func_count
|
|
75
|
-
func_count=$(grep -c "^[a-zA-Z_][a-zA-Z0-9_]*().*{" "$target_file" 2>/dev/null ||
|
|
75
|
+
func_count=$(grep -c "^[a-zA-Z_][a-zA-Z0-9_]*().*{" "$target_file" 2>/dev/null || true)
|
|
76
|
+
func_count="${func_count:-0}"
|
|
76
77
|
if [[ "$func_count" -gt 0 ]]; then
|
|
77
78
|
while IFS= read -r line; do
|
|
78
79
|
if [[ "$line" =~ ^[a-zA-Z_][a-zA-Z0-9_]*\(\).*\{ ]]; then
|
|
@@ -106,7 +107,8 @@ detect_code_smells() {
|
|
|
106
107
|
|
|
107
108
|
# Check 3: Duplicate code patterns (repeated >3 times)
|
|
108
109
|
local dup_count=0
|
|
109
|
-
dup_count=$(grep -c '^\s*\(cd\|cd\|mkdir\|rm\|echo\)' "$target_file" 2>/dev/null ||
|
|
110
|
+
dup_count=$(grep -c '^\s*\(cd\|cd\|mkdir\|rm\|echo\)' "$target_file" 2>/dev/null || true)
|
|
111
|
+
dup_count="${dup_count:-0}"
|
|
110
112
|
if [[ $dup_count -gt 3 ]]; then
|
|
111
113
|
issues+=("REPEATED_PATTERNS: Common operations appear $dup_count times (consider helper functions)")
|
|
112
114
|
fi
|
|
@@ -139,7 +141,8 @@ check_solid_principles() {
|
|
|
139
141
|
|
|
140
142
|
# Single Responsibility: Check if scripts do multiple unrelated things
|
|
141
143
|
local sourced_count
|
|
142
|
-
sourced_count=$(grep -c '^\s*source\|^\s*\.\s' "$target_file" 2>/dev/null ||
|
|
144
|
+
sourced_count=$(grep -c '^\s*source\|^\s*\.\s' "$target_file" 2>/dev/null || true)
|
|
145
|
+
sourced_count="${sourced_count:-0}"
|
|
143
146
|
if [[ $sourced_count -gt 3 ]]; then
|
|
144
147
|
violations+=("SRP_VIOLATION: Script sources $sourced_count modules (too many responsibilities)")
|
|
145
148
|
fi
|
|
@@ -159,7 +162,8 @@ check_solid_principles() {
|
|
|
159
162
|
if [[ "$line" =~ ^[a-zA-Z_][a-zA-Z0-9_]*\(\) ]]; then
|
|
160
163
|
local func_name="${line%%(*}"
|
|
161
164
|
local body
|
|
162
|
-
body=$(awk "/^${func_name}\\(\\)/,/^}/" "$target_file" | grep -c '\$[0-9]' ||
|
|
165
|
+
body=$(awk "/^${func_name}\\(\\)/,/^}/" "$target_file" | grep -c '\$[0-9]' || true)
|
|
166
|
+
body="${body:-0}"
|
|
163
167
|
if [[ $body -gt 5 ]]; then
|
|
164
168
|
violations+=("ISP_VIOLATION: Function $func_name uses >5 parameters")
|
|
165
169
|
fi
|
|
@@ -227,7 +231,9 @@ analyze_complexity() {
|
|
|
227
231
|
|
|
228
232
|
# Cyclomatic complexity: count decision points (if, elif, case, &&, ||)
|
|
229
233
|
local cc=1
|
|
230
|
-
|
|
234
|
+
local _cc_count
|
|
235
|
+
_cc_count=$(sed -n "${start_line},${end_line}p" "$target_file" | grep -cE '\s(if|elif|case|&&|\|\|)' || true)
|
|
236
|
+
cc=$((cc + ${_cc_count:-0}))
|
|
231
237
|
|
|
232
238
|
# Lines of code
|
|
233
239
|
local loc=$((end_line - start_line))
|
|
@@ -266,8 +272,12 @@ check_style_consistency() {
|
|
|
266
272
|
local has_trap=false
|
|
267
273
|
local has_set_e=false
|
|
268
274
|
|
|
269
|
-
|
|
270
|
-
|
|
275
|
+
local _trap_count
|
|
276
|
+
_trap_count=$(grep -c 'trap.*ERR' "$target_file" 2>/dev/null || true)
|
|
277
|
+
[[ "${_trap_count:-0}" -gt 0 ]] && has_trap=true
|
|
278
|
+
local _set_e_count
|
|
279
|
+
_set_e_count=$(grep -c 'set -e' "$target_file" 2>/dev/null || true)
|
|
280
|
+
[[ "${_set_e_count:-0}" -gt 0 ]] && has_set_e=true
|
|
271
281
|
|
|
272
282
|
if [[ "$has_set_e" == "true" ]] && [[ "$has_trap" == "false" ]]; then
|
|
273
283
|
issues+=("STYLE: Missing ERR trap despite 'set -e' (inconsistent error handling)")
|
|
@@ -276,8 +286,10 @@ check_style_consistency() {
|
|
|
276
286
|
# Check for inconsistent quote usage
|
|
277
287
|
local single_quotes
|
|
278
288
|
local double_quotes
|
|
279
|
-
single_quotes=$(grep -o "'" "$target_file" 2>/dev/null | wc -l ||
|
|
280
|
-
|
|
289
|
+
single_quotes=$(grep -o "'" "$target_file" 2>/dev/null | wc -l || true)
|
|
290
|
+
single_quotes="${single_quotes:-0}"
|
|
291
|
+
double_quotes=$(grep -o '"' "$target_file" 2>/dev/null | wc -l || true)
|
|
292
|
+
double_quotes="${double_quotes:-0}"
|
|
281
293
|
if [[ $single_quotes -gt $((double_quotes * 3)) ]] || [[ $double_quotes -gt $((single_quotes * 3)) ]]; then
|
|
282
294
|
issues+=("STYLE: Inconsistent quote style (mix of single and double quotes)")
|
|
283
295
|
fi
|
|
@@ -285,8 +297,10 @@ check_style_consistency() {
|
|
|
285
297
|
# Check for inconsistent spacing/indentation
|
|
286
298
|
local tab_count
|
|
287
299
|
local space_count
|
|
288
|
-
tab_count=$(grep -c $'^\t' "$target_file" 2>/dev/null ||
|
|
289
|
-
|
|
300
|
+
tab_count=$(grep -c $'^\t' "$target_file" 2>/dev/null || true)
|
|
301
|
+
tab_count="${tab_count:-0}"
|
|
302
|
+
space_count=$(grep -c '^ ' "$target_file" 2>/dev/null || true)
|
|
303
|
+
space_count="${space_count:-0}"
|
|
290
304
|
if [[ $tab_count -gt 0 ]] && [[ $space_count -gt 0 ]]; then
|
|
291
305
|
issues+=("STYLE: Mixed tabs and spaces")
|
|
292
306
|
fi
|
|
@@ -333,7 +347,8 @@ auto_fix() {
|
|
|
333
347
|
|
|
334
348
|
# Fix 2: Trailing whitespace
|
|
335
349
|
local trailing_ws
|
|
336
|
-
trailing_ws=$(grep -c '[[:space:]]$' "$target_file" 2>/dev/null ||
|
|
350
|
+
trailing_ws=$(grep -c '[[:space:]]$' "$target_file" 2>/dev/null || true)
|
|
351
|
+
trailing_ws="${trailing_ws:-0}"
|
|
337
352
|
if [[ $trailing_ws -gt 0 ]]; then
|
|
338
353
|
sed -i '' 's/[[:space:]]*$//' "$target_file"
|
|
339
354
|
info "Removed $trailing_ws lines of trailing whitespace"
|
|
@@ -349,7 +364,8 @@ auto_fix() {
|
|
|
349
364
|
|
|
350
365
|
# Fix 4: Consistent spacing around operators (simple cases)
|
|
351
366
|
local spacing_fixes=0
|
|
352
|
-
spacing_fixes=$(grep -c '==' "$target_file" 2>/dev/null ||
|
|
367
|
+
spacing_fixes=$(grep -c '==' "$target_file" 2>/dev/null || true)
|
|
368
|
+
spacing_fixes="${spacing_fixes:-0}"
|
|
353
369
|
if [[ $spacing_fixes -gt 0 ]]; then
|
|
354
370
|
info "Flagged $spacing_fixes operator spacing cases (manual review recommended)"
|
|
355
371
|
fi
|
|
@@ -507,9 +523,12 @@ scan_codebase() {
|
|
|
507
523
|
local solids=0
|
|
508
524
|
local arch_issues=0
|
|
509
525
|
|
|
510
|
-
smells=$(detect_code_smells "$file" 2>/dev/null | wc -l ||
|
|
511
|
-
|
|
512
|
-
|
|
526
|
+
smells=$(detect_code_smells "$file" 2>/dev/null | wc -l || true)
|
|
527
|
+
smells="${smells:-0}"
|
|
528
|
+
solids=$(check_solid_principles "$file" 2>/dev/null | wc -l || true)
|
|
529
|
+
solids="${solids:-0}"
|
|
530
|
+
arch_issues=$(check_architecture_boundaries "$file" 2>/dev/null | wc -l || true)
|
|
531
|
+
arch_issues="${arch_issues:-0}"
|
|
513
532
|
|
|
514
533
|
local file_issues=$((smells + solids + arch_issues))
|
|
515
534
|
total_issues=$((total_issues + file_issues))
|
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="3.
|
|
11
|
+
VERSION="3.2.0"
|
|
12
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
13
|
|
|
14
14
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
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="3.
|
|
9
|
+
VERSION="3.2.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -142,9 +142,9 @@ cost_record() {
|
|
|
142
142
|
local cost_usd
|
|
143
143
|
cost_usd=$(cost_calculate "$input_tokens" "$output_tokens" "$model")
|
|
144
144
|
|
|
145
|
-
# Try SQLite first
|
|
145
|
+
# Try SQLite first (arg order must match db_record_cost signature: tokens, tokens, model, cost, stage, issue)
|
|
146
146
|
if type db_record_cost >/dev/null 2>&1; then
|
|
147
|
-
db_record_cost "$input_tokens" "$output_tokens" "$model" "$
|
|
147
|
+
db_record_cost "$input_tokens" "$output_tokens" "$model" "$cost_usd" "$stage" "$issue" 2>/dev/null || true
|
|
148
148
|
fi
|
|
149
149
|
|
|
150
150
|
# Always write to JSON (dual-write period)
|
|
@@ -154,7 +154,7 @@ cost_record() {
|
|
|
154
154
|
fi
|
|
155
155
|
local tmp_file
|
|
156
156
|
tmp_file=$(mktemp "${COST_FILE}.tmp.XXXXXX")
|
|
157
|
-
jq --argjson input "$input_tokens" \
|
|
157
|
+
if ! jq --argjson input "$input_tokens" \
|
|
158
158
|
--argjson output "$output_tokens" \
|
|
159
159
|
--arg model "$model" \
|
|
160
160
|
--arg stage "$stage" \
|
|
@@ -172,7 +172,16 @@ cost_record() {
|
|
|
172
172
|
ts: $ts,
|
|
173
173
|
ts_epoch: $epoch
|
|
174
174
|
}] | .entries = (.entries | .[-1000:])' \
|
|
175
|
-
"$COST_FILE" > "$tmp_file"
|
|
175
|
+
"$COST_FILE" > "$tmp_file" 2>/dev/null; then
|
|
176
|
+
error "Cost jq transformation failed — entry may be lost"
|
|
177
|
+
rm -f "$tmp_file"
|
|
178
|
+
# Continue without updating cost file
|
|
179
|
+
else
|
|
180
|
+
mv "$tmp_file" "$COST_FILE" || {
|
|
181
|
+
error "Failed to update cost file"
|
|
182
|
+
rm -f "$tmp_file"
|
|
183
|
+
}
|
|
184
|
+
fi
|
|
176
185
|
) 200>"${COST_FILE}.lock"
|
|
177
186
|
|
|
178
187
|
emit_event "cost.record" \
|
|
@@ -264,6 +273,11 @@ cost_remaining_budget() {
|
|
|
264
273
|
budget_usd=$(jq -r '.daily_budget_usd' "$BUDGET_FILE" 2>/dev/null || echo "0")
|
|
265
274
|
|
|
266
275
|
if [[ "$budget_enabled" != "true" || "$budget_usd" == "0" ]]; then
|
|
276
|
+
if [[ -z "${_BUDGET_UNCONFIGURED_WARNED:-}" ]]; then
|
|
277
|
+
info "Budget not configured — unlimited. Use 'shipwright cost budget set <amount>'"
|
|
278
|
+
emit_event "cost.budget_unconfigured" "status=unlimited"
|
|
279
|
+
_BUDGET_UNCONFIGURED_WARNED=1
|
|
280
|
+
fi
|
|
267
281
|
echo "unlimited"
|
|
268
282
|
return 0
|
|
269
283
|
fi
|
|
@@ -785,6 +799,54 @@ cost_dashboard() {
|
|
|
785
799
|
fi
|
|
786
800
|
fi
|
|
787
801
|
|
|
802
|
+
# Context efficiency (from loop.context_efficiency events)
|
|
803
|
+
local events_file="${HOME}/.shipwright/events.jsonl"
|
|
804
|
+
if [[ -f "$events_file" ]]; then
|
|
805
|
+
local ctx_events
|
|
806
|
+
ctx_events=$(grep '"type":"loop.context_efficiency"' "$events_file" 2>/dev/null | tail -200 || true)
|
|
807
|
+
local ctx_count
|
|
808
|
+
ctx_count=$(echo "$ctx_events" | grep -c '"loop.context_efficiency"' 2>/dev/null || echo "0")
|
|
809
|
+
ctx_count="${ctx_count:-0}"
|
|
810
|
+
|
|
811
|
+
if [[ "$ctx_count" -gt 0 ]]; then
|
|
812
|
+
# Parse metrics using awk (Bash 3.2 safe — no arrays needed)
|
|
813
|
+
local avg_utilization avg_trim_ratio total_raw total_trimmed trim_count
|
|
814
|
+
eval "$(echo "$ctx_events" | awk -F'"' '
|
|
815
|
+
/"loop.context_efficiency"/ {
|
|
816
|
+
for (i=1; i<=NF; i++) {
|
|
817
|
+
if ($i == "budget_utilization") util = $(i+2)
|
|
818
|
+
if ($i == "trim_ratio") ratio = $(i+2)
|
|
819
|
+
if ($i == "raw_prompt_chars") raw = $(i+2)
|
|
820
|
+
if ($i == "trimmed_prompt_chars") trimmed = $(i+2)
|
|
821
|
+
}
|
|
822
|
+
total_util += util; total_ratio += ratio
|
|
823
|
+
total_raw += raw; total_trimmed += trimmed
|
|
824
|
+
if (ratio + 0 > 0) trim_events++
|
|
825
|
+
n++
|
|
826
|
+
}
|
|
827
|
+
END {
|
|
828
|
+
if (n > 0) {
|
|
829
|
+
printf "avg_utilization=%.1f\n", total_util / n
|
|
830
|
+
printf "avg_trim_ratio=%.1f\n", total_ratio / n
|
|
831
|
+
} else {
|
|
832
|
+
printf "avg_utilization=0\navg_trim_ratio=0\n"
|
|
833
|
+
}
|
|
834
|
+
printf "total_raw=%d\ntotal_trimmed=%d\ntrim_count=%d\n", total_raw, total_trimmed, (trim_events+0)
|
|
835
|
+
}
|
|
836
|
+
')"
|
|
837
|
+
|
|
838
|
+
local total_discarded=$(( total_raw - total_trimmed ))
|
|
839
|
+
|
|
840
|
+
echo -e "${BOLD} CONTEXT EFFICIENCY${RESET}"
|
|
841
|
+
echo -e " Avg budget used ${avg_utilization}%"
|
|
842
|
+
echo -e " Avg trim ratio ${avg_trim_ratio}%"
|
|
843
|
+
echo -e " Chars generated $(printf "%'d" "$total_raw")"
|
|
844
|
+
echo -e " Chars discarded $(printf "%'d" "$total_discarded")"
|
|
845
|
+
echo -e " Trim events ${trim_count} / ${ctx_count} iterations"
|
|
846
|
+
echo ""
|
|
847
|
+
fi
|
|
848
|
+
fi
|
|
849
|
+
|
|
788
850
|
echo -e "${PURPLE}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
789
851
|
echo ""
|
|
790
852
|
}
|
|
@@ -894,6 +956,8 @@ show_help() {
|
|
|
894
956
|
}
|
|
895
957
|
|
|
896
958
|
# ─── Command Router ─────────────────────────────────────────────────────────
|
|
959
|
+
# Only run CLI when executed directly (not when sourced by sw-pipeline.sh)
|
|
960
|
+
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
897
961
|
|
|
898
962
|
SUBCOMMAND="${1:-help}"
|
|
899
963
|
shift 2>/dev/null || true
|
|
@@ -943,3 +1007,5 @@ case "$SUBCOMMAND" in
|
|
|
943
1007
|
exit 1
|
|
944
1008
|
;;
|
|
945
1009
|
esac
|
|
1010
|
+
|
|
1011
|
+
fi # end source guard
|