maistro 1.2.12 → 1.2.17

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/app.d.ts CHANGED
@@ -69,6 +69,11 @@ export declare class MaistroApp {
69
69
  * Uses full-screen approach for each step
70
70
  */
71
71
  private handleDiscovery;
72
+ /**
73
+ * Prompt user for each required secret one at a time.
74
+ * Returns a map of envVar -> value for secrets the user provided, or null if cancelled.
75
+ */
76
+ private promptForSecrets;
72
77
  /**
73
78
  * Handle discovery phase for planning requests (no skip option)
74
79
  * This is used when the user submits a planning request to refine the plan.
package/dist/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAiOA,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,cAAc,CAA+B;gBAEzC,WAAW,GAAE,MAAY;IAsBrC,OAAO,CAAC,eAAe;IASvB;;;OAGG;YACW,qBAAqB;IA+CnC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4G5B;;;OAGG;YACW,iBAAiB;IAiE/B;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;;OAGG;IACG,YAAY,CAAC,SAAS,UAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAqFtD;;;OAGG;YACW,iBAAiB;IAwD/B;;OAEG;YACW,sBAAsB;IAsCpC;;;OAGG;YACW,iBAAiB;IAoB/B;;;OAGG;YACW,oBAAoB;IAkClC;;OAEG;YACW,qBAAqB;IA4BnC;;OAEG;YACW,gBAAgB;IAoB9B;;;OAGG;YACW,qBAAqB;IAoEnC;;;OAGG;YACW,eAAe;IAyH7B;;;OAGG;YACW,uBAAuB;IAyErC;;OAEG;YACW,4BAA4B;IAiE1C;;OAEG;YACW,oBAAoB;IAgElC;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IA2J7B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,gBAAgB,CAAK;IAE7B;;;OAGG;YACW,YAAY;IAqT1B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA2CxB;;OAEG;YACW,aAAa;IAsmB3B;;;OAGG;YACW,iBAAiB;IAgI/B;;OAEG;YACW,cAAc;IAwU5B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,aAAa,CAAS;IAE9B;;OAEG;YACW,gBAAgB;IAkB9B;;OAEG;YACW,gBAAgB;IAe9B;;;OAGG;YACW,WAAW;IAsEzB;;;OAGG;YACW,mBAAmB;IA8BjC;;;OAGG;YACW,cAAc;IAkB5B;;;;OAIG;YACW,6BAA6B;IAqI3C;;;OAGG;YACW,aAAa;IA+B3B;;;;OAIG;YACW,sBAAsB;IA6MpC;;;;OAIG;YACW,sBAAsB;YA0GtB,UAAU;IA4HxB,OAAO,CAAC,YAAY;IAmFpB;;;OAGG;YACW,eAAe;IAqD7B;;OAEG;YACW,cAAc;IAsO5B;;OAEG;YACW,oBAAoB;IAwBlC;;OAEG;YACW,eAAe;IAU7B;;OAEG;YACW,wBAAwB;YAsBxB,gBAAgB;IA4B9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsD;IAEpF;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAoB3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAQ/B;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,sBAAsB;YAoEhB,SAAS;YAiIT,sBAAsB;CAkcrC"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAmOA,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,cAAc,CAA+B;gBAEzC,WAAW,GAAE,MAAY;IAsBrC,OAAO,CAAC,eAAe;IASvB;;;OAGG;YACW,qBAAqB;IA+CnC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkH5B;;;OAGG;YACW,iBAAiB;IAiE/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAUvB;;;OAGG;IACG,YAAY,CAAC,SAAS,UAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAqFtD;;;OAGG;YACW,iBAAiB;IAwD/B;;OAEG;YACW,sBAAsB;IAsCpC;;;OAGG;YACW,iBAAiB;IAoB/B;;;OAGG;YACW,oBAAoB;IAkClC;;OAEG;YACW,qBAAqB;IA4BnC;;OAEG;YACW,gBAAgB;IAoB9B;;;OAGG;YACW,qBAAqB;IAoEnC;;;OAGG;YACW,eAAe;IAwJ7B;;;OAGG;YACW,gBAAgB;IAiD9B;;;OAGG;YACW,uBAAuB;IAyErC;;OAEG;YACW,4BAA4B;IAkE1C;;OAEG;YACW,oBAAoB;IAiElC;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IA2J7B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,gBAAgB,CAAK;IAE7B;;;OAGG;YACW,YAAY;IAyT1B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA2CxB;;OAEG;YACW,aAAa;IAsmB3B;;;OAGG;YACW,iBAAiB;IAgI/B;;OAEG;YACW,cAAc;IAwU5B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,aAAa,CAAS;IAE9B;;OAEG;YACW,gBAAgB;IAkB9B;;OAEG;YACW,gBAAgB;IAe9B;;;OAGG;YACW,WAAW;IAsEzB;;;OAGG;YACW,mBAAmB;IA8BjC;;;OAGG;YACW,cAAc;IAkB5B;;;;OAIG;YACW,6BAA6B;IAqI3C;;;OAGG;YACW,aAAa;IA+B3B;;;;OAIG;YACW,sBAAsB;IA8MpC;;;;OAIG;YACW,sBAAsB;YA2GtB,UAAU;IA8HxB,OAAO,CAAC,YAAY;IAmFpB;;;OAGG;YACW,eAAe;IAqD7B;;OAEG;YACW,cAAc;IAsO5B;;OAEG;YACW,oBAAoB;IAwBlC;;OAEG;YACW,eAAe;IAU7B;;OAEG;YACW,wBAAwB;YAsBxB,gBAAgB;IA4B9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsD;IAEpF;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAqB3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAQ/B;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,sBAAsB;YAoEhB,SAAS;YAiIT,sBAAsB;CAkcrC"}
package/dist/app.js CHANGED
@@ -6,6 +6,7 @@ import { exec } from 'node:child_process';
6
6
  import { Orchestrator } from './orchestrator.js';
7
7
  import { InteractiveUI, ExitRequestedError, formatTaskStatus, formatTaskStatusIcon, formatStatusName, formatBlockedStatus, progressBar } from './ui.js';
8
8
  import { planTasks, generateReadme, discoverRequirements, buildProjectContext, formatContextForDecomposition } from './planner.js';
9
+ import { readExistingEnvVars, writeSecretsToEnvFile } from './envFile.js';
9
10
  import { isTaskBlocked } from './taskQueue.js';
10
11
  import { isClaudeCodeInstalled, isClaudeCodeAuthenticated, testClaudeInteraction, launchClaudeLogin, loadConfig, saveConfig, } from './config.js';
11
12
  import { initLogger, getLogger } from './logger.js';
@@ -18,6 +19,7 @@ import { getPreventSleep, setPreventSleep, getConfigPath, detectTerminal, getShi
18
19
  import { hasClipboardImage, saveClipboardImage, cleanupTempImage } from './clipboard.js';
19
20
  import { isGitRepo } from './git.js';
20
21
  import { UI, TipsManager } from './constants.js';
22
+ import { setStatusLine, setStatusState, clearStatusLine } from './statusLine.js';
21
23
  import { getUpdateNotice, checkForUpdates, performUpdate, isAutoUpdatePromptEnabled, isAutoUpdateEnabled, } from './versionCheck.js';
22
24
  /**
23
25
  * Format an error for display with limited stack trace
@@ -222,6 +224,8 @@ export class MaistroApp {
222
224
  this.ui.init();
223
225
  // Simple startup check (no header yet - showPlanView will show full UI)
224
226
  console.log(`\n\x1b[90mStarting maistro v${VERSION}\x1b[0m`);
227
+ // Set initial terminal status line
228
+ setStatusLine('idle', this.projectPath);
225
229
  // Check for updates (skip if --no-update-check flag or MAISTRO_SKIP_UPDATE_CHECK env)
226
230
  if (!process.env.MAISTRO_SKIP_UPDATE_CHECK) {
227
231
  await this.handleUpdateCheck();
@@ -233,6 +237,7 @@ export class MaistroApp {
233
237
  if (!configured) {
234
238
  getLogger()?.info('Exit: not configured');
235
239
  getLogger()?.session('end');
240
+ clearStatusLine();
236
241
  this.ui.print('\n\x1b[90mGoodbye!\x1b[0m\n');
237
242
  this.ui.close();
238
243
  return;
@@ -303,6 +308,7 @@ export class MaistroApp {
303
308
  if (err instanceof ExitRequestedError) {
304
309
  getLogger()?.info('Exit: user requested via double-escape');
305
310
  getLogger()?.session('end');
311
+ clearStatusLine();
306
312
  this.ui.close();
307
313
  process.exit(0); // Exit immediately
308
314
  }
@@ -310,6 +316,7 @@ export class MaistroApp {
310
316
  }
311
317
  }
312
318
  getLogger()?.session('end');
319
+ clearStatusLine();
313
320
  this.ui.close();
314
321
  }
315
322
  /**
@@ -375,6 +382,7 @@ export class MaistroApp {
375
382
  exitAfterUpdate() {
376
383
  console.log(`\x1b[32m✓ Updated successfully!\x1b[0m`);
377
384
  console.log(`\x1b[90mPlease start maistro again to launch the new version.\x1b[0m\n`);
385
+ clearStatusLine();
378
386
  this.ui.disableRawMode();
379
387
  this.ui.close();
380
388
  process.exit(0);
@@ -709,6 +717,7 @@ export class MaistroApp {
709
717
  return null; // Skip discovery
710
718
  }
711
719
  // Show analyzing screen with spinner (with escape-to-exit support)
720
+ setStatusState('working');
712
721
  this.ui.clear();
713
722
  this.showHeader();
714
723
  console.log('\x1b[1m── Analyzing Project ──\x1b[0m\n');
@@ -760,6 +769,16 @@ export class MaistroApp {
760
769
  answers[question.id] = answer;
761
770
  getLogger()?.info('Discovery answer', { questionId: question.id, question: question.question, answer });
762
771
  }
772
+ // Prompt for required secrets if any were detected
773
+ const collectedSecrets = {};
774
+ if (result.requiredSecrets && result.requiredSecrets.length > 0) {
775
+ const secretResults = await this.promptForSecrets(result.requiredSecrets);
776
+ if (secretResults === null) {
777
+ // User cancelled
778
+ return null;
779
+ }
780
+ Object.assign(collectedSecrets, secretResults);
781
+ }
763
782
  // Show confirmation screen (full-screen)
764
783
  this.ui.clear();
765
784
  this.showHeader();
@@ -771,6 +790,19 @@ export class MaistroApp {
771
790
  const label = question ? question.id.replace(/-/g, ' ').replace(/^\w/, c => c.toUpperCase()) : id;
772
791
  console.log(` \x1b[36m${label}:\x1b[0m ${value}`);
773
792
  }
793
+ // Show secrets status summary
794
+ if (result.requiredSecrets && result.requiredSecrets.length > 0) {
795
+ console.log('\n\x1b[90mSecrets/Credentials:\x1b[0m');
796
+ for (const secret of result.requiredSecrets) {
797
+ const provided = collectedSecrets[secret.envVar];
798
+ if (provided) {
799
+ console.log(` \x1b[32m✓\x1b[0m \x1b[33m${secret.envVar}\x1b[0m — provided`);
800
+ }
801
+ else {
802
+ console.log(` \x1b[90m○\x1b[0m \x1b[33m${secret.envVar}\x1b[0m — skipped`);
803
+ }
804
+ }
805
+ }
774
806
  console.log('');
775
807
  const confirmChoice = await this.ui.quickPick([
776
808
  { key: 'y', label: 'Continue', description: 'Create plan with these preferences' },
@@ -785,15 +817,66 @@ export class MaistroApp {
785
817
  if (confirmChoice === null) {
786
818
  return null;
787
819
  }
788
- getLogger()?.info('Discovery completed', { summary: result.summary, answers });
820
+ // Write collected secrets to .env.local
821
+ if (Object.keys(collectedSecrets).length > 0) {
822
+ writeSecretsToEnvFile(this.projectPath, collectedSecrets);
823
+ }
824
+ getLogger()?.info('Discovery completed', { summary: result.summary, answers, requiredSecrets: result.requiredSecrets, secretsProvided: Object.keys(collectedSecrets) });
789
825
  return {
790
826
  summary: result.summary,
791
827
  questions: result.questions,
792
828
  assumptions: result.assumptions,
793
829
  answers,
794
830
  confirmed: true,
831
+ requiredSecrets: result.requiredSecrets,
795
832
  };
796
833
  }
834
+ /**
835
+ * Prompt user for each required secret one at a time.
836
+ * Returns a map of envVar -> value for secrets the user provided, or null if cancelled.
837
+ */
838
+ async promptForSecrets(secrets) {
839
+ const collected = {};
840
+ // Check which secrets already exist in .env or .env.local
841
+ const existing = readExistingEnvVars(this.projectPath);
842
+ for (let i = 0; i < secrets.length; i++) {
843
+ const secret = secrets[i];
844
+ const existingValue = existing[secret.envVar];
845
+ const progressText = `Secret ${i + 1} of ${secrets.length}`;
846
+ if (existingValue) {
847
+ // Already present - show and let user confirm or update
848
+ const choice = await this.fullScreenMenu({
849
+ title: progressText,
850
+ subtitle: `\x1b[33m${secret.envVar}\x1b[0m — ${secret.name} (${secret.service})\n\nAlready set in .env file: ${existingValue.slice(0, 8)}${'*'.repeat(Math.max(0, existingValue.length - 8))}`,
851
+ items: [
852
+ { key: 'k', label: 'Keep', description: 'Keep existing value' },
853
+ { key: 'u', label: 'Update', description: 'Enter a new value' },
854
+ ],
855
+ exitOnDoubleEscape: true,
856
+ });
857
+ if (choice === null)
858
+ return null;
859
+ if (choice === 'k')
860
+ continue;
861
+ // Fall through to prompt for new value
862
+ }
863
+ const phaseLabel = secret.phase === 'both' ? 'build + runtime' : secret.phase;
864
+ const subtitle = `\x1b[33m${secret.envVar}\x1b[0m — ${secret.name} (${secret.service})\n\n\x1b[90mObtain from:\x1b[0m ${secret.obtainFrom}\n\x1b[90mNeeded at:\x1b[0m ${phaseLabel}`;
865
+ const value = await this.fullScreenTextInput({
866
+ title: progressText,
867
+ subtitle,
868
+ placeholder: 'paste your secret value... (Enter to skip)',
869
+ hint: 'Enter to continue, Esc to cancel setup',
870
+ exitOnDoubleEscape: true,
871
+ });
872
+ if (value === null)
873
+ return null; // User cancelled
874
+ if (value.trim()) {
875
+ collected[secret.envVar] = value.trim();
876
+ }
877
+ }
878
+ return collected;
879
+ }
797
880
  /**
798
881
  * Handle discovery phase for planning requests (no skip option)
799
882
  * This is used when the user submits a planning request to refine the plan.
@@ -864,6 +947,7 @@ export class MaistroApp {
864
947
  * Ask a single discovery question (required - no skip option)
865
948
  */
866
949
  async askDiscoveryQuestionRequired(question, questionNum, totalQuestions, summary) {
950
+ setStatusState('question');
867
951
  const progressText = `Question ${questionNum} of ${totalQuestions}`;
868
952
  if (question.type === 'text') {
869
953
  const answer = await this.fullScreenTextInput({
@@ -918,6 +1002,7 @@ export class MaistroApp {
918
1002
  * Ask a single discovery question using full-screen approach
919
1003
  */
920
1004
  async askDiscoveryQuestion(question, questionNum, totalQuestions, summary) {
1005
+ setStatusState('question');
921
1006
  const progressText = `Question ${questionNum} of ${totalQuestions}`;
922
1007
  if (question.type === 'text') {
923
1008
  // Full-screen text input
@@ -1147,6 +1232,9 @@ export class MaistroApp {
1147
1232
  console.log('\x1b[31mNo project found.\x1b[0m\n');
1148
1233
  return false;
1149
1234
  }
1235
+ // Update terminal status line - idle while waiting for user input
1236
+ const projectName = orchestrator.getProjectName();
1237
+ setStatusLine('idle', this.projectPath, projectName || undefined);
1150
1238
  const failed = tasks.filter(t => t.status === 'failed');
1151
1239
  const allDone = tasks.every(t => t.status === 'completed' || t.status === 'skipped');
1152
1240
  const hasFailed = failed.length > 0;
@@ -2731,6 +2819,7 @@ export class MaistroApp {
2731
2819
  * No separate screen or approve/reject prompt - task list updates automatically.
2732
2820
  */
2733
2821
  async handleInlineDiscussion(userInput, currentTasks, goal) {
2822
+ setStatusState('working');
2734
2823
  // Show discussion header inline
2735
2824
  this.ui.clear();
2736
2825
  this.showHeader();
@@ -2909,6 +2998,7 @@ export class MaistroApp {
2909
2998
  */
2910
2999
  async handleExistingRepoInit(detection) {
2911
3000
  try {
3001
+ setStatusState('question');
2912
3002
  // Auto-detect project name from folder
2913
3003
  const projectName = basename(this.projectPath);
2914
3004
  // Show welcome screen with project info
@@ -2999,6 +3089,7 @@ ${detection.projectContext}`;
2999
3089
  }
3000
3090
  async handleInit() {
3001
3091
  try {
3092
+ setStatusState('question');
3002
3093
  // Step 1: Get project name (full-screen)
3003
3094
  const currentFolderName = basename(this.projectPath);
3004
3095
  const nameInput = await this.fullScreenTextInput({
@@ -3052,6 +3143,7 @@ ${detection.projectContext}`;
3052
3143
  projectContext = formatContextForDecomposition(context);
3053
3144
  }
3054
3145
  // Decompose goal with context (with escape-to-exit support)
3146
+ setStatusState('working');
3055
3147
  const spinner = this.ui.spinnerWithEscape('Creating task plan...');
3056
3148
  const abortController = new AbortController();
3057
3149
  const orchestrator = this.getOrchestrator();
@@ -3522,6 +3614,7 @@ ${detection.projectContext}`;
3522
3614
  * Initialize execution state when starting task execution
3523
3615
  */
3524
3616
  startExecutionState() {
3617
+ setStatusState('working');
3525
3618
  this.executionState = {
3526
3619
  isExecuting: true,
3527
3620
  currentTask: null,