@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.
Files changed (62) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/index.js +42 -4
  3. package/dist/commands/base.d.ts +41 -0
  4. package/dist/commands/base.js +141 -0
  5. package/dist/commands/configure.d.ts +29 -0
  6. package/dist/commands/configure.js +187 -0
  7. package/dist/commands/init.js +21 -7
  8. package/dist/commands/prd/create.d.ts +1 -1
  9. package/dist/commands/prd/create.js +29 -11
  10. package/dist/commands/prd/generate-arch.d.ts +1 -1
  11. package/dist/commands/prd/generate-arch.js +6 -5
  12. package/dist/commands/retro/list.js +6 -5
  13. package/dist/commands/tasks/generate.d.ts +1 -1
  14. package/dist/commands/tasks/generate.js +83 -56
  15. package/dist/commands/upgrade.js +49 -16
  16. package/dist/commands/workflow/check.d.ts +17 -0
  17. package/dist/commands/workflow/check.js +482 -35
  18. package/dist/commands/workflow/commit.js +117 -60
  19. package/dist/commands/workflow/do.d.ts +1 -0
  20. package/dist/commands/workflow/do.js +206 -13
  21. package/dist/commands/workflow/next.js +4 -4
  22. package/dist/commands/workflow/resume.js +9 -6
  23. package/dist/commands/workflow/start.js +11 -11
  24. package/dist/index.d.ts +4 -0
  25. package/dist/index.js +6 -0
  26. package/dist/lib/config-paths.d.ts +15 -15
  27. package/dist/lib/config-paths.js +20 -15
  28. package/dist/lib/file-validator.d.ts +119 -0
  29. package/dist/lib/file-validator.js +291 -0
  30. package/dist/lib/git.js +4 -2
  31. package/dist/lib/log-parser.d.ts +91 -0
  32. package/dist/lib/log-parser.js +178 -0
  33. package/dist/lib/retrospective.d.ts +27 -0
  34. package/dist/lib/retrospective.js +111 -1
  35. package/dist/lib/types.d.ts +19 -6
  36. package/dist/lib/types.js +20 -1
  37. package/dist/lib/validation.d.ts +0 -3
  38. package/dist/lib/validation.js +1 -15
  39. package/dist/llm/base.d.ts +52 -0
  40. package/dist/llm/base.js +35 -0
  41. package/dist/llm/factory.d.ts +39 -0
  42. package/dist/llm/factory.js +102 -0
  43. package/dist/llm/index.d.ts +7 -0
  44. package/dist/llm/index.js +7 -0
  45. package/dist/llm/model-selector.d.ts +71 -0
  46. package/dist/llm/model-selector.js +139 -0
  47. package/dist/llm/providers/anthropic.d.ts +31 -0
  48. package/dist/llm/providers/anthropic.js +116 -0
  49. package/dist/llm/providers/index.d.ts +6 -0
  50. package/dist/llm/providers/index.js +6 -0
  51. package/dist/llm/providers/ollama.d.ts +28 -0
  52. package/dist/llm/providers/ollama.js +91 -0
  53. package/dist/llm/providers/openai-compatible.d.ts +30 -0
  54. package/dist/llm/providers/openai-compatible.js +93 -0
  55. package/dist/schemas/config.d.ts +82 -0
  56. package/dist/schemas/config.js +35 -0
  57. package/dist/schemas/task.d.ts +2 -2
  58. package/dist/state-machine.d.ts +12 -0
  59. package/dist/state-machine.js +2 -2
  60. package/package.json +1 -1
  61. package/dist/lib/package-manager.d.ts +0 -17
  62. package/dist/lib/package-manager.js +0 -53
@@ -0,0 +1,93 @@
1
+ /**
2
+ * OpenAI-compatible LLM Provider
3
+ * Supports: OpenAI, Azure OpenAI, Together AI, Groq, DeepSeek, and any OpenAI-compatible API
4
+ */
5
+ import { LLMProvider, LLMProviderType, } from "../base.js";
6
+ export class OpenAICompatibleProvider extends LLMProvider {
7
+ config;
8
+ constructor(config) {
9
+ super(LLMProviderType.OpenAICompatible, config.model);
10
+ this.config = config;
11
+ }
12
+ /**
13
+ * Generate text using OpenAI-compatible API
14
+ */
15
+ async generate(messages, options) {
16
+ if (!this.isConfigured()) {
17
+ throw new Error("OpenAI-compatible provider is not configured properly");
18
+ }
19
+ const response = await fetch(`${this.config.baseUrl}/chat/completions`, {
20
+ method: "POST",
21
+ headers: {
22
+ "Content-Type": "application/json",
23
+ Authorization: `Bearer ${this.config.apiKey}`,
24
+ },
25
+ body: JSON.stringify({
26
+ model: this.config.model,
27
+ messages,
28
+ max_tokens: options?.maxTokens,
29
+ temperature: options?.temperature,
30
+ top_p: options?.topP,
31
+ stream: false,
32
+ }),
33
+ });
34
+ if (!response.ok) {
35
+ const error = await response.text();
36
+ throw new Error(`OpenAI-compatible API error: ${response.status} - ${error}`);
37
+ }
38
+ const data = (await response.json());
39
+ const choice = data.choices[0];
40
+ if (!choice) {
41
+ throw new Error("No choice returned from OpenAI API");
42
+ }
43
+ return {
44
+ content: choice.message.content,
45
+ model: data.model,
46
+ tokensUsed: data.usage?.total_tokens ?? 0,
47
+ finishReason: choice.finish_reason,
48
+ };
49
+ }
50
+ /**
51
+ * Check if provider is configured
52
+ */
53
+ isConfigured() {
54
+ return !!(this.config.apiKey && this.config.baseUrl);
55
+ }
56
+ /**
57
+ * Create provider from environment variables
58
+ */
59
+ static fromEnv(config) {
60
+ const baseUrl = config.baseUrl ||
61
+ process.env.OPENAI_BASE_URL ||
62
+ process.env.AI_BASE_URL ||
63
+ "https://api.openai.com/v1";
64
+ const apiKey = config.apiKey ||
65
+ process.env.OPENAI_API_KEY ||
66
+ process.env.AI_API_KEY ||
67
+ "";
68
+ const model = config.model || process.env.AI_MODEL || "gpt-4o-mini";
69
+ if (!apiKey) {
70
+ console.warn("Warning: OpenAI-compatible provider missing API key");
71
+ }
72
+ return new OpenAICompatibleProvider({
73
+ baseUrl,
74
+ apiKey: expandEnvVar(apiKey),
75
+ model,
76
+ });
77
+ }
78
+ }
79
+ /**
80
+ * Expand environment variable in string (e.g., "${VAR_NAME}" -> actual value)
81
+ */
82
+ function expandEnvVar(value) {
83
+ if (!value) {
84
+ return value;
85
+ }
86
+ const envVarMatch = value.match(/^\$\{([^}]+)\}$/);
87
+ if (envVarMatch?.[1]) {
88
+ const envVar = envVarMatch[1];
89
+ const envValue = process.env[envVar];
90
+ return envValue ?? value;
91
+ }
92
+ return value;
93
+ }
@@ -14,6 +14,48 @@ export declare const GatesSchema: z.ZodObject<{
14
14
  requirePlanApproval: z.ZodDefault<z.ZodBoolean>;
15
15
  requireTestPass: z.ZodDefault<z.ZodBoolean>;
16
16
  }, z.core.$strip>;
17
+ declare const LLMProviderTypeSchema: z.ZodEnum<{
18
+ "openai-compatible": "openai-compatible";
19
+ anthropic: "anthropic";
20
+ ollama: "ollama";
21
+ }>;
22
+ declare const AIConfigSchema: z.ZodOptional<z.ZodObject<{
23
+ enabled: z.ZodDefault<z.ZodBoolean>;
24
+ provider: z.ZodDefault<z.ZodEnum<{
25
+ "openai-compatible": "openai-compatible";
26
+ anthropic: "anthropic";
27
+ ollama: "ollama";
28
+ }>>;
29
+ apiKey: z.ZodOptional<z.ZodString>;
30
+ models: z.ZodDefault<z.ZodObject<{
31
+ default: z.ZodDefault<z.ZodString>;
32
+ planning: z.ZodOptional<z.ZodString>;
33
+ execution: z.ZodOptional<z.ZodString>;
34
+ analysis: z.ZodOptional<z.ZodString>;
35
+ }, z.core.$strip>>;
36
+ planningProvider: z.ZodOptional<z.ZodEnum<{
37
+ "openai-compatible": "openai-compatible";
38
+ anthropic: "anthropic";
39
+ ollama: "ollama";
40
+ }>>;
41
+ planningApiKey: z.ZodOptional<z.ZodString>;
42
+ executionProvider: z.ZodOptional<z.ZodEnum<{
43
+ "openai-compatible": "openai-compatible";
44
+ anthropic: "anthropic";
45
+ ollama: "ollama";
46
+ }>>;
47
+ executionApiKey: z.ZodOptional<z.ZodString>;
48
+ analysisProvider: z.ZodOptional<z.ZodEnum<{
49
+ "openai-compatible": "openai-compatible";
50
+ anthropic: "anthropic";
51
+ ollama: "ollama";
52
+ }>>;
53
+ analysisApiKey: z.ZodOptional<z.ZodString>;
54
+ ollamaBaseUrl: z.ZodDefault<z.ZodString>;
55
+ openaiBaseUrl: z.ZodDefault<z.ZodString>;
56
+ autoContinueTask: z.ZodDefault<z.ZodBoolean>;
57
+ clearContextOnComplete: z.ZodDefault<z.ZodBoolean>;
58
+ }, z.core.$strip>>;
17
59
  export declare const TaskflowConfigSchema: z.ZodObject<{
18
60
  version: z.ZodDefault<z.ZodString>;
19
61
  projectType: z.ZodDefault<z.ZodString>;
@@ -36,5 +78,45 @@ export declare const TaskflowConfigSchema: z.ZodObject<{
36
78
  validate: z.ZodOptional<z.ZodString>;
37
79
  test: z.ZodOptional<z.ZodString>;
38
80
  }, z.core.$strip>>;
81
+ ai: z.ZodOptional<z.ZodObject<{
82
+ enabled: z.ZodDefault<z.ZodBoolean>;
83
+ provider: z.ZodDefault<z.ZodEnum<{
84
+ "openai-compatible": "openai-compatible";
85
+ anthropic: "anthropic";
86
+ ollama: "ollama";
87
+ }>>;
88
+ apiKey: z.ZodOptional<z.ZodString>;
89
+ models: z.ZodDefault<z.ZodObject<{
90
+ default: z.ZodDefault<z.ZodString>;
91
+ planning: z.ZodOptional<z.ZodString>;
92
+ execution: z.ZodOptional<z.ZodString>;
93
+ analysis: z.ZodOptional<z.ZodString>;
94
+ }, z.core.$strip>>;
95
+ planningProvider: z.ZodOptional<z.ZodEnum<{
96
+ "openai-compatible": "openai-compatible";
97
+ anthropic: "anthropic";
98
+ ollama: "ollama";
99
+ }>>;
100
+ planningApiKey: z.ZodOptional<z.ZodString>;
101
+ executionProvider: z.ZodOptional<z.ZodEnum<{
102
+ "openai-compatible": "openai-compatible";
103
+ anthropic: "anthropic";
104
+ ollama: "ollama";
105
+ }>>;
106
+ executionApiKey: z.ZodOptional<z.ZodString>;
107
+ analysisProvider: z.ZodOptional<z.ZodEnum<{
108
+ "openai-compatible": "openai-compatible";
109
+ anthropic: "anthropic";
110
+ ollama: "ollama";
111
+ }>>;
112
+ analysisApiKey: z.ZodOptional<z.ZodString>;
113
+ ollamaBaseUrl: z.ZodDefault<z.ZodString>;
114
+ openaiBaseUrl: z.ZodDefault<z.ZodString>;
115
+ autoContinueTask: z.ZodDefault<z.ZodBoolean>;
116
+ clearContextOnComplete: z.ZodDefault<z.ZodBoolean>;
117
+ }, z.core.$strip>>;
39
118
  }, z.core.$strip>;
40
119
  export type TaskflowConfig = z.infer<typeof TaskflowConfigSchema>;
120
+ export type AIConfig = z.infer<typeof AIConfigSchema>;
121
+ export type LLMProviderType = z.infer<typeof LLMProviderTypeSchema>;
122
+ export {};
@@ -12,6 +12,40 @@ export const GatesSchema = z.object({
12
12
  requirePlanApproval: z.boolean().default(true),
13
13
  requireTestPass: z.boolean().default(true),
14
14
  });
15
+ // ============================================================================
16
+ // AI Configuration Schema
17
+ // ============================================================================
18
+ const LLMProviderTypeSchema = z.enum([
19
+ "openai-compatible",
20
+ "anthropic",
21
+ "ollama",
22
+ ]);
23
+ const AIModelsSchema = z.object({
24
+ default: z.string().default("gpt-4o-mini"),
25
+ planning: z.string().optional(),
26
+ execution: z.string().optional(),
27
+ analysis: z.string().optional(),
28
+ });
29
+ const AIConfigSchema = z
30
+ .object({
31
+ enabled: z.boolean().default(false),
32
+ provider: LLMProviderTypeSchema.default("openai-compatible"),
33
+ apiKey: z.string().optional(),
34
+ models: AIModelsSchema.default({
35
+ default: "gpt-4o-mini",
36
+ }),
37
+ planningProvider: LLMProviderTypeSchema.optional(),
38
+ planningApiKey: z.string().optional(),
39
+ executionProvider: LLMProviderTypeSchema.optional(),
40
+ executionApiKey: z.string().optional(),
41
+ analysisProvider: LLMProviderTypeSchema.optional(),
42
+ analysisApiKey: z.string().optional(),
43
+ ollamaBaseUrl: z.string().default("http://localhost:11434"),
44
+ openaiBaseUrl: z.string().default("https://api.openai.com/v1"),
45
+ autoContinueTask: z.boolean().default(false),
46
+ clearContextOnComplete: z.boolean().default(true),
47
+ })
48
+ .optional();
15
49
  export const TaskflowConfigSchema = z.object({
16
50
  version: z.string().default("2.0"),
17
51
  projectType: z.string().default("custom"),
@@ -31,4 +65,5 @@ export const TaskflowConfigSchema = z.object({
31
65
  test: z.string().optional(),
32
66
  })
33
67
  .optional(),
68
+ ai: AIConfigSchema,
34
69
  });
@@ -1,12 +1,12 @@
1
1
  import { z } from "zod";
2
2
  export declare const TaskStatusSchema: z.ZodEnum<{
3
+ planning: "planning";
3
4
  implementing: "implementing";
4
5
  verifying: "verifying";
5
6
  completed: "completed";
6
7
  blocked: "blocked";
7
8
  todo: "todo";
8
9
  in_progress: "in_progress";
9
- planning: "planning";
10
10
  }>;
11
11
  export declare const SubtaskSchema: z.ZodObject<{
12
12
  id: z.ZodString;
@@ -21,13 +21,13 @@ export declare const TaskSchema: z.ZodObject<{
21
21
  title: z.ZodString;
22
22
  description: z.ZodString;
23
23
  status: z.ZodDefault<z.ZodEnum<{
24
+ planning: "planning";
24
25
  implementing: "implementing";
25
26
  verifying: "verifying";
26
27
  completed: "completed";
27
28
  blocked: "blocked";
28
29
  todo: "todo";
29
30
  in_progress: "in_progress";
30
- planning: "planning";
31
31
  }>>;
32
32
  type: z.ZodDefault<z.ZodEnum<{
33
33
  feat: "feat";
@@ -1,3 +1,15 @@
1
+ /**
2
+ * Workflow State Machine
3
+ *
4
+ * This class provides an in-memory state machine for managing task workflow.
5
+ * It operates at a higher level of abstraction than task files - it manages
6
+ * the developer's session state rather than persistent task state.
7
+ *
8
+ * NOTE: This is currently NOT used by the CLI commands. CLI commands
9
+ * (StartCommand, CheckCommand, etc.) use task files directly via the
10
+ * data-access module. This class can be used as an alternative
11
+ * programmatic API or for future session management features.
12
+ */
1
13
  import type { ConfigLoader } from "./config.js";
2
14
  import type { GitManager } from "./git.js";
3
15
  export type MachineState = "IDLE" | "PLANNING" | "EXECUTION" | "VERIFICATION" | "COMPLETED";
@@ -52,7 +52,7 @@ export class StateMachine {
52
52
  // In a real impl, we would find the task file.
53
53
  this.activeTaskId = taskId;
54
54
  this.currentState = "PLANNING";
55
- // TODO: Return initial context (Rules, Retro)
55
+ // FUTURE: Return initial context (Rules, Retro) when this is actively used
56
56
  }
57
57
  /**
58
58
  * Transition: PLANNING -> EXECUTION
@@ -82,7 +82,7 @@ export class StateMachine {
82
82
  if (this.currentState !== "VERIFICATION") {
83
83
  throw new Error("Cannot complete task: Not in VERIFICATION state.");
84
84
  }
85
- // TODO: Verify checks passed? (Or rely on caller to ensure this)
85
+ // FUTURE: Verify checks passed? For now, rely on caller to ensure this
86
86
  this.currentState = "COMPLETED";
87
87
  this.activeTaskId = null;
88
88
  // Auto-transition back to IDLE? Or stay in COMPLETED until explicit 'next'?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krr2020/taskflow-core",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0-beta.5",
4
4
  "description": "Core logic for Taskflow - AI-first task management and workflow system",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -1,17 +0,0 @@
1
- export type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
2
- export interface PackageManagerCommands {
3
- run: string;
4
- exec: string;
5
- }
6
- /**
7
- * Detect the package manager used in the project
8
- */
9
- export declare function detectPackageManager(projectRoot: string): PackageManager;
10
- /**
11
- * Get the run command prefix for the detected package manager
12
- */
13
- export declare function getRunCommand(projectRoot: string): string;
14
- /**
15
- * Get the exec command prefix for the detected package manager
16
- */
17
- export declare function getExecCommand(projectRoot: string): string;
@@ -1,53 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- const COMMANDS = {
4
- npm: {
5
- run: "npm run",
6
- exec: "npx",
7
- },
8
- pnpm: {
9
- run: "pnpm run",
10
- exec: "pnpm exec",
11
- },
12
- yarn: {
13
- run: "yarn run",
14
- exec: "yarn run",
15
- },
16
- bun: {
17
- run: "bun run",
18
- exec: "bun x",
19
- },
20
- };
21
- /**
22
- * Detect the package manager used in the project
23
- */
24
- export function detectPackageManager(projectRoot) {
25
- if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) {
26
- return "pnpm";
27
- }
28
- if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) {
29
- return "yarn";
30
- }
31
- if (fs.existsSync(path.join(projectRoot, "bun.lockb"))) {
32
- return "bun";
33
- }
34
- if (fs.existsSync(path.join(projectRoot, "package-lock.json"))) {
35
- return "npm";
36
- }
37
- // Default to npm if no lock file found
38
- return "npm";
39
- }
40
- /**
41
- * Get the run command prefix for the detected package manager
42
- */
43
- export function getRunCommand(projectRoot) {
44
- const pm = detectPackageManager(projectRoot);
45
- return COMMANDS[pm].run;
46
- }
47
- /**
48
- * Get the exec command prefix for the detected package manager
49
- */
50
- export function getExecCommand(projectRoot) {
51
- const pm = detectPackageManager(projectRoot);
52
- return COMMANDS[pm].exec;
53
- }