@onlooker-community/ecosystem 0.0.2 → 0.2.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.
@@ -12,7 +12,7 @@
12
12
  "name": "ecosystem",
13
13
  "source": "./",
14
14
  "description": "Fill this out",
15
- "version": "0.0.2",
15
+ "version": "0.2.0",
16
16
  "author": {
17
17
  "name": "Onlooker Community"
18
18
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecosystem",
3
- "version": "0.0.2",
3
+ "version": "0.2.0",
4
4
  "description": "TODO fill this out",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -9,9 +9,6 @@
9
9
  "homepage": "https://onlooker.dev",
10
10
  "repository": "https://github.com/onlooker-community/ecosystem",
11
11
  "license": "MIT",
12
- "skills": [
13
- "./skills/",
14
- "./commands/"
15
- ],
12
+ "skills": [],
16
13
  "agents": []
17
14
  }
@@ -0,0 +1,26 @@
1
+ name: Test
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ jobs:
10
+ test:
11
+ name: Test
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install mise
17
+ uses: jdx/mise-action@v2
18
+
19
+ - name: Install tools
20
+ run: mise install
21
+
22
+ - name: Install Node dependencies
23
+ run: npm ci
24
+
25
+ - name: Run CI test suite
26
+ run: npm run test:ci
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.0.2"
2
+ ".": "0.2.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.0](https://github.com/onlooker-community/ecosystem/compare/v0.1.0...v0.2.0) (2026-05-21)
4
+
5
+
6
+ ### Features
7
+
8
+ * **hooks:** emit canonical schema events for tool history :sparkles: ([1e49a24](https://github.com/onlooker-community/ecosystem/commit/1e49a24bfb930942fa477b594395ef352618f574))
9
+ * **hooks:** track tool call sequence on every PreToolUse :sparkles: ([0ad9546](https://github.com/onlooker-community/ecosystem/commit/0ad95465cc22a237e26115a67814a6e7b2951b1d))
10
+
11
+
12
+ ### Chores
13
+
14
+ * **deps:** use published @onlooker-community/schema from npm :relieved: ([efc92d8](https://github.com/onlooker-community/ecosystem/commit/efc92d8171592aa5a5f1c27853387e810fee612f))
15
+
16
+ ## [0.1.0](https://github.com/onlooker-community/ecosystem/compare/v0.0.3...v0.1.0) (2026-05-21)
17
+
18
+
19
+ ### Features
20
+
21
+ * add configuration and hooks for agent spawn tracking ([3ef4590](https://github.com/onlooker-community/ecosystem/commit/3ef459006bbbda246604bdd1ffaf9af0a59f9740))
22
+
23
+
24
+ ### Chores
25
+
26
+ * clean up README.md by removing outdated badge links ([42d47d6](https://github.com/onlooker-community/ecosystem/commit/42d47d602aa7b68db719874a1cf4193433d1bd68))
27
+ * remove skills from plugin.json to streamline configuration ([fdfd8eb](https://github.com/onlooker-community/ecosystem/commit/fdfd8eb4faa0c807eff97feb1a20961de1fe154d))
28
+
29
+ ## [0.0.3](https://github.com/onlooker-community/ecosystem/compare/v0.0.2...v0.0.3) (2026-05-21)
30
+
31
+
32
+ ### Chores
33
+
34
+ * update release-please configuration to include custom pull request title pattern ([e860f1c](https://github.com/onlooker-community/ecosystem/commit/e860f1c5a7b58909a53ec38a3b3da89f22f0434c))
35
+
3
36
  ## [0.0.2](https://github.com/onlooker-community/ecosystem/compare/v0.0.1...v0.0.2) (2026-05-21)
4
37
 
5
38
 
package/README.md CHANGED
@@ -1,21 +1,23 @@
1
1
  # Onlooker Ecosystem
2
2
 
3
- [![Stars](https://img.shields.io/github/stars/onlooker-community/ecosystem?style=flat)](https://github.com/onlooker-community/ecosystem/stargazers)
4
- [![Forks](https://img.shields.io/github/forks/onlooker-community/ecosysem?style=flat)](https://github.com/onlooker-community/ecosystem/network/members)
5
- [![Contributors](https://img.shields.io/github/contributors/onlooker-community/ecosysetm?style=flat)](https://github.com/onlooker-community/ecosystem/graphs/contributors)
6
- [![License](https://img.shields.io/badge/license-BlueOak-blue.svg)](LICENSE)
7
- ![Shell](https://img.shields.io/badge/-Shell-4EAA25?logo=gnu-bash&logoColor=white)
8
- ![TypeScript](https://img.shields.io/badge/-TypeScript-3178C6?logo=typescript&logoColor=white)
9
- ![Python](https://img.shields.io/badge/-Python-3776AB?logo=python&logoColor=white)
10
- ![Go](https://img.shields.io/badge/-Go-00ADD8?logo=go&logoColor=white)
11
- ![Ruby](https://img.shields.io/badge/-Ruby-ED8B00?logo=openjdk&logoColor=white)
12
- ![Markdown](https://img.shields.io/badge/-Markdown-000000?logo=markdown&logoColor=white)
3
+ Agents, skills, hooks, commands, rules, and MCP configurations that power [Onlooker](https://onlooker.dev).
13
4
 
14
5
  ---
15
6
 
16
- Agents, skills, hooks, commands, rules, and MCP configurations that power [Onlooker](https://onlooker.dev).
7
+ ## Development
17
8
 
18
- ---
9
+ Install tools with [mise](https://mise.jdx.dev/) (`mise install`), then install dependencies (includes [`@onlooker-community/schema`](https://www.npmjs.com/package/@onlooker-community/schema) from npm):
10
+
11
+ ```bash
12
+ npm ci
13
+ npm test # bats + schema validation tests
14
+ npm run test:shellcheck
15
+ npm run test:ci # shellcheck + bats + schema + lint
16
+ ```
17
+
18
+ Hooks emit [canonical Onlooker events](https://github.com/onlooker-community/schema) via `scripts/lib/onlooker-event.mjs`. Bash helpers live in `scripts/lib/onlooker-schema.sh`.
19
+
20
+ Tests live under `test/bats/` and `test/node/` and use an isolated temp home so nothing writes to your real `~/.onlooker`.
19
21
 
20
22
  ## Quick Start
21
23
 
package/config.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "plugin_name": "onlooker",
3
+ "storage_path": "~/.onlooker"
4
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "*",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "\"$CLAUDE_PLUGIN_ROOT\"/scripts/hooks/tool-sequence-tracker.sh"
10
+ }
11
+ ]
12
+ },
13
+ {
14
+ "matcher": "Agent",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "\"$CLAUDE_PLUGIN_ROOT\"/scripts/hooks/agent-spawn-tracker.sh"
19
+ }
20
+ ]
21
+ }
22
+ ],
23
+ "PostToolUse": [
24
+ {
25
+ "matcher": "*",
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "\"$CLAUDE_PLUGIN_ROOT\"/scripts/hooks/tool-history-tracker.sh"
30
+ }
31
+ ]
32
+ }
33
+ ],
34
+ "PostToolUseFailure": [
35
+ {
36
+ "matcher": "*",
37
+ "hooks": [
38
+ {
39
+ "type": "command",
40
+ "command": "\"$CLAUDE_PLUGIN_ROOT\"/scripts/hooks/tool-history-tracker.sh"
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ }
46
+ }
package/mise.toml CHANGED
@@ -1,2 +1,13 @@
1
1
  [tools]
2
2
  node = "26.1.0"
3
+ bats = "1.13.0"
4
+ shellcheck = "0.10.0"
5
+ jq = "1.7.1"
6
+
7
+ [tasks.test]
8
+ description = "Run bats test suite"
9
+ run = "bats test/bats"
10
+
11
+ [tasks."test:shellcheck"]
12
+ description = "Run shellcheck on shell scripts"
13
+ run = "shellcheck -S error -x install.sh scripts/common.sh scripts/hooks/*.sh scripts/lib/*.sh"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlooker-community/ecosystem",
3
- "version": "0.0.2",
3
+ "version": "0.2.0",
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",
@@ -18,9 +18,17 @@
18
18
  "bin": {
19
19
  "onlooker-install": "install.sh"
20
20
  },
21
+ "dependencies": {
22
+ "@onlooker-community/schema": "^1.3.0"
23
+ },
21
24
  "scripts": {
22
25
  "postinstall": "echo '\\n onlooker-ecosystem installed!\\n Run: npx onlooker-install typescript\\n Docs: https://github.com/onlooker-community/ecosystem\\n'",
23
- "lint:check": "biome check . && markdownlint '**/*.md' --ignore node_modules",
26
+ "test": "npm run test:bats && npm run test:schema",
27
+ "test:bats": "bats test/bats",
28
+ "test:schema": "node --test test/node/*.test.mjs",
29
+ "test:shellcheck": "shellcheck -S error -x install.sh scripts/common.sh scripts/hooks/*.sh scripts/lib/*.sh",
30
+ "test:ci": "npm run test:shellcheck && npm run test:bats && npm run test:schema && npm run lint:check",
31
+ "lint:check": "biome check . && markdownlint '**/*.md' --ignore node_modules --ignore CHANGELOG.md",
24
32
  "lint": "biome lint --write",
25
33
  "format": "biome format --write",
26
34
  "format:check": "biome format",
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+ # Shared helpers for Onlooker hook scripts.
3
+ # Requires validate-path.sh and onlooker-schema.sh.
4
+
5
+ # Emit canonical tool.agent.spawn telemetry event (schema payload).
6
+ # Usage: onlooker_emit_tool_agent_spawn SESSION_ID SUBAGENT_TYPE DESCRIPTION MODEL RUN_IN_BACKGROUND ISOLATION
7
+ onlooker_emit_tool_agent_spawn() {
8
+ local session_id="$1"
9
+ local subagent_type="$2"
10
+ local description="$3"
11
+ local _model="$4"
12
+ local _run_in_background="$5"
13
+ local _isolation="$6"
14
+
15
+ local subagent_id="${session_id}-$(date +%s)"
16
+
17
+ local params
18
+ params=$(jq -n \
19
+ --arg plugin "${ONLOOKER_PLUGIN_NAME:-onlooker}" \
20
+ --arg sid "$session_id" \
21
+ --arg subagent_id "$subagent_id" \
22
+ --arg agent_name "$subagent_type" \
23
+ --arg task_summary "$description" \
24
+ '{
25
+ plugin: $plugin,
26
+ session_id: $sid,
27
+ event_type: "tool.agent.spawn",
28
+ payload: {
29
+ subagent_id: $subagent_id,
30
+ agent_name: $agent_name,
31
+ task_summary: $task_summary
32
+ }
33
+ }')
34
+
35
+ local event
36
+ event=$(printf '%s' "$params" | ONLOOKER_DIR="$ONLOOKER_DIR" ONLOOKER_PLUGIN_NAME="$ONLOOKER_PLUGIN_NAME" \
37
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/lib/onlooker-event.mjs" emit 2>/dev/null) || return 1
38
+
39
+ onlooker_append_event "$event"
40
+ }
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env bash
2
+ # Onlooker Agent Spawn Tracker Script
3
+ # Invoked by the PreToolUse hook (via command) when an Agent tool is used.
4
+ #
5
+ # Usage:
6
+ # echo "$INPUT" | agent-spawn-tracker.sh
7
+ #
8
+ # Input:
9
+ # {
10
+ # "session_id": "123",
11
+ # "tool_name": "Agent",
12
+ # "tool_input": {
13
+ # "agent_id": "456"
14
+ # }
15
+ # }
16
+
17
+ set -uo pipefail # No -e: we must never exit non-zero and block the hook
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ source "$SCRIPT_DIR/../lib/validate-path.sh"
21
+ source "$SCRIPT_DIR/../lib/onlooker-schema.sh"
22
+ source "${CLAUDE_PLUGIN_ROOT:-$SCRIPT_DIR/../..}/scripts/common.sh"
23
+
24
+ hook_register "agent-spawn-tracker" "Agent Spawn Tracker" "Tracks when an agent is spawned"
25
+
26
+ INPUT=$(cat)
27
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "Unknown"')
28
+
29
+ hook_set_context "$INPUT" "PreToolUse"
30
+
31
+ json_response() {
32
+ jq -n --arg decision "$1" --arg reason "$2" '{ "decision": $decision, "reason": $reason }'
33
+ }
34
+
35
+ # Only process Agent tool calls
36
+ if [[ "$TOOL_NAME" != "Agent" ]]; then
37
+ json_response "approve" "Not an Agent tool call"
38
+ hook_success
39
+ exit 0
40
+ fi
41
+
42
+ # Extract agent parameters
43
+ SUBAGENT_TYPE=$(jq -r '.tool_input.subagent_type // "general-purpose"' <<<"$INPUT")
44
+ DESCRIPTION=$(jq -r '.tool_input.description // ""' <<<"$INPUT")
45
+ RUN_IN_BACKGROUND=$(jq -r '.tool_input.run_in_background // false' <<<"$INPUT")
46
+ ISOLATION=$(jq -r '.tool_input.isolation // "worktree"' <<<"$INPUT")
47
+ MODEL=$(jq -r '.tool_input.model // "sonnet"' <<<"$INPUT")
48
+ MAX_TURNS=$(jq -r '.tool_input.max_turns // 10' <<<"$INPUT")
49
+ TOOLS=$(jq -r '.tool_input.tools // []' <<<"$INPUT")
50
+ DISALLOWED_TOOLS=$(jq -r '.tool_input.disallowed_tools // []' <<<"$INPUT")
51
+ SKILLS=$(jq -r '.tool_input.skills // []' <<<"$INPUT")
52
+ MEMORY=$(jq -r '.tool_input.memory // false' <<<"$INPUT")
53
+ BACKGROUND=$(jq -r '.tool_input.background // false' <<<"$INPUT")
54
+ ISOLATION=$(jq -r '.tool_input.isolation // "worktree"' <<<"$INPUT")
55
+ MODEL=$(jq -r '.tool_input.model // "sonnet"' <<<"$INPUT")
56
+ MAX_TURNS=$(jq -r '.tool_input.max_turns // 10' <<<"$INPUT")
57
+ TOOLS=$(jq -r '.tool_input.tools // []' <<<"$INPUT")
58
+ DISALLOWED_TOOLS=$(jq -r '.tool_input.disallowed_tools // []' <<<"$INPUT")
59
+ SKILLS=$(jq -r '.tool_input.skills // []' <<<"$INPUT")
60
+ MEMORY=$(jq -r '.tool_input.memory // false' <<<"$INPUT")
61
+ BACKGROUND=$(jq -r '.tool_input.background // false' <<<"$INPUT")
62
+
63
+ # Track agent spawns in telemetry log
64
+ STATE_FILE="$ONLOOKER_DIR/agent-spawn-trackers.json"
65
+ LOCKFILE="$STATE_FILE.lock"
66
+
67
+ # Use flock for exclusive access
68
+ exec 200>"$LOCKFILE"
69
+ flock -w 5 200 || {
70
+ json_response "deny" "Failed to acquire lock"
71
+ hook_failure
72
+ exit 0
73
+ }
74
+
75
+ # Load or initialize state
76
+ if [[ -f "$STATE_FILE" ]]; then
77
+ STATE=$(jq '.' "$STATE_FILE" 2>/dev/null) || STATE='{}'
78
+ else
79
+ STATE='{}'
80
+ fi
81
+
82
+ # Get Session ID for tracking
83
+ SESSION_ID=$(jq -r '.session_id // "unknown"' <<<"$INPUT") || SESSION_ID="unknown"
84
+
85
+ # Export turn state so envelope and payload include parent lineage
86
+ turn_state_export "$SESSION_ID"
87
+
88
+ # Initialize session tracking if needed
89
+ STATE=$(jq --arg sid "$SESSION_ID" '
90
+ if .sessions[$sid] == null then
91
+ .sessions[$sid] = {
92
+ spawns: 0,
93
+ background_spawns: 0,
94
+ types: {},
95
+ first_spawn: now | strftime("%Y-%m-%dT%H:%M:%SZ"),
96
+ last_spawn: null
97
+ }
98
+ else .
99
+ end
100
+ ' <<<"$STATE")
101
+
102
+ # Save state
103
+ echo "$STATE" > "$STATE_FILE" 2>/dev/null || true
104
+
105
+ # Release lock
106
+ flock -u 200
107
+
108
+ # Get current session stats
109
+ SPAWN_COUNT=$(jq -r --arg sid "$SESSION_ID" '.sessions[$sid].spawns' <<<"$STATE")
110
+ BG_SPAWN_COUNT=$(jq -r --arg sid "$SESSION_ID" '.sessions[$sid].background_spawns' <<<"$STATE")
111
+
112
+ ## Warn on potential issues
113
+ WARNING=""
114
+
115
+ # High spawn count warning
116
+ if (( SPAWN_COUNT > 10 )); then
117
+ WARNING="Note: $SPAWN_COUNT agents spawned this session. Consider if tasks could be consolidated."
118
+ fi
119
+
120
+ # Many background agents warning (cumulative this session)
121
+ if (( BG_SPAWN_COUNT > 5 )); then
122
+ WARNING="${WARNING:+$WARNING\n}Note: $BG_SPAWN_COUNT background agents spawned this session. Consider tracking completion."
123
+ fi
124
+
125
+ # Worktree isolation note (informational)
126
+ if [[ "$ISOLATION" == "worktree" ]]; then
127
+ WARNING="${WARNING:+$WARNING\n}Info: Agent using worktree isolation - changes will be in separate branch."
128
+ fi
129
+
130
+ if [[ -n "$WARNING" ]]; then
131
+ json_response "approve" "$WARNING"
132
+ else
133
+ json_response "approve" "Agent spawn tracked (#$SPAWN_COUNT: $SUBAGENT_TYPE)"
134
+ fi
135
+
136
+ PAYLOAD=$(jq -n \
137
+ --arg type "$SUBAGENT_TYPE" \
138
+ --arg desc "$DESCRIPTION" \
139
+ --argjson bg "$RUN_IN_BACKGROUND" \
140
+ --arg isolation "$ISOLATION" \
141
+ --arg model "$MODEL" \
142
+ --argjson spawn_num "$SPAWN_COUNT" \
143
+ --arg parent_sid "$SESSION_ID" \
144
+ --arg parent_turn "${ONLOOKER_TURN_NUMBER:-}" \
145
+ '{
146
+ subagent_type: $type,
147
+ description: $desc,
148
+ run_in_background: $bg,
149
+ isolation: $isolation,
150
+ model: $model,
151
+ session_spawn_number: $spawn_num,
152
+ parent_session_id: $parent_sid
153
+ }
154
+ + (if $parent_turn != "" then {parent_turn: ($parent_turn | tonumber)} else {} end)
155
+ ')
156
+
157
+ # Canonical event: tool.agent.spawn (@onlooker-community/schema)
158
+ onlooker_emit_tool_agent_spawn "$SESSION_ID" "$SUBAGENT_TYPE" "$DESCRIPTION" "$MODEL" "$RUN_IN_BACKGROUND" "$ISOLATION" 2>/dev/null && hook_success || hook_failure "Failed to emit tool.agent.spawn event"
159
+
160
+ exit 0
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ # Onlooker Tool History Tracker
3
+ # Invoked by PostToolUse and PostToolUseFailure (matcher: *) after each tool call.
4
+ #
5
+ # Appends canonical OnlookerEvent records to:
6
+ # ~/.onlooker/session-history/<session_id>.jsonl (per-session analysis)
7
+ # ~/.onlooker/logs/onlooker-events.jsonl (global telemetry)
8
+ #
9
+ # Usage:
10
+ # echo "$INPUT" | tool-history-tracker.sh
11
+
12
+ set -uo pipefail # No -e: never block or alter tool results
13
+
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ source "$SCRIPT_DIR/../lib/validate-path.sh"
16
+ source "$SCRIPT_DIR/../lib/onlooker-schema.sh"
17
+ source "$SCRIPT_DIR/../lib/tool-history.sh"
18
+
19
+ hook_register "tool-history-tracker" "Tool History Tracker" "Records canonical tool events to session JSONL"
20
+
21
+ INPUT=$(cat)
22
+
23
+ HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // "PostToolUse"')
24
+ hook_set_context "$INPUT" "$HOOK_EVENT"
25
+
26
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
27
+ turn_state_export "$SESSION_ID"
28
+
29
+ RECORD=$(tool_history_build_record "$INPUT")
30
+ if [[ -n "$RECORD" ]]; then
31
+ tool_history_append "$SESSION_ID" "$RECORD" || hook_failure "Failed to append session history"
32
+ onlooker_append_event "$RECORD" || hook_failure "Failed to append global event log"
33
+ fi
34
+
35
+ hook_success
36
+ exit 0
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bash
2
+ # Onlooker Tool Sequence Tracker
3
+ # Invoked by PreToolUse (matcher: *) before every tool call.
4
+ #
5
+ # Increments turn_tool_seq in the session tracker so downstream hooks and
6
+ # event emission can stamp tool_call_seq on the current turn.
7
+ #
8
+ # Usage:
9
+ # echo "$INPUT" | tool-sequence-tracker.sh
10
+
11
+ set -uo pipefail # No -e: never block the tool call
12
+
13
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
+ source "$SCRIPT_DIR/../lib/validate-path.sh"
15
+
16
+ hook_register "tool-sequence-tracker" "Tool Sequence Tracker" "Increments tool call sequence within the current turn"
17
+
18
+ INPUT=$(cat)
19
+ hook_set_context "$INPUT" "PreToolUse"
20
+
21
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
22
+
23
+ json_response() {
24
+ jq -n --arg decision "$1" --arg reason "$2" '{ "decision": $decision, "reason": $reason }'
25
+ }
26
+
27
+ turn_state_next_tool "$SESSION_ID"
28
+ turn_state_export "$SESSION_ID"
29
+
30
+ json_response "approve" "Tool sequence #${ONLOOKER_TURN_TOOL_SEQ:-0} (turn ${ONLOOKER_TURN_NUMBER:-1})"
31
+ hook_success
32
+ exit 0
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ # onlooker-emit.sh - Legacy shim: emit canonical events via @onlooker-community/schema.
3
+ #
4
+ # Prefer onlooker_emit_from_hook / onlooker_append_event from onlooker-schema.sh.
5
+ #
6
+ # Usage:
7
+ # onlooker-emit.sh "tool.agent.spawn" '{"subagent_id":"x",...}'
8
+ #
9
+ # The first argument is treated as event_type; payload must match schema for that type.
10
+ set -euo pipefail
11
+
12
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+ # shellcheck source=validate-path.sh
14
+ source "$SCRIPT_DIR/validate-path.sh"
15
+ # shellcheck source=onlooker-schema.sh
16
+ source "$SCRIPT_DIR/onlooker-schema.sh"
17
+
18
+ EVENT_TYPE="$1"
19
+ PAYLOAD_JSON="$2"
20
+
21
+ SESSION_ID="${_HOOK_SESSION_ID:-}"
22
+ if [[ -z "$SESSION_ID" ]]; then
23
+ SESSION_ID=$(echo "$PAYLOAD_JSON" | jq -r '.session_id // "unknown"' 2>/dev/null) || SESSION_ID="unknown"
24
+ fi
25
+
26
+ PARAMS=$(jq -n \
27
+ --arg plugin "${ONLOOKER_PLUGIN_NAME:-onlooker}" \
28
+ --arg sid "$SESSION_ID" \
29
+ --arg type "$EVENT_TYPE" \
30
+ --argjson payload "$PAYLOAD_JSON" \
31
+ '{plugin: $plugin, session_id: $sid, event_type: $type, payload: $payload}')
32
+
33
+ EVENT=$(printf '%s' "$PARAMS" | ONLOOKER_DIR="$ONLOOKER_DIR" ONLOOKER_PLUGIN_NAME="$ONLOOKER_PLUGIN_NAME" \
34
+ node "$_ONLOOKER_EVENT_JS" emit 2>/dev/null) || {
35
+ hook_failure "Failed to build canonical event"
36
+ exit 0
37
+ }
38
+
39
+ onlooker_append_event "$EVENT"
40
+ hook_success
41
+ exit 0