@codeyam/codeyam-cli 0.1.0-staging.ae0de75 → 0.1.0-staging.b8f4f94
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/analyzer-template/.build-info.json +7 -7
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +3 -3
- package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +13 -7
- package/analyzer-template/packages/analyze/src/lib/asts/index.ts +7 -2
- package/codeyam-cli/src/__tests__/memory-scripts/filter-session.test.js +196 -0
- package/codeyam-cli/src/__tests__/memory-scripts/filter-session.test.js.map +1 -0
- package/codeyam-cli/src/__tests__/memory-scripts/read-json-field.test.js +114 -0
- package/codeyam-cli/src/__tests__/memory-scripts/read-json-field.test.js.map +1 -0
- package/codeyam-cli/src/__tests__/memory-scripts/ripgrep-fallback.test.js +149 -0
- package/codeyam-cli/src/__tests__/memory-scripts/ripgrep-fallback.test.js.map +1 -0
- package/codeyam-cli/src/commands/default.js +3 -46
- package/codeyam-cli/src/commands/default.js.map +1 -1
- package/codeyam-cli/src/commands/editor.js +254 -66
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +234 -0
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js +12 -1
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +29 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +1217 -0
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -0
- package/codeyam-cli/src/utils/backgroundServer.js +2 -2
- package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +45 -5
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorJournal.js +7 -0
- package/codeyam-cli/src/utils/editorJournal.js.map +1 -1
- package/codeyam-cli/src/utils/entityChangeStatus.js +255 -0
- package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -0
- package/codeyam-cli/src/utils/install-skills.js +1 -1
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +5 -1
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/Terminal-nZNBALox.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-file-diff-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-file-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor-DTwKl1Xu.js +10 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-D8ILZMR0.js → entity._sha.scenarios._scenarioId.dev-DjACbfdI.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/git-CdN8sCqs.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-h1-1oFYI.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/index-yHOVb4rc.js +15 -0
- package/codeyam-cli/src/webserver/build/client/assets/manifest-9422aeab.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{memory-FweZHj5U.js → memory-Dg0mvYrI.js} +4 -1
- package/codeyam-cli/src/webserver/build/client/assets/{root-DUKqhFlb.js → root-BzQgN2ff.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{index-BLhjL9Xi.js → index-Bh_pNxNA.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-Bqr22tlO.js +367 -0
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/src/webserver/server.js +32 -6
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +23 -4
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/editor-step-hook.py +53 -6
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +10 -5
- package/codeyam-cli/templates/skills/codeyam-memory/SKILL.md +10 -10
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.mjs +139 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.mjs +52 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/lib/read-json-field.mjs +61 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/lib/ripgrep-fallback.mjs +155 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.mjs +13 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter-session.mjs +95 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.mjs +160 -0
- package/package.json +10 -10
- package/packages/analyze/src/lib/ProjectAnalyzer.js +10 -4
- package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
- package/packages/analyze/src/lib/asts/index.js +4 -2
- package/packages/analyze/src/lib/asts/index.js.map +1 -1
- package/scripts/npm-post-install.cjs +22 -0
- package/codeyam-cli/src/webserver/build/client/assets/Terminal-wkqC0AQk.js +0 -41
- package/codeyam-cli/src/webserver/build/client/assets/editor-CdjF_fX6.js +0 -8
- package/codeyam-cli/src/webserver/build/client/assets/git-CFCTYk9I.js +0 -15
- package/codeyam-cli/src/webserver/build/client/assets/globals-B17TBSS6.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-b8fd6b07.js +0 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-DyMuI5mU.js +0 -363
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.sh +0 -108
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.sh +0 -69
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.sh +0 -12
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter.jq +0 -45
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.sh +0 -139
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Gathers deprecation signals from package.json, source markers, and git history.
|
|
3
|
-
# Outputs structured JSON to /tmp/codeyam-memory/deprecated-scan.json
|
|
4
|
-
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
OUTPUT_DIR="/tmp/codeyam-memory"
|
|
8
|
-
OUTPUT_FILE="$OUTPUT_DIR/deprecated-scan.json"
|
|
9
|
-
mkdir -p "$OUTPUT_DIR"
|
|
10
|
-
|
|
11
|
-
# --- Dependency scan ---
|
|
12
|
-
# Collect all dependency names from package.json files (root + workspace packages)
|
|
13
|
-
deps_json="[]"
|
|
14
|
-
while IFS= read -r pkg_file; do
|
|
15
|
-
# Extract dependency names from both dependencies and devDependencies
|
|
16
|
-
file_deps=$(jq -r '(.dependencies // {} | keys[]) , (.devDependencies // {} | keys[])' "$pkg_file" 2>/dev/null || true)
|
|
17
|
-
if [ -n "$file_deps" ]; then
|
|
18
|
-
deps_json=$(echo "$deps_json" | jq --arg d "$file_deps" '. + ($d | split("\n") | map(select(. != "")))')
|
|
19
|
-
fi
|
|
20
|
-
done < <(find . -name "package.json" -not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/.next/*" 2>/dev/null)
|
|
21
|
-
|
|
22
|
-
# Deduplicate
|
|
23
|
-
deps_json=$(echo "$deps_json" | jq 'unique')
|
|
24
|
-
|
|
25
|
-
# --- Explicit marker scan ---
|
|
26
|
-
markers_json="[]"
|
|
27
|
-
marker_output=$(rg -n "@deprecated|// legacy|// deprecated|// old approach|TODO.*deprecat|FIXME.*deprecat" \
|
|
28
|
-
--type ts --type js \
|
|
29
|
-
--glob '!node_modules' --glob '!dist' --glob '!build' --glob '!.next' \
|
|
30
|
-
-C 2 2>/dev/null || true)
|
|
31
|
-
|
|
32
|
-
if [ -n "$marker_output" ]; then
|
|
33
|
-
# Parse ripgrep output into JSON entries
|
|
34
|
-
markers_json=$(echo "$marker_output" | awk -F: '
|
|
35
|
-
/^[^-].*:[0-9]+:/ {
|
|
36
|
-
file = $1
|
|
37
|
-
line = $2
|
|
38
|
-
# Rejoin the rest as text (handles colons in content)
|
|
39
|
-
text = ""
|
|
40
|
-
for (i = 3; i <= NF; i++) {
|
|
41
|
-
text = (text == "" ? $i : text ":" $i)
|
|
42
|
-
}
|
|
43
|
-
gsub(/^[ \t]+/, "", text)
|
|
44
|
-
gsub(/"/, "\\\"", text)
|
|
45
|
-
gsub(/\t/, " ", text)
|
|
46
|
-
printf "{\"file\":\"%s\",\"line\":%s,\"text\":\"%s\"},\n", file, line, text
|
|
47
|
-
}
|
|
48
|
-
' | sed '$ s/,$//' | awk 'BEGIN{print "["} {print} END{print "]"}')
|
|
49
|
-
|
|
50
|
-
# Validate JSON — fall back to empty array if malformed
|
|
51
|
-
if ! echo "$markers_json" | jq empty 2>/dev/null; then
|
|
52
|
-
markers_json="[]"
|
|
53
|
-
fi
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# --- Git recency comparison ---
|
|
57
|
-
# Extract all import lines from git patches in two passes (recent + old), then count per-dep
|
|
58
|
-
recency_json="{}"
|
|
59
|
-
dep_count=$(echo "$deps_json" | jq length)
|
|
60
|
-
|
|
61
|
-
if [ "$dep_count" -gt 0 ]; then
|
|
62
|
-
RECENT_IMPORTS="$OUTPUT_DIR/recent-imports.txt"
|
|
63
|
-
OLD_IMPORTS="$OUTPUT_DIR/old-imports.txt"
|
|
64
|
-
|
|
65
|
-
# Single git log pass per time window — extract only import lines
|
|
66
|
-
git log --since="3 months ago" -p -- '*.ts' '*.tsx' '*.js' '*.jsx' 2>/dev/null \
|
|
67
|
-
| grep -oE "from ['\"][^'\"]+['\"]" > "$RECENT_IMPORTS" 2>/dev/null || true
|
|
68
|
-
git log --since="12 months ago" --until="3 months ago" -p -- '*.ts' '*.tsx' '*.js' '*.jsx' 2>/dev/null \
|
|
69
|
-
| grep -oE "from ['\"][^'\"]+['\"]" > "$OLD_IMPORTS" 2>/dev/null || true
|
|
70
|
-
|
|
71
|
-
for dep in $(echo "$deps_json" | jq -r '.[]'); do
|
|
72
|
-
# Skip short names that would match too broadly
|
|
73
|
-
if [ ${#dep} -lt 3 ]; then
|
|
74
|
-
continue
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
recent=$(grep -c "from ['\"]${dep}" "$RECENT_IMPORTS" 2>/dev/null || true)
|
|
78
|
-
recent=${recent:-0}
|
|
79
|
-
old=$(grep -c "from ['\"]${dep}" "$OLD_IMPORTS" 2>/dev/null || true)
|
|
80
|
-
old=${old:-0}
|
|
81
|
-
|
|
82
|
-
if [ "$recent" -gt 0 ] || [ "$old" -gt 0 ]; then
|
|
83
|
-
recency_json=$(echo "$recency_json" | jq \
|
|
84
|
-
--arg dep "$dep" \
|
|
85
|
-
--argjson recent "$recent" \
|
|
86
|
-
--argjson old "$old" \
|
|
87
|
-
'. + {($dep): {"recent_imports": $recent, "old_imports": $old}}')
|
|
88
|
-
fi
|
|
89
|
-
done
|
|
90
|
-
|
|
91
|
-
rm -f "$RECENT_IMPORTS" "$OLD_IMPORTS"
|
|
92
|
-
fi
|
|
93
|
-
|
|
94
|
-
# --- Assemble final output ---
|
|
95
|
-
jq -n \
|
|
96
|
-
--argjson dependencies "$deps_json" \
|
|
97
|
-
--argjson explicit_markers "$markers_json" \
|
|
98
|
-
--argjson git_recency "$recency_json" \
|
|
99
|
-
'{
|
|
100
|
-
dependencies: $dependencies,
|
|
101
|
-
explicit_markers: $explicit_markers,
|
|
102
|
-
git_recency: $git_recency
|
|
103
|
-
}' > "$OUTPUT_FILE"
|
|
104
|
-
|
|
105
|
-
echo "Deprecated pattern scan complete: $OUTPUT_FILE"
|
|
106
|
-
echo " Dependencies found: $(echo "$deps_json" | jq length)"
|
|
107
|
-
echo " Explicit markers found: $(echo "$markers_json" | jq length)"
|
|
108
|
-
echo " Deps with git activity: $(echo "$recency_json" | jq 'length')"
|
package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.sh
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Indexes the project's public API surface by finding all exports.
|
|
3
|
-
# Outputs structured JSON to /tmp/codeyam-memory/exports-scan.json
|
|
4
|
-
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
OUTPUT_DIR="/tmp/codeyam-memory"
|
|
8
|
-
OUTPUT_FILE="$OUTPUT_DIR/exports-scan.json"
|
|
9
|
-
mkdir -p "$OUTPUT_DIR"
|
|
10
|
-
|
|
11
|
-
# Find all export declarations, excluding noise directories and .d.ts files
|
|
12
|
-
export_output=$(rg -n "^export (function|const|class|default|async function|type|interface|enum)" \
|
|
13
|
-
--type ts --type js \
|
|
14
|
-
--glob '!node_modules' --glob '!dist' --glob '!build' --glob '!.next' \
|
|
15
|
-
--glob '!*.d.ts' --glob '!*.map' \
|
|
16
|
-
2>/dev/null || true)
|
|
17
|
-
|
|
18
|
-
if [ -z "$export_output" ]; then
|
|
19
|
-
# No exports found — output empty structure
|
|
20
|
-
jq -n '{files: {}, stats: {total_files: 0, total_exports: 0}}' > "$OUTPUT_FILE"
|
|
21
|
-
echo "Export scan complete: $OUTPUT_FILE (no exports found)"
|
|
22
|
-
exit 0
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# Parse ripgrep output into JSON grouped by file
|
|
26
|
-
# Format: file:line:text
|
|
27
|
-
files_json=$(echo "$export_output" | awk -F: '
|
|
28
|
-
{
|
|
29
|
-
file = $1
|
|
30
|
-
line = $2
|
|
31
|
-
# Rejoin rest as text
|
|
32
|
-
text = ""
|
|
33
|
-
for (i = 3; i <= NF; i++) {
|
|
34
|
-
text = (text == "" ? $i : text ":" $i)
|
|
35
|
-
}
|
|
36
|
-
# Trim leading whitespace
|
|
37
|
-
gsub(/^[ \t]+/, "", text)
|
|
38
|
-
# Escape quotes for JSON
|
|
39
|
-
gsub(/"/, "\\\"", text)
|
|
40
|
-
gsub(/\t/, " ", text)
|
|
41
|
-
printf "%s\t%s\t%s\n", file, line, text
|
|
42
|
-
}' | jq -Rsn '
|
|
43
|
-
[inputs | split("\n") | .[] | select(. != "") |
|
|
44
|
-
split("\t") | select(length >= 3) |
|
|
45
|
-
{file: .[0], line: (.[1] | tonumber), text: .[2]}
|
|
46
|
-
] |
|
|
47
|
-
group_by(.file) |
|
|
48
|
-
reduce .[] as $group ({};
|
|
49
|
-
. + {($group[0].file): [$group[] | {line: .line, text: .text}]}
|
|
50
|
-
)
|
|
51
|
-
')
|
|
52
|
-
|
|
53
|
-
# Compute stats
|
|
54
|
-
total_files=$(echo "$files_json" | jq 'length')
|
|
55
|
-
total_exports=$(echo "$files_json" | jq '[.[] | length] | add // 0')
|
|
56
|
-
|
|
57
|
-
# Assemble final output
|
|
58
|
-
jq -n \
|
|
59
|
-
--argjson files "$files_json" \
|
|
60
|
-
--argjson total_files "$total_files" \
|
|
61
|
-
--argjson total_exports "$total_exports" \
|
|
62
|
-
'{
|
|
63
|
-
files: $files,
|
|
64
|
-
stats: {total_files: $total_files, total_exports: $total_exports}
|
|
65
|
-
}' > "$OUTPUT_FILE"
|
|
66
|
-
|
|
67
|
-
echo "Export scan complete: $OUTPUT_FILE"
|
|
68
|
-
echo " Files with exports: $total_files"
|
|
69
|
-
echo " Total exports: $total_exports"
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Removes cached preprocessed session files.
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
|
|
5
|
-
OUTPUT_DIR="/tmp/cc-session-analysis"
|
|
6
|
-
|
|
7
|
-
if [ -d "$OUTPUT_DIR" ]; then
|
|
8
|
-
rm -rf "$OUTPUT_DIR"
|
|
9
|
-
echo "Cleaned up $OUTPUT_DIR"
|
|
10
|
-
else
|
|
11
|
-
echo "Nothing to clean up ($OUTPUT_DIR does not exist)"
|
|
12
|
-
fi
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Session log filter for codeyam-memory skill.
|
|
2
|
-
# Strips progress/system/queue records, compresses tool inputs to 300 chars,
|
|
3
|
-
# tool results to 500 chars, thinking to 1000 chars. Output is compact JSONL.
|
|
4
|
-
|
|
5
|
-
select(.type == "user" or .type == "assistant") |
|
|
6
|
-
if .type == "assistant" then
|
|
7
|
-
{
|
|
8
|
-
type: "assistant",
|
|
9
|
-
ts: .timestamp,
|
|
10
|
-
content: [
|
|
11
|
-
(.message.content[]? |
|
|
12
|
-
if .type == "text" then
|
|
13
|
-
{t: "text", text}
|
|
14
|
-
elif .type == "thinking" then
|
|
15
|
-
{t: "think", thinking: (.thinking // "" | if length > 1000 then (.[0:1000] + "...[truncated]") else . end)}
|
|
16
|
-
elif .type == "tool_use" then
|
|
17
|
-
{t: "tool", name, input: (.input | tostring | if length > 300 then .[0:300] + "..." else . end)}
|
|
18
|
-
else empty
|
|
19
|
-
end
|
|
20
|
-
)
|
|
21
|
-
]
|
|
22
|
-
}
|
|
23
|
-
elif .type == "user" then
|
|
24
|
-
{
|
|
25
|
-
type: "user",
|
|
26
|
-
ts: .timestamp,
|
|
27
|
-
content: (
|
|
28
|
-
if (.message.content | type) == "string" then
|
|
29
|
-
.message.content
|
|
30
|
-
elif (.message.content | type) == "array" then
|
|
31
|
-
[.message.content[]? |
|
|
32
|
-
if .type == "tool_result" then
|
|
33
|
-
{t: "result", id: .tool_use_id, err: (.is_error // false),
|
|
34
|
-
content: (.content | tostring | if length > 500 then .[0:500] + "...[truncated]" else . end)}
|
|
35
|
-
else
|
|
36
|
-
{t: "msg", text: (. | tostring | if length > 500 then .[0:500] + "...[truncated]" else . end)}
|
|
37
|
-
end
|
|
38
|
-
]
|
|
39
|
-
else
|
|
40
|
-
(.message.content | tostring | if length > 500 then .[0:500] + "...[truncated]" else . end)
|
|
41
|
-
end
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
else empty
|
|
45
|
-
end
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Preprocesses Claude Code session logs for the codeyam-memory skill.
|
|
3
|
-
#
|
|
4
|
-
# - Finds JSONL session files (>=10KB, last 30 days)
|
|
5
|
-
# - Excludes the current active session
|
|
6
|
-
# - Runs jq filter for 5-50x compression
|
|
7
|
-
# - Caches results in /tmp/cc-session-analysis/
|
|
8
|
-
# - Prints filtered file paths to stdout (one per line)
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
-
FILTER="$SCRIPT_DIR/filter.jq"
|
|
13
|
-
OUTPUT_DIR="/tmp/cc-session-analysis"
|
|
14
|
-
MIN_SIZE=10240 # 10KB
|
|
15
|
-
MAX_AGE_DAYS=30
|
|
16
|
-
MAX_SESSIONS=30 # Diminishing returns past this; keeps agent fleet manageable
|
|
17
|
-
JQ_TIMEOUT=30 # Seconds — skip files that take longer than this to filter
|
|
18
|
-
|
|
19
|
-
# --- Dependency check ---
|
|
20
|
-
if ! command -v jq &>/dev/null; then
|
|
21
|
-
echo "Error: jq is required but not installed." >&2
|
|
22
|
-
echo " macOS: brew install jq" >&2
|
|
23
|
-
echo " Ubuntu: sudo apt-get install jq" >&2
|
|
24
|
-
echo " Other: https://jqlang.github.io/jq/download/" >&2
|
|
25
|
-
exit 1
|
|
26
|
-
fi
|
|
27
|
-
|
|
28
|
-
# --- Compute Claude project directory ---
|
|
29
|
-
PROJECT_DIR="$PWD"
|
|
30
|
-
# Claude Code replaces both / and . with - in the project hash
|
|
31
|
-
PROJECT_HASH=$(echo "$PROJECT_DIR" | sed 's|[/.]|-|g')
|
|
32
|
-
SESSION_DIR="$HOME/.claude/projects/$PROJECT_HASH"
|
|
33
|
-
|
|
34
|
-
if [ ! -d "$SESSION_DIR" ]; then
|
|
35
|
-
echo "No Claude session directory found at $SESSION_DIR" >&2
|
|
36
|
-
echo "This project may not have any Claude Code session history." >&2
|
|
37
|
-
exit 0
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
# --- Find session files ---
|
|
41
|
-
mkdir -p "$OUTPUT_DIR"
|
|
42
|
-
|
|
43
|
-
# Collect eligible session files: >=10KB, modified within last 30 days, .jsonl extension
|
|
44
|
-
# Sort by mtime (newest first) so the cap keeps the most recent sessions
|
|
45
|
-
ALL_SESSIONS=()
|
|
46
|
-
while IFS= read -r file; do
|
|
47
|
-
ALL_SESSIONS+=("$file")
|
|
48
|
-
done < <(find "$SESSION_DIR" -maxdepth 1 -name "*.jsonl" -size +"$MIN_SIZE"c -mtime -"$MAX_AGE_DAYS" -print0 2>/dev/null \
|
|
49
|
-
| xargs -0 stat -f "%m %N" 2>/dev/null \
|
|
50
|
-
| sort -rn \
|
|
51
|
-
| awk '{print $2}')
|
|
52
|
-
|
|
53
|
-
if [ ${#ALL_SESSIONS[@]} -eq 0 ]; then
|
|
54
|
-
exit 0
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
# Cap to most recent N sessions
|
|
58
|
-
SESSIONS=("${ALL_SESSIONS[@]:0:$MAX_SESSIONS}")
|
|
59
|
-
if [ ${#ALL_SESSIONS[@]} -gt "$MAX_SESSIONS" ]; then
|
|
60
|
-
echo "Note: ${#ALL_SESSIONS[@]} sessions found, capping to $MAX_SESSIONS most recent" >&2
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
# --- Exclude current active session ---
|
|
64
|
-
# The newest file by mtime is likely the current session.
|
|
65
|
-
# If CLAUDE_SESSION_ID is set, use that for a more precise match.
|
|
66
|
-
NEWEST_FILE=""
|
|
67
|
-
NEWEST_MTIME=0
|
|
68
|
-
for file in "${SESSIONS[@]}"; do
|
|
69
|
-
mtime=$(stat -f "%m" "$file" 2>/dev/null || stat -c "%Y" "$file" 2>/dev/null || echo "0")
|
|
70
|
-
if [ "$mtime" -gt "$NEWEST_MTIME" ]; then
|
|
71
|
-
NEWEST_MTIME="$mtime"
|
|
72
|
-
NEWEST_FILE="$file"
|
|
73
|
-
fi
|
|
74
|
-
done
|
|
75
|
-
|
|
76
|
-
ACTIVE_SESSION=""
|
|
77
|
-
if [ -n "${CLAUDE_SESSION_ID:-}" ]; then
|
|
78
|
-
for file in "${SESSIONS[@]}"; do
|
|
79
|
-
if [[ "$(basename "$file" .jsonl)" == "$CLAUDE_SESSION_ID" ]]; then
|
|
80
|
-
ACTIVE_SESSION="$file"
|
|
81
|
-
break
|
|
82
|
-
fi
|
|
83
|
-
done
|
|
84
|
-
fi
|
|
85
|
-
# Fall back to newest file if no env var match
|
|
86
|
-
if [ -z "$ACTIVE_SESSION" ]; then
|
|
87
|
-
ACTIVE_SESSION="$NEWEST_FILE"
|
|
88
|
-
fi
|
|
89
|
-
|
|
90
|
-
# --- Process each session ---
|
|
91
|
-
for file in "${SESSIONS[@]}"; do
|
|
92
|
-
# Skip active session
|
|
93
|
-
if [ "$file" = "$ACTIVE_SESSION" ]; then
|
|
94
|
-
continue
|
|
95
|
-
fi
|
|
96
|
-
|
|
97
|
-
uuid=$(basename "$file" .jsonl)
|
|
98
|
-
filtered="$OUTPUT_DIR/${uuid}.filtered.jsonl"
|
|
99
|
-
|
|
100
|
-
# Check cache: skip if filtered file exists and has a valid sentinel
|
|
101
|
-
if [ -f "$filtered" ]; then
|
|
102
|
-
first_line=$(head -1 "$filtered" 2>/dev/null || true)
|
|
103
|
-
if echo "$first_line" | jq -e '.processed_at' &>/dev/null; then
|
|
104
|
-
echo "$filtered"
|
|
105
|
-
continue
|
|
106
|
-
fi
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
# Run jq filter
|
|
110
|
-
source_size=$(stat -f "%z" "$file" 2>/dev/null || stat -c "%s" "$file" 2>/dev/null || echo "0")
|
|
111
|
-
sentinel=$(printf '{"processed_at":"%s","source_size":%s}' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$source_size")
|
|
112
|
-
|
|
113
|
-
# Write sentinel as first line, then filtered content (with timeout)
|
|
114
|
-
jq_exit=0
|
|
115
|
-
{
|
|
116
|
-
echo "$sentinel"
|
|
117
|
-
if command -v timeout &>/dev/null; then
|
|
118
|
-
timeout "$JQ_TIMEOUT" jq -c -f "$FILTER" "$file" 2>/dev/null
|
|
119
|
-
elif command -v gtimeout &>/dev/null; then
|
|
120
|
-
gtimeout "$JQ_TIMEOUT" jq -c -f "$FILTER" "$file" 2>/dev/null
|
|
121
|
-
else
|
|
122
|
-
jq -c -f "$FILTER" "$file" 2>/dev/null
|
|
123
|
-
fi
|
|
124
|
-
} > "$filtered" || jq_exit=$?
|
|
125
|
-
|
|
126
|
-
if [ "$jq_exit" -eq 124 ]; then
|
|
127
|
-
echo "Warning: jq timed out after ${JQ_TIMEOUT}s on $(basename "$file") — skipping" >&2
|
|
128
|
-
rm -f "$filtered"
|
|
129
|
-
continue
|
|
130
|
-
fi
|
|
131
|
-
|
|
132
|
-
# Only output if the file has content beyond the sentinel
|
|
133
|
-
line_count=$(wc -l < "$filtered" | tr -d ' ')
|
|
134
|
-
if [ "$line_count" -gt 1 ]; then
|
|
135
|
-
echo "$filtered"
|
|
136
|
-
else
|
|
137
|
-
rm -f "$filtered"
|
|
138
|
-
fi
|
|
139
|
-
done
|