@onlooker-community/ecosystem 0.32.2 → 0.33.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/.claude-plugin/plugin.json +1 -1
- package/.release-please-manifest.json +9 -9
- package/CHANGELOG.md +19 -0
- package/package.json +1 -1
- package/plugins/archivist/scripts/lib/archivist-config.sh +10 -34
- package/plugins/assayer/.claude-plugin/plugin.json +1 -1
- package/plugins/assayer/CHANGELOG.md +7 -0
- package/plugins/assayer/scripts/lib/assayer-config.sh +39 -62
- package/plugins/bursar/.claude-plugin/plugin.json +1 -1
- package/plugins/bursar/CHANGELOG.md +12 -0
- package/plugins/bursar/scripts/hooks/bursar-session-end.sh +6 -5
- package/plugins/bursar/scripts/lib/bursar-config.sh +10 -43
- package/plugins/cartographer/scripts/lib/cartographer-config.sh +11 -29
- package/plugins/compass/scripts/lib/compass-config.sh +16 -46
- package/plugins/counsel/scripts/lib/counsel-config.sh +15 -46
- package/plugins/curator/.claude-plugin/plugin.json +1 -1
- package/plugins/curator/CHANGELOG.md +7 -0
- package/plugins/curator/scripts/lib/curator-config.sh +19 -44
- package/plugins/echo/scripts/lib/echo-config.sh +30 -59
- package/plugins/governor/scripts/lib/governor-config.sh +11 -41
- package/plugins/historian/scripts/lib/historian-config.sh +9 -33
- package/plugins/inspector/.claude-plugin/plugin.json +1 -1
- package/plugins/inspector/CHANGELOG.md +7 -0
- package/plugins/inspector/scripts/lib/inspector-config.sh +39 -63
- package/plugins/librarian/.claude-plugin/plugin.json +1 -1
- package/plugins/librarian/CHANGELOG.md +7 -0
- package/plugins/librarian/scripts/hooks/librarian-session-end.sh +38 -3
- package/plugins/librarian/scripts/lib/librarian-config.sh +10 -34
- package/plugins/lineage/.claude-plugin/plugin.json +1 -1
- package/plugins/lineage/CHANGELOG.md +7 -0
- package/plugins/lineage/scripts/lib/lineage-config.sh +17 -53
- package/plugins/scribe/.claude-plugin/plugin.json +1 -1
- package/plugins/scribe/CHANGELOG.md +7 -0
- package/plugins/scribe/scripts/lib/scribe-config.sh +17 -47
- package/plugins/tribunal/.claude-plugin/plugin.json +1 -1
- package/plugins/tribunal/CHANGELOG.md +7 -0
- package/plugins/tribunal/scripts/lib/tribunal-config.sh +25 -63
- package/plugins/warden/scripts/lib/warden-config.sh +17 -54
- package/scripts/lib/config-loader.sh +138 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "curator",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Maintenance layer for the user's typed auto-memory store. At every SessionStart, runs four cheap heuristic checks (date_decayed, path_broken, broken_index, orphaned_memory) against the memories at ~/.claude/projects/<encoded>/memory/ inside a wall-clock budget. Surfaces findings as a one-line pointer to /curator review; never edits the memory store directly. Builds on the Onlooker ecosystem plugin.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Onlooker Community",
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.2](https://github.com/onlooker-community/ecosystem/compare/curator-v0.2.1...curator-v0.2.2) (2026-08-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* restore config convenience functions & refactor execution plugins to shared loader ([#128](https://github.com/onlooker-community/ecosystem/issues/128)) ([4b3660c](https://github.com/onlooker-community/ecosystem/commit/4b3660c5a8b234187ec1e71c37b63e6a3d305c98))
|
|
9
|
+
|
|
3
10
|
## [0.2.1](https://github.com/onlooker-community/ecosystem/compare/curator-v0.2.0...curator-v0.2.1) (2026-08-01)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,60 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Config resolution for
|
|
2
|
+
# Config resolution for curator.
|
|
3
3
|
#
|
|
4
|
-
# Reads
|
|
4
|
+
# Uses the shared config loader from ecosystem. Reads five layers, latest wins:
|
|
5
5
|
# 1. plugins/curator/config.json (defaults shipped with the plugin)
|
|
6
6
|
# 2. ~/.claude/settings.json
|
|
7
|
-
# 3.
|
|
7
|
+
# 3. ~/.claude/settings.local.json (local overrides user)
|
|
8
|
+
# 4. <repo>/.claude/settings.json
|
|
9
|
+
# 5. <repo>/.claude/settings.local.json (local overrides project)
|
|
8
10
|
#
|
|
9
11
|
# Exposes:
|
|
10
|
-
# curator_config_load <repo_root> # populates
|
|
12
|
+
# curator_config_load <repo_root> # populates _curator_CONFIG (JSON)
|
|
11
13
|
# curator_config_get <jq-path> # echoes string value (empty if unset)
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
+
# curator_config_get_json <jq-path> # echoes JSON value (null if unset)
|
|
15
|
+
|
|
16
|
+
# shellcheck source=../../../scripts/lib/config-loader.sh
|
|
17
|
+
source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
_curator_CONFIG="{}"
|
|
16
20
|
|
|
17
21
|
curator_config_load() {
|
|
18
22
|
local repo_root="${1:-}"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
local merged="{}"
|
|
23
|
-
local file
|
|
24
|
-
|
|
25
|
-
file="${plugin_root}/config.json"
|
|
26
|
-
if [[ -f "$file" ]]; then
|
|
27
|
-
local defaults
|
|
28
|
-
defaults=$(jq '.' "$file" 2>/dev/null) || defaults="{}"
|
|
29
|
-
merged=$(jq -n --argjson a "$merged" --argjson b "$defaults" '$a * $b' 2>/dev/null) \
|
|
30
|
-
|| merged="$defaults"
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
for file in "${home_dir}/.claude/settings.json" "${repo_root}/.claude/settings.json"; do
|
|
34
|
-
[[ -n "$file" && -f "$file" ]] || continue
|
|
35
|
-
local overlay
|
|
36
|
-
overlay=$(jq '{ curator: (.curator // {}) }' "$file" 2>/dev/null) || continue
|
|
37
|
-
[[ -z "$overlay" ]] && continue
|
|
38
|
-
merged=$(jq -n --argjson a "$merged" --argjson b "$overlay" '
|
|
39
|
-
def deepmerge($a; $b):
|
|
40
|
-
if ($a|type) == "object" and ($b|type) == "object" then
|
|
41
|
-
reduce (($a|keys) + ($b|keys) | unique)[] as $k
|
|
42
|
-
({}; .[$k] = deepmerge($a[$k]; $b[$k]))
|
|
43
|
-
elif $b == null then $a
|
|
44
|
-
else $b end;
|
|
45
|
-
deepmerge($a; $b)
|
|
46
|
-
' 2>/dev/null) || true
|
|
47
|
-
done
|
|
48
|
-
|
|
49
|
-
_CURATOR_CONFIG="$merged"
|
|
23
|
+
config_load_plugin "curator" "$repo_root" "_curator_CONFIG"
|
|
24
|
+
return 0
|
|
50
25
|
}
|
|
51
26
|
|
|
52
27
|
curator_config_get() {
|
|
53
28
|
local path="$1"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
29
|
+
config_get "_curator_CONFIG" "${path}"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
curator_config_get_json() {
|
|
33
|
+
local path="$1"
|
|
34
|
+
config_get_json "_curator_CONFIG" "${path}"
|
|
60
35
|
}
|
|
@@ -1,100 +1,71 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Config
|
|
3
|
-
#
|
|
4
|
-
#
|
|
2
|
+
# Config resolution for echo.
|
|
3
|
+
#
|
|
4
|
+
# Uses the shared config loader from ecosystem. Reads five layers, latest wins:
|
|
5
|
+
# 1. plugins/echo/config.json (defaults shipped with the plugin)
|
|
6
|
+
# 2. ~/.claude/settings.json
|
|
7
|
+
# 3. ~/.claude/settings.local.json (local overrides user)
|
|
8
|
+
# 4. <repo>/.claude/settings.json
|
|
9
|
+
# 5. <repo>/.claude/settings.local.json (local overrides project)
|
|
10
|
+
#
|
|
11
|
+
# Exposes:
|
|
12
|
+
# echo_config_load <repo_root> # populates _echo_CONFIG (JSON)
|
|
13
|
+
# echo_config_get <jq-path> # echoes string value (empty if unset)
|
|
14
|
+
# echo_config_get_json <jq-path> # echoes JSON value (null if unset)
|
|
5
15
|
|
|
6
|
-
|
|
7
|
-
|
|
16
|
+
# shellcheck source=../../../scripts/lib/config-loader.sh
|
|
17
|
+
source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
|
|
18
|
+
|
|
19
|
+
_echo_CONFIG="{}"
|
|
8
20
|
|
|
9
21
|
echo_config_load() {
|
|
10
22
|
local repo_root="${1:-}"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
local plugin_config="${CLAUDE_PLUGIN_ROOT:-}/config.json"
|
|
14
|
-
if [[ -f "$plugin_config" ]]; then
|
|
15
|
-
_ECHO_PLUGIN_CONFIG_JSON=$(cat "$plugin_config" 2>/dev/null) || _ECHO_PLUGIN_CONFIG_JSON=""
|
|
16
|
-
fi
|
|
17
|
-
|
|
18
|
-
_ECHO_CONFIG_JSON=""
|
|
19
|
-
if [[ -n "$repo_root" ]]; then
|
|
20
|
-
local settings_file="${repo_root}/.claude/settings.json"
|
|
21
|
-
if [[ -f "$settings_file" ]]; then
|
|
22
|
-
local settings
|
|
23
|
-
settings=$(cat "$settings_file" 2>/dev/null) || settings=""
|
|
24
|
-
local echo_block
|
|
25
|
-
echo_block=$(printf '%s' "$settings" | jq -c '.echo // empty' 2>/dev/null) || echo_block=""
|
|
26
|
-
[[ -n "$echo_block" ]] && _ECHO_CONFIG_JSON="$echo_block"
|
|
27
|
-
fi
|
|
28
|
-
fi
|
|
23
|
+
config_load_plugin "echo" "$repo_root" "_echo_CONFIG"
|
|
24
|
+
return 0
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
# Get a single scalar value. Checks settings.json first, then plugin config.json.
|
|
32
27
|
echo_config_get() {
|
|
33
|
-
local
|
|
34
|
-
|
|
35
|
-
if [[ -n "$_ECHO_CONFIG_JSON" ]]; then
|
|
36
|
-
local val
|
|
37
|
-
val=$(printf '%s' "$_ECHO_CONFIG_JSON" | jq -r "${key} // empty" 2>/dev/null) || val=""
|
|
38
|
-
[[ -n "$val" && "$val" != "null" ]] && { printf '%s' "$val"; return 0; }
|
|
39
|
-
fi
|
|
40
|
-
|
|
41
|
-
if [[ -n "$_ECHO_PLUGIN_CONFIG_JSON" ]]; then
|
|
42
|
-
local val
|
|
43
|
-
val=$(printf '%s' "$_ECHO_PLUGIN_CONFIG_JSON" | jq -r ".echo${key} // empty" 2>/dev/null) || val=""
|
|
44
|
-
[[ -n "$val" && "$val" != "null" ]] && { printf '%s' "$val"; return 0; }
|
|
45
|
-
fi
|
|
28
|
+
local path="$1"
|
|
29
|
+
config_get "_echo_CONFIG" "${path}"
|
|
46
30
|
}
|
|
47
31
|
|
|
48
32
|
echo_config_get_json() {
|
|
49
|
-
local
|
|
50
|
-
|
|
51
|
-
if [[ -n "$_ECHO_CONFIG_JSON" ]]; then
|
|
52
|
-
local val
|
|
53
|
-
val=$(printf '%s' "$_ECHO_CONFIG_JSON" | jq -c "${key} // empty" 2>/dev/null) || val=""
|
|
54
|
-
[[ -n "$val" && "$val" != "null" && "$val" != "empty" ]] && { printf '%s' "$val"; return 0; }
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
if [[ -n "$_ECHO_PLUGIN_CONFIG_JSON" ]]; then
|
|
58
|
-
local val
|
|
59
|
-
val=$(printf '%s' "$_ECHO_PLUGIN_CONFIG_JSON" | jq -c ".echo${key} // empty" 2>/dev/null) || val=""
|
|
60
|
-
[[ -n "$val" && "$val" != "null" && "$val" != "empty" ]] && { printf '%s' "$val"; return 0; }
|
|
61
|
-
fi
|
|
33
|
+
local path="$1"
|
|
34
|
+
config_get_json "_echo_CONFIG" "${path}"
|
|
62
35
|
}
|
|
63
36
|
|
|
64
37
|
echo_config_model() {
|
|
65
38
|
local val
|
|
66
|
-
val=$(echo_config_get '.evaluation.model')
|
|
39
|
+
val=$(echo_config_get '.echo.evaluation.model')
|
|
67
40
|
printf '%s' "${val:-claude-haiku-4-5-20251001}"
|
|
68
41
|
}
|
|
69
42
|
|
|
70
43
|
echo_config_timeout() {
|
|
71
44
|
local val
|
|
72
|
-
val=$(echo_config_get '.evaluation.timeout_seconds')
|
|
45
|
+
val=$(echo_config_get '.echo.evaluation.timeout_seconds')
|
|
73
46
|
printf '%s' "${val:-60}"
|
|
74
47
|
}
|
|
75
48
|
|
|
76
49
|
echo_config_drift_threshold() {
|
|
77
50
|
local val
|
|
78
|
-
val=$(echo_config_get '.drift_threshold')
|
|
51
|
+
val=$(echo_config_get '.echo.drift_threshold')
|
|
79
52
|
printf '%s' "${val:-0.05}"
|
|
80
53
|
}
|
|
81
54
|
|
|
82
|
-
# Prints newline-separated list of watch glob patterns.
|
|
83
55
|
echo_config_watch_paths() {
|
|
84
56
|
local raw
|
|
85
|
-
raw=$(echo_config_get_json '.watch_paths')
|
|
86
|
-
if [[ -n "$raw" ]]; then
|
|
57
|
+
raw=$(echo_config_get_json '.echo.watch_paths')
|
|
58
|
+
if [[ -n "$raw" && "$raw" != "null" ]]; then
|
|
87
59
|
printf '%s' "$raw" | jq -r '.[]' 2>/dev/null
|
|
88
60
|
else
|
|
89
61
|
printf 'plugins/*/agents/*.md\n'
|
|
90
62
|
fi
|
|
91
63
|
}
|
|
92
64
|
|
|
93
|
-
# Prints newline-separated list of exclude glob patterns.
|
|
94
65
|
echo_config_exclude_paths() {
|
|
95
66
|
local raw
|
|
96
|
-
raw=$(echo_config_get_json '.exclude_paths')
|
|
97
|
-
if [[ -n "$raw" ]]; then
|
|
67
|
+
raw=$(echo_config_get_json '.echo.exclude_paths')
|
|
68
|
+
if [[ -n "$raw" && "$raw" != "null" ]]; then
|
|
98
69
|
printf '%s' "$raw" | jq -r '.[]' 2>/dev/null
|
|
99
70
|
fi
|
|
100
71
|
# Always exclude Echo's own tree — hardcoded, not overridable.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Config resolution for Governor.
|
|
3
3
|
#
|
|
4
|
-
# Reads
|
|
4
|
+
# Uses the shared config loader from ecosystem. Reads five layers, latest wins:
|
|
5
5
|
# 1. plugins/governor/config.json (defaults shipped with the plugin)
|
|
6
6
|
# 2. ~/.claude/settings.json
|
|
7
|
-
# 3.
|
|
7
|
+
# 3. ~/.claude/settings.local.json (local overrides user)
|
|
8
|
+
# 4. <repo>/.claude/settings.json
|
|
9
|
+
# 5. <repo>/.claude/settings.local.json (local overrides project)
|
|
8
10
|
#
|
|
9
11
|
# Exposes:
|
|
10
12
|
# governor_config_load <repo_root> # populates _GOVERNOR_CONFIG (JSON)
|
|
@@ -12,57 +14,25 @@
|
|
|
12
14
|
# governor_config_get_json <jq-path> # echoes JSON value (null if unset)
|
|
13
15
|
# governor_config_enforcement # echoes "soft" or "hard"
|
|
14
16
|
|
|
17
|
+
# shellcheck source=../../../scripts/lib/config-loader.sh
|
|
18
|
+
source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
|
|
19
|
+
|
|
15
20
|
_GOVERNOR_CONFIG="{}"
|
|
16
21
|
|
|
17
22
|
governor_config_load() {
|
|
18
23
|
local repo_root="${1:-}"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
local merged="{}"
|
|
23
|
-
local file
|
|
24
|
-
|
|
25
|
-
file="${plugin_root}/config.json"
|
|
26
|
-
if [[ -f "$file" ]]; then
|
|
27
|
-
local defaults
|
|
28
|
-
defaults=$(jq '.' "$file" 2>/dev/null) || defaults="{}"
|
|
29
|
-
merged=$(jq -n --argjson a "$merged" --argjson b "$defaults" '$a * $b' 2>/dev/null) \
|
|
30
|
-
|| merged="$defaults"
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
local repo_settings=""
|
|
34
|
-
[[ -n "$repo_root" ]] && repo_settings="${repo_root}/.claude/settings.json"
|
|
35
|
-
|
|
36
|
-
for file in "${home_dir}/.claude/settings.json" "$repo_settings"; do
|
|
37
|
-
[[ -n "$file" && -f "$file" ]] || continue
|
|
38
|
-
local overlay
|
|
39
|
-
overlay=$(jq '{ governor: (.governor // {}) }' "$file" 2>/dev/null) || continue
|
|
40
|
-
[[ -z "$overlay" ]] && continue
|
|
41
|
-
local attempt
|
|
42
|
-
if attempt=$(jq -n --argjson a "$merged" --argjson b "$overlay" '
|
|
43
|
-
def deepmerge($a; $b):
|
|
44
|
-
if ($a|type) == "object" and ($b|type) == "object" then
|
|
45
|
-
reduce (($a|keys) + ($b|keys) | unique)[] as $k
|
|
46
|
-
({}; .[$k] = deepmerge($a[$k]; $b[$k]))
|
|
47
|
-
elif $b == null then $a
|
|
48
|
-
else $b end;
|
|
49
|
-
deepmerge($a; $b)
|
|
50
|
-
' 2>/dev/null) && [[ -n "$attempt" ]]; then
|
|
51
|
-
merged="$attempt"
|
|
52
|
-
fi
|
|
53
|
-
done
|
|
54
|
-
|
|
55
|
-
_GOVERNOR_CONFIG="$merged"
|
|
24
|
+
config_load_plugin "governor" "$repo_root" "_GOVERNOR_CONFIG"
|
|
25
|
+
return 0
|
|
56
26
|
}
|
|
57
27
|
|
|
58
28
|
governor_config_get() {
|
|
59
29
|
local path="$1"
|
|
60
|
-
|
|
30
|
+
config_get "_GOVERNOR_CONFIG" "${path}"
|
|
61
31
|
}
|
|
62
32
|
|
|
63
33
|
governor_config_get_json() {
|
|
64
34
|
local path="$1"
|
|
65
|
-
|
|
35
|
+
config_get_json "_GOVERNOR_CONFIG" "${path}"
|
|
66
36
|
}
|
|
67
37
|
|
|
68
38
|
governor_config_enforcement() {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Config resolution for Historian.
|
|
3
3
|
#
|
|
4
|
-
# Reads
|
|
4
|
+
# Uses the shared config loader from ecosystem. Reads five layers, latest wins:
|
|
5
5
|
# 1. plugins/historian/config.json (defaults shipped with the plugin)
|
|
6
6
|
# 2. ~/.claude/settings.json
|
|
7
|
-
# 3.
|
|
7
|
+
# 3. ~/.claude/settings.local.json (local overrides user)
|
|
8
|
+
# 4. <repo>/.claude/settings.json
|
|
9
|
+
# 5. <repo>/.claude/settings.local.json (local overrides project)
|
|
8
10
|
#
|
|
9
11
|
# Exposes:
|
|
10
12
|
# historian_config_load <repo_root> # populates _HISTORIAN_CONFIG (JSON)
|
|
@@ -12,41 +14,15 @@
|
|
|
12
14
|
#
|
|
13
15
|
# Settings overlay only touches the `historian.*` subtree of settings.json.
|
|
14
16
|
|
|
17
|
+
# shellcheck source=../../../scripts/lib/config-loader.sh
|
|
18
|
+
source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
|
|
19
|
+
|
|
15
20
|
_HISTORIAN_CONFIG="{}"
|
|
16
21
|
|
|
17
22
|
historian_config_load() {
|
|
18
23
|
local repo_root="${1:-}"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
local merged="{}"
|
|
23
|
-
local file
|
|
24
|
-
|
|
25
|
-
file="${plugin_root}/config.json"
|
|
26
|
-
if [[ -f "$file" ]]; then
|
|
27
|
-
local defaults
|
|
28
|
-
defaults=$(jq '.' "$file" 2>/dev/null) || defaults="{}"
|
|
29
|
-
merged=$(jq -n --argjson a "$merged" --argjson b "$defaults" '$a * $b' 2>/dev/null) \
|
|
30
|
-
|| merged="$defaults"
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
for file in "${home_dir}/.claude/settings.json" "${repo_root}/.claude/settings.json"; do
|
|
34
|
-
[[ -n "$file" && -f "$file" ]] || continue
|
|
35
|
-
local overlay
|
|
36
|
-
overlay=$(jq '{ historian: (.historian // {}) }' "$file" 2>/dev/null) || continue
|
|
37
|
-
[[ -z "$overlay" ]] && continue
|
|
38
|
-
merged=$(jq -n --argjson a "$merged" --argjson b "$overlay" '
|
|
39
|
-
def deepmerge($a; $b):
|
|
40
|
-
if ($a|type) == "object" and ($b|type) == "object" then
|
|
41
|
-
reduce (($a|keys) + ($b|keys) | unique)[] as $k
|
|
42
|
-
({}; .[$k] = deepmerge($a[$k]; $b[$k]))
|
|
43
|
-
elif $b == null then $a
|
|
44
|
-
else $b end;
|
|
45
|
-
deepmerge($a; $b)
|
|
46
|
-
' 2>/dev/null) || true
|
|
47
|
-
done
|
|
48
|
-
|
|
49
|
-
_HISTORIAN_CONFIG="$merged"
|
|
24
|
+
config_load_plugin "historian" "$repo_root" "_HISTORIAN_CONFIG"
|
|
25
|
+
return 0
|
|
50
26
|
}
|
|
51
27
|
|
|
52
28
|
# Read a value from the loaded config. The explicit null check (instead of
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "inspector",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Per-edit lint and typecheck gate. Runs the project's configured checks on just the touched file after every Write / Edit / MultiEdit, so the agent sees its own type errors before claiming success. Cheaper than proctor (which runs project-wide verification at Stop); complements assayer (which catches the agent lying about claims). Builds on the Onlooker ecosystem plugin.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Onlooker Community",
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.2](https://github.com/onlooker-community/ecosystem/compare/inspector-v0.3.1...inspector-v0.3.2) (2026-08-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* restore config convenience functions & refactor execution plugins to shared loader ([#128](https://github.com/onlooker-community/ecosystem/issues/128)) ([4b3660c](https://github.com/onlooker-community/ecosystem/commit/4b3660c5a8b234187ec1e71c37b63e6a3d305c98))
|
|
9
|
+
|
|
3
10
|
## [0.3.1](https://github.com/onlooker-community/ecosystem/compare/inspector-v0.3.0...inspector-v0.3.1) (2026-08-01)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,62 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
2
|
+
# Config resolution for inspector.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
# 1. plugins/inspector/config.json
|
|
6
|
-
# 2. ~/.claude/settings.json
|
|
7
|
-
# 3.
|
|
4
|
+
# Uses the shared config loader from ecosystem. Reads five layers, latest wins:
|
|
5
|
+
# 1. plugins/inspector/config.json (defaults shipped with the plugin)
|
|
6
|
+
# 2. ~/.claude/settings.json
|
|
7
|
+
# 3. ~/.claude/settings.local.json (local overrides user)
|
|
8
|
+
# 4. <repo>/.claude/settings.json
|
|
9
|
+
# 5. <repo>/.claude/settings.local.json (local overrides project)
|
|
8
10
|
#
|
|
9
|
-
#
|
|
10
|
-
# inspector_config_load <repo_root>
|
|
11
|
-
# inspector_config_get
|
|
12
|
-
# inspector_config_get_json
|
|
13
|
-
# inspector_config_checks_for_extension ".ts"
|
|
11
|
+
# Exposes:
|
|
12
|
+
# inspector_config_load <repo_root> # populates _inspector_CONFIG (JSON)
|
|
13
|
+
# inspector_config_get <jq-path> # echoes string value (empty if unset)
|
|
14
|
+
# inspector_config_get_json <jq-path> # echoes JSON value (null if unset)
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
# shellcheck source=../../../scripts/lib/config-loader.sh
|
|
17
|
+
source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
|
|
18
|
+
|
|
19
|
+
_inspector_CONFIG="{}"
|
|
17
20
|
|
|
18
21
|
inspector_config_load() {
|
|
19
22
|
local repo_root="${1:-}"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
local plugin_config="$plugin_dir/config.json"
|
|
23
|
-
|
|
24
|
-
_INSPECTOR_PLUGIN_CONFIG="{}"
|
|
25
|
-
if [[ -f "$plugin_config" ]]; then
|
|
26
|
-
_INSPECTOR_PLUGIN_CONFIG=$(cat "$plugin_config")
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
local home_settings="{}"
|
|
30
|
-
if [[ -f "$HOME/.claude/settings.json" ]]; then
|
|
31
|
-
home_settings=$(cat "$HOME/.claude/settings.json")
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
local repo_settings="{}"
|
|
35
|
-
if [[ -n "$repo_root" && -f "$repo_root/.claude/settings.json" ]]; then
|
|
36
|
-
repo_settings=$(cat "$repo_root/.claude/settings.json")
|
|
37
|
-
fi
|
|
38
|
-
|
|
39
|
-
_INSPECTOR_CONFIG=$(jq -n \
|
|
40
|
-
--argjson plugin "$_INSPECTOR_PLUGIN_CONFIG" \
|
|
41
|
-
--argjson home "$home_settings" \
|
|
42
|
-
--argjson repo "$repo_settings" \
|
|
43
|
-
'$plugin * {"inspector": (($plugin.inspector // {}) * ($home.inspector // {}) * ($repo.inspector // {}))}')
|
|
23
|
+
config_load_plugin "inspector" "$repo_root" "_inspector_CONFIG"
|
|
24
|
+
return 0
|
|
44
25
|
}
|
|
45
26
|
|
|
46
27
|
inspector_config_get() {
|
|
47
|
-
local path="$
|
|
48
|
-
|
|
28
|
+
local path="$1"
|
|
29
|
+
config_get "_inspector_CONFIG" "${path}"
|
|
49
30
|
}
|
|
50
31
|
|
|
51
32
|
inspector_config_get_json() {
|
|
52
|
-
local path="$
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
inspector_config_show_clean_runs() {
|
|
57
|
-
local v
|
|
58
|
-
v=$(inspector_config_get '.inspector.show_clean_runs')
|
|
59
|
-
[[ "$v" == "true" ]]
|
|
33
|
+
local path="$1"
|
|
34
|
+
config_get_json "_inspector_CONFIG" "${path}"
|
|
60
35
|
}
|
|
61
36
|
|
|
62
37
|
inspector_config_timeout_per_check() {
|
|
@@ -77,25 +52,26 @@ inspector_config_output_excerpt_max_bytes() {
|
|
|
77
52
|
printf '%s' "${v:-4096}"
|
|
78
53
|
}
|
|
79
54
|
|
|
55
|
+
inspector_config_show_clean_runs() {
|
|
56
|
+
local v
|
|
57
|
+
v=$(inspector_config_get '.inspector.show_clean_runs')
|
|
58
|
+
[[ "$v" == "true" ]]
|
|
59
|
+
}
|
|
60
|
+
|
|
80
61
|
inspector_config_exclude_paths() {
|
|
81
|
-
inspector_config_get_json '.inspector.exclude_paths // []'
|
|
62
|
+
inspector_config_get_json '.inspector.exclude_paths // ["node_modules",".git","vendor",".venv","dist",".next",".nuxt","build","__pycache__","target","coverage"]'
|
|
82
63
|
}
|
|
83
64
|
|
|
84
|
-
# Emits a JSON array of {name, argv, kind} objects for the given file extension
|
|
85
|
-
# (including the leading dot). Returns an empty array when no checks are
|
|
86
|
-
# configured for the extension.
|
|
87
65
|
inspector_config_checks_for_extension() {
|
|
88
|
-
local ext="$
|
|
89
|
-
[[ -z "$ext" ]] && { printf '[]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
)
|
|
100
|
-
' 2>/dev/null || printf '[]'
|
|
66
|
+
local ext="$1"
|
|
67
|
+
[[ -z "$ext" ]] && { printf '%s\n' "[]"; return 0; }
|
|
68
|
+
local raw
|
|
69
|
+
raw=$(inspector_config_get_json ".inspector.checks[\"$ext\"] // []")
|
|
70
|
+
[[ -z "$raw" ]] && { printf '%s\n' "[]"; return 0; }
|
|
71
|
+
# Normalize bare argv arrays into objects with name, kind, argv fields
|
|
72
|
+
printf '%s' "$raw" | jq -c 'map(
|
|
73
|
+
if type == "array" then
|
|
74
|
+
{name: .[0], kind: "lint", argv: .}
|
|
75
|
+
else . end
|
|
76
|
+
)'
|
|
101
77
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "librarian",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Consolidation layer between archivist's per-session artifacts and the user's durable typed memory store. Detects which session decisions, dead-ends, and open questions deserve to live across sessions, classifies them into the user/feedback/project/reference types, and queues them as proposals for explicit confirmation. Auto-promotion is opt-in. Builds on the Onlooker ecosystem plugin.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Onlooker Community",
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.1](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.6.0...librarian-v0.6.1) (2026-08-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **hooks:** prevent SessionEnd timeouts causing breadcrumb accumulation ([#121](https://github.com/onlooker-community/ecosystem/issues/121)) ([3c7a409](https://github.com/onlooker-community/ecosystem/commit/3c7a40937aa3ea8c1db85dd0bb3fcc22589bbb49))
|
|
9
|
+
|
|
3
10
|
## [0.6.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.5.0...librarian-v0.6.0) (2026-08-02)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -60,6 +60,17 @@ source "${PLUGIN_ROOT}/scripts/lib/librarian-classifier.sh"
|
|
|
60
60
|
# shellcheck source=../lib/librarian-conflict-detector.sh
|
|
61
61
|
source "${PLUGIN_ROOT}/scripts/lib/librarian-conflict-detector.sh"
|
|
62
62
|
|
|
63
|
+
librarian_now_ms() {
|
|
64
|
+
local now_ms
|
|
65
|
+
now_ms=$(python3 - <<'PY' 2>/dev/null
|
|
66
|
+
import time
|
|
67
|
+
print(int(time.time() * 1000))
|
|
68
|
+
PY
|
|
69
|
+
) || now_ms=""
|
|
70
|
+
[[ -z "$now_ms" ]] && now_ms=$(( $(date +%s) * 1000 ))
|
|
71
|
+
printf '%s' "$now_ms"
|
|
72
|
+
}
|
|
73
|
+
|
|
63
74
|
INPUT=$(cat 2>/dev/null || true)
|
|
64
75
|
CWD=$(printf '%s' "$INPUT" | jq -r '.cwd // ""' 2>/dev/null) || CWD=""
|
|
65
76
|
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // ""' 2>/dev/null) || SESSION_ID=""
|
|
@@ -98,7 +109,7 @@ fi
|
|
|
98
109
|
# Emit scan.started and load candidate window.
|
|
99
110
|
# ----------------------------------------------------------------------------
|
|
100
111
|
|
|
101
|
-
|
|
112
|
+
SCAN_START_TS_MS=$(librarian_now_ms)
|
|
102
113
|
ARTIFACTS=$(librarian_archivist_load_since "$PROJECT_KEY" "$WATERMARK")
|
|
103
114
|
ARTIFACT_COUNT=$(printf '%s' "$ARTIFACTS" | jq 'length' 2>/dev/null) || ARTIFACT_COUNT=0
|
|
104
115
|
|
|
@@ -114,7 +125,7 @@ librarian_emit "librarian.scan.started" "$SESSION_ID" "$(jq -cn \
|
|
|
114
125
|
# don't re-walk the same window.
|
|
115
126
|
if [[ "$ARTIFACT_COUNT" == "0" ]]; then
|
|
116
127
|
librarian_storage_write_last_scan "$PROJECT_KEY" || true
|
|
117
|
-
DURATION_MS=$((
|
|
128
|
+
DURATION_MS=$(( $(librarian_now_ms) - SCAN_START_TS_MS ))
|
|
118
129
|
librarian_emit "librarian.scan.complete" "$SESSION_ID" "$(jq -cn \
|
|
119
130
|
--arg outcome "empty" \
|
|
120
131
|
--argjson duration_ms "$DURATION_MS" \
|
|
@@ -155,6 +166,30 @@ for ((i = 0; i < DROPPED_EMIT_COUNT; i++)); do
|
|
|
155
166
|
| with_entries(select(.value != null))')"
|
|
156
167
|
done
|
|
157
168
|
|
|
169
|
+
# ----------------------------------------------------------------------------
|
|
170
|
+
# Runtime budget check: SessionEnd has 1.5s total. If we're running low on time,
|
|
171
|
+
# skip classification to ensure scan.complete can be emitted before CLI timeout.
|
|
172
|
+
# Retained artifacts will be re-scanned on the next session when time permits.
|
|
173
|
+
# ----------------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
ELAPSED_MS=$(( $(librarian_now_ms) - SCAN_START_TS_MS ))
|
|
176
|
+
BUDGET_THRESHOLD_MS=1000
|
|
177
|
+
if [[ "$ELAPSED_MS" -ge "$BUDGET_THRESHOLD_MS" ]]; then
|
|
178
|
+
librarian_storage_write_last_scan "$PROJECT_KEY" || true
|
|
179
|
+
DURATION_MS=$(( $(librarian_now_ms) - SCAN_START_TS_MS ))
|
|
180
|
+
librarian_emit "librarian.scan.complete" "$SESSION_ID" "$(jq -cn \
|
|
181
|
+
--arg outcome "budget_exceeded" \
|
|
182
|
+
--argjson duration_ms "$DURATION_MS" \
|
|
183
|
+
--argjson candidates_proposed 0 \
|
|
184
|
+
--argjson candidates_dropped "$DROPPED_TOTAL" \
|
|
185
|
+
--argjson artifact_count_in_window "$ARTIFACT_COUNT" \
|
|
186
|
+
'{ outcome: $outcome, duration_ms: $duration_ms,
|
|
187
|
+
candidates_proposed: $candidates_proposed,
|
|
188
|
+
candidates_dropped: $candidates_dropped,
|
|
189
|
+
artifact_count_in_window: $artifact_count_in_window }')"
|
|
190
|
+
exit 0
|
|
191
|
+
fi
|
|
192
|
+
|
|
158
193
|
# ----------------------------------------------------------------------------
|
|
159
194
|
# Classifier loop — one Haiku call per surviving candidate.
|
|
160
195
|
# ----------------------------------------------------------------------------
|
|
@@ -398,7 +433,7 @@ librarian_storage_write_last_scan "$PROJECT_KEY" || true
|
|
|
398
433
|
TOTAL_DROPPED=$((DROPPED_TOTAL + POST_CLASSIFIER_DROPPED))
|
|
399
434
|
OUTCOME="ok"
|
|
400
435
|
[[ "$PROPOSED_COUNT" == "0" ]] && OUTCOME="empty"
|
|
401
|
-
DURATION_MS=$((
|
|
436
|
+
DURATION_MS=$(( $(librarian_now_ms) - SCAN_START_TS_MS ))
|
|
402
437
|
|
|
403
438
|
librarian_emit "librarian.scan.complete" "$SESSION_ID" "$(jq -cn \
|
|
404
439
|
--arg outcome "$OUTCOME" \
|