sinapse-ai 1.6.1 → 1.8.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/.claude/CLAUDE.md +5 -11
- package/.claude/hooks/README.md +14 -1
- package/.claude/hooks/code-intel-pretool.cjs +115 -0
- package/.claude/hooks/enforce-delegation.cjs +31 -3
- package/.claude/hooks/enforce-framework-boundary.cjs +324 -0
- package/.claude/hooks/enforce-permission-mode.cjs +249 -0
- package/.claude/hooks/secret-scanning.cjs +34 -43
- package/.claude/hooks/synapse-engine.cjs +23 -23
- package/.claude/hooks/telemetry-post-tool.cjs +128 -0
- package/.claude/hooks/telemetry-stop.cjs +132 -0
- package/.claude/hooks/verify-packages.cjs +9 -2
- package/.claude/rules/documentation-first.md +1 -1
- package/.claude/rules/hook-governance.md +2 -0
- package/.sinapse-ai/cli/commands/health/index.js +24 -0
- package/.sinapse-ai/core/README.md +11 -0
- package/.sinapse-ai/core/config/config-loader.js +19 -0
- package/.sinapse-ai/core/config/merge-utils.js +8 -0
- package/.sinapse-ai/core/errors/constants.js +147 -0
- package/.sinapse-ai/core/errors/error-registry.js +176 -0
- package/.sinapse-ai/core/errors/index.js +50 -0
- package/.sinapse-ai/core/errors/serializer.js +147 -0
- package/.sinapse-ai/core/errors/sinapse-error.js +144 -0
- package/.sinapse-ai/core/errors/utils.js +187 -0
- package/.sinapse-ai/core/execution/build-orchestrator.js +47 -49
- package/.sinapse-ai/core/execution/build-state-manager.js +183 -31
- package/.sinapse-ai/core/execution/parallel-executor.js +7 -1
- package/.sinapse-ai/core/execution/semantic-merge-engine.js +26 -14
- package/.sinapse-ai/core/execution/subagent-dispatcher.js +201 -60
- package/.sinapse-ai/core/execution/wave-executor.js +4 -1
- package/.sinapse-ai/core/grounding/README.md +71 -11
- package/.sinapse-ai/core/health-check/checks/project/framework-config.js +38 -2
- package/.sinapse-ai/core/health-check/checks/project/package-json.js +47 -3
- package/.sinapse-ai/core/health-check/checks/services/gemini-cli.js +117 -0
- package/.sinapse-ai/core/health-check/checks/services/index.js +2 -0
- package/.sinapse-ai/core/health-check/healers/index.js +40 -3
- package/.sinapse-ai/core/ideation/ideation-engine.js +212 -107
- package/.sinapse-ai/core/ids/gate-evaluator.js +318 -0
- package/.sinapse-ai/core/ids/gates/g5-semantic-handshake.js +190 -0
- package/.sinapse-ai/core/ids/gates/g6-ci-integrity.js +162 -0
- package/.sinapse-ai/core/ids/index.js +30 -0
- package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +11 -0
- package/.sinapse-ai/core/memory/gotchas-memory.js +37 -2
- package/.sinapse-ai/core/orchestration/agent-invoker.js +29 -6
- package/.sinapse-ai/core/orchestration/brownfield-handler.js +36 -3
- package/.sinapse-ai/core/orchestration/condition-evaluator.js +57 -0
- package/.sinapse-ai/core/orchestration/executors/epic-3-executor.js +76 -5
- package/.sinapse-ai/core/orchestration/executors/epic-4-executor.js +63 -17
- package/.sinapse-ai/core/orchestration/executors/epic-6-executor.js +153 -41
- package/.sinapse-ai/core/orchestration/executors/epic-executor.js +40 -0
- package/.sinapse-ai/core/orchestration/greenfield-handler.js +87 -3
- package/.sinapse-ai/core/orchestration/master-orchestrator.js +150 -10
- package/.sinapse-ai/core/orchestration/parallel-executor.js +6 -1
- package/.sinapse-ai/core/orchestration/recovery-handler.js +81 -8
- package/.sinapse-ai/core/orchestration/workflow-executor.js +41 -0
- package/.sinapse-ai/core/registry/registry-loader.js +71 -5
- package/.sinapse-ai/core/registry/squad-agent-resolver.js +253 -0
- package/.sinapse-ai/core/synapse/context/context-tracker.js +104 -9
- package/.sinapse-ai/core/synapse/context/index.js +19 -0
- package/.sinapse-ai/core/synapse/context/semantic-handshake-engine.js +555 -0
- package/.sinapse-ai/core/synapse/diagnostics/collectors/pipeline-collector.js +4 -2
- package/.sinapse-ai/core/synapse/engine.js +43 -3
- package/.sinapse-ai/core/telemetry/ids-sink.js +188 -0
- package/.sinapse-ai/core/utils/output-formatter.js +8 -290
- package/.sinapse-ai/core/utils/spawn-safe.js +186 -0
- package/.sinapse-ai/core-config.yaml +68 -1
- package/.sinapse-ai/data/entity-registry.yaml +15082 -13618
- package/.sinapse-ai/data/registry-update-log.jsonl +143 -0
- package/.sinapse-ai/development/agents/developer.md +2 -0
- package/.sinapse-ai/development/agents/devops.md +9 -0
- package/.sinapse-ai/development/external-executors/README.md +18 -0
- package/.sinapse-ai/development/external-executors/codex.md +56 -0
- package/.sinapse-ai/development/scripts/populate-entity-registry.js +65 -9
- package/.sinapse-ai/development/scripts/squad/squad-downloader.js +169 -14
- package/.sinapse-ai/development/tasks/delegate-to-external-executor.md +152 -0
- package/.sinapse-ai/development/tasks/github-devops-pre-push-quality-gate.md +46 -29
- package/.sinapse-ai/development/tasks/update-sinapse.md +3 -3
- package/.sinapse-ai/hooks/sinapse-brand-grounding.cjs +4 -7
- package/.sinapse-ai/hooks/sinapse-ds-grounding.cjs +5 -8
- package/.sinapse-ai/hooks/sinapse-vault-grounding.cjs +6 -9
- package/.sinapse-ai/infrastructure/integrations/ai-providers/ai-provider-factory.js +4 -1
- package/.sinapse-ai/infrastructure/integrations/ai-providers/claude-provider.js +57 -55
- package/.sinapse-ai/infrastructure/integrations/pm-adapters/github-adapter.js +9 -7
- package/.sinapse-ai/infrastructure/scripts/ide-sync/gemini-commands.js +298 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/index.js +127 -6
- package/.sinapse-ai/infrastructure/scripts/ide-sync/persona-renderer.js +97 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/antigravity.js +121 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/cursor.js +119 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/github-copilot.js +191 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/kimi.js +448 -0
- package/.sinapse-ai/install-manifest.yaml +218 -114
- package/.sinapse-ai/product/templates/engine/renderer.js +20 -1
- package/.sinapse-ai/scripts/pm.sh +18 -6
- package/bin/cli.js +17 -0
- package/bin/commands/agents.js +96 -0
- package/bin/commands/doctor.js +15 -0
- package/bin/commands/ideate.js +129 -0
- package/bin/commands/uninstall.js +40 -0
- package/bin/postinstall.js +50 -4
- package/bin/sinapse.js +146 -2
- package/bin/utils/secret-scanner-core.js +253 -0
- package/bin/utils/staged-secret-scan.js +106 -40
- package/docs/framework/collaboration-autonomy-plan.md +18 -18
- package/docs/guides/parallel-workflow.md +6 -6
- package/package.json +22 -5
- package/packages/installer/src/installer/git-hooks-installer.js +384 -0
- package/packages/installer/src/installer/sinapse-ai-installer.js +16 -0
- package/packages/installer/src/wizard/ide-config-generator.js +23 -0
- package/packages/installer/src/wizard/validators.js +38 -1
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +5 -1
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +44 -22
- package/packages/installer/tests/unit/git-hooks-installer.test.js +262 -0
- package/scripts/eval-runner.js +422 -0
- package/scripts/generate-install-manifest.js +13 -9
- package/scripts/generate-synapse-runtime.js +51 -0
- package/scripts/regenerate-orqx-stubs.ps1 +6 -5
- package/scripts/validate-all.js +1 -0
- package/scripts/validate-evals.js +466 -0
- package/scripts/validate-schemas.js +539 -0
- package/scripts/validate-squad-orqx.js +9 -2
- package/squads/claude-code-mastery/knowledge-base/memory-systems-reference.md +1 -1
- package/squads/squad-brand/templates/client-delivery-template.md +1 -1
- package/squads/squad-content/knowledge-base/social-compression-framework.md +1 -1
- package/squads/squad-council/knowledge-base/brand-strategy-models.md +1 -1
- package/.sinapse-ai/development/scripts/elicitation-engine.js +0 -385
- package/.sinapse-ai/development/scripts/elicitation-session-manager.js +0 -300
- package/.sinapse-ai/development/tasks/test-validation-task.md +0 -172
- package/docs/chrome-brain-upgrade-plan.md +0 -624
- package/docs/constitution-compliance.md +0 -87
- package/docs/mega-upgrade-orchestration-plan.md +0 -71
- package/docs/research-synthesis-for-upgrade.md +0 -511
- package/docs/security-audit-report.md +0 -306
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive Elicitation Engine for SINAPSE-FULLSTACK
|
|
3
|
-
* Handles progressive disclosure and contextual validation for component creation
|
|
4
|
-
* @module elicitation-engine
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const inquirer = require('inquirer');
|
|
8
|
-
const fs = require('fs-extra');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const SecurityChecker = require('./security-checker');
|
|
11
|
-
const ElicitationSessionManager = require('./elicitation-session-manager');
|
|
12
|
-
const chalk = require('chalk');
|
|
13
|
-
|
|
14
|
-
class ElicitationEngine {
|
|
15
|
-
constructor() {
|
|
16
|
-
this.securityChecker = new SecurityChecker();
|
|
17
|
-
this.sessionManager = new ElicitationSessionManager();
|
|
18
|
-
this.sessionData = {};
|
|
19
|
-
this.sessionFile = null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Start a new elicitation session
|
|
24
|
-
* @param {string} componentType - Type of component being created
|
|
25
|
-
* @param {Object} options - Session options
|
|
26
|
-
*/
|
|
27
|
-
async startSession(componentType, options = {}) {
|
|
28
|
-
this.sessionData = {
|
|
29
|
-
componentType,
|
|
30
|
-
startTime: new Date().toISOString(),
|
|
31
|
-
answers: {},
|
|
32
|
-
currentStep: 0,
|
|
33
|
-
options
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
if (options.saveSession) {
|
|
37
|
-
this.sessionFile = path.join(
|
|
38
|
-
process.cwd(),
|
|
39
|
-
'.sinapse-sessions',
|
|
40
|
-
`${componentType}-${Date.now()}.json`
|
|
41
|
-
);
|
|
42
|
-
await fs.ensureDir(path.dirname(this.sessionFile));
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Run progressive elicitation workflow
|
|
48
|
-
* @param {Array} steps - Array of elicitation steps
|
|
49
|
-
* @returns {Promise<Object>} Collected answers
|
|
50
|
-
*/
|
|
51
|
-
async runProgressive(steps) {
|
|
52
|
-
// If mocked, return mocked answers immediately
|
|
53
|
-
if (this.isMocked) {
|
|
54
|
-
this.isMocked = false;
|
|
55
|
-
return this.mockedAnswers;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
console.log(chalk.blue(`\n🚀 Starting ${this.sessionData.componentType} creation wizard...\n`));
|
|
59
|
-
|
|
60
|
-
for (let i = 0; i < steps.length; i++) {
|
|
61
|
-
const step = steps[i];
|
|
62
|
-
this.sessionData.currentStep = i;
|
|
63
|
-
|
|
64
|
-
// Show step header
|
|
65
|
-
console.log(chalk.yellow(`\n📋 Step ${i + 1}/${steps.length}: ${step.title}`));
|
|
66
|
-
if (step.description) {
|
|
67
|
-
console.log(chalk.gray(step.description));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Check if step should be shown based on previous answers
|
|
71
|
-
if (step.condition && !this.evaluateCondition(step.condition)) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Run step questions
|
|
76
|
-
const stepAnswers = await this.runStep(step);
|
|
77
|
-
Object.assign(this.sessionData.answers, stepAnswers);
|
|
78
|
-
|
|
79
|
-
// Save session after each step
|
|
80
|
-
if (this.sessionFile) {
|
|
81
|
-
await this.saveSession();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Allow early exit if requested
|
|
85
|
-
if (stepAnswers._exit) {
|
|
86
|
-
console.log(chalk.yellow('\n⚠️ Elicitation cancelled by user'));
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return this.sessionData.answers;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Run a single elicitation step
|
|
96
|
-
* @private
|
|
97
|
-
*/
|
|
98
|
-
async runStep(step) {
|
|
99
|
-
const questions = step.questions.map(q => this.enhanceQuestion(q, step));
|
|
100
|
-
|
|
101
|
-
// Add contextual help if available
|
|
102
|
-
if (step.help) {
|
|
103
|
-
questions.unshift({
|
|
104
|
-
type: 'confirm',
|
|
105
|
-
name: '_showHelp',
|
|
106
|
-
message: 'Would you like to see help for this step?',
|
|
107
|
-
default: false
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const answers = await inquirer.prompt(questions);
|
|
112
|
-
|
|
113
|
-
// Show help if requested
|
|
114
|
-
if (answers._showHelp && step.help) {
|
|
115
|
-
console.log(chalk.cyan('\n💡 ' + step.help));
|
|
116
|
-
delete answers._showHelp;
|
|
117
|
-
return this.runStep(step); // Re-run the step
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Validate answers
|
|
121
|
-
const validation = await this.validateStepAnswers(answers, step);
|
|
122
|
-
if (!validation.valid) {
|
|
123
|
-
console.log(chalk.red('\n❌ Validation errors:'));
|
|
124
|
-
validation.errors.forEach(err => console.log(chalk.red(` - ${err}`)));
|
|
125
|
-
return this.runStep(step); // Re-run the step
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return answers;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Enhance a question with smart defaults and validation
|
|
133
|
-
* @private
|
|
134
|
-
*/
|
|
135
|
-
enhanceQuestion(question, step) {
|
|
136
|
-
const enhanced = { ...question };
|
|
137
|
-
|
|
138
|
-
// Add smart defaults based on previous answers
|
|
139
|
-
if (question.smartDefault) {
|
|
140
|
-
enhanced.default = this.getSmartDefault(question.smartDefault);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Add validation with security checks
|
|
144
|
-
const originalValidate = enhanced.validate;
|
|
145
|
-
enhanced.validate = async (input) => {
|
|
146
|
-
// Type validation
|
|
147
|
-
if (typeof input !== 'string' && question.type === 'input') {
|
|
148
|
-
return 'Invalid input type';
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Security validation using the refactored SecurityChecker
|
|
152
|
-
// Note: SecurityChecker.checkCode expects string input for validation
|
|
153
|
-
const securityResult = this.securityChecker.checkCode(String(input));
|
|
154
|
-
if (!securityResult.valid) {
|
|
155
|
-
return `Security check failed: ${securityResult.errors[0]?.message || 'Invalid input'}`;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Original validation
|
|
159
|
-
if (originalValidate) {
|
|
160
|
-
const result = await originalValidate(input);
|
|
161
|
-
if (result !== true) return result;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Step-specific validation
|
|
165
|
-
if (step.validation && step.validation[question.name]) {
|
|
166
|
-
const validator = step.validation[question.name];
|
|
167
|
-
const result = await this.runValidator(validator, input);
|
|
168
|
-
if (result !== true) return result;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return true;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
// Add examples to message if available
|
|
175
|
-
if (question.examples && question.examples.length > 0) {
|
|
176
|
-
enhanced.message += chalk.gray(` (e.g., ${question.examples.join(', ')})`);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return enhanced;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Get smart default value based on previous answers
|
|
184
|
-
* @private
|
|
185
|
-
*/
|
|
186
|
-
getSmartDefault(smartDefaultConfig) {
|
|
187
|
-
const { type, source, transform } = smartDefaultConfig;
|
|
188
|
-
|
|
189
|
-
switch (type) {
|
|
190
|
-
case 'fromAnswer':
|
|
191
|
-
const value = this.sessionData.answers[source];
|
|
192
|
-
return transform ? transform(value) : value;
|
|
193
|
-
|
|
194
|
-
case 'generated':
|
|
195
|
-
return this.generateDefault(smartDefaultConfig);
|
|
196
|
-
|
|
197
|
-
case 'conditional':
|
|
198
|
-
const condition = this.evaluateCondition(smartDefaultConfig.condition);
|
|
199
|
-
return condition ? smartDefaultConfig.ifTrue : smartDefaultConfig.ifFalse;
|
|
200
|
-
|
|
201
|
-
default:
|
|
202
|
-
return undefined;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Generate a default value
|
|
208
|
-
* @private
|
|
209
|
-
*/
|
|
210
|
-
generateDefault(config) {
|
|
211
|
-
switch (config.generator) {
|
|
212
|
-
case 'kebabCase':
|
|
213
|
-
const source = this.sessionData.answers[config.source] || '';
|
|
214
|
-
return source.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
215
|
-
|
|
216
|
-
case 'timestamp':
|
|
217
|
-
return new Date().toISOString();
|
|
218
|
-
|
|
219
|
-
case 'version':
|
|
220
|
-
return '1.0.0';
|
|
221
|
-
|
|
222
|
-
default:
|
|
223
|
-
return '';
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Evaluate a condition based on answers
|
|
229
|
-
* @private
|
|
230
|
-
*/
|
|
231
|
-
evaluateCondition(condition) {
|
|
232
|
-
const { field, operator, value } = condition;
|
|
233
|
-
const fieldValue = this.sessionData.answers[field];
|
|
234
|
-
|
|
235
|
-
switch (operator) {
|
|
236
|
-
case 'equals':
|
|
237
|
-
return fieldValue === value;
|
|
238
|
-
case 'notEquals':
|
|
239
|
-
return fieldValue !== value;
|
|
240
|
-
case 'includes':
|
|
241
|
-
return Array.isArray(fieldValue) && fieldValue.includes(value);
|
|
242
|
-
case 'exists':
|
|
243
|
-
return fieldValue !== undefined && fieldValue !== null;
|
|
244
|
-
default:
|
|
245
|
-
return true;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Validate step answers
|
|
251
|
-
* @private
|
|
252
|
-
*/
|
|
253
|
-
async validateStepAnswers(answers, step) {
|
|
254
|
-
const errors = [];
|
|
255
|
-
|
|
256
|
-
// Check required fields
|
|
257
|
-
if (step.required) {
|
|
258
|
-
for (const field of step.required) {
|
|
259
|
-
if (!answers[field]) {
|
|
260
|
-
errors.push(`${field} is required`);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Run custom validators
|
|
266
|
-
if (step.validators) {
|
|
267
|
-
for (const validator of step.validators) {
|
|
268
|
-
const result = await this.runValidator(validator, answers);
|
|
269
|
-
if (result !== true) {
|
|
270
|
-
errors.push(result);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return {
|
|
276
|
-
valid: errors.length === 0,
|
|
277
|
-
errors
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Run a validator function
|
|
283
|
-
* @private
|
|
284
|
-
*/
|
|
285
|
-
async runValidator(validator, value) {
|
|
286
|
-
if (typeof validator === 'function') {
|
|
287
|
-
return validator(value);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (typeof validator === 'object') {
|
|
291
|
-
switch (validator.type) {
|
|
292
|
-
case 'regex':
|
|
293
|
-
const regex = new RegExp(validator.pattern);
|
|
294
|
-
return regex.test(value) || validator.message;
|
|
295
|
-
|
|
296
|
-
case 'length':
|
|
297
|
-
if (validator.min && value.length < validator.min) {
|
|
298
|
-
return `Must be at least ${validator.min} characters`;
|
|
299
|
-
}
|
|
300
|
-
if (validator.max && value.length > validator.max) {
|
|
301
|
-
return `Must be at most ${validator.max} characters`;
|
|
302
|
-
}
|
|
303
|
-
return true;
|
|
304
|
-
|
|
305
|
-
case 'unique':
|
|
306
|
-
const exists = await this.checkExists(validator.path, value);
|
|
307
|
-
return !exists || `${value} already exists`;
|
|
308
|
-
|
|
309
|
-
default:
|
|
310
|
-
return true;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return true;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Check if a component already exists
|
|
319
|
-
* @private
|
|
320
|
-
*/
|
|
321
|
-
async checkExists(pathTemplate, name) {
|
|
322
|
-
const filePath = pathTemplate.replace('{name}', name);
|
|
323
|
-
return fs.pathExists(filePath);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Save current session to file
|
|
328
|
-
* @private
|
|
329
|
-
*/
|
|
330
|
-
async saveSession() {
|
|
331
|
-
if (this.sessionFile) {
|
|
332
|
-
await fs.writeJson(this.sessionFile, this.sessionData, { spaces: 2 });
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Load a saved session
|
|
338
|
-
* @param {string} sessionPath - Path to session file
|
|
339
|
-
*/
|
|
340
|
-
async loadSession(sessionPath) {
|
|
341
|
-
this.sessionData = await fs.readJson(sessionPath);
|
|
342
|
-
this.sessionFile = sessionPath;
|
|
343
|
-
return this.sessionData;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Get session summary
|
|
348
|
-
* @returns {Object} Summary of current session
|
|
349
|
-
*/
|
|
350
|
-
getSessionSummary() {
|
|
351
|
-
return {
|
|
352
|
-
componentType: this.sessionData.componentType,
|
|
353
|
-
completedSteps: this.sessionData.currentStep + 1,
|
|
354
|
-
answers: Object.keys(this.sessionData.answers).length,
|
|
355
|
-
duration: this.sessionData.startTime ?
|
|
356
|
-
Date.now() - new Date(this.sessionData.startTime).getTime() : 0
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Mock a session with predefined answers for batch creation
|
|
362
|
-
* @param {Object} answers - Predefined answers
|
|
363
|
-
*/
|
|
364
|
-
async mockSession(answers) {
|
|
365
|
-
this.mockedAnswers = answers;
|
|
366
|
-
this.isMocked = true;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Complete elicitation session
|
|
371
|
-
* @param {string} status - Completion status
|
|
372
|
-
*/
|
|
373
|
-
async completeSession(status) {
|
|
374
|
-
if (this.currentSession) {
|
|
375
|
-
this.currentSession.status = status;
|
|
376
|
-
this.currentSession.completedAt = new Date().toISOString();
|
|
377
|
-
|
|
378
|
-
if (this.currentSession.saveSession) {
|
|
379
|
-
await this.sessionManager.saveSession(this.currentSession);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
module.exports = ElicitationEngine;
|
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Elicitation Session Manager
|
|
3
|
-
* Handles saving and loading elicitation sessions
|
|
4
|
-
* @module elicitation-session-manager
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const fs = require('fs-extra');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const crypto = require('crypto');
|
|
10
|
-
|
|
11
|
-
class ElicitationSessionManager {
|
|
12
|
-
constructor(sessionDir = '.sinapse-sessions') {
|
|
13
|
-
this.sessionDir = path.resolve(process.cwd(), sessionDir);
|
|
14
|
-
this.activeSession = null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Initialize session storage
|
|
19
|
-
*/
|
|
20
|
-
async init() {
|
|
21
|
-
await fs.ensureDir(this.sessionDir);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Create a new session
|
|
26
|
-
* @param {string} type - Component type (agent, task, workflow)
|
|
27
|
-
* @param {Object} metadata - Additional session metadata
|
|
28
|
-
* @returns {Promise<string>} Session ID
|
|
29
|
-
*/
|
|
30
|
-
async createSession(type, metadata = {}) {
|
|
31
|
-
const sessionId = this.generateSessionId();
|
|
32
|
-
const session = {
|
|
33
|
-
id: sessionId,
|
|
34
|
-
type,
|
|
35
|
-
version: '1.0',
|
|
36
|
-
created: new Date().toISOString(),
|
|
37
|
-
updated: new Date().toISOString(),
|
|
38
|
-
status: 'active',
|
|
39
|
-
currentStep: 0,
|
|
40
|
-
totalSteps: 0,
|
|
41
|
-
answers: {},
|
|
42
|
-
metadata: {
|
|
43
|
-
...metadata,
|
|
44
|
-
user: process.env.USER || 'unknown',
|
|
45
|
-
hostname: require('os').hostname()
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
this.activeSession = session;
|
|
50
|
-
await this.saveSession(session);
|
|
51
|
-
|
|
52
|
-
return sessionId;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Save current session state
|
|
57
|
-
* @param {Object} session - Session data to save
|
|
58
|
-
*/
|
|
59
|
-
async saveSession(session = null) {
|
|
60
|
-
const sessionToSave = session || this.activeSession;
|
|
61
|
-
if (!sessionToSave) {
|
|
62
|
-
throw new Error('No active session to save');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
sessionToSave.updated = new Date().toISOString();
|
|
66
|
-
|
|
67
|
-
const sessionPath = this.getSessionPath(sessionToSave.id);
|
|
68
|
-
await fs.writeJson(sessionPath, sessionToSave, { spaces: 2 });
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Load an existing session
|
|
73
|
-
* @param {string} sessionId - Session ID to load
|
|
74
|
-
* @returns {Promise<Object>} Session data
|
|
75
|
-
*/
|
|
76
|
-
async loadSession(sessionId) {
|
|
77
|
-
const sessionPath = this.getSessionPath(sessionId);
|
|
78
|
-
|
|
79
|
-
if (!await fs.pathExists(sessionPath)) {
|
|
80
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const session = await fs.readJson(sessionPath);
|
|
84
|
-
this.activeSession = session;
|
|
85
|
-
|
|
86
|
-
return session;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Update session answers
|
|
91
|
-
* @param {Object} answers - New answers to merge
|
|
92
|
-
* @param {number} stepIndex - Current step index
|
|
93
|
-
*/
|
|
94
|
-
async updateAnswers(answers, stepIndex = null) {
|
|
95
|
-
if (!this.activeSession) {
|
|
96
|
-
throw new Error('No active session');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Merge answers
|
|
100
|
-
Object.assign(this.activeSession.answers, answers);
|
|
101
|
-
|
|
102
|
-
// Update step index if provided
|
|
103
|
-
if (stepIndex !== null) {
|
|
104
|
-
this.activeSession.currentStep = stepIndex;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
await this.saveSession();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* List all sessions
|
|
112
|
-
* @param {Object} filters - Filter options
|
|
113
|
-
* @returns {Promise<Array>} List of sessions
|
|
114
|
-
*/
|
|
115
|
-
async listSessions(filters = {}) {
|
|
116
|
-
const files = await fs.readdir(this.sessionDir);
|
|
117
|
-
const sessions = [];
|
|
118
|
-
|
|
119
|
-
for (const file of files) {
|
|
120
|
-
if (file.endsWith('.json')) {
|
|
121
|
-
try {
|
|
122
|
-
const sessionPath = path.join(this.sessionDir, file);
|
|
123
|
-
const session = await fs.readJson(sessionPath);
|
|
124
|
-
|
|
125
|
-
// Apply filters
|
|
126
|
-
if (filters.type && session.type !== filters.type) continue;
|
|
127
|
-
if (filters.status && session.status !== filters.status) continue;
|
|
128
|
-
if (filters.after && new Date(session.created) < new Date(filters.after)) continue;
|
|
129
|
-
|
|
130
|
-
sessions.push({
|
|
131
|
-
id: session.id,
|
|
132
|
-
type: session.type,
|
|
133
|
-
created: session.created,
|
|
134
|
-
updated: session.updated,
|
|
135
|
-
status: session.status,
|
|
136
|
-
progress: session.totalSteps > 0 ?
|
|
137
|
-
Math.round((session.currentStep / session.totalSteps) * 100) : 0
|
|
138
|
-
});
|
|
139
|
-
} catch (_error) {
|
|
140
|
-
// Skip invalid session files
|
|
141
|
-
console.warn(`Invalid session file: ${file}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Sort by updated date (newest first)
|
|
147
|
-
sessions.sort((a, b) => new Date(b.updated) - new Date(a.updated));
|
|
148
|
-
|
|
149
|
-
return sessions;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Resume a session
|
|
154
|
-
* @param {string} sessionId - Session ID to resume
|
|
155
|
-
* @returns {Promise<Object>} Session data with resume info
|
|
156
|
-
*/
|
|
157
|
-
async resumeSession(sessionId) {
|
|
158
|
-
const session = await this.loadSession(sessionId);
|
|
159
|
-
|
|
160
|
-
// Calculate resume information
|
|
161
|
-
const resumeInfo = {
|
|
162
|
-
...session,
|
|
163
|
-
resumeFrom: session.currentStep,
|
|
164
|
-
completedSteps: Object.keys(session.answers).length,
|
|
165
|
-
remainingSteps: session.totalSteps - session.currentStep,
|
|
166
|
-
percentComplete: session.totalSteps > 0 ?
|
|
167
|
-
Math.round((session.currentStep / session.totalSteps) * 100) : 0
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
return resumeInfo;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Complete a session
|
|
175
|
-
* @param {string} result - Completion result (success, cancelled, error)
|
|
176
|
-
*/
|
|
177
|
-
async completeSession(result = 'success') {
|
|
178
|
-
if (!this.activeSession) {
|
|
179
|
-
throw new Error('No active session');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
this.activeSession.status = 'completed';
|
|
183
|
-
this.activeSession.completedAt = new Date().toISOString();
|
|
184
|
-
this.activeSession.result = result;
|
|
185
|
-
|
|
186
|
-
await this.saveSession();
|
|
187
|
-
|
|
188
|
-
// Move to completed directory if success
|
|
189
|
-
if (result === 'success') {
|
|
190
|
-
const completedDir = path.join(this.sessionDir, 'completed');
|
|
191
|
-
await fs.ensureDir(completedDir);
|
|
192
|
-
|
|
193
|
-
const oldPath = this.getSessionPath(this.activeSession.id);
|
|
194
|
-
const newPath = path.join(completedDir, path.basename(oldPath));
|
|
195
|
-
|
|
196
|
-
await fs.move(oldPath, newPath, { overwrite: true });
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
this.activeSession = null;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Delete a session
|
|
204
|
-
* @param {string} sessionId - Session ID to delete
|
|
205
|
-
*/
|
|
206
|
-
async deleteSession(sessionId) {
|
|
207
|
-
const sessionPath = this.getSessionPath(sessionId);
|
|
208
|
-
const completedPath = path.join(this.sessionDir, 'completed', `${sessionId}.json`);
|
|
209
|
-
|
|
210
|
-
// Check both active and completed directories
|
|
211
|
-
if (await fs.pathExists(sessionPath)) {
|
|
212
|
-
await fs.remove(sessionPath);
|
|
213
|
-
} else if (await fs.pathExists(completedPath)) {
|
|
214
|
-
await fs.remove(completedPath);
|
|
215
|
-
} else {
|
|
216
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Clear active session if it matches
|
|
220
|
-
if (this.activeSession && this.activeSession.id === sessionId) {
|
|
221
|
-
this.activeSession = null;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Export session data
|
|
227
|
-
* @param {string} sessionId - Session ID to export
|
|
228
|
-
* @param {string} format - Export format (json, yaml)
|
|
229
|
-
* @returns {Promise<string>} Exported data
|
|
230
|
-
*/
|
|
231
|
-
async exportSession(sessionId, format = 'json') {
|
|
232
|
-
const session = await this.loadSession(sessionId);
|
|
233
|
-
|
|
234
|
-
switch (format) {
|
|
235
|
-
case 'json':
|
|
236
|
-
return JSON.stringify(session, null, 2);
|
|
237
|
-
|
|
238
|
-
case 'yaml':
|
|
239
|
-
const yaml = require('js-yaml');
|
|
240
|
-
return yaml.dump(session);
|
|
241
|
-
|
|
242
|
-
default:
|
|
243
|
-
throw new Error(`Unsupported export format: ${format}`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Clean up old sessions
|
|
249
|
-
* @param {number} daysOld - Delete sessions older than this many days
|
|
250
|
-
*/
|
|
251
|
-
async cleanupOldSessions(daysOld = 30) {
|
|
252
|
-
const sessions = await this.listSessions();
|
|
253
|
-
const cutoffDate = new Date();
|
|
254
|
-
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
|
|
255
|
-
|
|
256
|
-
let deletedCount = 0;
|
|
257
|
-
|
|
258
|
-
for (const session of sessions) {
|
|
259
|
-
if (new Date(session.updated) < cutoffDate && session.status !== 'active') {
|
|
260
|
-
await this.deleteSession(session.id);
|
|
261
|
-
deletedCount++;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return deletedCount;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Generate a unique session ID
|
|
270
|
-
* @private
|
|
271
|
-
*/
|
|
272
|
-
generateSessionId() {
|
|
273
|
-
return crypto.randomBytes(8).toString('hex');
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Get session file path
|
|
278
|
-
* @private
|
|
279
|
-
*/
|
|
280
|
-
getSessionPath(sessionId) {
|
|
281
|
-
return path.join(this.sessionDir, `${sessionId}.json`);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Get active session
|
|
286
|
-
* @returns {Object|null} Active session or null
|
|
287
|
-
*/
|
|
288
|
-
getActiveSession() {
|
|
289
|
-
return this.activeSession;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Clear active session
|
|
294
|
-
*/
|
|
295
|
-
clearActiveSession() {
|
|
296
|
-
this.activeSession = null;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
module.exports = ElicitationSessionManager;
|