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.
@@ -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 buttons; their click composes their answer as the next message:
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?", "options": ["Magic link", "Password", "Both"], "multiSelect": false}
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
- each label short enough to sit on a button. multiSelect true only for a
248
- genuine check-several-of-these case. NEVER for an open question ask those
249
- in prose, like anyone would. And ask the question in prose above the block
250
- as well: a client that doesn't render the fence shows it as plain text, so
251
- the reply has to read as a question with its options either way.
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
- buttons; their click composes their answer as the next message:
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.`;