@awebai/oats-pi 0.22.0
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 +33 -0
- package/extension/core-loader.mjs +62 -0
- package/extension/index.ts +59 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @awebai/oats-pi
|
|
2
|
+
|
|
3
|
+
Pi runtime bridge for [OATS](https://github.com/awebai/oats).
|
|
4
|
+
|
|
5
|
+
The runtime-neutral kernel and universal `oats` CLI live in
|
|
6
|
+
`@awebai/oats`. Publishes in lockstep with the kernel (same version
|
|
7
|
+
from the same release tag). This bridge registers no operational tools. It only:
|
|
8
|
+
|
|
9
|
+
- exposes `oats-getting-started` before an OATS workspace exists (the
|
|
10
|
+
acquisition funnel);
|
|
11
|
+
- contributes the instance-local `.agents/skills` set inside a spawned
|
|
12
|
+
instance;
|
|
13
|
+
- journals compaction summaries and sends resume nudges when the active
|
|
14
|
+
knowledge capability created `STATE.md`/`log.md` — the OKF session
|
|
15
|
+
protocol enforced at runtime.
|
|
16
|
+
|
|
17
|
+
Skill resolution itself is owned by the kernel: spawn materializes the exact
|
|
18
|
+
kernel + soul + active-capability set into each instance's `.agents/skills`
|
|
19
|
+
and launches pi with that directory as an explicit skill path. Ambient
|
|
20
|
+
skills (user-level, packages, work tree) coexist with the OATS-composed set.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g @awebai/oats
|
|
24
|
+
pi install npm:@awebai/oats-pi
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
OATS
|
|
28
|
+
publishes both packages from the same version tag. Reload pi after an adapter
|
|
29
|
+
install or upgrade.
|
|
30
|
+
|
|
31
|
+
All lifecycle/config/package operations use the shell-visible CLI: `oats
|
|
32
|
+
status`, `oats spawn`, `oats doctor`, `oats install`, `oats trust`, `oats use`, and
|
|
33
|
+
`oats retire`.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core-loader.mjs — locate the globally installed @awebai/oats kernel and
|
|
3
|
+
* re-export its lib/core.mjs. The pi package is a thin adapter: it never ships
|
|
4
|
+
* the kernel, skills, injects, or capabilities — those live in the global CLI
|
|
5
|
+
* package (npm i -g @awebai/oats), the single source of truth that the
|
|
6
|
+
* future Claude plugin shares.
|
|
7
|
+
*
|
|
8
|
+
* Resolution order:
|
|
9
|
+
* 1. $OATS_PKG_ROOT (explicit override, e.g. a dev clone)
|
|
10
|
+
* 2. the `oats` binary on PATH → realpath → its package root
|
|
11
|
+
* 3. `npm root -g`/@awebai/oats (binary not linked but package present)
|
|
12
|
+
*/
|
|
13
|
+
import { execSync } from "node:child_process";
|
|
14
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
import { pathToFileURL } from "node:url";
|
|
17
|
+
|
|
18
|
+
const PKG_NAME = "@awebai/oats";
|
|
19
|
+
|
|
20
|
+
function isKernelRoot(dir) {
|
|
21
|
+
const pj = join(dir, "package.json");
|
|
22
|
+
if (!existsSync(pj) || !existsSync(join(dir, "lib", "core.mjs"))) return false;
|
|
23
|
+
try { return JSON.parse(readFileSync(pj, "utf8")).name === PKG_NAME; } catch { return false; }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function findKernelRoot() {
|
|
27
|
+
if (process.env.OATS_PKG_ROOT && isKernelRoot(process.env.OATS_PKG_ROOT)) return process.env.OATS_PKG_ROOT;
|
|
28
|
+
try {
|
|
29
|
+
const bin = execSync("command -v oats", { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
30
|
+
if (bin) {
|
|
31
|
+
let d = dirname(realpathSync(bin)); // <pkg>/bin/oats.mjs → <pkg>/bin
|
|
32
|
+
while (d !== dirname(d)) {
|
|
33
|
+
if (isKernelRoot(d)) return d;
|
|
34
|
+
d = dirname(d);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch { /* not on PATH */ }
|
|
38
|
+
try {
|
|
39
|
+
const g = execSync("npm root -g", { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
40
|
+
const cand = join(g, PKG_NAME);
|
|
41
|
+
if (isKernelRoot(cand)) return cand;
|
|
42
|
+
} catch { /* no npm */ }
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const OATS_PKG_ROOT = findKernelRoot();
|
|
47
|
+
if (!OATS_PKG_ROOT) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
"OATS kernel not found — the pi adapter needs the oats CLI installed globally.\n" +
|
|
50
|
+
" Install it: npm install -g @awebai/oats\n" +
|
|
51
|
+
" (or point OATS_PKG_ROOT at a checkout of the awebai/oats repo)",
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const core = await import(pathToFileURL(join(OATS_PKG_ROOT, "lib", "core.mjs")).href);
|
|
56
|
+
|
|
57
|
+
export const { appendLogEntry, PACKAGED_SKILLS_DIR } = core;
|
|
58
|
+
|
|
59
|
+
/** Kernel package version (for skew diagnostics against the adapter). */
|
|
60
|
+
export function kernelVersion() {
|
|
61
|
+
try { return JSON.parse(readFileSync(join(OATS_PKG_ROOT, "package.json"), "utf8")).version; } catch { return "unknown"; }
|
|
62
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OATS pi runtime bridge — minimal glue.
|
|
3
|
+
*
|
|
4
|
+
* The kernel materializes every spawned instance's exact set in
|
|
5
|
+
* .agents/skills; this bridge contributes it inside an instance, plus the
|
|
6
|
+
* pre-workspace oats-getting-started bootstrap outside one, and drives the
|
|
7
|
+
* memory session events. Ambient skills coexist with the OATS-composed set.
|
|
8
|
+
*/
|
|
9
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { appendLogEntry, PACKAGED_SKILLS_DIR } from "./core-loader.mjs";
|
|
11
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
|
|
14
|
+
export default function (pi: ExtensionAPI) {
|
|
15
|
+
const agentHome = process.env.PI_AGENT_HOME;
|
|
16
|
+
const isInstance = !!agentHome && existsSync(join(agentHome, "instance.json"));
|
|
17
|
+
|
|
18
|
+
pi.on("resources_discover", async () => {
|
|
19
|
+
if (isInstance) {
|
|
20
|
+
const local = join(agentHome!, ".agents", "skills");
|
|
21
|
+
return existsSync(local) ? { skillPaths: [local] } : undefined;
|
|
22
|
+
}
|
|
23
|
+
const gettingStarted = join(PACKAGED_SKILLS_DIR, "oats-getting-started");
|
|
24
|
+
return existsSync(gettingStarted) ? { skillPaths: [gettingStarted] } : undefined;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (isInstance) {
|
|
28
|
+
pi.on("session_compact", async (event) => {
|
|
29
|
+
if (!existsSync(join(agentHome!, "STATE.md"))) return;
|
|
30
|
+
try {
|
|
31
|
+
const summary = (event.compactionEntry?.summary ?? "").replace(/\s+/g, " ").trim();
|
|
32
|
+
appendLogEntry(
|
|
33
|
+
join(agentHome!, "log.md"),
|
|
34
|
+
`**Compaction** (${event.reason}): ${summary.slice(0, 400)}${summary.length > 400 ? "…" : ""}`,
|
|
35
|
+
"Instance Log",
|
|
36
|
+
);
|
|
37
|
+
} catch { /* memory automation must never break a session */ }
|
|
38
|
+
pi.sendMessage({
|
|
39
|
+
customType: "oats-memory",
|
|
40
|
+
content: "Context was just compacted. Before continuing, update ./STATE.md (Plan/Progress/Next) so a fresh session could resume from files alone.",
|
|
41
|
+
display: false,
|
|
42
|
+
}, { deliverAs: "steer" });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
pi.on("session_start", async (event) => {
|
|
46
|
+
if (event.reason !== "startup" && event.reason !== "resume" && event.reason !== "new") return;
|
|
47
|
+
const statePath = join(agentHome!, "STATE.md");
|
|
48
|
+
if (!existsSync(statePath)) return;
|
|
49
|
+
const state = readFileSync(statePath, "utf8");
|
|
50
|
+
const touched = !/_No task assigned yet/.test(state) || !/_\(the single next action/.test(state);
|
|
51
|
+
if (event.reason === "startup" && !touched) return;
|
|
52
|
+
pi.sendMessage({
|
|
53
|
+
customType: "oats-memory",
|
|
54
|
+
content: `You are agent instance home ${agentHome}. Read ./STATE.md and the recent entries of ./log.md now, then continue from STATE.md's "Next" section. Keep STATE.md current as you work.`,
|
|
55
|
+
display: false,
|
|
56
|
+
}, { deliverAs: "steer", triggerTurn: false });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@awebai/oats-pi",
|
|
3
|
+
"version": "0.22.0",
|
|
4
|
+
"description": "OATS pi runtime bridge — memory session events and pre-workspace bootstrap over the runtime-neutral @awebai/oats kernel",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"agents",
|
|
8
|
+
"oats"
|
|
9
|
+
],
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/awebai/oats",
|
|
13
|
+
"directory": "packages/pi"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"pi": {
|
|
18
|
+
"extensions": [
|
|
19
|
+
"./extension/index.ts"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"extension/",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@earendil-works/pi-ai": "*",
|
|
28
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
29
|
+
"typebox": "*"
|
|
30
|
+
}
|
|
31
|
+
}
|