babavoss 0.12.5 → 0.13.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/gui/babavoss-web.js +234 -0
- package/gui/chunk-5hpp1ypv.js +11710 -0
- package/gui/chunk-a20zfekq.js +8639 -0
- package/gui/chunk-k3cm8j6r.js +433 -0
- package/gui/chunk-pk09y98y.js +50 -0
- package/gui/chunk-smz02qa6.js +186 -0
- package/gui/chunk-wwqypxre.js +49 -0
- package/gui/gui.js +2015 -0
- package/gui/react-compiler-runtime.js +39 -0
- package/gui/react-dom-client.js +21 -0
- package/gui/react-dom.js +44 -0
- package/gui/react-jsx-runtime.js +19 -0
- package/gui/react.js +105 -0
- package/gui/theme.css +3053 -0
- package/index.ts +1 -1
- package/package.json +8 -6
- package/src/{promptware → agents}/compile.ts +12 -12
- package/src/{promptware → agents}/define.ts +2 -2
- package/src/{promptware → agents}/disk.ts +7 -4
- package/src/{promptware → agents}/sync.ts +2 -2
- package/src/{promptware → agents}/system.ts +17 -17
- package/src/baba/check.ts +3 -3
- package/src/baba/config.ts +5 -2
- package/src/baba/init.ts +13 -18
- package/src/baba/node.ts +2 -2
- package/src/baba/worker.ts +13 -5
- package/src/build/mdx.ts +4 -4
- package/src/build/project.ts +6 -1
- package/src/build/views.ts +3 -3
- package/src/desktop/index.ts +1 -1
- package/src/door/core.ts +2 -2
- package/src/ecs/baba.ts +27 -17
- package/src/generated/build.ts +1 -1
- package/src/gui/gui.tsx +3 -3
- package/src/gui/{promptware.tsx → instructions.tsx} +12 -12
- package/src/gui/theme.css +1 -1
- package/src/guide/add-a-desktop.mdx +2 -2
- package/src/guide/index.ts +4 -4
- package/src/guide/write-a-system.mdx +3 -3
- package/src/guide/{write-promptware.mdx → write-agentic-instructions.mdx} +17 -24
- package/src/{prompt → instructions}/evals.ts +1 -1
- package/src/{prompt → instructions}/index.ts +9 -8
- package/src/{prompt → instructions}/jsx-runtime.ts +3 -3
- package/src/mcp/main.ts +3 -3
- package/src/mcp/tools.ts +4 -4
- package/src/runtime/harness.ts +2 -2
- package/src/shell/run.ts +18 -18
- package/src/spec/index.ts +1 -1
- package/src/web/core.tsx +2 -2
- /package/src/{promptware → agents}/markdown.d.ts +0 -0
- /package/src/{prompt → instructions}/jsx-dev-runtime.ts +0 -0
- /package/src/{prompt → instructions}/mdx.d.ts +0 -0
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// babavoss: the framework. What a baba imports.
|
|
2
|
-
export { baba, effect, source, accept, resource, shape, manifestOf, isBaba, isSystem, nameOf, SYSTEM } from "./src/ecs/baba.ts";
|
|
2
|
+
export { baba, instructed, effect, source, accept, resource, shape, manifestOf, isBaba, isSystem, nameOf, SYSTEM } from "./src/ecs/baba.ts";
|
|
3
3
|
export { system, declare, part } from "./src/ecs/system.ts";
|
|
4
4
|
export type { Decl, Entry, Handles, HandlesOf, Building, Maker, Part, Piece, Maybe, StepPiece, OnPiece, ActionPiece, QueryPiece, BindingOf, Scope, Reader, StateOf, ReaderOf, Act, Read } from "./src/ecs/system.ts";
|
|
5
5
|
export { not, added, changed, removed, select } from "./src/ecs/state.ts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babavoss",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": "github:alanremarc/babavoss",
|
|
6
6
|
"homepage": "https://babavoss.org",
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
"./web": "./src/web/index.tsx",
|
|
15
15
|
"./test": "./src/test/index.ts",
|
|
16
16
|
"./bench": "./src/bench/index.ts",
|
|
17
|
-
"./
|
|
18
|
-
"./
|
|
19
|
-
"./
|
|
17
|
+
"./instructions": "./src/instructions/index.ts",
|
|
18
|
+
"./instructions/jsx-runtime": "./src/instructions/jsx-runtime.ts",
|
|
19
|
+
"./instructions/jsx-dev-runtime": "./src/instructions/jsx-dev-runtime.ts",
|
|
20
20
|
"./desktop": "./src/desktop/index.ts",
|
|
21
21
|
"./kit": "./src/kit/index.ts",
|
|
22
22
|
"./spec": "./src/spec/index.ts",
|
|
23
|
-
"./
|
|
23
|
+
"./instructions/register": "./src/build/mdx-register.ts"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"bin",
|
|
@@ -37,7 +37,9 @@
|
|
|
37
37
|
"test": "bun test",
|
|
38
38
|
"test:kernel": "VOSS_KERNEL=1 bun test ./test/kernel.test.ts",
|
|
39
39
|
"test:all": "VOSS_KERNEL=1 bun test",
|
|
40
|
-
"check": "bunx tsc --noEmit -p ../.."
|
|
40
|
+
"check": "bunx tsc --noEmit -p ../..",
|
|
41
|
+
"prepack": "bun ../../tools/release/pack.ts prepack",
|
|
42
|
+
"postpack": "bun ../../tools/release/pack.ts postpack"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
43
45
|
"@types/react-dom": "19"
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Instructions to harness files, as text. Pure: the manifest, with the baba's
|
|
2
2
|
// context and skills rendered and its harness declaration, becomes what
|
|
3
3
|
// each output path, relative to the project, should hold.
|
|
4
4
|
//
|
|
5
5
|
// Claude Code CLAUDE.md .claude/skills/NAME/SKILL.md .claude/settings.local.json
|
|
6
6
|
// Codex AGENTS.md .agents/skills/NAME/SKILL.md
|
|
7
7
|
//
|
|
8
|
-
//
|
|
8
|
+
// Instructions is know-how; the contract teaches itself at the doors, the
|
|
9
9
|
// CLI's index and schemas and the MCP server's tools. The instruction file
|
|
10
10
|
// teaches the doors, true whatever the contract becomes; the table of calls
|
|
11
11
|
// is rendered only when the baba asks for it. The MCP registration is
|
|
12
|
-
// not
|
|
12
|
+
// not agentic instructions's: it is the machine's. The permissions, in a file the
|
|
13
13
|
// project shares with others, are a merge: voss owns only its entries.
|
|
14
14
|
import type { Json } from "../kernel/schema.ts";
|
|
15
15
|
import type { Manifest } from "../ecs/baba.ts";
|
|
@@ -18,7 +18,7 @@ import { exampleOf } from "../ecs/handles.ts";
|
|
|
18
18
|
import { skillsOf, type SkillManifest } from "./define.ts";
|
|
19
19
|
|
|
20
20
|
/** The systems voss gives every baba: their calls are not the project's contract. */
|
|
21
|
-
export const VOSS = new Set(["
|
|
21
|
+
export const VOSS = new Set(["agents", "maker"]);
|
|
22
22
|
|
|
23
23
|
export type Harness = "claude" | "codex";
|
|
24
24
|
export const harnesses: Harness[] = ["claude", "codex"];
|
|
@@ -31,8 +31,8 @@ const files: Record<Harness, { instructions: string; skills: string }> = {
|
|
|
31
31
|
/** Which harness an output path belongs to. */
|
|
32
32
|
export const harnessOf = (path: string): Harness => (path === "AGENTS.md" || path.startsWith(".agents/") || path.startsWith(".codex/") ? "codex" : "claude");
|
|
33
33
|
|
|
34
|
-
/** Where the
|
|
35
|
-
export const SOURCE = ".baba/
|
|
34
|
+
/** Where the agentic instructions lives, relative to the project. */
|
|
35
|
+
export const SOURCE = ".baba/agents";
|
|
36
36
|
|
|
37
37
|
// ---- merges ------------------------------------------------------------
|
|
38
38
|
|
|
@@ -55,11 +55,11 @@ export type Merge =
|
|
|
55
55
|
export const CLAUDE_SETTINGS = ".claude/settings.local.json";
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* The outputs are made from the
|
|
58
|
+
* The outputs are made from the agentic instructions: a checkout makes its own with `voss baba agents sync` (a landing does),
|
|
59
59
|
* and git keeps none of them. Voss's lines of the project's .gitignore say so.
|
|
60
60
|
*/
|
|
61
61
|
export const ignoreLines = (): string[] => [
|
|
62
|
-
"# voss makes these from .baba/
|
|
62
|
+
"# voss makes these from .baba/agents: `voss baba agents sync` writes them, git keeps none",
|
|
63
63
|
...harnesses.flatMap((h) => [`/${files[h].instructions}`, `/${files[h].skills}/`]),
|
|
64
64
|
`/${CLAUDE_SETTINGS}`,
|
|
65
65
|
];
|
|
@@ -108,7 +108,7 @@ export function compile(input: CompileInput): Compiled {
|
|
|
108
108
|
const perms = st.claude.permissions;
|
|
109
109
|
if (perms.length) lists.push({ at: ["permissions", "allow"], values: [...new Set(perms)] });
|
|
110
110
|
const byEvent = new Map<string, unknown[]>();
|
|
111
|
-
for (const hk of input.manifest.
|
|
111
|
+
for (const hk of input.manifest.instructions.hooks) {
|
|
112
112
|
const command = `${input.program} ${hk.action} @- --hook ${hk.on}${hk.strict ? " --strict=true" : ""}`;
|
|
113
113
|
const entry = { ...(hk.match ? { matcher: hk.match } : {}), hooks: [{ type: "command", command }] };
|
|
114
114
|
if (!byEvent.has(hk.on)) byEvent.set(hk.on, []);
|
|
@@ -136,10 +136,10 @@ function instructionFile(input: CompileInput): string {
|
|
|
136
136
|
const prog = input.program;
|
|
137
137
|
const m = input.manifest;
|
|
138
138
|
const lines = [
|
|
139
|
-
`<!-- Generated by ${prog} from the baba's
|
|
139
|
+
`<!-- Generated by ${prog} from the baba's agentic instructions. Do not edit: a hand edit is held and never overwritten. Edit the agentic instructions under .baba/agents/; they sync. -->`,
|
|
140
140
|
`# ${input.title ?? prog}`,
|
|
141
141
|
];
|
|
142
|
-
const context = m.
|
|
142
|
+
const context = m.instructions.context?.trim();
|
|
143
143
|
if (context) lines.push("", context);
|
|
144
144
|
lines.push(
|
|
145
145
|
"", "## This baba", "",
|
|
@@ -172,7 +172,7 @@ function instructionFile(input: CompileInput): string {
|
|
|
172
172
|
...Object.entries(m.queries).filter(([, q]) => !VOSS.has(q.owner)).map(([n, q]) => `| \`${prog} ${[n, ...usage(q.args, q.positional)].join(" ")}\` | query | ${q.owner} | ${system(q.summary)} |`),
|
|
173
173
|
);
|
|
174
174
|
}
|
|
175
|
-
lines.push("", "Voss adds two systems to this baba. `
|
|
175
|
+
lines.push("", "Voss adds two systems to this baba. `agents` compiles what the baba tells agents, its context, skills and hooks, each an .mdx file under `.baba/agents/`, into each harness's files: `CLAUDE.md`, `AGENTS.md`, the skills, Claude Code's permissions and hooks. Those are generated: edit the agentic instructions, never the outputs. `maker` keeps the specs, each system's `systems/NAME/spec.ts` beside it and the whole baba's `.baba/spec.ts`, each a seeded state with its scenarios and the adapters that play its outside, proved under `bun test` by `.baba/spec.test.ts`; `spec` lists them with their verdicts, `spec-run` runs a state's scenarios headless so the verdicts are remade, and the Maker app runs them live, where `spec-step` and `spec-read` reach the state a person has open.");
|
|
176
176
|
const skills = skillsOf(m).map((sk) => sk.name);
|
|
177
177
|
if (skills.length) lines.push("", `Skills: ${skills.join(", ")}.`);
|
|
178
178
|
return lines.join("\n") + "\n";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// What a baba tells agent harnesses, as data: its
|
|
1
|
+
// What a baba tells agent harnesses, as data: its agentic instructions rendered into
|
|
2
2
|
// the manifest; the kernel renders the files from it.
|
|
3
3
|
import type { Manifest } from "../ecs/baba.ts";
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ export interface SkillManifest { name: string; description: string; body: string
|
|
|
8
8
|
/** The skills of a manifest, by name, each checked to name actions and queries that exist. */
|
|
9
9
|
export function skillsOf(m: Manifest): SkillManifest[] {
|
|
10
10
|
const out: SkillManifest[] = [];
|
|
11
|
-
for (const [name, s] of Object.entries(m.
|
|
11
|
+
for (const [name, s] of Object.entries(m.instructions.skills).sort(([a], [b]) => a.localeCompare(b))) {
|
|
12
12
|
for (const u of s.uses ?? []) if (!(u in m.actions) && !(u in m.queries)) throw new Error(`skill ${name} uses ${u}, which is not an action or query of the baba`);
|
|
13
13
|
out.push({ name, description: s.description, body: s.body, uses: [...(s.uses ?? [])] });
|
|
14
14
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
// wrote, which carries the baba's
|
|
1
|
+
// Instructions with the baba down: the shell reads the manifest the last load
|
|
2
|
+
// wrote, which carries the baba's agentic instructions rendered and its harness, and
|
|
3
3
|
// runs the same compiler and the same plan the system runs.
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import { lstat, mkdir, rename, rm, rmdir } from "node:fs/promises";
|
|
@@ -32,7 +32,7 @@ async function look(root: string, path: string): Promise<Seen> {
|
|
|
32
32
|
|
|
33
33
|
async function writeAtomic(path: string, text: string): Promise<void> {
|
|
34
34
|
await mkdir(dirname(path), { recursive: true });
|
|
35
|
-
const tmp = join(dirname(path), `.
|
|
35
|
+
const tmp = join(dirname(path), `.agents-${crypto.randomUUID()}`);
|
|
36
36
|
try {
|
|
37
37
|
await Bun.write(tmp, text);
|
|
38
38
|
await rename(tmp, path);
|
|
@@ -44,7 +44,10 @@ async function writeAtomic(path: string, text: string): Promise<void> {
|
|
|
44
44
|
|
|
45
45
|
/** What the compiler takes for the project at root. */
|
|
46
46
|
export function sources(_root: string, manifest: Manifest, title?: string): CompileInput {
|
|
47
|
-
|
|
47
|
+
// A manifest an older voss wrote names the instructions `promptware`: read as they are.
|
|
48
|
+
const older = (manifest as unknown as { promptware?: Manifest["instructions"] }).promptware;
|
|
49
|
+
const m = manifest.instructions || !older ? manifest : { ...manifest, instructions: older };
|
|
50
|
+
return { program: "voss baba", ...(title !== undefined ? { title } : {}), manifest: m };
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
/** Compiles and plans against the project at root. Writes nothing. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Compiled
|
|
1
|
+
// Compiled agentic instructions onto disk, owning only what it wrote. The state file
|
|
2
2
|
// records the sha256 of every output the compiler last wrote, and of its
|
|
3
3
|
// part of a shared file; comparing it with the files gives each output a
|
|
4
4
|
// status:
|
|
@@ -25,7 +25,7 @@ export type Status = "current" | "stale" | "missing" | "held" | "foreign";
|
|
|
25
25
|
export type Action = "write" | "remove";
|
|
26
26
|
|
|
27
27
|
/** Relative to the project root. */
|
|
28
|
-
export const statePath = ".baba/.voss/
|
|
28
|
+
export const statePath = ".baba/.voss/agents.json";
|
|
29
29
|
|
|
30
30
|
/** What the compiler last wrote: output path to the hash of its text, or of its part of a shared file, and those parts. */
|
|
31
31
|
export interface Written {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// The
|
|
2
|
-
// baba's
|
|
1
|
+
// The agents system: voss gives it to every baba. The know-how is the
|
|
2
|
+
// baba's agentic instructions, rendered into the manifest with its harness
|
|
3
3
|
// declaration; this system's state is the outputs' hashes; its effects
|
|
4
4
|
// compile the know-how for each harness and write the files through the
|
|
5
5
|
// kernel. The system is made per baba, given the manifest of the
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { effect, manifestOf, resource, baba, nameOf, type Manifest, type Baba } from "../ecs/baba.ts";
|
|
8
8
|
import { system, type StateOf } from "../ecs/system.ts";
|
|
9
9
|
import { s } from "../kernel/schema.ts";
|
|
10
|
-
import type { Prompt } from "../
|
|
10
|
+
import type { Prompt } from "../instructions/index.ts";
|
|
11
11
|
import type { Fs } from "../kernel/context.ts";
|
|
12
12
|
import type { Value } from "../ecs/state.ts";
|
|
13
13
|
import maker from "../maker/system.ts";
|
|
@@ -65,9 +65,9 @@ const writtenRow = s.object({ path: s.string(), hash: s.string(), harness: s.str
|
|
|
65
65
|
* nothing. Its rows are resources, not entities: voss's own system takes no
|
|
66
66
|
* ids from the baba's sequence and adds nothing to its entities.
|
|
67
67
|
*/
|
|
68
|
-
export const
|
|
69
|
-
//
|
|
70
|
-
name: "
|
|
68
|
+
export const agentsSystem = (manifest: () => Manifest | null) => system({
|
|
69
|
+
// agentic instructions, declared: the same for every baba; what it compiles is the manifest each implementation is given.
|
|
70
|
+
name: "agents",
|
|
71
71
|
model: {
|
|
72
72
|
/** One row per output of the last plan: its hash on disk then, its harness, its status and what a sync does there. */
|
|
73
73
|
pwWritten: resource(s.array(writtenRow), []),
|
|
@@ -105,9 +105,9 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
105
105
|
},
|
|
106
106
|
}),
|
|
107
107
|
},
|
|
108
|
-
}, (
|
|
109
|
-
type W = StateOf<typeof
|
|
110
|
-
const { pwWritten, pwStatus, pwScan, pwWrite } =
|
|
108
|
+
}, (agents, p) => {
|
|
109
|
+
type W = StateOf<typeof agents>;
|
|
110
|
+
const { pwWritten, pwStatus, pwScan, pwWrite } = agents;
|
|
111
111
|
const setIf = <K extends typeof pwWritten | typeof pwStatus>(w: W, r: K, v: Value<K>) => { if (!same(w.get(r), v)) w.set(r, v); };
|
|
112
112
|
|
|
113
113
|
/** The outputs the state compiles to and their key; null before the baba has a manifest. */
|
|
@@ -148,7 +148,7 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
148
148
|
},
|
|
149
149
|
}),
|
|
150
150
|
|
|
151
|
-
// What the baba's
|
|
151
|
+
// What the baba's agentic instructions compiles to, planned when it changed, written when the harness says auto or a sync asked.
|
|
152
152
|
p.step("plan", (w) => {
|
|
153
153
|
const st = w.get(pwStatus);
|
|
154
154
|
if (w.running(pwWrite) || !st.scanned) return;
|
|
@@ -167,7 +167,7 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
167
167
|
});
|
|
168
168
|
}),
|
|
169
169
|
|
|
170
|
-
p.action("
|
|
170
|
+
p.action("agents-sync", {
|
|
171
171
|
summary: "compile the know-how for each harness and write the files now",
|
|
172
172
|
result: s.object({ asked: s.boolean(), outputs: s.array(writtenRow) }),
|
|
173
173
|
run: (w) => {
|
|
@@ -176,7 +176,7 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
176
176
|
},
|
|
177
177
|
}),
|
|
178
178
|
|
|
179
|
-
p.query("
|
|
179
|
+
p.query("agents-plan", {
|
|
180
180
|
summary: "each output of each harness: its status and what a sync does there",
|
|
181
181
|
result: s.object({ auto: s.boolean(), lastSync: s.nullable(s.integer()), syncs: s.integer(), pending: s.integer(), error: s.nullable(s.string()), outputs: s.array(writtenRow) }),
|
|
182
182
|
read: (w) => {
|
|
@@ -187,7 +187,7 @@ export const promptwareSystem = (manifest: () => Manifest | null) => system({
|
|
|
187
187
|
];
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
-
/** What voss tells agents in every baba, after the baba's own
|
|
190
|
+
/** What voss tells agents in every baba, after the baba's own agentic instructions: its skills. The word on the two systems it adds is the compiler's, in the shell of the context file. */
|
|
191
191
|
const voss: readonly Prompt[] = guide;
|
|
192
192
|
|
|
193
193
|
/** Voss's own systems, by identity: filtered from what a project's docs and tables show. */
|
|
@@ -195,20 +195,20 @@ export const ours = new WeakSet<object>();
|
|
|
195
195
|
export const isOurs = (c: object) => ours.has(c);
|
|
196
196
|
|
|
197
197
|
/** The baba with voss's own systems after its own: what the runtime runs. Every baba gets the shell, the desktop system, over the apps it declares. */
|
|
198
|
-
export function
|
|
198
|
+
export function withInstructions(v: Baba): Baba {
|
|
199
199
|
if (v.systems.some((c) => ours.has(c))) return v;
|
|
200
|
-
for (const name of ["
|
|
200
|
+
for (const name of ["agents", "maker"]) if (v.systems.some((c) => nameOf(c) === name)) throw new Error(`a system of this baba is named ${name}, which voss gives every baba; name it otherwise`);
|
|
201
201
|
// A baba from before the shell was voss's brings its own desktop system, and keeps it; one system or the other, never both.
|
|
202
202
|
const own = v.systems.find((c) => nameOf(c) === "desktop");
|
|
203
203
|
if (own && v.apps.length) throw new Error("this baba declares apps and also has a system named desktop; the shell is voss's now: drop the system and keep baba({ apps })");
|
|
204
204
|
let m: Manifest | null = null;
|
|
205
|
-
const pw =
|
|
205
|
+
const pw = agentsSystem(() => m);
|
|
206
206
|
const catalog = own ? v.apps : catalogOf(v.apps);
|
|
207
207
|
const shell = own ? null : desktop({ apps: catalog });
|
|
208
208
|
ours.add(pw);
|
|
209
209
|
ours.add(maker);
|
|
210
210
|
if (shell) ours.add(shell);
|
|
211
|
-
const composed = baba({ systems: [...v.systems, ...(shell ? [shell] : []), pw, maker], apps: catalog,
|
|
211
|
+
const composed = baba({ systems: [...v.systems, ...(shell ? [shell] : []), pw, maker], apps: catalog, instructions: [...v.instructions, ...voss], harness: v.harness, ...(v.tick !== null ? { tick: v.tick } : {}) });
|
|
212
212
|
m = manifestOf(composed);
|
|
213
213
|
return composed;
|
|
214
214
|
}
|
package/src/baba/check.ts
CHANGED
|
@@ -81,10 +81,10 @@ export async function check(p: Project, stdout: (s: string) => void, stderr: (s:
|
|
|
81
81
|
}
|
|
82
82
|
if (err) stderr(err);
|
|
83
83
|
}
|
|
84
|
-
const mdx = [...new Bun.Glob("
|
|
84
|
+
const mdx = [...new Bun.Glob("agents/**/*.mdx").scanSync({ cwd: p.baba })];
|
|
85
85
|
if (mdx.length) {
|
|
86
|
-
try { await bundleProject(p); stdout("
|
|
87
|
-
catch (error) { failed = true; stderr(`
|
|
86
|
+
try { await bundleProject(p); stdout("agentic instructions MDX: clean\n"); }
|
|
87
|
+
catch (error) { failed = true; stderr(`agentic instructions MDX: ${String(error)}\n`); }
|
|
88
88
|
}
|
|
89
89
|
return failed ? 1 : 0;
|
|
90
90
|
}
|
package/src/baba/config.ts
CHANGED
|
@@ -38,7 +38,7 @@ const note = "Written by voss on every load. Edit .baba/tsconfig.json to extend
|
|
|
38
38
|
export function editorConfig(): string {
|
|
39
39
|
return JSON.stringify({
|
|
40
40
|
"//": note,
|
|
41
|
-
// JSX is React's unless a file says otherwise:
|
|
41
|
+
// JSX is React's unless a file says otherwise: agentic instructions, TSX that renders to Markdown, names its runtime with a pragma.
|
|
42
42
|
// No alias here: the project's tsconfig.json, which extends this, states `@/` relative to itself, and tools that read it resolve it there.
|
|
43
43
|
compilerOptions: { lib: ["ESNext"], ...options, jsx: "react-jsx", jsxImportSource: "react" },
|
|
44
44
|
include: ["guard.d.ts", "assets.d.ts", "../**/*.ts"],
|
|
@@ -205,7 +205,7 @@ export function assetDeclarations(): string {
|
|
|
205
205
|
"// Written by voss on every load: what a baba imports besides code.",
|
|
206
206
|
"// Text: `import guide from \"./guide.md\" with { type: \"text\" }`.",
|
|
207
207
|
...["md", "txt"].map((ext) => `declare module "*.${ext}" {\n const text: string;\n export default text;\n}`),
|
|
208
|
-
`declare module "*.mdx" { const prompt: import("babavoss/
|
|
208
|
+
`declare module "*.mdx" { const prompt: import("babavoss/instructions").Prompt; export default prompt; }`,
|
|
209
209
|
"// Every spec of the baba, found where it lives (systems/NAME/spec.ts, spec.ts): what the Maker's page runs.",
|
|
210
210
|
`declare module "babavoss:specs" { const specs: import("babavoss/spec").Spec<any, any>[]; export default specs; }`,
|
|
211
211
|
"// A stylesheet, bundled with the view and attached to its windows: `import \"./view.css\"`.",
|
|
@@ -242,6 +242,9 @@ export async function writeEditorConfig(p: Project): Promise<string> {
|
|
|
242
242
|
* tsconfig.json and .gitignore that pointed there follow.
|
|
243
243
|
*/
|
|
244
244
|
export async function moveOldDir(p: Project): Promise<void> {
|
|
245
|
+
// The agentic instructions lived in .baba/promptware, their record in .voss/promptware.json: moved once, by name.
|
|
246
|
+
if (existsSync(join(p.baba, "promptware")) && !existsSync(join(p.baba, "agents"))) await rename(join(p.baba, "promptware"), join(p.baba, "agents"));
|
|
247
|
+
if (existsSync(join(p.baba, VOSS, "promptware.json")) && !existsSync(join(p.baba, VOSS, "agents.json"))) await rename(join(p.baba, VOSS, "promptware.json"), join(p.baba, VOSS, "agents.json"));
|
|
245
248
|
const old = join(p.baba, "state");
|
|
246
249
|
if (!existsSync(old) || existsSync(join(p.baba, VOSS))) return;
|
|
247
250
|
await rename(old, join(p.baba, VOSS));
|
package/src/baba/init.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// voss init: a .baba in a directory, its dependencies installed. One entry,
|
|
2
|
-
// one interface, one system to start from, and the
|
|
2
|
+
// one interface, one system to start from, and the agentic instructions: what the baba tells
|
|
3
3
|
// agents, as code, versioned with the project.
|
|
4
4
|
import { basename, join, resolve } from "node:path";
|
|
5
5
|
import { mkdir } from "node:fs/promises";
|
|
@@ -42,7 +42,7 @@ export async function init(dir: string, o: InitOptions): Promise<{ name: string;
|
|
|
42
42
|
await Bun.write(join(baba, "systems", "hello", "index.ts"), systemFile("hello"));
|
|
43
43
|
await Bun.write(join(baba, "systems", "hello", "spec.ts"), systemSpecFile("hello"));
|
|
44
44
|
await Bun.write(join(baba, "spec.test.ts"), specRunnerFile);
|
|
45
|
-
await Bun.write(join(baba, "
|
|
45
|
+
await Bun.write(join(baba, "agents", "context.mdx"), contextFile(o.title ?? name));
|
|
46
46
|
if (o.install !== false) await install(baba);
|
|
47
47
|
return { name, dir };
|
|
48
48
|
}
|
|
@@ -55,34 +55,29 @@ export async function install(baba: string): Promise<void> {
|
|
|
55
55
|
if (code !== 0) throw new Error(`bun install failed in ${baba}: ${(err || out).trim().split("\n").slice(-5).join("; ")}`);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/** The
|
|
59
|
-
export const
|
|
58
|
+
/** The agentic instructions: what the baba tells agents, one .mdx per document under .baba/agents/. The contract is not here: agents get it at `voss baba`. */
|
|
59
|
+
export const contextFile = (title: string) => `export const prompt = { kind: "context" };
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// Markdown and compiled by voss into CLAUDE.md and the skills.
|
|
64
|
-
export default [
|
|
65
|
-
context(md\`
|
|
66
|
-
${title.replaceAll("\\", "\\\\").replaceAll("`", "\\`").replaceAll("$", "\\$")}: say here what this project is, in a sentence or two.
|
|
61
|
+
{/* What this baba tells agents: its section of the context file. A skill is another .mdx beside this one,
|
|
62
|
+
with export const prompt = { kind: "skill", name, description, uses }. A hook is hooks: [{ on, match, action }] here. */}
|
|
67
63
|
|
|
68
|
-
|
|
64
|
+
${title.replaceAll("{", "\\{").replaceAll("}", "\\}").replaceAll("<", "\\<")}: say here what this project is, in a sentence or two.
|
|
69
65
|
|
|
70
|
-
|
|
66
|
+
How to work on it: the conventions, the checks to run before a commit, what not to touch.
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
## Rules
|
|
69
|
+
|
|
70
|
+
- Keep the tests green on every commit.
|
|
75
71
|
`;
|
|
76
72
|
|
|
77
|
-
/** The entry: the baba, its systems listed
|
|
73
|
+
/** The entry: the baba, its systems listed; `harness` for which harnesses get the files. The agentic instructions are the .mdx files under agents/, read by voss. */
|
|
78
74
|
export const entry = `import { baba } from "babavoss";
|
|
79
75
|
import { kit } from "babavoss/kit";
|
|
80
76
|
import hello from "./systems/hello/index.ts";
|
|
81
|
-
import promptware from "./promptware/index.ts";
|
|
82
77
|
|
|
83
78
|
// kit keeps the baba's components, under ui/, as objects: \`voss baba ui\` lists them, \`ui-add\` brings more from shadcn.
|
|
84
79
|
// The apps voss's shell offers; interface.tsx gives each its component.
|
|
85
|
-
export default baba({ systems: [hello, kit], apps: [{ key: "hello", title: "Hello", icon: "hand" }]
|
|
80
|
+
export default baba({ systems: [hello, kit], apps: [{ key: "hello", title: "Hello", icon: "hand" }] });
|
|
86
81
|
`;
|
|
87
82
|
|
|
88
83
|
/** A system: a data model, one action, one query, written as one expression: the declaration, then its pieces made from it. */
|
package/src/baba/node.ts
CHANGED
|
@@ -60,7 +60,7 @@ const ACTIVATE_MS = 10_000;
|
|
|
60
60
|
const QUIET_MS = 300;
|
|
61
61
|
/** How often a reload that waits for a job asks again. */
|
|
62
62
|
const WAIT_MS = 500;
|
|
63
|
-
// What changes under .baba without changing the code: voss's own files and the dependencies.
|
|
63
|
+
// What changes under .baba without changing the code: voss's own files and the dependencies. Instructions is code: a change there reloads.
|
|
64
64
|
const IGNORED = [".voss", "node_modules"];
|
|
65
65
|
|
|
66
66
|
/** Makes the transport a Node loads a project over. The worker is the first; a runtime process is next. */
|
|
@@ -188,7 +188,7 @@ export class Node {
|
|
|
188
188
|
throw err;
|
|
189
189
|
});
|
|
190
190
|
fresh.manifest = loaded.manifest;
|
|
191
|
-
// The contract outlives the run: discovery and
|
|
191
|
+
// The contract outlives the run: discovery and agentic instructions read it with the baba down.
|
|
192
192
|
await mkdir(join(this.project.baba, ".voss"), { recursive: true })
|
|
193
193
|
.then(() => Bun.write(manifestPath(this.project), JSON.stringify(loaded.manifest, null, 2) + "\n"))
|
|
194
194
|
.catch((err) => this.log(`${this.name}: the manifest was not written: ${err instanceof Error ? err.message : String(err)}`));
|
package/src/baba/worker.ts
CHANGED
|
@@ -5,7 +5,8 @@ import { plugin as bunPlugin } from "bun";
|
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import type { ToRuntime, FromRuntime } from "../transport/messages.ts";
|
|
7
7
|
import type { Snapshot } from "../ecs/state.ts";
|
|
8
|
-
import { isBaba } from "../ecs/baba.ts";
|
|
8
|
+
import { isBaba, instructed } from "../ecs/baba.ts";
|
|
9
|
+
import type { Prompt } from "../instructions/index.ts";
|
|
9
10
|
import { openProject } from "./project.ts";
|
|
10
11
|
import { readEnv } from "../runtime/env.ts";
|
|
11
12
|
import { Harness } from "../runtime/harness.ts";
|
|
@@ -20,9 +21,9 @@ const names: Record<string, string> = {
|
|
|
20
21
|
"babavoss": join(root, "index.ts"),
|
|
21
22
|
"babavoss/desktop": join(root, "src/desktop/index.ts"),
|
|
22
23
|
"babavoss/kit": join(root, "src/kit/index.ts"),
|
|
23
|
-
"babavoss/
|
|
24
|
-
"babavoss/
|
|
25
|
-
"babavoss/
|
|
24
|
+
"babavoss/instructions": join(root, "src/instructions/index.ts"),
|
|
25
|
+
"babavoss/instructions/jsx-runtime": join(root, "src/instructions/jsx-runtime.ts"),
|
|
26
|
+
"babavoss/instructions/jsx-dev-runtime": join(root, "src/instructions/jsx-dev-runtime.ts"),
|
|
26
27
|
"babavoss/test": join(root, "src/test/index.ts"),
|
|
27
28
|
"babavoss/bench": join(root, "src/bench/index.ts"),
|
|
28
29
|
};
|
|
@@ -45,7 +46,14 @@ self.onmessage = async (e: MessageEvent<ToWorker>) => {
|
|
|
45
46
|
const project = await openProject(m.dir);
|
|
46
47
|
const mod = (await import(`${project.entry}?t=${Date.now()}`)) as { default?: unknown };
|
|
47
48
|
if (!isBaba(mod.default)) throw new Error(`${project.entry}: the default export is not a baba(); export default baba({ systems: [...] })`);
|
|
48
|
-
|
|
49
|
+
// The agentic instructions: every .mdx under .baba/agents/, in name order, as the builder's bundle has them.
|
|
50
|
+
const docs: Prompt[] = [];
|
|
51
|
+
for (const f of [...new Bun.Glob("agents/*.mdx").scanSync({ cwd: project.baba })].sort()) {
|
|
52
|
+
const d = (await import(join(project.baba, f))) as { default?: Prompt }; // the plugin reads .mdx by its suffix: no query here
|
|
53
|
+
if (!d.default || typeof d.default !== "object" || !("kind" in d.default)) throw new Error(`${f}: not an instruction document; export const prompt = { kind: "context" | "skill", … }`);
|
|
54
|
+
docs.push(d.default);
|
|
55
|
+
}
|
|
56
|
+
h.load(instructed(mod.default, docs), { name: project.name, dir: project.dir, baba: project.baba }, await readEnv(project.baba), m.snapshot, m.prepare);
|
|
49
57
|
} catch (err) {
|
|
50
58
|
post({ type: "failed", error: err instanceof Error ? err.message : String(err) });
|
|
51
59
|
}
|
package/src/build/mdx.ts
CHANGED
|
@@ -6,28 +6,28 @@ import { forbiddenModules, advice } from "./guard.ts";
|
|
|
6
6
|
function promptExport() {
|
|
7
7
|
return (tree: any) => {
|
|
8
8
|
const metadata = tree.body.some((n: any) => n.type === "ExportNamedDeclaration" && n.declaration?.type === "VariableDeclaration" && n.declaration.declarations.some((d: any) => d.id.name === "prompt"));
|
|
9
|
-
if (!metadata) throw new Error('MDX
|
|
9
|
+
if (!metadata) throw new Error('an MDX instruction file needs an exported prompt metadata object');
|
|
10
10
|
const exported = tree.body.find((n: any) => n.type === "ExportDefaultDeclaration");
|
|
11
11
|
if (!exported || exported.declaration.type !== "FunctionDeclaration") throw new Error("MDX must export a document component");
|
|
12
12
|
const component = exported.declaration;
|
|
13
13
|
const id = (name: string) => ({ type: "Identifier", name });
|
|
14
14
|
const literal = (value: string) => ({ type: "Literal", value });
|
|
15
15
|
tree.body[tree.body.indexOf(exported)] = component;
|
|
16
|
-
tree.body.push({ type: "ImportDeclaration", source: literal("babavoss/
|
|
16
|
+
tree.body.push({ type: "ImportDeclaration", source: literal("babavoss/instructions"), specifiers: [{ type: "ImportSpecifier", imported: id("mdxDocument"), local: id("$promptDocument") }] });
|
|
17
17
|
tree.body.push({ type: "ExportDefaultDeclaration", declaration: { type: "CallExpression", callee: id("$promptDocument"), arguments: [id("prompt"), id(component.id.name)], optional: false } });
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export async function compilePromptMdx(value: string, path: string): Promise<string> {
|
|
22
22
|
const result = String(await compile({ value, path }, {
|
|
23
|
-
jsxImportSource: "babavoss/
|
|
23
|
+
jsxImportSource: "babavoss/instructions", format: "mdx", development: false,
|
|
24
24
|
remarkPlugins: [gfm], recmaPlugins: [promptExport],
|
|
25
25
|
}));
|
|
26
26
|
// Bun's bun-target builtin imports can bypass onResolve: apply the same guard after MDX compilation.
|
|
27
27
|
for (const item of new Bun.Transpiler({ loader: "js" }).scanImports(result)) {
|
|
28
28
|
if (forbiddenModules[item.path]) throw new Error(`${path} imports ${item.path}; ${advice(forbiddenModules[item.path]!)}`);
|
|
29
29
|
}
|
|
30
|
-
if (/\b(?:with|assert)\s*\{\s*type\s*:\s*["']macro["']\s*\}/.test(result)) throw new Error(`${path}: macros are not allowed in
|
|
30
|
+
if (/\b(?:with|assert)\s*\{\s*type\s*:\s*["']macro["']\s*\}/.test(result)) throw new Error(`${path}: macros are not allowed in agentic instructions`);
|
|
31
31
|
return result;
|
|
32
32
|
}
|
|
33
33
|
|
package/src/build/project.ts
CHANGED
|
@@ -12,9 +12,14 @@ const harness = join(framework, "src/runtime/worker.ts");
|
|
|
12
12
|
const ENTRY = "/babavoss/entry.ts";
|
|
13
13
|
|
|
14
14
|
export async function bundleProject(project: Project): Promise<string> {
|
|
15
|
+
// The agentic instructions: every .mdx under .baba/agents/, in name order, each a document the baba is given.
|
|
16
|
+
const docs = [...new Bun.Glob("agents/*.mdx").scanSync({ cwd: project.baba })].sort().map((f) => join(project.baba, f));
|
|
15
17
|
const text = [
|
|
16
|
-
`import
|
|
18
|
+
`import entry from ${JSON.stringify(project.entry)};`,
|
|
19
|
+
`import { instructed } from "babavoss";`,
|
|
17
20
|
`import { serve } from ${JSON.stringify(harness)};`,
|
|
21
|
+
...docs.map((f, i) => `import doc${i} from ${JSON.stringify(f)};`),
|
|
22
|
+
`const v = instructed(entry, [${docs.map((_, i) => `doc${i}`).join(", ")}]);`,
|
|
18
23
|
`export default v;`,
|
|
19
24
|
`serve(v);`,
|
|
20
25
|
].join("\n") + "\n";
|
package/src/build/views.ts
CHANGED
|
@@ -27,9 +27,9 @@ export const names: Record<string, string> = {
|
|
|
27
27
|
"babavoss/web": join(framework, "src/web/index.tsx"),
|
|
28
28
|
"babavoss/test": join(framework, "src/test/index.ts"),
|
|
29
29
|
"babavoss/bench": join(framework, "src/bench/index.ts"),
|
|
30
|
-
"babavoss/
|
|
31
|
-
"babavoss/
|
|
32
|
-
"babavoss/
|
|
30
|
+
"babavoss/instructions": join(framework, "src/instructions/index.ts"),
|
|
31
|
+
"babavoss/instructions/jsx-runtime": join(framework, "src/instructions/jsx-runtime.ts"),
|
|
32
|
+
"babavoss/instructions/jsx-dev-runtime": join(framework, "src/instructions/jsx-dev-runtime.ts"),
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
/**
|
package/src/desktop/index.ts
CHANGED
|
@@ -293,7 +293,7 @@ export const vossApps = [
|
|
|
293
293
|
{ key: "maker", title: "Maker", icon: "hammer", group: "Voss" },
|
|
294
294
|
{ key: "state", title: "State", icon: "database", group: "Voss" },
|
|
295
295
|
{ key: "contract", title: "Contract", icon: "square-terminal", group: "Voss" },
|
|
296
|
-
{ key: "
|
|
296
|
+
{ key: "instructions", title: "Instructions", icon: "book-open-text", group: "Voss" },
|
|
297
297
|
{ key: "settings", title: "Settings", icon: "settings", group: "Voss" },
|
|
298
298
|
] as const satisfies readonly AppSpec[];
|
|
299
299
|
export type VossApp = typeof vossApps[number]["key"];
|
package/src/door/core.ts
CHANGED
|
@@ -73,7 +73,7 @@ export interface Index {
|
|
|
73
73
|
error?: string;
|
|
74
74
|
systems: { name: string }[];
|
|
75
75
|
/** What the baba tells agents: whether it has a context, and its skills by name. */
|
|
76
|
-
|
|
76
|
+
instructions: { context: boolean; skills: string[] };
|
|
77
77
|
calls: IndexCall[];
|
|
78
78
|
components: Record<string, { system: string; key?: string; accept?: true }>;
|
|
79
79
|
resources: string[];
|
|
@@ -115,7 +115,7 @@ export function index(m: Manifest, extra: IndexExtra = {}): Index {
|
|
|
115
115
|
...(extra.gen !== undefined ? { gen: extra.gen } : {}),
|
|
116
116
|
...(extra.error ? { error: extra.error } : {}),
|
|
117
117
|
systems: m.systems.map((c) => ({ name: c.name })),
|
|
118
|
-
|
|
118
|
+
instructions: { context: !!m.instructions.context, skills: Object.keys(m.instructions.skills) },
|
|
119
119
|
calls: entries(m).map(({ name, kind, c }) => ({ name, kind, system: c.owner, summary: c.summary, example: exampleCall(name, c, extra.prog) })),
|
|
120
120
|
components: Object.fromEntries(Object.entries(m.components).map(([n, c]) => {
|
|
121
121
|
const key = keyField(c.schema);
|