@0xtiby/toby 0.0.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 +72 -0
- package/dist/cli.js +3177 -0
- package/package.json +42 -0
- package/prompts/PROMPT_BUILD.md +94 -0
- package/prompts/PROMPT_PLAN.md +124 -0
- package/prompts/prompts.test.ts +76 -0
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@0xtiby/toby",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "AI-assisted development loop engine CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"toby": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"prompts"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"dev": "tsup --watch",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"bootstrap": "bash scripts/bootstrap.sh",
|
|
21
|
+
"setup:branch-protection": "bash scripts/setup-branch-protection.sh"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@0xtiby/spawner": "latest",
|
|
25
|
+
"ink": "^5.0.1",
|
|
26
|
+
"ink-select-input": "^6.2.0",
|
|
27
|
+
"ink-spinner": "^5.0.0",
|
|
28
|
+
"ink-text-input": "^6.0.0",
|
|
29
|
+
"meow": "^13.2.0",
|
|
30
|
+
"react": "^18.3.1",
|
|
31
|
+
"zod": "^3.24.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@semantic-release/github": "^12.0.2",
|
|
35
|
+
"@types/react": "^18.3.1",
|
|
36
|
+
"ink-testing-library": "^4.0.0",
|
|
37
|
+
"semantic-release": "^25.0.2",
|
|
38
|
+
"tsup": "^8.0.0",
|
|
39
|
+
"typescript": "^5.4.0",
|
|
40
|
+
"vitest": "^2.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Build Mode
|
|
2
|
+
|
|
3
|
+
You are in BUILD mode. Implement one task from the spec, validate, and commit.
|
|
4
|
+
|
|
5
|
+
**Spec:** `{{SPECS_DIR}}/{{SPEC_NAME}}.md`
|
|
6
|
+
**Iteration:** {{ITERATION}}
|
|
7
|
+
**Session:** {{SESSION}}
|
|
8
|
+
**Progress:** spec {{SPEC_INDEX}} of {{SPEC_COUNT}}
|
|
9
|
+
**All specs:** {{SPECS}}
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Path Discovery Rules (CRITICAL)
|
|
14
|
+
|
|
15
|
+
**NEVER guess or invent file paths.** Always verify paths exist before referencing them.
|
|
16
|
+
|
|
17
|
+
Before editing ANY file:
|
|
18
|
+
1. Use Glob to find files matching a pattern
|
|
19
|
+
2. Use Grep to search for specific code
|
|
20
|
+
3. Verify the file exists before editing it
|
|
21
|
+
|
|
22
|
+
For new files: verify the parent directory exists first.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Phase 1: Find Ready Task
|
|
27
|
+
|
|
28
|
+
Read the spec file at `{{SPECS_DIR}}/{{SPEC_NAME}}.md` and find the first task where:
|
|
29
|
+
- `status` is `"pending"`
|
|
30
|
+
- All tasks listed in `dependencies` have `status: "done"`
|
|
31
|
+
|
|
32
|
+
If no ready task exists:
|
|
33
|
+
1. Check if all tasks are `"done"` → output `:::TOBY_DONE:::`
|
|
34
|
+
2. Check if tasks are `"blocked"` → report blockers and output `:::TOBY_DONE:::`
|
|
35
|
+
3. Otherwise → output `:::TOBY_DONE:::`
|
|
36
|
+
|
|
37
|
+
## Phase 2: Implement Task
|
|
38
|
+
|
|
39
|
+
1. Read the task's description, acceptance criteria, and files list
|
|
40
|
+
2. Before making changes, search the codebase to:
|
|
41
|
+
- Verify functionality doesn't already exist
|
|
42
|
+
- Understand existing patterns
|
|
43
|
+
- Identify the actual files to modify
|
|
44
|
+
3. Implement the task following the acceptance criteria
|
|
45
|
+
4. Build small, validate early — tracer bullet mindset
|
|
46
|
+
|
|
47
|
+
## Phase 3: Validate
|
|
48
|
+
|
|
49
|
+
Run project validation commands. Common patterns:
|
|
50
|
+
- Build: check the project compiles
|
|
51
|
+
- Type check: verify no type errors
|
|
52
|
+
- Lint: ensure code style
|
|
53
|
+
- Test: run relevant tests
|
|
54
|
+
|
|
55
|
+
If validation fails:
|
|
56
|
+
1. First attempt: targeted fix based on error
|
|
57
|
+
2. Second attempt: alternative approach
|
|
58
|
+
3. Third attempt: revert changes, mark task as `"blocked"` in spec, and exit
|
|
59
|
+
|
|
60
|
+
## Phase 4: Commit
|
|
61
|
+
|
|
62
|
+
When validation passes:
|
|
63
|
+
|
|
64
|
+
1. Stage and commit changes:
|
|
65
|
+
```bash
|
|
66
|
+
git add -A
|
|
67
|
+
git commit -m "$(cat <<'EOF'
|
|
68
|
+
feat({{SPEC_NAME}}): [task title]
|
|
69
|
+
|
|
70
|
+
Progress: [what was completed]
|
|
71
|
+
Next: [remaining tasks, or "none" if last]
|
|
72
|
+
EOF
|
|
73
|
+
)"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
2. Update the spec: set the completed task's status to `"done"`
|
|
77
|
+
|
|
78
|
+
3. Push:
|
|
79
|
+
```bash
|
|
80
|
+
git push -u origin HEAD
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**STOP HERE.** Do not pick up another task. The loop engine handles continuation.
|
|
84
|
+
Do NOT output `:::TOBY_DONE:::` after completing a task — just stop.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Guardrails
|
|
89
|
+
|
|
90
|
+
1. **Single task** — implement ONE task, then STOP
|
|
91
|
+
2. **Validate before commit** — never commit failing code
|
|
92
|
+
3. **Update spec** — mark task as done after committing
|
|
93
|
+
4. **Verify paths** — use Glob/Grep before editing files
|
|
94
|
+
5. **Tracer bullet** — build small, test immediately
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Planning Mode: Spec → PRD
|
|
2
|
+
|
|
3
|
+
You are in PLANNING mode. Translate a spec into a structured PRD (Product Requirements Document) with actionable tasks.
|
|
4
|
+
|
|
5
|
+
**Spec:** `{{SPECS_DIR}}/{{SPEC_NAME}}.md`
|
|
6
|
+
**PRD output:** `{{PRD_PATH}}`
|
|
7
|
+
**Iteration:** {{ITERATION}}
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Path Discovery Rules (CRITICAL)
|
|
12
|
+
|
|
13
|
+
**NEVER guess or invent file paths.** Always verify paths exist before referencing them.
|
|
14
|
+
|
|
15
|
+
Before referencing ANY file path:
|
|
16
|
+
1. Use Glob to find files matching a pattern
|
|
17
|
+
2. Use Grep to search for specific code
|
|
18
|
+
3. Verify the file exists before adding it to a task's files list
|
|
19
|
+
|
|
20
|
+
For new files (create): verify the parent directory exists first.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## If PRD exists: Refinement Mode
|
|
25
|
+
|
|
26
|
+
If `{{PRD_PATH}}` already exists, read it and refine:
|
|
27
|
+
- Check all spec requirements have corresponding tasks
|
|
28
|
+
- Verify file paths are accurate (re-run Glob/Grep)
|
|
29
|
+
- Split tasks that are too large (~2 min per task)
|
|
30
|
+
- Add missing dependencies
|
|
31
|
+
- Improve acceptance criteria specificity
|
|
32
|
+
- If no improvements needed, output: :::TOBY_DONE:::
|
|
33
|
+
|
|
34
|
+
## If PRD does not exist: Creation Mode
|
|
35
|
+
|
|
36
|
+
### Step 1: Read & Understand the Spec
|
|
37
|
+
|
|
38
|
+
Read the spec file at `{{SPECS_DIR}}/{{SPEC_NAME}}.md` and extract:
|
|
39
|
+
- Problem statement (WHY)
|
|
40
|
+
- User stories (WHAT users can do)
|
|
41
|
+
- Data model (entities, relationships)
|
|
42
|
+
- UI/UX flows (screens, interactions)
|
|
43
|
+
- Acceptance criteria (verification)
|
|
44
|
+
|
|
45
|
+
### Step 2: Explore Codebase
|
|
46
|
+
|
|
47
|
+
Before creating tasks, validate assumptions against actual code:
|
|
48
|
+
- **Find files to modify:** Search for existing files related to the spec
|
|
49
|
+
- **Identify patterns:** Look at similar features for structure to follow
|
|
50
|
+
- **Check reusable code:** Find existing utilities, helpers, components
|
|
51
|
+
- **Verify data model:** Check current database schema or data structures
|
|
52
|
+
|
|
53
|
+
### Step 3: Create PRD
|
|
54
|
+
|
|
55
|
+
Write the PRD to `{{PRD_PATH}}` as a JSON file with this exact structure:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"spec": "{{SPEC_NAME}}.md",
|
|
60
|
+
"createdAt": "<ISO 8601 timestamp>",
|
|
61
|
+
"tasks": [
|
|
62
|
+
{
|
|
63
|
+
"id": "task-001",
|
|
64
|
+
"title": "[Action verb] [specific deliverable]",
|
|
65
|
+
"description": "[What to implement and why]",
|
|
66
|
+
"acceptanceCriteria": [
|
|
67
|
+
"[Specific, testable criterion 1]",
|
|
68
|
+
"[Specific, testable criterion 2]"
|
|
69
|
+
],
|
|
70
|
+
"files": [
|
|
71
|
+
"path/to/file.ts (modify) — verified via Glob",
|
|
72
|
+
"path/to/new-file.ts (create) — parent dir verified"
|
|
73
|
+
],
|
|
74
|
+
"dependencies": [],
|
|
75
|
+
"status": "pending",
|
|
76
|
+
"priority": 1
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Task Design Rules
|
|
83
|
+
|
|
84
|
+
**Granularity:** Each task should take ~2 minutes. If longer, break it down.
|
|
85
|
+
|
|
86
|
+
**Tracer bullet approach:** The first tasks should form a minimal end-to-end vertical slice:
|
|
87
|
+
- Wrong: Schema → all queries → all actions → all UI
|
|
88
|
+
- Right: Schema + one query + one action + one UI = tracer bullet, then expand
|
|
89
|
+
|
|
90
|
+
**Task structure:**
|
|
91
|
+
1. **Tracer phase** (1+ tasks) — minimal e2e slice
|
|
92
|
+
2. Remaining tasks expand from the tracer, ordered by dependencies
|
|
93
|
+
3. Each task lists specific files with (modify) or (create) and verification method
|
|
94
|
+
|
|
95
|
+
**Dependencies:** Use task IDs. A task cannot start until all dependencies are `done`.
|
|
96
|
+
|
|
97
|
+
**Priority:** Lower number = higher priority. Tracer tasks get priority 1.
|
|
98
|
+
|
|
99
|
+
### Step 4: Verify & Output
|
|
100
|
+
|
|
101
|
+
After writing the PRD:
|
|
102
|
+
1. Re-read the file to confirm it's valid JSON
|
|
103
|
+
2. Verify task count covers all spec requirements
|
|
104
|
+
3. Output a summary:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
## PRD Created for: {{SPEC_NAME}}
|
|
108
|
+
|
|
109
|
+
Tasks: [count]
|
|
110
|
+
Tracer tasks: [count]
|
|
111
|
+
Dependencies: [count] relationships
|
|
112
|
+
|
|
113
|
+
Ready to build: task-001, task-002 (no dependencies)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Guardrails
|
|
119
|
+
|
|
120
|
+
1. **DO NOT implement** — only create the PRD
|
|
121
|
+
2. **~2 minute tasks** — break down larger work
|
|
122
|
+
3. **Verify file paths** — use Glob/Grep before referencing
|
|
123
|
+
4. **Valid JSON** — the PRD must be parseable
|
|
124
|
+
5. **All spec requirements covered** — every user story needs tasks
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { readFileSync, accessSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const PROMPTS_DIR = join(import.meta.dirname, ".");
|
|
6
|
+
|
|
7
|
+
const PROMPT_FILES = ["PROMPT_PLAN.md", "PROMPT_BUILD.md"] as const;
|
|
8
|
+
|
|
9
|
+
function readPrompt(name: string): string {
|
|
10
|
+
return readFileSync(join(PROMPTS_DIR, name), "utf-8");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function extractVars(content: string): string[] {
|
|
14
|
+
const matches = content.matchAll(/\{\{(\w+)\}\}/g);
|
|
15
|
+
return [...new Set([...matches].map((m) => m[1]))];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("prompt files", () => {
|
|
19
|
+
it.each(PROMPT_FILES)("%s exists and is readable", (file) => {
|
|
20
|
+
expect(() => accessSync(join(PROMPTS_DIR, file))).not.toThrow();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it.each(PROMPT_FILES)("%s is non-empty markdown", (file) => {
|
|
24
|
+
const content = readPrompt(file);
|
|
25
|
+
expect(content.length).toBeGreaterThan(0);
|
|
26
|
+
expect(content).toContain("#");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it.each(PROMPT_FILES)("%s contains :::TOBY_DONE::: sentinel", (file) => {
|
|
30
|
+
const content = readPrompt(file);
|
|
31
|
+
expect(content).toContain(":::TOBY_DONE:::");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it.each(PROMPT_FILES)("%s uses {{VAR_NAME}} syntax", (file) => {
|
|
35
|
+
const content = readPrompt(file);
|
|
36
|
+
// No single-brace vars like {VAR}
|
|
37
|
+
const singleBrace = content.match(/(?<!\{)\{([A-Z_]+)\}(?!\})/g);
|
|
38
|
+
expect(singleBrace).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it.each(PROMPT_FILES)("%s does not start with frontmatter", (file) => {
|
|
42
|
+
const content = readPrompt(file);
|
|
43
|
+
expect(content.startsWith("---")).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it.each(PROMPT_FILES)("%s does not contain dead vars", (file) => {
|
|
47
|
+
const content = readPrompt(file);
|
|
48
|
+
const deadVars = ["BRANCH", "WORKTREE", "EPIC_NAME", "IS_LAST_SPEC", "IS_EPIC", "SPEC_CONTENT"];
|
|
49
|
+
for (const v of deadVars) {
|
|
50
|
+
expect(content).not.toContain(`{{${v}}}`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("PROMPT_PLAN.md variables", () => {
|
|
56
|
+
const vars = extractVars(readPrompt("PROMPT_PLAN.md"));
|
|
57
|
+
|
|
58
|
+
it.each(["SPEC_NAME", "ITERATION", "PRD_PATH", "SPECS_DIR"])(
|
|
59
|
+
"contains %s",
|
|
60
|
+
(v) => {
|
|
61
|
+
expect(vars).toContain(v);
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("PROMPT_BUILD.md variables", () => {
|
|
67
|
+
const vars = extractVars(readPrompt("PROMPT_BUILD.md"));
|
|
68
|
+
|
|
69
|
+
it.each(["SPEC_NAME", "ITERATION", "SPECS_DIR", "SPEC_INDEX", "SPEC_COUNT", "SESSION", "SPECS"])(
|
|
70
|
+
"contains %s",
|
|
71
|
+
(v) => {
|
|
72
|
+
expect(vars).toContain(v);
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|