237 lines
6.6 KiB
JavaScript
Executable File
237 lines
6.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Sequential Thinking Thought Processor
|
|
*
|
|
* Validates and tracks sequential thoughts with revision and branching support.
|
|
* Provides deterministic validation and context management.
|
|
*
|
|
* Usage:
|
|
* node process-thought.js --thought "Analysis text" --number 1 --total 5 --next true
|
|
* node process-thought.js --thought "Revision" --number 2 --total 5 --next true --revision 1
|
|
* node process-thought.js --reset # Reset thought history
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Configuration
|
|
const HISTORY_FILE = path.join(__dirname, '.thought-history.json');
|
|
const DISABLE_LOGGING = process.env.DISABLE_THOUGHT_LOGGING?.toLowerCase() === 'true';
|
|
|
|
class ThoughtProcessor {
|
|
constructor() {
|
|
this.loadHistory();
|
|
}
|
|
|
|
loadHistory() {
|
|
try {
|
|
if (fs.existsSync(HISTORY_FILE)) {
|
|
const data = JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf8'));
|
|
this.thoughtHistory = data.thoughtHistory || [];
|
|
this.branches = data.branches || {};
|
|
} else {
|
|
this.thoughtHistory = [];
|
|
this.branches = {};
|
|
}
|
|
} catch (error) {
|
|
this.thoughtHistory = [];
|
|
this.branches = {};
|
|
}
|
|
}
|
|
|
|
saveHistory() {
|
|
fs.writeFileSync(
|
|
HISTORY_FILE,
|
|
JSON.stringify({
|
|
thoughtHistory: this.thoughtHistory,
|
|
branches: this.branches
|
|
}, null, 2)
|
|
);
|
|
}
|
|
|
|
resetHistory() {
|
|
this.thoughtHistory = [];
|
|
this.branches = {};
|
|
if (fs.existsSync(HISTORY_FILE)) {
|
|
fs.unlinkSync(HISTORY_FILE);
|
|
}
|
|
}
|
|
|
|
validateThought(input) {
|
|
const errors = [];
|
|
|
|
if (!input.thought || typeof input.thought !== 'string' || input.thought.trim() === '') {
|
|
errors.push('Invalid thought: must be a non-empty string');
|
|
}
|
|
|
|
if (!input.thoughtNumber || typeof input.thoughtNumber !== 'number' || input.thoughtNumber < 1) {
|
|
errors.push('Invalid thoughtNumber: must be a positive number');
|
|
}
|
|
|
|
if (!input.totalThoughts || typeof input.totalThoughts !== 'number' || input.totalThoughts < 1) {
|
|
errors.push('Invalid totalThoughts: must be a positive number');
|
|
}
|
|
|
|
if (typeof input.nextThoughtNeeded !== 'boolean') {
|
|
errors.push('Invalid nextThoughtNeeded: must be a boolean');
|
|
}
|
|
|
|
// Optional field validations
|
|
if (input.isRevision !== undefined && typeof input.isRevision !== 'boolean') {
|
|
errors.push('Invalid isRevision: must be a boolean');
|
|
}
|
|
|
|
if (input.revisesThought !== undefined && (typeof input.revisesThought !== 'number' || input.revisesThought < 1)) {
|
|
errors.push('Invalid revisesThought: must be a positive number');
|
|
}
|
|
|
|
if (input.branchFromThought !== undefined && (typeof input.branchFromThought !== 'number' || input.branchFromThought < 1)) {
|
|
errors.push('Invalid branchFromThought: must be a positive number');
|
|
}
|
|
|
|
if (input.branchId !== undefined && typeof input.branchId !== 'string') {
|
|
errors.push('Invalid branchId: must be a string');
|
|
}
|
|
|
|
if (input.needsMoreThoughts !== undefined && typeof input.needsMoreThoughts !== 'boolean') {
|
|
errors.push('Invalid needsMoreThoughts: must be a boolean');
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
processThought(input) {
|
|
const errors = this.validateThought(input);
|
|
|
|
if (errors.length > 0) {
|
|
return {
|
|
success: false,
|
|
errors,
|
|
status: 'failed'
|
|
};
|
|
}
|
|
|
|
// Auto-adjust totalThoughts if thoughtNumber exceeds it
|
|
if (input.thoughtNumber > input.totalThoughts) {
|
|
input.totalThoughts = input.thoughtNumber;
|
|
}
|
|
|
|
// Create thought data
|
|
const thoughtData = {
|
|
thought: input.thought,
|
|
thoughtNumber: input.thoughtNumber,
|
|
totalThoughts: input.totalThoughts,
|
|
nextThoughtNeeded: input.nextThoughtNeeded,
|
|
isRevision: input.isRevision,
|
|
revisesThought: input.revisesThought,
|
|
branchFromThought: input.branchFromThought,
|
|
branchId: input.branchId,
|
|
needsMoreThoughts: input.needsMoreThoughts,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
// Add to history
|
|
this.thoughtHistory.push(thoughtData);
|
|
|
|
// Track branches
|
|
if (thoughtData.branchFromThought && thoughtData.branchId) {
|
|
if (!this.branches[thoughtData.branchId]) {
|
|
this.branches[thoughtData.branchId] = [];
|
|
}
|
|
this.branches[thoughtData.branchId].push(thoughtData);
|
|
}
|
|
|
|
// Save history
|
|
this.saveHistory();
|
|
|
|
return {
|
|
success: true,
|
|
thoughtNumber: thoughtData.thoughtNumber,
|
|
totalThoughts: thoughtData.totalThoughts,
|
|
nextThoughtNeeded: thoughtData.nextThoughtNeeded,
|
|
branches: Object.keys(this.branches),
|
|
thoughtHistoryLength: this.thoughtHistory.length,
|
|
timestamp: thoughtData.timestamp
|
|
};
|
|
}
|
|
|
|
getHistory() {
|
|
return {
|
|
thoughts: this.thoughtHistory,
|
|
branches: this.branches,
|
|
totalThoughts: this.thoughtHistory.length
|
|
};
|
|
}
|
|
}
|
|
|
|
// CLI Interface
|
|
if (require.main === module) {
|
|
const args = process.argv.slice(2);
|
|
const processor = new ThoughtProcessor();
|
|
|
|
// Parse arguments
|
|
const parseArgs = (args) => {
|
|
const parsed = {};
|
|
for (let i = 0; i < args.length; i++) {
|
|
const arg = args[i];
|
|
if (arg.startsWith('--')) {
|
|
const key = arg.slice(2);
|
|
const value = args[i + 1];
|
|
|
|
if (key === 'reset') {
|
|
return { reset: true };
|
|
}
|
|
|
|
if (key === 'history') {
|
|
return { history: true };
|
|
}
|
|
|
|
if (value && !value.startsWith('--')) {
|
|
// Parse boolean
|
|
if (value === 'true') parsed[key] = true;
|
|
else if (value === 'false') parsed[key] = false;
|
|
// Parse number
|
|
else if (!isNaN(value)) parsed[key] = parseFloat(value);
|
|
// String
|
|
else parsed[key] = value;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
return parsed;
|
|
};
|
|
|
|
const input = parseArgs(args);
|
|
|
|
if (input.reset) {
|
|
processor.resetHistory();
|
|
console.log(JSON.stringify({ success: true, message: 'History reset' }, null, 2));
|
|
process.exit(0);
|
|
}
|
|
|
|
if (input.history) {
|
|
console.log(JSON.stringify(processor.getHistory(), null, 2));
|
|
process.exit(0);
|
|
}
|
|
|
|
// Map CLI args to expected field names
|
|
const thoughtInput = {
|
|
thought: input.thought,
|
|
thoughtNumber: input.number,
|
|
totalThoughts: input.total,
|
|
nextThoughtNeeded: input.next,
|
|
isRevision: input.revision !== undefined ? true : input.isRevision,
|
|
revisesThought: input.revision,
|
|
branchFromThought: input.branch,
|
|
branchId: input.branchId,
|
|
needsMoreThoughts: input.needsMore
|
|
};
|
|
|
|
const result = processor.processThought(thoughtInput);
|
|
console.log(JSON.stringify(result, null, 2));
|
|
process.exit(result.success ? 0 : 1);
|
|
}
|
|
|
|
module.exports = { ThoughtProcessor };
|