flowviant 0.74.2 → 0.76.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/bin/lib/agentPlan.mjs +187 -0
- package/bin/lib/deploy.mjs +4 -0
- package/bin/lib/fleet.mjs +25 -0
- package/bin/lib/landed.mjs +175 -0
- package/bin/lib/prompts.mjs +277 -10
- package/bin/lib/work.mjs +1149 -2
- package/bin/lib/worktreeDiff.mjs +1 -1
- package/package.json +1 -1
package/bin/lib/prompts.mjs
CHANGED
|
@@ -237,18 +237,32 @@ MECHANICS OF THIS TAB:
|
|
|
237
237
|
is their word to say, not yours to infer.
|
|
238
238
|
5. WHEN THEY HAVE TO CHOOSE, HAND THEM THE CHOICES. A real pick between known
|
|
239
239
|
options — not an open question — ends your reply with a fenced block the app
|
|
240
|
-
renders as
|
|
240
|
+
renders as an answer card; picking an option and pressing Submit sends its
|
|
241
|
+
label as their next message, so every label must read as an answer a person
|
|
242
|
+
would say out loud:
|
|
241
243
|
|
|
242
244
|
\`\`\`flowviant-ask
|
|
243
|
-
{"question": "Which auth flow
|
|
245
|
+
{"question": "Which auth flow should the preview gate use?",
|
|
246
|
+
"header": "Auth flow",
|
|
247
|
+
"options": [
|
|
248
|
+
{"label": "Cookie + CSP change", "description": "Ships today; needs the frame-src change."},
|
|
249
|
+
{"label": "Header-based", "description": "No CSP change, but every daemon must upgrade."},
|
|
250
|
+
"Prototype both"
|
|
251
|
+
],
|
|
252
|
+
"multiSelect": false}
|
|
244
253
|
\`\`\`
|
|
245
254
|
|
|
246
|
-
ONE block per reply, and always the LAST thing in it. Two to eight options
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
in
|
|
250
|
-
|
|
251
|
-
|
|
255
|
+
ONE block per reply, and always the LAST thing in it. Two to eight options.
|
|
256
|
+
A label IS the answer — a few words, never a comma (multi-select answers
|
|
257
|
+
arrive as the chosen labels comma-joined, in the order listed); a tradeoff
|
|
258
|
+
goes in "description", one short sentence, optional. "header" is an optional
|
|
259
|
+
topic tag, three words at most. Plain-string options still work. Do NOT add
|
|
260
|
+
an "Other" option — the card offers a free-text path itself. multiSelect
|
|
261
|
+
true only for a genuine check-several-of-these case. NEVER for an open
|
|
262
|
+
question — ask those in prose, like anyone would. And ask the question in
|
|
263
|
+
prose above the block as well: a client that doesn't render the fence shows
|
|
264
|
+
it as plain text, so the reply has to read as a question with its options
|
|
265
|
+
either way.
|
|
252
266
|
|
|
253
267
|
THE LEDGER. This session's work is logged as CARDS as it happens, by you,
|
|
254
268
|
through tools — so a four-hour churn doesn't evaporate into scrollback. The
|
|
@@ -369,8 +383,10 @@ MECHANICS OF THIS TAB:
|
|
|
369
383
|
their word to say, not yours to infer.
|
|
370
384
|
5. WHEN THEY HAVE TO CHOOSE, HAND THEM THE CHOICES. You have no tools here, but
|
|
371
385
|
this one costs none — it is text. A real pick between known options (not an
|
|
372
|
-
open question) ends your reply with a fenced block the app renders as
|
|
373
|
-
|
|
386
|
+
open question) ends your reply with a fenced block the app renders as an
|
|
387
|
+
answer card; picking an option and pressing Submit sends its label as their
|
|
388
|
+
next message, so every label must read as an answer a person would say out
|
|
389
|
+
loud:
|
|
374
390
|
|
|
375
391
|
\`\`\`flowviant-ask
|
|
376
392
|
{"question": "Which auth flow?", "options": ["Magic link", "Password", "Both"], "multiSelect": false}
|
|
@@ -485,3 +501,254 @@ export const REGROUND_KICKOFF = ({ sha, title, files, vaultDir, predictedPages =
|
|
|
485
501
|
`chapter that covers them), append the feature-history entry to log.md,\n` +
|
|
486
502
|
`then output REGROUND_DONE.`;
|
|
487
503
|
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* THE PLANNER — the scratch agent behind a Deploy press (2026-09-03).
|
|
507
|
+
*
|
|
508
|
+
* Somebody selected cards and pressed Deploy. This turn's whole job is to
|
|
509
|
+
* decide HOW THAT WORK SHOULD BE SPLIT across agents, and then stop. It writes
|
|
510
|
+
* no code, edits no files and starts nothing: a person reads what it proposes,
|
|
511
|
+
* edits it on the board, and accepting is what spawns anything.
|
|
512
|
+
*
|
|
513
|
+
* It runs READ-ONLY IN THE CHECKOUT under CONSULT_PERM — no Write, no Edit, no
|
|
514
|
+
* mkdir, no rm, and no MCP at all. The proposal comes back as its final
|
|
515
|
+
* message, not through a tool, which is what lets that permission set be this
|
|
516
|
+
* narrow. A planner authors a decision, and there is no file on this machine it
|
|
517
|
+
* has any business touching.
|
|
518
|
+
*
|
|
519
|
+
* The two facts it is asked to weigh are the only two that are actually
|
|
520
|
+
* knowable here: what the cards SAY, and what each live agent has already
|
|
521
|
+
* TOUCHED. Everything else — how long something will take, who should own it,
|
|
522
|
+
* whether it is a good idea — is not a question a planner can answer from a
|
|
523
|
+
* repository, and asking for it produces confident invention.
|
|
524
|
+
*/
|
|
525
|
+
export const SYSTEM_PLAN = `You are the human's own Claude, planning a batch of work in their repository.
|
|
526
|
+
|
|
527
|
+
You are READ-ONLY. You cannot write, edit or create files, and you have no tools
|
|
528
|
+
beyond reading the repo. Do not try. Your entire output is a plan.
|
|
529
|
+
|
|
530
|
+
WHAT YOU ARE DECIDING: a set of task cards has been selected. Split them across
|
|
531
|
+
one or more AGENTS. An agent is one CLI in one git worktree on one branch,
|
|
532
|
+
working its cards ONE AT A TIME in the order you give, and landing all of them
|
|
533
|
+
as a single reviewable branch.
|
|
534
|
+
|
|
535
|
+
THE RULES THAT MATTER:
|
|
536
|
+
|
|
537
|
+
1. SEQUENTIAL WORK BELONGS IN ONE AGENT. If B needs A's code to exist, put them
|
|
538
|
+
in the same agent, A first. Splitting a chain across agents means the second
|
|
539
|
+
one waits for the first to MERGE before it can even start.
|
|
540
|
+
|
|
541
|
+
2. SPLIT ONLY WHAT CAN GENUINELY RUN AT THE SAME TIME. Two agents editing the
|
|
542
|
+
same files land two branches that conflict, and somebody resolves it by hand.
|
|
543
|
+
Read the repo to find out whether they collide — do not guess from titles.
|
|
544
|
+
|
|
545
|
+
3. AN AGENT IS ONE REVIEWABLE BRANCH. Everything you put in one agent gets
|
|
546
|
+
approved or rejected TOGETHER. Cards a person would want to judge separately
|
|
547
|
+
belong in separate agents.
|
|
548
|
+
|
|
549
|
+
4. FEWER, LARGER AGENTS BEAT MANY SMALL ONES. Only a limited number run at once;
|
|
550
|
+
past that they queue, and a queue of tiny agents is slower than a few real
|
|
551
|
+
ones. Never propose more agents than the cap you were given.
|
|
552
|
+
|
|
553
|
+
5. NAME EACH AGENT AFTER THE WORK ("auth", "billing webhooks"), not after a
|
|
554
|
+
number. People say these names out loud.
|
|
555
|
+
|
|
556
|
+
6. A POINTS BUDGET bounds how far an agent may grow while it works — it files
|
|
557
|
+
its own follow-up cards when it finds things, and the budget is where it
|
|
558
|
+
stops and asks. Set it from the size of what you put in, leaving some room.
|
|
559
|
+
Omit it if the cards carry no sizes.
|
|
560
|
+
|
|
561
|
+
7. YOU MAY ADD CARDS TO A LIVE AGENT instead of creating a new one, when the
|
|
562
|
+
work needs what that agent has already built and has not merged yet. Use its
|
|
563
|
+
id in "intoAgentId".
|
|
564
|
+
|
|
565
|
+
ANSWER WITH ONE JSON OBJECT AND NOTHING ELSE — no prose before it, no prose
|
|
566
|
+
after it. Wrap it in a \`\`\`json fence:
|
|
567
|
+
|
|
568
|
+
\`\`\`json
|
|
569
|
+
{
|
|
570
|
+
"note": "one sentence on why you split it this way",
|
|
571
|
+
"agents": [
|
|
572
|
+
{
|
|
573
|
+
"tempId": "a1",
|
|
574
|
+
"name": "auth",
|
|
575
|
+
"taskIds": ["<card id>", "<card id>"],
|
|
576
|
+
"pointsBudget": 8,
|
|
577
|
+
"waitsOn": [],
|
|
578
|
+
"intoAgentId": null
|
|
579
|
+
}
|
|
580
|
+
]
|
|
581
|
+
}
|
|
582
|
+
\`\`\`
|
|
583
|
+
|
|
584
|
+
Every selected card id must appear EXACTLY ONCE across all agents. Use the ids
|
|
585
|
+
exactly as given. "waitsOn" holds tempIds of agents that must MERGE first.`;
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* The planner's turn.
|
|
589
|
+
*
|
|
590
|
+
* The cards and the live agents are FENCED: their titles, briefs and criteria
|
|
591
|
+
* are written by whoever files cards in this project and by agents themselves,
|
|
592
|
+
* and this turn reads a repository afterwards. The instruction that matters —
|
|
593
|
+
* "split this" — is ours and sits outside the fence.
|
|
594
|
+
*/
|
|
595
|
+
export const AGENT_PLAN_KICKOFF = ({ tasks, liveAgents, agentCap }) => {
|
|
596
|
+
const cards = tasks
|
|
597
|
+
.map(
|
|
598
|
+
(t) =>
|
|
599
|
+
`- id: ${t.id}\n title: ${t.title}\n` +
|
|
600
|
+
(t.points ? ` points: ${t.points}\n` : '') +
|
|
601
|
+
(t.anchors?.length ? ` owns: ${t.anchors.join(', ')}\n` : '') +
|
|
602
|
+
(t.brief ? ` brief: ${t.brief}\n` : '') +
|
|
603
|
+
(t.criteria?.length ? ` done when:\n${t.criteria.map((c) => ` - ${c}`).join('\n')}\n` : '')
|
|
604
|
+
)
|
|
605
|
+
.join('\n');
|
|
606
|
+
const live = liveAgents.length
|
|
607
|
+
? liveAgents
|
|
608
|
+
.map(
|
|
609
|
+
(a) =>
|
|
610
|
+
`- id: ${a.id}\n name: ${a.name || '(unnamed)'}\n status: ${a.status}\n` +
|
|
611
|
+
(a.changedFiles?.length
|
|
612
|
+
? ` has already changed:\n${a.changedFiles.map((f) => ` - ${f}`).join('\n')}\n`
|
|
613
|
+
: ' has changed nothing yet\n')
|
|
614
|
+
)
|
|
615
|
+
.join('\n')
|
|
616
|
+
: '(none)';
|
|
617
|
+
return (
|
|
618
|
+
`Split this batch of work across agents.\n\n` +
|
|
619
|
+
`At most ${agentCap} agent${agentCap === 1 ? '' : 's'} run at once on this machine; ` +
|
|
620
|
+
`propose no more than that.\n\n` +
|
|
621
|
+
`${fence('THE SELECTED CARDS', cards)}\n\n` +
|
|
622
|
+
`${fence('AGENTS ALREADY RUNNING (you may add to one)', live)}\n\n` +
|
|
623
|
+
`Read whatever you need from the repository to decide whether these collide. ` +
|
|
624
|
+
`Then answer with the JSON object and nothing else.`
|
|
625
|
+
);
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* AN AGENT'S SYSTEM PROMPT — the contract for one card in one worktree.
|
|
630
|
+
*
|
|
631
|
+
* Deliberately not SYSTEM_WORK. A Workbench tab is a CONVERSATION: it narrates
|
|
632
|
+
* through tools, holds context across many turns, and a person is watching. An
|
|
633
|
+
* agent turn is a TASK — it starts, does one card, and ends — and nobody is
|
|
634
|
+
* watching while it runs. So the whole contract is: do this card, commit it,
|
|
635
|
+
* and end with a JSON object saying what happened.
|
|
636
|
+
*
|
|
637
|
+
* IT HAS NO TOOLS BEYOND THE REPOSITORY. There is no MCP on this turn at all,
|
|
638
|
+
* which is why everything it needs to say has to fit in that final object and
|
|
639
|
+
* everything it needs to PROVE is measured from git afterwards. An agent naming
|
|
640
|
+
* its own commit shas would be a receipt pointing at whatever it liked; the
|
|
641
|
+
* daemon reads the log instead.
|
|
642
|
+
*
|
|
643
|
+
* THE ONE THING IT MUST NOT DO IS GUESS. A turn that ends with a question costs
|
|
644
|
+
* a person one reply; a turn that guesses costs them a review, a rejection and
|
|
645
|
+
* a second run — and the guess arrives wearing a confident summary.
|
|
646
|
+
*/
|
|
647
|
+
export const SYSTEM_AGENT = `You are the human's own Claude, working one task in a git worktree of their
|
|
648
|
+
repository. Nobody is watching this run. You have the repo and nothing else —
|
|
649
|
+
no project tools, no board, no chat.
|
|
650
|
+
|
|
651
|
+
WHAT TO DO:
|
|
652
|
+
|
|
653
|
+
1. Do the card you are given. Read whatever you need first. Follow the
|
|
654
|
+
repository's own conventions over anything you would do by default.
|
|
655
|
+
|
|
656
|
+
2. COMMIT YOUR WORK before you finish. Small, real commit messages. Uncommitted
|
|
657
|
+
work is work nobody can review or merge.
|
|
658
|
+
|
|
659
|
+
3. If you cannot finish because you need a DECISION only a person can make —
|
|
660
|
+
an ambiguous requirement, a choice between two designs, a missing credential
|
|
661
|
+
— STOP AND ASK. Do not guess. A question costs one reply; a guess costs a
|
|
662
|
+
review, a rejection and a second run, and it arrives looking finished.
|
|
663
|
+
|
|
664
|
+
4. If you find something broken that is NOT this card — a bug, a failing test
|
|
665
|
+
you did not cause — you may fix it, and you must SAY SO by raising it. It
|
|
666
|
+
becomes its own card so a person can see it happened rather than finding it
|
|
667
|
+
in the diff.
|
|
668
|
+
|
|
669
|
+
COMMIT TRAILER: every commit you make must end with a line reading
|
|
670
|
+
Flowviant-Task: <the card id you were given>
|
|
671
|
+
It is how the board attaches your commits to the card; a commit without it is
|
|
672
|
+
work nobody can trace back.
|
|
673
|
+
|
|
674
|
+
END YOUR TURN WITH ONE JSON OBJECT AND NOTHING AFTER IT, in a \`\`\`json fence:
|
|
675
|
+
|
|
676
|
+
\`\`\`json
|
|
677
|
+
{
|
|
678
|
+
"status": "delivered",
|
|
679
|
+
"summary": "one or two sentences on what you actually changed",
|
|
680
|
+
"raised": [{ "title": "short title", "brief": "what is wrong and what you did" }]
|
|
681
|
+
}
|
|
682
|
+
\`\`\`
|
|
683
|
+
|
|
684
|
+
or, if you are stopping to ask:
|
|
685
|
+
|
|
686
|
+
\`\`\`json
|
|
687
|
+
{
|
|
688
|
+
"status": "blocked",
|
|
689
|
+
"question": "the specific thing you need decided, in one or two sentences"
|
|
690
|
+
}
|
|
691
|
+
\`\`\`
|
|
692
|
+
|
|
693
|
+
"raised" is optional and only for work you did that was NOT this card. Do not
|
|
694
|
+
list the card itself there. Do not put commit shas in the summary — they are
|
|
695
|
+
read from git.`;
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* The turn itself.
|
|
699
|
+
*
|
|
700
|
+
* The card is FENCED: its title, brief and criteria are written by whoever
|
|
701
|
+
* files cards in this project, and this turn is about to edit code. The
|
|
702
|
+
* instruction is ours and sits outside the fence.
|
|
703
|
+
*
|
|
704
|
+
* The queue POSITION is stated because it changes behaviour: an agent that
|
|
705
|
+
* thinks it is finishing tidies up, writes summaries and stops; one that knows
|
|
706
|
+
* three more cards are coming leaves the ground ready for them.
|
|
707
|
+
*/
|
|
708
|
+
export const AGENT_TASK_KICKOFF = ({ agentName, task, position, total }) =>
|
|
709
|
+
`You are the agent "${safeName(agentName)}", working card ${position} of ${total} ` +
|
|
710
|
+
`on this branch. Everything you commit here is reviewed and merged TOGETHER with ` +
|
|
711
|
+
`the other cards in this run.\n\n` +
|
|
712
|
+
`${fence('THE CARD', taskBlock(task))}\n\n` +
|
|
713
|
+
`When you commit, put this trailer on the LAST line of each commit message so ` +
|
|
714
|
+
`the card can find its own commits:\n` +
|
|
715
|
+
`Flowviant-Task: ${task?.id ?? ''}\n\n` +
|
|
716
|
+
`Do it, commit it, and end with the JSON object.`;
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* An agent's NAME is model-authored — the planner chose it — and it is
|
|
720
|
+
* interpolated at the head of the prompt, OUTSIDE the fence, where a sentence
|
|
721
|
+
* would read as an instruction from us. Fencing the name would be absurd (it is
|
|
722
|
+
* two words in the middle of ours), so it is reduced to something that cannot
|
|
723
|
+
* be a sentence: one line, no fence delimiters, and short.
|
|
724
|
+
*/
|
|
725
|
+
const safeName = (n) =>
|
|
726
|
+
String(n ?? '')
|
|
727
|
+
.replace(/[\r\n]+/g, ' ')
|
|
728
|
+
.replace(/<<<|>>>/g, '')
|
|
729
|
+
.replace(/"/g, "'")
|
|
730
|
+
.trim()
|
|
731
|
+
.slice(0, 60) || 'agent';
|
|
732
|
+
|
|
733
|
+
const taskBlock = (task) =>
|
|
734
|
+
`id: ${task?.id ?? ''}\n` +
|
|
735
|
+
`title: ${task?.title ?? ''}\n` +
|
|
736
|
+
(task?.brief ? `\nbrief:\n${task.brief}\n` : '') +
|
|
737
|
+
(task?.criteria?.length ? `\ndone when:\n${task.criteria.map((c) => `- ${c}`).join('\n')}\n` : '') +
|
|
738
|
+
(task?.anchors?.length ? `\nthis card owns:\n${task.anchors.map((a) => `- ${a}`).join('\n')}\n` : '');
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* A PERSON SPOKE TO THE AGENT — usually the answer to its own question.
|
|
742
|
+
*
|
|
743
|
+
* It is the same shape as a task turn on purpose: the agent still ends with the
|
|
744
|
+
* JSON object, because whatever it does next either finishes the card it was on
|
|
745
|
+
* or blocks again, and those are the only two things the board can act on.
|
|
746
|
+
*/
|
|
747
|
+
export const AGENT_HUMAN_KICKOFF = ({ agentName, message, askedByName, task, position, total }) =>
|
|
748
|
+
`You are the agent "${safeName(agentName)}"` +
|
|
749
|
+
(task ? `, working card ${position} of ${total} on this branch` : '') +
|
|
750
|
+
`.\n\n` +
|
|
751
|
+
`${fence('WHO IS TALKING', askedByName || 'a member of this project')}\n\n` +
|
|
752
|
+
`${fence('WHAT THEY SAID', message)}\n\n` +
|
|
753
|
+
(task ? `${fence('THE CARD YOU ARE ON', taskBlock(task))}\n\n` : '') +
|
|
754
|
+
`Carry on, and end with the JSON object as usual.`;
|