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.
@@ -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,278 @@
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 SUBC_ENV_FILE, or a sibling .env / env.example.
53
+ SHARED_ENV="${SUBC_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
+ subc pi install [--gateway-url URL] [--api-key KEY] [--model MODEL]
68
+ [--context-window N] [--max-tokens N]
69
+ subc pi uninstall
70
+ subc pi status
71
+
72
+ Merges a Subconscious provider into ~/.pi/agent/models.json without replacing
73
+ other providers. Launch Pi with subc pi after install.
74
+
75
+ Requires: jq. Restart Pi after install (or /reload for the extension).
76
+ EOF
77
+ }
78
+
79
+ while [[ $# -gt 0 ]]; do
80
+ case "$1" in
81
+ install|uninstall|status)
82
+ COMMAND="$1"
83
+ shift
84
+ ;;
85
+ --gateway-url)
86
+ GATEWAY_URL="${2:-}"
87
+ shift 2
88
+ ;;
89
+ --api-key)
90
+ API_KEY="${2:-}"
91
+ shift 2
92
+ ;;
93
+ --model)
94
+ MODEL="${2:-}"
95
+ shift 2
96
+ ;;
97
+ --context-window)
98
+ CONTEXT_WINDOW="${2:-}"
99
+ shift 2
100
+ ;;
101
+ --max-tokens)
102
+ MAX_TOKENS="${2:-}"
103
+ shift 2
104
+ ;;
105
+ -h|--help|help)
106
+ usage
107
+ exit 0
108
+ ;;
109
+ *)
110
+ echo "unknown argument: $1" >&2
111
+ usage >&2
112
+ exit 1
113
+ ;;
114
+ esac
115
+ done
116
+
117
+ DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.2
118
+ subconscious/tim-qwen3.6-27b
119
+ subconscious/deepseek-v4-flash-marathon"
120
+ SUPPORTED_MODELS=()
121
+
122
+ add_supported_model() {
123
+ local model_id="$1" existing
124
+ [[ -n "$model_id" ]] || return 0
125
+ if [[ ! "$model_id" =~ ^[-A-Za-z0-9._:/+]+$ ]]; then
126
+ echo "error: invalid model id: $model_id" >&2
127
+ exit 1
128
+ fi
129
+ if [[ "${#SUPPORTED_MODELS[@]}" -gt 0 ]]; then
130
+ for existing in "${SUPPORTED_MODELS[@]}"; do
131
+ [[ "$existing" == "$model_id" ]] && return 0
132
+ done
133
+ fi
134
+ SUPPORTED_MODELS+=("$model_id")
135
+ }
136
+
137
+ add_supported_model "$MODEL"
138
+ while IFS= read -r model_id; do
139
+ add_supported_model "$model_id"
140
+ done <<< "${SUBCONSCIOUS_MODELS:-$DEFAULT_SUBCONSCIOUS_MODELS}"
141
+
142
+ MODEL_ENTRIES_JSON=""
143
+ for model_id in "${SUPPORTED_MODELS[@]}"; do
144
+ model_json="{\"id\":\"${model_id}\",\"contextWindow\":${CONTEXT_WINDOW},\"maxTokens\":${MAX_TOKENS},\"compat\":{\"sendSessionAffinityHeaders\":true,\"sessionAffinityFormat\":\"openai-nosession\"}}"
145
+ if [[ -n "$MODEL_ENTRIES_JSON" ]]; then
146
+ MODEL_ENTRIES_JSON="${MODEL_ENTRIES_JSON},${model_json}"
147
+ else
148
+ MODEL_ENTRIES_JSON="$model_json"
149
+ fi
150
+ done
151
+
152
+ PI_DIR="${HOME}/.pi/agent"
153
+ MODELS_JSON="${PI_DIR}/models.json"
154
+ EXTENSIONS_DIR="${PI_DIR}/extensions"
155
+ EXTENSION_DST="${EXTENSIONS_DIR}/subconscious-compaction.ts"
156
+ ENV_FILE="${PI_DIR}/subconscious.env"
157
+ MARKER='x-subconscious-client'
158
+
159
+ require_cmds() {
160
+ local missing=0
161
+ for c in jq; do
162
+ if ! command -v "$c" >/dev/null 2>&1; then
163
+ echo "missing required command: $c" >&2
164
+ missing=1
165
+ fi
166
+ done
167
+ if [[ "$missing" -ne 0 ]]; then
168
+ exit 1
169
+ fi
170
+ }
171
+
172
+ write_config() {
173
+ mkdir -p "$PI_DIR"
174
+ local base_url="${GATEWAY_URL%/}/v1"
175
+ local provider
176
+ provider=$(cat <<EOF
177
+ {
178
+ "baseUrl": "${base_url}",
179
+ "api": "openai-completions",
180
+ "apiKey": "${API_KEY}",
181
+ "headers": {
182
+ "x-subconscious-client": "pi"
183
+ },
184
+ "models": [${MODEL_ENTRIES_JSON}]
185
+ }
186
+ EOF
187
+ )
188
+ if [[ -f "$MODELS_JSON" ]]; then
189
+ local tmp
190
+ tmp="$(mktemp)"
191
+ jq --argjson provider "$provider" '
192
+ .providers = (.providers // {})
193
+ | .providers.subconscious = $provider
194
+ ' "$MODELS_JSON" >"$tmp"
195
+ mv "$tmp" "$MODELS_JSON"
196
+ else
197
+ jq -n --argjson provider "$provider" '{providers: {subconscious: $provider}}' >"$MODELS_JSON"
198
+ fi
199
+ chmod 600 "$MODELS_JSON"
200
+
201
+ umask 077
202
+ cat >"$ENV_FILE" <<EOF
203
+ # Generated by subc — do not commit secrets.
204
+ # Loaded by subconscious-compaction.ts if process env is unset.
205
+ export SUBCONSCIOUS_GATEWAY_URL='${GATEWAY_URL%/}'
206
+ export SUBCONSCIOUS_API_KEY='${API_KEY}'
207
+ EOF
208
+ chmod 600 "$ENV_FILE"
209
+ }
210
+
211
+ write_extension() {
212
+ if [[ ! -f "$EXTENSION_SRC" ]]; then
213
+ echo "warning: ${EXTENSION_SRC} missing; skipping compaction extension" >&2
214
+ return
215
+ fi
216
+ mkdir -p "$EXTENSIONS_DIR"
217
+ cp "$EXTENSION_SRC" "$EXTENSION_DST"
218
+ }
219
+
220
+ uninstall_config() {
221
+ if [[ -f "$MODELS_JSON" ]]; then
222
+ local tmp
223
+ tmp="$(mktemp)"
224
+ jq 'del(.providers.subconscious)' "$MODELS_JSON" >"$tmp"
225
+ mv "$tmp" "$MODELS_JSON"
226
+ echo "Removed Subconscious provider from $MODELS_JSON"
227
+ else
228
+ echo "No Pi models.json at $MODELS_JSON"
229
+ fi
230
+ rm -f "$EXTENSION_DST" "$ENV_FILE"
231
+ }
232
+
233
+ status() {
234
+ echo "scope: user"
235
+ echo "pi dir: $PI_DIR"
236
+ echo "config: $MODELS_JSON"
237
+ if [[ -f "$MODELS_JSON" ]] && grep -q "$MARKER" "$MODELS_JSON" 2>/dev/null; then
238
+ echo "status: installed"
239
+ echo "models: $(jq -r '[.providers.subconscious.models[].id] | join(", ")' "$MODELS_JSON" 2>/dev/null || echo 'unknown')"
240
+ echo "contextWindow: $(jq -r '.providers.subconscious.models[0].contextWindow // "unset"' "$MODELS_JSON" 2>/dev/null || echo 'unknown')"
241
+ else
242
+ echo "status: not installed"
243
+ fi
244
+ if [[ -f "$EXTENSION_DST" ]]; then
245
+ echo "compaction extension: $EXTENSION_DST (installed)"
246
+ else
247
+ echo "compaction extension: not installed"
248
+ fi
249
+ if [[ -f "$ENV_FILE" ]]; then
250
+ echo "env: $ENV_FILE (present)"
251
+ else
252
+ echo "env: missing"
253
+ fi
254
+ }
255
+
256
+ case "$COMMAND" in
257
+ install)
258
+ require_cmds
259
+ if [[ -z "$GATEWAY_URL" || -z "$API_KEY" ]]; then
260
+ echo "--gateway-url and --api-key are required for install" >&2
261
+ exit 1
262
+ fi
263
+ write_config
264
+ write_extension
265
+ echo "Merged Subconscious Pi provider into $MODELS_JSON"
266
+ if [[ -f "$EXTENSION_DST" ]]; then
267
+ echo "Installed compaction reporting extension at $EXTENSION_DST"
268
+ fi
269
+ echo "Compaction extension reads ${ENV_FILE} automatically (sourcing optional)."
270
+ echo "Restart any running Pi sessions (or /reload for the extension)."
271
+ ;;
272
+ uninstall)
273
+ uninstall_config
274
+ ;;
275
+ status)
276
+ status
277
+ ;;
278
+ esac
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bash
2
+ # Launch Pi with the Subconscious provider configured by `subc pi install`.
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 pi install' 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 pi install' 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.1",
4
- "description": "CLI for Subconscious — log in and launch coding agents (Claude Code, OpenCode, Aider, Codex) on your hosted models",
3
+ "version": "0.3.1",
4
+ "description": "CLI for Subconscious — run Claude Code, Codex, OpenCode, Cursor, Copilot, and Pi",
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",