auq-mcp-server 0.1.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.
- package/LICENSE +25 -0
- package/README.md +176 -0
- package/dist/__tests__/schema-validation.test.js +137 -0
- package/dist/__tests__/server.integration.test.js +263 -0
- package/dist/add.js +1 -0
- package/dist/add.test.js +5 -0
- package/dist/bin/auq.js +245 -0
- package/dist/bin/test-session-menu.js +28 -0
- package/dist/bin/test-tabbar.js +42 -0
- package/dist/file-utils.js +59 -0
- package/dist/format/ResponseFormatter.js +206 -0
- package/dist/format/__tests__/ResponseFormatter.test.js +380 -0
- package/dist/package.json +74 -0
- package/dist/server.js +107 -0
- package/dist/session/ResponseFormatter.js +130 -0
- package/dist/session/SessionManager.js +474 -0
- package/dist/session/__tests__/ResponseFormatter.test.js +417 -0
- package/dist/session/__tests__/SessionManager.test.js +553 -0
- package/dist/session/__tests__/atomic-operations.test.js +345 -0
- package/dist/session/__tests__/file-watcher.test.js +311 -0
- package/dist/session/__tests__/workflow.integration.test.js +334 -0
- package/dist/session/atomic-operations.js +307 -0
- package/dist/session/file-watcher.js +218 -0
- package/dist/session/index.js +7 -0
- package/dist/session/types.js +20 -0
- package/dist/session/utils.js +125 -0
- package/dist/session-manager.js +171 -0
- package/dist/session-watcher.js +110 -0
- package/dist/src/__tests__/schema-validation.test.js +170 -0
- package/dist/src/__tests__/server.integration.test.js +274 -0
- package/dist/src/add.js +1 -0
- package/dist/src/add.test.js +5 -0
- package/dist/src/server.js +163 -0
- package/dist/src/session/ResponseFormatter.js +163 -0
- package/dist/src/session/SessionManager.js +572 -0
- package/dist/src/session/__tests__/ResponseFormatter.test.js +741 -0
- package/dist/src/session/__tests__/SessionManager.test.js +593 -0
- package/dist/src/session/__tests__/atomic-operations.test.js +346 -0
- package/dist/src/session/__tests__/file-watcher.test.js +311 -0
- package/dist/src/session/atomic-operations.js +307 -0
- package/dist/src/session/file-watcher.js +227 -0
- package/dist/src/session/index.js +7 -0
- package/dist/src/session/types.js +20 -0
- package/dist/src/session/utils.js +180 -0
- package/dist/src/tui/__tests__/session-watcher.test.js +368 -0
- package/dist/src/tui/components/AnimatedGradient.js +45 -0
- package/dist/src/tui/components/ConfirmationDialog.js +89 -0
- package/dist/src/tui/components/CustomInput.js +14 -0
- package/dist/src/tui/components/Footer.js +55 -0
- package/dist/src/tui/components/Header.js +35 -0
- package/dist/src/tui/components/MultiLineTextInput.js +65 -0
- package/dist/src/tui/components/OptionsList.js +115 -0
- package/dist/src/tui/components/QuestionDisplay.js +36 -0
- package/dist/src/tui/components/ReviewScreen.js +57 -0
- package/dist/src/tui/components/SessionSelectionMenu.js +151 -0
- package/dist/src/tui/components/StepperView.js +166 -0
- package/dist/src/tui/components/TabBar.js +42 -0
- package/dist/src/tui/components/Toast.js +19 -0
- package/dist/src/tui/components/WaitingScreen.js +20 -0
- package/dist/src/tui/session-watcher.js +195 -0
- package/dist/src/tui/theme.js +114 -0
- package/dist/src/tui/utils/gradientText.js +24 -0
- package/dist/tui/__tests__/session-watcher.test.js +368 -0
- package/dist/tui/session-watcher.js +183 -0
- package/package.json +78 -0
- package/scripts/postinstall.cjs +51 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response Formatter
|
|
3
|
+
*
|
|
4
|
+
* Formats user answers according to PRD specification for returning to AI models
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* ResponseFormatter - Formats session answers into human-readable text
|
|
8
|
+
* according to the PRD specification
|
|
9
|
+
*/
|
|
10
|
+
export class ResponseFormatter {
|
|
11
|
+
/**
|
|
12
|
+
* Format user answers into PRD-compliant text response
|
|
13
|
+
*
|
|
14
|
+
* Format specification:
|
|
15
|
+
* - Header: "Here are the user's answers:"
|
|
16
|
+
* - Numbered questions: "1. {prompt}"
|
|
17
|
+
* - Arrow symbol for answers: "→ {label} — {description}"
|
|
18
|
+
* - Custom text: "→ Other: '{customText}'"
|
|
19
|
+
* - Double newline separation between questions
|
|
20
|
+
*
|
|
21
|
+
* @param answers - Session answer data containing user responses
|
|
22
|
+
* @param questions - Original questions asked to the user
|
|
23
|
+
* @returns Formatted text response ready to send to AI model
|
|
24
|
+
*/
|
|
25
|
+
static formatUserResponse(answers, questions) {
|
|
26
|
+
// Validate that we have matching questions and answers
|
|
27
|
+
if (answers.answers.length === 0) {
|
|
28
|
+
throw new Error("No answers provided in session");
|
|
29
|
+
}
|
|
30
|
+
if (questions.length === 0) {
|
|
31
|
+
throw new Error("No questions provided");
|
|
32
|
+
}
|
|
33
|
+
// Start with header
|
|
34
|
+
const lines = ["Here are the user's answers:", ""];
|
|
35
|
+
// Format each question and its answer
|
|
36
|
+
const formattedQuestions = [];
|
|
37
|
+
for (let i = 0; i < questions.length; i++) {
|
|
38
|
+
const question = questions[i];
|
|
39
|
+
const answer = answers.answers.find((a) => a.questionIndex === i);
|
|
40
|
+
if (!answer) {
|
|
41
|
+
// If no answer found for this question, skip it
|
|
42
|
+
// (This shouldn't happen in normal operation, but handle gracefully)
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Format the question and answer
|
|
46
|
+
const formattedQA = this.formatQuestion(question, answer, i + 1);
|
|
47
|
+
formattedQuestions.push(formattedQA);
|
|
48
|
+
}
|
|
49
|
+
// Join formatted questions with blank lines between them
|
|
50
|
+
lines.push(formattedQuestions.join("\n\n"));
|
|
51
|
+
return lines.join("\n");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validate that answers match the questions
|
|
55
|
+
*
|
|
56
|
+
* @param answers - Session answer data
|
|
57
|
+
* @param questions - Original questions
|
|
58
|
+
* @throws Error if validation fails
|
|
59
|
+
*/
|
|
60
|
+
static validateAnswers(answers, questions) {
|
|
61
|
+
// Check that we have answers
|
|
62
|
+
if (!answers || !answers.answers || answers.answers.length === 0) {
|
|
63
|
+
throw new Error("No answers provided");
|
|
64
|
+
}
|
|
65
|
+
// Check that we have questions
|
|
66
|
+
if (!questions || questions.length === 0) {
|
|
67
|
+
throw new Error("No questions provided");
|
|
68
|
+
}
|
|
69
|
+
// Check each answer references a valid question
|
|
70
|
+
for (const answer of answers.answers) {
|
|
71
|
+
if (answer.questionIndex < 0 ||
|
|
72
|
+
answer.questionIndex >= questions.length) {
|
|
73
|
+
throw new Error(`Answer references invalid question index: ${answer.questionIndex}`);
|
|
74
|
+
}
|
|
75
|
+
// Check that answer has either selectedOption, selectedOptions, or customText
|
|
76
|
+
if (!answer.selectedOption && !answer.customText && !answer.selectedOptions) {
|
|
77
|
+
throw new Error(`Answer for question ${answer.questionIndex} has neither selectedOption, selectedOptions, nor customText`);
|
|
78
|
+
}
|
|
79
|
+
// If selectedOption is provided, verify it exists in the question's options
|
|
80
|
+
if (answer.selectedOption) {
|
|
81
|
+
const question = questions[answer.questionIndex];
|
|
82
|
+
const optionExists = question.options.some((opt) => opt.label === answer.selectedOption);
|
|
83
|
+
if (!optionExists) {
|
|
84
|
+
throw new Error(`Answer for question ${answer.questionIndex} references non-existent option: ${answer.selectedOption}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Validate multi-select options
|
|
88
|
+
if (answer.selectedOptions) {
|
|
89
|
+
const question = questions[answer.questionIndex];
|
|
90
|
+
for (const selectedOpt of answer.selectedOptions) {
|
|
91
|
+
const optionExists = question.options.some((opt) => opt.label === selectedOpt);
|
|
92
|
+
if (!optionExists) {
|
|
93
|
+
throw new Error(`Answer for question ${answer.questionIndex} references non-existent option: ${selectedOpt}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Format a single question and its answer
|
|
101
|
+
*
|
|
102
|
+
* @param question - The question data
|
|
103
|
+
* @param answer - The user's answer
|
|
104
|
+
* @param index - Question number (1-indexed for display)
|
|
105
|
+
* @returns Formatted string for this question/answer pair
|
|
106
|
+
*/
|
|
107
|
+
static formatQuestion(question, answer, index) {
|
|
108
|
+
const lines = [];
|
|
109
|
+
// Add question with number
|
|
110
|
+
lines.push(`${index}. ${question.prompt}`);
|
|
111
|
+
// Track if any answer was provided
|
|
112
|
+
let hasAnswer = false;
|
|
113
|
+
// Format multi-select options (if present)
|
|
114
|
+
if (answer.selectedOptions && answer.selectedOptions.length > 0) {
|
|
115
|
+
hasAnswer = true;
|
|
116
|
+
for (const selectedLabel of answer.selectedOptions) {
|
|
117
|
+
const option = question.options.find((opt) => opt.label === selectedLabel);
|
|
118
|
+
if (option) {
|
|
119
|
+
if (option.description) {
|
|
120
|
+
lines.push(`→ ${option.label} — ${option.description}`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
lines.push(`→ ${option.label}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (answer.selectedOptions && answer.selectedOptions.length === 0 && !answer.customText) {
|
|
129
|
+
// Multi-select with no selections and no custom text
|
|
130
|
+
hasAnswer = true;
|
|
131
|
+
lines.push("→ (No selection)");
|
|
132
|
+
}
|
|
133
|
+
// Format single-select option (if present and no multi-select)
|
|
134
|
+
if (answer.selectedOption && !answer.selectedOptions) {
|
|
135
|
+
hasAnswer = true;
|
|
136
|
+
const option = question.options.find((opt) => opt.label === answer.selectedOption);
|
|
137
|
+
if (option) {
|
|
138
|
+
// Format with description if available
|
|
139
|
+
if (option.description) {
|
|
140
|
+
lines.push(`→ ${option.label} — ${option.description}`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
lines.push(`→ ${option.label}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// Option not found - shouldn't happen, but handle gracefully
|
|
148
|
+
lines.push(`→ ${answer.selectedOption}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Format custom text (if present) - can coexist with selectedOptions in multi-select
|
|
152
|
+
if (answer.customText) {
|
|
153
|
+
hasAnswer = true;
|
|
154
|
+
const escapedText = answer.customText.replace(/'/g, "\\'");
|
|
155
|
+
lines.push(`→ Other: '${escapedText}'`);
|
|
156
|
+
}
|
|
157
|
+
// If no answer at all
|
|
158
|
+
if (!hasAnswer) {
|
|
159
|
+
lines.push("→ No answer provided");
|
|
160
|
+
}
|
|
161
|
+
return lines.join("\n");
|
|
162
|
+
}
|
|
163
|
+
}
|