@smilintux/skcapstone 0.4.6 → 0.4.7

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 (38) hide show
  1. package/.github/workflows/publish.yml +8 -1
  2. package/docs/CUSTOM_AGENT.md +184 -0
  3. package/docs/GETTING_STARTED.md +3 -0
  4. package/launchd/com.skcapstone.daemon.plist +52 -0
  5. package/launchd/com.skcapstone.memory-compress.plist +45 -0
  6. package/launchd/com.skcapstone.skcomm-heartbeat.plist +33 -0
  7. package/launchd/com.skcapstone.skcomm-queue-drain.plist +34 -0
  8. package/launchd/install-launchd.sh +156 -0
  9. package/package.json +1 -1
  10. package/pyproject.toml +1 -1
  11. package/scripts/archive-sessions.sh +88 -0
  12. package/scripts/install.sh +39 -8
  13. package/scripts/notion-api.py +259 -0
  14. package/scripts/nvidia-proxy.mjs +856 -0
  15. package/scripts/proxy-monitor.sh +89 -0
  16. package/scripts/skgateway.mjs +856 -0
  17. package/scripts/telegram-catchup-all.sh +136 -0
  18. package/src/skcapstone/__init__.py +1 -1
  19. package/src/skcapstone/blueprints/builtins/itil-operations.yaml +40 -0
  20. package/src/skcapstone/cli/__init__.py +2 -0
  21. package/src/skcapstone/cli/daemon.py +116 -41
  22. package/src/skcapstone/cli/itil.py +434 -0
  23. package/src/skcapstone/consciousness_config.py +27 -0
  24. package/src/skcapstone/coordination.py +1 -0
  25. package/src/skcapstone/daemon.py +19 -11
  26. package/src/skcapstone/dreaming.py +761 -0
  27. package/src/skcapstone/fuse_mount.py +21 -13
  28. package/src/skcapstone/heartbeat.py +33 -29
  29. package/src/skcapstone/itil.py +1104 -0
  30. package/src/skcapstone/launchd.py +426 -0
  31. package/src/skcapstone/mcp_server.py +258 -0
  32. package/src/skcapstone/mcp_tools/__init__.py +2 -0
  33. package/src/skcapstone/mcp_tools/gtd_tools.py +1 -1
  34. package/src/skcapstone/mcp_tools/itil_tools.py +657 -0
  35. package/src/skcapstone/onboard.py +130 -10
  36. package/src/skcapstone/scheduled_tasks.py +107 -0
  37. package/src/skcapstone/service_health.py +81 -2
  38. package/src/skcapstone/systemd.py +17 -0
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env bash
2
+ # telegram-catchup-all.sh — Import all configured Telegram groups into SKMemory
3
+ #
4
+ # Reads groups from ~/.skcapstone/agents/lumina/config/telegram.yaml
5
+ # and runs `skcapstone telegram catchup` for each enabled group.
6
+ #
7
+ # Usage:
8
+ # bash scripts/telegram-catchup-all.sh [--since YYYY-MM-DD] [--limit N] [--group NAME]
9
+ #
10
+ # Examples:
11
+ # bash scripts/telegram-catchup-all.sh # All groups, last 2000 msgs
12
+ # bash scripts/telegram-catchup-all.sh --since 2026-03-01 # All groups since March 1
13
+ # bash scripts/telegram-catchup-all.sh --group brother-john # Just one group
14
+ #
15
+ # Requires:
16
+ # - TELEGRAM_API_ID and TELEGRAM_API_HASH environment variables
17
+ # - ~/.skenv/bin/skcapstone on PATH
18
+ # - Telethon installed in ~/.skenv/
19
+
20
+ set -uo pipefail # no -e: individual group failures shouldn't stop the batch
21
+
22
+ SKENV="${HOME}/.skenv/bin"
23
+ SKCAPSTONE="${SKENV}/skcapstone"
24
+ CONFIG="${HOME}/.skcapstone/agents/lumina/config/telegram.yaml"
25
+ export SKCAPSTONE_AGENT="${SKCAPSTONE_AGENT:-lumina}"
26
+ export PATH="${SKENV}:${PATH}"
27
+
28
+ # Parse args
29
+ SINCE=""
30
+ LIMIT="2000"
31
+ ONLY_GROUP=""
32
+
33
+ while [[ $# -gt 0 ]]; do
34
+ case "$1" in
35
+ --since) SINCE="$2"; shift 2 ;;
36
+ --limit) LIMIT="$2"; shift 2 ;;
37
+ --group) ONLY_GROUP="$2"; shift 2 ;;
38
+ *) echo "Unknown arg: $1"; exit 1 ;;
39
+ esac
40
+ done
41
+
42
+ # Check prerequisites
43
+ if [[ -z "${TELEGRAM_API_ID:-}" || -z "${TELEGRAM_API_HASH:-}" ]]; then
44
+ echo "ERROR: TELEGRAM_API_ID and TELEGRAM_API_HASH must be set."
45
+ echo "Get them from https://my.telegram.org"
46
+ exit 1
47
+ fi
48
+
49
+ if [[ ! -f "$CONFIG" ]]; then
50
+ echo "ERROR: Config not found: $CONFIG"
51
+ exit 1
52
+ fi
53
+
54
+ # Parse groups from YAML (simple grep — no yq dependency)
55
+ echo "=== Telegram Catch-Up All ==="
56
+ echo "Config: $CONFIG"
57
+ echo "Agent: $SKCAPSTONE_AGENT"
58
+ echo "Limit: $LIMIT"
59
+ [[ -n "$SINCE" ]] && echo "Since: $SINCE"
60
+ [[ -n "$ONLY_GROUP" ]] && echo "Only group: $ONLY_GROUP"
61
+ echo ""
62
+
63
+ # Extract group entries: name, chat ID, tags, enabled status
64
+ SUCCESS=0
65
+ FAILED=0
66
+ SKIPPED=0
67
+
68
+ current_name=""
69
+ current_chat=""
70
+ current_tags=""
71
+ current_enabled=""
72
+
73
+ process_group() {
74
+ local name="$1" chat="$2" tags="$3" enabled="$4"
75
+
76
+ if [[ "$enabled" != "true" ]]; then
77
+ echo " SKIP $name (disabled)"
78
+ SKIPPED=$((SKIPPED + 1))
79
+ return
80
+ fi
81
+
82
+ if [[ -n "$ONLY_GROUP" && "$name" != *"$ONLY_GROUP"* ]]; then
83
+ SKIPPED=$((SKIPPED + 1))
84
+ return
85
+ fi
86
+
87
+ echo -n " IMPORTING $name (chat: $chat) ... "
88
+
89
+ local cmd="$SKCAPSTONE telegram catchup $chat --limit $LIMIT --min-length 20"
90
+ [[ -n "$SINCE" ]] && cmd="$cmd --since $SINCE"
91
+ [[ -n "$tags" ]] && cmd="$cmd --tags $tags"
92
+
93
+ if eval "$cmd" > /tmp/telegram-catchup-$name.log 2>&1; then
94
+ echo "OK"
95
+ SUCCESS=$((SUCCESS + 1))
96
+ else
97
+ echo "FAILED (see /tmp/telegram-catchup-$name.log)"
98
+ FAILED=$((FAILED + 1))
99
+ fi
100
+
101
+ # Rate limit — avoid hitting Telegram flood control
102
+ sleep 3
103
+ }
104
+
105
+ # Parse the YAML manually
106
+ while IFS= read -r line; do
107
+ # Detect new group entry
108
+ if [[ "$line" =~ ^[[:space:]]*-[[:space:]]*name:[[:space:]]*(.*) ]]; then
109
+ # Process previous group if we have one
110
+ if [[ -n "$current_name" ]]; then
111
+ process_group "$current_name" "$current_chat" "$current_tags" "$current_enabled"
112
+ fi
113
+ current_name="${BASH_REMATCH[1]}"
114
+ current_chat=""
115
+ current_tags=""
116
+ current_enabled="true"
117
+ elif [[ "$line" =~ ^[[:space:]]*chat:[[:space:]]*\"?([0-9]+)\"? ]]; then
118
+ current_chat="${BASH_REMATCH[1]}"
119
+ elif [[ "$line" =~ ^[[:space:]]*tags:[[:space:]]*\[(.*)\] ]]; then
120
+ # Convert YAML list to comma-separated
121
+ current_tags=$(echo "${BASH_REMATCH[1]}" | sed 's/,/ /g' | tr -s ' ' ',' | sed 's/^,//;s/,$//')
122
+ elif [[ "$line" =~ ^[[:space:]]*enabled:[[:space:]]*(.*) ]]; then
123
+ current_enabled="${BASH_REMATCH[1]}"
124
+ fi
125
+ done < "$CONFIG"
126
+
127
+ # Process last group
128
+ if [[ -n "$current_name" ]]; then
129
+ process_group "$current_name" "$current_chat" "$current_tags" "$current_enabled"
130
+ fi
131
+
132
+ echo ""
133
+ echo "=== Done ==="
134
+ echo " Success: $SUCCESS"
135
+ echo " Failed: $FAILED"
136
+ echo " Skipped: $SKIPPED"
@@ -11,7 +11,7 @@ import os
11
11
  import platform
12
12
  from pathlib import Path
13
13
 
14
- __version__ = "0.4.4"
14
+ __version__ = "0.4.7"
15
15
  __author__ = "smilinTux"
16
16
 
17
17
 
@@ -0,0 +1,40 @@
1
+ name: "ITIL Operations"
2
+ slug: "itil-operations"
3
+ version: "1.0.0"
4
+ description: "ITIL service management — incident, problem, and change lifecycle with SLA monitoring and continuous improvement."
5
+ icon: "🔄"
6
+ author: "smilinTux"
7
+
8
+ agents:
9
+ deming:
10
+ role: ops
11
+ model: reason
12
+ model_name: "deepseek-r1:32b"
13
+ description: "ITIL expert — incident triage, problem analysis, change management, SLA monitoring, and blameless postmortems."
14
+ vm_type: container
15
+ resources:
16
+ memory: "4g"
17
+ cores: 2
18
+ disk: "20g"
19
+ soul_blueprint: "souls/deming.yaml"
20
+ skills: [incident-management, problem-analysis, change-management, kedb, sla-monitoring, root-cause-analysis]
21
+
22
+ default_provider: local
23
+ estimated_cost: "$0 (local)"
24
+
25
+ network:
26
+ mesh_vpn: tailscale
27
+ discovery: skref_registry
28
+
29
+ storage:
30
+ skref_vault: "team-itil-ops"
31
+ memory_backend: filesystem
32
+ memory_sync: true
33
+
34
+ coordination:
35
+ queen: lumina
36
+ pattern: supervisor
37
+ heartbeat: "5m"
38
+ escalation: chef
39
+
40
+ tags: [ops, itil, incident-management, problem-analysis, change-management, sla-monitoring, continuous-improvement]
@@ -86,6 +86,7 @@ from .search_cmd import register_search_commands
86
86
  from .mood_cmd import register_mood_commands
87
87
  from .register_cmd import register_register_commands
88
88
  from .gtd import register_gtd_commands
89
+ from .itil import register_itil_commands
89
90
  from .skseed import register_skseed_commands
90
91
  from .service_cmd import register_service_commands
91
92
  from .telegram import register_telegram_commands
@@ -138,6 +139,7 @@ register_search_commands(main)
138
139
  register_mood_commands(main)
139
140
  register_register_commands(main)
140
141
  register_gtd_commands(main)
142
+ register_itil_commands(main)
141
143
  register_skseed_commands(main)
142
144
  register_service_commands(main)
143
145
  register_telegram_commands(main)
@@ -270,60 +270,117 @@ def register_daemon_commands(main: click.Group) -> None:
270
270
  console.print(f" [yellow]API unreachable on port {effective_port}[/]\n")
271
271
 
272
272
  @daemon.command("install")
273
- def daemon_install():
274
- """Install the daemon as a systemd user service.
273
+ @click.option("--agent", "agent_name", default=None,
274
+ help="Agent name for SKCAPSTONE_AGENT (default: from env or 'sovereign').")
275
+ @click.option("--start", is_flag=True, help="Start services immediately after installing.")
276
+ def daemon_install(agent_name: str | None, start: bool):
277
+ """Install the daemon as a system service.
275
278
 
276
- Copies unit files to ~/.config/systemd/user/, enables at login,
277
- and starts immediately. No root required.
279
+ On Linux: installs systemd user service units.
280
+ On macOS: installs launchd plist files to ~/Library/LaunchAgents/.
281
+
282
+ The --agent flag sets the SKCAPSTONE_AGENT environment variable
283
+ in the service definition. If not provided, uses the
284
+ SKCAPSTONE_AGENT env var or defaults to 'sovereign'.
278
285
 
279
286
  Examples:
280
287
 
281
288
  skcapstone daemon install
289
+
290
+ skcapstone daemon install --agent myagent --start
282
291
  """
283
- from ..systemd import install_service, systemd_available
292
+ import platform
284
293
 
285
- if not systemd_available():
286
- console.print("[red]systemd user session not available.[/]")
287
- console.print("[dim]This command requires a Linux system with systemd.[/]")
288
- raise SystemExit(1)
294
+ effective_agent = agent_name or os.environ.get("SKCAPSTONE_AGENT", "sovereign")
289
295
 
290
- console.print("\n[cyan]Installing skcapstone systemd service...[/]")
291
- result = install_service()
296
+ if platform.system() == "Darwin":
297
+ from ..launchd import install_service as launchd_install
292
298
 
293
- if result["installed"]:
294
- console.print("[green] Unit files installed.[/]")
295
- if result["enabled"]:
296
- console.print("[green] Service enabled at login.[/]")
297
- if result["started"]:
298
- console.print("[green] Service started.[/]")
299
- console.print()
299
+ console.print(f"\n[cyan]Installing launchd services for agent '{effective_agent}'...[/]")
300
+ result = launchd_install(agent_name=effective_agent, start=start)
301
+
302
+ if result["installed"]:
303
+ for svc in result.get("services", []):
304
+ status = "[green]loaded[/]" if svc.get("loaded") else "[green]installed[/]"
305
+ console.print(f" [green]✓[/] {svc['label']} — {status}")
306
+ console.print()
307
+ console.print("[dim] Manage: launchctl list | grep skcapstone[/]")
308
+ if not start:
309
+ console.print("[dim] Start: launchctl start com.skcapstone.daemon[/]")
310
+ console.print("[dim] Or re-run with --start to load immediately.[/]")
311
+ else:
312
+ console.print("[red]Installation failed. Check logs.[/]")
313
+ raise SystemExit(1)
314
+ console.print()
300
315
 
301
- if not result["installed"]:
302
- console.print("[red]Installation failed. Check logs.[/]")
316
+ elif platform.system() == "Linux":
317
+ from ..systemd import install_service, systemd_available
318
+
319
+ if not systemd_available():
320
+ console.print("[red]systemd user session not available.[/]")
321
+ console.print("[dim]This command requires a Linux system with systemd.[/]")
322
+ raise SystemExit(1)
323
+
324
+ console.print("\n[cyan]Installing skcapstone systemd service...[/]")
325
+ result = install_service(start=start)
326
+
327
+ if result["installed"]:
328
+ console.print("[green] Unit files installed.[/]")
329
+ if result["enabled"]:
330
+ console.print("[green] Service enabled at login.[/]")
331
+ if result.get("started"):
332
+ console.print("[green] Service started.[/]")
333
+ console.print()
334
+
335
+ if not result["installed"]:
336
+ console.print("[red]Installation failed. Check logs.[/]")
337
+ raise SystemExit(1)
338
+ else:
339
+ console.print(f"[red]Auto-start not supported on {platform.system()}.[/]")
303
340
  raise SystemExit(1)
304
341
 
305
342
  @daemon.command("uninstall")
306
343
  def daemon_uninstall():
307
- """Uninstall the systemd user service.
344
+ """Uninstall the system service.
308
345
 
309
- Stops, disables, and removes the unit files.
346
+ On Linux: stops, disables, and removes systemd unit files.
347
+ On macOS: unloads and removes launchd plist files.
310
348
 
311
349
  Examples:
312
350
 
313
351
  skcapstone daemon uninstall
314
352
  """
315
- from ..systemd import uninstall_service
353
+ import platform
316
354
 
317
- console.print("\n[cyan]Uninstalling skcapstone systemd service...[/]")
318
- result = uninstall_service()
355
+ if platform.system() == "Darwin":
356
+ from ..launchd import uninstall_service as launchd_uninstall
319
357
 
320
- if result["stopped"]:
321
- console.print("[green] Service stopped.[/]")
322
- if result["disabled"]:
323
- console.print("[green] Service disabled.[/]")
324
- if result["removed"]:
325
- console.print("[green] Unit files removed.[/]")
326
- console.print()
358
+ console.print("\n[cyan]Uninstalling skcapstone launchd services...[/]")
359
+ result = launchd_uninstall()
360
+
361
+ if result["stopped"]:
362
+ console.print("[green] Services unloaded.[/]")
363
+ if result["removed"]:
364
+ for label in result.get("services", []):
365
+ console.print(f" [green]✓[/] Removed {label}")
366
+ console.print()
367
+
368
+ elif platform.system() == "Linux":
369
+ from ..systemd import uninstall_service
370
+
371
+ console.print("\n[cyan]Uninstalling skcapstone systemd service...[/]")
372
+ result = uninstall_service()
373
+
374
+ if result["stopped"]:
375
+ console.print("[green] Service stopped.[/]")
376
+ if result["disabled"]:
377
+ console.print("[green] Service disabled.[/]")
378
+ if result["removed"]:
379
+ console.print("[green] Unit files removed.[/]")
380
+ console.print()
381
+
382
+ else:
383
+ console.print(f"[red]Not supported on {platform.system()}.[/]")
327
384
 
328
385
  @daemon.command("components")
329
386
  @click.option("--agent", default=None, help="Named agent to query.")
@@ -413,7 +470,10 @@ def register_daemon_commands(main: click.Group) -> None:
413
470
  @click.option("--lines", "-n", default=50, help="Number of lines (default: 50).")
414
471
  @click.option("--follow", "-f", is_flag=True, help="Show the command to follow logs live.")
415
472
  def daemon_logs(lines: int, follow: bool):
416
- """Show daemon logs from journald.
473
+ """Show daemon logs.
474
+
475
+ On Linux: reads from journald.
476
+ On macOS: reads from ~/.skcapstone/logs/ files.
417
477
 
418
478
  Examples:
419
479
 
@@ -423,14 +483,29 @@ def register_daemon_commands(main: click.Group) -> None:
423
483
 
424
484
  skcapstone daemon logs -f
425
485
  """
426
- from ..systemd import service_logs
486
+ import platform
427
487
 
428
- if follow:
429
- cmd = service_logs(follow=True)
430
- console.print(f"\n Run: [bold cyan]{cmd}[/]\n")
488
+ if platform.system() == "Darwin":
489
+ if follow:
490
+ log_path = Path.home() / ".skcapstone" / "logs" / "daemon.stdout.log"
491
+ console.print(f"\n Run: [bold cyan]tail -f {log_path}[/]\n")
492
+ else:
493
+ from ..launchd import service_logs
494
+ output = service_logs(lines=lines)
495
+ if output.strip():
496
+ click.echo(output)
497
+ else:
498
+ console.print("[dim]No logs found in ~/.skcapstone/logs/[/]")
499
+ console.print("[dim]Is the service installed? Run: skcapstone daemon install[/]")
431
500
  else:
432
- output = service_logs(lines=lines)
433
- if output.strip():
434
- click.echo(output)
501
+ from ..systemd import service_logs
502
+
503
+ if follow:
504
+ cmd = service_logs(follow=True)
505
+ console.print(f"\n Run: [bold cyan]{cmd}[/]\n")
435
506
  else:
436
- console.print("[dim]No logs found. Is the service installed?[/]")
507
+ output = service_logs(lines=lines)
508
+ if output.strip():
509
+ click.echo(output)
510
+ else:
511
+ console.print("[dim]No logs found. Is the service installed?[/]")