task-o-matic 0.0.6 → 0.0.8

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 (57) hide show
  1. package/README.md +86 -23
  2. package/dist/commands/benchmark.d.ts +3 -0
  3. package/dist/commands/benchmark.d.ts.map +1 -0
  4. package/dist/commands/benchmark.js +227 -0
  5. package/dist/commands/prd.d.ts.map +1 -1
  6. package/dist/commands/prd.js +203 -9
  7. package/dist/commands/workflow.d.ts.map +1 -1
  8. package/dist/commands/workflow.js +464 -286
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -0
  11. package/dist/lib/ai-service/ai-operations.d.ts +5 -0
  12. package/dist/lib/ai-service/ai-operations.d.ts.map +1 -1
  13. package/dist/lib/ai-service/ai-operations.js +167 -0
  14. package/dist/lib/benchmark/registry.d.ts +11 -0
  15. package/dist/lib/benchmark/registry.d.ts.map +1 -0
  16. package/dist/lib/benchmark/registry.js +78 -0
  17. package/dist/lib/benchmark/runner.d.ts +6 -0
  18. package/dist/lib/benchmark/runner.d.ts.map +1 -0
  19. package/dist/lib/benchmark/runner.js +150 -0
  20. package/dist/lib/benchmark/storage.d.ts +13 -0
  21. package/dist/lib/benchmark/storage.d.ts.map +1 -0
  22. package/dist/lib/benchmark/storage.js +99 -0
  23. package/dist/lib/benchmark/types.d.ts +54 -0
  24. package/dist/lib/benchmark/types.d.ts.map +1 -0
  25. package/dist/lib/benchmark/types.js +2 -0
  26. package/dist/lib/index.d.ts +9 -0
  27. package/dist/lib/index.d.ts.map +1 -1
  28. package/dist/lib/index.js +7 -1
  29. package/dist/lib/prompt-registry.d.ts.map +1 -1
  30. package/dist/lib/prompt-registry.js +23 -0
  31. package/dist/prompts/index.d.ts +7 -6
  32. package/dist/prompts/index.d.ts.map +1 -1
  33. package/dist/prompts/index.js +1 -0
  34. package/dist/prompts/prd-question.d.ts +3 -0
  35. package/dist/prompts/prd-question.d.ts.map +1 -0
  36. package/dist/prompts/prd-question.js +40 -0
  37. package/dist/services/benchmark.d.ts +12 -0
  38. package/dist/services/benchmark.d.ts.map +1 -0
  39. package/dist/services/benchmark.js +18 -0
  40. package/dist/services/prd.d.ts +25 -0
  41. package/dist/services/prd.d.ts.map +1 -1
  42. package/dist/services/prd.js +188 -28
  43. package/dist/services/workflow.d.ts +85 -0
  44. package/dist/services/workflow.d.ts.map +1 -0
  45. package/dist/services/workflow.js +363 -0
  46. package/dist/types/index.d.ts +3 -0
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/types/options.d.ts +3 -1
  49. package/dist/types/options.d.ts.map +1 -1
  50. package/dist/types/options.js +16 -0
  51. package/dist/types/workflow-options.d.ts +45 -0
  52. package/dist/types/workflow-options.d.ts.map +1 -0
  53. package/dist/types/workflow-options.js +2 -0
  54. package/dist/types/workflow-results.d.ts +55 -0
  55. package/dist/types/workflow-results.d.ts.map +1 -0
  56. package/dist/types/workflow-results.js +2 -0
  57. package/package.json +1 -1
@@ -1,5 +1,38 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
38
  };
@@ -9,27 +42,84 @@ const commander_1 = require("commander");
9
42
  const chalk_1 = __importDefault(require("chalk"));
10
43
  const fs_1 = require("fs");
11
44
  const path_1 = require("path");
12
- const config_1 = require("../lib/config");
13
- const better_t_stack_cli_1 = require("../lib/better-t-stack-cli");
45
+ const workflow_1 = require("../services/workflow");
14
46
  const prd_1 = require("../services/prd");
15
- const tasks_1 = require("../services/tasks");
16
- const workflow_ai_assistant_1 = require("../services/workflow-ai-assistant");
47
+ const inquirer_1 = __importDefault(require("inquirer"));
17
48
  const workflow_prompts_1 = require("../utils/workflow-prompts");
18
49
  const streaming_options_1 = require("../utils/streaming-options");
19
50
  const progress_1 = require("../cli/display/progress");
20
51
  exports.workflowCommand = new commander_1.Command("workflow")
21
52
  .description("Interactive workflow for complete project setup and task management")
53
+ // Existing AI options
22
54
  .option("--stream", "Show streaming AI output")
23
55
  .option("--ai-provider <provider>", "AI provider override")
24
56
  .option("--ai-model <model>", "AI model override")
25
57
  .option("--ai-key <key>", "AI API key override")
26
- .action(async (options) => {
58
+ .option("--ai-provider-url <url>", "AI provider URL override")
59
+ // Global workflow control
60
+ .option("--skip-all", "Skip all optional steps (use defaults)")
61
+ .option("--auto-accept", "Auto-accept all AI suggestions")
62
+ .option("--config-file <path>", "Load workflow options from JSON file")
63
+ // Step 1: Initialize
64
+ .option("--skip-init", "Skip initialization step")
65
+ .option("--project-name <name>", "Project name")
66
+ .option("--init-method <method>", "Initialization method: quick, custom, ai")
67
+ .option("--project-description <desc>", "Project description for AI-assisted init")
68
+ .option("--use-existing-config", "Use existing configuration if found")
69
+ .option("--frontend <framework>", "Frontend framework")
70
+ .option("--backend <framework>", "Backend framework")
71
+ .option("--database <db>", "Database choice")
72
+ .option("--auth", "Include authentication")
73
+ .option("--no-auth", "Exclude authentication")
74
+ .option("--bootstrap", "Bootstrap with Better-T-Stack")
75
+ .option("--no-bootstrap", "Skip bootstrapping")
76
+ // Step 2: Define PRD
77
+ .option("--skip-prd", "Skip PRD definition")
78
+ .option("--prd-method <method>", "PRD method: upload, manual, ai, skip")
79
+ .option("--prd-file <path>", "Path to existing PRD file")
80
+ .option("--prd-description <desc>", "Product description for AI-assisted PRD")
81
+ .option("--prd-content <content>", "Direct PRD content")
82
+ // Step 2.5: PRD Question/Refine (NEW)
83
+ .option("--skip-prd-question-refine", "Skip PRD question/refine step")
84
+ .option("--prd-question-refine", "Use question-based PRD refinement")
85
+ .option("--prd-answer-mode <mode>", "Who answers questions: user, ai")
86
+ .option("--prd-answer-ai-provider <provider>", "AI provider for answering (optional override)")
87
+ .option("--prd-answer-ai-model <model>", "AI model for answering (optional override)")
88
+ .option("--prd-answer-ai-reasoning", "Enable reasoning for AI answering model (if supported)")
89
+ // Step 3: Refine PRD
90
+ .option("--skip-refine", "Skip PRD refinement")
91
+ .option("--refine-method <method>", "Refinement method: manual, ai, skip")
92
+ .option("--refine-feedback <feedback>", "Feedback for AI refinement")
93
+ // Step 4: Generate Tasks
94
+ .option("--skip-generate", "Skip task generation")
95
+ .option("--generate-method <method>", "Generation method: standard, ai")
96
+ .option("--generate-instructions <instructions>", "Custom task generation instructions")
97
+ // Step 5: Split Tasks
98
+ .option("--skip-split", "Skip task splitting")
99
+ .option("--split-tasks <ids>", "Comma-separated task IDs to split")
100
+ .option("--split-all", "Split all tasks")
101
+ .option("--split-method <method>", "Split method: interactive, standard, custom")
102
+ .option("--split-instructions <instructions>", "Custom split instructions")
103
+ .action(async (cliOptions) => {
27
104
  try {
105
+ // Load and merge options from config file if specified
106
+ const options = await loadWorkflowOptions(cliOptions);
28
107
  console.log(chalk_1.default.blue.bold("\nšŸš€ Task-O-Matic Interactive Workflow\n"));
108
+ // Show automation status
109
+ if (options.configFile) {
110
+ console.log(chalk_1.default.cyan(`šŸ“‹ Using config: ${options.configFile}`));
111
+ }
112
+ if (options.skipAll) {
113
+ console.log(chalk_1.default.yellow("⚔ Fast mode: Skipping all optional steps"));
114
+ }
115
+ if (options.autoAccept) {
116
+ console.log(chalk_1.default.yellow("āœ“ Auto-accepting all AI suggestions"));
117
+ }
29
118
  console.log(chalk_1.default.gray("This wizard will guide you through:"));
30
119
  console.log(chalk_1.default.gray(" 1. Project initialization & bootstrap"));
31
120
  console.log(chalk_1.default.gray(" 2. PRD definition"));
32
- console.log(chalk_1.default.gray(" 3. PRD refinement"));
121
+ console.log(chalk_1.default.gray(" 2.5. PRD question & refinement (optional)"));
122
+ console.log(chalk_1.default.gray(" 3. PRD manual refinement (optional)"));
33
123
  console.log(chalk_1.default.gray(" 4. Task generation"));
34
124
  console.log(chalk_1.default.gray(" 5. Task splitting\n"));
35
125
  const state = {
@@ -37,23 +127,19 @@ exports.workflowCommand = new commander_1.Command("workflow")
37
127
  currentStep: "initialize",
38
128
  projectDir: process.cwd(),
39
129
  };
40
- // Store AI options for later use
41
- const aiOptions = {
42
- aiProvider: options.aiProvider,
43
- aiModel: options.aiModel,
44
- aiKey: options.aiKey,
45
- };
46
130
  const streamingOptions = (0, streaming_options_1.createStreamingOptions)(options.stream, "Workflow");
47
131
  // Step 1: Initialize/Bootstrap
48
- await stepInitialize(state, aiOptions, streamingOptions);
132
+ await stepInitialize(state, options, streamingOptions);
49
133
  // Step 2: Define PRD
50
- await stepDefinePRD(state, aiOptions, streamingOptions);
134
+ await stepDefinePRD(state, options, streamingOptions);
135
+ // Step 2.5: PRD Question/Refine (NEW)
136
+ await stepPRDQuestionRefine(state, options, streamingOptions);
51
137
  // Step 3: Refine PRD
52
- await stepRefinePRD(state, aiOptions, streamingOptions);
138
+ await stepRefinePRD(state, options, streamingOptions);
53
139
  // Step 4: Generate Tasks
54
- await stepGenerateTasks(state, aiOptions, streamingOptions);
140
+ await stepGenerateTasks(state, options, streamingOptions);
55
141
  // Step 5: Split Tasks
56
- await stepSplitTasks(state, aiOptions, streamingOptions);
142
+ await stepSplitTasks(state, options, streamingOptions);
57
143
  // Complete
58
144
  state.currentStep = "complete";
59
145
  console.log(chalk_1.default.green.bold("\nāœ… Workflow Complete!\n"));
@@ -68,370 +154,417 @@ exports.workflowCommand = new commander_1.Command("workflow")
68
154
  process.exit(1);
69
155
  }
70
156
  });
157
+ /**
158
+ * Load and merge workflow options from config file if specified
159
+ */
160
+ async function loadWorkflowOptions(cliOptions) {
161
+ let options = { ...cliOptions };
162
+ if (cliOptions.configFile) {
163
+ try {
164
+ const configPath = (0, path_1.resolve)(cliOptions.configFile);
165
+ const { existsSync, readFileSync } = await Promise.resolve().then(() => __importStar(require("fs")));
166
+ if (existsSync(configPath)) {
167
+ const configContent = readFileSync(configPath, "utf-8");
168
+ const fileOptions = JSON.parse(configContent);
169
+ // CLI options override file options
170
+ options = { ...fileOptions, ...cliOptions };
171
+ console.log(chalk_1.default.green(`āœ“ Loaded workflow config from ${configPath}`));
172
+ }
173
+ else {
174
+ console.log(chalk_1.default.yellow(`⚠ Config file not found: ${configPath}`));
175
+ }
176
+ }
177
+ catch (error) {
178
+ console.log(chalk_1.default.red(`āœ— Failed to load config file: ${error}`));
179
+ }
180
+ }
181
+ return options;
182
+ }
183
+ /**
184
+ * Helper to get pre-answered value or prompt user
185
+ */
186
+ async function getOrPrompt(preAnswered, promptFn, skipCondition = false) {
187
+ if (skipCondition) {
188
+ throw new Error("Step skipped");
189
+ }
190
+ if (preAnswered !== undefined) {
191
+ return preAnswered;
192
+ }
193
+ return promptFn();
194
+ }
71
195
  /**
72
196
  * Step 1: Initialize/Bootstrap
197
+ * Uses workflowService.initializeProject()
73
198
  */
74
- async function stepInitialize(state, aiOptions, streamingOptions) {
199
+ async function stepInitialize(state, options, streamingOptions) {
75
200
  console.log(chalk_1.default.blue.bold("\nšŸ“¦ Step 1: Project Initialization\n"));
76
- // Check if already initialized in current directory
77
- const taskOMaticDir = config_1.configManager.getTaskOMaticDir();
78
- const alreadyInitialized = (0, fs_1.existsSync)(taskOMaticDir);
79
- if (alreadyInitialized) {
80
- console.log(chalk_1.default.yellow("āœ“ Project already initialized"));
81
- const useExisting = await (0, workflow_prompts_1.confirmPrompt)("Use existing configuration?", true);
82
- if (useExisting) {
83
- state.initialized = true;
84
- state.currentStep = "define-prd";
85
- return;
86
- }
201
+ if (options.skipInit) {
202
+ console.log(chalk_1.default.yellow("⚠ Skipping initialization (--skip-init)"));
203
+ state.initialized = false;
204
+ state.currentStep = "define-prd";
205
+ return;
87
206
  }
88
- const shouldInitialize = await (0, workflow_prompts_1.confirmPrompt)("Initialize a new task-o-matic project?", true);
207
+ const shouldInitialize = await getOrPrompt(options.skipInit === false ? true : undefined, () => (0, workflow_prompts_1.confirmPrompt)("Initialize a new task-o-matic project?", true));
89
208
  if (!shouldInitialize) {
90
209
  console.log(chalk_1.default.yellow("⚠ Skipping initialization"));
91
210
  state.initialized = false;
92
211
  state.currentStep = "define-prd";
93
212
  return;
94
213
  }
95
- const projectName = await (0, workflow_prompts_1.textInputPrompt)("What is the name of your project?", "my-app");
96
- // Choose initialization method
97
- let initMethod = await (0, workflow_prompts_1.selectPrompt)("How would you like to configure your project?", [
214
+ const projectName = await getOrPrompt(options.projectName, () => (0, workflow_prompts_1.textInputPrompt)("What is the name of your project?", "my-app"));
215
+ // Determine initialization method
216
+ const initMethod = await getOrPrompt(options.initMethod, () => (0, workflow_prompts_1.selectPrompt)("How would you like to configure your project stack?", [
98
217
  { name: "Quick start (recommended defaults)", value: "quick" },
99
218
  { name: "Custom configuration", value: "custom" },
100
219
  { name: "AI-assisted (describe your project)", value: "ai" },
101
- ]);
102
- let config;
220
+ ]));
221
+ let projectDescription;
103
222
  if (initMethod === "ai") {
104
- console.log(chalk_1.default.cyan("\nšŸ¤– AI-Assisted Configuration\n"));
105
- const description = await (0, workflow_prompts_1.textInputPrompt)("Describe your project (e.g., 'A SaaS app for team collaboration with real-time features'):");
106
- console.log(chalk_1.default.gray("\n Analyzing your requirements...\n"));
107
- config = await workflow_ai_assistant_1.workflowAIAssistant.assistInitConfig({
108
- userDescription: description,
109
- aiOptions,
110
- streamingOptions,
111
- });
112
- // Override AI's project name with user's choice if they provided one (though we just asked for it, so we should use it)
113
- config.projectName = projectName;
114
- console.log(chalk_1.default.green("\nāœ“ AI Recommendations:"));
115
- console.log(chalk_1.default.gray(` Project: ${config.projectName}`));
116
- console.log(chalk_1.default.gray(` AI Provider: ${config.aiProvider}`));
117
- console.log(chalk_1.default.gray(` Frontend: ${config.frontend || "none"}`));
118
- console.log(chalk_1.default.gray(` Backend: ${config.backend || "none"}`));
119
- console.log(chalk_1.default.gray(` Database: ${config.database || "none"}`));
120
- console.log(chalk_1.default.gray(` Auth: ${config.auth ? "yes" : "no"}`));
121
- if (config.reasoning) {
122
- console.log(chalk_1.default.gray(`\n ${config.reasoning}\n`));
123
- }
124
- const acceptRecommendation = await (0, workflow_prompts_1.confirmPrompt)("Accept these recommendations?", true);
125
- if (!acceptRecommendation) {
126
- console.log(chalk_1.default.yellow("⚠ Falling back to custom configuration"));
127
- initMethod = "custom";
128
- }
129
- }
130
- if (initMethod === "quick") {
131
- config = {
132
- projectName: projectName,
133
- aiProvider: "openrouter",
134
- aiModel: "anthropic/claude-3.5-sonnet",
135
- frontend: "next",
136
- backend: "hono",
137
- database: "sqlite",
138
- auth: true,
139
- reasoning: "Modern, well-supported stack",
140
- };
223
+ projectDescription = await getOrPrompt(options.projectDescription, () => (0, workflow_prompts_1.textInputPrompt)("Describe your project (e.g., 'A SaaS app for team collaboration with real-time features'):"));
141
224
  }
142
- else if (initMethod === "custom") {
143
- config = {
144
- projectName: projectName,
145
- aiProvider: await (0, workflow_prompts_1.selectPrompt)("AI Provider:", [
146
- "openrouter",
147
- "anthropic",
148
- "openai",
149
- "custom",
150
- ]),
151
- aiModel: await (0, workflow_prompts_1.textInputPrompt)("AI Model:", "anthropic/claude-3.5-sonnet"),
152
- };
153
- const shouldBootstrap = await (0, workflow_prompts_1.confirmPrompt)("Bootstrap with Better-T-Stack?", true);
225
+ // Collect stack config for custom mode
226
+ let stackConfig = {};
227
+ if (initMethod === "custom") {
228
+ const shouldBootstrap = await getOrPrompt(options.bootstrap, () => (0, workflow_prompts_1.confirmPrompt)("Bootstrap with Better-T-Stack?", true));
154
229
  if (shouldBootstrap) {
155
- config.frontend = await (0, workflow_prompts_1.selectPrompt)("Frontend framework:", [
230
+ stackConfig.frontend = await getOrPrompt(options.frontend, () => (0, workflow_prompts_1.selectPrompt)("Frontend framework:", [
156
231
  "next",
157
232
  "tanstack-router",
158
233
  "react-router",
159
234
  "vite-react",
160
235
  "remix",
161
- ]);
162
- config.backend = await (0, workflow_prompts_1.selectPrompt)("Backend framework:", [
236
+ ]));
237
+ stackConfig.backend = await getOrPrompt(options.backend, () => (0, workflow_prompts_1.selectPrompt)("Backend framework:", [
163
238
  "hono",
164
239
  "express",
165
240
  "elysia",
166
241
  "fastify",
167
- ]);
168
- config.database = await (0, workflow_prompts_1.selectPrompt)("Database:", [
242
+ ]));
243
+ stackConfig.database = await getOrPrompt(options.database, () => (0, workflow_prompts_1.selectPrompt)("Database:", [
169
244
  "sqlite",
170
245
  "postgres",
171
246
  "mysql",
172
247
  "mongodb",
173
248
  "turso",
174
249
  "neon",
175
- ]);
176
- config.auth = await (0, workflow_prompts_1.confirmPrompt)("Include authentication?", true);
250
+ ]));
251
+ stackConfig.auth = await getOrPrompt(options.auth, () => (0, workflow_prompts_1.confirmPrompt)("Include authentication?", true));
177
252
  }
178
253
  }
179
- // Bootstrap Logic
180
- let projectDir = process.cwd();
181
- let didBootstrap = false;
182
- if (config.frontend || config.backend) {
183
- const shouldBootstrap = await (0, workflow_prompts_1.confirmPrompt)("Bootstrap project now?", true);
184
- if (shouldBootstrap) {
185
- console.log(chalk_1.default.cyan("\n Bootstrapping with Better-T-Stack...\n"));
186
- try {
187
- const result = await (0, better_t_stack_cli_1.runBetterTStackCLI)({
188
- projectName: config.projectName,
189
- frontend: config.frontend || "next",
190
- backend: config.backend || "hono",
191
- database: config.database || "sqlite",
192
- noAuth: !config.auth,
193
- // Default values for required fields that might be missing from simple config
194
- orm: "drizzle",
195
- packageManager: "npm",
196
- runtime: "node",
197
- noInstall: false,
198
- noGit: false,
199
- }, process.cwd());
200
- if (result.success) {
201
- console.log(chalk_1.default.green(`\nāœ“ ${result.message}\n`));
202
- didBootstrap = true;
203
- // Update project directory if a new directory was created
204
- if (result.projectPath) {
205
- projectDir = (0, path_1.resolve)(process.cwd(), result.projectPath);
206
- console.log(chalk_1.default.cyan(` šŸ“‚ Switching to project directory: ${projectDir}\n`));
207
- process.chdir(projectDir);
208
- config_1.configManager.setWorkingDirectory(projectDir);
209
- state.projectDir = projectDir;
210
- }
211
- }
212
- else {
213
- console.log(chalk_1.default.red(`\nāœ— Bootstrap failed: ${result.message}\n`));
214
- console.log(chalk_1.default.yellow("You can try running 'task-o-matic init bootstrap' manually later.\n"));
215
- }
216
- }
217
- catch (error) {
218
- console.log(chalk_1.default.red(`\nāœ— Bootstrap failed: ${error}\n`));
219
- }
220
- }
221
- }
222
- // Initialize task-o-matic in the correct directory (projectDir)
223
- console.log(chalk_1.default.cyan("\n Initializing task-o-matic...\n"));
224
- // Re-check task-o-matic dir in the new location
225
- const newTaskOMaticDir = (0, path_1.join)(projectDir, ".task-o-matic");
226
- if (!(0, fs_1.existsSync)(newTaskOMaticDir)) {
227
- (0, fs_1.mkdirSync)(newTaskOMaticDir, { recursive: true });
228
- ["tasks", "prd", "logs"].forEach((dir) => {
229
- (0, fs_1.mkdirSync)((0, path_1.join)(newTaskOMaticDir, dir), { recursive: true });
230
- });
231
- }
232
- // Handle .env configuration
233
- const envPath = (0, path_1.join)(projectDir, ".env");
234
- let envContent = "";
235
- if ((0, fs_1.existsSync)(envPath)) {
236
- envContent = (0, fs_1.readFileSync)(envPath, "utf-8");
237
- }
238
- // Check if we need to ask for API key
239
- const providerKeyName = config.aiProvider === "openai"
240
- ? "OPENAI_API_KEY"
241
- : config.aiProvider === "anthropic"
242
- ? "ANTHROPIC_API_KEY"
243
- : config.aiProvider === "openrouter"
244
- ? "OPENROUTER_API_KEY"
245
- : "AI_API_KEY";
246
- // Check if key exists in current env OR in the target .env file
247
- const hasKeyInEnv = process.env[providerKeyName] || envContent.includes(providerKeyName);
248
- if (!hasKeyInEnv) {
249
- console.log(chalk_1.default.yellow(`\nāš ļø No API key found for ${config.aiProvider}`));
250
- const apiKey = await (0, workflow_prompts_1.textInputPrompt)(`Enter your ${config.aiProvider} API Key:`);
251
- // Prepare .env content
252
- let newEnvContent = envContent;
253
- if (newEnvContent && !newEnvContent.endsWith("\n")) {
254
- newEnvContent += "\n";
255
- }
256
- if (!newEnvContent.includes("AI_PROVIDER=")) {
257
- newEnvContent += `AI_PROVIDER=${config.aiProvider}\n`;
258
- }
259
- if (!newEnvContent.includes("AI_MODEL=")) {
260
- newEnvContent += `AI_MODEL=${config.aiModel}\n`;
261
- }
262
- if (!newEnvContent.includes(`${providerKeyName}=`)) {
263
- newEnvContent += `${providerKeyName}=${apiKey}\n`;
264
- }
265
- (0, fs_1.writeFileSync)(envPath, newEnvContent);
266
- console.log(chalk_1.default.green(`āœ“ Saved configuration to ${envPath}`));
267
- // Update process.env for immediate use in this session
268
- process.env[providerKeyName] = apiKey;
269
- process.env.AI_PROVIDER = config.aiProvider;
270
- process.env.AI_MODEL = config.aiModel;
271
- }
272
- // Save configuration
273
- config_1.configManager.setConfig({
274
- ai: {
275
- provider: config.aiProvider, // Cast to satisfy AIProvider type
276
- model: config.aiModel,
277
- maxTokens: 32768,
278
- temperature: 0.5,
279
- apiKey: process.env[providerKeyName], // Ensure key is in config if needed
254
+ // Determine if we should bootstrap
255
+ const bootstrap = initMethod === "quick" ||
256
+ (initMethod === "ai" && options.bootstrap !== false) ||
257
+ (initMethod === "custom" && Object.keys(stackConfig).length > 0);
258
+ // Call service
259
+ const result = await workflow_1.workflowService.initializeProject({
260
+ projectName,
261
+ initMethod: initMethod,
262
+ projectDescription,
263
+ aiOptions: options,
264
+ stackConfig,
265
+ bootstrap,
266
+ streamingOptions,
267
+ callbacks: {
268
+ onProgress: progress_1.displayProgress,
269
+ onError: progress_1.displayError,
280
270
  },
281
271
  });
282
- config_1.configManager.save();
283
272
  console.log(chalk_1.default.green("āœ“ Project initialized"));
284
273
  state.initialized = true;
285
- state.projectName = config.projectName;
286
- state.aiConfig = {
287
- provider: config.aiProvider,
288
- model: config.aiModel,
289
- };
274
+ state.projectName = result.projectName;
275
+ state.projectDir = result.projectDir;
276
+ state.aiConfig = result.aiConfig;
290
277
  state.currentStep = "define-prd";
291
278
  }
292
279
  /**
293
280
  * Step 2: Define PRD
281
+ * Uses workflowService.definePRD()
294
282
  */
295
- async function stepDefinePRD(state, aiOptions, streamingOptions) {
283
+ async function stepDefinePRD(state, options, streamingOptions) {
296
284
  console.log(chalk_1.default.blue.bold("\nšŸ“ Step 2: Define PRD\n"));
297
- const prdMethod = await (0, workflow_prompts_1.selectPrompt)("How would you like to define your PRD?", [
285
+ if (options.skipPrd) {
286
+ console.log(chalk_1.default.yellow("⚠ Skipping PRD definition (--skip-prd)"));
287
+ state.currentStep = "refine-prd";
288
+ return;
289
+ }
290
+ const prdMethod = await getOrPrompt(options.prdMethod, () => (0, workflow_prompts_1.selectPrompt)("How would you like to define your PRD?", [
298
291
  { name: "Upload existing file", value: "upload" },
299
292
  { name: "Write manually (open editor)", value: "manual" },
300
293
  { name: "AI-assisted creation", value: "ai" },
301
294
  { name: "Skip (use existing PRD)", value: "skip" },
302
- ]);
303
- const taskOMaticDir = config_1.configManager.getTaskOMaticDir();
304
- const prdDir = (0, path_1.join)(taskOMaticDir, "prd");
295
+ ]));
305
296
  if (prdMethod === "skip") {
306
297
  console.log(chalk_1.default.yellow("⚠ Skipping PRD definition"));
307
298
  state.currentStep = "refine-prd";
308
299
  return;
309
300
  }
310
- let prdContent = "";
311
- let prdFilename = "prd.md";
301
+ let prdFile;
302
+ let prdDescription;
303
+ let prdContent;
312
304
  if (prdMethod === "upload") {
313
- const filePath = await (0, workflow_prompts_1.textInputPrompt)("Path to PRD file:");
314
- if (!(0, fs_1.existsSync)(filePath)) {
315
- console.log(chalk_1.default.red(`āœ— File not found: ${filePath}`));
316
- return stepDefinePRD(state, aiOptions, streamingOptions);
317
- }
318
- prdContent = (0, fs_1.readFileSync)(filePath, "utf-8");
319
- prdFilename = filePath.split("/").pop() || "prd.md";
305
+ prdFile = await getOrPrompt(options.prdFile, () => (0, workflow_prompts_1.textInputPrompt)("Path to PRD file:"));
320
306
  }
321
307
  else if (prdMethod === "manual") {
322
308
  console.log(chalk_1.default.gray("\n Opening editor...\n"));
323
309
  prdContent = await (0, workflow_prompts_1.editorPrompt)("Write your PRD (save and close editor when done):", "# Product Requirements Document\n\n## Overview\n\n## Objectives\n\n## Features\n\n");
324
310
  }
325
311
  else if (prdMethod === "ai") {
326
- console.log(chalk_1.default.cyan("\nšŸ¤– AI-Assisted PRD Creation\n"));
327
- const description = await (0, workflow_prompts_1.textInputPrompt)("Describe your product in detail:");
328
- console.log(chalk_1.default.gray("\n Generating PRD...\n"));
329
- prdContent = await workflow_ai_assistant_1.workflowAIAssistant.assistPRDCreation({
330
- userDescription: description,
331
- aiOptions,
332
- streamingOptions,
333
- });
312
+ prdDescription = await getOrPrompt(options.prdDescription, () => (0, workflow_prompts_1.textInputPrompt)("Describe your product in detail:"));
313
+ }
314
+ // Call service
315
+ const result = await workflow_1.workflowService.definePRD({
316
+ method: prdMethod,
317
+ prdFile,
318
+ prdDescription,
319
+ prdContent,
320
+ projectDir: state.projectDir,
321
+ aiOptions: options,
322
+ streamingOptions,
323
+ callbacks: {
324
+ onProgress: progress_1.displayProgress,
325
+ onError: progress_1.displayError,
326
+ },
327
+ });
328
+ if (prdMethod === "ai") {
334
329
  console.log(chalk_1.default.green("\nāœ“ PRD generated"));
335
- console.log(chalk_1.default.gray("\n" + prdContent.substring(0, 500) + "...\n"));
336
- const acceptPRD = await (0, workflow_prompts_1.confirmPrompt)("Accept this PRD?", true);
330
+ console.log(chalk_1.default.gray("\n" + result.prdContent.substring(0, 500) + "...\n"));
331
+ const acceptPRD = await getOrPrompt(options.autoAccept ? true : undefined, () => (0, workflow_prompts_1.confirmPrompt)("Accept this PRD?", true));
337
332
  if (!acceptPRD) {
338
333
  console.log(chalk_1.default.yellow("⚠ Regenerating..."));
339
- return stepDefinePRD(state, aiOptions, streamingOptions);
334
+ return stepDefinePRD(state, options, streamingOptions);
340
335
  }
341
336
  }
342
- // Save PRD
343
- const prdPath = (0, path_1.join)(prdDir, prdFilename);
344
- (0, fs_1.writeFileSync)(prdPath, prdContent);
345
- console.log(chalk_1.default.green(`āœ“ PRD saved to ${prdPath}`));
346
- state.prdFile = prdPath;
347
- state.prdContent = prdContent;
337
+ state.prdFile = result.prdFile;
338
+ state.prdContent = result.prdContent;
339
+ state.currentStep = "question-refine-prd";
340
+ }
341
+ /**
342
+ * Step 2.5: PRD Question/Refine (NEW)
343
+ * Uses prdService.refinePRDWithQuestions()
344
+ */
345
+ async function stepPRDQuestionRefine(state, options, streamingOptions) {
346
+ console.log(chalk_1.default.blue.bold("\nā“ Step 2.5: PRD Question & Refine\n"));
347
+ if (!state.prdFile) {
348
+ console.log(chalk_1.default.yellow("⚠ No PRD file found, skipping"));
349
+ state.currentStep = "refine-prd";
350
+ return;
351
+ }
352
+ if (options.skipPrdQuestionRefine || options.skipAll) {
353
+ console.log(chalk_1.default.gray(" Skipping question-based refinement"));
354
+ state.currentStep = "refine-prd";
355
+ return;
356
+ }
357
+ // Ask if user wants question-based refinement
358
+ const useQuestions = await getOrPrompt(options.prdQuestionRefine, () => (0, workflow_prompts_1.confirmPrompt)("Refine PRD with clarifying questions?", false));
359
+ if (!useQuestions) {
360
+ console.log(chalk_1.default.gray(" Skipping question-based refinement"));
361
+ state.currentStep = "refine-prd";
362
+ return;
363
+ }
364
+ // Ask who should answer: user or AI
365
+ const answerMode = await getOrPrompt(options.prdAnswerMode, () => (0, workflow_prompts_1.selectPrompt)("Who should answer the questions?", [
366
+ { name: "I will answer", value: "user" },
367
+ { name: "AI assistant (uses PRD + stack context)", value: "ai" },
368
+ ]));
369
+ let questionAIOptions = undefined;
370
+ if (answerMode === "ai") {
371
+ // Ask if they want to use a different AI model for answering
372
+ const useCustomAI = await getOrPrompt(options.prdAnswerAiProvider !== undefined, () => (0, workflow_prompts_1.confirmPrompt)("Use a different AI model for answering? (e.g., a smarter model)", false));
373
+ if (useCustomAI) {
374
+ const provider = await getOrPrompt(options.prdAnswerAiProvider, () => (0, workflow_prompts_1.selectPrompt)("AI Provider for answering:", [
375
+ { name: "OpenRouter", value: "openrouter" },
376
+ { name: "Anthropic", value: "anthropic" },
377
+ { name: "OpenAI", value: "openai" },
378
+ ]));
379
+ const model = await getOrPrompt(options.prdAnswerAiModel, () => (0, workflow_prompts_1.textInputPrompt)("AI Model for answering:", provider === "openrouter" ? "anthropic/claude-3.5-sonnet" : ""));
380
+ questionAIOptions = {
381
+ aiProvider: provider,
382
+ aiModel: model,
383
+ };
384
+ }
385
+ // Check if reasoning should be enabled
386
+ if (options.prdAnswerAiReasoning) {
387
+ if (!questionAIOptions) {
388
+ // No custom AI specified, use main AI with reasoning
389
+ questionAIOptions = {
390
+ aiProvider: options.aiProvider,
391
+ aiModel: options.aiModel,
392
+ aiReasoning: "enabled",
393
+ };
394
+ }
395
+ else {
396
+ // Custom AI specified, add reasoning to it
397
+ questionAIOptions.aiReasoning = "enabled";
398
+ }
399
+ }
400
+ }
401
+ // For user mode, we need to collect answers interactively
402
+ let answers;
403
+ if (answerMode === "user") {
404
+ // First, generate questions
405
+ console.log(chalk_1.default.cyan("\n Generating questions...\n"));
406
+ const questions = await prd_1.prdService.generateQuestions({
407
+ file: state.prdFile,
408
+ workingDirectory: state.projectDir,
409
+ aiOptions: options,
410
+ streamingOptions,
411
+ callbacks: {
412
+ onProgress: progress_1.displayProgress,
413
+ onError: progress_1.displayError,
414
+ },
415
+ });
416
+ if (questions.length === 0) {
417
+ console.log(chalk_1.default.yellow("No questions generated - PRD appears complete"));
418
+ state.currentStep = "refine-prd";
419
+ return;
420
+ }
421
+ console.log(chalk_1.default.blue(`\nPlease answer the following ${questions.length} questions to refine the PRD:\n`));
422
+ answers = {};
423
+ for (let i = 0; i < questions.length; i++) {
424
+ const q = questions[i];
425
+ const answer = await inquirer_1.default.prompt([
426
+ {
427
+ type: "input",
428
+ name: "response",
429
+ message: `${i + 1}/${questions.length}: ${q}`,
430
+ validate: (input) => input.trim().length > 0 || "Please provide an answer",
431
+ },
432
+ ]);
433
+ answers[q] = answer.response;
434
+ }
435
+ }
436
+ // Call service - it will automatically refine after answering
437
+ console.log(chalk_1.default.cyan("\n Generating questions and refining PRD...\n"));
438
+ const result = await prd_1.prdService.refinePRDWithQuestions({
439
+ file: state.prdFile,
440
+ questionMode: answerMode,
441
+ answers, // Only provided for user mode
442
+ questionAIOptions,
443
+ workingDirectory: state.projectDir,
444
+ aiOptions: options,
445
+ streamingOptions,
446
+ callbacks: {
447
+ onProgress: progress_1.displayProgress,
448
+ onError: progress_1.displayError,
449
+ },
450
+ });
451
+ console.log(chalk_1.default.green(`\nāœ“ PRD refined with ${result.questions.length} questions answered`));
452
+ console.log(chalk_1.default.gray("\n Questions & Answers:"));
453
+ result.questions.forEach((q, i) => {
454
+ console.log(chalk_1.default.cyan(` Q${i + 1}: ${q}`));
455
+ console.log(chalk_1.default.gray(` A${i + 1}: ${result.answers[q]?.substring(0, 100)}...\n`));
456
+ });
457
+ // Update state with refined PRD
458
+ state.prdFile = result.refinedPRDPath;
459
+ state.prdContent = (0, fs_1.readFileSync)(result.refinedPRDPath, "utf-8");
348
460
  state.currentStep = "refine-prd";
349
461
  }
350
462
  /**
351
463
  * Step 3: Refine PRD
464
+ * Uses workflowService.refinePRD()
352
465
  */
353
- async function stepRefinePRD(state, aiOptions, streamingOptions) {
466
+ async function stepRefinePRD(state, options, streamingOptions) {
354
467
  console.log(chalk_1.default.blue.bold("\n✨ Step 3: Refine PRD\n"));
355
468
  if (!state.prdFile) {
356
469
  console.log(chalk_1.default.yellow("⚠ No PRD file found, skipping refinement"));
357
470
  state.currentStep = "generate-tasks";
358
471
  return;
359
472
  }
360
- const shouldRefine = await (0, workflow_prompts_1.confirmPrompt)("Refine your PRD?", false);
473
+ if (options.skipRefine || options.skipAll) {
474
+ console.log(chalk_1.default.gray(" Skipping refinement"));
475
+ state.currentStep = "generate-tasks";
476
+ return;
477
+ }
478
+ const shouldRefine = await getOrPrompt(options.skipRefine === false ? true : undefined, () => (0, workflow_prompts_1.confirmPrompt)("Refine your PRD further?", false));
361
479
  if (!shouldRefine) {
362
480
  console.log(chalk_1.default.gray(" Skipping refinement"));
363
481
  state.currentStep = "generate-tasks";
364
482
  return;
365
483
  }
366
- const refineMethod = await (0, workflow_prompts_1.selectPrompt)("How would you like to refine?", [
484
+ const refineMethod = await getOrPrompt(options.refineMethod, () => (0, workflow_prompts_1.selectPrompt)("How would you like to refine?", [
367
485
  { name: "Manual editing (open editor)", value: "manual" },
368
486
  { name: "AI-assisted refinement", value: "ai" },
369
487
  { name: "Skip", value: "skip" },
370
- ]);
488
+ ]));
371
489
  if (refineMethod === "skip") {
372
490
  state.currentStep = "generate-tasks";
373
491
  return;
374
492
  }
375
493
  let refinedContent = state.prdContent || (0, fs_1.readFileSync)(state.prdFile, "utf-8");
494
+ let feedback;
376
495
  if (refineMethod === "manual") {
377
496
  console.log(chalk_1.default.gray("\n Opening editor...\n"));
378
497
  refinedContent = await (0, workflow_prompts_1.editorPrompt)("Edit your PRD (save and close when done):", refinedContent);
379
498
  }
380
499
  else if (refineMethod === "ai") {
381
- console.log(chalk_1.default.cyan("\nšŸ¤– AI-Assisted Refinement\n"));
382
- const feedback = await (0, workflow_prompts_1.textInputPrompt)("What would you like to improve? (e.g., 'Add more technical details', 'Focus on MVP features'):");
383
- console.log(chalk_1.default.gray("\n Refining PRD...\n"));
384
- refinedContent = await workflow_ai_assistant_1.workflowAIAssistant.assistPRDRefinement({
385
- currentPRD: refinedContent,
386
- userFeedback: feedback,
387
- aiOptions,
388
- streamingOptions,
389
- });
500
+ feedback = await getOrPrompt(options.refineFeedback, () => (0, workflow_prompts_1.textInputPrompt)("What would you like to improve? (e.g., 'Add more technical details', 'Focus on MVP features'):"));
501
+ }
502
+ // Call service
503
+ const result = await workflow_1.workflowService.refinePRD({
504
+ method: refineMethod,
505
+ prdFile: state.prdFile,
506
+ prdContent: refineMethod === "manual" ? refinedContent : undefined,
507
+ feedback,
508
+ projectDir: state.projectDir,
509
+ aiOptions: options,
510
+ streamingOptions,
511
+ callbacks: {
512
+ onProgress: progress_1.displayProgress,
513
+ onError: progress_1.displayError,
514
+ },
515
+ });
516
+ if (refineMethod === "ai") {
390
517
  console.log(chalk_1.default.green("\nāœ“ PRD refined"));
391
- console.log(chalk_1.default.gray("\n" + refinedContent.substring(0, 500) + "...\n"));
392
- const acceptRefinement = await (0, workflow_prompts_1.confirmPrompt)("Accept refinements?", true);
518
+ console.log(chalk_1.default.gray("\n" + result.prdContent.substring(0, 500) + "...\n"));
519
+ const acceptRefinement = await getOrPrompt(options.autoAccept ? true : undefined, () => (0, workflow_prompts_1.confirmPrompt)("Accept refinements?", true));
393
520
  if (!acceptRefinement) {
394
521
  console.log(chalk_1.default.yellow("⚠ Keeping original PRD"));
395
522
  state.currentStep = "generate-tasks";
396
523
  return;
397
524
  }
398
525
  }
399
- // Save refined PRD
400
- (0, fs_1.writeFileSync)(state.prdFile, refinedContent);
401
- state.prdContent = refinedContent;
526
+ state.prdContent = result.prdContent;
402
527
  console.log(chalk_1.default.green(`āœ“ PRD updated`));
403
528
  state.currentStep = "generate-tasks";
404
529
  }
405
530
  /**
406
531
  * Step 4: Generate Tasks
532
+ * Uses workflowService.generateTasks()
407
533
  */
408
- async function stepGenerateTasks(state, aiOptions, streamingOptions) {
534
+ async function stepGenerateTasks(state, options, streamingOptions) {
409
535
  console.log(chalk_1.default.blue.bold("\nšŸŽÆ Step 4: Generate Tasks\n"));
410
536
  if (!state.prdFile) {
411
537
  console.log(chalk_1.default.yellow("⚠ No PRD file found, skipping task generation"));
412
538
  state.currentStep = "split-tasks";
413
539
  return;
414
540
  }
415
- const shouldGenerate = await (0, workflow_prompts_1.confirmPrompt)("Generate tasks from PRD?", true);
541
+ if (options.skipGenerate || options.skipAll) {
542
+ console.log(chalk_1.default.gray(" Skipping task generation"));
543
+ state.currentStep = "split-tasks";
544
+ return;
545
+ }
546
+ const shouldGenerate = await getOrPrompt(options.skipGenerate === false ? true : undefined, () => (0, workflow_prompts_1.confirmPrompt)("Generate tasks from PRD?", true));
416
547
  if (!shouldGenerate) {
417
548
  console.log(chalk_1.default.gray(" Skipping task generation"));
418
549
  state.currentStep = "split-tasks";
419
550
  return;
420
551
  }
421
- const generationMethod = await (0, workflow_prompts_1.selectPrompt)("Choose generation method:", [
552
+ const generationMethod = await getOrPrompt(options.generateMethod, () => (0, workflow_prompts_1.selectPrompt)("Choose generation method:", [
422
553
  { name: "Standard parsing", value: "standard" },
423
554
  { name: "AI-assisted with custom instructions", value: "ai" },
424
- ]);
555
+ ]));
425
556
  let customInstructions;
426
557
  if (generationMethod === "ai") {
427
- customInstructions = await (0, workflow_prompts_1.textInputPrompt)("Custom instructions (e.g., 'Focus on MVP features', 'Break into small tasks'):", "");
558
+ customInstructions = await getOrPrompt(options.generateInstructions, () => (0, workflow_prompts_1.textInputPrompt)("Custom instructions (e.g., 'Focus on MVP features', 'Break into small tasks'):", ""));
428
559
  }
429
560
  console.log(chalk_1.default.cyan("\n Parsing PRD and generating tasks...\n"));
430
- const result = await prd_1.prdService.parsePRD({
431
- file: state.prdFile,
432
- workingDirectory: state.projectDir,
433
- aiOptions,
434
- messageOverride: customInstructions,
561
+ // Call service
562
+ const result = await workflow_1.workflowService.generateTasks({
563
+ prdFile: state.prdFile,
564
+ method: generationMethod,
565
+ customInstructions,
566
+ projectDir: state.projectDir,
567
+ aiOptions: options,
435
568
  streamingOptions,
436
569
  callbacks: {
437
570
  onProgress: progress_1.displayProgress,
@@ -453,51 +586,96 @@ async function stepGenerateTasks(state, aiOptions, streamingOptions) {
453
586
  }
454
587
  /**
455
588
  * Step 5: Split Complex Tasks
589
+ * Uses workflowService.splitTasks()
456
590
  */
457
- async function stepSplitTasks(state, aiOptions, streamingOptions) {
591
+ async function stepSplitTasks(state, options, streamingOptions) {
458
592
  console.log(chalk_1.default.blue.bold("\nšŸ”€ Step 5: Split Complex Tasks\n"));
459
593
  if (!state.tasks || state.tasks.length === 0) {
460
594
  console.log(chalk_1.default.yellow("⚠ No tasks found, skipping splitting"));
461
595
  return;
462
596
  }
463
- const shouldSplit = await (0, workflow_prompts_1.confirmPrompt)("Split any complex tasks into subtasks?", false);
464
- if (!shouldSplit) {
597
+ if (options.skipSplit || options.skipAll) {
465
598
  console.log(chalk_1.default.gray(" Skipping task splitting"));
466
599
  return;
467
600
  }
468
- // Show tasks with effort estimates
469
- const tasksToSplit = await (0, workflow_prompts_1.multiSelectPrompt)("Select tasks to split:", state.tasks.map((t) => ({
470
- name: `${t.title}${t.description ? ` - ${t.description.substring(0, 50)}...` : ""}`,
471
- value: t.id,
472
- })));
601
+ // Handle --split-tasks and --split-all options
602
+ let tasksToSplit;
603
+ if (options.splitAll) {
604
+ tasksToSplit = state.tasks?.map((t) => t.id) || [];
605
+ console.log(chalk_1.default.cyan(` Splitting all ${tasksToSplit.length} tasks`));
606
+ }
607
+ else if (options.splitTasks) {
608
+ tasksToSplit = options.splitTasks.split(",").map((id) => id.trim());
609
+ console.log(chalk_1.default.cyan(` Splitting ${tasksToSplit.length} specified tasks`));
610
+ }
611
+ else {
612
+ const shouldSplit = await (0, workflow_prompts_1.confirmPrompt)("Split any complex tasks into subtasks?", false);
613
+ if (!shouldSplit) {
614
+ console.log(chalk_1.default.gray(" Skipping task splitting"));
615
+ return;
616
+ }
617
+ tasksToSplit = await (0, workflow_prompts_1.multiSelectPrompt)("Select tasks to split:", state.tasks.map((t) => ({
618
+ name: `${t.title}${t.description ? ` - ${t.description.substring(0, 50)}...` : ""}`,
619
+ value: t.id,
620
+ })));
621
+ }
473
622
  if (tasksToSplit.length === 0) {
474
623
  console.log(chalk_1.default.gray(" No tasks selected"));
475
624
  return;
476
625
  }
477
- for (const taskId of tasksToSplit) {
478
- const task = state.tasks.find((t) => t.id === taskId);
479
- if (!task)
480
- continue;
481
- console.log(chalk_1.default.cyan(`\n Splitting: ${task.title}\n`));
482
- const splitMethod = await (0, workflow_prompts_1.selectPrompt)("Split method:", [
483
- { name: "Standard AI split", value: "standard" },
484
- { name: "Custom instructions", value: "custom" },
485
- ]);
486
- let customInstructions;
487
- if (splitMethod === "custom") {
488
- customInstructions = await (0, workflow_prompts_1.textInputPrompt)("Custom instructions (e.g., 'Break into 2-4 hour chunks'):", "");
626
+ let globalSplitMethod = "interactive";
627
+ let globalCustomInstructions;
628
+ if (tasksToSplit.length > 1) {
629
+ globalSplitMethod = await getOrPrompt(options.splitMethod, () => (0, workflow_prompts_1.selectPrompt)("How would you like to split these tasks?", [
630
+ { name: "Interactive (ask for each task)", value: "interactive" },
631
+ { name: "Standard AI split for ALL", value: "standard" },
632
+ { name: "Same custom instructions for ALL", value: "custom" },
633
+ ]));
634
+ if (globalSplitMethod === "custom") {
635
+ globalCustomInstructions = await getOrPrompt(options.splitInstructions, () => (0, workflow_prompts_1.textInputPrompt)("Custom instructions for ALL tasks (e.g., 'Break into 2-4 hour chunks'):", ""));
489
636
  }
490
- try {
491
- const result = await tasks_1.taskService.splitTask(taskId, aiOptions, undefined, // promptOverride
492
- customInstructions, streamingOptions);
493
- console.log(chalk_1.default.green(` āœ“ Created ${result.subtasks.length} subtasks`));
494
- result.subtasks.forEach((subtask, index) => {
637
+ }
638
+ // Collect per-task instructions for interactive mode
639
+ const taskInstructions = {};
640
+ if (globalSplitMethod === "interactive") {
641
+ for (const taskId of tasksToSplit) {
642
+ const task = state.tasks.find((t) => t.id === taskId);
643
+ if (!task)
644
+ continue;
645
+ console.log(chalk_1.default.cyan(`\n Task: ${task.title}\n`));
646
+ const splitMethod = await (0, workflow_prompts_1.selectPrompt)("Split method:", [
647
+ { name: "Standard AI split", value: "standard" },
648
+ { name: "Custom instructions", value: "custom" },
649
+ ]);
650
+ if (splitMethod === "custom") {
651
+ taskInstructions[taskId] = await (0, workflow_prompts_1.textInputPrompt)("Custom instructions (e.g., 'Break into 2-4 hour chunks'):", "");
652
+ }
653
+ }
654
+ }
655
+ // Call service
656
+ const result = await workflow_1.workflowService.splitTasks({
657
+ taskIds: tasksToSplit,
658
+ splitMethod: globalSplitMethod,
659
+ customInstructions: globalCustomInstructions,
660
+ aiOptions: options,
661
+ streamingOptions,
662
+ callbacks: {
663
+ onProgress: progress_1.displayProgress,
664
+ onError: progress_1.displayError,
665
+ },
666
+ });
667
+ // Display results
668
+ result.results.forEach((taskResult) => {
669
+ const task = state.tasks?.find((t) => t.id === taskResult.taskId);
670
+ if (taskResult.error) {
671
+ console.log(chalk_1.default.red(` āœ— Failed to split ${task?.title}: ${taskResult.error}`));
672
+ }
673
+ else {
674
+ console.log(chalk_1.default.green(` āœ“ Split ${task?.title} into ${taskResult.subtasks.length} subtasks`));
675
+ taskResult.subtasks.forEach((subtask, index) => {
495
676
  console.log(chalk_1.default.gray(` ${index + 1}. ${subtask.title}`));
496
677
  });
497
678
  }
498
- catch (error) {
499
- console.log(chalk_1.default.red(` āœ— Failed to split task: ${error}`));
500
- }
501
- }
679
+ });
502
680
  console.log(chalk_1.default.green("\nāœ“ Task splitting complete"));
503
681
  }