@method-ai-hq/sdk 0.17.3 → 0.18.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 +2 -2
- package/dist/packages/sdk/package.json +2 -2
- package/dist/packages/sdk/src/authoring-instructions.d.ts +4 -4
- package/dist/packages/sdk/src/authoring-instructions.js +5 -5
- package/dist/packages/sdk/src/authoring.js +8 -6
- package/dist/packages/sdk/src/capabilities.d.ts +9 -6
- package/dist/packages/sdk/src/capabilities.js +15 -32
- package/dist/packages/sdk/src/computer-settings.d.ts +2 -5
- package/dist/packages/sdk/src/computer-settings.js +4 -11
- package/dist/packages/sdk/src/current-runtime.js +19 -8
- package/dist/packages/sdk/src/improve.d.ts +104 -29
- package/dist/packages/sdk/src/improve.js +240 -70
- package/dist/packages/sdk/src/judge.d.ts +12 -4
- package/dist/packages/sdk/src/judge.js +46 -10
- package/dist/packages/sdk/src/local-cli.d.ts +0 -1
- package/dist/packages/sdk/src/local-cli.js +16 -15
- package/dist/packages/sdk/src/local-judgments.d.ts +40 -0
- package/dist/packages/sdk/src/local-judgments.js +144 -0
- package/dist/packages/sdk/src/local-setup.d.ts +0 -2
- package/dist/packages/sdk/src/local-setup.js +1 -1
- package/dist/packages/sdk/src/method-client.d.ts +2 -0
- package/dist/packages/sdk/src/method-client.js +6 -2
- package/dist/packages/sdk/src/method-help.d.ts +1 -1
- package/dist/packages/sdk/src/method-help.js +20 -23
- package/dist/packages/sdk/src/method.js +35 -2
- package/dist/packages/sdk/src/model-call.d.ts +73 -0
- package/dist/packages/sdk/src/model-call.js +74 -0
- package/dist/packages/sdk/src/outbox.d.ts +1 -1
- package/dist/packages/sdk/src/outbox.js +6 -5
- package/dist/packages/sdk/src/own-model-key.d.ts +1 -1
- package/dist/packages/sdk/src/own-model-key.js +1 -1
- package/dist/packages/sdk/src/proposals.d.ts +10 -26
- package/dist/packages/sdk/src/proposals.js +32 -64
- package/dist/packages/sdk/src/quality.d.ts +1 -2
- package/dist/packages/sdk/src/quality.js +25 -16
- package/package.json +2 -2
|
@@ -10,12 +10,13 @@ import { checkNode } from "./doctor.js";
|
|
|
10
10
|
import { writePrivateJson } from "./files.js";
|
|
11
11
|
import { isWorkflowLink, saveWorkflowLink } from "./link.js";
|
|
12
12
|
import { inspectRun } from "./inspect.js";
|
|
13
|
-
import { accountNeeds
|
|
13
|
+
import { accountNeeds } from './capabilities.js';
|
|
14
|
+
import { agentSignedIn, localAgent } from '@withmethod/runtime/agents.js';
|
|
15
|
+
import { executable } from '@withmethod/runtime/io.js';
|
|
14
16
|
import { MethodClient } from "./method-client.js";
|
|
15
17
|
import { MethodSync as Sync } from "./method-sync.js";
|
|
16
18
|
import { prepareVersion, savedLine, methodContent } from "./versions.js";
|
|
17
19
|
import { Outbox, savedVersions } from "./outbox.js";
|
|
18
|
-
import { readComputerSettings } from "./computer-settings.js";
|
|
19
20
|
import { localSetup } from "./local-setup.js";
|
|
20
21
|
import { readDocument } from "./authoring.js";
|
|
21
22
|
import { withInputsFrom } from "./inputs-from.js";
|
|
@@ -24,7 +25,7 @@ import { randomUUID } from "node:crypto";
|
|
|
24
25
|
const help = "Use method run, steps, prompt, inspect, or doctor. See method help.";
|
|
25
26
|
export function parse(args) {
|
|
26
27
|
return parseArgs({ args, allowPositionals: true, options: {
|
|
27
|
-
server: { type: "string" },
|
|
28
|
+
server: { type: "string" }, background: { type: "boolean" }, state: { type: "string" }, out: { type: "string" }, inputs: { type: "string" }, "inputs-from": { type: "string" }, workspace: { type: "string" },
|
|
28
29
|
"run-dir": { type: "string" }, fresh: { type: "boolean" }, rerun: { type: "string", multiple: true }, resume: { type: "boolean" }, human: { type: "string" }, retry: { type: "string", multiple: true },
|
|
29
30
|
"include-files": { type: "boolean" }, verbose: { type: "boolean" }, help: { type: "boolean" }
|
|
30
31
|
} });
|
|
@@ -85,7 +86,7 @@ async function accountSync(path, flags) {
|
|
|
85
86
|
}
|
|
86
87
|
const method = readDocument(path);
|
|
87
88
|
const setup = await localSetup(path, flags);
|
|
88
|
-
const needs = accountNeeds(method, setup.config
|
|
89
|
+
const needs = accountNeeds(method, setup.config);
|
|
89
90
|
if (needs.classification && !client.token())
|
|
90
91
|
await client.login();
|
|
91
92
|
if (!client.token()) {
|
|
@@ -131,18 +132,18 @@ export async function localMain(args = process.argv.slice(2)) {
|
|
|
131
132
|
process.exitCode = 1;
|
|
132
133
|
return;
|
|
133
134
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
process.stdout.write(
|
|
135
|
+
// Each installed local agent is checked; steps that name no model run on the calling or installed agent.
|
|
136
|
+
for (const backend of ['codex', 'claude']) {
|
|
137
|
+
try {
|
|
138
|
+
await executable(backend);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
process.stdout.write(await agentSignedIn(backend) ? `${backend}: installed and signed in.\n` : `${backend}: installed, not signed in. Sign in with ${backend}.\n`);
|
|
143
144
|
}
|
|
144
|
-
await
|
|
145
|
-
process.stdout.write('
|
|
145
|
+
const agent = await localAgent();
|
|
146
|
+
process.stdout.write(`${agent ? `Steps that name no model run on ${agent === 'claude' ? 'Claude Code' : 'Codex'} with your own subscription.` : 'No signed-in Claude Code or Codex: steps that name no model use hosted models on account credit after method login.'} No method was run. Use method validate FILE to check a Method and its dependencies.\n`);
|
|
146
147
|
return;
|
|
147
148
|
}
|
|
148
149
|
if (!["steps", "prompt", "run"].includes(command) || !target || parsed.positionals.length !== 2)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A judgment that method judge keeps on this computer, for a Method whose run content stays here (run_data: device, or
|
|
3
|
+
* an organization that keeps run content on devices). The account keeps the judgment without its note and corrected
|
|
4
|
+
* result; this record keeps them, for the improvement loop on this computer.
|
|
5
|
+
*/
|
|
6
|
+
export type LocalJudgment = {
|
|
7
|
+
id: string;
|
|
8
|
+
run_id: string;
|
|
9
|
+
account_run_id: string | null;
|
|
10
|
+
run_dir: string;
|
|
11
|
+
method_id: string | null;
|
|
12
|
+
file: string;
|
|
13
|
+
step_id: string | null;
|
|
14
|
+
verdict: 'right' | 'wrong';
|
|
15
|
+
note: string | null;
|
|
16
|
+
corrected?: unknown;
|
|
17
|
+
created_at: string;
|
|
18
|
+
};
|
|
19
|
+
/** The file that a run folder records: the Method file that method run ran. */
|
|
20
|
+
export declare const methodFileRecord = "method-file.json";
|
|
21
|
+
/**
|
|
22
|
+
* Every ID that names a run folder: the ID that method run printed (run-id.json), the account run ID and sync ID
|
|
23
|
+
* (method-sync.json), request.json run_id, the execution ID (run.started in events.jsonl), the folder, and its name.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runFolderIds(dir: string): string[];
|
|
26
|
+
/** The run folder on this computer that a run ID names, from the Method cache and .method-runs in this folder. */
|
|
27
|
+
export declare function findRunFolder(runId: string): string | undefined;
|
|
28
|
+
/** Every run folder on this computer by each of its IDs, read once (for many judgments). */
|
|
29
|
+
export declare function runFolderIndex(): Map<string, string>;
|
|
30
|
+
/** Whether a run folder's Method keeps run content on this computer. */
|
|
31
|
+
export declare function deviceRun(dir: string): boolean;
|
|
32
|
+
/** The Method file that a run ran: its record in the run folder, else a .method file in this folder with the run's Method ID (or name). */
|
|
33
|
+
export declare function methodFileOfRun(dir: string): string | undefined;
|
|
34
|
+
/** The private .method folder next to a Method file. It is not for Git: its files can hold run content. */
|
|
35
|
+
export declare function localMethodFolder(file: string): string;
|
|
36
|
+
export declare const localJudgmentsFile: (file: string) => string;
|
|
37
|
+
/** Add a judgment to the Method's judgments file, one JSON line each. */
|
|
38
|
+
export declare function saveLocalJudgment(file: string, judgment: LocalJudgment): string;
|
|
39
|
+
/** The judgments kept on this computer for this Method file, oldest first. */
|
|
40
|
+
export declare function localJudgments(file: string): LocalJudgment[];
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, openSync, closeSync, readFileSync, readSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { readDocument } from './authoring.js';
|
|
4
|
+
import { methodCache } from './prepare.js';
|
|
5
|
+
/** The file that a run folder records: the Method file that method run ran. */
|
|
6
|
+
export const methodFileRecord = 'method-file.json';
|
|
7
|
+
const readJson = (path) => { try {
|
|
8
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
} };
|
|
13
|
+
/** The first line of a file, read without the rest. */
|
|
14
|
+
function firstLine(path) {
|
|
15
|
+
let fd;
|
|
16
|
+
try {
|
|
17
|
+
fd = openSync(path, 'r');
|
|
18
|
+
const buffer = Buffer.alloc(64 * 1024), size = readSync(fd, buffer, 0, buffer.length, 0);
|
|
19
|
+
return buffer.subarray(0, size).toString('utf8').split('\n')[0] ?? '';
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
if (fd !== undefined)
|
|
26
|
+
closeSync(fd);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Every ID that names a run folder: the ID that method run printed (run-id.json), the account run ID and sync ID
|
|
31
|
+
* (method-sync.json), request.json run_id, the execution ID (run.started in events.jsonl), the folder, and its name.
|
|
32
|
+
*/
|
|
33
|
+
export function runFolderIds(dir) {
|
|
34
|
+
const folder = resolve(dir), sync = readJson(join(folder, 'method-sync.json')) ?? {};
|
|
35
|
+
let started = {};
|
|
36
|
+
try {
|
|
37
|
+
started = JSON.parse(firstLine(join(folder, 'events.jsonl')));
|
|
38
|
+
}
|
|
39
|
+
catch { /* no events yet */ }
|
|
40
|
+
return [...new Set([readJson(join(folder, 'run-id.json'))?.id, sync.id, sync.dashboard_id, readJson(join(folder, 'request.json'))?.run_id,
|
|
41
|
+
started?.event === 'run.started' ? started.execution_id : undefined, folder, basename(folder)].filter((id) => typeof id === 'string' && !!id))];
|
|
42
|
+
}
|
|
43
|
+
/** The run folder on this computer that a run ID names, from the Method cache and .method-runs in this folder. */
|
|
44
|
+
export function findRunFolder(runId) {
|
|
45
|
+
const direct = resolve(runId);
|
|
46
|
+
if (existsSync(join(direct, 'method.json')))
|
|
47
|
+
return direct;
|
|
48
|
+
for (const root of [join(methodCache(), 'runs'), join(process.cwd(), '.method-runs')]) {
|
|
49
|
+
let names = [];
|
|
50
|
+
try {
|
|
51
|
+
names = readdirSync(root);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// A folder named by the ID is the quick match; the others need their records read.
|
|
57
|
+
if (names.includes(runId) && existsSync(join(root, runId, 'method.json')))
|
|
58
|
+
return join(root, runId);
|
|
59
|
+
for (const name of names) {
|
|
60
|
+
const dir = join(root, name);
|
|
61
|
+
if (existsSync(join(dir, 'method.json')) && runFolderIds(dir).includes(runId))
|
|
62
|
+
return dir;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
/** Every run folder on this computer by each of its IDs, read once (for many judgments). */
|
|
68
|
+
export function runFolderIndex() {
|
|
69
|
+
const index = new Map();
|
|
70
|
+
for (const root of [join(process.cwd(), '.method-runs'), join(methodCache(), 'runs')]) {
|
|
71
|
+
let names = [];
|
|
72
|
+
try {
|
|
73
|
+
names = readdirSync(root);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
for (const name of names) {
|
|
79
|
+
const dir = join(root, name);
|
|
80
|
+
if (existsSync(join(dir, 'method.json')))
|
|
81
|
+
for (const id of runFolderIds(dir))
|
|
82
|
+
index.set(id, dir);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return index;
|
|
86
|
+
}
|
|
87
|
+
/** Whether a run folder's Method keeps run content on this computer. */
|
|
88
|
+
export function deviceRun(dir) {
|
|
89
|
+
return readJson(join(dir, 'method-sync.json'))?.device === true || readJson(join(dir, 'method.json'))?.run_data === 'device';
|
|
90
|
+
}
|
|
91
|
+
/** The Method file that a run ran: its record in the run folder, else a .method file in this folder with the run's Method ID (or name). */
|
|
92
|
+
export function methodFileOfRun(dir) {
|
|
93
|
+
const recorded = readJson(join(dir, methodFileRecord))?.path;
|
|
94
|
+
if (typeof recorded === 'string' && existsSync(recorded))
|
|
95
|
+
return recorded;
|
|
96
|
+
const method = readJson(join(dir, 'method.json')) ?? {};
|
|
97
|
+
let names = [];
|
|
98
|
+
try {
|
|
99
|
+
names = readdirSync(process.cwd()).filter(name => name.endsWith('.method'));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
for (const name of names.sort()) {
|
|
105
|
+
const file = join(process.cwd(), name);
|
|
106
|
+
try {
|
|
107
|
+
const document = readDocument(file);
|
|
108
|
+
if (typeof method.id === 'string' ? document.id === method.id : document.name === method.name)
|
|
109
|
+
return file;
|
|
110
|
+
}
|
|
111
|
+
catch { /* not a readable Method */ }
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
/** The private .method folder next to a Method file. It is not for Git: its files can hold run content. */
|
|
116
|
+
export function localMethodFolder(file) {
|
|
117
|
+
const folder = join(dirname(file), '.method'), ignore = join(folder, '.gitignore');
|
|
118
|
+
mkdirSync(folder, { recursive: true, mode: 0o700 });
|
|
119
|
+
if (!existsSync(ignore))
|
|
120
|
+
writeFileSync(ignore, '*\n', { mode: 0o600 });
|
|
121
|
+
return folder;
|
|
122
|
+
}
|
|
123
|
+
export const localJudgmentsFile = (file) => join(dirname(file), '.method', 'judgments.jsonl');
|
|
124
|
+
/** Add a judgment to the Method's judgments file, one JSON line each. */
|
|
125
|
+
export function saveLocalJudgment(file, judgment) {
|
|
126
|
+
localMethodFolder(file);
|
|
127
|
+
const path = localJudgmentsFile(file);
|
|
128
|
+
appendFileSync(path, JSON.stringify(judgment) + '\n', { mode: 0o600 });
|
|
129
|
+
return path;
|
|
130
|
+
}
|
|
131
|
+
/** The judgments kept on this computer for this Method file, oldest first. */
|
|
132
|
+
export function localJudgments(file) {
|
|
133
|
+
const path = localJudgmentsFile(file);
|
|
134
|
+
if (!existsSync(path) || !statSync(path).isFile())
|
|
135
|
+
return [];
|
|
136
|
+
const id = readDocument(file).id;
|
|
137
|
+
return readFileSync(path, 'utf8').split('\n').filter(Boolean).flatMap(line => { try {
|
|
138
|
+
return [JSON.parse(line)];
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return [];
|
|
142
|
+
} })
|
|
143
|
+
.filter(j => j && j.file === basename(file) && (typeof id !== 'string' || !j.method_id || j.method_id === id));
|
|
144
|
+
}
|
|
@@ -38,5 +38,5 @@ export async function localSetup(file, flags, base) {
|
|
|
38
38
|
if (method.tools && Object.keys(method.tools).length)
|
|
39
39
|
config.tools = { ...config.tools, ...method.tools };
|
|
40
40
|
validateConfig(config);
|
|
41
|
-
return { config: browserConfig(method, config), sourceRoot
|
|
41
|
+
return { config: browserConfig(method, config), sourceRoot };
|
|
42
42
|
}
|
|
@@ -22,6 +22,8 @@ export declare class MethodClient {
|
|
|
22
22
|
onProgress?: (sent: number) => void;
|
|
23
23
|
attempts?: number;
|
|
24
24
|
}): Promise<Uint8Array>;
|
|
25
|
+
/** A lost connection: code network, and the message says which server. */
|
|
26
|
+
private unreachable;
|
|
25
27
|
/** Send bytes in chunks and read the answer in chunks; a watchdog aborts when no bytes moved for stallMs. */
|
|
26
28
|
private send;
|
|
27
29
|
login(openBrowser?: (url: string) => void): Promise<void>;
|
|
@@ -75,7 +75,7 @@ export class MethodClient {
|
|
|
75
75
|
continue;
|
|
76
76
|
}
|
|
77
77
|
if (lost)
|
|
78
|
-
throw
|
|
78
|
+
throw this.unreachable(method, path, error);
|
|
79
79
|
throw error;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -109,11 +109,15 @@ export class MethodClient {
|
|
|
109
109
|
}
|
|
110
110
|
catch (error) {
|
|
111
111
|
if (attempt >= (options.attempts ?? 3) || error.retryable === false)
|
|
112
|
-
throw error;
|
|
112
|
+
throw error?.name === 'TypeError' ? this.unreachable(data ? 'PUT' : 'GET', path, error) : error;
|
|
113
113
|
await new Promise(resolve => setTimeout(resolve, 500 * 2 ** (attempt - 1)));
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
+
/** A lost connection: code network, and the message says which server. */
|
|
118
|
+
unreachable(method, path, error) {
|
|
119
|
+
return Object.assign(Error(`Method cannot be reached at ${this.server} (${method} ${path}): ${error.cause?.code ?? error.message}. Check the connection and run again.`), { code: "network" });
|
|
120
|
+
}
|
|
117
121
|
/** Send bytes in chunks and read the answer in chunks; a watchdog aborts when no bytes moved for stallMs. */
|
|
118
122
|
async send(path, method, options) {
|
|
119
123
|
const stallMs = options.stallMs ?? 15_000, controller = new AbortController();
|
|
@@ -12,7 +12,7 @@ export declare const commandHelp: Record<string, Command>;
|
|
|
12
12
|
export declare function renderCommand(name: string, includeCommon?: boolean): string;
|
|
13
13
|
export declare const guideTopics: readonly ["start", "concepts", "execution", "examples", "example", "recipes", "recovery", "commands"];
|
|
14
14
|
export declare function authoringGuide(topic?: string, exampleId?: string): string;
|
|
15
|
-
export declare const overviewHelp = "Method \u2014 author, save and run methods with your coding agent.\n\nBefore creating, editing, or proposing a Method, run `method authoring` and read its guidance. Do this before choosing an existing Method as a reference.\n\nLearn: method authoring Start the installed guide.\n method authoring all Read the full manual offline.\n method COMMAND --help Read arguments, results and examples.\n method help step add Read help for a subcommand.\n method schema [TYPE] Read a formal JSON schema.\n\nLocal drafts: init, show, set, remove, step add/update/remove/move,\n check set/remove, validate, diff.\nOnline documents: list, get, steps, step, publish, explain.\nResults and improvements: judge, changes
|
|
15
|
+
export declare const overviewHelp = "Method \u2014 author, save and run methods with your coding agent.\n\nBefore creating, editing, or proposing a Method, run `method authoring` and read its guidance. Do this before choosing an existing Method as a reference.\n\nLearn: method authoring Start the installed guide.\n method authoring all Read the full manual offline.\n method COMMAND --help Read arguments, results and examples.\n method help step add Read help for a subcommand.\n method schema [TYPE] Read a formal JSON schema.\n\nLocal drafts: init, show, set, remove, step add/update/remove/move,\n check set/remove, validate, diff.\nOnline documents: list, get, steps, step, publish, explain.\nResults and improvements: judge, changes.\nKeys for scripts: secret find, secret import, secret set, secret list.\nExecution and evidence: run, run-status, wait, cancel, runs, logs, sync, observe.\nEffects and corrections: effect add/list, case new/retire/list, test.\nInputs and shared state: bind, state.\nBrowser and computer setup: browser connect, config.\nAccount access: status, login, logout, devices, revoke.\nProduction runs: connect, answer, keys create/list/revoke, worker.\n\nSign in with method login first. Read the authoring guide, give each prompt its own step, write the files, validate, and run. Each signed-in run saves a version when the file changed. Publish a version when the user wants to share or schedule it.\nUse method run task.method to run it.\nOnline server: https://app.withmethod.ai. Use --server only to select another\nserver. Sign-in uses browser approval. Never send credentials in chat.\n";
|
|
16
16
|
/** Resolve help before parsing flags, creating a client, or touching any draft. */
|
|
17
17
|
export declare function methodHelp(args: string[]): string | undefined;
|
|
18
18
|
/** The CLI and published reference use this same command inventory. */
|
|
@@ -6,10 +6,10 @@ const commonErrors = "File commands require readable YAML or JSON. Editing comma
|
|
|
6
6
|
const valueFlags = "Use exactly one: --json JSON, --value-file FILE (YAML or JSON), --text TEXT, --text-file FILE (UTF-8 text). Prefer files for long text.";
|
|
7
7
|
export const commandHelp = {
|
|
8
8
|
'browser connect': { usage: 'method browser connect [--name NAME] [--cdp URL]', purpose: 'Select a private browser connection.', arguments: 'NAME defaults to default. --cdp attaches to a Chrome session that permits remote control. Without --cdp, Method runs headless. On macOS it copies your last-used Chrome profile to reuse sign-ins. Use method-browser:NAME for a named browser binding.', result: 'Saves the browser selection on this computer. No browser is started by this command.', errors: 'Invalid name or endpoint. Credentials must not be in the URL.', example: 'method browser connect' },
|
|
9
|
-
doctor: { usage: "method doctor
|
|
9
|
+
doctor: { usage: "method doctor", purpose: "Check Node and the installed local agents without running a Method.", arguments: "Checks each installed Codex or Claude Code agent and says which one runs the steps that name no model. Use validate FILE for a Method's own dependencies.", result: "Setup findings; no task execution.", errors: "An old Node.js.", example: "method doctor" },
|
|
10
10
|
inspect: { usage: "method inspect RUN_DIRECTORY --out FILE", purpose: "Export saved execution evidence.", arguments: "Use a new output file. --include-files attaches declared result files.", result: "Saved inspection JSON.", errors: "Missing run or existing output.", example: "method inspect runs/example --out inspection.json" },
|
|
11
11
|
prompt: { usage: "method prompt FILE", purpose: "Print the Method as plain-text instructions.", arguments: "Current Method file.", result: "Text; no execution.", errors: "Invalid document.", example: "method prompt task.method" },
|
|
12
|
-
config: { usage: "method config
|
|
12
|
+
config: { usage: "method config\nmethod config model-key [--off]", purpose: "Show or change this computer's settings.", arguments: "Settings live in ~/.config/method/computer.json (private): bindings (method bind) and model_key_env. model-key imports OPENROUTER_API_KEY from the one nearby key file that holds it, or else opens a private browser form for it (the value stays in this computer's secret store), and then sends every hosted model step and every classify step on this computer to OpenRouter with your key, in place of the account's model credit; the run prints Using your own OpenRouter key. Those requests keep Method's private options: no provider that trains on the data, and zero data retention. --off goes back to the account. Run limits belong to the Method (limits:). Nothing goes into a Method file.", result: "The settings, or one line.", errors: "Unknown setting.", example: "method config model-key" },
|
|
13
13
|
"new-id": { usage: "method new-id FILE", purpose: "Make a copied Method file a new Method.", arguments: "Writes a new id: line. A copy that keeps its id: saves to the same Method.", result: "JSON with file and method_id.", errors: "Missing file.", example: "method new-id copy.method" },
|
|
14
14
|
bind: { usage: "method bind FILE_OR_ID NAME (--file FOLDER | --connection URL) [--upload]", purpose: "Save a named input location or connection.", arguments: "Default: this computer only, in ~/.config/method/computer.json under the Method's ID (a FILE gets its id: line). A folder named NAME beside the Method needs no binding. --upload saves only the selected input folder or connection URL in the account. It never scans your disk. Keep credentials in the service's normal sign-in store.", result: "Saved binding and scope.", errors: "Missing input, invalid name, or unsafe path.", example: "method bind METHOD_ID prepared_day --file day-records --upload", remote: true },
|
|
15
15
|
state: { usage: "method state ID [--enable --file state.json | --release RUN_ID]", purpose: "Read or explicitly enable shared account state.", arguments: "Accepted state updates use revision checks. Inspect a stopped run's external actions before releasing its ownership.", result: "Current revision, value, and owner run.", errors: "Concurrent ownership or stale revision. Existing state is never silently overwritten.", example: "method state METHOD_ID --enable --file initial-state.json", remote: true },
|
|
@@ -36,10 +36,8 @@ export const commandHelp = {
|
|
|
36
36
|
get: { usage: "method get WORKFLOW_ID [--version VERSION_ID] [--out FILE] [--server URL]", purpose: "Read an online method; optionally make a local draft for editing.", arguments: "Default: latest version. --out must name a new file. Use a version ID to read an exact saved version.", result: "Without --out: {workflow_id,version_id,version_number,workflow}. With --out: {workflow_id,version_id,version_number,file}; writes the workflow with its id: line and the saved files.", errors: "Missing method or version; output file already exists.", example: "method get wf_example --out edit.method", remote: true },
|
|
37
37
|
explain: { usage: "method explain FILE [--step ID] [--no-round-trip] [--server URL]", purpose: "Explain each script step of the saved version in a script card.", arguments: "FILE is a local Method whose current content is a saved version (run or publish it while signed in). For each run step whose script has no card, code analysis finds the hosts, secrets, environment variables, files, and commands the script uses; a hosted model writes a one-sentence summary and 3 to 8 steps; every number, quoted name, and host in that text must appear in the code (one retry). The round trip writes a program from the steps alone and replays up to 3 recorded inputs of the step from local runs through both programs without network access. A script that calls the network is not replayed. --step explains one step. --no-round-trip skips the replay. A signed-in run starts this in the background; publish waits for it.", result: "One line per step with its summary and check results. Cards upload to the version; the dashboard shows them beside each script. A failed check is shown on the card and never blocks publish.", errors: "No saved version, local files that differ from the saved version, or no hosted-model credit. Without sign-in it prints one line and makes no card.", example: "method explain leads.method --step score", remote: true },
|
|
38
38
|
publish: { usage: "method publish FILE [--reason TEXT] [--accept-failing-case ID[,ID]] [--cloud|--workers] [--env NAME] [--server URL]", purpose: "Mark a version as published, for sharing, schedules, and production runs.", arguments: "Runs the Method's cases first; a version that breaks an approved case is refused unless --accept-failing-case names it, and the version records that. Uses the version of the file's current content: it finishes that upload (no time limit while bytes move), checks the cases, then marks it published with the reason (default: Published.). --accept-failing-case needs --reason. The first save of a new file creates the Method. --cloud runs the production runs of the environment on Method Cloud; --workers runs them on your workers again. --env NAME selects the environment (default production).", result: "JSON with workflow_id, version_id, url, published_at, the cases line, and the open warnings and notes (issues). With --cloud or --workers: placement. Warnings and notes never block a publish.", errors: "An error issue (nothing is published), failing case, --accept-failing-case without --reason, or no network.", example: "method publish task.method --reason 'Shorter introduction'", remote: true },
|
|
39
|
-
judge: { usage: "method judge RUN_ID right|wrong [--step STEP_ID] [--note TEXT] [--corrected FILE] [--json] [--server URL]", purpose: "Record the person's reaction to a run's result.", arguments: "RUN_ID is the run_id of the run's start line, or the run ID on the dashboard. right: the person used or approved the result. wrong: the person corrected it, or asked to change the Method because of it. --note: the person's words (at most 4000 characters). --corrected FILE: the result that the person says is right; JSON is sent as JSON, other text as text (at most 1 MB). --step: the step whose output was wrong. A wrong judgment
|
|
40
|
-
changes: { usage: "method changes FILE [--json] [--server URL]", purpose: "List the changes that Method applied to a Method.", arguments: "Lists the changes that
|
|
41
|
-
improve: { usage: "method improve FILE [--note TEXT] [--step ID] [--server URL]", purpose: "Improve a Method that keeps its run content on this computer.", arguments: "For a Method with run_data: device, or in an organization that keeps run content on devices: runs Method's improvement on this computer, so the run content stays here. --note: the correction, in the person's words; it is about the newest run of the Method on this computer, which the improvement reads. --step: improve only this step. For a Method that keeps run content in the account, it starts nothing and says to use method judge: Method fixes those Methods from the person's judgments.", result: "JSON with the local proposal ID and the next step; the proposal is in .method/proposals/. Nothing is published, and the file does not change. For an account Method, one line.", errors: "An unknown step, or a step of the improvement that fails.", example: "method improve task.method --step summary --note 'Name the customer in the first sentence.'", remote: true },
|
|
42
|
-
apply: { usage: "method apply FILE [PROPOSAL_ID] [--resolved] [--server URL]", purpose: "Merge a local proposal from method improve into the file.", arguments: "Default: the newest open local proposal. The merge is per step: a part that only the proposal changed takes the change; a part that you and the proposal both changed is a conflict. --resolved: you merged a conflict by hand; this marks the proposal applied. When a change that Method applied conflicts with your edits, the line before the command names it; give its ID to see both versions.", result: "JSON with status applied and the changed steps; the file changes and the proposal is marked applied. Nothing is published. A conflict lists each part with the base, your, and the proposed value, changes nothing, and exits 1.", errors: "No open proposal, or a conflict.", example: "method apply task.method", remote: true },
|
|
39
|
+
judge: { usage: "method judge RUN_ID right|wrong [--step STEP_ID] [--note TEXT] [--corrected FILE] [--json] [--server URL]", purpose: "Record the person's reaction to a run's result. A wrong one fixes the Method on this computer.", arguments: "RUN_ID is the run_id of the run's start line, or the run ID on the dashboard. right: the person used or approved the result; it is recorded only. wrong: the person corrected it, or asked to change the Method because of it. --note: the person's words (at most 4000 characters), even when they are vague. --corrected FILE: the result that the person says is right; JSON is sent as JSON, other text as text (at most 1 MB). --step: the step whose output was wrong. A wrong judgment then fixes the Method in the foreground, with your own Claude Code or Codex (hosted models only when neither is signed in): it records the correction as a case in cases/ next to the Method file, finds the step that causes it, tests up to three small changes against this run, every earlier corrected or approved run, and every case, and changes the file only when a change fixes this run and breaks no earlier correction. The change is saved as a new version; restoring the version before it on the Method page undoes it. It takes a few minutes. Corrections that wait (from the dashboard, or a fix that stopped) are fixed in the same loop. For a run whose content stays on this computer (run_data: device), the account gets only the verdict and the step; the note and the corrected result go in .method/judgments.jsonl next to the Method file. Run it in the folder of the Method file when the run came from an older CLI. Do not ask the person to confirm a judgment, and do not edit the Method yourself for the same correction.", result: "One line: what changed and how many earlier corrections it passes, with how to undo it; or No safe change found and the reason. A subscription limit or another failure of the model service is an error, and the correction waits for the next method judge or method run. --json prints the judgment and the fix (kind change or none, cause, steps, cases made, version, usage with null dollars on a subscription, what the replay caps left out, and the log file).", errors: "An unknown run, a run of a Method that is not in your account, no sign-in, or a failure of the model service.", example: "method judge 6f1c… wrong --note \"it's not even redlined\"", remote: true },
|
|
40
|
+
changes: { usage: "method changes FILE [--json] [--server URL]", purpose: "List the changes that Method applied to a Method.", arguments: "Lists, newest first, the changes that method judge applied on this computer and the changes that Method made in the account. Commands that take a signed-in FILE merge a change made in the account into the file first and print one line.", result: "One line per change: its date, its version, and its cause. --json prints {file, method_id, changes: [{id, source, cause, date, version_id, steps}]}. No changes.", errors: "No id: line and no change made on this computer.", example: "method changes task.method", remote: true },
|
|
43
41
|
secret: { usage: "method secret find [FILE]\nmethod secret import FILE NAME...\nmethod secret set NAME\nmethod secret list [NAME...]", purpose: "Give this computer the values of a Method's declared secrets.", arguments: "find lists the KEY=VALUE files in this folder (3 levels down) and in the parent folder (2 levels down) with the key names in each, never values; for the Method FILE (or the only Method here) it says which declared secrets each file holds and prints the import command. import copies the named values from a KEY=VALUE file that the user names, without printing them. set opens a private form on 127.0.0.1 in the browser for one value. list shows names and where each value is found (shell, this computer, or missing), never values. Values are kept in ~/.config/method/secrets.json (mode 0600) and are never sent to Method. A value exported in the shell is used first. Every script of the Method receives all of its declared secrets.", result: "JSON with the saved names, or the list.", errors: "A name is missing from the file, or no value was entered.", example: "method secret find\nmethod secret import ../service/.env ARCHIVE_TOKEN" },
|
|
44
42
|
connect: { usage: "method connect [APP_FOLDER] [--file METHOD_FILE]", purpose: "Connect an app to the Method in this folder, so the app runs it in production.", arguments: "Run it in the Method's folder. APP_FOLDER (default: this folder) is the app's folder. It makes a service key named after the app folder and writes METHOD_API_KEY to the app's .env (a new file, or a new or replaced line); it never prints the key. It adds .env to .gitignore in a git repository. When the Method has no published version, it publishes the current file. It detects Node (package.json) or Python (pyproject.toml, requirements.txt), and prints the install line and the code to add. Run it again: an app with a working key keeps it.", result: "JSON with the method_id, the published version, where the key is, install, code, secrets_to_set_on_the_host, and next.", errors: "No or several .method files (give --file), an app folder with no language marker, or a failing case at publish.", example: "method connect ../app", remote: true },
|
|
45
43
|
answer: { usage: "method answer RUN_ID [--answer JSON]", purpose: "Show or answer the question of a production run that waits on an ask step.", arguments: "Without --answer it prints the question and its form (the fields of the answer). With --answer it checks the answer against the form and sends it; the run continues. A run that keeps its content on devices has a sealed question: run this where the app's METHOD_API_KEY is (the environment, or the .env of this folder), or on the laptop that started the run with its sign-in. Local runs (method run FILE) take answers with --human FILE as before.", result: "The question and form, or the run after the answer.", errors: "The run is not waiting, the answer does not match the form, or the key cannot open a sealed question (wrong_key).", example: "method answer 6f1c… --answer '{\"approved\":true}'", remote: true },
|
|
@@ -56,22 +54,22 @@ export const commandHelp = {
|
|
|
56
54
|
runs: { usage: "method runs [--method WORKFLOW_ID] [--server URL]", purpose: "Read saved run summaries.", arguments: "Default: all methods. --method filters the list.", result: "Server JSON containing runs. No changes.", errors: "", example: "method runs --method wf_example", remote: true },
|
|
57
55
|
logs: { usage: "method logs RUN_ID [--server URL]", purpose: "Read saved run evidence before a repair.", arguments: "Use a run ID from method runs. Saved logs may be incomplete if upload failed.", result: "Server JSON containing the run, inputs, outputs, checks and events. No execution or changes.", errors: "Missing run.", example: "method logs run_example > run.json", remote: true },
|
|
58
56
|
sync: { usage: "method sync RUN_DIRECTORY [--server URL]", purpose: "Retry upload of records from an existing local run.", arguments: "Directory must contain method-sync.json. Default destination is the saved run's server. Do not use a new business run to repair an upload.", result: "Upload status text. Updates dashboard records and local sync metadata. Does not execute steps.", errors: "Missing run/sync records; access or network error. Keep the original run directory and retry.", example: "method sync .method-runs/wf_example/saved-run", remote: true },
|
|
59
|
-
run: { usage: "method run FILE.method [OPTIONS]\nmethod run WORKFLOW_ID [--version VERSION_ID] [--server URL] [OPTIONS]", purpose: "Run a local .method file, or a saved Method by ID. When this computer is signed in, a run of a local file starts at once and saves in the background: the version of the file's content and the run's records go to the dashboard through the outbox (~/.cache/method/outbox), and the last line says Saved as version N, or Will save when online; without sign-in the records stay in .method-runs. Steps whose definition and inputs match an earlier accepted run on this computer are reused.", arguments: `Models come from the Method's models: (or the account default)
|
|
57
|
+
run: { usage: "method run FILE.method [OPTIONS]\nmethod run WORKFLOW_ID [--version VERSION_ID] [--server URL] [OPTIONS]", purpose: "Run a local .method file, or a saved Method by ID. When this computer is signed in, a run of a local file starts at once and saves in the background: the version of the file's content and the run's records go to the dashboard through the outbox (~/.cache/method/outbox), and the last line says Saved as version N, or Will save when online; without sign-in the records stay in .method-runs. Steps whose definition and inputs match an earlier accepted run on this computer are reused.", arguments: `Models come from the Method's models: (or the account default). --workspace selects a different helper folder. See method authoring execution. Optional --state FILE initializes state for a new run. Resume with --resume --run-dir DIR; authorize unfinished work with --retry STEP:ITERATION.
|
|
60
58
|
|
|
61
59
|
--inputs FILE: JSON input values.
|
|
62
60
|
--inputs-from RUN_ID: use the inputs of that run; --inputs FILE overrides single values.
|
|
63
61
|
--run-dir DIR: the folder for this run's records (new runs too; default .method-runs/ID).
|
|
64
|
-
--
|
|
65
|
-
--resume: continue the same saved run with its saved agent.
|
|
62
|
+
--resume: continue the same saved run with its saved models.
|
|
66
63
|
--rerun STEP: run this step again even when an earlier run can be reused. Repeat for more steps.
|
|
67
64
|
--fresh: run every step; reuse nothing.
|
|
68
65
|
--human FILE: answers for ask steps, as {steps: {"STEP": {outputs: {...}}}} (STEP:ITERATION, counted from 0, for a step with each or repeat).
|
|
69
66
|
--verbose: print runtime events.
|
|
67
|
+
Before a signed-in run of a FILE, corrections of the Method that wait (from the dashboard, or a fix that stopped) are fixed first, as method judge fixes them, with one line on stderr.
|
|
70
68
|
Use method doctor to check the installed Node and configured tools.`, result: "When signed in, the start line and the final result have dashboard: the link to the run's page; give it to the user when the run starts. Progress, then the final result as JSON on stdout; a run that stopped has failure (owner, headline, step, next), and a failed run also prints Failed, Whose problem, and Next lines on stderr. Local result.json and run evidence. The runtime executes the method's declared scripts, calls, agents, and checks. Executes trusted local processes; changes declarations do not enforce permissions. Runs exit 0 on completion, 1 on failure, 2 when human input is needed, and 3 when an observer could not confirm an external change (unconfirmed).", errors: "Missing inputs/access, failed check, timeout, unsafe resume/version mismatch, upload failure. See recovery. Never retry a business write without inspecting its saved changes.", example: "method run wf_example --version v_example --inputs inputs.json", remote: true },
|
|
71
69
|
observe: { usage: "method observe [RUN_DIRECTORY...] [--pending ROOT]...", purpose: "Make the effect observations that are due for finished runs.", arguments: "Without directories, checks runs with open effects under .method-runs and the Method cache. Runs only observers and judges; never repeats an action. Schedule it, for example hourly, so late evidence such as a bounce reaches the run.", result: "JSON with each run's new verdicts, status, and next observation time. A synced run whose status changed is uploaded again.", errors: "Locked run, changed bundle, or a missing observer credential.", example: "method observe --pending .method-runs" },
|
|
72
|
-
test: { usage: "method test FILE [--case ID]... [--baseline OLD_FILE] [--new ID]... [--cases DIR]
|
|
70
|
+
test: { usage: "method test FILE [--case ID]... [--baseline OLD_FILE] [--new ID]... [--cases DIR]", purpose: "Replay recorded cases against this version of a Method. Every case must pass.", arguments: "Cases are in cases/ beside the Method. Unchanged steps return their recorded outputs; changed steps run; files connections are scratch folders, so a changed step that writes files runs safely. A changed step that asks a person or acts on a service makes the case unverifiable. --baseline also runs each case on the old version, to show what the change fixed or broke.", result: "JSON report with a verdict per case and passed:true when no case blocks the change. Exit 0 or 1.", errors: "Unknown case, invalid Method, or setup errors.", example: "method test task.method --baseline task-before.method --new bounce-reported" },
|
|
73
71
|
case: { usage: "method case new|retire|list FILE ...", purpose: "Keep a correction as a recorded case that every later version must pass.", arguments: "Use method help case new, method help case retire, or method help case list.", result: "See the subcommand.", errors: "Unknown action. Choose new, retire, or list.", example: "method help case new" },
|
|
74
|
-
"case new": { usage: "method case new FILE --id ID --note TEXT (--run BAD_RUN | --passing-run GOOD_RUN | both) (--rubric SENTENCE... | --expect FILE) [--ref outputs.NAME] [--context REF]... [--accept-conflict TEXT]
|
|
72
|
+
"case new": { usage: "method case new FILE --id ID --note TEXT (--run BAD_RUN | --passing-run GOOD_RUN | both) (--rubric SENTENCE... | --expect FILE) [--ref outputs.NAME] [--context REF]... [--accept-conflict TEXT]", purpose: "Turn a correction into a recorded case.", arguments: "--run is the run that went wrong; the case must fail on it. --passing-run is the run the person accepted after the fix; the case must pass on it. With only --passing-run, the case pins behaviour that is already right. --rubric is a plain sentence that must be true of the output (repeatable); a model judges it with quotes. --ref selects the output to judge; the default is the Method's result. --context REF gives the judge other values to check against, such as the sources or the person's words (outputs.NAME or inputs.NAME); they are read, not judged. --expect FILE gives exact checks instead: {kind: equals, ref: outputs.NAME, value}, {kind: status, in: [STATUS]}, {kind: effect, effect: STEP/ITERATION/NAME, verdict: [VERDICT]}, or {kind: predicate, runtime: node, entrypoint: check.mjs}. --redact FILE maps recorded text to replacements. --accept-conflict TEXT keeps a case that conflicts with an instruction of the Method, with the person's answer.", result: "The saved case, with its result on each run. A case that the bad run already meets is refused: it does not capture the problem, or the note does not match the run. A case that can pass only by breaking an instruction of the Method's model steps is refused (case_conflicts): the error names the rule and the instruction.", errors: "A case that does not fail on the bad run, or does not pass on the passing run; a case that conflicts with an instruction; an existing ID; an unreadable run.", example: "method case new report.method --id sources-named --run .method-runs/bad --passing-run .method-runs/fixed --note 'Say which source backs each point.' --rubric 'Every point names the source file that supports it.'" },
|
|
75
73
|
"case retire": { usage: "method case retire FILE ID --reason TEXT [--by NEW_ID]", purpose: "Retire a case whose rule no longer applies.", arguments: "The case stays on disk with its reason and is no longer run. Use it when a policy changed, not to make a failing change pass.", result: "JSON {retired}.", errors: "Unknown or already retired case.", example: "method case retire task.method old-terms --by new-terms --reason 'Payment terms changed on 1 October.'" },
|
|
76
74
|
"case list": { usage: "method case list FILE", purpose: "List the cases of a Method.", arguments: "Reads cases/ beside the Method unless --cases DIR is given.", result: "JSON list of cases with status and note.", errors: "", example: "method case list task.method" },
|
|
77
75
|
effect: { usage: "method effect add|list ...", purpose: "Use a reviewed observer for an external change.", arguments: "Use method help effect add or method help effect list.", result: "See the subcommand.", errors: "Unknown action. Choose add or list.", example: "method effect list" },
|
|
@@ -254,13 +252,13 @@ destination and the rule used.”
|
|
|
254
252
|
const execution = `
|
|
255
253
|
# Execution setup
|
|
256
254
|
|
|
257
|
-
Use method run FILE_OR_ID [--inputs inputs.json] [--inputs-from RUN_ID] [--state state.json] [--run-dir DIR] [--workspace HELPERS_FOLDER] [--
|
|
258
|
-
A Method needs no configuration file. What changes the result is in the Method: models, limits, tools, and secrets (the names). What belongs to this computer is in ~/.config/method/computer.json: method config (
|
|
255
|
+
Use method run FILE_OR_ID [--inputs inputs.json] [--inputs-from RUN_ID] [--state state.json] [--run-dir DIR] [--workspace HELPERS_FOLDER] [--background].
|
|
256
|
+
A Method needs no configuration file. What changes the result is in the Method: models, limits, tools, and secrets (the names). What belongs to this computer is in ~/.config/method/computer.json: method config (own model key) and method bind (folders and connections). python and node are found on PATH and prepared automatically. A saved version carries its helpers; versions saved without files still need --workspace DIR.
|
|
259
257
|
|
|
260
258
|
## Models
|
|
261
259
|
|
|
262
|
-
A call or agent step names model: NAME from the Method's models: ({NAME: provider/model | {model, max_output_tokens, reasoning_effort}}), a model ID such as openai/gpt-6-luna, or default. A step without model uses default: the account's default model. Hosted models need no key and no local agent. They are private by default: requests go only to providers that do not train on them and keep no copy (zero data retention). method models lists those models; validate warns with model_not_private when a step names another, and hosted runs refuse it. Hosted models and classification share the account's model credit; the run summary reports usage.cost_usd. When the credit is used, run method config model-key: hosted model and classify steps on this computer then call OpenRouter with the user's own key.
|
|
263
|
-
A models: entry {agent: codex | claude, model, reasoning_effort} runs the steps that name it with that local agent, which must be installed on the computer that runs the Method.
|
|
260
|
+
A call or agent step names model: NAME from the Method's models: ({NAME: provider/model | {model, max_output_tokens, reasoning_effort}}), a model ID such as openai/gpt-6-luna, or default. A step without model uses default: the user's own subscription first, which is the Claude Code or Codex that started the run, else an installed and signed-in Claude Code or Codex (Claude Code when both are); the account's default hosted model on account credit only when no local agent is signed in. If the local agent fails, for example at a usage limit, the step fails like any other outside service; run again later. Each step's run record names the backend and model that ran it (model.used). Hosted models need no key and no local agent. They are private by default: requests go only to providers that do not train on them and keep no copy (zero data retention). method models lists those models; validate warns with model_not_private when a step names another, and hosted runs refuse it. Hosted models and classification share the account's model credit; the run summary reports usage.cost_usd. When the credit is used, run method config model-key: hosted model and classify steps on this computer then call OpenRouter with the user's own key.
|
|
261
|
+
A models: entry {agent: codex | claude, model, reasoning_effort} runs the steps that name it with that local agent, which must be installed on the computer that runs the Method. A step that names a model keeps it; no flag or computer setting overrides the Method's models:. Declared tools reach the agent through a temporary local MCP connection; no persistent agent configuration is edited. The model choice of a run stays fixed on resume.
|
|
264
262
|
|
|
265
263
|
## Classification
|
|
266
264
|
|
|
@@ -300,13 +298,12 @@ const recipes = `# Recipes
|
|
|
300
298
|
## Corrections become cases
|
|
301
299
|
|
|
302
300
|
When the user says a result was wrong:
|
|
303
|
-
1. Record it: method judge RUN_ID wrong --note "the user's words" (--corrected FILE when they gave the right result). Do not ask them to confirm it
|
|
304
|
-
2.
|
|
305
|
-
3.
|
|
306
|
-
4.
|
|
307
|
-
5.
|
|
308
|
-
6.
|
|
309
|
-
7. Run method test FILE. Every case must pass. method publish refuses a version that breaks a case.
|
|
301
|
+
1. Record it: method judge RUN_ID wrong --note "the user's words" (--corrected FILE when they gave the right result). Use their words even when they are vague. Do not ask them to confirm it, and do not edit the Method for the same correction: method judge records the correction as a case, finds a change with the user's own agent, and applies it only when it fixes the run and breaks no earlier correction. It takes a few minutes.
|
|
302
|
+
2. Tell the user the line it prints in plain words, then run the Method again and show the new result.
|
|
303
|
+
3. When it prints No safe change found, tell the user the reason. If the user's words do not say what the right result is, ask before you change anything. Name the readings that the run and the Method's instructions allow, and what each one would change, in one question. A complaint about a missing part of the output can mean "produce it from what the inputs give", "mark what is missing", or "ask for it"; a complaint about wrong content can mean a wrong source, a wrong rule, or a wrong reading of the input. Do not choose a reading yourself, and never one that breaks the user's instructions. Then record the user's answer with method judge RUN_ID wrong --note "their answer".
|
|
304
|
+
4. For a rule the user states outside a run, record it: method case new FILE --id ID --run BAD_RUN --passing-run GOOD_RUN --note "the user's words" --rubric "plain sentence that must be true" (repeat --rubric for each rule). --ref names the output to judge (default: the Method's result; a path to a file that the run saved is judged by that file's contents). When a rule compares the output with the sources, add --context with the step output that holds them, for example --context outputs.material. Make each sentence fail on the bad run; case new warns about one that does not. For an exact value, use --expect instead.
|
|
305
|
+
5. case new refuses a case that can pass only by breaking one of the Method's instructions (case_conflicts), for example a rule that needs a fact that the inputs do not give when an instruction says not to invent facts, or a rule that requires what an instruction forbids. Show the user the rule and the instruction, and ask which one is right. When the user says the case is right, change the instruction first and record the case again. Add --accept-conflict "the user's answer" only when the user wants both.
|
|
306
|
+
6. After any edit by hand, run method test FILE. Every case must pass. method publish refuses a version that breaks a case.
|
|
310
307
|
For a one-time preference, edit and run again; a case is not needed.
|
|
311
308
|
|
|
312
309
|
## Checked on every run, or on every new version
|
|
@@ -374,7 +371,7 @@ Learn: method authoring Start the installed guide.
|
|
|
374
371
|
Local drafts: init, show, set, remove, step add/update/remove/move,
|
|
375
372
|
check set/remove, validate, diff.
|
|
376
373
|
Online documents: list, get, steps, step, publish, explain.
|
|
377
|
-
Results and improvements: judge, changes
|
|
374
|
+
Results and improvements: judge, changes.
|
|
378
375
|
Keys for scripts: secret find, secret import, secret set, secret list.
|
|
379
376
|
Execution and evidence: run, run-status, wait, cancel, runs, logs, sync, observe.
|
|
380
377
|
Effects and corrections: effect add/list, case new/retire/list, test.
|
|
@@ -49,6 +49,35 @@ async function dispatchRunWorker(args, flags, extra = [], savedClient) {
|
|
|
49
49
|
await startRunWorker(workerArgs, directory, flags.background, server);
|
|
50
50
|
return true;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Before method run FILE: corrections that wait for this Method (from the dashboard, or a loop that stopped) are fixed
|
|
54
|
+
* first, with one line about them. A failure of the loop is one line too; the run goes on. Signed out, offline, a
|
|
55
|
+
* resumed run, or the run worker: nothing.
|
|
56
|
+
*/
|
|
57
|
+
async function fixWaitingCorrections(args, clientFactory) {
|
|
58
|
+
if (process.env.METHOD_RUN_WORKER === '1' || args.includes('--resume') || !existsSync(args[1]))
|
|
59
|
+
return;
|
|
60
|
+
const server = parseArgs({ args: args.slice(2), strict: false, options: { server: { type: 'string' } } }).values.server;
|
|
61
|
+
let client;
|
|
62
|
+
try {
|
|
63
|
+
client = clientFactory(typeof server === 'string' ? server : DEFAULT_SERVER);
|
|
64
|
+
if (!client.token())
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const { fixCorrections, waitingLine } = await import('./improve.js');
|
|
71
|
+
try {
|
|
72
|
+
const fix = await fixCorrections(client, authoringPath(args[1]), { waitMs: 3000 });
|
|
73
|
+
if (fix)
|
|
74
|
+
process.stderr.write(waitingLine(fix) + '\n');
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (!/^(fetch failed|401:|404:)|abort|timed? ?out/i.test(error.message))
|
|
78
|
+
process.stderr.write(`The waiting corrections are not fixed yet: ${error.message}\n`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
52
81
|
export async function methodMain(args = process.argv.slice(2), clientFactory = server => new MethodClient(server)) {
|
|
53
82
|
const resumed = (await import('./resume-release.js')).resumeWithOriginalRelease(args);
|
|
54
83
|
if (resumed !== undefined) {
|
|
@@ -64,13 +93,17 @@ export async function methodMain(args = process.argv.slice(2), clientFactory = s
|
|
|
64
93
|
}
|
|
65
94
|
if (args[0] === 'judge')
|
|
66
95
|
return (await import('./judge.js')).judgeCommand(args.slice(1), clientFactory);
|
|
67
|
-
if (
|
|
68
|
-
return (await import('./proposals.js')).
|
|
96
|
+
if (args[0] === 'changes')
|
|
97
|
+
return (await import('./proposals.js')).changesMain(args.slice(1), clientFactory);
|
|
69
98
|
await (await import('./proposals.js')).autoApply(args, clientFactory);
|
|
99
|
+
if (args[0] === 'run' && /\.method$/i.test(args[1] ?? '') && !args.includes('--help'))
|
|
100
|
+
await fixWaitingCorrections(args, clientFactory);
|
|
70
101
|
if (args[0] === 'worker' || args[0] === 'keys')
|
|
71
102
|
return (await import('./worker.js')).productionCommand(args, clientFactory);
|
|
72
103
|
if (args[0] === '__run-data')
|
|
73
104
|
return (await import('./run-data.js')).runDataCommand(args.slice(1));
|
|
105
|
+
if (args[0] === '__model')
|
|
106
|
+
return (await import('./model-call.js')).modelCallCommand();
|
|
74
107
|
if (args[0] === 'connect' || args[0] === 'answer') {
|
|
75
108
|
const server = parseArgs({ args: args.slice(1), strict: false, options: { server: { type: 'string' } } }).values.server;
|
|
76
109
|
const client = clientFactory(typeof server === 'string' ? server : DEFAULT_SERVER);
|