@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 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
+ }