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 +5 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +94 -1
- package/dist/app.js.map +1 -1
- package/dist/envFile.d.ts +11 -0
- package/dist/envFile.d.ts.map +1 -0
- package/dist/envFile.js +56 -0
- package/dist/envFile.js.map +1 -0
- package/dist/orchestrator.d.ts +4 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +6 -0
- package/dist/orchestrator.js.map +1 -1
- package/dist/planner.d.ts +2 -1
- package/dist/planner.d.ts.map +1 -1
- package/dist/planner.js +38 -0
- package/dist/planner.js.map +1 -1
- package/dist/statusLine.d.ts +39 -0
- package/dist/statusLine.d.ts.map +1 -0
- package/dist/statusLine.js +74 -0
- package/dist/statusLine.js.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
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":"
|
|
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
|
-
|
|
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,
|