@workweave/router 0.1.4 → 0.1.5

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/cc-statusline.sh CHANGED
@@ -145,6 +145,7 @@ prices='{
145
145
  "gemini-3-pro-preview": 0.002,
146
146
  "gemini-3.1-flash-lite-preview": 0.0001,
147
147
  "gemini-3.1-pro-preview": 0.002,
148
+ "gemini-3.5-flash": 0.0015,
148
149
  "gpt-4.1": 0.002,
149
150
  "gpt-4.1-mini": 0.0004,
150
151
  "gpt-4.1-nano": 0.0001,
@@ -190,6 +191,7 @@ prices='{
190
191
  "gemini-3-pro-preview": 0.008,
191
192
  "gemini-3.1-flash-lite-preview": 0.0004,
192
193
  "gemini-3.1-pro-preview": 0.008,
194
+ "gemini-3.5-flash": 0.009,
193
195
  "gpt-4.1": 0.008,
194
196
  "gpt-4.1-mini": 0.0016,
195
197
  "gpt-4.1-nano": 0.0004,
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Pin this session to a specific model via the Weave Router
3
+ argument-hint: <model-id>
4
+ ---
5
+
6
+ /force-model $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: Clear an active /force-model pin and resume automatic routing
3
+ ---
4
+
5
+ /unforce-model
package/install.sh CHANGED
@@ -395,6 +395,10 @@ write_codex_config() {
395
395
  if [ -n "$block_name" ]; then
396
396
  headers_parts="${headers_parts}, \"X-Weave-User-Name\" = \"${esc_name}\""
397
397
  fi
398
+ # Tag the client so telemetry can attribute traffic to Codex vs other CLIs
399
+ # that share the same router key. The router otherwise has to guess from
400
+ # User-Agent.
401
+ headers_parts="${headers_parts}, \"X-App\" = \"codex\""
398
402
  local headers_line="http_headers = { ${headers_parts} }"
399
403
 
400
404
  local block
@@ -839,11 +843,57 @@ else
839
843
  info "No identity set — router traffic will be attributed by account UUID only."
840
844
  fi
841
845
 
846
+ # ---------- slash command wrappers (shared by both targets) ----------
847
+ #
848
+ # Claude Code intercepts any prompt starting with "/" as a local slash command,
849
+ # so a typed /force-model would resolve to "Unknown command" and never reach
850
+ # the router. Codex CLI does the same (its built-in / menu has its own set).
851
+ # Drop wrapper markdown files into the per-target commands directory so the
852
+ # slash invocation expands locally into a literal "/force-model …" prompt that
853
+ # the router's first-line parser picks up.
854
+ #
855
+ # Layout:
856
+ # Claude: <settings_dir>/commands/{force-model,unforce-model}.md → /force-model
857
+ # Codex: <codex_dir>/prompts/{force-model,unforce-model}.md → /prompts:force-model
858
+ #
859
+ # Files come from install/commands/ in the repo (or the colocated commands/
860
+ # directory the npm package ships alongside install.sh).
861
+ install_slash_commands() {
862
+ dst_dir="$1"
863
+ commands_src_dir=""
864
+ for candidate in \
865
+ "$script_dir/commands" \
866
+ "$script_dir/../commands"
867
+ do
868
+ if [ -d "$candidate" ]; then
869
+ commands_src_dir="$candidate"
870
+ break
871
+ fi
872
+ done
873
+ [ -n "$commands_src_dir" ] || return 0
874
+
875
+ if [ "$scope" = "project" ] || [ -n "$install_dir" ]; then
876
+ refuse_if_symlink "$dst_dir"
877
+ fi
878
+ mkdir -p "$dst_dir"
879
+ for cmd in force-model unforce-model; do
880
+ src="$commands_src_dir/$cmd.md"
881
+ dst="$dst_dir/$cmd.md"
882
+ [ -f "$src" ] || continue
883
+ if [ "$scope" = "project" ] || [ -n "$install_dir" ]; then
884
+ refuse_if_symlink "$dst"
885
+ fi
886
+ cp "$src" "$dst"
887
+ done
888
+ ok "Slash commands written to $dst_dir (force-model, unforce-model)"
889
+ }
890
+
842
891
  # ---------- codex install path (dispatch + exit before the Claude-only writes) ----------
843
892
 
844
893
  if [ "$target" = "codex" ]; then
845
894
  write_codex_config "$codex_config_file" "$base_url" "$api_key" "$user_email" "$user_name"
846
895
  ok "Codex config written to $codex_config_file"
896
+ install_slash_commands "$codex_dir/prompts"
847
897
 
848
898
  # Project scope: ensure the per-teammate config (which holds the router key)
849
899
  # is gitignored. The base URL is the same for every teammate, so a
@@ -1043,6 +1093,7 @@ prices='{
1043
1093
  "gemini-3-pro-preview": 0.002,
1044
1094
  "gemini-3.1-flash-lite-preview": 0.0001,
1045
1095
  "gemini-3.1-pro-preview": 0.002,
1096
+ "gemini-3.5-flash": 0.0015,
1046
1097
  "gpt-4.1": 0.002,
1047
1098
  "gpt-4.1-mini": 0.0004,
1048
1099
  "gpt-4.1-nano": 0.0001,
@@ -1088,6 +1139,7 @@ prices='{
1088
1139
  "gemini-3-pro-preview": 0.008,
1089
1140
  "gemini-3.1-flash-lite-preview": 0.0004,
1090
1141
  "gemini-3.1-pro-preview": 0.008,
1142
+ "gemini-3.5-flash": 0.009,
1091
1143
  "gpt-4.1": 0.008,
1092
1144
  "gpt-4.1-mini": 0.0016,
1093
1145
  "gpt-4.1-nano": 0.0004,
@@ -1297,6 +1349,7 @@ fi
1297
1349
  if [ -n "$user_name" ]; then
1298
1350
  custom_headers="$custom_headers"$'\n'"X-Weave-User-Name: $user_name"
1299
1351
  fi
1352
+ custom_headers="$custom_headers"$'\n'"X-App: claude-code"
1300
1353
 
1301
1354
  if [ "$scope" = "project" ] && [ -z "$install_dir" ]; then
1302
1355
  jq -n --arg url "$base_url" --arg sl "$statusline_path_for_settings" '{
@@ -1329,6 +1382,9 @@ else
1329
1382
  fi
1330
1383
  ok "Settings written to $settings_file"
1331
1384
 
1385
+ # Slash command wrappers — see install_slash_commands() below for the why.
1386
+ install_slash_commands "$settings_dir/commands"
1387
+
1332
1388
  if [ "$scope" = "project" ] && [ -z "$install_dir" ]; then
1333
1389
  jq -n --arg header "$custom_headers" '{
1334
1390
  env: { ANTHROPIC_CUSTOM_HEADERS: $header }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workweave/router",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "One-command installer that points Claude Code at the Weave Router.",
5
5
  "bin": {
6
6
  "weave-router": "bin.js"
@@ -10,6 +10,7 @@
10
10
  "install.sh",
11
11
  "uninstall.sh",
12
12
  "cc-statusline.sh",
13
+ "commands/",
13
14
  "README.md",
14
15
  "LICENSE"
15
16
  ],
package/uninstall.sh CHANGED
@@ -156,6 +156,22 @@ if [ "$target" = "codex" ]; then
156
156
  info "No Codex config at $codex_config_file (already uninstalled?)"
157
157
  fi
158
158
 
159
+ # Remove only the prompt files this installer owns; leave any user-authored
160
+ # entries in prompts/ alone. The dir itself is dropped only if empty after.
161
+ codex_prompts_dir="$codex_dir/prompts"
162
+ if [ -d "$codex_prompts_dir" ]; then
163
+ refuse_if_symlink "$codex_prompts_dir"
164
+ for cmd in force-model unforce-model; do
165
+ cmd_file="$codex_prompts_dir/$cmd.md"
166
+ if [ -f "$cmd_file" ]; then
167
+ refuse_if_symlink "$cmd_file"
168
+ rm -f "$cmd_file"
169
+ ok "Removed $cmd_file"
170
+ fi
171
+ done
172
+ rmdir "$codex_prompts_dir" 2>/dev/null || true
173
+ fi
174
+
159
175
  if [ -n "$install_dir" ]; then
160
176
  ok "Weave Router uninstalled from $install_dir (Codex)."
161
177
  else
@@ -269,6 +285,24 @@ for f in "$statusline_file"; do
269
285
  fi
270
286
  done
271
287
 
288
+ # Remove only the slash command files this installer owns; leave any other
289
+ # files in commands/ alone. The directory itself stays if it still contains
290
+ # unrelated user commands.
291
+ commands_dir="$(dirname "$settings_file")/commands"
292
+ if [ -d "$commands_dir" ]; then
293
+ refuse_if_symlink "$commands_dir"
294
+ for cmd in force-model unforce-model; do
295
+ cmd_file="$commands_dir/$cmd.md"
296
+ if [ -f "$cmd_file" ]; then
297
+ refuse_if_symlink "$cmd_file"
298
+ rm -f "$cmd_file"
299
+ ok "Removed $cmd_file"
300
+ fi
301
+ done
302
+ # Clean up the dir only if we left nothing behind.
303
+ rmdir "$commands_dir" 2>/dev/null || true
304
+ fi
305
+
272
306
  if [ -n "$install_dir" ]; then
273
307
  ok "Weave Router uninstalled from $install_dir."
274
308
  else