@onlooker-community/ecosystem 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.claude-plugin/marketplace.json +39 -0
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.release-please-manifest.json +5 -2
  4. package/CHANGELOG.md +15 -0
  5. package/CLAUDE.md +88 -0
  6. package/package.json +2 -2
  7. package/plugins/compass/.claude-plugin/plugin.json +14 -0
  8. package/plugins/compass/CHANGELOG.md +8 -0
  9. package/plugins/compass/config.json +71 -0
  10. package/plugins/compass/docs/adr/001-evaluate-prompts-in-context.md +82 -0
  11. package/plugins/compass/docs/design.md +421 -0
  12. package/plugins/compass/hooks/hooks.json +82 -0
  13. package/plugins/compass/scripts/hooks/compass-bash-gate.sh +95 -0
  14. package/plugins/compass/scripts/hooks/compass-pre-tool-use.sh +86 -0
  15. package/plugins/compass/scripts/hooks/compass-record-write.sh +97 -0
  16. package/plugins/compass/scripts/hooks/compass-session-start.sh +77 -0
  17. package/plugins/compass/scripts/lib/compass-config.sh +72 -0
  18. package/plugins/compass/scripts/lib/compass-evaluator.sh +374 -0
  19. package/plugins/compass/scripts/lib/compass-events.sh +81 -0
  20. package/plugins/compass/scripts/lib/compass-gate.sh +465 -0
  21. package/plugins/compass/scripts/lib/compass-sanitizer.sh +82 -0
  22. package/plugins/compass/scripts/lib/compass-transcript.sh +135 -0
  23. package/plugins/counsel/.claude-plugin/plugin.json +14 -0
  24. package/plugins/counsel/CHANGELOG.md +8 -0
  25. package/plugins/counsel/config.json +20 -0
  26. package/plugins/counsel/hooks/hooks.json +15 -0
  27. package/plugins/counsel/scripts/hooks/counsel-session-start.sh +106 -0
  28. package/plugins/counsel/scripts/lib/counsel-brief.sh +247 -0
  29. package/plugins/counsel/scripts/lib/counsel-config.sh +72 -0
  30. package/plugins/counsel/scripts/lib/counsel-events.sh +80 -0
  31. package/plugins/counsel/scripts/lib/counsel-project-key.sh +79 -0
  32. package/plugins/counsel/scripts/lib/counsel-reader.sh +114 -0
  33. package/plugins/counsel/scripts/lib/counsel-synthesize.sh +103 -0
  34. package/plugins/counsel/scripts/lib/counsel-ulid.sh +45 -0
  35. package/plugins/governor/.claude-plugin/plugin.json +1 -1
  36. package/plugins/governor/CHANGELOG.md +7 -0
  37. package/plugins/scribe/.claude-plugin/plugin.json +12 -0
  38. package/plugins/scribe/CHANGELOG.md +8 -0
  39. package/plugins/scribe/config.json +20 -0
  40. package/plugins/scribe/hooks/hooks.json +37 -0
  41. package/plugins/scribe/scripts/hooks/scribe-capture.sh +76 -0
  42. package/plugins/scribe/scripts/hooks/scribe-session-start.sh +58 -0
  43. package/plugins/scribe/scripts/hooks/scribe-stop.sh +67 -0
  44. package/plugins/scribe/scripts/lib/scribe-config.sh +72 -0
  45. package/plugins/scribe/scripts/lib/scribe-distill.sh +239 -0
  46. package/plugins/scribe/scripts/lib/scribe-events.sh +80 -0
  47. package/plugins/scribe/scripts/lib/scribe-extract.sh +147 -0
  48. package/plugins/scribe/scripts/lib/scribe-project-key.sh +89 -0
  49. package/plugins/scribe/scripts/lib/scribe-ulid.sh +50 -0
  50. package/release-please-config.json +48 -0
  51. package/test/bats/counsel-project-key.bats +82 -0
  52. package/test/bats/counsel-reader.bats +132 -0
  53. package/test/bats/scribe-extract.bats +102 -0
  54. package/test/bats/scribe-project-key.bats +75 -0
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env bash
2
+ # Compass PreToolUse hook — main alignment gate for Write, Edit, MultiEdit.
3
+ #
4
+ # Fires before write-class tool calls. Resolves the file path from the
5
+ # tool input and delegates to the shared compass-gate.sh pipeline.
6
+ #
7
+ # Hook contract (Claude Code PreToolUse protocol):
8
+ # - Always exits 0.
9
+ # - To block: compass_run_gate writes {"decision":"block","reason":"..."} to stdout.
10
+ # - To allow: nothing written to stdout.
11
+ # - Errors are written to stderr only.
12
+
13
+ set -uo pipefail
14
+
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
17
+
18
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
19
+
20
+ # shellcheck source=../lib/compass-config.sh
21
+ source "${PLUGIN_ROOT}/scripts/lib/compass-config.sh"
22
+ # shellcheck source=../lib/compass-events.sh
23
+ source "${PLUGIN_ROOT}/scripts/lib/compass-events.sh"
24
+ # shellcheck source=../lib/compass-sanitizer.sh
25
+ source "${PLUGIN_ROOT}/scripts/lib/compass-sanitizer.sh"
26
+ # shellcheck source=../lib/compass-transcript.sh
27
+ source "${PLUGIN_ROOT}/scripts/lib/compass-transcript.sh"
28
+ # shellcheck source=../lib/compass-evaluator.sh
29
+ source "${PLUGIN_ROOT}/scripts/lib/compass-evaluator.sh"
30
+ # shellcheck source=../lib/compass-gate.sh
31
+ source "${PLUGIN_ROOT}/scripts/lib/compass-gate.sh"
32
+
33
+ INPUT=$(cat)
34
+ SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // ""' 2>/dev/null) || SESSION_ID=""
35
+ CWD=$(printf '%s' "$INPUT" | jq -r '.cwd // ""' 2>/dev/null) || CWD=""
36
+ TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null) || TOOL_NAME=""
37
+
38
+ export _HOOK_SESSION_ID="$SESSION_ID"
39
+
40
+ compass_config_load "$CWD"
41
+
42
+ if ! compass_config_enabled; then
43
+ exit 0
44
+ fi
45
+
46
+ # -----------------------------------------------------------------------
47
+ # Resolve file path and context from tool input.
48
+ # -----------------------------------------------------------------------
49
+ FILE_PATH=""
50
+ CONTEXT=""
51
+ OPERATION=""
52
+
53
+ case "$TOOL_NAME" in
54
+ Write)
55
+ FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // ""' 2>/dev/null) || FILE_PATH=""
56
+ CONTEXT=$(printf '%s' "$INPUT" | jq -r '.tool_input.content // ""' 2>/dev/null) || CONTEXT=""
57
+ OPERATION="write"
58
+ ;;
59
+ Edit)
60
+ FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // ""' 2>/dev/null) || FILE_PATH=""
61
+ # Context: the old_string + new_string gives meaningful signal about what's changing.
62
+ _old_str=$(printf '%s' "$INPUT" | jq -r '.tool_input.old_string // ""' 2>/dev/null) || _old_str=""
63
+ _new_str=$(printf '%s' "$INPUT" | jq -r '.tool_input.new_string // ""' 2>/dev/null) || _new_str=""
64
+ CONTEXT="Replacing: ${_old_str} With: ${_new_str}"
65
+ OPERATION="edit"
66
+ ;;
67
+ MultiEdit)
68
+ # For MultiEdit, use the first file path and a summary as context.
69
+ FILE_PATH=$(printf '%s' "$INPUT" \
70
+ | jq -r '.tool_input.edits[0].file_path // ""' 2>/dev/null) || FILE_PATH=""
71
+ _edit_count=$(printf '%s' "$INPUT" \
72
+ | jq '.tool_input.edits | length' 2>/dev/null) || _edit_count="?"
73
+ _file_list=$(printf '%s' "$INPUT" \
74
+ | jq -r '[.tool_input.edits[].file_path] | unique | join(", ")' 2>/dev/null) \
75
+ || _file_list="(multiple files)"
76
+ CONTEXT="MultiEdit: ${_edit_count} edit(s) across: ${_file_list}"
77
+ OPERATION="multi_edit"
78
+ ;;
79
+ *)
80
+ # Unknown tool — allow through; this hook should only fire for known tools.
81
+ exit 0
82
+ ;;
83
+ esac
84
+
85
+ compass_run_gate "$TOOL_NAME" "$FILE_PATH" "$OPERATION" "$CONTEXT" "$SESSION_ID" "$CWD"
86
+ exit $?
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env bash
2
+ # Compass PostToolUse hook — cooldown recorder.
3
+ #
4
+ # Fires after a successful Write, Edit, or MultiEdit. Records the file
5
+ # path's dir+stem identity and a timestamp to the session state so the
6
+ # trigger gate can skip re-checking the same file within cooldown.seconds.
7
+ #
8
+ # Hook contract:
9
+ # - Always exits 0. Never blocks PostToolUse.
10
+ # - Errors are written to stderr only; stdout is kept clean.
11
+
12
+ set -uo pipefail
13
+
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
16
+
17
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
18
+
19
+ # shellcheck source=../lib/compass-config.sh
20
+ source "${PLUGIN_ROOT}/scripts/lib/compass-config.sh"
21
+
22
+ INPUT=$(cat)
23
+ SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // ""' 2>/dev/null) || SESSION_ID=""
24
+ CWD=$(printf '%s' "$INPUT" | jq -r '.cwd // ""' 2>/dev/null) || CWD=""
25
+
26
+ _done() { exit 0; }
27
+
28
+ compass_config_load "$CWD"
29
+
30
+ if ! compass_config_enabled; then
31
+ _done
32
+ fi
33
+
34
+ [[ -z "$SESSION_ID" ]] && _done
35
+
36
+ # Extract the file path from the tool output.
37
+ TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null) || TOOL_NAME=""
38
+ FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""' 2>/dev/null) || FILE_PATH=""
39
+
40
+ # MultiEdit: record all target paths.
41
+ if [[ "$TOOL_NAME" == "MultiEdit" ]]; then
42
+ MULTI_PATHS=$(printf '%s' "$INPUT" \
43
+ | jq -r '.tool_input.edits[]?.file_path // empty' 2>/dev/null) || MULTI_PATHS=""
44
+ fi
45
+
46
+ ONLOOKER_DIR="${ONLOOKER_DIR:-${HOME}/.onlooker}"
47
+ STATE_FILE="${ONLOOKER_DIR}/compass/sessions/${SESSION_ID}.json"
48
+
49
+ [[ -f "$STATE_FILE" ]] || _done
50
+
51
+ # Compute dir+stem identity for a file path.
52
+ _dir_plus_stem() {
53
+ local path="$1"
54
+ [[ -z "$path" ]] && return 1
55
+ local dir base stem
56
+ dir=$(dirname "$path" 2>/dev/null) || dir="."
57
+ base=$(basename "$path" 2>/dev/null) || base="$path"
58
+ # Stem = everything before the first dot in the basename.
59
+ stem="${base%%.*}"
60
+ [[ -z "$stem" ]] && stem="$base"
61
+ printf '%s/%s' "$dir" "$stem"
62
+ }
63
+
64
+ _record_path() {
65
+ local path="$1"
66
+ [[ -z "$path" ]] && return
67
+
68
+ local identity
69
+ identity=$(_dir_plus_stem "$path") || return
70
+
71
+ local now
72
+ now=$(date +%s 2>/dev/null) || now=0
73
+
74
+ local updated
75
+ updated=$(jq \
76
+ --arg identity "$identity" \
77
+ --arg path "$path" \
78
+ --argjson ts "$now" \
79
+ '.cooldown = (
80
+ [.cooldown[] | select(.identity != $identity)]
81
+ + [{"identity": $identity, "path": $path, "ts": $ts}]
82
+ )' "$STATE_FILE" 2>/dev/null) || return
83
+
84
+ [[ -n "$updated" ]] && printf '%s' "$updated" > "$STATE_FILE"
85
+ }
86
+
87
+ if [[ -n "$FILE_PATH" ]]; then
88
+ _record_path "$FILE_PATH"
89
+ fi
90
+
91
+ if [[ "$TOOL_NAME" == "MultiEdit" && -n "${MULTI_PATHS:-}" ]]; then
92
+ while IFS= read -r p; do
93
+ [[ -n "$p" ]] && _record_path "$p"
94
+ done <<< "$MULTI_PATHS"
95
+ fi
96
+
97
+ _done
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ # Compass SessionStart hook.
3
+ #
4
+ # Fires at every session start. Responsibilities:
5
+ # 1. Skip silently when compass.enabled is false.
6
+ # 2. Create storage directories.
7
+ # 3. Initialize session state file:
8
+ # - turn_check_count: 0
9
+ # - cooldown table: empty
10
+ # - circuit_breaker: {state: "closed", consecutive_failures: 0}
11
+ #
12
+ # Hook contract:
13
+ # - Always exits 0. Never blocks SessionStart.
14
+ # - Errors are written to stderr only; stdout is kept clean.
15
+
16
+ set -uo pipefail
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
+ PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
20
+
21
+ _ECOSYSTEM_ROOT="${ONLOOKER_ECOSYSTEM_ROOT:-}"
22
+ if [[ -z "$_ECOSYSTEM_ROOT" ]]; then
23
+ _candidate="$(cd "${PLUGIN_ROOT}/../.." 2>/dev/null && pwd)"
24
+ if [[ -f "${_candidate}/scripts/lib/validate-path.sh" ]]; then
25
+ _ECOSYSTEM_ROOT="$_candidate"
26
+ fi
27
+ fi
28
+
29
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
30
+
31
+ # shellcheck source=../lib/compass-config.sh
32
+ source "${PLUGIN_ROOT}/scripts/lib/compass-config.sh"
33
+ # shellcheck source=../lib/compass-events.sh
34
+ source "${PLUGIN_ROOT}/scripts/lib/compass-events.sh"
35
+
36
+ INPUT=$(cat)
37
+ SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // ""' 2>/dev/null) || SESSION_ID=""
38
+ CWD=$(printf '%s' "$INPUT" | jq -r '.cwd // ""' 2>/dev/null) || CWD=""
39
+
40
+ _done() { exit 0; }
41
+
42
+ compass_config_load "$CWD"
43
+
44
+ if ! compass_config_enabled; then
45
+ _done
46
+ fi
47
+
48
+ export _HOOK_SESSION_ID="$SESSION_ID"
49
+
50
+ ONLOOKER_DIR="${ONLOOKER_DIR:-${HOME}/.onlooker}"
51
+ COMPASS_STATE_DIR="${ONLOOKER_DIR}/compass/sessions"
52
+ mkdir -p "$COMPASS_STATE_DIR" 2>/dev/null || true
53
+
54
+ if [[ -z "$SESSION_ID" ]]; then
55
+ printf 'compass-session-start: no session_id in hook input\n' >&2
56
+ _done
57
+ fi
58
+
59
+ STATE_FILE="${COMPASS_STATE_DIR}/${SESSION_ID}.json"
60
+
61
+ jq -n \
62
+ --arg sid "$SESSION_ID" \
63
+ '{
64
+ session_id: $sid,
65
+ turn_check_count: 0,
66
+ cooldown: [],
67
+ circuit_breaker: {
68
+ state: "closed",
69
+ consecutive_failures: 0,
70
+ opened_at: null
71
+ }
72
+ }' 2>/dev/null > "$STATE_FILE" || {
73
+ printf 'compass-session-start: failed to write state file %s\n' "$STATE_FILE" >&2
74
+ _done
75
+ }
76
+
77
+ _done
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env bash
2
+ # Config resolution for Compass.
3
+ #
4
+ # Reads three layers, latest wins:
5
+ # 1. plugins/compass/config.json (defaults shipped with the plugin)
6
+ # 2. ~/.claude/settings.json
7
+ # 3. <repo>/.claude/settings.json
8
+ #
9
+ # 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)
13
+ # compass_config_enabled # 0 if compass.enabled is true
14
+
15
+ _COMPASS_CONFIG="{}"
16
+
17
+ compass_config_load() {
18
+ local repo_root="${1:-}"
19
+ local plugin_root="${CLAUDE_PLUGIN_ROOT:-}"
20
+ local home_dir="${HOME:-}"
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 '{ compass: (.compass // {}) }' "$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
+ _COMPASS_CONFIG="$merged"
56
+ }
57
+
58
+ compass_config_get() {
59
+ local path="$1"
60
+ printf '%s' "$_COMPASS_CONFIG" | jq -r "${path} // empty" 2>/dev/null
61
+ }
62
+
63
+ compass_config_get_json() {
64
+ local path="$1"
65
+ printf '%s' "$_COMPASS_CONFIG" | jq -c "${path}" 2>/dev/null
66
+ }
67
+
68
+ compass_config_enabled() {
69
+ local v
70
+ v=$(compass_config_get '.compass.enabled')
71
+ [[ "$v" == "true" ]]
72
+ }