subconscious-cli 0.2.1 → 0.3.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/README.md +200 -76
- package/bin/agents.js +430 -23
- package/bin/auth.js +116 -24
- package/bin/branding.js +39 -0
- package/bin/cli.js +238 -18
- package/bin/colors.js +22 -10
- package/bin/profiles.js +782 -0
- package/bin/registry.generated.json +182 -44
- package/bin/runbook/README.md +22 -0
- package/bin/runbook/claude-code/install.sh +290 -0
- package/bin/runbook/claude-code/run.sh +164 -0
- package/bin/runbook/codex/hook.sh +112 -0
- package/bin/runbook/codex/hooks-lib.sh +139 -0
- package/bin/runbook/codex/hooks.json +29 -0
- package/bin/runbook/codex/install.sh +82 -0
- package/bin/runbook/codex/run.sh +248 -0
- package/bin/runbook/copilot/hook.sh +173 -0
- package/bin/runbook/copilot/hooks.json +19 -0
- package/bin/runbook/copilot/install.sh +470 -0
- package/bin/runbook/cursor/hook.sh +145 -0
- package/bin/runbook/cursor/hooks.json +17 -0
- package/bin/runbook/cursor/install.sh +284 -0
- package/bin/runbook/opencode/install.sh +298 -0
- package/bin/runbook/opencode/run.sh +106 -0
- package/bin/runbook/opencode/subconscious-compaction.ts +100 -0
- package/bin/runbook/pi/install.sh +278 -0
- package/bin/runbook/pi/run.sh +25 -0
- package/bin/runbook/pi/subconscious-compaction.ts +151 -0
- package/package.json +10 -5
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Point the `claude` CLI at the Subconscious gateway — ephemerally.
|
|
3
|
+
#
|
|
4
|
+
# Uses env vars only (ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN) so nothing is
|
|
5
|
+
# written to ~/.claude/ config files.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# ./run.sh # uses GATEWAY_URL/API_KEY from ../.env
|
|
9
|
+
# ./run.sh --continue # pass args through to claude
|
|
10
|
+
# ./run.sh --compact-window 1000000 -- --continue
|
|
11
|
+
# ./run.sh --model subconscious/glm-5.2 -p "hi"
|
|
12
|
+
#
|
|
13
|
+
# Config: copy ../env.example to ../.env and edit. .env is gitignored.
|
|
14
|
+
# Profile env is injected by subc. A sibling .env is only used when SUBC_ENV_FILE is unset.
|
|
15
|
+
#
|
|
16
|
+
# Or source it to just export the env:
|
|
17
|
+
# source run.sh
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
|
+
|
|
23
|
+
# Load shared env from SUBC_ENV_FILE, or a sibling .env / env.example.
|
|
24
|
+
SHARED_ENV="${SUBC_ENV_FILE:-${SCRIPT_DIR}/../.env}"
|
|
25
|
+
[[ -f "$SHARED_ENV" ]] || SHARED_ENV="${SCRIPT_DIR}/../env.example"
|
|
26
|
+
if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
27
|
+
|
|
28
|
+
DEFAULT_MODEL="subconscious/glm-5.2"
|
|
29
|
+
DEFAULT_COMPACT_WINDOW="1000000"
|
|
30
|
+
DEFAULT_MAX_CONTEXT_TOKENS="3000000"
|
|
31
|
+
DEFAULT_MAX_CONCURRENT_SUBAGENTS="4"
|
|
32
|
+
DEFAULT_MAX_SUBAGENT_SPAWN_DEPTH="1"
|
|
33
|
+
DEFAULT_OPUS_MODEL="subconscious/glm-5.2"
|
|
34
|
+
DEFAULT_SONNET_MODEL="subconscious/tim-qwen3.6-27b"
|
|
35
|
+
DEFAULT_HAIKU_MODEL="subconscious/deepseek-v4-flash-marathon"
|
|
36
|
+
|
|
37
|
+
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
38
|
+
API_KEY="${CLAUDE_CODE_API_KEY:-${API_KEY:-}}"
|
|
39
|
+
MODEL="${MODEL:-$DEFAULT_MODEL}"
|
|
40
|
+
COMPACT_WINDOW="${CLAUDE_CODE_AUTO_COMPACT_WINDOW:-${COMPACT_WINDOW:-$DEFAULT_COMPACT_WINDOW}}"
|
|
41
|
+
MAX_CONTEXT_TOKENS="${CLAUDE_CODE_MAX_CONTEXT_TOKENS:-${MAX_CONTEXT_TOKENS:-$DEFAULT_MAX_CONTEXT_TOKENS}}"
|
|
42
|
+
MAX_CONCURRENT_SUBAGENTS="${MAX_CONCURRENT_SUBAGENTS:-$DEFAULT_MAX_CONCURRENT_SUBAGENTS}"
|
|
43
|
+
MAX_SUBAGENT_SPAWN_DEPTH="${MAX_SUBAGENT_SPAWN_DEPTH:-$DEFAULT_MAX_SUBAGENT_SPAWN_DEPTH}"
|
|
44
|
+
OPUS_MODEL="${ANTHROPIC_DEFAULT_OPUS_MODEL:-$DEFAULT_OPUS_MODEL}"
|
|
45
|
+
OPUS_MODEL_NAME="${ANTHROPIC_DEFAULT_OPUS_MODEL_NAME:-$OPUS_MODEL}"
|
|
46
|
+
OPUS_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION:-Subconscious default model}"
|
|
47
|
+
SONNET_MODEL="${ANTHROPIC_DEFAULT_SONNET_MODEL:-$DEFAULT_SONNET_MODEL}"
|
|
48
|
+
SONNET_MODEL_NAME="${ANTHROPIC_DEFAULT_SONNET_MODEL_NAME:-$SONNET_MODEL}"
|
|
49
|
+
SONNET_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION:-Subconscious TIM Qwen 3.6 27B}"
|
|
50
|
+
HAIKU_MODEL="${ANTHROPIC_DEFAULT_HAIKU_MODEL:-$DEFAULT_HAIKU_MODEL}"
|
|
51
|
+
HAIKU_MODEL_NAME="${ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME:-$HAIKU_MODEL}"
|
|
52
|
+
HAIKU_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION:-Subconscious DeepSeek V4 Flash Marathon}"
|
|
53
|
+
|
|
54
|
+
usage() {
|
|
55
|
+
cat <<'EOF'
|
|
56
|
+
Usage:
|
|
57
|
+
run.sh [options] [-- CLAUDE_ARGS...]
|
|
58
|
+
run.sh [options] CLAUDE_ARGS...
|
|
59
|
+
|
|
60
|
+
Options (same as install.sh; flags override env for this run):
|
|
61
|
+
--gateway-url URL Gateway origin (e.g. https://gateway.example)
|
|
62
|
+
--api-key KEY Gateway API key (sk-gw-...)
|
|
63
|
+
--model MODEL Model name (default: subconscious/glm-5.2)
|
|
64
|
+
--compact-window N CLAUDE_CODE_AUTO_COMPACT_WINDOW (default: 1000000; Claude Code clamps to 100000–1000000)
|
|
65
|
+
See https://code.claude.com/docs/en/env-vars and
|
|
66
|
+
https://code.claude.com/docs/en/context-window#set-the-auto-compact-window
|
|
67
|
+
--max-context-tokens N CLAUDE_CODE_MAX_CONTEXT_TOKENS (default: 3000000)
|
|
68
|
+
See https://code.claude.com/docs/en/env-vars
|
|
69
|
+
-h, --help Show this help
|
|
70
|
+
|
|
71
|
+
Anything after --, or unrecognized flags/args, is passed through to `claude`.
|
|
72
|
+
EOF
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Parse args (only when executed, not sourced)
|
|
76
|
+
PASSTHRU=()
|
|
77
|
+
if [[ "${BASH_SOURCE[0]:-$0}" == "${0}" ]]; then
|
|
78
|
+
while [[ $# -gt 0 ]]; do
|
|
79
|
+
case "$1" in
|
|
80
|
+
--gateway-url)
|
|
81
|
+
GATEWAY_URL="${2:-}"
|
|
82
|
+
shift 2
|
|
83
|
+
;;
|
|
84
|
+
--api-key)
|
|
85
|
+
API_KEY="${2:-}"
|
|
86
|
+
shift 2
|
|
87
|
+
;;
|
|
88
|
+
--model)
|
|
89
|
+
MODEL="${2:-}"
|
|
90
|
+
shift 2
|
|
91
|
+
;;
|
|
92
|
+
--compact-window)
|
|
93
|
+
COMPACT_WINDOW="${2:-}"
|
|
94
|
+
shift 2
|
|
95
|
+
;;
|
|
96
|
+
--max-context-tokens)
|
|
97
|
+
MAX_CONTEXT_TOKENS="${2:-}"
|
|
98
|
+
shift 2
|
|
99
|
+
;;
|
|
100
|
+
-h|--help)
|
|
101
|
+
usage
|
|
102
|
+
exit 0
|
|
103
|
+
;;
|
|
104
|
+
--)
|
|
105
|
+
shift
|
|
106
|
+
PASSTHRU+=("$@")
|
|
107
|
+
break
|
|
108
|
+
;;
|
|
109
|
+
*)
|
|
110
|
+
PASSTHRU+=("$1")
|
|
111
|
+
shift
|
|
112
|
+
;;
|
|
113
|
+
esac
|
|
114
|
+
done
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if [[ -z "$GATEWAY_URL" || -z "$API_KEY" ]]; then
|
|
118
|
+
echo "error: GATEWAY_URL and API_KEY must be set in ../.env (or pass --gateway-url / --api-key)" >&2
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# Optional CLAUDE_GATEWAY_URL in shared .env overrides GATEWAY_URL for Claude only
|
|
123
|
+
# (CLI --gateway-url already updated GATEWAY_URL above).
|
|
124
|
+
EFFECTIVE_GATEWAY_URL="${CLAUDE_GATEWAY_URL:-$GATEWAY_URL}"
|
|
125
|
+
|
|
126
|
+
export ANTHROPIC_BASE_URL="$EFFECTIVE_GATEWAY_URL"
|
|
127
|
+
export ANTHROPIC_AUTH_TOKEN="$API_KEY"
|
|
128
|
+
export ANTHROPIC_MODEL="${ANTHROPIC_MODEL:-$MODEL}"
|
|
129
|
+
export ANTHROPIC_SMALL_FAST_MODEL="${ANTHROPIC_SMALL_FAST_MODEL:-$MODEL}"
|
|
130
|
+
export ANTHROPIC_DEFAULT_OPUS_MODEL="${ANTHROPIC_DEFAULT_OPUS_MODEL:-$OPUS_MODEL}"
|
|
131
|
+
export ANTHROPIC_DEFAULT_OPUS_MODEL_NAME="${ANTHROPIC_DEFAULT_OPUS_MODEL_NAME:-$OPUS_MODEL_NAME}"
|
|
132
|
+
export ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION:-$OPUS_MODEL_DESCRIPTION}"
|
|
133
|
+
export ANTHROPIC_DEFAULT_SONNET_MODEL="${ANTHROPIC_DEFAULT_SONNET_MODEL:-$SONNET_MODEL}"
|
|
134
|
+
export ANTHROPIC_DEFAULT_SONNET_MODEL_NAME="${ANTHROPIC_DEFAULT_SONNET_MODEL_NAME:-$SONNET_MODEL_NAME}"
|
|
135
|
+
export ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION:-$SONNET_MODEL_DESCRIPTION}"
|
|
136
|
+
export ANTHROPIC_DEFAULT_HAIKU_MODEL="${ANTHROPIC_DEFAULT_HAIKU_MODEL:-$HAIKU_MODEL}"
|
|
137
|
+
export ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="${ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME:-$HAIKU_MODEL_NAME}"
|
|
138
|
+
export ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION:-$HAIKU_MODEL_DESCRIPTION}"
|
|
139
|
+
export CLAUDE_CODE_SUBAGENT_MODEL="${CLAUDE_CODE_SUBAGENT_MODEL:-$MODEL}"
|
|
140
|
+
export CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS="${MAX_CONCURRENT_SUBAGENTS}"
|
|
141
|
+
export CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH="${MAX_SUBAGENT_SPAWN_DEPTH}"
|
|
142
|
+
export CLAUDE_CODE_AUTO_COMPACT_WINDOW="${COMPACT_WINDOW}"
|
|
143
|
+
export CLAUDE_CODE_MAX_CONTEXT_TOKENS="${MAX_CONTEXT_TOKENS}"
|
|
144
|
+
# Claude Code purpose attribution only: api_request logs → gateway POST /v1/logs
|
|
145
|
+
# (not a general logs API). Docs:
|
|
146
|
+
# https://code.claude.com/docs/en/monitoring-usage#api-request-event
|
|
147
|
+
# Requires Claude Code >= 2.1.152.
|
|
148
|
+
export CLAUDE_CODE_ENABLE_TELEMETRY="${CLAUDE_CODE_ENABLE_TELEMETRY:-1}"
|
|
149
|
+
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-1}"
|
|
150
|
+
export OTEL_LOGS_EXPORTER="${OTEL_LOGS_EXPORTER:-otlp}"
|
|
151
|
+
export OTEL_EXPORTER_OTLP_PROTOCOL="${OTEL_EXPORTER_OTLP_PROTOCOL:-http/json}"
|
|
152
|
+
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT="${EFFECTIVE_GATEWAY_URL%/}/v1/logs"
|
|
153
|
+
export OTEL_EXPORTER_OTLP_HEADERS="x-api-key=${API_KEY}"
|
|
154
|
+
export OTEL_EXPORTER_OTLP_TIMEOUT="${OTEL_EXPORTER_OTLP_TIMEOUT:-2000}"
|
|
155
|
+
export OTEL_LOGS_EXPORT_INTERVAL="${OTEL_LOGS_EXPORT_INTERVAL:-2000}"
|
|
156
|
+
# Do not set OTEL_TRACES_EXPORTER / OTEL_METRICS_EXPORTER (including to "none") -
|
|
157
|
+
# Claude's telemetry init can abort the whole pipeline on unknown exporter types.
|
|
158
|
+
# If sourced, export env and return (caller can unset when done).
|
|
159
|
+
if [[ "${BASH_SOURCE[0]:-$0}" != "${0}" ]]; then
|
|
160
|
+
return 0 2>/dev/null || true
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
# If executed, launch claude with any passed-through args.
|
|
164
|
+
exec claude ${PASSTHRU[@]+"${PASSTHRU[@]}"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fail-open Codex hook: report PreCompact / PostCompact to the gateway.
|
|
3
|
+
# Never blocks compaction (do not return continue: false).
|
|
4
|
+
#
|
|
5
|
+
# Docs: https://developers.openai.com/codex/hooks
|
|
6
|
+
#
|
|
7
|
+
# PreCompact -> conversation_compaction { phase: start }
|
|
8
|
+
# PostCompact -> conversation_compaction { phase: end }
|
|
9
|
+
#
|
|
10
|
+
# Custom (non-OpenAI-named) providers compact locally through the configured
|
|
11
|
+
# Responses endpoint, so the summarization request hits the gateway between
|
|
12
|
+
# these two signals. conversation_id is the hook session_id, which Codex
|
|
13
|
+
# examples show as thr_… — the same value the gateway uses as thread-id
|
|
14
|
+
# grouping. Capture after upgrades if that ever diverges.
|
|
15
|
+
#
|
|
16
|
+
# Stdin: Codex hook JSON. Stdout: empty success (or {} ). Exit 0 always.
|
|
17
|
+
|
|
18
|
+
set -u
|
|
19
|
+
|
|
20
|
+
CONFIG="${SUBCONSCIOUS_HOOKS_ENV:-${HOME}/.codex/subconscious-hooks.env}"
|
|
21
|
+
if [[ -f "$CONFIG" ]]; then
|
|
22
|
+
# shellcheck disable=SC1090
|
|
23
|
+
source "$CONFIG"
|
|
24
|
+
fi
|
|
25
|
+
# Older overwrite-style setup also wrote subconscious.env; keep reading it.
|
|
26
|
+
if [[ -f "${HOME}/.codex/subconscious.env" ]]; then
|
|
27
|
+
# shellcheck disable=SC1090
|
|
28
|
+
source "${HOME}/.codex/subconscious.env"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
GATEWAY_URL="${SUBCONSCIOUS_GATEWAY_URL:-}"
|
|
32
|
+
API_KEY="${SUBCONSCIOUS_API_KEY:-}"
|
|
33
|
+
|
|
34
|
+
fail_open() {
|
|
35
|
+
exit 0
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if [[ -z "$GATEWAY_URL" || -z "$API_KEY" ]]; then
|
|
39
|
+
echo "subconscious codex hook: missing SUBCONSCIOUS_GATEWAY_URL or SUBCONSCIOUS_API_KEY" >&2
|
|
40
|
+
fail_open
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
for tool in jq curl; do
|
|
44
|
+
if ! command -v "$tool" >/dev/null 2>&1; then
|
|
45
|
+
echo "subconscious codex hook: ${tool} is required" >&2
|
|
46
|
+
fail_open
|
|
47
|
+
fi
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
INPUT="$(cat || true)"
|
|
51
|
+
if [[ -z "$INPUT" ]]; then
|
|
52
|
+
fail_open
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Local fire-log for capture/debug (never blocks).
|
|
56
|
+
FIRE_LOG_DIR="${SUBCONSCIOUS_CODEX_HOOK_LOG_DIR:-${HOME}/.codex/subconscious-hook-logs}"
|
|
57
|
+
mkdir -p "$FIRE_LOG_DIR" 2>/dev/null || true
|
|
58
|
+
|
|
59
|
+
HOOK_EVENT="$(printf '%s' "$INPUT" | jq -r '.hook_event_name // empty' 2>/dev/null || true)"
|
|
60
|
+
case "$HOOK_EVENT" in
|
|
61
|
+
PreCompact) PHASE="start" ;;
|
|
62
|
+
PostCompact) PHASE="end" ;;
|
|
63
|
+
*) fail_open ;;
|
|
64
|
+
esac
|
|
65
|
+
|
|
66
|
+
SESSION_ID="$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null || true)"
|
|
67
|
+
if [[ -z "$SESSION_ID" ]]; then
|
|
68
|
+
fail_open
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
{
|
|
72
|
+
printf '%s event=%s session_id=%s payload=' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$HOOK_EVENT" "$SESSION_ID"
|
|
73
|
+
printf '%s\n' "$INPUT"
|
|
74
|
+
} >>"${FIRE_LOG_DIR}/${SESSION_ID}.log" 2>/dev/null || true
|
|
75
|
+
|
|
76
|
+
TURN_ID="$(printf '%s' "$INPUT" | jq -r '.turn_id // empty' 2>/dev/null || true)"
|
|
77
|
+
TRIGGER="$(printf '%s' "$INPUT" | jq -r '.trigger // empty' 2>/dev/null || true)"
|
|
78
|
+
NOW_MS="$(date +%s)-$$-$RANDOM"
|
|
79
|
+
if [[ -n "$TURN_ID" ]]; then
|
|
80
|
+
DEDUPE_KEY="${SESSION_ID}:${PHASE}:${TURN_ID}"
|
|
81
|
+
else
|
|
82
|
+
DEDUPE_KEY="${SESSION_ID}:${PHASE}:${NOW_MS}"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
PAYLOAD="$(jq -n \
|
|
86
|
+
--arg conversation_id "$SESSION_ID" \
|
|
87
|
+
--arg phase "$PHASE" \
|
|
88
|
+
--arg hook_event_name "$HOOK_EVENT" \
|
|
89
|
+
--arg dedupe_key "$DEDUPE_KEY" \
|
|
90
|
+
--arg trigger "$TRIGGER" \
|
|
91
|
+
--arg turn_id "$TURN_ID" \
|
|
92
|
+
'{
|
|
93
|
+
event: "conversation_compaction",
|
|
94
|
+
conversation_id: $conversation_id,
|
|
95
|
+
phase: $phase,
|
|
96
|
+
hook_event_name: $hook_event_name,
|
|
97
|
+
dedupe_key: $dedupe_key,
|
|
98
|
+
metadata: {
|
|
99
|
+
trigger: (if $trigger == "" then null else $trigger end),
|
|
100
|
+
turn_id: (if $turn_id == "" then null else $turn_id end)
|
|
101
|
+
} | with_entries(select(.value != null))
|
|
102
|
+
}'
|
|
103
|
+
)"
|
|
104
|
+
|
|
105
|
+
curl -sS -m 2 \
|
|
106
|
+
-H "Authorization: Bearer ${API_KEY}" \
|
|
107
|
+
-H "Content-Type: application/json" \
|
|
108
|
+
-H "x-subconscious-client: codex" \
|
|
109
|
+
-d "$PAYLOAD" \
|
|
110
|
+
"${GATEWAY_URL%/}/v1/agent-hooks" >/dev/null 2>&1 || true
|
|
111
|
+
|
|
112
|
+
fail_open
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Surgical Codex hook merge/unmerge. Sourced by install.sh and run.sh.
|
|
3
|
+
# Never reads, writes, or deletes ~/.codex/config.toml.
|
|
4
|
+
|
|
5
|
+
CODEX_DIR="${CODEX_DIR:-${HOME}/.codex}"
|
|
6
|
+
HOOKS_JSON="${HOOKS_JSON:-${CODEX_DIR}/hooks.json}"
|
|
7
|
+
HOOK_DST="${HOOK_DST:-${CODEX_DIR}/subconscious-hook.sh}"
|
|
8
|
+
HOOKS_ENV_FILE="${HOOKS_ENV_FILE:-${CODEX_DIR}/subconscious-hooks.env}"
|
|
9
|
+
MARKER="${MARKER:-subconscious-hook.sh}"
|
|
10
|
+
|
|
11
|
+
codex_hooks_have_jq() {
|
|
12
|
+
command -v jq >/dev/null 2>&1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
codex_hooks_json_valid() {
|
|
16
|
+
[[ -f "$HOOKS_JSON" ]] && jq -e . "$HOOKS_JSON" >/dev/null 2>&1
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
codex_strip_hook_entries() {
|
|
20
|
+
local tmp
|
|
21
|
+
tmp="$(mktemp)"
|
|
22
|
+
jq --arg marker "$MARKER" '
|
|
23
|
+
def strip:
|
|
24
|
+
(. // [])
|
|
25
|
+
| map(
|
|
26
|
+
.hooks = ((.hooks // []) | map(select((.command // "") | tostring | contains($marker) | not)))
|
|
27
|
+
)
|
|
28
|
+
| map(select((.hooks | length) > 0));
|
|
29
|
+
.hooks = (.hooks // {})
|
|
30
|
+
| .hooks |= with_entries(.value |= strip)
|
|
31
|
+
' "$HOOKS_JSON" >"$tmp"
|
|
32
|
+
mv "$tmp" "$HOOKS_JSON"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
codex_append_hook_groups() {
|
|
36
|
+
local tmp
|
|
37
|
+
tmp="$(mktemp)"
|
|
38
|
+
jq --arg cmd "$HOOK_DST" '
|
|
39
|
+
def group($msg):
|
|
40
|
+
[{ "hooks": [{ "type": "command", "command": $cmd, "timeout": 3, "statusMessage": $msg }] }];
|
|
41
|
+
.hooks = (.hooks // {})
|
|
42
|
+
| .hooks.PreCompact = ((.hooks.PreCompact // []) + group("Reporting compaction start"))
|
|
43
|
+
| .hooks.PostCompact = ((.hooks.PostCompact // []) + group("Reporting compaction end"))
|
|
44
|
+
' "$HOOKS_JSON" >"$tmp"
|
|
45
|
+
mv "$tmp" "$HOOKS_JSON"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
codex_write_hook_script() {
|
|
49
|
+
mkdir -p "$CODEX_DIR"
|
|
50
|
+
cp "$HOOK_SRC" "$HOOK_DST"
|
|
51
|
+
chmod +x "$HOOK_DST"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
codex_write_hooks_env() {
|
|
55
|
+
umask 077
|
|
56
|
+
cat >"$HOOKS_ENV_FILE" <<EOF
|
|
57
|
+
# Generated by subconscious-cli Codex integration — do not commit secrets.
|
|
58
|
+
export SUBCONSCIOUS_GATEWAY_URL='${GATEWAY_URL%/}'
|
|
59
|
+
export SUBCONSCIOUS_API_KEY='${API_KEY}'
|
|
60
|
+
EOF
|
|
61
|
+
chmod 600 "$HOOKS_ENV_FILE"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# codex_ensure_hooks [strict|best-effort]
|
|
65
|
+
codex_ensure_hooks() {
|
|
66
|
+
local mode="${1:-strict}"
|
|
67
|
+
if ! codex_hooks_have_jq; then
|
|
68
|
+
echo "warning: jq is required to merge Codex hooks without overwriting ~/.codex/hooks.json" >&2
|
|
69
|
+
if [[ "$mode" == "strict" ]]; then
|
|
70
|
+
echo "missing required command: jq" >&2
|
|
71
|
+
return 1
|
|
72
|
+
fi
|
|
73
|
+
return 0
|
|
74
|
+
fi
|
|
75
|
+
if [[ -z "${GATEWAY_URL:-}" || -z "${API_KEY:-}" ]]; then
|
|
76
|
+
echo "--gateway-url and --api-key are required to install Codex hooks" >&2
|
|
77
|
+
return 1
|
|
78
|
+
fi
|
|
79
|
+
if [[ -z "${HOOK_SRC:-}" || ! -f "$HOOK_SRC" ]]; then
|
|
80
|
+
echo "warning: Codex hook script missing; skipping hooks" >&2
|
|
81
|
+
return 0
|
|
82
|
+
fi
|
|
83
|
+
mkdir -p "$CODEX_DIR"
|
|
84
|
+
if [[ -f "$HOOKS_JSON" ]] && ! codex_hooks_json_valid; then
|
|
85
|
+
echo "warning: ${HOOKS_JSON} is not valid JSON; leaving it unchanged" >&2
|
|
86
|
+
if [[ "$mode" == "strict" ]]; then
|
|
87
|
+
return 1
|
|
88
|
+
fi
|
|
89
|
+
return 0
|
|
90
|
+
fi
|
|
91
|
+
if [[ ! -f "$HOOKS_JSON" ]]; then
|
|
92
|
+
printf '%s\n' '{"hooks":{}}' >"$HOOKS_JSON"
|
|
93
|
+
fi
|
|
94
|
+
codex_write_hook_script
|
|
95
|
+
codex_write_hooks_env
|
|
96
|
+
codex_strip_hook_entries
|
|
97
|
+
codex_append_hook_groups
|
|
98
|
+
echo "Merged Subconscious Codex hooks into $HOOKS_JSON"
|
|
99
|
+
echo "Trust new hooks in Codex with /hooks (or --dangerously-bypass-hook-trust)."
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
codex_uninstall_hooks() {
|
|
103
|
+
if [[ -f "$HOOKS_JSON" ]] && codex_hooks_have_jq && codex_hooks_json_valid; then
|
|
104
|
+
codex_strip_hook_entries
|
|
105
|
+
echo "Removed Subconscious entries from $HOOKS_JSON"
|
|
106
|
+
elif [[ -f "$HOOKS_JSON" ]]; then
|
|
107
|
+
echo "warning: ${HOOKS_JSON} is not valid JSON; leaving it unchanged" >&2
|
|
108
|
+
else
|
|
109
|
+
echo "No Codex hooks.json at $HOOKS_JSON"
|
|
110
|
+
fi
|
|
111
|
+
rm -f "$HOOK_DST" "$HOOKS_ENV_FILE"
|
|
112
|
+
rm -f "${CODEX_DIR}/subconscious.env"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
codex_hooks_status() {
|
|
116
|
+
echo "scope: user"
|
|
117
|
+
echo "codex dir: $CODEX_DIR"
|
|
118
|
+
echo "hooks.json: $HOOKS_JSON"
|
|
119
|
+
if [[ -f "$HOOKS_JSON" ]] && codex_hooks_have_jq && jq -e --arg m "$MARKER" '
|
|
120
|
+
[.hooks[]?[]?.hooks[]? | select((.command // "") | tostring | contains($m))] | length > 0
|
|
121
|
+
' "$HOOKS_JSON" >/dev/null 2>&1; then
|
|
122
|
+
echo "hooks: installed"
|
|
123
|
+
else
|
|
124
|
+
echo "hooks: not installed"
|
|
125
|
+
fi
|
|
126
|
+
if [[ -x "$HOOK_DST" ]]; then
|
|
127
|
+
echo "hook script: $HOOK_DST (executable)"
|
|
128
|
+
else
|
|
129
|
+
echo "hook script: missing"
|
|
130
|
+
fi
|
|
131
|
+
if [[ -f "$HOOKS_ENV_FILE" ]]; then
|
|
132
|
+
echo "env: $HOOKS_ENV_FILE (present)"
|
|
133
|
+
else
|
|
134
|
+
echo "env: missing"
|
|
135
|
+
fi
|
|
136
|
+
if [[ -f "${CODEX_DIR}/config.toml" ]] && grep -q 'model_provider = "subconscious"' "${CODEX_DIR}/config.toml" 2>/dev/null; then
|
|
137
|
+
echo "legacy config.toml: contains a Subconscious provider (this CLI no longer manages that file)"
|
|
138
|
+
fi
|
|
139
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Report Codex auto/manual compaction to the Subconscious API Gateway.",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"PreCompact": [
|
|
5
|
+
{
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "HOOK_SH_PATH",
|
|
10
|
+
"timeout": 3,
|
|
11
|
+
"statusMessage": "Reporting compaction start"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"PostCompact": [
|
|
17
|
+
{
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "HOOK_SH_PATH",
|
|
22
|
+
"timeout": 3,
|
|
23
|
+
"statusMessage": "Reporting compaction end"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ── Subconscious API Gateway — Codex hooks ────────────────────────────────────
|
|
3
|
+
# Merge Subconscious compaction hooks into ~/.codex/hooks.json without touching
|
|
4
|
+
# ~/.codex/config.toml. Launch Codex with `subc codex`.
|
|
5
|
+
#
|
|
6
|
+
# subc codex install # merge hooks
|
|
7
|
+
# subc codex status
|
|
8
|
+
# subc codex uninstall # strip only Subconscious hook entries
|
|
9
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
HOOK_SRC="${SCRIPT_DIR}/hook.sh"
|
|
15
|
+
|
|
16
|
+
SHARED_ENV="${SUBC_ENV_FILE:-${SCRIPT_DIR}/../.env}"
|
|
17
|
+
[[ -f "$SHARED_ENV" ]] || SHARED_ENV="${SCRIPT_DIR}/../env.example"
|
|
18
|
+
if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
|
|
19
|
+
|
|
20
|
+
# shellcheck source=hooks-lib.sh
|
|
21
|
+
source "${SCRIPT_DIR}/hooks-lib.sh"
|
|
22
|
+
|
|
23
|
+
COMMAND="install"
|
|
24
|
+
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
25
|
+
API_KEY="${CODEX_API_KEY:-${API_KEY:-}}"
|
|
26
|
+
|
|
27
|
+
usage() {
|
|
28
|
+
cat <<'EOF'
|
|
29
|
+
Usage:
|
|
30
|
+
subc codex install [--gateway-url URL] [--api-key KEY]
|
|
31
|
+
subc codex status
|
|
32
|
+
subc codex uninstall
|
|
33
|
+
|
|
34
|
+
Merges Subconscious compaction hooks into ~/.codex/hooks.json. Does not write
|
|
35
|
+
config.toml. Launch Codex with subc codex.
|
|
36
|
+
EOF
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
while [[ $# -gt 0 ]]; do
|
|
40
|
+
case "$1" in
|
|
41
|
+
install|uninstall|status)
|
|
42
|
+
COMMAND="$1"
|
|
43
|
+
shift
|
|
44
|
+
;;
|
|
45
|
+
--gateway-url)
|
|
46
|
+
GATEWAY_URL="${2:-}"
|
|
47
|
+
shift 2
|
|
48
|
+
;;
|
|
49
|
+
--api-key)
|
|
50
|
+
API_KEY="${2:-}"
|
|
51
|
+
shift 2
|
|
52
|
+
;;
|
|
53
|
+
--model)
|
|
54
|
+
shift 2
|
|
55
|
+
;;
|
|
56
|
+
-h|--help|help)
|
|
57
|
+
usage
|
|
58
|
+
exit 0
|
|
59
|
+
;;
|
|
60
|
+
--)
|
|
61
|
+
shift
|
|
62
|
+
break
|
|
63
|
+
;;
|
|
64
|
+
*)
|
|
65
|
+
echo "unknown argument: $1 (launch options belong on subc codex, not subc codex install)" >&2
|
|
66
|
+
usage >&2
|
|
67
|
+
exit 1
|
|
68
|
+
;;
|
|
69
|
+
esac
|
|
70
|
+
done
|
|
71
|
+
|
|
72
|
+
case "$COMMAND" in
|
|
73
|
+
install)
|
|
74
|
+
codex_ensure_hooks strict
|
|
75
|
+
;;
|
|
76
|
+
uninstall)
|
|
77
|
+
codex_uninstall_hooks
|
|
78
|
+
;;
|
|
79
|
+
status)
|
|
80
|
+
codex_hooks_status
|
|
81
|
+
;;
|
|
82
|
+
esac
|