dreamteamer 0.29.0 → 0.31.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 +47 -153
- package/collections/collections.collection.yaml +0 -9
- package/package.json +14 -2
- package/skills/using-dreamteamer/SKILL.md +10 -8
- package/skills/using-dreamteamer/references/before-you-build.md +4 -3
- package/skills/using-dreamteamer/references/commands.md +3 -3
- package/skills/using-dreamteamer/references/extensions.md +60 -0
- package/skills/using-dreamteamer/references/sessions.md +4 -5
- package/skills/using-dreamteamer/references/skills.md +3 -5
- package/src/api.d.ts +109 -0
- package/src/api.js +70 -0
- package/src/checkout.js +105 -330
- package/src/cli.js +60 -248
- package/src/collections-cli.js +16 -165
- package/src/compile.js +110 -156
- package/src/extensions.js +135 -0
- package/src/filter.js +1 -1
- package/src/harnesses.js +82 -135
- package/src/init.js +9 -3
- package/src/records-api.d.ts +95 -0
- package/src/records-api.js +40 -0
- package/src/runtime.js +2 -2
- package/src/schema-ops.js +16 -7
- package/collections/containers.collection.yaml +0 -81
- package/collections/images.collection.yaml +0 -46
- package/collections/proofs.collection.yaml +0 -96
- package/skills/using-dreamteamer/references/exporting.md +0 -53
- package/skills/using-dreamteamer/references/proofs.md +0 -435
- package/skills/using-dreamteamer/references/worktrees.md +0 -235
- package/src/containers.js +0 -492
- package/src/export-notebooklm.js +0 -502
- package/src/land.js +0 -743
- package/src/prove.js +0 -1922
- package/src/server.js +0 -481
package/README.md
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
# dreamteamer
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**dreamteamer is a modular AI workspace builder.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
untracked and unshared. And memory *is* context, which is the single biggest lever on what your agent
|
|
7
|
-
decides and how well it does it. So the most consequential thing in your setup is the one you have the
|
|
8
|
-
least access to.
|
|
5
|
+
It gives your coding agents a structured, modular memory. Instead of hiding context in unmanaged prose, dreamteamer stores memory as **plain markdown files with a schema** directly in your git repo. Agents read it natively, and you can browse it as tables, boards, and forms.
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
No server, no account, no telemetry. Just a lightweight npm package.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Structured Memory as Files**: A record is just a markdown file with YAML frontmatter. Your agent opens it like any other file.
|
|
14
|
+
- **Strict Validation**: Schemas ensure your agents use agreed-upon terminology. Invalid links, wrong types, or unknown fields are rejected before they touch the disk.
|
|
15
|
+
- **Agent Agnostic**: Author a skill or schema once, use it everywhere. Core compiles to Claude Code, Cursor, Gemini CLI, and more.
|
|
16
|
+
- **NPM Modularity**: Distribute domain knowledge, skills, and agents using the npm ecosystem you already know.
|
|
17
|
+
- **Visual Editor**: The [VS Code extension](https://github.com/dreamteamer/dreamteamer-vscode) gives you a powerful UI (tables, boards, forms) over your plain text files.
|
|
18
|
+
|
|
19
|
+
## Getting Started
|
|
20
|
+
|
|
21
|
+
Scaffold your AI workspace in seconds:
|
|
12
22
|
|
|
13
23
|
```bash
|
|
14
24
|
npm i dreamteamer
|
|
@@ -18,15 +28,10 @@ npx dreamteamer check # prove every record and every link is intact
|
|
|
18
28
|
npx dreamteamer help # the full command surface
|
|
19
29
|
```
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
## How It Works
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
A record is a file. That's the whole trick.
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
data/meetings/2026/07/kickoff.meeting.md
|
|
29
|
-
```
|
|
33
|
+
### 1. Define Records
|
|
34
|
+
A record is a file inside your collection directory (e.g., `data/meetings/2026/07/kickoff.meeting.md`):
|
|
30
35
|
|
|
31
36
|
```yaml
|
|
32
37
|
---
|
|
@@ -38,160 +43,49 @@ project: projects/apollo
|
|
|
38
43
|
Ada walked through the constraints. Lin owns the spec by Friday.
|
|
39
44
|
```
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
But `attendees` isn't a string — it's a link. `dreamteamer check` proves every one of them resolves,
|
|
45
|
-
and renaming `contacts/ada` updates everything pointing at it. A write with an unknown field, a wrong
|
|
46
|
-
type, or a reference to a record that doesn't exist is **rejected before it touches disk**.
|
|
47
|
-
|
|
48
|
-
**A schema is an agreement about what things are called.** Shared terminology with guardrails — not a
|
|
49
|
-
cage, because it stays negotiable. You change it by saying so:
|
|
50
|
-
|
|
51
|
-
> *"From here on a client has a renewal date, and it's a date."*
|
|
52
|
-
|
|
53
|
-
That's a schema update and a data migration, and it's an **explicit, reviewable event** rather than
|
|
54
|
-
silent drift. Once it exists you get the column in a table, the field in a form, validation, sorting
|
|
55
|
-
and aggregation — all of it falling out of having said what the thing is.
|
|
56
|
-
|
|
57
|
-
The shape of a record is deliberately dull, because dull is what survives:
|
|
58
|
-
|
|
59
|
-
- records are `<id>.<suffix>.<ext>` files; **the id is the path** inside the collection folder
|
|
60
|
-
- references are `<collection>/<id>` — always qualified, greppable, never a bare name
|
|
61
|
-
- a collection may be scoped under a **declared namespace** — `health/doctors/dana-levi`, stored in
|
|
62
|
-
`data/health/doctors/`. The default namespace is the empty prefix, so `tasks/kickoff` is unchanged
|
|
63
|
-
- a write lands on disk; `dreamteamer commit` publishes it, one commit per repo
|
|
64
|
-
- schemas are JSON Schema in a YAML file, one per collection
|
|
65
|
-
|
|
66
|
-
### Machine-specific references
|
|
67
|
-
|
|
68
|
-
Some things a record points at only exist on one machine — a synced Drive folder, an external disk,
|
|
69
|
-
a checkout somewhere else. Those are written as **templates**, never as absolute paths:
|
|
70
|
-
|
|
71
|
-
```yaml
|
|
72
|
-
source_file: ${env:FILES_FOLDER}/2026/q3.pdf
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
Three variables, borrowing VS Code's grammar: `${env:NAME}` — declared in `dreamteamer.vars` in
|
|
76
|
-
`package.json`, valued in the gitignored `.env` (an empty or whitespace-only value counts as no
|
|
77
|
-
value at all) — plus `${workspaceFolder}` and `${userHome}`. One verb renders them:
|
|
46
|
+
### 2. Enforce Schemas
|
|
47
|
+
Your agents read the file directly, but `dreamteamer check` ensures that every reference (like `contacts/ada`) actually resolves. **A schema is an agreement about what things are called.**
|
|
78
48
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
npx dreamteamer resolve <collection>/<id> <field> # render what a record already holds
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
**Templates are ordinary data — write them literally; nothing substitutes until `resolve` is
|
|
85
|
-
called.** `get`, `list`, `check` and every harness see the template verbatim, which is exactly what
|
|
86
|
-
makes the record mean the same thing on every machine instead of quietly meaning two things. An
|
|
87
|
-
undeclared key and a declared-but-absent one are different errors, and `compile` warns — by name,
|
|
88
|
-
never by value — when a declared var has nothing behind it here.
|
|
49
|
+
### 3. Share & Compose
|
|
50
|
+
Because dreamteamer uses npm, you can install domain modules containing collections, skills, and agents. If a module doesn't perfectly fit your needs, you don't fork it—you adapt it locally by overriding just the schema fields you need to change.
|
|
89
51
|
|
|
90
|
-
##
|
|
52
|
+
## Programmatic Usage
|
|
91
53
|
|
|
92
|
-
|
|
93
|
-
data is arbitrary functionality — but composing that with no module system is where most setups stall.
|
|
54
|
+
Use dreamteamer in your own scripts or apps:
|
|
94
55
|
|
|
95
|
-
|
|
56
|
+
```javascript
|
|
57
|
+
import { openWorkspace, Store } from 'dreamteamer';
|
|
96
58
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
UI views — and skills and agents are treated as exactly what they are: **memory that loads into
|
|
100
|
-
context**, living in the same module structure as everything else, in a standard your tooling already
|
|
101
|
-
understands.
|
|
59
|
+
const ws = await openWorkspace('.'); // no compile, no writes
|
|
60
|
+
const store = new Store(ws);
|
|
102
61
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
modules/<name>/ # lives in this repo
|
|
107
|
-
git_modules/<name>/ # lives in its own repo
|
|
108
|
-
node_modules/<name>/ # published package
|
|
62
|
+
for (const { id, fields } of store.readAll('notes')) {
|
|
63
|
+
console.log(id, fields.title);
|
|
64
|
+
}
|
|
109
65
|
```
|
|
110
66
|
|
|
111
|
-
|
|
112
|
-
module and use it in the same workspace at the same time.
|
|
113
|
-
|
|
114
|
-
A published package or a git clone may carry **several** modules: when its root has a `modules/`
|
|
115
|
-
folder, each `modules/<name>/` with a `dreamteamer` key in its `package.json` is a module on that
|
|
116
|
-
channel, and the root itself is never compiled. One `npm install` then delivers a whole family, and the
|
|
117
|
-
workspace keeps what it wants — a bare module name in `dreamteamer.disable` drops a module before
|
|
118
|
-
compile looks at it (an entry with a slash, `<module>/<entity>`, still disables one entity):
|
|
119
|
-
|
|
120
|
-
```json
|
|
121
|
-
"dreamteamer": { "disable": ["recordings", "introspection"] }
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Disabling a module another one declares in `dependencies` is refused, naming what is present. The
|
|
125
|
-
workspace's own `modules/*` never nest.
|
|
126
|
-
|
|
127
|
-
Sources live **flat at a module root** — `modules/crm/skills/`, beside `package.json` — and a folder
|
|
128
|
-
at a module root that isn't a known kind is a compile error rather than a silent skip.
|
|
129
|
-
|
|
130
|
-
### Modules are not rigid
|
|
131
|
-
|
|
132
|
-
This is the part that differs from npm on purpose.
|
|
133
|
-
|
|
134
|
-
Installing a module into a workspace that already has opinions — its own idea of what a `contact` is —
|
|
135
|
-
is a **negotiation, not an overwrite**. Four workspaces wanted a CRM and all four wanted a different
|
|
136
|
-
`contacts`. A hard import would force one answer and make every divergence a fork.
|
|
137
|
-
|
|
138
|
-
Two same-name collections is a compile error that names both descriptors and tells you the move:
|
|
139
|
-
declare `extends: <module>/<collection>` and overlay only what differs. Because every schema is one
|
|
140
|
-
small YAML file, adapting is cheap — read it, change what doesn't fit, and the diff shows exactly what
|
|
141
|
-
you agreed to.
|
|
142
|
-
|
|
143
|
-
So domain modules are **recipes you copy and adapt, not packages you install**, and divergence is the
|
|
144
|
-
normal case rather than a failure.
|
|
145
|
-
|
|
146
|
-
## Every harness, one source
|
|
147
|
-
|
|
148
|
-
`compile` writes `.dreamteamer/` — the single runtime read surface — and from there into per-harness
|
|
149
|
-
adapters: Claude Code, Codex, Pi, Gemini CLI, Cursor. Author a skill once; every agent you run sees it.
|
|
150
|
-
|
|
151
|
-
## The editor
|
|
152
|
-
|
|
153
|
-
Extension id `dreamteamer.dreamteamer-vscode` (Marketplace · Open VSX). `init` and `compile` write the
|
|
154
|
-
`.vscode/extensions.json` recommendation, and `dt status` reports whether it is active.
|
|
155
|
-
|
|
156
|
-
[dreamteamer-vscode](https://github.com/dreamteamer/dreamteamer-vscode) gives you tables, boards,
|
|
157
|
-
calendars, maps, forms and a data-model designer over the same files — and it loads **the engine your
|
|
158
|
-
workspace pins**, so the editor, the CLI and any agent session are provably running the same code.
|
|
159
|
-
|
|
160
|
-
## Docs
|
|
161
|
-
|
|
162
|
-
This is an agent-native tool, so its documentation is shipped as skills the agent loads on demand —
|
|
163
|
-
and you can read them like any other file:
|
|
67
|
+
## Extensions
|
|
164
68
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
- [`docs/one-skill-blast-radius.md`](docs/one-skill-blast-radius.md) — the 0.16.0 skill
|
|
170
|
-
consolidation: what breaks for a consumer, what to grep for, which claims were verified live
|
|
171
|
-
- [`docs/repos-and-modules.md`](docs/repos-and-modules.md) — attached repos vs modules, and why they
|
|
172
|
-
have different homes
|
|
173
|
-
- [`docs/namespaces-blast-radius.md`](docs/namespaces-blast-radius.md) — scoping collections under a
|
|
174
|
-
namespace (`health/doctors`), what it costs consumers, and why the default namespace is transparent
|
|
175
|
-
- [`UPDATING.md`](UPDATING.md) — what to do when upgrading, one section per release
|
|
69
|
+
An extension adds verbs, source kinds or harnesses through one contract
|
|
70
|
+
([`references/extensions.md`](skills/using-dreamteamer/references/extensions.md)). It can be a
|
|
71
|
+
workspace module (`modules/<id>/package.json` declaring `dreamteamer.extension`) or an installed
|
|
72
|
+
dependency.
|
|
176
73
|
|
|
177
|
-
|
|
74
|
+
Behaviour proofs, worktrees, the REST API, the NotebookLM exporter and the local Docker host left core
|
|
75
|
+
in 0.31.0 and return as extensions. None is published yet.
|
|
178
76
|
|
|
179
|
-
|
|
180
|
-
and no account. Not a note-taking app — it's the layer underneath one.
|
|
77
|
+
## Agent-Native Documentation
|
|
181
78
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
79
|
+
Because this is an agent-native tool, documentation is shipped as skills your agent loads on demand:
|
|
80
|
+
- [`skills/using-dreamteamer`](skills/using-dreamteamer) — Core skill for working with records and modeling the workspace.
|
|
81
|
+
- [`docs/`](docs/) — Deeper architectural context, upgrade guides, and rationale.
|
|
185
82
|
|
|
186
83
|
## Contributing
|
|
187
84
|
|
|
188
|
-
|
|
189
|
-
this is a small, deliberately lean codebase (`npm run metrics` enforces size budgets), and it's better
|
|
190
|
-
to agree on the shape first.
|
|
85
|
+
We welcome issues and discussions! For anything larger than a typo, please open a discussion before submitting a PR. This is a deliberately lean codebase and we prefer to agree on shape first. `npm run verify` enforces our size and test budgets.
|
|
191
86
|
|
|
192
|
-
|
|
193
|
-
zero dependencies, a few seconds). See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
87
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
194
88
|
|
|
195
89
|
## License
|
|
196
90
|
|
|
197
|
-
Apache-2.0 © 2026 Gilad Khen.
|
|
91
|
+
[Apache-2.0](LICENSE) © 2026 Gilad Khen.
|
|
@@ -67,15 +67,6 @@ schema:
|
|
|
67
67
|
have lived in `modules/<module>/<kind>/` since the 2026-08-05 flatten, and a `system/`
|
|
68
68
|
prefix is only how `runtime.js` recognises a RUNTIME collection in a descriptor compiled
|
|
69
69
|
by a pre-flatten engine.
|
|
70
|
-
driver:
|
|
71
|
-
type: string
|
|
72
|
-
description: >-
|
|
73
|
-
The name of an engine DRIVER that answers this collection's verbs instead of a folder of
|
|
74
|
-
files — `docker` is the one shipped (src/containers.js: `containers`, `images`). A driver
|
|
75
|
-
collection has no records on disk: its derived `path` names a folder that never exists,
|
|
76
|
-
so check, commit and the store read zero records and never write one; the CLI and the REST
|
|
77
|
-
route dispatch to the driver first. A string checked against the drivers the engine has,
|
|
78
|
-
not an enum — one implementation, and a second is a code change, not a vocabulary change.
|
|
79
70
|
codec:
|
|
80
71
|
type: string
|
|
81
72
|
enum: [md, yaml, json, file]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dreamteamer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "A workspace compiler for coding agents — schema-validated records as plain files over git, compiled into every harness",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Gilad Khen <giladkhen@gmail.com>",
|
|
@@ -28,6 +28,19 @@
|
|
|
28
28
|
"engines": {
|
|
29
29
|
"node": ">=20"
|
|
30
30
|
},
|
|
31
|
+
"main": "./src/api.js",
|
|
32
|
+
"types": "./src/api.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./src/api.d.ts",
|
|
36
|
+
"default": "./src/api.js"
|
|
37
|
+
},
|
|
38
|
+
"./records": {
|
|
39
|
+
"types": "./src/records-api.d.ts",
|
|
40
|
+
"default": "./src/records-api.js"
|
|
41
|
+
},
|
|
42
|
+
"./package.json": "./package.json"
|
|
43
|
+
},
|
|
31
44
|
"bin": {
|
|
32
45
|
"dreamteamer": "bin/dreamteamer.js",
|
|
33
46
|
"dt": "bin/dreamteamer.js"
|
|
@@ -47,7 +60,6 @@
|
|
|
47
60
|
"dependencies": {
|
|
48
61
|
"ajv": "^8.17.1",
|
|
49
62
|
"ajv-formats": "^3.0.1",
|
|
50
|
-
"express": "^5.2.1",
|
|
51
63
|
"fractional-indexing": "^4.0.0",
|
|
52
64
|
"js-yaml": "^4.1.0",
|
|
53
65
|
"yaml": "2.8.4"
|
|
@@ -63,7 +63,11 @@ dispatch, so it cannot drift):
|
|
|
63
63
|
- read & measure — `list` `get` `values` `history` `diff` `next` `relations` `resolve`
|
|
64
64
|
- write & publish — `add` `set` `rm` `rename` `move` `revert` `commit`
|
|
65
65
|
- fields (sources, through the compile gate) — `add-field` `set-field` `rm-field` `rename-field` (system entities — modules, collections, skills, ui-views… — take the RECORD verbs above)
|
|
66
|
-
- workspace — `init` `install` `
|
|
66
|
+
- workspace — `init` `install` `update` `compile` `check` `status` `changes` `help`
|
|
67
|
+
- an EXTENSION (a workspace module or a dependency declaring `dreamteamer.extension`) adds verbs of
|
|
68
|
+
its own, and `dt help` lists them under its name; each ships the skill that teaches it. A verb that
|
|
69
|
+
answers "left core in 0.31.0" (`dt prove` · `dt land` · `dt worktree` · `dt serve` · `dt notebooklm` · the Docker
|
|
70
|
+
host) has no published extension yet — see `references/extensions.md`.
|
|
67
71
|
|
|
68
72
|
don't learn syntax from prose, this skill included: prose drifts, and `help` ships in
|
|
69
73
|
the same file as the dispatch it documents. run it once before your first write of a session.
|
|
@@ -99,17 +103,15 @@ Load by the map; nothing here is loaded "just in case".
|
|
|
99
103
|
| a brand-new or empty workspace, dreamteamer over an existing pile of files, "help me set this up" | `references/getting-started.md` |
|
|
100
104
|
| read, create, update, rename, delete, commit — or UNDO — a record | `references/records.md` |
|
|
101
105
|
| "what changed while I was away" | `references/changes.md` |
|
|
102
|
-
| the workspace has to reach a reader that is not a coding agent — a NotebookLM notebook; "which fields are sensitive" | `references/exporting.md` |
|
|
103
106
|
| the workspace seems unable to do something — a new kind of thing, a missing capability, "don't we already have this?" | `references/before-you-build.md` (look first); a new model then continues `references/data-modeling.md` (decide) → `references/collections.md` (write it) |
|
|
104
107
|
| a collection or field, mechanically — the descriptor, the system and field verbs, `templates:`/`extends:`, a compile or check message | `references/collections.md` |
|
|
105
108
|
| knowledge a session should find on its own | `references/skills.md` |
|
|
106
109
|
| "let me type one word and have this done" | `references/commands.md` |
|
|
107
110
|
| "which command applies to this record?" — a binding, a gate | `references/commands.md` |
|
|
108
|
-
| "how would anyone know this still works?" — a proof of a skill, a command or a script, and the exit code `dt prove` answers with | `references/proofs.md` |
|
|
109
111
|
| a job needing a fresh context and its own tools | `references/agents.md` |
|
|
110
112
|
| a route, a nav entry, a board / calendar / map over records | `references/ui-views.md` |
|
|
111
113
|
| a rendering or editing behaviour nothing registered has | `references/ui-components.md` |
|
|
112
|
-
|
|
|
114
|
+
| an optional tool — behaviour proofs, worktrees, a REST server, an exporter, a new harness — and whether to write one | `references/extensions.md` |
|
|
113
115
|
| other agent sessions are running on this machine — finding them, messaging one, coordinating several, and what may not cross between them | `references/sessions.md` |
|
|
114
116
|
|
|
115
117
|
three act-two tie-breakers, because they are the ones that go wrong:
|
|
@@ -131,7 +133,8 @@ workspace's decision log (where one exists) wins over older documents.
|
|
|
131
133
|
## system entities take the RECORD verbs
|
|
132
134
|
|
|
133
135
|
Modules, collections, skills, agents, commands, command-bindings, ui-views, collection-templates
|
|
134
|
-
and
|
|
136
|
+
— and any kind an installed extension contributes — are collections in the runtime, and since
|
|
137
|
+
0.19.0 the ordinary verbs write them:
|
|
135
138
|
|
|
136
139
|
```
|
|
137
140
|
dt add modules --name core --description "The shared nouns."
|
|
@@ -143,9 +146,8 @@ dt set modules/hr namespaces=hr dependencies=modules/core
|
|
|
143
146
|
dt rm modules/hr --force # --dry-run first; it prints its plan
|
|
144
147
|
```
|
|
145
148
|
|
|
146
|
-
⚠ **`
|
|
147
|
-
|
|
148
|
-
(`modules/<module>/proofs/<id>.proof.yaml`, `references/proofs.md`). Every other verb works on it.
|
|
149
|
+
⚠ **`add` scaffolds skills only.** Agents, commands, bindings, templates and contributed kinds are
|
|
150
|
+
hand-authored — `dt add agents` is refused, naming the file to write. Every other verb works on them.
|
|
149
151
|
|
|
150
152
|
`dt schema <op>` is **gone** since 0.19.0 and fails with the translation printed. `UPDATING.md` has
|
|
151
153
|
the complete mapping table.
|
|
@@ -49,9 +49,10 @@ Only after all four: build it, in the module that owns the concept.
|
|
|
49
49
|
|
|
50
50
|
## name the proof before you build it
|
|
51
51
|
|
|
52
|
-
**Before writing the thing, say how anyone would know it works** — one sentence, in
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
**Before writing the thing, say how anyone would know it works** — one sentence: *this record, in
|
|
53
|
+
this state, after this step, must look like this*. It costs a minute and it is the cheapest design
|
|
54
|
+
review there is. (With a proofs extension installed, that sentence is exactly the shape of a
|
|
55
|
+
`proofs` record, and `dt prove` runs it — its own skill says how.)
|
|
55
56
|
|
|
56
57
|
⚠ **When you cannot name one, the artifact has no observable post-state, and THAT is the first
|
|
57
58
|
thing to change** — not something to note and carry on past. A skill nothing can check is a skill
|
|
@@ -151,11 +151,11 @@ they read stay honest:
|
|
|
151
151
|
`{ summary: { _nempty: true } }` works the moment `summary` is a mirror — or ship the binding
|
|
152
152
|
without a `can-exit` and accept that it never shows done. What is not honest is a proxy field a
|
|
153
153
|
human must remember to set.
|
|
154
|
-
- **A `can-exit` and a proof's `count` answer different questions — put
|
|
155
|
-
side.** A gate is a filter over ONE record, evaluated on every render of `dt next` and every board
|
|
154
|
+
- **A `can-exit` and a proof's `count` (a proofs extension) answer different questions — put
|
|
155
|
+
each expectation on its own side.** A gate is a filter over ONE record, evaluated on every render of `dt next` and every board
|
|
156
156
|
the studio draws, so it can only ever read that record's own fields (plus one outbound hop) — which
|
|
157
157
|
is exactly the gap the bullet above names: "a summary referencing this record exists" is
|
|
158
|
-
inexpressible there. A **proof**
|
|
158
|
+
inexpressible there. A **proof** is evaluated on demand and is collection-scoped, so
|
|
159
159
|
it says the thing a gate cannot: `{ collection: summaries, where: { about: { _eq: '{record}' } },
|
|
160
160
|
count: { _delta: 1 } }` — *running this command left one more summary behind*. (`{record}` inside a
|
|
161
161
|
proof's `where` is SUBSTITUTED with the picked record's reference before the filter runs, which is
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# extensions — optional tools, and the one seam they plug into
|
|
2
|
+
|
|
3
|
+
Core is records plus the workspace compiler. Anything with a lifecycle of its own — a server, a
|
|
4
|
+
Docker host, a behaviour-test runner, an exporter to one vendor — is an **extension**: code the engine
|
|
5
|
+
calls, which a workspace has when it wants that capability and does without otherwise.
|
|
6
|
+
|
|
7
|
+
The verbs that left core in 0.31.0 — `prove` · `land` · `worktree`, `serve`, `notebooklm`, and the
|
|
8
|
+
Docker host — return as extensions, and **none is published yet**. Typed against core, each fails with
|
|
9
|
+
exit 2 and says so. A workspace that needs one now carries it as its own module (below), or stays on
|
|
10
|
+
0.30.x.
|
|
11
|
+
|
|
12
|
+
`dt status` lists the extensions this workspace loaded, and `dt help` appends each one's usage.
|
|
13
|
+
|
|
14
|
+
## how a workspace turns one on
|
|
15
|
+
|
|
16
|
+
Two places, one declaration — a `package.json` carrying `"dreamteamer": { "extension": "./entry.js" }`:
|
|
17
|
+
|
|
18
|
+
- **a workspace module**, `modules/<id>/package.json`. Its code is the workspace's own, like `bin/`,
|
|
19
|
+
so it needs no package and no npm. This is how a workspace carries an extension nobody has
|
|
20
|
+
published, and it shadows a dependency of the same name.
|
|
21
|
+
- **a direct dependency.** npm put the code there on purpose; a transitive package is never loaded
|
|
22
|
+
however it advertises.
|
|
23
|
+
|
|
24
|
+
A bare entry in `dreamteamer.disable` switches one off while leaving it in place. Two extensions
|
|
25
|
+
claiming the same verb, source kind or harness is a refusal at load, naming both — never "last one
|
|
26
|
+
wins".
|
|
27
|
+
|
|
28
|
+
An extension is ALSO a content module (its `dreamteamer` key makes it one): its collections and skills
|
|
29
|
+
travel with its code, and compile discovers them like any other module's.
|
|
30
|
+
|
|
31
|
+
⚠ **Remove an extension and its kind goes with it.** A workspace holding `proofs/` folders with no
|
|
32
|
+
extension contributing `proofs` fails compile on the unknown folder — deliberately: a proof that
|
|
33
|
+
compiles with no validator would be a claim nothing checks. The next compile after a removal also
|
|
34
|
+
prunes the extension's compiled folder and its managed blocks.
|
|
35
|
+
|
|
36
|
+
## writing one — the contract
|
|
37
|
+
|
|
38
|
+
The entry's default export is `activate(dt)`. `dt` is the RUNNING engine's public API
|
|
39
|
+
(`import('dreamteamer')`'s namespace), so the extension never imports an engine of its own and can
|
|
40
|
+
never disagree with the one the operator ran. It returns a contribution; every key is optional:
|
|
41
|
+
|
|
42
|
+
| key | shape | what core does with it |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `commands` | `{ <verb>: { usage, run(ws, argv) } }` | `dt <verb> …` runs it in-process with the opened workspace; the return is the exit code |
|
|
45
|
+
| `sourceKinds` | `[{ kind, exclude?: [subtree] }]` | compile stages `<module>/<kind>/` like a built-in kind; `exclude` keeps fixtures out |
|
|
46
|
+
| `analyze` | `(draft) → { errors?, warnings?, notes? }` | runs after assembly, before any output is replaced; an error fails the compile (and every schema write, which compiles) |
|
|
47
|
+
| `harnesses` | `{ <id>: (ctx) → { blocks: { <file>: text }, summary } }` | a harness adapter writing managed blocks into user-owned files |
|
|
48
|
+
| `orientation` | a string | one paragraph appended to every orientation block |
|
|
49
|
+
| `hooks` | `{ <ClaudeHookEvent>: '<dt verb args>' }` | merged into `dt install --print-adapters` |
|
|
50
|
+
|
|
51
|
+
The `draft` is data only: the staged entries, the final merged descriptors, the modules, declared var
|
|
52
|
+
and env key NAMES, and the previous manifest. No writer, no Store, no environment values — an analysis
|
|
53
|
+
that needs one is a command, not an analysis.
|
|
54
|
+
|
|
55
|
+
## a module, or an extension?
|
|
56
|
+
|
|
57
|
+
A **module** ships collections, skills, commands, agents, views and UI code — content. An
|
|
58
|
+
**extension** ships code the engine calls. Reach for an extension only when the capability needs one
|
|
59
|
+
of the contribution keys above; everything else is a module, and a module is copied and adapted per
|
|
60
|
+
workspace rather than installed (`references/before-you-build.md`).
|
|
@@ -6,7 +6,7 @@ refusals that keep that from looping, lying, or carrying private data somewhere
|
|
|
6
6
|
|
|
7
7
|
The whole design turns on one asymmetry. Two sessions in the same tree are conflict-BLIND, and so are
|
|
8
8
|
two sessions in one conversation: **the second write wins and nobody is told.** Sessions are
|
|
9
|
-
|
|
9
|
+
git worktrees' twin — observed, never stored, and the registry is the authority rather than anyone's
|
|
10
10
|
memory of it.
|
|
11
11
|
|
|
12
12
|
| the question | read |
|
|
@@ -175,10 +175,9 @@ A coordinator spans repos by construction, and one of them may publish.
|
|
|
175
175
|
records it may not message a publishing session at all, whatever it means to say. This is what
|
|
176
176
|
keeps rule 1 safe after compaction, when it can no longer recall precisely what it read.
|
|
177
177
|
6. ⚠ **`cwd` is not the repo, and one repo is not one tree.** Harnesses put worktrees *inside* the
|
|
178
|
-
repo or *under the home directory* depending on the harness
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
worktrees` is the instrument.
|
|
178
|
+
repo or *under the home directory* depending on the harness. A `cwd` under a harness's own
|
|
179
|
+
worktree root is still that repo and carries its full boundary — so a path-prefix test against
|
|
180
|
+
the primary root gets it wrong in the dangerous direction. `git worktree list` is the instrument.
|
|
182
181
|
|
|
183
182
|
## not stepping on your own toes
|
|
184
183
|
|
|
@@ -160,12 +160,10 @@ Skills ship with modules and are read by any operator on any machine:
|
|
|
160
160
|
renamed verb or a changed limit in a skill sends every future session down the old path
|
|
161
161
|
confidently. When you catch a skill lying, fixing it is part of the task you are on, not a
|
|
162
162
|
follow-up.
|
|
163
|
-
- **Give it a
|
|
163
|
+
- **Give it a mechanical check, and know what one cannot cover.** Something must pin the
|
|
164
164
|
mechanical half: the script the skill names runs, the record it promises appears, the path it
|
|
165
|
-
files to exists
|
|
166
|
-
|
|
167
|
-
`dt list proofs --missing` names every artifact nobody claimed anything about. ⚠ **A
|
|
168
|
-
green proof is not evidence the skill TEACHES.** Whether a fresh session finds it, loads it and
|
|
165
|
+
files to exists — in a workspace with a proofs extension, that is a `proofs` record, written
|
|
166
|
+
in the same commit. ⚠ **A green check is not evidence the skill TEACHES.** Whether a fresh session finds it, loads it and
|
|
169
167
|
does the job right is the eval layer: real tasks, blind sessions, a scoring sheet — a procedure,
|
|
170
168
|
never something the engine runs.
|
|
171
169
|
- **Retire what nothing loads.** A skill nobody uses still costs its line in every session's index.
|
package/src/api.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// The typed contract of `import … from 'dreamteamer'` (src/api.js). It re-exports the record half
|
|
2
|
+
// (`dreamteamer/records`, src/records-api.d.ts) and adds the workspace half. A test pins each runtime
|
|
3
|
+
// export list to its declaration, so a name added to one and not the other fails the suite.
|
|
4
|
+
|
|
5
|
+
export * from './records-api.js';
|
|
6
|
+
import type { Fields, Descriptor, Descriptors, Manifest, Store } from './records-api.js';
|
|
7
|
+
|
|
8
|
+
// ---- the workspace and extensions ------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
export interface Workspace {
|
|
11
|
+
/** absolute path of the workspace root */
|
|
12
|
+
root: string;
|
|
13
|
+
/** the workspace package.json */
|
|
14
|
+
pkg: { name?: string; dependencies?: Record<string, string>; devDependencies?: Record<string, string>; dreamteamer?: Record<string, any>; [k: string]: unknown };
|
|
15
|
+
/** the ACTIVATED extensions, present on a handle from `openWorkspace` */
|
|
16
|
+
extensions?: LoadedExtension[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** What an extension's `activate(dt)` returns. Every key is optional. */
|
|
20
|
+
export interface Contribution {
|
|
21
|
+
commands?: Record<string, { usage?: string; run(ws: Workspace, argv: string[]): number | void | Promise<number | void> }>;
|
|
22
|
+
sourceKinds?: (string | { kind: string; exclude?: string[] })[];
|
|
23
|
+
analyze?(draft: CompileDraft): { errors?: string[]; warnings?: string[]; notes?: string[] } | void;
|
|
24
|
+
harnesses?: Record<string, (ctx: HarnessContext) => { blocks?: Record<string, string | null>; summary?: string } | void>;
|
|
25
|
+
orientation?: string | ((ctx: { entries: Map<string, Entry> }) => string);
|
|
26
|
+
hooks?: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
export type Activate = (dt: typeof import('./api.js')) => Contribution | Promise<Contribution>;
|
|
29
|
+
export interface LoadedExtension {
|
|
30
|
+
name: string;
|
|
31
|
+
version: string;
|
|
32
|
+
commands: NonNullable<Contribution['commands']>;
|
|
33
|
+
sourceKinds: { kind: string; exclude: string[]; extension: string }[];
|
|
34
|
+
analyze: Contribution['analyze'] | null;
|
|
35
|
+
harnesses: NonNullable<Contribution['harnesses']>;
|
|
36
|
+
orientation: Contribution['orientation'] | null;
|
|
37
|
+
hooks: Record<string, string>;
|
|
38
|
+
}
|
|
39
|
+
export type Entry = { sources: { path: string; hash: string }[]; bytes: Buffer };
|
|
40
|
+
export interface CompileDraft {
|
|
41
|
+
/** runtime-relative path → staged entry; read-only */
|
|
42
|
+
readonly entries: Map<string, Entry>;
|
|
43
|
+
/** the FINAL merged descriptors */
|
|
44
|
+
readonly descriptors: Descriptors;
|
|
45
|
+
readonly modules: { id: string; name: string; root: string; channel: 'inline' | 'git' | 'npm' }[];
|
|
46
|
+
/** names only — never values */
|
|
47
|
+
readonly declaredVars: string[];
|
|
48
|
+
readonly declaredEnv: string[];
|
|
49
|
+
readonly previousManifest: Manifest | null;
|
|
50
|
+
/** parse one staged YAML entry, with its source path in any error */
|
|
51
|
+
parse(runtimePath: string): any;
|
|
52
|
+
}
|
|
53
|
+
export interface HarnessContext {
|
|
54
|
+
entries: Map<string, Entry>;
|
|
55
|
+
version: string;
|
|
56
|
+
collections: { name: string; generated: boolean; systemGroup: boolean; description: string; module: string; sensitive: boolean; sensitiveFields: string[] }[];
|
|
57
|
+
modules: { id: string; title: string; description: string; path: string }[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const EXTENSION_API: 1;
|
|
61
|
+
export function openWorkspace(start?: string): Promise<Workspace & { extensions: LoadedExtension[] }>;
|
|
62
|
+
export function findWorkspace(start?: string): Workspace;
|
|
63
|
+
export function declaredExtensions(ws: Workspace): { name: string; version: string; dir: string; entry: string }[];
|
|
64
|
+
export const engineBin: string;
|
|
65
|
+
export const engineRoot: string;
|
|
66
|
+
|
|
67
|
+
export function envContext(ws: Workspace): unknown;
|
|
68
|
+
export function renderTemplate(template: string, ctx: unknown): string;
|
|
69
|
+
export function parseEnvValues(text: string): Map<string, string>;
|
|
70
|
+
export function satisfies(version: string, range: string): boolean | null;
|
|
71
|
+
|
|
72
|
+
// ---- the compiler, schema and module operations --------------------------------------------------
|
|
73
|
+
|
|
74
|
+
/** throws CompileError on a bad source; prints its summary; returns 0 */
|
|
75
|
+
export function compile(ws: Workspace): 0;
|
|
76
|
+
export function staleness(root: string): { compiled: boolean; stale: string[]; manifest?: Manifest; message?: string };
|
|
77
|
+
export function warnIfStale(root: string): ReturnType<typeof staleness>;
|
|
78
|
+
export function discoverModules(root: string, pkg: Workspace['pkg']): { modules: { name: string; root: string; channel: string }[]; shadows: unknown[]; disabledModules: string[] };
|
|
79
|
+
export class CompileError extends Error {}
|
|
80
|
+
export const KINDS: readonly string[];
|
|
81
|
+
export const MANAGED_BLOCKS: readonly { id: 'orientation' | 'instructions'; begin: string; end: string }[];
|
|
82
|
+
type SchemaOp = (ws: Workspace, store: Store, ...args: any[]) => any;
|
|
83
|
+
export const createCollection: SchemaOp, removeCollection: SchemaOp, renameCollection: SchemaOp, moveCollection: SchemaOp, setCollectionScalars: SchemaOp;
|
|
84
|
+
export const addField: SchemaOp, updateField: SchemaOp, removeField: SchemaOp, removeFieldPlan: SchemaOp, renameField: SchemaOp, renameFieldPlan: SchemaOp;
|
|
85
|
+
export function fieldDef(store: Store, flags: Record<string, unknown>, collection: string): any;
|
|
86
|
+
export function statedKeywords(flags: Record<string, unknown>): any;
|
|
87
|
+
export const saveUiView: SchemaOp, removeUiView: SchemaOp;
|
|
88
|
+
export const createModule: SchemaOp, setModule: SchemaOp, renameModule: SchemaOp, removeModule: SchemaOp;
|
|
89
|
+
export const createSkill: SchemaOp, refuseHandAuthored: SchemaOp, removeEntity: SchemaOp, renameEntity: SchemaOp, setEntityFrontmatter: SchemaOp;
|
|
90
|
+
export function init(opts?: { flags?: Record<string, string> }): 0;
|
|
91
|
+
export function ensureRepo(ws: Workspace, id: string): { path: string; cloned: boolean };
|
|
92
|
+
export function ensureAllRepos(ws: Workspace): { path: string; cloned: boolean }[];
|
|
93
|
+
export function listRepos(ws: Workspace): { id: string; path: string; present: boolean; unresolved?: string }[];
|
|
94
|
+
export function installClone(ws: Workspace, url: string, name?: string): number;
|
|
95
|
+
|
|
96
|
+
// ---- the checkout -------------------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
export function describeCheckout(root: string, git?: (args: string[], cwd: string) => string): { root: string; kind: 'primary' | 'linked'; gitDir: string; commonDir: string; primary: string; insideRoot: boolean };
|
|
99
|
+
export function installCommand(ws: Workspace, argv: string[], opts?: { open?: (at: string) => Promise<Workspace> }): Promise<number>;
|
|
100
|
+
export function resolveNpm(execPath?: string, env?: NodeJS.ProcessEnv): string | null;
|
|
101
|
+
export function childEnv(): NodeJS.ProcessEnv;
|
|
102
|
+
export function readHookInput(stdinText: string): { cwd: string | null; name: string | null; raw: Record<string, unknown> };
|
|
103
|
+
export function readStdin(isTTY?: boolean): string;
|
|
104
|
+
|
|
105
|
+
// ---- CLI helpers ------------------------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
/** write `text` plus a trailing newline, synchronously, looping on short writes — safe before process.exit */
|
|
108
|
+
export function emit(text: string, fd?: number): void;
|
|
109
|
+
export function parseArgs(argv: string[]): { flags: Record<string, string | boolean | string[]>; pos: string[] };
|