bonecode 1.2.3 → 1.4.1

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.
Files changed (85) hide show
  1. package/README.md +62 -0
  2. package/compat/opencode_adapter.ts +69 -8
  3. package/dist/compat/opencode_adapter.js +63 -7
  4. package/dist/compat/opencode_adapter.js.map +1 -1
  5. package/dist/src/db_adapter.js +30 -0
  6. package/dist/src/db_adapter.js.map +1 -1
  7. package/dist/src/engine/agent/prompt/compaction.txt +9 -0
  8. package/dist/src/engine/agent/prompt/explore.txt +18 -0
  9. package/dist/src/engine/agent/prompt/scout.txt +36 -0
  10. package/dist/src/engine/agent/prompt/summary.txt +11 -0
  11. package/dist/src/engine/agent/prompt/title.txt +44 -0
  12. package/dist/src/engine/session/build_mode.d.ts +83 -0
  13. package/dist/src/engine/session/build_mode.js +789 -0
  14. package/dist/src/engine/session/build_mode.js.map +1 -0
  15. package/dist/src/engine/session/build_mode_helpers.d.ts +6 -0
  16. package/dist/src/engine/session/build_mode_helpers.js +61 -0
  17. package/dist/src/engine/session/build_mode_helpers.js.map +1 -0
  18. package/dist/src/engine/session/prompt/anthropic.txt +105 -0
  19. package/dist/src/engine/session/prompt/beast.txt +147 -0
  20. package/dist/src/engine/session/prompt/bonescript.txt +402 -0
  21. package/dist/src/engine/session/prompt/build-switch.txt +5 -0
  22. package/dist/src/engine/session/prompt/codex.txt +79 -0
  23. package/dist/src/engine/session/prompt/copilot-gpt-5.txt +143 -0
  24. package/dist/src/engine/session/prompt/default.txt +105 -0
  25. package/dist/src/engine/session/prompt/gemini.txt +155 -0
  26. package/dist/src/engine/session/prompt/gpt.txt +107 -0
  27. package/dist/src/engine/session/prompt/kimi.txt +95 -0
  28. package/dist/src/engine/session/prompt/max-steps.txt +16 -0
  29. package/dist/src/engine/session/prompt/plan-reminder-anthropic.txt +67 -0
  30. package/dist/src/engine/session/prompt/plan.txt +26 -0
  31. package/dist/src/engine/session/prompt/trinity.txt +97 -0
  32. package/dist/src/engine/session/prompt.js +92 -4
  33. package/dist/src/engine/session/prompt.js.map +1 -1
  34. package/dist/src/engine/skill/prompt/customize-opencode.md +377 -0
  35. package/dist/src/engine/tool/apply_patch.txt +33 -0
  36. package/dist/src/engine/tool/edit.txt +10 -0
  37. package/dist/src/engine/tool/glob.txt +6 -0
  38. package/dist/src/engine/tool/grep.txt +8 -0
  39. package/dist/src/engine/tool/lsp.txt +24 -0
  40. package/dist/src/engine/tool/plan-enter.txt +14 -0
  41. package/dist/src/engine/tool/plan-exit.txt +13 -0
  42. package/dist/src/engine/tool/question.txt +10 -0
  43. package/dist/src/engine/tool/read.txt +14 -0
  44. package/dist/src/engine/tool/repo_clone.txt +5 -0
  45. package/dist/src/engine/tool/repo_overview.txt +4 -0
  46. package/dist/src/engine/tool/shell/shell.txt +77 -0
  47. package/dist/src/engine/tool/skill.txt +5 -0
  48. package/dist/src/engine/tool/task.txt +58 -0
  49. package/dist/src/engine/tool/task_status.txt +13 -0
  50. package/dist/src/engine/tool/todowrite.txt +167 -0
  51. package/dist/src/engine/tool/tool/apply_patch.txt +33 -0
  52. package/dist/src/engine/tool/tool/edit.txt +10 -0
  53. package/dist/src/engine/tool/tool/glob.txt +6 -0
  54. package/dist/src/engine/tool/tool/grep.txt +8 -0
  55. package/dist/src/engine/tool/tool/lsp.txt +24 -0
  56. package/dist/src/engine/tool/tool/plan-enter.txt +14 -0
  57. package/dist/src/engine/tool/tool/plan-exit.txt +13 -0
  58. package/dist/src/engine/tool/tool/question.txt +10 -0
  59. package/dist/src/engine/tool/tool/read.txt +14 -0
  60. package/dist/src/engine/tool/tool/repo_clone.txt +5 -0
  61. package/dist/src/engine/tool/tool/repo_overview.txt +4 -0
  62. package/dist/src/engine/tool/tool/shell/shell.txt +77 -0
  63. package/dist/src/engine/tool/tool/skill.txt +5 -0
  64. package/dist/src/engine/tool/tool/task.txt +58 -0
  65. package/dist/src/engine/tool/tool/task_status.txt +13 -0
  66. package/dist/src/engine/tool/tool/todowrite.txt +167 -0
  67. package/dist/src/engine/tool/tool/webfetch.txt +13 -0
  68. package/dist/src/engine/tool/tool/websearch.txt +14 -0
  69. package/dist/src/engine/tool/tool/write.txt +8 -0
  70. package/dist/src/engine/tool/webfetch.txt +13 -0
  71. package/dist/src/engine/tool/websearch.txt +14 -0
  72. package/dist/src/engine/tool/write.txt +8 -0
  73. package/dist/src/tui.js +146 -9
  74. package/dist/src/tui.js.map +1 -1
  75. package/package.json +2 -2
  76. package/scripts/copy_prompts.js +58 -0
  77. package/scripts/test_bonescript_primer.js +111 -0
  78. package/scripts/test_build_fallback.js +221 -0
  79. package/scripts/test_build_mode.js +301 -0
  80. package/src/db_adapter.ts +29 -0
  81. package/src/engine/session/build_mode.ts +895 -0
  82. package/src/engine/session/build_mode_helpers.ts +72 -0
  83. package/src/engine/session/prompt/bonescript.txt +402 -0
  84. package/src/engine/session/prompt.ts +105 -4
  85. package/src/tui.ts +147 -9
package/src/tui.ts CHANGED
@@ -84,6 +84,7 @@ interface Command {
84
84
 
85
85
  const COMMANDS: Command[] = [
86
86
  { name: "/new", description: "Start a new session" },
87
+ { name: "/build", description: "Start autonomous build mode", args: "<project description>" },
87
88
  { name: "/session", description: "Show current session ID" },
88
89
  { name: "/sessions", description: "List recent sessions" },
89
90
  { name: "/model", description: "Switch model", args: "<provider/model>" },
@@ -702,6 +703,88 @@ async function streamPrompt(opts: {
702
703
  nl(` ${BLUE}⊕ Context compacted${R}`);
703
704
  continue;
704
705
  }
706
+
707
+ // Build mode events — autonomous orchestration progress
708
+ if (ev.type === "session.warning") {
709
+ flushTextLine();
710
+ nl(` ${YELLOW}⚠ ${ev.message || ""}${R}`);
711
+ continue;
712
+ }
713
+ if (ev.type === "build.stage") {
714
+ flushTextLine();
715
+ const stage = (ev.stage || "?").toUpperCase();
716
+ nl(` ${CYAN}${BOLD}▶ Build stage: ${stage}${R}`);
717
+ continue;
718
+ }
719
+ if (ev.type === "build.questions") {
720
+ flushTextLine();
721
+ nl(` ${CYAN}${BOLD}? Clarifying questions:${R}`);
722
+ for (const q of ev.questions || []) {
723
+ nl(` ${WHITE}- ${q}${R}`);
724
+ }
725
+ nl(` ${GRAY}Reply with your answers to continue.${R}`);
726
+ continue;
727
+ }
728
+ if (ev.type === "build.design") {
729
+ flushTextLine();
730
+ const d = ev.design || {};
731
+ nl(` ${CYAN}${BOLD}✓ Design locked in${R}`);
732
+ if (d.goal) nl(` ${GRAY}goal: ${d.goal}${R}`);
733
+ if (Array.isArray(d.requirements)) nl(` ${GRAY}${d.requirements.length} requirement(s), ${d.artifacts?.length || 0} artifact(s)${R}`);
734
+ continue;
735
+ }
736
+ if (ev.type === "build.plan") {
737
+ flushTextLine();
738
+ const todos = ev.todos || [];
739
+ nl(` ${CYAN}${BOLD}✓ Plan: ${todos.length} task(s)${R}`);
740
+ for (let i = 0; i < Math.min(todos.length, 10); i++) {
741
+ const t = todos[i];
742
+ nl(` ${GRAY}${(i + 1).toString().padStart(2, " ")}. ${t.title}${R}`);
743
+ }
744
+ if (todos.length > 10) nl(` ${GRAY}... ${todos.length - 10} more${R}`);
745
+ continue;
746
+ }
747
+ if (ev.type === "build.todo.start") {
748
+ flushTextLine();
749
+ const t = ev.todo || {};
750
+ nl(` ${WHITE}● ${t.title || ""}${R}`);
751
+ continue;
752
+ }
753
+ if (ev.type === "build.todo.done") {
754
+ flushTextLine();
755
+ const t = ev.todo || {};
756
+ nl(` ${GREEN}✓ ${t.title || ""}${R}${t.evidence ? ` ${GRAY}(${t.evidence})${R}` : ""}`);
757
+ continue;
758
+ }
759
+ if (ev.type === "build.todo.retry") {
760
+ flushTextLine();
761
+ const t = ev.todo || {};
762
+ nl(` ${YELLOW}⟳ Retry ${ev.attempt || ""}: ${t.title || ""}${R}`);
763
+ continue;
764
+ }
765
+ if (ev.type === "build.todo.failed") {
766
+ flushTextLine();
767
+ const t = ev.todo || {};
768
+ nl(` ${RED}✗ ${t.title || ""}${R} ${GRAY}${ev.reason || ""}${R}`);
769
+ continue;
770
+ }
771
+ if (ev.type === "build.verify.item") {
772
+ flushTextLine();
773
+ const icon = ev.satisfied ? `${GREEN}✓${R}` : `${RED}✗${R}`;
774
+ nl(` ${icon} ${WHITE}${ev.requirement || ""}${R}`);
775
+ if (ev.evidence) nl(` ${GRAY}${ev.evidence}${R}`);
776
+ continue;
777
+ }
778
+ if (ev.type === "build.replan") {
779
+ flushTextLine();
780
+ nl(` ${YELLOW}↻ Re-planning: ${ev.added || 0} new task(s) to address gaps${R}`);
781
+ continue;
782
+ }
783
+ if (ev.type === "build.done") {
784
+ flushTextLine();
785
+ nl(` ${GREEN}${BOLD}✓✓ Build complete — all requirements satisfied${R}`);
786
+ continue;
787
+ }
705
788
  } catch {
706
789
  // Ignore malformed events
707
790
  }
@@ -856,36 +939,62 @@ export async function runTUI(opts: {
856
939
  const promptStr = () => `${CYAN}${BOLD}>${R} `;
857
940
 
858
941
  // ─── Ctrl+C handling ──────────────────────────────────────────────────────
859
- // When streaming: abort the request AND notify server
860
- // When idle: clear menu/input or hint to use /exit
942
+ // Windows quirk: rl.on("SIGINT") only fires when readline is actively
943
+ // reading. We pause readline during streaming, which makes Ctrl+C dead.
944
+ // Use process.on("SIGINT") as the always-on handler so streaming aborts work.
945
+
946
+ let interruptCount = 0;
947
+
861
948
  const onSigint = async () => {
862
949
  if (streaming && abort) {
863
950
  abort.abort();
864
- // Also tell the server to cancel the agent loop
951
+ // Tell the server to cancel the agent loop too
865
952
  try {
866
953
  await fetch(`http://localhost:${port}/v2/session/${sessionId}/cancel`, {
867
954
  method: "POST",
868
955
  headers: { "Authorization": `Bearer ${token}` },
869
956
  });
870
957
  } catch { /* server may not have the endpoint, abort is enough */ }
958
+ out(`\n${YELLOW}interrupted${R}\n`);
871
959
  // Don't reprompt here — the streamPrompt finally block will handle UI
872
960
  return;
873
961
  }
874
962
 
875
- // Idle: clear menu if visible, else hint
963
+ interruptCount++;
964
+
965
+ // Idle: clear menu/input first, then on second Ctrl+C, exit
876
966
  if (menu.visible) {
877
967
  clearMenu(menu.rowsRendered);
878
968
  menu.visible = false;
879
969
  menu.rowsRendered = 0;
880
970
  menu.selected = 0;
971
+ interruptCount = 0;
972
+ out(`\n${promptStr()}`);
973
+ return;
881
974
  }
882
- out(`\n${GRAY}(Ctrl+D or /exit to quit)${R}\n`);
883
- rl.setPrompt(promptStr());
884
- rl.prompt();
975
+
976
+ // If user typed something, clear it
977
+ const lineLen = ((rl as any).line || "").length;
978
+ if (lineLen > 0) {
979
+ (rl as any).line = "";
980
+ (rl as any).cursor = 0;
981
+ out(`\r${ESC}[2K${promptStr()}`);
982
+ interruptCount = 0;
983
+ return;
984
+ }
985
+
986
+ // Empty prompt + Ctrl+C: first time hint, second time exit
987
+ if (interruptCount >= 2) {
988
+ out(`\n${GRAY}Goodbye.${R}\n`);
989
+ process.exit(0);
990
+ }
991
+ out(`\n${GRAY}(Press Ctrl+C again or Ctrl+D to exit)${R}\n${promptStr()}`);
885
992
  };
886
993
 
887
- // Detach readline's default SIGINT (which closes the line buffer)
888
- // and route it to our handler.
994
+ // Register on BOTH process and readline so it works during streaming AND
995
+ // during input. Process-level catches signals while rl is paused; readline
996
+ // catches them while the user is typing.
997
+ process.on("SIGINT", onSigint);
889
998
  rl.on("SIGINT", onSigint);
890
999
 
891
1000
  rl.on("close", () => {
@@ -1043,6 +1152,35 @@ export async function runTUI(opts: {
1043
1152
  } catch (e: any) { nl(`${RED}✗ ${e.message}${R}`); }
1044
1153
  break;
1045
1154
 
1155
+ case "build": {
1156
+ // /build <description> — start autonomous build mode for this prompt
1157
+ const description = args.join(" ").trim();
1158
+ if (!description) {
1159
+ nl(`${YELLOW}Usage:${R} /build <project description>`);
1160
+ nl(`${GRAY}Example: /build a 2D market simulation with 1000 shops over 100 years${R}`);
1161
+ break;
1162
+ }
1163
+ // Send the prompt prefixed with /build so the server enters build mode
1164
+ rl.pause();
1165
+ renderUserMessage(`/build ${description}`);
1166
+ out(` ${GRAY}entering build mode...${R}`);
1167
+ streaming = true;
1168
+ abort = new AbortController();
1169
+ const result = await streamPrompt({
1170
+ port, token, sessionId: sessionId!, model, provider,
1171
+ message: `/build ${description}`,
1172
+ worktree, abortSignal: abort.signal,
1173
+ });
1174
+ streaming = false;
1175
+ abort = null;
1176
+ if (!result.text && !result.error) clearLine();
1177
+ renderTurnEnd(model, result.elapsedMs, result.interrupted);
1178
+ if (result.error && !result.interrupted) nl(` ${RED}✗ ${result.error}${R}`);
1179
+ nl();
1180
+ rl.resume();
1181
+ break;
1182
+ }
1183
+
1046
1184
  case "session":
1047
1185
  nl(`${GRAY}${sessionId}${R}`);
1048
1186
  break;