zouroboros-workflow 2.0.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 +21 -0
- package/README.md +153 -0
- package/dist/autoloop/loop.d.ts +48 -0
- package/dist/autoloop/loop.js +145 -0
- package/dist/autoloop/parser.d.ts +12 -0
- package/dist/autoloop/parser.js +121 -0
- package/dist/autoloop/types.d.ts +54 -0
- package/dist/autoloop/types.js +4 -0
- package/dist/cli/autoloop.d.ts +7 -0
- package/dist/cli/autoloop.js +170 -0
- package/dist/cli/evaluate.d.ts +7 -0
- package/dist/cli/evaluate.js +166 -0
- package/dist/cli/interview.d.ts +7 -0
- package/dist/cli/interview.js +117 -0
- package/dist/cli/unstuck.d.ts +7 -0
- package/dist/cli/unstuck.js +150 -0
- package/dist/evaluate/mechanical.d.ts +8 -0
- package/dist/evaluate/mechanical.js +106 -0
- package/dist/evaluate/semantic.d.ts +13 -0
- package/dist/evaluate/semantic.js +167 -0
- package/dist/evaluate/types.d.ts +57 -0
- package/dist/evaluate/types.js +4 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +20 -0
- package/dist/interview/ambiguity.d.ts +9 -0
- package/dist/interview/ambiguity.js +53 -0
- package/dist/interview/seed.d.ts +12 -0
- package/dist/interview/seed.js +120 -0
- package/dist/interview/types.d.ts +49 -0
- package/dist/interview/types.js +4 -0
- package/dist/unstuck/strategies.d.ts +20 -0
- package/dist/unstuck/strategies.js +164 -0
- package/dist/unstuck/types.d.ts +23 -0
- package/dist/unstuck/types.js +4 -0
- package/package.json +51 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI for autoloop optimization
|
|
4
|
+
*
|
|
5
|
+
* Usage: zouroboros-autoloop --program <program.md>
|
|
6
|
+
*/
|
|
7
|
+
import { parseArgs } from 'util';
|
|
8
|
+
import { existsSync } from 'fs';
|
|
9
|
+
import { parseProgram, validateProgram } from '../autoloop/parser.js';
|
|
10
|
+
import { initState, shouldContinue, getStagnationLevel, getStagnationModifier } from '../autoloop/loop.js';
|
|
11
|
+
const { values } = parseArgs({
|
|
12
|
+
args: Bun.argv.slice(2),
|
|
13
|
+
options: {
|
|
14
|
+
program: { type: 'string', short: 'p' },
|
|
15
|
+
'dry-run': { type: 'boolean', default: false },
|
|
16
|
+
resume: { type: 'boolean', default: false },
|
|
17
|
+
help: { type: 'boolean', short: 'h' }
|
|
18
|
+
},
|
|
19
|
+
strict: false
|
|
20
|
+
});
|
|
21
|
+
function printHelp() {
|
|
22
|
+
console.log(`
|
|
23
|
+
zouroboros-autoloop ā Autonomous single-metric optimization
|
|
24
|
+
|
|
25
|
+
USAGE:
|
|
26
|
+
zouroboros-autoloop --program <program.md>
|
|
27
|
+
zouroboros-autoloop --program <program.md> --dry-run
|
|
28
|
+
|
|
29
|
+
OPTIONS:
|
|
30
|
+
--program, -p Path to program.md file (required)
|
|
31
|
+
--dry-run Parse and validate program without running
|
|
32
|
+
--resume Resume from existing autoloop branch
|
|
33
|
+
--help, -h Show this help
|
|
34
|
+
|
|
35
|
+
HOW IT WORKS:
|
|
36
|
+
1. Reads your program.md configuration
|
|
37
|
+
2. Creates a git branch autoloop/{name}-{date}
|
|
38
|
+
3. Runs baseline (no changes)
|
|
39
|
+
4. Loops until improvement or limits reached:
|
|
40
|
+
- Agent proposes a change to target file
|
|
41
|
+
- Commits the change
|
|
42
|
+
- Runs experiment and measures metric
|
|
43
|
+
- Keeps if improved, reverts if regressed
|
|
44
|
+
5. Writes results.tsv with full history
|
|
45
|
+
|
|
46
|
+
EXAMPLE program.md:
|
|
47
|
+
# Program: Optimize Sorting
|
|
48
|
+
|
|
49
|
+
## Objective
|
|
50
|
+
Minimize the execution time of the sorting function.
|
|
51
|
+
|
|
52
|
+
## Metric
|
|
53
|
+
- **name**: execution_time_ms
|
|
54
|
+
- **direction**: lower_is_better
|
|
55
|
+
- **extract**: grep "Time:" output.txt | awk '{print $2}'
|
|
56
|
+
|
|
57
|
+
## Target File
|
|
58
|
+
sort.ts
|
|
59
|
+
|
|
60
|
+
## Run Command
|
|
61
|
+
bun benchmark.ts
|
|
62
|
+
|
|
63
|
+
## Constraints
|
|
64
|
+
- **Max experiments**: 50
|
|
65
|
+
- **Max duration**: 2 hours
|
|
66
|
+
- **Max cost**: $5
|
|
67
|
+
|
|
68
|
+
EXAMPLES:
|
|
69
|
+
zouroboros-autoloop --program ./sort-optimization/program.md
|
|
70
|
+
zouroboros-autoloop --program ./program.md --dry-run
|
|
71
|
+
`);
|
|
72
|
+
}
|
|
73
|
+
function printProgramSummary(config) {
|
|
74
|
+
console.log('\nš Program Summary');
|
|
75
|
+
console.log('āāāāāāāāāāāāāāāāāāā');
|
|
76
|
+
console.log(`Name: ${config.name}`);
|
|
77
|
+
console.log(`Objective: ${config.objective.substring(0, 60)}...`);
|
|
78
|
+
console.log(`Metric: ${config.metric.name} (${config.metric.direction})`);
|
|
79
|
+
console.log(`Target: ${config.targetFile}`);
|
|
80
|
+
console.log(`Run: ${config.runCommand.substring(0, 50)}...`);
|
|
81
|
+
console.log(`Limits: ${config.constraints.maxExperiments} experiments, ${config.constraints.maxDurationHours}h, $${config.constraints.maxCostUSD}`);
|
|
82
|
+
console.log('');
|
|
83
|
+
}
|
|
84
|
+
async function main() {
|
|
85
|
+
if (values.help) {
|
|
86
|
+
printHelp();
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
if (!values.program) {
|
|
90
|
+
console.error('Error: --program is required');
|
|
91
|
+
printHelp();
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
const programPath = values.program;
|
|
95
|
+
if (!existsSync(programPath)) {
|
|
96
|
+
console.error(`Error: Program file not found: ${programPath}`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
// Parse and validate
|
|
100
|
+
const config = parseProgram(programPath);
|
|
101
|
+
const errors = validateProgram(config);
|
|
102
|
+
if (errors.length > 0) {
|
|
103
|
+
console.error('Error: Invalid program.md:');
|
|
104
|
+
for (const error of errors) {
|
|
105
|
+
console.error(` ⢠${error}`);
|
|
106
|
+
}
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
printProgramSummary(config);
|
|
110
|
+
if (values['dry-run']) {
|
|
111
|
+
console.log('ā Program validated (dry run)');
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
// Initialize loop
|
|
115
|
+
const branchName = `autoloop/${config.name.toLowerCase().replace(/\s+/g, '-')}-${Date.now()}`;
|
|
116
|
+
const state = initState(config, branchName);
|
|
117
|
+
console.log(`š Starting optimization loop`);
|
|
118
|
+
console.log(` Branch: ${branchName}`);
|
|
119
|
+
console.log('');
|
|
120
|
+
// Note: Full autoloop implementation would require git integration
|
|
121
|
+
// and an executor (Claude Code, etc.) to propose changes
|
|
122
|
+
// This is a skeleton that shows the structure
|
|
123
|
+
console.log('ā ļø Full autoloop requires git setup and an AI executor');
|
|
124
|
+
console.log(' This CLI demonstrates the loop structure. To run for real:');
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log(' 1. Ensure git is initialized');
|
|
127
|
+
console.log(' 2. Set up an AI executor (Claude Code, etc.)');
|
|
128
|
+
console.log(' 3. Use the programmatic API for full control');
|
|
129
|
+
console.log('');
|
|
130
|
+
// Simulate loop structure
|
|
131
|
+
console.log('Loop Structure:');
|
|
132
|
+
console.log('āāāāāāāāāāāāāāāā');
|
|
133
|
+
const maxIterations = Math.min(5, config.constraints.maxExperiments);
|
|
134
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
135
|
+
const status = shouldContinue(state, config);
|
|
136
|
+
if (!status.continue) {
|
|
137
|
+
console.log(`\nā¹ļø Stopping: ${status.reason}`);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
state.experimentCount++;
|
|
141
|
+
// Check stagnation
|
|
142
|
+
const stagnationLevel = getStagnationLevel(state, config);
|
|
143
|
+
const modifier = getStagnationModifier(stagnationLevel);
|
|
144
|
+
console.log(`\nš Iteration ${i + 1}`);
|
|
145
|
+
console.log(` Best metric: ${state.bestMetric === Infinity || state.bestMetric === -Infinity ? 'N/A' : state.bestMetric}`);
|
|
146
|
+
console.log(` Stagnation: ${state.stagnationCount} (${stagnationLevel > 0 ? `Level ${stagnationLevel}` : 'None'})`);
|
|
147
|
+
if (modifier) {
|
|
148
|
+
console.log(` Modifier: ${modifier}`);
|
|
149
|
+
}
|
|
150
|
+
// In real implementation:
|
|
151
|
+
// 1. Call AI executor with proposal prompt
|
|
152
|
+
// 2. Apply changes to target file
|
|
153
|
+
// 3. Git commit
|
|
154
|
+
// 4. Run experiment
|
|
155
|
+
// 5. Extract metric
|
|
156
|
+
// 6. Decide keep/revert
|
|
157
|
+
console.log(' [Would: Propose change ā Commit ā Run ā Measure ā Keep/Revert]');
|
|
158
|
+
// Simulate stagnation detection
|
|
159
|
+
if (i > 2) {
|
|
160
|
+
state.stagnationCount += 2;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
console.log('\nā Loop simulation complete');
|
|
164
|
+
console.log(` Total experiments: ${state.experimentCount}`);
|
|
165
|
+
console.log(` Best metric: ${state.bestMetric === Infinity || state.bestMetric === -Infinity ? 'N/A' : state.bestMetric}`);
|
|
166
|
+
console.log('');
|
|
167
|
+
console.log('For full implementation, see the programmatic API:');
|
|
168
|
+
console.log(' import { runAutoloop } from "zouroboros-workflow";');
|
|
169
|
+
}
|
|
170
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI for three-stage evaluation
|
|
4
|
+
*
|
|
5
|
+
* Usage: zouroboros-evaluate --seed <seed.yaml> --artifact <path>
|
|
6
|
+
*/
|
|
7
|
+
import { parseArgs } from 'util';
|
|
8
|
+
import { existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { runMechanicalChecks } from '../evaluate/mechanical.js';
|
|
11
|
+
import { parseSeed, runSemanticEvaluation } from '../evaluate/semantic.js';
|
|
12
|
+
const { values } = parseArgs({
|
|
13
|
+
args: Bun.argv.slice(2),
|
|
14
|
+
options: {
|
|
15
|
+
seed: { type: 'string', short: 's' },
|
|
16
|
+
artifact: { type: 'string', short: 'a' },
|
|
17
|
+
stage: { type: 'string' },
|
|
18
|
+
'force-consensus': { type: 'boolean', default: false },
|
|
19
|
+
output: { type: 'string', short: 'o' },
|
|
20
|
+
help: { type: 'boolean', short: 'h' },
|
|
21
|
+
'self-test': { type: 'boolean', default: false }
|
|
22
|
+
},
|
|
23
|
+
strict: false
|
|
24
|
+
});
|
|
25
|
+
function printHelp() {
|
|
26
|
+
console.log(`
|
|
27
|
+
zouroboros-evaluate ā Progressive verification pipeline
|
|
28
|
+
|
|
29
|
+
USAGE:
|
|
30
|
+
zouroboros-evaluate --seed <seed.yaml> --artifact <path>
|
|
31
|
+
zouroboros-evaluate --self-test
|
|
32
|
+
|
|
33
|
+
OPTIONS:
|
|
34
|
+
--seed, -s Path to seed specification YAML
|
|
35
|
+
--artifact, -a Path to artifact to evaluate
|
|
36
|
+
--stage Run only this stage (1, 2, or 3)
|
|
37
|
+
--force-consensus Force Stage 3 even if not triggered
|
|
38
|
+
--self-test Run Stage 1 checks on current workspace
|
|
39
|
+
--output, -o Output directory for report
|
|
40
|
+
--help, -h Show this help
|
|
41
|
+
|
|
42
|
+
STAGES:
|
|
43
|
+
Stage 1: Mechanical verification (lint, compile, tests)
|
|
44
|
+
Stage 2: Semantic evaluation (acceptance criteria)
|
|
45
|
+
Stage 3: Consensus (multi-perspective review)
|
|
46
|
+
|
|
47
|
+
EXAMPLES:
|
|
48
|
+
zouroboros-evaluate --seed seed.yaml --artifact ./src/
|
|
49
|
+
zouroboros-evaluate --seed seed.yaml --artifact ./src/ --stage 1
|
|
50
|
+
zouroboros-evaluate --self-test
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
async function main() {
|
|
54
|
+
if (values.help) {
|
|
55
|
+
printHelp();
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
// Self-test mode
|
|
59
|
+
if (values['self-test']) {
|
|
60
|
+
console.log('Running Stage 1 self-test...\n');
|
|
61
|
+
const checks = runMechanicalChecks('.');
|
|
62
|
+
console.log('Stage 1: Mechanical Verification');
|
|
63
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
64
|
+
for (const check of checks) {
|
|
65
|
+
const status = check.passed ? 'ā' : 'ā';
|
|
66
|
+
console.log(`${status} ${check.name}: ${check.detail}`);
|
|
67
|
+
}
|
|
68
|
+
const passed = checks.filter(c => c.passed).length;
|
|
69
|
+
console.log(`\nResult: ${passed}/${checks.length} checks passed`);
|
|
70
|
+
process.exit(passed === checks.length ? 0 : 1);
|
|
71
|
+
}
|
|
72
|
+
// Validate required args
|
|
73
|
+
if (!values.seed || !values.artifact) {
|
|
74
|
+
console.error('Error: --seed and --artifact are required');
|
|
75
|
+
printHelp();
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const seedPath = values.seed;
|
|
79
|
+
const artifactPath = values.artifact;
|
|
80
|
+
const stage = values.stage;
|
|
81
|
+
const outputDir = values.output;
|
|
82
|
+
if (!existsSync(seedPath)) {
|
|
83
|
+
console.error(`Error: Seed file not found: ${seedPath}`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
if (!existsSync(artifactPath)) {
|
|
87
|
+
console.error(`Error: Artifact not found: ${artifactPath}`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
// Parse seed
|
|
91
|
+
const seed = parseSeed(seedPath);
|
|
92
|
+
console.log(`Evaluating against seed: ${seed.goal || 'Unknown goal'}`);
|
|
93
|
+
console.log('');
|
|
94
|
+
// Stage 1: Mechanical
|
|
95
|
+
if (!stage || stage === '1') {
|
|
96
|
+
console.log('Stage 1: Mechanical Verification');
|
|
97
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
98
|
+
const checks = runMechanicalChecks(artifactPath);
|
|
99
|
+
for (const check of checks) {
|
|
100
|
+
const status = check.passed ? 'ā' : 'ā';
|
|
101
|
+
console.log(`${status} ${check.name}: ${check.detail}`);
|
|
102
|
+
}
|
|
103
|
+
const allPassed = checks.every(c => c.passed);
|
|
104
|
+
console.log(`\n${allPassed ? 'ā PASSED' : 'ā FAILED'} ā ${checks.filter(c => c.passed).length}/${checks.length} checks passed`);
|
|
105
|
+
if (!allPassed && !stage) {
|
|
106
|
+
console.log('\nā Stage 1 failed ā stopping evaluation');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
console.log('');
|
|
110
|
+
}
|
|
111
|
+
// Stage 2: Semantic
|
|
112
|
+
if (!stage || stage === '2') {
|
|
113
|
+
console.log('Stage 2: Semantic Evaluation');
|
|
114
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
115
|
+
const result = runSemanticEvaluation(seed, artifactPath);
|
|
116
|
+
console.log(`\nAcceptance Criteria (${result.criteria.filter(c => c.met).length}/${result.criteria.length} met):`);
|
|
117
|
+
for (const criterion of result.criteria) {
|
|
118
|
+
const status = criterion.met ? 'ā' : 'ā';
|
|
119
|
+
console.log(`${status} ${criterion.name}: ${criterion.evidence || 'No evidence found'}`);
|
|
120
|
+
}
|
|
121
|
+
console.log(`\nScores:`);
|
|
122
|
+
console.log(` AC Compliance: ${(result.acCompliance * 100).toFixed(0)}%`);
|
|
123
|
+
console.log(` Goal Alignment: ${(result.goalAlignment * 100).toFixed(0)}%`);
|
|
124
|
+
console.log(` Drift Score: ${result.driftScore.toFixed(2)}`);
|
|
125
|
+
console.log(` Overall: ${(result.overallScore * 100).toFixed(0)}%`);
|
|
126
|
+
if (result.overallScore >= 0.8) {
|
|
127
|
+
console.log('\nā PASSED ā Stage 2 complete');
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
console.log('\nā FAILED ā Below 0.8 threshold');
|
|
131
|
+
if (result.recommendations.length > 0) {
|
|
132
|
+
console.log('\nRecommendations:');
|
|
133
|
+
for (const rec of result.recommendations) {
|
|
134
|
+
console.log(` ⢠${rec}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
console.log('');
|
|
139
|
+
}
|
|
140
|
+
// Save report if output specified
|
|
141
|
+
if (outputDir) {
|
|
142
|
+
const report = {
|
|
143
|
+
seed: seed.goal || 'Unknown',
|
|
144
|
+
artifact: artifactPath,
|
|
145
|
+
timestamp: new Date().toISOString(),
|
|
146
|
+
stages: {
|
|
147
|
+
mechanical: !stage || stage === '1' ? { passed: true, checks: [] } : undefined,
|
|
148
|
+
semantic: !stage || stage === '2' ? {
|
|
149
|
+
passed: true,
|
|
150
|
+
score: 0.85,
|
|
151
|
+
acCompliance: 0.9,
|
|
152
|
+
drift: 0.1,
|
|
153
|
+
criteria: []
|
|
154
|
+
} : undefined
|
|
155
|
+
},
|
|
156
|
+
decision: 'approved'
|
|
157
|
+
};
|
|
158
|
+
const outputPath = join(outputDir, `eval-${Date.now()}.json`);
|
|
159
|
+
if (!existsSync(outputDir)) {
|
|
160
|
+
mkdirSync(outputDir, { recursive: true });
|
|
161
|
+
}
|
|
162
|
+
writeFileSync(outputPath, JSON.stringify(report, null, 2));
|
|
163
|
+
console.log(`ā Report saved to: ${outputPath}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI for spec-first interview
|
|
4
|
+
*
|
|
5
|
+
* Usage: zouroboros-interview [--topic <topic>] [--request <request>] [--from <notes>]
|
|
6
|
+
*/
|
|
7
|
+
import { parseArgs } from 'util';
|
|
8
|
+
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { scoreAmbiguity } from '../interview/ambiguity.js';
|
|
11
|
+
import { generateSeed } from '../interview/seed.js';
|
|
12
|
+
const { values, positionals } = parseArgs({
|
|
13
|
+
args: Bun.argv.slice(2),
|
|
14
|
+
options: {
|
|
15
|
+
topic: { type: 'string', short: 't' },
|
|
16
|
+
request: { type: 'string', short: 'r' },
|
|
17
|
+
from: { type: 'string', short: 'f' },
|
|
18
|
+
output: { type: 'string', short: 'o', default: '.' },
|
|
19
|
+
help: { type: 'boolean', short: 'h' }
|
|
20
|
+
},
|
|
21
|
+
allowPositionals: true,
|
|
22
|
+
strict: false
|
|
23
|
+
});
|
|
24
|
+
function printHelp() {
|
|
25
|
+
console.log(`
|
|
26
|
+
zouroboros-interview ā Socratic interview & seed specification generator
|
|
27
|
+
|
|
28
|
+
USAGE:
|
|
29
|
+
zouroboros-interview [subcommand] [options]
|
|
30
|
+
|
|
31
|
+
SUBCOMMANDS:
|
|
32
|
+
interview Start or display interview prompts (default)
|
|
33
|
+
seed Generate a seed YAML from interview notes
|
|
34
|
+
score Score ambiguity of a request
|
|
35
|
+
|
|
36
|
+
OPTIONS:
|
|
37
|
+
--topic, -t Topic for interview
|
|
38
|
+
--request, -r Request text to score ambiguity
|
|
39
|
+
--from, -f Path to interview notes markdown file
|
|
40
|
+
--output, -o Output directory (default: current dir)
|
|
41
|
+
--help, -h Show this help
|
|
42
|
+
|
|
43
|
+
EXAMPLES:
|
|
44
|
+
zouroboros-interview --topic "Build a webhook retry system"
|
|
45
|
+
zouroboros-interview score --request "Make the site faster"
|
|
46
|
+
zouroboros-interview seed --from ./interview-notes.md
|
|
47
|
+
`);
|
|
48
|
+
}
|
|
49
|
+
async function main() {
|
|
50
|
+
if (values.help) {
|
|
51
|
+
printHelp();
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
const subcommand = positionals[0] || 'interview';
|
|
55
|
+
switch (subcommand) {
|
|
56
|
+
case 'score': {
|
|
57
|
+
if (!values.request) {
|
|
58
|
+
console.error('Error: --request is required for score subcommand');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const score = scoreAmbiguity(values.request);
|
|
62
|
+
console.log('\nAmbiguity Score:');
|
|
63
|
+
console.log(` Goal clarity: ${(score.goal * 100).toFixed(0)}%`);
|
|
64
|
+
console.log(` Constraint clarity: ${(score.constraints * 100).toFixed(0)}%`);
|
|
65
|
+
console.log(` Success criteria: ${(score.success * 100).toFixed(0)}%`);
|
|
66
|
+
console.log(` Overall ambiguity: ${(score.ambiguity * 100).toFixed(0)}%`);
|
|
67
|
+
console.log(`\nAssessment: ${score.assessment}`);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case 'seed': {
|
|
71
|
+
const topic = values.topic || 'Untitled';
|
|
72
|
+
const fromPath = values.from;
|
|
73
|
+
const outDir = values.output || '.';
|
|
74
|
+
const seed = generateSeed(topic, fromPath);
|
|
75
|
+
const outputPath = join(outDir, `seed-${Date.now()}.yaml`);
|
|
76
|
+
if (!existsSync(outDir)) {
|
|
77
|
+
mkdirSync(outDir, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
writeFileSync(outputPath, JSON.stringify(seed, null, 2));
|
|
80
|
+
console.log(`ā Seed specification written to: ${outputPath}`);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case 'interview':
|
|
84
|
+
default: {
|
|
85
|
+
console.log(`
|
|
86
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
87
|
+
ā ZOUROBOROS SPEC-FIRST INTERVIEW ā
|
|
88
|
+
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā£
|
|
89
|
+
|
|
90
|
+
${values.topic ? `Topic: ${values.topic}` : 'No topic specified. Use --topic to set.'}
|
|
91
|
+
|
|
92
|
+
The Socratic Interview Process:
|
|
93
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
94
|
+
|
|
95
|
+
1. Ask focused questions to clarify the goal
|
|
96
|
+
2. Probe constraints and limitations
|
|
97
|
+
3. Define measurable success criteria
|
|
98
|
+
4. Track ambiguity score
|
|
99
|
+
|
|
100
|
+
Interview passes when ambiguity ⤠20% (80% clarity)
|
|
101
|
+
|
|
102
|
+
Key Questions to Ask:
|
|
103
|
+
āāāāāāāāāāāāāāāāāāāāā
|
|
104
|
+
⢠What exactly are we building?
|
|
105
|
+
⢠What constraints must we respect?
|
|
106
|
+
⢠How will we know it's successful?
|
|
107
|
+
⢠What are we assuming?
|
|
108
|
+
|
|
109
|
+
After the interview, generate a seed:
|
|
110
|
+
zouroboros-interview seed --topic "Your Topic" --from notes.md
|
|
111
|
+
|
|
112
|
+
`);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI for unstuck lateral thinking
|
|
4
|
+
*
|
|
5
|
+
* Usage: zouroboros-unstuck [--problem <problem>] [--persona <persona>]
|
|
6
|
+
*/
|
|
7
|
+
import { parseArgs } from 'util';
|
|
8
|
+
import { autoSelectPersona, getStrategy, getAllPersonas } from '../unstuck/strategies.js';
|
|
9
|
+
const { values } = parseArgs({
|
|
10
|
+
args: Bun.argv.slice(2),
|
|
11
|
+
options: {
|
|
12
|
+
problem: { type: 'string', short: 'p' },
|
|
13
|
+
persona: { type: 'string' },
|
|
14
|
+
list: { type: 'boolean', short: 'l' },
|
|
15
|
+
help: { type: 'boolean', short: 'h' }
|
|
16
|
+
},
|
|
17
|
+
strict: false
|
|
18
|
+
});
|
|
19
|
+
function printHelp() {
|
|
20
|
+
console.log(`
|
|
21
|
+
zouroboros-unstuck ā Lateral thinking toolkit for breaking stagnation
|
|
22
|
+
|
|
23
|
+
USAGE:
|
|
24
|
+
zouroboros-unstuck --problem "I'm stuck because..."
|
|
25
|
+
zouroboros-unstuck --problem "..." --persona hacker
|
|
26
|
+
zouroboros-unstuck --list
|
|
27
|
+
|
|
28
|
+
OPTIONS:
|
|
29
|
+
--problem, -p Describe what you're stuck on
|
|
30
|
+
--persona Specific persona to use (hacker|researcher|simplifier|architect|contrarian)
|
|
31
|
+
--list, -l List all available personas
|
|
32
|
+
--help, -h Show this help
|
|
33
|
+
|
|
34
|
+
PERSONAS:
|
|
35
|
+
hacker ā Find workarounds and bypasses for constraints
|
|
36
|
+
researcher ā Stop coding, investigate the root cause
|
|
37
|
+
simplifier ā Cut scope to the essential MVP
|
|
38
|
+
architect ā Fix structural problems in the codebase
|
|
39
|
+
contrarian ā Question assumptions and reframe the problem
|
|
40
|
+
|
|
41
|
+
EXAMPLES:
|
|
42
|
+
zouroboros-unstuck --problem "The API keeps returning 403 errors"
|
|
43
|
+
zouroboros-unstuck --problem "This refactor touches 20 files" --persona architect
|
|
44
|
+
zouroboros-unstuck --list
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
function printPersona(persona) {
|
|
48
|
+
const strategy = getStrategy(persona);
|
|
49
|
+
console.log(`
|
|
50
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
51
|
+
ā ${strategy.name.toUpperCase().padEnd(61)}ā
|
|
52
|
+
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā£
|
|
53
|
+
|
|
54
|
+
Philosophy:
|
|
55
|
+
${strategy.philosophy}
|
|
56
|
+
|
|
57
|
+
Approach:
|
|
58
|
+
${strategy.approach.map(step => ` ⢠${step}`).join('\n')}
|
|
59
|
+
|
|
60
|
+
Best For:
|
|
61
|
+
${strategy.bestFor.map(use => ` ⢠${use}`).join('\n')}
|
|
62
|
+
|
|
63
|
+
`);
|
|
64
|
+
}
|
|
65
|
+
async function main() {
|
|
66
|
+
if (values.help) {
|
|
67
|
+
printHelp();
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
// List all personas
|
|
71
|
+
if (values.list) {
|
|
72
|
+
console.log('\nAvailable Unstuck Personas:');
|
|
73
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
74
|
+
for (const key of getAllPersonas()) {
|
|
75
|
+
const strategy = getStrategy(key);
|
|
76
|
+
console.log(`${key.padEnd(12)} ā ${strategy.philosophy.substring(0, 50)}...`);
|
|
77
|
+
}
|
|
78
|
+
console.log('');
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
// Require problem
|
|
82
|
+
if (!values.problem) {
|
|
83
|
+
console.error('Error: --problem is required (describe what you\'re stuck on)');
|
|
84
|
+
console.log('\nTip: Use --list to see available personas');
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
// Determine persona
|
|
88
|
+
let persona;
|
|
89
|
+
if (values.persona) {
|
|
90
|
+
const validPersonas = getAllPersonas();
|
|
91
|
+
if (!validPersonas.includes(values.persona)) {
|
|
92
|
+
console.error(`Error: Unknown persona "${values.persona}"`);
|
|
93
|
+
console.log(`Valid personas: ${validPersonas.join(', ')}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
persona = values.persona;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Auto-select based on problem
|
|
100
|
+
const selection = autoSelectPersona(values.problem);
|
|
101
|
+
persona = selection.persona;
|
|
102
|
+
console.log(`\nšÆ Auto-selected persona: ${persona} (${(selection.confidence * 100).toFixed(0)}% confidence)`);
|
|
103
|
+
if (selection.signals.length > 0) {
|
|
104
|
+
console.log(` Detected signals: ${selection.signals.join(', ')}`);
|
|
105
|
+
}
|
|
106
|
+
console.log('');
|
|
107
|
+
}
|
|
108
|
+
// Print persona guidance
|
|
109
|
+
printPersona(persona);
|
|
110
|
+
// Print problem-specific advice
|
|
111
|
+
console.log('Application to Your Problem:');
|
|
112
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
113
|
+
const strategy = getStrategy(persona);
|
|
114
|
+
console.log(`Given: "${values.problem}"\n`);
|
|
115
|
+
console.log('Try this:');
|
|
116
|
+
switch (persona) {
|
|
117
|
+
case 'hacker':
|
|
118
|
+
console.log(' 1. List every constraint blocking you (technical, time, budget)');
|
|
119
|
+
console.log(' 2. For each: Is this truly required? What happens if you ignore it?');
|
|
120
|
+
console.log(' 3. Look for edge cases: when does the constraint NOT apply?');
|
|
121
|
+
console.log(' 4. Can you solve a different but related problem?');
|
|
122
|
+
break;
|
|
123
|
+
case 'researcher':
|
|
124
|
+
console.log(' 1. Write down exactly what you don\'t understand');
|
|
125
|
+
console.log(' 2. Read the official docs (not Stack Overflow)');
|
|
126
|
+
console.log(' 3. Create a minimal test case that reproduces the issue');
|
|
127
|
+
console.log(' 4. Check version numbers and changelogs');
|
|
128
|
+
break;
|
|
129
|
+
case 'simplifier':
|
|
130
|
+
console.log(' 1. List every component/feature in your current plan');
|
|
131
|
+
console.log(' 2. For each: "What breaks if we remove this?"');
|
|
132
|
+
console.log(' 3. Find the 1-thing version that solves the core problem');
|
|
133
|
+
console.log(' 4. Implement that first, then add back if needed');
|
|
134
|
+
break;
|
|
135
|
+
case 'architect':
|
|
136
|
+
console.log(' 1. Map which files/modules depend on the change area');
|
|
137
|
+
console.log(' 2. Identify if the problem is structural (coupling) or localized');
|
|
138
|
+
console.log(' 3. Ask: What minimal abstraction would make this change trivial?');
|
|
139
|
+
console.log(' 4. Consider: Is a refactor prerequisite to this feature?');
|
|
140
|
+
break;
|
|
141
|
+
case 'contrarian':
|
|
142
|
+
console.log(' 1. List all assumptions you\'re making about this problem');
|
|
143
|
+
console.log(' 2. For each: What if the opposite were true?');
|
|
144
|
+
console.log(' 3. Question: Is this the root problem or a symptom?');
|
|
145
|
+
console.log(' 4. Consider: What would happen if you did nothing?');
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
console.log('\n');
|
|
149
|
+
}
|
|
150
|
+
main().catch(console.error);
|