@smilintux/skcapstone 0.9.0 → 0.12.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/.env.example +10 -4
  2. package/.github/workflows/ci.yml +2 -2
  3. package/.github/workflows/publish.yml +9 -2
  4. package/.openclaw-workspace.json +2 -2
  5. package/CLAUDE.md +37 -0
  6. package/MISSION.md +17 -2
  7. package/README.md +282 -3
  8. package/docker/Dockerfile +7 -7
  9. package/docker/compose-templates/dev-team.yml +12 -12
  10. package/docker/compose-templates/mini-team.yml +9 -9
  11. package/docker/compose-templates/ops-team.yml +10 -10
  12. package/docker/compose-templates/research-team.yml +10 -10
  13. package/docker/entrypoint.sh +4 -4
  14. package/docs/ADR-optional-integration-backbone.md +181 -0
  15. package/docs/ARCHITECTURE.md +186 -43
  16. package/docs/BOND_WITH_GROK.md +6 -6
  17. package/docs/CUSTOM_AGENT.md +278 -1
  18. package/docs/DREAMING.md +70 -0
  19. package/docs/GETTING_STARTED.md +10 -7
  20. package/docs/QUICKSTART.md +10 -6
  21. package/docs/SKJOULE_ARCHITECTURE.md +3 -3
  22. package/docs/SOUL_SWAPPER.md +5 -5
  23. package/docs/hammertime-audit.md +402 -0
  24. package/docs/sk-integration-HANDOFF.md +117 -0
  25. package/docs/skscheduler.md +155 -0
  26. package/docs/superpowers/examples/jobs.yaml +31 -0
  27. package/docs/superpowers/plans/2026-06-08-skscheduler.md +1265 -0
  28. package/docs/superpowers/specs/2026-06-08-skscheduler-design.md +186 -0
  29. package/examples/custom-bond-template.json +1 -1
  30. package/examples/grok-feb.json +1 -1
  31. package/examples/queen-ava-feb.json +1 -1
  32. package/launchd/com.skcapstone.daemon.plist +52 -0
  33. package/launchd/com.skcapstone.memory-compress.plist +45 -0
  34. package/launchd/com.skcapstone.skcomms-heartbeat.plist +33 -0
  35. package/launchd/com.skcapstone.skcomms-queue-drain.plist +34 -0
  36. package/launchd/install-launchd.sh +156 -0
  37. package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/index.ts +3 -2
  38. package/package.json +1 -1
  39. package/pyproject.toml +16 -10
  40. package/scripts/archive-sessions.sh +95 -0
  41. package/scripts/check-updates.py +4 -4
  42. package/scripts/install-bundle.sh +8 -8
  43. package/scripts/install.ps1 +12 -11
  44. package/scripts/install.sh +196 -11
  45. package/scripts/model-fallback-monitor.sh +102 -0
  46. package/scripts/notion-api.py +259 -0
  47. package/scripts/nvidia-proxy.mjs +908 -0
  48. package/scripts/proxy-monitor.sh +89 -0
  49. package/scripts/refresh-anthropic-token.sh +172 -0
  50. package/scripts/release.sh +98 -0
  51. package/scripts/session-to-memory.py +219 -0
  52. package/scripts/skgateway.mjs +856 -0
  53. package/scripts/telegram-catchup-all.sh +147 -0
  54. package/scripts/verify_install.sh +2 -2
  55. package/scripts/wargov-ufo-capture/README.md +43 -0
  56. package/scripts/wargov-ufo-capture/cdp_capture_release2.py +273 -0
  57. package/scripts/wargov-ufo-capture/cdp_capture_splc_doj.py +246 -0
  58. package/scripts/wargov-ufo-capture/cdp_finish.py +271 -0
  59. package/scripts/wargov-ufo-capture/cdp_probe.py +188 -0
  60. package/scripts/wargov-ufo-capture/cdp_splc_pressrelease.py +101 -0
  61. package/scripts/wargov-ufo-capture/parse_csv.py +95 -0
  62. package/scripts/wargov-ufo-capture/pull_dvids.sh +107 -0
  63. package/scripts/watch-anthropic-token.sh +212 -0
  64. package/scripts/windows/install-tasks.ps1 +7 -7
  65. package/scripts/windows/skcapstone-task.xml +1 -1
  66. package/src/skcapstone/__init__.py +45 -3
  67. package/src/skcapstone/_cli_monolith.py +20 -15
  68. package/src/skcapstone/activity.py +5 -1
  69. package/src/skcapstone/agent_card.py +3 -2
  70. package/src/skcapstone/api.py +41 -40
  71. package/src/skcapstone/auction.py +14 -11
  72. package/src/skcapstone/backup.py +2 -1
  73. package/src/skcapstone/blueprint_registry.py +4 -3
  74. package/src/skcapstone/blueprints/builtins/itil-operations.yaml +40 -0
  75. package/src/skcapstone/brain_first.py +238 -0
  76. package/src/skcapstone/changelog.py +1 -1
  77. package/src/skcapstone/chat.py +22 -17
  78. package/src/skcapstone/cli/__init__.py +9 -1
  79. package/src/skcapstone/cli/_common.py +1 -0
  80. package/src/skcapstone/cli/agents_spawner.py +5 -2
  81. package/src/skcapstone/cli/alerts.py +25 -4
  82. package/src/skcapstone/cli/bench.py +15 -15
  83. package/src/skcapstone/cli/chat.py +7 -4
  84. package/src/skcapstone/cli/consciousness.py +5 -2
  85. package/src/skcapstone/cli/context_cmd.py +18 -4
  86. package/src/skcapstone/cli/daemon.py +121 -42
  87. package/src/skcapstone/cli/gtd.py +26 -1
  88. package/src/skcapstone/cli/housekeeping.py +3 -3
  89. package/src/skcapstone/cli/identity_cmd.py +378 -0
  90. package/src/skcapstone/cli/joule_cmd.py +7 -3
  91. package/src/skcapstone/cli/memory.py +8 -6
  92. package/src/skcapstone/cli/peers_dir.py +1 -1
  93. package/src/skcapstone/cli/register_cmd.py +29 -3
  94. package/src/skcapstone/cli/scheduler_cmd.py +167 -0
  95. package/src/skcapstone/cli/session.py +25 -0
  96. package/src/skcapstone/cli/setup.py +96 -29
  97. package/src/skcapstone/cli/shell_cmd.py +53 -1
  98. package/src/skcapstone/cli/skills_cmd.py +2 -2
  99. package/src/skcapstone/cli/soul.py +8 -5
  100. package/src/skcapstone/cli/status.py +37 -11
  101. package/src/skcapstone/cli/telegram.py +21 -0
  102. package/src/skcapstone/cli/test_cmd.py +5 -5
  103. package/src/skcapstone/cli/test_connection.py +2 -2
  104. package/src/skcapstone/cli/upgrade_cmd.py +23 -14
  105. package/src/skcapstone/cli/version_cmd.py +1 -1
  106. package/src/skcapstone/cli/watch_cmd.py +9 -6
  107. package/src/skcapstone/cloud9_bridge.py +14 -14
  108. package/src/skcapstone/codex_setup.py +255 -0
  109. package/src/skcapstone/config_validator.py +7 -4
  110. package/src/skcapstone/consciousness_config.py +5 -1
  111. package/src/skcapstone/consciousness_loop.py +313 -273
  112. package/src/skcapstone/context_loader.py +121 -0
  113. package/src/skcapstone/coord_federation.py +2 -1
  114. package/src/skcapstone/coordination.py +23 -6
  115. package/src/skcapstone/crush_integration.py +2 -1
  116. package/src/skcapstone/daemon.py +151 -88
  117. package/src/skcapstone/dashboard.py +10 -10
  118. package/src/skcapstone/data/sk-agent-picker.sh +421 -0
  119. package/src/skcapstone/data/systemd/skcapstone-api.socket +9 -0
  120. package/src/skcapstone/data/systemd/skcapstone-memory-compress.service +18 -0
  121. package/src/skcapstone/data/systemd/skcapstone-memory-compress.timer +11 -0
  122. package/src/skcapstone/data/systemd/skcapstone.service +37 -0
  123. package/src/skcapstone/data/systemd/skcapstone@.service +50 -0
  124. package/src/skcapstone/data/systemd/skcomms-heartbeat.service +18 -0
  125. package/{systemd/skcomm-heartbeat.timer → src/skcapstone/data/systemd/skcomms-heartbeat.timer} +2 -2
  126. package/src/skcapstone/data/systemd/skcomms-queue-drain.service +17 -0
  127. package/{systemd/skcomm-queue-drain.timer → src/skcapstone/data/systemd/skcomms-queue-drain.timer} +2 -2
  128. package/src/skcapstone/defaults/claude/CLAUDE.md +67 -0
  129. package/src/skcapstone/defaults/claude/settings.json +74 -0
  130. package/src/skcapstone/defaults/lumina/config/claude-hooks.md +57 -0
  131. package/src/skcapstone/defaults/lumina/config/skgraph.yaml +55 -10
  132. package/src/skcapstone/defaults/lumina/config/skmemory.yaml +79 -13
  133. package/src/skcapstone/defaults/lumina/config/skvector.yaml +60 -9
  134. package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +2 -2
  135. package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
  136. package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +9 -9
  137. package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +2 -2
  138. package/src/skcapstone/defaults/unhinged.json +13 -0
  139. package/src/skcapstone/discovery.py +43 -20
  140. package/src/skcapstone/doctor.py +941 -22
  141. package/src/skcapstone/dreaming.py +1183 -109
  142. package/src/skcapstone/emotion_tracker.py +2 -2
  143. package/src/skcapstone/export.py +4 -3
  144. package/src/skcapstone/fuse_mount.py +35 -25
  145. package/src/skcapstone/gui_installer.py +2 -2
  146. package/src/skcapstone/heartbeat.py +34 -30
  147. package/src/skcapstone/housekeeping.py +14 -14
  148. package/src/skcapstone/install_wizard.py +209 -7
  149. package/src/skcapstone/itil.py +13 -4
  150. package/src/skcapstone/kms_scheduler.py +10 -8
  151. package/src/skcapstone/launchd.py +426 -0
  152. package/src/skcapstone/mcp_launcher.py +15 -1
  153. package/src/skcapstone/mcp_server.py +341 -49
  154. package/src/skcapstone/mcp_tools/__init__.py +2 -0
  155. package/src/skcapstone/mcp_tools/_helpers.py +2 -2
  156. package/src/skcapstone/mcp_tools/ansible_tools.py +7 -4
  157. package/src/skcapstone/mcp_tools/brain_first_tools.py +90 -0
  158. package/src/skcapstone/mcp_tools/capauth_tools.py +7 -4
  159. package/src/skcapstone/mcp_tools/comm_tools.py +10 -10
  160. package/src/skcapstone/mcp_tools/coord_tools.py +8 -4
  161. package/src/skcapstone/mcp_tools/did_tools.py +11 -8
  162. package/src/skcapstone/mcp_tools/gtd_tools.py +4 -4
  163. package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
  164. package/src/skcapstone/mcp_tools/notification_tools.py +22 -6
  165. package/src/skcapstone/mcp_tools/{skcomm_tools.py → skcomms_tools.py} +14 -14
  166. package/src/skcapstone/mcp_tools/soul_tools.py +8 -2
  167. package/src/skcapstone/mdns_discovery.py +2 -2
  168. package/src/skcapstone/memory_curator.py +1 -1
  169. package/src/skcapstone/memory_engine.py +10 -3
  170. package/src/skcapstone/metrics.py +30 -16
  171. package/src/skcapstone/migrate_memories.py +4 -3
  172. package/src/skcapstone/migrate_multi_agent.py +8 -7
  173. package/src/skcapstone/models.py +47 -5
  174. package/src/skcapstone/notifications.py +42 -18
  175. package/src/skcapstone/onboard.py +1000 -126
  176. package/src/skcapstone/operator_link.py +170 -0
  177. package/src/skcapstone/peer_directory.py +4 -4
  178. package/src/skcapstone/peers.py +19 -19
  179. package/src/skcapstone/pillars/__init__.py +7 -5
  180. package/src/skcapstone/pillars/consciousness.py +191 -0
  181. package/src/skcapstone/pillars/identity.py +51 -7
  182. package/src/skcapstone/pillars/memory.py +9 -3
  183. package/src/skcapstone/pillars/sync.py +2 -2
  184. package/src/skcapstone/preflight.py +3 -3
  185. package/src/skcapstone/providers/docker.py +28 -28
  186. package/src/skcapstone/register.py +6 -6
  187. package/src/skcapstone/registry_client.py +5 -4
  188. package/src/skcapstone/runtime.py +14 -3
  189. package/src/skcapstone/scheduled_tasks.py +254 -19
  190. package/src/skcapstone/scheduler_jobs.py +456 -0
  191. package/src/skcapstone/scheduler_runner.py +239 -0
  192. package/src/skcapstone/scheduler_state.py +162 -0
  193. package/src/skcapstone/sdk.py +310 -0
  194. package/src/skcapstone/service_health.py +279 -39
  195. package/src/skcapstone/session_briefing.py +108 -0
  196. package/src/skcapstone/session_capture.py +1 -1
  197. package/src/skcapstone/shell.py +7 -1
  198. package/src/skcapstone/soul.py +3 -1
  199. package/src/skcapstone/soul_switch.py +3 -1
  200. package/src/skcapstone/summary.py +6 -6
  201. package/src/skcapstone/sync_engine.py +15 -15
  202. package/src/skcapstone/sync_watcher.py +2 -2
  203. package/src/skcapstone/systemd.py +72 -21
  204. package/src/skcapstone/team_comms.py +8 -8
  205. package/src/skcapstone/team_engine.py +1 -1
  206. package/src/skcapstone/testrunner.py +3 -3
  207. package/src/skcapstone/trust_graph.py +40 -5
  208. package/src/skcapstone/unified_search.py +15 -6
  209. package/src/skcapstone/uninstall_wizard.py +11 -3
  210. package/src/skcapstone/version_check.py +8 -4
  211. package/src/skcapstone/warmth_anchor.py +4 -2
  212. package/src/skcapstone/whoami.py +4 -4
  213. package/systemd/skcapstone.service +4 -6
  214. package/systemd/skcapstone@.service +7 -8
  215. package/systemd/skcomms-heartbeat.service +21 -0
  216. package/systemd/skcomms-heartbeat.timer +12 -0
  217. package/systemd/skcomms-queue-drain.service +17 -0
  218. package/systemd/skcomms-queue-drain.timer +12 -0
  219. package/tests/conftest.py +39 -0
  220. package/tests/integration/test_consciousness_e2e.py +39 -39
  221. package/tests/test_agent_card.py +1 -1
  222. package/tests/test_agent_home_scaffold.py +34 -0
  223. package/tests/test_alerts_consumer_topics.py +27 -0
  224. package/tests/test_backup.py +2 -1
  225. package/tests/test_chat.py +6 -6
  226. package/tests/test_claude_md.py +2 -2
  227. package/tests/test_cli_skills.py +10 -10
  228. package/tests/test_cli_test_cmd.py +4 -4
  229. package/tests/test_cli_test_connection.py +1 -1
  230. package/tests/test_cloud9_bridge.py +6 -6
  231. package/tests/test_consciousness_e2e.py +1 -1
  232. package/tests/test_consciousness_loop.py +10 -10
  233. package/tests/test_coordination.py +25 -0
  234. package/tests/test_cross_package.py +21 -21
  235. package/tests/test_daemon.py +4 -4
  236. package/tests/test_daemon_shutdown.py +1 -1
  237. package/tests/test_docker_provider.py +29 -29
  238. package/tests/test_doctor.py +400 -0
  239. package/tests/test_doctor_skscheduler.py +50 -0
  240. package/tests/test_dreaming_engine.py +147 -0
  241. package/tests/test_dreaming_gtd_capture.py +35 -0
  242. package/tests/test_e2e_automated.py +8 -5
  243. package/tests/test_fuse_mount.py +10 -10
  244. package/tests/test_gtd_brief.py +46 -0
  245. package/tests/test_gtd_malformed_tolerance.py +31 -0
  246. package/tests/test_housekeeping.py +15 -15
  247. package/tests/test_identity_migrate.py +251 -0
  248. package/tests/test_integration_backbone.py +598 -0
  249. package/tests/test_itil_gtd_lifecycle.py +37 -0
  250. package/tests/test_jobs_dropins.py +84 -0
  251. package/tests/test_mcp_server.py +82 -37
  252. package/tests/test_models.py +48 -4
  253. package/tests/test_multi_agent.py +31 -29
  254. package/tests/test_notifications.py +122 -32
  255. package/tests/test_onboard.py +63 -75
  256. package/tests/test_operator_link.py +78 -0
  257. package/tests/test_peers.py +14 -14
  258. package/tests/test_pillars.py +98 -0
  259. package/tests/test_preflight.py +3 -3
  260. package/tests/test_runtime.py +21 -0
  261. package/tests/test_scheduled_tasks.py +11 -6
  262. package/tests/test_scheduler_cli.py +47 -0
  263. package/tests/test_scheduler_features.py +133 -0
  264. package/tests/test_scheduler_integration.py +87 -0
  265. package/tests/test_scheduler_jobs.py +155 -0
  266. package/tests/test_scheduler_runner.py +64 -0
  267. package/tests/test_scheduler_state.py +57 -0
  268. package/tests/test_sdk.py +70 -0
  269. package/tests/test_service_health_incidents.py +34 -0
  270. package/tests/test_service_registry.py +52 -0
  271. package/tests/test_session_briefing.py +130 -0
  272. package/tests/test_snapshots.py +4 -4
  273. package/tests/test_sync_pipeline.py +26 -26
  274. package/tests/test_team_comms.py +2 -2
  275. package/tests/test_testrunner.py +2 -2
  276. package/tests/test_trust_graph.py +18 -0
  277. package/tests/test_unified_search.py +2 -2
  278. package/tests/test_version_check.py +10 -0
  279. package/tests/test_version_cmd.py +8 -8
  280. package/tests/test_whoami.py +1 -1
  281. package/systemd/skcomm-heartbeat.service +0 -18
  282. package/systemd/skcomm-queue-drain.service +0 -17
  283. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/package.json +0 -0
  284. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/openclaw.plugin.json +0 -0
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env bash
2
+ # Watch Claude Code credentials file and sync token to OpenClaw immediately on change.
3
+ #
4
+ # Replaces the 2-hour timer approach which missed token refreshes.
5
+ # Claude Code auto-refreshes its OAuth token and writes to .credentials.json.
6
+ # This script detects the write and syncs within seconds.
7
+ #
8
+ # Requires: inotifywait (from inotify-tools package)
9
+ # Install: sudo apt install inotify-tools
10
+ #
11
+ # Run as systemd user service (not timer).
12
+
13
+ set -euo pipefail
14
+
15
+ CREDS="$HOME/.claude/.credentials.json"
16
+ OPENCLAW_JSON="$HOME/.openclaw/openclaw.json"
17
+ OPENCLAW_ENV="$HOME/.openclaw/.env"
18
+ OVERRIDE_CONF="$HOME/.config/systemd/user/openclaw-gateway.service.d/override.conf"
19
+ LOG_TAG="anthropic-token-watch"
20
+
21
+ log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [$LOG_TAG] $*"; }
22
+
23
+ sync_token() {
24
+ if [ ! -f "$CREDS" ]; then
25
+ log "ERROR: Claude credentials not found at $CREDS"
26
+ return 1
27
+ fi
28
+
29
+ # Read current token from Claude Code
30
+ local new_token
31
+ new_token=$(python3 -c "import json; print(json.load(open('$CREDS'))['claudeAiOauth']['accessToken'])" 2>/dev/null) || {
32
+ log "ERROR: Failed to read token from credentials"
33
+ return 1
34
+ }
35
+
36
+ local expires_in
37
+ expires_in=$(python3 -c "import json,time; print(f'{(json.load(open(\"$CREDS\"))[\"claudeAiOauth\"][\"expiresAt\"]/1000 - time.time())/3600:.1f}h')" 2>/dev/null || echo "unknown")
38
+
39
+ # Read current token from credentials file (track changes by comparing with last known)
40
+ local state_file="$HOME/.skcapstone/agents/lumina/logs/anthropic-token.last"
41
+ local current_token
42
+ current_token=$(cat "$state_file" 2>/dev/null || echo "")
43
+
44
+ if [ "$new_token" = "$current_token" ]; then
45
+ log "Token unchanged (expires in $expires_in)"
46
+ return 0
47
+ fi
48
+
49
+ log "Token changed! Syncing... (new token expires in $expires_in)"
50
+
51
+ # 1. Save new token to state file
52
+ echo "$new_token" > "$state_file"
53
+ log "State file updated"
54
+
55
+ # NOTE: anthropic provider removed from openclaw.json — all Claude models
56
+ # now route through claude-code proxy (port 18782). No openclaw.json update needed.
57
+
58
+ # 2. Update .env (kept for any scripts that source it)
59
+ if grep -q "^ANTHROPIC_API_KEY=" "$OPENCLAW_ENV" 2>/dev/null; then
60
+ sed -i "s|^ANTHROPIC_API_KEY=.*|ANTHROPIC_API_KEY=$new_token|" "$OPENCLAW_ENV"
61
+ else
62
+ echo "ANTHROPIC_API_KEY=$new_token" >> "$OPENCLAW_ENV"
63
+ fi
64
+ log "Updated .env"
65
+
66
+ # 3. Update systemd override (ANTHROPIC_API_KEY kept for claude-code-api server)
67
+ if [ -f "$OVERRIDE_CONF" ]; then
68
+ local nvidia_key
69
+ nvidia_key=$(grep "NVIDIA_API_KEY=" "$OVERRIDE_CONF" 2>/dev/null | sed 's/.*NVIDIA_API_KEY=//' || true)
70
+ cat > "$OVERRIDE_CONF" << EOF
71
+ [Unit]
72
+ StartLimitIntervalSec=60
73
+ StartLimitBurst=10
74
+
75
+ [Service]
76
+ RestartSec=10
77
+ Environment=NVIDIA_API_KEY=${nvidia_key}
78
+ Environment=ANTHROPIC_API_KEY=${new_token}
79
+ EOF
80
+ log "Updated systemd override"
81
+ fi
82
+
83
+ # 4. Reload and restart gateway
84
+ systemctl --user daemon-reload 2>/dev/null || true
85
+ systemctl --user restart openclaw-gateway 2>/dev/null && log "Gateway restarted" || log "WARN: Gateway restart failed (may not be running as systemd service)"
86
+
87
+ # 5. Sync credentials to GPU box (.100) for skvoice service
88
+ scp -q "$CREDS" cbrd21@192.168.0.100:~/.claude/.credentials.json 2>/dev/null && log "Synced credentials to .100" || log "WARN: Failed to sync credentials to .100"
89
+
90
+ log "Sync complete. Token expires in $expires_in"
91
+ }
92
+
93
+ # Proactive token refresh — refresh before expiry even if no Claude Code session is running
94
+ refresh_token_proactively() {
95
+ if [ ! -f "$CREDS" ]; then return 0; fi
96
+
97
+ local remaining_ms
98
+ remaining_ms=$(python3 -c "
99
+ import json, time
100
+ creds = json.load(open('$CREDS'))
101
+ exp = creds.get('claudeAiOauth',{}).get('expiresAt', 0)
102
+ print(int(exp - time.time() * 1000))
103
+ " 2>/dev/null || echo "999999999")
104
+
105
+ # Refresh if less than 3 hours remaining (10800000 ms) — gives time for retries before expiry
106
+ if [ "$remaining_ms" -gt 10800000 ]; then
107
+ local remaining_h=$(( remaining_ms / 3600000 ))
108
+ log "Token still valid (${remaining_h}h remaining), no refresh needed"
109
+ return 0
110
+ fi
111
+
112
+ log "Token expiring/expired (${remaining_ms}ms remaining) — proactively refreshing..."
113
+
114
+ # Strategy: use `claude auth status` to trigger Claude Code's built-in
115
+ # token refresh. This is far more reliable than calling the OAuth endpoint
116
+ # ourselves (which gets 429 rate-limited every time).
117
+ # Claude Code manages its own PKCE state, session cookies, etc. — just let it.
118
+ local MAX_RETRIES=3
119
+ local attempt=0
120
+ local refreshed=false
121
+
122
+ while [ "$attempt" -lt "$MAX_RETRIES" ]; do
123
+ attempt=$((attempt + 1))
124
+ log "Refresh attempt $attempt/$MAX_RETRIES via 'claude auth status'..."
125
+
126
+ # claude auth status checks credentials and refreshes if needed
127
+ # --output json ensures clean non-interactive output
128
+ local output
129
+ output=$(claude auth status --output json 2>&1) || true
130
+
131
+ # Check if the token was actually refreshed (file mtime changed)
132
+ local new_remaining_ms
133
+ new_remaining_ms=$(python3 -c "
134
+ import json, time
135
+ creds = json.load(open('$CREDS'))
136
+ exp = creds.get('claudeAiOauth',{}).get('expiresAt', 0)
137
+ print(int(exp - time.time() * 1000))
138
+ " 2>/dev/null || echo "0")
139
+
140
+ if [ "$new_remaining_ms" -gt 10800000 ]; then
141
+ local new_h=$(( new_remaining_ms / 3600000 ))
142
+ log "Token refreshed successfully (${new_h}h remaining)"
143
+ refreshed=true
144
+ break
145
+ fi
146
+
147
+ log "Token still expired after attempt $attempt, waiting 30s..."
148
+ sleep 30
149
+ done
150
+
151
+ if [ "$refreshed" = "false" ]; then
152
+ log "ERROR: All $MAX_RETRIES refresh attempts via claude auth failed"
153
+ log "Token may require manual 'claude auth login' to re-authenticate"
154
+ fi
155
+
156
+ local rc=$?
157
+ if [ "$rc" -eq 0 ]; then
158
+ log "Proactive refresh succeeded"
159
+ # sync_token will fire from the inotifywait detecting the file write,
160
+ # but also call it directly in case inotifywait misses the self-write
161
+ sync_token
162
+ else
163
+ log "ERROR: Proactive refresh failed (rc=$rc)"
164
+ fi
165
+ return 0 # Never let refresh failure kill the watcher loop
166
+ }
167
+
168
+ # Compute inotifywait timeout based on token remaining life.
169
+ # When token is healthy: check every 30m. Near expiry (<2h): check every 5m.
170
+ # Already expired: check every 2m (retry window for 429 backoff).
171
+ get_watch_timeout() {
172
+ local remaining_ms
173
+ remaining_ms=$(python3 -c "
174
+ import json, time
175
+ creds = json.load(open('$CREDS'))
176
+ exp = creds.get('claudeAiOauth',{}).get('expiresAt', 0)
177
+ print(int(exp - time.time() * 1000))
178
+ " 2>/dev/null || echo "0")
179
+
180
+ if [ "$remaining_ms" -le 0 ]; then
181
+ echo 120 # Expired: retry every 2 minutes
182
+ elif [ "$remaining_ms" -le 10800000 ]; then
183
+ echo 180 # <3h remaining: check every 3 minutes
184
+ else
185
+ echo 1800 # Healthy: check every 30 minutes
186
+ fi
187
+ }
188
+
189
+ # Initial sync on startup — also refresh proactively if token is expired/expiring
190
+ log "Starting token watcher..."
191
+ sync_token || true
192
+ refresh_token_proactively || true
193
+
194
+ # Watch for changes to credentials file + proactive refresh timer
195
+ log "Watching $CREDS for changes (with adaptive refresh interval)..."
196
+ while true; do
197
+ timeout=$(get_watch_timeout)
198
+ # inotifywait returns: 0=event, 1=error, 2=timeout
199
+ # CRITICAL: use `|| true` to prevent set -e from killing the script on timeout
200
+ inotifywait -q -t "$timeout" -e modify -e close_write -e moved_to \
201
+ "$(dirname "$CREDS")" --include "$(basename "$CREDS")" 2>/dev/null || true
202
+
203
+ # Always check for proactive refresh on every loop iteration
204
+ # This handles both timeout and file-change cases
205
+ refresh_token_proactively || true
206
+
207
+ # If file was modified externally (Claude Code session), also sync
208
+ if [ -f "$CREDS" ]; then
209
+ sleep 1
210
+ sync_token || true
211
+ fi
212
+ done
@@ -62,7 +62,7 @@ if (-not $SkenvPath) {
62
62
 
63
63
  $PythonExe = Join-Path $SkenvPath 'Scripts\python.exe'
64
64
  $SKCapstoneExe = Join-Path $SkenvPath 'Scripts\skcapstone.exe'
65
- $SKCommExe = Join-Path $SkenvPath 'Scripts\skcomm.exe'
65
+ $SKCommsExe = Join-Path $SkenvPath 'Scripts\skcomms.exe'
66
66
 
67
67
  if (-not (Test-Path $PythonExe)) {
68
68
  Write-Host "ERROR: Python not found at $PythonExe" -ForegroundColor Red
@@ -261,10 +261,10 @@ if (Remove-ExistingTask 'SKCapstone-SyncWatcher') {
261
261
  }
262
262
 
263
263
  # ---------------------------------------------------------------------------
264
- # Task 4: Health Monitor / Heartbeat (equivalent to skcomm-heartbeat.timer
265
- # and skcomm-queue-drain.timer combined)
264
+ # Task 4: Health Monitor / Heartbeat (equivalent to skcomms-heartbeat.timer
265
+ # and skcomms-queue-drain.timer combined)
266
266
  # Trigger: Every 60 seconds after logon
267
- # Action: skcomm heartbeat && skcomm queue drain
267
+ # Action: skcomms heartbeat && skcomms queue drain
268
268
  # ---------------------------------------------------------------------------
269
269
  Write-Host '[4/5] Health Monitor Heartbeat (every 60s)...' -ForegroundColor Green
270
270
 
@@ -279,9 +279,9 @@ if (Remove-ExistingTask 'SKCapstone-Heartbeat') {
279
279
  $content = @"
280
280
  @echo off
281
281
  echo [%date% %time%] === heartbeat started === >> "$logFile"
282
- "$SKCommExe" heartbeat >> "$logFile" 2>&1
282
+ "$SKCommsExe" heartbeat >> "$logFile" 2>&1
283
283
  echo [%date% %time%] heartbeat exit: %ERRORLEVEL% >> "$logFile"
284
- "$SKCommExe" queue drain >> "$logFile" 2>&1
284
+ "$SKCommsExe" queue drain >> "$logFile" 2>&1
285
285
  echo [%date% %time%] queue-drain exit: %ERRORLEVEL% >> "$logFile"
286
286
  echo [%date% %time%] === heartbeat finished === >> "$logFile"
287
287
  "@
@@ -310,7 +310,7 @@ echo [%date% %time%] === heartbeat finished === >> "$logFile"
310
310
  -Trigger $trigger `
311
311
  -Settings $settings `
312
312
  -Principal $principal `
313
- -Description 'Heartbeat + queue drain — equivalent to skcomm-heartbeat.timer + skcomm-queue-drain.timer' | Out-Null
313
+ -Description 'Heartbeat + queue drain — equivalent to skcomms-heartbeat.timer + skcomms-queue-drain.timer' | Out-Null
314
314
 
315
315
  Write-Host ' Registered: Every 60 seconds after logon'
316
316
  $registered++
@@ -15,7 +15,7 @@
15
15
  the Command/Arguments and Triggers sections for other tasks:
16
16
  - MemoryCompress: Command=skcapstone.exe memory compress, Trigger=Weekly
17
17
  - SyncWatcher: Command=python.exe -m skcapstone.cli sync poll, Trigger=Repetition/2min
18
- - Heartbeat: Command=skcomm.exe heartbeat, Trigger=Repetition/1min
18
+ - Heartbeat: Command=skcomms.exe heartbeat, Trigger=Repetition/1min
19
19
  - Housekeeping: See install-tasks.ps1 for the batch wrapper approach
20
20
  -->
21
21
  <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
@@ -11,9 +11,16 @@ import os
11
11
  import platform
12
12
  from pathlib import Path
13
13
 
14
- __version__ = "0.4.4"
14
+ __version__ = "0.6.8"
15
15
  __author__ = "smilinTux"
16
16
 
17
+ # Canonical default agent for the entire SK* suite. This is THE single source
18
+ # of truth for the fallback agent name — used by Python paths directly and
19
+ # propagated to the shell picker + child processes via `skcapstone shell-init`
20
+ # (which emits `export SK_DEFAULT_AGENT=<this>`). Override with the
21
+ # SK_DEFAULT_AGENT environment variable.
22
+ DEFAULT_AGENT = (os.environ.get("SK_DEFAULT_AGENT") or "lumina").strip() or "lumina"
23
+
17
24
 
18
25
  def _default_home() -> str:
19
26
  """Platform-aware default home for skcapstone."""
@@ -25,11 +32,41 @@ def _default_home() -> str:
25
32
  return os.path.expanduser("~/.skcapstone")
26
33
 
27
34
 
35
+ def _detect_active_agent(root: str | None = None) -> str | None:
36
+ """Best-effort active agent discovery.
37
+
38
+ Resolution order:
39
+ 1. Explicit SKAGENT / SKCAPSTONE_AGENT environment variable
40
+ 2. SK_DEFAULT_AGENT (defaults to "lumina") if that agent dir exists
41
+ 3. First non-template directory under ~/.skcapstone/agents (alphabetical)
42
+
43
+ Returns:
44
+ The active agent name if one can be resolved, else None.
45
+ """
46
+ env_agent = (os.environ.get("SKAGENT") or os.environ.get("SKCAPSTONE_AGENT", "")).strip()
47
+ if env_agent:
48
+ return env_agent
49
+
50
+ base = Path(root or os.environ.get("SKCAPSTONE_HOME", _default_home())).expanduser()
51
+ agents_dir = base / "agents"
52
+ if not agents_dir.exists():
53
+ return None
54
+
55
+ candidates = sorted(
56
+ entry.name
57
+ for entry in agents_dir.iterdir()
58
+ if entry.is_dir() and not entry.name.endswith("-template")
59
+ )
60
+ if not candidates:
61
+ return None
62
+ return DEFAULT_AGENT if DEFAULT_AGENT in candidates else candidates[0]
63
+
64
+
28
65
  # Root of the skcapstone tree (shared infra lives here)
29
66
  AGENT_HOME = os.environ.get("SKCAPSTONE_HOME", _default_home())
30
67
 
31
68
  # Which agent this process is running as (set by daemon/connector)
32
- SKCAPSTONE_AGENT = os.environ.get("SKCAPSTONE_AGENT", "lumina")
69
+ SKCAPSTONE_AGENT = _detect_active_agent() or ""
33
70
 
34
71
  # Default daemon port
35
72
  DEFAULT_PORT = int(os.environ.get("SKCAPSTONE_PORT", "9383"))
@@ -59,13 +96,18 @@ def agent_home(agent_name: str | None = None) -> Path:
59
96
  Returns:
60
97
  Path to the agent-specific home directory.
61
98
  """
62
- name = agent_name or SKCAPSTONE_AGENT
99
+ name = agent_name or SKCAPSTONE_AGENT or _detect_active_agent()
63
100
  root = Path(AGENT_HOME).expanduser()
64
101
  if name:
65
102
  return root / "agents" / name
66
103
  return root
67
104
 
68
105
 
106
+ def active_agent_name() -> str | None:
107
+ """Return the currently active agent name, if one can be resolved."""
108
+ return SKCAPSTONE_AGENT or _detect_active_agent()
109
+
110
+
69
111
  def shared_home() -> Path:
70
112
  """Return the shared root directory (~/.skcapstone/).
71
113
 
@@ -643,7 +643,7 @@ def doctor(home: str, json_out: bool):
643
643
  "agent": "Agent Home",
644
644
  "identity": "Identity (CapAuth)",
645
645
  "memory": "Memory (SKMemory)",
646
- "transport": "Transport (SKComm)",
646
+ "transport": "Transport (SKComms)",
647
647
  "sync": "Sync (Singularity)",
648
648
  }
649
649
 
@@ -2818,7 +2818,7 @@ def completions_uninstall(shell_name: str):
2818
2818
 
2819
2819
 
2820
2820
  @main.command("test")
2821
- @click.option("--package", "-p", default=None, help="Test a single package (e.g., skcomm, capauth).")
2821
+ @click.option("--package", "-p", default=None, help="Test a single package (e.g., skcomms, capauth).")
2822
2822
  @click.option("--fast", is_flag=True, help="Stop on first package failure.")
2823
2823
  @click.option("--verbose", "-v", is_flag=True, help="Verbose pytest output.")
2824
2824
  @click.option("--json-out", is_flag=True, help="Machine-readable JSON report.")
@@ -2826,15 +2826,15 @@ def completions_uninstall(shell_name: str):
2826
2826
  def test_cmd(package: str, fast: bool, verbose: bool, json_out: bool, timeout: int):
2827
2827
  """Run tests across all ecosystem packages.
2828
2828
 
2829
- Discovers skcapstone, capauth, skcomm, skchat, skmemory, and
2830
- cloud9-python test suites and runs them with a consolidated summary.
2829
+ Discovers skcapstone, capauth, skcomms, skchat, skmemory, and
2830
+ cloud9 test suites and runs them with a consolidated summary.
2831
2831
  Works from any terminal — no CI server required.
2832
2832
 
2833
2833
  Examples:
2834
2834
 
2835
2835
  skcapstone test
2836
2836
 
2837
- skcapstone test --package skcomm
2837
+ skcapstone test --package skcomms
2838
2838
 
2839
2839
  skcapstone test --fast --verbose
2840
2840
 
@@ -3037,7 +3037,7 @@ def peer_list(sk_home: str, json_out: bool):
3037
3037
  def peer_remove(name: str, sk_home: str):
3038
3038
  """Remove a peer by name.
3039
3039
 
3040
- Removes from both skcapstone and skcomm registries.
3040
+ Removes from both skcapstone and skcomms registries.
3041
3041
 
3042
3042
  Examples:
3043
3043
 
@@ -3212,8 +3212,8 @@ def dashboard(home: str, port: int, no_open: bool):
3212
3212
  import webbrowser
3213
3213
  try:
3214
3214
  webbrowser.open(url)
3215
- except Exception:
3216
- pass
3215
+ except Exception as exc:
3216
+ logger.warning("Failed to open browser for dashboard: %s", exc)
3217
3217
 
3218
3218
  server = start_dashboard(home_path, port=port)
3219
3219
  try:
@@ -3288,6 +3288,7 @@ def backup_create(home: str, output: str, no_encrypt: bool):
3288
3288
  console.print(f" [green]Backup saved:[/] {result_path}")
3289
3289
 
3290
3290
  except Exception as exc:
3291
+ logger.warning("_cli_monolith.py: %s", exc)
3291
3292
  console.print(f" [red]Backup failed:[/] {exc}")
3292
3293
  sys.exit(1)
3293
3294
 
@@ -3325,6 +3326,7 @@ def backup_restore(backup_file: str, home: str, force: bool):
3325
3326
  console.print(f" [yellow]{exc}[/]")
3326
3327
  sys.exit(1)
3327
3328
  except Exception as exc:
3329
+ logger.warning("_cli_monolith.py: %s", exc)
3328
3330
  console.print(f" [red]Restore failed:[/] {exc}")
3329
3331
  sys.exit(1)
3330
3332
 
@@ -3396,7 +3398,7 @@ def chat():
3396
3398
  def chat_send(peer: str, message: str, home: str, thread: Optional[str]):
3397
3399
  """Send a message to a peer agent.
3398
3400
 
3399
- Stores locally and delivers via SKComm if transports
3401
+ Stores locally and delivers via SKComms if transports
3400
3402
  are configured.
3401
3403
 
3402
3404
  Examples:
@@ -3434,7 +3436,7 @@ def chat_inbox(home: str, limit: int, poll: bool):
3434
3436
  """Show recent messages.
3435
3437
 
3436
3438
  Displays messages from local history. Use --poll to check
3437
- SKComm transports for new messages first.
3439
+ SKComms transports for new messages first.
3438
3440
 
3439
3441
  Examples:
3440
3442
 
@@ -5059,7 +5061,8 @@ def agents_messages(deployment_id: str, agent_name: Optional[str], limit: int, h
5059
5061
  content[:80] + ("…" if len(content) > 80 else ""),
5060
5062
  )
5061
5063
  total_shown += 1
5062
- except Exception:
5064
+ except Exception as e:
5065
+ logger.warning("_cli_monolith.py: %s", e)
5063
5066
  continue
5064
5067
 
5065
5068
  if table.row_count:
@@ -5106,7 +5109,8 @@ def agents_messages(deployment_id: str, agent_name: Optional[str], limit: int, h
5106
5109
  content[:90] + ("…" if len(content) > 90 else ""),
5107
5110
  )
5108
5111
  total_shown += 1
5109
- except Exception:
5112
+ except Exception as e:
5113
+ logger.warning("_cli_monolith.py: %s", e)
5110
5114
  continue
5111
5115
 
5112
5116
  if bc_table.row_count:
@@ -5353,8 +5357,8 @@ def agents_spawn(
5353
5357
  prov_backend = DockerProvider()
5354
5358
  elif prov_type == ProviderType.PROXMOX:
5355
5359
  prov_backend = ProxmoxProvider()
5356
- except Exception:
5357
- pass
5360
+ except Exception as exc:
5361
+ logger.warning("Failed to initialize provider backend for %s: %s", provider, exc)
5358
5362
 
5359
5363
  # Auto-classify for display
5360
5364
  detected_role, detected_model = classify_task(task)
@@ -5814,7 +5818,8 @@ def skills_install(source: str, agent: str | None, home: str, force: bool) -> No
5814
5818
  try:
5815
5819
  data = _json.loads(identity_path.read_text(encoding="utf-8"))
5816
5820
  agent = data.get("name", "global")
5817
- except Exception:
5821
+ except Exception as e:
5822
+ logger.warning("_cli_monolith.py: %s", e)
5818
5823
  agent = "global"
5819
5824
  else:
5820
5825
  agent = "global"
@@ -33,6 +33,9 @@ from collections import deque
33
33
  from datetime import datetime, timezone
34
34
  from typing import Any
35
35
 
36
+ import logging
37
+ logger = logging.getLogger(__name__)
38
+
36
39
  _MAXLEN = 100
37
40
 
38
41
  _history: deque[dict] = deque(maxlen=_MAXLEN)
@@ -93,7 +96,8 @@ def _fan_out(event: dict) -> None:
93
96
  for q in clients:
94
97
  try:
95
98
  q.put_nowait(chunk)
96
- except Exception:
99
+ except Exception as e:
100
+ logger.warning("activity.py: %s", e)
97
101
  dead.add(q)
98
102
  if dead:
99
103
  with _clients_lock:
@@ -6,7 +6,7 @@ with you: identity, public key, contact transports, capabilities,
6
6
  and trust level.
7
7
 
8
8
  Cards are JSON files signed with the agent's PGP key. They can be
9
- shared over SKComm, published to Nostr, posted as QR codes, or
9
+ shared over SKComms, published to Nostr, posted as QR codes, or
10
10
  exchanged via any out-of-band channel.
11
11
 
12
12
  Usage:
@@ -251,7 +251,8 @@ class AgentCard(BaseModel):
251
251
 
252
252
  verification = pub_key.verify(pgp_message)
253
253
  return bool(verification)
254
- except Exception:
254
+ except Exception as e:
255
+ logger.warning("agent_card.py: %s", e)
255
256
  return False
256
257
 
257
258
  def save(self, filepath: str | Path) -> Path: