self-evolve-framework 1.5.0 → 1.6.1
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/README.md +33 -23
- package/bin/cli.js +24 -67
- package/package.json +1 -1
- package/template/rules/knowledge/README.md +56 -0
- package/template/rules/knowledge/ant-design-vue.mdc +54 -0
- package/template/rules/knowledge/general.mdc +56 -0
- package/template/rules/knowledge/rust.mdc +55 -0
- package/template/rules/knowledge/shadcn-svelte.mdc +64 -0
- package/template/rules/knowledge/svelte.mdc +135 -0
- package/template/rules/knowledge/tailwind.mdc +66 -0
- package/template/rules/knowledge/tauri.mdc +65 -0
- package/template/rules/knowledge/typescript.mdc +41 -0
- package/template/rules/knowledge/vue.mdc +48 -0
- package/template/rules/ponytail.mdc +23 -98
- package/template/rules/self-evolve.mdc +24 -0
- package/template/skills/impeccable/SKILL.md +47 -174
- package/template/skills/skillopt-sleep/SKILL.md +34 -48
- package/template/skills/impeccable/agents/impeccable_asset_producer.toml +0 -92
- package/template/skills/impeccable/agents/impeccable_manual_edit_applier.toml +0 -95
- package/template/skills/impeccable/agents/openai.yaml +0 -4
- package/template/skills/impeccable/reference/adapt.md +0 -311
- package/template/skills/impeccable/reference/animate.md +0 -201
- package/template/skills/impeccable/reference/audit.md +0 -133
- package/template/skills/impeccable/reference/bolder.md +0 -113
- package/template/skills/impeccable/reference/brand.md +0 -108
- package/template/skills/impeccable/reference/clarify.md +0 -288
- package/template/skills/impeccable/reference/codex.md +0 -105
- package/template/skills/impeccable/reference/colorize.md +0 -257
- package/template/skills/impeccable/reference/craft.md +0 -123
- package/template/skills/impeccable/reference/critique.md +0 -790
- package/template/skills/impeccable/reference/delight.md +0 -302
- package/template/skills/impeccable/reference/distill.md +0 -111
- package/template/skills/impeccable/reference/document.md +0 -429
- package/template/skills/impeccable/reference/extract.md +0 -69
- package/template/skills/impeccable/reference/harden.md +0 -347
- package/template/skills/impeccable/reference/hooks.md +0 -90
- package/template/skills/impeccable/reference/init.md +0 -172
- package/template/skills/impeccable/reference/interaction-design.md +0 -189
- package/template/skills/impeccable/reference/layout.md +0 -161
- package/template/skills/impeccable/reference/live.md +0 -720
- package/template/skills/impeccable/reference/onboard.md +0 -234
- package/template/skills/impeccable/reference/optimize.md +0 -258
- package/template/skills/impeccable/reference/overdrive.md +0 -130
- package/template/skills/impeccable/reference/polish.md +0 -241
- package/template/skills/impeccable/reference/product.md +0 -60
- package/template/skills/impeccable/reference/quieter.md +0 -99
- package/template/skills/impeccable/reference/shape.md +0 -165
- package/template/skills/impeccable/reference/typeset.md +0 -279
- package/template/skills/impeccable/scripts/command-metadata.json +0 -94
- package/template/skills/impeccable/scripts/context-signals.mjs +0 -225
- package/template/skills/impeccable/scripts/context.mjs +0 -961
- package/template/skills/impeccable/scripts/critique-storage.mjs +0 -242
- package/template/skills/impeccable/scripts/detect-csp.mjs +0 -198
- package/template/skills/impeccable/scripts/detect.mjs +0 -21
- package/template/skills/impeccable/scripts/detector/browser/injected/index.mjs +0 -1937
- package/template/skills/impeccable/scripts/detector/cli/main.mjs +0 -290
- package/template/skills/impeccable/scripts/detector/design-system.mjs +0 -750
- package/template/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +0 -5138
- package/template/skills/impeccable/scripts/detector/detect-antipatterns.mjs +0 -50
- package/template/skills/impeccable/scripts/detector/engines/browser/detect-url.mjs +0 -277
- package/template/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +0 -568
- package/template/skills/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +0 -1015
- package/template/skills/impeccable/scripts/detector/engines/static-html/detect-html.mjs +0 -234
- package/template/skills/impeccable/scripts/detector/engines/visual/screenshot-contrast.mjs +0 -189
- package/template/skills/impeccable/scripts/detector/findings.mjs +0 -12
- package/template/skills/impeccable/scripts/detector/node/file-system.mjs +0 -198
- package/template/skills/impeccable/scripts/detector/profile/profiler.mjs +0 -166
- package/template/skills/impeccable/scripts/detector/registry/antipatterns.mjs +0 -448
- package/template/skills/impeccable/scripts/detector/rules/checks.mjs +0 -2671
- package/template/skills/impeccable/scripts/detector/shared/color.mjs +0 -124
- package/template/skills/impeccable/scripts/detector/shared/constants.mjs +0 -101
- package/template/skills/impeccable/scripts/detector/shared/inline-ignores.mjs +0 -148
- package/template/skills/impeccable/scripts/detector/shared/page.mjs +0 -7
- package/template/skills/impeccable/scripts/hook-admin.mjs +0 -661
- package/template/skills/impeccable/scripts/hook-before-edit.mjs +0 -476
- package/template/skills/impeccable/scripts/hook-lib.mjs +0 -1632
- package/template/skills/impeccable/scripts/hook.mjs +0 -61
- package/template/skills/impeccable/scripts/lib/design-parser.mjs +0 -842
- package/template/skills/impeccable/scripts/lib/impeccable-config.mjs +0 -638
- package/template/skills/impeccable/scripts/lib/impeccable-paths.mjs +0 -128
- package/template/skills/impeccable/scripts/lib/is-generated.mjs +0 -69
- package/template/skills/impeccable/scripts/lib/target-args.mjs +0 -42
- package/template/skills/impeccable/scripts/live/browser-script-parts.mjs +0 -49
- package/template/skills/impeccable/scripts/live/completion.mjs +0 -19
- package/template/skills/impeccable/scripts/live/event-validation.mjs +0 -137
- package/template/skills/impeccable/scripts/live/insert-ui.mjs +0 -458
- package/template/skills/impeccable/scripts/live/manual-apply.mjs +0 -939
- package/template/skills/impeccable/scripts/live/manual-edit-routes.mjs +0 -357
- package/template/skills/impeccable/scripts/live/manual-edits-buffer.mjs +0 -152
- package/template/skills/impeccable/scripts/live/session-store.mjs +0 -289
- package/template/skills/impeccable/scripts/live/svelte-component.mjs +0 -826
- package/template/skills/impeccable/scripts/live/sveltekit-adapter.mjs +0 -274
- package/template/skills/impeccable/scripts/live/ui-core.mjs +0 -180
- package/template/skills/impeccable/scripts/live/vocabulary.mjs +0 -36
- package/template/skills/impeccable/scripts/live-accept.mjs +0 -812
- package/template/skills/impeccable/scripts/live-browser-dom.js +0 -146
- package/template/skills/impeccable/scripts/live-browser-session.js +0 -123
- package/template/skills/impeccable/scripts/live-browser.js +0 -11173
- package/template/skills/impeccable/scripts/live-commit-manual-edits.mjs +0 -1241
- package/template/skills/impeccable/scripts/live-complete.mjs +0 -75
- package/template/skills/impeccable/scripts/live-copy-edit-agent.mjs +0 -683
- package/template/skills/impeccable/scripts/live-discard-manual-edits.mjs +0 -51
- package/template/skills/impeccable/scripts/live-inject.mjs +0 -583
- package/template/skills/impeccable/scripts/live-insert.mjs +0 -272
- package/template/skills/impeccable/scripts/live-manual-edit-evidence.mjs +0 -363
- package/template/skills/impeccable/scripts/live-poll.mjs +0 -384
- package/template/skills/impeccable/scripts/live-resume.mjs +0 -94
- package/template/skills/impeccable/scripts/live-server.mjs +0 -1135
- package/template/skills/impeccable/scripts/live-status.mjs +0 -61
- package/template/skills/impeccable/scripts/live-target.mjs +0 -30
- package/template/skills/impeccable/scripts/live-wrap.mjs +0 -894
- package/template/skills/impeccable/scripts/live.mjs +0 -297
- package/template/skills/impeccable/scripts/modern-screenshot.umd.js +0 -14
- package/template/skills/impeccable/scripts/palette.mjs +0 -633
- package/template/skills/impeccable/scripts/pin.mjs +0 -214
- package/template/skills/skillopt-sleep/scripts/python/__init__.py +0 -20
- package/template/skills/skillopt-sleep/scripts/python/__main__.py +0 -343
- package/template/skills/skillopt-sleep/scripts/python/backend.py +0 -1371
- package/template/skills/skillopt-sleep/scripts/python/budget.py +0 -75
- package/template/skills/skillopt-sleep/scripts/python/config.py +0 -162
- package/template/skills/skillopt-sleep/scripts/python/consolidate.py +0 -238
- package/template/skills/skillopt-sleep/scripts/python/cycle.py +0 -291
- package/template/skills/skillopt-sleep/scripts/python/dream.py +0 -138
- package/template/skills/skillopt-sleep/scripts/python/experiments/__init__.py +0 -1
- package/template/skills/skillopt-sleep/scripts/python/experiments/gbrain_bench.py +0 -119
- package/template/skills/skillopt-sleep/scripts/python/experiments/personas.py +0 -86
- package/template/skills/skillopt-sleep/scripts/python/experiments/report.py +0 -132
- package/template/skills/skillopt-sleep/scripts/python/experiments/run_experiment.py +0 -178
- package/template/skills/skillopt-sleep/scripts/python/experiments/run_gbrain.py +0 -209
- package/template/skills/skillopt-sleep/scripts/python/experiments/run_transfer.py +0 -155
- package/template/skills/skillopt-sleep/scripts/python/experiments/sweep.py +0 -164
- package/template/skills/skillopt-sleep/scripts/python/gate.py +0 -50
- package/template/skills/skillopt-sleep/scripts/python/harvest.py +0 -304
- package/template/skills/skillopt-sleep/scripts/python/harvest_codex.py +0 -253
- package/template/skills/skillopt-sleep/scripts/python/harvest_sources.py +0 -41
- package/template/skills/skillopt-sleep/scripts/python/judges.py +0 -84
- package/template/skills/skillopt-sleep/scripts/python/llm_miner.py +0 -134
- package/template/skills/skillopt-sleep/scripts/python/memory.py +0 -129
- package/template/skills/skillopt-sleep/scripts/python/mine.py +0 -312
- package/template/skills/skillopt-sleep/scripts/python/replay.py +0 -146
- package/template/skills/skillopt-sleep/scripts/python/rollout.py +0 -153
- package/template/skills/skillopt-sleep/scripts/python/scheduler.py +0 -138
- package/template/skills/skillopt-sleep/scripts/python/slow_update.py +0 -142
- package/template/skills/skillopt-sleep/scripts/python/staging.py +0 -103
- package/template/skills/skillopt-sleep/scripts/python/state.py +0 -96
- package/template/skills/skillopt-sleep/scripts/python/tasks_file.py +0 -81
- package/template/skills/skillopt-sleep/scripts/python/types.py +0 -146
- package/template/skills/skillopt-sleep/scripts/shell/__init__.py +0 -0
- package/template/skills/skillopt-sleep/scripts/shell/eval_only.py +0 -466
- package/template/skills/skillopt-sleep/scripts/shell/materialize_searchqa.py +0 -148
- package/template/skills/skillopt-sleep/scripts/shell/run_alfworld.sh +0 -60
- package/template/skills/skillopt-sleep/scripts/shell/run_searchqa.sh +0 -40
- package/template/skills/skillopt-sleep/scripts/shell/run_spreadsheetbench.sh +0 -39
- package/template/skills/skillopt-sleep/scripts/shell/train.py +0 -556
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Pin/unpin sub-commands as standalone skill shortcuts.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* node <scripts_path>/pin.mjs pin <command>
|
|
7
|
-
* node <scripts_path>/pin.mjs unpin <command>
|
|
8
|
-
*
|
|
9
|
-
* `pin audit` creates a lightweight /audit skill that redirects to /impeccable audit.
|
|
10
|
-
* `unpin audit` removes that shortcut.
|
|
11
|
-
*
|
|
12
|
-
* The script discovers harness directories (.claude/skills, .cursor/skills, etc.)
|
|
13
|
-
* in the project root and creates/removes the pin in all of them.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync, readdirSync } from 'node:fs';
|
|
17
|
-
import { join, resolve, dirname } from 'node:path';
|
|
18
|
-
import { fileURLToPath } from 'node:url';
|
|
19
|
-
|
|
20
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
|
|
22
|
-
// All known harness directories
|
|
23
|
-
const HARNESS_DIRS = [
|
|
24
|
-
'.claude', '.cursor', '.gemini', '.codex', '.agents',
|
|
25
|
-
'.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
// Valid sub-command names
|
|
29
|
-
const VALID_COMMANDS = [
|
|
30
|
-
'craft', 'init', 'extract', 'document', 'shape',
|
|
31
|
-
'critique', 'audit',
|
|
32
|
-
'polish', 'bolder', 'quieter', 'distill', 'harden', 'onboard', 'live',
|
|
33
|
-
'animate', 'colorize', 'typeset', 'layout', 'delight', 'overdrive',
|
|
34
|
-
'clarify', 'adapt', 'optimize',
|
|
35
|
-
];
|
|
36
|
-
|
|
37
|
-
// Marker to identify pinned skills (so unpin doesn't delete user skills)
|
|
38
|
-
const PIN_MARKER = '<!-- impeccable-pinned-skill -->';
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Walk up from startDir to find a project root.
|
|
42
|
-
*/
|
|
43
|
-
function findProjectRoot(startDir = process.cwd()) {
|
|
44
|
-
let dir = resolve(startDir);
|
|
45
|
-
while (dir !== '/') {
|
|
46
|
-
if (
|
|
47
|
-
existsSync(join(dir, 'package.json')) ||
|
|
48
|
-
existsSync(join(dir, '.git')) ||
|
|
49
|
-
existsSync(join(dir, 'skills-lock.json'))
|
|
50
|
-
) {
|
|
51
|
-
return dir;
|
|
52
|
-
}
|
|
53
|
-
const parent = resolve(dir, '..');
|
|
54
|
-
if (parent === dir) break;
|
|
55
|
-
dir = parent;
|
|
56
|
-
}
|
|
57
|
-
return resolve(startDir);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Find harness skill directories that have an impeccable skill installed.
|
|
62
|
-
*/
|
|
63
|
-
function findHarnessDirs(projectRoot) {
|
|
64
|
-
const dirs = [];
|
|
65
|
-
for (const harness of HARNESS_DIRS) {
|
|
66
|
-
const skillsDir = join(projectRoot, harness, 'skills');
|
|
67
|
-
// Only pin in harness dirs that already have impeccable installed
|
|
68
|
-
const impeccableDir = join(skillsDir, 'impeccable');
|
|
69
|
-
if (existsSync(impeccableDir) || existsSync(join(skillsDir, 'i-impeccable'))) {
|
|
70
|
-
dirs.push(skillsDir);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return dirs;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Load command metadata (descriptions for pinned skills).
|
|
78
|
-
*/
|
|
79
|
-
function loadCommandMetadata() {
|
|
80
|
-
const metadataPath = join(__dirname, 'command-metadata.json');
|
|
81
|
-
if (existsSync(metadataPath)) {
|
|
82
|
-
return JSON.parse(readFileSync(metadataPath, 'utf-8'));
|
|
83
|
-
}
|
|
84
|
-
return {};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Generate a pinned skill's SKILL.md content.
|
|
89
|
-
*/
|
|
90
|
-
function generatePinnedSkill(command, metadata) {
|
|
91
|
-
const desc = metadata[command]?.description || `Shortcut for /impeccable ${command}.`;
|
|
92
|
-
const hint = metadata[command]?.argumentHint || '[target]';
|
|
93
|
-
|
|
94
|
-
return `---
|
|
95
|
-
name: ${command}
|
|
96
|
-
description: "${desc}"
|
|
97
|
-
argument-hint: "${hint}"
|
|
98
|
-
user-invocable: true
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
${PIN_MARKER}
|
|
102
|
-
|
|
103
|
-
This is a pinned shortcut for \`{{command_prefix}}impeccable ${command}\`.
|
|
104
|
-
|
|
105
|
-
Invoke {{command_prefix}}impeccable ${command}, passing along any arguments provided here, and follow its instructions.
|
|
106
|
-
`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Pin a command: create shortcut skill in all harness dirs.
|
|
111
|
-
*/
|
|
112
|
-
function pin(command, projectRoot) {
|
|
113
|
-
const metadata = loadCommandMetadata();
|
|
114
|
-
const harnessDirs = findHarnessDirs(projectRoot);
|
|
115
|
-
|
|
116
|
-
if (harnessDirs.length === 0) {
|
|
117
|
-
console.log('No harness directories with impeccable installed found.');
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const content = generatePinnedSkill(command, metadata);
|
|
122
|
-
let created = 0;
|
|
123
|
-
|
|
124
|
-
for (const skillsDir of harnessDirs) {
|
|
125
|
-
// Check if skill already exists (and isn't a pin)
|
|
126
|
-
const skillDir = join(skillsDir, command);
|
|
127
|
-
if (existsSync(skillDir)) {
|
|
128
|
-
const existingMd = join(skillDir, 'SKILL.md');
|
|
129
|
-
if (existsSync(existingMd)) {
|
|
130
|
-
const existing = readFileSync(existingMd, 'utf-8');
|
|
131
|
-
if (!existing.includes(PIN_MARKER)) {
|
|
132
|
-
console.log(` SKIP: ${skillDir} (non-pinned skill already exists)`);
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
mkdirSync(skillDir, { recursive: true });
|
|
139
|
-
writeFileSync(join(skillDir, 'SKILL.md'), content, 'utf-8');
|
|
140
|
-
console.log(` + ${skillDir}`);
|
|
141
|
-
created++;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (created > 0) {
|
|
145
|
-
console.log(`\nPinned '${command}' as a standalone shortcut in ${created} location(s).`);
|
|
146
|
-
console.log(`You can now use /${command} directly.`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return created > 0;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Unpin a command: remove shortcut skill from all harness dirs.
|
|
154
|
-
*/
|
|
155
|
-
function unpin(command, projectRoot) {
|
|
156
|
-
const harnessDirs = findHarnessDirs(projectRoot);
|
|
157
|
-
let removed = 0;
|
|
158
|
-
|
|
159
|
-
for (const skillsDir of harnessDirs) {
|
|
160
|
-
const skillDir = join(skillsDir, command);
|
|
161
|
-
if (!existsSync(skillDir)) continue;
|
|
162
|
-
|
|
163
|
-
const skillMd = join(skillDir, 'SKILL.md');
|
|
164
|
-
if (!existsSync(skillMd)) continue;
|
|
165
|
-
|
|
166
|
-
// Safety: only remove if it's a pinned skill
|
|
167
|
-
const content = readFileSync(skillMd, 'utf-8');
|
|
168
|
-
if (!content.includes(PIN_MARKER)) {
|
|
169
|
-
console.log(` SKIP: ${skillDir} (not a pinned skill)`);
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
rmSync(skillDir, { recursive: true, force: true });
|
|
174
|
-
console.log(` - ${skillDir}`);
|
|
175
|
-
removed++;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (removed > 0) {
|
|
179
|
-
console.log(`\nUnpinned '${command}' from ${removed} location(s).`);
|
|
180
|
-
console.log(`Use /impeccable ${command} to access it.`);
|
|
181
|
-
} else {
|
|
182
|
-
console.log(`No pinned '${command}' shortcut found.`);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return removed > 0;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// --- CLI ---
|
|
189
|
-
const [,, action, command] = process.argv;
|
|
190
|
-
|
|
191
|
-
if (!action || !command) {
|
|
192
|
-
console.log('Usage: node pin.mjs <pin|unpin> <command>');
|
|
193
|
-
console.log(`\nAvailable commands: ${VALID_COMMANDS.join(', ')}`);
|
|
194
|
-
process.exit(1);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (action !== 'pin' && action !== 'unpin') {
|
|
198
|
-
console.error(`Unknown action: ${action}. Use 'pin' or 'unpin'.`);
|
|
199
|
-
process.exit(1);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (!VALID_COMMANDS.includes(command)) {
|
|
203
|
-
console.error(`Unknown command: ${command}`);
|
|
204
|
-
console.error(`Available commands: ${VALID_COMMANDS.join(', ')}`);
|
|
205
|
-
process.exit(1);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const root = findProjectRoot();
|
|
209
|
-
|
|
210
|
-
if (action === 'pin') {
|
|
211
|
-
pin(command, root);
|
|
212
|
-
} else {
|
|
213
|
-
unpin(command, root);
|
|
214
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"""SkillOpt-Sleep — nightly offline self-evolution for a local Claude agent.
|
|
2
|
-
|
|
3
|
-
A Claude Code plugin engine that gives a user's agent a "sleep cycle":
|
|
4
|
-
harvest the day's real session transcripts, mine recurring tasks, replay
|
|
5
|
-
them offline, and consolidate short-term experience into long-term memory
|
|
6
|
-
(CLAUDE.md) and skills (SKILL.md) behind a SkillOpt validation gate.
|
|
7
|
-
|
|
8
|
-
Synthesizes three ideas:
|
|
9
|
-
* SkillOpt — validation-gated bounded text optimization (this repo)
|
|
10
|
-
* Dreams — offline memory consolidation, input never mutated
|
|
11
|
-
* Sleep — short-term experience -> long-term competence, offline
|
|
12
|
-
|
|
13
|
-
Public entry points:
|
|
14
|
-
* skillopt_sleep.cli — `python -m skillopt_sleep ...`
|
|
15
|
-
* skillopt_sleep.cycle.run_sleep_cycle(...)
|
|
16
|
-
"""
|
|
17
|
-
from __future__ import annotations
|
|
18
|
-
|
|
19
|
-
__all__ = ["__version__"]
|
|
20
|
-
__version__ = "0.1.0"
|
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
"""SkillOpt-Sleep — command-line interface.
|
|
2
|
-
|
|
3
|
-
python -m skillopt_sleep run # full cycle: harvest->mine->replay->gate->stage
|
|
4
|
-
python -m skillopt_sleep dry-run # same but report only, no staging/adopt
|
|
5
|
-
python -m skillopt_sleep status # show state + latest staged proposal
|
|
6
|
-
python -m skillopt_sleep adopt # apply the latest staged proposal (with backup)
|
|
7
|
-
python -m skillopt_sleep harvest # just print what would be mined (debug)
|
|
8
|
-
|
|
9
|
-
Common flags:
|
|
10
|
-
--project PATH project to evolve (default: cwd)
|
|
11
|
-
--scope all|invoked harvest scope (default: invoked)
|
|
12
|
-
--max-sessions N cap transcript sessions per run
|
|
13
|
-
--max-tasks N cap mined tasks per run
|
|
14
|
-
--target-skill-path PATH explicit live SKILL.md to stage/adopt
|
|
15
|
-
--tasks-file PATH reviewed TaskRecord JSON file to replay instead of harvesting
|
|
16
|
-
--backend mock|claude|codex|copilot
|
|
17
|
-
--source claude|codex|auto
|
|
18
|
-
--model NAME
|
|
19
|
-
--lookback-hours N
|
|
20
|
-
--auto-adopt
|
|
21
|
-
--json machine-readable output
|
|
22
|
-
"""
|
|
23
|
-
from __future__ import annotations
|
|
24
|
-
|
|
25
|
-
import argparse
|
|
26
|
-
import json
|
|
27
|
-
import os
|
|
28
|
-
import sys
|
|
29
|
-
from typing import Any, Dict
|
|
30
|
-
|
|
31
|
-
from skillopt_sleep.config import load_config
|
|
32
|
-
from skillopt_sleep.cycle import run_sleep_cycle
|
|
33
|
-
from skillopt_sleep.harvest_sources import harvest_for_config
|
|
34
|
-
from skillopt_sleep.mine import mine
|
|
35
|
-
from skillopt_sleep.staging import adopt as adopt_staging
|
|
36
|
-
from skillopt_sleep.staging import latest_staging
|
|
37
|
-
from skillopt_sleep.state import SleepState
|
|
38
|
-
from skillopt_sleep.tasks_file import load_tasks_file, make_tasks_payload, write_tasks_file
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _read_text(path: str) -> str:
|
|
42
|
-
try:
|
|
43
|
-
with open(path, encoding="utf-8") as f:
|
|
44
|
-
return f.read()
|
|
45
|
-
except Exception:
|
|
46
|
-
return ""
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def _report_payload(rep, outcome) -> Dict[str, Any]:
|
|
50
|
-
return {
|
|
51
|
-
"night": rep.night,
|
|
52
|
-
"accepted": rep.accepted,
|
|
53
|
-
"gate_action": rep.gate_action,
|
|
54
|
-
"no_edits_reason": getattr(rep, "no_edits_reason", ""),
|
|
55
|
-
"baseline": rep.baseline_score,
|
|
56
|
-
"candidate": rep.candidate_score,
|
|
57
|
-
"n_tasks": rep.n_tasks,
|
|
58
|
-
"n_sessions": rep.n_sessions,
|
|
59
|
-
"n_accepted_edits": len(rep.edits),
|
|
60
|
-
"n_rejected_edits": len(rep.rejected_edits),
|
|
61
|
-
"edits": [e.__dict__ for e in rep.edits],
|
|
62
|
-
"rejected_edits": [e.__dict__ for e in rep.rejected_edits],
|
|
63
|
-
"notes": rep.notes,
|
|
64
|
-
"staging_dir": outcome.staging_dir,
|
|
65
|
-
"adopted": outcome.adopted,
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def _add_common(p: argparse.ArgumentParser) -> None:
|
|
70
|
-
p.add_argument("--project", default="")
|
|
71
|
-
p.add_argument("--scope", default="", choices=["", "all", "invoked"])
|
|
72
|
-
p.add_argument("--backend", default="", choices=["", "mock", "claude", "codex", "copilot"])
|
|
73
|
-
p.add_argument("--model", default="")
|
|
74
|
-
p.add_argument("--codex-path", default="", help="path to the real @openai/codex binary")
|
|
75
|
-
p.add_argument("--claude-home", default="", help="override ~/.claude (also isolates state)")
|
|
76
|
-
p.add_argument("--codex-home", default="", help="override ~/.codex for archived session harvest")
|
|
77
|
-
p.add_argument("--source", default="", choices=["", "claude", "codex", "auto"],
|
|
78
|
-
help="session transcript source")
|
|
79
|
-
p.add_argument("--lookback-hours", type=int, default=None,
|
|
80
|
-
help="harvest window in hours; 0 = scan full history")
|
|
81
|
-
p.add_argument("--edit-budget", type=int, default=0)
|
|
82
|
-
p.add_argument("--max-sessions", type=int, default=0,
|
|
83
|
-
help="cap harvested sessions before mining; default derives from max tasks")
|
|
84
|
-
p.add_argument("--max-tasks", type=int, default=0,
|
|
85
|
-
help="cap mined tasks for this run")
|
|
86
|
-
p.add_argument("--target-skill-path", default="",
|
|
87
|
-
help="explicit live SKILL.md path to evolve/stage/adopt")
|
|
88
|
-
p.add_argument("--tasks-file", default="",
|
|
89
|
-
help="reviewed TaskRecord JSON file to replay instead of harvesting")
|
|
90
|
-
p.add_argument("--progress", action="store_true",
|
|
91
|
-
help="print phase progress to stderr")
|
|
92
|
-
p.add_argument("--auto-adopt", action="store_true")
|
|
93
|
-
p.add_argument("--json", action="store_true")
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def _cfg_from_args(args, task_meta: Dict[str, Any] | None = None) -> Any:
|
|
97
|
-
overrides: Dict[str, Any] = {}
|
|
98
|
-
if args.project:
|
|
99
|
-
overrides["invoked_project"] = os.path.abspath(args.project)
|
|
100
|
-
overrides["projects"] = "invoked"
|
|
101
|
-
if args.scope:
|
|
102
|
-
overrides["projects"] = args.scope
|
|
103
|
-
if args.backend:
|
|
104
|
-
overrides["backend"] = args.backend
|
|
105
|
-
if args.model:
|
|
106
|
-
overrides["model"] = args.model
|
|
107
|
-
if getattr(args, "codex_path", ""):
|
|
108
|
-
overrides["codex_path"] = os.path.abspath(args.codex_path)
|
|
109
|
-
if getattr(args, "claude_home", ""):
|
|
110
|
-
overrides["claude_home"] = os.path.abspath(args.claude_home)
|
|
111
|
-
if getattr(args, "codex_home", ""):
|
|
112
|
-
overrides["codex_home"] = os.path.abspath(args.codex_home)
|
|
113
|
-
if getattr(args, "source", ""):
|
|
114
|
-
overrides["transcript_source"] = args.source
|
|
115
|
-
lh = getattr(args, "lookback_hours", None)
|
|
116
|
-
if lh is not None: # --lookback-hours was explicitly passed (0 = full history)
|
|
117
|
-
overrides["lookback_hours"] = lh
|
|
118
|
-
if getattr(args, "edit_budget", 0):
|
|
119
|
-
overrides["edit_budget"] = args.edit_budget
|
|
120
|
-
if getattr(args, "max_sessions", 0):
|
|
121
|
-
overrides["max_sessions_per_night"] = args.max_sessions
|
|
122
|
-
if getattr(args, "max_tasks", 0):
|
|
123
|
-
overrides["max_tasks_per_night"] = args.max_tasks
|
|
124
|
-
target_skill_path = getattr(args, "target_skill_path", "")
|
|
125
|
-
if not target_skill_path and task_meta:
|
|
126
|
-
target_skill_path = str(task_meta.get("target_skill_path") or "")
|
|
127
|
-
if target_skill_path:
|
|
128
|
-
path = os.path.expanduser(target_skill_path)
|
|
129
|
-
if args.project and not os.path.isabs(path):
|
|
130
|
-
path = os.path.join(os.path.abspath(args.project), path)
|
|
131
|
-
overrides["target_skill_path"] = os.path.abspath(path)
|
|
132
|
-
if getattr(args, "progress", False):
|
|
133
|
-
overrides["progress"] = True
|
|
134
|
-
if getattr(args, "auto_adopt", False):
|
|
135
|
-
overrides["auto_adopt"] = True
|
|
136
|
-
return load_config(**overrides)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def cmd_run(args, dry: bool = False) -> int:
|
|
140
|
-
task_meta: Dict[str, Any] = {}
|
|
141
|
-
tasks = None
|
|
142
|
-
if getattr(args, "tasks_file", ""):
|
|
143
|
-
# Load once before config so target_skill_path can default from metadata.
|
|
144
|
-
tasks, task_meta = load_tasks_file(args.tasks_file)
|
|
145
|
-
cfg = _cfg_from_args(args, task_meta=task_meta)
|
|
146
|
-
if getattr(args, "tasks_file", ""):
|
|
147
|
-
tasks, task_meta = load_tasks_file(
|
|
148
|
-
args.tasks_file,
|
|
149
|
-
holdout_fraction=cfg.get("holdout_fraction", 0.34),
|
|
150
|
-
seed=cfg.get("seed", 42),
|
|
151
|
-
)
|
|
152
|
-
if cfg.get("backend", "mock") != "mock" and task_meta.get("reviewed") is not True:
|
|
153
|
-
print(
|
|
154
|
-
"[sleep] refusing real-backend replay from an unreviewed tasks file; "
|
|
155
|
-
"inspect/redact it and set \"reviewed\": true first",
|
|
156
|
-
file=sys.stderr,
|
|
157
|
-
)
|
|
158
|
-
return 2
|
|
159
|
-
outcome = run_sleep_cycle(cfg, seed_tasks=tasks, dry_run=dry)
|
|
160
|
-
rep = outcome.report
|
|
161
|
-
if args.json:
|
|
162
|
-
payload = _report_payload(rep, outcome)
|
|
163
|
-
if task_meta:
|
|
164
|
-
payload["tasks_file"] = task_meta.get("tasks_file", "")
|
|
165
|
-
payload["tasks_reviewed"] = task_meta.get("reviewed", False)
|
|
166
|
-
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
167
|
-
else:
|
|
168
|
-
print(f"[sleep] night {rep.night}: {rep.n_sessions} sessions -> {rep.n_tasks} tasks")
|
|
169
|
-
print(f"[sleep] held-out {rep.baseline_score:.3f} -> {rep.candidate_score:.3f} "
|
|
170
|
-
f"=> {rep.gate_action} (accepted={rep.accepted})")
|
|
171
|
-
for e in rep.edits:
|
|
172
|
-
print(f" + [{e.target}/{e.op}] {e.content}")
|
|
173
|
-
if rep.rejected_edits:
|
|
174
|
-
print("[sleep] rejected by gate:")
|
|
175
|
-
for e in rep.rejected_edits:
|
|
176
|
-
print(f" - [{e.target}/{e.op}] {e.content}")
|
|
177
|
-
if outcome.staging_dir:
|
|
178
|
-
print(f"[sleep] staged: {outcome.staging_dir}")
|
|
179
|
-
if not outcome.adopted:
|
|
180
|
-
print("[sleep] review it, then: python -m skillopt_sleep adopt")
|
|
181
|
-
if outcome.adopted:
|
|
182
|
-
print(f"[sleep] auto-adopted: {', '.join(outcome.adopted_paths)}")
|
|
183
|
-
return 0
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def cmd_status(args) -> int:
|
|
187
|
-
cfg = _cfg_from_args(args)
|
|
188
|
-
state = SleepState.load(cfg.state_path)
|
|
189
|
-
project = cfg.get("invoked_project") or os.getcwd()
|
|
190
|
-
latest = latest_staging(project)
|
|
191
|
-
info = {
|
|
192
|
-
"night": state.night,
|
|
193
|
-
"state_path": cfg.state_path,
|
|
194
|
-
"project": project,
|
|
195
|
-
"history_tail": state.data.get("history", [])[-5:],
|
|
196
|
-
"latest_staging": latest,
|
|
197
|
-
"slow_memory_chars": len(state.slow_memory),
|
|
198
|
-
}
|
|
199
|
-
if args.json:
|
|
200
|
-
print(json.dumps(info, ensure_ascii=False, indent=2))
|
|
201
|
-
else:
|
|
202
|
-
print(f"[sleep] nights so far: {state.night}")
|
|
203
|
-
print(f"[sleep] project: {project}")
|
|
204
|
-
if latest:
|
|
205
|
-
print(f"[sleep] latest staged proposal: {latest}")
|
|
206
|
-
rp = os.path.join(latest, "report.md")
|
|
207
|
-
if os.path.exists(rp):
|
|
208
|
-
with open(rp) as f:
|
|
209
|
-
print("\n" + f.read())
|
|
210
|
-
else:
|
|
211
|
-
print("[sleep] no staged proposals yet.")
|
|
212
|
-
return 0
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def cmd_adopt(args) -> int:
|
|
216
|
-
cfg = _cfg_from_args(args)
|
|
217
|
-
project = cfg.get("invoked_project") or os.getcwd()
|
|
218
|
-
target = args.staging or latest_staging(project)
|
|
219
|
-
if not target or not os.path.isdir(target):
|
|
220
|
-
print("[sleep] nothing to adopt (no staging dir).")
|
|
221
|
-
return 1
|
|
222
|
-
updated = adopt_staging(target)
|
|
223
|
-
print(f"[sleep] adopted from {target}")
|
|
224
|
-
for p in updated:
|
|
225
|
-
print(f" -> {p}")
|
|
226
|
-
if not updated:
|
|
227
|
-
print("[sleep] (proposal contained no accepted changes)")
|
|
228
|
-
return 0
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
def cmd_harvest(args) -> int:
|
|
232
|
-
cfg = _cfg_from_args(args)
|
|
233
|
-
session_limit = cfg.get("max_sessions_per_night", 0) or cfg.get("max_tasks_per_night", 40) * 3
|
|
234
|
-
target_skill_path = cfg.managed_skill_path() if cfg.get("target_skill_path", "") else ""
|
|
235
|
-
target_skill_text = _read_text(target_skill_path) if target_skill_path else ""
|
|
236
|
-
max_tasks = cfg.get("max_tasks_per_night", 40)
|
|
237
|
-
candidate_limit = max_tasks
|
|
238
|
-
if cfg.get("target_task_filter", True) and target_skill_text:
|
|
239
|
-
candidate_limit = max(max_tasks, max_tasks * 3)
|
|
240
|
-
digests = harvest_for_config(cfg, limit=session_limit)
|
|
241
|
-
tasks = mine(
|
|
242
|
-
digests,
|
|
243
|
-
max_tasks=max_tasks,
|
|
244
|
-
candidate_limit=candidate_limit,
|
|
245
|
-
holdout_fraction=cfg.get("holdout_fraction", 0.34),
|
|
246
|
-
seed=cfg.get("seed", 42),
|
|
247
|
-
target_skill_text=target_skill_text,
|
|
248
|
-
target_skill_path=target_skill_path,
|
|
249
|
-
)
|
|
250
|
-
payload = make_tasks_payload(
|
|
251
|
-
tasks,
|
|
252
|
-
project=cfg.get("invoked_project") or os.getcwd(),
|
|
253
|
-
transcript_source=cfg.get("transcript_source", ""),
|
|
254
|
-
n_sessions=len(digests),
|
|
255
|
-
target_skill_path=target_skill_path,
|
|
256
|
-
)
|
|
257
|
-
output_path = ""
|
|
258
|
-
if getattr(args, "output", ""):
|
|
259
|
-
output_path = write_tasks_file(args.output, payload)
|
|
260
|
-
if args.json:
|
|
261
|
-
json_payload = dict(payload)
|
|
262
|
-
if output_path:
|
|
263
|
-
json_payload["output"] = output_path
|
|
264
|
-
print(json.dumps(json_payload, ensure_ascii=False, indent=2))
|
|
265
|
-
else:
|
|
266
|
-
print(f"[sleep] {len(digests)} sessions -> {len(tasks)} tasks")
|
|
267
|
-
if output_path:
|
|
268
|
-
print(f"[sleep] wrote reviewed-task draft: {output_path}")
|
|
269
|
-
for t in tasks:
|
|
270
|
-
print(f" [{t.split}/{t.outcome}] {t.intent[:90]}")
|
|
271
|
-
return 0
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def cmd_schedule(args) -> int:
|
|
275
|
-
from skillopt_sleep.scheduler import schedule, list_scheduled
|
|
276
|
-
cfg = _cfg_from_args(args)
|
|
277
|
-
project = cfg.get("invoked_project") or os.getcwd()
|
|
278
|
-
ok, msg = schedule(project, backend=cfg.get("backend", "mock"),
|
|
279
|
-
hour=args.hour, minute=args.minute,
|
|
280
|
-
extra=("--auto-adopt" if getattr(args, "auto_adopt", False) else ""))
|
|
281
|
-
print("[sleep] " + msg)
|
|
282
|
-
cur = list_scheduled()
|
|
283
|
-
if cur:
|
|
284
|
-
print("[sleep] currently scheduled:")
|
|
285
|
-
for ln in cur:
|
|
286
|
-
print(" " + ln[:140])
|
|
287
|
-
return 0 if ok else 1
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
def cmd_unschedule(args) -> int:
|
|
291
|
-
from skillopt_sleep.scheduler import unschedule
|
|
292
|
-
cfg = _cfg_from_args(args)
|
|
293
|
-
project = cfg.get("invoked_project") or os.getcwd()
|
|
294
|
-
ok, msg = unschedule(project, all_projects=getattr(args, "all", False))
|
|
295
|
-
print("[sleep] " + msg)
|
|
296
|
-
return 0 if ok else 1
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
def main(argv=None) -> int:
|
|
300
|
-
parser = argparse.ArgumentParser(prog="skillopt_sleep", description="SkillOpt-Sleep nightly self-evolution")
|
|
301
|
-
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
302
|
-
|
|
303
|
-
p_run = sub.add_parser("run", help="run a full sleep cycle")
|
|
304
|
-
_add_common(p_run)
|
|
305
|
-
p_dry = sub.add_parser("dry-run", help="harvest+mine+replay, report only")
|
|
306
|
-
_add_common(p_dry)
|
|
307
|
-
p_status = sub.add_parser("status", help="show state + latest proposal")
|
|
308
|
-
_add_common(p_status)
|
|
309
|
-
p_adopt = sub.add_parser("adopt", help="apply latest staged proposal")
|
|
310
|
-
_add_common(p_adopt)
|
|
311
|
-
p_adopt.add_argument("--staging", default="", help="specific staging dir")
|
|
312
|
-
p_harvest = sub.add_parser("harvest", help="debug: show mined tasks")
|
|
313
|
-
_add_common(p_harvest)
|
|
314
|
-
p_harvest.add_argument("--output", default="", help="write mined tasks JSON for review")
|
|
315
|
-
p_sched = sub.add_parser("schedule", help="install a nightly cron entry for this project")
|
|
316
|
-
_add_common(p_sched)
|
|
317
|
-
p_sched.add_argument("--hour", type=int, default=3)
|
|
318
|
-
p_sched.add_argument("--minute", type=int, default=17)
|
|
319
|
-
p_unsched = sub.add_parser("unschedule", help="remove the nightly cron entry")
|
|
320
|
-
_add_common(p_unsched)
|
|
321
|
-
p_unsched.add_argument("--all", action="store_true", help="remove all managed entries")
|
|
322
|
-
|
|
323
|
-
args = parser.parse_args(argv)
|
|
324
|
-
if args.cmd == "run":
|
|
325
|
-
return cmd_run(args, dry=False)
|
|
326
|
-
if args.cmd == "dry-run":
|
|
327
|
-
return cmd_run(args, dry=True)
|
|
328
|
-
if args.cmd == "status":
|
|
329
|
-
return cmd_status(args)
|
|
330
|
-
if args.cmd == "adopt":
|
|
331
|
-
return cmd_adopt(args)
|
|
332
|
-
if args.cmd == "harvest":
|
|
333
|
-
return cmd_harvest(args)
|
|
334
|
-
if args.cmd == "schedule":
|
|
335
|
-
return cmd_schedule(args)
|
|
336
|
-
if args.cmd == "unschedule":
|
|
337
|
-
return cmd_unschedule(args)
|
|
338
|
-
parser.print_help()
|
|
339
|
-
return 2
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if __name__ == "__main__":
|
|
343
|
-
sys.exit(main())
|