sisyphi 1.1.25 → 1.1.27

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.
Files changed (76) hide show
  1. package/README.md +35 -35
  2. package/deploy/aws/main.tf +1 -1
  3. package/deploy/aws/variables.tf +1 -1
  4. package/deploy/aws/versions.tf +1 -1
  5. package/deploy/hetzner/variables.tf +1 -1
  6. package/deploy/hetzner/versions.tf +1 -1
  7. package/deploy/shared/cloud-init.yaml.tpl +1 -1
  8. package/dist/cli.js +619 -200
  9. package/dist/cli.js.map +1 -1
  10. package/dist/daemon.js +23 -11
  11. package/dist/daemon.js.map +1 -1
  12. package/dist/deploy/aws/main.tf +1 -1
  13. package/dist/deploy/aws/variables.tf +1 -1
  14. package/dist/deploy/aws/versions.tf +1 -1
  15. package/dist/deploy/hetzner/variables.tf +1 -1
  16. package/dist/deploy/hetzner/versions.tf +1 -1
  17. package/dist/deploy/shared/cloud-init.yaml.tpl +1 -1
  18. package/dist/templates/agent-plugin/agents/explore.md +2 -2
  19. package/dist/templates/agent-plugin/agents/implementor.md +2 -2
  20. package/dist/templates/agent-plugin/agents/operator.md +3 -3
  21. package/dist/templates/agent-plugin/agents/plan.md +2 -2
  22. package/dist/templates/agent-plugin/agents/problem.md +8 -8
  23. package/dist/templates/agent-plugin/agents/review-plan/CLAUDE.md +1 -1
  24. package/dist/templates/agent-plugin/agents/spec/requirements-writer.md +1 -1
  25. package/dist/templates/agent-plugin/agents/spec.md +19 -19
  26. package/dist/templates/agent-plugin/skills/humanloop/SKILL.md +7 -7
  27. package/dist/templates/agent-plugin/skills/perspective-fanout/SKILL.md +2 -2
  28. package/dist/templates/agent-plugin/skills/problem-plateau-breakers/SKILL.md +2 -2
  29. package/dist/templates/agent-suffix.md +3 -3
  30. package/dist/templates/dashboard-claude.md +13 -13
  31. package/dist/templates/orchestrator-base.md +13 -13
  32. package/dist/templates/orchestrator-completion.md +11 -11
  33. package/dist/templates/orchestrator-discovery.md +5 -5
  34. package/dist/templates/orchestrator-impl.md +8 -8
  35. package/dist/templates/orchestrator-planning.md +6 -6
  36. package/dist/templates/orchestrator-plugin/commands/sisyphus/scratch.md +1 -1
  37. package/dist/templates/orchestrator-plugin/commands/sisyphus/strategize.md +2 -2
  38. package/dist/templates/orchestrator-plugin/skills/humanloop/SKILL.md +9 -9
  39. package/dist/templates/orchestrator-plugin/skills/orchestration/CLAUDE.md +1 -1
  40. package/dist/templates/orchestrator-plugin/skills/orchestration/SKILL.md +1 -1
  41. package/dist/templates/orchestrator-plugin/skills/orchestration/strategy.md +4 -4
  42. package/dist/templates/orchestrator-plugin/skills/orchestration/task-patterns.md +2 -2
  43. package/dist/templates/orchestrator-plugin/skills/orchestration/workflow-examples.md +1 -1
  44. package/dist/templates/orchestrator-validation.md +5 -5
  45. package/dist/templates/termrender-haiku-system.md +1 -1
  46. package/dist/tui.js +8 -8
  47. package/dist/tui.js.map +1 -1
  48. package/package.json +2 -1
  49. package/templates/agent-plugin/agents/explore.md +2 -2
  50. package/templates/agent-plugin/agents/implementor.md +2 -2
  51. package/templates/agent-plugin/agents/operator.md +3 -3
  52. package/templates/agent-plugin/agents/plan.md +2 -2
  53. package/templates/agent-plugin/agents/problem.md +8 -8
  54. package/templates/agent-plugin/agents/review-plan/CLAUDE.md +1 -1
  55. package/templates/agent-plugin/agents/spec/requirements-writer.md +1 -1
  56. package/templates/agent-plugin/agents/spec.md +19 -19
  57. package/templates/agent-plugin/skills/humanloop/SKILL.md +7 -7
  58. package/templates/agent-plugin/skills/perspective-fanout/SKILL.md +2 -2
  59. package/templates/agent-plugin/skills/problem-plateau-breakers/SKILL.md +2 -2
  60. package/templates/agent-suffix.md +3 -3
  61. package/templates/dashboard-claude.md +13 -13
  62. package/templates/orchestrator-base.md +13 -13
  63. package/templates/orchestrator-completion.md +11 -11
  64. package/templates/orchestrator-discovery.md +5 -5
  65. package/templates/orchestrator-impl.md +8 -8
  66. package/templates/orchestrator-planning.md +6 -6
  67. package/templates/orchestrator-plugin/commands/sisyphus/scratch.md +1 -1
  68. package/templates/orchestrator-plugin/commands/sisyphus/strategize.md +2 -2
  69. package/templates/orchestrator-plugin/skills/humanloop/SKILL.md +9 -9
  70. package/templates/orchestrator-plugin/skills/orchestration/CLAUDE.md +1 -1
  71. package/templates/orchestrator-plugin/skills/orchestration/SKILL.md +1 -1
  72. package/templates/orchestrator-plugin/skills/orchestration/strategy.md +4 -4
  73. package/templates/orchestrator-plugin/skills/orchestration/task-patterns.md +2 -2
  74. package/templates/orchestrator-plugin/skills/orchestration/workflow-examples.md +1 -1
  75. package/templates/orchestrator-validation.md +5 -5
  76. package/templates/termrender-haiku-system.md +1 -1
package/dist/cli.js CHANGED
@@ -120,6 +120,15 @@ function deployCredsPath(provider) {
120
120
  function deployTailscaleEnvPath() {
121
121
  return join(deployDir(), "tailscale.env");
122
122
  }
123
+ function boxRepoPath(repo) {
124
+ return `~/projects/${repo}`;
125
+ }
126
+ function boxCloudSidecarPath(repo) {
127
+ return `~/.sisyphus/cloud/${repo}.json`;
128
+ }
129
+ function boxCloudSidecarDir() {
130
+ return `~/.sisyphus/cloud`;
131
+ }
123
132
  var init_paths = __esm({
124
133
  "src/shared/paths.ts"() {
125
134
  "use strict";
@@ -138,16 +147,16 @@ function atomicWrite(filePath, data) {
138
147
  }
139
148
  async function withLock(key, fn) {
140
149
  const prev = locks.get(key) ?? Promise.resolve();
141
- let resolve11;
150
+ let resolve12;
142
151
  const next = new Promise((r) => {
143
- resolve11 = r;
152
+ resolve12 = r;
144
153
  });
145
154
  locks.set(key, next);
146
155
  await prev;
147
156
  try {
148
157
  return fn();
149
158
  } finally {
150
- resolve11();
159
+ resolve12();
151
160
  if (locks.get(key) === next) {
152
161
  locks.delete(key);
153
162
  }
@@ -164,7 +173,9 @@ var init_atomic = __esm({
164
173
  // src/cli/deploy/creds.ts
165
174
  var creds_exports = {};
166
175
  __export(creds_exports, {
176
+ PROVIDERS: () => PROVIDERS,
167
177
  ensureDeployDir: () => ensureDeployDir,
178
+ isValidProvider: () => isValidProvider,
168
179
  loadProviderCreds: () => loadProviderCreds,
169
180
  maskValue: () => maskValue,
170
181
  promptLine: () => promptLine,
@@ -173,6 +184,9 @@ __export(creds_exports, {
173
184
  });
174
185
  import { chmodSync as chmodSync3, existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync27 } from "fs";
175
186
  import { createInterface as createInterface4 } from "readline";
187
+ function isValidProvider(value) {
188
+ return PROVIDERS.includes(value);
189
+ }
176
190
  function ensureDeployDir() {
177
191
  const dir = deployDir();
178
192
  if (!existsSync24(dir)) mkdirSync12(dir, { recursive: true, mode: 448 });
@@ -194,7 +208,7 @@ function parseEnvFile(text) {
194
208
  return out;
195
209
  }
196
210
  function serializeEnvFile(values) {
197
- const lines = ["# Managed by `sisyphus deploy`. Do not edit unless you know what you are doing."];
211
+ const lines = ["# Managed by `sis deploy`. Do not edit unless you know what you are doing."];
198
212
  for (const [k, v] of Object.entries(values)) {
199
213
  lines.push(`${k}=${v}`);
200
214
  }
@@ -222,7 +236,7 @@ async function promptLine(question, hidden) {
222
236
  }
223
237
  };
224
238
  }
225
- const answer = await new Promise((resolve11) => rl.question(question, resolve11));
239
+ const answer = await new Promise((resolve12) => rl.question(question, resolve12));
226
240
  rl.close();
227
241
  if (hidden) process.stdout.write("\n");
228
242
  return answer.trim();
@@ -272,12 +286,13 @@ function writeTailscaleEnv(env) {
272
286
  if (env.tag) values.TS_TAG = env.tag;
273
287
  writeEnvFile(deployTailscaleEnvPath(), values);
274
288
  }
275
- var SPECS;
289
+ var PROVIDERS, SPECS;
276
290
  var init_creds = __esm({
277
291
  "src/cli/deploy/creds.ts"() {
278
292
  "use strict";
279
293
  init_atomic();
280
294
  init_paths();
295
+ PROVIDERS = ["hetzner", "aws"];
281
296
  SPECS = {
282
297
  hetzner: {
283
298
  provider: "hetzner",
@@ -296,8 +311,8 @@ var init_creds = __esm({
296
311
 
297
312
  // src/cli/index.ts
298
313
  import { Command } from "commander";
299
- import { existsSync as existsSync29, mkdirSync as mkdirSync14, readFileSync as readFileSync31 } from "fs";
300
- import { dirname as dirname12, join as join26 } from "path";
314
+ import { existsSync as existsSync30, mkdirSync as mkdirSync14, readFileSync as readFileSync31 } from "fs";
315
+ import { dirname as dirname12, join as join27 } from "path";
301
316
  import { fileURLToPath as fileURLToPath5 } from "url";
302
317
 
303
318
  // src/cli/commands/start.ts
@@ -309,13 +324,13 @@ init_paths();
309
324
  import { connect } from "net";
310
325
  function rawSend(request, timeoutMs = 1e4) {
311
326
  const sock = socketPath();
312
- return new Promise((resolve11, reject) => {
327
+ return new Promise((resolve12, reject) => {
313
328
  const socket = connect(sock);
314
329
  let data = "";
315
330
  const timeout = setTimeout(() => {
316
331
  socket.destroy();
317
332
  reject(new Error(`Request timed out after ${(timeoutMs / 1e3).toFixed(0)}s. The daemon may be overloaded.
318
- Check: sisyphus admin doctor
333
+ Check: sis admin doctor
319
334
  Logs: tail -20 ~/.sisyphus/daemon.log`));
320
335
  }, timeoutMs);
321
336
  socket.on("connect", () => {
@@ -329,7 +344,7 @@ function rawSend(request, timeoutMs = 1e4) {
329
344
  const line = data.slice(0, newlineIdx);
330
345
  socket.destroy();
331
346
  try {
332
- resolve11(JSON.parse(line));
347
+ resolve12(JSON.parse(line));
333
348
  } catch {
334
349
  reject(new Error(`Invalid JSON response from daemon: ${line}`));
335
350
  }
@@ -594,7 +609,7 @@ while IFS=$'\\t' read -r type name scwd phase sid; do
594
609
  [ "$sid" = "$current_id" ] && { cwd="$scwd"; break; }
595
610
  done < "$MANIFEST"
596
611
  if [ -z "$cwd" ]; then
597
- tmux display-message "sisyphus: '$current_name' has no @sisyphus_cwd \u2014 run 'sisyphus start' here to register"
612
+ tmux display-message "sisyphus: '$current_name' has no @sisyphus_cwd \u2014 run 'sis start' here to register"
598
613
  exit 0
599
614
  fi
600
615
  session_ids=()
@@ -699,7 +714,7 @@ tmpfile=$(mktemp /tmp/sisyphus-new-XXXXXX.md)
699
714
  trap 'rm -f "$tmpfile"' EXIT
700
715
  nvim "$tmpfile"
701
716
  grep -q '[^[:space:]]' "$tmpfile" || exit 0
702
- exec sisyphus start "$(cat "$tmpfile")"
717
+ exec sis start "$(cat "$tmpfile")"
703
718
  `;
704
719
  var MESSAGE_SCRIPT = `#!/bin/bash
705
720
  # Open nvim to compose a message for the current session's orchestrator
@@ -730,7 +745,7 @@ tmpfile=$(mktemp /tmp/sisyphus-msg-XXXXXX.md)
730
745
  trap 'rm -f "$tmpfile"' EXIT
731
746
  nvim "$tmpfile"
732
747
  grep -q '[^[:space:]]' "$tmpfile" || exit 0
733
- exec sisyphus message --session "$session_id" "$(cat "$tmpfile")"
748
+ exec sis message --session "$session_id" "$(cat "$tmpfile")"
734
749
  `;
735
750
  var SESSION_RESOLVE = `
736
751
  tmux_sid=$(tmux display-message -p '#{session_id}')
@@ -766,7 +781,7 @@ var KILL_SESSION_SCRIPT = `#!/bin/bash
766
781
  # Kill the sisyphus session associated with the current tmux session
767
782
  ${SESSION_RESOLVE}
768
783
 
769
- sisyphus session kill "$session_id" >/dev/null 2>&1
784
+ sis session kill "$session_id" >/dev/null 2>&1
770
785
  ${GO_HOME_AFTER}
771
786
  `;
772
787
  var DELETE_SESSION_SCRIPT = `#!/bin/bash
@@ -776,7 +791,7 @@ ${SESSION_RESOLVE}
776
791
  printf "\\033[31mType 'yes' to confirm:\\033[0m "
777
792
  read -r answer
778
793
  [ "$answer" = "yes" ] || exit 0
779
- sisyphus session delete "$session_id" --cwd "$cwd" >/dev/null 2>&1
794
+ sis session delete "$session_id" --cwd "$cwd" >/dev/null 2>&1
780
795
  ${GO_HOME_AFTER}
781
796
  `;
782
797
  var HELP_SCRIPT = `#!/bin/bash
@@ -810,9 +825,9 @@ if [ -z "$session_id" ]; then
810
825
  fi
811
826
 
812
827
  if [ -n "$session_id" ]; then
813
- sisyphus status "$session_id" 2>&1 | less -R
828
+ sis status "$session_id" 2>&1 | less -R
814
829
  else
815
- sisyphus list 2>&1 | less -R
830
+ sis list 2>&1 | less -R
816
831
  fi
817
832
  `;
818
833
  var PICK_SESSION_SCRIPT = `#!/bin/bash
@@ -871,7 +886,7 @@ short_id="\${session_id:0:8}"
871
886
  printf "\\033[33mContinue session %s...?\\033[0m (y/n) " "$short_id"
872
887
  read -r answer
873
888
  [ "$answer" = "y" ] || [ "$answer" = "yes" ] || exit 0
874
- sisyphus session continue --session "$session_id"
889
+ sis session continue --session "$session_id"
875
890
  sleep 1
876
891
  `;
877
892
  var OPEN_ROADMAP_SCRIPT = `#!/bin/bash
@@ -895,18 +910,18 @@ var EXPORT_SESSION_SCRIPT = `#!/bin/bash
895
910
  ${SESSION_RESOLVE}
896
911
 
897
912
  echo "Exporting session \${session_id:0:8}..."
898
- sisyphus admin export "$session_id" --cwd "$cwd"
913
+ sis admin export "$session_id" --cwd "$cwd"
899
914
  echo ""
900
915
  read -n 1 -s -r -p "Press a key to close."
901
916
  `;
902
917
  var RESTART_AGENT_SCRIPT = `#!/bin/bash
903
918
  # Pick a sisyphus agent and restart it (fzf picker with confirm for running agents).
904
- # Assumes macOS (fzf optional). Requires \`sisyphus status --json\`.
919
+ # Assumes macOS (fzf optional). Requires \`sis status --json\`.
905
920
  ${SESSION_RESOLVE}
906
921
 
907
922
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
908
923
 
909
- agents_json=$(sisyphus status "$session_id" --json 2>/dev/null)
924
+ agents_json=$(sis status "$session_id" --json 2>/dev/null)
910
925
  if [ -z "$agents_json" ]; then
911
926
  echo "Failed to read session status"; sleep 1; exit 1
912
927
  fi
@@ -943,7 +958,7 @@ if [ "\${statuses[$idx]}" = "running" ]; then
943
958
  [ "$answer" = "yes" ] || exit 0
944
959
  fi
945
960
 
946
- sisyphus agent restart "\${ids[$idx]}" --session "$session_id"
961
+ sis agent restart "\${ids[$idx]}" --session "$session_id"
947
962
  echo ""
948
963
  read -n 1 -s -r -p "Press a key to close."
949
964
  `;
@@ -993,9 +1008,9 @@ nvim "$tmpfile"
993
1008
  body=$(grep -v '^[[:space:]]*#' "$tmpfile" | sed '/^[[:space:]]*$/d')
994
1009
 
995
1010
  if [ -z "$body" ]; then
996
- exec sisyphus session resume "$session_id"
1011
+ exec sis session resume "$session_id"
997
1012
  else
998
- exec sisyphus session resume "$session_id" "$body"
1013
+ exec sis session resume "$session_id" "$body"
999
1014
  fi
1000
1015
  `;
1001
1016
  var ROLLBACK_SESSION_SCRIPT = `#!/bin/bash
@@ -1022,7 +1037,7 @@ if [ "$cycle_input" -lt 1 ]; then
1022
1037
  exit 0
1023
1038
  fi
1024
1039
 
1025
- sisyphus session rollback "$session_id" "$cycle_input"
1040
+ sis session rollback "$session_id" "$cycle_input"
1026
1041
  echo ""
1027
1042
  echo "Rolled back to cycle $cycle_input \u2014 use [C-s S r] to resume."
1028
1043
  read -n 1 -s -r -p "Press a key to close."
@@ -1059,12 +1074,12 @@ fi
1059
1074
 
1060
1075
  # Fallback: orchestrator window is gone. Open last claude session in a popup.
1061
1076
  state="$cwd/.sisyphus/sessions/$session_id/state.json"
1062
- [ ! -f "$state" ] && { tmux display-message "Window dead and no state.json \u2014 try sisyphus session resume"; exit 0; }
1077
+ [ ! -f "$state" ] && { tmux display-message "Window dead and no state.json \u2014 try sis session resume"; exit 0; }
1063
1078
 
1064
1079
  claude_sid=$(jq -r '[.orchestratorCycles[].claudeSessionId] | last // empty' "$state")
1065
1080
 
1066
1081
  if [ -z "$claude_sid" ]; then
1067
- tmux display-message "No orchestrator claude session id found \u2014 try sisyphus session resume"
1082
+ tmux display-message "No orchestrator claude session id found \u2014 try sis session resume"
1068
1083
  exit 0
1069
1084
  fi
1070
1085
 
@@ -1086,7 +1101,7 @@ nvim "$tmpfile"
1086
1101
  body=$(grep -v '^[[:space:]]*#' "$tmpfile" | sed '/^[[:space:]]*$/d')
1087
1102
  [ -z "$body" ] && exit 0
1088
1103
 
1089
- exec sisyphus agent spawn --session "$session_id" --name "agent" --instruction "$body"
1104
+ exec sis agent spawn --session "$session_id" --name "agent" --instruction "$body"
1090
1105
  `;
1091
1106
  var SEARCH_REPORTS_SCRIPT = `#!/bin/bash
1092
1107
  # fzf over reports/*.md across all sessions for the current cwd.
@@ -1125,12 +1140,12 @@ fi
1125
1140
  `;
1126
1141
  var JUMP_TO_PANE_SCRIPT = `#!/bin/bash
1127
1142
  # Pick a sisyphus agent and jump to its tmux pane.
1128
- # Assumes macOS (pbcopy, fzf optional). Requires \`sisyphus status --json\`.
1143
+ # Assumes macOS (pbcopy, fzf optional). Requires \`sis status --json\`.
1129
1144
  ${SESSION_RESOLVE}
1130
1145
 
1131
1146
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
1132
1147
 
1133
- agents_json=$(sisyphus status "$session_id" --json 2>/dev/null)
1148
+ agents_json=$(sis status "$session_id" --json 2>/dev/null)
1134
1149
  if [ -z "$agents_json" ]; then
1135
1150
  echo "Failed to read session status"; sleep 1; exit 1
1136
1151
  fi
@@ -1171,12 +1186,12 @@ tmux select-pane -t "$target_pane"
1171
1186
  `;
1172
1187
  var MSG_AGENT_SCRIPT = `#!/bin/bash
1173
1188
  # Pick a sisyphus agent and send it a message via nvim.
1174
- # Assumes macOS (fzf optional). Requires \`sisyphus status --json\` and \`--agent\` on message.
1189
+ # Assumes macOS (fzf optional). Requires \`sis status --json\` and \`--agent\` on message.
1175
1190
  ${SESSION_RESOLVE}
1176
1191
 
1177
1192
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
1178
1193
 
1179
- agents_json=$(sisyphus status "$session_id" --json 2>/dev/null)
1194
+ agents_json=$(sis status "$session_id" --json 2>/dev/null)
1180
1195
  if [ -z "$agents_json" ]; then
1181
1196
  echo "Failed to read session status"; sleep 1; exit 1
1182
1197
  fi
@@ -1209,16 +1224,16 @@ tmpfile=$(mktemp /tmp/sisyphus-msg-agent-XXXX.md)
1209
1224
  trap 'rm -f "$tmpfile"' EXIT
1210
1225
  nvim "$tmpfile"
1211
1226
  grep -q '[^[:space:]]' "$tmpfile" || exit 0
1212
- exec sisyphus message --session "$session_id" --agent "\${ids[$idx]}" "$(cat "$tmpfile")"
1227
+ exec sis message --session "$session_id" --agent "\${ids[$idx]}" "$(cat "$tmpfile")"
1213
1228
  `;
1214
1229
  var RERUN_AGENT_SCRIPT = `#!/bin/bash
1215
1230
  # Pick a sisyphus agent and spawn a retry with its original instruction.
1216
- # Assumes macOS (fzf optional). Requires \`sisyphus status --json\`.
1231
+ # Assumes macOS (fzf optional). Requires \`sis status --json\`.
1217
1232
  ${SESSION_RESOLVE}
1218
1233
 
1219
1234
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
1220
1235
 
1221
- agents_json=$(sisyphus status "$session_id" --json 2>/dev/null)
1236
+ agents_json=$(sis status "$session_id" --json 2>/dev/null)
1222
1237
  if [ -z "$agents_json" ]; then
1223
1238
  echo "Failed to read session status"; sleep 1; exit 1
1224
1239
  fi
@@ -1260,11 +1275,11 @@ if [ "\${#instr}" -lt 20 ]; then
1260
1275
  exit 1
1261
1276
  fi
1262
1277
 
1263
- exec sisyphus agent spawn --session "$session_id" --agent-type "\${atypes[$idx]}" --name "\${anames[$idx]}-retry-$(date +%s)" --instruction "$instr"
1278
+ exec sis agent spawn --session "$session_id" --agent-type "\${atypes[$idx]}" --name "\${anames[$idx]}-retry-$(date +%s)" --instruction "$instr"
1264
1279
  `;
1265
1280
  var OPEN_CLAUDE_AGENT_SCRIPT = `#!/bin/bash
1266
1281
  # Pick a sisyphus agent or orchestrator cycle and resume its Claude session.
1267
- # Assumes macOS (fzf optional). Requires \`sisyphus status --json\`.
1282
+ # Assumes macOS (fzf optional). Requires \`sis status --json\`.
1268
1283
  ${SESSION_RESOLVE}
1269
1284
 
1270
1285
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
@@ -1272,7 +1287,7 @@ command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
1272
1287
  state="$cwd/.sisyphus/sessions/$session_id/state.json"
1273
1288
  [ ! -f "$state" ] && { echo "No state.json for this session"; sleep 1; exit 1; }
1274
1289
 
1275
- agents_json=$(sisyphus status "$session_id" --json 2>/dev/null)
1290
+ agents_json=$(sis status "$session_id" --json 2>/dev/null)
1276
1291
  if [ -z "$agents_json" ]; then
1277
1292
  echo "Failed to read session status"; sleep 1; exit 1
1278
1293
  fi
@@ -1312,12 +1327,12 @@ cd "$cwd" && exec claude --resume "$cid"
1312
1327
  var TAIL_AGENT_LOGS_SCRIPT = `#!/bin/bash
1313
1328
  # Pick a sisyphus agent and view its tmux pane scrollback (last 2000 lines) in less.
1314
1329
  # Uses tmux capture-pane \u2014 no tail -f, no pipe-pane side effects.
1315
- # Assumes macOS (fzf optional). Requires \`sisyphus status --json\`.
1330
+ # Assumes macOS (fzf optional). Requires \`sis status --json\`.
1316
1331
  ${SESSION_RESOLVE}
1317
1332
 
1318
1333
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
1319
1334
 
1320
- agents_json=$(sisyphus status "$session_id" --json 2>/dev/null)
1335
+ agents_json=$(sis status "$session_id" --json 2>/dev/null)
1321
1336
  if [ -z "$agents_json" ]; then
1322
1337
  echo "Failed to read session status"; sleep 1; exit 1
1323
1338
  fi
@@ -1354,12 +1369,12 @@ tmux capture-pane -t "$target_pane" -p -S -2000 | less +G
1354
1369
  `;
1355
1370
  var KILL_AGENT_SCRIPT = `#!/bin/bash
1356
1371
  # Pick a sisyphus agent and kill it (with red confirmation prompt).
1357
- # Assumes macOS (fzf optional). Requires \`sisyphus status --json\` and \`sisyphus agent kill\`.
1372
+ # Assumes macOS (fzf optional). Requires \`sis status --json\` and \`sis agent kill\`.
1358
1373
  ${SESSION_RESOLVE}
1359
1374
 
1360
1375
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
1361
1376
 
1362
- agents_json=$(sisyphus status "$session_id" --json 2>/dev/null)
1377
+ agents_json=$(sis status "$session_id" --json 2>/dev/null)
1363
1378
  if [ -z "$agents_json" ]; then
1364
1379
  echo "Failed to read session status"; sleep 1; exit 1
1365
1380
  fi
@@ -1391,18 +1406,18 @@ fi
1391
1406
  printf '\\033[31mKill %s? (yes/no): \\033[0m' "\${ids[$idx]}"
1392
1407
  read -r answer
1393
1408
  [ "$answer" = "yes" ] || exit 0
1394
- sisyphus agent kill "\${ids[$idx]}" --session "$session_id"
1409
+ sis agent kill "\${ids[$idx]}" --session "$session_id"
1395
1410
  echo ""
1396
1411
  read -n 1 -s -r -p "Press a key to close."
1397
1412
  `;
1398
1413
  var COPY_AGENT_ID_SCRIPT = `#!/bin/bash
1399
1414
  # Pick a sisyphus agent and copy its ID to clipboard.
1400
- # Assumes macOS (pbcopy, fzf optional). Requires \`sisyphus status --json\`.
1415
+ # Assumes macOS (pbcopy, fzf optional). Requires \`sis status --json\`.
1401
1416
  ${SESSION_RESOLVE}
1402
1417
 
1403
1418
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
1404
1419
 
1405
- agents_json=$(sisyphus status "$session_id" --json 2>/dev/null)
1420
+ agents_json=$(sis status "$session_id" --json 2>/dev/null)
1406
1421
  if [ -z "$agents_json" ]; then
1407
1422
  echo "Failed to read session status"; sleep 1; exit 1
1408
1423
  fi
@@ -1477,10 +1492,10 @@ tmux display-message "Copied session ID"
1477
1492
  `;
1478
1493
  var COPY_CONTEXT_SCRIPT = `#!/bin/bash
1479
1494
  # Copy the session context XML to clipboard.
1480
- # Assumes macOS (pbcopy). Requires \`sisyphus session context\`.
1495
+ # Assumes macOS (pbcopy). Requires \`sis session context\`.
1481
1496
  ${SESSION_RESOLVE}
1482
1497
 
1483
- sisyphus session context "$session_id" --cwd "$cwd" | pbcopy
1498
+ sis session context "$session_id" --cwd "$cwd" | pbcopy
1484
1499
  tmux display-message "Copied session context (XML)"
1485
1500
  `;
1486
1501
  var EDIT_CONTEXT_FILE_SCRIPT = `#!/bin/bash
@@ -1541,7 +1556,7 @@ if [ "\${#instruction}" -lt 20 ]; then
1541
1556
  fi
1542
1557
 
1543
1558
  name="explore-$(date +%s)"
1544
- sisyphus agent spawn \\
1559
+ sis agent spawn \\
1545
1560
  --agent-type sisyphus:explore \\
1546
1561
  --name "$name" \\
1547
1562
  --session "$session_id" \\
@@ -1575,7 +1590,7 @@ if [ "\${#instruction}" -lt 20 ]; then
1575
1590
  fi
1576
1591
 
1577
1592
  name="debug-$(date +%s)"
1578
- sisyphus agent spawn \\
1593
+ sis agent spawn \\
1579
1594
  --agent-type sisyphus:debug \\
1580
1595
  --name "$name" \\
1581
1596
  --session "$session_id" \\
@@ -1623,7 +1638,7 @@ args=()
1623
1638
  [ -n "$clone_name" ] && args+=(--name "$clone_name")
1624
1639
  [ "$copy_strategy" = "y" ] || [ "$copy_strategy" = "Y" ] && args+=(--strategy)
1625
1640
 
1626
- sisyphus session clone "\${args[@]}" "$goal"
1641
+ sis session clone "\${args[@]}" "$goal"
1627
1642
  exit_code=$?
1628
1643
  read -n 1 -s -r -p "Press a key to close."
1629
1644
  exit $exit_code
@@ -1631,12 +1646,12 @@ exit $exit_code
1631
1646
  var HISTORY_SCRIPT = `#!/bin/bash
1632
1647
  # Show rich session detail (history command's per-session view) in a popup.
1633
1648
  ${SESSION_RESOLVE}
1634
- sisyphus admin history "$session_id" 2>&1 | less -R
1649
+ sis admin history "$session_id" 2>&1 | less -R
1635
1650
  `;
1636
1651
  var RECONNECT_SCRIPT = `#!/bin/bash
1637
1652
  # Reconnect daemon to an orphaned tmux session for the current cwd.
1638
1653
  ${SESSION_RESOLVE}
1639
- sisyphus session reconnect "$session_id"
1654
+ sis session reconnect "$session_id"
1640
1655
  exit_code=$?
1641
1656
  read -n 1 -s -r -p "Press a key to close."
1642
1657
  exit $exit_code
@@ -1644,7 +1659,7 @@ exit $exit_code
1644
1659
  var OPEN_SCRATCH_SCRIPT = `#!/bin/bash
1645
1660
  # Open a standalone Claude scratch window in the home tmux session for this cwd.
1646
1661
  # scratch resolves the home session itself via @sisyphus_cwd; no session_id needed.
1647
- exec sisyphus admin scratch
1662
+ exec sis admin scratch
1648
1663
  `;
1649
1664
  function installScript(name, content) {
1650
1665
  mkdirSync(join2(globalDir(), "bin"), { recursive: true });
@@ -1724,7 +1739,7 @@ function isSisyphusBinding(binding) {
1724
1739
  async function confirmConfAppend(userConf, line) {
1725
1740
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
1726
1741
  const rl = createInterface({ input: process.stdin, output: process.stdout });
1727
- return new Promise((resolve11) => {
1742
+ return new Promise((resolve12) => {
1728
1743
  const question = `
1729
1744
  Sisyphus needs to append one line to ${userConf} so its tmux keybindings persist:
1730
1745
  ${line}
@@ -1732,7 +1747,7 @@ Sisyphus needs to append one line to ${userConf} so its tmux keybindings persist
1732
1747
  Append it now? (y/N) `;
1733
1748
  rl.question(question, (answer) => {
1734
1749
  rl.close();
1735
- resolve11(answer.trim().toLowerCase() === "y");
1750
+ resolve12(answer.trim().toLowerCase() === "y");
1736
1751
  });
1737
1752
  });
1738
1753
  }
@@ -1754,7 +1769,7 @@ async function setupTmuxKeybind(cycleKey = DEFAULT_CYCLE_KEY, prefixKey = DEFAUL
1754
1769
  if (existing !== null && !isSisyphusBinding(existing)) {
1755
1770
  return {
1756
1771
  status: "conflict",
1757
- message: `Tmux key ${key} (${label}) is already bound to something else. Run "sisyphus admin setup-keybind <key>" to use a different key.`,
1772
+ message: `Tmux key ${key} (${label}) is already bound to something else. Run "sis admin setup-keybind <key>" to use a different key.`,
1758
1773
  existingBinding: existing
1759
1774
  };
1760
1775
  }
@@ -2145,20 +2160,20 @@ function printGettingStarted(keybindResult, sisyphusPlugin) {
2145
2160
  if (sisyphusPlugin.installed && sisyphusPlugin.autoInstalled) {
2146
2161
  lines.push(`Sisyphus plugin installed: sisyphus@sisyphus \u2192 ${sisyphusPlugin.installPath}`, "");
2147
2162
  } else if (!sisyphusPlugin.installed) {
2148
- lines.push("Sisyphus plugin: failed to install (run `sisyphus admin setup` to retry; needs `claude` CLI)", "");
2163
+ lines.push("Sisyphus plugin: failed to install (run `sis admin setup` to retry; needs `claude` CLI)", "");
2149
2164
  }
2150
2165
  lines.push(
2151
- "Run `sisyphus admin getting-started` for a complete usage guide.",
2166
+ "Run `sis admin getting-started` for a complete usage guide.",
2152
2167
  ""
2153
2168
  );
2154
2169
  console.log(lines.join("\n"));
2155
2170
  }
2156
2171
  function testConnection() {
2157
- return new Promise((resolve11, reject) => {
2172
+ return new Promise((resolve12, reject) => {
2158
2173
  const sock = connect2(socketPath());
2159
2174
  sock.on("connect", () => {
2160
2175
  sock.destroy();
2161
- resolve11();
2176
+ resolve12();
2162
2177
  });
2163
2178
  sock.on("error", (err) => {
2164
2179
  sock.destroy();
@@ -2167,7 +2182,7 @@ function testConnection() {
2167
2182
  });
2168
2183
  }
2169
2184
  function sleep(ms) {
2170
- return new Promise((resolve11) => setTimeout(resolve11, ms));
2185
+ return new Promise((resolve12) => setTimeout(resolve12, ms));
2171
2186
  }
2172
2187
  async function waitForDaemon(maxWaitMs = 6e3) {
2173
2188
  const start = Date.now();
@@ -2203,7 +2218,7 @@ function rawSend2(request, timeoutMs = 1e4) {
2203
2218
  return rawSend(request, timeoutMs);
2204
2219
  }
2205
2220
  async function sendRequest(request, timeoutMs) {
2206
- const sleep2 = (ms) => new Promise((resolve11) => setTimeout(resolve11, ms));
2221
+ const sleep2 = (ms) => new Promise((resolve12) => setTimeout(resolve12, ms));
2207
2222
  const MAX_ATTEMPTS = 5;
2208
2223
  const RETRY_DELAY_MS = 2e3;
2209
2224
  let installedDaemon = false;
@@ -2257,7 +2272,7 @@ async function sendRequest(request, timeoutMs) {
2257
2272
  }
2258
2273
  lines.push(
2259
2274
  "",
2260
- " Diagnose: sisyphus admin doctor",
2275
+ " Diagnose: sis admin doctor",
2261
2276
  " Logs: tail -f ~/.sisyphus/daemon.log"
2262
2277
  );
2263
2278
  throw new Error(lines.join("\n"));
@@ -2291,6 +2306,9 @@ function getTmuxSessionInfo() {
2291
2306
  function shellQuote(s) {
2292
2307
  return `'${s.replace(/'/g, "'\\''")}'`;
2293
2308
  }
2309
+ function validateRepoName(repo) {
2310
+ return !repo.includes("/") && !repo.includes("\\") && !repo.includes("..");
2311
+ }
2294
2312
  function escapeAppleScript(s) {
2295
2313
  return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
2296
2314
  }
@@ -2392,7 +2410,7 @@ function registerStart(program2) {
2392
2410
  console.log(`Tmux session: ${tmuxSessionName}`);
2393
2411
  console.log(` tmux attach -t ${tmuxSessionName}`);
2394
2412
  }
2395
- console.log(`Monitor: sisyphus status ${sessionId}`);
2413
+ console.log(`Monitor: sis status ${sessionId}`);
2396
2414
  return;
2397
2415
  }
2398
2416
  let tmuxSession;
@@ -2436,7 +2454,7 @@ function registerStart(program2) {
2436
2454
  if (!process.env["TMUX"]) {
2437
2455
  attachToTmuxSession(tmuxSession);
2438
2456
  }
2439
- console.log(`Monitor: sisyphus status ${sessionId}`);
2457
+ console.log(`Monitor: sis status ${sessionId}`);
2440
2458
  });
2441
2459
  }
2442
2460
 
@@ -2759,7 +2777,7 @@ function registerList(program2) {
2759
2777
  if (sessions.length === 0) {
2760
2778
  if (filtered && totalCount && totalCount > 0) {
2761
2779
  console.log(`No sessions in this project. ${totalCount} session(s) in other projects.`);
2762
- console.log(`${DIM2}Run ${RESET2}sisyphus list --all${DIM2} to show all.${RESET2}`);
2780
+ console.log(`${DIM2}Run ${RESET2}sis list --all${DIM2} to show all.${RESET2}`);
2763
2781
  } else {
2764
2782
  console.log("No sessions");
2765
2783
  }
@@ -2777,7 +2795,7 @@ function registerList(program2) {
2777
2795
  if (filtered && totalCount && totalCount > sessions.length) {
2778
2796
  const otherCount = totalCount - sessions.length;
2779
2797
  console.log(`
2780
- ${DIM2}${otherCount} more session(s) in other projects. Run ${RESET2}sisyphus list --all${DIM2} to show all.${RESET2}`);
2798
+ ${DIM2}${otherCount} more session(s) in other projects. Run ${RESET2}sis list --all${DIM2} to show all.${RESET2}`);
2781
2799
  }
2782
2800
  } else {
2783
2801
  console.error(`Error: ${response.error}`);
@@ -3507,7 +3525,7 @@ Posts a deck of questions to the user's dashboard inbox. They walk through it an
3507
3525
 
3508
3526
  The CLI always blocks until the user answers (which can take 10+ minutes).
3509
3527
 
3510
- - **Orchestrator:** invoke synchronously so the orchestrator's pane stays alive while the bash blocks. Daemon refuses \`sisyphus orch yield\` while orchestrator owns a pending deck; foreground is the supported pattern.
3528
+ - **Orchestrator:** invoke synchronously so the orchestrator's pane stays alive while the bash blocks. Daemon refuses \`sis orch yield\` while orchestrator owns a pending deck; foreground is the supported pattern.
3511
3529
  - **Agents / one-off Claude Code sessions:** invoke through the Bash tool with \`run_in_background: true\` and end your turn \u2014 the bash completion notification wakes you with stdout ready to parse.
3512
3530
 
3513
3531
  For guidance on when to use a deck, how to design options the user can actually choose between, and how to bundle related questions into one deck, read the \`humanloop\` skill before authoring.
@@ -3817,7 +3835,7 @@ function registerComplete(program2) {
3817
3835
  console.log("Session completed.");
3818
3836
  console.log(`
3819
3837
  To keep working in this session:`);
3820
- console.log(` sisyphus session continue # reactivate session and clear roadmap for new work`);
3838
+ console.log(` sis session continue # reactivate session and clear roadmap for new work`);
3821
3839
  } else {
3822
3840
  console.error(`Error: ${response.error}`);
3823
3841
  process.exit(1);
@@ -3839,7 +3857,7 @@ function registerRollback(program2) {
3839
3857
  if (response.ok) {
3840
3858
  const data = response.data;
3841
3859
  console.log(`Session ${sessionId} rolled back to cycle ${data.restoredToCycle}.`);
3842
- console.log(`Session is now paused. Use 'sisyphus session resume ${sessionId}' to respawn the orchestrator.`);
3860
+ console.log(`Session is now paused. Use 'sis session resume ${sessionId}' to respawn the orchestrator.`);
3843
3861
  } else {
3844
3862
  console.error(`Error: ${response.error}`);
3845
3863
  process.exit(1);
@@ -3875,7 +3893,7 @@ function registerClone(program2) {
3875
3893
  }
3876
3894
  const agentId = process.env.SISYPHUS_AGENT_ID;
3877
3895
  if (agentId !== "orchestrator") {
3878
- console.error("Error: clone can only be called by the orchestrator. Use sisyphus message to ask the orchestrator to clone.");
3896
+ console.error("Error: clone can only be called by the orchestrator. Use sis message to ask the orchestrator to clone.");
3879
3897
  process.exit(1);
3880
3898
  }
3881
3899
  const request = {
@@ -4115,12 +4133,12 @@ import { join as join14, resolve as resolve5 } from "path";
4115
4133
  // src/cli/stdin.ts
4116
4134
  function readStdin2() {
4117
4135
  if (process.stdin.isTTY) return Promise.resolve(null);
4118
- return new Promise((resolve11, reject) => {
4136
+ return new Promise((resolve12, reject) => {
4119
4137
  const chunks = [];
4120
4138
  process.stdin.on("data", (chunk) => chunks.push(chunk));
4121
4139
  process.stdin.on("end", () => {
4122
4140
  const text = Buffer.concat(chunks).toString("utf-8").trim();
4123
- resolve11(text || null);
4141
+ resolve12(text || null);
4124
4142
  });
4125
4143
  process.stdin.on("error", reject);
4126
4144
  });
@@ -4276,11 +4294,11 @@ function registerSpawn(program2) {
4276
4294
  if (response.ok) {
4277
4295
  const agentId = response.data?.agentId;
4278
4296
  console.log(`Agent spawned: ${agentId}`);
4279
- console.log(`Tip: \`sisyphus agent await ${agentId}\` blocks for the report and consumes it inline (won't appear in next cycle).`);
4280
- console.log("Run `sisyphus orch yield` when done spawning agents.");
4297
+ console.log(`Tip: \`sis agent await ${agentId}\` blocks for the report and consumes it inline (won't appear in next cycle).`);
4298
+ console.log("Run `sis orch yield` when done spawning agents.");
4281
4299
  } else {
4282
4300
  console.error(`Error: ${response.error}`);
4283
- if (response.error?.includes("Unknown session")) console.error("Hint: run `sisyphus list` to see active sessions.");
4301
+ if (response.error?.includes("Unknown session")) console.error("Hint: run `sis list` to see active sessions.");
4284
4302
  process.exit(1);
4285
4303
  }
4286
4304
  });
@@ -4897,7 +4915,7 @@ function printResults(result, daemonOk, keybindMsg) {
4897
4915
  }
4898
4916
  }
4899
4917
  console.log("");
4900
- console.log("Run 'sisyphus admin getting-started' for a usage guide.");
4918
+ console.log("Run 'sis admin getting-started' for a usage guide.");
4901
4919
  console.log("");
4902
4920
  }
4903
4921
  function registerSetup(program2) {
@@ -4943,9 +4961,9 @@ function registerSetupKeybind(program2) {
4943
4961
  console.log(` ${result.existingBinding}`);
4944
4962
  console.log("");
4945
4963
  console.log("Use a different key, e.g.:");
4946
- console.log(" sisyphus admin setup-keybind M-S");
4947
- console.log(" sisyphus admin setup-keybind M-w");
4948
- console.log(" sisyphus admin setup-keybind M-j");
4964
+ console.log(" sis admin setup-keybind M-S");
4965
+ console.log(" sis admin setup-keybind M-w");
4966
+ console.log(" sis admin setup-keybind M-j");
4949
4967
  break;
4950
4968
  case "unsupported-tmux":
4951
4969
  console.log(result.message);
@@ -4959,9 +4977,40 @@ function registerSetupKeybind(program2) {
4959
4977
  });
4960
4978
  }
4961
4979
 
4980
+ // src/cli/commands/home-init.ts
4981
+ import { execSync as execSync11 } from "child_process";
4982
+ function registerHomeInit(parent) {
4983
+ parent.command("home-init <name> <cwd>").description("Bootstrap a tmux home session with the sisyphus dashboard.").action((name, cwd) => {
4984
+ ensureSession(name, cwd);
4985
+ setSessionCwd(name, cwd);
4986
+ openDashboardWindow(name, cwd);
4987
+ });
4988
+ }
4989
+ function sessionExists(name) {
4990
+ try {
4991
+ execSync11(`tmux has-session -t ${shellQuote(name)}`, { stdio: "pipe" });
4992
+ return true;
4993
+ } catch {
4994
+ return false;
4995
+ }
4996
+ }
4997
+ function ensureSession(name, cwd) {
4998
+ if (sessionExists(name)) return;
4999
+ execSync11(
5000
+ `tmux new-session -d -s ${shellQuote(name)} -c ${shellQuote(cwd)}`,
5001
+ { stdio: "pipe" }
5002
+ );
5003
+ }
5004
+ function setSessionCwd(name, cwd) {
5005
+ execSync11(
5006
+ `tmux set-option -t ${shellQuote(name)} @sisyphus_cwd ${shellQuote(cwd.replace(/\/+$/, ""))}`,
5007
+ { stdio: "pipe" }
5008
+ );
5009
+ }
5010
+
4962
5011
  // src/cli/commands/doctor.ts
4963
5012
  init_paths();
4964
- import { execSync as execSync11 } from "child_process";
5013
+ import { execSync as execSync12 } from "child_process";
4965
5014
  import { existsSync as existsSync15, statSync as statSync3 } from "fs";
4966
5015
  import { homedir as homedir9 } from "os";
4967
5016
  import { join as join16 } from "path";
@@ -4974,7 +5023,7 @@ function checkNodeVersion() {
4974
5023
  }
4975
5024
  function checkClaudeCli() {
4976
5025
  try {
4977
- execSync11("which claude", { stdio: "pipe" });
5026
+ execSync12("which claude", { stdio: "pipe" });
4978
5027
  return { name: "Claude CLI", status: "ok", detail: "Found on PATH" };
4979
5028
  } catch {
4980
5029
  return {
@@ -4987,7 +5036,7 @@ function checkClaudeCli() {
4987
5036
  }
4988
5037
  function checkGit() {
4989
5038
  try {
4990
- const version = execSync11("git --version", { encoding: "utf-8", stdio: "pipe" }).trim();
5039
+ const version = execSync12("git --version", { encoding: "utf-8", stdio: "pipe" }).trim();
4991
5040
  return { name: "git", status: "ok", detail: version };
4992
5041
  } catch {
4993
5042
  return { name: "git", status: "fail", detail: "Not found on PATH", fix: "Install git: https://git-scm.com/downloads" };
@@ -4995,7 +5044,7 @@ function checkGit() {
4995
5044
  }
4996
5045
  function checkTmuxVersion() {
4997
5046
  try {
4998
- const version = execSync11("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
5047
+ const version = execSync12("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
4999
5048
  const match = version.match(/(\d+\.\d+)/);
5000
5049
  if (!match) return { name: "tmux version", status: "warn", detail: `Could not parse version: ${version}` };
5001
5050
  const ver = parseFloat(match[1]);
@@ -5017,7 +5066,7 @@ function checkDaemonInstalled() {
5017
5066
  name: "Daemon plist",
5018
5067
  status: "fail",
5019
5068
  detail: "Not installed",
5020
- fix: 'Run any sisyphus command to auto-install, or: sisyphus start "test"'
5069
+ fix: 'Run any sis command to auto-install, or: sis start "test"'
5021
5070
  };
5022
5071
  }
5023
5072
  const pid = daemonPidPath();
@@ -5044,7 +5093,7 @@ function checkDaemonRunning() {
5044
5093
  }
5045
5094
  try {
5046
5095
  const sock = socketPath();
5047
- execSync11(`test -S "${sock}"`, { stdio: "pipe" });
5096
+ execSync12(`test -S "${sock}"`, { stdio: "pipe" });
5048
5097
  return { name: "Daemon process", status: "ok", detail: `Socket at ${sock}` };
5049
5098
  } catch {
5050
5099
  return {
@@ -5057,13 +5106,13 @@ function checkDaemonRunning() {
5057
5106
  }
5058
5107
  function checkTmux() {
5059
5108
  try {
5060
- execSync11("which tmux", { stdio: "pipe" });
5109
+ execSync12("which tmux", { stdio: "pipe" });
5061
5110
  } catch {
5062
5111
  const installHint = process.platform === "darwin" ? "brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
5063
5112
  return { name: "tmux", status: "fail", detail: "Not found on PATH", fix: installHint };
5064
5113
  }
5065
5114
  try {
5066
- execSync11("tmux list-sessions", { stdio: "pipe" });
5115
+ execSync12("tmux list-sessions", { stdio: "pipe" });
5067
5116
  return { name: "tmux", status: "ok", detail: "Running" };
5068
5117
  } catch {
5069
5118
  return { name: "tmux", status: "warn", detail: "Installed but no server running" };
@@ -5076,7 +5125,7 @@ function checkCycleScript() {
5076
5125
  name: "Cycle script",
5077
5126
  status: "fail",
5078
5127
  detail: `Not found at ${path}`,
5079
- fix: "sisyphus admin setup-keybind"
5128
+ fix: "sis admin setup-keybind"
5080
5129
  };
5081
5130
  }
5082
5131
  try {
@@ -5107,7 +5156,7 @@ function checkTmuxKeybind() {
5107
5156
  name: `Tmux keybind (${DEFAULT_CYCLE_KEY})`,
5108
5157
  status: "fail",
5109
5158
  detail: "Not bound",
5110
- fix: "sisyphus admin setup-keybind"
5159
+ fix: "sis admin setup-keybind"
5111
5160
  };
5112
5161
  }
5113
5162
  if (isSisyphusBinding(existing)) {
@@ -5117,7 +5166,7 @@ function checkTmuxKeybind() {
5117
5166
  name: `Tmux keybind (${DEFAULT_CYCLE_KEY})`,
5118
5167
  status: "warn",
5119
5168
  detail: `Bound to something else: ${existing}`,
5120
- fix: "sisyphus admin setup-keybind M-S (or another free key)"
5169
+ fix: "sis admin setup-keybind M-S (or another free key)"
5121
5170
  };
5122
5171
  }
5123
5172
  function checkGlobalDir() {
@@ -5168,13 +5217,13 @@ function checkSisyphusPlugin() {
5168
5217
  name: "sisyphus@sisyphus plugin",
5169
5218
  status: "warn",
5170
5219
  detail: "Not installed (slash commands /sisyphus:begin, /sisyphus:autopsy, /sisyphus:configure-upload unavailable)",
5171
- fix: "sisyphus admin setup"
5220
+ fix: "sis admin setup"
5172
5221
  };
5173
5222
  }
5174
5223
  function checkTermrender() {
5175
5224
  if (isTermrenderAvailable()) {
5176
5225
  try {
5177
- const version = execSync11("termrender --version", { encoding: "utf-8", stdio: "pipe" }).trim();
5226
+ const version = execSync12("termrender --version", { encoding: "utf-8", stdio: "pipe" }).trim();
5178
5227
  return { name: "termrender", status: "ok", detail: version };
5179
5228
  } catch {
5180
5229
  return { name: "termrender", status: "ok", detail: "installed" };
@@ -5193,7 +5242,7 @@ function checkNvim() {
5193
5242
  return { name: "nvim", status: "warn", detail: "Not installed", fix };
5194
5243
  }
5195
5244
  try {
5196
- const version = execSync11("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "");
5245
+ const version = execSync12("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "");
5197
5246
  return { name: "nvim", status: "ok", detail: version ?? "installed" };
5198
5247
  } catch {
5199
5248
  return { name: "nvim", status: "ok", detail: "installed" };
@@ -5298,10 +5347,10 @@ function registerInit(program2) {
5298
5347
  import { createInterface as createInterface2 } from "readline";
5299
5348
  async function confirm(question) {
5300
5349
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
5301
- return new Promise((resolve11) => {
5350
+ return new Promise((resolve12) => {
5302
5351
  rl.question(question, (answer) => {
5303
5352
  rl.close();
5304
- resolve11(answer.trim().toLowerCase() === "y");
5353
+ resolve12(answer.trim().toLowerCase() === "y");
5305
5354
  });
5306
5355
  });
5307
5356
  }
@@ -5326,27 +5375,27 @@ import { createInterface as createInterface3 } from "readline";
5326
5375
  import { dirname as dirname6 } from "path";
5327
5376
  async function readUrlFromInput(interactive) {
5328
5377
  if (interactive) {
5329
- return new Promise((resolve11) => {
5378
+ return new Promise((resolve12) => {
5330
5379
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
5331
5380
  rl.question("Paste the upload URL (with embedded ?token=): ", (answer) => {
5332
5381
  rl.close();
5333
- resolve11(answer.trim());
5382
+ resolve12(answer.trim());
5334
5383
  });
5335
5384
  });
5336
5385
  }
5337
- return new Promise((resolve11) => {
5386
+ return new Promise((resolve12) => {
5338
5387
  const chunks = [];
5339
5388
  process.stdin.setEncoding("utf-8");
5340
5389
  process.stdin.on("data", (chunk) => {
5341
5390
  chunks.push(chunk);
5342
5391
  });
5343
5392
  process.stdin.on("end", () => {
5344
- resolve11(chunks.join("").trim());
5393
+ resolve12(chunks.join("").trim());
5345
5394
  });
5346
5395
  });
5347
5396
  }
5348
5397
  function registerConfigureUpload(program2) {
5349
- program2.command("configure-upload").description("Configure the upload proxy from a token-bearing URL (writes ~/.sisyphus/config.json)").argument("[url]", "Worker URL with embedded ?token= query (https://worker/upload?token=sisyphus_pat_...); omit to read from stdin").option("--stdin", "Read URL from stdin (pipe-friendly: pbpaste | sisyphus admin configure-upload --stdin)").action(async (urlArg, opts) => {
5398
+ program2.command("configure-upload").description("Configure the upload proxy from a token-bearing URL (writes ~/.sisyphus/config.json)").argument("[url]", "Worker URL with embedded ?token= query (https://worker/upload?token=sisyphus_pat_...); omit to read from stdin").option("--stdin", "Read URL from stdin (pipe-friendly: pbpaste | sis admin configure-upload --stdin)").action(async (urlArg, opts) => {
5350
5399
  let rawUrl;
5351
5400
  const fromStdin = opts.stdin || urlArg === "-" || !urlArg && process.stdin.isTTY === false;
5352
5401
  const fromInteractive = !urlArg && !opts.stdin && process.stdin.isTTY === true;
@@ -5355,7 +5404,7 @@ function registerConfigureUpload(program2) {
5355
5404
  } else {
5356
5405
  rawUrl = urlArg;
5357
5406
  console.warn(
5358
- "warning: passing the token on argv exposes it via `ps` and shell history; pipe it on stdin instead: `pbpaste | sisyphus admin configure-upload --stdin`"
5407
+ "warning: passing the token on argv exposes it via `ps` and shell history; pipe it on stdin instead: `pbpaste | sis admin configure-upload --stdin`"
5359
5408
  );
5360
5409
  }
5361
5410
  let parsed;
@@ -5396,7 +5445,7 @@ function registerConfigureUpload(program2) {
5396
5445
  }
5397
5446
 
5398
5447
  // src/cli/commands/getting-started.ts
5399
- import { execSync as execSync12 } from "child_process";
5448
+ import { execSync as execSync13 } from "child_process";
5400
5449
  import { dirname as dirname7, join as join18 } from "path";
5401
5450
  import { fileURLToPath as fileURLToPath3 } from "url";
5402
5451
  function templatePath(name) {
@@ -5408,7 +5457,7 @@ function isClaudeCode() {
5408
5457
  function printNonClaudeMessage() {
5409
5458
  console.log(`
5410
5459
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
5411
- \u2551 sisyphus admin getting-started \u2014 Interactive Tutorial \u2551
5460
+ \u2551 sis admin getting-started \u2014 Interactive Tutorial \u2551
5412
5461
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
5413
5462
 
5414
5463
  This command provides an interactive tutorial best experienced
@@ -5416,11 +5465,11 @@ function printNonClaudeMessage() {
5416
5465
 
5417
5466
  To start:
5418
5467
  1. Open Claude Code: claude
5419
- 2. Run: sisyphus admin getting-started
5468
+ 2. Run: sis admin getting-started
5420
5469
 
5421
5470
  If you just want the quick reference, run:
5422
- sisyphus --help
5423
- sisyphus admin doctor
5471
+ sis --help
5472
+ sis admin doctor
5424
5473
  `);
5425
5474
  }
5426
5475
  function printStep0() {
@@ -5450,14 +5499,14 @@ This tutorial has 6 steps. Share this overview so the user knows what's coming a
5450
5499
 
5451
5500
  | Step | Topic | Command |
5452
5501
  |------|-------|---------|
5453
- | 0 | Entry & tmux gate (you are here) | \`sisyphus admin getting-started\` |
5502
+ | 0 | Entry & tmux gate (you are here) | \`sis admin getting-started\` |
5454
5503
  | 1 | Tmux basics \u2014 sessions, panes, navigation | \`--tutorial 1\` |
5455
5504
  | 2 | Nvim basics \u2014 open, save, quit (optional) | \`--tutorial 2\` |
5456
5505
  | 3 | Sisyphus concepts \u2014 session model & keybinds | \`--tutorial 3\` |
5457
5506
  | 4 | Live demo \u2014 launch and observe a real session | \`--tutorial 4\` |
5458
5507
  | 5 | What's next \u2014 real usage guidance & suggestions | \`--tutorial 5\` |
5459
5508
 
5460
- Tell the user they can skip to any step with \`sisyphus admin getting-started --tutorial <N>\`.
5509
+ Tell the user they can skip to any step with \`sis admin getting-started --tutorial <N>\`.
5461
5510
 
5462
5511
  ## Instructions for Claude
5463
5512
 
@@ -5467,8 +5516,8 @@ You are guiding a user through the Sisyphus interactive tutorial.
5467
5516
 
5468
5517
  Ask the user if they'd like the interactive walkthrough. If they decline, give this quick summary and stop:
5469
5518
 
5470
- > Sisyphus is a multi-agent orchestrator for Claude Code. Start a session with \`sisyphus start "task"\`,
5471
- > monitor with \`sisyphus dashboard\`, and check health with \`sisyphus admin doctor\`.
5519
+ > Sisyphus is a multi-agent orchestrator for Claude Code. Start a session with \`sis start "task"\`,
5520
+ > monitor with \`sis dashboard\`, and check health with \`sis admin doctor\`.
5472
5521
 
5473
5522
  ### If they want the tutorial:
5474
5523
 
@@ -5490,17 +5539,17 @@ Ask the user if they'd like the interactive walkthrough. If they decline, give t
5490
5539
  - Tell them to install tmux:
5491
5540
  - macOS: \`brew install tmux\`
5492
5541
  - Linux: \`apt install tmux\` or their package manager
5493
- - After install, re-run: \`sisyphus admin getting-started --tutorial 0\` to verify
5542
+ - After install, re-run: \`sis admin getting-started --tutorial 0\` to verify
5494
5543
 
5495
5544
  **Case 2: tmux installed but NOT in a tmux session (inTmux: false)**
5496
5545
  - Tell the user they need to be inside a tmux session for the tutorial
5497
5546
  - Have them run: \`tmux new-session\`
5498
5547
  - Then resume the conversation with Claude in the new tmux session: \`claude\`
5499
- - Then re-run: \`sisyphus admin getting-started --tutorial 0\` to verify
5548
+ - Then re-run: \`sis admin getting-started --tutorial 0\` to verify
5500
5549
 
5501
5550
  **Case 3: In tmux (inTmux: true)**
5502
5551
  - Tell the user they're all set \u2014 tmux is running
5503
- - Proceed by running: \`sisyphus admin getting-started --tutorial 1\`
5552
+ - Proceed by running: \`sis admin getting-started --tutorial 1\`
5504
5553
  </claude-instructions>
5505
5554
  `);
5506
5555
  }
@@ -5578,7 +5627,7 @@ Ask the user to confirm: "Can you navigate between panes with Ctrl+h and Ctrl+l?
5578
5627
 
5579
5628
  Once confirmed, proceed:
5580
5629
  \`\`\`
5581
- sisyphus admin getting-started --tutorial 2
5630
+ sis admin getting-started --tutorial 2
5582
5631
  \`\`\`
5583
5632
  </claude-instructions>
5584
5633
  `);
@@ -5620,7 +5669,7 @@ Ask if they were able to edit and save the file (or if they skipped).
5620
5669
 
5621
5670
  Proceed:
5622
5671
  \`\`\`
5623
- sisyphus admin getting-started --tutorial 3
5672
+ sis admin getting-started --tutorial 3
5624
5673
  \`\`\`
5625
5674
  </claude-instructions>
5626
5675
  `);
@@ -5681,7 +5730,7 @@ function printStep3() {
5681
5730
  > typing special characters (accents, symbols). The right Option key
5682
5731
  > becomes your "Meta" key for tmux/sisyphus keybinds.
5683
5732
 
5684
- After they change it, have them verify by re-running \`sisyphus admin doctor\` \u2014 look for "Right Option Key: Esc+".
5733
+ After they change it, have them verify by re-running \`sis admin doctor\` \u2014 look for "Right Option Key: Esc+".
5685
5734
 
5686
5735
  - **rightOptionKeyStatus: not-iterm** \u2014 They're not using iTerm2. Explain:
5687
5736
  > Sisyphus keybinds use Option as Meta. In iTerm2 this is configured via
@@ -5727,12 +5776,12 @@ Two keybinds to remember (both use the RIGHT Option key):
5727
5776
 
5728
5777
  ### 4. Verify keybinds are installed
5729
5778
 
5730
- Run \`sisyphus admin doctor\` and check the output. Look for:
5779
+ Run \`sis admin doctor\` and check the output. Look for:
5731
5780
  - "Cycle script" \u2014 should be \u2713
5732
5781
  - "Tmux keybind" \u2014 should be \u2713
5733
5782
  - "Right Option Key" \u2014 should be "Esc+"
5734
5783
 
5735
- If cycle script or keybind is missing, run: \`sisyphus admin setup-keybind\`
5784
+ If cycle script or keybind is missing, run: \`sis admin setup-keybind\`
5736
5785
 
5737
5786
  ### 5. Test the keybind
5738
5787
 
@@ -5744,12 +5793,12 @@ If they see \`\xDF\` or similar, circle back to the Right Option Key setup above
5744
5793
 
5745
5794
  Confirm:
5746
5795
  - They understand the two-session model (their session vs sisyphus session)
5747
- - \`sisyphus admin doctor\` shows keybinds installed AND Right Option Key: Esc+
5796
+ - \`sis admin doctor\` shows keybinds installed AND Right Option Key: Esc+
5748
5797
  - Right Option + s doesn't produce a special character
5749
5798
 
5750
5799
  Proceed:
5751
5800
  \`\`\`
5752
- sisyphus admin getting-started --tutorial 4
5801
+ sis admin getting-started --tutorial 4
5753
5802
  \`\`\`
5754
5803
  </claude-instructions>
5755
5804
  `);
@@ -5765,12 +5814,12 @@ This is the grand finale \u2014 a live demo session.
5765
5814
 
5766
5815
  ### 1. Health check
5767
5816
 
5768
- Run \`sisyphus admin doctor\` first. If any checks are failing, help the user fix them before proceeding.
5817
+ Run \`sis admin doctor\` first. If any checks are failing, help the user fix them before proceeding.
5769
5818
  All core checks (tmux, daemon, keybinds) should be \u2713.
5770
5819
 
5771
5820
  ### 2. BEFORE launching: Teach navigation
5772
5821
 
5773
- **This is critical.** When \`sisyphus start\` runs, it auto-opens the dashboard in a new tmux window. The user will suddenly be looking at the dashboard and may feel "stuck". Teach them how to navigate BEFORE launching:
5822
+ **This is critical.** When \`sis start\` runs, it auto-opens the dashboard in a new tmux window. The user will suddenly be looking at the dashboard and may feel "stuck". Teach them how to navigate BEFORE launching:
5774
5823
 
5775
5824
  Explain clearly:
5776
5825
 
@@ -5815,7 +5864,7 @@ Tell the user:
5815
5864
 
5816
5865
  Then launch from the demo directory:
5817
5866
  \`\`\`
5818
- cd /tmp/sisyphus-tutorial-demo && sisyphus start "Add three improvements to this todo app: (1) add a priority field (high/medium/low) to todos, (2) add a GET /todos/stats endpoint that returns counts of total/done/pending todos, (3) add tests for the new features. Explain your thinking at each step." -c "TUTORIAL DEMO: A user is watching this session to learn how sisyphus works. Be EXTRA VERBOSE \u2014 explain your reasoning, narrate what you're doing, and make your planning visible. When spawning agents, give each agent context that this is a tutorial demo and they should explain their work clearly. Keep scope small: 2-3 agents, 1-2 cycles."
5867
+ cd /tmp/sisyphus-tutorial-demo && sis start "Add three improvements to this todo app: (1) add a priority field (high/medium/low) to todos, (2) add a GET /todos/stats endpoint that returns counts of total/done/pending todos, (3) add tests for the new features. Explain your thinking at each step." -c "TUTORIAL DEMO: A user is watching this session to learn how sisyphus works. Be EXTRA VERBOSE \u2014 explain your reasoning, narrate what you're doing, and make your planning visible. When spawning agents, give each agent context that this is a tutorial demo and they should explain their work clearly. Keep scope small: 2-3 agents, 1-2 cycles."
5819
5868
  \`\`\`
5820
5869
 
5821
5870
  After launching, tell them:
@@ -5828,7 +5877,7 @@ Wait for them to confirm they're back, then start live commentary.
5828
5877
 
5829
5878
  **This is the most important part of the demo.** Don't just launch and wait \u2014 actively narrate.
5830
5879
 
5831
- Once the user is back, start a polling loop. Every ~45 seconds, run \`sisyphus status --verbose <session-id>\` and provide SHORT, contextual commentary about what's happening. The \`--verbose\` flag shows agent instructions, full roadmap, cycle logs, and live pane output from the orchestrator and running agents \u2014 use this rich data to narrate what's actually happening, not just phase names.
5880
+ Once the user is back, start a polling loop. Every ~45 seconds, run \`sis status --verbose <session-id>\` and provide SHORT, contextual commentary about what's happening. The \`--verbose\` flag shows agent instructions, full roadmap, cycle logs, and live pane output from the orchestrator and running agents \u2014 use this rich data to narrate what's actually happening, not just phase names.
5832
5881
 
5833
5882
  **How to narrate each phase:**
5834
5883
 
@@ -5867,7 +5916,7 @@ Once the session shows "completed":
5867
5916
 
5868
5917
  Tell the user the demo is done. Then run:
5869
5918
  \`\`\`
5870
- sisyphus admin getting-started --tutorial 5
5919
+ sis admin getting-started --tutorial 5
5871
5920
  \`\`\`
5872
5921
  </claude-instructions>
5873
5922
  `);
@@ -5876,11 +5925,11 @@ function printStep5() {
5876
5925
  let recentCommits = "";
5877
5926
  let topLevelFiles = "";
5878
5927
  try {
5879
- recentCommits = execSync12("git log --oneline -15 2>/dev/null", { encoding: "utf-8" }).trim();
5928
+ recentCommits = execSync13("git log --oneline -15 2>/dev/null", { encoding: "utf-8" }).trim();
5880
5929
  } catch {
5881
5930
  }
5882
5931
  try {
5883
- topLevelFiles = execSync12("ls -1 2>/dev/null", { encoding: "utf-8" }).trim();
5932
+ topLevelFiles = execSync13("ls -1 2>/dev/null", { encoding: "utf-8" }).trim();
5884
5933
  } catch {
5885
5934
  }
5886
5935
  console.log(`
@@ -5943,7 +5992,7 @@ This is the most important part. Explain clearly:
5943
5992
  The easiest way is the \`/sisyphus:begin\` slash command inside Claude Code. Just tell Claude
5944
5993
  what you want to build and it'll hand it off to sisyphus with the right context.
5945
5994
 
5946
- Or directly: \`sisyphus start "your task" -c "any background context"\`
5995
+ Or directly: \`sis start "your task" -c "any background context"\`
5947
5996
 
5948
5997
  ### 4. Suggest real tasks for THEIR codebase
5949
5998
 
@@ -5965,7 +6014,7 @@ Tell them:
5965
6014
  > to understand the philosophy, or you want a deeper rundown on the dashboard,
5966
6015
  > monitoring, configuration, or how to steer sessions \u2014 just ask and I'll explain.
5967
6016
 
5968
- If the user says yes or asks to learn more, run \`sisyphus admin getting-started --explain\`
6017
+ If the user says yes or asks to learn more, run \`sis admin getting-started --explain\`
5969
6018
  and use its output to explain the system to them conversationally. Don't dump the whole
5970
6019
  thing \u2014 answer what they're curious about, using the reference as your source material.
5971
6020
  </claude-instructions>
@@ -5981,17 +6030,17 @@ function buildCommandTable(program2) {
5981
6030
  const fmtArgs = (c2) => c2.registeredArguments.map((a) => a.required ? `<${a.name()}>` : `[${a.name()}]`).join(" ");
5982
6031
  if (subs.length === 0) {
5983
6032
  const args2 = fmtArgs(cmd);
5984
- const usage = args2 ? `sisyphus ${cmd.name()} ${args2}` : `sisyphus ${cmd.name()}`;
6033
+ const usage = args2 ? `sis ${cmd.name()} ${args2}` : `sis ${cmd.name()}`;
5985
6034
  lines.push(`| \`${usage}\` | ${cmd.description()} |`);
5986
6035
  } else {
5987
6036
  if (hasOwnAction) {
5988
6037
  const args2 = fmtArgs(cmd);
5989
- const usage = args2 ? `sisyphus ${cmd.name()} ${args2}` : `sisyphus ${cmd.name()}`;
6038
+ const usage = args2 ? `sis ${cmd.name()} ${args2}` : `sis ${cmd.name()}`;
5990
6039
  lines.push(`| \`${usage}\` | ${cmd.description()} |`);
5991
6040
  }
5992
6041
  for (const sub of subs) {
5993
6042
  const args2 = fmtArgs(sub);
5994
- const usage = args2 ? `sisyphus ${cmd.name()} ${sub.name()} ${args2}` : `sisyphus ${cmd.name()} ${sub.name()}`;
6043
+ const usage = args2 ? `sis ${cmd.name()} ${sub.name()} ${args2}` : `sis ${cmd.name()} ${sub.name()}`;
5995
6044
  lines.push(`| \`${usage}\` | ${sub.description()} |`);
5996
6045
  }
5997
6046
  }
@@ -6141,7 +6190,7 @@ code that looks right and code that works.
6141
6190
  \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
6142
6191
  \u2502 SESSION LIFECYCLE \u2502
6143
6192
  \u2502 \u2502
6144
- \u2502 sisyphus start "task" \u2502
6193
+ \u2502 sis start "task" \u2502
6145
6194
  \u2502 \u2502 \u2502
6146
6195
  \u2502 \u25BC \u2502
6147
6196
  \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 spawn agents \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
@@ -6149,7 +6198,7 @@ code that looks right and code that works.
6149
6198
  \u2502 \u2502 plans \u2502 then yields \u2502 in parallel \u2502 \u2502
6150
6199
  \u2502 \u2514\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
6151
6200
  \u2502 \u2502 \u2502 each calls \u2502
6152
- \u2502 \u2502 orchestrator \u2502 sisyphus agent submit \u2502
6201
+ \u2502 \u2502 orchestrator \u2502 sis agent submit \u2502
6153
6202
  \u2502 \u2502 is KILLED \u2502 when done \u2502
6154
6203
  \u2502 \u2502 \u25BC \u2502
6155
6204
  \u2502 \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
@@ -6184,7 +6233,7 @@ This means it never runs out of context, no matter how many cycles a session tak
6184
6233
 
6185
6234
  ## The Dashboard
6186
6235
 
6187
- The dashboard is a real-time TUI that shows session state. Launch with \`sisyphus dashboard\`
6236
+ The dashboard is a real-time TUI that shows session state. Launch with \`sis dashboard\`
6188
6237
  or it auto-opens when a session starts.
6189
6238
 
6190
6239
  **Dashboard sections:**
@@ -6223,14 +6272,14 @@ Sisyphus sessions should be actively monitored. Here's what to watch for:
6223
6272
 
6224
6273
  **When to intervene:**
6225
6274
  - Use \`m\` in the dashboard to message the orchestrator with corrections
6226
- - Use \`sisyphus session kill <id>\` to stop a runaway session
6227
- - Use \`sisyphus session resume <id> "new instructions"\` to restart with different direction
6275
+ - Use \`sis session kill <id>\` to stop a runaway session
6276
+ - Use \`sis session resume <id> "new instructions"\` to restart with different direction
6228
6277
 
6229
6278
  **Useful monitoring commands:**
6230
6279
  \`\`\`
6231
- sisyphus status <id> # Quick status check
6232
- sisyphus status --verbose <id> # Full detail: roadmap, pane output, agent instructions
6233
- sisyphus dashboard # Interactive TUI
6280
+ sis status <id> # Quick status check
6281
+ sis status --verbose <id> # Full detail: roadmap, pane output, agent instructions
6282
+ sis dashboard # Interactive TUI
6234
6283
  tail -f ~/.sisyphus/daemon.log # Daemon activity log
6235
6284
  \`\`\`
6236
6285
 
@@ -6296,11 +6345,11 @@ Then describe your task. Claude will hand it off with the right context.
6296
6345
 
6297
6346
  **Direct CLI:**
6298
6347
  \`\`\`
6299
- sisyphus start "task description" -c "background context"
6300
- sisyphus start "Implement @requirements.md" -n my-feature
6348
+ sis start "task description" -c "background context"
6349
+ sis start "Implement @requirements.md" -n my-feature
6301
6350
  \`\`\`
6302
6351
 
6303
- **Reference files with @**: \`sisyphus start "Build @docs/spec.md"\` \u2014 the orchestrator
6352
+ **Reference files with @**: \`sis start "Build @docs/spec.md"\` \u2014 the orchestrator
6304
6353
  will read the referenced file as part of its planning.
6305
6354
 
6306
6355
  **The -c flag** adds background context the orchestrator sees but doesn't act on directly.
@@ -6322,10 +6371,10 @@ sisyphusd restart
6322
6371
  **Keybinds not working (special characters appear):**
6323
6372
  iTerm2 \u2192 Settings \u2192 Profiles \u2192 Keys \u2192 Right Option Key \u2192 Esc+
6324
6373
 
6325
- **Agents stuck:** Check \`sisyphus status --verbose <id>\` to see pane output. If an
6374
+ **Agents stuck:** Check \`sis status --verbose <id>\` to see pane output. If an
6326
6375
  agent is waiting for input, kill the session and restart with clearer instructions.
6327
6376
 
6328
- **Dashboard not opening:** Run \`sisyphus dashboard\` manually. Must be inside tmux.
6377
+ **Dashboard not opening:** Run \`sis dashboard\` manually. Must be inside tmux.
6329
6378
 
6330
6379
  **Session seems hung:** Check \`tail -20 ~/.sisyphus/daemon.log\` for errors.
6331
6380
  The daemon polls panes every 2s \u2014 if a pane dies unexpectedly, it'll be detected.
@@ -6645,7 +6694,7 @@ function showSession(idOrName, opts) {
6645
6694
  console.log(`${DIM3}Compute:${RESET3} ${formatDuration(computeMs)} ${DIM3}Interactive:${RESET3} ${formatDuration(interactiveMs)} ${DIM3}(TUI wait time, not compute)${RESET3}`);
6646
6695
  }
6647
6696
  if (s.userBlockedMs > 0) {
6648
- console.log(`${DIM3}Waiting on user:${RESET3} ${formatDuration(s.userBlockedMs)} ${DIM3}(blocked on sisyphus ask, not compute)${RESET3}`);
6697
+ console.log(`${DIM3}Waiting on user:${RESET3} ${formatDuration(s.userBlockedMs)} ${DIM3}(blocked on sis ask, not compute)${RESET3}`);
6649
6698
  }
6650
6699
  console.log("");
6651
6700
  console.log(`${BOLD3}Task${RESET3}`);
@@ -7047,7 +7096,7 @@ function registerExport(program2) {
7047
7096
  }
7048
7097
  if (!sessionId) {
7049
7098
  console.error("Error: No session ID provided and no active session found.");
7050
- console.error("Usage: sisyphus admin export [session-id]");
7099
+ console.error("Usage: sis admin export [session-id]");
7051
7100
  process.exit(1);
7052
7101
  }
7053
7102
  try {
@@ -7171,13 +7220,13 @@ function registerUpload(program2) {
7171
7220
  }
7172
7221
  if (!sessionId) {
7173
7222
  console.error("Error: No session ID provided and no active session found.");
7174
- console.error("Usage: sisyphus admin upload [session-id]");
7223
+ console.error("Usage: sis admin upload [session-id]");
7175
7224
  process.exit(1);
7176
7225
  }
7177
7226
  const config = loadConfig(cwd);
7178
7227
  if (!isUploadConfigured(config.upload)) {
7179
7228
  console.error(
7180
- "Error: upload not configured. Run 'sisyphus admin configure-upload <url-with-token>' or set { upload: { url, token } } in .sisyphus/config.json."
7229
+ "Error: upload not configured. Run 'sis admin configure-upload <url-with-token>' or set { upload: { url, token } } in .sisyphus/config.json."
7181
7230
  );
7182
7231
  process.exit(1);
7183
7232
  }
@@ -7236,12 +7285,12 @@ function registerUpload(program2) {
7236
7285
  }
7237
7286
 
7238
7287
  // src/cli/commands/scratch.ts
7239
- import { execSync as execSync13 } from "child_process";
7288
+ import { execSync as execSync14 } from "child_process";
7240
7289
  function findHomeSession(cwd) {
7241
7290
  const normalizedCwd = cwd.replace(/\/+$/, "");
7242
7291
  let output;
7243
7292
  try {
7244
- output = execSync13('tmux list-sessions -F "#{session_id}|#{session_name}"', {
7293
+ output = execSync14('tmux list-sessions -F "#{session_id}|#{session_name}"', {
7245
7294
  encoding: "utf-8",
7246
7295
  stdio: ["pipe", "pipe", "pipe"]
7247
7296
  }).trim();
@@ -7255,7 +7304,7 @@ function findHomeSession(cwd) {
7255
7304
  const name = line.slice(pipeIdx + 1);
7256
7305
  if (name.startsWith("ssyph_")) continue;
7257
7306
  try {
7258
- const val = execSync13(
7307
+ const val = execSync14(
7259
7308
  `tmux show-options -t ${shellQuote(sessId)} -v @sisyphus_cwd`,
7260
7309
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7261
7310
  ).trim();
@@ -7271,7 +7320,7 @@ function registerScratch(program2) {
7271
7320
  const cwd = opts.cwd ?? process.env["SISYPHUS_CWD"] ?? process.cwd();
7272
7321
  const homeSession = findHomeSession(cwd);
7273
7322
  if (!homeSession) {
7274
- const current = execSync13('tmux display-message -p "#{session_name}"', {
7323
+ const current = execSync14('tmux display-message -p "#{session_name}"', {
7275
7324
  encoding: "utf-8"
7276
7325
  }).trim();
7277
7326
  openScratchWindow(current, cwd, promptParts.join(" "));
@@ -7281,7 +7330,7 @@ function registerScratch(program2) {
7281
7330
  });
7282
7331
  }
7283
7332
  function openScratchWindow(tmuxSession, cwd, prompt) {
7284
- const windowId = execSync13(
7333
+ const windowId = execSync14(
7285
7334
  `tmux new-window -t ${shellQuote(tmuxSession + ":")} -n "scratch" -c ${shellQuote(cwd)} -P -F "#{window_id}"`,
7286
7335
  { encoding: "utf-8" }
7287
7336
  ).trim();
@@ -7289,7 +7338,7 @@ function openScratchWindow(tmuxSession, cwd, prompt) {
7289
7338
  if (prompt) {
7290
7339
  cmd += ` -p ${shellQuote(prompt)}`;
7291
7340
  }
7292
- execSync13(
7341
+ execSync14(
7293
7342
  `tmux send-keys -t ${shellQuote(windowId)} ${shellQuote(cmd)} Enter`
7294
7343
  );
7295
7344
  console.log(`Scratch session opened in ${tmuxSession}`);
@@ -7331,14 +7380,14 @@ File resolution (first match wins):
7331
7380
  3. Most recent session with a requirements.json
7332
7381
 
7333
7382
  Examples:
7334
- $ sisyphus admin requirements Auto-detect from current session
7335
- $ sisyphus admin requirements path/to/requirements.json Open a specific file
7336
- $ sisyphus admin requirements --session-id abc123 Target a specific session
7337
- $ sisyphus admin requirements --schema Print the JSON schema
7338
- $ sisyphus admin requirements --annotated Print schema with writing guidance
7339
- $ sisyphus admin requirements --export Render requirements.md from JSON
7340
- $ sisyphus admin requirements --export --session-id abc123 Target a specific session
7341
- $ sisyphus admin requirements --export --force Overwrite even if hand-edited
7383
+ $ sis admin requirements Auto-detect from current session
7384
+ $ sis admin requirements path/to/requirements.json Open a specific file
7385
+ $ sis admin requirements --session-id abc123 Target a specific session
7386
+ $ sis admin requirements --schema Print the JSON schema
7387
+ $ sis admin requirements --annotated Print schema with writing guidance
7388
+ $ sis admin requirements --export Render requirements.md from JSON
7389
+ $ sis admin requirements --export --session-id abc123 Target a specific session
7390
+ $ sis admin requirements --export --force Overwrite even if hand-edited
7342
7391
  `).action(async (file, opts) => {
7343
7392
  if (opts.force && !opts.export) {
7344
7393
  console.error("Error: --force requires --export");
@@ -7492,7 +7541,7 @@ var REQUIREMENTS_SCHEMA = {
7492
7541
  var REQUIREMENTS_ANNOTATED = `# requirements.json \u2014 Annotated Writing Guide
7493
7542
  #
7494
7543
  # This is NOT valid JSON \u2014 it's a reference showing every field with
7495
- # inline guidance. Run \`sisyphus admin requirements --schema\` for the raw
7544
+ # inline guidance. Run \`sis admin requirements --schema\` for the raw
7496
7545
  # JSON Schema.
7497
7546
  #
7498
7547
  # Safe assumptions must satisfy the same EARS shape requirements as
@@ -9543,7 +9592,7 @@ async function discoverNodeWithRetry(requestedName, maxRetries = 30, intervalMs
9543
9592
  const node = discoverNode(requestedName);
9544
9593
  if (node) return node;
9545
9594
  if (i < maxRetries - 1) {
9546
- await new Promise((resolve11) => setTimeout(resolve11, intervalMs));
9595
+ await new Promise((resolve12) => setTimeout(resolve12, intervalMs));
9547
9596
  }
9548
9597
  }
9549
9598
  return null;
@@ -9585,14 +9634,14 @@ async function mintTailscaleKey(opts) {
9585
9634
  }
9586
9635
  if (env.oauthClientId && env.oauthClientSecret) {
9587
9636
  if (!env.tag) {
9588
- throw new Error("Tailscale tag is missing from ~/.sisyphus/deploy/tailscale.env. Re-run `sisyphus deploy auth tailscale`.");
9637
+ throw new Error("Tailscale tag is missing from ~/.sisyphus/deploy/tailscale.env. Re-run `sis deploy auth tailscale`.");
9589
9638
  }
9590
9639
  return mintViaOAuth(env.oauthClientId, env.oauthClientSecret, env.tag, opts.hostname);
9591
9640
  }
9592
9641
  if (env.authKey) {
9593
9642
  return env.authKey;
9594
9643
  }
9595
- throw new Error("Tailscale not configured. Run `sisyphus deploy auth tailscale`.");
9644
+ throw new Error("Tailscale not configured. Run `sis deploy auth tailscale`.");
9596
9645
  }
9597
9646
  async function firstRunPrompt() {
9598
9647
  console.log("");
@@ -9621,12 +9670,12 @@ async function firstRunPrompt() {
9621
9670
  }
9622
9671
  throw new Error(`Invalid choice: ${choice}`);
9623
9672
  }
9624
- async function mintViaOAuth(clientId, clientSecret, tag, hostname) {
9673
+ async function mintViaOAuth(clientId, clientSecret, tag, hostname2) {
9625
9674
  const token = await fetchAccessToken(clientId, clientSecret);
9626
9675
  try {
9627
- const removed = await deleteStaleDevicesForHostname(token, hostname);
9676
+ const removed = await deleteStaleDevicesForHostname(token, hostname2);
9628
9677
  if (removed > 0) {
9629
- console.log(`Tailscale: removed ${removed} stale offline node(s) named "${hostname}".`);
9678
+ console.log(`Tailscale: removed ${removed} stale offline node(s) named "${hostname2}".`);
9630
9679
  }
9631
9680
  } catch (err) {
9632
9681
  console.log(`Tailscale: skipped stale-node cleanup (${err.message}). Add 'devices:write' scope to clean up automatically.`);
@@ -9643,7 +9692,7 @@ async function mintViaOAuth(clientId, clientSecret, tag, hostname) {
9643
9692
  }
9644
9693
  },
9645
9694
  expirySeconds: KEY_EXPIRY_SECONDS,
9646
- description: `sisyphus deploy: ${hostname}`
9695
+ description: `sisyphus deploy: ${hostname2}`
9647
9696
  };
9648
9697
  const res = await fetch(`${TS_API}/tailnet/-/keys`, {
9649
9698
  method: "POST",
@@ -9661,7 +9710,7 @@ async function mintViaOAuth(clientId, clientSecret, tag, hostname) {
9661
9710
  if (!data.key) throw new Error("Tailscale API returned no key.");
9662
9711
  return data.key;
9663
9712
  }
9664
- async function deleteStaleDevicesForHostname(token, hostname) {
9713
+ async function deleteStaleDevicesForHostname(token, hostname2) {
9665
9714
  const res = await fetch(`${TS_API}/tailnet/-/devices`, {
9666
9715
  headers: { Authorization: `Bearer ${token}` }
9667
9716
  });
@@ -9672,7 +9721,7 @@ async function deleteStaleDevicesForHostname(token, hostname) {
9672
9721
  const STALE_AFTER_MS = 5 * 60 * 1e3;
9673
9722
  const now = Date.now();
9674
9723
  const stale = data.devices.filter((d) => {
9675
- if (d.hostname !== hostname) return false;
9724
+ if (d.hostname !== hostname2) return false;
9676
9725
  const seen = Date.parse(d.lastSeen);
9677
9726
  if (Number.isNaN(seen)) return false;
9678
9727
  return now - seen > STALE_AFTER_MS;
@@ -9715,7 +9764,7 @@ async function authTailscale() {
9715
9764
  console.log("");
9716
9765
  console.log("Verifying credentials...");
9717
9766
  await fetchAccessToken(env.oauthClientId, env.oauthClientSecret);
9718
- console.log("OAuth client verified \u2014 `sisyphus deploy <provider> up` will mint ephemeral keys.");
9767
+ console.log("OAuth client verified \u2014 `sis deploy <provider> up` will mint ephemeral keys.");
9719
9768
  } else {
9720
9769
  console.log("");
9721
9770
  console.log("Auth key saved. Note: reusable auth keys are less secure than OAuth clients.");
@@ -9835,7 +9884,7 @@ async function deployUp(provider, opts) {
9835
9884
  const outputs = readOutputs(provider);
9836
9885
  if (!outputs) {
9837
9886
  console.log(`
9838
- Applied \u2014 but could not parse outputs. Run \`sisyphus deploy ${provider} status\`.`);
9887
+ Applied \u2014 but could not parse outputs. Run \`sis deploy ${provider} status\`.`);
9839
9888
  return;
9840
9889
  }
9841
9890
  console.log("");
@@ -9868,8 +9917,8 @@ Applied \u2014 but could not parse outputs. Run \`sisyphus deploy ${provider} st
9868
9917
  console.log(" (Local `tailscale` CLI not on PATH \u2014 skipping tailnet discovery.)");
9869
9918
  }
9870
9919
  console.log("");
9871
- console.log(` Tail provisioning: sisyphus deploy ${provider} logs`);
9872
- console.log(` Verify daemon: sisyphus deploy ${provider} ssh -- sisyphus admin doctor`);
9920
+ console.log(` Tail provisioning: sis deploy ${provider} logs`);
9921
+ console.log(` Verify daemon: sis deploy ${provider} ssh -- sis admin doctor`);
9873
9922
  console.log("");
9874
9923
  }
9875
9924
  async function deployDown(provider, opts) {
@@ -9915,7 +9964,7 @@ function deployStatus(provider) {
9915
9964
  }
9916
9965
  const outputs = readOutputs(provider);
9917
9966
  if (!outputs) {
9918
- console.log(`${provider}: state present but outputs unreadable. Try \`sisyphus deploy ${provider} up\` to reconcile.`);
9967
+ console.log(`${provider}: state present but outputs unreadable. Try \`sis deploy ${provider} up\` to reconcile.`);
9919
9968
  return;
9920
9969
  }
9921
9970
  const runtime = readRuntimeState(provider);
@@ -9948,7 +9997,7 @@ function effectiveSshTarget(provider) {
9948
9997
  if (runtime) return `sisyphus@${runtime.tailscaleHostname}`;
9949
9998
  const outputs = readOutputs(provider);
9950
9999
  if (!outputs) {
9951
- throw new Error(`${provider} not provisioned. Run \`sisyphus deploy ${provider} up\`.`);
10000
+ throw new Error(`${provider} not provisioned. Run \`sis deploy ${provider} up\`.`);
9952
10001
  }
9953
10002
  return `sisyphus@${outputs.tailscale_hostname}`;
9954
10003
  }
@@ -9993,8 +10042,8 @@ async function confirmReprovision(provider, yes) {
9993
10042
  console.log("Cloud-init won't re-run on the live box, and the freshly-minted Tailscale key will be wasted.");
9994
10043
  }
9995
10044
  console.log("");
9996
- console.log(`To push a new sisyphus version: sisyphus deploy ${provider} update`);
9997
- console.log(`To rebuild from scratch: sisyphus deploy ${provider} down && sisyphus deploy ${provider} up`);
10045
+ console.log(`To push a new sisyphus version: sis deploy ${provider} update`);
10046
+ console.log(`To rebuild from scratch: sis deploy ${provider} down && sis deploy ${provider} up`);
9998
10047
  console.log("");
9999
10048
  if (yes) return true;
10000
10049
  const confirmed = await confirm2('Type "yes" to proceed anyway:');
@@ -10003,7 +10052,7 @@ async function confirmReprovision(provider, yes) {
10003
10052
  }
10004
10053
 
10005
10054
  // src/cli/commands/deploy.ts
10006
- var PROVIDERS = ["hetzner", "aws"];
10055
+ init_creds();
10007
10056
  function assertArch(raw) {
10008
10057
  if (raw === "arm" || raw === "x86") return raw;
10009
10058
  throw new Error(`Invalid --arch: ${raw}. Must be 'arm' or 'x86'.`);
@@ -10060,6 +10109,374 @@ function registerDeploy(program2) {
10060
10109
  }
10061
10110
  }
10062
10111
 
10112
+ // src/cli/cloud/runner.ts
10113
+ init_paths();
10114
+ import { spawn as spawn4 } from "child_process";
10115
+ import { hostname } from "os";
10116
+ init_creds();
10117
+
10118
+ // src/cli/deploy/ssh-exec.ts
10119
+ import { spawn as spawn3, spawnSync as spawnSync4 } from "child_process";
10120
+ function runOnBox(provider, cmd) {
10121
+ const target = effectiveSshTarget(provider);
10122
+ const result = spawnSync4("ssh", [target, cmd], {
10123
+ encoding: "utf-8",
10124
+ env: EXEC_ENV
10125
+ });
10126
+ if (typeof result.stdout !== "string" || typeof result.stderr !== "string") {
10127
+ throw new Error("Internal: ssh spawn did not capture output as string");
10128
+ }
10129
+ return {
10130
+ stdout: result.stdout,
10131
+ stderr: result.stderr,
10132
+ // status is null when killed by signal — treat as failure.
10133
+ exitCode: result.status === null ? 1 : result.status
10134
+ };
10135
+ }
10136
+ function runOnBoxStreaming(provider, cmd) {
10137
+ const target = effectiveSshTarget(provider);
10138
+ return new Promise((resolve12, reject) => {
10139
+ const child = spawn3("ssh", [target, cmd], {
10140
+ stdio: "inherit",
10141
+ env: EXEC_ENV
10142
+ });
10143
+ child.on("error", reject);
10144
+ child.on("exit", (code) => resolve12(code === null ? 1 : code));
10145
+ });
10146
+ }
10147
+
10148
+ // src/cli/cloud/grove.ts
10149
+ var GROVE_VERSION = "0.2.13";
10150
+ function ensureGroveInstalled(provider) {
10151
+ const probe = runOnBox(provider, "command -v grove >/dev/null 2>&1");
10152
+ if (probe.exitCode === 0) return;
10153
+ process.stderr.write("Installing grove on box...\n");
10154
+ const install = runOnBox(provider, `sudo npm i -g @crouton-kit/grove@${GROVE_VERSION}`);
10155
+ if (install.exitCode !== 0) {
10156
+ throw new Error(`Failed to install grove: ${install.stderr || install.stdout}`);
10157
+ }
10158
+ }
10159
+ function ensureGroveRegistered(provider, repo, instancePath) {
10160
+ const cmd = `grove register --update --name ${shellQuote(repo)} ${shellQuote(instancePath)}`;
10161
+ const result = runOnBox(provider, cmd);
10162
+ if (result.exitCode !== 0) {
10163
+ throw new Error(`Failed to register grove project ${repo}: ${result.stderr || result.stdout}`);
10164
+ }
10165
+ }
10166
+
10167
+ // src/cli/cloud/repo.ts
10168
+ import { spawnSync as spawnSync5 } from "child_process";
10169
+ import { existsSync as existsSync28 } from "fs";
10170
+ import { basename as basename6, join as join26 } from "path";
10171
+ function captureGit(args2) {
10172
+ const result = spawnSync5("git", args2, {
10173
+ encoding: "utf-8",
10174
+ env: EXEC_ENV
10175
+ });
10176
+ if (typeof result.stdout !== "string") {
10177
+ throw new Error("Internal: git spawn did not capture stdout as string");
10178
+ }
10179
+ return { stdout: result.stdout.trim(), ok: result.status === 0 };
10180
+ }
10181
+ function inferRepoName() {
10182
+ const { stdout, ok } = captureGit(["rev-parse", "--show-toplevel"]);
10183
+ if (!ok) {
10184
+ throw new Error("Not inside a git repository. Run from a repo or pass --name.");
10185
+ }
10186
+ if (!stdout) {
10187
+ throw new Error("git rev-parse returned empty toplevel.");
10188
+ }
10189
+ return basename6(stdout);
10190
+ }
10191
+ function getOriginUrl() {
10192
+ const { stdout, ok } = captureGit(["remote", "get-url", "origin"]);
10193
+ if (!ok) return null;
10194
+ return stdout.length > 0 ? stdout : null;
10195
+ }
10196
+ function getRepoToplevel() {
10197
+ const { stdout, ok } = captureGit(["rev-parse", "--show-toplevel"]);
10198
+ if (!ok) {
10199
+ throw new Error("Not inside a git repository.");
10200
+ }
10201
+ return stdout;
10202
+ }
10203
+ var DEFAULT_EXCLUDES = [
10204
+ ".sisyphus/",
10205
+ ".terraform/",
10206
+ "node_modules/",
10207
+ "dist/",
10208
+ ".next/",
10209
+ ".turbo/",
10210
+ "coverage/",
10211
+ "tmp/",
10212
+ ".git/lfs/",
10213
+ ".DS_Store"
10214
+ ];
10215
+ function buildRsyncArgs(localDir, remoteTarget) {
10216
+ const src = localDir.endsWith("/") ? localDir : `${localDir}/`;
10217
+ return [
10218
+ "-avz",
10219
+ "--filter=:- .gitignore",
10220
+ ...DEFAULT_EXCLUDES.map((e) => `--exclude=${e}`),
10221
+ "-e",
10222
+ "ssh",
10223
+ src,
10224
+ remoteTarget
10225
+ ];
10226
+ }
10227
+ function detectPackageManager(toplevel) {
10228
+ if (existsSync28(join26(toplevel, "pnpm-lock.yaml"))) return "pnpm";
10229
+ if (existsSync28(join26(toplevel, "bun.lockb"))) return "bun";
10230
+ if (existsSync28(join26(toplevel, "yarn.lock"))) return "yarn";
10231
+ if (existsSync28(join26(toplevel, "package-lock.json"))) return "npm";
10232
+ return null;
10233
+ }
10234
+ function packageManagerInstallCmd(pm) {
10235
+ switch (pm) {
10236
+ case "pnpm":
10237
+ return "pnpm install";
10238
+ case "bun":
10239
+ return "bun install";
10240
+ case "yarn":
10241
+ return "yarn install";
10242
+ case "npm":
10243
+ return "npm install";
10244
+ default:
10245
+ return null;
10246
+ }
10247
+ }
10248
+
10249
+ // src/cli/cloud/sidecar.ts
10250
+ init_paths();
10251
+ function readSidecar(provider, repo) {
10252
+ const path = boxCloudSidecarPath(repo);
10253
+ const result = runOnBox(provider, `cat ${shellQuote(path)} 2>/dev/null`);
10254
+ if (result.exitCode !== 0 || !result.stdout.trim()) return null;
10255
+ try {
10256
+ const parsed = JSON.parse(result.stdout);
10257
+ return parsed;
10258
+ } catch {
10259
+ return null;
10260
+ }
10261
+ }
10262
+ function writeSidecar(provider, repo, data) {
10263
+ const dir = boxCloudSidecarDir();
10264
+ const path = boxCloudSidecarPath(repo);
10265
+ const json = JSON.stringify(data, null, 2);
10266
+ const cmd = [
10267
+ `mkdir -p ${shellQuote(dir)}`,
10268
+ `cat > ${shellQuote(path)} <<'SISYPHUS_CLOUD_SIDECAR_EOF'`,
10269
+ json,
10270
+ "SISYPHUS_CLOUD_SIDECAR_EOF"
10271
+ ].join("\n");
10272
+ const result = runOnBox(provider, cmd);
10273
+ if (result.exitCode !== 0) {
10274
+ throw new Error(`Failed to write sidecar for ${repo}: ${result.stderr || result.stdout}`);
10275
+ }
10276
+ }
10277
+
10278
+ // src/cli/cloud/runner.ts
10279
+ async function cloudSync(provider, repo, opts) {
10280
+ const target = effectiveSshTarget(provider);
10281
+ const remoteDir = boxRepoPath(repo);
10282
+ const localOrigin = getOriginUrl();
10283
+ ensureGroveInstalled(provider);
10284
+ const existing = readSidecar(provider, repo);
10285
+ if (existing && existing.originUrl && localOrigin && existing.originUrl !== localOrigin) {
10286
+ throw new Error(
10287
+ `Repo "${repo}" on the box is registered to a different origin:
10288
+ box: ${existing.originUrl}
10289
+ local: ${localOrigin}
10290
+ Pass --name <slug> to disambiguate, or --fresh to overwrite.`
10291
+ );
10292
+ }
10293
+ if (opts.fresh) {
10294
+ if (!localOrigin) {
10295
+ throw new Error("--fresh requires an `origin` remote on the local repo.");
10296
+ }
10297
+ if (!opts.yes) {
10298
+ console.log(`This will wipe ~/projects/${repo} on the box and re-clone from ${localOrigin}.`);
10299
+ const confirmed = (await promptLine('Continue? Type "yes": ', false)).toLowerCase() === "yes";
10300
+ if (!confirmed) {
10301
+ console.log("Aborted.");
10302
+ return;
10303
+ }
10304
+ }
10305
+ console.log(`\u2192 wiping ${remoteDir} and cloning ${localOrigin} on box...`);
10306
+ const cloneCmd = [
10307
+ `rm -rf ${shellQuote(remoteDir)}`,
10308
+ `mkdir -p ${shellQuote("~/projects")}`,
10309
+ `git clone ${shellQuote(localOrigin)} ${shellQuote(remoteDir)}`
10310
+ ].join(" && ");
10311
+ const code = await runOnBoxStreaming(provider, cloneCmd);
10312
+ if (code !== 0) throw new Error(`fresh clone failed (exit ${code})`);
10313
+ } else {
10314
+ const mkdir = runOnBox(provider, `mkdir -p ${shellQuote(remoteDir)}`);
10315
+ if (mkdir.exitCode !== 0) {
10316
+ throw new Error(`Failed to mkdir on box: ${mkdir.stderr}`);
10317
+ }
10318
+ const toplevel = getRepoToplevel();
10319
+ const args2 = buildRsyncArgs(toplevel, `${target}:${remoteDir}/`);
10320
+ console.log(`\u2192 rsync ${toplevel}/ \u2192 ${target}:${remoteDir}/`);
10321
+ const code = await runRsync(args2);
10322
+ if (code !== 0) throw new Error(`rsync failed (exit ${code})`);
10323
+ }
10324
+ ensureGroveRegistered(provider, repo, remoteDir);
10325
+ const sidecar = {
10326
+ originUrl: localOrigin,
10327
+ localHostname: hostname(),
10328
+ lastSync: (/* @__PURE__ */ new Date()).toISOString(),
10329
+ packageManager: existing?.packageManager,
10330
+ lastInstall: existing?.lastInstall
10331
+ };
10332
+ writeSidecar(provider, repo, sidecar);
10333
+ console.log(`\u2713 synced ${repo} \u2192 ${target}:${remoteDir}/`);
10334
+ }
10335
+ function runRsync(args2) {
10336
+ return new Promise((resolve12, reject) => {
10337
+ const child = spawn4("rsync", args2, { stdio: "inherit", env: EXEC_ENV });
10338
+ child.on("error", reject);
10339
+ child.on("exit", (code) => resolve12(code === null ? 1 : code));
10340
+ });
10341
+ }
10342
+ async function cloudInstall(provider, repo) {
10343
+ const remoteDir = boxRepoPath(repo);
10344
+ const toplevel = getRepoToplevel();
10345
+ const pm = detectPackageManager(toplevel);
10346
+ const cmd = packageManagerInstallCmd(pm);
10347
+ if (!cmd) {
10348
+ console.log("No lockfile detected \u2014 skipping install.");
10349
+ return;
10350
+ }
10351
+ console.log(`\u2192 ${pm} install in ${remoteDir} on box...`);
10352
+ const remoteCmd = `cd ${shellQuote(remoteDir)} && ${cmd}`;
10353
+ const code = await runOnBoxStreaming(provider, remoteCmd);
10354
+ if (code !== 0) throw new Error(`${pm} install failed (exit ${code})`);
10355
+ const existing = readSidecar(provider, repo);
10356
+ const sidecar = {
10357
+ originUrl: existing && existing.originUrl !== void 0 ? existing.originUrl : getOriginUrl(),
10358
+ localHostname: existing ? existing.localHostname : hostname(),
10359
+ lastSync: existing?.lastSync,
10360
+ lastInstall: (/* @__PURE__ */ new Date()).toISOString(),
10361
+ packageManager: pm
10362
+ };
10363
+ writeSidecar(provider, repo, sidecar);
10364
+ console.log(`\u2713 installed ${repo} (${pm})`);
10365
+ }
10366
+ async function cloudSession(provider, repo) {
10367
+ const remoteDir = boxRepoPath(repo);
10368
+ const cmd = `sis admin home-init ${shellQuote(repo)} ${shellQuote(remoteDir)}`;
10369
+ console.log(`\u2192 initializing tmux home session "${repo}" on box...`);
10370
+ const result = runOnBox(provider, cmd);
10371
+ if (result.exitCode !== 0) {
10372
+ throw new Error(`home-init failed: ${result.stderr || result.stdout}`);
10373
+ }
10374
+ if (result.stdout.trim()) console.log(result.stdout.trim());
10375
+ console.log(`\u2713 session "${repo}" ready on box`);
10376
+ }
10377
+ function cloudAttach(provider, repo) {
10378
+ if (process.env.TMUX) {
10379
+ throw new Error(
10380
+ `Refusing to attach from inside tmux \u2014 would nest the cloud tmux client.
10381
+ Use a fresh terminal, or run from outside tmux:
10382
+ tmux new-window 'ssh -t ${effectiveSshTarget(provider)} tmux attach -t ${repo}'`
10383
+ );
10384
+ }
10385
+ const target = effectiveSshTarget(provider);
10386
+ const child = spawn4("ssh", ["-t", target, `tmux attach-session -t ${shellQuote(repo)}`], {
10387
+ stdio: "inherit",
10388
+ env: EXEC_ENV
10389
+ });
10390
+ child.on("exit", (code) => process.exit(code === null ? 1 : code));
10391
+ }
10392
+ async function cloudStart(provider, repo, opts) {
10393
+ await cloudSync(provider, repo, { fresh: opts.fresh, yes: opts.yes });
10394
+ await cloudInstall(provider, repo);
10395
+ await cloudSession(provider, repo);
10396
+ console.log("");
10397
+ console.log(`Box-side dashboard ready. Attach with:`);
10398
+ console.log(` tmux new-window 'ssh -t ${effectiveSshTarget(provider)} tmux attach -t ${repo}'`);
10399
+ console.log("(or run inside the slash command, which does this for you.)");
10400
+ }
10401
+ function cloudStatus(provider, repo) {
10402
+ const target = effectiveSshTarget(provider);
10403
+ const sidecar = readSidecar(provider, repo);
10404
+ const sessionProbe = runOnBox(provider, `tmux has-session -t ${shellQuote(repo)} 2>/dev/null`);
10405
+ const sessionRunning = sessionProbe.exitCode === 0;
10406
+ console.log(`Cloud status for "${repo}":`);
10407
+ console.log(` Provider: ${provider}`);
10408
+ console.log(` Target: ${target}`);
10409
+ console.log(` Planted: ${sidecar ? "yes" : "no"}`);
10410
+ if (sidecar) {
10411
+ console.log(` Origin: ${sidecar.originUrl ? sidecar.originUrl : "(none)"}`);
10412
+ console.log(` Last sync: ${sidecar.lastSync ? sidecar.lastSync : "(never)"}`);
10413
+ console.log(` Last install: ${sidecar.lastInstall ? sidecar.lastInstall : "(never)"}`);
10414
+ console.log(` Package manager: ${sidecar.packageManager ? sidecar.packageManager : "(none)"}`);
10415
+ }
10416
+ console.log(` Session: ${sessionRunning ? "running" : "absent"}`);
10417
+ if (sessionRunning) {
10418
+ console.log(` Attach: tmux new-window 'ssh -t ${target} tmux attach -t ${repo}'`);
10419
+ }
10420
+ }
10421
+
10422
+ // src/cli/deploy/provider-pick.ts
10423
+ init_creds();
10424
+ function pickProvider(explicit) {
10425
+ if (explicit) {
10426
+ if (!isValidProvider(explicit)) {
10427
+ throw new Error(`Unknown provider "${explicit}". Valid: ${PROVIDERS.join(", ")}.`);
10428
+ }
10429
+ return explicit;
10430
+ }
10431
+ const provisioned = PROVIDERS.filter((p) => isProvisioned(p));
10432
+ if (provisioned.length === 1) return provisioned[0];
10433
+ if (provisioned.length === 0) {
10434
+ throw new Error(
10435
+ "No cloud provider provisioned. Run `sis deploy <hetzner|aws> up` first."
10436
+ );
10437
+ }
10438
+ throw new Error(
10439
+ `Multiple providers provisioned (${provisioned.join(", ")}). Pass --provider <name>.`
10440
+ );
10441
+ }
10442
+
10443
+ // src/cli/commands/cloud.ts
10444
+ function resolve11(raw) {
10445
+ const provider = pickProvider(raw.provider);
10446
+ const repo = raw.name ? raw.name : inferRepoName();
10447
+ if (!validateRepoName(repo)) {
10448
+ throw new Error(`Invalid --name "${repo}": must not contain '/' '\\' or '..'.`);
10449
+ }
10450
+ return { provider, repo };
10451
+ }
10452
+ function registerCloud(program2) {
10453
+ const cloud = program2.command("cloud").description("Per-repo workflow on the shared cloud box (sync, install, dashboard).");
10454
+ cloud.command("sync").description("Rsync this repo to the cloud box; ensures grove is installed and the repo is registered.").option("--fresh", "Wipe the box-side dir and `git clone` from origin instead of rsync.").option("-y, --yes", "Skip the --fresh confirmation prompt.").option("--name <repo>", "Override the repo name (default: basename of git toplevel).").option("--provider <name>", "Cloud provider (default: auto-pick if exactly one is provisioned).").action(async (raw) => {
10455
+ const { provider, repo } = resolve11(raw);
10456
+ await cloudSync(provider, repo, { fresh: raw.fresh === true, yes: raw.yes === true });
10457
+ });
10458
+ cloud.command("install").description("Run the repo's package-manager install on the box.").option("--name <repo>", "Override the repo name.").option("--provider <name>", "Cloud provider.").action(async (raw) => {
10459
+ const { provider, repo } = resolve11(raw);
10460
+ await cloudInstall(provider, repo);
10461
+ });
10462
+ cloud.command("session").description("Create or refresh the box-side tmux home session for this repo.").option("--name <repo>", "Override the repo name.").option("--provider <name>", "Cloud provider.").action(async (raw) => {
10463
+ const { provider, repo } = resolve11(raw);
10464
+ await cloudSession(provider, repo);
10465
+ });
10466
+ cloud.command("attach").description("Attach to the box-side tmux home session for this repo.").option("--name <repo>", "Override the repo name.").option("--provider <name>", "Cloud provider.").action((raw) => {
10467
+ const { provider, repo } = resolve11(raw);
10468
+ cloudAttach(provider, repo);
10469
+ });
10470
+ cloud.command("start").description("Sync, install, and start the dashboard session in one shot. (Stops short of attach.)").option("--fresh", "Wipe the box-side dir and `git clone` from origin instead of rsync.").option("-y, --yes", "Skip the --fresh confirmation prompt.").option("--name <repo>", "Override the repo name.").option("--provider <name>", "Cloud provider.").action(async (raw) => {
10471
+ const { provider, repo } = resolve11(raw);
10472
+ await cloudStart(provider, repo, { fresh: raw.fresh === true, yes: raw.yes === true });
10473
+ });
10474
+ cloud.command("status").description("Print box-side status for this repo (planted, session running, last sync/install).").option("--name <repo>", "Override the repo name.").option("--provider <name>", "Cloud provider.").action((raw) => {
10475
+ const { provider, repo } = resolve11(raw);
10476
+ cloudStatus(provider, repo);
10477
+ });
10478
+ }
10479
+
10063
10480
  // src/cli/commands/notify.ts
10064
10481
  function attachNotify(diagnostic2) {
10065
10482
  const notify = diagnostic2.command("notify").description("Internal notifications (fire-and-forget)");
@@ -10073,11 +10490,11 @@ function attachNotify(diagnostic2) {
10073
10490
  }
10074
10491
 
10075
10492
  // src/cli/commands/tmux-status.ts
10076
- import { execSync as execSync14 } from "child_process";
10493
+ import { execSync as execSync15 } from "child_process";
10077
10494
  function attachTmuxStatus(diagnostic2) {
10078
10495
  diagnostic2.command("tmux-status").description("Output session status dots for tmux status bar").action(() => {
10079
10496
  try {
10080
- const status = execSync14(
10497
+ const status = execSync15(
10081
10498
  "tmux show-option -gv @sisyphus_status",
10082
10499
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
10083
10500
  ).trim();
@@ -10089,8 +10506,8 @@ function attachTmuxStatus(diagnostic2) {
10089
10506
 
10090
10507
  // src/cli/commands/tmux-sessions.ts
10091
10508
  init_paths();
10092
- import { execSync as execSync15 } from "child_process";
10093
- import { readFileSync as readFileSync30, existsSync as existsSync28 } from "fs";
10509
+ import { execSync as execSync16 } from "child_process";
10510
+ import { readFileSync as readFileSync30, existsSync as existsSync29 } from "fs";
10094
10511
  var DOT_MAP = {
10095
10512
  "orchestrator:processing": { icon: "\u25CF", color: "#d4ad6a" },
10096
10513
  "orchestrator:idle": { icon: "\u25CF", color: "#d47766" },
@@ -10101,7 +10518,7 @@ var DOT_MAP = {
10101
10518
  };
10102
10519
  function readManifest() {
10103
10520
  const p = sessionsManifestPath();
10104
- if (!existsSync28(p)) return null;
10521
+ if (!existsSync29(p)) return null;
10105
10522
  try {
10106
10523
  return JSON.parse(readFileSync30(p, "utf-8"));
10107
10524
  } catch {
@@ -10110,7 +10527,7 @@ function readManifest() {
10110
10527
  }
10111
10528
  function tmuxExec(cmd) {
10112
10529
  try {
10113
- return execSync15(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
10530
+ return execSync16(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
10114
10531
  } catch {
10115
10532
  return null;
10116
10533
  }
@@ -10147,9 +10564,9 @@ if (nodeVersion < 22) {
10147
10564
  process.exit(1);
10148
10565
  }
10149
10566
  var program = new Command();
10150
- program.name("sisyphus").description("tmux-integrated orchestration daemon for Claude Code").version(
10567
+ program.name("sis").description("tmux-integrated orchestration daemon for Claude Code").version(
10151
10568
  JSON.parse(
10152
- readFileSync31(join26(dirname12(fileURLToPath5(import.meta.url)), "..", "package.json"), "utf-8")
10569
+ readFileSync31(join27(dirname12(fileURLToPath5(import.meta.url)), "..", "package.json"), "utf-8")
10153
10570
  ).version
10154
10571
  );
10155
10572
  program.configureHelp({
@@ -10190,6 +10607,7 @@ registerSegmentUnregister(segment);
10190
10607
  var admin = program.command("admin").description("Admin / setup commands");
10191
10608
  registerSetup(admin);
10192
10609
  registerSetupKeybind(admin);
10610
+ registerHomeInit(admin);
10193
10611
  registerDoctor(admin);
10194
10612
  registerInit(admin);
10195
10613
  registerUninstall(admin);
@@ -10202,27 +10620,28 @@ registerScratch(admin);
10202
10620
  registerReview(admin);
10203
10621
  registerCompanion(program);
10204
10622
  registerDeploy(program);
10623
+ registerCloud(program);
10205
10624
  var diagnostic = program.command("diagnostic", { hidden: true });
10206
10625
  attachNotify(diagnostic);
10207
10626
  attachTmuxStatus(diagnostic);
10208
10627
  attachTmuxSessions(diagnostic);
10209
10628
  program.addHelpText("after", `
10210
10629
  Examples:
10211
- $ sisyphus start "Implement auth system" Start a new session
10212
- $ sisyphus start "Build @reqs.md" -n auth Start with name + requirements
10213
- $ sisyphus status Check current sessions
10214
- $ sisyphus dashboard Open the TUI
10215
- $ sisyphus admin doctor Verify installation
10630
+ $ sis start "Implement auth system" Start a new session
10631
+ $ sis start "Build @reqs.md" -n auth Start with name + requirements
10632
+ $ sis status Check current sessions
10633
+ $ sis dashboard Open the TUI
10634
+ $ sis admin doctor Verify installation
10216
10635
 
10217
- Run 'sisyphus admin getting-started' for a complete usage guide.
10636
+ Run 'sis admin getting-started' for a complete usage guide.
10218
10637
  `);
10219
10638
  var args = process.argv.slice(2);
10220
10639
  var firstArg = args[0];
10221
10640
  var skipWelcome = ["admin", "help", "--help", "-h", "--version", "-V"];
10222
- if (!existsSync29(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
10641
+ if (!existsSync30(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
10223
10642
  mkdirSync14(globalDir(), { recursive: true });
10224
10643
  console.log("");
10225
- console.log(" Welcome to Sisyphus. Run 'sisyphus admin setup' to get started.");
10644
+ console.log(" Welcome to Sisyphus. Run 'sis admin setup' to get started.");
10226
10645
  console.log("");
10227
10646
  }
10228
10647
  program.parseAsync(process.argv).catch((err) => {