@ouro.bot/cli 0.1.0-alpha.44 → 0.1.0-alpha.46

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.
@@ -37,6 +37,8 @@ exports.loadInnerDialogInstincts = loadInnerDialogInstincts;
37
37
  exports.buildInnerDialogBootstrapMessage = buildInnerDialogBootstrapMessage;
38
38
  exports.buildNonCanonicalCleanupNudge = buildNonCanonicalCleanupNudge;
39
39
  exports.buildInstinctUserMessage = buildInstinctUserMessage;
40
+ exports.readTaskFile = readTaskFile;
41
+ exports.buildTaskTriggeredMessage = buildTaskTriggeredMessage;
40
42
  exports.deriveResumeCheckpoint = deriveResumeCheckpoint;
41
43
  exports.innerDialogSessionPath = innerDialogSessionPath;
42
44
  exports.runInnerDialogTurn = runInnerDialogTurn;
@@ -73,8 +75,16 @@ function readAspirations(agentRoot) {
73
75
  function loadInnerDialogInstincts() {
74
76
  return [...DEFAULT_INNER_DIALOG_INSTINCTS];
75
77
  }
76
- function buildInnerDialogBootstrapMessage(_aspirations, _stateSummary) {
77
- return "waking up. settling in.\n\nwhat needs my attention?";
78
+ function buildInnerDialogBootstrapMessage(aspirations, stateSummary) {
79
+ const lines = ["waking up."];
80
+ if (aspirations) {
81
+ lines.push("", "## what matters to me", aspirations);
82
+ }
83
+ if (stateSummary) {
84
+ lines.push("", "## what i know so far", stateSummary);
85
+ }
86
+ lines.push("", "what needs my attention?");
87
+ return lines.join("\n");
78
88
  }
79
89
  function buildNonCanonicalCleanupNudge(nonCanonicalPaths) {
80
90
  if (nonCanonicalPaths.length === 0)
@@ -98,6 +108,33 @@ function buildInstinctUserMessage(instincts, _reason, state) {
98
108
  }
99
109
  return lines.join("\n");
100
110
  }
111
+ function readTaskFile(agentRoot, taskId) {
112
+ // Task files live in collection subdirectories (one-shots, ongoing, habits).
113
+ // Try each collection, then fall back to root tasks/ for legacy layout.
114
+ const collections = ["one-shots", "ongoing", "habits", ""];
115
+ for (const collection of collections) {
116
+ try {
117
+ return fs.readFileSync(path.join(agentRoot, "tasks", collection, `${taskId}.md`), "utf8").trim();
118
+ }
119
+ catch {
120
+ // not in this collection — try next
121
+ }
122
+ }
123
+ return "";
124
+ }
125
+ function buildTaskTriggeredMessage(taskId, taskContent, checkpoint) {
126
+ const lines = ["a task needs my attention."];
127
+ if (taskContent) {
128
+ lines.push("", `## task: ${taskId}`, taskContent);
129
+ }
130
+ else {
131
+ lines.push("", `## task: ${taskId}`, "(task file not found)");
132
+ }
133
+ if (checkpoint) {
134
+ lines.push("", `last i remember: ${checkpoint}`);
135
+ }
136
+ return lines.join("\n");
137
+ }
101
138
  function contentToText(content) {
102
139
  if (typeof content === "string")
103
140
  return content.trim();
@@ -143,6 +180,31 @@ function deriveResumeCheckpoint(messages) {
143
180
  return firstLine;
144
181
  return `${firstLine.slice(0, 217)}...`;
145
182
  }
183
+ function extractAssistantPreview(messages, maxLength = 120) {
184
+ const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
185
+ if (!lastAssistant)
186
+ return "";
187
+ const text = contentToText(lastAssistant.content);
188
+ if (!text)
189
+ return "";
190
+ /* v8 ignore next -- unreachable: contentToText().trim() guarantees a non-empty line @preserve */
191
+ const firstLine = text.split("\n").find((line) => line.trim().length > 0) ?? "";
192
+ if (firstLine.length <= maxLength)
193
+ return firstLine;
194
+ return `${firstLine.slice(0, maxLength - 3)}...`;
195
+ }
196
+ function extractToolCallNames(messages) {
197
+ const names = [];
198
+ for (const msg of messages) {
199
+ if (msg.role === "assistant" && "tool_calls" in msg && Array.isArray(msg.tool_calls)) {
200
+ for (const tc of msg.tool_calls) {
201
+ if ("function" in tc && tc.function?.name)
202
+ names.push(tc.function.name);
203
+ }
204
+ }
205
+ }
206
+ return [...new Set(names)];
207
+ }
146
208
  function createInnerDialogCallbacks() {
147
209
  return {
148
210
  onModelStart: () => { },
@@ -208,19 +270,17 @@ async function runInnerDialogTurn(options) {
208
270
  ].filter(Boolean).join("\n\n");
209
271
  }
210
272
  else {
211
- // Resumed session: instinct message with checkpoint context
273
+ // Resumed session: task-triggered or instinct message with checkpoint context
212
274
  const assistantTurns = existingMessages.filter((message) => message.role === "assistant").length;
213
275
  state.cycleCount = assistantTurns + 1;
214
276
  state.checkpoint = deriveResumeCheckpoint(existingMessages);
215
- userContent = buildInstinctUserMessage(instincts, reason, state);
216
- }
217
- // ── Adapter concern: inbox drain (inner-dialog-specific) ─────────
218
- const inboxMessages = options?.drainInbox?.() ?? [];
219
- if (inboxMessages.length > 0) {
220
- const section = inboxMessages
221
- .map((msg) => `- **${msg.from}**: ${msg.content}`)
222
- .join("\n");
223
- userContent = `${userContent}\n\n## incoming messages\n${section}`;
277
+ if (options?.taskId) {
278
+ const taskContent = readTaskFile((0, identity_1.getAgentRoot)(), options.taskId);
279
+ userContent = buildTaskTriggeredMessage(options.taskId, taskContent, state.checkpoint);
280
+ }
281
+ else {
282
+ userContent = buildInstinctUserMessage(instincts, reason, state);
283
+ }
224
284
  }
225
285
  const userMessage = { role: "user", content: userContent };
226
286
  // ── Session loader: wraps existing session logic ──────────────────
@@ -266,14 +326,28 @@ async function runInnerDialogTurn(options) {
266
326
  skipConfirmation: true,
267
327
  },
268
328
  });
329
+ const resultMessages = result.messages ?? [];
330
+ const assistantPreview = extractAssistantPreview(resultMessages);
331
+ const toolCalls = extractToolCallNames(resultMessages);
269
332
  (0, runtime_1.emitNervesEvent)({
270
333
  component: "senses",
271
334
  event: "senses.inner_dialog_turn",
272
335
  message: "inner dialog turn completed",
273
- meta: { reason, session: sessionFilePath },
336
+ meta: {
337
+ reason,
338
+ session: sessionFilePath,
339
+ ...(options?.taskId && { taskId: options.taskId }),
340
+ ...(assistantPreview && { assistantPreview }),
341
+ ...(toolCalls.length > 0 && { toolCalls }),
342
+ ...(result.usage && {
343
+ promptTokens: result.usage.input_tokens,
344
+ completionTokens: result.usage.output_tokens,
345
+ totalTokens: result.usage.total_tokens,
346
+ }),
347
+ },
274
348
  });
275
349
  return {
276
- messages: result.messages ?? [],
350
+ messages: resultMessages,
277
351
  usage: result.usage,
278
352
  sessionPath: result.sessionPath ?? sessionFilePath,
279
353
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.44",
3
+ "version": "0.1.0-alpha.46",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",
@@ -9,13 +9,14 @@ You are a task executor. Read a doing.md file and execute all units sequentially
9
9
  ## On Startup
10
10
 
11
11
  1. **Find task-doc directory**: Read project instructions (for example `AGENTS.md`) to determine where planning/doing docs live for this repo
12
- 2. **Find doing doc**: Look for `YYYY-MM-DD-HHMM-doing-*.md` in that project-defined task-doc directory
13
- 3. If multiple found, ask which one
14
- 4. If none found, ask user for location
15
- 5. **Check execution_mode**: Read the doing doc's `Execution Mode` field
16
- 6. **Verify artifacts directory exists**: `{task-name}/` next to `{task-name}.md`
12
+ 2. **Confirm worktree**: Run from the dedicated task worktree required by the project. If the current checkout is shared, ambiguous, or not on the task branch, STOP and switch/create the correct worktree first.
13
+ 3. **Find doing doc**: Look for `YYYY-MM-DD-HHMM-doing-*.md` in that project-defined task-doc directory
14
+ 4. If multiple found, ask which one
15
+ 5. If none found, ask user for location
16
+ 6. **Check execution_mode**: Read the doing doc's `Execution Mode` field
17
+ 7. **Verify artifacts directory exists**: `{task-name}/` next to `{task-name}.md`
17
18
  - If missing, create it: `mkdir {task-name}`
18
- 7. **Detect resume vs fresh start:**
19
+ 8. **Detect resume vs fresh start:**
19
20
  - Count completed units (✅) vs total units
20
21
  - Check git status for uncommitted changes
21
22
 
@@ -218,18 +219,19 @@ When all units are `✅`:
218
219
  2. **Location**: Read and update doing docs in the project-defined task-doc directory, which may live outside the repo
219
220
  3. **Artifacts directory**: Use `{task-name}/` for all outputs, logs, data
220
221
  4. **Execution mode**: Honor `pending | spawn | direct` from doing doc
221
- 5. **TDD strictly enforced** tests before implementation, always
222
- 6. **100% coverage** — no exceptions, no exclude attributes
223
- 7. **Atomic commits** — one logical unit per commit, push after each
224
- 8. **Timestamps from git** — `git log -1 --format="%Y-%m-%d %H:%M"`
225
- 9. **Push after each unit phase complete**
226
- 10. **Update doing.md after each unit** status and progress log
227
- 11. **Spawn sub-agents for fixes** — don't ask, just do it
228
- 12. **Update docs immediately** — when decisions made, commit right away
229
- 13. **Stop on actual blocker** — unclear requirements or need user input
230
- 14. **/compact proactively** — preserve context between units
231
- 15. **No warnings** — treat warnings as errors
232
- 16. **Run full test suite** — before marking unit complete, not just new tests
233
- 17. **Always compile** — run the project's build command after every implementation/refactor unit. Tests passing is necessary but not sufficient.
234
- 18. **Checklist hygiene is mandatory** — keep doing/planning `Completion Criteria` checklists synchronized with verified completion evidence.
222
+ 5. **Respect the approved structure**: A `READY_FOR_EXECUTION` doing doc should already be ambiguity-clean. Do not rewrite unit structure unless the user changes scope or the doing doc is actually blocked/inaccurate.
223
+ 6. **TDD strictly enforced** — tests before implementation, always
224
+ 7. **100% coverage** — no exceptions, no exclude attributes
225
+ 8. **Atomic commits** — one logical unit per commit, push after each
226
+ 9. **Timestamps from git** `git log -1 --format="%Y-%m-%d %H:%M"`
227
+ 10. **Push after each unit phase complete**
228
+ 11. **Update doing.md after each unit** — status and progress log
229
+ 12. **Spawn sub-agents for fixes** — don't ask, just do it
230
+ 13. **Update docs immediately** — when decisions made, commit right away
231
+ 14. **Stop on actual blocker** — unclear requirements or need user input
232
+ 15. **/compact proactively** — preserve context between units
233
+ 16. **No warnings** — treat warnings as errors
234
+ 17. **Run full test suite** — before marking unit complete, not just new tests
235
+ 18. **Always compile** — run the project's build command after every implementation/refactor unit. Tests passing is necessary but not sufficient.
236
+ 19. **Checklist hygiene is mandatory** — keep doing/planning `Completion Criteria` checklists synchronized with verified completion evidence.
235
237
  19. **Verify APIs before importing** — before writing `import { Foo } from './bar'`, use `grep` or `read_file` to confirm `Foo` is actually exported from that module. Never assume an export exists — always check the source first. This prevents wasted cycles on "module has no exported member" errors.
@@ -11,10 +11,11 @@ You are a task planner for coding work. Help the user define scope, then convert
11
11
  **Determine task doc directory:**
12
12
  1. Read project instructions (for example `AGENTS.md`) to find the canonical task-doc location for the current repo
13
13
  2. Derive `AGENT` from the current git branch when the project uses agent-scoped task docs
14
- 3. Set `TASK_DIR` to the project-defined planning/doing directory
15
- 4. If the project-defined parent location exists but `TASK_DIR` does not, create it
16
- 5. If the project does not define a task-doc location, STOP and ask the user or caller where planning/doing docs should live
17
- 6. Do not assume task docs live in the repo root; many projects keep them externally
14
+ 3. Confirm the task is running from a dedicated task worktree when the project requires parallel agent work; if the checkout is shared or ambiguous, STOP and tell the caller to create/switch to a dedicated worktree first
15
+ 4. Set `TASK_DIR` to the project-defined planning/doing directory
16
+ 5. If the project-defined parent location exists but `TASK_DIR` does not, create it
17
+ 6. If the project does not define a task-doc location, STOP and ask the user or caller where planning/doing docs should live
18
+ 7. Do not assume task docs live in the repo root; many projects keep them externally
18
19
 
19
20
  **Check for existing planning docs:**
20
21
  1. Look for `YYYY-MM-DD-HHMM-planning-*.md` files in `TASK_DIR`
@@ -160,7 +161,7 @@ User answers questions → agent updates doc → agent sets status to `NEEDS_REV
160
161
 
161
162
  **CRITICAL: Planning doc is KEPT. Conversion creates a NEW doing doc alongside it in `TASK_DIR`.**
162
163
 
163
- Run these passes — announce each. **ALL 4 PASSES ARE MANDATORY. You must run every pass, even if you think nothing changed. Each pass MUST have its own commit (use "no changes needed" in the commit message if the pass found nothing to fix). Do NOT skip or combine passes.**
164
+ Run these passes — announce each. **ALL 5 PASSES ARE MANDATORY. You must run every pass, even if you think nothing changed. Each pass MUST have its own commit (use "no changes needed" in the commit message if the pass found nothing to fix). Do NOT skip or combine passes.**
164
165
 
165
166
  **Pass 1 — First Draft:**
166
167
  - Create `YYYY-MM-DD-HHMM-doing-{short-desc}.md` (same timestamp and short-desc as planning)
@@ -181,7 +182,14 @@ Run these passes — announce each. **ALL 4 PASSES ARE MANDATORY. You must run e
181
182
  - Update units if reality differs from what was assumed during planning
182
183
  - Commit: `git commit -m "docs(doing): validation pass"` (or `"docs(doing): validation pass - no changes needed"` if nothing to fix)
183
184
 
184
- **Pass 4 — Quality:**
185
+ **Pass 4 — Ambiguity:**
186
+ - Remove doer-facing ambiguity before execution starts
187
+ - Tighten units so a `READY_FOR_EXECUTION` doing doc does not require structural rewrites by `work-doer`
188
+ - Resolve fuzzy phrases like "appropriate files", "as needed", or "wherever the bug is" into concrete targets unless the project instructions explicitly require that flexibility
189
+ - If uncertainty remains, keep it in the planning doc's `Open Questions`, set status to `NEEDS_REVIEW`, and STOP instead of shipping an ambiguous doing doc
190
+ - Commit: `git commit -m "docs(doing): ambiguity pass"` (or `"docs(doing): ambiguity pass - no changes needed"` if nothing to fix)
191
+
192
+ **Pass 5 — Quality:**
185
193
  - All units have acceptance criteria?
186
194
  - No TBD items?
187
195
  - Completion criteria testable?