opencode-code-archaeology 2.0.2 → 2.2.1
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/AGENTS.md +42 -10
- package/CHANGELOG.md +66 -0
- package/CONTRIBUTING.md +2 -0
- package/INSTALL.md +90 -20
- package/README.md +90 -29
- package/VERSION +1 -1
- package/assets/code-archaeology-banner.svg +173 -44
- package/commands/code-archaeology.md +7 -5
- package/dist/cli.js +28 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -17
- package/dist/index.js.map +1 -1
- package/dist/platform.d.ts +4 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +11 -0
- package/dist/platform.js.map +1 -0
- package/dist/plugin.d.ts +2 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +2 -1
- package/dist/plugin.js.map +1 -1
- package/dist/runtime.d.ts +18 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +49 -0
- package/dist/runtime.js.map +1 -0
- package/dist/types.d.ts +1 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/ARCHITECTURE.md +41 -10
- package/docs/INSTALL.md +50 -11
- package/docs/README.md +37 -9
- package/docs/RELEASE.md +20 -16
- package/docs/index.html +740 -0
- package/hooks/hermes/runner.ps1 +247 -0
- package/hooks/hermes/runner.sh +262 -0
- package/hooks/hermes/setup.ps1 +41 -0
- package/hooks/hermes/setup.sh +41 -0
- package/hooks/opencode/init.ps1 +83 -0
- package/hooks/opencode/revert-phase.ps1 +12 -0
- package/hooks/opencode/revert-phase.sh +3 -8
- package/hooks/opencode/update-expedition.ps1 +51 -0
- package/hooks/opencode/verify-package.sh +1 -0
- package/hooks/opencode/verify-phase.ps1 +35 -0
- package/hooks/opencode/verify-phase.sh +7 -7
- package/hooks/shared/command-utils.ps1 +100 -0
- package/package.json +16 -3
- package/skills/code-archaeology/SKILL.md +2 -2
- package/skills/hermes/INTEGRATION.md +120 -0
- package/skills/hermes/README.md +167 -0
- package/skills/hermes/code-archaeology-prompt.md +203 -0
- package/wiki/Expedition-Workflow.md +3 -1
- package/wiki/Home.md +2 -2
- package/wiki/Installation.md +2 -0
- package/wiki/Release-Process.md +6 -6
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -63
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -48
- package/.github/pull_request_template.md +0 -26
- package/.github/workflows/ci.yml +0 -45
- package/plugins/code-archaeology.ts +0 -8
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Hermes agent expedition runner - execute one Code Archaeology phase per cron run
|
|
2
|
+
$ErrorActionPreference = 'Stop'
|
|
3
|
+
|
|
4
|
+
$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
5
|
+
$REPO_ROOT = Resolve-Path "$SCRIPT_DIR/../.." | Select-Object -ExpandProperty Path
|
|
6
|
+
$ARCHAEOLOGY_DIR = "$REPO_ROOT/.archaeology"
|
|
7
|
+
$SESSION_FILE = "$ARCHAEOLOGY_DIR/session.json"
|
|
8
|
+
|
|
9
|
+
# Restore mode can execute operator-provided verification commands. Keep the
|
|
10
|
+
# authorization outside repository-controlled files so a malicious checkout
|
|
11
|
+
# cannot enable unattended command execution by shipping .archaeology state.
|
|
12
|
+
$RESTORE_APPROVAL_ENV = "HERMES_RESTORE_APPROVED"
|
|
13
|
+
|
|
14
|
+
New-Item -ItemType Directory -Force -Path "$ARCHAEOLOGY_DIR" | Out-Null
|
|
15
|
+
. (Join-Path $SCRIPT_DIR "../shared/command-utils.ps1")
|
|
16
|
+
|
|
17
|
+
function Block-Session {
|
|
18
|
+
param(
|
|
19
|
+
[string]$Reason,
|
|
20
|
+
[string]$Message = $Reason
|
|
21
|
+
)
|
|
22
|
+
if (Test-Path "$SESSION_FILE") {
|
|
23
|
+
$session = Get-Content "$SESSION_FILE" -Raw | ConvertFrom-Json
|
|
24
|
+
$session.status = "blocked"
|
|
25
|
+
if (!($session.PSObject.Properties["flags"])) {
|
|
26
|
+
$session | Add-Member -MemberType NoteProperty -Name "flags" -Value @{}
|
|
27
|
+
}
|
|
28
|
+
$session.flags.blocked_reason = $Reason
|
|
29
|
+
$session | ConvertTo-Json -Depth 10 | Set-Content "$SESSION_FILE" -Encoding UTF8
|
|
30
|
+
}
|
|
31
|
+
Write-Host "ERROR: $Message" -ForegroundColor Red
|
|
32
|
+
exit 1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Read-SessionString {
|
|
36
|
+
param([object]$Session, [string]$Key)
|
|
37
|
+
if (!($Session.PSObject.Properties[$Key]) -or !($Session.$Key -is [string]) -or [string]::IsNullOrWhiteSpace($Session.$Key)) {
|
|
38
|
+
Block-Session -Reason "invalid session field: $Key" -Message "Invalid Hermes session field: $Key"
|
|
39
|
+
}
|
|
40
|
+
return $Session.$Key
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function Require-RestoreApproval {
|
|
44
|
+
if ([Environment]::GetEnvironmentVariable($RESTORE_APPROVAL_ENV) -ne "1") {
|
|
45
|
+
Block-Session `
|
|
46
|
+
-Reason "restore mode requires $RESTORE_APPROVAL_ENV=1" `
|
|
47
|
+
-Message "Hermes restore mode is disabled until the operator sets $RESTORE_APPROVAL_ENV=1 outside session.json"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function Validate-BranchName {
|
|
52
|
+
param([string]$Branch)
|
|
53
|
+
if ([string]::IsNullOrWhiteSpace($Branch) -or $Branch.StartsWith("-") -or $Branch -match "\s") {
|
|
54
|
+
Block-Session -Reason "invalid branch_name" -Message "Invalid Hermes branch_name: $Branch"
|
|
55
|
+
}
|
|
56
|
+
git check-ref-format --branch "$Branch" *> $null
|
|
57
|
+
if ($LASTEXITCODE -ne 0) {
|
|
58
|
+
Block-Session -Reason "invalid branch_name" -Message "Invalid Hermes branch_name: $Branch"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Phase definitions (fixed order)
|
|
63
|
+
$PHASES = @(
|
|
64
|
+
"site-survey"
|
|
65
|
+
"dead-code"
|
|
66
|
+
"legacy-removal"
|
|
67
|
+
"dependency-mapping"
|
|
68
|
+
"type-consolidation"
|
|
69
|
+
"type-hardening"
|
|
70
|
+
"dry-stratification"
|
|
71
|
+
"error-handling"
|
|
72
|
+
"artifact-cleaning"
|
|
73
|
+
"final-catalog"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Detect current phase from session file
|
|
77
|
+
$current_phase = ""
|
|
78
|
+
if (Test-Path "$SESSION_FILE") {
|
|
79
|
+
$session = Get-Content "$SESSION_FILE" -Raw | ConvertFrom-Json
|
|
80
|
+
if ($session.PSObject.Properties["current_phase"] -and $session.current_phase) {
|
|
81
|
+
$current_phase = $session.current_phase
|
|
82
|
+
} else {
|
|
83
|
+
Block-Session -Reason "invalid session.json" -Message "Invalid Hermes session file: $SESSION_FILE"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (-not $current_phase) {
|
|
88
|
+
$current_phase = $PHASES[0]
|
|
89
|
+
# Initialize session
|
|
90
|
+
$NOW = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
91
|
+
$session = @{
|
|
92
|
+
runtime = "hermes"
|
|
93
|
+
status = "running"
|
|
94
|
+
current_phase = $current_phase
|
|
95
|
+
completed_phases = @()
|
|
96
|
+
mode = "survey"
|
|
97
|
+
repo_path = "."
|
|
98
|
+
language = "typescript"
|
|
99
|
+
test_command = "npm test"
|
|
100
|
+
typecheck_command = "npx tsc --noEmit"
|
|
101
|
+
branch_name = "refactor/archaeology"
|
|
102
|
+
strict_mode = $false
|
|
103
|
+
started_at = $NOW
|
|
104
|
+
}
|
|
105
|
+
$session | ConvertTo-Json -Depth 10 | Set-Content "$SESSION_FILE" -Encoding UTF8
|
|
106
|
+
Write-Host "Initialized Hermes session. Starting phase: $current_phase"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# Find phase index
|
|
110
|
+
$phase_idx = 0
|
|
111
|
+
$phase_found = $false
|
|
112
|
+
for ($i = 0; $i -lt $PHASES.Count; $i++) {
|
|
113
|
+
if ($PHASES[$i] -eq $current_phase) {
|
|
114
|
+
$phase_idx = $i
|
|
115
|
+
$phase_found = $true
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (-not $phase_found) {
|
|
121
|
+
Block-Session -Reason "unknown phase: $current_phase" -Message "Unknown Hermes phase: $current_phase"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
$phase_number = $phase_idx + 1
|
|
125
|
+
$total_phases = $PHASES.Count
|
|
126
|
+
|
|
127
|
+
Write-Host "=== Code Archaeology Hermes Runner ==="
|
|
128
|
+
Write-Host "Phase $phase_number/$total_phases: $current_phase"
|
|
129
|
+
$session = Get-Content "$SESSION_FILE" -Raw | ConvertFrom-Json
|
|
130
|
+
Write-Host "Mode: $(Read-SessionString -Session $session -Key 'mode')"
|
|
131
|
+
Write-Host ""
|
|
132
|
+
|
|
133
|
+
# Run the phase
|
|
134
|
+
Set-Location "$REPO_ROOT"
|
|
135
|
+
|
|
136
|
+
$mode = Read-SessionString -Session $session -Key "mode"
|
|
137
|
+
if ($mode -eq "restore") {
|
|
138
|
+
Require-RestoreApproval
|
|
139
|
+
Block-Session -Reason "restore mode is not implemented in Hermes runner" -Message "Hermes restore mode is not implemented. Use OpenCode restore after reviewing generated patches."
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Create branch if needed
|
|
143
|
+
$branch = Read-SessionString -Session $session -Key "branch_name"
|
|
144
|
+
Validate-BranchName -Branch $branch
|
|
145
|
+
git checkout -b "$branch" 2>$null
|
|
146
|
+
if (-not $?) {
|
|
147
|
+
git checkout "$branch"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Execute phase based on mode
|
|
151
|
+
if ($mode -eq "survey") {
|
|
152
|
+
Write-Host "Running SURVEY for phase $current_phase..."
|
|
153
|
+
# Generate reports only, no file changes
|
|
154
|
+
try {
|
|
155
|
+
& "$SCRIPT_DIR/../opencode/init.ps1" 2>$null
|
|
156
|
+
} catch {}
|
|
157
|
+
|
|
158
|
+
# Run phase-specific analysis
|
|
159
|
+
switch ($current_phase) {
|
|
160
|
+
"site-survey" { Write-Host "Generating site survey and baseline inventory..." }
|
|
161
|
+
"dead-code" { Write-Host "Cataloging dead code, unused exports, unreachable functions..." }
|
|
162
|
+
"legacy-removal" { Write-Host "Identifying legacy fallbacks, deprecated shims, compatibility layers..." }
|
|
163
|
+
"dependency-mapping" { Write-Host "Mapping circular dependencies..." }
|
|
164
|
+
"type-consolidation" { Write-Host "Finding duplicate type definitions..." }
|
|
165
|
+
"type-hardening" { Write-Host "Identifying weak types..." }
|
|
166
|
+
"dry-stratification" { Write-Host "Finding semantic duplication..." }
|
|
167
|
+
"error-handling" { Write-Host "Reviewing error-handling patterns..." }
|
|
168
|
+
"artifact-cleaning" { Write-Host "Identifying stale artifacts and documentation gaps..." }
|
|
169
|
+
"final-catalog" { Write-Host "Generating final excavation catalog..." }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
# Write report
|
|
173
|
+
$report_file = "$ARCHAEOLOGY_DIR/expedition${phase_number}-report.md"
|
|
174
|
+
$reportTimestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
175
|
+
@"
|
|
176
|
+
# Expedition $phase_number`: $current_phase
|
|
177
|
+
|
|
178
|
+
**Mode:** survey
|
|
179
|
+
**Phase:** $phase_number/$total_phases
|
|
180
|
+
**Runtime:** hermes
|
|
181
|
+
**Timestamp:** $reportTimestamp
|
|
182
|
+
|
|
183
|
+
## Findings
|
|
184
|
+
|
|
185
|
+
Hermes recorded this phase handoff. It does not perform autonomous static analysis in this hook.
|
|
186
|
+
|
|
187
|
+
## Recommendations
|
|
188
|
+
|
|
189
|
+
Run the matching Code Archaeology prompt with OpenCode, then keep this report with the reviewed findings.
|
|
190
|
+
|
|
191
|
+
## Safety Notes
|
|
192
|
+
|
|
193
|
+
- All findings are reports only; no code changes made
|
|
194
|
+
- Review before proceeding to excavate or restore mode
|
|
195
|
+
"@ | Set-Content "$report_file" -Encoding UTF8
|
|
196
|
+
|
|
197
|
+
Write-Host "Report written: $report_file"
|
|
198
|
+
|
|
199
|
+
} elseif ($mode -eq "excavate") {
|
|
200
|
+
Write-Host "Running EXCAVATE for phase $current_phase..."
|
|
201
|
+
# Generate mock patches (no file changes)
|
|
202
|
+
New-Item -ItemType Directory -Force -Path "$ARCHAEOLOGY_DIR/patches" | Out-Null
|
|
203
|
+
$patch_file = "$ARCHAEOLOGY_DIR/patches/expedition${phase_number}-mock.patch"
|
|
204
|
+
@"
|
|
205
|
+
# Patch handoff for $current_phase
|
|
206
|
+
# Hermes does not generate code changes from this hook.
|
|
207
|
+
# Run the matching OpenCode prompt, review the diff, then restore manually.
|
|
208
|
+
"@ | Set-Content "$patch_file" -Encoding UTF8
|
|
209
|
+
Write-Host "Mock patch written: $patch_file"
|
|
210
|
+
|
|
211
|
+
} else {
|
|
212
|
+
Block-Session -Reason "unknown mode: $mode" -Message "Unknown Hermes mode: $mode"
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# Update session: mark phase complete, advance to next
|
|
216
|
+
$session = Get-Content "$SESSION_FILE" -Raw | ConvertFrom-Json
|
|
217
|
+
$completed = if ($session.PSObject.Properties["completed_phases"]) {
|
|
218
|
+
if ($session.completed_phases -and $session.completed_phases.Count -gt 0) {
|
|
219
|
+
($session.completed_phases -join ",") + ",$current_phase"
|
|
220
|
+
} else {
|
|
221
|
+
$current_phase
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
$current_phase
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
$next_phase = ""
|
|
228
|
+
if ($phase_idx -lt ($PHASES.Count - 1)) {
|
|
229
|
+
$next_phase = $PHASES[$phase_idx + 1]
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
$session.completed_phases = $completed -split ","
|
|
233
|
+
$session.current_phase = $next_phase
|
|
234
|
+
$session | ConvertTo-Json -Depth 10 | Set-Content "$SESSION_FILE" -Encoding UTF8
|
|
235
|
+
|
|
236
|
+
if ($next_phase) {
|
|
237
|
+
Write-Host ""
|
|
238
|
+
Write-Host "Phase $current_phase complete. Next: $next_phase"
|
|
239
|
+
Write-Host "STOP. Next cron run will execute phase: $next_phase"
|
|
240
|
+
} else {
|
|
241
|
+
Write-Host ""
|
|
242
|
+
Write-Host "=== ALL PHASES COMPLETE ==="
|
|
243
|
+
Write-Host "Final catalog: $ARCHAEOLOGY_DIR/FINAL_CATALOG.md"
|
|
244
|
+
$session = Get-Content "$SESSION_FILE" -Raw | ConvertFrom-Json
|
|
245
|
+
$session.status = "complete"
|
|
246
|
+
$session | ConvertTo-Json -Depth 10 | Set-Content "$SESSION_FILE" -Encoding UTF8
|
|
247
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Hermes agent expedition runner — execute one Code Archaeology phase per cron run
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
7
|
+
ARCHAEOLOGY_DIR="$REPO_ROOT/.archaeology"
|
|
8
|
+
SESSION_FILE="$ARCHAEOLOGY_DIR/session.json"
|
|
9
|
+
|
|
10
|
+
# Restore mode can execute operator-provided verification commands. Keep the
|
|
11
|
+
# authorization outside repository-controlled files so a malicious checkout
|
|
12
|
+
# cannot enable unattended command execution by shipping .archaeology state.
|
|
13
|
+
RESTORE_APPROVAL_ENV="HERMES_RESTORE_APPROVED"
|
|
14
|
+
|
|
15
|
+
mkdir -p "$ARCHAEOLOGY_DIR"
|
|
16
|
+
|
|
17
|
+
block_session() {
|
|
18
|
+
local reason="$1"
|
|
19
|
+
local message="${2:-$reason}"
|
|
20
|
+
echo "ERROR: $message" >&2
|
|
21
|
+
if command -v jq >/dev/null 2>&1 && [[ -f "$SESSION_FILE" ]]; then
|
|
22
|
+
write_session_jq --arg reason "$reason" \
|
|
23
|
+
'.status = "blocked" | .flags = (.flags // {}) | .flags.blocked_reason = $reason' || true
|
|
24
|
+
fi
|
|
25
|
+
exit 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
require_jq() {
|
|
29
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
30
|
+
echo "ERROR: jq is required for Hermes session management" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
write_session_jq() {
|
|
36
|
+
local tmp
|
|
37
|
+
tmp=$(mktemp "$ARCHAEOLOGY_DIR/session.json.XXXXXX")
|
|
38
|
+
if jq "$@" "$SESSION_FILE" > "$tmp"; then
|
|
39
|
+
chmod 600 "$tmp" 2>/dev/null || true
|
|
40
|
+
mv -f "$tmp" "$SESSION_FILE"
|
|
41
|
+
else
|
|
42
|
+
rm -f "$tmp"
|
|
43
|
+
return 1
|
|
44
|
+
fi
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
read_session_string() {
|
|
48
|
+
local key="$1"
|
|
49
|
+
if ! jq -er --arg key "$key" '.[$key] | strings | select(test("\\S"))' "$SESSION_FILE" 2>/dev/null; then
|
|
50
|
+
block_session "invalid session field: $key" "Invalid Hermes session field: $key"
|
|
51
|
+
fi
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
require_restore_approval() {
|
|
55
|
+
if [[ "${!RESTORE_APPROVAL_ENV:-}" != "1" ]]; then
|
|
56
|
+
block_session \
|
|
57
|
+
"restore mode requires ${RESTORE_APPROVAL_ENV}=1" \
|
|
58
|
+
"Hermes restore mode is disabled until the operator sets ${RESTORE_APPROVAL_ENV}=1 outside session.json"
|
|
59
|
+
fi
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
validate_branch_name() {
|
|
63
|
+
local branch="$1"
|
|
64
|
+
if [[ -z "$branch" || "$branch" == -* || "$branch" =~ [[:space:]] ]]; then
|
|
65
|
+
block_session "invalid branch_name" "Invalid Hermes branch_name: $branch"
|
|
66
|
+
fi
|
|
67
|
+
if ! git check-ref-format --branch "$branch" >/dev/null 2>&1; then
|
|
68
|
+
block_session "invalid branch_name" "Invalid Hermes branch_name: $branch"
|
|
69
|
+
fi
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
require_jq
|
|
73
|
+
|
|
74
|
+
# Phase definitions (fixed order)
|
|
75
|
+
PHASES=(
|
|
76
|
+
"site-survey"
|
|
77
|
+
"dead-code"
|
|
78
|
+
"legacy-removal"
|
|
79
|
+
"dependency-mapping"
|
|
80
|
+
"type-consolidation"
|
|
81
|
+
"type-hardening"
|
|
82
|
+
"dry-stratification"
|
|
83
|
+
"error-handling"
|
|
84
|
+
"artifact-cleaning"
|
|
85
|
+
"final-catalog"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Detect current phase from session file
|
|
89
|
+
current_phase=""
|
|
90
|
+
if [[ -f "$SESSION_FILE" ]]; then
|
|
91
|
+
if ! current_phase=$(jq -er '.current_phase // empty' "$SESSION_FILE" 2>/dev/null); then
|
|
92
|
+
block_session "invalid session.json" "Invalid Hermes session file: $SESSION_FILE"
|
|
93
|
+
fi
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
if [[ -z "$current_phase" ]]; then
|
|
97
|
+
current_phase="${PHASES[0]}"
|
|
98
|
+
# Initialize session
|
|
99
|
+
cat > "$SESSION_FILE" <<EOF
|
|
100
|
+
{
|
|
101
|
+
"runtime": "hermes",
|
|
102
|
+
"status": "running",
|
|
103
|
+
"current_phase": "$current_phase",
|
|
104
|
+
"completed_phases": [],
|
|
105
|
+
"mode": "survey",
|
|
106
|
+
"repo_path": ".",
|
|
107
|
+
"language": "typescript",
|
|
108
|
+
"test_command": "npm test",
|
|
109
|
+
"typecheck_command": "npx tsc --noEmit",
|
|
110
|
+
"branch_name": "refactor/archaeology",
|
|
111
|
+
"strict_mode": false,
|
|
112
|
+
"started_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
113
|
+
}
|
|
114
|
+
EOF
|
|
115
|
+
echo "Initialized Hermes session. Starting phase: $current_phase"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# Find phase index
|
|
119
|
+
phase_idx=0
|
|
120
|
+
phase_found=false
|
|
121
|
+
for i in "${!PHASES[@]}"; do
|
|
122
|
+
if [[ "${PHASES[$i]}" == "$current_phase" ]]; then
|
|
123
|
+
phase_idx=$i
|
|
124
|
+
phase_found=true
|
|
125
|
+
break
|
|
126
|
+
fi
|
|
127
|
+
done
|
|
128
|
+
|
|
129
|
+
if [[ "$phase_found" != true ]]; then
|
|
130
|
+
block_session "unknown phase: $current_phase" "Unknown Hermes phase: $current_phase"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
phase_number=$((phase_idx + 1))
|
|
134
|
+
total_phases=${#PHASES[@]}
|
|
135
|
+
|
|
136
|
+
echo "=== Code Archaeology Hermes Runner ==="
|
|
137
|
+
echo "Phase $phase_number/$total_phases: $current_phase"
|
|
138
|
+
echo "Mode: $(read_session_string mode)"
|
|
139
|
+
echo ""
|
|
140
|
+
|
|
141
|
+
# Run the phase
|
|
142
|
+
cd "$REPO_ROOT"
|
|
143
|
+
|
|
144
|
+
mode=$(read_session_string mode)
|
|
145
|
+
if [[ "$mode" == "restore" ]]; then
|
|
146
|
+
require_restore_approval
|
|
147
|
+
block_session \
|
|
148
|
+
"restore mode is not implemented in Hermes runner" \
|
|
149
|
+
"Hermes restore mode is not implemented. Use OpenCode restore after reviewing generated patches."
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# Create branch if needed
|
|
153
|
+
branch=$(read_session_string branch_name)
|
|
154
|
+
validate_branch_name "$branch"
|
|
155
|
+
git checkout -b "$branch" 2>/dev/null || git checkout "$branch"
|
|
156
|
+
|
|
157
|
+
# Execute phase based on mode
|
|
158
|
+
if [[ "$mode" == "survey" ]]; then
|
|
159
|
+
echo "Running SURVEY for phase $current_phase..."
|
|
160
|
+
# Generate reports only, no file changes
|
|
161
|
+
bash "$SCRIPT_DIR/../opencode/init.sh" 2>/dev/null || true
|
|
162
|
+
|
|
163
|
+
# Run phase-specific analysis
|
|
164
|
+
case "$current_phase" in
|
|
165
|
+
"site-survey")
|
|
166
|
+
echo "Generating site survey and baseline inventory..."
|
|
167
|
+
;;
|
|
168
|
+
"dead-code")
|
|
169
|
+
echo "Cataloging dead code, unused exports, unreachable functions..."
|
|
170
|
+
;;
|
|
171
|
+
"legacy-removal")
|
|
172
|
+
echo "Identifying legacy fallbacks, deprecated shims, compatibility layers..."
|
|
173
|
+
;;
|
|
174
|
+
"dependency-mapping")
|
|
175
|
+
echo "Mapping circular dependencies..."
|
|
176
|
+
;;
|
|
177
|
+
"type-consolidation")
|
|
178
|
+
echo "Finding duplicate type definitions..."
|
|
179
|
+
;;
|
|
180
|
+
"type-hardening")
|
|
181
|
+
echo "Identifying weak types..."
|
|
182
|
+
;;
|
|
183
|
+
"dry-stratification")
|
|
184
|
+
echo "Finding semantic duplication..."
|
|
185
|
+
;;
|
|
186
|
+
"error-handling")
|
|
187
|
+
echo "Reviewing error-handling patterns..."
|
|
188
|
+
;;
|
|
189
|
+
"artifact-cleaning")
|
|
190
|
+
echo "Identifying stale artifacts and documentation gaps..."
|
|
191
|
+
;;
|
|
192
|
+
"final-catalog")
|
|
193
|
+
echo "Generating final excavation catalog..."
|
|
194
|
+
;;
|
|
195
|
+
esac
|
|
196
|
+
|
|
197
|
+
# Write report
|
|
198
|
+
report_file="$ARCHAEOLOGY_DIR/expedition${phase_number}-report.md"
|
|
199
|
+
cat > "$report_file" <<EOF
|
|
200
|
+
# Expedition $phase_number: $current_phase
|
|
201
|
+
|
|
202
|
+
**Mode:** survey
|
|
203
|
+
**Phase:** $phase_number/$total_phases
|
|
204
|
+
**Runtime:** hermes
|
|
205
|
+
**Timestamp:** $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
206
|
+
|
|
207
|
+
## Findings
|
|
208
|
+
|
|
209
|
+
Hermes recorded this phase handoff. It does not perform autonomous static analysis in this hook.
|
|
210
|
+
|
|
211
|
+
## Recommendations
|
|
212
|
+
|
|
213
|
+
Run the matching Code Archaeology prompt with OpenCode, then keep this report with the reviewed findings.
|
|
214
|
+
|
|
215
|
+
## Safety Notes
|
|
216
|
+
|
|
217
|
+
- All findings are reports only; no code changes made
|
|
218
|
+
- Review before proceeding to excavate or restore mode
|
|
219
|
+
EOF
|
|
220
|
+
|
|
221
|
+
echo "Report written: $report_file"
|
|
222
|
+
|
|
223
|
+
elif [[ "$mode" == "excavate" ]]; then
|
|
224
|
+
echo "Running EXCAVATE for phase $current_phase..."
|
|
225
|
+
# Generate mock patches (no file changes)
|
|
226
|
+
mkdir -p "$ARCHAEOLOGY_DIR/patches"
|
|
227
|
+
patch_file="$ARCHAEOLOGY_DIR/patches/expedition${phase_number}-mock.patch"
|
|
228
|
+
echo "# Patch handoff for $current_phase" > "$patch_file"
|
|
229
|
+
echo "# Hermes does not generate code changes from this hook." >> "$patch_file"
|
|
230
|
+
echo "# Run the matching OpenCode prompt, review the diff, then restore manually." >> "$patch_file"
|
|
231
|
+
echo "Mock patch written: $patch_file"
|
|
232
|
+
|
|
233
|
+
else
|
|
234
|
+
block_session "unknown mode: $mode" "Unknown Hermes mode: $mode"
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
# Update session: mark phase complete, advance to next
|
|
238
|
+
completed=$(jq -r '.completed_phases | join(",")' "$SESSION_FILE")
|
|
239
|
+
if [[ -n "$completed" ]]; then
|
|
240
|
+
completed="$completed,$current_phase"
|
|
241
|
+
else
|
|
242
|
+
completed="$current_phase"
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
next_phase=""
|
|
246
|
+
if [[ $phase_idx -lt $((${#PHASES[@]} - 1)) ]]; then
|
|
247
|
+
next_phase="${PHASES[$((phase_idx + 1))]}"
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
write_session_jq --arg completed "$completed" --arg next "$next_phase" \
|
|
251
|
+
'.completed_phases = ($completed | split(",")) | .current_phase = $next'
|
|
252
|
+
|
|
253
|
+
if [[ -n "$next_phase" ]]; then
|
|
254
|
+
echo ""
|
|
255
|
+
echo "Phase $current_phase complete. Next: $next_phase"
|
|
256
|
+
echo "STOP. Next cron run will execute phase: $next_phase"
|
|
257
|
+
else
|
|
258
|
+
echo ""
|
|
259
|
+
echo "=== ALL PHASES COMPLETE ==="
|
|
260
|
+
echo "Final catalog: $ARCHAEOLOGY_DIR/FINAL_CATALOG.md"
|
|
261
|
+
write_session_jq '.status = "complete"'
|
|
262
|
+
fi
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Hermes agent setup - discover Hermes capabilities for Code Archaeology
|
|
2
|
+
$ErrorActionPreference = 'Stop'
|
|
3
|
+
|
|
4
|
+
$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
5
|
+
$REPO_ROOT = Resolve-Path "$SCRIPT_DIR/../.." | Select-Object -ExpandProperty Path
|
|
6
|
+
$ARCHAEOLOGY_DIR = "$REPO_ROOT/.archaeology"
|
|
7
|
+
|
|
8
|
+
New-Item -ItemType Directory -Force -Path "$ARCHAEOLOGY_DIR" | Out-Null
|
|
9
|
+
|
|
10
|
+
# Check if Hermes CLI is available
|
|
11
|
+
$HERMES_AVAILABLE = $false
|
|
12
|
+
try {
|
|
13
|
+
$null = Get-Command hermes -ErrorAction Stop
|
|
14
|
+
$HERMES_AVAILABLE = $true
|
|
15
|
+
} catch {}
|
|
16
|
+
|
|
17
|
+
# Check if we're running inside a Hermes session
|
|
18
|
+
$HERMES_ACTIVE = $false
|
|
19
|
+
if ($env:HERMES_SESSION_ID -or $env:HERMES_CWD -or $env:HERMES_PROVIDER) {
|
|
20
|
+
$HERMES_ACTIVE = $true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# Write Hermes runtime config
|
|
24
|
+
$hermesRuntime = @{
|
|
25
|
+
runtime = "hermes"
|
|
26
|
+
available = $HERMES_AVAILABLE
|
|
27
|
+
active_session = $HERMES_ACTIVE
|
|
28
|
+
max_concurrent = 1
|
|
29
|
+
dispatch_method = "cronjob"
|
|
30
|
+
phase_interval = "15m"
|
|
31
|
+
notes = "Code Archaeology runs one expedition phase per cron run. Test gates between phases."
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
$hermesRuntime | ConvertTo-Json -Depth 5 | Set-Content "$ARCHAEOLOGY_DIR/hermes-runtime.json" -Encoding UTF8
|
|
35
|
+
|
|
36
|
+
Write-Host "Hermes runtime configured for Code Archaeology:"
|
|
37
|
+
Write-Host " CLI available: $HERMES_AVAILABLE"
|
|
38
|
+
Write-Host " Active session: $HERMES_ACTIVE"
|
|
39
|
+
Write-Host " Max concurrent: 1 (one phase at a time)"
|
|
40
|
+
Write-Host " Phase interval: 15 minutes"
|
|
41
|
+
Write-Host " Config: $ARCHAEOLOGY_DIR/hermes-runtime.json"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Hermes agent setup — discover Hermes capabilities for Code Archaeology
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
7
|
+
ARCHAEOLOGY_DIR="$REPO_ROOT/.archaeology"
|
|
8
|
+
|
|
9
|
+
mkdir -p "$ARCHAEOLOGY_DIR"
|
|
10
|
+
|
|
11
|
+
# Check if Hermes CLI is available
|
|
12
|
+
HERMES_AVAILABLE=false
|
|
13
|
+
if command -v hermes &>/dev/null; then
|
|
14
|
+
HERMES_AVAILABLE=true
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Check if we're running inside a Hermes session
|
|
18
|
+
HERMES_ACTIVE=false
|
|
19
|
+
if [[ -n "${HERMES_SESSION_ID:-}" || -n "${HERMES_CWD:-}" || -n "${HERMES_PROVIDER:-}" ]]; then
|
|
20
|
+
HERMES_ACTIVE=true
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Write Hermes runtime config
|
|
24
|
+
cat > "$ARCHAEOLOGY_DIR/hermes-runtime.json" <<EOF
|
|
25
|
+
{
|
|
26
|
+
"runtime": "hermes",
|
|
27
|
+
"available": $HERMES_AVAILABLE,
|
|
28
|
+
"active_session": $HERMES_ACTIVE,
|
|
29
|
+
"max_concurrent": 1,
|
|
30
|
+
"dispatch_method": "cronjob",
|
|
31
|
+
"phase_interval": "15m",
|
|
32
|
+
"notes": "Code Archaeology runs one expedition phase per cron run. Test gates between phases."
|
|
33
|
+
}
|
|
34
|
+
EOF
|
|
35
|
+
|
|
36
|
+
echo "Hermes runtime configured for Code Archaeology:"
|
|
37
|
+
echo " CLI available: $HERMES_AVAILABLE"
|
|
38
|
+
echo " Active session: $HERMES_ACTIVE"
|
|
39
|
+
echo " Max concurrent: 1 (one phase at a time)"
|
|
40
|
+
echo " Phase interval: 15 minutes"
|
|
41
|
+
echo " Config: $ARCHAEOLOGY_DIR/hermes-runtime.json"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Initialize .archaeology/ directory for Code Archaeology.
|
|
2
|
+
|
|
3
|
+
$ErrorActionPreference = 'Stop'
|
|
4
|
+
|
|
5
|
+
$ARCHAEOLOGY_DIR = ".archaeology"
|
|
6
|
+
$SESSION_FILE = "$ARCHAEOLOGY_DIR/session.json"
|
|
7
|
+
|
|
8
|
+
$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
9
|
+
$RELEASE_ROOT = Resolve-Path "$SCRIPT_DIR/../.." | Select-Object -ExpandProperty Path
|
|
10
|
+
if (Test-Path "$RELEASE_ROOT/VERSION") {
|
|
11
|
+
$PLUGIN_VERSION = (Get-Content "$RELEASE_ROOT/VERSION" -Raw).Trim()
|
|
12
|
+
} else {
|
|
13
|
+
$PLUGIN_VERSION = "dev"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
$REPO_ROOT = $null
|
|
17
|
+
try {
|
|
18
|
+
$REPO_ROOT = (git rev-parse --show-toplevel 2>$null).Trim()
|
|
19
|
+
} catch {
|
|
20
|
+
Write-Error "Error: not inside a git repository"
|
|
21
|
+
exit 1
|
|
22
|
+
}
|
|
23
|
+
Set-Location "$REPO_ROOT"
|
|
24
|
+
|
|
25
|
+
Write-Host "Code Archaeology v${PLUGIN_VERSION} initializing..."
|
|
26
|
+
|
|
27
|
+
New-Item -ItemType Directory -Force -Path "$ARCHAEOLOGY_DIR" | Out-Null
|
|
28
|
+
New-Item -ItemType Directory -Force -Path "$ARCHAEOLOGY_DIR/patches" | Out-Null
|
|
29
|
+
|
|
30
|
+
# Initialize session.json
|
|
31
|
+
if (!(Test-Path "$SESSION_FILE")) {
|
|
32
|
+
$NOW = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
33
|
+
$SESSION_ID = "archaeology-$([DateTimeOffset]::UtcNow.ToUnixTimeSeconds())-$PID"
|
|
34
|
+
$BASELINE_COMMIT = "unknown"
|
|
35
|
+
try {
|
|
36
|
+
$BASELINE_COMMIT = (git rev-parse HEAD 2>$null).Trim()
|
|
37
|
+
} catch {}
|
|
38
|
+
|
|
39
|
+
$session = @{
|
|
40
|
+
version = 1
|
|
41
|
+
plugin_version = $PLUGIN_VERSION
|
|
42
|
+
session_id = $SESSION_ID
|
|
43
|
+
config = @{
|
|
44
|
+
repo_path = "."
|
|
45
|
+
language = "typescript"
|
|
46
|
+
mode = "survey"
|
|
47
|
+
strict_mode = $false
|
|
48
|
+
test_command = "npm test"
|
|
49
|
+
typecheck_command = "npx tsc --noEmit"
|
|
50
|
+
branch_name = "refactor/archaeology"
|
|
51
|
+
}
|
|
52
|
+
started_at = $NOW
|
|
53
|
+
updated_at = $NOW
|
|
54
|
+
expeditions = @(
|
|
55
|
+
@{phase = "survey"; name = "Site Survey & Baseline"; status = "pending"; findings_count = 0}
|
|
56
|
+
@{phase = "dead_code"; name = "Dead Code Excavation"; status = "pending"; findings_count = 0}
|
|
57
|
+
@{phase = "legacy"; name = "Legacy Stratum Removal"; status = "pending"; findings_count = 0}
|
|
58
|
+
@{phase = "dependencies"; name = "Circular Dependency Cartography"; status = "pending"; findings_count = 0}
|
|
59
|
+
@{phase = "types_consolidate"; name = "Type Catalog Consolidation"; status = "pending"; findings_count = 0}
|
|
60
|
+
@{phase = "types_harden"; name = "Type Restoration & Hardening"; status = "pending"; findings_count = 0}
|
|
61
|
+
@{phase = "dry"; name = "DRY Stratification"; status = "pending"; findings_count = 0}
|
|
62
|
+
@{phase = "errors"; name = "Error Handling Stratigraphy"; status = "pending"; findings_count = 0}
|
|
63
|
+
@{phase = "polish"; name = "Artifact Cleaning & Documentation"; status = "pending"; findings_count = 0}
|
|
64
|
+
@{phase = "final_verify"; name = "Site Preservation & Final Catalog"; status = "pending"; findings_count = 0}
|
|
65
|
+
)
|
|
66
|
+
total_findings = 0
|
|
67
|
+
auto_fixable_count = 0
|
|
68
|
+
baseline_commit = $BASELINE_COMMIT
|
|
69
|
+
completed = $false
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
$session | ConvertTo-Json -Depth 10 | Set-Content "$SESSION_FILE" -Encoding UTF8
|
|
73
|
+
Write-Host "Initialized $SESSION_FILE"
|
|
74
|
+
} else {
|
|
75
|
+
$NOW = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
76
|
+
$session = Get-Content "$SESSION_FILE" -Raw | ConvertFrom-Json
|
|
77
|
+
$session.updated_at = $NOW
|
|
78
|
+
$session.plugin_version = $PLUGIN_VERSION
|
|
79
|
+
$session | ConvertTo-Json -Depth 10 | Set-Content "$SESSION_FILE" -Encoding UTF8
|
|
80
|
+
Write-Host "Refreshed $SESSION_FILE"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Write-Host "Code Archaeology workspace ready at $ARCHAEOLOGY_DIR"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Move current working tree changes into a recoverable stash.
|
|
2
|
+
# Usage: revert-phase.ps1 [phase-name]
|
|
3
|
+
|
|
4
|
+
$ErrorActionPreference = 'Stop'
|
|
5
|
+
|
|
6
|
+
$PHASE = if ($args[0]) { $args[0] } else { "unknown" }
|
|
7
|
+
|
|
8
|
+
Write-Host "[$PHASE] Reverting changes due to failure..."
|
|
9
|
+
|
|
10
|
+
git stash push -m "code-archaeology-revert-$PHASE" --include-untracked *> $null
|
|
11
|
+
|
|
12
|
+
Write-Host "[$PHASE] Changes preserved in git stash"
|