@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.
Files changed (39) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.release-please-manifest.json +9 -9
  3. package/CHANGELOG.md +19 -0
  4. package/package.json +1 -1
  5. package/plugins/archivist/scripts/lib/archivist-config.sh +10 -34
  6. package/plugins/assayer/.claude-plugin/plugin.json +1 -1
  7. package/plugins/assayer/CHANGELOG.md +7 -0
  8. package/plugins/assayer/scripts/lib/assayer-config.sh +39 -62
  9. package/plugins/bursar/.claude-plugin/plugin.json +1 -1
  10. package/plugins/bursar/CHANGELOG.md +12 -0
  11. package/plugins/bursar/scripts/hooks/bursar-session-end.sh +6 -5
  12. package/plugins/bursar/scripts/lib/bursar-config.sh +10 -43
  13. package/plugins/cartographer/scripts/lib/cartographer-config.sh +11 -29
  14. package/plugins/compass/scripts/lib/compass-config.sh +16 -46
  15. package/plugins/counsel/scripts/lib/counsel-config.sh +15 -46
  16. package/plugins/curator/.claude-plugin/plugin.json +1 -1
  17. package/plugins/curator/CHANGELOG.md +7 -0
  18. package/plugins/curator/scripts/lib/curator-config.sh +19 -44
  19. package/plugins/echo/scripts/lib/echo-config.sh +30 -59
  20. package/plugins/governor/scripts/lib/governor-config.sh +11 -41
  21. package/plugins/historian/scripts/lib/historian-config.sh +9 -33
  22. package/plugins/inspector/.claude-plugin/plugin.json +1 -1
  23. package/plugins/inspector/CHANGELOG.md +7 -0
  24. package/plugins/inspector/scripts/lib/inspector-config.sh +39 -63
  25. package/plugins/librarian/.claude-plugin/plugin.json +1 -1
  26. package/plugins/librarian/CHANGELOG.md +7 -0
  27. package/plugins/librarian/scripts/hooks/librarian-session-end.sh +38 -3
  28. package/plugins/librarian/scripts/lib/librarian-config.sh +10 -34
  29. package/plugins/lineage/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lineage/CHANGELOG.md +7 -0
  31. package/plugins/lineage/scripts/lib/lineage-config.sh +17 -53
  32. package/plugins/scribe/.claude-plugin/plugin.json +1 -1
  33. package/plugins/scribe/CHANGELOG.md +7 -0
  34. package/plugins/scribe/scripts/lib/scribe-config.sh +17 -47
  35. package/plugins/tribunal/.claude-plugin/plugin.json +1 -1
  36. package/plugins/tribunal/CHANGELOG.md +7 -0
  37. package/plugins/tribunal/scripts/lib/tribunal-config.sh +25 -63
  38. package/plugins/warden/scripts/lib/warden-config.sh +17 -54
  39. package/scripts/lib/config-loader.sh +138 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecosystem",
3
- "version": "0.32.2",
3
+ "version": "0.33.1",
4
4
  "description": "Observability substrate for Claude Code. Provides the shared $ONLOOKER_DIR storage root (default $HOME/.onlooker), canonical schema-validated event emission, session and tool tracking hooks, and prompt rules. Required by all other Onlooker plugins.",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -1,19 +1,19 @@
1
1
  {
2
- ".": "0.32.2",
2
+ ".": "0.33.1",
3
3
  "plugins/archivist": "0.3.1",
4
- "plugins/tribunal": "1.1.1",
4
+ "plugins/tribunal": "1.1.2",
5
5
  "plugins/echo": "0.3.1",
6
6
  "plugins/cartographer": "0.3.1",
7
7
  "plugins/governor": "0.3.2",
8
8
  "plugins/compass": "0.4.1",
9
- "plugins/scribe": "0.4.1",
9
+ "plugins/scribe": "0.4.2",
10
10
  "plugins/counsel": "0.5.1",
11
11
  "plugins/warden": "0.3.1",
12
- "plugins/librarian": "0.6.0",
13
- "plugins/curator": "0.2.1",
12
+ "plugins/librarian": "0.6.1",
13
+ "plugins/curator": "0.2.2",
14
14
  "plugins/historian": "0.3.2",
15
- "plugins/assayer": "1.1.1",
16
- "plugins/bursar": "0.2.2",
17
- "plugins/lineage": "0.2.1",
18
- "plugins/inspector": "0.3.1"
15
+ "plugins/assayer": "1.1.2",
16
+ "plugins/bursar": "0.3.0",
17
+ "plugins/lineage": "0.2.2",
18
+ "plugins/inspector": "0.3.2"
19
19
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.33.1](https://github.com/onlooker-community/ecosystem/compare/ecosystem-v0.33.0...ecosystem-v0.33.1) (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
+
10
+ ## [0.33.0](https://github.com/onlooker-community/ecosystem/compare/ecosystem-v0.32.2...ecosystem-v0.33.0) (2026-08-02)
11
+
12
+
13
+ ### Features
14
+
15
+ * **config:** add shared config loader supporting all five settings layers :sparkles: ([#123](https://github.com/onlooker-community/ecosystem/issues/123)) ([c048b76](https://github.com/onlooker-community/ecosystem/commit/c048b76bee2b39abeb7f1af77181191dd48f9cf5))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **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))
21
+
3
22
  ## [0.32.2](https://github.com/onlooker-community/ecosystem/compare/ecosystem-v0.32.1...ecosystem-v0.32.2) (2026-08-02)
4
23
 
5
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlooker-community/ecosystem",
3
- "version": "0.32.2",
3
+ "version": "0.33.1",
4
4
  "description": "Agents, skills, hooks, commands, rules, and MCP configurations that power [Onlooker](https://onlooker.dev)",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env bash
2
2
  # Config resolution for Archivist.
3
3
  #
4
- # Reads three layers, latest wins:
4
+ # Uses the shared config loader from ecosystem. Reads five layers, latest wins:
5
5
  # 1. plugins/archivist/config.json (defaults shipped with the plugin)
6
6
  # 2. ~/.claude/settings.json
7
- # 3. <repo>/.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
11
  # Exposes:
10
12
  # archivist_config_load <repo_root> # populates _ARCHIVIST_CONFIG (JSON)
@@ -13,46 +15,20 @@
13
15
  # Settings overlay only touches the `archivist.*` subtree of settings.json so it
14
16
  # coexists with other plugins' configuration.
15
17
 
18
+ # shellcheck source=../../../scripts/lib/config-loader.sh
19
+ source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
20
+
16
21
  _ARCHIVIST_CONFIG="{}"
17
22
 
18
23
  archivist_config_load() {
19
24
  local repo_root="${1:-}"
20
- local plugin_root="${CLAUDE_PLUGIN_ROOT:-}"
21
- local home_dir="${HOME:-}"
22
-
23
- local merged="{}"
24
- local file
25
-
26
- file="${plugin_root}/config.json"
27
- if [[ -f "$file" ]]; then
28
- local defaults
29
- defaults=$(jq '.' "$file" 2>/dev/null) || defaults="{}"
30
- merged=$(jq -n --argjson a "$merged" --argjson b "$defaults" '$a * $b' 2>/dev/null) \
31
- || merged="$defaults"
32
- fi
33
-
34
- for file in "${home_dir}/.claude/settings.json" "${repo_root}/.claude/settings.json"; do
35
- [[ -n "$file" && -f "$file" ]] || continue
36
- local overlay
37
- overlay=$(jq '{ archivist: (.archivist // {}) }' "$file" 2>/dev/null) || continue
38
- [[ -z "$overlay" ]] && continue
39
- merged=$(jq -n --argjson a "$merged" --argjson b "$overlay" '
40
- def deepmerge($a; $b):
41
- if ($a|type) == "object" and ($b|type) == "object" then
42
- reduce (($a|keys) + ($b|keys) | unique)[] as $k
43
- ({}; .[$k] = deepmerge($a[$k]; $b[$k]))
44
- elif $b == null then $a
45
- else $b end;
46
- deepmerge($a; $b)
47
- ' 2>/dev/null) || true
48
- done
49
-
50
- _ARCHIVIST_CONFIG="$merged"
25
+ config_load_plugin "archivist" "$repo_root" "_ARCHIVIST_CONFIG"
26
+ return 0
51
27
  }
52
28
 
53
29
  # Read a value from the loaded config. Usage:
54
30
  # archivist_config_get '.archivist.injection.max_items'
55
31
  archivist_config_get() {
56
32
  local path="$1"
57
- printf '%s' "$_ARCHIVIST_CONFIG" | jq -r "${path} // empty" 2>/dev/null
33
+ config_get "_ARCHIVIST_CONFIG" "${path}"
58
34
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assayer",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Claim verification. At session end, parses the agent's final message for testable claims (\"I ran the tests, they pass\", \"the build is green\") and checks each against the actual command results in the session transcript, classifying it corroborated, contradicted, or unverifiable. Catches lying-without-malice. Advisory by default. Builds on the Onlooker ecosystem plugin.",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.2](https://github.com/onlooker-community/ecosystem/compare/assayer-v1.1.1...assayer-v1.1.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
  ## [1.1.1](https://github.com/onlooker-community/ecosystem/compare/assayer-v1.1.0...assayer-v1.1.1) (2026-08-01)
4
11
 
5
12
 
@@ -1,82 +1,59 @@
1
1
  #!/usr/bin/env bash
2
- # Config loading for Assayer.
3
- # Reads the repo's .claude/settings.json assayer.* keys, falling back to the
4
- # plugin's own config.json defaults.
5
-
6
- _ASSAYER_CONFIG_JSON=""
7
- _ASSAYER_PLUGIN_CONFIG_JSON=""
2
+ # Config resolution for assayer.
3
+ #
4
+ # Uses the shared config loader from ecosystem. Reads five layers, latest wins:
5
+ # 1. plugins/assayer/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
+ # assayer_config_load <repo_root> # populates _assayer_CONFIG (JSON)
13
+ # assayer_config_get <jq-path> # echoes string value (empty if unset)
14
+ # assayer_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"
18
+
19
+ _assayer_CONFIG="{}"
8
20
 
9
21
  assayer_config_load() {
10
22
  local repo_root="${1:-}"
11
-
12
- _ASSAYER_PLUGIN_CONFIG_JSON=""
13
- local plugin_config="${CLAUDE_PLUGIN_ROOT:-}/config.json"
14
- if [[ -f "$plugin_config" ]]; then
15
- _ASSAYER_PLUGIN_CONFIG_JSON=$(cat "$plugin_config" 2>/dev/null) || _ASSAYER_PLUGIN_CONFIG_JSON=""
16
- fi
17
-
18
- _ASSAYER_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 block
25
- block=$(printf '%s' "$settings" | jq -c '.assayer // empty' 2>/dev/null) || block=""
26
- [[ -n "$block" ]] && _ASSAYER_CONFIG_JSON="$block"
27
- fi
28
- fi
23
+ config_load_plugin "assayer" "$repo_root" "_assayer_CONFIG"
24
+ return 0
29
25
  }
30
26
 
31
- # Get a single scalar value. Checks settings.json first, then plugin config.json.
32
27
  assayer_config_get() {
33
- local key="$1"
34
-
35
- if [[ -n "$_ASSAYER_CONFIG_JSON" ]]; then
36
- local val
37
- val=$(printf '%s' "$_ASSAYER_CONFIG_JSON" | jq -r "${key} // empty" 2>/dev/null) || val=""
38
- [[ -n "$val" && "$val" != "null" ]] && {
39
- printf '%s' "$val"
40
- return 0
41
- }
42
- fi
43
-
44
- if [[ -n "$_ASSAYER_PLUGIN_CONFIG_JSON" ]]; then
45
- local val
46
- val=$(printf '%s' "$_ASSAYER_PLUGIN_CONFIG_JSON" | jq -r ".assayer${key} // empty" 2>/dev/null) || val=""
47
- [[ -n "$val" && "$val" != "null" ]] && {
48
- printf '%s' "$val"
49
- return 0
50
- }
51
- fi
28
+ local path="$1"
29
+ config_get "_assayer_CONFIG" "${path}"
52
30
  }
53
31
 
54
- assayer_config_model() {
55
- local val
56
- val=$(assayer_config_get '.evaluation.model')
57
- printf '%s' "${val:-claude-haiku-4-5-20251001}"
32
+ assayer_config_get_json() {
33
+ local path="$1"
34
+ config_get_json "_assayer_CONFIG" "${path}"
58
35
  }
59
36
 
60
- assayer_config_timeout() {
61
- local val
62
- val=$(assayer_config_get '.evaluation.timeout_seconds')
63
- printf '%s' "${val:-60}"
37
+ assayer_config_model() {
38
+ local v
39
+ v=$(assayer_config_get '.assayer.evaluation.model')
40
+ printf '%s' "${v:-claude-haiku-4-5-20251001}"
64
41
  }
65
42
 
66
43
  assayer_config_max_claims() {
67
- local val
68
- val=$(assayer_config_get '.max_claims')
69
- printf '%s' "${val:-12}"
44
+ local v
45
+ v=$(assayer_config_get '.assayer.max_claims')
46
+ printf '%s' "${v:-12}"
70
47
  }
71
48
 
72
49
  assayer_config_min_confidence() {
73
- local val
74
- val=$(assayer_config_get '.min_confidence')
75
- printf '%s' "${val:-0.5}"
50
+ local v
51
+ v=$(assayer_config_get '.assayer.min_confidence')
52
+ printf '%s' "${v:-0.5}"
76
53
  }
77
54
 
78
- assayer_config_final_message_chars() {
79
- local val
80
- val=$(assayer_config_get '.final_message_chars')
81
- printf '%s' "${val:-6000}"
55
+ assayer_config_timeout() {
56
+ local v
57
+ v=$(assayer_config_get '.assayer.evaluation.timeout_seconds')
58
+ printf '%s' "${v:-60}"
82
59
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bursar",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Multi-session, per-project budget accounting for the Onlooker ecosystem. Rolls each session's spend into a per-project ledger on SessionEnd and surfaces \"this project burned $X this week\" at SessionStart. Where governor regulates a single session, bursar is the cross-session rollup: it reads governor.session.complete off the shared event bus and emits bursar.* events for audit. Named for the officer who keeps the accounts. Builds on the Onlooker ecosystem plugin.",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0](https://github.com/onlooker-community/ecosystem/compare/bursar-v0.2.2...bursar-v0.3.0) (2026-08-02)
4
+
5
+
6
+ ### Features
7
+
8
+ * **config:** add shared config loader supporting all five settings layers :sparkles: ([#123](https://github.com/onlooker-community/ecosystem/issues/123)) ([c048b76](https://github.com/onlooker-community/ecosystem/commit/c048b76bee2b39abeb7f1af77181191dd48f9cf5))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **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))
14
+
3
15
  ## [0.2.2](https://github.com/onlooker-community/ecosystem/compare/bursar-v0.2.1...bursar-v0.2.2) (2026-08-02)
4
16
 
5
17
 
@@ -148,12 +148,13 @@ MODEL=""
148
148
  # must keep the breadcrumb so the session→project attribution survives for a
149
149
  # later attempt rather than being lost behind a false "recorded" event.
150
150
  #
151
- # Use a longer lock timeout (60s) to handle high concurrency: under typical
152
- # usage, SessionEnd hooks from concurrent sessions compete for the per-project
153
- # ledger lock. The default 5s timeout is too aggressive with 100+ concurrent
154
- # sessions, leading to silent recording failures and breadcrumb accumulation.
151
+ # Use a short lock timeout (1s) to ensure the hook completes within the CLI's
152
+ # 1.5s SessionEnd budget. If the lock is contended, fail fast and retain the
153
+ # breadcrumb for retry on a future session. Retries on subsequent SessionStart
154
+ # will eventually succeed when contention subsides; this prevents the hook from
155
+ # being cancelled mid-execution by the CLI timeout.
155
156
  if [[ -n "$RECORD" ]]; then
156
- if bursar_ledger_record "$PROJECT_KEY" "$RECORD" 60; then
157
+ if bursar_ledger_record "$PROJECT_KEY" "$RECORD" 1; then
157
158
  [[ -n "$EV" ]] && bursar_emit_event "bursar.session.recorded" "$EV" "$SESSION_ID" || true
158
159
  rm -f "$BREADCRUMB" 2>/dev/null || true
159
160
  else
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env bash
2
2
  # Config resolution for bursar.
3
3
  #
4
- # Reads three layers, latest wins:
4
+ # Uses the shared config loader from ecosystem. Reads five layers, latest wins:
5
5
  # 1. plugins/bursar/config.json (defaults shipped with the plugin)
6
6
  # 2. ~/.claude/settings.json
7
- # 3. <repo>/.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
11
  # Exposes:
10
12
  # bursar_config_load <repo_root> # populates _BURSAR_CONFIG (JSON)
@@ -14,60 +16,25 @@
14
16
  # bursar_config_surface_enabled # 0 if bursar.surface_at_session_start is true
15
17
  # bursar_config_week_start # echoes "monday" or "sunday"
16
18
 
19
+ # shellcheck source=../../../scripts/lib/config-loader.sh
20
+ source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
21
+
17
22
  _BURSAR_CONFIG="{}"
18
23
 
19
24
  bursar_config_load() {
20
25
  local repo_root="${1:-}"
21
- local plugin_root="${CLAUDE_PLUGIN_ROOT:-}"
22
- local home_dir="${HOME:-}"
23
-
24
- # Read each layer's raw text with the no-fork `$(<file)` builtin (NOT `cat`),
25
- # then deep-merge all three layers in a SINGLE jq invocation. The dominant
26
- # cost in the SessionEnd hook is jq process startup, not the merge itself, so
27
- # this collapses what was one-jq-per-file (up to 6 forks) down to one.
28
- local default_txt="" home_txt="" repo_txt=""
29
- local default_file="${plugin_root}/config.json"
30
- local home_file="${home_dir}/.claude/settings.json"
31
- local repo_file=""
32
- [[ -n "$repo_root" ]] && repo_file="${repo_root}/.claude/settings.json"
33
-
34
- [[ -f "$default_file" ]] && default_txt="$(<"$default_file")"
35
- [[ -f "$home_file" ]] && home_txt="$(<"$home_file")"
36
- [[ -n "$repo_file" && -f "$repo_file" ]] && repo_txt="$(<"$repo_file")"
37
-
38
- # Precedence (latest wins): defaults < home settings < repo settings. The
39
- # defaults file is merged whole; settings files contribute only their .bursar
40
- # key. `fromjson? // {}` parses each layer defensively — a missing or malformed
41
- # file degrades to {} rather than aborting the merge (matches the prior
42
- # per-file fallback).
43
- _BURSAR_CONFIG=$(jq -n \
44
- --arg d "$default_txt" \
45
- --arg h "$home_txt" \
46
- --arg r "$repo_txt" \
47
- '
48
- def deepmerge($a; $b):
49
- if ($a|type) == "object" and ($b|type) == "object" then
50
- reduce (($a|keys) + ($b|keys) | unique)[] as $k
51
- ({}; .[$k] = deepmerge($a[$k]; $b[$k]))
52
- elif $b == null then $a
53
- else $b end;
54
- ($d | fromjson? // {}) as $defaults
55
- | (($h | fromjson? // {}) | {bursar: (.bursar // {})}) as $home
56
- | (($r | fromjson? // {}) | {bursar: (.bursar // {})}) as $repo
57
- | deepmerge(deepmerge($defaults; $home); $repo)
58
- ' 2>/dev/null) || _BURSAR_CONFIG="{}"
59
- [[ -z "$_BURSAR_CONFIG" ]] && _BURSAR_CONFIG="{}"
26
+ config_load_plugin "bursar" "$repo_root" "_BURSAR_CONFIG"
60
27
  return 0
61
28
  }
62
29
 
63
30
  bursar_config_get() {
64
31
  local path="$1"
65
- printf '%s' "$_BURSAR_CONFIG" | jq -r "${path} // empty" 2>/dev/null
32
+ config_get "_BURSAR_CONFIG" "${path}"
66
33
  }
67
34
 
68
35
  bursar_config_get_json() {
69
36
  local path="$1"
70
- printf '%s' "$_BURSAR_CONFIG" | jq -c "${path}" 2>/dev/null
37
+ config_get_json "_BURSAR_CONFIG" "${path}"
71
38
  }
72
39
 
73
40
  bursar_config_surface_enabled() {
@@ -1,54 +1,36 @@
1
1
  #!/usr/bin/env bash
2
2
  # cartographer-config.sh — load and query Cartographer configuration.
3
3
  #
4
- # Merges three layers in precedence order (later wins):
4
+ # Uses the shared config loader from ecosystem. Merges five layers in precedence order:
5
5
  # 1. plugins/cartographer/config.json (plugin defaults)
6
6
  # 2. ~/.claude/settings.json (.cartographer subtree)
7
- # 3. <repo>/.claude/settings.json (.cartographer subtree)
7
+ # 3. ~/.claude/settings.local.json (.cartographer subtree, local overrides user)
8
+ # 4. <repo>/.claude/settings.json (.cartographer subtree)
9
+ # 5. <repo>/.claude/settings.local.json (.cartographer subtree, local overrides project)
8
10
  #
9
11
  # Usage:
10
12
  # cartographer_config_load <repo_root>
11
13
  # cartographer_config_get_json ".cartographer.exclude_paths"
12
14
 
15
+ # shellcheck source=../../../scripts/lib/config-loader.sh
16
+ source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
17
+
13
18
  _CARTOGRAPHER_CONFIG=""
14
- _CARTOGRAPHER_PLUGIN_CONFIG=""
15
19
 
16
20
  cartographer_config_load() {
17
21
  local repo_root="${1:-}"
18
- local plugin_dir
19
- plugin_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
20
- local plugin_config="$plugin_dir/config.json"
21
-
22
- _CARTOGRAPHER_PLUGIN_CONFIG="{}"
23
- if [[ -f "$plugin_config" ]]; then
24
- _CARTOGRAPHER_PLUGIN_CONFIG=$(cat "$plugin_config")
25
- fi
26
-
27
- local home_settings="{}"
28
- if [[ -f "$HOME/.claude/settings.json" ]]; then
29
- home_settings=$(cat "$HOME/.claude/settings.json")
30
- fi
31
-
32
- local repo_settings="{}"
33
- if [[ -n "$repo_root" && -f "$repo_root/.claude/settings.json" ]]; then
34
- repo_settings=$(cat "$repo_root/.claude/settings.json")
35
- fi
36
-
37
- _CARTOGRAPHER_CONFIG=$(jq -n \
38
- --argjson plugin "$_CARTOGRAPHER_PLUGIN_CONFIG" \
39
- --argjson home "$home_settings" \
40
- --argjson repo "$repo_settings" \
41
- '$plugin * {"cartographer": (($plugin.cartographer // {}) * ($home.cartographer // {}) * ($repo.cartographer // {}))}')
22
+ config_load_plugin "cartographer" "$repo_root" "_CARTOGRAPHER_CONFIG"
23
+ return 0
42
24
  }
43
25
 
44
26
  cartographer_config_get() {
45
27
  local path="${1:-}"
46
- printf '%s' "$_CARTOGRAPHER_CONFIG" | jq -r "$path // empty" 2>/dev/null
28
+ config_get "_CARTOGRAPHER_CONFIG" "${path}"
47
29
  }
48
30
 
49
31
  cartographer_config_get_json() {
50
32
  local path="${1:-}"
51
- printf '%s' "$_CARTOGRAPHER_CONFIG" | jq -c "$path // empty" 2>/dev/null
33
+ config_get_json "_CARTOGRAPHER_CONFIG" "${path}"
52
34
  }
53
35
 
54
36
  cartographer_config_model_extraction() {
@@ -1,65 +1,35 @@
1
1
  #!/usr/bin/env bash
2
- # Config resolution for Compass.
2
+ # Config resolution for compass.
3
3
  #
4
- # Reads three layers, latest wins:
4
+ # Uses the shared config loader from ecosystem. Reads five layers, latest wins:
5
5
  # 1. plugins/compass/config.json (defaults shipped with the plugin)
6
6
  # 2. ~/.claude/settings.json
7
- # 3. <repo>/.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
11
  # Exposes:
10
- # compass_config_load <repo_root> # populates _COMPASS_CONFIG (JSON)
11
- # compass_config_get <jq-path> # echoes string value (empty if unset)
12
- # compass_config_get_json <jq-path> # echoes JSON value (null if unset)
12
+ # compass_config_load <repo_root> # populates _compass_CONFIG (JSON)
13
+ # compass_config_get <jq-path> # echoes string value (empty if unset)
14
+ # compass_config_get_json <jq-path> # echoes JSON value (null if unset)
13
15
 
14
- _COMPASS_CONFIG="{}"
16
+ # shellcheck source=../../../scripts/lib/config-loader.sh
17
+ source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
18
+
19
+ _compass_CONFIG="{}"
15
20
 
16
21
  compass_config_load() {
17
22
  local repo_root="${1:-}"
18
- local plugin_root="${CLAUDE_PLUGIN_ROOT:-}"
19
- local home_dir="${HOME:-}"
20
-
21
- local merged="{}"
22
- local file
23
-
24
- file="${plugin_root}/config.json"
25
- if [[ -f "$file" ]]; then
26
- local defaults
27
- defaults=$(jq '.' "$file" 2>/dev/null) || defaults="{}"
28
- merged=$(jq -n --argjson a "$merged" --argjson b "$defaults" '$a * $b' 2>/dev/null) \
29
- || merged="$defaults"
30
- fi
31
-
32
- local repo_settings=""
33
- [[ -n "$repo_root" ]] && repo_settings="${repo_root}/.claude/settings.json"
34
-
35
- for file in "${home_dir}/.claude/settings.json" "$repo_settings"; do
36
- [[ -n "$file" && -f "$file" ]] || continue
37
- local overlay
38
- overlay=$(jq '{ compass: (.compass // {}) }' "$file" 2>/dev/null) || continue
39
- [[ -z "$overlay" ]] && continue
40
- local attempt
41
- if attempt=$(jq -n --argjson a "$merged" --argjson b "$overlay" '
42
- def deepmerge($a; $b):
43
- if ($a|type) == "object" and ($b|type) == "object" then
44
- reduce (($a|keys) + ($b|keys) | unique)[] as $k
45
- ({}; .[$k] = deepmerge($a[$k]; $b[$k]))
46
- elif $b == null then $a
47
- else $b end;
48
- deepmerge($a; $b)
49
- ' 2>/dev/null) && [[ -n "$attempt" ]]; then
50
- merged="$attempt"
51
- fi
52
- done
53
-
54
- _COMPASS_CONFIG="$merged"
23
+ config_load_plugin "compass" "$repo_root" "_compass_CONFIG"
24
+ return 0
55
25
  }
56
26
 
57
27
  compass_config_get() {
58
28
  local path="$1"
59
- printf '%s' "$_COMPASS_CONFIG" | jq -r "${path} // empty" 2>/dev/null
29
+ config_get "_compass_CONFIG" "${path}"
60
30
  }
61
31
 
62
32
  compass_config_get_json() {
63
33
  local path="$1"
64
- printf '%s' "$_COMPASS_CONFIG" | jq -c "${path}" 2>/dev/null
34
+ config_get_json "_compass_CONFIG" "${path}"
65
35
  }
@@ -1,66 +1,35 @@
1
1
  #!/usr/bin/env bash
2
- # Config resolution for Counsel.
2
+ # Config resolution for counsel.
3
3
  #
4
- # Three-layer merge, latest wins:
5
- # 1. plugins/counsel/config.json (plugin defaults)
4
+ # Uses the shared config loader from ecosystem. Reads five layers, latest wins:
5
+ # 1. plugins/counsel/config.json (defaults shipped with the plugin)
6
6
  # 2. ~/.claude/settings.json
7
- # 3. <repo>/.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
11
  # Exposes:
10
- # counsel_config_load <repo_root> # populates _COUNSEL_CONFIG (JSON)
12
+ # counsel_config_load <repo_root> # populates _counsel_CONFIG (JSON)
11
13
  # counsel_config_get <jq-path> # echoes string value (empty if unset)
12
14
  # counsel_config_get_json <jq-path> # echoes JSON value (null if unset)
13
15
 
14
- _COUNSEL_CONFIG="{}"
16
+ # shellcheck source=../../../scripts/lib/config-loader.sh
17
+ source "${PLUGIN_ROOT}/../../scripts/lib/config-loader.sh"
18
+
19
+ _counsel_CONFIG="{}"
15
20
 
16
21
  counsel_config_load() {
17
22
  local repo_root="${1:-}"
18
- local plugin_root="${CLAUDE_PLUGIN_ROOT:-}"
19
- local home_dir="${HOME:-}"
20
-
21
- local merged="{}"
22
- local file
23
-
24
- file="${plugin_root}/config.json"
25
- if [[ -f "$file" ]]; then
26
- local defaults
27
- defaults=$(jq '.' "$file" 2>/dev/null) || defaults="{}"
28
- merged=$(jq -n --argjson a "$merged" --argjson b "$defaults" '$a * $b' 2>/dev/null) \
29
- || merged="$defaults"
30
- fi
31
-
32
- local repo_settings=""
33
- [[ -n "$repo_root" ]] && repo_settings="${repo_root}/.claude/settings.json"
34
-
35
- for file in "${home_dir}/.claude/settings.json" "$repo_settings"; do
36
- [[ -n "$file" && -f "$file" ]] || continue
37
- local overlay
38
- overlay=$(jq '{ counsel: (.counsel // {}) }' "$file" 2>/dev/null) || continue
39
- [[ -z "$overlay" ]] && continue
40
- local attempt
41
- if attempt=$(jq -n --argjson a "$merged" --argjson b "$overlay" '
42
- def deepmerge($a; $b):
43
- if ($a|type) == "object" and ($b|type) == "object" then
44
- reduce (($a|keys) + ($b|keys) | unique)[] as $k
45
- ({}; .[$k] = deepmerge($a[$k]; $b[$k]))
46
- elif $b == null then $a
47
- else $b end;
48
- deepmerge($a; $b)
49
- ' 2>/dev/null) && [[ -n "$attempt" ]]; then
50
- merged="$attempt"
51
- fi
52
- done
53
-
54
- _COUNSEL_CONFIG="$merged"
23
+ config_load_plugin "counsel" "$repo_root" "_counsel_CONFIG"
24
+ return 0
55
25
  }
56
26
 
57
27
  counsel_config_get() {
58
28
  local path="$1"
59
- printf '%s' "$_COUNSEL_CONFIG" | jq -r "${path} // empty" 2>/dev/null
29
+ config_get "_counsel_CONFIG" "${path}"
60
30
  }
61
31
 
62
32
  counsel_config_get_json() {
63
33
  local path="$1"
64
- printf '%s' "$_COUNSEL_CONFIG" | jq -c "${path}" 2>/dev/null
34
+ config_get_json "_counsel_CONFIG" "${path}"
65
35
  }
66
-