@smilintux/skcapstone 0.2.6 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/openclaw-plugin/src/index.ts +75 -4
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/install.ps1 +2 -1
- package/scripts/install.sh +2 -1
- package/src/skcapstone/__init__.py +1 -1
- package/src/skcapstone/cli/_common.py +5 -5
- package/src/skcapstone/cli/status.py +17 -11
- package/src/skcapstone/defaults/lumina/manifest.json +1 -1
- package/src/skcapstone/pillars/sync.py +11 -4
- package/src/skcapstone/register.py +8 -0
- package/systemd/skcapstone.service +5 -6
package/README.md
CHANGED
|
@@ -165,6 +165,66 @@ skcapstone status
|
|
|
165
165
|
|
|
166
166
|
---
|
|
167
167
|
|
|
168
|
+
## Windows Quickstart
|
|
169
|
+
|
|
170
|
+
SKCapstone runs natively on Windows. The installer creates a virtualenv at `%LOCALAPPDATA%\skenv` and adds its `Scripts` directory to your user PATH.
|
|
171
|
+
|
|
172
|
+
### Prerequisites
|
|
173
|
+
|
|
174
|
+
- **Python 3.10+** — [python.org/downloads](https://www.python.org/downloads/) (check "Add to PATH" during install)
|
|
175
|
+
- **Git for Windows** — [git-scm.com](https://git-scm.com/download/win)
|
|
176
|
+
- **Syncthing** (optional) — for cross-device sync ([syncthing.net](https://syncthing.net/downloads/))
|
|
177
|
+
|
|
178
|
+
### Install
|
|
179
|
+
|
|
180
|
+
```powershell
|
|
181
|
+
# Clone and install (creates %LOCALAPPDATA%\skenv venv)
|
|
182
|
+
git clone https://github.com/smilintux-org/skcapstone.git
|
|
183
|
+
cd skcapstone
|
|
184
|
+
.\scripts\install.ps1
|
|
185
|
+
|
|
186
|
+
# The installer adds %LOCALAPPDATA%\skenv\Scripts to your user PATH.
|
|
187
|
+
# Restart your terminal for PATH changes to take effect.
|
|
188
|
+
|
|
189
|
+
# Initialize your agent
|
|
190
|
+
skcapstone init --name "YourAgent"
|
|
191
|
+
|
|
192
|
+
# Check status
|
|
193
|
+
skcapstone status
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Syncthing Sync (Optional)
|
|
197
|
+
|
|
198
|
+
To sync your agent across devices (e.g., Windows desktop + Linux server):
|
|
199
|
+
|
|
200
|
+
1. Install [Syncthing](https://syncthing.net/downloads/) on both machines
|
|
201
|
+
2. Share the `%USERPROFILE%\.skcapstone` folder between devices
|
|
202
|
+
3. Agent state (memories, identity, trust, seeds) syncs automatically
|
|
203
|
+
|
|
204
|
+
### OpenClaw Integration
|
|
205
|
+
|
|
206
|
+
If you're running [OpenClaw](https://github.com/smilintux-org/openclaw), the SK* plugins register automatically during install:
|
|
207
|
+
|
|
208
|
+
```powershell
|
|
209
|
+
# Re-register if needed
|
|
210
|
+
skcapstone register
|
|
211
|
+
|
|
212
|
+
# Verify plugins are loaded in OpenClaw
|
|
213
|
+
# Plugins provide tools for status, rehydration, coordination,
|
|
214
|
+
# soul management, and agent profiles directly in OpenClaw agents.
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Task Scheduler (Background Service)
|
|
218
|
+
|
|
219
|
+
On Windows, the daemon runs via Task Scheduler instead of systemd:
|
|
220
|
+
|
|
221
|
+
```powershell
|
|
222
|
+
# Install the scheduled task (runs at logon)
|
|
223
|
+
.\scripts\windows\install-tasks.ps1
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
168
228
|
## DID Tools
|
|
169
229
|
|
|
170
230
|
SKCapstone exposes a set of **Decentralized Identifier (DID)** MCP tools for sovereign identity management. These tools are available to Claude Code and other MCP clients through the `mcp_tools/did_tools.py` module.
|
|
@@ -223,6 +283,7 @@ DIDs are organized in three tiers of trust and discoverability:
|
|
|
223
283
|
| **SKComm** | Communication — Encrypted channels between agents |
|
|
224
284
|
| **SKChat** | Chat — AI-native encrypted messaging |
|
|
225
285
|
| **SKForge** | Generation — Blueprint creation with agent context |
|
|
286
|
+
| **SKSeed** | Epistemic rigor — Steel man collider, truth alignment, memory audit |
|
|
226
287
|
| **SKStacks** | Infrastructure — Self-hosted deployment patterns |
|
|
227
288
|
|
|
228
289
|
---
|
|
@@ -16,16 +16,28 @@ const SKCAPSTONE_BIN = process.env.SKCAPSTONE_BIN || "skcapstone";
|
|
|
16
16
|
const SKMEMORY_BIN = process.env.SKMEMORY_BIN || "skmemory";
|
|
17
17
|
const SKCAPSTONE_AGENT = process.env.SKCAPSTONE_AGENT || "lumina";
|
|
18
18
|
const EXEC_TIMEOUT = 60_000;
|
|
19
|
+
const IS_WIN = process.platform === "win32";
|
|
19
20
|
|
|
20
|
-
function
|
|
21
|
+
function skenvPath(): string {
|
|
22
|
+
if (IS_WIN) {
|
|
23
|
+
const local = process.env.LOCALAPPDATA || "";
|
|
24
|
+
return `${local}\\skenv\\Scripts`;
|
|
25
|
+
}
|
|
26
|
+
const home = process.env.HOME || "";
|
|
27
|
+
return `${home}/.local/bin:${home}/.skenv/bin`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function runCli(bin: string, args: string, agentOverride?: string): { ok: boolean; output: string } {
|
|
31
|
+
const sep = IS_WIN ? ";" : ":";
|
|
32
|
+
const agent = agentOverride || SKCAPSTONE_AGENT;
|
|
21
33
|
try {
|
|
22
34
|
const raw = execSync(`${bin} ${args}`, {
|
|
23
35
|
encoding: "utf-8",
|
|
24
36
|
timeout: EXEC_TIMEOUT,
|
|
25
37
|
env: {
|
|
26
38
|
...process.env,
|
|
27
|
-
SKCAPSTONE_AGENT,
|
|
28
|
-
PATH: `${
|
|
39
|
+
SKCAPSTONE_AGENT: agent,
|
|
40
|
+
PATH: `${skenvPath()}${sep}${process.env.PATH}`,
|
|
29
41
|
},
|
|
30
42
|
}).trim();
|
|
31
43
|
return { ok: true, output: raw };
|
|
@@ -299,6 +311,62 @@ function createSKCapstoneSoulShowTool() {
|
|
|
299
311
|
};
|
|
300
312
|
}
|
|
301
313
|
|
|
314
|
+
function createSKCapstoneAgentListTool() {
|
|
315
|
+
return {
|
|
316
|
+
name: "skcapstone_agent_list",
|
|
317
|
+
label: "SKCapstone Agent List",
|
|
318
|
+
description:
|
|
319
|
+
"List all skcapstone agent profiles available on this node. Each profile has its own identity, memories, soul, and trust state. Use this to discover which agents can be loaded.",
|
|
320
|
+
parameters: { type: "object", properties: {} },
|
|
321
|
+
async execute() {
|
|
322
|
+
const result = runCli(SKCAPSTONE_BIN, "agents list --json");
|
|
323
|
+
return textResult(result.output);
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function createSKCapstoneAgentStatusTool() {
|
|
329
|
+
return {
|
|
330
|
+
name: "skcapstone_agent_status",
|
|
331
|
+
label: "SKCapstone Agent Status",
|
|
332
|
+
description:
|
|
333
|
+
"Show the status of a specific skcapstone agent profile — identity, memories, trust, sync state. Use this to load a different agent's context into the current OpenClaw session.",
|
|
334
|
+
parameters: {
|
|
335
|
+
type: "object",
|
|
336
|
+
required: ["agent"],
|
|
337
|
+
properties: {
|
|
338
|
+
agent: { type: "string", description: "Agent name (e.g. 'lumina', 'opus', 'jarvis')." },
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
342
|
+
const agent = String(params.agent ?? "");
|
|
343
|
+
const result = runCli(SKCAPSTONE_BIN, `status --agent ${escapeShellArg(agent)}`, agent);
|
|
344
|
+
return textResult(result.output);
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function createSKCapstoneAgentCreateTool() {
|
|
350
|
+
return {
|
|
351
|
+
name: "skcapstone_agent_create",
|
|
352
|
+
label: "SKCapstone Create Agent",
|
|
353
|
+
description:
|
|
354
|
+
"Create a new skcapstone agent profile with its own identity, memory store, and sync folder. The profile will immediately begin syncing via Syncthing to all connected nodes.",
|
|
355
|
+
parameters: {
|
|
356
|
+
type: "object",
|
|
357
|
+
required: ["name"],
|
|
358
|
+
properties: {
|
|
359
|
+
name: { type: "string", description: "Agent name (lowercase, e.g. 'casey', 'nova')." },
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
363
|
+
const name = String(params.name ?? "").toLowerCase();
|
|
364
|
+
const result = runCli(SKCAPSTONE_BIN, `init --name ${escapeShellArg(name)} --agent ${escapeShellArg(name)}`);
|
|
365
|
+
return textResult(result.output);
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
302
370
|
// ── Plugin registration ─────────────────────────────────────────────────
|
|
303
371
|
|
|
304
372
|
const skcapstonePlugin = {
|
|
@@ -324,6 +392,9 @@ const skcapstonePlugin = {
|
|
|
324
392
|
createSKCapstoneSoulSwapTool(),
|
|
325
393
|
createSKCapstoneSoulStatusTool(),
|
|
326
394
|
createSKCapstoneSoulShowTool(),
|
|
395
|
+
createSKCapstoneAgentListTool(),
|
|
396
|
+
createSKCapstoneAgentStatusTool(),
|
|
397
|
+
createSKCapstoneAgentCreateTool(),
|
|
327
398
|
];
|
|
328
399
|
|
|
329
400
|
for (const tool of tools) {
|
|
@@ -344,7 +415,7 @@ const skcapstonePlugin = {
|
|
|
344
415
|
},
|
|
345
416
|
});
|
|
346
417
|
|
|
347
|
-
api.logger.info?.(
|
|
418
|
+
api.logger.info?.(`SKCapstone plugin registered (17 tools + /skcapstone command) [agent=${SKCAPSTONE_AGENT}]`);
|
|
348
419
|
},
|
|
349
420
|
};
|
|
350
421
|
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "skcapstone"
|
|
7
|
-
version = "0.2
|
|
7
|
+
version = "0.4.2"
|
|
8
8
|
description = "Sovereign Agent Framework — conscious AI through identity, trust, memory, and security"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "GPL-3.0-or-later"}
|
package/scripts/install.ps1
CHANGED
|
@@ -162,6 +162,7 @@ Install-Pkg -Name 'skchat-sovereign' -Extras 'all' -Paths @((Join
|
|
|
162
162
|
Install-Pkg -Name 'skseal' -Extras '' -Paths @((Join-Path $ParentDir 'skseal'))
|
|
163
163
|
Install-Pkg -Name 'skskills' -Extras '' -Paths @((Join-Path $ParentDir 'skskills'))
|
|
164
164
|
Install-Pkg -Name 'sksecurity' -Extras '' -Paths @((Join-Path $ParentDir 'sksecurity'))
|
|
165
|
+
Install-Pkg -Name 'skseed' -Extras '' -Paths @((Join-Path $PillarDir 'skseed'), (Join-Path $ParentDir 'skseed'))
|
|
165
166
|
|
|
166
167
|
# ---------------------------------------------------------------------------
|
|
167
168
|
# Step 4: Dev tools (optional)
|
|
@@ -248,6 +249,6 @@ if ($failures -eq 0) {
|
|
|
248
249
|
Write-Host "=== Installation complete with $failures warning(s) ===" -ForegroundColor Yellow
|
|
249
250
|
}
|
|
250
251
|
Write-Host ''
|
|
251
|
-
Write-Host "Commands available: skcomm, skcapstone, capauth, skchat, skseal, skmemory, skskills, sksecurity"
|
|
252
|
+
Write-Host "Commands available: skcomm, skcapstone, capauth, skchat, skseal, skmemory, skskills, sksecurity, skseed"
|
|
252
253
|
Write-Host "Venv location: $SKENV"
|
|
253
254
|
Write-Host "To activate: & $SKENV\Scripts\Activate.ps1"
|
package/scripts/install.sh
CHANGED
|
@@ -118,6 +118,7 @@ install_pkg "skchat-sovereign" "all" "$PARENT/skchat"
|
|
|
118
118
|
install_pkg "skseal" "" "$PARENT/skseal"
|
|
119
119
|
install_pkg "skskills" "" "$PARENT/skskills"
|
|
120
120
|
install_pkg "sksecurity" "" "$PARENT/sksecurity"
|
|
121
|
+
install_pkg "skseed" "" "$PILLAR/skseed $PARENT/skseed"
|
|
121
122
|
|
|
122
123
|
# ---------------------------------------------------------------------------
|
|
123
124
|
# Step 4: Dev tools (optional)
|
|
@@ -180,6 +181,6 @@ else
|
|
|
180
181
|
echo "=== Installation complete with $failures warning(s) ==="
|
|
181
182
|
fi
|
|
182
183
|
echo ""
|
|
183
|
-
echo "Commands available: skcomm, skcapstone, capauth, skchat, skseal, skmemory, skskills, sksecurity"
|
|
184
|
+
echo "Commands available: skcomm, skcapstone, capauth, skchat, skseal, skmemory, skskills, sksecurity, skseed"
|
|
184
185
|
echo "Venv location: $SKENV"
|
|
185
186
|
echo "To activate: source $SKENV/bin/activate"
|
|
@@ -46,17 +46,17 @@ def resolve_agent_home(agent: str) -> Path:
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def apply_agent_override(agent: str) -> None:
|
|
49
|
-
"""
|
|
49
|
+
"""Set the active agent name when --agent is specified.
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
Only mutates SKCAPSTONE_AGENT so that agent_home() resolves to
|
|
52
|
+
the correct per-agent directory. Does NOT mutate AGENT_HOME
|
|
53
|
+
(the shared root) — that would break agent_home() (double
|
|
54
|
+
nesting) and shared_home() (wrong path).
|
|
53
55
|
|
|
54
56
|
Args:
|
|
55
57
|
agent: Agent name from the --agent CLI option or env var.
|
|
56
58
|
"""
|
|
57
59
|
if agent:
|
|
58
|
-
new_home = str(Path(SHARED_ROOT) / "agents" / agent)
|
|
59
|
-
_pkg.AGENT_HOME = new_home
|
|
60
60
|
_pkg.SKCAPSTONE_AGENT = agent
|
|
61
61
|
os.environ["SKCAPSTONE_AGENT"] = agent
|
|
62
62
|
|
|
@@ -195,19 +195,25 @@ def register_status_commands(main: click.Group) -> None:
|
|
|
195
195
|
"""Register all status/overview commands on the main CLI group."""
|
|
196
196
|
|
|
197
197
|
@main.command()
|
|
198
|
-
@click.option("--home", default=
|
|
199
|
-
|
|
198
|
+
@click.option("--home", default=None, help="Agent home directory.", type=click.Path())
|
|
199
|
+
@click.option("--agent", default=None, help="Agent name (e.g. opus, lumina).")
|
|
200
|
+
def status(home: Optional[str], agent: Optional[str]):
|
|
200
201
|
"""Show the sovereign agent's current state."""
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if not home_path.exists():
|
|
204
|
-
console.print(
|
|
205
|
-
"[bold red]No agent found.[/] "
|
|
206
|
-
"Run [bold]skcapstone init --name \"YourAgent\"[/] first."
|
|
207
|
-
)
|
|
208
|
-
sys.exit(1)
|
|
202
|
+
from .. import SKCAPSTONE_AGENT as default_agent
|
|
209
203
|
|
|
210
|
-
|
|
204
|
+
if home:
|
|
205
|
+
home_path = Path(home).expanduser()
|
|
206
|
+
if not home_path.exists():
|
|
207
|
+
console.print(
|
|
208
|
+
"[bold red]No agent found.[/] "
|
|
209
|
+
"Run [bold]skcapstone init --name \"YourAgent\"[/] first."
|
|
210
|
+
)
|
|
211
|
+
sys.exit(1)
|
|
212
|
+
runtime = get_runtime(home=home_path)
|
|
213
|
+
else:
|
|
214
|
+
agent_name = agent or default_agent
|
|
215
|
+
runtime = get_runtime(agent_name=agent_name)
|
|
216
|
+
home_path = runtime.home
|
|
211
217
|
m = runtime.manifest
|
|
212
218
|
|
|
213
219
|
console.print()
|
|
@@ -72,7 +72,7 @@ def initialize_sync(home: Path, config: Optional[SyncConfig] = None) -> SyncStat
|
|
|
72
72
|
else:
|
|
73
73
|
state.status = PillarStatus.DEGRADED
|
|
74
74
|
|
|
75
|
-
state.seed_count = _count_seeds(sync_dir)
|
|
75
|
+
state.seed_count = _count_seeds(sync_dir, home=home)
|
|
76
76
|
return state
|
|
77
77
|
|
|
78
78
|
|
|
@@ -396,7 +396,7 @@ def discover_sync(home: Path) -> SyncState:
|
|
|
396
396
|
state = SyncState(
|
|
397
397
|
transport=transport,
|
|
398
398
|
sync_path=sync_dir,
|
|
399
|
-
seed_count=_count_seeds(sync_dir),
|
|
399
|
+
seed_count=_count_seeds(sync_dir, home=home),
|
|
400
400
|
status=PillarStatus.ACTIVE,
|
|
401
401
|
)
|
|
402
402
|
|
|
@@ -483,8 +483,8 @@ def _get_hostname() -> str:
|
|
|
483
483
|
return socket.gethostname()
|
|
484
484
|
|
|
485
485
|
|
|
486
|
-
def _count_seeds(sync_dir: Path) -> int:
|
|
487
|
-
"""Count seed files across
|
|
486
|
+
def _count_seeds(sync_dir: Path, home: Optional[Path] = None) -> int:
|
|
487
|
+
"""Count seed files across sync subdirs and the agent seeds directory."""
|
|
488
488
|
count = 0
|
|
489
489
|
for subdir in ("outbox", "inbox", "archive"):
|
|
490
490
|
d = sync_dir / subdir
|
|
@@ -492,6 +492,13 @@ def _count_seeds(sync_dir: Path) -> int:
|
|
|
492
492
|
count += sum(
|
|
493
493
|
1 for f in d.iterdir() if f.name.endswith(SEED_EXTENSION) or f.suffix == ".gpg"
|
|
494
494
|
)
|
|
495
|
+
# Also count seeds in the agent's seeds/ directory
|
|
496
|
+
if home is not None:
|
|
497
|
+
seeds_dir = home / "seeds"
|
|
498
|
+
if seeds_dir.is_dir():
|
|
499
|
+
count += sum(
|
|
500
|
+
1 for f in seeds_dir.iterdir() if f.name.endswith(SEED_EXTENSION)
|
|
501
|
+
)
|
|
495
502
|
return count
|
|
496
503
|
|
|
497
504
|
|
|
@@ -97,6 +97,13 @@ def _build_package_registry(workspace: Optional[Path] = None) -> list[dict]:
|
|
|
97
97
|
"mcp_env": None,
|
|
98
98
|
"openclaw_plugin_path": workspace / "pillar-repos" / "sksecurity" / "openclaw-plugin" / "src" / "index.ts",
|
|
99
99
|
},
|
|
100
|
+
{
|
|
101
|
+
"name": "skseed",
|
|
102
|
+
"mcp_cmd": None,
|
|
103
|
+
"mcp_args": None,
|
|
104
|
+
"mcp_env": None,
|
|
105
|
+
"openclaw_plugin_path": workspace / "pillar-repos" / "skseed" / "openclaw-plugin" / "src" / "index.ts",
|
|
106
|
+
},
|
|
100
107
|
{
|
|
101
108
|
"name": "skgit",
|
|
102
109
|
"mcp_cmd": "node",
|
|
@@ -119,6 +126,7 @@ _PILLAR_DIR_MAP: dict[str, Optional[str]] = {
|
|
|
119
126
|
"capauth": "capauth",
|
|
120
127
|
"cloud9": "cloud9-python",
|
|
121
128
|
"sksecurity": "sksecurity",
|
|
129
|
+
"skseed": "skseed",
|
|
122
130
|
"skgit": None, # skill dir only, no pillar repo
|
|
123
131
|
}
|
|
124
132
|
|
|
@@ -5,19 +5,18 @@ After=network-online.target ollama.service syncthing.service
|
|
|
5
5
|
Wants=network-online.target
|
|
6
6
|
|
|
7
7
|
[Service]
|
|
8
|
-
Type=
|
|
9
|
-
ExecStart
|
|
10
|
-
ExecStop
|
|
8
|
+
Type=simple
|
|
9
|
+
ExecStart=%h/.skenv/bin/skcapstone daemon start --foreground
|
|
10
|
+
ExecStop=%h/.skenv/bin/skcapstone daemon stop
|
|
11
11
|
ExecReload=/bin/kill -HUP $MAINPID
|
|
12
12
|
Restart=on-failure
|
|
13
13
|
RestartSec=10
|
|
14
|
-
# Watchdog: daemon must call sd_notify("WATCHDOG=1") at least every 5 minutes
|
|
15
|
-
WatchdogSec=300
|
|
16
14
|
# Cap memory to prevent OOM from large model loading
|
|
17
15
|
MemoryMax=4G
|
|
18
16
|
# Keep Ollama models warm for 5 minutes between requests
|
|
19
17
|
Environment=PYTHONUNBUFFERED=1
|
|
20
18
|
Environment=OLLAMA_KEEP_ALIVE=5m
|
|
19
|
+
Environment=SKCAPSTONE_AGENT=lumina
|
|
21
20
|
# Journal logging
|
|
22
21
|
StandardOutput=journal
|
|
23
22
|
StandardError=journal
|
|
@@ -27,7 +26,7 @@ SyslogIdentifier=skcapstone
|
|
|
27
26
|
NoNewPrivileges=true
|
|
28
27
|
ProtectSystem=strict
|
|
29
28
|
ProtectHome=read-only
|
|
30
|
-
ReadWritePaths=%h/.skcapstone %h/.
|
|
29
|
+
ReadWritePaths=%h/.skcapstone %h/.skenv %h/.capauth %h/.cloud9 %h/.skcomm %h/.skchat
|
|
31
30
|
PrivateTmp=true
|
|
32
31
|
ProtectKernelTunables=true
|
|
33
32
|
ProtectControlGroups=true
|