@made-by-moonlight/athene-plugin-agent-claude-code 0.9.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/LICENSE +22 -0
- package/dist/activity-detection.d.ts +92 -0
- package/dist/activity-detection.d.ts.map +1 -0
- package/dist/activity-detection.js +409 -0
- package/dist/activity-detection.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1127 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Agent } from "@made-by-moonlight/athene-core";
|
|
2
|
+
export { resetPsCache, resolveWorkspaceForClaude, toClaudeProjectPath } from "./activity-detection.js";
|
|
3
|
+
/** Hook script content that updates session metadata on git/gh commands.
|
|
4
|
+
* Exported for integration testing. */
|
|
5
|
+
export declare const METADATA_UPDATER_SCRIPT = "#!/usr/bin/env bash\n# Metadata Updater Hook for Athene\n#\n# This PostToolUse hook automatically updates session metadata when:\n# - gh pr create: extracts PR URL and writes to metadata\n# - git checkout -b / git switch -c: extracts branch name and writes to metadata\n# - gh pr merge: updates status to \"merged\"\n\nset -euo pipefail\n\n# Configuration\nAO_DATA_DIR=\"${AO_DATA_DIR:-$HOME/.ao-sessions}\"\n\n# Read hook input from stdin\ninput=$(cat)\n\n# Extract fields from JSON (using jq if available, otherwise basic parsing)\nif command -v jq &>/dev/null; then\n tool_name=$(echo \"$input\" | jq -r '.tool_name // empty')\n command=$(echo \"$input\" | jq -r '.tool_input.command // empty')\n output=$(echo \"$input\" | jq -r '.tool_response // empty')\n exit_code=$(echo \"$input\" | jq -r '.exit_code // 0')\nelse\n # Fallback: basic JSON parsing without jq\n tool_name=$(echo \"$input\" | grep -o '\"tool_name\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | cut -d'\"' -f4 || echo \"\")\n command=$(echo \"$input\" | grep -o '\"command\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | cut -d'\"' -f4 || echo \"\")\n output=$(echo \"$input\" | grep -o '\"tool_response\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | cut -d'\"' -f4 || echo \"\")\n exit_code=$(echo \"$input\" | grep -o '\"exit_code\"[[:space:]]*:[[:space:]]*[0-9]*' | grep -o '[0-9]*$' || echo \"0\")\nfi\n\n# Only process successful commands (exit code 0)\nif [[ \"$exit_code\" -ne 0 ]]; then\n echo '{}'\n exit 0\nfi\n\n# Only process Bash tool calls\nif [[ \"$tool_name\" != \"Bash\" ]]; then\n echo '{}' # Empty JSON output\n exit 0\nfi\n\n# Validate AO_SESSION is set\nif [[ -z \"${AO_SESSION:-}\" ]]; then\n echo '{\"systemMessage\": \"AO_SESSION environment variable not set, skipping metadata update\"}'\n exit 0\nfi\n\n# Construct metadata file path\n# AO_DATA_DIR is already set to the project-specific sessions directory\n# V2 storage uses .json extension\nmetadata_file=\"$AO_DATA_DIR/${AO_SESSION}.json\"\n\n# Fallback to bare filename for pre-migration layouts\nif [[ ! -f \"$metadata_file\" ]]; then\n metadata_file=\"$AO_DATA_DIR/$AO_SESSION\"\nfi\n\n# Ensure metadata file exists\nif [[ ! -f \"$metadata_file\" ]]; then\n echo '{\"systemMessage\": \"Metadata file not found: '\"$AO_DATA_DIR/${AO_SESSION}\"'\"}'\n exit 0\nfi\n\n# Detect if metadata file is JSON format\nis_json_metadata() {\n local first_char\n first_char=$(head -c1 \"$metadata_file\" 2>/dev/null)\n [[ \"$first_char\" == \"{\" ]]\n}\n\n# Update a single key in metadata (handles both JSON and key=value formats)\nupdate_metadata_key() {\n local key=\"$1\"\n local value=\"$2\"\n local temp_file=\"${metadata_file}.tmp\"\n\n if is_json_metadata; then\n # JSON format\n if command -v jq &>/dev/null; then\n jq --arg k \"$key\" --arg v \"$value\" '.[$k] = $v' \"$metadata_file\" > \"$temp_file\"\n mv \"$temp_file\" \"$metadata_file\"\n else\n # jq unavailable \u2014 use node (hard dep) for safe nested JSON update\n node -e \"\n const fs = require('fs');\n const d = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));\n d[process.argv[2]] = process.argv[3];\n fs.writeFileSync(process.argv[4], JSON.stringify(d, null, 2));\n \" \"$metadata_file\" \"$key\" \"$value\" \"$temp_file\"\n mv \"$temp_file\" \"$metadata_file\"\n fi\n else\n # Key=value format (legacy)\n local escaped_value=$(echo \"$value\" | sed 's/[&|\\/]/\\\\&/g')\n if grep -q \"^$key=\" \"$metadata_file\" 2>/dev/null; then\n sed \"s|^$key=.*|$key=$escaped_value|\" \"$metadata_file\" > \"$temp_file\"\n else\n cp \"$metadata_file\" \"$temp_file\"\n echo \"$key=$value\" >> \"$temp_file\"\n fi\n mv \"$temp_file\" \"$metadata_file\"\n fi\n}\n\n# ============================================================================\n# Command Detection and Parsing\n# ============================================================================\n\n# Strip leading directory-change prefixes so that commands like\n# cd ~/.worktrees/project && gh pr create ...\n# are correctly detected. Agents frequently cd into a worktree first.\n# Store the regex pattern in a variable for clarity (avoids shell quoting confusion).\n# Uses space-padded (&&|;) to avoid breaking on paths containing & or ; chars.\ncd_prefix_pattern='^[[:space:]]*cd[[:space:]]+.*[[:space:]]+(&&|;)[[:space:]]+(.*)'\nclean_command=\"$command\"\nwhile [[ \"$clean_command\" =~ ^[[:space:]]*cd[[:space:]] ]]; do\n if [[ \"$clean_command\" =~ $cd_prefix_pattern ]]; then\n clean_command=\"${BASH_REMATCH[2]}\"\n else\n break\n fi\ndone\n\n# Detect: gh pr create\nif [[ \"$clean_command\" =~ ^gh[[:space:]]+pr[[:space:]]+create ]]; then\n sanitized_output=$(printf '%s' \"$output\" | sed -E $'s/\u001B\\[[0-9;]*[A-Za-z]//g')\n # Extract PR URL from output\n pr_url=\"\"\n # GitHub PR URLs are whitespace-delimited in gh output after ANSI stripping.\n if [[ \"$sanitized_output\" =~ (https://github[.]com/[^[:space:]]+/[^[:space:]]+/pull/[0-9]+) ]]; then\n pr_url=\"${BASH_REMATCH[1]}\"\n fi\n\n if [[ -n \"$pr_url\" ]]; then\n update_metadata_key \"pr\" \"$pr_url\"\n # Append to prs field (comma-separated list of all PR URLs for this session).\n # Supports multiple PRs per session \u2014 same repo or different repos.\n existing_prs=\"\"\n if is_json_metadata; then\n if command -v jq &>/dev/null; then\n existing_prs=$(jq -r '.prs // empty' \"$metadata_file\" 2>/dev/null || echo \"\")\n else\n existing_prs=$(node -e \"\n const fs = require('fs');\n const d = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));\n process.stdout.write(d.prs || '');\n \" \"$metadata_file\" 2>/dev/null || echo \"\")\n fi\n else\n existing_prs=$(grep '^prs=' \"$metadata_file\" 2>/dev/null | cut -d'=' -f2- || echo \"\")\n fi\n if [[ -z \"$existing_prs\" ]]; then\n new_prs=\"$pr_url\"\n else\n # Only append if not already present (exact comma-delimited match to avoid /pull/1 matching /pull/10)\n if ! echo \",$existing_prs,\" | grep -qF \",$pr_url,\"; then\n new_prs=\"$existing_prs,$pr_url\"\n else\n new_prs=\"$existing_prs\"\n fi\n fi\n update_metadata_key \"prs\" \"$new_prs\"\n update_metadata_key \"status\" \"pr_open\"\n echo '{\"systemMessage\": \"Updated metadata: PR created at '\"$pr_url\"'\"}'\n exit 0\n fi\nfi\n\n# Detect: git checkout -b <branch> or git switch -c <branch>\nif [[ \"$clean_command\" =~ ^git[[:space:]]+checkout[[:space:]]+-b[[:space:]]+([^[:space:]]+) ]] || \\\n [[ \"$clean_command\" =~ ^git[[:space:]]+switch[[:space:]]+-c[[:space:]]+([^[:space:]]+) ]]; then\n branch=\"${BASH_REMATCH[1]}\"\n\n if [[ -n \"$branch\" ]]; then\n update_metadata_key \"branch\" \"$branch\"\n echo '{\"systemMessage\": \"Updated metadata: branch = '\"$branch\"'\"}'\n exit 0\n fi\nfi\n\n# Detect: git checkout <branch> (without -b) or git switch <branch> (without -c)\n# Only update if the branch name looks like a feature branch (contains / or -)\nif [[ \"$clean_command\" =~ ^git[[:space:]]+checkout[[:space:]]+([^[:space:]-]+[/-][^[:space:]]+) ]] || \\\n [[ \"$clean_command\" =~ ^git[[:space:]]+switch[[:space:]]+([^[:space:]-]+[/-][^[:space:]]+) ]]; then\n branch=\"${BASH_REMATCH[1]}\"\n\n # Avoid updating for checkout of commits/tags\n if [[ -n \"$branch\" && \"$branch\" != \"HEAD\" ]]; then\n update_metadata_key \"branch\" \"$branch\"\n echo '{\"systemMessage\": \"Updated metadata: branch = '\"$branch\"'\"}'\n exit 0\n fi\nfi\n\n# Detect: gh pr merge\nif [[ \"$clean_command\" =~ ^gh[[:space:]]+pr[[:space:]]+merge ]]; then\n update_metadata_key \"status\" \"merged\"\n echo '{\"systemMessage\": \"Updated metadata: status = merged\"}'\n exit 0\nfi\n\n# No matching command, exit silently\necho '{}'\nexit 0\n";
|
|
6
|
+
/**
|
|
7
|
+
* Node.js equivalent of METADATA_UPDATER_SCRIPT for Windows.
|
|
8
|
+
* Reads JSON from stdin, parses it with Node built-ins, and updates the
|
|
9
|
+
* key=value metadata file. No bash, jq, grep, sed, or chmod needed.
|
|
10
|
+
* Exported for testing.
|
|
11
|
+
*/
|
|
12
|
+
export declare const METADATA_UPDATER_SCRIPT_NODE = "#!/usr/bin/env node\n// Metadata Updater Hook for Athene (Node.js \u2014 Windows)\n//\n// This PostToolUse hook automatically updates session metadata when:\n// - gh pr create: extracts PR URL and writes to metadata\n// - git checkout -b / git switch -c: extracts branch name and writes to metadata\n// - gh pr merge: updates status to \"merged\"\n\nconst { readFileSync, writeFileSync, renameSync, existsSync, realpathSync } = require(\"node:fs\");\nconst { join, sep, resolve: resolvePath } = require(\"node:path\");\nconst os = require(\"node:os\");\n\nconst AO_DATA_DIR = process.env.AO_DATA_DIR || join(process.env.HOME || process.env.USERPROFILE || \"\", \".ao-sessions\");\nconst AO_SESSION = process.env.AO_SESSION || \"\";\n\n// Read hook input from stdin (fd 0 is cross-platform, no /dev/stdin needed)\nlet inputRaw = \"\";\ntry {\n inputRaw = readFileSync(0, \"utf-8\");\n} catch {\n inputRaw = \"\";\n}\n\nlet input;\ntry {\n input = JSON.parse(inputRaw || \"{}\");\n} catch {\n process.stdout.write(\"{}\\n\");\n process.exit(0);\n}\n\nconst toolName = input.tool_name || \"\";\nconst command = (input.tool_input && input.tool_input.command) || \"\";\nconst output = input.tool_response || \"\";\nconst exitCode = typeof input.exit_code === \"number\" ? input.exit_code : 0;\n\n// Only process successful commands\nif (exitCode !== 0) {\n process.stdout.write(\"{}\\n\");\n process.exit(0);\n}\n\n// Only process Bash tool calls\nif (toolName !== \"Bash\") {\n process.stdout.write(\"{}\\n\");\n process.exit(0);\n}\n\n// Validate AO_SESSION is set\nif (!AO_SESSION) {\n process.stdout.write(JSON.stringify({ systemMessage: \"AO_SESSION environment variable not set, skipping metadata update\" }) + \"\\n\");\n process.exit(0);\n}\n\n// Validate AO_SESSION contains no path traversal components\nif (AO_SESSION.includes(\"/\") || AO_SESSION.includes(\"\\\\\") || AO_SESSION.includes(\"..\")) {\n process.stdout.write(JSON.stringify({ systemMessage: \"AO_SESSION contains invalid path characters, skipping metadata update\" }) + \"\\n\");\n process.exit(0);\n}\n\n// Validate AO_DATA_DIR is within an allowed base directory (mirrors ao-metadata-helper.sh)\nconst home = os.homedir();\nlet resolvedAoDir;\ntry { resolvedAoDir = realpathSync(AO_DATA_DIR); } catch { resolvedAoDir = resolvePath(AO_DATA_DIR); }\nconst allowedBases = [join(home, \".ao\"), join(home, \".agent-orchestrator\"), os.tmpdir()];\nif (!allowedBases.some((a) => resolvedAoDir === a || resolvedAoDir.startsWith(a + sep))) {\n process.stdout.write(JSON.stringify({ systemMessage: \"AO_DATA_DIR is outside allowed directories, skipping metadata update\" }) + \"\\n\");\n process.exit(0);\n}\n\nconst metadataFile = join(AO_DATA_DIR, AO_SESSION);\n\nif (!existsSync(metadataFile)) {\n process.stdout.write(JSON.stringify({ systemMessage: \"Metadata file not found: \" + metadataFile }) + \"\\n\");\n process.exit(0);\n}\n\n/**\n * Update or append a key=value line in the metadata file (atomic via temp file).\n */\nfunction updateMetadataKey(key, value) {\n const lines = readFileSync(metadataFile, \"utf-8\").split(\"\\n\");\n let found = false;\n const updated = lines.map((line) => {\n if (line.startsWith(key + \"=\")) {\n found = true;\n return key + \"=\" + value;\n }\n return line;\n });\n if (!found) {\n // Insert before the trailing empty line (if any) so the file ends cleanly\n updated.push(key + \"=\" + value);\n }\n const tmpFile = metadataFile + \".tmp.\" + process.pid;\n writeFileSync(tmpFile, updated.join(\"\\n\"), \"utf-8\");\n renameSync(tmpFile, metadataFile);\n}\n\n// Strip leading cd ... && / cd ... ; prefixes (agents frequently cd into a\n// worktree before running the real command)\nlet cleanCommand = command;\nconst cdPrefixRe = /^\\s*cd\\s+\\S.*?\\s+(?:&&|;)\\s+(.*)/;\nlet m;\nwhile ((m = cdPrefixRe.exec(cleanCommand)) !== null && /^\\s*cd\\s/.test(cleanCommand)) {\n cleanCommand = m[1];\n}\n\n// Detect: gh pr create\nif (/^gh\\s+pr\\s+create/.test(cleanCommand)) {\n const prMatch = output.match(/https:\\/\\/github[.]com\\/[^/]+\\/[^/]+\\/pull\\/\\d+/);\n if (prMatch) {\n const prUrl = prMatch[0];\n let existingPrs = \"\";\n try {\n const raw = readFileSync(metadataFile, \"utf-8\");\n if (metadataFile.endsWith(\".json\")) {\n existingPrs = JSON.parse(raw).prs || \"\";\n } else {\n const prsLine = raw.split(\"\\n\").find((l) => l.startsWith(\"prs=\"));\n existingPrs = prsLine ? prsLine.slice(4) : \"\";\n }\n } catch {}\n const newPrs = !existingPrs\n ? prUrl\n : existingPrs.split(\",\").map((u) => u.trim()).includes(prUrl)\n ? existingPrs\n : existingPrs + \",\" + prUrl;\n updateMetadataKey(\"pr\", prUrl);\n updateMetadataKey(\"prs\", newPrs);\n updateMetadataKey(\"status\", \"pr_open\");\n process.stdout.write(JSON.stringify({ systemMessage: \"Updated metadata: PR created at \" + prUrl }) + \"\\n\");\n process.exit(0);\n }\n}\n\n// Detect: git checkout -b <branch> or git switch -c <branch>\nconst checkoutNewBranch = cleanCommand.match(/^git\\s+checkout\\s+-b\\s+(\\S+)/) ||\n cleanCommand.match(/^git\\s+switch\\s+-c\\s+(\\S+)/);\nif (checkoutNewBranch) {\n const branch = checkoutNewBranch[1];\n if (branch) {\n updateMetadataKey(\"branch\", branch);\n process.stdout.write(JSON.stringify({ systemMessage: \"Updated metadata: branch = \" + branch }) + \"\\n\");\n process.exit(0);\n }\n}\n\n// Detect: git checkout <branch> or git switch <branch> (without -b/-c)\n// Only update if branch looks like a feature branch (contains / or -)\nconst checkoutBranch = cleanCommand.match(/^git\\s+checkout\\s+([^\\s-]+[/-][^\\s]+)/) ||\n cleanCommand.match(/^git\\s+switch\\s+([^\\s-]+[/-][^\\s]+)/);\nif (checkoutBranch) {\n const branch = checkoutBranch[1];\n if (branch && branch !== \"HEAD\") {\n updateMetadataKey(\"branch\", branch);\n process.stdout.write(JSON.stringify({ systemMessage: \"Updated metadata: branch = \" + branch }) + \"\\n\");\n process.exit(0);\n }\n}\n\n// Detect: gh pr merge\nif (/^gh\\s+pr\\s+merge/.test(cleanCommand)) {\n updateMetadataKey(\"status\", \"merged\");\n process.stdout.write(JSON.stringify({ systemMessage: \"Updated metadata: status = merged\" }) + \"\\n\");\n process.exit(0);\n}\n\n// No matching command\nprocess.stdout.write(\"{}\\n\");\nprocess.exit(0);\n";
|
|
13
|
+
/**
|
|
14
|
+
* Bash hook script that translates Claude Code lifecycle hooks into AO activity
|
|
15
|
+
* JSONL entries. Registered on every event whose firing carries activity
|
|
16
|
+
* information (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
|
|
17
|
+
* PermissionRequest, Notification, Stop, SubagentStop, StopFailure, PreCompact,
|
|
18
|
+
* PostCompact, SubagentStart, PostToolBatch).
|
|
19
|
+
*
|
|
20
|
+
* Reads the JSON payload from stdin, parses `hook_event_name`, maps it to an
|
|
21
|
+
* activity state, and appends a single JSONL entry to
|
|
22
|
+
* `$CLAUDE_PROJECT_DIR/.ao/activity.jsonl` with `source: "hook"`.
|
|
23
|
+
*
|
|
24
|
+
* Notification is filtered by `notification_type` — only `permission_prompt`
|
|
25
|
+
* and `idle_prompt` map to `waiting_input`; `auth_success`/`elicitation_*` etc.
|
|
26
|
+
* are skipped because they don't represent a stuck-on-the-user transition.
|
|
27
|
+
*
|
|
28
|
+
* The script always exits 0 (never blocks Claude). Unknown events exit
|
|
29
|
+
* silently. Exported for integration testing.
|
|
30
|
+
*/
|
|
31
|
+
export declare const ACTIVITY_UPDATER_SCRIPT = "#!/usr/bin/env bash\n# Activity Updater Hook for Athene\n#\n# Records Claude Code lifecycle events to {workspace}/.ao/activity.jsonl so\n# the dashboard / lifecycle reducer derives activity state from authoritative\n# platform events instead of regex over rendered terminal output. (#1941)\n\nset -uo pipefail\n\ninput=$(cat)\n\nif command -v jq &>/dev/null; then\n event=$(printf '%s' \"$input\" | jq -r '.hook_event_name // empty')\n notif_type=$(printf '%s' \"$input\" | jq -r '.notification_type // empty')\n tool_name=$(printf '%s' \"$input\" | jq -r '.tool_name // empty')\n error_type=$(printf '%s' \"$input\" | jq -r '.error_type // empty')\nelse\n event=$(printf '%s' \"$input\" | grep -o '\"hook_event_name\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | cut -d'\"' -f4)\n notif_type=$(printf '%s' \"$input\" | grep -o '\"notification_type\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | cut -d'\"' -f4)\n tool_name=$(printf '%s' \"$input\" | grep -o '\"tool_name\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | cut -d'\"' -f4)\n error_type=$(printf '%s' \"$input\" | grep -o '\"error_type\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | cut -d'\"' -f4)\nfi\n\nstate=\"\"\ntrigger=\"\"\ncase \"$event\" in\n SessionStart|Stop|SubagentStop)\n state=\"ready\"\n trigger=\"$event\"\n ;;\n UserPromptSubmit|PreToolUse|PostToolUse|PostToolUseFailure|PreCompact|PostCompact|SubagentStart|PostToolBatch)\n state=\"active\"\n trigger=\"$event\"\n ;;\n PermissionRequest)\n state=\"waiting_input\"\n if [[ -n \"$tool_name\" ]]; then\n trigger=\"PermissionRequest ($tool_name)\"\n else\n trigger=\"PermissionRequest\"\n fi\n ;;\n Notification)\n if [[ \"$notif_type\" == \"permission_prompt\" || \"$notif_type\" == \"idle_prompt\" ]]; then\n state=\"waiting_input\"\n trigger=\"Notification ($notif_type)\"\n else\n # auth_success / elicitation_* / unrecognized \u2014 not an activity transition\n echo '{}'\n exit 0\n fi\n ;;\n StopFailure)\n state=\"blocked\"\n if [[ -n \"$error_type\" ]]; then\n trigger=\"StopFailure ($error_type)\"\n else\n trigger=\"StopFailure\"\n fi\n ;;\n *)\n echo '{}'\n exit 0\n ;;\nesac\n\nworkspace=\"${CLAUDE_PROJECT_DIR:-$(pwd)}\"\nlog_dir=\"$workspace/.ao\"\nlog_file=\"$log_dir/activity.jsonl\"\n\nmkdir -p \"$log_dir\" 2>/dev/null || { echo '{}'; exit 0; }\n\n# Node is a hard runtime dep of Claude Code, so node -p is always available\n# and gives millisecond-precision ISO timestamps matching the rest of the\n# activity-JSONL log. Fall back to seconds-precision date for the unlikely\n# case where node is unavailable (still valid ISO 8601).\nts=$(node -p 'new Date().toISOString()' 2>/dev/null || date -u +\"%Y-%m-%dT%H:%M:%SZ\")\n\n# Escape JSON-special characters in the trigger value. Triggers are bounded\n# today to event/tool/error names (no control chars in practice) but escape\n# defensively \u2014 \\ and \" for content, plus the five common control chars\n# (\\n \\r \\t \\b \\f) so the JSONL line stays parseable for any future\n# trigger source. Matches what Node's JSON.stringify produces in the .cjs\n# variant so both implementations stay in lockstep.\nescape_json() {\n local s=\"$1\"\n s=\"${s//\\\\/\\\\\\\\}\"\n s=\"${s//\\\"/\\\\\\\"}\"\n s=\"${s//$'\\n'/\\\\n}\"\n s=\"${s//$'\\r'/\\\\r}\"\n s=\"${s//$'\\t'/\\\\t}\"\n s=\"${s//$'\\b'/\\\\b}\"\n s=\"${s//$'\\f'/\\\\f}\"\n printf '%s' \"$s\"\n}\n\nif [[ \"$state\" == \"waiting_input\" || \"$state\" == \"blocked\" ]]; then\n esc_trigger=$(escape_json \"$trigger\")\n printf '{\"ts\":\"%s\",\"state\":\"%s\",\"source\":\"hook\",\"trigger\":\"%s\"}\\n' \"$ts\" \"$state\" \"$esc_trigger\" >> \"$log_file\"\nelse\n printf '{\"ts\":\"%s\",\"state\":\"%s\",\"source\":\"hook\"}\\n' \"$ts\" \"$state\" >> \"$log_file\"\nfi\n\necho '{}'\nexit 0\n";
|
|
32
|
+
/**
|
|
33
|
+
* Node.js equivalent of ACTIVITY_UPDATER_SCRIPT for Windows. No bash, no jq,
|
|
34
|
+
* no shebang interpretation; relies only on Node built-ins. Exported for
|
|
35
|
+
* testing.
|
|
36
|
+
*/
|
|
37
|
+
export declare const ACTIVITY_UPDATER_SCRIPT_NODE = "#!/usr/bin/env node\n// Activity Updater Hook for Athene (Node.js \u2014 Windows). See\n// ACTIVITY_UPDATER_SCRIPT for the canonical bash version. (#1941)\n\nconst { appendFileSync, mkdirSync, readFileSync } = require(\"node:fs\");\nconst { join } = require(\"node:path\");\n\nlet inputRaw = \"\";\ntry {\n inputRaw = readFileSync(0, \"utf-8\");\n} catch {\n process.stdout.write(\"{}\\n\");\n process.exit(0);\n}\n\nlet payload;\ntry {\n payload = JSON.parse(inputRaw || \"{}\");\n} catch {\n process.stdout.write(\"{}\\n\");\n process.exit(0);\n}\n\nconst event = typeof payload.hook_event_name === \"string\" ? payload.hook_event_name : \"\";\nconst notifType = typeof payload.notification_type === \"string\" ? payload.notification_type : \"\";\nconst toolName = typeof payload.tool_name === \"string\" ? payload.tool_name : \"\";\nconst errorType = typeof payload.error_type === \"string\" ? payload.error_type : \"\";\n\nlet state = \"\";\nlet trigger = \"\";\nswitch (event) {\n case \"SessionStart\":\n case \"Stop\":\n case \"SubagentStop\":\n state = \"ready\";\n trigger = event;\n break;\n case \"UserPromptSubmit\":\n case \"PreToolUse\":\n case \"PostToolUse\":\n case \"PostToolUseFailure\":\n case \"PreCompact\":\n case \"PostCompact\":\n case \"SubagentStart\":\n case \"PostToolBatch\":\n state = \"active\";\n trigger = event;\n break;\n case \"PermissionRequest\":\n state = \"waiting_input\";\n trigger = toolName ? `PermissionRequest (${toolName})` : \"PermissionRequest\";\n break;\n case \"Notification\":\n if (notifType === \"permission_prompt\" || notifType === \"idle_prompt\") {\n state = \"waiting_input\";\n trigger = `Notification (${notifType})`;\n } else {\n process.stdout.write(\"{}\\n\");\n process.exit(0);\n }\n break;\n case \"StopFailure\":\n state = \"blocked\";\n trigger = errorType ? `StopFailure (${errorType})` : \"StopFailure\";\n break;\n default:\n process.stdout.write(\"{}\\n\");\n process.exit(0);\n}\n\nconst workspace = process.env.CLAUDE_PROJECT_DIR || process.cwd();\nconst logDir = join(workspace, \".ao\");\nconst logFile = join(logDir, \"activity.jsonl\");\n\ntry {\n mkdirSync(logDir, { recursive: true });\n} catch {\n process.stdout.write(\"{}\\n\");\n process.exit(0);\n}\n\nconst ts = new Date().toISOString();\nconst entry =\n state === \"waiting_input\" || state === \"blocked\"\n ? { ts, state, source: \"hook\", trigger }\n : { ts, state, source: \"hook\" };\n\ntry {\n appendFileSync(logFile, JSON.stringify(entry) + \"\\n\", \"utf-8\");\n} catch {\n // Best-effort \u2014 never block Claude on log append failure\n}\n\nprocess.stdout.write(\"{}\\n\");\nprocess.exit(0);\n";
|
|
38
|
+
export declare const manifest: {
|
|
39
|
+
name: string;
|
|
40
|
+
slot: "agent";
|
|
41
|
+
description: string;
|
|
42
|
+
version: string;
|
|
43
|
+
displayName: string;
|
|
44
|
+
};
|
|
45
|
+
export declare function create(): Agent;
|
|
46
|
+
export declare function detect(): boolean;
|
|
47
|
+
declare const _default: {
|
|
48
|
+
manifest: {
|
|
49
|
+
name: string;
|
|
50
|
+
slot: "agent";
|
|
51
|
+
description: string;
|
|
52
|
+
version: string;
|
|
53
|
+
displayName: string;
|
|
54
|
+
};
|
|
55
|
+
create: typeof create;
|
|
56
|
+
detect: typeof detect;
|
|
57
|
+
};
|
|
58
|
+
export default _default;
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,KAAK,EAYX,MAAM,gCAAgC,CAAC;AAexC,OAAO,EAAE,YAAY,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAMvG;wCACwC;AACxC,eAAO,MAAM,uBAAuB,mvPA6MnC,CAAC;AAMF;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,mxMA2KxC,CAAC;AAMF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,uBAAuB,0xHAyGnC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,msFA+FxC,CAAC;AAMF,eAAO,MAAM,QAAQ;;;;;;CAMpB,CAAC;AA8iBF,wBAAgB,MAAM,IAAI,KAAK,CAE9B;AAED,wBAAgB,MAAM,IAAI,OAAO,CAahC;;;;;;;;;;;;AAED,wBAA0E"}
|