claude-turing 4.5.0 → 4.6.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-plugin/marketplace.json +18 -0
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/commands/turing.md +85 -77
- package/config/commands.yaml +928 -0
- package/package.json +3 -2
- package/src/command-registry.js +151 -0
- package/src/install.js +8 -34
- package/src/verify.js +5 -88
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-turing",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Autonomous ML research harness for Claude Code. The autoresearch loop as a formal protocol — iteratively trains, evaluates, and improves ML models with structured experiment tracking, convergence detection, immutable evaluation infrastructure, and safety guardrails.",
|
|
6
6
|
"bin": {
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"node": ">=18.0.0"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"commander": "^13.0.0"
|
|
52
|
+
"commander": "^13.0.0",
|
|
53
|
+
"yaml": "^2.6.1"
|
|
53
54
|
}
|
|
54
55
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import YAML from 'yaml';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const PLUGIN_ROOT = dirname(__dirname);
|
|
8
|
+
const REGISTRY_PATH = join(PLUGIN_ROOT, 'config', 'commands.yaml');
|
|
9
|
+
|
|
10
|
+
const COMMAND_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
11
|
+
const INVOCATION_MODES = new Set(['slash_only']);
|
|
12
|
+
const MODEL_INVOCATIONS = new Set(['disabled', 'enabled']);
|
|
13
|
+
const SCRIPT_LOCATIONS = new Set(['repo', 'scaffold']);
|
|
14
|
+
|
|
15
|
+
function isRecord(value) {
|
|
16
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function requireRecord(value, label) {
|
|
20
|
+
if (!isRecord(value)) {
|
|
21
|
+
throw new Error(`${label} must be a mapping`);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function requireNonEmptyString(value, label) {
|
|
27
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
28
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function requireNonEmptyStringList(value, label) {
|
|
34
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
35
|
+
throw new Error(`${label} must be a non-empty string list`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const [index, item] of value.entries()) {
|
|
39
|
+
requireNonEmptyString(item, `${label}[${index}]`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function requireEnum(value, allowed, label) {
|
|
46
|
+
requireNonEmptyString(value, label);
|
|
47
|
+
if (!allowed.has(value)) {
|
|
48
|
+
throw new Error(`${label} must be one of: ${Array.from(allowed).join(', ')}`);
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function validateEquivalentScript(value, commandName) {
|
|
54
|
+
const label = `commands.${commandName}.equivalent_script`;
|
|
55
|
+
const script = requireRecord(value, label);
|
|
56
|
+
const keys = Object.keys(script).sort();
|
|
57
|
+
const expectedKeys = ['location', 'path'];
|
|
58
|
+
if (keys.length !== expectedKeys.length || keys.some((key, index) => key !== expectedKeys[index])) {
|
|
59
|
+
throw new Error(`${label} must contain exactly: ${expectedKeys.join(', ')}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
requireNonEmptyString(script.path, `${label}.path`);
|
|
63
|
+
requireEnum(
|
|
64
|
+
script.location,
|
|
65
|
+
SCRIPT_LOCATIONS,
|
|
66
|
+
`${label}.location`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function validateCommand(commandName, value) {
|
|
71
|
+
if (!COMMAND_NAME_PATTERN.test(commandName)) {
|
|
72
|
+
throw new Error(`Invalid command name: ${commandName}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const command = requireRecord(value, `commands.${commandName}`);
|
|
76
|
+
requireNonEmptyString(command.description, `commands.${commandName}.description`);
|
|
77
|
+
requireNonEmptyString(command.lifecycle, `commands.${commandName}.lifecycle`);
|
|
78
|
+
requireEnum(command.invocation_mode, INVOCATION_MODES, `commands.${commandName}.invocation_mode`);
|
|
79
|
+
requireEnum(
|
|
80
|
+
command.model_invocation,
|
|
81
|
+
MODEL_INVOCATIONS,
|
|
82
|
+
`commands.${commandName}.model_invocation`,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (typeof command.mutates_project !== 'boolean') {
|
|
86
|
+
throw new Error(`commands.${commandName}.mutates_project must be a boolean`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
requireNonEmptyStringList(command.tools, `commands.${commandName}.tools`);
|
|
90
|
+
|
|
91
|
+
if ('argument_hint' in command) {
|
|
92
|
+
requireNonEmptyString(command.argument_hint, `commands.${commandName}.argument_hint`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if ('equivalent_script' in command) {
|
|
96
|
+
validateEquivalentScript(command.equivalent_script, commandName);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function validateRegistry(value) {
|
|
101
|
+
const registry = requireRecord(value, 'Command registry root');
|
|
102
|
+
const configFiles = requireNonEmptyStringList(registry.config_files, 'config_files');
|
|
103
|
+
const commands = requireRecord(registry.commands, 'commands');
|
|
104
|
+
|
|
105
|
+
for (const [commandName, command] of Object.entries(commands)) {
|
|
106
|
+
validateCommand(commandName, command);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
commands,
|
|
111
|
+
commandNames: Object.keys(commands).sort(),
|
|
112
|
+
configFiles: [...configFiles].sort(),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function loadCommandRegistry(registryPath = REGISTRY_PATH) {
|
|
117
|
+
let source;
|
|
118
|
+
try {
|
|
119
|
+
source = await readFile(registryPath, 'utf8');
|
|
120
|
+
} catch (error) {
|
|
121
|
+
throw new Error(`Failed to read command registry at ${registryPath}: ${error.message}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let parsed;
|
|
125
|
+
try {
|
|
126
|
+
parsed = YAML.parse(source);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
throw new Error(`Failed to parse command registry at ${registryPath}: ${error.message}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
return validateRegistry(parsed);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
throw new Error(`Invalid command registry at ${registryPath}: ${error.message}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function getCommandNames(registryPath) {
|
|
139
|
+
const registry = await loadCommandRegistry(registryPath);
|
|
140
|
+
return registry.commandNames;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function getExpectedCommandPaths(registryPath) {
|
|
144
|
+
const names = await getCommandNames(registryPath);
|
|
145
|
+
return ['SKILL.md', ...names.map((name) => `${name}/SKILL.md`)];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function getConfigFiles(registryPath) {
|
|
149
|
+
const registry = await loadCommandRegistry(registryPath);
|
|
150
|
+
return registry.configFiles;
|
|
151
|
+
}
|
package/src/install.js
CHANGED
|
@@ -14,43 +14,24 @@ import { join, dirname } from "path";
|
|
|
14
14
|
import { fileURLToPath } from "url";
|
|
15
15
|
import { getTargetPaths } from "./paths.js";
|
|
16
16
|
import { updateClaudeMd } from "./claude-md.js";
|
|
17
|
+
import { getCommandNames, getConfigFiles } from "./command-registry.js";
|
|
17
18
|
|
|
18
19
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
20
|
const PLUGIN_ROOT = join(__dirname, "..");
|
|
20
21
|
|
|
21
|
-
// Single source of truth for sub-commands (DRY — used for dirs and file copy)
|
|
22
|
-
const SUB_COMMANDS = [
|
|
23
|
-
"init", "train", "status", "compare", "sweep", "validate",
|
|
24
|
-
"try", "brief", "suggest", "explore", "design", "logbook", "poster",
|
|
25
|
-
"report", "mode", "preflight", "card", "seed", "reproduce",
|
|
26
|
-
"diagnose", "ablate", "frontier", "profile", "checkpoint", "export",
|
|
27
|
-
"lit", "paper", "queue", "retry", "fork",
|
|
28
|
-
"diff", "watch", "regress",
|
|
29
|
-
"ensemble", "stitch", "warm",
|
|
30
|
-
"scale", "budget", "distill",
|
|
31
|
-
"transfer", "audit",
|
|
32
|
-
"sanity", "baseline", "leak",
|
|
33
|
-
"xray", "sensitivity", "calibrate",
|
|
34
|
-
"feature", "curriculum",
|
|
35
|
-
"prune", "quantize", "merge", "surgery",
|
|
36
|
-
"trend", "flashback", "archive", "annotate", "search", "template", "replay",
|
|
37
|
-
"cite", "present", "changelog",
|
|
38
|
-
"onboard", "share", "review",
|
|
39
|
-
"whatif", "counterfactual", "simulate",
|
|
40
|
-
"update", "registry",
|
|
41
|
-
"postmortem", "doctor", "plan",
|
|
42
|
-
];
|
|
43
22
|
|
|
44
23
|
export async function install(opts = {}) {
|
|
45
24
|
const scope = opts.global ? "global" : opts.project ? "project" : "global";
|
|
46
25
|
const paths = getTargetPaths(scope);
|
|
26
|
+
const subCommands = await getCommandNames();
|
|
27
|
+
const configFiles = await getConfigFiles();
|
|
47
28
|
|
|
48
29
|
console.log("Turing ML Research Harness — Installer");
|
|
49
30
|
console.log(`Target: ${paths.commands} (${scope})`);
|
|
50
31
|
console.log("");
|
|
51
32
|
|
|
52
33
|
// Create directories for each sub-command + agents + config
|
|
53
|
-
for (const subDir of ["", "agents", "config", "rules", "templates", ...
|
|
34
|
+
for (const subDir of ["", "agents", "config", "rules", "templates", ...subCommands]) {
|
|
54
35
|
await mkdir(join(paths.commands, subDir), { recursive: true });
|
|
55
36
|
}
|
|
56
37
|
|
|
@@ -62,13 +43,13 @@ export async function install(opts = {}) {
|
|
|
62
43
|
console.log(" Router -> SKILL.md");
|
|
63
44
|
|
|
64
45
|
// Copy sub-commands as <name>/SKILL.md
|
|
65
|
-
for (const cmd of
|
|
46
|
+
for (const cmd of subCommands) {
|
|
66
47
|
await copyFile(
|
|
67
48
|
join(PLUGIN_ROOT, "commands", `${cmd}.md`),
|
|
68
49
|
join(paths.commands, cmd, "SKILL.md"),
|
|
69
50
|
);
|
|
70
51
|
}
|
|
71
|
-
console.log(` ${
|
|
52
|
+
console.log(` ${subCommands.length} commands installed`);
|
|
72
53
|
|
|
73
54
|
// Copy rules
|
|
74
55
|
await copyFile(
|
|
@@ -88,20 +69,13 @@ export async function install(opts = {}) {
|
|
|
88
69
|
console.log(` ${agentFiles.length} agents installed`);
|
|
89
70
|
|
|
90
71
|
// Copy config (static schema files only)
|
|
91
|
-
const
|
|
92
|
-
"defaults.yaml", "lifecycle.toml", "taxonomy.toml",
|
|
93
|
-
"experiment_archetypes.yaml", "novelty_aliases.yaml",
|
|
94
|
-
"relationships.toml", "state.toml", "task_taxonomy.yaml",
|
|
95
|
-
"failure_modes.yaml",
|
|
96
|
-
"watch_alerts.yaml",
|
|
97
|
-
];
|
|
98
|
-
for (const file of CONFIG_FILES) {
|
|
72
|
+
for (const file of configFiles) {
|
|
99
73
|
await copyFile(
|
|
100
74
|
join(PLUGIN_ROOT, "config", file),
|
|
101
75
|
join(paths.config, file),
|
|
102
76
|
);
|
|
103
77
|
}
|
|
104
|
-
console.log(` ${
|
|
78
|
+
console.log(` ${configFiles.length} config files installed`);
|
|
105
79
|
|
|
106
80
|
// Copy templates used by /turing:init
|
|
107
81
|
await cp(
|
package/src/verify.js
CHANGED
|
@@ -11,99 +11,14 @@
|
|
|
11
11
|
import { access, readdir } from "fs/promises";
|
|
12
12
|
import { dirname, join } from "path";
|
|
13
13
|
import { fileURLToPath } from "url";
|
|
14
|
+
import { getConfigFiles, getExpectedCommandPaths } from "./command-registry.js";
|
|
14
15
|
import { getTargetPaths } from "./paths.js";
|
|
15
16
|
|
|
16
17
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
18
|
const PLUGIN_ROOT = join(__dirname, "..");
|
|
18
19
|
|
|
19
|
-
const EXPECTED_COMMANDS = [
|
|
20
|
-
"SKILL.md",
|
|
21
|
-
"init/SKILL.md",
|
|
22
|
-
"train/SKILL.md",
|
|
23
|
-
"status/SKILL.md",
|
|
24
|
-
"compare/SKILL.md",
|
|
25
|
-
"sweep/SKILL.md",
|
|
26
|
-
"validate/SKILL.md",
|
|
27
|
-
"try/SKILL.md",
|
|
28
|
-
"brief/SKILL.md",
|
|
29
|
-
"suggest/SKILL.md",
|
|
30
|
-
"explore/SKILL.md",
|
|
31
|
-
"design/SKILL.md",
|
|
32
|
-
"logbook/SKILL.md",
|
|
33
|
-
"poster/SKILL.md",
|
|
34
|
-
"report/SKILL.md",
|
|
35
|
-
"mode/SKILL.md",
|
|
36
|
-
"preflight/SKILL.md",
|
|
37
|
-
"card/SKILL.md",
|
|
38
|
-
"seed/SKILL.md",
|
|
39
|
-
"reproduce/SKILL.md",
|
|
40
|
-
"diagnose/SKILL.md",
|
|
41
|
-
"ablate/SKILL.md",
|
|
42
|
-
"frontier/SKILL.md",
|
|
43
|
-
"profile/SKILL.md",
|
|
44
|
-
"checkpoint/SKILL.md",
|
|
45
|
-
"export/SKILL.md",
|
|
46
|
-
"lit/SKILL.md",
|
|
47
|
-
"paper/SKILL.md",
|
|
48
|
-
"queue/SKILL.md",
|
|
49
|
-
"retry/SKILL.md",
|
|
50
|
-
"fork/SKILL.md",
|
|
51
|
-
"diff/SKILL.md",
|
|
52
|
-
"watch/SKILL.md",
|
|
53
|
-
"regress/SKILL.md",
|
|
54
|
-
"ensemble/SKILL.md",
|
|
55
|
-
"stitch/SKILL.md",
|
|
56
|
-
"warm/SKILL.md",
|
|
57
|
-
"scale/SKILL.md",
|
|
58
|
-
"budget/SKILL.md",
|
|
59
|
-
"distill/SKILL.md",
|
|
60
|
-
"transfer/SKILL.md",
|
|
61
|
-
"audit/SKILL.md",
|
|
62
|
-
"sanity/SKILL.md",
|
|
63
|
-
"baseline/SKILL.md",
|
|
64
|
-
"leak/SKILL.md",
|
|
65
|
-
"xray/SKILL.md",
|
|
66
|
-
"sensitivity/SKILL.md",
|
|
67
|
-
"calibrate/SKILL.md",
|
|
68
|
-
"feature/SKILL.md",
|
|
69
|
-
"curriculum/SKILL.md",
|
|
70
|
-
"prune/SKILL.md",
|
|
71
|
-
"quantize/SKILL.md",
|
|
72
|
-
"merge/SKILL.md",
|
|
73
|
-
"surgery/SKILL.md",
|
|
74
|
-
"trend/SKILL.md",
|
|
75
|
-
"flashback/SKILL.md",
|
|
76
|
-
"archive/SKILL.md",
|
|
77
|
-
"annotate/SKILL.md",
|
|
78
|
-
"search/SKILL.md",
|
|
79
|
-
"template/SKILL.md",
|
|
80
|
-
"replay/SKILL.md",
|
|
81
|
-
"cite/SKILL.md",
|
|
82
|
-
"present/SKILL.md",
|
|
83
|
-
"changelog/SKILL.md",
|
|
84
|
-
"onboard/SKILL.md",
|
|
85
|
-
"share/SKILL.md",
|
|
86
|
-
"review/SKILL.md",
|
|
87
|
-
"whatif/SKILL.md",
|
|
88
|
-
"counterfactual/SKILL.md",
|
|
89
|
-
"simulate/SKILL.md",
|
|
90
|
-
"update/SKILL.md",
|
|
91
|
-
"registry/SKILL.md",
|
|
92
|
-
"postmortem/SKILL.md",
|
|
93
|
-
"doctor/SKILL.md",
|
|
94
|
-
"plan/SKILL.md",
|
|
95
|
-
];
|
|
96
|
-
|
|
97
20
|
const EXPECTED_AGENTS = ["ml-researcher.md", "ml-evaluator.md"];
|
|
98
21
|
|
|
99
|
-
const EXPECTED_CONFIG = [
|
|
100
|
-
"defaults.yaml", "lifecycle.toml", "taxonomy.toml",
|
|
101
|
-
"experiment_archetypes.yaml", "novelty_aliases.yaml",
|
|
102
|
-
"relationships.toml", "state.toml", "task_taxonomy.yaml",
|
|
103
|
-
"failure_modes.yaml",
|
|
104
|
-
"watch_alerts.yaml",
|
|
105
|
-
];
|
|
106
|
-
|
|
107
22
|
async function templateFiles(root, relativeDir = "templates") {
|
|
108
23
|
const dir = join(root, relativeDir);
|
|
109
24
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
@@ -135,6 +50,8 @@ async function fileExists(path) {
|
|
|
135
50
|
}
|
|
136
51
|
|
|
137
52
|
export async function verify(opts = {}) {
|
|
53
|
+
const expectedCommands = await getExpectedCommandPaths();
|
|
54
|
+
const expectedConfig = await getConfigFiles();
|
|
138
55
|
const scopes = opts.scope ? [opts.scope] : ["global", "project"];
|
|
139
56
|
const expectedTemplates = await templateFiles(PLUGIN_ROOT);
|
|
140
57
|
let found = false;
|
|
@@ -151,7 +68,7 @@ export async function verify(opts = {}) {
|
|
|
151
68
|
let missing = 0;
|
|
152
69
|
|
|
153
70
|
console.log("Commands:");
|
|
154
|
-
for (const cmd of
|
|
71
|
+
for (const cmd of expectedCommands) {
|
|
155
72
|
const ok = await fileExists(join(paths.commands, cmd));
|
|
156
73
|
console.log(` ${ok ? "✓" : "✗"} commands/${cmd}`);
|
|
157
74
|
if (!ok) missing++;
|
|
@@ -165,7 +82,7 @@ export async function verify(opts = {}) {
|
|
|
165
82
|
}
|
|
166
83
|
|
|
167
84
|
console.log("\nConfig:");
|
|
168
|
-
for (const cfg of
|
|
85
|
+
for (const cfg of expectedConfig) {
|
|
169
86
|
const ok = await fileExists(join(paths.config, cfg));
|
|
170
87
|
console.log(` ${ok ? "✓" : "✗"} config/${cfg}`);
|
|
171
88
|
if (!ok) missing++;
|