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
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shellcheck disable=SC2034,SC2064
|
|
3
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
4
|
+
# ║ lib/recruit-roles.sh — Role Management, Creation, Matching, Evaluation ║
|
|
5
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
|
|
7
|
+
[[ -n "${_RECRUIT_ROLES_LOADED:-}" ]] && return 0
|
|
8
|
+
_RECRUIT_ROLES_LOADED=1
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="${SCRIPT_DIR:-.}"
|
|
11
|
+
RECRUIT_ROOT="${RECRUIT_ROOT:-${HOME}/.shipwright/recruitment}"
|
|
12
|
+
ROLES_DB="${ROLES_DB:-${RECRUIT_ROOT}/roles.json}"
|
|
13
|
+
PROFILES_DB="${PROFILES_DB:-${RECRUIT_ROOT}/profiles.json}"
|
|
14
|
+
MATCH_HISTORY="${MATCH_HISTORY:-${RECRUIT_ROOT}/match-history.jsonl}"
|
|
15
|
+
ROLE_USAGE_DB="${ROLE_USAGE_DB:-${RECRUIT_ROOT}/role-usage.json}"
|
|
16
|
+
HEURISTICS_DB="${HEURISTICS_DB:-${RECRUIT_ROOT}/heuristics.json}"
|
|
17
|
+
|
|
18
|
+
# Fallback color/output helpers
|
|
19
|
+
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
20
|
+
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
21
|
+
[[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
|
|
22
|
+
[[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
|
|
23
|
+
|
|
24
|
+
if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
25
|
+
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
26
|
+
now_epoch() { date +%s; }
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Fallback for color codes
|
|
30
|
+
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
31
|
+
RESET="${RESET:-\033[0m}"
|
|
32
|
+
BOLD="${BOLD:-\033[1m}"
|
|
33
|
+
DIM="${DIM:-\033[2m}"
|
|
34
|
+
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
35
|
+
|
|
36
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
37
|
+
# BUILT-IN ROLE DEFINITIONS
|
|
38
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
39
|
+
|
|
40
|
+
initialize_builtin_roles() {
|
|
41
|
+
ensure_recruit_dir
|
|
42
|
+
|
|
43
|
+
if jq -e '.architect' "$ROLES_DB" >/dev/null 2>&1; then
|
|
44
|
+
return 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
local roles_json
|
|
48
|
+
roles_json=$(cat <<'EOF'
|
|
49
|
+
{
|
|
50
|
+
"architect": {
|
|
51
|
+
"title": "Architect",
|
|
52
|
+
"description": "System design, architecture decisions, scalability planning",
|
|
53
|
+
"required_skills": ["system-design", "technology-evaluation", "code-review", "documentation"],
|
|
54
|
+
"recommended_model": "opus",
|
|
55
|
+
"context_needs": ["codebase-architecture", "system-patterns", "past-designs", "dependency-graph"],
|
|
56
|
+
"success_metrics": ["design-quality", "implementation-feasibility", "team-alignment"],
|
|
57
|
+
"estimated_cost_per_task_usd": 2.5,
|
|
58
|
+
"origin": "builtin",
|
|
59
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
60
|
+
},
|
|
61
|
+
"builder": {
|
|
62
|
+
"title": "Builder",
|
|
63
|
+
"description": "Feature implementation, core development, code generation",
|
|
64
|
+
"required_skills": ["coding", "testing", "debugging", "performance-optimization"],
|
|
65
|
+
"recommended_model": "sonnet",
|
|
66
|
+
"context_needs": ["codebase-structure", "api-specs", "test-patterns", "build-system"],
|
|
67
|
+
"success_metrics": ["tests-passing", "code-quality", "productivity", "bug-rate"],
|
|
68
|
+
"estimated_cost_per_task_usd": 1.5,
|
|
69
|
+
"origin": "builtin",
|
|
70
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
71
|
+
},
|
|
72
|
+
"reviewer": {
|
|
73
|
+
"title": "Code Reviewer",
|
|
74
|
+
"description": "Code review, quality assurance, best practices enforcement",
|
|
75
|
+
"required_skills": ["code-review", "static-analysis", "security-review", "best-practices"],
|
|
76
|
+
"recommended_model": "sonnet",
|
|
77
|
+
"context_needs": ["coding-standards", "previous-reviews", "common-errors", "team-patterns"],
|
|
78
|
+
"success_metrics": ["review-quality", "issue-detection-rate", "feedback-clarity"],
|
|
79
|
+
"estimated_cost_per_task_usd": 1.2,
|
|
80
|
+
"origin": "builtin",
|
|
81
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
82
|
+
},
|
|
83
|
+
"tester": {
|
|
84
|
+
"title": "Test Specialist",
|
|
85
|
+
"description": "Test strategy, test case generation, test automation, quality validation",
|
|
86
|
+
"required_skills": ["testing", "coverage-analysis", "automation", "edge-case-detection"],
|
|
87
|
+
"recommended_model": "sonnet",
|
|
88
|
+
"context_needs": ["test-framework", "coverage-metrics", "failure-patterns", "requirements"],
|
|
89
|
+
"success_metrics": ["coverage-increase", "bug-detection", "test-execution-time"],
|
|
90
|
+
"estimated_cost_per_task_usd": 1.2,
|
|
91
|
+
"origin": "builtin",
|
|
92
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
93
|
+
},
|
|
94
|
+
"security-auditor": {
|
|
95
|
+
"title": "Security Auditor",
|
|
96
|
+
"description": "Security analysis, vulnerability detection, compliance verification",
|
|
97
|
+
"required_skills": ["security-analysis", "threat-modeling", "penetration-testing", "compliance"],
|
|
98
|
+
"recommended_model": "opus",
|
|
99
|
+
"context_needs": ["security-policies", "vulnerability-database", "threat-models", "compliance-reqs"],
|
|
100
|
+
"success_metrics": ["vulnerabilities-found", "severity-accuracy", "remediation-quality"],
|
|
101
|
+
"estimated_cost_per_task_usd": 2.0,
|
|
102
|
+
"origin": "builtin",
|
|
103
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
104
|
+
},
|
|
105
|
+
"docs-writer": {
|
|
106
|
+
"title": "Documentation Writer",
|
|
107
|
+
"description": "Documentation creation, API docs, user guides, onboarding materials",
|
|
108
|
+
"required_skills": ["documentation", "clarity", "completeness", "example-generation"],
|
|
109
|
+
"recommended_model": "haiku",
|
|
110
|
+
"context_needs": ["codebase-knowledge", "api-specs", "user-personas", "doc-templates"],
|
|
111
|
+
"success_metrics": ["documentation-completeness", "clarity-score", "example-coverage"],
|
|
112
|
+
"estimated_cost_per_task_usd": 0.8,
|
|
113
|
+
"origin": "builtin",
|
|
114
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
115
|
+
},
|
|
116
|
+
"optimizer": {
|
|
117
|
+
"title": "Performance Optimizer",
|
|
118
|
+
"description": "Performance analysis, optimization, profiling, efficiency improvements",
|
|
119
|
+
"required_skills": ["performance-analysis", "profiling", "optimization", "metrics-analysis"],
|
|
120
|
+
"recommended_model": "sonnet",
|
|
121
|
+
"context_needs": ["performance-benchmarks", "profiling-tools", "optimization-history"],
|
|
122
|
+
"success_metrics": ["performance-gain", "memory-efficiency", "latency-reduction"],
|
|
123
|
+
"estimated_cost_per_task_usd": 1.5,
|
|
124
|
+
"origin": "builtin",
|
|
125
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
126
|
+
},
|
|
127
|
+
"devops": {
|
|
128
|
+
"title": "DevOps Engineer",
|
|
129
|
+
"description": "Infrastructure, deployment pipelines, CI/CD, monitoring, reliability",
|
|
130
|
+
"required_skills": ["infrastructure-as-code", "deployment", "monitoring", "incident-response"],
|
|
131
|
+
"recommended_model": "sonnet",
|
|
132
|
+
"context_needs": ["infrastructure-config", "deployment-pipelines", "monitoring-setup", "runbooks"],
|
|
133
|
+
"success_metrics": ["deployment-success-rate", "incident-response-time", "uptime"],
|
|
134
|
+
"estimated_cost_per_task_usd": 1.8,
|
|
135
|
+
"origin": "builtin",
|
|
136
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
137
|
+
},
|
|
138
|
+
"pm": {
|
|
139
|
+
"title": "Project Manager",
|
|
140
|
+
"description": "Task decomposition, priority management, stakeholder communication, tracking",
|
|
141
|
+
"required_skills": ["task-decomposition", "prioritization", "communication", "planning"],
|
|
142
|
+
"recommended_model": "sonnet",
|
|
143
|
+
"context_needs": ["project-state", "requirements", "team-capacity", "past-estimates"],
|
|
144
|
+
"success_metrics": ["estimation-accuracy", "deadline-met", "scope-management"],
|
|
145
|
+
"estimated_cost_per_task_usd": 1.0,
|
|
146
|
+
"origin": "builtin",
|
|
147
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
148
|
+
},
|
|
149
|
+
"incident-responder": {
|
|
150
|
+
"title": "Incident Responder",
|
|
151
|
+
"description": "Crisis management, root cause analysis, rapid issue resolution, hotfixes",
|
|
152
|
+
"required_skills": ["crisis-management", "root-cause-analysis", "debugging", "communication"],
|
|
153
|
+
"recommended_model": "opus",
|
|
154
|
+
"context_needs": ["incident-history", "system-health", "alerting-rules", "past-incidents"],
|
|
155
|
+
"success_metrics": ["incident-resolution-time", "accuracy", "escalation-prevention"],
|
|
156
|
+
"estimated_cost_per_task_usd": 2.0,
|
|
157
|
+
"origin": "builtin",
|
|
158
|
+
"created_at": "2025-01-01T00:00:00Z"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
EOF
|
|
162
|
+
)
|
|
163
|
+
local _tmp_roles
|
|
164
|
+
_tmp_roles=$(mktemp)
|
|
165
|
+
trap "rm -f '$_tmp_roles'" RETURN
|
|
166
|
+
if echo "$roles_json" | jq '.' > "$_tmp_roles" 2>/dev/null && [[ -s "$_tmp_roles" ]]; then
|
|
167
|
+
mv "$_tmp_roles" "$ROLES_DB"
|
|
168
|
+
else
|
|
169
|
+
rm -f "$_tmp_roles"
|
|
170
|
+
error "Failed to initialize roles DB"
|
|
171
|
+
return 1
|
|
172
|
+
fi
|
|
173
|
+
success "Initialized 10 built-in agent roles"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
177
|
+
# LLM-POWERED SEMANTIC MATCHING (Tier 1)
|
|
178
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
179
|
+
|
|
180
|
+
_recruit_keyword_match() {
|
|
181
|
+
local task_description="$1"
|
|
182
|
+
local detected_skills=""
|
|
183
|
+
|
|
184
|
+
# Always run built-in regex patterns first (most reliable)
|
|
185
|
+
[[ "$task_description" =~ (architecture|design|scalability) ]] && detected_skills="${detected_skills}architect "
|
|
186
|
+
[[ "$task_description" =~ (build|feature|implement|code) ]] && detected_skills="${detected_skills}builder "
|
|
187
|
+
[[ "$task_description" =~ (review|quality|best.practice) ]] && detected_skills="${detected_skills}reviewer "
|
|
188
|
+
[[ "$task_description" =~ (test|coverage|automation) ]] && detected_skills="${detected_skills}tester "
|
|
189
|
+
[[ "$task_description" =~ (security|vulnerability|compliance) ]] && detected_skills="${detected_skills}security-auditor "
|
|
190
|
+
[[ "$task_description" =~ (document|guide|readme|api.doc|write.doc) ]] && detected_skills="${detected_skills}docs-writer "
|
|
191
|
+
[[ "$task_description" =~ (performance|optimization|profile|speed|latency|faster) ]] && detected_skills="${detected_skills}optimizer "
|
|
192
|
+
[[ "$task_description" =~ (deploy|infra|ci.cd|monitoring|docker|kubernetes) ]] && detected_skills="${detected_skills}devops "
|
|
193
|
+
[[ "$task_description" =~ (plan|decompose|estimate|priorit) ]] && detected_skills="${detected_skills}pm "
|
|
194
|
+
[[ "$task_description" =~ (urgent|incident|crisis|hotfix|outage) ]] && detected_skills="${detected_skills}incident-responder "
|
|
195
|
+
|
|
196
|
+
# Boost with learned keyword weights (override only if no regex match)
|
|
197
|
+
if [[ -z "$detected_skills" && -f "$HEURISTICS_DB" ]]; then
|
|
198
|
+
local learned_weights
|
|
199
|
+
learned_weights=$(jq -r '.keyword_weights // {}' "$HEURISTICS_DB" 2>/dev/null || echo "{}")
|
|
200
|
+
|
|
201
|
+
if [[ -n "$learned_weights" && "$learned_weights" != "{}" && "$learned_weights" != "null" ]]; then
|
|
202
|
+
local best_role="" best_score=0
|
|
203
|
+
local task_lower
|
|
204
|
+
task_lower=$(echo "$task_description" | tr '[:upper:]' '[:lower:]')
|
|
205
|
+
|
|
206
|
+
while IFS= read -r keyword; do
|
|
207
|
+
[[ -z "$keyword" ]] && continue
|
|
208
|
+
local kw_lower
|
|
209
|
+
kw_lower=$(echo "$keyword" | tr '[:upper:]' '[:lower:]')
|
|
210
|
+
if echo "$task_lower" | grep -q "$kw_lower" 2>/dev/null; then
|
|
211
|
+
local role_score
|
|
212
|
+
role_score=$(echo "$learned_weights" | jq -r --arg k "$keyword" '.[$k] | if type == "object" then .role else "" end' 2>/dev/null || echo "")
|
|
213
|
+
local weight
|
|
214
|
+
weight=$(echo "$learned_weights" | jq -r --arg k "$keyword" '.[$k] | if type == "object" then .weight else (. // 0) end' 2>/dev/null || echo "0")
|
|
215
|
+
|
|
216
|
+
if [[ -n "$role_score" && "$role_score" != "null" && "$role_score" != "" ]]; then
|
|
217
|
+
if awk -v w="$weight" -v b="$best_score" 'BEGIN{exit !(w > b)}' 2>/dev/null; then
|
|
218
|
+
best_role="$role_score"
|
|
219
|
+
best_score="$weight"
|
|
220
|
+
fi
|
|
221
|
+
fi
|
|
222
|
+
fi
|
|
223
|
+
done < <(echo "$learned_weights" | jq -r 'keys[]' 2>/dev/null || true)
|
|
224
|
+
|
|
225
|
+
if [[ -n "$best_role" ]]; then
|
|
226
|
+
detected_skills="$best_role"
|
|
227
|
+
fi
|
|
228
|
+
fi
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
# Default to builder if no match
|
|
232
|
+
if [[ -z "$detected_skills" ]]; then
|
|
233
|
+
detected_skills="builder"
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
echo "$detected_skills"
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
# LLM-powered semantic matching
|
|
240
|
+
_recruit_llm_match() {
|
|
241
|
+
local task_description="$1"
|
|
242
|
+
local available_roles="$2"
|
|
243
|
+
|
|
244
|
+
local prompt
|
|
245
|
+
prompt="You are an agent recruitment system. Given a task description, select the best role(s) from the available roles.
|
|
246
|
+
|
|
247
|
+
Task: ${task_description}
|
|
248
|
+
|
|
249
|
+
Available roles (JSON):
|
|
250
|
+
${available_roles}
|
|
251
|
+
|
|
252
|
+
Return ONLY a JSON object with:
|
|
253
|
+
{\"primary_role\": \"<role_key>\", \"secondary_roles\": [\"<role_key>\", ...], \"confidence\": <0.0-1.0>, \"reasoning\": \"<one line>\", \"new_role_needed\": false, \"suggested_role\": null}
|
|
254
|
+
|
|
255
|
+
If NO existing role is a good fit, set new_role_needed=true and provide:
|
|
256
|
+
{\"primary_role\": \"builder\", \"secondary_roles\": [], \"confidence\": 0.3, \"reasoning\": \"...\", \"new_role_needed\": true, \"suggested_role\": {\"key\": \"<kebab-case>\", \"title\": \"<Title>\", \"description\": \"<desc>\", \"required_skills\": [\"<skill>\"], \"recommended_model\": \"sonnet\", \"context_needs\": [\"<need>\"], \"success_metrics\": [\"<metric>\"], \"estimated_cost_per_task_usd\": 1.5}}
|
|
257
|
+
|
|
258
|
+
Return JSON only, no markdown fences."
|
|
259
|
+
|
|
260
|
+
local result
|
|
261
|
+
result=$(_recruit_call_claude "$prompt")
|
|
262
|
+
|
|
263
|
+
if [[ -n "$result" ]] && echo "$result" | jq -e '.primary_role' >/dev/null 2>&1; then
|
|
264
|
+
echo "$result"
|
|
265
|
+
return 0
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
echo ""
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
# Record a match for learning
|
|
272
|
+
_recruit_record_match() {
|
|
273
|
+
local task="$1"
|
|
274
|
+
local role="$2"
|
|
275
|
+
local method="$3"
|
|
276
|
+
local confidence="${4:-0.5}"
|
|
277
|
+
local agent_id="${5:-}"
|
|
278
|
+
|
|
279
|
+
mkdir -p "$RECRUIT_ROOT"
|
|
280
|
+
local match_epoch
|
|
281
|
+
match_epoch=$(now_epoch)
|
|
282
|
+
local match_id="match-${match_epoch}-$$"
|
|
283
|
+
|
|
284
|
+
local record
|
|
285
|
+
record=$(jq -c -n \
|
|
286
|
+
--arg ts "$(now_iso)" \
|
|
287
|
+
--argjson epoch "$match_epoch" \
|
|
288
|
+
--arg match_id "$match_id" \
|
|
289
|
+
--arg task "$task" \
|
|
290
|
+
--arg role "$role" \
|
|
291
|
+
--arg method "$method" \
|
|
292
|
+
--argjson conf "$confidence" \
|
|
293
|
+
--arg agent "$agent_id" \
|
|
294
|
+
'{ts: $ts, ts_epoch: $epoch, match_id: $match_id, task: $task, role: $role, method: $method, confidence: $conf, agent_id: $agent, outcome: null}')
|
|
295
|
+
echo "$record" >> "$MATCH_HISTORY"
|
|
296
|
+
|
|
297
|
+
# Enforce max match history size (from policy)
|
|
298
|
+
local max_history="${RECRUIT_MAX_MATCH_HISTORY:-5000}"
|
|
299
|
+
local current_lines
|
|
300
|
+
current_lines=$(wc -l < "$MATCH_HISTORY" 2>/dev/null | tr -d ' ')
|
|
301
|
+
if [[ "$current_lines" -gt "$max_history" ]]; then
|
|
302
|
+
local tmp_trunc
|
|
303
|
+
tmp_trunc=$(mktemp)
|
|
304
|
+
trap "rm -f '$tmp_trunc'" RETURN
|
|
305
|
+
tail -n "$max_history" "$MATCH_HISTORY" > "$tmp_trunc" && _recruit_locked_write "$MATCH_HISTORY" "$tmp_trunc" || rm -f "$tmp_trunc"
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
# Update role usage stats
|
|
309
|
+
_recruit_track_role_usage "$role" "match"
|
|
310
|
+
|
|
311
|
+
# Store match_id in global for callers (avoids stdout contamination)
|
|
312
|
+
LAST_MATCH_ID="$match_id"
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
316
|
+
# DYNAMIC ROLE CREATION (Tier 1)
|
|
317
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
318
|
+
|
|
319
|
+
cmd_create_role() {
|
|
320
|
+
local role_key="${1:-}"
|
|
321
|
+
local role_title="${2:-}"
|
|
322
|
+
local role_desc="${3:-}"
|
|
323
|
+
|
|
324
|
+
if [[ -z "$role_key" ]]; then
|
|
325
|
+
error "Usage: shipwright recruit create-role <key> [title] [description]"
|
|
326
|
+
echo " Or use: shipwright recruit create-role --auto \"<task description>\""
|
|
327
|
+
exit 1
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
ensure_recruit_dir
|
|
331
|
+
initialize_builtin_roles
|
|
332
|
+
|
|
333
|
+
# Auto-generate via LLM if --auto flag
|
|
334
|
+
if [[ "$role_key" == "--auto" ]]; then
|
|
335
|
+
local task_desc="${role_title:-$role_desc}"
|
|
336
|
+
if [[ -z "$task_desc" ]]; then
|
|
337
|
+
error "Usage: shipwright recruit create-role --auto \"<task description>\""
|
|
338
|
+
exit 1
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
info "Generating role definition via AI for: ${CYAN}${task_desc}${RESET}"
|
|
342
|
+
|
|
343
|
+
local existing_roles
|
|
344
|
+
existing_roles=$(jq -r 'keys | join(", ")' "$ROLES_DB" 2>/dev/null || echo "none")
|
|
345
|
+
|
|
346
|
+
local prompt
|
|
347
|
+
prompt="Create a new agent role definition for a task that doesn't fit existing roles.
|
|
348
|
+
|
|
349
|
+
Task description: ${task_desc}
|
|
350
|
+
Existing roles: ${existing_roles}
|
|
351
|
+
|
|
352
|
+
Return ONLY a JSON object:
|
|
353
|
+
{\"key\": \"<kebab-case-unique-key>\", \"title\": \"<Title>\", \"description\": \"<description>\", \"required_skills\": [\"<skill1>\", \"<skill2>\", \"<skill3>\"], \"recommended_model\": \"sonnet\", \"context_needs\": [\"<need1>\", \"<need2>\"], \"success_metrics\": [\"<metric1>\", \"<metric2>\"], \"estimated_cost_per_task_usd\": 1.5}
|
|
354
|
+
|
|
355
|
+
Return JSON only."
|
|
356
|
+
|
|
357
|
+
local result
|
|
358
|
+
result=$(_recruit_call_claude "$prompt")
|
|
359
|
+
|
|
360
|
+
if [[ -n "$result" ]] && echo "$result" | jq -e '.key' >/dev/null 2>&1; then
|
|
361
|
+
role_key=$(echo "$result" | jq -r '.key')
|
|
362
|
+
role_title=$(echo "$result" | jq -r '.title')
|
|
363
|
+
role_desc=$(echo "$result" | jq -r '.description')
|
|
364
|
+
|
|
365
|
+
# Add origin and timestamp
|
|
366
|
+
result=$(echo "$result" | jq --arg ts "$(now_iso)" '. + {origin: "ai-generated", created_at: $ts}')
|
|
367
|
+
|
|
368
|
+
# Persist to roles DB
|
|
369
|
+
local tmp_file
|
|
370
|
+
tmp_file=$(mktemp)
|
|
371
|
+
trap "rm -f '$tmp_file'" RETURN
|
|
372
|
+
if jq --arg key "$role_key" --argjson role "$(echo "$result" | jq 'del(.key)')" '.[$key] = $role' "$ROLES_DB" > "$tmp_file"; then
|
|
373
|
+
_recruit_locked_write "$ROLES_DB" "$tmp_file"
|
|
374
|
+
else
|
|
375
|
+
rm -f "$tmp_file"
|
|
376
|
+
error "Failed to save role to database"
|
|
377
|
+
return 1
|
|
378
|
+
fi
|
|
379
|
+
|
|
380
|
+
# Log the invention
|
|
381
|
+
echo "$result" | jq -c --arg trigger "$task_desc" '. + {trigger: $trigger}' >> "$INVENTED_ROLES_LOG" 2>/dev/null || true
|
|
382
|
+
|
|
383
|
+
success "Created AI-generated role: ${CYAN}${role_key}${RESET} — ${role_title}"
|
|
384
|
+
echo " ${role_desc}"
|
|
385
|
+
emit_event "recruit_role_created" "role=${role_key}" "method=ai" "title=${role_title}"
|
|
386
|
+
return 0
|
|
387
|
+
else
|
|
388
|
+
warn "AI generation failed, falling back to manual creation"
|
|
389
|
+
fi
|
|
390
|
+
|
|
391
|
+
# Generate a slug from the task description for the fallback key
|
|
392
|
+
role_key="custom-$(echo "$task_desc" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '-' | sed 's/^-//;s/-$//' | cut -c1-50)"
|
|
393
|
+
role_title="$task_desc"
|
|
394
|
+
role_desc="Auto-created role for: ${task_desc}"
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
# Manual role creation
|
|
398
|
+
if [[ -z "$role_title" ]]; then
|
|
399
|
+
role_title="$role_key"
|
|
400
|
+
fi
|
|
401
|
+
if [[ -z "$role_desc" ]]; then
|
|
402
|
+
role_desc="Custom role: ${role_title}"
|
|
403
|
+
fi
|
|
404
|
+
|
|
405
|
+
local role_json
|
|
406
|
+
role_json=$(jq -n \
|
|
407
|
+
--arg title "$role_title" \
|
|
408
|
+
--arg desc "$role_desc" \
|
|
409
|
+
--arg ts "$(now_iso)" \
|
|
410
|
+
'{
|
|
411
|
+
title: $title,
|
|
412
|
+
description: $desc,
|
|
413
|
+
required_skills: ["general"],
|
|
414
|
+
recommended_model: "sonnet",
|
|
415
|
+
context_needs: ["codebase-structure"],
|
|
416
|
+
success_metrics: ["task-completion"],
|
|
417
|
+
estimated_cost_per_task_usd: 1.5,
|
|
418
|
+
origin: "manual",
|
|
419
|
+
created_at: $ts
|
|
420
|
+
}')
|
|
421
|
+
|
|
422
|
+
local tmp_file
|
|
423
|
+
tmp_file=$(mktemp)
|
|
424
|
+
trap "rm -f '$tmp_file'" RETURN
|
|
425
|
+
if jq --arg key "$role_key" --argjson role "$role_json" '.[$key] = $role' "$ROLES_DB" > "$tmp_file"; then
|
|
426
|
+
_recruit_locked_write "$ROLES_DB" "$tmp_file"
|
|
427
|
+
else
|
|
428
|
+
rm -f "$tmp_file"
|
|
429
|
+
error "Failed to save role to database"
|
|
430
|
+
return 1
|
|
431
|
+
fi
|
|
432
|
+
|
|
433
|
+
success "Created role: ${CYAN}${role_key}${RESET} — ${role_title}"
|
|
434
|
+
emit_event "recruit_role_created" "role=${role_key}" "method=manual" "title=${role_title}"
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
438
|
+
# ORIGINAL COMMANDS (enhanced)
|
|
439
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
440
|
+
|
|
441
|
+
cmd_roles() {
|
|
442
|
+
ensure_recruit_dir
|
|
443
|
+
initialize_builtin_roles
|
|
444
|
+
|
|
445
|
+
info "Available Agent Roles ($(jq 'length' "$ROLES_DB" 2>/dev/null || echo "?") total):"
|
|
446
|
+
echo ""
|
|
447
|
+
|
|
448
|
+
jq -r 'to_entries | sort_by(.key) | .[] |
|
|
449
|
+
"\(.key): \(.value.title) — \(.value.description)\n Model: \(.value.recommended_model) | Cost: $\(.value.estimated_cost_per_task_usd)/task | Origin: \(.value.origin // "builtin")\n Skills: \(.value.required_skills | join(", "))\n"' \
|
|
450
|
+
"$ROLES_DB"
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
cmd_match() {
|
|
454
|
+
local json_mode=false
|
|
455
|
+
if [[ "${1:-}" == "--json" ]]; then
|
|
456
|
+
json_mode=true
|
|
457
|
+
shift
|
|
458
|
+
fi
|
|
459
|
+
local task_description="${1:-}"
|
|
460
|
+
|
|
461
|
+
if [[ -z "$task_description" ]]; then
|
|
462
|
+
error "Usage: shipwright recruit match [--json] \"<task description>\""
|
|
463
|
+
exit 1
|
|
464
|
+
fi
|
|
465
|
+
|
|
466
|
+
ensure_recruit_dir
|
|
467
|
+
initialize_builtin_roles
|
|
468
|
+
|
|
469
|
+
if ! $json_mode; then
|
|
470
|
+
info "Analyzing task: ${CYAN}${task_description}${RESET}"
|
|
471
|
+
echo ""
|
|
472
|
+
fi
|
|
473
|
+
|
|
474
|
+
local primary_role="" secondary_roles="" confidence=0.5 method="keyword" reasoning=""
|
|
475
|
+
|
|
476
|
+
# Try LLM-powered matching first
|
|
477
|
+
if _recruit_has_claude; then
|
|
478
|
+
local available_roles
|
|
479
|
+
available_roles=$(jq -c '.' "$ROLES_DB" 2>/dev/null || echo "{}")
|
|
480
|
+
|
|
481
|
+
local llm_result
|
|
482
|
+
llm_result=$(_recruit_llm_match "$task_description" "$available_roles")
|
|
483
|
+
|
|
484
|
+
if [[ -n "$llm_result" ]] && echo "$llm_result" | jq -e '.primary_role' >/dev/null 2>&1; then
|
|
485
|
+
primary_role=$(echo "$llm_result" | jq -r '.primary_role')
|
|
486
|
+
secondary_roles=$(echo "$llm_result" | jq -r '.secondary_roles // [] | join(", ")')
|
|
487
|
+
confidence=$(echo "$llm_result" | jq -r '.confidence // 0.8')
|
|
488
|
+
reasoning=$(echo "$llm_result" | jq -r '.reasoning // ""')
|
|
489
|
+
method="llm"
|
|
490
|
+
|
|
491
|
+
# Check if a new role was suggested
|
|
492
|
+
local new_role_needed
|
|
493
|
+
new_role_needed=$(echo "$llm_result" | jq -r '.new_role_needed // false')
|
|
494
|
+
if [[ "$new_role_needed" == "true" ]]; then
|
|
495
|
+
local suggested
|
|
496
|
+
suggested=$(echo "$llm_result" | jq '.suggested_role // null')
|
|
497
|
+
if [[ "$suggested" != "null" ]]; then
|
|
498
|
+
echo ""
|
|
499
|
+
warn "No perfect role match — AI suggests creating a new role:"
|
|
500
|
+
echo " $(echo "$suggested" | jq -r '.title // "Unknown"'): $(echo "$suggested" | jq -r '.description // ""')"
|
|
501
|
+
echo " Run: shipwright recruit create-role --auto \"${task_description}\""
|
|
502
|
+
echo ""
|
|
503
|
+
fi
|
|
504
|
+
fi
|
|
505
|
+
fi
|
|
506
|
+
fi
|
|
507
|
+
|
|
508
|
+
# Fallback to keyword matching
|
|
509
|
+
if [[ -z "$primary_role" ]]; then
|
|
510
|
+
local detected_skills
|
|
511
|
+
detected_skills=$(_recruit_keyword_match "$task_description")
|
|
512
|
+
primary_role=$(echo "$detected_skills" | awk '{print $1}')
|
|
513
|
+
secondary_roles=$(echo "$detected_skills" | cut -d' ' -f2- | tr ' ' ',' | sed 's/,$//')
|
|
514
|
+
method="keyword"
|
|
515
|
+
confidence=0.5
|
|
516
|
+
fi
|
|
517
|
+
|
|
518
|
+
# Validate role exists
|
|
519
|
+
if ! jq -e ".\"${primary_role}\"" "$ROLES_DB" >/dev/null 2>&1; then
|
|
520
|
+
primary_role="builder"
|
|
521
|
+
fi
|
|
522
|
+
|
|
523
|
+
# Record for learning
|
|
524
|
+
_recruit_record_match "$task_description" "$primary_role" "$method" "$confidence"
|
|
525
|
+
|
|
526
|
+
local role_info
|
|
527
|
+
role_info=$(jq ".\"${primary_role}\"" "$ROLES_DB")
|
|
528
|
+
local recommended_model
|
|
529
|
+
recommended_model=$(echo "$role_info" | jq -r '.recommended_model // "sonnet"')
|
|
530
|
+
|
|
531
|
+
# JSON mode: structured output for programmatic consumption
|
|
532
|
+
if $json_mode; then
|
|
533
|
+
jq -c -n \
|
|
534
|
+
--arg role "$primary_role" \
|
|
535
|
+
--arg secondary "$secondary_roles" \
|
|
536
|
+
--argjson confidence "$confidence" \
|
|
537
|
+
--arg method "$method" \
|
|
538
|
+
--arg model "$recommended_model" \
|
|
539
|
+
--arg reasoning "$reasoning" \
|
|
540
|
+
'{
|
|
541
|
+
primary_role: $role,
|
|
542
|
+
secondary_roles: ($secondary | split(", ") | map(select(. != ""))),
|
|
543
|
+
confidence: $confidence,
|
|
544
|
+
method: $method,
|
|
545
|
+
model: $model,
|
|
546
|
+
reasoning: $reasoning
|
|
547
|
+
}'
|
|
548
|
+
return 0
|
|
549
|
+
fi
|
|
550
|
+
|
|
551
|
+
success "Recommended role: ${CYAN}${primary_role}${RESET} ${DIM}(confidence: $(awk -v c="$confidence" 'BEGIN{printf "%.0f", c*100}')%, method: ${method})${RESET}"
|
|
552
|
+
[[ -n "$reasoning" ]] && echo -e " ${DIM}${reasoning}${RESET}"
|
|
553
|
+
echo ""
|
|
554
|
+
|
|
555
|
+
echo " $(echo "$role_info" | jq -r '.description')"
|
|
556
|
+
echo " Model: ${recommended_model}"
|
|
557
|
+
echo " Skills: $(echo "$role_info" | jq -r '.required_skills | join(", ")')"
|
|
558
|
+
|
|
559
|
+
if [[ -n "$secondary_roles" && "$secondary_roles" != "null" ]]; then
|
|
560
|
+
echo ""
|
|
561
|
+
warn "Secondary roles: ${secondary_roles}"
|
|
562
|
+
fi
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
cmd_evaluate() {
|
|
566
|
+
local agent_id="${1:-}"
|
|
567
|
+
|
|
568
|
+
if [[ -z "$agent_id" ]]; then
|
|
569
|
+
error "Usage: shipwright recruit evaluate <agent-id>"
|
|
570
|
+
exit 1
|
|
571
|
+
fi
|
|
572
|
+
|
|
573
|
+
ensure_recruit_dir
|
|
574
|
+
|
|
575
|
+
info "Evaluating agent: ${CYAN}${agent_id}${RESET}"
|
|
576
|
+
echo ""
|
|
577
|
+
|
|
578
|
+
local profile
|
|
579
|
+
profile=$(jq ".\"${agent_id}\"" "$PROFILES_DB" 2>/dev/null || echo "{}")
|
|
580
|
+
|
|
581
|
+
if [[ "$profile" == "{}" || "$profile" == "null" ]]; then
|
|
582
|
+
warn "No evaluation history for ${agent_id}"
|
|
583
|
+
return 0
|
|
584
|
+
fi
|
|
585
|
+
|
|
586
|
+
echo "Performance Metrics:"
|
|
587
|
+
echo " Success Rate: $(echo "$profile" | jq -r '.success_rate // "N/A"')%"
|
|
588
|
+
echo " Avg Time: $(echo "$profile" | jq -r '.avg_time_minutes // "N/A"') minutes"
|
|
589
|
+
echo " Quality Score: $(echo "$profile" | jq -r '.quality_score // "N/A"')/10"
|
|
590
|
+
echo " Cost Efficiency: $(echo "$profile" | jq -r '.cost_efficiency // "N/A"')%"
|
|
591
|
+
echo " Tasks Completed: $(echo "$profile" | jq -r '.tasks_completed // "0"')"
|
|
592
|
+
echo ""
|
|
593
|
+
|
|
594
|
+
# Use population-aware thresholds for performance evaluation
|
|
595
|
+
local pop_stats
|
|
596
|
+
pop_stats=$(_recruit_compute_population_stats)
|
|
597
|
+
local mean_success
|
|
598
|
+
mean_success=$(echo "$pop_stats" | jq -r '.mean_success')
|
|
599
|
+
local stddev
|
|
600
|
+
stddev=$(echo "$pop_stats" | jq -r '.stddev_success')
|
|
601
|
+
local agent_count
|
|
602
|
+
agent_count=$(echo "$pop_stats" | jq -r '.count')
|
|
603
|
+
|
|
604
|
+
local success_rate
|
|
605
|
+
success_rate=$(echo "$profile" | jq -r '.success_rate // 0')
|
|
606
|
+
|
|
607
|
+
if [[ "$agent_count" -ge 3 ]]; then
|
|
608
|
+
# Population-aware evaluation
|
|
609
|
+
local promote_threshold demote_threshold
|
|
610
|
+
promote_threshold=$(awk -v m="$mean_success" -v s="$stddev" 'BEGIN{v=m+s; if(v>95) v=95; printf "%.0f", v}')
|
|
611
|
+
demote_threshold=$(awk -v m="$mean_success" -v s="$stddev" 'BEGIN{v=m-s; if(v<40) v=40; printf "%.0f", v}')
|
|
612
|
+
|
|
613
|
+
echo -e " ${DIM}Population thresholds (${agent_count} agents): promote ≥${promote_threshold}%, demote <${demote_threshold}%${RESET}"
|
|
614
|
+
|
|
615
|
+
if awk -v sr="$success_rate" -v t="$demote_threshold" 'BEGIN{exit !(sr < t)}' 2>/dev/null; then
|
|
616
|
+
warn "Performance below population threshold. Consider downgrading or retraining."
|
|
617
|
+
elif awk -v sr="$success_rate" -v t="$promote_threshold" 'BEGIN{exit !(sr >= t)}' 2>/dev/null; then
|
|
618
|
+
success "Excellent performance (top tier). Consider for promotion."
|
|
619
|
+
else
|
|
620
|
+
success "Acceptable performance. Continue current assignment."
|
|
621
|
+
fi
|
|
622
|
+
else
|
|
623
|
+
# Fallback to fixed thresholds
|
|
624
|
+
if (( $(echo "$success_rate < 70" | bc -l 2>/dev/null || echo "1") )); then
|
|
625
|
+
warn "Performance below threshold. Consider downgrading or retraining."
|
|
626
|
+
elif (( $(echo "$success_rate >= 90" | bc -l 2>/dev/null || echo "0") )); then
|
|
627
|
+
success "Excellent performance. Consider for promotion."
|
|
628
|
+
else
|
|
629
|
+
success "Acceptable performance. Continue current assignment."
|
|
630
|
+
fi
|
|
631
|
+
fi
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
cmd_profiles() {
|
|
635
|
+
ensure_recruit_dir
|
|
636
|
+
|
|
637
|
+
info "Agent Performance Profiles:"
|
|
638
|
+
echo ""
|
|
639
|
+
|
|
640
|
+
if [[ ! -s "$PROFILES_DB" || "$(jq 'length' "$PROFILES_DB" 2>/dev/null || echo 0)" -eq 0 ]]; then
|
|
641
|
+
warn "No performance profiles recorded yet"
|
|
642
|
+
return 0
|
|
643
|
+
fi
|
|
644
|
+
|
|
645
|
+
jq -r 'to_entries | .[] |
|
|
646
|
+
"\(.key):\n Success: \(.value.success_rate // "N/A")% | Quality: \(.value.quality_score // "N/A")/10 | Tasks: \(.value.tasks_completed // 0)\n Avg Time: \(.value.avg_time_minutes // "N/A")min | Efficiency: \(.value.cost_efficiency // "N/A")%\n Model: \(.value.model // "unknown") | Role: \(.value.role // "unassigned")\n"' \
|
|
647
|
+
"$PROFILES_DB"
|
|
648
|
+
}
|