lynxprompt 0.5.0 → 0.5.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.
package/dist/index.js CHANGED
@@ -3322,13 +3322,14 @@ function generateYamlConfig(options, platform2) {
3322
3322
 
3323
3323
  // src/commands/wizard.ts
3324
3324
  var DRAFTS_DIR = ".lynxprompt/drafts";
3325
- async function saveDraftLocally(name, config2) {
3325
+ async function saveDraftLocally(name, config2, stepReached) {
3326
3326
  const draftsPath = join6(process.cwd(), DRAFTS_DIR);
3327
3327
  await mkdir4(draftsPath, { recursive: true });
3328
3328
  const draft = {
3329
3329
  name,
3330
3330
  savedAt: (/* @__PURE__ */ new Date()).toISOString(),
3331
- config: config2
3331
+ config: config2,
3332
+ stepReached
3332
3333
  };
3333
3334
  const filename = `${name.replace(/[^a-zA-Z0-9-_]/g, "_")}.json`;
3334
3335
  await writeFile4(join6(draftsPath, filename), JSON.stringify(draft, null, 2), "utf-8");
@@ -4076,8 +4077,9 @@ async function saveDraftOnExit() {
4076
4077
  const draftName = `autosave-${timestamp}`;
4077
4078
  console.log();
4078
4079
  console.log(chalk8.yellow(` \u{1F4BE} Saving wizard state to draft: ${draftName}...`));
4079
- await saveDraftLocally(draftName, wizardState.answers);
4080
+ await saveDraftLocally(draftName, wizardState.answers, wizardState.stepReached);
4080
4081
  console.log(chalk8.green(` \u2713 Draft saved! Resume with: lynxp wizard --load-draft ${draftName}`));
4082
+ console.log(chalk8.gray(` Saved at step ${wizardState.stepReached}`));
4081
4083
  console.log();
4082
4084
  } catch (err) {
4083
4085
  console.log(chalk8.red(` \u2717 Could not save draft: ${err instanceof Error ? err.message : "unknown error"}`));
@@ -4103,9 +4105,13 @@ async function wizardCommand(options) {
4103
4105
  if (options.loadDraft) {
4104
4106
  const draft = await loadDraftLocally(options.loadDraft);
4105
4107
  if (draft) {
4106
- console.log(chalk8.green(` \u2713 Loaded draft: ${draft.name} (saved ${new Date(draft.savedAt).toLocaleString()})`));
4108
+ const stepInfo = draft.stepReached ? ` at step ${draft.stepReached}` : "";
4109
+ console.log(chalk8.green(` \u2713 Loaded draft: ${draft.name} (saved ${new Date(draft.savedAt).toLocaleString()}${stepInfo})`));
4107
4110
  console.log();
4108
- Object.assign(options, draft.config);
4111
+ options._draftAnswers = draft.config;
4112
+ options._resumeFromStep = draft.stepReached;
4113
+ if (draft.config.name) options.name = draft.config.name;
4114
+ if (draft.config.description) options.description = draft.config.description;
4109
4115
  } else {
4110
4116
  const availableDrafts = await listLocalDrafts();
4111
4117
  console.log(chalk8.red(` \u2717 Draft "${options.loadDraft}" not found.`));
@@ -4358,43 +4364,72 @@ async function wizardCommand(options) {
4358
4364
  initial: true
4359
4365
  });
4360
4366
  if (savePrefsResponse.savePrefs) {
4361
- const saveSpinner = ora7("Saving preferences to your profile...").start();
4362
- try {
4363
- await api.saveWizardPreferences({
4364
- commands: config2.commands,
4365
- codeStyle: {
4366
- naming: config2.namingConvention,
4367
- errorHandling: config2.errorHandling,
4368
- loggingConventions: config2.loggingConventions,
4369
- loggingConventionsOther: config2.loggingConventionsOther,
4370
- notes: config2.styleNotes
4371
- },
4372
- boundaries: {
4373
- always: config2.boundaryAlways,
4374
- never: config2.boundaryNever,
4375
- ask: config2.boundaryAsk
4376
- },
4377
- testing: {
4378
- levels: config2.testLevels,
4379
- frameworks: config2.testFrameworks,
4380
- coverage: config2.coverageTarget,
4381
- notes: config2.testNotes
4382
- }
4383
- });
4384
- saveSpinner.succeed("Preferences saved to your profile");
4385
- } catch (err) {
4386
- saveSpinner.fail("Could not save preferences (you can still use the generated files)");
4387
- if (err instanceof ApiRequestError) {
4388
- if (err.statusCode === 401) {
4389
- console.log(chalk8.yellow(" Your session may have expired. Try: lynxp login"));
4390
- } else {
4391
- console.log(chalk8.gray(` ${err.message} (status: ${err.statusCode})`));
4367
+ let saved = false;
4368
+ let attempts = 0;
4369
+ const maxAttempts = 3;
4370
+ while (!saved && attempts < maxAttempts) {
4371
+ attempts++;
4372
+ const saveSpinner = ora7("Saving preferences to your profile...").start();
4373
+ try {
4374
+ await api.saveWizardPreferences({
4375
+ commands: config2.commands,
4376
+ codeStyle: {
4377
+ naming: config2.namingConvention,
4378
+ errorHandling: config2.errorHandling,
4379
+ loggingConventions: config2.loggingConventions,
4380
+ loggingConventionsOther: config2.loggingConventionsOther,
4381
+ notes: config2.styleNotes
4382
+ },
4383
+ boundaries: {
4384
+ always: config2.boundaryAlways,
4385
+ never: config2.boundaryNever,
4386
+ ask: config2.boundaryAsk
4387
+ },
4388
+ testing: {
4389
+ levels: config2.testLevels,
4390
+ frameworks: config2.testFrameworks,
4391
+ coverage: config2.coverageTarget,
4392
+ notes: config2.testNotes
4393
+ }
4394
+ });
4395
+ saveSpinner.succeed("Preferences saved to your profile");
4396
+ saved = true;
4397
+ } catch (err) {
4398
+ saveSpinner.fail("Could not save preferences");
4399
+ let errorType = "unknown";
4400
+ if (err instanceof ApiRequestError) {
4401
+ if (err.statusCode === 401) {
4402
+ console.log(chalk8.yellow(" Your session may have expired. Try: lynxp login"));
4403
+ break;
4404
+ } else {
4405
+ console.log(chalk8.gray(` ${err.message} (status: ${err.statusCode})`));
4406
+ errorType = "api";
4407
+ }
4408
+ } else if (err instanceof Error) {
4409
+ if (err.message.includes("fetch failed") || err.message.includes("ENOTFOUND")) {
4410
+ console.log(chalk8.yellow(" Network error. Check your internet connection."));
4411
+ errorType = "network";
4412
+ } else {
4413
+ console.log(chalk8.gray(` ${err.message}`));
4414
+ }
4392
4415
  }
4393
- } else if (err instanceof Error) {
4394
- if (err.message.includes("fetch failed") || err.message.includes("ENOTFOUND")) {
4395
- console.log(chalk8.yellow(" Network error. Check your internet connection."));
4416
+ if (errorType === "network" || errorType === "api") {
4417
+ if (attempts < maxAttempts) {
4418
+ const retryResponse = await prompts4({
4419
+ type: "confirm",
4420
+ name: "retry",
4421
+ message: chalk8.white("Would you like to retry?"),
4422
+ initial: true
4423
+ });
4424
+ if (!retryResponse.retry) {
4425
+ console.log(chalk8.gray(" Skipping preference save. Your config files are still generated."));
4426
+ break;
4427
+ }
4428
+ } else {
4429
+ console.log(chalk8.gray(` Max retries (${maxAttempts}) reached. Your config files are still generated.`));
4430
+ }
4396
4431
  } else {
4397
- console.log(chalk8.gray(` ${err.message}`));
4432
+ break;
4398
4433
  }
4399
4434
  }
4400
4435
  }
@@ -4411,12 +4446,23 @@ async function wizardCommand(options) {
4411
4446
  }
4412
4447
  }
4413
4448
  async function runInteractiveWizard(options, detected, userTier) {
4414
- const answers = {};
4449
+ const answers = options._draftAnswers ? { ...options._draftAnswers } : {};
4450
+ const resumeFromStep = options._resumeFromStep || 0;
4415
4451
  const availableSteps = getAvailableSteps(userTier);
4416
4452
  let currentStepNum = 0;
4417
4453
  wizardState.inProgress = true;
4418
4454
  wizardState.answers = answers;
4419
- wizardState.stepReached = 0;
4455
+ wizardState.stepReached = resumeFromStep;
4456
+ if (resumeFromStep > 0 && Object.keys(answers).length > 0) {
4457
+ console.log(chalk8.cyan(` \u{1F4CB} Resuming from step ${resumeFromStep}...`));
4458
+ console.log(chalk8.gray(" Previously saved answers:"));
4459
+ if (answers.name) console.log(chalk8.gray(` \u2022 Name: ${answers.name}`));
4460
+ if (answers.platforms) console.log(chalk8.gray(` \u2022 Platforms: ${answers.platforms.join(", ")}`));
4461
+ if (answers.stack) console.log(chalk8.gray(` \u2022 Stack: ${answers.stack.slice(0, 5).join(", ")}${answers.stack.length > 5 ? "..." : ""}`));
4462
+ console.log();
4463
+ console.log(chalk8.yellow(" Steps 1-" + (resumeFromStep - 1) + " will use saved values. Continuing from step " + resumeFromStep + "."));
4464
+ console.log();
4465
+ }
4420
4466
  const getCurrentStep = (stepId) => {
4421
4467
  const step = availableSteps.find((s) => s.id === stepId);
4422
4468
  if (step) {
@@ -4426,13 +4472,20 @@ async function runInteractiveWizard(options, detected, userTier) {
4426
4472
  }
4427
4473
  return null;
4428
4474
  };
4475
+ const shouldSkipStep = (stepNum) => {
4476
+ return resumeFromStep > 0 && stepNum < resumeFromStep;
4477
+ };
4429
4478
  const formatStep = getCurrentStep("format");
4430
- showStep(currentStepNum, formatStep, userTier);
4431
4479
  let platforms;
4432
- if (options.format) {
4480
+ if (shouldSkipStep(currentStepNum) && answers.platforms) {
4481
+ platforms = answers.platforms;
4482
+ console.log(chalk8.gray(` Step 1 (Output Format): Using saved platforms: ${platforms.join(", ")}`));
4483
+ } else if (options.format) {
4484
+ showStep(currentStepNum, formatStep, userTier);
4433
4485
  platforms = options.format.split(",").map((f) => f.trim());
4434
4486
  console.log(chalk8.gray(` Using format from flag: ${platforms.join(", ")}`));
4435
4487
  } else {
4488
+ showStep(currentStepNum, formatStep, userTier);
4436
4489
  console.log(chalk8.gray(" Select the AI editors you want to generate config for:"));
4437
4490
  console.log(chalk8.gray(" (AGENTS.md is recommended - works with most AI tools)"));
4438
4491
  console.log(chalk8.gray(" Type to search/filter the list."));
@@ -4518,6 +4571,15 @@ async function runInteractiveWizard(options, detected, userTier) {
4518
4571
  initial: 0
4519
4572
  }, promptConfig);
4520
4573
  answers.architecture = archResponse.architecture || "";
4574
+ if (answers.architecture === "other") {
4575
+ const customArchResponse = await prompts4({
4576
+ type: "text",
4577
+ name: "customArchitecture",
4578
+ message: chalk8.white("Describe your architecture pattern:"),
4579
+ hint: chalk8.gray("e.g., CQRS, Hexagonal, Clean Architecture")
4580
+ }, promptConfig);
4581
+ answers.architectureOther = customArchResponse.customArchitecture || "";
4582
+ }
4521
4583
  console.log();
4522
4584
  console.log(chalk8.yellow(" \u{1F9E9} Blueprint Template Mode"));
4523
4585
  console.log(chalk8.gray(" Create a reusable template with [[VARIABLE|default]] placeholders"));
@@ -4860,10 +4922,19 @@ async function runInteractiveWizard(options, detected, userTier) {
4860
4922
  initial: 0
4861
4923
  }, promptConfig);
4862
4924
  answers.errorHandling = errorResponse.errorHandling || "";
4925
+ if (answers.errorHandling === "other") {
4926
+ const customErrorResponse = await prompts4({
4927
+ type: "text",
4928
+ name: "customErrorHandling",
4929
+ message: chalk8.white("Describe your error handling approach:"),
4930
+ hint: chalk8.gray("e.g., Railway-oriented, custom error boundaries")
4931
+ }, promptConfig);
4932
+ answers.errorHandlingOther = customErrorResponse.customErrorHandling || "";
4933
+ }
4863
4934
  const loggingResponse = await prompts4({
4864
- type: "select",
4935
+ type: "autocomplete",
4865
4936
  name: "loggingConventions",
4866
- message: chalk8.white("Logging conventions:"),
4937
+ message: chalk8.white("Logging conventions (type to search):"),
4867
4938
  choices: [
4868
4939
  { title: chalk8.gray("\u23ED Skip"), value: "" },
4869
4940
  ...LOGGING_OPTIONS.map((l) => ({
@@ -4893,14 +4964,6 @@ async function runInteractiveWizard(options, detected, userTier) {
4893
4964
  }
4894
4965
  const aiStep = getCurrentStep("ai");
4895
4966
  showStep(currentStepNum, aiStep, userTier);
4896
- console.log(chalk8.gray(" Select which behaviors AI should follow:"));
4897
- console.log();
4898
- for (const rule of AI_BEHAVIOR_RULES) {
4899
- const recBadge = rule.recommended ? chalk8.green(" \u2605 recommended") : "";
4900
- console.log(chalk8.cyan(` \u2022 ${rule.label}${recBadge}`));
4901
- console.log(chalk8.gray(` ${rule.description}`));
4902
- }
4903
- console.log();
4904
4967
  const aiBehaviorResponse = await prompts4({
4905
4968
  type: "autocompleteMultiselect",
4906
4969
  name: "aiBehavior",
@@ -4915,6 +4978,13 @@ async function runInteractiveWizard(options, detected, userTier) {
4915
4978
  instructions: false
4916
4979
  }, promptConfig);
4917
4980
  answers.aiBehavior = aiBehaviorResponse.aiBehavior || [];
4981
+ if (answers.aiBehavior.length > 0) {
4982
+ console.log(chalk8.green(" \u2713 Selected:"));
4983
+ for (const ruleId of answers.aiBehavior) {
4984
+ const rule = AI_BEHAVIOR_RULES.find((r) => r.id === ruleId);
4985
+ if (rule) console.log(chalk8.cyan(` \u2022 ${rule.label}`));
4986
+ }
4987
+ }
4918
4988
  const importantFilesResponse = await prompts4({
4919
4989
  type: "autocompleteMultiselect",
4920
4990
  name: "importantFiles",
@@ -4940,7 +5010,7 @@ async function runInteractiveWizard(options, detected, userTier) {
4940
5010
  const includePersonalResponse = await prompts4({
4941
5011
  type: "toggle",
4942
5012
  name: "includePersonalData",
4943
- message: chalk8.white("Include personal data (name/email for commits)?"),
5013
+ message: chalk8.white("Include personal data (as saved in WebUI)?"),
4944
5014
  initial: false,
4945
5015
  active: "Yes",
4946
5016
  inactive: "No"
@@ -4949,24 +5019,24 @@ async function runInteractiveWizard(options, detected, userTier) {
4949
5019
  if (canAccessTier(userTier, "advanced")) {
4950
5020
  const boundariesStep = getCurrentStep("boundaries");
4951
5021
  showStep(currentStepNum, boundariesStep, userTier);
4952
- console.log(chalk8.gray(" Define what AI should always do, ask first, or never do."));
5022
+ console.log(chalk8.gray(" Define what AI should never do, ask first, or always do."));
4953
5023
  console.log(chalk8.gray(" Each option can only be in one category."));
4954
5024
  console.log();
4955
5025
  const usedOptions = /* @__PURE__ */ new Set();
4956
- console.log(chalk8.green.bold(" \u2713 ALWAYS ALLOW - AI will do these automatically"));
4957
- const alwaysResponse = await prompts4({
5026
+ console.log(chalk8.red.bold(" \u2717 NEVER ALLOW - AI will refuse to do"));
5027
+ const neverResponse = await prompts4({
4958
5028
  type: "autocompleteMultiselect",
4959
- name: "always",
4960
- message: chalk8.white("Always allow (type to filter):"),
5029
+ name: "never",
5030
+ message: chalk8.white("Never allow (type to filter):"),
4961
5031
  choices: BOUNDARY_OPTIONS.map((o) => ({
4962
- title: chalk8.green(o),
5032
+ title: chalk8.red(o),
4963
5033
  value: o
4964
5034
  })),
4965
5035
  hint: chalk8.gray("space select \u2022 enter confirm"),
4966
5036
  instructions: false
4967
5037
  }, promptConfig);
4968
- answers.boundaryAlways = alwaysResponse.always || [];
4969
- answers.boundaryAlways.forEach((o) => usedOptions.add(o));
5038
+ answers.boundaryNever = neverResponse.never || [];
5039
+ answers.boundaryNever.forEach((o) => usedOptions.add(o));
4970
5040
  console.log();
4971
5041
  console.log(chalk8.yellow.bold(" ? ASK FIRST - AI will ask before doing"));
4972
5042
  const availableForAsk = BOUNDARY_OPTIONS.filter((o) => !usedOptions.has(o));
@@ -4984,20 +5054,20 @@ async function runInteractiveWizard(options, detected, userTier) {
4984
5054
  answers.boundaryAsk = askResponse.ask || [];
4985
5055
  answers.boundaryAsk.forEach((o) => usedOptions.add(o));
4986
5056
  console.log();
4987
- console.log(chalk8.red.bold(" \u2717 NEVER ALLOW - AI will refuse to do"));
4988
- const availableForNever = BOUNDARY_OPTIONS.filter((o) => !usedOptions.has(o));
4989
- const neverResponse = await prompts4({
5057
+ console.log(chalk8.green.bold(" \u2713 ALWAYS ALLOW - AI will do these automatically"));
5058
+ const availableForAlways = BOUNDARY_OPTIONS.filter((o) => !usedOptions.has(o));
5059
+ const alwaysResponse = await prompts4({
4990
5060
  type: "autocompleteMultiselect",
4991
- name: "never",
4992
- message: chalk8.white("Never allow (type to filter):"),
4993
- choices: availableForNever.map((o) => ({
4994
- title: chalk8.red(o),
5061
+ name: "always",
5062
+ message: chalk8.white("Always allow (type to filter):"),
5063
+ choices: availableForAlways.map((o) => ({
5064
+ title: chalk8.green(o),
4995
5065
  value: o
4996
5066
  })),
4997
5067
  hint: chalk8.gray("space select \u2022 enter confirm"),
4998
5068
  instructions: false
4999
5069
  }, promptConfig);
5000
- answers.boundaryNever = neverResponse.never || [];
5070
+ answers.boundaryAlways = alwaysResponse.always || [];
5001
5071
  console.log();
5002
5072
  console.log(chalk8.gray(" Boundary summary:"));
5003
5073
  if (answers.boundaryAlways.length > 0) {