clavix 2.3.0 → 2.4.0

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 (180) hide show
  1. package/dist/cli/commands/init.js +1 -1
  2. package/dist/core 2/adapters/agents-md-generator.d.ts +26 -0
  3. package/dist/core 2/adapters/agents-md-generator.js +102 -0
  4. package/dist/core 2/adapters/amp-adapter.d.ts +27 -0
  5. package/dist/core 2/adapters/amp-adapter.js +42 -0
  6. package/dist/core 2/adapters/augment-adapter.d.ts +22 -0
  7. package/dist/core 2/adapters/augment-adapter.js +77 -0
  8. package/dist/core 2/adapters/base-adapter.d.ts +45 -0
  9. package/dist/core 2/adapters/base-adapter.js +142 -0
  10. package/dist/core 2/adapters/claude-code-adapter.d.ts +32 -0
  11. package/dist/core 2/adapters/claude-code-adapter.js +116 -0
  12. package/dist/core 2/adapters/cline-adapter.d.ts +34 -0
  13. package/dist/core 2/adapters/cline-adapter.js +52 -0
  14. package/dist/core 2/adapters/codebuddy-adapter.d.ts +24 -0
  15. package/dist/core 2/adapters/codebuddy-adapter.js +82 -0
  16. package/dist/core 2/adapters/codex-adapter.d.ts +24 -0
  17. package/dist/core 2/adapters/codex-adapter.js +79 -0
  18. package/dist/core 2/adapters/copilot-instructions-generator.d.ts +26 -0
  19. package/dist/core 2/adapters/copilot-instructions-generator.js +104 -0
  20. package/dist/core 2/adapters/crush-adapter.d.ts +35 -0
  21. package/dist/core 2/adapters/crush-adapter.js +49 -0
  22. package/dist/core 2/adapters/cursor-adapter.d.ts +25 -0
  23. package/dist/core 2/adapters/cursor-adapter.js +40 -0
  24. package/dist/core 2/adapters/droid-adapter.d.ts +33 -0
  25. package/dist/core 2/adapters/droid-adapter.js +57 -0
  26. package/dist/core 2/adapters/gemini-adapter.d.ts +27 -0
  27. package/dist/core 2/adapters/gemini-adapter.js +90 -0
  28. package/dist/core 2/adapters/kilocode-adapter.d.ts +34 -0
  29. package/dist/core 2/adapters/kilocode-adapter.js +49 -0
  30. package/dist/core 2/adapters/octo-md-generator.d.ts +26 -0
  31. package/dist/core 2/adapters/octo-md-generator.js +102 -0
  32. package/dist/core 2/adapters/opencode-adapter.d.ts +33 -0
  33. package/dist/core 2/adapters/opencode-adapter.js +56 -0
  34. package/dist/core 2/adapters/qwen-adapter.d.ts +27 -0
  35. package/dist/core 2/adapters/qwen-adapter.js +90 -0
  36. package/dist/core 2/adapters/roocode-adapter.d.ts +40 -0
  37. package/dist/core 2/adapters/roocode-adapter.js +68 -0
  38. package/dist/core 2/adapters/warp-md-generator.d.ts +17 -0
  39. package/dist/core 2/adapters/warp-md-generator.js +88 -0
  40. package/dist/core 2/adapters/windsurf-adapter.d.ts +34 -0
  41. package/dist/core 2/adapters/windsurf-adapter.js +49 -0
  42. package/dist/core 2/agent-manager.d.ts +51 -0
  43. package/dist/core 2/agent-manager.js +126 -0
  44. package/dist/core 2/archive-manager.d.ts +100 -0
  45. package/dist/core 2/archive-manager.js +338 -0
  46. package/dist/core 2/conversation-analyzer.d.ts +86 -0
  47. package/dist/core 2/doc-injector.d.ts +51 -0
  48. package/dist/core 2/doc-injector.js +236 -0
  49. package/dist/core 2/git-manager.d.ts +100 -0
  50. package/dist/core 2/git-manager.js +214 -0
  51. package/dist/core 2/prompt-optimizer.d.ts +268 -0
  52. package/dist/core 2/prompt-optimizer.js +963 -0
  53. package/dist/core 2/question-engine.d.ts +167 -0
  54. package/dist/core 2/question-engine.js +395 -0
  55. package/dist/core 2/session-manager.d.ts +139 -0
  56. package/dist/core 2/session-manager.js +403 -0
  57. package/dist/core 2/task-manager.d.ts +155 -0
  58. package/dist/core 2/task-manager.js +689 -0
  59. package/dist/utils/template-loader.js +24 -22
  60. package/package.json +1 -1
  61. package/dist/templates/slash-commands/augment/archive.md +0 -291
  62. package/dist/templates/slash-commands/augment/deep.md +0 -207
  63. package/dist/templates/slash-commands/augment/fast.md +0 -183
  64. package/dist/templates/slash-commands/augment/implement.md +0 -267
  65. package/dist/templates/slash-commands/augment/plan.md +0 -173
  66. package/dist/templates/slash-commands/augment/prd.md +0 -178
  67. package/dist/templates/slash-commands/augment/start.md +0 -142
  68. package/dist/templates/slash-commands/augment/summarize.md +0 -179
  69. package/dist/templates/slash-commands/claude-code/archive.md +0 -291
  70. package/dist/templates/slash-commands/claude-code/deep.md +0 -207
  71. package/dist/templates/slash-commands/claude-code/fast.md +0 -183
  72. package/dist/templates/slash-commands/claude-code/implement.md +0 -267
  73. package/dist/templates/slash-commands/claude-code/plan.md +0 -173
  74. package/dist/templates/slash-commands/claude-code/prd.md +0 -178
  75. package/dist/templates/slash-commands/claude-code/start.md +0 -142
  76. package/dist/templates/slash-commands/claude-code/summarize.md +0 -179
  77. package/dist/templates/slash-commands/cline/archive.md +0 -291
  78. package/dist/templates/slash-commands/cline/deep.md +0 -207
  79. package/dist/templates/slash-commands/cline/fast.md +0 -183
  80. package/dist/templates/slash-commands/cline/implement.md +0 -267
  81. package/dist/templates/slash-commands/cline/plan.md +0 -173
  82. package/dist/templates/slash-commands/cline/prd.md +0 -178
  83. package/dist/templates/slash-commands/cline/start.md +0 -142
  84. package/dist/templates/slash-commands/cline/summarize.md +0 -179
  85. package/dist/templates/slash-commands/codebuddy/archive.md +0 -291
  86. package/dist/templates/slash-commands/codebuddy/deep.md +0 -207
  87. package/dist/templates/slash-commands/codebuddy/fast.md +0 -183
  88. package/dist/templates/slash-commands/codebuddy/implement.md +0 -267
  89. package/dist/templates/slash-commands/codebuddy/plan.md +0 -173
  90. package/dist/templates/slash-commands/codebuddy/prd.md +0 -178
  91. package/dist/templates/slash-commands/codebuddy/start.md +0 -142
  92. package/dist/templates/slash-commands/codebuddy/summarize.md +0 -179
  93. package/dist/templates/slash-commands/codex/archive.md +0 -291
  94. package/dist/templates/slash-commands/codex/deep.md +0 -207
  95. package/dist/templates/slash-commands/codex/fast.md +0 -183
  96. package/dist/templates/slash-commands/codex/implement.md +0 -267
  97. package/dist/templates/slash-commands/codex/plan.md +0 -173
  98. package/dist/templates/slash-commands/codex/prd.md +0 -178
  99. package/dist/templates/slash-commands/codex/start.md +0 -142
  100. package/dist/templates/slash-commands/codex/summarize.md +0 -179
  101. package/dist/templates/slash-commands/crush/archive.md +0 -291
  102. package/dist/templates/slash-commands/crush/deep.md +0 -207
  103. package/dist/templates/slash-commands/crush/fast.md +0 -183
  104. package/dist/templates/slash-commands/crush/implement.md +0 -267
  105. package/dist/templates/slash-commands/crush/plan.md +0 -173
  106. package/dist/templates/slash-commands/crush/prd.md +0 -178
  107. package/dist/templates/slash-commands/crush/start.md +0 -142
  108. package/dist/templates/slash-commands/crush/summarize.md +0 -179
  109. package/dist/templates/slash-commands/cursor/archive.md +0 -291
  110. package/dist/templates/slash-commands/cursor/deep.md +0 -207
  111. package/dist/templates/slash-commands/cursor/fast.md +0 -183
  112. package/dist/templates/slash-commands/cursor/implement.md +0 -267
  113. package/dist/templates/slash-commands/cursor/plan.md +0 -173
  114. package/dist/templates/slash-commands/cursor/prd.md +0 -178
  115. package/dist/templates/slash-commands/cursor/start.md +0 -142
  116. package/dist/templates/slash-commands/cursor/summarize.md +0 -179
  117. package/dist/templates/slash-commands/droid/archive.md +0 -291
  118. package/dist/templates/slash-commands/droid/deep.md +0 -207
  119. package/dist/templates/slash-commands/droid/fast.md +0 -183
  120. package/dist/templates/slash-commands/droid/implement.md +0 -267
  121. package/dist/templates/slash-commands/droid/plan.md +0 -173
  122. package/dist/templates/slash-commands/droid/prd.md +0 -178
  123. package/dist/templates/slash-commands/droid/start.md +0 -142
  124. package/dist/templates/slash-commands/droid/summarize.md +0 -179
  125. package/dist/templates/slash-commands/gemini/archive.toml +0 -290
  126. package/dist/templates/slash-commands/gemini/deep.toml +0 -206
  127. package/dist/templates/slash-commands/gemini/fast.toml +0 -182
  128. package/dist/templates/slash-commands/gemini/implement.toml +0 -266
  129. package/dist/templates/slash-commands/gemini/plan.toml +0 -170
  130. package/dist/templates/slash-commands/gemini/prd.toml +0 -177
  131. package/dist/templates/slash-commands/gemini/start.toml +0 -141
  132. package/dist/templates/slash-commands/gemini/summarize.toml +0 -178
  133. package/dist/templates/slash-commands/kilocode/archive.md +0 -291
  134. package/dist/templates/slash-commands/kilocode/deep.md +0 -207
  135. package/dist/templates/slash-commands/kilocode/fast.md +0 -183
  136. package/dist/templates/slash-commands/kilocode/implement.md +0 -267
  137. package/dist/templates/slash-commands/kilocode/plan.md +0 -173
  138. package/dist/templates/slash-commands/kilocode/prd.md +0 -178
  139. package/dist/templates/slash-commands/kilocode/start.md +0 -142
  140. package/dist/templates/slash-commands/kilocode/summarize.md +0 -179
  141. package/dist/templates/slash-commands/opencode/archive.md +0 -291
  142. package/dist/templates/slash-commands/opencode/deep.md +0 -207
  143. package/dist/templates/slash-commands/opencode/fast.md +0 -183
  144. package/dist/templates/slash-commands/opencode/implement.md +0 -267
  145. package/dist/templates/slash-commands/opencode/plan.md +0 -173
  146. package/dist/templates/slash-commands/opencode/prd.md +0 -178
  147. package/dist/templates/slash-commands/opencode/start.md +0 -142
  148. package/dist/templates/slash-commands/opencode/summarize.md +0 -179
  149. package/dist/templates/slash-commands/qwen/archive.toml +0 -290
  150. package/dist/templates/slash-commands/qwen/deep.toml +0 -206
  151. package/dist/templates/slash-commands/qwen/fast.toml +0 -182
  152. package/dist/templates/slash-commands/qwen/implement.toml +0 -266
  153. package/dist/templates/slash-commands/qwen/plan.toml +0 -170
  154. package/dist/templates/slash-commands/qwen/prd.toml +0 -177
  155. package/dist/templates/slash-commands/qwen/start.toml +0 -141
  156. package/dist/templates/slash-commands/qwen/summarize.toml +0 -178
  157. package/dist/templates/slash-commands/roocode/archive.md +0 -291
  158. package/dist/templates/slash-commands/roocode/deep.md +0 -207
  159. package/dist/templates/slash-commands/roocode/fast.md +0 -183
  160. package/dist/templates/slash-commands/roocode/implement.md +0 -267
  161. package/dist/templates/slash-commands/roocode/plan.md +0 -173
  162. package/dist/templates/slash-commands/roocode/prd.md +0 -178
  163. package/dist/templates/slash-commands/roocode/start.md +0 -142
  164. package/dist/templates/slash-commands/roocode/summarize.md +0 -179
  165. package/dist/templates/slash-commands/windsurf/archive.md +0 -291
  166. package/dist/templates/slash-commands/windsurf/deep.md +0 -207
  167. package/dist/templates/slash-commands/windsurf/fast.md +0 -183
  168. package/dist/templates/slash-commands/windsurf/implement.md +0 -267
  169. package/dist/templates/slash-commands/windsurf/plan.md +0 -173
  170. package/dist/templates/slash-commands/windsurf/prd.md +0 -178
  171. package/dist/templates/slash-commands/windsurf/start.md +0 -142
  172. package/dist/templates/slash-commands/windsurf/summarize.md +0 -179
  173. /package/dist/templates/slash-commands/{amp → _canonical}/archive.md +0 -0
  174. /package/dist/templates/slash-commands/{amp → _canonical}/deep.md +0 -0
  175. /package/dist/templates/slash-commands/{amp → _canonical}/fast.md +0 -0
  176. /package/dist/templates/slash-commands/{amp → _canonical}/implement.md +0 -0
  177. /package/dist/templates/slash-commands/{amp → _canonical}/plan.md +0 -0
  178. /package/dist/templates/slash-commands/{amp → _canonical}/prd.md +0 -0
  179. /package/dist/templates/slash-commands/{amp → _canonical}/start.md +0 -0
  180. /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