archondev 2.19.4 → 2.19.6

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.
@@ -22,13 +22,17 @@ import {
22
22
  SUPABASE_ANON_KEY,
23
23
  SUPABASE_URL
24
24
  } from "./chunk-M4LGRTLC.js";
25
+ import {
26
+ handleTierSetup,
27
+ updateUserTier
28
+ } from "./chunk-GBYW3YAY.js";
25
29
  import {
26
30
  isAuthenticated,
27
31
  loadConfig
28
32
  } from "./chunk-SVU7MLG6.js";
29
33
 
30
34
  // src/cli/plan.ts
31
- import chalk from "chalk";
35
+ import chalk2 from "chalk";
32
36
  import { existsSync } from "fs";
33
37
  import { readFile, writeFile, mkdir, stat } from "fs/promises";
34
38
  import { join } from "path";
@@ -767,6 +771,50 @@ var TrackedAdversarialPlanner = class {
767
771
  }
768
772
  };
769
773
 
774
+ // src/cli/credits-recovery.ts
775
+ import chalk from "chalk";
776
+ import readline from "readline";
777
+ function prompt(question) {
778
+ return new Promise((resolve) => {
779
+ const rl = readline.createInterface({
780
+ input: process.stdin,
781
+ output: process.stdout
782
+ });
783
+ rl.question(`${chalk.cyan("?")} ${question}: `, (answer) => {
784
+ rl.close();
785
+ resolve(answer.trim());
786
+ });
787
+ });
788
+ }
789
+ async function handleInsufficientCreditsRecovery(input) {
790
+ console.log(chalk.red("\n\u274C Insufficient credits"));
791
+ console.log(chalk.dim(`Your balance: $${(input.balanceCents / 100).toFixed(2)}`));
792
+ console.log(chalk.dim(`Estimated ${input.operationLabel} cost: $${(input.estimatedCostCents / 100).toFixed(2)}`));
793
+ console.log();
794
+ console.log(chalk.bold("Choose next step:"));
795
+ console.log(` ${chalk.cyan("1")}) Add credits now (open checkout)`);
796
+ console.log(` ${chalk.cyan("2")}) Switch to BYOK and add API key now`);
797
+ console.log(` ${chalk.cyan("3")}) Cancel`);
798
+ console.log();
799
+ const choice = await prompt("Enter choice (1-3)");
800
+ if (choice === "1") {
801
+ await handleTierSetup("CREDITS");
802
+ return "retry";
803
+ }
804
+ if (choice === "2") {
805
+ const switchResult = await updateUserTier("BYOK");
806
+ if (!switchResult.success) {
807
+ console.log(chalk.red(switchResult.error ?? "Failed to switch to BYOK."));
808
+ return "abort";
809
+ }
810
+ console.log(chalk.green("\u2713 Switched to BYOK."));
811
+ await handleTierSetup("BYOK");
812
+ return "retry";
813
+ }
814
+ console.log(chalk.dim("Operation cancelled."));
815
+ return "abort";
816
+ }
817
+
770
818
  // src/cli/plan.ts
771
819
  var ATOMS_DIR = ".archon/atoms";
772
820
  function createPrompt() {
@@ -782,24 +830,24 @@ function createPrompt() {
782
830
  };
783
831
  }
784
832
  async function plan(description, options) {
785
- const prompt = createPrompt();
833
+ const prompt2 = createPrompt();
786
834
  try {
787
835
  if (!await isAuthenticated()) {
788
- console.log(chalk.yellow('Not authenticated. Run "archon login" first.'));
789
- console.log(chalk.dim("For local development, you can continue without authentication."));
836
+ console.log(chalk2.yellow('Not authenticated. Run "archon login" first.'));
837
+ console.log(chalk2.dim("For local development, you can continue without authentication."));
790
838
  }
791
839
  const archPath = join(process.cwd(), "ARCHITECTURE.md");
792
840
  if (!existsSync(archPath)) {
793
- console.error(chalk.red('ARCHITECTURE.md not found. Run "archon init" first.'));
841
+ console.error(chalk2.red('ARCHITECTURE.md not found. Run "archon init" first.'));
794
842
  process.exit(1);
795
843
  }
796
- console.log(chalk.dim("Parsing ARCHITECTURE.md..."));
844
+ console.log(chalk2.dim("Parsing ARCHITECTURE.md..."));
797
845
  const parser = new ArchitectureParser(archPath);
798
846
  const parseResult = await parser.parse();
799
847
  if (!parseResult.success || !parseResult.schema) {
800
- console.error(chalk.red("Failed to parse ARCHITECTURE.md:"));
848
+ console.error(chalk2.red("Failed to parse ARCHITECTURE.md:"));
801
849
  for (const error of parseResult.errors) {
802
- console.error(chalk.red(` - ${error.message}`));
850
+ console.error(chalk2.red(` - ${error.message}`));
803
851
  }
804
852
  process.exit(1);
805
853
  }
@@ -807,24 +855,24 @@ async function plan(description, options) {
807
855
  const references = extractReferencedFiles(description);
808
856
  const { foundFiles, missingFiles, fileSummaries } = await loadReferencedFileSummaries(references);
809
857
  if (references.length > 0) {
810
- console.log(chalk.dim("\nReferenced inputs detected:"));
858
+ console.log(chalk2.dim("\nReferenced inputs detected:"));
811
859
  for (const ref of references) {
812
- const status = foundFiles.includes(ref) ? chalk.green("\u2713") : chalk.yellow("!");
860
+ const status = foundFiles.includes(ref) ? chalk2.green("\u2713") : chalk2.yellow("!");
813
861
  console.log(` ${status} ${ref}`);
814
862
  }
815
863
  if (missingFiles.length > 0) {
816
- console.log(chalk.yellow("\nMissing referenced files:"));
864
+ console.log(chalk2.yellow("\nMissing referenced files:"));
817
865
  for (const missing of missingFiles) {
818
- console.log(chalk.yellow(` - ${missing}`));
866
+ console.log(chalk2.yellow(` - ${missing}`));
819
867
  }
820
- console.log(chalk.dim("\nHow would you like to proceed?"));
821
- console.log(chalk.dim(" 1) Cancel planning and add the missing files"));
822
- console.log(chalk.dim(" 2) Continue and mark missing files as required"));
823
- console.log(chalk.dim(" 3) Continue with default placeholders"));
824
- const proceedChoice = await prompt.ask("Choose 1/2/3 (default: 1): ");
868
+ console.log(chalk2.dim("\nHow would you like to proceed?"));
869
+ console.log(chalk2.dim(" 1) Cancel planning and add the missing files"));
870
+ console.log(chalk2.dim(" 2) Continue and mark missing files as required"));
871
+ console.log(chalk2.dim(" 3) Continue with default placeholders"));
872
+ const proceedChoice = await prompt2.ask("Choose 1/2/3 (default: 1): ");
825
873
  const choice = proceedChoice.trim() || "1";
826
874
  if (choice === "1") {
827
- console.log(chalk.dim("Planning cancelled."));
875
+ console.log(chalk2.dim("Planning cancelled."));
828
876
  return;
829
877
  }
830
878
  if (choice === "2") {
@@ -836,7 +884,7 @@ async function plan(description, options) {
836
884
  }
837
885
  }
838
886
  const deliverableTarget = await promptForDeliverableTarget(
839
- prompt,
887
+ prompt2,
840
888
  requirements,
841
889
  references
842
890
  );
@@ -844,9 +892,9 @@ async function plan(description, options) {
844
892
  requirements.push(`Deliverables saved to: ${deliverableTarget}`);
845
893
  }
846
894
  if (requirements.length > 0) {
847
- console.log(chalk.dim("\nDetected requirements:"));
895
+ console.log(chalk2.dim("\nDetected requirements:"));
848
896
  requirements.forEach((req, i) => console.log(` ${i + 1}. ${req}`));
849
- const confirm = await prompt.ask("\nUse these as acceptance criteria? (Y/n): ");
897
+ const confirm = await prompt2.ask("\nUse these as acceptance criteria? (Y/n): ");
850
898
  if (confirm.toLowerCase() === "n") {
851
899
  requirements.length = 0;
852
900
  }
@@ -858,7 +906,7 @@ async function plan(description, options) {
858
906
  deliverableTarget: deliverableTarget ?? void 0
859
907
  });
860
908
  const isContentOnlyTask = classification.kind === "content";
861
- const designApproved = await promptForDesignApproval(prompt, {
909
+ const designApproved = await promptForDesignApproval(prompt2, {
862
910
  description,
863
911
  requirements,
864
912
  references,
@@ -866,10 +914,10 @@ async function plan(description, options) {
866
914
  classification
867
915
  });
868
916
  if (!designApproved) {
869
- console.log(chalk.yellow("\nDesign not approved. Planning cancelled."));
917
+ console.log(chalk2.yellow("\nDesign not approved. Planning cancelled."));
870
918
  return;
871
919
  }
872
- console.log(chalk.dim("Creating atom from description..."));
920
+ console.log(chalk2.dim("Creating atom from description..."));
873
921
  const atomInput = parseAtomDescription(description, options, requirements);
874
922
  const atom = createAtom(atomInput, {
875
923
  referencedFiles: references,
@@ -882,18 +930,18 @@ async function plan(description, options) {
882
930
  });
883
931
  const validation = validateAtom(atom);
884
932
  if (!validation.valid) {
885
- console.error(chalk.red("Invalid atom:"));
933
+ console.error(chalk2.red("Invalid atom:"));
886
934
  for (const error of validation.errors) {
887
- console.error(chalk.red(` - ${error.field}: ${error.message}`));
935
+ console.error(chalk2.red(` - ${error.field}: ${error.message}`));
888
936
  }
889
937
  process.exit(1);
890
938
  }
891
- console.log(chalk.blue(`
939
+ console.log(chalk2.blue(`
892
940
  Atom created: ${atom.externalId}`));
893
- console.log(chalk.dim(`Title: ${atom.title}`));
894
- console.log(chalk.dim(`Acceptance Criteria: ${atom.acceptanceCriteria.length} items`));
941
+ console.log(chalk2.dim(`Title: ${atom.title}`));
942
+ console.log(chalk2.dim(`Acceptance Criteria: ${atom.acceptanceCriteria.length} items`));
895
943
  if (isContentOnlyTask) {
896
- console.log(chalk.blue("\nContent task detected. Creating a lightweight plan (no adversarial loop)."));
944
+ console.log(chalk2.blue("\nContent task detected. Creating a lightweight plan (no adversarial loop)."));
897
945
  atom.plan = buildContentPlan({
898
946
  description,
899
947
  requirements,
@@ -903,13 +951,13 @@ Atom created: ${atom.externalId}`));
903
951
  });
904
952
  atom.status = "READY";
905
953
  await saveAtom(atom);
906
- console.log(chalk.green(`
954
+ console.log(chalk2.green(`
907
955
  \u2705 Atom saved: ${atom.externalId}`));
908
- console.log(chalk.dim(`Status: ${atom.status}`));
909
- console.log(chalk.dim(`
956
+ console.log(chalk2.dim(`Status: ${atom.status}`));
957
+ console.log(chalk2.dim(`
910
958
  Next steps:`));
911
- console.log(chalk.dim(` - Execute: archon execute ${atom.externalId}`));
912
- console.log(chalk.dim(` - View: archon show ${atom.externalId}`));
959
+ console.log(chalk2.dim(` - Execute: archon execute ${atom.externalId}`));
960
+ console.log(chalk2.dim(` - View: archon show ${atom.externalId}`));
913
961
  return;
914
962
  }
915
963
  let apiKey = process.env["ANTHROPIC_API_KEY"];
@@ -921,16 +969,16 @@ Next steps:`));
921
969
  }
922
970
  }
923
971
  if (!apiKey) {
924
- console.log(chalk.yellow("\nNo API key configured. Skipping adversarial planning."));
925
- console.log(chalk.dim('Set ANTHROPIC_API_KEY or use "archon keys add anthropic"'));
972
+ console.log(chalk2.yellow("\nNo API key configured. Skipping adversarial planning."));
973
+ console.log(chalk2.dim('Set ANTHROPIC_API_KEY or use "archon keys add anthropic"'));
926
974
  await saveAtom(atom);
927
- console.log(chalk.green(`
975
+ console.log(chalk2.green(`
928
976
  Atom saved: ${atom.externalId}`));
929
- console.log(chalk.dim(`Next: Configure API key and run "archon plan ${atom.externalId} --continue"`));
977
+ console.log(chalk2.dim(`Next: Configure API key and run "archon plan ${atom.externalId} --continue"`));
930
978
  return;
931
979
  }
932
- console.log(chalk.blue("\nStarting adversarial planning..."));
933
- console.log(chalk.dim("Architect will generate a plan, Sentinel will validate it.\n"));
980
+ console.log(chalk2.blue("\nStarting adversarial planning..."));
981
+ console.log(chalk2.dim("Architect will generate a plan, Sentinel will validate it.\n"));
934
982
  const config = await loadConfig();
935
983
  let billingContext;
936
984
  if (config.userId && config.accessToken) {
@@ -945,24 +993,36 @@ Atom saved: ${atom.externalId}`));
945
993
  }
946
994
  const planner = billingContext ? new TrackedAdversarialPlanner({ apiKey, billing: billingContext }) : new AdversarialPlanner({ apiKey });
947
995
  if (billingContext && planner instanceof TrackedAdversarialPlanner) {
948
- const balanceCheck = await planner.checkBalance();
996
+ let balanceCheck = await planner.checkBalance();
997
+ if (balanceCheck.tier === "CREDITS") {
998
+ console.log(chalk2.dim(`Estimated planning cost: $${(balanceCheck.estimatedCostCents / 100).toFixed(2)} | Balance: $${(balanceCheck.balance / 100).toFixed(2)}`));
999
+ }
949
1000
  if (!balanceCheck.sufficient && balanceCheck.tier === "CREDITS") {
950
- console.log(chalk.red("\n\u274C Insufficient credits"));
951
- console.log(chalk.dim(`Your balance: $${(balanceCheck.balance / 100).toFixed(2)}`));
952
- console.log(chalk.dim(`Estimated cost: $${(balanceCheck.estimatedCostCents / 100).toFixed(2)}`));
953
- console.log(chalk.dim("\nAdd credits with: archon credits add"));
954
- console.log(chalk.dim("Or switch to BYOK: archon keys add anthropic"));
955
- process.exit(1);
1001
+ const recovery = await handleInsufficientCreditsRecovery({
1002
+ balanceCents: balanceCheck.balance,
1003
+ estimatedCostCents: balanceCheck.estimatedCostCents,
1004
+ operationLabel: "planning"
1005
+ });
1006
+ if (recovery === "abort") {
1007
+ return;
1008
+ }
1009
+ balanceCheck = await planner.checkBalance();
1010
+ if (!balanceCheck.sufficient && balanceCheck.tier === "CREDITS") {
1011
+ console.log(chalk2.red("\nStill insufficient credits after recovery action."));
1012
+ console.log(chalk2.dim("Please add more credits with: archon credits add"));
1013
+ printPlanNextActions(atom.externalId, false);
1014
+ return;
1015
+ }
956
1016
  }
957
1017
  }
958
1018
  const planResult = await planner.planAtom(atom, parseResult.schema);
959
1019
  if (!planResult.approved) {
960
- console.log(chalk.red("\n\u274C Plan not approved after maximum iterations."));
961
- console.log(chalk.yellow("Issues found:"));
1020
+ console.log(chalk2.red("\n\u274C Plan not approved after maximum iterations."));
1021
+ console.log(chalk2.yellow("Issues found:"));
962
1022
  for (const iteration of planResult.iterations) {
963
1023
  if (!iteration.validation.passed) {
964
1024
  for (const issue of iteration.validation.issues) {
965
- console.log(chalk.yellow(` - [${issue.severity}] ${issue.message}`));
1025
+ console.log(chalk2.yellow(` - [${issue.severity}] ${issue.message}`));
966
1026
  }
967
1027
  }
968
1028
  }
@@ -974,7 +1034,7 @@ Atom saved: ${atom.externalId}`));
974
1034
  )
975
1035
  });
976
1036
  if (shouldAutoFallback) {
977
- console.log(chalk.blue("\nTask looks content-focused. Switching to lightweight plan."));
1037
+ console.log(chalk2.blue("\nTask looks content-focused. Switching to lightweight plan."));
978
1038
  atom.plan = buildContentPlan({
979
1039
  description,
980
1040
  requirements,
@@ -984,19 +1044,19 @@ Atom saved: ${atom.externalId}`));
984
1044
  });
985
1045
  atom.status = "READY";
986
1046
  await saveAtom(atom);
987
- console.log(chalk.green(`
1047
+ console.log(chalk2.green(`
988
1048
  \u2705 Atom saved: ${atom.externalId}`));
989
- console.log(chalk.dim(`Status: ${atom.status}`));
990
- console.log(chalk.dim(`
1049
+ console.log(chalk2.dim(`Status: ${atom.status}`));
1050
+ console.log(chalk2.dim(`
991
1051
  Next steps:`));
992
- console.log(chalk.dim(` - Execute: archon execute ${atom.externalId}`));
993
- console.log(chalk.dim(` - View: archon show ${atom.externalId}`));
1052
+ console.log(chalk2.dim(` - Execute: archon execute ${atom.externalId}`));
1053
+ console.log(chalk2.dim(` - View: archon show ${atom.externalId}`));
994
1054
  return;
995
1055
  }
996
- const answer2 = await prompt.ask("\nSave as draft anyway? (y/N): ");
1056
+ const answer2 = await prompt2.ask("\nSave as draft anyway? (y/N): ");
997
1057
  const normalized = answer2.toLowerCase();
998
1058
  if (normalized.includes("wrong") || normalized.includes("content") || normalized.includes("not a code")) {
999
- console.log(chalk.blue("\nReclassifying as content task and creating a lightweight plan."));
1059
+ console.log(chalk2.blue("\nReclassifying as content task and creating a lightweight plan."));
1000
1060
  atom.plan = buildContentPlan({
1001
1061
  description,
1002
1062
  requirements,
@@ -1006,61 +1066,73 @@ Next steps:`));
1006
1066
  });
1007
1067
  atom.status = "READY";
1008
1068
  await saveAtom(atom);
1009
- console.log(chalk.green(`
1069
+ console.log(chalk2.green(`
1010
1070
  \u2705 Atom saved: ${atom.externalId}`));
1011
- console.log(chalk.dim(`Status: ${atom.status}`));
1012
- console.log(chalk.dim(`
1071
+ console.log(chalk2.dim(`Status: ${atom.status}`));
1072
+ console.log(chalk2.dim(`
1013
1073
  Next steps:`));
1014
- console.log(chalk.dim(` - Execute: archon execute ${atom.externalId}`));
1015
- console.log(chalk.dim(` - View: archon show ${atom.externalId}`));
1074
+ console.log(chalk2.dim(` - Execute: archon execute ${atom.externalId}`));
1075
+ console.log(chalk2.dim(` - View: archon show ${atom.externalId}`));
1016
1076
  return;
1017
1077
  }
1018
1078
  if (normalized !== "y") {
1019
- console.log(chalk.dim("Atom discarded."));
1079
+ console.log(chalk2.dim("Atom discarded."));
1020
1080
  return;
1021
1081
  }
1022
1082
  }
1023
1083
  if (planResult.finalPlan) {
1024
1084
  const enrichedPlan = enforcePlanStructure(planResult.finalPlan, requirements);
1025
1085
  planResult.finalPlan = enrichedPlan;
1026
- console.log(chalk.green("\n\u2705 Plan approved!"));
1086
+ console.log(chalk2.green("\n\u2705 Plan approved!"));
1027
1087
  displayPlan(enrichedPlan);
1028
1088
  } else {
1029
- console.log(chalk.yellow("\nNo approved plan available."));
1089
+ console.log(chalk2.yellow("\nNo approved plan available."));
1030
1090
  }
1031
- console.log(chalk.dim(`
1091
+ console.log(chalk2.dim(`
1032
1092
  Token usage: ${planResult.totalUsage.inputTokens} input, ${planResult.totalUsage.outputTokens} output`));
1033
1093
  if (planner instanceof TrackedAdversarialPlanner) {
1034
1094
  const trackedResult = planResult;
1035
1095
  if (trackedResult.totalCostCents && trackedResult.totalCostCents > 0) {
1036
- console.log(chalk.dim(`Credits used: $${(trackedResult.totalCostCents / 100).toFixed(4)}`));
1096
+ console.log(chalk2.dim(`Credits used: $${(trackedResult.totalCostCents / 100).toFixed(4)}`));
1037
1097
  if (trackedResult.remainingBalance !== void 0) {
1038
- console.log(chalk.dim(`Remaining balance: $${(trackedResult.remainingBalance / 100).toFixed(2)}`));
1098
+ console.log(chalk2.dim(`Remaining balance: $${(trackedResult.remainingBalance / 100).toFixed(2)}`));
1039
1099
  }
1040
1100
  }
1041
1101
  }
1042
- const answer = await prompt.ask("\nApprove this plan? (y/N/e for edit): ");
1102
+ const answer = await prompt2.ask("\nApprove this plan? (y/N/e for edit): ");
1043
1103
  if (answer.toLowerCase() === "e") {
1044
- console.log(chalk.yellow("Edit mode not yet implemented. Saving as draft."));
1104
+ console.log(chalk2.yellow("Edit mode not yet implemented. Saving as draft."));
1045
1105
  atom.status = "DRAFT";
1046
1106
  } else if (answer.toLowerCase() !== "y") {
1047
- console.log(chalk.dim("Atom discarded."));
1107
+ console.log(chalk2.dim("Atom discarded."));
1048
1108
  return;
1049
1109
  } else {
1050
1110
  atom.plan = planResult.finalPlan;
1051
1111
  atom.status = "READY";
1052
1112
  }
1053
1113
  await saveAtom(atom);
1054
- console.log(chalk.green(`
1114
+ console.log(chalk2.green(`
1055
1115
  \u2705 Atom saved: ${atom.externalId}`));
1056
- console.log(chalk.dim(`Status: ${atom.status}`));
1057
- console.log(chalk.dim(`
1116
+ console.log(chalk2.dim(`Status: ${atom.status}`));
1117
+ console.log(chalk2.dim(`
1058
1118
  Next steps:`));
1059
- console.log(chalk.dim(` - Execute: archon execute ${atom.externalId}`));
1060
- console.log(chalk.dim(` - View: archon show ${atom.externalId}`));
1119
+ console.log(chalk2.dim(` - Execute: archon execute ${atom.externalId}`));
1120
+ console.log(chalk2.dim(` - View: archon show ${atom.externalId}`));
1121
+ printPlanNextActions(atom.externalId, true);
1061
1122
  } finally {
1062
- prompt.close();
1123
+ prompt2.close();
1124
+ }
1125
+ }
1126
+ function printPlanNextActions(atomExternalId, success) {
1127
+ console.log();
1128
+ console.log(chalk2.bold("Next best action:"));
1129
+ if (success) {
1130
+ console.log(chalk2.dim(` \u2022 Run ${chalk2.cyan(`archon execute ${atomExternalId}`)} to move this atom forward.`));
1131
+ } else {
1132
+ console.log(chalk2.dim(` \u2022 Run ${chalk2.cyan("archon credits add")} or switch to ${chalk2.cyan("BYOK")} to continue planning.`));
1063
1133
  }
1134
+ console.log(chalk2.dim(` \u2022 Run ${chalk2.cyan(`archon show ${atomExternalId}`)} to inspect acceptance criteria.`));
1135
+ console.log(chalk2.dim(` \u2022 Run ${chalk2.cyan("archon preferences")} to tune model routing/cost.`));
1064
1136
  }
1065
1137
  function parseAtomDescription(description, options, extractedCriteria = []) {
1066
1138
  const title = deriveTitle(description);
@@ -1130,32 +1202,32 @@ function extractNumberedSteps(description) {
1130
1202
  }
1131
1203
  return steps;
1132
1204
  }
1133
- async function promptForDesignApproval(prompt, input) {
1134
- console.log(chalk.blue("\nDesign Approval Gate"));
1135
- console.log(chalk.dim("\u2500".repeat(40)));
1136
- console.log(chalk.dim(`Task type: ${input.classification.kind} (${input.classification.confidence} confidence)`));
1137
- console.log(chalk.dim(`Title: ${deriveTitle(input.description)}`));
1205
+ async function promptForDesignApproval(prompt2, input) {
1206
+ console.log(chalk2.blue("\nDesign Approval Gate"));
1207
+ console.log(chalk2.dim("\u2500".repeat(40)));
1208
+ console.log(chalk2.dim(`Task type: ${input.classification.kind} (${input.classification.confidence} confidence)`));
1209
+ console.log(chalk2.dim(`Title: ${deriveTitle(input.description)}`));
1138
1210
  if (input.requirements.length > 0) {
1139
- console.log(chalk.dim("\nRequirements summary:"));
1211
+ console.log(chalk2.dim("\nRequirements summary:"));
1140
1212
  for (const req of input.requirements.slice(0, 6)) {
1141
- console.log(chalk.dim(` - ${req}`));
1213
+ console.log(chalk2.dim(` - ${req}`));
1142
1214
  }
1143
1215
  if (input.requirements.length > 6) {
1144
- console.log(chalk.dim(` - ... and ${input.requirements.length - 6} more`));
1216
+ console.log(chalk2.dim(` - ... and ${input.requirements.length - 6} more`));
1145
1217
  }
1146
1218
  }
1147
1219
  if (input.references.length > 0) {
1148
- console.log(chalk.dim("\nReferenced inputs:"));
1220
+ console.log(chalk2.dim("\nReferenced inputs:"));
1149
1221
  for (const ref of input.references.slice(0, 8)) {
1150
- console.log(chalk.dim(` - ${ref}`));
1222
+ console.log(chalk2.dim(` - ${ref}`));
1151
1223
  }
1152
1224
  }
1153
1225
  if (input.deliverableTarget) {
1154
- console.log(chalk.dim(`
1226
+ console.log(chalk2.dim(`
1155
1227
  Deliverable target: ${input.deliverableTarget}`));
1156
1228
  }
1157
- console.log(chalk.dim("\nI will generate a step-by-step implementation plan from this design."));
1158
- const approval = await prompt.ask("Approve design and proceed to planning? (Y/n): ");
1229
+ console.log(chalk2.dim("\nI will generate a step-by-step implementation plan from this design."));
1230
+ const approval = await prompt2.ask("Approve design and proceed to planning? (Y/n): ");
1159
1231
  return approval.trim().toLowerCase() !== "n";
1160
1232
  }
1161
1233
  function enforcePlanStructure(plan2, requirements) {
@@ -1337,17 +1409,17 @@ async function loadReferencedFileSummaries(references) {
1337
1409
  }
1338
1410
  return { foundFiles, missingFiles, fileSummaries };
1339
1411
  }
1340
- async function promptForDeliverableTarget(prompt, requirements, references) {
1412
+ async function promptForDeliverableTarget(prompt2, requirements, references) {
1341
1413
  const mentionsResearch = requirements.some((req) => req.toLowerCase().includes("research"));
1342
1414
  const defaultTarget = references.find((ref) => ref.toLowerCase().endsWith(".md"));
1343
1415
  if (!mentionsResearch && !defaultTarget) {
1344
1416
  return null;
1345
1417
  }
1346
- console.log(chalk.dim("\nWhere should the deliverables be written?"));
1418
+ console.log(chalk2.dim("\nWhere should the deliverables be written?"));
1347
1419
  if (defaultTarget) {
1348
- console.log(chalk.dim(`Press Enter to use ${defaultTarget}`));
1420
+ console.log(chalk2.dim(`Press Enter to use ${defaultTarget}`));
1349
1421
  }
1350
- const answer = await prompt.ask("Target file or folder (leave blank to skip): ");
1422
+ const answer = await prompt2.ask("Target file or folder (leave blank to skip): ");
1351
1423
  const trimmed = answer.trim();
1352
1424
  if (trimmed.length === 0) {
1353
1425
  return defaultTarget ?? null;
@@ -1366,29 +1438,29 @@ async function resolveProfileId(authId, accessToken) {
1366
1438
  }
1367
1439
  }
1368
1440
  function displayPlan(plan2) {
1369
- console.log(chalk.bold("\n\u{1F4DD} Implementation Plan"));
1370
- console.log(chalk.dim("\u2500".repeat(40)));
1371
- console.log(chalk.bold("\nSteps:"));
1441
+ console.log(chalk2.bold("\n\u{1F4DD} Implementation Plan"));
1442
+ console.log(chalk2.dim("\u2500".repeat(40)));
1443
+ console.log(chalk2.bold("\nSteps:"));
1372
1444
  plan2.steps.forEach((step, i) => {
1373
1445
  console.log(` ${i + 1}. ${step}`);
1374
1446
  });
1375
- console.log(chalk.bold("\nFiles to modify:"));
1447
+ console.log(chalk2.bold("\nFiles to modify:"));
1376
1448
  for (const file of plan2.files_to_modify) {
1377
1449
  console.log(` - ${file}`);
1378
1450
  }
1379
1451
  if (plan2.dependencies.length > 0) {
1380
- console.log(chalk.bold("\nDependencies:"));
1452
+ console.log(chalk2.bold("\nDependencies:"));
1381
1453
  for (const dep of plan2.dependencies) {
1382
1454
  console.log(` - ${dep}`);
1383
1455
  }
1384
1456
  }
1385
1457
  if (plan2.risks.length > 0) {
1386
- console.log(chalk.bold("\nRisks:"));
1458
+ console.log(chalk2.bold("\nRisks:"));
1387
1459
  for (const risk of plan2.risks) {
1388
- console.log(chalk.yellow(` [!] ${risk}`));
1460
+ console.log(chalk2.yellow(` [!] ${risk}`));
1389
1461
  }
1390
1462
  }
1391
- console.log(chalk.bold("\nComplexity:"), plan2.estimated_complexity);
1463
+ console.log(chalk2.bold("\nComplexity:"), plan2.estimated_complexity);
1392
1464
  }
1393
1465
  async function saveAtom(atom) {
1394
1466
  const atomsDir = join(process.cwd(), ATOMS_DIR);
@@ -1427,6 +1499,7 @@ async function listLocalAtoms() {
1427
1499
 
1428
1500
  export {
1429
1501
  UsageRecorder,
1502
+ handleInsufficientCreditsRecovery,
1430
1503
  plan,
1431
1504
  parseAtomDescription,
1432
1505
  loadAtom,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  listLocalAtoms
3
- } from "./chunk-IKMCIWK3.js";
3
+ } from "./chunk-OTODLPY4.js";
4
4
 
5
5
  // src/cli/list.ts
6
6
  import chalk from "chalk";
@@ -5,8 +5,9 @@ import {
5
5
  } from "./chunk-EBHHIUCB.js";
6
6
  import {
7
7
  UsageRecorder,
8
+ handleInsufficientCreditsRecovery,
8
9
  loadAtom
9
- } from "./chunk-IKMCIWK3.js";
10
+ } from "./chunk-OTODLPY4.js";
10
11
  import {
11
12
  transitionAtom
12
13
  } from "./chunk-PCTP3LKJ.js";
@@ -20,6 +21,9 @@ import {
20
21
  import {
21
22
  ArchitectureParser
22
23
  } from "./chunk-5EVHUDQX.js";
24
+ import {
25
+ KeyManager
26
+ } from "./chunk-RDG5BUED.js";
23
27
  import {
24
28
  SUPABASE_ANON_KEY,
25
29
  SUPABASE_URL
@@ -4793,7 +4797,7 @@ function createPrompt() {
4793
4797
  }
4794
4798
  async function execute(atomId, options) {
4795
4799
  if (options.parallel && options.parallel.length > 0) {
4796
- const { parallelExecute } = await import("./parallel-QLKYSZZ3.js");
4800
+ const { parallelExecute } = await import("./parallel-KRBYFJ4O.js");
4797
4801
  const allAtomIds = [atomId, ...options.parallel];
4798
4802
  await parallelExecute(allAtomIds, { skipGates: options.skipGates === true });
4799
4803
  return;
@@ -4808,7 +4812,12 @@ async function execute(atomId, options) {
4808
4812
  console.log(chalk2.red("\n\u274C Cloud execution requires Credits tier"));
4809
4813
  console.log(chalk2.dim("BYOK and Free tiers run locally only."));
4810
4814
  console.log(chalk2.dim("Upgrade with: archon upgrade"));
4811
- process.exit(1);
4815
+ const upgradeNow = await prompt.ask("Switch tier now? (y/N): ");
4816
+ if (upgradeNow.trim().toLowerCase() === "y") {
4817
+ const { showUpgradeMenu } = await import("./tier-selection-XFBM4SZ4.js");
4818
+ await showUpgradeMenu();
4819
+ }
4820
+ return;
4812
4821
  }
4813
4822
  const projectName = basename(cwd);
4814
4823
  console.log(chalk2.dim(`Queueing cloud execution for ${atomId}...`));
@@ -4967,16 +4976,36 @@ ${conflictReport.blockerCount} blocking conflict(s) found.`));
4967
4976
  }
4968
4977
  const roleOverrides = await loadRoleOverrides(cwd);
4969
4978
  const executorConfig = roleOverrides?.executor?.model ? { model: roleOverrides.executor.model } : void 0;
4970
- const executor = billingContext ? new TrackedExecutorAgent({ billing: billingContext, clientConfig: executorConfig }) : new ExecutorAgent(executorConfig);
4979
+ let apiKey = process.env["ANTHROPIC_API_KEY"];
4980
+ if (!apiKey) {
4981
+ const keyManager = new KeyManager();
4982
+ const storedKey = await keyManager.getKey("anthropic");
4983
+ if (storedKey) {
4984
+ apiKey = storedKey;
4985
+ }
4986
+ }
4987
+ const executor = billingContext ? new TrackedExecutorAgent({ billing: billingContext, clientConfig: executorConfig, apiKey }) : new ExecutorAgent(executorConfig, apiKey);
4971
4988
  if (billingContext && executor instanceof TrackedExecutorAgent) {
4972
- const balanceCheck = await executor.checkBalance();
4989
+ let balanceCheck = await executor.checkBalance();
4990
+ if (balanceCheck.tier === "CREDITS") {
4991
+ console.log(chalk2.dim(`Estimated execution cost: $${(balanceCheck.estimatedCostCents / 100).toFixed(2)} | Balance: $${(balanceCheck.balance / 100).toFixed(2)}`));
4992
+ }
4973
4993
  if (!balanceCheck.sufficient && balanceCheck.tier === "CREDITS") {
4974
- console.log(chalk2.red("\n\u274C Insufficient credits"));
4975
- console.log(chalk2.dim(`Your balance: $${(balanceCheck.balance / 100).toFixed(2)}`));
4976
- console.log(chalk2.dim(`Estimated cost: $${(balanceCheck.estimatedCostCents / 100).toFixed(2)}`));
4977
- console.log(chalk2.dim("\nAdd credits with: archon credits add"));
4978
- console.log(chalk2.dim("Or switch to BYOK: archon keys add anthropic"));
4979
- process.exit(1);
4994
+ const recovery = await handleInsufficientCreditsRecovery({
4995
+ balanceCents: balanceCheck.balance,
4996
+ estimatedCostCents: balanceCheck.estimatedCostCents,
4997
+ operationLabel: "execution"
4998
+ });
4999
+ if (recovery === "abort") {
5000
+ return;
5001
+ }
5002
+ balanceCheck = await executor.checkBalance();
5003
+ if (!balanceCheck.sufficient && balanceCheck.tier === "CREDITS") {
5004
+ console.log(chalk2.red("\nStill insufficient credits after recovery action."));
5005
+ console.log(chalk2.dim("Please add more credits with: archon credits add"));
5006
+ printExecuteNextActions(atom.externalId, false);
5007
+ return;
5008
+ }
4980
5009
  }
4981
5010
  }
4982
5011
  const executionResult = await executor.executeAtom(atom, atom.plan, parseResult.schema, cwd);
@@ -5104,10 +5133,22 @@ Running quality gates for ${targetEnvName}...`));
5104
5133
  console.log(chalk2.dim(`Atom: ${atom.externalId}`));
5105
5134
  console.log(chalk2.dim(`Status: ${atom.status}`));
5106
5135
  console.log(chalk2.dim(`Files changed: ${filesChanged.length}`));
5136
+ printExecuteNextActions(atom.externalId, true);
5107
5137
  } finally {
5108
5138
  prompt.close();
5109
5139
  }
5110
5140
  }
5141
+ function printExecuteNextActions(atomExternalId, success) {
5142
+ console.log();
5143
+ console.log(chalk2.bold("Next best action:"));
5144
+ if (success) {
5145
+ console.log(chalk2.dim(` \u2022 Run ${chalk2.cyan("archon list")} to select your next atom.`));
5146
+ } else {
5147
+ console.log(chalk2.dim(` \u2022 Run ${chalk2.cyan(`archon show ${atomExternalId}`)} to inspect this atom.`));
5148
+ }
5149
+ console.log(chalk2.dim(` \u2022 Run ${chalk2.cyan("archon credits")} to verify balance/tier.`));
5150
+ console.log(chalk2.dim(` \u2022 Run ${chalk2.cyan("archon preferences")} to tune model routing.`));
5151
+ }
5111
5152
  async function saveAtom(atom) {
5112
5153
  const atomsDir = join4(process.cwd(), ATOMS_DIR);
5113
5154
  const atomFile = join4(atomsDir, `${atom.externalId}.json`);