@assuredloop/orca-bot-kit 0.2.0 → 0.3.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.
- package/README.md +15 -12
- package/SETUP.md +7 -2
- package/package.json +1 -1
- package/rules/finishing.md +3 -0
- package/rules/mail.md +3 -3
- package/rules/scope.md +4 -2
- package/rules/tests-first.md +6 -4
- package/skills/obk-arch/LICENSES.md +7 -0
- package/skills/obk-arch/NOTICE.md +113 -5
- package/skills/obk-arch/SKILL.md +221 -10
- package/skills/obk-bot-building/NOTICE.md +2 -2
- package/skills/obk-bot-building/SKILL.md +85 -8
- package/skills/obk-debugging/SKILL.md +30 -15
- package/skills/obk-decision-memo/NOTICE.md +1 -1
- package/skills/obk-finops/NOTICE.md +1 -1
- package/skills/obk-finops/SKILL.md +6 -4
- package/skills/obk-fleet-review/SKILL.md +42 -5
- package/skills/obk-grilling/NOTICE.md +11 -3
- package/skills/obk-grilling/SKILL.md +11 -7
- package/skills/obk-grooming/SKILL.md +5 -2
- package/skills/obk-handoff/NOTICE.md +1 -1
- package/skills/obk-recall/NOTICE.md +1 -1
- package/skills/obk-researching/NOTICE.md +1 -1
- package/skills/obk-researching/SKILL.md +19 -9
- package/skills/obk-reviewing/SKILL.md +4 -1
- package/skills/obk-tdd/SKILL.md +54 -23
- package/skills/obk-teach/NOTICE.md +2 -2
- package/skills/obk-why/NOTICE.md +1 -1
- package/skills/obk-why/SKILL.md +21 -10
- package/src/book.js +2 -2
- package/src/bot.js +1 -1
- package/src/cli.js +59 -20
- package/src/groom.js +5 -3
- package/src/health.js +2 -2
- package/src/hooks.js +73 -18
- package/src/init.js +3 -3
- package/src/launch.js +28 -5
- package/src/message.js +36 -14
- package/src/orca.js +95 -24
- package/src/pause.js +11 -6
- package/src/record.js +1 -1
- package/src/restart.js +6 -5
- package/src/retire.js +2 -2
- package/src/roster.js +1 -1
- package/src/rules.js +2 -2
- package/src/skills.js +7 -6
- package/src/sources.js +4 -3
- package/src/up.js +40 -15
- package/src/usage.js +2 -2
package/skills/obk-tdd/SKILL.md
CHANGED
|
@@ -35,8 +35,12 @@ tests live and how they are named, what the tests next door already do, and
|
|
|
35
35
|
which commands the project's own checks actually run. Never assume a default
|
|
36
36
|
command because it is the usual one for the language.
|
|
37
37
|
|
|
38
|
-
Use the focused command through the loop
|
|
39
|
-
|
|
38
|
+
Use the focused command through the loop. Before you finish, run what the
|
|
39
|
+
project requires, plus the checks that cover the change and its credible effects
|
|
40
|
+
elsewhere. The whole suite earns its cost on a broad or shared change and at a
|
|
41
|
+
release gate; when you leave it out, name the coverage you ran instead and why it
|
|
42
|
+
is enough. A gate the project requires is never waived, and a run still going is
|
|
43
|
+
not a run that passed.
|
|
40
44
|
|
|
41
45
|
## Start from the requirement, not the code
|
|
42
46
|
|
|
@@ -306,8 +310,9 @@ A seam below that gives a green test and no protection. If no correct seam
|
|
|
306
310
|
exists, that is itself the finding: report it, because the shape of the code is
|
|
307
311
|
what is stopping the bug from being pinned down.
|
|
308
312
|
|
|
309
|
-
Minimise the reproduction until
|
|
310
|
-
|
|
313
|
+
Minimise the reproduction until reducing it further would not improve the
|
|
314
|
+
diagnosis or the regression check, and keep the evidence you stopped on. Turn it
|
|
315
|
+
into a failing test, watch it fail, fix the cause, watch it pass, then run the
|
|
311
316
|
original unminimised scenario again.
|
|
312
317
|
|
|
313
318
|
Two tests are often better than one here: one at the interface, saying what a
|
|
@@ -316,15 +321,20 @@ lives. Get both to pass. The first is what stops the bug coming back in a way
|
|
|
316
321
|
anyone would notice; the second says where it was.
|
|
317
322
|
|
|
318
323
|
A regression test written after the fix has proved nothing yet. Prove it:
|
|
319
|
-
revert the fix, run it and watch it fail, restore the fix, run it again.
|
|
324
|
+
revert the fix, run it and watch it fail, restore the fix, run it again. A test
|
|
325
|
+
that already failed on a named pre-fix baseline and passes with the fix, unchanged
|
|
326
|
+
in between, has its proof: keep that receipt instead of reverting again. Where
|
|
327
|
+
the test changed since, or its sensitivity is unproven, prove it again on the bad
|
|
328
|
+
baseline in a disposable copy.
|
|
320
329
|
|
|
321
330
|
When the bug is intermittent, make the test deterministic if you can, and say
|
|
322
331
|
which signal you pinned down. When it is one of a class, land the test for the
|
|
323
332
|
one in front of you first, then say what else has the same shape.
|
|
324
333
|
|
|
325
334
|
When a failing test really is impractical, say so out loud, say why, and name
|
|
326
|
-
the closest executable check you used instead.
|
|
327
|
-
|
|
335
|
+
the closest executable check you used instead. Naming a check you will run later
|
|
336
|
+
is not having run one. Quiet omission is the part that does damage: a reviewer
|
|
337
|
+
treats a missing red receipt as a finding.
|
|
328
338
|
|
|
329
339
|
## Tests that catch nothing
|
|
330
340
|
|
|
@@ -406,11 +416,13 @@ The question is the whole point: if a real mistake were made in this code,
|
|
|
406
416
|
would a test fail? There is no score to reach.
|
|
407
417
|
|
|
408
418
|
**Everyday: your own hand check.** Once per piece of work, at the end, only
|
|
409
|
-
where it earns its place.
|
|
410
|
-
the work changed (money, permissions, eligibility,
|
|
411
|
-
lost, and whatever else the requirement cares most
|
|
412
|
-
look at the tests, each one run, each one expected to
|
|
413
|
-
one put back.
|
|
419
|
+
where it earns its place. A small representative set of distinct, plausible
|
|
420
|
+
mistakes in the risky logic the work changed (money, permissions, eligibility,
|
|
421
|
+
safety, data that could be lost, and whatever else the requirement cares most
|
|
422
|
+
about), chosen before you look at the tests, each one run, each one expected to
|
|
423
|
+
make a test fail, each one put back. Stop when another break would add no
|
|
424
|
+
evidence, and say why the set you ran was enough: two meaningful breaks beat five
|
|
425
|
+
padded ones. Reported in three lines.
|
|
414
426
|
|
|
415
427
|
One break at a time, never two at once: note the original, apply the break, run
|
|
416
428
|
the tests, record killed or survived, restore the code immediately, then the
|
|
@@ -429,8 +441,14 @@ removed so an absent value reaches further than it should; a call to a
|
|
|
429
441
|
collaborator deleted.
|
|
430
442
|
|
|
431
443
|
Skip it, and say that you skipped it, for work with no real logic in it: docs,
|
|
432
|
-
|
|
433
|
-
|
|
444
|
+
wiring, renames, prototypes. Size and file type do not decide whether logic is
|
|
445
|
+
risky: a one-character permission fix is risky logic, and so is a configuration
|
|
446
|
+
that decides who gets what.
|
|
447
|
+
|
|
448
|
+
Code with no runnable tests is a different case: there is logic, and nothing to
|
|
449
|
+
run it against. Report the missing automated coverage, the reason, the stand-in
|
|
450
|
+
check you used and what is still uncertain. It is a finding when behaviour the
|
|
451
|
+
requirement cares about lacks adequate verification.
|
|
434
452
|
|
|
435
453
|
**A survivor**, a break no test objected to, gets the equivalence question
|
|
436
454
|
first: is there any input for which the changed code returns a different value,
|
|
@@ -503,14 +521,27 @@ the work is handed on.
|
|
|
503
521
|
|
|
504
522
|
## Away from code
|
|
505
523
|
|
|
506
|
-
The same three moves work on
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
written for people
|
|
524
|
+
The same three moves work on configuration and data, where no tool will help:
|
|
525
|
+
state the check before you make the change, see the check fail first, and at
|
|
526
|
+
the end break the artefact on purpose to find out whether the check notices.
|
|
527
|
+
|
|
528
|
+
They apply where there is something that can be wrong and a check that can be
|
|
529
|
+
seen to fail: a configuration that decides who gets what, a transformation over
|
|
530
|
+
data. The skip rule is the same as for code.
|
|
531
|
+
|
|
532
|
+
Prose written for people is checked by review, not by a test: a test that matches
|
|
533
|
+
its text buys nothing. Text written for a model, a prompt included, is checked the
|
|
534
|
+
same way, with one addition: where the requirement is how the model behaves, a
|
|
535
|
+
representative use earns its place, and it is the whole check, without the three
|
|
536
|
+
moves above. A prompt that must hold an action until it is approved is exercised
|
|
537
|
+
once with the approval and once without. Neither needs a manufactured red, and no
|
|
538
|
+
wording edit needs a test of its own.
|
|
539
|
+
|
|
540
|
+
A decision that cannot be driven locally, such as a CI workflow or a setting in
|
|
541
|
+
someone else's service, is checked in two parts: its parsed structure and
|
|
542
|
+
whatever of it can be executed, then the real run when that is authorised.
|
|
543
|
+
Publishing, spending money or changing someone else's settings is never a
|
|
544
|
+
mandatory step of a check. A real run put off for later names who triggers it and
|
|
545
|
+
says what stays unverified until then.
|
|
515
546
|
|
|
516
547
|
Sources and licences: [NOTICE.md](NOTICE.md).
|
|
@@ -48,14 +48,14 @@ thanks:
|
|
|
48
48
|
|
|
49
49
|
Made generic, which is what the owner asked for. The original is built on top
|
|
50
50
|
of two other skills it invokes by name, and a kit skill carries what it needs
|
|
51
|
-
rather than depending on another being loaded (ADR
|
|
51
|
+
rather than depending on another being loaded (ADR 0019); here getting
|
|
52
52
|
oriented is part of the work, and where the reasons are the point that is
|
|
53
53
|
named as its own kind of digging without requiring a particular skill. Its
|
|
54
54
|
named image-generation tool is likewise described by what it produces rather
|
|
55
55
|
than by the tool.
|
|
56
56
|
|
|
57
57
|
One departure: the original keeps itself from being invoked on its own with
|
|
58
|
-
`disable-model-invocation: true`, which is not portable frontmatter (ADR
|
|
58
|
+
`disable-model-invocation: true`, which is not portable frontmatter (ADR 0019
|
|
59
59
|
and PRD 7.2: these run on purpose, never automatically). The description
|
|
60
60
|
carries that instead: the phrases that ask for it, and the note that answering
|
|
61
61
|
a question in passing is not this.
|
package/skills/obk-why/NOTICE.md
CHANGED
|
@@ -60,7 +60,7 @@ confidence tiers and the null-result discipline in the skill itself, and leaves
|
|
|
60
60
|
how the work is divided up to whoever is doing it.
|
|
61
61
|
|
|
62
62
|
One departure: the original keeps itself from being invoked on its own with
|
|
63
|
-
`disable-model-invocation: true`, which is not portable frontmatter (ADR
|
|
63
|
+
`disable-model-invocation: true`, which is not portable frontmatter (ADR 0019
|
|
64
64
|
and PRD 7.2: these run on purpose, never automatically). The description
|
|
65
65
|
carries that instead: the phrases that ask for it, and the note that it is not
|
|
66
66
|
something to start unasked before an ordinary change.
|
package/skills/obk-why/SKILL.md
CHANGED
|
@@ -46,14 +46,15 @@ hangs off that anchor, and an investigation that never had one tends to answer
|
|
|
46
46
|
a question nobody asked.
|
|
47
47
|
|
|
48
48
|
Where the project keeps its history, start there. Who last touched these
|
|
49
|
-
lines and in what change;
|
|
50
|
-
|
|
51
|
-
to it, which is usually where the
|
|
49
|
+
lines and in what change; as much of the file's life, through renames, as it
|
|
50
|
+
takes to reach the change that answers the question; what the change said about
|
|
51
|
+
itself; and whatever discussion is attached to it, which is usually where the
|
|
52
|
+
reason actually is.
|
|
52
53
|
|
|
53
54
|
## Then look where else this project keeps its reasons
|
|
54
55
|
|
|
55
56
|
Different reasons live in different places, and which of these exist varies by
|
|
56
|
-
project. Use the ones it has:
|
|
57
|
+
project. Use the ones it has, as far as the question needs:
|
|
57
58
|
|
|
58
59
|
- **The history and its discussion**, where the project keeps them. Best for
|
|
59
60
|
the reason given at the time by the person doing it, under review. For a
|
|
@@ -72,11 +73,15 @@ project. Use the ones it has:
|
|
|
72
73
|
Best for defensive code: a guard, a retry, a limit, a special case usually
|
|
73
74
|
exists because something happened.
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
How many of them to open depends on what is still unanswered: once a change
|
|
77
|
+
and its discussion settle the reason, the rest adds nothing unless something
|
|
78
|
+
contradicts it, and they need neither a look nor a line in the write-up. While
|
|
79
|
+
the answer is open, look in the sources that could still change it, in parallel
|
|
80
|
+
where you can, one pass per source with its own question. Where one of those does
|
|
81
|
+
not exist or you cannot reach it, that is a finding: write down that you could
|
|
82
|
+
not look there. Skipping one of those because it is probably irrelevant is not
|
|
83
|
+
the same as it being provably irrelevant, and the first belongs in the write-up
|
|
84
|
+
as a gap.
|
|
80
85
|
|
|
81
86
|
Watch for the newest change looking authoritative. The shape you are asking
|
|
82
87
|
about is usually the accumulation of several decisions, and the most recent one
|
|
@@ -150,12 +155,18 @@ explanation of it.
|
|
|
150
155
|
|
|
151
156
|
## What you hand back
|
|
152
157
|
|
|
158
|
+
These are the questions the hand-back answers, not headings to print: a one-line
|
|
159
|
+
answer is one line, and it still carries its citation and any uncertainty or
|
|
160
|
+
search limit that would change how it is used.
|
|
161
|
+
|
|
153
162
|
- **The question**, as you understood it.
|
|
154
163
|
- **The code**, so the reader knows what is being explained.
|
|
155
164
|
- **What was actually recorded**, cited.
|
|
156
165
|
- **What follows from it**, marked as reasoning.
|
|
157
166
|
- **Other explanations that fit**, where more than one does.
|
|
158
|
-
- **What nobody wrote down**, with what you searched
|
|
167
|
+
- **What nobody wrote down**, with what you searched: where the search
|
|
168
|
+
stopped, what settled it, and the places that could matter which you left
|
|
169
|
+
unchecked.
|
|
159
170
|
- **Where you looked**, one line per source, including the ones that held
|
|
160
171
|
nothing and the ones you could not reach, with the reason.
|
|
161
172
|
- **How sure you are overall**, in a line.
|
package/src/book.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// The book: `sessions.yaml` in a bot's home. It is the authority for what the
|
|
2
|
-
// kit knows about a bot's sessions (ADR
|
|
2
|
+
// kit knows about a bot's sessions (ADR 0012) — Orca forgets a tab's resume
|
|
3
3
|
// record the moment the tab is closed, so the kit keeps its own record.
|
|
4
4
|
//
|
|
5
5
|
// It is an ordinary file of the user's bots repo: committed, readable, and
|
|
@@ -254,7 +254,7 @@ const asRecord = (value) => (value !== null && typeof value === 'object' && !Arr
|
|
|
254
254
|
/**
|
|
255
255
|
* The session entry to keep once a harness has named the session it is running
|
|
256
256
|
* as. The id the harness gave replaces the one the book held, and the one it
|
|
257
|
-
* held goes into the history with the reason it was replaced (ADR
|
|
257
|
+
* held goes into the history with the reason it was replaced (ADR 0012): old
|
|
258
258
|
* ids are what recall, auditing and finops read later.
|
|
259
259
|
*
|
|
260
260
|
* The same id come back is a session resumed, not a new one, so nothing moves.
|
package/src/bot.js
CHANGED
|
@@ -59,7 +59,7 @@ export function botNames(bots) {
|
|
|
59
59
|
* link the user made takes it out of that folder; undefined when it stays in.
|
|
60
60
|
*
|
|
61
61
|
* The kit writes only in the bot folder, never in user-level settings (PRD 6.3,
|
|
62
|
-
* ADR
|
|
62
|
+
* ADR 0020), and a link would make the one the other without a word. So the
|
|
63
63
|
* answer is the file system's, not the spelling's: see `whereItLeads`.
|
|
64
64
|
*/
|
|
65
65
|
export function leadsOutside(home, target) {
|
package/src/cli.js
CHANGED
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
import { readFileSync, realpathSync } from 'node:fs';
|
|
11
11
|
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
12
13
|
import { parseArgs } from 'node:util';
|
|
13
14
|
|
|
14
|
-
import { addSession, changeBot, changeSession, createBot, readBot, SESSION_FIELDS } from './bot.js';
|
|
15
|
+
import { addSession, changeBot, changeSession, createBot, leadsOutside, readBot, SESSION_FIELDS } from './bot.js';
|
|
15
16
|
import { grooming } from './groom.js';
|
|
16
17
|
import { checkHealth, orcaSettingFindings } from './health.js';
|
|
17
18
|
import { initBots } from './init.js';
|
|
18
|
-
import { APPROVALS, HARNESSES, shellWord } from './launch.js';
|
|
19
|
+
import { APPROVALS, HARNESSES, ownCli, shellWord, workDirOf } from './launch.js';
|
|
19
20
|
import { checkMail, lookUp, sendMessage } from './message.js';
|
|
20
21
|
import { orcaCli, orcaTrouble } from './orca.js';
|
|
21
22
|
import { pauseSessions, unpauseSessions } from './pause.js';
|
|
@@ -380,6 +381,21 @@ function sameFleet(bots) {
|
|
|
380
381
|
*/
|
|
381
382
|
const lookAt = (terminal) => `${shellWord(orcaCli())} terminal read --terminal ${terminal} --screen`;
|
|
382
383
|
|
|
384
|
+
/** The SETUP.md shipped with the running kit, whose section 5 has the answers. */
|
|
385
|
+
const SETUP = fileURLToPath(new URL('../SETUP.md', import.meta.url));
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Who answers a tab left on a screen: the caller, not the user (PRD 6.5). The
|
|
389
|
+
* kit names where the answers are rather than carrying them, because which
|
|
390
|
+
* keys answer which screen is judgment that changes with every harness
|
|
391
|
+
* release, and that belongs in the skill (ADR 0016).
|
|
392
|
+
*/
|
|
393
|
+
const ANSWER_IT = [
|
|
394
|
+
` Answer what is on screen yourself, without asking the user: section 5 of ${SETUP}`,
|
|
395
|
+
' and the obk-bot-building skill have the keys for the usual screens.',
|
|
396
|
+
' Anything you do not recognise gets no keypress: take it to the user.',
|
|
397
|
+
];
|
|
398
|
+
|
|
383
399
|
/** The one command a harness runs rather than a person: the kit's hook. */
|
|
384
400
|
const RECORD = 'session record';
|
|
385
401
|
|
|
@@ -389,7 +405,7 @@ const RECORD = 'session record';
|
|
|
389
405
|
* A hook runs inside the user's own session, so this one stays out of the way:
|
|
390
406
|
* it writes on standard output only what the harness is to read as JSON, and
|
|
391
407
|
* whatever goes wrong, it goes wrong quietly. A book left stale is a thing the
|
|
392
|
-
* health check finds later; a session disturbed is the user's work (ADR
|
|
408
|
+
* health check finds later; a session disturbed is the user's work (ADR 0020).
|
|
393
409
|
*/
|
|
394
410
|
async function record(bots, bot) {
|
|
395
411
|
try {
|
|
@@ -470,7 +486,7 @@ const commands = {
|
|
|
470
486
|
lines: [
|
|
471
487
|
...paused.closed.map((tab) => `closed ${tab.bot} ${tab.name} tab ${tab.tabId} terminal ${tab.terminal}`),
|
|
472
488
|
`${paused.changed ? 'paused' : 'there'.padEnd(6)} ${what}${paused.changed ? '' : ' was paused already'}`,
|
|
473
|
-
`obk up leaves it closed, and the book keeps its conversations. Bring it back:
|
|
489
|
+
`obk up leaves it closed, and the book keeps its conversations. Bring it back: ${shellWord(ownCli())} unpause --bots ${shellWord(bots)} --bot ${values.bot}${back}`,
|
|
474
490
|
],
|
|
475
491
|
};
|
|
476
492
|
},
|
|
@@ -532,8 +548,8 @@ const commands = {
|
|
|
532
548
|
...rulesLines(rules, bots),
|
|
533
549
|
...skillsLines(skills),
|
|
534
550
|
trouble
|
|
535
|
-
? `${made.bot} is written, and its rules are not. Settle what the line above says, then:
|
|
536
|
-
: `${made.bot} is written. Give it a session:
|
|
551
|
+
? `${made.bot} is written, and its rules are not. Settle what the line above says, then: ${shellWord(ownCli())} rules build --bots ${shellWord(bots)} --bot ${made.bot}`
|
|
552
|
+
: `${made.bot} is written. Give it a session: ${shellWord(ownCli())} session add --bots ${shellWord(bots)} --bot ${made.bot} --name <name>`,
|
|
537
553
|
],
|
|
538
554
|
code: trouble ? 1 : 0,
|
|
539
555
|
};
|
|
@@ -554,7 +570,7 @@ const commands = {
|
|
|
554
570
|
`changed the charter in ${path.join('bots', changed.bot, 'bot.yaml')}`,
|
|
555
571
|
...rulesLines(rules, bots),
|
|
556
572
|
trouble
|
|
557
|
-
? `${changed.bot}'s charter is written, and its rules are not. Settle what the line above says, then:
|
|
573
|
+
? `${changed.bot}'s charter is written, and its rules are not. Settle what the line above says, then: ${shellWord(ownCli())} rules build --bots ${shellWord(bots)} --bot ${changed.bot}`
|
|
558
574
|
: `${changed.bot}'s charter is changed. A session that is running read the old one when it started; it reads this one when it next starts.`,
|
|
559
575
|
],
|
|
560
576
|
code: trouble ? 1 : 0,
|
|
@@ -584,7 +600,7 @@ const commands = {
|
|
|
584
600
|
added.state === 'added'
|
|
585
601
|
? `added ${added.skill} to ${path.join('bots', added.bot, 'bot.yaml')}`
|
|
586
602
|
: `there ${added.skill} is on ${added.bot}'s list already, and nothing was written`,
|
|
587
|
-
`Link it:
|
|
603
|
+
`Link it: ${shellWord(ownCli())} skills build --bots ${shellWord(bots)} --bot ${added.bot}`,
|
|
588
604
|
],
|
|
589
605
|
};
|
|
590
606
|
},
|
|
@@ -597,7 +613,7 @@ const commands = {
|
|
|
597
613
|
removed.state === 'removed'
|
|
598
614
|
? `removed ${removed.skill} from ${path.join('bots', removed.bot, 'bot.yaml')}`
|
|
599
615
|
: `absent ${removed.skill} is not on ${removed.bot}'s list, and nothing was written`,
|
|
600
|
-
`Unlink it:
|
|
616
|
+
`Unlink it: ${shellWord(ownCli())} skills build --bots ${shellWord(bots)} --bot ${removed.bot}`,
|
|
601
617
|
],
|
|
602
618
|
};
|
|
603
619
|
},
|
|
@@ -611,7 +627,7 @@ const commands = {
|
|
|
611
627
|
...Object.entries(source)
|
|
612
628
|
.filter(([key]) => key !== 'name')
|
|
613
629
|
.map(([key, value]) => ` ${key.padEnd(5)} ${value}`),
|
|
614
|
-
`Fetch it:
|
|
630
|
+
`Fetch it: ${shellWord(ownCli())} skills fetch --bots ${shellWord(bots)} --source ${source.name}`,
|
|
615
631
|
],
|
|
616
632
|
};
|
|
617
633
|
},
|
|
@@ -746,7 +762,8 @@ const commands = {
|
|
|
746
762
|
|
|
747
763
|
'session add'(bots, values) {
|
|
748
764
|
const added = addSession(bots, values.bot, settingsOf(values));
|
|
749
|
-
const
|
|
765
|
+
const found = workDirFound(values, added.bot, added.home, added.session.name);
|
|
766
|
+
const answer = { bots, bot: added.bot, home: added.home, session: added.session, found };
|
|
750
767
|
return {
|
|
751
768
|
answer,
|
|
752
769
|
lines: [
|
|
@@ -754,7 +771,8 @@ const commands = {
|
|
|
754
771
|
...Object.entries(added.session)
|
|
755
772
|
.filter(([key]) => key !== 'name')
|
|
756
773
|
.map(([key, value]) => ` ${key} ${oneLine(value)}`),
|
|
757
|
-
|
|
774
|
+
...foundLines(found),
|
|
775
|
+
`Bring it up: ${shellWord(ownCli())} up --bots ${shellWord(bots)} --bot ${added.bot}`,
|
|
758
776
|
],
|
|
759
777
|
};
|
|
760
778
|
},
|
|
@@ -765,14 +783,16 @@ const commands = {
|
|
|
765
783
|
}
|
|
766
784
|
const { name, ...settings } = settingsOf(values);
|
|
767
785
|
const changed = changeSession(bots, values.bot, values.session, settings);
|
|
768
|
-
const restart =
|
|
786
|
+
const restart = `${shellWord(ownCli())} restart --bots ${shellWord(bots)} --bot ${changed.bot} --session ${values.session}`;
|
|
787
|
+
const found = workDirFound(values, changed.bot, changed.home, values.session);
|
|
769
788
|
return {
|
|
770
|
-
answer: { bots, bot: changed.bot, home: changed.home, session: changed.session, restart },
|
|
789
|
+
answer: { bots, bot: changed.bot, home: changed.home, session: changed.session, restart, found },
|
|
771
790
|
lines: [
|
|
772
791
|
`changed session ${values.session} in ${path.join('bots', changed.bot, 'bot.yaml')}`,
|
|
773
792
|
...Object.entries(changed.session)
|
|
774
793
|
.filter(([key]) => key !== 'name')
|
|
775
794
|
.map(([key, value]) => ` ${key} ${oneLine(value)}`),
|
|
795
|
+
...foundLines(found),
|
|
776
796
|
`A running session takes this when it next starts: ${restart}`,
|
|
777
797
|
],
|
|
778
798
|
};
|
|
@@ -794,7 +814,7 @@ function toLines(answer, bots, where) {
|
|
|
794
814
|
...(answer.unnamed === true
|
|
795
815
|
? [` ${where} is a Claude session running under no name the kit gave it: it was started before the kit named sessions, and nothing renames a live harness. It gets one the next time it starts. Until then the mailbox is the road that reaches it.`]
|
|
796
816
|
: []),
|
|
797
|
-
` Send it:
|
|
817
|
+
` Send it: ${shellWord(ownCli())} message send --bots ${shellWord(bots)} --to ${shellWord(where)} --subject <text> --text <text>`,
|
|
798
818
|
];
|
|
799
819
|
}
|
|
800
820
|
|
|
@@ -814,6 +834,23 @@ function nudgeLine(answer, where) {
|
|
|
814
834
|
return ` ${where} is not up, so nothing was typed anywhere: the message waits in its mailbox.`;
|
|
815
835
|
}
|
|
816
836
|
|
|
837
|
+
/**
|
|
838
|
+
* What there is to say about the work dir this command was given: one finding
|
|
839
|
+
* when it leads out of the bot home, through `..` or a link or as a path
|
|
840
|
+
* elsewhere, and nothing otherwise (#222). A bot's work and its clones belong
|
|
841
|
+
* under its own `work/`. It is said, not refused: the user may ask for a folder
|
|
842
|
+
* anywhere (PRD 6.3), and it is written as they gave it.
|
|
843
|
+
*/
|
|
844
|
+
function workDirFound(values, bot, home, session) {
|
|
845
|
+
const workDir = workDirOf({ work_dir: values['work-dir'] }, home);
|
|
846
|
+
if (workDir === undefined || leadsOutside(home, workDir) === undefined) return [];
|
|
847
|
+
return [{
|
|
848
|
+
kind: 'work-dir',
|
|
849
|
+
where: workDir,
|
|
850
|
+
says: `${workDir} is outside ${bot}'s folder, ${home}. A session's work and every clone it needs go under the bot's work/, as work/${session}, unless you asked for this place in plain words. It is written as given, and nothing was moved.`,
|
|
851
|
+
}];
|
|
852
|
+
}
|
|
853
|
+
|
|
817
854
|
/** The settings a `session add` was given, as they go into bot.yaml. */
|
|
818
855
|
function settingsOf(values) {
|
|
819
856
|
const settings = { name: values.name };
|
|
@@ -938,15 +975,15 @@ function groomLines(groom, bots) {
|
|
|
938
975
|
if (!groom.exists) {
|
|
939
976
|
return [
|
|
940
977
|
`${'groom'.padEnd(9)} there is no daily grooming yet`,
|
|
941
|
-
`Make one:
|
|
978
|
+
`Make one: ${shellWord(ownCli())} groom --bots ${shellWord(bots)} --at 04:00`,
|
|
942
979
|
];
|
|
943
980
|
}
|
|
944
981
|
|
|
945
982
|
return [
|
|
946
983
|
`${'groom'.padEnd(9)} daily at ${groom.at} ${groom.enabled ? 'on' : 'off'}`,
|
|
947
984
|
groom.enabled
|
|
948
|
-
? `It runs every day at ${groom.at} and spends tokens each time. Turn it off with:
|
|
949
|
-
: `It is not running yet. Try it by hand, read what it gives you, then:
|
|
985
|
+
? `It runs every day at ${groom.at} and spends tokens each time. Turn it off with: ${shellWord(ownCli())} groom --bots ${shellWord(bots)} --off`
|
|
986
|
+
: `It is not running yet. Try it by hand, read what it gives you, then: ${shellWord(ownCli())} groom --bots ${shellWord(bots)} --on`,
|
|
950
987
|
];
|
|
951
988
|
}
|
|
952
989
|
|
|
@@ -980,7 +1017,7 @@ function rosterLines(roster, bots) {
|
|
|
980
1017
|
}
|
|
981
1018
|
|
|
982
1019
|
lines.push(roster.length === 0
|
|
983
|
-
? `No bots yet. Make one:
|
|
1020
|
+
? `No bots yet. Make one: ${shellWord(ownCli())} bot create --bots ${shellWord(bots)} --name <name> --harness claude|codex`
|
|
984
1021
|
: `${roster.length} bot${roster.length === 1 ? '' : 's'}. Your bots folder: ${bots}`);
|
|
985
1022
|
return lines;
|
|
986
1023
|
}
|
|
@@ -1073,7 +1110,7 @@ function tabLines({ bots, created, completed, rules, skills, tabs, paused = [],
|
|
|
1073
1110
|
for (const one of paused) {
|
|
1074
1111
|
const what = one.session === undefined ? one.bot : `${one.bot} ${one.session}`;
|
|
1075
1112
|
const back = one.session === undefined ? '' : ` --session ${one.session}`;
|
|
1076
|
-
lines.push(`${'paused'.padEnd(9)} ${what} left closed. Bring it back:
|
|
1113
|
+
lines.push(`${'paused'.padEnd(9)} ${what} left closed. Bring it back: ${shellWord(ownCli())} unpause --bots ${shellWord(bots)} --bot ${one.bot}${back}`);
|
|
1077
1114
|
}
|
|
1078
1115
|
|
|
1079
1116
|
// Last before the summary, because what a check found is about the setup the
|
|
@@ -1103,6 +1140,7 @@ function harnessLines(tab, bots) {
|
|
|
1103
1140
|
...how,
|
|
1104
1141
|
' the harness was typed in, and no session came up in the tab.',
|
|
1105
1142
|
` Look at it: ${lookAt(tab.terminal)}`,
|
|
1143
|
+
...ANSWER_IT,
|
|
1106
1144
|
];
|
|
1107
1145
|
}
|
|
1108
1146
|
|
|
@@ -1112,6 +1150,7 @@ function harnessLines(tab, bots) {
|
|
|
1112
1150
|
...how,
|
|
1113
1151
|
` the harness was typed in and came up, waiting on: ${tab.blockedReason}`,
|
|
1114
1152
|
` Look at it: ${lookAt(tab.terminal)}`,
|
|
1153
|
+
...ANSWER_IT,
|
|
1115
1154
|
];
|
|
1116
1155
|
|
|
1117
1156
|
if (tab.promptSent === true) lines.push(' the start prompt was typed in.');
|
package/src/groom.js
CHANGED
|
@@ -22,6 +22,7 @@ import path from 'node:path';
|
|
|
22
22
|
|
|
23
23
|
import { botDir, readBot, requireBotsFolder } from './bot.js';
|
|
24
24
|
import { readBook, takeLock } from './book.js';
|
|
25
|
+
import { ownCli, shellWord } from './launch.js';
|
|
25
26
|
import { orca } from './orca.js';
|
|
26
27
|
import { BOT_FATHER } from './up.js';
|
|
27
28
|
|
|
@@ -35,10 +36,11 @@ const AT = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
|
|
35
36
|
const prompt = (bots) =>
|
|
36
37
|
'Run the daily grooming for this fleet, with the obk-grooming skill. Fix this '
|
|
37
38
|
+ 'run\'s end now and read what has happened since the last run\'s end up to it, '
|
|
38
|
-
+
|
|
39
|
+
+ `counting with ${shellWord(ownCli())} usage --since <last end> --until <this end>, and write this `
|
|
39
40
|
+ 'end down for the next run. Work out what it cost with obk-finops, keep '
|
|
40
41
|
+ 'each bot\'s profile notes, and send one short report to Bot Father\'s '
|
|
41
|
-
+ `management session. The
|
|
42
|
+
+ `management session. The kit here is ${shellWord(ownCli())}: run it wherever a skill says obk. `
|
|
43
|
+
+ `The bots folder is ${bots}.`;
|
|
42
44
|
|
|
43
45
|
/**
|
|
44
46
|
* Say what the grooming is, and make it so when asked.
|
|
@@ -66,7 +68,7 @@ export function grooming(bots, { at, on } = {}) {
|
|
|
66
68
|
const bot = readBot(home, BOT_FATHER);
|
|
67
69
|
const book = readBook(home);
|
|
68
70
|
if (book.orca.project === undefined) {
|
|
69
|
-
throw new Error(`${BOT_FATHER} has no Orca project yet, so there is nothing for the grooming to run in. Run
|
|
71
|
+
throw new Error(`${BOT_FATHER} has no Orca project yet, so there is nothing for the grooming to run in. Run ${shellWord(ownCli())} up --bots ${shellWord(bots)} --bot ${BOT_FATHER} first.`);
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
// Looking and making are one turn. Orca will not stop two runs creating two
|
package/src/health.js
CHANGED
|
@@ -266,8 +266,8 @@ function inOrca(home, bot, setups) {
|
|
|
266
266
|
/**
|
|
267
267
|
* The conversations a harness has on record in this bot's folder that the book
|
|
268
268
|
* does not name anywhere. The hook is how an id reaches the book, and when it
|
|
269
|
-
* fails it fails quietly (ADR
|
|
270
|
-
* harness's own record set beside it (ADR
|
|
269
|
+
* fails it fails quietly (ADR 0020), so this is where a stale book shows: the
|
|
270
|
+
* harness's own record set beside it (ADR 0012).
|
|
271
271
|
*/
|
|
272
272
|
function offTheBook(home, bot, book) {
|
|
273
273
|
const named = new Set();
|