babavoss 0.12.6 → 0.13.1

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.
Files changed (62) hide show
  1. package/gui/babavoss-web.js +4 -1
  2. package/gui/{chunk-9gmmm54v.js → chunk-qgzmajan.js} +619 -574
  3. package/gui/gui.js +1538 -277
  4. package/gui/theme.css +2007 -1128
  5. package/index.ts +1 -1
  6. package/package.json +5 -5
  7. package/src/{promptware → agents}/compile.ts +23 -31
  8. package/src/{promptware → agents}/define.ts +2 -2
  9. package/src/{promptware → agents}/disk.ts +7 -4
  10. package/src/{promptware → agents}/sync.ts +2 -2
  11. package/src/{promptware → agents}/system.ts +17 -17
  12. package/src/baba/check.ts +3 -3
  13. package/src/baba/config.ts +5 -2
  14. package/src/baba/init.ts +13 -18
  15. package/src/baba/node.ts +2 -2
  16. package/src/baba/project.ts +2 -2
  17. package/src/baba/requirements.ts +39 -0
  18. package/src/baba/setup.ts +30 -0
  19. package/src/baba/worker.ts +13 -5
  20. package/src/build/mdx.ts +4 -4
  21. package/src/build/project.ts +6 -1
  22. package/src/build/views.ts +3 -3
  23. package/src/desktop/desktop.css +35 -39
  24. package/src/desktop/index.ts +9 -7
  25. package/src/desktop/keys.ts +153 -0
  26. package/src/desktop/view.tsx +174 -130
  27. package/src/door/core.ts +2 -2
  28. package/src/ecs/baba.ts +27 -17
  29. package/src/generated/build.ts +1 -1
  30. package/src/gui/gui.tsx +21 -31
  31. package/src/gui/index.ts +6 -2
  32. package/src/gui/{promptware.tsx → instructions.tsx} +12 -12
  33. package/src/gui/levels.tsx +241 -59
  34. package/src/gui/lockup.ts +11 -0
  35. package/src/gui/settings.tsx +241 -0
  36. package/src/gui/setup.tsx +77 -0
  37. package/src/gui/theme.css +205 -58
  38. package/src/gui/theme.ts +9 -5
  39. package/src/gui/voss-settings.ts +48 -0
  40. package/src/gui/wizard.tsx +17 -40
  41. package/src/guide/add-a-desktop.mdx +7 -7
  42. package/src/guide/index.ts +4 -4
  43. package/src/guide/write-a-system.mdx +3 -3
  44. package/src/guide/{write-promptware.mdx → write-agentic-instructions.mdx} +17 -24
  45. package/src/http/server.ts +140 -17
  46. package/src/{prompt → instructions}/evals.ts +1 -1
  47. package/src/{prompt → instructions}/index.ts +9 -8
  48. package/src/{prompt → instructions}/jsx-runtime.ts +3 -3
  49. package/src/mcp/main.ts +3 -3
  50. package/src/mcp/tools.ts +4 -4
  51. package/src/runtime/harness.ts +2 -2
  52. package/src/server/edge.ts +31 -3
  53. package/src/server/main.ts +2 -1
  54. package/src/server/messages.ts +47 -7
  55. package/src/shell/run.ts +18 -18
  56. package/src/spec/index.ts +1 -1
  57. package/src/web/core.tsx +2 -2
  58. package/src/web/index.tsx +1 -1
  59. package/src/desktop/bob.ts +0 -76
  60. /package/src/{promptware → agents}/markdown.d.ts +0 -0
  61. /package/src/{prompt → instructions}/jsx-dev-runtime.ts +0 -0
  62. /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.12.6",
3
+ "version": "0.13.1",
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
- "./prompt": "./src/prompt/index.ts",
18
- "./prompt/jsx-runtime": "./src/prompt/jsx-runtime.ts",
19
- "./prompt/jsx-dev-runtime": "./src/prompt/jsx-dev-runtime.ts",
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
- "./prompt/register": "./src/build/mdx-register.ts"
23
+ "./instructions/register": "./src/build/mdx-register.ts"
24
24
  },
25
25
  "files": [
26
26
  "bin",
@@ -1,15 +1,15 @@
1
- // Promptware to harness files, as text. Pure: the manifest, with the baba's
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
- // Promptware is know-how; the contract teaches itself at the doors, the
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 promptware's: it is the machine's. The permissions, in a file the
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(["promptware", "maker"]);
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 promptware lives, relative to the project. */
35
- export const SOURCE = ".baba/promptware";
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 promptware: a checkout makes its own with `voss baba promptware sync` (a landing does),
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/promptware: `voss baba promptware sync` writes them, git keeps none",
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.promptware.hooks) {
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, []);
@@ -131,32 +131,25 @@ const quoted = (v: unknown) => `'${JSON.stringify(v).replaceAll("'", `'\\''`)}'`
131
131
  /** The CLI line of a call with a literal example of its args. */
132
132
  const cliLine = (prog: string, name: string, args: Json) => `${prog} ${name} ${quoted(exampleOf(args))}`;
133
133
 
134
- /** The shell around the baba's context: the header, the context, what a baba is and how to reach it at its doors, an example, the two systems voss adds, the skills by name. */
134
+ /**
135
+ * The instruction file: the title, then voss's own section, short, what a baba is, that the agent is its author and
136
+ * which skills say how, and how the baba is reached by MCP and at the shell, with one real example; then the baba's
137
+ * own context, as it wrote it.
138
+ */
135
139
  function instructionFile(input: CompileInput): string {
136
140
  const prog = input.program;
137
141
  const m = input.manifest;
142
+ const skills = skillsOf(m).map((sk) => sk.name);
138
143
  const lines = [
139
- `<!-- Generated by ${prog} from the baba's promptware. Do not edit: a hand edit is held and never overwritten. Edit the promptware; it syncs. -->`,
144
+ `<!-- 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
145
  `# ${input.title ?? prog}`,
141
- ];
142
- const context = m.promptware.context?.trim();
143
- if (context) lines.push("", context);
144
- lines.push(
145
- "", "## This baba", "",
146
- "A baba is this project's own program: systems and apps over one state of entities and resources, moved by the actions its systems declare and read by their queries; the apps show it to people. Its code is under `.baba/`, and voss runs it.",
146
+ "", "## Baba Voss", "",
147
+ "This project has a baba: its own program, under `.baba/`, run by voss. A baba is systems over one state of entities and resources, and apps that show it to people. Each system declares the components it owns, the actions that move it and the queries that read it, and reaches outside only through effects; a spec beside it proves it. Voss serves the baba from a service on loopback, `http://localhost:47802/` by default; `voss service status` gives the configured URL.",
147
148
  "",
148
- `The contract is live: \`${prog}\` answers the index, every action and query with its system, kind, summary and an example, and \`${prog} NAME --schema\` one call's args and result schemas. Never rely on a copied list: a system added or changed shows up there within seconds.`,
149
+ `You are this baba's author. Use it through its contract, and change it when it falls short: a capability it lacks is a system to write, a mistake an agent made is a sentence for its instructions. The skills say how, read each when its description fits: ${skills.join(", ")}.`,
149
150
  "",
150
- `- Call one with JSON: \`${prog} NAME '{"arg": value}'\` (flags exist for people). Every answer is one line of JSON; an error is one line on stderr, \`{"error":{"code","message","retry"}}\`, and the exit code says whose: 2 your arguments, 1 it failed, 3 time ran out, 4 gone.`,
151
- `- An entity in an answer is a number: \`${prog} state N\` reads it, \`${prog} follow N [--until COMPONENT]\` waits on it.`,
152
- `- \`${prog} run\` sends several calls in one turn: lines of \`{"NAME": args}\`, \`"$0.entity"\` to reference an earlier answer.`,
153
- `- \`${prog} state\` reads the state; \`${prog} raw …\` writes it, only when no action does it. \`--help\` after any word explains it.`,
154
- `- \`--key K\` on an action makes a retry safe: the baba remembers the answer for a day and answers it again without running. A secret is never on the command line: \`--token @-\` reads it from stdin. An answer over 20 000 characters goes to \`.baba/.voss/out/\` and the line says where.`,
155
- "",
156
- "If your harness has this baba's MCP server (`voss baba mcp`), its `manifest`, `call`, `read`, `follow`, `watch` and `state` tools are the same six operations: prefer them.",
157
- "",
158
- "Voss serves this baba from a service on loopback, `http://localhost:47802/` by default. `voss baba start` opens it there, installing the service if need be; `voss service status` gives the configured URL and whether it runs. Never guess another port: read the status.",
159
- );
151
+ `The contract is live; never rely on a copied list. By MCP, when your harness has this baba's server (\`voss baba mcp\`): \`manifest\`, \`call\`, \`read\`, \`follow\`, \`watch\` and \`state\`; prefer them. At the shell: \`${prog}\` lists every action and query with an example, \`${prog} NAME '{"arg": value}'\` calls one, \`${prog} NAME --schema\` gives its schemas, \`${prog} state N\` reads an entity an answer named and \`${prog} follow N [--until COMPONENT]\` waits on it, \`${prog} run\` sends several calls, \`${prog} state\` reads the state and \`${prog} raw …\` writes it, only when no action does. Every answer is one line of JSON; an error is one line on stderr, \`{"error":{"code","message","retry"}}\`, and the exit code says whose: 2 your arguments, 1 it failed, 3 time ran out, 4 gone. \`--key K\` on an action makes a retry safe; a secret is never on the command line, \`--token @-\` reads it from stdin; an answer over 20 000 characters goes to \`.baba/.voss/out/\` and the line says where.`,
152
+ ];
160
153
  const first = Object.entries(m.actions).find(([, d]) => !VOSS.has(d.owner));
161
154
  if (first) {
162
155
  const [name, d] = first;
@@ -172,9 +165,8 @@ function instructionFile(input: CompileInput): string {
172
165
  ...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
166
  );
174
167
  }
175
- lines.push("", "Voss adds two systems to this baba. `promptware` compiles what the baba tells agents, its context, skills and hooks written as code under `.baba/promptware/`, into each harness's files: `CLAUDE.md`, `AGENTS.md`, the skills, Claude Code's permissions and hooks. Those are generated: edit the promptware, 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
- const skills = skillsOf(m).map((sk) => sk.name);
177
- if (skills.length) lines.push("", `Skills: ${skills.join(", ")}.`);
168
+ const context = m.instructions.context?.trim();
169
+ if (context) lines.push("", context);
178
170
  return lines.join("\n") + "\n";
179
171
  }
180
172
 
@@ -1,4 +1,4 @@
1
- // What a baba tells agent harnesses, as data: its promptware rendered into
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.promptware.skills).sort(([a], [b]) => a.localeCompare(b))) {
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
- // Promptware with the baba down: the shell reads the manifest the last load
2
- // wrote, which carries the baba's promptware rendered and its harness, and
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), `.promptware-${crypto.randomUUID()}`);
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
- return { program: "voss baba", ...(title !== undefined ? { title } : {}), manifest };
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 promptware onto disk, owning only what it wrote. The state file
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/promptware.json";
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 promptware system: voss gives it to every baba. The know-how is the
2
- // baba's promptware, rendered into the manifest with its harness
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 "../prompt/index.ts";
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 promptwareSystem = (manifest: () => Manifest | null) => system({
69
- // promptware, declared: the same for every baba; what it compiles is the manifest each implementation is given.
70
- name: "promptware",
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
- }, (promptware, p) => {
109
- type W = StateOf<typeof promptware>;
110
- const { pwWritten, pwStatus, pwScan, pwWrite } = promptware;
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 promptware compiles to, planned when it changed, written when the harness says auto or a sync asked.
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("promptware-sync", {
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("promptware-plan", {
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 promptware: its skills. The word on the two systems it adds is the compiler's, in the shell of the context file. */
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 withPromptware(v: Baba): Baba {
198
+ export function withInstructions(v: Baba): Baba {
199
199
  if (v.systems.some((c) => ours.has(c))) return v;
200
- for (const name of ["promptware", "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`);
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 = promptwareSystem(() => m);
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, promptware: [...v.promptware, ...voss], harness: v.harness, ...(v.tick !== null ? { tick: v.tick } : {}) });
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("promptware/**/*.mdx").scanSync({ cwd: p.baba })];
84
+ const mdx = [...new Bun.Glob("agents/**/*.mdx").scanSync({ cwd: p.baba })];
85
85
  if (mdx.length) {
86
- try { await bundleProject(p); stdout("promptware MDX: clean\n"); }
87
- catch (error) { failed = true; stderr(`promptware MDX: ${String(error)}\n`); }
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
  }
@@ -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: promptware, TSX that renders to Markdown, names its runtime with a pragma.
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/prompt").Prompt; export default prompt; }`,
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 promptware: what the baba tells
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, "promptware", "index.ts"), promptwareFile(o.title ?? name));
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 promptware: what the baba tells agents, as code. The contract is not here: agents get it at `voss baba`. */
59
- export const promptwareFile = (title: string) => `import { context, md } from "babavoss/prompt";
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
- // What this baba tells agents: its section of the context file, its skills
62
- // (one skill(...) per .tsx beside this file), its hooks. Code, rendered to
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
- How to work on it: the conventions, the checks to run before a commit, what not to touch.
64
+ ${title.replaceAll("{", "\\{").replaceAll("}", "\\}").replaceAll("<", "\\<")}: say here what this project is, in a sentence or two.
69
65
 
70
- ## Rules
66
+ How to work on it: the conventions, the checks to run before a commit, what not to touch.
71
67
 
72
- - Keep the tests green on every commit.
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, its promptware; `harness` for which harnesses get the files. */
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" }], promptware });
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. Promptware is code: a change there reloads.
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 promptware read it with the baba down.
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)}`));
@@ -21,8 +21,8 @@ export interface Project {
21
21
  }
22
22
 
23
23
  const NAME = /^[a-z][a-z0-9-]*$/;
24
- /** The page's own first segments: a baba's address is `/NAME/…`, so no baba is named one of them. */
25
- export const PAGE_WORDS = new Set(["api", "assets", "view", "ws"]);
24
+ import { PAGE_WORDS } from "./requirements.ts";
25
+ export { PAGE_WORDS };
26
26
 
27
27
  interface Pkg { name?: unknown; baba?: unknown; dependencies?: unknown; devDependencies?: unknown }
28
28
 
@@ -0,0 +1,39 @@
1
+ // What voss needs of a baba before it runs it: a set of properties, each read
2
+ // from the baba's .baba/package.json and written back there by the setup
3
+ // wizard. A folder voss remembers is ready when every one is set and valid;
4
+ // until then it is set up, not run, and the picker opens its wizard in place
5
+ // of its page. A folder with no .baba lacks them all, and setting them up
6
+ // makes its .baba first. For now there is one: the name.
7
+ // Pure, so the page checks a name as the kernel will; reading and writing a
8
+ // folder is setup.ts's.
9
+
10
+ /** The page's own first segments: a baba's address is `/NAME/…`, so no baba is named one of them. */
11
+ export const PAGE_WORDS = new Set(["api", "assets", "view", "ws"]);
12
+
13
+ /** The properties voss needs of every baba, in the order the wizard asks for them. */
14
+ export const requirements = [
15
+ {
16
+ key: "name", title: "Name",
17
+ summary: "The baba's address and its name at the shell: voss serves it at /NAME/, and the CLI reaches it as voss NAME from anywhere.",
18
+ },
19
+ ] as const;
20
+ export type Requirement = typeof requirements[number]["key"];
21
+ /** What the wizard has to set: each requirement's value. */
22
+ export type RequirementValues = Partial<Record<Requirement, string>>;
23
+
24
+ const NAME = /^[a-z][a-z0-9-]*$/;
25
+
26
+ /** Why a value will not do for a requirement, or null when it does; `taken` are the names other babas already have. */
27
+ export function requirementError(key: Requirement, value: string, taken: readonly string[] = []): string | null {
28
+ if (key === "name") {
29
+ if (!value) return "A baba needs a name.";
30
+ if (!NAME.test(value)) return "Lowercase letters, digits and dashes, starting with a letter.";
31
+ if (PAGE_WORDS.has(value)) return `/${value}/ is the page's own address; choose another.`;
32
+ if (taken.includes(value)) return `Another baba is already called ${value}.`;
33
+ }
34
+ return null;
35
+ }
36
+
37
+ /** A folder's name as a baba's name: what the wizard offers first. */
38
+ export const nameFrom = (dir: string) => (dir.split("/").filter(Boolean).pop() ?? "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/^[^a-z]+/, "");
39
+
@@ -0,0 +1,30 @@
1
+ // A folder's standing against what voss requires of a baba, read from its
2
+ // .baba/package.json, and the requirements written back there.
3
+ import { join, resolve } from "node:path";
4
+ import { exists } from "./project.ts";
5
+ import { nameFrom, requirementError, requirements, type Requirement, type RequirementValues } from "./requirements.ts";
6
+
7
+ /** Where a folder stands: whether it has a .baba, the requirements it lacks, and the values it already has. */
8
+ export interface Setup { dir: string; baba: boolean; missing: Requirement[]; values: RequirementValues; suggested: RequirementValues }
9
+
10
+ /** Reads a folder's .baba/package.json for the requirements; a package.json that does not parse is an error, not a missing value. */
11
+ export async function setupOf(dir: string): Promise<Setup> {
12
+ dir = resolve(dir);
13
+ const file = join(dir, ".baba", "package.json");
14
+ const suggested = { name: nameFrom(dir) };
15
+ if (!(await exists(file))) return { dir, baba: false, missing: requirements.map((r) => r.key), values: {}, suggested };
16
+ const pkg = await Bun.file(file).json() as { name?: unknown; baba?: { name?: unknown } };
17
+ const name = typeof pkg.baba?.name === "string" ? pkg.baba.name : typeof pkg.name === "string" ? pkg.name : undefined;
18
+ const values: RequirementValues = name === undefined ? {} : { name };
19
+ const missing = requirements.map((r) => r.key).filter((k) => values[k] === undefined || requirementError(k, values[k]!) !== null);
20
+ return { dir, baba: true, missing, values, suggested };
21
+ }
22
+
23
+ /** Writes the requirements into a .baba that exists, leaving everything else in its package.json as it was. */
24
+ export async function writeRequirements(dir: string, values: RequirementValues): Promise<void> {
25
+ const file = join(resolve(dir), ".baba", "package.json");
26
+ const pkg = await Bun.file(file).json() as Record<string, unknown> & { baba?: Record<string, unknown> };
27
+ const baba = { ...(typeof pkg.baba === "object" && pkg.baba ? pkg.baba : {}) };
28
+ if (values.name !== undefined) baba.name = values.name;
29
+ await Bun.write(file, JSON.stringify({ ...pkg, baba }, null, 2) + "\n");
30
+ }
@@ -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/prompt": join(root, "src/prompt/index.ts"),
24
- "babavoss/prompt/jsx-runtime": join(root, "src/prompt/jsx-runtime.ts"),
25
- "babavoss/prompt/jsx-dev-runtime": join(root, "src/prompt/jsx-dev-runtime.ts"),
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
- h.load(mod.default, { name: project.name, dir: project.dir, baba: project.baba }, await readEnv(project.baba), m.snapshot, m.prepare);
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 promptware needs an exported prompt metadata object');
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/prompt"), specifiers: [{ type: "ImportSpecifier", imported: id("mdxDocument"), local: id("$promptDocument") }] });
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/prompt", format: "mdx", development: false,
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 promptware`);
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