@krr2020/taskflow-core 0.1.0-beta.3 → 0.1.0-beta.5
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/README.md +1 -1
- package/dist/cli/index.js +42 -4
- package/dist/commands/base.d.ts +41 -0
- package/dist/commands/base.js +141 -0
- package/dist/commands/configure.d.ts +29 -0
- package/dist/commands/configure.js +187 -0
- package/dist/commands/init.js +21 -7
- package/dist/commands/prd/create.d.ts +1 -1
- package/dist/commands/prd/create.js +29 -11
- package/dist/commands/prd/generate-arch.d.ts +1 -1
- package/dist/commands/prd/generate-arch.js +6 -5
- package/dist/commands/retro/list.js +6 -5
- package/dist/commands/tasks/generate.d.ts +1 -1
- package/dist/commands/tasks/generate.js +83 -56
- package/dist/commands/upgrade.js +49 -16
- package/dist/commands/workflow/check.d.ts +17 -0
- package/dist/commands/workflow/check.js +482 -35
- package/dist/commands/workflow/commit.js +117 -60
- package/dist/commands/workflow/do.d.ts +1 -0
- package/dist/commands/workflow/do.js +206 -13
- package/dist/commands/workflow/next.js +4 -4
- package/dist/commands/workflow/resume.js +9 -6
- package/dist/commands/workflow/start.js +11 -11
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/lib/config-paths.d.ts +15 -15
- package/dist/lib/config-paths.js +20 -15
- package/dist/lib/file-validator.d.ts +119 -0
- package/dist/lib/file-validator.js +291 -0
- package/dist/lib/git.js +4 -2
- package/dist/lib/log-parser.d.ts +91 -0
- package/dist/lib/log-parser.js +178 -0
- package/dist/lib/retrospective.d.ts +27 -0
- package/dist/lib/retrospective.js +111 -1
- package/dist/lib/types.d.ts +19 -6
- package/dist/lib/types.js +20 -1
- package/dist/lib/validation.d.ts +0 -3
- package/dist/lib/validation.js +1 -15
- package/dist/llm/base.d.ts +52 -0
- package/dist/llm/base.js +35 -0
- package/dist/llm/factory.d.ts +39 -0
- package/dist/llm/factory.js +102 -0
- package/dist/llm/index.d.ts +7 -0
- package/dist/llm/index.js +7 -0
- package/dist/llm/model-selector.d.ts +71 -0
- package/dist/llm/model-selector.js +139 -0
- package/dist/llm/providers/anthropic.d.ts +31 -0
- package/dist/llm/providers/anthropic.js +116 -0
- package/dist/llm/providers/index.d.ts +6 -0
- package/dist/llm/providers/index.js +6 -0
- package/dist/llm/providers/ollama.d.ts +28 -0
- package/dist/llm/providers/ollama.js +91 -0
- package/dist/llm/providers/openai-compatible.d.ts +30 -0
- package/dist/llm/providers/openai-compatible.js +93 -0
- package/dist/schemas/config.d.ts +82 -0
- package/dist/schemas/config.js +35 -0
- package/dist/schemas/task.d.ts +2 -2
- package/dist/state-machine.d.ts +12 -0
- package/dist/state-machine.js +2 -2
- package/package.json +1 -1
- package/dist/lib/package-manager.d.ts +0 -17
- package/dist/lib/package-manager.js +0 -53
package/README.md
CHANGED
|
@@ -172,7 +172,7 @@ Most users should use this package via:
|
|
|
172
172
|
- **[@krr2020/taskflow-mcp-server](https://www.npmjs.com/package/@krr2020/taskflow-mcp-server)** - MCP Server for Claude Desktop
|
|
173
173
|
- **CLI** - Direct command-line usage via `npx @krr2020/taskflow-core`
|
|
174
174
|
|
|
175
|
-
See the main [Taskflow documentation](
|
|
175
|
+
See the main [Taskflow documentation](../README.md) for complete usage examples.
|
|
176
176
|
|
|
177
177
|
## 📄 License
|
|
178
178
|
|
package/dist/cli/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
// Import commands
|
|
7
|
+
import { ConfigureAICommand } from "../commands/configure.js";
|
|
7
8
|
import { InitCommand } from "../commands/init.js";
|
|
8
9
|
import { PrdCreateCommand } from "../commands/prd/create.js";
|
|
9
10
|
import { PrdGenerateArchCommand } from "../commands/prd/generate-arch.js";
|
|
@@ -51,6 +52,42 @@ export async function runCLI() {
|
|
|
51
52
|
}
|
|
52
53
|
});
|
|
53
54
|
// ========================================
|
|
55
|
+
// CONFIGURE COMMAND
|
|
56
|
+
// ========================================
|
|
57
|
+
const configureCommand = program
|
|
58
|
+
.command("configure")
|
|
59
|
+
.description("Configure taskflow settings");
|
|
60
|
+
configureCommand
|
|
61
|
+
.command("ai")
|
|
62
|
+
.description("Configure AI/LLM provider for manual command execution")
|
|
63
|
+
.option("--provider <provider>", "Provider type (openai-compatible, anthropic, ollama)")
|
|
64
|
+
.option("--apiKey <key>", "API key for the provider (can use $${ENV_VAR} format)")
|
|
65
|
+
.option("--model <model>", "Default model to use")
|
|
66
|
+
.option("--planning <model>", "Model for planning phase (e.g., claude-opus-4)")
|
|
67
|
+
.option("--execution <model>", "Model for execution phase (e.g., gemini-pro-2.0)")
|
|
68
|
+
.option("--analysis <model>", "Model for analysis phase (e.g., claude-sonnet-4)")
|
|
69
|
+
.option("--planningProvider <provider>", "Different provider for planning")
|
|
70
|
+
.option("--planningApiKey <key>", "API key for planning provider")
|
|
71
|
+
.option("--executionProvider <provider>", "Different provider for execution")
|
|
72
|
+
.option("--executionApiKey <key>", "API key for execution provider")
|
|
73
|
+
.option("--analysisProvider <provider>", "Different provider for analysis")
|
|
74
|
+
.option("--analysisApiKey <key>", "API key for analysis provider")
|
|
75
|
+
.option("--ollamaBaseUrl <url>", "Ollama base URL (default: http://localhost:11434)")
|
|
76
|
+
.option("--openaiBaseUrl <url>", "OpenAI-compatible base URL (default: https://api.openai.com/v1)")
|
|
77
|
+
.option("--enable", "Enable AI features")
|
|
78
|
+
.option("--disable", "Disable AI features")
|
|
79
|
+
.action(async (options) => {
|
|
80
|
+
try {
|
|
81
|
+
const cmd = new ConfigureAICommand(context);
|
|
82
|
+
const result = await cmd.execute(options);
|
|
83
|
+
console.log(formatSuccess(result));
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
handleError(error);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// ========================================
|
|
54
91
|
// UPGRADE COMMAND
|
|
55
92
|
// ========================================
|
|
56
93
|
program
|
|
@@ -232,10 +269,11 @@ export async function runCLI() {
|
|
|
232
269
|
.command("create")
|
|
233
270
|
.description("Create a new PRD")
|
|
234
271
|
.argument("<feature-name>", "Name of the feature")
|
|
235
|
-
.
|
|
272
|
+
.option("--description <desc>", "Feature description/requirements (optional)")
|
|
273
|
+
.action(async (featureName, options) => {
|
|
236
274
|
try {
|
|
237
275
|
const cmd = new PrdCreateCommand(context);
|
|
238
|
-
const result = await cmd.execute(featureName);
|
|
276
|
+
const result = await cmd.execute(featureName, options.description);
|
|
239
277
|
console.log(formatSuccess(result));
|
|
240
278
|
process.exit(0);
|
|
241
279
|
}
|
|
@@ -245,7 +283,7 @@ export async function runCLI() {
|
|
|
245
283
|
});
|
|
246
284
|
prdCommand
|
|
247
285
|
.command("generate-arch")
|
|
248
|
-
.description("Generate
|
|
286
|
+
.description("Generate coding-standards.md and architecture-rules.md from PRD")
|
|
249
287
|
.argument("<prd-file>", "PRD filename")
|
|
250
288
|
.action(async (prdFile) => {
|
|
251
289
|
try {
|
|
@@ -267,7 +305,7 @@ export async function runCLI() {
|
|
|
267
305
|
tasksCommand
|
|
268
306
|
.command("generate")
|
|
269
307
|
.description("Generate task breakdown from PRD")
|
|
270
|
-
.argument("
|
|
308
|
+
.argument("[prd-file]", "PRD filename (optional - shows selection if not provided)")
|
|
271
309
|
.action(async (prdFile) => {
|
|
272
310
|
try {
|
|
273
311
|
const cmd = new TasksGenerateCommand(context);
|
package/dist/commands/base.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Base command infrastructure for AI-first command design
|
|
3
3
|
* Every command returns structured guidance for AI agents
|
|
4
4
|
*/
|
|
5
|
+
import { ConfigLoader } from "../lib/config-loader.js";
|
|
6
|
+
import type { LLMProvider } from "../llm/base.js";
|
|
5
7
|
export interface CommandContext {
|
|
6
8
|
projectRoot: string;
|
|
7
9
|
}
|
|
@@ -16,7 +18,46 @@ export interface CommandResult {
|
|
|
16
18
|
}
|
|
17
19
|
export declare abstract class BaseCommand {
|
|
18
20
|
protected context: CommandContext;
|
|
21
|
+
protected llmProvider: LLMProvider | undefined;
|
|
22
|
+
protected configLoader: ConfigLoader;
|
|
19
23
|
constructor(context: CommandContext);
|
|
24
|
+
/**
|
|
25
|
+
* Initialize LLM provider if configured
|
|
26
|
+
*/
|
|
27
|
+
private initializeLLMProvider;
|
|
28
|
+
/**
|
|
29
|
+
* Check if LLM is available
|
|
30
|
+
*/
|
|
31
|
+
protected isLLMAvailable(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Get LLM guidance for a given context
|
|
34
|
+
* Keeps guidance concise (200 words max)
|
|
35
|
+
*/
|
|
36
|
+
protected getLLMGuidance(context: {
|
|
37
|
+
task?: string;
|
|
38
|
+
status?: string;
|
|
39
|
+
files?: string[];
|
|
40
|
+
errors?: string[];
|
|
41
|
+
instructions?: string;
|
|
42
|
+
}): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Get error analysis with LLM
|
|
45
|
+
* Groups errors by file and provides targeted fixes
|
|
46
|
+
*/
|
|
47
|
+
protected getErrorAnalysis(errors: Array<{
|
|
48
|
+
file: string;
|
|
49
|
+
message: string;
|
|
50
|
+
line?: number;
|
|
51
|
+
code?: string;
|
|
52
|
+
}>): Promise<string>;
|
|
53
|
+
/**
|
|
54
|
+
* Group errors by file
|
|
55
|
+
*/
|
|
56
|
+
private groupErrorsByFile;
|
|
57
|
+
/**
|
|
58
|
+
* Truncate text to maximum word count
|
|
59
|
+
*/
|
|
60
|
+
private truncateToWords;
|
|
20
61
|
abstract execute(...args: unknown[]): Promise<CommandResult>;
|
|
21
62
|
/**
|
|
22
63
|
* Format command result for terminal output
|
package/dist/commands/base.js
CHANGED
|
@@ -2,10 +2,151 @@
|
|
|
2
2
|
* Base command infrastructure for AI-first command design
|
|
3
3
|
* Every command returns structured guidance for AI agents
|
|
4
4
|
*/
|
|
5
|
+
import { ConfigLoader } from "../lib/config-loader.js";
|
|
6
|
+
import { Phase } from "../llm/base.js";
|
|
7
|
+
import { ProviderFactory } from "../llm/factory.js";
|
|
5
8
|
export class BaseCommand {
|
|
6
9
|
context;
|
|
10
|
+
llmProvider;
|
|
11
|
+
configLoader;
|
|
7
12
|
constructor(context) {
|
|
8
13
|
this.context = context;
|
|
14
|
+
this.configLoader = new ConfigLoader(context.projectRoot);
|
|
15
|
+
this.initializeLLMProvider();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Initialize LLM provider if configured
|
|
19
|
+
*/
|
|
20
|
+
initializeLLMProvider() {
|
|
21
|
+
try {
|
|
22
|
+
const config = this.configLoader.load();
|
|
23
|
+
// Check if AI is configured and enabled
|
|
24
|
+
if (config.ai && "enabled" in config.ai && config.ai.enabled) {
|
|
25
|
+
const aiConfig = config.ai;
|
|
26
|
+
const selector = ProviderFactory.createSelector(aiConfig);
|
|
27
|
+
if (selector.isConfigured()) {
|
|
28
|
+
this.llmProvider = selector.getProvider(Phase.Planning);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// LLM provider initialization failed gracefully
|
|
34
|
+
this.llmProvider = void 0;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if LLM is available
|
|
39
|
+
*/
|
|
40
|
+
isLLMAvailable() {
|
|
41
|
+
return this.llmProvider?.isConfigured() === true;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get LLM guidance for a given context
|
|
45
|
+
* Keeps guidance concise (200 words max)
|
|
46
|
+
*/
|
|
47
|
+
async getLLMGuidance(context) {
|
|
48
|
+
if (!this.isLLMAvailable() || !this.llmProvider) {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const { task, status, files, errors, instructions } = context;
|
|
53
|
+
const prompt = `Generate concise guidance (max 200 words) for:
|
|
54
|
+
${task ? `Task: ${task}` : ""}
|
|
55
|
+
${status ? `Status: ${status}` : ""}
|
|
56
|
+
${files && files.length > 0 ? `Files: ${files.slice(0, 5).join(", ")}` : ""}
|
|
57
|
+
${errors && errors.length > 0 ? `Errors: ${errors.slice(0, 3).join("; ")}` : ""}
|
|
58
|
+
${instructions ? `Instructions: ${instructions}` : ""}
|
|
59
|
+
|
|
60
|
+
Provide:
|
|
61
|
+
1. Key context to understand
|
|
62
|
+
2. Critical files to check
|
|
63
|
+
3. Common pitfalls to avoid
|
|
64
|
+
4. Recommended approach
|
|
65
|
+
|
|
66
|
+
Be concise and actionable.`;
|
|
67
|
+
const response = await this.llmProvider.generate([
|
|
68
|
+
{
|
|
69
|
+
role: "system",
|
|
70
|
+
content: "You are a helpful coding assistant providing concise guidance.",
|
|
71
|
+
},
|
|
72
|
+
{ role: "user", content: prompt },
|
|
73
|
+
], { maxTokens: 300, temperature: 0.5 });
|
|
74
|
+
return this.truncateToWords(response.content, 200);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// LLM call failed, return empty string
|
|
78
|
+
return "";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get error analysis with LLM
|
|
83
|
+
* Groups errors by file and provides targeted fixes
|
|
84
|
+
*/
|
|
85
|
+
async getErrorAnalysis(errors) {
|
|
86
|
+
if (!this.isLLMAvailable() || !this.llmProvider || errors.length === 0) {
|
|
87
|
+
return "";
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const groupedErrors = this.groupErrorsByFile(errors);
|
|
91
|
+
const prompt = `Analyze these errors and provide targeted fixes:
|
|
92
|
+
|
|
93
|
+
${Object.entries(groupedErrors)
|
|
94
|
+
.map(([file, fileErrors]) => `\n${file}:\n${fileErrors.map((e) => ` ${e.message}`).join("\n")}`)
|
|
95
|
+
.join("\n")}
|
|
96
|
+
|
|
97
|
+
Provide:
|
|
98
|
+
1. Root cause analysis per file
|
|
99
|
+
2. Specific fix suggestions
|
|
100
|
+
3. File-by-file approach to resolve
|
|
101
|
+
|
|
102
|
+
Be concise and actionable.`;
|
|
103
|
+
const response = await this.llmProvider.generate([
|
|
104
|
+
{
|
|
105
|
+
role: "system",
|
|
106
|
+
content: "You are a debugging assistant providing error analysis.",
|
|
107
|
+
},
|
|
108
|
+
{ role: "user", content: prompt },
|
|
109
|
+
], { maxTokens: 500, temperature: 0.3 });
|
|
110
|
+
return response.content;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return "";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Group errors by file
|
|
118
|
+
*/
|
|
119
|
+
groupErrorsByFile(errors) {
|
|
120
|
+
const grouped = {};
|
|
121
|
+
for (const error of errors) {
|
|
122
|
+
if (!grouped[error.file]) {
|
|
123
|
+
grouped[error.file] = [];
|
|
124
|
+
}
|
|
125
|
+
const entry = {
|
|
126
|
+
message: error.message,
|
|
127
|
+
};
|
|
128
|
+
if (error.line !== undefined) {
|
|
129
|
+
entry.line = error.line;
|
|
130
|
+
}
|
|
131
|
+
if (error.code !== undefined) {
|
|
132
|
+
entry.code = error.code;
|
|
133
|
+
}
|
|
134
|
+
const array = grouped[error.file];
|
|
135
|
+
if (array) {
|
|
136
|
+
array.push(entry);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return grouped;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Truncate text to maximum word count
|
|
143
|
+
*/
|
|
144
|
+
truncateToWords(text, maxWords) {
|
|
145
|
+
const words = text.split(/\s+/);
|
|
146
|
+
if (words.length <= maxWords) {
|
|
147
|
+
return text;
|
|
148
|
+
}
|
|
149
|
+
return words.slice(0, maxWords).join(" ");
|
|
9
150
|
}
|
|
10
151
|
/**
|
|
11
152
|
* Format command result for terminal output
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configure AI command
|
|
3
|
+
* Configure LLM provider and per-phase model selection
|
|
4
|
+
*/
|
|
5
|
+
import { BaseCommand, type CommandResult } from "./base.js";
|
|
6
|
+
export declare class ConfigureAICommand extends BaseCommand {
|
|
7
|
+
execute(options: {
|
|
8
|
+
provider?: string;
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
planning?: string;
|
|
12
|
+
execution?: string;
|
|
13
|
+
analysis?: string;
|
|
14
|
+
planningProvider?: string;
|
|
15
|
+
planningApiKey?: string;
|
|
16
|
+
executionProvider?: string;
|
|
17
|
+
executionApiKey?: string;
|
|
18
|
+
analysisProvider?: string;
|
|
19
|
+
analysisApiKey?: string;
|
|
20
|
+
ollamaBaseUrl?: string;
|
|
21
|
+
openaiBaseUrl?: string;
|
|
22
|
+
enable?: boolean;
|
|
23
|
+
disable?: boolean;
|
|
24
|
+
}): Promise<CommandResult>;
|
|
25
|
+
private saveConfig;
|
|
26
|
+
private formatConfigOutput;
|
|
27
|
+
private generateNextSteps;
|
|
28
|
+
private getWarnings;
|
|
29
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configure AI command
|
|
3
|
+
* Configure LLM provider and per-phase model selection
|
|
4
|
+
*/
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { ProviderFactory } from "../llm/factory.js";
|
|
8
|
+
import { BaseCommand } from "./base.js";
|
|
9
|
+
export class ConfigureAICommand extends BaseCommand {
|
|
10
|
+
async execute(options) {
|
|
11
|
+
const configPath = path.join(this.context.projectRoot, "taskflow.config.json");
|
|
12
|
+
if (!fs.existsSync(configPath)) {
|
|
13
|
+
return this.failure("Configuration file not found", [
|
|
14
|
+
`taskflow.config.json not found in ${this.context.projectRoot}`,
|
|
15
|
+
"Run 'taskflow init' to initialize the project first",
|
|
16
|
+
], "Initialize taskflow with 'taskflow init'");
|
|
17
|
+
}
|
|
18
|
+
// Load existing config
|
|
19
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
20
|
+
const config = JSON.parse(raw);
|
|
21
|
+
// Check for enable/disable flags
|
|
22
|
+
if (options.disable) {
|
|
23
|
+
config.ai = config.ai || {};
|
|
24
|
+
config.ai.enabled = false;
|
|
25
|
+
this.saveConfig(config, configPath);
|
|
26
|
+
return this.success("AI configuration disabled", "AI features will not be used. Commands will show guidance only.", {
|
|
27
|
+
aiGuidance: "To re-enable, run: taskflow configure ai --enable\n" +
|
|
28
|
+
"Or configure a provider: taskflow configure ai --provider anthropic --apiKey $${ANTHROPIC_API_KEY}",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (options.enable) {
|
|
32
|
+
config.ai = config.ai || { enabled: true };
|
|
33
|
+
config.ai.enabled = true;
|
|
34
|
+
this.saveConfig(config, configPath);
|
|
35
|
+
return this.success("AI configuration enabled", "AI features will be used when properly configured with a provider.", {
|
|
36
|
+
aiGuidance: "Configure a provider: taskflow configure ai --provider anthropic --apiKey $${ANTHROPIC_API_KEY}",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// Ensure ai config exists
|
|
40
|
+
if (!config.ai) {
|
|
41
|
+
config.ai = {
|
|
42
|
+
enabled: true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Update basic configuration
|
|
46
|
+
if (options.provider) {
|
|
47
|
+
config.ai.provider = options.provider;
|
|
48
|
+
config.ai.enabled = true;
|
|
49
|
+
}
|
|
50
|
+
if (options.apiKey) {
|
|
51
|
+
config.ai.apiKey = options.apiKey;
|
|
52
|
+
}
|
|
53
|
+
if (options.model) {
|
|
54
|
+
config.ai.models = config.ai.models || { default: "gpt-4o-mini" };
|
|
55
|
+
config.ai.models.default = options.model;
|
|
56
|
+
}
|
|
57
|
+
// Update per-phase models
|
|
58
|
+
if (options.planning || options.execution || options.analysis) {
|
|
59
|
+
config.ai.models = config.ai.models || { default: "gpt-4o-mini" };
|
|
60
|
+
const models = config.ai.models;
|
|
61
|
+
if (options.planning) {
|
|
62
|
+
models.planning = options.planning;
|
|
63
|
+
}
|
|
64
|
+
if (options.execution) {
|
|
65
|
+
models.execution = options.execution;
|
|
66
|
+
}
|
|
67
|
+
if (options.analysis) {
|
|
68
|
+
models.analysis = options.analysis;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Update per-phase providers
|
|
72
|
+
if (options.planningProvider ||
|
|
73
|
+
options.executionProvider ||
|
|
74
|
+
options.analysisProvider) {
|
|
75
|
+
if (options.planningProvider) {
|
|
76
|
+
config.ai.planningProvider = options.planningProvider;
|
|
77
|
+
}
|
|
78
|
+
if (options.executionProvider) {
|
|
79
|
+
config.ai.executionProvider = options.executionProvider;
|
|
80
|
+
}
|
|
81
|
+
if (options.analysisProvider) {
|
|
82
|
+
config.ai.analysisProvider = options.analysisProvider;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Update per-phase API keys
|
|
86
|
+
if (options.planningApiKey ||
|
|
87
|
+
options.executionApiKey ||
|
|
88
|
+
options.analysisApiKey) {
|
|
89
|
+
if (options.planningApiKey) {
|
|
90
|
+
config.ai.planningApiKey = options.planningApiKey;
|
|
91
|
+
}
|
|
92
|
+
if (options.executionApiKey) {
|
|
93
|
+
config.ai.executionApiKey = options.executionApiKey;
|
|
94
|
+
}
|
|
95
|
+
if (options.analysisApiKey) {
|
|
96
|
+
config.ai.analysisApiKey = options.analysisApiKey;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Update base URLs
|
|
100
|
+
if (options.ollamaBaseUrl) {
|
|
101
|
+
config.ai.ollamaBaseUrl = options.ollamaBaseUrl;
|
|
102
|
+
}
|
|
103
|
+
if (options.openaiBaseUrl) {
|
|
104
|
+
config.ai.openaiBaseUrl = options.openaiBaseUrl;
|
|
105
|
+
}
|
|
106
|
+
// Save config
|
|
107
|
+
this.saveConfig(config, configPath);
|
|
108
|
+
// Generate output
|
|
109
|
+
const output = this.formatConfigOutput(config.ai);
|
|
110
|
+
const nextSteps = this.generateNextSteps(config.ai);
|
|
111
|
+
return this.success("AI configuration updated successfully", nextSteps, {
|
|
112
|
+
aiGuidance: output,
|
|
113
|
+
warnings: this.getWarnings(config.ai),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
saveConfig(config, configPath) {
|
|
117
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
118
|
+
}
|
|
119
|
+
formatConfigOutput(aiConfig) {
|
|
120
|
+
const lines = [];
|
|
121
|
+
lines.push("AI Configuration:");
|
|
122
|
+
lines.push("─".repeat(60));
|
|
123
|
+
lines.push(`Enabled: ${aiConfig.enabled ? "✓" : "✗"}`);
|
|
124
|
+
lines.push(`Provider: ${aiConfig.provider || "Not configured"}`);
|
|
125
|
+
lines.push(`API Key: ${aiConfig.apiKey ? "***configured***" : "Not configured"}`);
|
|
126
|
+
lines.push("");
|
|
127
|
+
lines.push("Models:");
|
|
128
|
+
const models = aiConfig.models;
|
|
129
|
+
lines.push(` Default: ${models?.default || "Not configured"}`);
|
|
130
|
+
lines.push(` Planning: ${models?.planning || "Uses default"}`);
|
|
131
|
+
lines.push(` Execution: ${models?.execution || "Uses default"}`);
|
|
132
|
+
lines.push(` Analysis: ${models?.analysis || "Uses default"}`);
|
|
133
|
+
lines.push("");
|
|
134
|
+
lines.push("Per-Phase Providers:");
|
|
135
|
+
lines.push(` Planning: ${aiConfig.planningProvider || "Uses default provider"}`);
|
|
136
|
+
lines.push(` Execution: ${aiConfig.executionProvider || "Uses default provider"}`);
|
|
137
|
+
lines.push(` Analysis: ${aiConfig.analysisProvider || "Uses default provider"}`);
|
|
138
|
+
lines.push("");
|
|
139
|
+
lines.push("Base URLs:");
|
|
140
|
+
lines.push(` Ollama: ${aiConfig.ollamaBaseUrl || "http://localhost:11434"}`);
|
|
141
|
+
lines.push(` OpenAI: ${aiConfig.openaiBaseUrl || "https://api.openai.com/v1"}`);
|
|
142
|
+
return lines.join("\n");
|
|
143
|
+
}
|
|
144
|
+
generateNextSteps(aiConfig) {
|
|
145
|
+
if (!aiConfig.enabled) {
|
|
146
|
+
return "Enable AI features: taskflow configure ai --enable";
|
|
147
|
+
}
|
|
148
|
+
if (!aiConfig.provider || !aiConfig.apiKey) {
|
|
149
|
+
const availableProviders = ProviderFactory.getAvailableProviders();
|
|
150
|
+
const examples = [];
|
|
151
|
+
if (availableProviders.includes("anthropic")) {
|
|
152
|
+
examples.push(" taskflow configure ai --provider anthropic --apiKey $${ANTHROPIC_API_KEY}");
|
|
153
|
+
}
|
|
154
|
+
if (availableProviders.includes("openai-compatible")) {
|
|
155
|
+
examples.push(" taskflow configure ai --provider openai-compatible --apiKey $${OPENAI_API_KEY}");
|
|
156
|
+
}
|
|
157
|
+
if (availableProviders.includes("ollama")) {
|
|
158
|
+
examples.push(" taskflow configure ai --provider ollama");
|
|
159
|
+
}
|
|
160
|
+
return `Configure a provider:\n${examples.join("\n")}`;
|
|
161
|
+
}
|
|
162
|
+
const models = aiConfig.models;
|
|
163
|
+
if (!models?.planning && !models?.execution && !models?.analysis) {
|
|
164
|
+
return ("Configure per-phase models (optional):\n" +
|
|
165
|
+
" taskflow configure ai --planning claude-opus-4 --execution gemini-pro-2.0 --analysis claude-sonnet-4-20250514");
|
|
166
|
+
}
|
|
167
|
+
return "AI is fully configured. Try running: taskflow tasks generate";
|
|
168
|
+
}
|
|
169
|
+
getWarnings(aiConfig) {
|
|
170
|
+
const warnings = [];
|
|
171
|
+
if (!aiConfig.enabled) {
|
|
172
|
+
warnings.push("AI is disabled. Commands will show guidance only.");
|
|
173
|
+
}
|
|
174
|
+
if (!aiConfig.provider) {
|
|
175
|
+
warnings.push("No provider configured. Set one with --provider option.");
|
|
176
|
+
}
|
|
177
|
+
if (aiConfig.provider &&
|
|
178
|
+
!aiConfig.apiKey &&
|
|
179
|
+
aiConfig.provider !== "ollama") {
|
|
180
|
+
warnings.push("API key not configured for this provider.");
|
|
181
|
+
}
|
|
182
|
+
if (aiConfig.provider === "ollama" && !aiConfig.ollamaBaseUrl) {
|
|
183
|
+
warnings.push("Ollama base URL not configured. Using default: http://localhost:11434");
|
|
184
|
+
}
|
|
185
|
+
return warnings;
|
|
186
|
+
}
|
|
187
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -16,7 +16,21 @@ export class InitCommand extends BaseCommand {
|
|
|
16
16
|
configLoader.getConfigPath(),
|
|
17
17
|
], "Run 'taskflow status' to view your project state.");
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
// Try to get project name from package.json, then use provided name, then fallback to directory name
|
|
20
|
+
let detectedProjectName;
|
|
21
|
+
const packageJsonPath = path.join(this.context.projectRoot, "package.json");
|
|
22
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
23
|
+
try {
|
|
24
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
25
|
+
detectedProjectName = packageJson.name;
|
|
26
|
+
}
|
|
27
|
+
catch (_error) {
|
|
28
|
+
// If package.json is invalid, continue to fallback
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const finalProjectName = projectName ||
|
|
32
|
+
detectedProjectName ||
|
|
33
|
+
path.basename(this.context.projectRoot);
|
|
20
34
|
// Create default configuration
|
|
21
35
|
const config = ConfigLoader.createDefaultConfig(finalProjectName);
|
|
22
36
|
configLoader.save(config);
|
|
@@ -120,7 +134,7 @@ export class InitCommand extends BaseCommand {
|
|
|
120
134
|
"────────────────────",
|
|
121
135
|
"A PRD (Product Requirements Document) defines what you're building.",
|
|
122
136
|
"The AI will help you create it by:",
|
|
123
|
-
"1. Reading .taskflow/ref/
|
|
137
|
+
"1. Reading .taskflow/ref/prd-generator.md for guidelines",
|
|
124
138
|
"2. Gathering requirements through conversation",
|
|
125
139
|
"3. Creating a structured PRD document",
|
|
126
140
|
"",
|
|
@@ -128,15 +142,15 @@ export class InitCommand extends BaseCommand {
|
|
|
128
142
|
"that you can execute step-by-step.",
|
|
129
143
|
].join("\n"),
|
|
130
144
|
contextFiles: [
|
|
131
|
-
".taskflow/ref/
|
|
132
|
-
".taskflow/ref/
|
|
133
|
-
".taskflow/ref/
|
|
134
|
-
".taskflow/ref/
|
|
145
|
+
".taskflow/ref/ai-protocol.md - Core AI operating discipline",
|
|
146
|
+
".taskflow/ref/prd-generator.md - PRD creation guidelines",
|
|
147
|
+
".taskflow/ref/task-generator.md - Task breakdown guidelines",
|
|
148
|
+
".taskflow/ref/retrospective.md - Known error patterns",
|
|
135
149
|
],
|
|
136
150
|
warnings: [
|
|
137
151
|
"NEVER edit files in .taskflow/ or tasks/ directories directly",
|
|
138
152
|
"ALWAYS use taskflow commands for task management",
|
|
139
|
-
"Read
|
|
153
|
+
"Read ai-protocol.md before starting any task",
|
|
140
154
|
],
|
|
141
155
|
});
|
|
142
156
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { BaseCommand, type CommandResult } from "../base.js";
|
|
5
5
|
export declare class PrdCreateCommand extends BaseCommand {
|
|
6
|
-
execute(featureName: string): Promise<CommandResult>;
|
|
6
|
+
execute(featureName: string, description?: string): Promise<CommandResult>;
|
|
7
7
|
private generatePrdTemplate;
|
|
8
8
|
}
|
|
@@ -7,7 +7,7 @@ import { ConfigLoader } from "../../lib/config-loader.js";
|
|
|
7
7
|
import { getRefFilePath, REF_FILES } from "../../lib/config-paths.js";
|
|
8
8
|
import { BaseCommand } from "../base.js";
|
|
9
9
|
export class PrdCreateCommand extends BaseCommand {
|
|
10
|
-
async execute(featureName) {
|
|
10
|
+
async execute(featureName, description) {
|
|
11
11
|
const configLoader = new ConfigLoader(this.context.projectRoot);
|
|
12
12
|
const paths = configLoader.getPaths();
|
|
13
13
|
// Validate feature name
|
|
@@ -44,24 +44,39 @@ export class PrdCreateCommand extends BaseCommand {
|
|
|
44
44
|
].join("\n"));
|
|
45
45
|
}
|
|
46
46
|
// Create PRD template
|
|
47
|
-
const prdTemplate = this.generatePrdTemplate(featureName);
|
|
47
|
+
const prdTemplate = this.generatePrdTemplate(featureName, description);
|
|
48
48
|
// Write PRD file
|
|
49
49
|
fs.writeFileSync(prdFilePath, prdTemplate, "utf-8");
|
|
50
|
-
|
|
50
|
+
const initialRequirements = description
|
|
51
|
+
? [
|
|
52
|
+
"",
|
|
53
|
+
"INITIAL REQUIREMENTS PROVIDED:",
|
|
54
|
+
"───────────────────────────────",
|
|
55
|
+
description,
|
|
56
|
+
"",
|
|
57
|
+
"Use this as a starting point for the PRD.",
|
|
58
|
+
]
|
|
59
|
+
: [];
|
|
60
|
+
const nextStepsBase = [
|
|
51
61
|
`✓ PRD created: ${prdFilename}`,
|
|
52
62
|
`✓ Location: ${prdFilePath}`,
|
|
53
63
|
"",
|
|
54
64
|
"NEXT:",
|
|
55
65
|
"─".repeat(60),
|
|
56
66
|
"1. Fill out the PRD document with feature requirements",
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
];
|
|
68
|
+
if (description) {
|
|
69
|
+
nextStepsBase.push(" (Initial requirements already provided)");
|
|
70
|
+
}
|
|
71
|
+
nextStepsBase.push("2. Generate coding standards and architecture rules");
|
|
72
|
+
nextStepsBase.push("3. Generate task breakdown from PRD");
|
|
73
|
+
return this.success(nextStepsBase.join("\n"), [
|
|
60
74
|
"1. Edit the PRD file to add feature details:",
|
|
61
75
|
` Open: ${prdFilePath}`,
|
|
76
|
+
...initialRequirements,
|
|
62
77
|
"",
|
|
63
78
|
"2. Use AI to help fill out the PRD:",
|
|
64
|
-
" - Read .taskflow/ref/
|
|
79
|
+
" - Read .taskflow/ref/prd-generator.md for guidance",
|
|
65
80
|
" - Gather requirements through conversation",
|
|
66
81
|
" - Document goals, user stories, and acceptance criteria",
|
|
67
82
|
"",
|
|
@@ -109,7 +124,7 @@ export class PrdCreateCommand extends BaseCommand {
|
|
|
109
124
|
"",
|
|
110
125
|
"IMPORTANT:",
|
|
111
126
|
"───────────",
|
|
112
|
-
"Do NOT create
|
|
127
|
+
"Do NOT create coding-standards.md or architecture-rules.md yet.",
|
|
113
128
|
"Those will be generated in the next step using:",
|
|
114
129
|
` taskflow prd generate-arch ${prdFilename}`,
|
|
115
130
|
"",
|
|
@@ -127,14 +142,17 @@ export class PrdCreateCommand extends BaseCommand {
|
|
|
127
142
|
`${getRefFilePath(paths.refDir, REF_FILES.aiProtocol)} - Core AI operating discipline`,
|
|
128
143
|
],
|
|
129
144
|
warnings: [
|
|
130
|
-
"DO NOT skip the
|
|
145
|
+
"DO NOT skip the prd-generator.md - it contains critical guidance",
|
|
131
146
|
"DO NOT guess at requirements - ask the user for clarification",
|
|
132
147
|
"DO NOT create coding standards yet - wait for generate-arch command",
|
|
133
148
|
"DO ensure PRD is complete before generating tasks",
|
|
134
149
|
],
|
|
135
150
|
});
|
|
136
151
|
}
|
|
137
|
-
generatePrdTemplate(featureName) {
|
|
152
|
+
generatePrdTemplate(featureName, description) {
|
|
153
|
+
const problemStatement = description
|
|
154
|
+
? description.trim()
|
|
155
|
+
: "<!-- What problem does this feature solve? -->";
|
|
138
156
|
return `# PRD: ${featureName}
|
|
139
157
|
|
|
140
158
|
**Created:** ${new Date().toISOString().split("T")[0]}
|
|
@@ -146,7 +164,7 @@ export class PrdCreateCommand extends BaseCommand {
|
|
|
146
164
|
## 1. Overview
|
|
147
165
|
|
|
148
166
|
### Problem Statement
|
|
149
|
-
|
|
167
|
+
${problemStatement}
|
|
150
168
|
|
|
151
169
|
### Goals
|
|
152
170
|
<!-- What are we trying to achieve? -->
|