shipwright-cli 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/code-reviewer.md +2 -0
- package/.claude/agents/devops-engineer.md +2 -0
- package/.claude/agents/doc-fleet-agent.md +2 -0
- package/.claude/agents/pipeline-agent.md +2 -0
- package/.claude/agents/shell-script-specialist.md +2 -0
- package/.claude/agents/test-specialist.md +2 -0
- package/.claude/hooks/agent-crash-capture.sh +32 -0
- package/.claude/hooks/post-tool-use.sh +3 -2
- package/.claude/hooks/pre-tool-use.sh +35 -3
- package/README.md +22 -8
- package/claude-code/hooks/config-change.sh +18 -0
- package/claude-code/hooks/instructions-reloaded.sh +7 -0
- package/claude-code/hooks/worktree-create.sh +25 -0
- package/claude-code/hooks/worktree-remove.sh +20 -0
- package/config/code-constitution.json +130 -0
- package/config/defaults.json +25 -2
- package/config/policy.json +1 -1
- package/dashboard/middleware/auth.ts +134 -0
- package/dashboard/middleware/constants.ts +21 -0
- package/dashboard/public/index.html +8 -6
- package/dashboard/public/styles.css +176 -97
- package/dashboard/routes/auth.ts +38 -0
- package/dashboard/server.ts +117 -25
- package/dashboard/services/config.ts +26 -0
- package/dashboard/services/db.ts +118 -0
- package/dashboard/src/canvas/pixel-agent.ts +298 -0
- package/dashboard/src/canvas/pixel-sprites.ts +440 -0
- package/dashboard/src/canvas/shipyard-effects.ts +367 -0
- package/dashboard/src/canvas/shipyard-scene.ts +616 -0
- package/dashboard/src/canvas/submarine-layout.ts +267 -0
- package/dashboard/src/components/header.ts +8 -7
- package/dashboard/src/core/api.ts +5 -0
- package/dashboard/src/core/router.ts +1 -0
- package/dashboard/src/design/submarine-theme.ts +253 -0
- package/dashboard/src/main.ts +2 -0
- package/dashboard/src/types/api.ts +12 -1
- package/dashboard/src/views/activity.ts +2 -1
- package/dashboard/src/views/metrics.ts +69 -1
- package/dashboard/src/views/shipyard.ts +39 -0
- package/dashboard/types/index.ts +166 -0
- package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
- package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
- package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
- package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
- package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
- package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
- package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
- package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
- package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
- package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
- package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
- package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
- package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
- package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
- package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
- package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
- package/docs/research/RESEARCH_INDEX.md +439 -0
- package/docs/research/RESEARCH_SOURCES.md +440 -0
- package/docs/research/RESEARCH_SUMMARY.txt +275 -0
- package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
- package/package.json +2 -2
- package/scripts/lib/adaptive-model.sh +427 -0
- package/scripts/lib/adaptive-timeout.sh +316 -0
- package/scripts/lib/audit-trail.sh +309 -0
- package/scripts/lib/auto-recovery.sh +471 -0
- package/scripts/lib/bandit-selector.sh +431 -0
- package/scripts/lib/bootstrap.sh +104 -2
- package/scripts/lib/causal-graph.sh +455 -0
- package/scripts/lib/compat.sh +126 -0
- package/scripts/lib/compound-audit.sh +337 -0
- package/scripts/lib/constitutional.sh +454 -0
- package/scripts/lib/context-budget.sh +359 -0
- package/scripts/lib/convergence.sh +594 -0
- package/scripts/lib/cost-optimizer.sh +634 -0
- package/scripts/lib/daemon-adaptive.sh +14 -2
- package/scripts/lib/daemon-dispatch.sh +106 -17
- package/scripts/lib/daemon-failure.sh +34 -4
- package/scripts/lib/daemon-patrol.sh +25 -4
- package/scripts/lib/daemon-poll-github.sh +361 -0
- package/scripts/lib/daemon-poll-health.sh +299 -0
- package/scripts/lib/daemon-poll.sh +27 -611
- package/scripts/lib/daemon-state.sh +119 -66
- package/scripts/lib/daemon-triage.sh +10 -0
- package/scripts/lib/dod-scorecard.sh +442 -0
- package/scripts/lib/error-actionability.sh +300 -0
- package/scripts/lib/formal-spec.sh +461 -0
- package/scripts/lib/helpers.sh +180 -5
- package/scripts/lib/intent-analysis.sh +409 -0
- package/scripts/lib/loop-convergence.sh +350 -0
- package/scripts/lib/loop-iteration.sh +682 -0
- package/scripts/lib/loop-progress.sh +48 -0
- package/scripts/lib/loop-restart.sh +185 -0
- package/scripts/lib/memory-effectiveness.sh +506 -0
- package/scripts/lib/mutation-executor.sh +352 -0
- package/scripts/lib/outcome-feedback.sh +521 -0
- package/scripts/lib/pipeline-cli.sh +336 -0
- package/scripts/lib/pipeline-commands.sh +1216 -0
- package/scripts/lib/pipeline-detection.sh +101 -3
- package/scripts/lib/pipeline-execution.sh +897 -0
- package/scripts/lib/pipeline-github.sh +28 -3
- package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
- package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
- package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
- package/scripts/lib/pipeline-intelligence.sh +104 -1138
- package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
- package/scripts/lib/pipeline-quality-checks.sh +17 -711
- package/scripts/lib/pipeline-quality-gates.sh +563 -0
- package/scripts/lib/pipeline-stages-build.sh +730 -0
- package/scripts/lib/pipeline-stages-delivery.sh +965 -0
- package/scripts/lib/pipeline-stages-intake.sh +1133 -0
- package/scripts/lib/pipeline-stages-monitor.sh +407 -0
- package/scripts/lib/pipeline-stages-review.sh +1022 -0
- package/scripts/lib/pipeline-stages.sh +161 -2901
- package/scripts/lib/pipeline-state.sh +36 -5
- package/scripts/lib/pipeline-util.sh +487 -0
- package/scripts/lib/policy-learner.sh +438 -0
- package/scripts/lib/process-reward.sh +493 -0
- package/scripts/lib/project-detect.sh +649 -0
- package/scripts/lib/quality-profile.sh +334 -0
- package/scripts/lib/recruit-commands.sh +885 -0
- package/scripts/lib/recruit-learning.sh +739 -0
- package/scripts/lib/recruit-roles.sh +648 -0
- package/scripts/lib/reward-aggregator.sh +458 -0
- package/scripts/lib/rl-optimizer.sh +362 -0
- package/scripts/lib/root-cause.sh +427 -0
- package/scripts/lib/scope-enforcement.sh +445 -0
- package/scripts/lib/session-restart.sh +493 -0
- package/scripts/lib/skill-memory.sh +300 -0
- package/scripts/lib/skill-registry.sh +775 -0
- package/scripts/lib/spec-driven.sh +476 -0
- package/scripts/lib/test-helpers.sh +18 -7
- package/scripts/lib/test-holdout.sh +429 -0
- package/scripts/lib/test-optimizer.sh +511 -0
- package/scripts/shipwright-file-suggest.sh +45 -0
- package/scripts/skills/adversarial-quality.md +61 -0
- package/scripts/skills/api-design.md +44 -0
- package/scripts/skills/architecture-design.md +50 -0
- package/scripts/skills/brainstorming.md +43 -0
- package/scripts/skills/data-pipeline.md +44 -0
- package/scripts/skills/deploy-safety.md +64 -0
- package/scripts/skills/documentation.md +38 -0
- package/scripts/skills/frontend-design.md +45 -0
- package/scripts/skills/generated/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/.gitkeep +0 -0
- package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
- package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
- package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
- package/scripts/skills/generated/cli-version-management.md +29 -0
- package/scripts/skills/generated/collection-system-validation.md +99 -0
- package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
- package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
- package/scripts/skills/generated/test-parallelization-detection.md +65 -0
- package/scripts/skills/observability.md +79 -0
- package/scripts/skills/performance.md +48 -0
- package/scripts/skills/pr-quality.md +49 -0
- package/scripts/skills/product-thinking.md +43 -0
- package/scripts/skills/security-audit.md +49 -0
- package/scripts/skills/systematic-debugging.md +40 -0
- package/scripts/skills/testing-strategy.md +47 -0
- package/scripts/skills/two-stage-review.md +52 -0
- package/scripts/skills/validation-thoroughness.md +55 -0
- package/scripts/sw +9 -3
- package/scripts/sw-activity.sh +9 -8
- package/scripts/sw-adaptive.sh +8 -7
- package/scripts/sw-adversarial.sh +2 -1
- package/scripts/sw-architecture-enforcer.sh +3 -1
- package/scripts/sw-auth.sh +12 -2
- package/scripts/sw-autonomous.sh +5 -1
- package/scripts/sw-changelog.sh +4 -1
- package/scripts/sw-checkpoint.sh +2 -1
- package/scripts/sw-ci.sh +15 -6
- package/scripts/sw-cleanup.sh +4 -26
- package/scripts/sw-code-review.sh +45 -20
- package/scripts/sw-connect.sh +2 -1
- package/scripts/sw-context.sh +2 -1
- package/scripts/sw-cost.sh +107 -5
- package/scripts/sw-daemon.sh +71 -11
- package/scripts/sw-dashboard.sh +3 -1
- package/scripts/sw-db.sh +71 -20
- package/scripts/sw-decide.sh +8 -2
- package/scripts/sw-decompose.sh +360 -17
- package/scripts/sw-deps.sh +4 -1
- package/scripts/sw-developer-simulation.sh +4 -1
- package/scripts/sw-discovery.sh +378 -5
- package/scripts/sw-doc-fleet.sh +4 -1
- package/scripts/sw-docs-agent.sh +3 -1
- package/scripts/sw-docs.sh +2 -1
- package/scripts/sw-doctor.sh +453 -2
- package/scripts/sw-dora.sh +4 -1
- package/scripts/sw-durable.sh +12 -7
- package/scripts/sw-e2e-orchestrator.sh +17 -16
- package/scripts/sw-eventbus.sh +13 -4
- package/scripts/sw-evidence.sh +364 -12
- package/scripts/sw-feedback.sh +550 -9
- package/scripts/sw-fix.sh +20 -1
- package/scripts/sw-fleet-discover.sh +6 -2
- package/scripts/sw-fleet-viz.sh +9 -4
- package/scripts/sw-fleet.sh +5 -1
- package/scripts/sw-github-app.sh +18 -4
- package/scripts/sw-github-checks.sh +3 -2
- package/scripts/sw-github-deploy.sh +3 -2
- package/scripts/sw-github-graphql.sh +18 -7
- package/scripts/sw-guild.sh +5 -1
- package/scripts/sw-heartbeat.sh +5 -30
- package/scripts/sw-hello.sh +67 -0
- package/scripts/sw-hygiene.sh +10 -3
- package/scripts/sw-incident.sh +273 -5
- package/scripts/sw-init.sh +18 -2
- package/scripts/sw-instrument.sh +10 -2
- package/scripts/sw-intelligence.sh +44 -7
- package/scripts/sw-jira.sh +5 -1
- package/scripts/sw-launchd.sh +2 -1
- package/scripts/sw-linear.sh +4 -1
- package/scripts/sw-logs.sh +4 -1
- package/scripts/sw-loop.sh +436 -1076
- package/scripts/sw-memory.sh +357 -3
- package/scripts/sw-mission-control.sh +6 -1
- package/scripts/sw-model-router.sh +483 -27
- package/scripts/sw-otel.sh +15 -4
- package/scripts/sw-oversight.sh +14 -5
- package/scripts/sw-patrol-meta.sh +334 -0
- package/scripts/sw-pipeline-composer.sh +7 -1
- package/scripts/sw-pipeline-vitals.sh +12 -6
- package/scripts/sw-pipeline.sh +54 -2653
- package/scripts/sw-pm.sh +16 -8
- package/scripts/sw-pr-lifecycle.sh +2 -1
- package/scripts/sw-predictive.sh +17 -5
- package/scripts/sw-prep.sh +185 -2
- package/scripts/sw-ps.sh +5 -25
- package/scripts/sw-public-dashboard.sh +17 -4
- package/scripts/sw-quality.sh +14 -6
- package/scripts/sw-reaper.sh +8 -25
- package/scripts/sw-recruit.sh +156 -2303
- package/scripts/sw-regression.sh +19 -12
- package/scripts/sw-release-manager.sh +3 -1
- package/scripts/sw-release.sh +4 -1
- package/scripts/sw-remote.sh +3 -1
- package/scripts/sw-replay.sh +7 -1
- package/scripts/sw-retro.sh +158 -1
- package/scripts/sw-review-rerun.sh +3 -1
- package/scripts/sw-scale.sh +14 -5
- package/scripts/sw-security-audit.sh +6 -1
- package/scripts/sw-self-optimize.sh +173 -6
- package/scripts/sw-session.sh +9 -3
- package/scripts/sw-setup.sh +3 -1
- package/scripts/sw-stall-detector.sh +406 -0
- package/scripts/sw-standup.sh +15 -7
- package/scripts/sw-status.sh +3 -1
- package/scripts/sw-strategic.sh +14 -6
- package/scripts/sw-stream.sh +13 -4
- package/scripts/sw-swarm.sh +20 -7
- package/scripts/sw-team-stages.sh +13 -6
- package/scripts/sw-templates.sh +7 -31
- package/scripts/sw-testgen.sh +17 -6
- package/scripts/sw-tmux-pipeline.sh +4 -1
- package/scripts/sw-tmux-role-color.sh +2 -0
- package/scripts/sw-tmux-status.sh +1 -1
- package/scripts/sw-tmux.sh +37 -1
- package/scripts/sw-trace.sh +3 -1
- package/scripts/sw-tracker-github.sh +3 -0
- package/scripts/sw-tracker-jira.sh +3 -0
- package/scripts/sw-tracker-linear.sh +3 -0
- package/scripts/sw-tracker.sh +3 -1
- package/scripts/sw-triage.sh +3 -2
- package/scripts/sw-upgrade.sh +3 -1
- package/scripts/sw-ux.sh +5 -2
- package/scripts/sw-webhook.sh +5 -2
- package/scripts/sw-widgets.sh +9 -4
- package/scripts/sw-worktree.sh +15 -3
- package/scripts/test-skill-injection.sh +1233 -0
- package/templates/pipelines/autonomous.json +27 -3
- package/templates/pipelines/cost-aware.json +34 -8
- package/templates/pipelines/deployed.json +12 -0
- package/templates/pipelines/enterprise.json +12 -0
- package/templates/pipelines/fast.json +6 -0
- package/templates/pipelines/full.json +27 -3
- package/templates/pipelines/hotfix.json +6 -0
- package/templates/pipelines/standard.json +12 -0
- package/templates/pipelines/tdd.json +12 -0
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
You are a code review specialist for the Shipwright project. Your job is to review shell scripts, GitHub Actions workflows, and configuration files for correctness, security, and adherence to project conventions.
|
|
4
4
|
|
|
5
|
+
**Model Guidance**: Use Opus 4.6 for thorough architectural reviews. Set `maxTurns: 2` to prevent context bloat during iterative reviews. Use `worktree: true` for parallel code review runs.
|
|
6
|
+
|
|
5
7
|
## Review Checklist
|
|
6
8
|
|
|
7
9
|
### Bash 3.2 Compatibility (Blockers)
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
You are a DevOps and CI/CD specialist for the Shipwright project. You work on GitHub Actions workflows, deployment pipelines, infrastructure automation, and operational tooling.
|
|
4
4
|
|
|
5
|
+
**Model Guidance**: Use Opus 4.6 for complex infrastructure changes. Use `worktree: true` to isolate concurrent infrastructure updates. Set `maxTurns: 2` for exploratory work.
|
|
6
|
+
|
|
5
7
|
## GitHub Actions Workflows
|
|
6
8
|
|
|
7
9
|
Workflows live in `.github/workflows/` with the `shipwright-*.yml` naming prefix:
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
You are a specialized agent in the Shipwright documentation fleet. The fleet orchestrates multiple agents, each with a focused documentation role. Your specific role is assigned at spawn time.
|
|
4
4
|
|
|
5
|
+
**Model Guidance**: Use Sonnet 4.6 for documentation work. Use `background: true` for parallel doc fleet agents (audit, refactor, enhance). Set `maxTurns: 2` per role to focus on specialized tasks.
|
|
6
|
+
|
|
5
7
|
## Fleet Roles
|
|
6
8
|
|
|
7
9
|
### 1. Doc Architect (leader)
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
You are an autonomous agent running inside the Shipwright delivery pipeline's build stage. You were spawned by `shipwright loop`, which was called by `shipwright pipeline` during the build stage.
|
|
4
4
|
|
|
5
|
+
**Model Guidance**: Use Opus 4.6 for complex builds, Sonnet 4.6 for standard features, and Haiku for simple bug fixes. For background tasks (long-running test suites), set `background: true` and allow pre-approved tool permissions.
|
|
6
|
+
|
|
5
7
|
## Your Context
|
|
6
8
|
|
|
7
9
|
Your goal comes from the **enriched goal** assembled by the pipeline, which includes:
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
You are a shell script development specialist for the Shipwright project — an autonomous delivery platform built entirely in Bash (37+ scripts, 25,000+ lines).
|
|
4
4
|
|
|
5
|
+
**Model Guidance**: Use Sonnet 4.6 for script development. For long-running test harnesses, use `background: true` with pre-approved permissions.
|
|
6
|
+
|
|
5
7
|
## Bash 3.2 Compatibility (CRITICAL)
|
|
6
8
|
|
|
7
9
|
Shipwright must run on macOS default Bash 3.2. The following are **forbidden**:
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
You are a test development specialist for the Shipwright project. The project has 90+ test suites (see `package.json` scripts.test and the AUTO:test-suites table in `.claude/CLAUDE.md`), all written in Bash following a consistent harness pattern.
|
|
4
4
|
|
|
5
|
+
**Model Guidance**: Use Sonnet 4.6 for test development. Use `background: true` and `maxTurns: 3` for long test runs to prevent context bloat.
|
|
6
|
+
|
|
5
7
|
## Test Harness Conventions
|
|
6
8
|
|
|
7
9
|
### File Structure
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Hook: Capture diagnostics when a Skipper agent crashes
|
|
3
|
+
# Trigger: Post-tool-use on Bash failures that look like agent crashes
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# Read stdin for tool result context
|
|
8
|
+
INPUT="$(cat)"
|
|
9
|
+
|
|
10
|
+
# Check if this looks like an agent crash
|
|
11
|
+
if echo "$INPUT" | grep -qi "panic\|segfault\|killed\|oom\|fatal\|crash"; then
|
|
12
|
+
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
|
|
13
|
+
CRASH_DIR="${HOME}/.shipwright/crash-reports"
|
|
14
|
+
mkdir -p "$CRASH_DIR"
|
|
15
|
+
|
|
16
|
+
REPORT_FILE="${CRASH_DIR}/crash_${TIMESTAMP}.json"
|
|
17
|
+
|
|
18
|
+
# Capture diagnostics
|
|
19
|
+
cat > "$REPORT_FILE" << EOF
|
|
20
|
+
{
|
|
21
|
+
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
22
|
+
"working_directory": "$(pwd)",
|
|
23
|
+
"git_branch": "$(git branch --show-current 2>/dev/null || echo 'unknown')",
|
|
24
|
+
"git_commit": "$(git rev-parse HEAD 2>/dev/null || echo 'unknown')",
|
|
25
|
+
"error_context": $(echo "$INPUT" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()[:2000]))" 2>/dev/null || echo '"capture_failed"'),
|
|
26
|
+
"pipeline_state": "$(cat .claude/pipeline-state.md 2>/dev/null | head -20 | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null || echo '"none"')",
|
|
27
|
+
"recent_commits": "$(git log --oneline -5 2>/dev/null | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null || echo '"none"')"
|
|
28
|
+
}
|
|
29
|
+
EOF
|
|
30
|
+
|
|
31
|
+
echo "Crash report saved to $REPORT_FILE" >&2
|
|
32
|
+
fi
|
|
@@ -19,8 +19,9 @@ if [[ "$tool_name" == "Bash" ]] && [[ "${exit_code:-0}" != "0" ]]; then
|
|
|
19
19
|
# Classify error type
|
|
20
20
|
error_type="unknown"
|
|
21
21
|
case "$error_snippet" in
|
|
22
|
-
*"
|
|
23
|
-
*"
|
|
22
|
+
*"syntax"*|*"parse error"*) error_type="syntax" ;;
|
|
23
|
+
*"unexpected token"*|*"unexpected end"*) error_type="syntax" ;;
|
|
24
|
+
*"test"*|*"FAIL"*|*"assert"*|*"expect"*) error_type="test" ;;
|
|
24
25
|
*"not found"*|*"No such"*|*"ENOENT"*) error_type="missing" ;;
|
|
25
26
|
*"permission"*|*"denied"*|*"EACCES"*) error_type="permission" ;;
|
|
26
27
|
*"timeout"*|*"timed out"*|*"ETIMEDOUT"*) error_type="timeout" ;;
|
|
@@ -1,14 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Hook: PreToolUse —
|
|
2
|
+
# Hook: PreToolUse — Security checks and context injection
|
|
3
3
|
# Triggered before Write/Edit tools
|
|
4
4
|
|
|
5
5
|
# Read tool input from stdin (JSON)
|
|
6
6
|
input=$(cat)
|
|
7
7
|
|
|
8
|
-
# Extract
|
|
8
|
+
# Extract tool name and file path
|
|
9
|
+
tool_name=$(echo "$input" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
9
10
|
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
11
|
+
new_string=$(echo "$input" | jq -r '.tool_input.new_string // empty' 2>/dev/null)
|
|
12
|
+
tool_input=$(echo "$input" | jq -r '.tool_input // empty' 2>/dev/null)
|
|
10
13
|
|
|
11
|
-
#
|
|
14
|
+
# Security check: Block git push --no-verify (bypasses pre-commit hooks)
|
|
15
|
+
if echo "$tool_input" | grep -qE 'git\s+push.*--no-verify'; then
|
|
16
|
+
echo '{"message":"Blocked: git push --no-verify bypasses safety checks. Remove --no-verify flag."}'
|
|
17
|
+
exit 2
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Security check: Detect secrets being written to non-secret files
|
|
21
|
+
if [[ "$tool_name" == "Edit" || "$tool_name" == "Write" ]]; then
|
|
22
|
+
# Sensitive files that should not be added to version control
|
|
23
|
+
if [[ ! "$file_path" =~ (\.env|\.secret|secret|credential|key|token|private) ]]; then
|
|
24
|
+
# Check for common secret patterns
|
|
25
|
+
secret_patterns="sk-ant-|ANTHROPIC_API_KEY|GITHUB_TOKEN|OPENAI_API_KEY|AWS_SECRET|DATABASE_URL|PRIVATE_KEY|api_key|BEGIN RSA PRIVATE KEY|BEGIN PRIVATE KEY"
|
|
26
|
+
|
|
27
|
+
if echo "$new_string" | grep -qE "$secret_patterns"; then
|
|
28
|
+
cat << "SECURITY_WARNING"
|
|
29
|
+
⚠️ SECURITY WARNING: Secret pattern detected in non-secret file
|
|
30
|
+
|
|
31
|
+
The content being written to this file contains a potential secret.
|
|
32
|
+
File: $file_path
|
|
33
|
+
|
|
34
|
+
Sensitive data should NEVER be committed to version control.
|
|
35
|
+
|
|
36
|
+
If this is intentional (e.g., example code), please review carefully.
|
|
37
|
+
SECURITY_WARNING
|
|
38
|
+
exit 2
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Shell script context injection
|
|
12
44
|
if [[ "$file_path" == *.sh ]]; then
|
|
13
45
|
cat << 'REMINDER'
|
|
14
46
|
SHIPWRIGHT SHELL RULES:
|
package/README.md
CHANGED
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
<a href="https://github.com/sethdford/shipwright/actions/workflows/test.yml"><img src="https://github.com/sethdford/shipwright/actions/workflows/test.yml/badge.svg" alt="Tests"></a>
|
|
14
14
|
<a href="https://github.com/sethdford/shipwright/actions/workflows/shipwright-pipeline.yml"><img src="https://github.com/sethdford/shipwright/actions/workflows/shipwright-pipeline.yml/badge.svg" alt="Pipeline"></a>
|
|
15
15
|
<img src="https://img.shields.io/badge/tests-141_suites_passing-4ade80?style=flat-square" alt="141 suites">
|
|
16
|
-
<img src="https://img.shields.io/badge/version-3.
|
|
16
|
+
<img src="https://img.shields.io/badge/version-3.3.0-00d4ff?style=flat-square" alt="v3.3.0">
|
|
17
17
|
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="MIT License">
|
|
18
|
-
<img src="https://img.shields.io/badge/bash-3.2%2B-
|
|
18
|
+
<img src="https://img.shields.io/badge/bash-3.2%2B-555a66?style=flat-square" alt="Bash 3.2+">
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
---
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
- [Shipwright Builds Itself](#shipwright-builds-itself)
|
|
26
26
|
- [Code Factory Pattern](#code-factory-pattern)
|
|
27
|
-
- [What's New in v3.
|
|
27
|
+
- [What's New in v3.3.0](#whats-new-in-v330)
|
|
28
28
|
- [How It Works](#how-it-works)
|
|
29
29
|
- [Install](#install)
|
|
30
30
|
- [Quick Start](#quick-start)
|
|
@@ -77,7 +77,8 @@ Shipwright extends the Code Factory pattern with capabilities most implementatio
|
|
|
77
77
|
- **12-stage pipeline** with self-healing builds, adversarial review, and compound quality gates
|
|
78
78
|
- **Predictive risk scoring** using GitHub signals (security alerts, contributor expertise, file churn)
|
|
79
79
|
- **Persistent memory** — failure patterns, fix effectiveness, and prediction accuracy compound over time
|
|
80
|
-
- **Auto-learning** — self-optimize runs automatically after every pipeline completion
|
|
80
|
+
- **Auto-learning** — self-optimize runs automatically after every pipeline completion, including context efficiency tuning
|
|
81
|
+
- **Decision engine** — tiered autonomous decisions with outcome learning and deduplication
|
|
81
82
|
- **Unified model routing** — single source of truth for model selection across all components
|
|
82
83
|
- **Evidence-gated merges** — SHA discipline ensures all evidence validated against current PR head
|
|
83
84
|
- **Semantic quality audits** — Claude-powered audits with grep fallback when Claude unavailable
|
|
@@ -108,7 +109,7 @@ shipwright incident gap sla
|
|
|
108
109
|
|
|
109
110
|
---
|
|
110
111
|
|
|
111
|
-
## What's New in v3.
|
|
112
|
+
## What's New in v3.3.0
|
|
112
113
|
|
|
113
114
|
**Code Factory pattern** — deterministic, risk-aware agent delivery with machine-verifiable evidence:
|
|
114
115
|
|
|
@@ -282,7 +283,7 @@ Each stage is configurable with quality gates that auto-proceed or pause for app
|
|
|
282
283
|
|
|
283
284
|
### Intelligence Layer
|
|
284
285
|
|
|
285
|
-
7 modules that make the pipeline smarter over time. **
|
|
286
|
+
7 modules that make the pipeline smarter over time. **Enabled by default**: intelligence is on when Claude CLI is available, with optimization and prediction active out of the box. Set `intelligence.enabled=false` to disable. All modules degrade gracefully.
|
|
286
287
|
|
|
287
288
|
| Module | What It Does |
|
|
288
289
|
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -290,7 +291,7 @@ Each stage is configurable with quality gates that auto-proceed or pause for app
|
|
|
290
291
|
| **Pipeline Composer** | Generates custom pipeline configs from codebase analysis (file churn, test coverage, dependencies) |
|
|
291
292
|
| **Predictive Risk** | Scores issues for risk using GitHub signals (security alerts, similar past issues, contributor expertise) |
|
|
292
293
|
| **Adversarial Review** | Red-team code review — finds security flaws, edge cases, failure modes. Cross-checks against CodeQL/Dependabot alerts |
|
|
293
|
-
| **Self-Optimization** | Reads DORA metrics and auto-tunes daemon config.
|
|
294
|
+
| **Self-Optimization** | Reads DORA metrics and auto-tunes daemon config. Includes context efficiency closed loop for token budget tuning |
|
|
294
295
|
| **Developer Simulation** | 3-persona review (security, performance, maintainability) before PR creation |
|
|
295
296
|
| **Architecture Enforcement** | Living architectural model with violation detection and dependency direction rules |
|
|
296
297
|
|
|
@@ -309,6 +310,19 @@ Native GitHub API integration enriches every intelligence module:
|
|
|
309
310
|
| **Contributors** | CODEOWNERS-based reviewer routing, top-contributor fallback, auto-approve as last resort |
|
|
310
311
|
| **Branch Protection** | Checks required reviews and status checks before attempting auto-merge |
|
|
311
312
|
|
|
313
|
+
### Decision Engine
|
|
314
|
+
|
|
315
|
+
The autonomous decision engine (`config/policy.json` → `decision` section) handles routine operational decisions with outcome learning. Decisions are tiered by risk, with low-risk actions auto-approved and higher tiers escalated. The engine learns from outcomes to improve future decisions.
|
|
316
|
+
|
|
317
|
+
### Context Engineering
|
|
318
|
+
|
|
319
|
+
Intelligent context window management for pipeline agents:
|
|
320
|
+
|
|
321
|
+
- **Budget-aware trimming** — Configurable character budgets for prompt composition (`context_budget_chars`)
|
|
322
|
+
- **Section-level trimming** — Independent limits for memory, git history, hotspot files, and test output
|
|
323
|
+
- **Context efficiency metrics** — Tracks budget utilization and trim ratios per iteration
|
|
324
|
+
- **Self-tuning** — The self-optimization loop analyzes context efficiency events and recommends budget adjustments
|
|
325
|
+
|
|
312
326
|
### Autonomous Daemon
|
|
313
327
|
|
|
314
328
|
```bash
|
|
@@ -354,7 +368,7 @@ Per-pipeline cost tracking with model pricing, budget enforcement, and ROI analy
|
|
|
354
368
|
shipwright dashboard start
|
|
355
369
|
```
|
|
356
370
|
|
|
357
|
-
Web dashboard with live pipeline progress, GitHub context (security alerts, contributors, deployments), DORA metrics, and
|
|
371
|
+
Web dashboard with live pipeline progress, GitHub context (security alerts, contributors, deployments), DORA metrics, cost tracking, and context efficiency metrics. WebSocket-powered, updates in real-time.
|
|
358
372
|
|
|
359
373
|
### Webhook Receiver
|
|
360
374
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Hook: ConfigChange — notify daemon of config updates
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
input=$(cat)
|
|
6
|
+
|
|
7
|
+
# Log config change event
|
|
8
|
+
mkdir -p "$HOME/.shipwright" 2>/dev/null || true
|
|
9
|
+
echo "{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"config.changed\",\"detail\":$(echo "$input" | jq -c '.' 2>/dev/null || echo '{}')}" >> "$HOME/.shipwright/events.jsonl" 2>/dev/null || true
|
|
10
|
+
|
|
11
|
+
# Signal running daemon to reload config (if PID file exists)
|
|
12
|
+
pid_file="$HOME/.shipwright/daemon.pid"
|
|
13
|
+
if [[ -f "$pid_file" ]]; then
|
|
14
|
+
daemon_pid=$(cat "$pid_file" 2>/dev/null || true)
|
|
15
|
+
if [[ -n "$daemon_pid" ]] && kill -0 "$daemon_pid" 2>/dev/null; then
|
|
16
|
+
kill -USR1 "$daemon_pid" 2>/dev/null || true
|
|
17
|
+
fi
|
|
18
|
+
fi
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Hook: InstructionsLoaded (matcher: "compact")
|
|
3
|
+
# After auto-compaction, log reload event for observability
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
mkdir -p "$HOME/.shipwright" 2>/dev/null || true
|
|
7
|
+
echo "{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"instructions.reloaded\",\"trigger\":\"compaction\"}" >> "$HOME/.shipwright/events.jsonl" 2>/dev/null || true
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Hook: WorktreeCreate — auto-setup worktree for pipeline agents
|
|
3
|
+
# Copies essential config into new worktrees so agents inherit settings
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
# Read hook input from stdin (JSON with worktree details)
|
|
7
|
+
input=$(cat)
|
|
8
|
+
worktree_path=$(echo "$input" | jq -r '.worktree_path // empty' 2>/dev/null || true)
|
|
9
|
+
|
|
10
|
+
[[ -z "$worktree_path" ]] && exit 0
|
|
11
|
+
|
|
12
|
+
# Copy daemon config if it exists
|
|
13
|
+
src_root=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
|
14
|
+
if [[ -n "$src_root" ]]; then
|
|
15
|
+
src_config="$src_root/.claude/daemon-config.json"
|
|
16
|
+
if [[ -f "$src_config" ]]; then
|
|
17
|
+
mkdir -p "$worktree_path/.claude" 2>/dev/null || true
|
|
18
|
+
cp "$src_config" "$worktree_path/.claude/daemon-config.json" 2>/dev/null || true
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Copy pipeline artifacts directory structure
|
|
22
|
+
if [[ -d "$src_root/.claude/pipeline-artifacts" ]]; then
|
|
23
|
+
mkdir -p "$worktree_path/.claude/pipeline-artifacts" 2>/dev/null || true
|
|
24
|
+
fi
|
|
25
|
+
fi
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Hook: WorktreeRemove — clean up state for removed worktree agents
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
input=$(cat)
|
|
6
|
+
worktree_path=$(echo "$input" | jq -r '.worktree_path // empty' 2>/dev/null || true)
|
|
7
|
+
|
|
8
|
+
[[ -z "$worktree_path" ]] && exit 0
|
|
9
|
+
|
|
10
|
+
# Clean up heartbeat files associated with this worktree
|
|
11
|
+
heartbeat_dir="$HOME/.shipwright/heartbeats"
|
|
12
|
+
if [[ -d "$heartbeat_dir" ]]; then
|
|
13
|
+
for hb in "$heartbeat_dir"/*.json; do
|
|
14
|
+
[[ -f "$hb" ]] || continue
|
|
15
|
+
hb_path=$(jq -r '.worktree // empty' "$hb" 2>/dev/null || true)
|
|
16
|
+
if [[ "$hb_path" == "$worktree_path" ]]; then
|
|
17
|
+
rm -f "$hb"
|
|
18
|
+
fi
|
|
19
|
+
done
|
|
20
|
+
fi
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"description": "Constitutional principles for autonomous code self-critique",
|
|
4
|
+
"principles": {
|
|
5
|
+
"security": [
|
|
6
|
+
{
|
|
7
|
+
"id": "SEC-001",
|
|
8
|
+
"rule": "No hardcoded secrets, API keys, or passwords",
|
|
9
|
+
"severity": "critical",
|
|
10
|
+
"check": "grep -nE '(password|api_key|secret_key|api_secret|auth_token)\\s*=\\s*[\"'\"'\"']' {file}"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "SEC-002",
|
|
14
|
+
"rule": "All user inputs must be validated before use",
|
|
15
|
+
"severity": "critical"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "SEC-003",
|
|
19
|
+
"rule": "No eval() or exec() with dynamic content",
|
|
20
|
+
"severity": "critical",
|
|
21
|
+
"check": "grep -nE '\\beval\\b.*\\$|\\bexec\\b.*\\$' {file}"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "SEC-004",
|
|
25
|
+
"rule": "No SQL string concatenation — use parameterized queries",
|
|
26
|
+
"severity": "high"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "SEC-005",
|
|
30
|
+
"rule": "No shell injection via unquoted variables in commands",
|
|
31
|
+
"severity": "critical",
|
|
32
|
+
"check": "grep -nE '\\$\\{?[A-Za-z_]+\\}?[^\"'\"'\"']' {file}"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"error_handling": [
|
|
36
|
+
{
|
|
37
|
+
"id": "ERR-001",
|
|
38
|
+
"rule": "No empty catch blocks — all errors must be handled or re-thrown",
|
|
39
|
+
"severity": "high",
|
|
40
|
+
"check": "grep -nE 'catch\\s*\\([^)]*\\)\\s*\\{\\s*\\}' {file}"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "ERR-002",
|
|
44
|
+
"rule": "Error messages must be descriptive — no generic 'Error occurred'",
|
|
45
|
+
"severity": "medium",
|
|
46
|
+
"check": "grep -niE '(error occurred|something went wrong|an error happened)' {file}"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "ERR-003",
|
|
50
|
+
"rule": "Return values from commands must be checked",
|
|
51
|
+
"severity": "high"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "ERR-004",
|
|
55
|
+
"rule": "No silent failures — failed operations must log or propagate errors",
|
|
56
|
+
"severity": "medium"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"quality": [
|
|
60
|
+
{
|
|
61
|
+
"id": "QUA-001",
|
|
62
|
+
"rule": "Functions should be under 100 lines",
|
|
63
|
+
"severity": "medium"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "QUA-002",
|
|
67
|
+
"rule": "No TODO/FIXME/HACK comments in production code",
|
|
68
|
+
"severity": "low",
|
|
69
|
+
"check": "grep -nE '(TODO|FIXME|HACK|XXX)' {file}"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "QUA-003",
|
|
73
|
+
"rule": "No magic numbers — use named constants",
|
|
74
|
+
"severity": "low"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "QUA-004",
|
|
78
|
+
"rule": "No duplicate code blocks over 10 lines",
|
|
79
|
+
"severity": "medium"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "QUA-005",
|
|
83
|
+
"rule": "No console.log or print statements left in production code",
|
|
84
|
+
"severity": "low",
|
|
85
|
+
"check": "grep -nE '\\bconsole\\.log\\b|\\bprint\\(' {file}"
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"testing": [
|
|
89
|
+
{
|
|
90
|
+
"id": "TST-001",
|
|
91
|
+
"rule": "New functions must have corresponding tests",
|
|
92
|
+
"severity": "high"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "TST-002",
|
|
96
|
+
"rule": "Tests must cover error and edge cases, not just happy path",
|
|
97
|
+
"severity": "medium"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "TST-003",
|
|
101
|
+
"rule": "No test-only code in production files",
|
|
102
|
+
"severity": "medium",
|
|
103
|
+
"check": "grep -nE '(if.*TEST|ifdef.*TEST|IS_TEST)' {file}"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"id": "TST-004",
|
|
107
|
+
"rule": "Test assertions must be specific — no assertTrue(result) without message",
|
|
108
|
+
"severity": "low"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"performance": [
|
|
112
|
+
{
|
|
113
|
+
"id": "PRF-001",
|
|
114
|
+
"rule": "All database queries must have .limit() or LIMIT clause",
|
|
115
|
+
"severity": "high"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"id": "PRF-002",
|
|
119
|
+
"rule": "No unbounded loops over external data",
|
|
120
|
+
"severity": "medium"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "PRF-003",
|
|
124
|
+
"rule": "Avoid synchronous file I/O in hot paths",
|
|
125
|
+
"severity": "medium",
|
|
126
|
+
"check": "grep -nE 'readFileSync|writeFileSync' {file}"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
}
|
package/config/defaults.json
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"build_test_retries": 3,
|
|
26
26
|
"claude_timeout": 1800,
|
|
27
27
|
"heartbeat_interval": 30,
|
|
28
|
+
"composed_cache_ttl": 3600,
|
|
28
29
|
"branch_pattern": "shipwright/issue-{issue}",
|
|
29
30
|
"stage_order": [
|
|
30
31
|
"intake",
|
|
@@ -47,7 +48,12 @@
|
|
|
47
48
|
"max_restarts": 0,
|
|
48
49
|
"fast_test_interval": 5,
|
|
49
50
|
"convergence_threshold": 3,
|
|
50
|
-
"multi_agent_sleep": 5
|
|
51
|
+
"multi_agent_sleep": 5,
|
|
52
|
+
"context_budget_chars": 180000,
|
|
53
|
+
"context_trim_memory_chars": 20000,
|
|
54
|
+
"context_trim_git_entries": 10,
|
|
55
|
+
"context_trim_hotspot_files": 5,
|
|
56
|
+
"context_trim_test_lines": 50
|
|
51
57
|
},
|
|
52
58
|
"dashboard": {
|
|
53
59
|
"port": 8767,
|
|
@@ -77,12 +83,29 @@
|
|
|
77
83
|
"ab_test_ratio": 0.2,
|
|
78
84
|
"claude_timeout": 60
|
|
79
85
|
},
|
|
86
|
+
"predictive": {
|
|
87
|
+
"default_risk_score": 50,
|
|
88
|
+
"keyword_risk_score": 70
|
|
89
|
+
},
|
|
90
|
+
"api_optimization": {
|
|
91
|
+
"programmatic_tool_calling": true,
|
|
92
|
+
"tool_search_enabled": true,
|
|
93
|
+
"tool_search_type": "bm25",
|
|
94
|
+
"defer_unused_tools": true,
|
|
95
|
+
"web_search_version": "web_search_20260209",
|
|
96
|
+
"web_fetch_version": "web_fetch_20260209",
|
|
97
|
+
"dynamic_filtering": true,
|
|
98
|
+
"code_execution_sandbox": true,
|
|
99
|
+
"beta_header": "code-execution-web-tools-2026-02-09"
|
|
100
|
+
},
|
|
80
101
|
"quality": {
|
|
81
102
|
"gate_score_threshold": 70,
|
|
82
103
|
"secret_threshold": 3,
|
|
83
104
|
"min_file_count": 10,
|
|
84
105
|
"score_weight_per_file": 25,
|
|
85
|
-
"pass_rate_threshold": 5.0
|
|
106
|
+
"pass_rate_threshold": 5.0,
|
|
107
|
+
"bundle_growth_legacy_pct": 20,
|
|
108
|
+
"perf_regression_legacy_pct": 30
|
|
86
109
|
},
|
|
87
110
|
"cleanup": {
|
|
88
111
|
"artifact_age_days": 7,
|
package/config/policy.json
CHANGED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { join, extname } from "path";
|
|
2
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync, renameSync } from "fs";
|
|
3
|
+
import type { Session, AuthMode } from "../types/index.js";
|
|
4
|
+
import { SESSION_TTL_MS } from "./constants.js";
|
|
5
|
+
|
|
6
|
+
// Auth Config
|
|
7
|
+
const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID || "";
|
|
8
|
+
const GITHUB_CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET || "";
|
|
9
|
+
const GITHUB_PAT = process.env.GITHUB_PAT || "";
|
|
10
|
+
const DASHBOARD_REPO = process.env.DASHBOARD_REPO || "";
|
|
11
|
+
const SESSION_SECRET = process.env.SESSION_SECRET || crypto.randomUUID();
|
|
12
|
+
|
|
13
|
+
const HOME = process.env.HOME || "";
|
|
14
|
+
const SESSIONS_FILE = join(HOME, ".shipwright", "sessions.json");
|
|
15
|
+
|
|
16
|
+
export const sessions = new Map<string, Session>();
|
|
17
|
+
|
|
18
|
+
export function createSession(data: Omit<Session, "expiresAt">): string {
|
|
19
|
+
const sessionId = crypto.randomUUID();
|
|
20
|
+
sessions.set(sessionId, {
|
|
21
|
+
...data,
|
|
22
|
+
expiresAt: Date.now() + SESSION_TTL_MS,
|
|
23
|
+
});
|
|
24
|
+
saveSessions();
|
|
25
|
+
return sessionId;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getSession(req: Request): Session | null {
|
|
29
|
+
const cookie = req.headers.get("cookie");
|
|
30
|
+
if (!cookie) return null;
|
|
31
|
+
|
|
32
|
+
const match = cookie.match(/fleet_session=([^;]+)/);
|
|
33
|
+
if (!match) return null;
|
|
34
|
+
|
|
35
|
+
const sessionId = match[1];
|
|
36
|
+
const session = sessions.get(sessionId);
|
|
37
|
+
if (!session) return null;
|
|
38
|
+
|
|
39
|
+
if (Date.now() > session.expiresAt) {
|
|
40
|
+
sessions.delete(sessionId);
|
|
41
|
+
saveSessions();
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return session;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getSessionFromCookie(cookie: string | null): Session | null {
|
|
49
|
+
if (!cookie) return null;
|
|
50
|
+
const match = cookie.match(/fleet_session=([^;]+)/);
|
|
51
|
+
if (!match) return null;
|
|
52
|
+
const session = sessions.get(match[1]);
|
|
53
|
+
if (!session || Date.now() > session.expiresAt) return null;
|
|
54
|
+
return session;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function sessionCookie(sessionId: string): string {
|
|
58
|
+
return `fleet_session=${sessionId}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${Math.floor(SESSION_TTL_MS / 1000)}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function isLocalConnection(req: Request): boolean {
|
|
62
|
+
const host = req.headers.get("host") || "";
|
|
63
|
+
return (
|
|
64
|
+
host.startsWith("localhost:") ||
|
|
65
|
+
host.startsWith("127.0.0.1:") ||
|
|
66
|
+
host.startsWith("[::1]:")
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function clearSessionCookie(): string {
|
|
71
|
+
return "fleet_session=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function loadSessions(): void {
|
|
75
|
+
try {
|
|
76
|
+
if (existsSync(SESSIONS_FILE)) {
|
|
77
|
+
const data = JSON.parse(readFileSync(SESSIONS_FILE, "utf-8"));
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
if (data && typeof data === "object") {
|
|
80
|
+
for (const [id, sess] of Object.entries(data)) {
|
|
81
|
+
const s = sess as Session;
|
|
82
|
+
if (s.expiresAt > now) {
|
|
83
|
+
sessions.set(id, s);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
/* start fresh */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function saveSessions(): void {
|
|
94
|
+
const dir = join(HOME, ".shipwright");
|
|
95
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
96
|
+
const obj: Record<string, Session> = {};
|
|
97
|
+
for (const [id, sess] of sessions) {
|
|
98
|
+
obj[id] = sess;
|
|
99
|
+
}
|
|
100
|
+
const tmp = SESSIONS_FILE + ".tmp";
|
|
101
|
+
writeFileSync(tmp, JSON.stringify(obj, null, 2));
|
|
102
|
+
renameSync(tmp, SESSIONS_FILE);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getAuthMode(): AuthMode {
|
|
106
|
+
if (GITHUB_CLIENT_ID && GITHUB_CLIENT_SECRET && DASHBOARD_REPO)
|
|
107
|
+
return "oauth";
|
|
108
|
+
if (GITHUB_PAT && DASHBOARD_REPO) return "pat";
|
|
109
|
+
return "none";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function isAuthEnabled(): boolean {
|
|
113
|
+
return getAuthMode() !== "none";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function isPublicRoute(pathname: string): boolean {
|
|
117
|
+
return (
|
|
118
|
+
pathname === "/login" ||
|
|
119
|
+
pathname.startsWith("/auth/") ||
|
|
120
|
+
pathname === "/api/health" ||
|
|
121
|
+
pathname === "/api/ws-status" ||
|
|
122
|
+
pathname.startsWith("/api/join/") ||
|
|
123
|
+
pathname.startsWith("/api/connect/") ||
|
|
124
|
+
pathname === "/api/team" ||
|
|
125
|
+
pathname === "/api/team/activity" ||
|
|
126
|
+
pathname === "/api/team/invite" ||
|
|
127
|
+
pathname.startsWith("/api/team/invite/") ||
|
|
128
|
+
pathname === "/api/claim" ||
|
|
129
|
+
pathname === "/api/claim/release" ||
|
|
130
|
+
pathname === "/api/webhook/ci"
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export { SESSION_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_PAT, DASHBOARD_REPO };
|