clavix 2.3.1 → 2.4.1

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 (187) hide show
  1. package/README.md +0 -116
  2. package/bin/clavix.js +7 -0
  3. package/dist/cli/commands/implement.js +25 -33
  4. package/dist/cli/commands/plan.js +22 -27
  5. package/dist/cli/commands/prd.js +7 -12
  6. package/dist/cli/commands/start.js +4 -9
  7. package/dist/cli/commands/summarize.js +15 -22
  8. package/dist/core 2/adapters/agents-md-generator.d.ts +26 -0
  9. package/dist/core 2/adapters/agents-md-generator.js +102 -0
  10. package/dist/core 2/adapters/amp-adapter.d.ts +27 -0
  11. package/dist/core 2/adapters/amp-adapter.js +42 -0
  12. package/dist/core 2/adapters/augment-adapter.d.ts +22 -0
  13. package/dist/core 2/adapters/augment-adapter.js +77 -0
  14. package/dist/core 2/adapters/base-adapter.d.ts +45 -0
  15. package/dist/core 2/adapters/base-adapter.js +142 -0
  16. package/dist/core 2/adapters/claude-code-adapter.d.ts +32 -0
  17. package/dist/core 2/adapters/claude-code-adapter.js +116 -0
  18. package/dist/core 2/adapters/cline-adapter.d.ts +34 -0
  19. package/dist/core 2/adapters/cline-adapter.js +52 -0
  20. package/dist/core 2/adapters/codebuddy-adapter.d.ts +24 -0
  21. package/dist/core 2/adapters/codebuddy-adapter.js +82 -0
  22. package/dist/core 2/adapters/codex-adapter.d.ts +24 -0
  23. package/dist/core 2/adapters/codex-adapter.js +79 -0
  24. package/dist/core 2/adapters/copilot-instructions-generator.d.ts +26 -0
  25. package/dist/core 2/adapters/copilot-instructions-generator.js +104 -0
  26. package/dist/core 2/adapters/crush-adapter.d.ts +35 -0
  27. package/dist/core 2/adapters/crush-adapter.js +49 -0
  28. package/dist/core 2/adapters/cursor-adapter.d.ts +25 -0
  29. package/dist/core 2/adapters/cursor-adapter.js +40 -0
  30. package/dist/core 2/adapters/droid-adapter.d.ts +33 -0
  31. package/dist/core 2/adapters/droid-adapter.js +57 -0
  32. package/dist/core 2/adapters/gemini-adapter.d.ts +27 -0
  33. package/dist/core 2/adapters/gemini-adapter.js +90 -0
  34. package/dist/core 2/adapters/kilocode-adapter.d.ts +34 -0
  35. package/dist/core 2/adapters/kilocode-adapter.js +49 -0
  36. package/dist/core 2/adapters/octo-md-generator.d.ts +26 -0
  37. package/dist/core 2/adapters/octo-md-generator.js +102 -0
  38. package/dist/core 2/adapters/opencode-adapter.d.ts +33 -0
  39. package/dist/core 2/adapters/opencode-adapter.js +56 -0
  40. package/dist/core 2/adapters/qwen-adapter.d.ts +27 -0
  41. package/dist/core 2/adapters/qwen-adapter.js +90 -0
  42. package/dist/core 2/adapters/roocode-adapter.d.ts +40 -0
  43. package/dist/core 2/adapters/roocode-adapter.js +68 -0
  44. package/dist/core 2/adapters/warp-md-generator.d.ts +17 -0
  45. package/dist/core 2/adapters/warp-md-generator.js +88 -0
  46. package/dist/core 2/adapters/windsurf-adapter.d.ts +34 -0
  47. package/dist/core 2/adapters/windsurf-adapter.js +49 -0
  48. package/dist/core 2/agent-manager.d.ts +51 -0
  49. package/dist/core 2/agent-manager.js +126 -0
  50. package/dist/core 2/archive-manager.d.ts +100 -0
  51. package/dist/core 2/archive-manager.js +338 -0
  52. package/dist/core 2/conversation-analyzer.d.ts +86 -0
  53. package/dist/core 2/doc-injector.d.ts +51 -0
  54. package/dist/core 2/doc-injector.js +236 -0
  55. package/dist/core 2/git-manager.d.ts +100 -0
  56. package/dist/core 2/git-manager.js +214 -0
  57. package/dist/core 2/prompt-optimizer.d.ts +268 -0
  58. package/dist/core 2/prompt-optimizer.js +963 -0
  59. package/dist/core 2/question-engine.d.ts +167 -0
  60. package/dist/core 2/question-engine.js +395 -0
  61. package/dist/core 2/session-manager.d.ts +139 -0
  62. package/dist/core 2/session-manager.js +403 -0
  63. package/dist/core 2/task-manager.d.ts +155 -0
  64. package/dist/core 2/task-manager.js +689 -0
  65. package/dist/index.js +6 -0
  66. package/dist/utils/template-loader.js +24 -22
  67. package/package.json +1 -1
  68. package/dist/templates/slash-commands/augment/archive.md +0 -291
  69. package/dist/templates/slash-commands/augment/deep.md +0 -207
  70. package/dist/templates/slash-commands/augment/fast.md +0 -183
  71. package/dist/templates/slash-commands/augment/implement.md +0 -267
  72. package/dist/templates/slash-commands/augment/plan.md +0 -173
  73. package/dist/templates/slash-commands/augment/prd.md +0 -178
  74. package/dist/templates/slash-commands/augment/start.md +0 -142
  75. package/dist/templates/slash-commands/augment/summarize.md +0 -179
  76. package/dist/templates/slash-commands/claude-code/archive.md +0 -291
  77. package/dist/templates/slash-commands/claude-code/deep.md +0 -207
  78. package/dist/templates/slash-commands/claude-code/fast.md +0 -183
  79. package/dist/templates/slash-commands/claude-code/implement.md +0 -267
  80. package/dist/templates/slash-commands/claude-code/plan.md +0 -173
  81. package/dist/templates/slash-commands/claude-code/prd.md +0 -178
  82. package/dist/templates/slash-commands/claude-code/start.md +0 -142
  83. package/dist/templates/slash-commands/claude-code/summarize.md +0 -179
  84. package/dist/templates/slash-commands/cline/archive.md +0 -291
  85. package/dist/templates/slash-commands/cline/deep.md +0 -207
  86. package/dist/templates/slash-commands/cline/fast.md +0 -183
  87. package/dist/templates/slash-commands/cline/implement.md +0 -267
  88. package/dist/templates/slash-commands/cline/plan.md +0 -173
  89. package/dist/templates/slash-commands/cline/prd.md +0 -178
  90. package/dist/templates/slash-commands/cline/start.md +0 -142
  91. package/dist/templates/slash-commands/cline/summarize.md +0 -179
  92. package/dist/templates/slash-commands/codebuddy/archive.md +0 -291
  93. package/dist/templates/slash-commands/codebuddy/deep.md +0 -207
  94. package/dist/templates/slash-commands/codebuddy/fast.md +0 -183
  95. package/dist/templates/slash-commands/codebuddy/implement.md +0 -267
  96. package/dist/templates/slash-commands/codebuddy/plan.md +0 -173
  97. package/dist/templates/slash-commands/codebuddy/prd.md +0 -178
  98. package/dist/templates/slash-commands/codebuddy/start.md +0 -142
  99. package/dist/templates/slash-commands/codebuddy/summarize.md +0 -179
  100. package/dist/templates/slash-commands/codex/archive.md +0 -291
  101. package/dist/templates/slash-commands/codex/deep.md +0 -207
  102. package/dist/templates/slash-commands/codex/fast.md +0 -183
  103. package/dist/templates/slash-commands/codex/implement.md +0 -267
  104. package/dist/templates/slash-commands/codex/plan.md +0 -173
  105. package/dist/templates/slash-commands/codex/prd.md +0 -178
  106. package/dist/templates/slash-commands/codex/start.md +0 -142
  107. package/dist/templates/slash-commands/codex/summarize.md +0 -179
  108. package/dist/templates/slash-commands/crush/archive.md +0 -291
  109. package/dist/templates/slash-commands/crush/deep.md +0 -207
  110. package/dist/templates/slash-commands/crush/fast.md +0 -183
  111. package/dist/templates/slash-commands/crush/implement.md +0 -267
  112. package/dist/templates/slash-commands/crush/plan.md +0 -173
  113. package/dist/templates/slash-commands/crush/prd.md +0 -178
  114. package/dist/templates/slash-commands/crush/start.md +0 -142
  115. package/dist/templates/slash-commands/crush/summarize.md +0 -179
  116. package/dist/templates/slash-commands/cursor/archive.md +0 -291
  117. package/dist/templates/slash-commands/cursor/deep.md +0 -207
  118. package/dist/templates/slash-commands/cursor/fast.md +0 -183
  119. package/dist/templates/slash-commands/cursor/implement.md +0 -267
  120. package/dist/templates/slash-commands/cursor/plan.md +0 -173
  121. package/dist/templates/slash-commands/cursor/prd.md +0 -178
  122. package/dist/templates/slash-commands/cursor/start.md +0 -142
  123. package/dist/templates/slash-commands/cursor/summarize.md +0 -179
  124. package/dist/templates/slash-commands/droid/archive.md +0 -291
  125. package/dist/templates/slash-commands/droid/deep.md +0 -207
  126. package/dist/templates/slash-commands/droid/fast.md +0 -183
  127. package/dist/templates/slash-commands/droid/implement.md +0 -267
  128. package/dist/templates/slash-commands/droid/plan.md +0 -173
  129. package/dist/templates/slash-commands/droid/prd.md +0 -178
  130. package/dist/templates/slash-commands/droid/start.md +0 -142
  131. package/dist/templates/slash-commands/droid/summarize.md +0 -179
  132. package/dist/templates/slash-commands/gemini/archive.toml +0 -290
  133. package/dist/templates/slash-commands/gemini/deep.toml +0 -206
  134. package/dist/templates/slash-commands/gemini/fast.toml +0 -182
  135. package/dist/templates/slash-commands/gemini/implement.toml +0 -266
  136. package/dist/templates/slash-commands/gemini/plan.toml +0 -170
  137. package/dist/templates/slash-commands/gemini/prd.toml +0 -177
  138. package/dist/templates/slash-commands/gemini/start.toml +0 -141
  139. package/dist/templates/slash-commands/gemini/summarize.toml +0 -178
  140. package/dist/templates/slash-commands/kilocode/archive.md +0 -291
  141. package/dist/templates/slash-commands/kilocode/deep.md +0 -207
  142. package/dist/templates/slash-commands/kilocode/fast.md +0 -183
  143. package/dist/templates/slash-commands/kilocode/implement.md +0 -267
  144. package/dist/templates/slash-commands/kilocode/plan.md +0 -173
  145. package/dist/templates/slash-commands/kilocode/prd.md +0 -178
  146. package/dist/templates/slash-commands/kilocode/start.md +0 -142
  147. package/dist/templates/slash-commands/kilocode/summarize.md +0 -179
  148. package/dist/templates/slash-commands/opencode/archive.md +0 -291
  149. package/dist/templates/slash-commands/opencode/deep.md +0 -207
  150. package/dist/templates/slash-commands/opencode/fast.md +0 -183
  151. package/dist/templates/slash-commands/opencode/implement.md +0 -267
  152. package/dist/templates/slash-commands/opencode/plan.md +0 -173
  153. package/dist/templates/slash-commands/opencode/prd.md +0 -178
  154. package/dist/templates/slash-commands/opencode/start.md +0 -142
  155. package/dist/templates/slash-commands/opencode/summarize.md +0 -179
  156. package/dist/templates/slash-commands/qwen/archive.toml +0 -290
  157. package/dist/templates/slash-commands/qwen/deep.toml +0 -206
  158. package/dist/templates/slash-commands/qwen/fast.toml +0 -182
  159. package/dist/templates/slash-commands/qwen/implement.toml +0 -266
  160. package/dist/templates/slash-commands/qwen/plan.toml +0 -170
  161. package/dist/templates/slash-commands/qwen/prd.toml +0 -177
  162. package/dist/templates/slash-commands/qwen/start.toml +0 -141
  163. package/dist/templates/slash-commands/qwen/summarize.toml +0 -178
  164. package/dist/templates/slash-commands/roocode/archive.md +0 -291
  165. package/dist/templates/slash-commands/roocode/deep.md +0 -207
  166. package/dist/templates/slash-commands/roocode/fast.md +0 -183
  167. package/dist/templates/slash-commands/roocode/implement.md +0 -267
  168. package/dist/templates/slash-commands/roocode/plan.md +0 -173
  169. package/dist/templates/slash-commands/roocode/prd.md +0 -178
  170. package/dist/templates/slash-commands/roocode/start.md +0 -142
  171. package/dist/templates/slash-commands/roocode/summarize.md +0 -179
  172. package/dist/templates/slash-commands/windsurf/archive.md +0 -291
  173. package/dist/templates/slash-commands/windsurf/deep.md +0 -207
  174. package/dist/templates/slash-commands/windsurf/fast.md +0 -183
  175. package/dist/templates/slash-commands/windsurf/implement.md +0 -267
  176. package/dist/templates/slash-commands/windsurf/plan.md +0 -173
  177. package/dist/templates/slash-commands/windsurf/prd.md +0 -178
  178. package/dist/templates/slash-commands/windsurf/start.md +0 -142
  179. package/dist/templates/slash-commands/windsurf/summarize.md +0 -179
  180. /package/dist/templates/slash-commands/{amp → _canonical}/archive.md +0 -0
  181. /package/dist/templates/slash-commands/{amp → _canonical}/deep.md +0 -0
  182. /package/dist/templates/slash-commands/{amp → _canonical}/fast.md +0 -0
  183. /package/dist/templates/slash-commands/{amp → _canonical}/implement.md +0 -0
  184. /package/dist/templates/slash-commands/{amp → _canonical}/plan.md +0 -0
  185. /package/dist/templates/slash-commands/{amp → _canonical}/prd.md +0 -0
  186. /package/dist/templates/slash-commands/{amp → _canonical}/start.md +0 -0
  187. /package/dist/templates/slash-commands/{amp → _canonical}/summarize.md +0 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * QuestionEngine - Manages Socratic questioning flows for PRD generation
3
+ *
4
+ * This class handles:
5
+ * - Loading question templates
6
+ * - Sequential and conditional question flows
7
+ * - Answer collection and validation
8
+ * - Progress tracking
9
+ */
10
+ /**
11
+ * Type of question input
12
+ */
13
+ export type QuestionType = 'text' | 'list' | 'confirm';
14
+ /**
15
+ * A single question in the flow
16
+ */
17
+ export interface Question {
18
+ id: string;
19
+ text: string;
20
+ type: QuestionType;
21
+ required: boolean;
22
+ choices?: string[];
23
+ default?: string | boolean;
24
+ validate?: (answer: string) => boolean | string;
25
+ condition?: (answers: Record<string, Answer>) => boolean;
26
+ }
27
+ /**
28
+ * Answer to a question
29
+ */
30
+ export type Answer = string | boolean | string[];
31
+ /**
32
+ * Collection of answers keyed by question ID
33
+ */
34
+ export interface Answers {
35
+ [questionId: string]: Answer;
36
+ }
37
+ /**
38
+ * Question flow configuration
39
+ */
40
+ export interface QuestionFlow {
41
+ name: string;
42
+ description: string;
43
+ questions: Question[];
44
+ }
45
+ /**
46
+ * Progress information for multi-question flows
47
+ */
48
+ export interface QuestionProgress {
49
+ current: number;
50
+ total: number;
51
+ percentage: number;
52
+ }
53
+ /**
54
+ * QuestionEngine class
55
+ *
56
+ * Manages the flow of questions, answer collection, and validation
57
+ */
58
+ export declare class QuestionEngine {
59
+ private currentFlow;
60
+ private answers;
61
+ private currentQuestionIndex;
62
+ /**
63
+ * Load a question flow from a template file
64
+ *
65
+ * @param templatePath - Path to the question template
66
+ * @returns The loaded question flow
67
+ */
68
+ loadFlow(templatePath: string): Promise<QuestionFlow>;
69
+ /**
70
+ * Parse a question template into a QuestionFlow
71
+ *
72
+ * Template format:
73
+ * ```markdown
74
+ * # Flow Name
75
+ *
76
+ * Flow description
77
+ *
78
+ * ## Question 1
79
+ *
80
+ * **text:** What problem are you solving?
81
+ * **type:** text
82
+ * **required:** true
83
+ * **validation:** minLength:10
84
+ *
85
+ * ## Question 2
86
+ *
87
+ * **text:** Select your technology stack
88
+ * **type:** list
89
+ * **choices:** React, Vue, Angular, Other
90
+ * ```
91
+ *
92
+ * @param template - Template content
93
+ * @returns Parsed QuestionFlow
94
+ */
95
+ private parseTemplate;
96
+ /**
97
+ * Parse a single metadata field for a question
98
+ */
99
+ private parseQuestionMetadata;
100
+ /**
101
+ * Finalize a question by setting defaults and generating ID
102
+ */
103
+ private finalizeQuestion;
104
+ /**
105
+ * Create a validation function from a validation string
106
+ *
107
+ * Supported formats:
108
+ * - minLength:10 - Minimum length
109
+ * - maxLength:100 - Maximum length
110
+ * - pattern:^[a-z]+$ - Regex pattern
111
+ * - email - Email validation
112
+ * - url - URL validation
113
+ */
114
+ private createValidator;
115
+ /**
116
+ * Create a condition function from a condition string
117
+ *
118
+ * Supported formats:
119
+ * - q1=true - Question 1 answer equals true
120
+ * - q2=JWT - Question 2 answer equals "JWT"
121
+ * - q3!=None - Question 3 answer not equals "None"
122
+ */
123
+ private createCondition;
124
+ /**
125
+ * Get the next question in the flow
126
+ *
127
+ * @returns The next question, or null if flow is complete
128
+ */
129
+ getNextQuestion(): Question | null;
130
+ /**
131
+ * Submit an answer to the current question
132
+ *
133
+ * @param questionId - ID of the question being answered
134
+ * @param answer - The answer
135
+ * @returns Validation result (true if valid, error message if invalid)
136
+ */
137
+ submitAnswer(questionId: string, answer: Answer): boolean | string;
138
+ /**
139
+ * Get all collected answers
140
+ *
141
+ * @returns All answers collected so far
142
+ */
143
+ getAnswers(): Answers;
144
+ /**
145
+ * Get current progress through the question flow
146
+ *
147
+ * @returns Progress information
148
+ */
149
+ getProgress(): QuestionProgress;
150
+ /**
151
+ * Check if the question flow is complete
152
+ *
153
+ * @returns True if all required questions are answered
154
+ */
155
+ isComplete(): boolean;
156
+ /**
157
+ * Reset the question flow
158
+ */
159
+ reset(): void;
160
+ /**
161
+ * Get the current question flow
162
+ *
163
+ * @returns The loaded question flow, or null if none loaded
164
+ */
165
+ getCurrentFlow(): QuestionFlow | null;
166
+ }
167
+ //# sourceMappingURL=question-engine.d.ts.map
@@ -0,0 +1,395 @@
1
+ "use strict";
2
+ /**
3
+ * QuestionEngine - Manages Socratic questioning flows for PRD generation
4
+ *
5
+ * This class handles:
6
+ * - Loading question templates
7
+ * - Sequential and conditional question flows
8
+ * - Answer collection and validation
9
+ * - Progress tracking
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.QuestionEngine = void 0;
46
+ const fs = __importStar(require("fs-extra"));
47
+ const path = __importStar(require("path"));
48
+ /**
49
+ * QuestionEngine class
50
+ *
51
+ * Manages the flow of questions, answer collection, and validation
52
+ */
53
+ class QuestionEngine {
54
+ constructor() {
55
+ this.currentFlow = null;
56
+ this.answers = {};
57
+ this.currentQuestionIndex = 0;
58
+ }
59
+ /**
60
+ * Load a question flow from a template file
61
+ *
62
+ * @param templatePath - Path to the question template
63
+ * @returns The loaded question flow
64
+ */
65
+ async loadFlow(templatePath) {
66
+ const fullPath = path.resolve(templatePath);
67
+ if (!await fs.pathExists(fullPath)) {
68
+ throw new Error(`Question template not found: ${templatePath}`);
69
+ }
70
+ const content = await fs.readFile(fullPath, 'utf-8');
71
+ const flow = this.parseTemplate(content);
72
+ this.currentFlow = flow;
73
+ this.answers = {};
74
+ this.currentQuestionIndex = 0;
75
+ return flow;
76
+ }
77
+ /**
78
+ * Parse a question template into a QuestionFlow
79
+ *
80
+ * Template format:
81
+ * ```markdown
82
+ * # Flow Name
83
+ *
84
+ * Flow description
85
+ *
86
+ * ## Question 1
87
+ *
88
+ * **text:** What problem are you solving?
89
+ * **type:** text
90
+ * **required:** true
91
+ * **validation:** minLength:10
92
+ *
93
+ * ## Question 2
94
+ *
95
+ * **text:** Select your technology stack
96
+ * **type:** list
97
+ * **choices:** React, Vue, Angular, Other
98
+ * ```
99
+ *
100
+ * @param template - Template content
101
+ * @returns Parsed QuestionFlow
102
+ */
103
+ parseTemplate(template) {
104
+ const lines = template.split('\n');
105
+ const questions = [];
106
+ let name = 'Default Flow';
107
+ let description = '';
108
+ let currentQuestion = null;
109
+ for (let i = 0; i < lines.length; i++) {
110
+ const line = lines[i].trim();
111
+ // Skip empty lines
112
+ if (!line) {
113
+ continue;
114
+ }
115
+ // Extract flow name from first h1
116
+ if (line.startsWith('# ') && name === 'Default Flow') {
117
+ name = line.substring(2).trim();
118
+ continue;
119
+ }
120
+ // Extract description (first non-heading paragraph)
121
+ if (line && !line.startsWith('#') && !line.startsWith('**') && !description) {
122
+ description = line;
123
+ continue;
124
+ }
125
+ // Start new question on h2
126
+ if (line.startsWith('## ')) {
127
+ // Save previous question if exists
128
+ if (currentQuestion && currentQuestion.text) {
129
+ questions.push(this.finalizeQuestion(currentQuestion, questions.length));
130
+ }
131
+ // Start new question
132
+ currentQuestion = {};
133
+ continue;
134
+ }
135
+ // Parse question metadata: **key:** value
136
+ const metadataMatch = line.match(/^\*\*(\w+):\*\*\s*(.+)$/);
137
+ if (metadataMatch && currentQuestion) {
138
+ const [, key, value] = metadataMatch;
139
+ this.parseQuestionMetadata(currentQuestion, key, value);
140
+ }
141
+ }
142
+ // Save last question
143
+ if (currentQuestion && currentQuestion.text) {
144
+ questions.push(this.finalizeQuestion(currentQuestion, questions.length));
145
+ }
146
+ return { name, description, questions };
147
+ }
148
+ /**
149
+ * Parse a single metadata field for a question
150
+ */
151
+ parseQuestionMetadata(question, key, value) {
152
+ switch (key.toLowerCase()) {
153
+ case 'text':
154
+ question.text = value;
155
+ break;
156
+ case 'type':
157
+ if (value === 'text' || value === 'list' || value === 'confirm') {
158
+ question.type = value;
159
+ }
160
+ break;
161
+ case 'required':
162
+ question.required = value.toLowerCase() === 'true';
163
+ break;
164
+ case 'choices':
165
+ question.choices = value.split(',').map((c) => c.trim());
166
+ break;
167
+ case 'default':
168
+ if (question.type === 'confirm') {
169
+ question.default = value.toLowerCase() === 'true';
170
+ }
171
+ else {
172
+ question.default = value;
173
+ }
174
+ break;
175
+ case 'validation':
176
+ question.validate = this.createValidator(value);
177
+ break;
178
+ case 'condition':
179
+ question.condition = this.createCondition(value);
180
+ break;
181
+ }
182
+ }
183
+ /**
184
+ * Finalize a question by setting defaults and generating ID
185
+ */
186
+ finalizeQuestion(partial, index) {
187
+ return {
188
+ id: `q${index + 1}`,
189
+ text: partial.text || '',
190
+ type: partial.type || 'text',
191
+ required: partial.required !== undefined ? partial.required : true,
192
+ choices: partial.choices,
193
+ default: partial.default,
194
+ validate: partial.validate,
195
+ condition: partial.condition,
196
+ };
197
+ }
198
+ /**
199
+ * Create a validation function from a validation string
200
+ *
201
+ * Supported formats:
202
+ * - minLength:10 - Minimum length
203
+ * - maxLength:100 - Maximum length
204
+ * - pattern:^[a-z]+$ - Regex pattern
205
+ * - email - Email validation
206
+ * - url - URL validation
207
+ */
208
+ createValidator(validationStr) {
209
+ const parts = validationStr.split(':');
210
+ const type = parts[0].toLowerCase();
211
+ const param = parts[1];
212
+ return (answer) => {
213
+ switch (type) {
214
+ case 'minlength':
215
+ if (answer.length < parseInt(param, 10)) {
216
+ return `Minimum length is ${param} characters`;
217
+ }
218
+ break;
219
+ case 'maxlength':
220
+ if (answer.length > parseInt(param, 10)) {
221
+ return `Maximum length is ${param} characters`;
222
+ }
223
+ break;
224
+ case 'pattern':
225
+ if (!new RegExp(param).test(answer)) {
226
+ return 'Answer does not match required format';
227
+ }
228
+ break;
229
+ case 'email':
230
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(answer)) {
231
+ return 'Please enter a valid email address';
232
+ }
233
+ break;
234
+ case 'url':
235
+ try {
236
+ new URL(answer);
237
+ }
238
+ catch {
239
+ return 'Please enter a valid URL';
240
+ }
241
+ break;
242
+ }
243
+ return true;
244
+ };
245
+ }
246
+ /**
247
+ * Create a condition function from a condition string
248
+ *
249
+ * Supported formats:
250
+ * - q1=true - Question 1 answer equals true
251
+ * - q2=JWT - Question 2 answer equals "JWT"
252
+ * - q3!=None - Question 3 answer not equals "None"
253
+ */
254
+ createCondition(conditionStr) {
255
+ const match = conditionStr.match(/^(\w+)\s*(=|!=)\s*(.+)$/);
256
+ if (!match) {
257
+ return () => true;
258
+ }
259
+ const [, questionId, operator, expectedValue] = match;
260
+ return (answers) => {
261
+ const actualValue = answers[questionId];
262
+ // Convert expected value to proper type
263
+ let expected = expectedValue;
264
+ if (expectedValue.toLowerCase() === 'true') {
265
+ expected = true;
266
+ }
267
+ else if (expectedValue.toLowerCase() === 'false') {
268
+ expected = false;
269
+ }
270
+ if (operator === '=') {
271
+ return actualValue === expected;
272
+ }
273
+ else {
274
+ return actualValue !== expected;
275
+ }
276
+ };
277
+ }
278
+ /**
279
+ * Get the next question in the flow
280
+ *
281
+ * @returns The next question, or null if flow is complete
282
+ */
283
+ getNextQuestion() {
284
+ if (!this.currentFlow) {
285
+ throw new Error('No question flow loaded');
286
+ }
287
+ // Find the next unanswered question that meets its condition
288
+ while (this.currentQuestionIndex < this.currentFlow.questions.length) {
289
+ const question = this.currentFlow.questions[this.currentQuestionIndex];
290
+ // Skip if already answered
291
+ if (question.id in this.answers) {
292
+ this.currentQuestionIndex++;
293
+ continue;
294
+ }
295
+ // Check if question condition is met (if it has one)
296
+ if (question.condition && !question.condition(this.answers)) {
297
+ this.currentQuestionIndex++;
298
+ continue;
299
+ }
300
+ return question;
301
+ }
302
+ return null;
303
+ }
304
+ /**
305
+ * Submit an answer to the current question
306
+ *
307
+ * @param questionId - ID of the question being answered
308
+ * @param answer - The answer
309
+ * @returns Validation result (true if valid, error message if invalid)
310
+ */
311
+ submitAnswer(questionId, answer) {
312
+ if (!this.currentFlow) {
313
+ throw new Error('No question flow loaded');
314
+ }
315
+ const question = this.currentFlow.questions.find((q) => q.id === questionId);
316
+ if (!question) {
317
+ throw new Error(`Question not found: ${questionId}`);
318
+ }
319
+ // Validate required fields (but allow false for boolean answers)
320
+ if (question.required && (answer === '' || answer === null || answer === undefined)) {
321
+ return 'This question is required';
322
+ }
323
+ // Run custom validation if provided
324
+ if (question.validate) {
325
+ const validationResult = question.validate(String(answer));
326
+ if (validationResult !== true) {
327
+ return validationResult;
328
+ }
329
+ }
330
+ // Store the answer
331
+ this.answers[questionId] = answer;
332
+ this.currentQuestionIndex++;
333
+ return true;
334
+ }
335
+ /**
336
+ * Get all collected answers
337
+ *
338
+ * @returns All answers collected so far
339
+ */
340
+ getAnswers() {
341
+ return { ...this.answers };
342
+ }
343
+ /**
344
+ * Get current progress through the question flow
345
+ *
346
+ * @returns Progress information
347
+ */
348
+ getProgress() {
349
+ if (!this.currentFlow) {
350
+ return { current: 0, total: 0, percentage: 0 };
351
+ }
352
+ const total = this.currentFlow.questions.length;
353
+ const current = Object.keys(this.answers).length;
354
+ const percentage = total > 0 ? Math.round((current / total) * 100) : 0;
355
+ return { current, total, percentage };
356
+ }
357
+ /**
358
+ * Check if the question flow is complete
359
+ *
360
+ * @returns True if all required questions are answered
361
+ */
362
+ isComplete() {
363
+ if (!this.currentFlow) {
364
+ return false;
365
+ }
366
+ // Check if all required questions have answers
367
+ for (const question of this.currentFlow.questions) {
368
+ // Skip if condition not met
369
+ if (question.condition && !question.condition(this.answers)) {
370
+ continue;
371
+ }
372
+ if (question.required && !(question.id in this.answers)) {
373
+ return false;
374
+ }
375
+ }
376
+ return true;
377
+ }
378
+ /**
379
+ * Reset the question flow
380
+ */
381
+ reset() {
382
+ this.answers = {};
383
+ this.currentQuestionIndex = 0;
384
+ }
385
+ /**
386
+ * Get the current question flow
387
+ *
388
+ * @returns The loaded question flow, or null if none loaded
389
+ */
390
+ getCurrentFlow() {
391
+ return this.currentFlow;
392
+ }
393
+ }
394
+ exports.QuestionEngine = QuestionEngine;
395
+ //# sourceMappingURL=question-engine.js.map
@@ -0,0 +1,139 @@
1
+ /**
2
+ * SessionManager - Manages conversational sessions for clavix start/summarize
3
+ *
4
+ * This class handles:
5
+ * - Session creation with unique IDs
6
+ * - Message tracking and storage
7
+ * - Session file persistence (JSON format)
8
+ * - Session listing, filtering, and search
9
+ * - CRUD operations for sessions
10
+ *
11
+ * Sessions are stored in `.clavix/sessions/` as JSON files:
12
+ * `.clavix/sessions/{session-id}.json`
13
+ */
14
+ import { Session, SessionMetadata, SessionFilter } from '../types/session';
15
+ /**
16
+ * Options for creating a new session
17
+ */
18
+ export interface CreateSessionOptions {
19
+ projectName?: string;
20
+ agent?: string;
21
+ description?: string;
22
+ tags?: string[];
23
+ }
24
+ /**
25
+ * SessionManager class
26
+ *
27
+ * Manages all session-related operations including creation, storage,
28
+ * retrieval, listing, and search functionality.
29
+ */
30
+ export declare class SessionManager {
31
+ private readonly sessionsDir;
32
+ private readonly defaultSessionsDir;
33
+ constructor(sessionsDir?: string);
34
+ /**
35
+ * Create a new session
36
+ *
37
+ * @param options - Session creation options
38
+ * @returns The created session
39
+ */
40
+ createSession(options?: CreateSessionOptions): Promise<Session>;
41
+ /**
42
+ * Get a session by ID
43
+ *
44
+ * @param sessionId - The session ID
45
+ * @returns The session, or null if not found
46
+ */
47
+ getSession(sessionId: string): Promise<Session | null>;
48
+ /**
49
+ * Save a session to disk
50
+ *
51
+ * @param session - The session to save
52
+ */
53
+ saveSession(session: Session): Promise<void>;
54
+ /**
55
+ * Update a session
56
+ *
57
+ * @param sessionId - The session ID
58
+ * @param updates - Partial session updates
59
+ * @returns The updated session, or null if not found
60
+ */
61
+ updateSession(sessionId: string, updates: Partial<Omit<Session, 'id' | 'created'>>): Promise<Session | null>;
62
+ /**
63
+ * Delete a session
64
+ *
65
+ * @param sessionId - The session ID
66
+ * @returns True if deleted, false if not found
67
+ */
68
+ deleteSession(sessionId: string): Promise<boolean>;
69
+ /**
70
+ * Add a message to a session
71
+ *
72
+ * @param sessionId - The session ID
73
+ * @param role - Message role (user or assistant)
74
+ * @param content - Message content
75
+ * @returns The updated session, or null if session not found
76
+ */
77
+ addMessage(sessionId: string, role: 'user' | 'assistant', content: string): Promise<Session | null>;
78
+ /**
79
+ * List all sessions with optional filtering
80
+ *
81
+ * @param filter - Optional filter criteria
82
+ * @returns Array of session metadata
83
+ */
84
+ listSessions(filter?: SessionFilter): Promise<SessionMetadata[]>;
85
+ /**
86
+ * Search sessions by keyword
87
+ *
88
+ * Searches in project name, description, tags, and message content
89
+ *
90
+ * @param keyword - Search keyword
91
+ * @returns Array of matching session metadata
92
+ */
93
+ searchSessions(keyword: string): Promise<SessionMetadata[]>;
94
+ /**
95
+ * Get the most recent active session
96
+ *
97
+ * @returns The active session, or null if none exists
98
+ */
99
+ getActiveSession(): Promise<Session | null>;
100
+ /**
101
+ * Mark a session as completed
102
+ *
103
+ * @param sessionId - The session ID
104
+ * @returns The updated session, or null if not found
105
+ */
106
+ completeSession(sessionId: string): Promise<Session | null>;
107
+ /**
108
+ * Mark a session as archived
109
+ *
110
+ * @param sessionId - The session ID
111
+ * @returns The updated session, or null if not found
112
+ */
113
+ archiveSession(sessionId: string): Promise<Session | null>;
114
+ /**
115
+ * Get the path to a session file
116
+ */
117
+ private getSessionPath;
118
+ /**
119
+ * Generate a default project name based on timestamp
120
+ */
121
+ private generateDefaultProjectName;
122
+ /**
123
+ * Serialize a session for file storage
124
+ */
125
+ private serializeSession;
126
+ /**
127
+ * Deserialize a session from file storage
128
+ */
129
+ private deserializeSession;
130
+ /**
131
+ * Extract metadata from a serialized session
132
+ */
133
+ private extractMetadata;
134
+ /**
135
+ * Check if session metadata matches filter criteria
136
+ */
137
+ private matchesFilter;
138
+ }
139
+ //# sourceMappingURL=session-manager.d.ts.map