@wipcomputer/wip-ldm-os 0.4.82-alpha.1 → 0.4.84
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 +2 -0
- package/SKILL.md +1 -1
- package/bin/ldm.js +140 -0
- package/docs/skills/README.md +2 -0
- package/docs/universal-installer/README.md +1 -1
- package/docs/universal-installer/SPEC.md +189 -16
- package/docs/universal-installer/TECHNICAL.md +84 -29
- package/lib/bin-manifest.mjs +257 -0
- package/package.json +35 -2
- package/scripts/test-bin-manifest.mjs +282 -0
- package/scripts/test-doctor-cron-target.mjs +172 -0
- package/scripts/test-ldm-install-preserves-foreign-bin.mjs +112 -0
- package/scripts/validate-bin-manifest.mjs +41 -0
- package/src/hosted-mcp/app/codex-remote-control/index.html +254 -0
- package/src/hosted-mcp/app/login.html +176 -0
- package/src/hosted-mcp/app/pair.html +118 -0
- package/src/hosted-mcp/nginx/wip.computer.conf +88 -0
- package/src/hosted-mcp/package-lock.json +22 -0
- package/src/hosted-mcp/package.json +1 -0
- package/src/hosted-mcp/server.mjs +418 -10
package/README.md
CHANGED
|
@@ -96,6 +96,8 @@ The OS connects your AIs. Add-ons are what they actually use. Each one is a full
|
|
|
96
96
|
|
|
97
97
|
**OpenClaw**
|
|
98
98
|
- Open-source agent runtime. Run AI agents 24/7 with identity, memory, and tool access. The existence proof for LDM OS.
|
|
99
|
+
- WIP contributions accepted upstream: `before_message_write` plugin hook (#18197), Codex app-server final chat events (#70815 -> maintainer PR #71293), memory-core seed cache streaming/yield (#73067 -> maintainer PR #73118), and fallback vector top-K streaming (#73069 -> maintainer PR #73100).
|
|
100
|
+
- Submitted / superseded: symlink plugin discovery fix (#45744; bug confirmed, superseded by #69971, not landed as submitted).
|
|
99
101
|
- [Read more about OpenClaw](https://github.com/openclaw/openclaw)
|
|
100
102
|
|
|
101
103
|
[See all skills](docs/skills/README.md)
|
package/SKILL.md
CHANGED
|
@@ -9,7 +9,7 @@ license: MIT
|
|
|
9
9
|
compatibility: Requires git, npm, node. Node.js 18+.
|
|
10
10
|
metadata:
|
|
11
11
|
display-name: "LDM OS"
|
|
12
|
-
version: "0.4.
|
|
12
|
+
version: "0.4.84"
|
|
13
13
|
homepage: "https://github.com/wipcomputer/wip-ldm-os"
|
|
14
14
|
author: "Parker Todd Brooks"
|
|
15
15
|
category: infrastructure
|
package/bin/ldm.js
CHANGED
|
@@ -1935,6 +1935,36 @@ async function cmdInstallCatalog() {
|
|
|
1935
1935
|
console.log(` + Migrated ${migrated} registry entries to v2 format (source info added)`);
|
|
1936
1936
|
}
|
|
1937
1937
|
|
|
1938
|
+
// Aggregate the bin ownership manifest BEFORE seedLocalCatalog,
|
|
1939
|
+
// deployBridge, deployScripts, and the heal walk run. If two
|
|
1940
|
+
// declarers claim the same file in ~/.ldm/bin/ we cannot safely
|
|
1941
|
+
// decide ownership, so install must abort before catalog.json,
|
|
1942
|
+
// bridge files, or any ~/.ldm/bin/ shim is written. See
|
|
1943
|
+
// ai/product/plans-prds/current/2026-04-28--cc-mini--ldm-bin-ownership-manifest-design.md
|
|
1944
|
+
const { aggregateBinManifest, healBinManifest } = await import('../lib/bin-manifest.mjs');
|
|
1945
|
+
const ldmCliRoot = join(__dirname, '..');
|
|
1946
|
+
const binManifestRegistry = readJSON(REGISTRY_PATH) || { extensions: {} };
|
|
1947
|
+
const manifest = aggregateBinManifest({
|
|
1948
|
+
ldmCliRoot,
|
|
1949
|
+
extensionsRoot: LDM_EXTENSIONS,
|
|
1950
|
+
binDir: join(LDM_ROOT, 'bin'),
|
|
1951
|
+
registry: binManifestRegistry,
|
|
1952
|
+
});
|
|
1953
|
+
if (manifest.conflicts.length > 0) {
|
|
1954
|
+
console.log('');
|
|
1955
|
+
console.log(' x bin manifest conflict ... aborting before seedLocalCatalog/deployBridge/deployScripts run:');
|
|
1956
|
+
for (const c of manifest.conflicts) {
|
|
1957
|
+
console.log(` "${c.name}" claimed by ${c.declarers.length} declarers:`);
|
|
1958
|
+
for (const d of c.declarers) {
|
|
1959
|
+
console.log(` ${d.declarer}: ${d.sourcePath.replace(HOME, '~')}`);
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
console.log('');
|
|
1963
|
+
console.log(' Resolve ownership in the package manifests, then re-run.');
|
|
1964
|
+
installLog(`ldm install aborted: ${manifest.conflicts.length} bin manifest conflict(s)`);
|
|
1965
|
+
process.exit(1);
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1938
1968
|
// Seed local catalog if missing (#262)
|
|
1939
1969
|
if (seedLocalCatalog()) {
|
|
1940
1970
|
console.log(` + catalog.json seeded to ~/.ldm/catalog.json`);
|
|
@@ -1950,6 +1980,24 @@ async function cmdInstallCatalog() {
|
|
|
1950
1980
|
deployDocs();
|
|
1951
1981
|
deployRules();
|
|
1952
1982
|
|
|
1983
|
+
// Manifest-driven self-heal. deployScripts() above wrote LDM CLI's own
|
|
1984
|
+
// *.sh files; this pass covers the rest of the manifest (LDM CLI files
|
|
1985
|
+
// deployed elsewhere, e.g. process-monitor.sh, plus extension-owned
|
|
1986
|
+
// shims like crystal-capture.sh). Read-only entries that already match
|
|
1987
|
+
// the manifest are no-ops.
|
|
1988
|
+
if (manifest.entries.length > 0) {
|
|
1989
|
+
const heal = healBinManifest(manifest.entries, { heal: !DRY_RUN });
|
|
1990
|
+
for (const e of heal.healed) {
|
|
1991
|
+
console.log(` + Restored ${e.name} from ${e.sourcePath.replace(HOME, '~')} (declarer: ${e.declarer})`);
|
|
1992
|
+
}
|
|
1993
|
+
for (const f of heal.failed) {
|
|
1994
|
+
console.log(` ! ${f.entry.name}: ${f.reason} (declarer: ${f.entry.declarer})`);
|
|
1995
|
+
}
|
|
1996
|
+
if (heal.ok.length > 0 && heal.healed.length === 0 && heal.failed.length === 0) {
|
|
1997
|
+
console.log(` + bin manifest: ${heal.ok.length} entr${heal.ok.length === 1 ? 'y' : 'ies'} verified`);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
|
|
1953
2001
|
// Check backup configuration
|
|
1954
2002
|
checkBackupHealth();
|
|
1955
2003
|
|
|
@@ -2947,6 +2995,98 @@ async function cmdDoctor() {
|
|
|
2947
2995
|
}
|
|
2948
2996
|
}
|
|
2949
2997
|
|
|
2998
|
+
// 3c. Cron target health (parallel to crystal doctor's checkCaptureShim)
|
|
2999
|
+
//
|
|
3000
|
+
// When an extension wires a shim into ~/.ldm/bin/ via cron, the cron
|
|
3001
|
+
// line is sticky: it keeps firing even if the shim file goes missing
|
|
3002
|
+
// for any reason. crystal doctor catches this for crystal-capture.sh,
|
|
3003
|
+
// but any cron line referencing ~/.ldm/bin/<file> is at the same risk
|
|
3004
|
+
// class. Walk the crontab and surface broken bin-targeted entries
|
|
3005
|
+
// explicitly. Read-only by default; --fix restores known shims from
|
|
3006
|
+
// their extension dist when the canonical source is present on disk.
|
|
3007
|
+
{
|
|
3008
|
+
const ldmBinPrefix = join(LDM_ROOT, 'bin') + '/';
|
|
3009
|
+
let crontab = '';
|
|
3010
|
+
try {
|
|
3011
|
+
crontab = execSync('crontab -l 2>/dev/null', { encoding: 'utf-8' });
|
|
3012
|
+
} catch {}
|
|
3013
|
+
function expandTilde(p) {
|
|
3014
|
+
return p.startsWith('~') ? join(HOME, p.slice(1)) : p;
|
|
3015
|
+
}
|
|
3016
|
+
function extractCronTarget(line) {
|
|
3017
|
+
const trimmed = line.trim();
|
|
3018
|
+
if (!trimmed.startsWith('*')) return null;
|
|
3019
|
+
const tokens = trimmed.split(/\s+/);
|
|
3020
|
+
if (tokens.length < 6) return null;
|
|
3021
|
+
const expanded = expandTilde(tokens[5]);
|
|
3022
|
+
return expanded.startsWith(ldmBinPrefix) ? expanded : null;
|
|
3023
|
+
}
|
|
3024
|
+
const cronTargets = new Map(); // path → ['missing'|'not executable']
|
|
3025
|
+
const seenTargets = new Set(); // every bin-targeted path we found
|
|
3026
|
+
for (const line of crontab.split('\n')) {
|
|
3027
|
+
const expanded = extractCronTarget(line);
|
|
3028
|
+
if (!expanded) continue;
|
|
3029
|
+
seenTargets.add(expanded);
|
|
3030
|
+
if (cronTargets.has(expanded)) continue;
|
|
3031
|
+
const problems = [];
|
|
3032
|
+
if (!existsSync(expanded)) {
|
|
3033
|
+
problems.push('missing');
|
|
3034
|
+
} else if ((statSync(expanded).mode & 0o111) === 0) {
|
|
3035
|
+
problems.push('not executable');
|
|
3036
|
+
}
|
|
3037
|
+
if (problems.length > 0) cronTargets.set(expanded, problems);
|
|
3038
|
+
}
|
|
3039
|
+
if (cronTargets.size > 0) {
|
|
3040
|
+
// Manifest-driven lookup. Aggregates LDM CLI's wipLdmOs.binFiles
|
|
3041
|
+
// and every registered extension's openclaw.plugin.json#binFiles.
|
|
3042
|
+
// If aggregation reports a conflict, the lookup stays empty and
|
|
3043
|
+
// each broken target becomes "owner unknown" until the conflict
|
|
3044
|
+
// is resolved at the package-manifest layer.
|
|
3045
|
+
const { aggregateBinManifest } = await import('../lib/bin-manifest.mjs');
|
|
3046
|
+
const drManifest = aggregateBinManifest({
|
|
3047
|
+
ldmCliRoot: join(__dirname, '..'),
|
|
3048
|
+
extensionsRoot: LDM_EXTENSIONS,
|
|
3049
|
+
binDir: join(LDM_ROOT, 'bin'),
|
|
3050
|
+
registry: readJSON(REGISTRY_PATH) || { extensions: {} },
|
|
3051
|
+
});
|
|
3052
|
+
const knownSources = {};
|
|
3053
|
+
const declarers = {};
|
|
3054
|
+
if (drManifest.conflicts.length === 0) {
|
|
3055
|
+
for (const e of drManifest.entries) {
|
|
3056
|
+
knownSources[e.name] = e.sourcePath;
|
|
3057
|
+
declarers[e.name] = e.declarer;
|
|
3058
|
+
}
|
|
3059
|
+
} else {
|
|
3060
|
+
console.log(` ! bin manifest conflict: ${drManifest.conflicts.length}; restore disabled until resolved`);
|
|
3061
|
+
}
|
|
3062
|
+
let healed = 0;
|
|
3063
|
+
for (const [path, problems] of cronTargets) {
|
|
3064
|
+
const detail = problems.join(', ');
|
|
3065
|
+
console.log(` ! cron target ${detail}: ${path}`);
|
|
3066
|
+
const base = basename(path);
|
|
3067
|
+
const src = knownSources[base];
|
|
3068
|
+
if (FIX_FLAG && src && existsSync(src)) {
|
|
3069
|
+
try {
|
|
3070
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
3071
|
+
cpSync(src, path);
|
|
3072
|
+
chmodSync(path, 0o755);
|
|
3073
|
+
console.log(` + Restored ${base} from ${src.replace(HOME, '~')}`);
|
|
3074
|
+
healed++;
|
|
3075
|
+
} catch (e) {
|
|
3076
|
+
console.log(` Restore failed: ${e.message}`);
|
|
3077
|
+
}
|
|
3078
|
+
} else if (src) {
|
|
3079
|
+
console.log(` Run: ldm doctor --fix to restore from ${src.replace(HOME, '~')}`);
|
|
3080
|
+
} else {
|
|
3081
|
+
console.log(` Owner unknown; consult the extension that registered this cron entry`);
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
issues += cronTargets.size - healed;
|
|
3085
|
+
} else if (seenTargets.size > 0) {
|
|
3086
|
+
console.log(` + Cron targets under ~/.ldm/bin/: ${seenTargets.size} entr${seenTargets.size === 1 ? 'y' : 'ies'}, all exist and executable`);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
|
|
2950
3090
|
// 4. Check sacred locations
|
|
2951
3091
|
const sacred = [
|
|
2952
3092
|
{ path: join(LDM_ROOT, 'memory'), label: 'memory/' },
|
package/docs/skills/README.md
CHANGED
|
@@ -31,6 +31,8 @@ Your AIs are only as powerful as what you give them. Here's everything available
|
|
|
31
31
|
|
|
32
32
|
**OpenClaw**
|
|
33
33
|
- Open-source agent runtime. Run AI agents 24/7 with identity, memory, and tool access. The existence proof for LDM OS.
|
|
34
|
+
- WIP contributions accepted upstream: `before_message_write` plugin hook (#18197), Codex app-server final chat events (#70815 -> maintainer PR #71293), memory-core seed cache streaming/yield (#73067 -> maintainer PR #73118), and fallback vector top-K streaming (#73069 -> maintainer PR #73100).
|
|
35
|
+
- Submitted / superseded: symlink plugin discovery fix (#45744; bug confirmed, superseded by #69971, not landed as submitted).
|
|
34
36
|
- [Read more about OpenClaw](https://github.com/openclaw/openclaw)
|
|
35
37
|
|
|
36
38
|
## Identity
|
|
@@ -68,7 +68,7 @@ If I say yes, run: ldm install --dry-run
|
|
|
68
68
|
Show me exactly what will change. Don't install anything until I say "install".
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
See [TECHNICAL.md](TECHNICAL.md) for sensors/actuators, the interface table, and real examples.
|
|
71
|
+
See [SPEC.md](SPEC.md) for the architecture layers, the **eight interfaces** (CLI, Module, MCP local stdio, Remote MCP, OpenClaw Plugin, Skill, Claude Code Hook, Claude Code Plugin), the install spec URL convention, track flags (alpha/beta), and the `agent.txt` distinction. See [TECHNICAL.md](TECHNICAL.md) for sensors/actuators, the interface table, and real examples.
|
|
72
72
|
|
|
73
73
|
---
|
|
74
74
|
|
|
@@ -4,7 +4,29 @@ Every tool is a sensor, an actuator, or both. Every tool should be accessible th
|
|
|
4
4
|
|
|
5
5
|
This is the spec.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Architecture Layers
|
|
8
|
+
|
|
9
|
+
Five layers. Each one does one job. Together they let any AI safely consume any product.
|
|
10
|
+
|
|
11
|
+
| Layer | What it is | Where it lives |
|
|
12
|
+
|-------|-----------|----------------|
|
|
13
|
+
| **Interface** | What a product exposes (CLI, MCP, Skill, etc.). Eight kinds, listed below in canonical order. | The product repo. |
|
|
14
|
+
| **Installer** | Detects a product's interfaces and installs them all. `ldm install`. Stable, alpha, and beta tracks via flags. | `wip-ldm-os` (`bin/ldm.js`). |
|
|
15
|
+
| **Catalog** | Slug→source resolver (npm package, repo, registry/CLI matches, status) **plus** the trust surface: provenance, version pinning, permission scopes, audits, install/update/revocation. Stays human-readable and browseable as a fallback discovery surface; not the primary steering wheel. | `catalog.json` at the LDM OS root. |
|
|
16
|
+
| **Install Spec** | Agent-readable install runbook published at `wip.computer/install/<slug>.txt`. Track-neutral. Teaches an AI to safely check, explain, dry-run, install, update, and pair the product. | `https://wip.computer/install/<slug>.txt`. See [Install Spec](#install-spec). |
|
|
17
|
+
| **Stacks** | Multi-product bundles. One install brings up several products and their MCP servers. | `catalog.json.stacks`. |
|
|
18
|
+
|
|
19
|
+
Use the install spec URL to learn the safe install flow; use catalog to resolve the slug; use `ldm install` with stable/alpha/beta track flags; installer detects and installs the product's declared interfaces; stacks install bundles.
|
|
20
|
+
|
|
21
|
+
### Primary flow
|
|
22
|
+
|
|
23
|
+
The user's path is **outcome → agent resolves services → install specs / catalog / auth → bespoke artifact**. Not "browse a plugin store and pick one." The catalog stays browseable for the times a user wants to look around, but it is no longer the steering wheel. The steering wheel is the user's stated outcome and the agent's composition.
|
|
24
|
+
|
|
25
|
+
**Personal context** (goals, preferences, prior experiments, constraints) does not come from this spec. It comes from **Memory Crystal**, a sibling LDM OS component. The universal-installer spec describes how *services* expose themselves; Memory Crystal describes how the *agent* knows you. Both feed the bespoke composition.
|
|
26
|
+
|
|
27
|
+
## The Eight Interfaces
|
|
28
|
+
|
|
29
|
+
The canonical order is fixed: CLI (1), Module (2), MCP Server local stdio (3), Remote MCP (4), OpenClaw Plugin (5), Skill (6), Claude Code Hook (7), Claude Code Plugin (8). Local and Remote MCP sit next to each other because they are sibling transports of the same protocol. Claude Code Plugin sits last because it bundles the others.
|
|
8
30
|
|
|
9
31
|
### 1. CLI
|
|
10
32
|
|
|
@@ -45,15 +67,15 @@ An importable ES module. The programmatic interface. Other tools compose with it
|
|
|
45
67
|
}
|
|
46
68
|
```
|
|
47
69
|
|
|
48
|
-
### 3. MCP Server
|
|
70
|
+
### 3. MCP Server (local stdio)
|
|
49
71
|
|
|
50
|
-
A JSON-RPC server implementing the Model Context Protocol.
|
|
72
|
+
A JSON-RPC server implementing the Model Context Protocol over stdio. Spawned as a child process by the agent (Claude Code, Cursor, OpenClaw). For the HTTP/SSE sibling, see [#4 Remote MCP](#4-remote-mcp).
|
|
51
73
|
|
|
52
74
|
**Convention:** `mcp-server.mjs` (or `.js`, `.ts`) at the repo root. Uses `@modelcontextprotocol/sdk`.
|
|
53
75
|
|
|
54
76
|
**Detection:** One of `mcp-server.mjs`, `mcp-server.js`, `mcp-server.ts`, `dist/mcp-server.js` exists.
|
|
55
77
|
|
|
56
|
-
**Install:** Add to `.mcp.json`:
|
|
78
|
+
**Install:** Add to `.mcp.json` with `command` + `args`:
|
|
57
79
|
|
|
58
80
|
```json
|
|
59
81
|
{
|
|
@@ -64,7 +86,51 @@ A JSON-RPC server implementing the Model Context Protocol. Any MCP-compatible ag
|
|
|
64
86
|
}
|
|
65
87
|
```
|
|
66
88
|
|
|
67
|
-
### 4.
|
|
89
|
+
### 4. Remote MCP
|
|
90
|
+
|
|
91
|
+
The HTTP/SSE (or streamable HTTP) sibling of #3. Hosted at an HTTPS endpoint, not spawned locally. The transport that lights up Claude Desktop connectors, web, and mobile clients.
|
|
92
|
+
|
|
93
|
+
**Contract:** Remote MCP endpoint is **declared by package/catalog metadata** and **registered by `ldm install`**. No filesystem-sniffing fallback.
|
|
94
|
+
|
|
95
|
+
**Convention:** `mcp.remote` field in `package.json`:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"mcp": {
|
|
100
|
+
"remote": {
|
|
101
|
+
"url": "https://example.com/mcp",
|
|
102
|
+
"transport": "streamable-http",
|
|
103
|
+
"auth": "oauth"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`url` may be a placeholder (`"https://__DEPLOYED_URL__"`) when the repo ships the server code and the URL is supplied by the catalog at install time.
|
|
110
|
+
|
|
111
|
+
**Detection:** `package.json.mcp.remote.url` is a string.
|
|
112
|
+
|
|
113
|
+
**Install:** Add to `.mcp.json` as a remote entry (`url` + `transport` instead of `command` + `args`). Print a one-line Claude Desktop hint so the user can also add it under Connectors. Implementation tracked in [bugs/installer/](../../ai/product/bugs/installer/2026-04-28--cc-mini--installer-remote-mcp-install.md).
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"tool-name": {
|
|
118
|
+
"url": "https://example.com/mcp",
|
|
119
|
+
"transport": "streamable-http"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**How it differs from #3:** sibling transport, not a flag on #3.
|
|
125
|
+
|
|
126
|
+
| | #3 Local stdio | #4 Remote |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| Transport | stdio (child process) | HTTPS + SSE or streamable HTTP |
|
|
129
|
+
| Process model | Per-session spawn | Long-running, multi-tenant |
|
|
130
|
+
| Auth | Trust the local process | OAuth or shared secret |
|
|
131
|
+
| Surfaces | Claude Code, Cursor, OpenClaw | Claude Desktop, web, mobile |
|
|
132
|
+
|
|
133
|
+
### 5. OpenClaw Plugin
|
|
68
134
|
|
|
69
135
|
A plugin for OpenClaw agents. Lifecycle hooks, tool registration, settings.
|
|
70
136
|
|
|
@@ -74,12 +140,14 @@ A plugin for OpenClaw agents. Lifecycle hooks, tool registration, settings.
|
|
|
74
140
|
|
|
75
141
|
**Install:** Copy to `~/.openclaw/extensions/<name>/`, run `npm install --omit=dev`.
|
|
76
142
|
|
|
77
|
-
###
|
|
143
|
+
### 6. Skill (SKILL.md)
|
|
78
144
|
|
|
79
145
|
A markdown file that teaches agents when and how to use the tool. The instruction interface. Follows the [Agent Skills Spec](https://agentskills.io/specification).
|
|
80
146
|
|
|
81
147
|
**Convention:** `SKILL.md` at the repo root. YAML frontmatter with name, description. Optional `references/` directory for context files.
|
|
82
148
|
|
|
149
|
+
**Platform variants:** Codex CLI reads `AGENTS.md` instead of `SKILL.md`, with the same role and the same content shape. Treat `AGENTS.md` as the Codex-flavored filename for this same interface, not a separate interface. A repo may ship both (or symlink one to the other) so it works in Codex and SKILL.md-aware agents.
|
|
150
|
+
|
|
83
151
|
**Detection:** `SKILL.md` exists.
|
|
84
152
|
|
|
85
153
|
**Install:** `SKILL.md` deployed to `~/.openclaw/skills/<name>/`. If `references/` exists, deployed alongside SKILL.md and to `settings/docs/skills/<name>/` in the workspace.
|
|
@@ -113,7 +181,7 @@ metadata:
|
|
|
113
181
|
---
|
|
114
182
|
```
|
|
115
183
|
|
|
116
|
-
###
|
|
184
|
+
### 7. Claude Code Hook
|
|
117
185
|
|
|
118
186
|
A hook that runs during Claude Code's tool lifecycle (PreToolUse, Stop, etc.).
|
|
119
187
|
|
|
@@ -138,6 +206,58 @@ A hook that runs during Claude Code's tool lifecycle (PreToolUse, Stop, etc.).
|
|
|
138
206
|
}
|
|
139
207
|
```
|
|
140
208
|
|
|
209
|
+
### 8. Claude Code Plugin
|
|
210
|
+
|
|
211
|
+
A distributable plugin for Claude Code. Bundles skills, agents, hooks, MCP servers, and LSP servers into one installable package. Shareable via marketplaces.
|
|
212
|
+
|
|
213
|
+
**Convention:** `.claude-plugin/plugin.json` at the repo root.
|
|
214
|
+
|
|
215
|
+
**Detection:** `.claude-plugin/plugin.json` exists.
|
|
216
|
+
|
|
217
|
+
**Install:** Registered with Claude Code via `/plugin install` or marketplace.
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
your-plugin/
|
|
221
|
+
├── .claude-plugin/
|
|
222
|
+
│ └── plugin.json # manifest (name, version, description)
|
|
223
|
+
├── skills/ # SKILL.md files
|
|
224
|
+
├── agents/ # subagent definitions
|
|
225
|
+
├── hooks/
|
|
226
|
+
│ └── hooks.json # event handlers
|
|
227
|
+
├── .mcp.json # MCP server configs
|
|
228
|
+
└── .lsp.json # LSP server configs
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"name": "your-plugin",
|
|
234
|
+
"version": "1.0.0",
|
|
235
|
+
"description": "What it does",
|
|
236
|
+
"author": { "name": "Your Name" }
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Out of scope by design
|
|
241
|
+
|
|
242
|
+
**Disposable, agent-generated artifacts** (custom dashboards, ephemeral scripts, one-off automations, the 300-line cardio tracker someone vibe-codes in an hour) are out of scope for this spec. They are products of an agent, not Universal Interface products. The eight interfaces describe what the agent has to *compose with*. The composition output is not itself a numbered interface and never will be.
|
|
243
|
+
|
|
244
|
+
### Worked example (compact sketch)
|
|
245
|
+
|
|
246
|
+
User says: *"Help me track my resting heart rate over the next 8 weeks. Goal: 50 → 45 bpm. Zone 2 cardio + 1 HIIT/week."*
|
|
247
|
+
|
|
248
|
+
The agent:
|
|
249
|
+
|
|
250
|
+
1. Reads personal context from Memory Crystal (RHR baseline, prior experiments, units preference).
|
|
251
|
+
2. Resolves the treadmill via catalog → install spec URL → declared Remote MCP (#4) for workout data.
|
|
252
|
+
3. Pulls calendar/time semantics for the 8-week window.
|
|
253
|
+
4. Composes a disposable dashboard (~300 lines).
|
|
254
|
+
|
|
255
|
+
The dashboard is **not** a Universal Interface product. It is the agent's output, assembled from agent-native sensors and actuators. Full version of this example is tracked separately ... see [bugs/installer/2026-04-28--cc-mini--installer-cardio-tracker-worked-example.md](../../ai/product/bugs/installer/2026-04-28--cc-mini--installer-cardio-tracker-worked-example.md).
|
|
256
|
+
|
|
257
|
+
### Future considerations
|
|
258
|
+
|
|
259
|
+
*LSP as a standalone interface (#9).* LSP servers are currently surfaced via Claude Code Plugin bundles (#8) ... `.lsp.json` is part of the plugin shape. If a product ships a standalone LSP server outside a CC Plugin, we will add it as a numbered interface. Not added today because no WIP product ships one yet, and the spec should describe interfaces we install and use, not interfaces software could theoretically have.
|
|
260
|
+
|
|
141
261
|
## Architecture
|
|
142
262
|
|
|
143
263
|
Every repo that follows this spec has the same basic structure:
|
|
@@ -153,9 +273,9 @@ your-tool/
|
|
|
153
273
|
ai/ development process (plans, todos, notes)
|
|
154
274
|
```
|
|
155
275
|
|
|
156
|
-
Not every tool needs all
|
|
276
|
+
Not every tool needs all eight interfaces. Build the ones that make sense.
|
|
157
277
|
|
|
158
|
-
The minimum viable agent-native tool has two interfaces: **Module** (importable) and **Skill** (agent instructions). Add CLI for humans. Add MCP for agents that speak MCP. Add OpenClaw/CC Hook for specific platforms.
|
|
278
|
+
The minimum viable agent-native tool has two interfaces: **Module** (importable) and **Skill** (agent instructions). Add CLI for humans. Add local MCP (#3) for agents that speak MCP over stdio. Add Remote MCP (#4) when you want Claude Desktop / web / mobile to reach the same server. Add OpenClaw Plugin / CC Hook / CC Plugin for specific platforms.
|
|
159
279
|
|
|
160
280
|
## The `ai/` Folder
|
|
161
281
|
|
|
@@ -175,20 +295,73 @@ The `ai/` folder is the development process. It is not part of the published pro
|
|
|
175
295
|
|
|
176
296
|
**Public/private split:** If a repo is public, the `ai/` folder should not ship. The recommended pattern is to maintain a private working repo (with `ai/`) and a public repo (everything except `ai/`). The public repo has everything an LLM or human needs to understand and use the tool. The `ai/` folder is operational context for the team building it.
|
|
177
297
|
|
|
178
|
-
## The
|
|
298
|
+
## The Installer
|
|
179
299
|
|
|
180
|
-
`ldm install` is the primary installer (part of LDM OS). `wip-install` is the standalone fallback. Both scan a repo, detect which interfaces exist, and install them all. One command.
|
|
300
|
+
`ldm install` is the primary installer (part of LDM OS). `wip-install` is the standalone fallback. Both scan a repo or slug, detect which interfaces exist, and install them all. One command.
|
|
181
301
|
|
|
182
302
|
```bash
|
|
183
|
-
ldm install /path/to/repo
|
|
184
|
-
ldm install org/repo
|
|
185
|
-
ldm install
|
|
186
|
-
|
|
187
|
-
|
|
303
|
+
ldm install /path/to/repo # local (via LDM OS)
|
|
304
|
+
ldm install org/repo # from GitHub
|
|
305
|
+
ldm install <slug> # from catalog (stable, default)
|
|
306
|
+
ldm install --alpha <slug> # alpha (validation track)
|
|
307
|
+
ldm install --beta <slug> # beta (validation track)
|
|
308
|
+
ldm install <slug> --dry-run # detect only, no changes
|
|
309
|
+
wip-install /path/to/repo # standalone fallback (bootstraps LDM OS if needed)
|
|
310
|
+
wip-install --json /path/to/repo # JSON output
|
|
188
311
|
```
|
|
189
312
|
|
|
313
|
+
Tracks select the npm dist-tag (or git ref) the installer pulls from. The same install spec URL covers all three; the AI follows the spec, the user (or releasing agent) picks the track via flag.
|
|
314
|
+
|
|
190
315
|
For toolbox repos (with a `tools/` directory containing sub-tools), the installer enters toolbox mode and installs each sub-tool.
|
|
191
316
|
|
|
317
|
+
## Install Spec
|
|
318
|
+
|
|
319
|
+
An **install spec** is an agent-readable install runbook published at a stable URL:
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
https://wip.computer/install/<slug>.txt
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
The contract is the URL and the behavior, not the file origin. An install spec can be generated from `SKILL.md`, mirrored from it, or live alongside it. What matters is that any AI can fetch it, read it, and walk a user through a safe install.
|
|
326
|
+
|
|
327
|
+
### Behavior contract
|
|
328
|
+
|
|
329
|
+
The spec teaches an AI to:
|
|
330
|
+
|
|
331
|
+
1. **Check** whether the product is already installed and at what version.
|
|
332
|
+
2. **Explain** what the product is, what it installs, and what changes for this AI and the user's other AIs.
|
|
333
|
+
3. **Dry-run** so the user sees what will change before anything is touched.
|
|
334
|
+
4. **Install** only after explicit user consent.
|
|
335
|
+
5. **Update** an existing install (skip steps the user already did).
|
|
336
|
+
6. **Pair** any post-install steps (passkey, device pairing, gateway start, etc.) with explicit consent at each step.
|
|
337
|
+
|
|
338
|
+
### Tracks
|
|
339
|
+
|
|
340
|
+
One install spec covers all release tracks. The user picks via flag:
|
|
341
|
+
|
|
342
|
+
| Track | Flag | Audience |
|
|
343
|
+
|-------|------|----------|
|
|
344
|
+
| Stable | (default) `ldm install <slug>` | End users. Owner-dogfooded. |
|
|
345
|
+
| Beta | `ldm install --beta <slug>` | Validation; agents may install. |
|
|
346
|
+
| Alpha | `ldm install --alpha <slug>` | Validation; agents may install. |
|
|
347
|
+
|
|
348
|
+
The spec text itself is track-neutral. Tracks are an installer concern, not a copy concern.
|
|
349
|
+
|
|
350
|
+
### Install spec vs `agent.txt`
|
|
351
|
+
|
|
352
|
+
These are related, not the same. Both are agent-readable. They sit at different scopes:
|
|
353
|
+
|
|
354
|
+
- **`agent.txt`** ... site- or product-level entrypoint for agents. "What can agents do here? What routes exist? What policies apply?" Lives at the root of a site or product (e.g. `wip.computer/agent.txt`).
|
|
355
|
+
- **`install/<slug>.txt`** ... per-product install runbook. "How should an agent safely check, explain, dry-run, install, update, and pair this product?" Lives under `wip.computer/install/`.
|
|
356
|
+
|
|
357
|
+
`agent.txt` can point agents at install specs. An install spec does not replace `agent.txt`.
|
|
358
|
+
|
|
359
|
+
### Worked example: Codex Remote Control
|
|
360
|
+
|
|
361
|
+
Install spec: [`https://wip.computer/install/wip-codex-remote-control.txt`](https://wip.computer/install/wip-codex-remote-control.txt).
|
|
362
|
+
|
|
363
|
+
The user pastes one prompt into Codex (or any AI). The AI fetches the install spec, checks installed state, explains the product, runs `ldm install --dry-run wip-codex-remote-control`, and only installs (and starts the daemon, and pairs the phone) after the user says yes at each step. Tracks are selected by flag against the same URL.
|
|
364
|
+
|
|
192
365
|
## Examples
|
|
193
366
|
|
|
194
367
|
### AI DevOps Toolbox (this repo)
|