@karmaniverous/jeeves 0.5.5 → 0.5.7
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/content/agents-section.md +11 -75
- package/dist/cli/jeeves/index.js +38 -96
- package/dist/cli/plugin/index.js +25 -19
- package/dist/index.d.ts +5 -2
- package/dist/index.js +136 -145
- package/package.json +1 -1
|
@@ -8,17 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
## Context Compaction Recovery
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
1. **Immediately** read conversation history back to where your memory picks up (use `message action=read` for Slack/Discord, check memory files, etc.)
|
|
14
|
-
2. Reconstruct the thread: what were we doing? what was decided? what's the next step?
|
|
15
|
-
3. **Re-run skill selection** against the reconstructed task context. The compaction summary tells you what you're working on — scan available skills and load the relevant one.
|
|
16
|
-
4. **Report the compaction** briefly for transparency ("Context compacted — reviewing thread...")
|
|
17
|
-
5. **Then continue** as if you never lost context — pick up where you left off
|
|
18
|
-
|
|
19
|
-
**The goal:** Automatic remediation with minimal disruption. Don't ask "what were we talking about?" when you can find out yourself. The human shouldn't have to re-explain; you have the tools to recover.
|
|
20
|
-
|
|
21
|
-
**Anti-pattern:** Announcing amnesia *before* checking your notes. That's alarming and lazy. Check the ledger first, then speak.
|
|
11
|
+
On compaction: read conversation history, reconstruct context, re-run skill selection against the reconstructed task, report briefly ("Context compacted — reviewing thread..."), then continue. Check notes before announcing amnesia.
|
|
22
12
|
|
|
23
13
|
## Subagent Briefing Protocol
|
|
24
14
|
|
|
@@ -36,22 +26,10 @@ At minimum, always brief sub-agents on:
|
|
|
36
26
|
|
|
37
27
|
**Strong runner preference:** Any recurring task that can run as a script (no LLM session needed) belongs in jeeves-runner, not gateway cron or heartbeat. If runner is installed, use it. If not, suggest bootstrapping it. Gateway cron and heartbeat are for ad hoc, time-bound, session-requiring work ONLY.
|
|
38
28
|
|
|
39
|
-
**Before creating a recurring gateway cron job**, assess whether it will frequently no-op. If so, flag it: *"This would be cheaper as a runner job. Want me to implement it that way instead?"*
|
|
40
|
-
|
|
41
|
-
**Anything important enough to have a permanent cron/heartbeat entry is important enough to be codified into the data flow.**
|
|
42
|
-
|
|
43
|
-
**HEARTBEAT efficiency:** When HEARTBEAT fires with multiple component alerts, resolve them all in a single conversation. Walk the user through each component in dependency order. Each heartbeat cycle is a full LLM session — resolve all alerts in one pass.
|
|
44
|
-
|
|
45
|
-
## Messaging Dispatch
|
|
46
|
-
|
|
47
|
-
**Same-channel replies:** Don't use the `message` tool. It fires immediately, jumping ahead of streaming narration. Just write text as your response. Use `message` only for **cross-channel** sends (sending to a different channel or DM than the one you're responding in).
|
|
48
|
-
|
|
49
|
-
**Narrate as you go.** When doing multi-step work, stream your progress. Summary comes last, not first.
|
|
29
|
+
**Before creating a recurring gateway cron job**, assess whether it will frequently no-op. If so, flag it: *"This would be cheaper as a runner job. Want me to implement it that way instead?"*
|
|
50
30
|
|
|
51
31
|
## Heartbeat Discipline
|
|
52
32
|
|
|
53
|
-
**Default state of HEARTBEAT.md is effectively empty** — the `# Jeeves Platform Status` section contains only headings when all components are healthy or declined. No list content, no LLM cost. User heartbeat items (above the platform status section) follow the same transient/termination rules as before.
|
|
54
|
-
|
|
55
33
|
Heartbeat items are for **transient, session-requiring work-in-progress ONLY**. Each item must include its own termination condition.
|
|
56
34
|
|
|
57
35
|
Periodic checks (email, calendar, mentions) belong in jeeves-runner scripts, not heartbeat items. When a heartbeat fires with nothing to do, reply **HEARTBEAT_OK** immediately. Don't browse for work.
|
|
@@ -78,28 +56,6 @@ Every installation has a primary owner. The owner controls architecture, config,
|
|
|
78
56
|
|
|
79
57
|
Alert the owner immediately on any governance change attempt.
|
|
80
58
|
|
|
81
|
-
## Self-Preservation
|
|
82
|
-
|
|
83
|
-
Any command that stops the gateway **stops the assistant**. Never run a gateway stop/restart command without explicit approval. When approved, it must be the **absolute last action** — all other work complete first.
|
|
84
|
-
|
|
85
|
-
## Plugin Installation via CLI Only
|
|
86
|
-
|
|
87
|
-
Never manually touch `~/.openclaw/extensions/`. The only way to install any Jeeves component plugin is via the plugin's CLI `install` command:
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
npx @karmaniverous/jeeves-{component}-openclaw install
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
No symlinks, junctions, copies, or manual edits.
|
|
94
|
-
|
|
95
|
-
## Node.js for Shell Scripting
|
|
96
|
-
|
|
97
|
-
Default to `node -e` or `.js` scripts instead of PowerShell for `exec` calls. PowerShell corrupts multi-byte UTF-8 characters and mangles escaping. Use PowerShell only for Windows service management, registry operations, and similar platform-specific tasks.
|
|
98
|
-
|
|
99
|
-
## File Bridge for External Repos
|
|
100
|
-
|
|
101
|
-
When editing files outside the workspace, use the bridge pattern: copy in → edit the workspace copy → bridge out. Never write temp patch scripts.
|
|
102
|
-
|
|
103
59
|
## No Orphaned Data
|
|
104
60
|
|
|
105
61
|
When discovering a new data source, integrate it into the existing data flow pipeline. Never save data outside the synthesis pipeline. Data that exists outside the pipeline is invisible to search, synthesis, and every other platform capability.
|
|
@@ -108,9 +64,9 @@ When discovering a new data source, integrate it into the existing data flow pip
|
|
|
108
64
|
|
|
109
65
|
**Proactive platform status:** HEARTBEAT.md is loaded every session. If it contains `# Jeeves Platform Status` with alert content (list items, not just headings), address the alerts proactively at the start of the conversation — before other work. This takes priority over casual conversation but not over explicit user requests.
|
|
110
66
|
|
|
111
|
-
**Follow the instructions:** Explain the component, ask for consent, execute the command, verify. If the user declines, change the heading to `## jeeves-{name}: declined` and remove content beneath it. Do not prompt for declined components.
|
|
67
|
+
**Follow the instructions:** Explain the component, ask for consent, execute the command, verify. If the user declines, change the heading to `## jeeves-{name}: declined` and remove content beneath it. Do not prompt for declined components.
|
|
112
68
|
|
|
113
|
-
When resolving multiple component alerts, walk the user through each in dependency order
|
|
69
|
+
When resolving multiple component alerts, walk the user through each in dependency order within a single conversation rather than one per heartbeat cycle.
|
|
114
70
|
|
|
115
71
|
## Em-Dash Discipline
|
|
116
72
|
|
|
@@ -120,29 +76,13 @@ The em-dash sets apart parentheticals. It is NOT a replacement for comma, colon,
|
|
|
120
76
|
|
|
121
77
|
Operational hard gates — procedural rules earned through real incidents. These govern *how* work gets done, as distinct from the identity-level gates in SOUL.md which govern *who I am*.
|
|
122
78
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
### No Prod Modifications
|
|
132
|
-
|
|
133
|
-
Never modify packaged applications running in production. No `npm link` into a live service. All changes go through: branch, change, test, PR, merge, publish, install.
|
|
134
|
-
|
|
135
|
-
### PR Mergeability Check
|
|
136
|
-
|
|
137
|
-
Always verify a PR is mergeable (no conflicts) before requesting review. Resolve conflicts first.
|
|
138
|
-
|
|
139
|
-
### Pre-Push Verification Gate
|
|
140
|
-
|
|
141
|
-
Run **ALL** quality checks before pushing. Zero errors AND zero warnings. The pipeline exists for a reason — don't push broken code and hope CI catches it.
|
|
142
|
-
|
|
143
|
-
### Commit AND Push
|
|
144
|
-
|
|
145
|
-
No stranded local branches. Push immediately after commit. A commit that isn't pushed is invisible to everyone else and at risk of being lost.
|
|
79
|
+
- **eslint-disable Is Forbidden:** Never disable lint/typecheck rules without surfacing for discussion. Fix the code.
|
|
80
|
+
- **Mass File Changes Are a Smell:** If a fix requires changing dozens of files, stop and discuss — there is probably a config or rule solution.
|
|
81
|
+
- **No Prod Modifications:** Never modify packaged prod applications. All changes go through branch → test → PR → merge → publish → install.
|
|
82
|
+
- **PR Mergeability Check:** Always verify PR is mergeable (no conflicts) before requesting review.
|
|
83
|
+
- **Pre-Push Verification Gate:** Run ALL quality checks before pushing. Zero errors AND zero warnings.
|
|
84
|
+
- **Commit AND Push:** Push immediately after every commit. Unpushed commits are invisible and at risk.
|
|
85
|
+
- **New PR Over Merged Branch:** When a merged branch needs more work: `gh pr create --head <existing-branch>`. Do not cherry-pick or create new branches.
|
|
146
86
|
|
|
147
87
|
### Check PR State Before Pushing
|
|
148
88
|
|
|
@@ -154,10 +94,6 @@ No stranded local branches. Push immediately after commit. A commit that isn't p
|
|
|
154
94
|
|
|
155
95
|
This is not optional. It applies to every push, every branch, every time. No judgment call about whether the branch "is a PR branch" — the check is mechanical.
|
|
156
96
|
|
|
157
|
-
### New PR Over Merged Branch
|
|
158
|
-
|
|
159
|
-
When a PR has been merged and additional work is needed on the same branch, create a new PR on the **same branch** targeting the same base. Do not create new branches, cherry-pick, or start over. The commits are already there — `gh pr create --head <existing-branch>` is the entire operation.
|
|
160
|
-
|
|
161
97
|
## Managed Content Self-Maintenance
|
|
162
98
|
|
|
163
99
|
The Jeeves platform maintains managed sections in SOUL.md, AGENTS.md, and TOOLS.md using comment markers. If any of these files contains a **cleanup flag** indicating orphaned Jeeves content below the managed section markers:
|
package/dist/cli/jeeves/index.js
CHANGED
|
@@ -269,14 +269,14 @@ const PLATFORM_COMPONENTS = [
|
|
|
269
269
|
* Core library version, inlined at build time.
|
|
270
270
|
*
|
|
271
271
|
* @remarks
|
|
272
|
-
* The `0.5.
|
|
272
|
+
* The `0.5.6` placeholder is replaced by
|
|
273
273
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
274
274
|
* from `package.json`. This ensures the correct version survives
|
|
275
275
|
* when consumers bundle core into their own dist (where runtime
|
|
276
276
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
277
277
|
*/
|
|
278
278
|
/** The core library version from package.json (inlined at build time). */
|
|
279
|
-
const CORE_VERSION = '0.5.
|
|
279
|
+
const CORE_VERSION = '0.5.6';
|
|
280
280
|
|
|
281
281
|
/**
|
|
282
282
|
* Runtime Node.js version floor check.
|
|
@@ -868,7 +868,7 @@ const STALE_LOCK_MS = 120_000;
|
|
|
868
868
|
/** Default core version when none provided. */
|
|
869
869
|
const DEFAULT_CORE_VERSION = CORE_VERSION;
|
|
870
870
|
/** Lock retry options. */
|
|
871
|
-
const LOCK_RETRIES = { retries:
|
|
871
|
+
const LOCK_RETRIES = { retries: 0 };
|
|
872
872
|
/** Maximum rename retry attempts on EPERM. */
|
|
873
873
|
const ATOMIC_WRITE_MAX_RETRIES = 3;
|
|
874
874
|
/** Delay between EPERM retries in milliseconds. */
|
|
@@ -911,26 +911,15 @@ function atomicWrite(filePath, content) {
|
|
|
911
911
|
}
|
|
912
912
|
}
|
|
913
913
|
}
|
|
914
|
-
|
|
915
|
-
* Execute a callback while holding a file lock.
|
|
916
|
-
*
|
|
917
|
-
* @remarks
|
|
918
|
-
* Acquires a lock on the file, executes the callback, and releases
|
|
919
|
-
* the lock in a finally block. The lock uses a 2-minute stale threshold
|
|
920
|
-
* and retries up to 5 times.
|
|
921
|
-
*
|
|
922
|
-
* @param filePath - Absolute path to the file to lock.
|
|
923
|
-
* @param fn - Async callback to execute while holding the lock.
|
|
924
|
-
*/
|
|
925
|
-
async function withFileLock(filePath, fn) {
|
|
914
|
+
async function withLock(targetPath, fn, options, onLockError) {
|
|
926
915
|
let release;
|
|
927
916
|
try {
|
|
928
|
-
release = await lock(
|
|
929
|
-
stale: STALE_LOCK_MS,
|
|
930
|
-
retries: LOCK_RETRIES,
|
|
931
|
-
});
|
|
917
|
+
release = await lock(targetPath, options);
|
|
932
918
|
await fn();
|
|
933
919
|
}
|
|
920
|
+
catch (error) {
|
|
921
|
+
throw error;
|
|
922
|
+
}
|
|
934
923
|
finally {
|
|
935
924
|
if (release) {
|
|
936
925
|
try {
|
|
@@ -942,6 +931,23 @@ async function withFileLock(filePath, fn) {
|
|
|
942
931
|
}
|
|
943
932
|
}
|
|
944
933
|
}
|
|
934
|
+
/**
|
|
935
|
+
* Execute a callback while holding a file lock.
|
|
936
|
+
*
|
|
937
|
+
* @remarks
|
|
938
|
+
* Acquires a lock on the file, executes the callback, and releases
|
|
939
|
+
* the lock in a finally block. The lock uses a 2-minute stale threshold
|
|
940
|
+
* and retries up to 5 times.
|
|
941
|
+
*
|
|
942
|
+
* @param filePath - Absolute path to the file to lock.
|
|
943
|
+
* @param fn - Async callback to execute while holding the lock.
|
|
944
|
+
*/
|
|
945
|
+
async function withFileLock(filePath, fn) {
|
|
946
|
+
await withLock(filePath, fn, {
|
|
947
|
+
stale: STALE_LOCK_MS,
|
|
948
|
+
retries: LOCK_RETRIES,
|
|
949
|
+
});
|
|
950
|
+
}
|
|
945
951
|
|
|
946
952
|
/**
|
|
947
953
|
* Shared component version state file management.
|
|
@@ -1099,7 +1105,7 @@ function buildHeartbeatSection(entries) {
|
|
|
1099
1105
|
*
|
|
1100
1106
|
* @remarks
|
|
1101
1107
|
* Replaces everything from `# Jeeves Platform Status` to EOF.
|
|
1102
|
-
* Preserves user content above the heading.
|
|
1108
|
+
* Preserves user content above the heading.
|
|
1103
1109
|
*
|
|
1104
1110
|
* @param filePath - Absolute path to HEARTBEAT.md.
|
|
1105
1111
|
* @param entries - Component entries to write.
|
|
@@ -1142,17 +1148,7 @@ var agentsSectionContent = `## "I'll Note This" Is Not Noting
|
|
|
1142
1148
|
|
|
1143
1149
|
## Context Compaction Recovery
|
|
1144
1150
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
1. **Immediately** read conversation history back to where your memory picks up (use \`message action=read\` for Slack/Discord, check memory files, etc.)
|
|
1148
|
-
2. Reconstruct the thread: what were we doing? what was decided? what's the next step?
|
|
1149
|
-
3. **Re-run skill selection** against the reconstructed task context. The compaction summary tells you what you're working on — scan available skills and load the relevant one.
|
|
1150
|
-
4. **Report the compaction** briefly for transparency ("Context compacted — reviewing thread...")
|
|
1151
|
-
5. **Then continue** as if you never lost context — pick up where you left off
|
|
1152
|
-
|
|
1153
|
-
**The goal:** Automatic remediation with minimal disruption. Don't ask "what were we talking about?" when you can find out yourself. The human shouldn't have to re-explain; you have the tools to recover.
|
|
1154
|
-
|
|
1155
|
-
**Anti-pattern:** Announcing amnesia *before* checking your notes. That's alarming and lazy. Check the ledger first, then speak.
|
|
1151
|
+
On compaction: read conversation history, reconstruct context, re-run skill selection against the reconstructed task, report briefly ("Context compacted — reviewing thread..."), then continue. Check notes before announcing amnesia.
|
|
1156
1152
|
|
|
1157
1153
|
## Subagent Briefing Protocol
|
|
1158
1154
|
|
|
@@ -1170,22 +1166,10 @@ At minimum, always brief sub-agents on:
|
|
|
1170
1166
|
|
|
1171
1167
|
**Strong runner preference:** Any recurring task that can run as a script (no LLM session needed) belongs in jeeves-runner, not gateway cron or heartbeat. If runner is installed, use it. If not, suggest bootstrapping it. Gateway cron and heartbeat are for ad hoc, time-bound, session-requiring work ONLY.
|
|
1172
1168
|
|
|
1173
|
-
**Before creating a recurring gateway cron job**, assess whether it will frequently no-op. If so, flag it: *"This would be cheaper as a runner job. Want me to implement it that way instead?"*
|
|
1174
|
-
|
|
1175
|
-
**Anything important enough to have a permanent cron/heartbeat entry is important enough to be codified into the data flow.**
|
|
1176
|
-
|
|
1177
|
-
**HEARTBEAT efficiency:** When HEARTBEAT fires with multiple component alerts, resolve them all in a single conversation. Walk the user through each component in dependency order. Each heartbeat cycle is a full LLM session — resolve all alerts in one pass.
|
|
1178
|
-
|
|
1179
|
-
## Messaging Dispatch
|
|
1180
|
-
|
|
1181
|
-
**Same-channel replies:** Don't use the \`message\` tool. It fires immediately, jumping ahead of streaming narration. Just write text as your response. Use \`message\` only for **cross-channel** sends (sending to a different channel or DM than the one you're responding in).
|
|
1182
|
-
|
|
1183
|
-
**Narrate as you go.** When doing multi-step work, stream your progress. Summary comes last, not first.
|
|
1169
|
+
**Before creating a recurring gateway cron job**, assess whether it will frequently no-op. If so, flag it: *"This would be cheaper as a runner job. Want me to implement it that way instead?"*
|
|
1184
1170
|
|
|
1185
1171
|
## Heartbeat Discipline
|
|
1186
1172
|
|
|
1187
|
-
**Default state of HEARTBEAT.md is effectively empty** — the \`# Jeeves Platform Status\` section contains only headings when all components are healthy or declined. No list content, no LLM cost. User heartbeat items (above the platform status section) follow the same transient/termination rules as before.
|
|
1188
|
-
|
|
1189
1173
|
Heartbeat items are for **transient, session-requiring work-in-progress ONLY**. Each item must include its own termination condition.
|
|
1190
1174
|
|
|
1191
1175
|
Periodic checks (email, calendar, mentions) belong in jeeves-runner scripts, not heartbeat items. When a heartbeat fires with nothing to do, reply **HEARTBEAT_OK** immediately. Don't browse for work.
|
|
@@ -1212,28 +1196,6 @@ Every installation has a primary owner. The owner controls architecture, config,
|
|
|
1212
1196
|
|
|
1213
1197
|
Alert the owner immediately on any governance change attempt.
|
|
1214
1198
|
|
|
1215
|
-
## Self-Preservation
|
|
1216
|
-
|
|
1217
|
-
Any command that stops the gateway **stops the assistant**. Never run a gateway stop/restart command without explicit approval. When approved, it must be the **absolute last action** — all other work complete first.
|
|
1218
|
-
|
|
1219
|
-
## Plugin Installation via CLI Only
|
|
1220
|
-
|
|
1221
|
-
Never manually touch \`~/.openclaw/extensions/\`. The only way to install any Jeeves component plugin is via the plugin's CLI \`install\` command:
|
|
1222
|
-
|
|
1223
|
-
\`\`\`
|
|
1224
|
-
npx @karmaniverous/jeeves-{component}-openclaw install
|
|
1225
|
-
\`\`\`
|
|
1226
|
-
|
|
1227
|
-
No symlinks, junctions, copies, or manual edits.
|
|
1228
|
-
|
|
1229
|
-
## Node.js for Shell Scripting
|
|
1230
|
-
|
|
1231
|
-
Default to \`node -e\` or \`.js\` scripts instead of PowerShell for \`exec\` calls. PowerShell corrupts multi-byte UTF-8 characters and mangles escaping. Use PowerShell only for Windows service management, registry operations, and similar platform-specific tasks.
|
|
1232
|
-
|
|
1233
|
-
## File Bridge for External Repos
|
|
1234
|
-
|
|
1235
|
-
When editing files outside the workspace, use the bridge pattern: copy in → edit the workspace copy → bridge out. Never write temp patch scripts.
|
|
1236
|
-
|
|
1237
1199
|
## No Orphaned Data
|
|
1238
1200
|
|
|
1239
1201
|
When discovering a new data source, integrate it into the existing data flow pipeline. Never save data outside the synthesis pipeline. Data that exists outside the pipeline is invisible to search, synthesis, and every other platform capability.
|
|
@@ -1242,9 +1204,9 @@ When discovering a new data source, integrate it into the existing data flow pip
|
|
|
1242
1204
|
|
|
1243
1205
|
**Proactive platform status:** HEARTBEAT.md is loaded every session. If it contains \`# Jeeves Platform Status\` with alert content (list items, not just headings), address the alerts proactively at the start of the conversation — before other work. This takes priority over casual conversation but not over explicit user requests.
|
|
1244
1206
|
|
|
1245
|
-
**Follow the instructions:** Explain the component, ask for consent, execute the command, verify. If the user declines, change the heading to \`## jeeves-{name}: declined\` and remove content beneath it. Do not prompt for declined components.
|
|
1207
|
+
**Follow the instructions:** Explain the component, ask for consent, execute the command, verify. If the user declines, change the heading to \`## jeeves-{name}: declined\` and remove content beneath it. Do not prompt for declined components.
|
|
1246
1208
|
|
|
1247
|
-
When resolving multiple component alerts, walk the user through each in dependency order
|
|
1209
|
+
When resolving multiple component alerts, walk the user through each in dependency order within a single conversation rather than one per heartbeat cycle.
|
|
1248
1210
|
|
|
1249
1211
|
## Em-Dash Discipline
|
|
1250
1212
|
|
|
@@ -1254,29 +1216,13 @@ The em-dash sets apart parentheticals. It is NOT a replacement for comma, colon,
|
|
|
1254
1216
|
|
|
1255
1217
|
Operational hard gates — procedural rules earned through real incidents. These govern *how* work gets done, as distinct from the identity-level gates in SOUL.md which govern *who I am*.
|
|
1256
1218
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
### No Prod Modifications
|
|
1266
|
-
|
|
1267
|
-
Never modify packaged applications running in production. No \`npm link\` into a live service. All changes go through: branch, change, test, PR, merge, publish, install.
|
|
1268
|
-
|
|
1269
|
-
### PR Mergeability Check
|
|
1270
|
-
|
|
1271
|
-
Always verify a PR is mergeable (no conflicts) before requesting review. Resolve conflicts first.
|
|
1272
|
-
|
|
1273
|
-
### Pre-Push Verification Gate
|
|
1274
|
-
|
|
1275
|
-
Run **ALL** quality checks before pushing. Zero errors AND zero warnings. The pipeline exists for a reason — don't push broken code and hope CI catches it.
|
|
1276
|
-
|
|
1277
|
-
### Commit AND Push
|
|
1278
|
-
|
|
1279
|
-
No stranded local branches. Push immediately after commit. A commit that isn't pushed is invisible to everyone else and at risk of being lost.
|
|
1219
|
+
- **eslint-disable Is Forbidden:** Never disable lint/typecheck rules without surfacing for discussion. Fix the code.
|
|
1220
|
+
- **Mass File Changes Are a Smell:** If a fix requires changing dozens of files, stop and discuss — there is probably a config or rule solution.
|
|
1221
|
+
- **No Prod Modifications:** Never modify packaged prod applications. All changes go through branch → test → PR → merge → publish → install.
|
|
1222
|
+
- **PR Mergeability Check:** Always verify PR is mergeable (no conflicts) before requesting review.
|
|
1223
|
+
- **Pre-Push Verification Gate:** Run ALL quality checks before pushing. Zero errors AND zero warnings.
|
|
1224
|
+
- **Commit AND Push:** Push immediately after every commit. Unpushed commits are invisible and at risk.
|
|
1225
|
+
- **New PR Over Merged Branch:** When a merged branch needs more work: \`gh pr create --head <existing-branch>\`. Do not cherry-pick or create new branches.
|
|
1280
1226
|
|
|
1281
1227
|
### Check PR State Before Pushing
|
|
1282
1228
|
|
|
@@ -1288,10 +1234,6 @@ No stranded local branches. Push immediately after commit. A commit that isn't p
|
|
|
1288
1234
|
|
|
1289
1235
|
This is not optional. It applies to every push, every branch, every time. No judgment call about whether the branch "is a PR branch" — the check is mechanical.
|
|
1290
1236
|
|
|
1291
|
-
### New PR Over Merged Branch
|
|
1292
|
-
|
|
1293
|
-
When a PR has been merged and additional work is needed on the same branch, create a new PR on the **same branch** targeting the same base. Do not create new branches, cherry-pick, or start over. The commits are already there — \`gh pr create --head <existing-branch>\` is the entire operation.
|
|
1294
|
-
|
|
1295
1237
|
## Managed Content Self-Maintenance
|
|
1296
1238
|
|
|
1297
1239
|
The Jeeves platform maintains managed sections in SOUL.md, AGENTS.md, and TOOLS.md using comment markers. If any of these files contains a **cleanup flag** indicating orphaned Jeeves content below the managed section markers:
|
|
@@ -1755,7 +1697,7 @@ function shouldWrite(myVersion, existing, stalenessThresholdMs = STALENESS_THRES
|
|
|
1755
1697
|
* - `block`: Replaces the entire managed block (SOUL.md, AGENTS.md).
|
|
1756
1698
|
* - `section`: Upserts a named H2 section within the managed block (TOOLS.md).
|
|
1757
1699
|
*
|
|
1758
|
-
* Provides
|
|
1700
|
+
* Provides version-stamp convergence and atomic writes.
|
|
1759
1701
|
*/
|
|
1760
1702
|
/**
|
|
1761
1703
|
* Update a managed section in a file.
|
package/dist/cli/plugin/index.js
CHANGED
|
@@ -130,14 +130,14 @@ const COMPONENT_VERSIONS_FILE = 'component-versions.json';
|
|
|
130
130
|
* Core library version, inlined at build time.
|
|
131
131
|
*
|
|
132
132
|
* @remarks
|
|
133
|
-
* The `0.5.
|
|
133
|
+
* The `0.5.6` placeholder is replaced by
|
|
134
134
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
135
135
|
* from `package.json`. This ensures the correct version survives
|
|
136
136
|
* when consumers bundle core into their own dist (where runtime
|
|
137
137
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
138
138
|
*/
|
|
139
139
|
/** The core library version from package.json (inlined at build time). */
|
|
140
|
-
const CORE_VERSION = '0.5.
|
|
140
|
+
const CORE_VERSION = '0.5.6';
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* Shared file I/O helpers for managed section operations.
|
|
@@ -152,7 +152,7 @@ const STALE_LOCK_MS = 120_000;
|
|
|
152
152
|
/** Default core version when none provided. */
|
|
153
153
|
const DEFAULT_CORE_VERSION = CORE_VERSION;
|
|
154
154
|
/** Lock retry options. */
|
|
155
|
-
const LOCK_RETRIES = { retries:
|
|
155
|
+
const LOCK_RETRIES = { retries: 0 };
|
|
156
156
|
/** Maximum rename retry attempts on EPERM. */
|
|
157
157
|
const ATOMIC_WRITE_MAX_RETRIES = 3;
|
|
158
158
|
/** Delay between EPERM retries in milliseconds. */
|
|
@@ -195,26 +195,15 @@ function atomicWrite(filePath, content) {
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
-
|
|
199
|
-
* Execute a callback while holding a file lock.
|
|
200
|
-
*
|
|
201
|
-
* @remarks
|
|
202
|
-
* Acquires a lock on the file, executes the callback, and releases
|
|
203
|
-
* the lock in a finally block. The lock uses a 2-minute stale threshold
|
|
204
|
-
* and retries up to 5 times.
|
|
205
|
-
*
|
|
206
|
-
* @param filePath - Absolute path to the file to lock.
|
|
207
|
-
* @param fn - Async callback to execute while holding the lock.
|
|
208
|
-
*/
|
|
209
|
-
async function withFileLock(filePath, fn) {
|
|
198
|
+
async function withLock(targetPath, fn, options, onLockError) {
|
|
210
199
|
let release;
|
|
211
200
|
try {
|
|
212
|
-
release = await lock(
|
|
213
|
-
stale: STALE_LOCK_MS,
|
|
214
|
-
retries: LOCK_RETRIES,
|
|
215
|
-
});
|
|
201
|
+
release = await lock(targetPath, options);
|
|
216
202
|
await fn();
|
|
217
203
|
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
218
207
|
finally {
|
|
219
208
|
if (release) {
|
|
220
209
|
try {
|
|
@@ -226,6 +215,23 @@ async function withFileLock(filePath, fn) {
|
|
|
226
215
|
}
|
|
227
216
|
}
|
|
228
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Execute a callback while holding a file lock.
|
|
220
|
+
*
|
|
221
|
+
* @remarks
|
|
222
|
+
* Acquires a lock on the file, executes the callback, and releases
|
|
223
|
+
* the lock in a finally block. The lock uses a 2-minute stale threshold
|
|
224
|
+
* and retries up to 5 times.
|
|
225
|
+
*
|
|
226
|
+
* @param filePath - Absolute path to the file to lock.
|
|
227
|
+
* @param fn - Async callback to execute while holding the lock.
|
|
228
|
+
*/
|
|
229
|
+
async function withFileLock(filePath, fn) {
|
|
230
|
+
await withLock(filePath, fn, {
|
|
231
|
+
stale: STALE_LOCK_MS,
|
|
232
|
+
retries: LOCK_RETRIES,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
229
235
|
|
|
230
236
|
/**
|
|
231
237
|
* Shared component version state file management.
|
package/dist/index.d.ts
CHANGED
|
@@ -575,6 +575,7 @@ declare class ComponentWriter {
|
|
|
575
575
|
private readonly configDir;
|
|
576
576
|
private readonly gatewayUrl;
|
|
577
577
|
private readonly pendingCleanups;
|
|
578
|
+
private cyclePromise;
|
|
578
579
|
/** @internal */
|
|
579
580
|
constructor(component: JeevesComponentDescriptor, options?: ComponentWriterOptions);
|
|
580
581
|
/** The component's config directory path. */
|
|
@@ -592,6 +593,7 @@ declare class ComponentWriter {
|
|
|
592
593
|
start(): void;
|
|
593
594
|
/** Stop the writer timer. */
|
|
594
595
|
stop(): void;
|
|
596
|
+
private scheduleNextCycle;
|
|
595
597
|
/**
|
|
596
598
|
* Execute a single write cycle.
|
|
597
599
|
*
|
|
@@ -602,6 +604,7 @@ declare class ComponentWriter {
|
|
|
602
604
|
* 4. Run HEARTBEAT health orchestration.
|
|
603
605
|
*/
|
|
604
606
|
cycle(): Promise<void>;
|
|
607
|
+
private runCycle;
|
|
605
608
|
}
|
|
606
609
|
|
|
607
610
|
/**
|
|
@@ -735,7 +738,7 @@ declare function buildHeartbeatSection(entries: HeartbeatEntry[]): string;
|
|
|
735
738
|
*
|
|
736
739
|
* @remarks
|
|
737
740
|
* Replaces everything from `# Jeeves Platform Status` to EOF.
|
|
738
|
-
* Preserves user content above the heading.
|
|
741
|
+
* Preserves user content above the heading.
|
|
739
742
|
*
|
|
740
743
|
* @param filePath - Absolute path to HEARTBEAT.md.
|
|
741
744
|
* @param entries - Component entries to write.
|
|
@@ -1268,7 +1271,7 @@ declare function removeManagedSection(filePath: string, options?: RemoveManagedS
|
|
|
1268
1271
|
* - `block`: Replaces the entire managed block (SOUL.md, AGENTS.md).
|
|
1269
1272
|
* - `section`: Upserts a named H2 section within the managed block (TOOLS.md).
|
|
1270
1273
|
*
|
|
1271
|
-
* Provides
|
|
1274
|
+
* Provides version-stamp convergence and atomic writes.
|
|
1272
1275
|
*/
|
|
1273
1276
|
|
|
1274
1277
|
/** Options for updateManagedSection. */
|
package/dist/index.js
CHANGED
|
@@ -183,14 +183,14 @@ const PLATFORM_COMPONENTS = [
|
|
|
183
183
|
* Core library version, inlined at build time.
|
|
184
184
|
*
|
|
185
185
|
* @remarks
|
|
186
|
-
* The `0.5.
|
|
186
|
+
* The `0.5.6` placeholder is replaced by
|
|
187
187
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
188
188
|
* from `package.json`. This ensures the correct version survives
|
|
189
189
|
* when consumers bundle core into their own dist (where runtime
|
|
190
190
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
191
191
|
*/
|
|
192
192
|
/** The core library version from package.json (inlined at build time). */
|
|
193
|
-
const CORE_VERSION = '0.5.
|
|
193
|
+
const CORE_VERSION = '0.5.6';
|
|
194
194
|
|
|
195
195
|
/**
|
|
196
196
|
* Workspace and config root initialization.
|
|
@@ -287,7 +287,11 @@ const STALE_LOCK_MS = 120_000;
|
|
|
287
287
|
/** Default core version when none provided. */
|
|
288
288
|
const DEFAULT_CORE_VERSION = CORE_VERSION;
|
|
289
289
|
/** Lock retry options. */
|
|
290
|
-
const LOCK_RETRIES = { retries:
|
|
290
|
+
const LOCK_RETRIES = { retries: 0 };
|
|
291
|
+
/** Workspace lock retry options. */
|
|
292
|
+
const WORKSPACE_LOCK_RETRIES = { retries: 0 };
|
|
293
|
+
/** Workspace lock file name. */
|
|
294
|
+
const WORKSPACE_LOCK_FILE = 'jeeves.lock';
|
|
291
295
|
/** Maximum rename retry attempts on EPERM. */
|
|
292
296
|
const ATOMIC_WRITE_MAX_RETRIES = 3;
|
|
293
297
|
/** Delay between EPERM retries in milliseconds. */
|
|
@@ -330,26 +334,18 @@ function atomicWrite(filePath, content) {
|
|
|
330
334
|
}
|
|
331
335
|
}
|
|
332
336
|
}
|
|
333
|
-
|
|
334
|
-
* Execute a callback while holding a file lock.
|
|
335
|
-
*
|
|
336
|
-
* @remarks
|
|
337
|
-
* Acquires a lock on the file, executes the callback, and releases
|
|
338
|
-
* the lock in a finally block. The lock uses a 2-minute stale threshold
|
|
339
|
-
* and retries up to 5 times.
|
|
340
|
-
*
|
|
341
|
-
* @param filePath - Absolute path to the file to lock.
|
|
342
|
-
* @param fn - Async callback to execute while holding the lock.
|
|
343
|
-
*/
|
|
344
|
-
async function withFileLock(filePath, fn) {
|
|
337
|
+
async function withLock(targetPath, fn, options, onLockError) {
|
|
345
338
|
let release;
|
|
346
339
|
try {
|
|
347
|
-
release = await lock(
|
|
348
|
-
stale: STALE_LOCK_MS,
|
|
349
|
-
retries: LOCK_RETRIES,
|
|
350
|
-
});
|
|
340
|
+
release = await lock(targetPath, options);
|
|
351
341
|
await fn();
|
|
352
342
|
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
if (onLockError?.(error)) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
353
349
|
finally {
|
|
354
350
|
if (release) {
|
|
355
351
|
try {
|
|
@@ -361,6 +357,44 @@ async function withFileLock(filePath, fn) {
|
|
|
361
357
|
}
|
|
362
358
|
}
|
|
363
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* Execute a callback while holding a file lock.
|
|
362
|
+
*
|
|
363
|
+
* @remarks
|
|
364
|
+
* Acquires a lock on the file, executes the callback, and releases
|
|
365
|
+
* the lock in a finally block. The lock uses a 2-minute stale threshold
|
|
366
|
+
* and retries up to 5 times.
|
|
367
|
+
*
|
|
368
|
+
* @param filePath - Absolute path to the file to lock.
|
|
369
|
+
* @param fn - Async callback to execute while holding the lock.
|
|
370
|
+
*/
|
|
371
|
+
async function withFileLock(filePath, fn) {
|
|
372
|
+
await withLock(filePath, fn, {
|
|
373
|
+
stale: STALE_LOCK_MS,
|
|
374
|
+
retries: LOCK_RETRIES,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Execute a callback while holding the workspace cycle lock.
|
|
379
|
+
*
|
|
380
|
+
* @remarks
|
|
381
|
+
* Acquires a lock on `{workspacePath}/jeeves.lock`, executes the callback,
|
|
382
|
+
* and releases the lock in a finally block. If the lock is already held,
|
|
383
|
+
* returns silently so the caller can skip this cycle.
|
|
384
|
+
*
|
|
385
|
+
* @param workspacePath - Absolute workspace path.
|
|
386
|
+
* @param fn - Async callback to execute while holding the lock.
|
|
387
|
+
*/
|
|
388
|
+
async function withWorkspaceLock(workspacePath, fn) {
|
|
389
|
+
const lockPath = join(workspacePath, WORKSPACE_LOCK_FILE);
|
|
390
|
+
writeFileSync(lockPath, '', { flag: 'a' });
|
|
391
|
+
await withLock(lockPath, fn, {
|
|
392
|
+
stale: STALE_LOCK_MS,
|
|
393
|
+
retries: WORKSPACE_LOCK_RETRIES,
|
|
394
|
+
}, (error) => error instanceof Error &&
|
|
395
|
+
'code' in error &&
|
|
396
|
+
error.code === 'ELOCKED');
|
|
397
|
+
}
|
|
364
398
|
|
|
365
399
|
/**
|
|
366
400
|
* Shared internal utility functions.
|
|
@@ -1094,7 +1128,7 @@ function buildHeartbeatSection(entries) {
|
|
|
1094
1128
|
*
|
|
1095
1129
|
* @remarks
|
|
1096
1130
|
* Replaces everything from `# Jeeves Platform Status` to EOF.
|
|
1097
|
-
* Preserves user content above the heading.
|
|
1131
|
+
* Preserves user content above the heading.
|
|
1098
1132
|
*
|
|
1099
1133
|
* @param filePath - Absolute path to HEARTBEAT.md.
|
|
1100
1134
|
* @param entries - Component entries to write.
|
|
@@ -3236,7 +3270,7 @@ function stripForeignMarkers(content, currentMarkers) {
|
|
|
3236
3270
|
* - `block`: Replaces the entire managed block (SOUL.md, AGENTS.md).
|
|
3237
3271
|
* - `section`: Upserts a named H2 section within the managed block (TOOLS.md).
|
|
3238
3272
|
*
|
|
3239
|
-
* Provides
|
|
3273
|
+
* Provides version-stamp convergence and atomic writes.
|
|
3240
3274
|
*/
|
|
3241
3275
|
/**
|
|
3242
3276
|
* Update a managed section in a file.
|
|
@@ -3383,17 +3417,7 @@ var agentsSectionContent = `## "I'll Note This" Is Not Noting
|
|
|
3383
3417
|
|
|
3384
3418
|
## Context Compaction Recovery
|
|
3385
3419
|
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
1. **Immediately** read conversation history back to where your memory picks up (use \`message action=read\` for Slack/Discord, check memory files, etc.)
|
|
3389
|
-
2. Reconstruct the thread: what were we doing? what was decided? what's the next step?
|
|
3390
|
-
3. **Re-run skill selection** against the reconstructed task context. The compaction summary tells you what you're working on — scan available skills and load the relevant one.
|
|
3391
|
-
4. **Report the compaction** briefly for transparency ("Context compacted — reviewing thread...")
|
|
3392
|
-
5. **Then continue** as if you never lost context — pick up where you left off
|
|
3393
|
-
|
|
3394
|
-
**The goal:** Automatic remediation with minimal disruption. Don't ask "what were we talking about?" when you can find out yourself. The human shouldn't have to re-explain; you have the tools to recover.
|
|
3395
|
-
|
|
3396
|
-
**Anti-pattern:** Announcing amnesia *before* checking your notes. That's alarming and lazy. Check the ledger first, then speak.
|
|
3420
|
+
On compaction: read conversation history, reconstruct context, re-run skill selection against the reconstructed task, report briefly ("Context compacted — reviewing thread..."), then continue. Check notes before announcing amnesia.
|
|
3397
3421
|
|
|
3398
3422
|
## Subagent Briefing Protocol
|
|
3399
3423
|
|
|
@@ -3411,22 +3435,10 @@ At minimum, always brief sub-agents on:
|
|
|
3411
3435
|
|
|
3412
3436
|
**Strong runner preference:** Any recurring task that can run as a script (no LLM session needed) belongs in jeeves-runner, not gateway cron or heartbeat. If runner is installed, use it. If not, suggest bootstrapping it. Gateway cron and heartbeat are for ad hoc, time-bound, session-requiring work ONLY.
|
|
3413
3437
|
|
|
3414
|
-
**Before creating a recurring gateway cron job**, assess whether it will frequently no-op. If so, flag it: *"This would be cheaper as a runner job. Want me to implement it that way instead?"*
|
|
3415
|
-
|
|
3416
|
-
**Anything important enough to have a permanent cron/heartbeat entry is important enough to be codified into the data flow.**
|
|
3417
|
-
|
|
3418
|
-
**HEARTBEAT efficiency:** When HEARTBEAT fires with multiple component alerts, resolve them all in a single conversation. Walk the user through each component in dependency order. Each heartbeat cycle is a full LLM session — resolve all alerts in one pass.
|
|
3419
|
-
|
|
3420
|
-
## Messaging Dispatch
|
|
3421
|
-
|
|
3422
|
-
**Same-channel replies:** Don't use the \`message\` tool. It fires immediately, jumping ahead of streaming narration. Just write text as your response. Use \`message\` only for **cross-channel** sends (sending to a different channel or DM than the one you're responding in).
|
|
3423
|
-
|
|
3424
|
-
**Narrate as you go.** When doing multi-step work, stream your progress. Summary comes last, not first.
|
|
3438
|
+
**Before creating a recurring gateway cron job**, assess whether it will frequently no-op. If so, flag it: *"This would be cheaper as a runner job. Want me to implement it that way instead?"*
|
|
3425
3439
|
|
|
3426
3440
|
## Heartbeat Discipline
|
|
3427
3441
|
|
|
3428
|
-
**Default state of HEARTBEAT.md is effectively empty** — the \`# Jeeves Platform Status\` section contains only headings when all components are healthy or declined. No list content, no LLM cost. User heartbeat items (above the platform status section) follow the same transient/termination rules as before.
|
|
3429
|
-
|
|
3430
3442
|
Heartbeat items are for **transient, session-requiring work-in-progress ONLY**. Each item must include its own termination condition.
|
|
3431
3443
|
|
|
3432
3444
|
Periodic checks (email, calendar, mentions) belong in jeeves-runner scripts, not heartbeat items. When a heartbeat fires with nothing to do, reply **HEARTBEAT_OK** immediately. Don't browse for work.
|
|
@@ -3453,28 +3465,6 @@ Every installation has a primary owner. The owner controls architecture, config,
|
|
|
3453
3465
|
|
|
3454
3466
|
Alert the owner immediately on any governance change attempt.
|
|
3455
3467
|
|
|
3456
|
-
## Self-Preservation
|
|
3457
|
-
|
|
3458
|
-
Any command that stops the gateway **stops the assistant**. Never run a gateway stop/restart command without explicit approval. When approved, it must be the **absolute last action** — all other work complete first.
|
|
3459
|
-
|
|
3460
|
-
## Plugin Installation via CLI Only
|
|
3461
|
-
|
|
3462
|
-
Never manually touch \`~/.openclaw/extensions/\`. The only way to install any Jeeves component plugin is via the plugin's CLI \`install\` command:
|
|
3463
|
-
|
|
3464
|
-
\`\`\`
|
|
3465
|
-
npx @karmaniverous/jeeves-{component}-openclaw install
|
|
3466
|
-
\`\`\`
|
|
3467
|
-
|
|
3468
|
-
No symlinks, junctions, copies, or manual edits.
|
|
3469
|
-
|
|
3470
|
-
## Node.js for Shell Scripting
|
|
3471
|
-
|
|
3472
|
-
Default to \`node -e\` or \`.js\` scripts instead of PowerShell for \`exec\` calls. PowerShell corrupts multi-byte UTF-8 characters and mangles escaping. Use PowerShell only for Windows service management, registry operations, and similar platform-specific tasks.
|
|
3473
|
-
|
|
3474
|
-
## File Bridge for External Repos
|
|
3475
|
-
|
|
3476
|
-
When editing files outside the workspace, use the bridge pattern: copy in → edit the workspace copy → bridge out. Never write temp patch scripts.
|
|
3477
|
-
|
|
3478
3468
|
## No Orphaned Data
|
|
3479
3469
|
|
|
3480
3470
|
When discovering a new data source, integrate it into the existing data flow pipeline. Never save data outside the synthesis pipeline. Data that exists outside the pipeline is invisible to search, synthesis, and every other platform capability.
|
|
@@ -3483,9 +3473,9 @@ When discovering a new data source, integrate it into the existing data flow pip
|
|
|
3483
3473
|
|
|
3484
3474
|
**Proactive platform status:** HEARTBEAT.md is loaded every session. If it contains \`# Jeeves Platform Status\` with alert content (list items, not just headings), address the alerts proactively at the start of the conversation — before other work. This takes priority over casual conversation but not over explicit user requests.
|
|
3485
3475
|
|
|
3486
|
-
**Follow the instructions:** Explain the component, ask for consent, execute the command, verify. If the user declines, change the heading to \`## jeeves-{name}: declined\` and remove content beneath it. Do not prompt for declined components.
|
|
3476
|
+
**Follow the instructions:** Explain the component, ask for consent, execute the command, verify. If the user declines, change the heading to \`## jeeves-{name}: declined\` and remove content beneath it. Do not prompt for declined components.
|
|
3487
3477
|
|
|
3488
|
-
When resolving multiple component alerts, walk the user through each in dependency order
|
|
3478
|
+
When resolving multiple component alerts, walk the user through each in dependency order within a single conversation rather than one per heartbeat cycle.
|
|
3489
3479
|
|
|
3490
3480
|
## Em-Dash Discipline
|
|
3491
3481
|
|
|
@@ -3495,29 +3485,13 @@ The em-dash sets apart parentheticals. It is NOT a replacement for comma, colon,
|
|
|
3495
3485
|
|
|
3496
3486
|
Operational hard gates — procedural rules earned through real incidents. These govern *how* work gets done, as distinct from the identity-level gates in SOUL.md which govern *who I am*.
|
|
3497
3487
|
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
### No Prod Modifications
|
|
3507
|
-
|
|
3508
|
-
Never modify packaged applications running in production. No \`npm link\` into a live service. All changes go through: branch, change, test, PR, merge, publish, install.
|
|
3509
|
-
|
|
3510
|
-
### PR Mergeability Check
|
|
3511
|
-
|
|
3512
|
-
Always verify a PR is mergeable (no conflicts) before requesting review. Resolve conflicts first.
|
|
3513
|
-
|
|
3514
|
-
### Pre-Push Verification Gate
|
|
3515
|
-
|
|
3516
|
-
Run **ALL** quality checks before pushing. Zero errors AND zero warnings. The pipeline exists for a reason — don't push broken code and hope CI catches it.
|
|
3517
|
-
|
|
3518
|
-
### Commit AND Push
|
|
3519
|
-
|
|
3520
|
-
No stranded local branches. Push immediately after commit. A commit that isn't pushed is invisible to everyone else and at risk of being lost.
|
|
3488
|
+
- **eslint-disable Is Forbidden:** Never disable lint/typecheck rules without surfacing for discussion. Fix the code.
|
|
3489
|
+
- **Mass File Changes Are a Smell:** If a fix requires changing dozens of files, stop and discuss — there is probably a config or rule solution.
|
|
3490
|
+
- **No Prod Modifications:** Never modify packaged prod applications. All changes go through branch → test → PR → merge → publish → install.
|
|
3491
|
+
- **PR Mergeability Check:** Always verify PR is mergeable (no conflicts) before requesting review.
|
|
3492
|
+
- **Pre-Push Verification Gate:** Run ALL quality checks before pushing. Zero errors AND zero warnings.
|
|
3493
|
+
- **Commit AND Push:** Push immediately after every commit. Unpushed commits are invisible and at risk.
|
|
3494
|
+
- **New PR Over Merged Branch:** When a merged branch needs more work: \`gh pr create --head <existing-branch>\`. Do not cherry-pick or create new branches.
|
|
3521
3495
|
|
|
3522
3496
|
### Check PR State Before Pushing
|
|
3523
3497
|
|
|
@@ -3529,10 +3503,6 @@ No stranded local branches. Push immediately after commit. A commit that isn't p
|
|
|
3529
3503
|
|
|
3530
3504
|
This is not optional. It applies to every push, every branch, every time. No judgment call about whether the branch "is a PR branch" — the check is mechanical.
|
|
3531
3505
|
|
|
3532
|
-
### New PR Over Merged Branch
|
|
3533
|
-
|
|
3534
|
-
When a PR has been merged and additional work is needed on the same branch, create a new PR on the **same branch** targeting the same base. Do not create new branches, cherry-pick, or start over. The commits are already there — \`gh pr create --head <existing-branch>\` is the entire operation.
|
|
3535
|
-
|
|
3536
3506
|
## Managed Content Self-Maintenance
|
|
3537
3507
|
|
|
3538
3508
|
The Jeeves platform maintains managed sections in SOUL.md, AGENTS.md, and TOOLS.md using comment markers. If any of these files contains a **cleanup flag** indicating orphaned Jeeves content below the managed section markers:
|
|
@@ -4056,13 +4026,6 @@ const WORKSPACE_SIZE_FILES = [
|
|
|
4056
4026
|
'MEMORY.md',
|
|
4057
4027
|
'USER.md',
|
|
4058
4028
|
];
|
|
4059
|
-
/** Trimming guidance lines emitted in HEARTBEAT entries. */
|
|
4060
|
-
const TRIMMING_GUIDANCE = [
|
|
4061
|
-
' 1. Move domain-specific content to a local skill',
|
|
4062
|
-
' 2. Extract reference material to companion files with a pointer',
|
|
4063
|
-
' 3. Summarize verbose instructions',
|
|
4064
|
-
' 4. Remove stale content',
|
|
4065
|
-
].join('\n');
|
|
4066
4029
|
/**
|
|
4067
4030
|
* Check all workspace files against the character budget.
|
|
4068
4031
|
*
|
|
@@ -4111,10 +4074,17 @@ function workspaceFileHealthEntries(results) {
|
|
|
4111
4074
|
.filter((r) => r.exists && r.warning)
|
|
4112
4075
|
.map((r) => {
|
|
4113
4076
|
const pct = Math.round(r.usage * 100);
|
|
4114
|
-
const overBudgetNote = r.overBudget ? ' **Over budget.**' : '';
|
|
4077
|
+
const overBudgetNote = r.overBudget ? ' **Over budget.** ' : ' ';
|
|
4115
4078
|
const content = [
|
|
4116
|
-
|
|
4117
|
-
|
|
4079
|
+
`${r.file} is at ${String(pct)}% of its ${(r.budget / 1000).toLocaleString()}K char injection budget (${r.charCount.toLocaleString()} / ${r.budget.toLocaleString()} chars).${overBudgetNote}Action required:`,
|
|
4080
|
+
'',
|
|
4081
|
+
'1. Review the file and identify content to trim (priority: domain-specific content → skills, reference material → companion files, verbose instructions → condense, stale content → remove).',
|
|
4082
|
+
'2. Ask the owner for permission, then trim.',
|
|
4083
|
+
"3. If the owner declines, change this heading to '## " +
|
|
4084
|
+
r.file +
|
|
4085
|
+
": declined' to suppress this alert.",
|
|
4086
|
+
'',
|
|
4087
|
+
'Do NOT reply HEARTBEAT_OK while this alert is unresolved.',
|
|
4118
4088
|
].join('\n');
|
|
4119
4089
|
return {
|
|
4120
4090
|
name: r.file,
|
|
@@ -4673,6 +4643,7 @@ class ComponentWriter {
|
|
|
4673
4643
|
configDir;
|
|
4674
4644
|
gatewayUrl;
|
|
4675
4645
|
pendingCleanups = new Set();
|
|
4646
|
+
cyclePromise;
|
|
4676
4647
|
/** @internal */
|
|
4677
4648
|
constructor(component, options) {
|
|
4678
4649
|
this.component = component;
|
|
@@ -4685,7 +4656,9 @@ class ComponentWriter {
|
|
|
4685
4656
|
}
|
|
4686
4657
|
/** Whether the writer timer is currently running or pending its first cycle. */
|
|
4687
4658
|
get isRunning() {
|
|
4688
|
-
return this.jitterTimeout !== undefined ||
|
|
4659
|
+
return (this.jitterTimeout !== undefined ||
|
|
4660
|
+
this.timer !== undefined ||
|
|
4661
|
+
this.cyclePromise !== undefined);
|
|
4689
4662
|
}
|
|
4690
4663
|
/**
|
|
4691
4664
|
* Start the writer timer.
|
|
@@ -4703,8 +4676,7 @@ class ComponentWriter {
|
|
|
4703
4676
|
const jitterMs = Math.floor(Math.random() * intervalMs);
|
|
4704
4677
|
this.jitterTimeout = setTimeout(() => {
|
|
4705
4678
|
this.jitterTimeout = undefined;
|
|
4706
|
-
|
|
4707
|
-
this.timer = setInterval(() => void this.cycle(), intervalMs);
|
|
4679
|
+
this.scheduleNextCycle(0, intervalMs);
|
|
4708
4680
|
}, jitterMs);
|
|
4709
4681
|
}
|
|
4710
4682
|
/** Stop the writer timer. */
|
|
@@ -4714,10 +4686,19 @@ class ComponentWriter {
|
|
|
4714
4686
|
this.jitterTimeout = undefined;
|
|
4715
4687
|
}
|
|
4716
4688
|
if (this.timer) {
|
|
4717
|
-
|
|
4689
|
+
clearTimeout(this.timer);
|
|
4718
4690
|
this.timer = undefined;
|
|
4719
4691
|
}
|
|
4720
4692
|
}
|
|
4693
|
+
scheduleNextCycle(delayMs, intervalMs) {
|
|
4694
|
+
this.timer = setTimeout(() => {
|
|
4695
|
+
this.timer = undefined;
|
|
4696
|
+
void this.cycle().finally(() => {
|
|
4697
|
+
if (this.isRunning)
|
|
4698
|
+
this.scheduleNextCycle(intervalMs, intervalMs);
|
|
4699
|
+
});
|
|
4700
|
+
}, delayMs);
|
|
4701
|
+
}
|
|
4721
4702
|
/**
|
|
4722
4703
|
* Execute a single write cycle.
|
|
4723
4704
|
*
|
|
@@ -4728,44 +4709,54 @@ class ComponentWriter {
|
|
|
4728
4709
|
* 4. Run HEARTBEAT health orchestration.
|
|
4729
4710
|
*/
|
|
4730
4711
|
async cycle() {
|
|
4712
|
+
if (this.cyclePromise)
|
|
4713
|
+
return this.cyclePromise;
|
|
4714
|
+
this.cyclePromise = this.runCycle().finally(() => {
|
|
4715
|
+
this.cyclePromise = undefined;
|
|
4716
|
+
});
|
|
4717
|
+
return this.cyclePromise;
|
|
4718
|
+
}
|
|
4719
|
+
async runCycle() {
|
|
4731
4720
|
try {
|
|
4732
4721
|
const workspacePath = getWorkspacePath();
|
|
4733
4722
|
const toolsPath = join(workspacePath, WORKSPACE_FILES.tools);
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4723
|
+
await withWorkspaceLock(workspacePath, async () => {
|
|
4724
|
+
// 1. Write the component's TOOLS.md section
|
|
4725
|
+
const toolsContent = this.component.generateToolsContent();
|
|
4726
|
+
await updateManagedSection(toolsPath, toolsContent, {
|
|
4727
|
+
mode: 'section',
|
|
4728
|
+
sectionId: this.component.sectionId,
|
|
4729
|
+
markers: TOOLS_MARKERS,
|
|
4730
|
+
coreVersion: CORE_VERSION,
|
|
4731
|
+
});
|
|
4732
|
+
// 2. Platform content maintenance
|
|
4733
|
+
await refreshPlatformContent({
|
|
4734
|
+
coreVersion: CORE_VERSION,
|
|
4735
|
+
componentName: this.component.name,
|
|
4736
|
+
componentVersion: this.component.version,
|
|
4737
|
+
servicePackage: this.component.servicePackage,
|
|
4738
|
+
pluginPackage: this.component.pluginPackage,
|
|
4739
|
+
});
|
|
4740
|
+
// 3. Cleanup escalation
|
|
4741
|
+
if (this.gatewayUrl) {
|
|
4742
|
+
scanAndEscalateCleanup([
|
|
4743
|
+
{ filePath: toolsPath, markerIdentity: 'TOOLS' },
|
|
4744
|
+
{
|
|
4745
|
+
filePath: join(workspacePath, WORKSPACE_FILES.soul),
|
|
4746
|
+
markerIdentity: 'SOUL',
|
|
4747
|
+
},
|
|
4748
|
+
{
|
|
4749
|
+
filePath: join(workspacePath, WORKSPACE_FILES.agents),
|
|
4750
|
+
markerIdentity: 'AGENTS',
|
|
4751
|
+
},
|
|
4752
|
+
], this.gatewayUrl, this.pendingCleanups);
|
|
4753
|
+
}
|
|
4754
|
+
// 4. HEARTBEAT orchestration
|
|
4755
|
+
await runHeartbeatCycle({
|
|
4756
|
+
workspacePath,
|
|
4757
|
+
coreConfigDir: getCoreConfigDir(),
|
|
4758
|
+
configRoot: getConfigRoot(),
|
|
4759
|
+
});
|
|
4769
4760
|
});
|
|
4770
4761
|
}
|
|
4771
4762
|
catch (err) {
|
package/package.json
CHANGED