@ouro.bot/cli 0.1.0-alpha.280 → 0.1.0-alpha.281

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/changelog.json CHANGED
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.281",
6
+ "changes": [
7
+ "feat(bundle): new `src/heart/bundle-state.ts` module exports `detectBundleState(agentRoot)` which returns a structured `BundleStateIssue[]` describing git-level problems the agent can remediate. Enum cases: `not_a_git_repo`, `no_remote_configured`, `first_commit_never_happened`, `pending_sync_exists`. Detection never throws — every git call is wrapped in try/catch so a broken bundle degrades to a clear signal rather than crashing the turn pipeline. Also exports `renderBundleStateHint(issues)` which produces first-person remediation guidance (per the memory rule) that tells the agent to call `bundle_check_sync_status` and the `bundle_*` tools shipping in a follow-up PR.",
8
+ "feat(bundle): `StartOfTurnPacket` gains an optional `bundleState?: BundleStateIssue[]` field, populated by the senses pipeline in `handleInboundTurn` at packet assembly time. Renders via a new `case \"bundleState\":` branch in `formatSections` with the `**Bundle:**` prefix, at the same priority tier as the legacy `syncFailure` free-form string (priority 7, truncated last). The two signals coexist during the transition — `syncFailure` is still emitted by sync.ts for humans, while `bundleState` is the structured form the agent pattern-matches on. Deferred to follow-up: `pending-sync.json` schema extension with `classification` / `conflictFiles` so sync.ts can distinguish push-rejected from pull-rebase-conflict."
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.280",
6
13
  "changes": [
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.detectBundleState = detectBundleState;
37
+ exports.renderBundleStateHint = renderBundleStateHint;
38
+ /**
39
+ * Bundle state detection: inspects an agent bundle's git + pending-sync
40
+ * state and returns a structured list of issues the agent needs to remediate
41
+ * via its `bundle_*` tools.
42
+ *
43
+ * Previously the sync pipeline surfaced a single free-form `syncFailure`
44
+ * string that was good for humans but hard for the agent to act on. The
45
+ * new `BundleStateIssue` enum lets the agent pattern-match on discrete
46
+ * cases and pick the correct remediation tool (shipping in PR 6).
47
+ *
48
+ * Detection never throws: every git call is wrapped in try/catch so a
49
+ * broken bundle or a missing git binary degrades to an empty array or a
50
+ * `not_a_git_repo` signal rather than exploding the turn pipeline.
51
+ *
52
+ * The classification returned here is the subset that's detectable purely
53
+ * from bundle state:
54
+ *
55
+ * - `not_a_git_repo`: `.git` directory missing.
56
+ * - `no_remote_configured`: `git remote` returns empty.
57
+ * - `first_commit_never_happened`: `.git` exists but `git rev-parse HEAD`
58
+ * fails (fresh `git init` with nothing committed).
59
+ * - `pending_sync_exists`: `state/pending-sync.json` exists — the agent
60
+ * should inspect it and clear the pending state.
61
+ *
62
+ * Future extensions (deferred to a follow-up PR):
63
+ * - `remote_push_failed` / `pull_rebase_conflict`: derived from the
64
+ * `classification` field of pending-sync.json once sync.ts writes it.
65
+ */
66
+ const child_process_1 = require("child_process");
67
+ const fs = __importStar(require("fs"));
68
+ const path = __importStar(require("path"));
69
+ const runtime_1 = require("../nerves/runtime");
70
+ function detectBundleState(agentRoot, deps = {}) {
71
+ const exec = deps.execFileSync ?? child_process_1.execFileSync;
72
+ const exists = deps.existsSync ?? fs.existsSync;
73
+ const issues = [];
74
+ (0, runtime_1.emitNervesEvent)({
75
+ component: "heart",
76
+ event: "heart.bundle_state_detect_start",
77
+ message: "detecting bundle state",
78
+ meta: { agentRoot },
79
+ });
80
+ const gitDir = path.join(agentRoot, ".git");
81
+ const isGitRepo = exists(gitDir);
82
+ if (!isGitRepo) {
83
+ issues.push("not_a_git_repo");
84
+ }
85
+ else {
86
+ // Check remote presence
87
+ try {
88
+ const remoteOutput = exec("git", ["remote"], {
89
+ cwd: agentRoot,
90
+ stdio: "pipe",
91
+ timeout: 5000,
92
+ }).toString().trim();
93
+ if (remoteOutput.length === 0) {
94
+ issues.push("no_remote_configured");
95
+ }
96
+ }
97
+ catch {
98
+ // Git missing or broken — treat as no_remote_configured so the
99
+ // agent surfaces a "fix git" signal rather than a silent pass.
100
+ issues.push("no_remote_configured");
101
+ }
102
+ // Check for initial commit
103
+ try {
104
+ exec("git", ["rev-parse", "HEAD"], {
105
+ cwd: agentRoot,
106
+ stdio: "pipe",
107
+ timeout: 5000,
108
+ });
109
+ }
110
+ catch {
111
+ issues.push("first_commit_never_happened");
112
+ }
113
+ }
114
+ // Pending-sync file — independent of git state (could exist on a bundle
115
+ // that was a repo and then had its .git deleted).
116
+ const pendingSyncPath = path.join(agentRoot, "state", "pending-sync.json");
117
+ if (exists(pendingSyncPath)) {
118
+ issues.push("pending_sync_exists");
119
+ }
120
+ (0, runtime_1.emitNervesEvent)({
121
+ component: "heart",
122
+ event: "heart.bundle_state_detect_end",
123
+ message: `bundle state detected: ${issues.length} issue(s)`,
124
+ meta: { agentRoot, issues },
125
+ });
126
+ return issues;
127
+ }
128
+ /**
129
+ * First-person remediation hint text for the start-of-turn packet. Reads
130
+ * as the agent's own voice per the "declarative first-person" memory rule.
131
+ * Returns empty string when there are no issues (so the packet renderer
132
+ * can skip the section entirely).
133
+ */
134
+ function renderBundleStateHint(issues) {
135
+ if (issues.length === 0)
136
+ return "";
137
+ const labels = {
138
+ not_a_git_repo: "not a git repo",
139
+ no_remote_configured: "no remote configured",
140
+ first_commit_never_happened: "first commit never happened",
141
+ pending_sync_exists: "pending sync from a prior turn",
142
+ };
143
+ const list = issues.map((issue) => labels[issue]).join(", ");
144
+ return (`my bundle has unresolved git state: ${list}. ` +
145
+ `i have the bundle_* tools available to fix this — i should run ` +
146
+ `bundle_check_sync_status first, then use the appropriate remediation tool.`);
147
+ }
@@ -40,6 +40,7 @@ exports.renderCompactStartOfTurnPacket = renderCompactStartOfTurnPacket;
40
40
  const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
42
  const runtime_1 = require("../nerves/runtime");
43
+ const bundle_state_1 = require("./bundle-state");
43
44
  const tempo_1 = require("./tempo");
44
45
  function estimateTokens(text) {
45
46
  return Math.ceil(text.length / 4);
@@ -222,6 +223,11 @@ function renderStartOfTurnPacket(packet) {
222
223
  // Assemble sections in priority order (highest priority first for budget allocation)
223
224
  // Each section is { label, content, priority } where lower priority number = truncated first
224
225
  const sections = [
226
+ // bundleState and syncFailure share the top remediation tier — both
227
+ // are actionable "fix your git" signals. bundleState is preferred
228
+ // because it's structured (array of enum values) while syncFailure is
229
+ // a legacy free-form string; both render when populated.
230
+ { label: "bundleState", content: (0, bundle_state_1.renderBundleStateHint)(packet.bundleState ?? []), priority: 7 },
225
231
  { label: "syncFailure", content: packet.syncFailure ?? "", priority: 7 },
226
232
  { label: "resume", content: packet.resumeHint, priority: 6 },
227
233
  { label: "obligations", content: packet.obligations, priority: 5 },
@@ -294,6 +300,9 @@ function formatSections(sections) {
294
300
  case "syncFailure":
295
301
  parts.push(`**Sync warning:** ${section.content}`);
296
302
  break;
303
+ case "bundleState":
304
+ parts.push(`**Bundle:** ${section.content}`);
305
+ break;
297
306
  }
298
307
  }
299
308
  return parts.join("\n\n");
@@ -57,6 +57,7 @@ const auth_flow_1 = require("../heart/auth/auth-flow");
57
57
  const tempo_1 = require("../heart/tempo");
58
58
  const temporal_view_1 = require("../heart/temporal-view");
59
59
  const start_of_turn_packet_1 = require("../heart/start-of-turn-packet");
60
+ const bundle_state_1 = require("../heart/bundle-state");
60
61
  const sync_1 = require("../heart/sync");
61
62
  const config_1 = require("../heart/config");
62
63
  const presence_1 = require("../arc/presence");
@@ -388,6 +389,13 @@ async function handleInboundTurn(input) {
388
389
  if (syncFailure) {
389
390
  startOfTurnPacket.syncFailure = syncFailure;
390
391
  }
392
+ // Structured bundle state detection — surfaces discrete issues the
393
+ // agent can remediate via the bundle_* tools. Runs independently of
394
+ // syncFailure so the two signals coexist during the transition away
395
+ // from the legacy free-form syncFailure string. Always assigned; the
396
+ // packet renderer's empty-filter handles the empty-array case without
397
+ // a separate branch here.
398
+ startOfTurnPacket.bundleState = (0, bundle_state_1.detectBundleState)(agentRoot);
391
399
  const capabilities = (0, start_of_turn_packet_1.buildCapabilitiesSection)(agentRoot);
392
400
  if (capabilities) {
393
401
  startOfTurnPacket.capabilities = capabilities;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.280",
3
+ "version": "0.1.0-alpha.281",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",