subconscious-cli 0.2.0 → 0.3.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.
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Report OpenCode compactions to the Subconscious API Gateway.
3
+ *
4
+ * Why this exists: OpenCode summarizes by issuing an ordinary completion through the
5
+ * configured provider, which is the gateway, carrying the same session headers as every
6
+ * other turn. Without a signal the gateway would count that summarization as the largest
7
+ * main-thread turn of the conversation, inflating peak context and the traditional
8
+ * comparison at exactly the turn where accuracy matters most.
9
+ *
10
+ * Two callbacks bracket it:
11
+ * experimental.session.compacting -> phase "start" (awaited, so it is ordered before
12
+ * the summarization request reaches the gateway)
13
+ * session.compacted event -> phase "end"
14
+ *
15
+ * Requests between them are the compaction. `sessionID` is the same value OpenCode sends
16
+ * as x-session-affinity / x-session-id, which is already the gateway's grouping key, so
17
+ * no fingerprinting is involved.
18
+ *
19
+ * Fail open: a gateway that is down or slow must never disturb a coding session.
20
+ *
21
+ * Install: copy to ~/.config/opencode/plugins/ (global) or .opencode/plugins/ (project).
22
+ * `opencode/install.sh` does this for you.
23
+ */
24
+
25
+ import type { Plugin } from "@opencode-ai/plugin"
26
+
27
+ const TIMEOUT_MS = 2000
28
+
29
+ function gatewayUrl(): string | undefined {
30
+ const raw = process.env.SUBCONSCIOUS_GATEWAY_URL ?? process.env.GATEWAY_URL
31
+ return raw ? raw.replace(/\/+$/, "") : undefined
32
+ }
33
+
34
+ function apiKey(): string | undefined {
35
+ return process.env.SUBCONSCIOUS_API_KEY ?? process.env.API_KEY
36
+ }
37
+
38
+ async function report(
39
+ sessionID: string,
40
+ phase: "start" | "end",
41
+ hookEventName: string,
42
+ ): Promise<void> {
43
+ const url = gatewayUrl()
44
+ const key = apiKey()
45
+ if (!url || !key || !sessionID) return
46
+
47
+ // Idempotency key for this one event. It must be unique per compaction but stable for
48
+ // a redelivery of this same POST, so it is derived from the wall clock rather than any
49
+ // in-process counter: a counter resets when OpenCode restarts, and a resumed session
50
+ // compacting again would then reuse a key the gateway has already seen and silently
51
+ // drop the signal. Computed once here, before the request, so a transport-level retry
52
+ // carries the identical body.
53
+ const dedupeKey = `${sessionID}:${phase}:${Date.now()}`
54
+
55
+ const controller = new AbortController()
56
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)
57
+ try {
58
+ await fetch(`${url}/v1/agent-hooks`, {
59
+ method: "POST",
60
+ headers: {
61
+ "content-type": "application/json",
62
+ authorization: `Bearer ${key}`,
63
+ "x-subconscious-client": "opencode",
64
+ },
65
+ body: JSON.stringify({
66
+ event: "conversation_compaction",
67
+ conversation_id: sessionID,
68
+ phase,
69
+ hook_event_name: hookEventName,
70
+ dedupe_key: dedupeKey,
71
+ }),
72
+ signal: controller.signal,
73
+ })
74
+ } catch {
75
+ // Fail open on timeout, offline gateway, or auth failure.
76
+ } finally {
77
+ clearTimeout(timer)
78
+ }
79
+ }
80
+
81
+ export const SubconsciousCompaction: Plugin = async () => {
82
+ return {
83
+ "experimental.session.compacting": async (input) => {
84
+ // Deliberately does not touch `output`: mutating `context` would change the
85
+ // customer's compaction prompt, and setting `prompt` would replace it entirely.
86
+ await report(input.sessionID, "start", "session.compacting")
87
+ },
88
+ event: async ({ event }) => {
89
+ if (event.type === "session.compacted") {
90
+ const sessionID = (event as { properties?: { sessionID?: string } }).properties
91
+ ?.sessionID
92
+ if (sessionID) {
93
+ // No shared id with the matching `start` is needed: the gateway pairs a start
94
+ // with the next end by server-stamped time, not by key.
95
+ await report(sessionID, "end", "session.compacted")
96
+ }
97
+ }
98
+ },
99
+ }
100
+ }
@@ -0,0 +1,282 @@
1
+ #!/usr/bin/env bash
2
+ # ── Subconscious API Gateway — Pi setup ───────────────────────────────────────
3
+ # Point the Pi CLI at your gateway. Writes a model config with session headers
4
+ # so the gateway can correlate Pi requests into Conversations.
5
+ #
6
+ # Quick start:
7
+ # ./install.sh --gateway-url https://your-gateway.example --api-key sk-gw-...
8
+ # ./install.sh status # show current config
9
+ # ./install.sh uninstall # revert to default model config
10
+ #
11
+ # Writes ~/.pi/agent/models.json (user config). Restart Pi after install.
12
+ #
13
+ # ── What this does under the hood ────────────────────────────────────────────
14
+ # Equivalent manual setup (writes ~/.pi/agent/models.json):
15
+ #
16
+ # {
17
+ # "providers": {
18
+ # "subconscious": {
19
+ # "baseUrl": "https://your-gateway.example/v1",
20
+ # "api": "openai-completions",
21
+ # "apiKey": "sk-gw-...",
22
+ # "headers": { "x-subconscious-client": "pi" },
23
+ # "models": [{
24
+ # "id": "subconscious/glm-5.2",
25
+ # "contextWindow": 5000000,
26
+ # "maxTokens": 65536,
27
+ # "compat": {
28
+ # "sendSessionAffinityHeaders": true,
29
+ # "sessionAffinityFormat": "openai-nosession"
30
+ # }
31
+ # }]
32
+ # }
33
+ # }
34
+ # }
35
+ #
36
+ # The compat flags make Pi send x-session-affinity headers (openai-nosession)
37
+ # so the gateway groups requests into Conversations.
38
+ #
39
+ # Also installs ~/.pi/agent/extensions/subconscious-compaction.ts so Pi reports
40
+ # session_before_compact / session_compact to /v1/agent-hooks. Pi summarizes
41
+ # through the configured provider; without the extension that turn looks like
42
+ # a normal main-thread peak.
43
+ # Docs: https://pi.dev/docs/latest/compaction
44
+ # https://pi.dev/docs/latest/extensions
45
+ # ─────────────────────────────────────────────────────────────────────────────
46
+
47
+ set -euo pipefail
48
+
49
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
50
+ EXTENSION_SRC="${SCRIPT_DIR}/subconscious-compaction.ts"
51
+
52
+ # Load shared env from coding-agents/.env (gitignored) or env.example.
53
+ SHARED_ENV="${MBTA_ENV_FILE:-${SCRIPT_DIR}/../.env}"
54
+ [[ -f "$SHARED_ENV" ]] || SHARED_ENV="${SCRIPT_DIR}/../env.example"
55
+ if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
56
+
57
+ COMMAND="install"
58
+ GATEWAY_URL="${GATEWAY_URL:-}"
59
+ API_KEY="${PI_API_KEY:-${API_KEY:-}}"
60
+ MODEL="${MODEL:-subconscious/glm-5.2}"
61
+ CONTEXT_WINDOW="${PI_CONTEXT_WINDOW:-5000000}"
62
+ MAX_TOKENS="${PI_MAX_TOKENS:-65536}"
63
+
64
+ usage() {
65
+ cat <<'EOF'
66
+ Usage:
67
+ install.sh [install] --gateway-url URL --api-key KEY [--model MODEL]
68
+ [--context-window N] [--max-tokens N]
69
+ install.sh uninstall
70
+ install.sh status
71
+
72
+ `install` is the default subcommand and may be omitted.
73
+
74
+ Writes a models.json that points Pi at your Subconscious gateway with
75
+ x-subconscious-client: pi and session-affinity headers enabled so the gateway
76
+ can group requests into Conversations.
77
+
78
+ Also sets model contextWindow (default 5000000) so Pi auto-compaction
79
+ (`contextTokens > contextWindow - reserveTokens`) respects the gateway window,
80
+ and installs an extension that reports compactions so the dashboard can restart
81
+ context accounting at the right turn.
82
+
83
+ Pi does not send session headers by default; this script enables
84
+ sendSessionAffinityHeaders with sessionAffinityFormat "openai-nosession"
85
+ (sends x-session-affinity without the underscore session_id header that
86
+ strict proxies may drop).
87
+
88
+ Requires: jq. Restart Pi after install (or /reload for the extension).
89
+ EOF
90
+ }
91
+
92
+ while [[ $# -gt 0 ]]; do
93
+ case "$1" in
94
+ install|uninstall|status)
95
+ COMMAND="$1"
96
+ shift
97
+ ;;
98
+ --gateway-url)
99
+ GATEWAY_URL="${2:-}"
100
+ shift 2
101
+ ;;
102
+ --api-key)
103
+ API_KEY="${2:-}"
104
+ shift 2
105
+ ;;
106
+ --model)
107
+ MODEL="${2:-}"
108
+ shift 2
109
+ ;;
110
+ --context-window)
111
+ CONTEXT_WINDOW="${2:-}"
112
+ shift 2
113
+ ;;
114
+ --max-tokens)
115
+ MAX_TOKENS="${2:-}"
116
+ shift 2
117
+ ;;
118
+ -h|--help)
119
+ usage
120
+ exit 0
121
+ ;;
122
+ *)
123
+ echo "unknown argument: $1" >&2
124
+ usage >&2
125
+ exit 1
126
+ ;;
127
+ esac
128
+ done
129
+
130
+ DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.2
131
+ subconscious/tim-qwen3.6-27b
132
+ subconscious/deepseek-v4-flash-marathon"
133
+ SUPPORTED_MODELS=()
134
+
135
+ add_supported_model() {
136
+ local model_id="$1" existing
137
+ [[ -n "$model_id" ]] || return 0
138
+ if [[ ! "$model_id" =~ ^[-A-Za-z0-9._:/+]+$ ]]; then
139
+ echo "error: invalid model id: $model_id" >&2
140
+ exit 1
141
+ fi
142
+ if [[ "${#SUPPORTED_MODELS[@]}" -gt 0 ]]; then
143
+ for existing in "${SUPPORTED_MODELS[@]}"; do
144
+ [[ "$existing" == "$model_id" ]] && return 0
145
+ done
146
+ fi
147
+ SUPPORTED_MODELS+=("$model_id")
148
+ }
149
+
150
+ add_supported_model "$MODEL"
151
+ while IFS= read -r model_id; do
152
+ add_supported_model "$model_id"
153
+ done <<< "${SUBCONSCIOUS_MODELS:-$DEFAULT_SUBCONSCIOUS_MODELS}"
154
+
155
+ MODEL_ENTRIES_JSON=""
156
+ for model_id in "${SUPPORTED_MODELS[@]}"; do
157
+ model_json="{\"id\":\"${model_id}\",\"contextWindow\":${CONTEXT_WINDOW},\"maxTokens\":${MAX_TOKENS},\"compat\":{\"sendSessionAffinityHeaders\":true,\"sessionAffinityFormat\":\"openai-nosession\"}}"
158
+ if [[ -n "$MODEL_ENTRIES_JSON" ]]; then
159
+ MODEL_ENTRIES_JSON="${MODEL_ENTRIES_JSON},${model_json}"
160
+ else
161
+ MODEL_ENTRIES_JSON="$model_json"
162
+ fi
163
+ done
164
+
165
+ PI_DIR="${HOME}/.pi/agent"
166
+ MODELS_JSON="${PI_DIR}/models.json"
167
+ EXTENSIONS_DIR="${PI_DIR}/extensions"
168
+ EXTENSION_DST="${EXTENSIONS_DIR}/subconscious-compaction.ts"
169
+ ENV_FILE="${PI_DIR}/subconscious.env"
170
+ MARKER='x-subconscious-client'
171
+
172
+ require_cmds() {
173
+ local missing=0
174
+ for c in jq; do
175
+ if ! command -v "$c" >/dev/null 2>&1; then
176
+ echo "missing required command: $c" >&2
177
+ missing=1
178
+ fi
179
+ done
180
+ if [[ "$missing" -ne 0 ]]; then
181
+ exit 1
182
+ fi
183
+ }
184
+
185
+ write_config() {
186
+ mkdir -p "$PI_DIR"
187
+ local base_url="${GATEWAY_URL%/}/v1"
188
+ local config
189
+ config=$(cat <<EOF
190
+ {
191
+ "providers": {
192
+ "subconscious": {
193
+ "baseUrl": "${base_url}",
194
+ "api": "openai-completions",
195
+ "apiKey": "${API_KEY}",
196
+ "headers": {
197
+ "x-subconscious-client": "pi"
198
+ },
199
+ "models": [${MODEL_ENTRIES_JSON}]
200
+ }
201
+ }
202
+ }
203
+ EOF
204
+ )
205
+ echo "$config" >"$MODELS_JSON"
206
+ chmod 600 "$MODELS_JSON"
207
+
208
+ umask 077
209
+ cat >"$ENV_FILE" <<EOF
210
+ # Generated by ol-runbook/coding-agents/pi/install.sh — do not commit secrets.
211
+ # Loaded by subconscious-compaction.ts if process env is unset.
212
+ export SUBCONSCIOUS_GATEWAY_URL='${GATEWAY_URL%/}'
213
+ export SUBCONSCIOUS_API_KEY='${API_KEY}'
214
+ EOF
215
+ chmod 600 "$ENV_FILE"
216
+ }
217
+
218
+ write_extension() {
219
+ if [[ ! -f "$EXTENSION_SRC" ]]; then
220
+ echo "warning: ${EXTENSION_SRC} missing; skipping compaction extension" >&2
221
+ return
222
+ fi
223
+ mkdir -p "$EXTENSIONS_DIR"
224
+ cp "$EXTENSION_SRC" "$EXTENSION_DST"
225
+ }
226
+
227
+ uninstall_config() {
228
+ if [[ -f "$MODELS_JSON" ]] && grep -q "$MARKER" "$MODELS_JSON" 2>/dev/null; then
229
+ rm -f "$MODELS_JSON"
230
+ echo "Removed $MODELS_JSON"
231
+ else
232
+ echo "No Subconscious Pi config found at $MODELS_JSON"
233
+ fi
234
+ rm -f "$EXTENSION_DST" "$ENV_FILE"
235
+ }
236
+
237
+ status() {
238
+ echo "scope: user"
239
+ echo "pi dir: $PI_DIR"
240
+ echo "config: $MODELS_JSON"
241
+ if [[ -f "$MODELS_JSON" ]] && grep -q "$MARKER" "$MODELS_JSON" 2>/dev/null; then
242
+ echo "status: installed"
243
+ echo "models: $(jq -r '[.providers.subconscious.models[].id] | join(", ")' "$MODELS_JSON" 2>/dev/null || echo 'unknown')"
244
+ echo "contextWindow: $(jq -r '.providers.subconscious.models[0].contextWindow // "unset"' "$MODELS_JSON" 2>/dev/null || echo 'unknown')"
245
+ else
246
+ echo "status: not installed"
247
+ fi
248
+ if [[ -f "$EXTENSION_DST" ]]; then
249
+ echo "compaction extension: $EXTENSION_DST (installed)"
250
+ else
251
+ echo "compaction extension: not installed"
252
+ fi
253
+ if [[ -f "$ENV_FILE" ]]; then
254
+ echo "env: $ENV_FILE (present)"
255
+ else
256
+ echo "env: missing"
257
+ fi
258
+ }
259
+
260
+ case "$COMMAND" in
261
+ install)
262
+ require_cmds
263
+ if [[ -z "$GATEWAY_URL" || -z "$API_KEY" ]]; then
264
+ echo "--gateway-url and --api-key are required for install" >&2
265
+ exit 1
266
+ fi
267
+ write_config
268
+ write_extension
269
+ echo "Installed Subconscious Pi config at $MODELS_JSON"
270
+ if [[ -f "$EXTENSION_DST" ]]; then
271
+ echo "Installed compaction reporting extension at $EXTENSION_DST"
272
+ fi
273
+ echo "Compaction extension reads ${ENV_FILE} automatically (sourcing optional)."
274
+ echo "Restart any running Pi sessions (or /reload for the extension)."
275
+ ;;
276
+ uninstall)
277
+ uninstall_config
278
+ ;;
279
+ status)
280
+ status
281
+ ;;
282
+ esac
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bash
2
+ # Launch Pi with the Subconscious provider configured by `subc setup`.
3
+ # This script is deliberately read-only: it never installs Pi, writes config,
4
+ # or updates the persistent integration.
5
+
6
+ set -euo pipefail
7
+
8
+ MODEL="${MODEL:-subconscious/glm-5.2}"
9
+ PI_DIR="${PI_CODING_AGENT_DIR:-${HOME}/.pi/agent}"
10
+ MODELS_JSON="${PI_DIR}/models.json"
11
+
12
+ if [[ ! -f "$MODELS_JSON" ]] || ! grep -q 'x-subconscious-client' "$MODELS_JSON" 2>/dev/null; then
13
+ echo "Pi is not configured for Subconscious. Run 'subc setup' first." >&2
14
+ exit 1
15
+ fi
16
+
17
+ if command -v jq >/dev/null 2>&1 && ! jq -e \
18
+ --arg model "$MODEL" \
19
+ '.providers.subconscious.models[]? | select(.id == $model)' \
20
+ "$MODELS_JSON" >/dev/null 2>&1; then
21
+ echo "Model '$MODEL' is not present in the Pi catalog. Run 'subc setup' to refresh it." >&2
22
+ exit 1
23
+ fi
24
+
25
+ exec pi --provider subconscious --model "$MODEL" "$@"
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Report Pi compactions to the Subconscious API Gateway.
3
+ *
4
+ * Why this exists: Pi summarizes by calling the configured provider (the
5
+ * gateway) with a structured compaction prompt. Without a signal the gateway
6
+ * counts that summarization as the largest main-thread turn, inflating peak
7
+ * context and the traditional comparison at the boundary.
8
+ *
9
+ * Two extension events bracket it:
10
+ * session_before_compact -> phase "start"
11
+ * session_compact -> phase "end"
12
+ *
13
+ * Observational only: never return { cancel } or a custom compaction summary.
14
+ * conversation_id is ctx.sessionManager.getSessionId(), which should match the
15
+ * x-session-affinity value used for conversation grouping when
16
+ * sendSessionAffinityHeaders is enabled (openai-nosession).
17
+ *
18
+ * Capture note (2026-08): Pi compaction/branch-summary requests use a *fresh*
19
+ * routing session id on the wire, so the summarization HTTP call lands in a
20
+ * separate Conversations row. start/end still open the epoch on the parent
21
+ * session via getSessionId(); the boundary charge on the parent may be empty.
22
+ * Split-turn /compact (huge mid-turn) may only drop a few k tokens because
23
+ * keepRecentTokens still retains most of the active turn.
24
+ *
25
+ * Fail open: a gateway that is down or slow must never disturb a coding session.
26
+ *
27
+ * Install: copy to ~/.pi/agent/extensions/ (global). `pi/install.sh` does this.
28
+ * Credentials: process env, or ~/.pi/agent/subconscious.env (loaded here so
29
+ * launching Pi without `source` still works).
30
+ * Docs: https://pi.dev/docs/latest/compaction
31
+ * https://pi.dev/docs/latest/extensions
32
+ */
33
+
34
+ import { readFileSync } from "node:fs"
35
+ import { homedir } from "node:os"
36
+ import { join } from "node:path"
37
+
38
+ const TIMEOUT_MS = 2000
39
+
40
+ type CompactPhase = "start" | "end"
41
+
42
+ let envFileLoaded = false
43
+
44
+ function loadSubconsciousEnvFile(): void {
45
+ if (envFileLoaded) return
46
+ envFileLoaded = true
47
+ if (process.env.SUBCONSCIOUS_GATEWAY_URL && process.env.SUBCONSCIOUS_API_KEY) {
48
+ return
49
+ }
50
+ try {
51
+ const path = join(homedir(), ".pi", "agent", "subconscious.env")
52
+ const text = readFileSync(path, "utf8")
53
+ for (const line of text.split("\n")) {
54
+ const trimmed = line.trim()
55
+ if (!trimmed || trimmed.startsWith("#")) continue
56
+ const m = trimmed.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/)
57
+ if (!m) continue
58
+ const key = m[1]
59
+ let val = m[2]
60
+ if (
61
+ (val.startsWith('"') && val.endsWith('"')) ||
62
+ (val.startsWith("'") && val.endsWith("'"))
63
+ ) {
64
+ val = val.slice(1, -1)
65
+ }
66
+ if (process.env[key] === undefined) {
67
+ process.env[key] = val
68
+ }
69
+ }
70
+ } catch {
71
+ // Missing file or unreadable: fall through to process env / no-op report.
72
+ }
73
+ }
74
+
75
+ function gatewayUrl(): string | undefined {
76
+ loadSubconsciousEnvFile()
77
+ const raw = process.env.SUBCONSCIOUS_GATEWAY_URL ?? process.env.GATEWAY_URL
78
+ return raw ? raw.replace(/\/+$/, "") : undefined
79
+ }
80
+
81
+ function apiKey(): string | undefined {
82
+ loadSubconsciousEnvFile()
83
+ return process.env.SUBCONSCIOUS_API_KEY ?? process.env.API_KEY ?? process.env.PI_API_KEY
84
+ }
85
+
86
+ async function report(
87
+ sessionID: string,
88
+ phase: CompactPhase,
89
+ hookEventName: string,
90
+ metadata?: Record<string, unknown>,
91
+ ): Promise<void> {
92
+ const url = gatewayUrl()
93
+ const key = apiKey()
94
+ if (!url || !key || !sessionID) return
95
+
96
+ // Unique per event, stable for a transport-level retry of this same POST.
97
+ const dedupeKey = `${sessionID}:${phase}:${Date.now()}`
98
+
99
+ const controller = new AbortController()
100
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)
101
+ try {
102
+ await fetch(`${url}/v1/agent-hooks`, {
103
+ method: "POST",
104
+ headers: {
105
+ "content-type": "application/json",
106
+ authorization: `Bearer ${key}`,
107
+ "x-subconscious-client": "pi",
108
+ },
109
+ body: JSON.stringify({
110
+ event: "conversation_compaction",
111
+ conversation_id: sessionID,
112
+ phase,
113
+ hook_event_name: hookEventName,
114
+ dedupe_key: dedupeKey,
115
+ metadata: metadata ?? undefined,
116
+ }),
117
+ signal: controller.signal,
118
+ })
119
+ } catch {
120
+ // Fail open on timeout, offline gateway, or auth failure.
121
+ } finally {
122
+ clearTimeout(timer)
123
+ }
124
+ }
125
+
126
+ // Pi auto-discovers default-exported extension factories from
127
+ // ~/.pi/agent/extensions/*.ts. See https://pi.dev/docs/latest/extensions
128
+ export default function (pi: {
129
+ on: (event: string, handler: (...args: any[]) => any) => void
130
+ }) {
131
+ pi.on("session_before_compact", async (event: any, ctx: any) => {
132
+ const sessionID = ctx?.sessionManager?.getSessionId?.()
133
+ if (sessionID) {
134
+ await report(sessionID, "start", "session_before_compact", {
135
+ reason: event?.reason ?? null,
136
+ tokens_before: event?.preparation?.tokensBefore ?? null,
137
+ })
138
+ }
139
+ // Deliberately return nothing: do not cancel or replace summarization.
140
+ })
141
+
142
+ pi.on("session_compact", async (event: any, ctx: any) => {
143
+ const sessionID = ctx?.sessionManager?.getSessionId?.()
144
+ if (sessionID) {
145
+ await report(sessionID, "end", "session_compact", {
146
+ reason: event?.reason ?? null,
147
+ from_extension: event?.fromExtension ?? null,
148
+ })
149
+ }
150
+ })
151
+ }
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "subconscious-cli",
3
- "version": "0.2.0",
4
- "description": "CLI for Subconscious — log in and launch coding agents (Claude Code, OpenCode, Aider, Codex) on your hosted models",
3
+ "version": "0.3.0",
4
+ "description": "CLI for Subconscious — run Claude Code, Codex, OpenCode, Cursor, Copilot, and Pi with ol-runbook integrations",
5
5
  "bin": {
6
- "subconscious": "./bin/cli.js",
7
- "subconscious-cli": "./bin/cli.js"
6
+ "subc": "./bin/cli.js"
7
+ },
8
+ "scripts": {
9
+ "test": "node --test"
8
10
  },
9
11
  "files": [
10
12
  "bin"
@@ -24,11 +26,14 @@
24
26
  "cli",
25
27
  "api-key",
26
28
  "authentication",
29
+ "profiles",
27
30
  "coding-agent",
28
31
  "claude-code",
29
32
  "opencode",
30
- "aider",
31
33
  "codex",
34
+ "cursor",
35
+ "copilot",
36
+ "pi",
32
37
  "launcher"
33
38
  ],
34
39
  "author": "Subconscious Systems",