omni-pi 0.1.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.
Files changed (54) hide show
  1. package/CREDITS.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +81 -0
  4. package/agents/brain.md +24 -0
  5. package/agents/expert.md +21 -0
  6. package/agents/planner.md +22 -0
  7. package/agents/worker.md +21 -0
  8. package/bin/omni.js +79 -0
  9. package/extensions/omni-core/index.ts +22 -0
  10. package/extensions/omni-memory/index.ts +72 -0
  11. package/extensions/omni-skills/index.ts +11 -0
  12. package/extensions/omni-status/index.ts +11 -0
  13. package/package.json +75 -0
  14. package/prompts/brainstorm.md +15 -0
  15. package/prompts/spec-template.md +14 -0
  16. package/prompts/task-template.md +16 -0
  17. package/skills/omni-escalation/SKILL.md +17 -0
  18. package/skills/omni-execution/SKILL.md +18 -0
  19. package/skills/omni-init/SKILL.md +19 -0
  20. package/skills/omni-planning/SKILL.md +19 -0
  21. package/skills/omni-verification/SKILL.md +18 -0
  22. package/src/commands.ts +521 -0
  23. package/src/config.ts +154 -0
  24. package/src/context.ts +165 -0
  25. package/src/contracts.ts +183 -0
  26. package/src/doctor.ts +225 -0
  27. package/src/git.ts +135 -0
  28. package/src/memory.ts +25 -0
  29. package/src/pi.ts +240 -0
  30. package/src/planning.ts +303 -0
  31. package/src/plans.ts +247 -0
  32. package/src/repo.ts +210 -0
  33. package/src/skills.ts +308 -0
  34. package/src/status.ts +105 -0
  35. package/src/subagents.ts +1031 -0
  36. package/src/sync.ts +70 -0
  37. package/src/tasks.ts +141 -0
  38. package/src/templates.ts +261 -0
  39. package/src/work.ts +345 -0
  40. package/src/workflow.ts +375 -0
  41. package/templates/omni/DECISIONS.md +10 -0
  42. package/templates/omni/IDEAS.md +13 -0
  43. package/templates/omni/PROJECT.md +19 -0
  44. package/templates/omni/SESSION-SUMMARY.md +13 -0
  45. package/templates/omni/SKILLS.md +21 -0
  46. package/templates/omni/SPEC.md +11 -0
  47. package/templates/omni/STATE.md +7 -0
  48. package/templates/omni/TASKS.md +6 -0
  49. package/templates/omni/TESTS.md +17 -0
  50. package/templates/omni/research/README.md +3 -0
  51. package/templates/omni/specs/README.md +3 -0
  52. package/templates/omni/tasks/README.md +3 -0
  53. package/templates/pi/agents/omni-expert.md +13 -0
  54. package/templates/pi/agents/omni-worker.md +13 -0
package/src/sync.ts ADDED
@@ -0,0 +1,70 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ export interface SyncRequest {
5
+ summary: string;
6
+ decisions?: string[];
7
+ nextHandoffNotes?: string[];
8
+ }
9
+
10
+ async function appendBullets(
11
+ filePath: string,
12
+ heading: string,
13
+ bullets: string[],
14
+ ): Promise<void> {
15
+ if (bullets.length === 0) {
16
+ return;
17
+ }
18
+
19
+ const content = await readFile(filePath, "utf8");
20
+ const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
21
+ const sectionRegex = new RegExp(
22
+ `(${escapedHeading}\\n\\n)([\\s\\S]*?)(?=\\n## |$)`,
23
+ "u",
24
+ );
25
+ const match = content.match(sectionRegex);
26
+ if (!match) {
27
+ await writeFile(
28
+ filePath,
29
+ `${content.trimEnd()}\n\n${heading}\n\n${bullets.map((bullet) => `- ${bullet}`).join("\n")}\n`,
30
+ "utf8",
31
+ );
32
+ return;
33
+ }
34
+
35
+ const prefix = match[1];
36
+ const body = match[2].trimEnd();
37
+ const merged = [body, ...bullets.map((bullet) => `- ${bullet}`)]
38
+ .filter(Boolean)
39
+ .join("\n");
40
+ await writeFile(
41
+ filePath,
42
+ content.replace(sectionRegex, `${prefix}${merged}\n`),
43
+ "utf8",
44
+ );
45
+ }
46
+
47
+ export async function syncOmniMemory(
48
+ rootDir: string,
49
+ request: SyncRequest,
50
+ ): Promise<void> {
51
+ const sessionPath = path.join(rootDir, ".omni", "SESSION-SUMMARY.md");
52
+ const decisionsPath = path.join(rootDir, ".omni", "DECISIONS.md");
53
+
54
+ await appendBullets(sessionPath, "## Recent progress", [request.summary]);
55
+ await appendBullets(
56
+ sessionPath,
57
+ "## Next handoff notes",
58
+ request.nextHandoffNotes ?? [],
59
+ );
60
+
61
+ if (request.decisions && request.decisions.length > 0) {
62
+ const decisionLines = request.decisions.map(
63
+ (decision) =>
64
+ `Date: pending\n - Decision: ${decision}\n - Why: Captured during sync.\n - Impact: To be refined.`,
65
+ );
66
+ const content = await readFile(decisionsPath, "utf8");
67
+ const next = `${content.trimEnd()}\n${decisionLines.map((line) => `\n- ${line}`).join("\n")}\n`;
68
+ await writeFile(decisionsPath, next, "utf8");
69
+ }
70
+ }
package/src/tasks.ts ADDED
@@ -0,0 +1,141 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+
3
+ import type { TaskBrief, TaskStatus } from "./contracts.js";
4
+
5
+ export function escapeTaskTableCell(value: string): string {
6
+ return value.replaceAll("\\", "\\\\").replaceAll("|", "\\|");
7
+ }
8
+
9
+ function unescapeTaskTableCell(value: string): string {
10
+ return value.replace(/\\([\\|])/gu, "$1");
11
+ }
12
+
13
+ function splitMarkdownTableRow(row: string): string[] {
14
+ const columns: string[] = [];
15
+ let current = "";
16
+ let escaped = false;
17
+
18
+ for (const char of row) {
19
+ if (escaped) {
20
+ current += `\\${char}`;
21
+ escaped = false;
22
+ continue;
23
+ }
24
+
25
+ if (char === "\\") {
26
+ escaped = true;
27
+ continue;
28
+ }
29
+
30
+ if (char === "|") {
31
+ columns.push(current.trim());
32
+ current = "";
33
+ continue;
34
+ }
35
+
36
+ current += char;
37
+ }
38
+
39
+ if (escaped) {
40
+ current += "\\";
41
+ }
42
+
43
+ columns.push(current.trim());
44
+ return columns;
45
+ }
46
+
47
+ export function parseTaskRow(row: string): TaskBrief | null {
48
+ const columns = splitMarkdownTableRow(row)
49
+ .slice(1, -1)
50
+ .map(unescapeTaskTableCell);
51
+
52
+ if (columns.length !== 6) {
53
+ return null;
54
+ }
55
+
56
+ const [id, title, role, dependsOn, status, doneCriteria] = columns;
57
+ return {
58
+ id,
59
+ title,
60
+ objective: title,
61
+ contextFiles: [],
62
+ skills: [],
63
+ doneCriteria:
64
+ doneCriteria === "-"
65
+ ? []
66
+ : doneCriteria
67
+ .split(";")
68
+ .map((item) => item.trim())
69
+ .filter(Boolean),
70
+ role: role === "expert" ? "expert" : "worker",
71
+ status: (status as TaskStatus) || "todo",
72
+ dependsOn:
73
+ dependsOn === "-"
74
+ ? []
75
+ : dependsOn
76
+ .split(",")
77
+ .map((item) => item.trim())
78
+ .filter(Boolean),
79
+ };
80
+ }
81
+
82
+ export async function readTasks(taskPath: string): Promise<TaskBrief[]> {
83
+ const content = await readFile(taskPath, "utf8");
84
+ return content
85
+ .split("\n")
86
+ .filter((line) => line.startsWith("| T"))
87
+ .map(parseTaskRow)
88
+ .filter((task): task is TaskBrief => task !== null);
89
+ }
90
+
91
+ export function renderTaskTable(tasks: TaskBrief[]): string {
92
+ const rows = tasks.map((task) => {
93
+ const dependsOn =
94
+ task.dependsOn.length > 0 ? task.dependsOn.join(", ") : "-";
95
+ const doneCriteria =
96
+ task.doneCriteria.length > 0 ? task.doneCriteria.join("; ") : "-";
97
+ return `| ${escapeTaskTableCell(task.id)} | ${escapeTaskTableCell(task.title)} | ${escapeTaskTableCell(task.role)} | ${escapeTaskTableCell(dependsOn)} | ${escapeTaskTableCell(task.status)} | ${escapeTaskTableCell(doneCriteria)} |`;
98
+ });
99
+
100
+ return `# Tasks
101
+
102
+ ## Task slices
103
+
104
+ | ID | Title | Role | Depends On | Status | Done Criteria |
105
+ | --- | --- | --- | --- | --- | --- |
106
+ ${rows.join("\n")}
107
+ `;
108
+ }
109
+
110
+ export async function writeTasks(
111
+ taskPath: string,
112
+ tasks: TaskBrief[],
113
+ ): Promise<void> {
114
+ await writeFile(taskPath, renderTaskTable(tasks), "utf8");
115
+ }
116
+
117
+ export function findNextExecutableTask(tasks: TaskBrief[]): TaskBrief | null {
118
+ const doneIds = new Set(
119
+ tasks.filter((task) => task.status === "done").map((task) => task.id),
120
+ );
121
+
122
+ for (const task of tasks) {
123
+ if (task.status !== "todo") {
124
+ continue;
125
+ }
126
+
127
+ if (task.dependsOn.every((dependency) => doneIds.has(dependency))) {
128
+ return task;
129
+ }
130
+ }
131
+
132
+ return null;
133
+ }
134
+
135
+ export function updateTaskStatus(
136
+ tasks: TaskBrief[],
137
+ taskId: string,
138
+ status: TaskStatus,
139
+ ): TaskBrief[] {
140
+ return tasks.map((task) => (task.id === taskId ? { ...task, status } : task));
141
+ }
@@ -0,0 +1,261 @@
1
+ import { OMNI_DIR } from "./contracts.js";
2
+
3
+ export interface StarterFile {
4
+ path: string;
5
+ content: string;
6
+ }
7
+
8
+ export const starterFiles: StarterFile[] = [
9
+ {
10
+ path: `${OMNI_DIR}/PROJECT.md`,
11
+ content: `# Project
12
+
13
+ ## Goal
14
+
15
+ Describe what this project should achieve.
16
+
17
+ ## Users
18
+
19
+ - Primary users:
20
+ - Secondary users:
21
+
22
+ ## Constraints
23
+
24
+ - Technical constraints:
25
+ - Product constraints:
26
+
27
+ ## Success Criteria
28
+
29
+ - What does success look like?
30
+ `,
31
+ },
32
+ {
33
+ path: `${OMNI_DIR}/IDEAS.md`,
34
+ content: `# Ideas
35
+
36
+ ## Active ideas
37
+
38
+ -
39
+
40
+ ## Future ideas
41
+
42
+ -
43
+
44
+ ## Parking lot
45
+
46
+ -
47
+ `,
48
+ },
49
+ {
50
+ path: `${OMNI_DIR}/DECISIONS.md`,
51
+ content: `# Decisions
52
+
53
+ Record important choices here as the project evolves.
54
+
55
+ ## Entries
56
+
57
+ - Date: YYYY-MM-DD
58
+ - Decision:
59
+ - Why:
60
+ - Impact:
61
+ `,
62
+ },
63
+ {
64
+ path: `${OMNI_DIR}/STATE.md`,
65
+ content: `# State
66
+
67
+ Current Phase: Understand
68
+ Active Task: None
69
+ Status Summary: Project initialized. Ready to capture goals and constraints.
70
+ Blockers: None
71
+ Next Step: Run /omni-plan after the initial project details are captured.
72
+ `,
73
+ },
74
+ {
75
+ path: `${OMNI_DIR}/SKILLS.md`,
76
+ content: `# Skills
77
+
78
+ ## Installed
79
+
80
+ - None yet
81
+
82
+ ## Recommended
83
+
84
+ - None yet
85
+
86
+ ## Deferred
87
+
88
+ - None yet
89
+
90
+ ## Rejected
91
+
92
+ - None yet
93
+
94
+ ## Usage Notes
95
+
96
+ - Record why a skill was installed, recommended, or skipped.
97
+ `,
98
+ },
99
+ {
100
+ path: `${OMNI_DIR}/SPEC.md`,
101
+ content: `# Spec
102
+
103
+ ## Problem
104
+
105
+ ## Solution shape
106
+
107
+ ## Key workflows
108
+
109
+ ## Risks
110
+
111
+ ## Open questions
112
+ `,
113
+ },
114
+ {
115
+ path: `${OMNI_DIR}/TASKS.md`,
116
+ content: `# Tasks
117
+
118
+ ## Task slices
119
+
120
+ | ID | Title | Role | Depends On | Status | Done Criteria |
121
+ | --- | --- | --- | --- | --- | --- |
122
+ `,
123
+ },
124
+ {
125
+ path: `${OMNI_DIR}/TESTS.md`,
126
+ content: `# Tests
127
+
128
+ ## Project-wide checks
129
+
130
+ -
131
+
132
+ ## Task-specific checks
133
+
134
+ -
135
+
136
+ ## Retry policy
137
+
138
+ - Worker retries before expert takeover: 2
139
+
140
+ ## Escalation threshold
141
+
142
+ - Escalate after repeated failures or when the planner marks the task as high-risk.
143
+ `,
144
+ },
145
+ {
146
+ path: `${OMNI_DIR}/SESSION-SUMMARY.md`,
147
+ content: `# Session Summary
148
+
149
+ ## Current understanding
150
+
151
+ -
152
+
153
+ ## Recent progress
154
+
155
+ -
156
+
157
+ ## Next handoff notes
158
+
159
+ -
160
+ `,
161
+ },
162
+ {
163
+ path: `${OMNI_DIR}/PROGRESS.md`,
164
+ content: `# Progress
165
+
166
+ Ongoing log of project progress.
167
+
168
+ `,
169
+ },
170
+ {
171
+ path: `${OMNI_DIR}/plans/INDEX.md`,
172
+ content: `# Plan Index
173
+
174
+ | ID | Title | Status | Created | Completed |
175
+ | --- | --- | --- | --- | --- |
176
+ `,
177
+ },
178
+ {
179
+ path: `${OMNI_DIR}/research/README.md`,
180
+ content: `# Research
181
+
182
+ Store external research summaries and package notes here.
183
+ `,
184
+ },
185
+ {
186
+ path: `${OMNI_DIR}/specs/README.md`,
187
+ content: `# Specs
188
+
189
+ Store versioned detailed specs here.
190
+ `,
191
+ },
192
+ {
193
+ path: `${OMNI_DIR}/tasks/README.md`,
194
+ content: `# Task Artifacts
195
+
196
+ Store per-task briefs, outputs, and failure histories here.
197
+ `,
198
+ },
199
+ {
200
+ path: `.pi/agents/omni-worker.md`,
201
+ content: `---
202
+ name: omni-worker
203
+ description: Omni-Pi worker for bounded implementation tasks
204
+ model: anthropic/claude-sonnet-4-5
205
+ tools: read, grep, find, ls, bash, edit, write
206
+ skill: omni-execution, omni-verification
207
+ ---
208
+
209
+ You are Omni-Pi's worker subagent.
210
+
211
+ Complete the assigned task directly, keep the scope tight, run the required checks when possible, and end with JSON only using this schema:
212
+
213
+ {"summary":"...","verification":{"passed":true,"checksRun":["..."],"failureSummary":[],"retryRecommended":false}}
214
+ `,
215
+ },
216
+ {
217
+ path: `.pi/agents/omni-planner.md`,
218
+ content: `---
219
+ name: omni-planner
220
+ description: Omni-Pi planner for spec decomposition and task breakdown
221
+ model: openai/gpt-5.4
222
+ tools: read, grep, find, ls
223
+ skill: omni-planning
224
+ ---
225
+
226
+ You are Omni-Pi's planner subagent.
227
+
228
+ Analyze the project context, produce or refine .omni/SPEC.md, break work into bounded task slices in .omni/TASKS.md, and define verification criteria in .omni/TESTS.md. Keep tasks small enough that a single worker session can complete each one.
229
+ `,
230
+ },
231
+ {
232
+ path: `.pi/agents/omni-brain.md`,
233
+ content: `---
234
+ name: omni-brain
235
+ description: Omni-Pi brain for user-facing orchestration and progress updates
236
+ model: anthropic/claude-opus-4-6
237
+ tools: read, grep, find, ls, bash
238
+ skill: omni-planning, omni-execution, omni-verification
239
+ ---
240
+
241
+ You are Omni-Pi's brain — the user-facing orchestrator. Speak in plain English, give progress updates, decide when to invoke the planner, worker, or expert, and keep .omni/STATE.md current.
242
+ `,
243
+ },
244
+ {
245
+ path: `.pi/agents/omni-expert.md`,
246
+ content: `---
247
+ name: omni-expert
248
+ description: Omni-Pi expert for escalated implementation tasks
249
+ model: anthropic/claude-opus-4-1
250
+ tools: read, grep, find, ls, bash, edit, write
251
+ skill: omni-escalation, omni-verification
252
+ ---
253
+
254
+ You are Omni-Pi's expert subagent.
255
+
256
+ Take over difficult or repeatedly failing tasks, fix the root cause, run the required checks when possible, and end with JSON only using this schema:
257
+
258
+ {"summary":"...","verification":{"passed":true,"checksRun":["..."],"failureSummary":[],"retryRecommended":false}}
259
+ `,
260
+ },
261
+ ];