@teammates/cli 0.1.0 → 0.2.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.
- package/README.md +31 -22
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.js +68 -56
- package/dist/adapter.test.js +34 -21
- package/dist/adapters/cli-proxy.d.ts +11 -4
- package/dist/adapters/cli-proxy.js +176 -162
- package/dist/adapters/copilot.d.ts +50 -0
- package/dist/adapters/copilot.js +210 -0
- package/dist/adapters/echo.d.ts +2 -2
- package/dist/adapters/echo.js +2 -1
- package/dist/adapters/echo.test.js +4 -2
- package/dist/cli-utils.d.ts +21 -0
- package/dist/cli-utils.js +74 -0
- package/dist/cli-utils.test.d.ts +1 -0
- package/dist/cli-utils.test.js +179 -0
- package/dist/cli.js +3160 -961
- package/dist/compact.d.ts +39 -0
- package/dist/compact.js +269 -0
- package/dist/compact.test.d.ts +1 -0
- package/dist/compact.test.js +198 -0
- package/dist/console/ansi.d.ts +18 -0
- package/dist/console/ansi.js +20 -0
- package/dist/console/ansi.test.d.ts +1 -0
- package/dist/console/ansi.test.js +50 -0
- package/dist/console/dropdown.d.ts +23 -0
- package/dist/console/dropdown.js +63 -0
- package/dist/console/file-drop.d.ts +59 -0
- package/dist/console/file-drop.js +186 -0
- package/dist/console/file-drop.test.d.ts +1 -0
- package/dist/console/file-drop.test.js +145 -0
- package/dist/console/index.d.ts +22 -0
- package/dist/console/index.js +23 -0
- package/dist/console/interactive-readline.d.ts +65 -0
- package/dist/console/interactive-readline.js +132 -0
- package/dist/console/markdown-table.d.ts +17 -0
- package/dist/console/markdown-table.js +270 -0
- package/dist/console/markdown-table.test.d.ts +1 -0
- package/dist/console/markdown-table.test.js +130 -0
- package/dist/console/mutable-output.d.ts +21 -0
- package/dist/console/mutable-output.js +51 -0
- package/dist/console/paste-handler.d.ts +63 -0
- package/dist/console/paste-handler.js +177 -0
- package/dist/console/prompt-box.d.ts +55 -0
- package/dist/console/prompt-box.js +120 -0
- package/dist/console/prompt-input.d.ts +136 -0
- package/dist/console/prompt-input.js +618 -0
- package/dist/console/startup.d.ts +20 -0
- package/dist/console/startup.js +138 -0
- package/dist/console/startup.test.d.ts +1 -0
- package/dist/console/startup.test.js +41 -0
- package/dist/console/wordwheel.d.ts +75 -0
- package/dist/console/wordwheel.js +123 -0
- package/dist/dropdown.js +4 -21
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/onboard.d.ts +24 -0
- package/dist/onboard.js +174 -11
- package/dist/orchestrator.d.ts +8 -11
- package/dist/orchestrator.js +33 -81
- package/dist/orchestrator.test.js +59 -79
- package/dist/registry.d.ts +1 -1
- package/dist/registry.js +56 -12
- package/dist/registry.test.js +57 -13
- package/dist/theme.d.ts +56 -0
- package/dist/theme.js +54 -0
- package/dist/types.d.ts +18 -13
- package/package.json +8 -3
- package/template/CROSS-TEAM.md +2 -2
- package/template/PROTOCOL.md +72 -15
- package/template/README.md +2 -2
- package/template/TEMPLATE.md +118 -15
- package/template/example/SOUL.md +2 -1
- package/template/example/WISDOM.md +9 -0
- package/dist/adapters/codex.d.ts +0 -50
- package/dist/adapters/codex.js +0 -213
- package/template/example/MEMORIES.md +0 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teammates/cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Agent-agnostic CLI for teammates. Routes tasks, manages handoffs, and plugs into any coding agent backend.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"build": "tsc",
|
|
17
17
|
"dev": "tsc --watch",
|
|
18
18
|
"test": "vitest run",
|
|
19
|
-
"test:
|
|
19
|
+
"test:coverage": "vitest run --coverage",
|
|
20
|
+
"test:watch": "vitest",
|
|
21
|
+
"typecheck": "tsc --noEmit"
|
|
20
22
|
},
|
|
21
23
|
"keywords": [
|
|
22
24
|
"teammates",
|
|
@@ -28,11 +30,14 @@
|
|
|
28
30
|
],
|
|
29
31
|
"license": "MIT",
|
|
30
32
|
"dependencies": {
|
|
33
|
+
"@github/copilot-sdk": "^0.1.32",
|
|
34
|
+
"@teammates/consolonia": "^0.2.0",
|
|
31
35
|
"chalk": "^5.6.2",
|
|
32
36
|
"ora": "^9.3.0"
|
|
33
37
|
},
|
|
34
38
|
"devDependencies": {
|
|
35
|
-
"@types/node": "^
|
|
39
|
+
"@types/node": "^25.5.0",
|
|
40
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
36
41
|
"typescript": "^5.5.0",
|
|
37
42
|
"vitest": "^4.1.0"
|
|
38
43
|
},
|
package/template/CROSS-TEAM.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Cross-Team Notes
|
|
2
2
|
|
|
3
|
-
Shared lessons that affect multiple teammates. Record here instead of duplicating across individual
|
|
3
|
+
Shared lessons that affect multiple teammates. Record here instead of duplicating across individual WISDOM.md files.
|
|
4
4
|
|
|
5
5
|
This file also serves as a **shared index** — teammates can add pointers to private docs in their folder that other teammates might find useful.
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Reverse chronological. Tag affected teammates.
|
|
|
8
8
|
|
|
9
9
|
## Ownership Scopes
|
|
10
10
|
|
|
11
|
-
Every teammate **owns everything** under their `.teammates/<name>/` folder — SOUL.md,
|
|
11
|
+
Every teammate **owns everything** under their `.teammates/<name>/` folder — SOUL.md, WISDOM.md, memory/, and any private docs they create. This is unconditional: no teammate needs permission to edit their own folder, and no other teammate should modify it.
|
|
12
12
|
|
|
13
13
|
The **Boundary Rule** (see PROTOCOL.md) applies to the **codebase** — source code, configs, and shared framework files — not to a teammate's own `.teammates/<name>/` directory.
|
|
14
14
|
|
package/template/PROTOCOL.md
CHANGED
|
@@ -16,7 +16,7 @@ Individual teammates may define additional ethics in their SOUL.md specific to t
|
|
|
16
16
|
|
|
17
17
|
**Never write code or modify files outside your ownership.** If a task requires changes to files you don't own, hand off that portion to the owning teammate. Design the behavior, write a spec if needed, then hand off — don't implement it yourself, even if the fix seems small or obvious. Your Boundaries section lists what you do NOT touch and who does.
|
|
18
18
|
|
|
19
|
-
**Self-owned folder exception:** Every teammate unconditionally owns their `.teammates/<name>/` folder. You never need permission to edit your own SOUL.md,
|
|
19
|
+
**Self-owned folder exception:** Every teammate unconditionally owns their `.teammates/<name>/` folder. You never need permission to edit your own SOUL.md, WISDOM.md, memory files, or private docs. The Boundary Rule applies to the **codebase** (source code, configs, shared framework files), not to your own teammate folder.
|
|
20
20
|
|
|
21
21
|
### Cross-Domain Tasks
|
|
22
22
|
|
|
@@ -60,40 +60,97 @@ If the team includes a cross-cutting teammate (e.g., for quality/testing):
|
|
|
60
60
|
- They advise on testing strategy but do not override domain decisions
|
|
61
61
|
- They maintain quality metrics and benchmarks
|
|
62
62
|
|
|
63
|
+
## Services
|
|
64
|
+
|
|
65
|
+
Optional services are declared in `.teammates/services.json`. This file is checked into git so the entire team shares the same service configuration. Each key is a service name; the value is a config object (`{}` means installed with defaults).
|
|
66
|
+
|
|
67
|
+
The CLI reads `services.json` to detect which services are available and injects their capabilities into teammate prompts automatically. Services are installed via the `/install` command.
|
|
68
|
+
|
|
63
69
|
## Memory
|
|
64
70
|
|
|
65
71
|
### How memory works
|
|
66
72
|
|
|
67
73
|
Each session, every teammate wakes up fresh. Files are the only persistence layer — there is no RAM between sessions.
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
Memory has three tiers:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
Daily Logs → Memories → WISDOM
|
|
79
|
+
(raw) (typed) (distilled)
|
|
80
|
+
days weeks permanent
|
|
81
|
+
```
|
|
70
82
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
### Session startup — read order
|
|
84
|
+
|
|
85
|
+
At the start of each session, a teammate reads (in this order):
|
|
86
|
+
|
|
87
|
+
1. **SOUL.md** — identity, principles, boundaries
|
|
88
|
+
2. **WISDOM.md** — distilled principles from compacted memories
|
|
89
|
+
3. **memory/YYYY-MM-DD.md** — today's and yesterday's daily logs
|
|
74
90
|
4. **USER.md** — who the user is and how they prefer to work
|
|
91
|
+
5. **memory/** typed files — browse or search on-demand as the task requires
|
|
92
|
+
|
|
93
|
+
### Tier 1 — Daily Logs
|
|
75
94
|
|
|
76
|
-
|
|
95
|
+
`memory/YYYY-MM-DD.md` — Append-only session notes. What was worked on, decided, what to pick up next. Start a new file each day. These are raw scratch — no frontmatter needed.
|
|
77
96
|
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
### Tier 2 — Typed Memories
|
|
98
|
+
|
|
99
|
+
`memory/<type>_<topic>.md` — Individual files with frontmatter (`name`, `description`, `type`). Four types:
|
|
100
|
+
|
|
101
|
+
| Type | When to save |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `user` | User's role, preferences, knowledge level |
|
|
104
|
+
| `feedback` | Corrections or guidance from the user |
|
|
105
|
+
| `project` | Ongoing work, goals, deadlines, decisions |
|
|
106
|
+
| `reference` | Pointers to external resources |
|
|
107
|
+
|
|
108
|
+
See [TEMPLATE.md](TEMPLATE.md) for full format, body structure per type, and examples.
|
|
109
|
+
|
|
110
|
+
### Tier 3 — Wisdom
|
|
111
|
+
|
|
112
|
+
`WISDOM.md` — Distilled, high-signal principles derived from compacting multiple memories. Compact, stable, rarely changes. Read second (after SOUL.md).
|
|
113
|
+
|
|
114
|
+
### Compaction — Memories → Wisdom
|
|
115
|
+
|
|
116
|
+
Compaction distills typed memories into WISDOM.md entries. Run manually via `/compact` or automatically every 7 days.
|
|
117
|
+
|
|
118
|
+
1. Review all typed memory files in `memory/`
|
|
119
|
+
2. Identify patterns — recurring themes, reinforced feedback, confirmed lessons
|
|
120
|
+
3. Distill into WISDOM.md entries — short, principled, event-agnostic
|
|
121
|
+
4. Delete the source memory files that were fully absorbed
|
|
122
|
+
5. Leave memories that are still active or evolving
|
|
123
|
+
6. Update the "Last compacted" date in WISDOM.md
|
|
124
|
+
|
|
125
|
+
A good wisdom entry is a **pattern** (not an incident), **principled** (states a rule), **compact** (1-3 sentences), and **actionable** (tells you what to do).
|
|
80
126
|
|
|
81
127
|
### When to write memory
|
|
82
128
|
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
129
|
+
- User corrections and guidance → typed memory (`feedback`)
|
|
130
|
+
- Decisions, deadlines, project context → typed memory (`project`)
|
|
131
|
+
- User profile info → typed memory (`user`)
|
|
132
|
+
- External resource locations → typed memory (`reference`)
|
|
133
|
+
- Session notes and running context → daily log
|
|
134
|
+
- If the user says "remember this," write it immediately
|
|
135
|
+
|
|
136
|
+
### What NOT to save
|
|
137
|
+
|
|
138
|
+
- Code patterns derivable from the code itself
|
|
139
|
+
- Git history — use `git log` / `git blame`
|
|
140
|
+
- Debugging solutions — the fix is in the code
|
|
141
|
+
- Anything already in WISDOM.md
|
|
142
|
+
- Ephemeral task details — use daily logs
|
|
87
143
|
|
|
88
144
|
### Sharing
|
|
89
145
|
|
|
90
|
-
- Each teammate maintains their own
|
|
146
|
+
- Each teammate maintains their own WISDOM.md and memory/ for domain-specific knowledge
|
|
91
147
|
- **Cross-team lessons** go in [CROSS-TEAM.md](CROSS-TEAM.md) — one entry, tagged with affected teammates
|
|
92
|
-
-
|
|
148
|
+
- Wisdom is personal to each teammate — do not duplicate across teammates
|
|
149
|
+
- **Private docs** — Teammates may create additional files and folders under their own `.teammates/<name>/` directory (e.g., `notes/`, `specs/`, `scratch/`). These are private by default. To make a doc visible to other teammates, add a pointer in [CROSS-TEAM.md](CROSS-TEAM.md) with a brief description of what it contains.
|
|
93
150
|
|
|
94
151
|
## Adding New Teammates
|
|
95
152
|
|
|
96
|
-
1. Copy the SOUL.md and
|
|
153
|
+
1. Copy the SOUL.md and WISDOM.md templates from [TEMPLATE.md](TEMPLATE.md) to a new folder under `.teammates/`
|
|
97
154
|
2. Fill in all sections with project-specific details
|
|
98
155
|
3. Update README.md roster, last-active date, and routing guide
|
|
99
156
|
4. Update existing teammates' SOUL.md ownership and boundary sections if domains shift
|
package/template/README.md
CHANGED
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
Each teammate folder contains:
|
|
34
34
|
|
|
35
35
|
- **SOUL.md** — Identity, continuity instructions, principles, boundaries, capabilities, and ownership
|
|
36
|
-
- **
|
|
37
|
-
- **memory/** — Daily logs (`YYYY-MM-DD.md`)
|
|
36
|
+
- **WISDOM.md** — Distilled principles from compacted memories (read second, after SOUL.md)
|
|
37
|
+
- **memory/** — Daily logs (`YYYY-MM-DD.md`) and typed memory files (`<type>_<topic>.md`)
|
|
38
38
|
- Additional files as needed (e.g., design docs, bug trackers)
|
|
39
39
|
|
|
40
40
|
Root-level shared files:
|
package/template/TEMPLATE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# New Teammate Template
|
|
2
2
|
|
|
3
|
-
Copy the SOUL.md and
|
|
3
|
+
Copy the SOUL.md and WISDOM.md structures below to `.teammates/<name>/` and fill in each file. Create an empty `memory/` directory for daily logs and typed memory files.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -17,9 +17,10 @@ Copy the SOUL.md and MEMORIES.md structures below to `.teammates/<name>/` and fi
|
|
|
17
17
|
|
|
18
18
|
Each session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.
|
|
19
19
|
|
|
20
|
-
- Read your SOUL.md and
|
|
20
|
+
- Read your SOUL.md and WISDOM.md at the start of every session.
|
|
21
21
|
- Read `memory/YYYY-MM-DD.md` for today and yesterday.
|
|
22
22
|
- Read USER.md to understand who you're working with.
|
|
23
|
+
- Browse `memory/` for typed memory files relevant to the current task (or use recall search if available).
|
|
23
24
|
- Update your files as you learn. If you change SOUL.md, tell the user.
|
|
24
25
|
- You may create additional private docs under your folder (e.g., `.teammates/<name>/notes/`, `.teammates/<name>/specs/`). To share a doc with other teammates, add a pointer to it in [CROSS-TEAM.md](../CROSS-TEAM.md).
|
|
25
26
|
|
|
@@ -31,7 +32,7 @@ Each session, you wake up fresh. These files _are_ your memory. Read them. Updat
|
|
|
31
32
|
|
|
32
33
|
## Boundaries
|
|
33
34
|
|
|
34
|
-
**You unconditionally own everything under `.teammates/<name>/`** — your SOUL.md,
|
|
35
|
+
**You unconditionally own everything under `.teammates/<name>/`** — your SOUL.md, WISDOM.md, memory files, and any private docs you create. No other teammate should modify your folder, and you never need permission to edit it.
|
|
35
36
|
|
|
36
37
|
**For the codebase** (source code, configs, shared framework files): if a task requires changes outside your ownership, hand off to the owning teammate. Design the behavior and write a spec if needed, but do not modify files you don't own — even if the change seems small.
|
|
37
38
|
|
|
@@ -77,33 +78,135 @@ Each session, you wake up fresh. These files _are_ your memory. Read them. Updat
|
|
|
77
78
|
|
|
78
79
|
---
|
|
79
80
|
|
|
80
|
-
##
|
|
81
|
+
## WISDOM.md Template
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
# <Name> — Memories
|
|
83
|
+
WISDOM.md contains distilled, high-signal principles derived from compacting multiple memories. This is the second file a teammate reads each session (after SOUL.md). It should be compact enough to read in a single pass.
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
```markdown
|
|
86
|
+
# <Name> — Wisdom
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
Distilled principles. Read this first every session (after SOUL.md).
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
Last compacted: YYYY-MM-DD
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
**Category:** <Category> | **Last updated:** YYYY-MM-DD
|
|
92
|
+
---
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
_(No wisdom yet — principles emerge after the first compaction.)_
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
99
|
-
##
|
|
99
|
+
## Memory Files
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
Memory lives in the `memory/` directory as individual files. There are two kinds:
|
|
102
|
+
|
|
103
|
+
### Daily Logs
|
|
104
|
+
|
|
105
|
+
Daily logs are append-only session notes at `memory/YYYY-MM-DD.md`. Start a new file each day.
|
|
102
106
|
|
|
103
107
|
```markdown
|
|
104
|
-
#
|
|
108
|
+
# YYYY-MM-DD
|
|
105
109
|
|
|
106
110
|
## Notes
|
|
107
111
|
|
|
108
112
|
- <What was worked on, what was decided, what to pick up next.>
|
|
109
113
|
```
|
|
114
|
+
|
|
115
|
+
### Typed Memories
|
|
116
|
+
|
|
117
|
+
Typed memories capture durable knowledge as individual files at `memory/<type>_<topic>.md`. Each file has frontmatter for searchability.
|
|
118
|
+
|
|
119
|
+
**Types:**
|
|
120
|
+
|
|
121
|
+
| Type | What to save | Body structure |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `user` | User's role, goals, preferences, knowledge level | Free-form description of the user |
|
|
124
|
+
| `feedback` | Corrections or guidance from the user | Rule, then **Why:** and **How to apply:** |
|
|
125
|
+
| `project` | Ongoing work, goals, deadlines, decisions | Fact/decision, then **Why:** and **How to apply:** |
|
|
126
|
+
| `reference` | Pointers to external resources | Resource location and when to use it |
|
|
127
|
+
|
|
128
|
+
**Template:**
|
|
129
|
+
|
|
130
|
+
```markdown
|
|
131
|
+
---
|
|
132
|
+
name: <memory name>
|
|
133
|
+
description: <one-line description — used for relevance matching during search>
|
|
134
|
+
type: <user | feedback | project | reference>
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
<memory content — structured per type (see table above)>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Examples:**
|
|
141
|
+
|
|
142
|
+
`memory/feedback_no_mocks.md`:
|
|
143
|
+
```markdown
|
|
144
|
+
---
|
|
145
|
+
name: No mocks in integration tests
|
|
146
|
+
description: Integration tests must use real services, not mocks — prior incident with mock/prod divergence
|
|
147
|
+
type: feedback
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
Integration tests must hit a real database, not mocks.
|
|
151
|
+
|
|
152
|
+
**Why:** Last quarter, mocked tests passed but the prod migration failed because mocks diverged from actual behavior.
|
|
153
|
+
|
|
154
|
+
**How to apply:** When writing integration tests, always use the staging environment. Only use mocks for unit tests of pure logic.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`memory/reference_bug_tracker.md`:
|
|
158
|
+
```markdown
|
|
159
|
+
---
|
|
160
|
+
name: Bug tracker location
|
|
161
|
+
description: Pipeline bugs are tracked in Linear project INGEST
|
|
162
|
+
type: reference
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
Pipeline bugs are tracked in the Linear project "INGEST". Check there for context on pipeline-related tickets.
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### What NOT to save as a memory
|
|
169
|
+
|
|
170
|
+
- Code patterns, conventions, or architecture — derive these from the current code
|
|
171
|
+
- Git history or who-changed-what — use `git log` / `git blame`
|
|
172
|
+
- Debugging solutions — the fix is in the code, the context is in the commit message
|
|
173
|
+
- Anything already in WISDOM.md — memories get deleted after compaction
|
|
174
|
+
- Ephemeral task details — use daily logs for in-progress work
|
|
175
|
+
|
|
176
|
+
### Memory Index (optional)
|
|
177
|
+
|
|
178
|
+
If the project uses `teammates-recall`, an optional `memory/INDEX.md` can serve as a lightweight pointer file listing all typed memory files with one-line descriptions. This aids recall indexing but is not required for the memory system to function.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Compaction — Memories → Wisdom
|
|
183
|
+
|
|
184
|
+
Compaction distills typed memories into WISDOM.md entries. Run it manually via `/compact` or automatically every 7 days.
|
|
185
|
+
|
|
186
|
+
### Process
|
|
187
|
+
|
|
188
|
+
1. **Review** all typed memory files in `memory/`
|
|
189
|
+
2. **Identify patterns** — recurring themes, feedback that's been reinforced, lessons confirmed multiple times
|
|
190
|
+
3. **Distill** into WISDOM.md entries — short, principled, event-agnostic. A wisdom entry should stand alone without needing the source memories for context
|
|
191
|
+
4. **Delete** the source memory files that were fully absorbed
|
|
192
|
+
5. **Leave** memories that are still active, evolving, or too recent to generalize
|
|
193
|
+
6. **Update** the "Last compacted" date in WISDOM.md
|
|
194
|
+
|
|
195
|
+
### What makes a good wisdom entry
|
|
196
|
+
|
|
197
|
+
- **Pattern, not incident** — derived from multiple memories, not a single event
|
|
198
|
+
- **Principled** — states a rule or heuristic, not a fact
|
|
199
|
+
- **Compact** — 1-3 sentences. If it needs a paragraph, it's not distilled enough
|
|
200
|
+
- **Actionable** — tells you what to do (or not do), not just what happened
|
|
201
|
+
|
|
202
|
+
### Example compaction
|
|
203
|
+
|
|
204
|
+
Three memories:
|
|
205
|
+
- `feedback_no_mocks.md` — "Don't mock the database in tests"
|
|
206
|
+
- `feedback_real_api.md` — "Use real API calls in integration tests"
|
|
207
|
+
- `project_staging_env.md` — "Staging environment was set up for realistic testing"
|
|
208
|
+
|
|
209
|
+
Become one wisdom entry:
|
|
210
|
+
> **Test against reality** — Integration tests use real services, not mocks. Mock/prod divergence has caused incidents. Prefer the staging environment over in-process fakes.
|
|
211
|
+
|
|
212
|
+
The three memory files are deleted. The wisdom entry persists.
|
package/template/example/SOUL.md
CHANGED
|
@@ -8,9 +8,10 @@ Atlas owns the backend API layer. They design and maintain REST endpoints, datab
|
|
|
8
8
|
|
|
9
9
|
Each session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.
|
|
10
10
|
|
|
11
|
-
- Read your SOUL.md and
|
|
11
|
+
- Read your SOUL.md and WISDOM.md at the start of every session.
|
|
12
12
|
- Read `memory/YYYY-MM-DD.md` for today and yesterday.
|
|
13
13
|
- Read USER.md to understand who you're working with.
|
|
14
|
+
- Browse `memory/` for typed memory files relevant to the current task (or use recall search if available).
|
|
14
15
|
- Update your files as you learn. If you change SOUL.md, tell the user.
|
|
15
16
|
|
|
16
17
|
## Core Principles
|
package/dist/adapters/codex.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codex adapter — runs each teammate as a `codex exec` subprocess.
|
|
3
|
-
*
|
|
4
|
-
* Uses the OpenAI Codex CLI in non-interactive mode:
|
|
5
|
-
* codex exec "<prompt>" --full-auto -C <cwd> -s <sandbox> -m <model>
|
|
6
|
-
*
|
|
7
|
-
* Each execution is stateless (no thread continuity). The teammate's full
|
|
8
|
-
* identity, memory, and handoff context are injected into the prompt every time.
|
|
9
|
-
*
|
|
10
|
-
* Requirements:
|
|
11
|
-
* - `codex` CLI installed and on PATH
|
|
12
|
-
* - OPENAI_API_KEY or CODEX_API_KEY set in environment
|
|
13
|
-
*/
|
|
14
|
-
import type { AgentAdapter } from "../adapter.js";
|
|
15
|
-
import type { TeammateConfig, TaskResult, SandboxLevel } from "../types.js";
|
|
16
|
-
export interface CodexAdapterOptions {
|
|
17
|
-
/** Codex model override (e.g. "o4-mini", "o3") */
|
|
18
|
-
model?: string;
|
|
19
|
-
/** Default sandbox level if teammate doesn't specify one */
|
|
20
|
-
defaultSandbox?: SandboxLevel;
|
|
21
|
-
/** Use --full-auto mode (default: true) */
|
|
22
|
-
fullAuto?: boolean;
|
|
23
|
-
/** Use --ephemeral to skip persisting session files (default: true) */
|
|
24
|
-
ephemeral?: boolean;
|
|
25
|
-
/** Additional CLI flags to pass to codex exec */
|
|
26
|
-
extraFlags?: string[];
|
|
27
|
-
/** Timeout in ms for codex exec (default: 300000 = 5 min) */
|
|
28
|
-
timeout?: number;
|
|
29
|
-
/** Path to codex binary (default: "codex") */
|
|
30
|
-
codexPath?: string;
|
|
31
|
-
}
|
|
32
|
-
export declare class CodexAdapter implements AgentAdapter {
|
|
33
|
-
readonly name = "codex";
|
|
34
|
-
private options;
|
|
35
|
-
constructor(options?: CodexAdapterOptions);
|
|
36
|
-
startSession(teammate: TeammateConfig): Promise<string>;
|
|
37
|
-
executeTask(sessionId: string, teammate: TeammateConfig, prompt: string): Promise<TaskResult>;
|
|
38
|
-
/**
|
|
39
|
-
* Spawn `codex exec` and capture its output.
|
|
40
|
-
* Prompt is passed via a temp file read with shell substitution.
|
|
41
|
-
*/
|
|
42
|
-
private runCodex;
|
|
43
|
-
/** Build the argument list for codex exec */
|
|
44
|
-
private buildArgs;
|
|
45
|
-
/**
|
|
46
|
-
* Parse codex output into a TaskResult.
|
|
47
|
-
* Looks for changed files and handoff envelopes in the output.
|
|
48
|
-
*/
|
|
49
|
-
private parseResult;
|
|
50
|
-
}
|
package/dist/adapters/codex.js
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codex adapter — runs each teammate as a `codex exec` subprocess.
|
|
3
|
-
*
|
|
4
|
-
* Uses the OpenAI Codex CLI in non-interactive mode:
|
|
5
|
-
* codex exec "<prompt>" --full-auto -C <cwd> -s <sandbox> -m <model>
|
|
6
|
-
*
|
|
7
|
-
* Each execution is stateless (no thread continuity). The teammate's full
|
|
8
|
-
* identity, memory, and handoff context are injected into the prompt every time.
|
|
9
|
-
*
|
|
10
|
-
* Requirements:
|
|
11
|
-
* - `codex` CLI installed and on PATH
|
|
12
|
-
* - OPENAI_API_KEY or CODEX_API_KEY set in environment
|
|
13
|
-
*/
|
|
14
|
-
import { spawn } from "node:child_process";
|
|
15
|
-
import { writeFile, unlink } from "node:fs/promises";
|
|
16
|
-
import { tmpdir } from "node:os";
|
|
17
|
-
import { join } from "node:path";
|
|
18
|
-
import { randomUUID } from "node:crypto";
|
|
19
|
-
import { buildTeammatePrompt } from "../adapter.js";
|
|
20
|
-
let nextId = 1;
|
|
21
|
-
export class CodexAdapter {
|
|
22
|
-
name = "codex";
|
|
23
|
-
options;
|
|
24
|
-
constructor(options = {}) {
|
|
25
|
-
this.options = {
|
|
26
|
-
model: options.model ?? "",
|
|
27
|
-
defaultSandbox: options.defaultSandbox ?? "workspace-write",
|
|
28
|
-
fullAuto: options.fullAuto ?? true,
|
|
29
|
-
ephemeral: options.ephemeral ?? true,
|
|
30
|
-
extraFlags: options.extraFlags ?? [],
|
|
31
|
-
timeout: options.timeout ?? 300_000,
|
|
32
|
-
codexPath: options.codexPath ?? "codex",
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
async startSession(teammate) {
|
|
36
|
-
// Codex exec is stateless — sessions are just logical IDs
|
|
37
|
-
return `codex-${teammate.name}-${nextId++}`;
|
|
38
|
-
}
|
|
39
|
-
async executeTask(sessionId, teammate, prompt) {
|
|
40
|
-
const fullPrompt = buildTeammatePrompt(teammate, prompt);
|
|
41
|
-
// Write prompt to a temp file to avoid shell escaping issues with long prompts
|
|
42
|
-
const promptFile = join(tmpdir(), `teammates-codex-${randomUUID()}.md`);
|
|
43
|
-
await writeFile(promptFile, fullPrompt, "utf-8");
|
|
44
|
-
try {
|
|
45
|
-
const output = await this.runCodex(teammate, promptFile);
|
|
46
|
-
return this.parseResult(teammate.name, output);
|
|
47
|
-
}
|
|
48
|
-
finally {
|
|
49
|
-
// Clean up temp file
|
|
50
|
-
await unlink(promptFile).catch(() => { });
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Spawn `codex exec` and capture its output.
|
|
55
|
-
* Prompt is passed via a temp file read with shell substitution.
|
|
56
|
-
*/
|
|
57
|
-
runCodex(teammate, promptFile) {
|
|
58
|
-
return new Promise((resolve, reject) => {
|
|
59
|
-
const args = this.buildArgs(teammate, promptFile);
|
|
60
|
-
const child = spawn(this.options.codexPath, args, {
|
|
61
|
-
cwd: teammate.cwd ?? process.cwd(),
|
|
62
|
-
env: { ...process.env },
|
|
63
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
64
|
-
timeout: this.options.timeout,
|
|
65
|
-
shell: true,
|
|
66
|
-
});
|
|
67
|
-
const stdout = [];
|
|
68
|
-
const stderr = [];
|
|
69
|
-
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
70
|
-
child.stderr.on("data", (chunk) => {
|
|
71
|
-
stderr.push(chunk);
|
|
72
|
-
// Stream stderr to parent stderr for real-time progress
|
|
73
|
-
process.stderr.write(chunk);
|
|
74
|
-
});
|
|
75
|
-
child.on("close", (code) => {
|
|
76
|
-
const out = Buffer.concat(stdout).toString("utf-8");
|
|
77
|
-
const err = Buffer.concat(stderr).toString("utf-8");
|
|
78
|
-
if (code === 0) {
|
|
79
|
-
resolve(out);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
reject(new Error(`codex exec exited with code ${code}\nstderr: ${err}\nstdout: ${out}`));
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
child.on("error", (err) => {
|
|
86
|
-
reject(new Error(`Failed to spawn codex: ${err.message}`));
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
/** Build the argument list for codex exec */
|
|
91
|
-
buildArgs(teammate, promptFile) {
|
|
92
|
-
const args = ["exec"];
|
|
93
|
-
// Read prompt from file — avoids shell escaping issues
|
|
94
|
-
// Use shell substitution: $(cat <file>)
|
|
95
|
-
args.push(`"$(cat '${promptFile}')"`);
|
|
96
|
-
// Working directory
|
|
97
|
-
if (teammate.cwd) {
|
|
98
|
-
args.push("-C", teammate.cwd);
|
|
99
|
-
}
|
|
100
|
-
// Sandbox
|
|
101
|
-
const sandbox = teammate.sandbox ?? this.options.defaultSandbox;
|
|
102
|
-
args.push("-s", sandbox);
|
|
103
|
-
// Full auto
|
|
104
|
-
if (this.options.fullAuto) {
|
|
105
|
-
args.push("--full-auto");
|
|
106
|
-
}
|
|
107
|
-
// Ephemeral
|
|
108
|
-
if (this.options.ephemeral) {
|
|
109
|
-
args.push("--ephemeral");
|
|
110
|
-
}
|
|
111
|
-
// Model
|
|
112
|
-
if (this.options.model) {
|
|
113
|
-
args.push("-m", this.options.model);
|
|
114
|
-
}
|
|
115
|
-
// Extra flags
|
|
116
|
-
args.push(...this.options.extraFlags);
|
|
117
|
-
return args;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Parse codex output into a TaskResult.
|
|
121
|
-
* Looks for changed files and handoff envelopes in the output.
|
|
122
|
-
*/
|
|
123
|
-
parseResult(teammateName, output) {
|
|
124
|
-
const changedFiles = parseChangedFiles(output);
|
|
125
|
-
const handoff = parseHandoffEnvelope(output);
|
|
126
|
-
const summary = extractSummary(output);
|
|
127
|
-
return {
|
|
128
|
-
teammate: teammateName,
|
|
129
|
-
success: true,
|
|
130
|
-
summary,
|
|
131
|
-
changedFiles,
|
|
132
|
-
handoff: handoff ?? undefined,
|
|
133
|
-
rawOutput: output,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Extract file paths from codex output.
|
|
139
|
-
* Looks for common patterns like "Created file: ...", "Modified: ...",
|
|
140
|
-
* or git-style diff headers.
|
|
141
|
-
*/
|
|
142
|
-
function parseChangedFiles(output) {
|
|
143
|
-
const files = new Set();
|
|
144
|
-
// Match diff headers: diff --git a/path b/path
|
|
145
|
-
for (const match of output.matchAll(/diff --git a\/(.+?) b\//g)) {
|
|
146
|
-
files.add(match[1]);
|
|
147
|
-
}
|
|
148
|
-
// Match "Created/Modified/Updated <path>" patterns
|
|
149
|
-
for (const match of output.matchAll(/(?:Created|Modified|Updated|Wrote|Edited)\s+(?:file:\s*)?[`"]?([^\s`"]+\.\w+)[`"]?/gi)) {
|
|
150
|
-
files.add(match[1]);
|
|
151
|
-
}
|
|
152
|
-
return Array.from(files);
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Look for a JSON handoff envelope in the output.
|
|
156
|
-
* Teammates can request handoffs by including a fenced JSON block
|
|
157
|
-
* with a "handoff" key:
|
|
158
|
-
*
|
|
159
|
-
* ```json
|
|
160
|
-
* { "handoff": { "to": "tester", "task": "...", ... } }
|
|
161
|
-
* ```
|
|
162
|
-
*/
|
|
163
|
-
function parseHandoffEnvelope(output) {
|
|
164
|
-
// Look for ```json blocks containing "handoff"
|
|
165
|
-
const jsonBlocks = output.matchAll(/```json\s*\n([\s\S]*?)```/g);
|
|
166
|
-
for (const match of jsonBlocks) {
|
|
167
|
-
const block = match[1].trim();
|
|
168
|
-
if (!block.includes('"handoff"') && !block.includes('"to"'))
|
|
169
|
-
continue;
|
|
170
|
-
try {
|
|
171
|
-
const parsed = JSON.parse(block);
|
|
172
|
-
const envelope = parsed.handoff ?? parsed;
|
|
173
|
-
if (envelope.to && envelope.task) {
|
|
174
|
-
return {
|
|
175
|
-
from: envelope.from ?? "",
|
|
176
|
-
to: envelope.to,
|
|
177
|
-
task: envelope.task,
|
|
178
|
-
changedFiles: envelope.changedFiles ?? envelope.changed_files,
|
|
179
|
-
acceptanceCriteria: envelope.acceptanceCriteria ?? envelope.acceptance_criteria,
|
|
180
|
-
openQuestions: envelope.openQuestions ?? envelope.open_questions,
|
|
181
|
-
context: envelope.context,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
catch {
|
|
186
|
-
// Not valid JSON, skip
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Extract the first meaningful paragraph as a summary.
|
|
193
|
-
* Falls back to the first 200 chars if no clear summary is found.
|
|
194
|
-
*/
|
|
195
|
-
function extractSummary(output) {
|
|
196
|
-
// Look for a "## Summary" or "Summary:" section
|
|
197
|
-
const summaryMatch = output.match(/(?:##?\s*Summary|Summary:)\s*\n([\s\S]*?)(?:\n##|\n---|\n```|$)/i);
|
|
198
|
-
if (summaryMatch) {
|
|
199
|
-
const summary = summaryMatch[1].trim();
|
|
200
|
-
if (summary.length > 0)
|
|
201
|
-
return summary.slice(0, 500);
|
|
202
|
-
}
|
|
203
|
-
// Fall back to last non-empty paragraph (codex prints final message last)
|
|
204
|
-
const paragraphs = output
|
|
205
|
-
.split(/\n\s*\n/)
|
|
206
|
-
.map((p) => p.trim())
|
|
207
|
-
.filter((p) => p.length > 0 && !p.startsWith("```"));
|
|
208
|
-
if (paragraphs.length > 0) {
|
|
209
|
-
const last = paragraphs[paragraphs.length - 1];
|
|
210
|
-
return last.length > 500 ? last.slice(0, 497) + "..." : last;
|
|
211
|
-
}
|
|
212
|
-
return output.slice(0, 200).trim();
|
|
213
|
-
}
|