@method-ai-hq/sdk 0.17.0 → 0.17.2

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  MIT-licensed JavaScript API and the `method` CLI. Source and releases: https://github.com/method-ai-hq/method-sdk
4
4
 
5
5
  ```sh
6
- npm install https://github.com/method-ai-hq/method-sdk/releases/download/v0.17.0/method-ai-hq-sdk-0.17.0.tgz
6
+ npm install https://github.com/method-ai-hq/method-sdk/releases/download/v0.17.2/method-ai-hq-sdk-0.17.2.tgz
7
7
  npx method --version
8
8
  ```
9
9
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@method-ai-hq/sdk",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
4
  "description": "Build and run reusable .method files with checked steps and saved results.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,7 +35,7 @@
35
35
  "prepack": "npm --prefix ../.. run build"
36
36
  },
37
37
  "dependencies": {
38
- "@withmethod/runtime": "https://codeload.github.com/method-ai-hq/method-spec/tar.gz/54461cb387d8f10813db05bb0cea29c0a3a53504",
38
+ "@withmethod/runtime": "https://codeload.github.com/method-ai-hq/method-spec/tar.gz/5d9183f52b64af45d677a69d718bda9e1e0077ab",
39
39
  "fflate": "^0.8.3",
40
40
  "yaml": "^2.9.0",
41
41
  "zod": "^3.25.76"
@@ -5,9 +5,9 @@ export declare const designProcedure = "# Choose the design\n\n| Requirement | S
5
5
  /** The full check rules; in concepts. */
6
6
  export declare const checkRules = "# Checks\n\nAdd a check only when it catches a concrete failure that matters to the result. Output types and the runtime already validate shapes; do not repeat them. Do not check wording, headings, keywords, lengths, or counts unless the task requires them: \"write accurate prose\" is not a reason to match strings.\n\nUse the simplest check that proves the fact: a built-in equals, count, present, or file check, a script, or an agent check. Local file writes need no check: the runtime observes files connections itself. A change to a service, an API, email, or a browser that sends needs an effect that reads the result back, because a receipt or a 200 status shows only that the request was accepted. Use a built-in observer (http, sqlite, file) when one fits. When a change cannot or need not be observed, such as a browser step that only reads, write no_effect_reason instead.\n\nExisting checks and tests are implementation choices, not user requirements. Remove a check that is unnecessary, repeats other validation, or enforces a requirement that nobody asked for. Delete the tests and instructions that exist only for it, and do not replace it. Do not change useful output only to satisfy a check.\n";
7
7
  /** The rules that make a first Method work quickly. They come before every other part of the guide. */
8
- export declare const firstMethodRules = "# Build a Method\n\n1. **Sign in first.** Run `method status`. If it is not signed in, run `method login` and let the user approve in the browser. Signed in, model and classification steps need no keys, each run saves a version when the file changed, and runs appear on the dashboard.\n2. **Each model or classifier request is its own step**: `call`, `agent`, or `classify`, with its prompt in the Method, so the user can change one prompt, run again, and compare. A script never calls a model API. When the user already has code that does the work, keep its fixed logic as `run` steps and move each prompt and rubric into its own step; do not wrap the code. \"The same thing\" means the same behavior in steps. If a request cannot become a step because Method lacks a feature, tell the user what is missing.\n3. **Show the design, then build it in the same turn.** Show each step with its type, purpose, and output, and a table of the prompts and rubrics in the user's existing work, each with the step that holds it. Do not wait for approval unless the user asked to approve first. Ask only for information that you cannot find and that would change the design.\n4. **Run early and often.** Write the first steps, validate, run, then add the next steps. Fix errors. Fix each warning, or accept it with the user's reason. Never add a check only to remove a warning. A new run reuses every step whose definition and inputs did not change, so each run executes only what changed. `--rerun STEP` runs a step again anyway; `--fresh` runs every step.\n5. **Keep the inputs of the existing code.** If the code takes an ID and looks up the record, the Method takes the same ID and looks it up the same way.\n6. **Use the defaults without asking.** Model steps use the account's hosted models and `classify` uses Method's classifier, so no model or classifier keys are needed. To keep the model that existing code uses, add `models: {writer: provider/model}` to the Method and use `model: writer` in the steps (or `model: provider/model` on the step); it needs no key. Keep the models that the Method or the existing code names: when a model step fails, show the error and ask before you change its model. Run records go to the user's account. When the user asks that run data stay on this computer or not be uploaded, set `run_data: device`: the run's inputs, outputs, and files stay here, and the account keeps only each step's status and timing. Model steps still send each step's input to the model to answer it; nothing is kept. Tell the user that in one line, then run again.\n7. **Keys stay out of chat.** Declare each key that a script needs under `secrets:` with its purpose; every script of the Method receives all of its declared secrets. Run `method secret find` in the Method's folder: it lists the key files nearby and the names in each, never the values, and prints the import command. Run that command and tell the user which file each key came from: the key stays on this computer, so there is nothing to ask. If it finds nothing, ask the user to run `method secret set NAME`, which opens a private form in their browser. Never open, print, or search a key file (`cat`, `grep`, an editor), including Method's own secret store, and never ask for a value in chat: the values would go into the chat. When a run says Method's model credit is used up, run `method config model-key` yourself: it takes the user's own OpenRouter key from the one nearby key file that holds it, or opens a private form for it. Then run again.\n8. **Choose a sample yourself** from the user's data, and check that it has the sources the Method needs. Ask only when there is no good sample.\n9. **Iterate.** `method run` prints a `dashboard` link when the run starts and again with its result. Give the user the link as soon as the run starts. After a good run, show the result and the link, and ask what to change. Change the step and run again. Show only results that a run made: never write or edit a result by hand, and fix a wrong result in the Method, not in the data it reads. If you cannot run, say so.\n10. **Publish** with `method publish FILE --reason TEXT` when the user wants to share or schedule a version. It runs the Method's cases first.\n11. **Talk about the work, not about Method.** Unless the user asks, do not mention Method's versions, formats, settings files, or other internal files, and do not ask the user about them, about models, or about which saved version to use. Method updates itself and converts older Method files when it runs them. When the user asks whether a Method works, or asks you to make it work, run it with the inputs of a recent run (`--inputs-from RUN_ID`) and show the result and the dashboard link in a few sentences. Say that it works only after a run completed.\n12. **One file for each Method.** Change and run a Method in its own file. Do not copy it to a temporary or other folder to change it: Method does not run or save the copy while the Method's file exists, and names that file.\n13. **Questions in a run are for the user.** When a run stops with `needs_input`, ask the user its `question` in plain words, wait for the answer, and continue the run with it as the output says. Never answer it yourself.\n14. **Make each run easy to find and to run again.** Give the Method a `run_label` that tells its runs apart, such as `inputs.topic`. When an input can be empty and a default picks the value, point `run_label` at the step output that holds the resolved value, not at the input. Put the readable output, such as the report text or the message, in `result`, not only in files. Write a `run_prompt` that says where each input comes from (for example \"as_of is today's date in YYYY-MM-DD\") and what to show at the end. Leave SDK, runtime, and version details out of it.\n15. **Run again from the dashboard.** When the user pastes a \"Run again\" prompt from the dashboard, run with `--inputs-from RUN_ID` and change only what the prompt names: put those values in a file and add `--inputs FILE`.\n\nWhen a run fails, read failure.headline and failure.next, change what they name, and run again.\n";
8
+ export declare const firstMethodRules = "# Build a Method\n\n1. **Sign in first.** Run `method status`. If it is not signed in, run `method login` and let the user approve in the browser. Signed in, model and classification steps need no keys, each run saves a version when the file changed, and runs appear on the dashboard.\n2. **Each model or classifier request is its own step**: `call`, `agent`, or `classify`, with its prompt in the Method, so the user can change one prompt, run again, and compare. A script never calls a model API. When the user already has code that does the work, keep its fixed logic as `run` steps and move each prompt and rubric into its own step; do not wrap the code. \"The same thing\" means the same behavior in steps. If a request cannot become a step because Method lacks a feature, tell the user what is missing.\n3. **Show the design, then build it in the same turn.** Show each step with its type, purpose, and output, and a table of the prompts, instructions, and rubrics in the user's existing work, each with the steps that receive it. Do not wait for approval unless the user asked to approve first. Ask only for information that you cannot find and that would change the design.\n4. **Run early and often.** Write the first steps, validate, run, then add the next steps. Fix errors. Fix each warning, or accept it with the user's reason. Never add a check only to remove a warning. A new run reuses every step whose definition and inputs did not change, so each run executes only what changed. `--rerun STEP` runs a step again anyway; `--fresh` runs every step.\n5. **Keep the inputs of the existing code.** If the code takes an ID and looks up the record, the Method takes the same ID and looks it up the same way.\n6. **Keep the user's rules word for word.** When you convert an agent, a prompt, a playbook, or a guide that the user wrote, copy each of their instruction files unchanged beside the Method and list them in `files`. A run step reads them and returns their complete text, and every model step that applies them receives that text in `in`. Each step prompt says the step's own task and tells the model to follow every rule in that text. Do not reword, shorten, summarize, or split the user's rules, and do not leave one out as background: a reworded rule can change its meaning, for example when a condition (\"only when\", \"unless\", \"after\") or an exception is lost. In the table of rule 3, list each instruction file with the steps that receive it.\n7. **Compare with the old agent before the Method replaces it.** Choose 3 to 5 sample inputs from the user's data, including the hard ones. Run the old agent as the user runs it and run the Method on the same inputs, and show the user every difference side by side. Fix each difference that the user does not accept, and run again. Do not say that the Method replaces the old agent, or does what it did, until the user has seen this comparison.\n8. **Use the defaults without asking.** Model steps use the account's hosted models and `classify` uses Method's classifier, so no model or classifier keys are needed. To keep the model that existing code uses, add `models: {writer: provider/model}` to the Method and use `model: writer` in the steps (or `model: provider/model` on the step); it needs no key. Keep the models that the Method or the existing code names: when a model step fails, show the error and ask before you change its model. Run records go to the user's account. When the user asks that run data stay on this computer or not be uploaded, set `run_data: device`: the run's inputs, outputs, and files stay here, and the account keeps only each step's status and timing. Model steps still send each step's input to the model to answer it; nothing is kept. Tell the user that in one line, then run again.\n9. **Keys stay out of chat.** Declare each key that a script needs under `secrets:` with its purpose; every script of the Method receives all of its declared secrets. Run `method secret find` in the Method's folder: it lists the key files nearby and the names in each, never the values, and prints the import command. Run that command and tell the user which file each key came from: the key stays on this computer, so there is nothing to ask. If it finds nothing, ask the user to run `method secret set NAME`, which opens a private form in their browser. Never open, print, or search a key file (`cat`, `grep`, an editor), including Method's own secret store, and never ask for a value in chat: the values would go into the chat. When a run says Method's model credit is used up, run `method config model-key` yourself: it takes the user's own OpenRouter key from the one nearby key file that holds it, or opens a private form for it. Then run again.\n10. **Choose a sample yourself** from the user's data, and check that it has the sources the Method needs. Ask only when there is no good sample.\n11. **Iterate.** `method run` prints a `dashboard` link when the run starts and again with its result. Give the user the link as soon as the run starts. After a good run, show the result and the link, and ask what to change. Change the step and run again. Show only results that a run made: never write or edit a result by hand, and fix a wrong result in the Method, not in the data it reads. If you cannot run, say so.\n12. **Publish** with `method publish FILE --reason TEXT` when the user wants to share or schedule a version. It runs the Method's cases first.\n13. **Talk about the work, not about Method.** Unless the user asks, do not mention Method's versions, formats, settings files, or other internal files, and do not ask the user about them, about models, or about which saved version to use. Method updates itself and converts older Method files when it runs them. When the user asks whether a Method works, or asks you to make it work, run it with the inputs of a recent run (`--inputs-from RUN_ID`) and show the result and the dashboard link in a few sentences. Say that it works only after a run completed.\n14. **One file for each Method.** Change and run a Method in its own file. Do not copy it to a temporary or other folder to change it: Method does not run or save the copy while the Method's file exists, and names that file.\n15. **Questions in a run are for the user.** When a run stops with `needs_input`, ask the user its `question` in plain words, wait for the answer, and continue the run with it as the output says. Never answer it yourself.\n16. **Make each run easy to find and to run again.** Give the Method a `run_label` that tells its runs apart, such as `inputs.topic`. When an input can be empty and a default picks the value, point `run_label` at the step output that holds the resolved value, not at the input. Put the readable output, such as the report text or the message, in `result`, not only in files. Write a `run_prompt` that says where each input comes from (for example \"as_of is today's date in YYYY-MM-DD\") and what to show at the end. Leave SDK, runtime, and version details out of it.\n17. **Run again from the dashboard.** When the user pastes a \"Run again\" prompt from the dashboard, run with `--inputs-from RUN_ID` and change only what the prompt names: put those values in a file and add `--inputs FILE`.\n\nWhen a run fails, read failure.headline and failure.next, change what they name, and run again.\n";
9
9
  /** How to write the prompt inside a call, agent, or classify step. */
10
- export declare const promptRules = "# Write each prompt\n\n- **One task per prompt.** If a prompt says \"then\" or has several jobs, make several steps.\n- **Say what to do.** Leave out background that does not change the output.\n- **Show 1 to 3 examples of good output.** Take them from the user's work: examples in an existing prompt, a past output that the user approved, or the first run that the user accepts. Pass a long example as an input, not as text in the prompt.\n- **Put the output shape in `out`**, not in the prompt.\n- **Say what to do when the input does not have the answer**, for example \"leave due empty\". For classify, add an option such as `unclear`.\n\nBefore:\n\n```text\nYou are an expert support analyst with years of experience. Our company values clear\ncommunication. Read the tickets, decide which ones are urgent, write a summary, and\nreturn JSON with a \"summary\" key. Make it good.\n```\n\nAfter (urgency is a classify step before this one; the shape is in `out`):\n\n```text\nWrite a five-sentence summary of this week's support tickets for the team.\nStart with the high-urgency tickets. Quote no customer names.\nWrite it in the style of this summary from an earlier week:\n\n{{example}}\n```\n";
10
+ export declare const promptRules = "# Write each prompt\n\n- **One task per prompt.** If a prompt says \"then\" or has several jobs, make several steps. Split the tasks, not the user's rules: every step that applies them receives all of them, word for word.\n- **Say what to do.** Leave out background that does not change the output. The user's own rules and instructions are not background: pass them unchanged as an input, and do not restate them in your own words.\n- **Show 1 to 3 examples of good output.** Take them from the user's work: examples in an existing prompt, a past output that the user approved, or the first run that the user accepts. Pass a long example as an input, not as text in the prompt.\n- **Put the output shape in `out`**, not in the prompt.\n- **Say what to do when the input does not have the answer**, for example \"leave due empty\". For classify, add an option such as `unclear`.\n\nBefore:\n\n```text\nYou are an expert support analyst with years of experience. Our company values clear\ncommunication. Read the tickets, decide which ones are urgent, write a summary, and\nreturn JSON with a \"summary\" key. Make it good.\n```\n\nAfter (urgency is a classify step before this one; the shape is in `out`):\n\n```text\nWrite a five-sentence summary of this week's support tickets for the team.\nStart with the high-urgency tickets. Quote no customer names.\nWrite it in the style of this summary from an earlier week:\n\n{{example}}\n```\n";
11
11
  /** Enough of the format to write a Method without reading the JSON schema. */
12
12
  export declare const fieldReference = "# Field reference\n\n```text\nformat: method/3.4\nname, goal text\ninputs {NAME: DATA} values the run receives (--inputs FILE)\nsecrets {NAME: purpose} keys that scripts receive as environment variables\nenvironment {NAME: {type: files | service | browser | desktop | tool, description}}\nfiles [PATH] helper files that scripts import or read\nsteps {ID: STEP}\nresult REF, or {NAME: REF}\nrun_data account (default) | device\n\nDATA {type: text | number | boolean | record | list | file, description,\n fields: {NAME: TYPE} (a record, or a list of records)\n items: TYPE (a list of one type)\n default: VALUE} A bare type such as text works inside fields and items.\nREF inputs.NAME | an output NAME | NAME.field | state.NAME | environment.NAME\n\nSTEP name, purpose give a run step both; purpose states its rules, result, and external changes\n in: {ALIAS: REF} the values that the step receives\n each: {ITEM: LIST_REF} run once per item; ITEM is given to the step, so do not repeat it in in\n concurrency: 1-32 items at once, for an each step that changes nothing\n when: BOOLEAN_REF after: ID or [ID] repeat: {max_iterations, until: BOOLEAN_OUTPUT}\n do: one of\n {kind: run, runtime: python | node, entrypoint: FILE, args: [...]}\n reads one JSON object (its in) on stdin, prints one JSON object (its out); files go under $METHOD_OUTPUT_DIR\n {kind: call, model: NAME, prompt: TEXT}\n the step's in go to the model as JSON; {{ALIAS}} puts a text, number, or boolean value into the prompt\n {kind: agent, model: NAME, prompt: TEXT, tools: [TOOL]}\n {kind: classify, question: TEXT, options: {ID: description}} value: {choice, probabilities}\n {kind: classify, question: TEXT, answer: yes_no} value: {answer, probability} (of yes)\n {kind: classify, question: TEXT, levels: [LOW, ..., HIGH]} value: {level, score, probabilities}; 2-10 levels\n Do counting, math, and date comparisons in a run step; the classifier reads the question literally. Choose thresholds from a few labeled samples.\n ask: TEXT in place of do: a question for the user\n out: {NAME: DATA} (classify: out: NAME)\n changes: [state.NAME | environment.NAME]; a service change needs effects or no_effect_reason\n check, limits: {timeout_ms, max_model_requests, max_agent_turns}\n accept: {CODE: reason} keep a warning or note with the user's reason\n\nmodels: {NAME: provider/model | {model, max_output_tokens, reasoning_effort}} hosted models; no key. A step without model uses the account default.\n NAME: {agent: codex | claude, model, reasoning_effort} a step with model: NAME runs on that local agent (it must be installed); --agent still overrides.\nlimits: {timeout_ms, max_model_requests, max_invocations, max_tool_calls, max_concurrency, ..., step: {timeout_ms, max_agent_turns, max_model_requests}}\ntools: {NAME: {description, in, out, run, effects}} script tools that agent steps list in tools\nid: wf_... written by the CLI at the first signed-in save; keep it. method new-id FILE makes a copy a new Method.\nConnections: a files connection uses the folder NAME beside the Method, or method bind FILE NAME --file PATH (this computer only).\nIssues: errors block; warnings and notes do not.\n```\n\n`method schema method` prints the complete JSON schema.\n";
13
13
  export declare const designExamples = "# Contrasting design outlines\n\nThese are design outlines, not runnable Method files.\n\n| Request | Design | Explanation |\n| --- | --- | --- |\n| Summarize supplied text | call \u2192 return summary | The model receives all source text. Add a save step only if a saved file is requested. Use an agent if it must find or inspect additional sources. |\n| Investigate a claim | agent to research and assess \u2192 return findings | Add a separate planning, checking, or saving step only when the task needs that boundary or result. When the user requires that the findings say only what the sources say, add an agent check on the assess step (method authoring recipes). |\n| Route a message with human review for low confidence | classify \u2192 threshold run \u2192 conditional ask | Classification returns probabilities. Code applies the threshold. Human input resolves cases below the threshold. Add a separate action with an effect if the Method must send or change anything. |\n| Send a daily summary email | call to write \u2192 run to send, with an effect | The send script puts METHOD_OPERATION_ID in the Message-ID. A mail.delivery effect searches the bounce mailbox through its own read-only connection until a 5-day horizon. A bounce fails the run; no bounce by the horizon is unrefuted, not proven. |\n";
@@ -31,27 +31,29 @@ export const firstMethodRules = `# Build a Method
31
31
 
32
32
  1. **Sign in first.** Run \`method status\`. If it is not signed in, run \`method login\` and let the user approve in the browser. Signed in, model and classification steps need no keys, each run saves a version when the file changed, and runs appear on the dashboard.
33
33
  2. **Each model or classifier request is its own step**: \`call\`, \`agent\`, or \`classify\`, with its prompt in the Method, so the user can change one prompt, run again, and compare. A script never calls a model API. When the user already has code that does the work, keep its fixed logic as \`run\` steps and move each prompt and rubric into its own step; do not wrap the code. "The same thing" means the same behavior in steps. If a request cannot become a step because Method lacks a feature, tell the user what is missing.
34
- 3. **Show the design, then build it in the same turn.** Show each step with its type, purpose, and output, and a table of the prompts and rubrics in the user's existing work, each with the step that holds it. Do not wait for approval unless the user asked to approve first. Ask only for information that you cannot find and that would change the design.
34
+ 3. **Show the design, then build it in the same turn.** Show each step with its type, purpose, and output, and a table of the prompts, instructions, and rubrics in the user's existing work, each with the steps that receive it. Do not wait for approval unless the user asked to approve first. Ask only for information that you cannot find and that would change the design.
35
35
  4. **Run early and often.** Write the first steps, validate, run, then add the next steps. Fix errors. Fix each warning, or accept it with the user's reason. Never add a check only to remove a warning. A new run reuses every step whose definition and inputs did not change, so each run executes only what changed. \`--rerun STEP\` runs a step again anyway; \`--fresh\` runs every step.
36
36
  5. **Keep the inputs of the existing code.** If the code takes an ID and looks up the record, the Method takes the same ID and looks it up the same way.
37
- 6. **Use the defaults without asking.** Model steps use the account's hosted models and \`classify\` uses Method's classifier, so no model or classifier keys are needed. To keep the model that existing code uses, add \`models: {writer: provider/model}\` to the Method and use \`model: writer\` in the steps (or \`model: provider/model\` on the step); it needs no key. Keep the models that the Method or the existing code names: when a model step fails, show the error and ask before you change its model. Run records go to the user's account. When the user asks that run data stay on this computer or not be uploaded, set \`run_data: device\`: the run's inputs, outputs, and files stay here, and the account keeps only each step's status and timing. Model steps still send each step's input to the model to answer it; nothing is kept. Tell the user that in one line, then run again.
38
- 7. **Keys stay out of chat.** Declare each key that a script needs under \`secrets:\` with its purpose; every script of the Method receives all of its declared secrets. Run \`method secret find\` in the Method's folder: it lists the key files nearby and the names in each, never the values, and prints the import command. Run that command and tell the user which file each key came from: the key stays on this computer, so there is nothing to ask. If it finds nothing, ask the user to run \`method secret set NAME\`, which opens a private form in their browser. Never open, print, or search a key file (\`cat\`, \`grep\`, an editor), including Method's own secret store, and never ask for a value in chat: the values would go into the chat. When a run says Method's model credit is used up, run \`method config model-key\` yourself: it takes the user's own OpenRouter key from the one nearby key file that holds it, or opens a private form for it. Then run again.
39
- 8. **Choose a sample yourself** from the user's data, and check that it has the sources the Method needs. Ask only when there is no good sample.
40
- 9. **Iterate.** \`method run\` prints a \`dashboard\` link when the run starts and again with its result. Give the user the link as soon as the run starts. After a good run, show the result and the link, and ask what to change. Change the step and run again. Show only results that a run made: never write or edit a result by hand, and fix a wrong result in the Method, not in the data it reads. If you cannot run, say so.
41
- 10. **Publish** with \`method publish FILE --reason TEXT\` when the user wants to share or schedule a version. It runs the Method's cases first.
42
- 11. **Talk about the work, not about Method.** Unless the user asks, do not mention Method's versions, formats, settings files, or other internal files, and do not ask the user about them, about models, or about which saved version to use. Method updates itself and converts older Method files when it runs them. When the user asks whether a Method works, or asks you to make it work, run it with the inputs of a recent run (\`--inputs-from RUN_ID\`) and show the result and the dashboard link in a few sentences. Say that it works only after a run completed.
43
- 12. **One file for each Method.** Change and run a Method in its own file. Do not copy it to a temporary or other folder to change it: Method does not run or save the copy while the Method's file exists, and names that file.
44
- 13. **Questions in a run are for the user.** When a run stops with \`needs_input\`, ask the user its \`question\` in plain words, wait for the answer, and continue the run with it as the output says. Never answer it yourself.
45
- 14. **Make each run easy to find and to run again.** Give the Method a \`run_label\` that tells its runs apart, such as \`inputs.topic\`. When an input can be empty and a default picks the value, point \`run_label\` at the step output that holds the resolved value, not at the input. Put the readable output, such as the report text or the message, in \`result\`, not only in files. Write a \`run_prompt\` that says where each input comes from (for example "as_of is today's date in YYYY-MM-DD") and what to show at the end. Leave SDK, runtime, and version details out of it.
46
- 15. **Run again from the dashboard.** When the user pastes a "Run again" prompt from the dashboard, run with \`--inputs-from RUN_ID\` and change only what the prompt names: put those values in a file and add \`--inputs FILE\`.
37
+ 6. **Keep the user's rules word for word.** When you convert an agent, a prompt, a playbook, or a guide that the user wrote, copy each of their instruction files unchanged beside the Method and list them in \`files\`. A run step reads them and returns their complete text, and every model step that applies them receives that text in \`in\`. Each step prompt says the step's own task and tells the model to follow every rule in that text. Do not reword, shorten, summarize, or split the user's rules, and do not leave one out as background: a reworded rule can change its meaning, for example when a condition ("only when", "unless", "after") or an exception is lost. In the table of rule 3, list each instruction file with the steps that receive it.
38
+ 7. **Compare with the old agent before the Method replaces it.** Choose 3 to 5 sample inputs from the user's data, including the hard ones. Run the old agent as the user runs it and run the Method on the same inputs, and show the user every difference side by side. Fix each difference that the user does not accept, and run again. Do not say that the Method replaces the old agent, or does what it did, until the user has seen this comparison.
39
+ 8. **Use the defaults without asking.** Model steps use the account's hosted models and \`classify\` uses Method's classifier, so no model or classifier keys are needed. To keep the model that existing code uses, add \`models: {writer: provider/model}\` to the Method and use \`model: writer\` in the steps (or \`model: provider/model\` on the step); it needs no key. Keep the models that the Method or the existing code names: when a model step fails, show the error and ask before you change its model. Run records go to the user's account. When the user asks that run data stay on this computer or not be uploaded, set \`run_data: device\`: the run's inputs, outputs, and files stay here, and the account keeps only each step's status and timing. Model steps still send each step's input to the model to answer it; nothing is kept. Tell the user that in one line, then run again.
40
+ 9. **Keys stay out of chat.** Declare each key that a script needs under \`secrets:\` with its purpose; every script of the Method receives all of its declared secrets. Run \`method secret find\` in the Method's folder: it lists the key files nearby and the names in each, never the values, and prints the import command. Run that command and tell the user which file each key came from: the key stays on this computer, so there is nothing to ask. If it finds nothing, ask the user to run \`method secret set NAME\`, which opens a private form in their browser. Never open, print, or search a key file (\`cat\`, \`grep\`, an editor), including Method's own secret store, and never ask for a value in chat: the values would go into the chat. When a run says Method's model credit is used up, run \`method config model-key\` yourself: it takes the user's own OpenRouter key from the one nearby key file that holds it, or opens a private form for it. Then run again.
41
+ 10. **Choose a sample yourself** from the user's data, and check that it has the sources the Method needs. Ask only when there is no good sample.
42
+ 11. **Iterate.** \`method run\` prints a \`dashboard\` link when the run starts and again with its result. Give the user the link as soon as the run starts. After a good run, show the result and the link, and ask what to change. Change the step and run again. Show only results that a run made: never write or edit a result by hand, and fix a wrong result in the Method, not in the data it reads. If you cannot run, say so.
43
+ 12. **Publish** with \`method publish FILE --reason TEXT\` when the user wants to share or schedule a version. It runs the Method's cases first.
44
+ 13. **Talk about the work, not about Method.** Unless the user asks, do not mention Method's versions, formats, settings files, or other internal files, and do not ask the user about them, about models, or about which saved version to use. Method updates itself and converts older Method files when it runs them. When the user asks whether a Method works, or asks you to make it work, run it with the inputs of a recent run (\`--inputs-from RUN_ID\`) and show the result and the dashboard link in a few sentences. Say that it works only after a run completed.
45
+ 14. **One file for each Method.** Change and run a Method in its own file. Do not copy it to a temporary or other folder to change it: Method does not run or save the copy while the Method's file exists, and names that file.
46
+ 15. **Questions in a run are for the user.** When a run stops with \`needs_input\`, ask the user its \`question\` in plain words, wait for the answer, and continue the run with it as the output says. Never answer it yourself.
47
+ 16. **Make each run easy to find and to run again.** Give the Method a \`run_label\` that tells its runs apart, such as \`inputs.topic\`. When an input can be empty and a default picks the value, point \`run_label\` at the step output that holds the resolved value, not at the input. Put the readable output, such as the report text or the message, in \`result\`, not only in files. Write a \`run_prompt\` that says where each input comes from (for example "as_of is today's date in YYYY-MM-DD") and what to show at the end. Leave SDK, runtime, and version details out of it.
48
+ 17. **Run again from the dashboard.** When the user pastes a "Run again" prompt from the dashboard, run with \`--inputs-from RUN_ID\` and change only what the prompt names: put those values in a file and add \`--inputs FILE\`.
47
49
 
48
50
  When a run fails, read failure.headline and failure.next, change what they name, and run again.
49
51
  `;
50
52
  /** How to write the prompt inside a call, agent, or classify step. */
51
53
  export const promptRules = `# Write each prompt
52
54
 
53
- - **One task per prompt.** If a prompt says "then" or has several jobs, make several steps.
54
- - **Say what to do.** Leave out background that does not change the output.
55
+ - **One task per prompt.** If a prompt says "then" or has several jobs, make several steps. Split the tasks, not the user's rules: every step that applies them receives all of them, word for word.
56
+ - **Say what to do.** Leave out background that does not change the output. The user's own rules and instructions are not background: pass them unchanged as an input, and do not restate them in your own words.
55
57
  - **Show 1 to 3 examples of good output.** Take them from the user's work: examples in an existing prompt, a past output that the user approved, or the first run that the user accepts. Pass a long example as an input, not as text in the prompt.
56
58
  - **Put the output shape in \`out\`**, not in the prompt.
57
59
  - **Say what to do when the input does not have the answer**, for example "leave due empty". For classify, add an option such as \`unclear\`.
@@ -3,7 +3,6 @@ import { type MethodClient } from './method-client.js';
3
3
  export type ImproveRequest = {
4
4
  step_id?: string;
5
5
  note?: string;
6
- case_id?: string;
7
6
  run_id?: string;
8
7
  };
9
8
  /** Runs a local Method file; the default is the runtime that `method run FILE` uses. */
@@ -21,10 +20,10 @@ export type ImproveProposal = {
21
20
  [key: string]: unknown;
22
21
  };
23
22
  };
24
- type ProposalKind = 'change' | 'case' | 'rubric' | 'script' | 'none';
23
+ type ProposalKind = 'change' | 'none';
25
24
  /**
26
25
  * Read the result of improve.method: {proposal: <SubmitProposal as JSON text>, kind, proposal_id}. A change has the
27
- * proposed workflow; a case or rubric fix has case_suggestion; a script change has script_change; none has neither.
26
+ * tested workflow; none has no workflow.
28
27
  */
29
28
  export declare function readImproveResult(result: unknown): ImproveProposal;
30
29
  export declare const judgmentsLine = "Method fixes this Method from your judgments. Record the person's reaction: method judge RUN_ID wrong --note \"...\".";
@@ -11,10 +11,10 @@ import { methodCache } from './prepare.js';
11
11
  import { writePrivateJson } from './files.js';
12
12
  import { currentVersion, methodContent } from './versions.js';
13
13
  import { localRuns, saveLocalProposal } from './proposals.js';
14
- const kinds = ['change', 'case', 'rubric', 'script', 'none'];
14
+ const kinds = ['change', 'none'];
15
15
  /**
16
16
  * Read the result of improve.method: {proposal: <SubmitProposal as JSON text>, kind, proposal_id}. A change has the
17
- * proposed workflow; a case or rubric fix has case_suggestion; a script change has script_change; none has neither.
17
+ * tested workflow; none has no workflow.
18
18
  */
19
19
  export function readImproveResult(result) {
20
20
  const value = result;
@@ -36,18 +36,11 @@ export function readImproveResult(result) {
36
36
  throw Error(`The improvement run says kind ${String(value.kind)}, but its proposal is kind ${evidence.kind}.`);
37
37
  if (evidence.kind === 'change' && (!proposal.workflow || typeof proposal.workflow !== 'object'))
38
38
  throw Error('The improvement proposal is a change without a workflow.');
39
- if ((evidence.kind === 'case' || evidence.kind === 'rubric') && !evidence.case_suggestion)
40
- throw Error(`The improvement proposal is a ${evidence.kind} fix without case_suggestion.`);
41
- if (evidence.kind === 'script' && !evidence.script_change)
42
- throw Error('The improvement proposal is a script change without script_change.');
43
39
  return { cause: proposal.cause.trim(), workflow: evidence.kind === 'change' ? proposal.workflow : null, evidence };
44
40
  }
45
41
  const nextStep = (file, id, kind) => ({
46
- change: `Read the proposal, then run method apply ${file} ${id}. It does not publish.`,
47
- case: `Run method apply ${file} ${id} to see the case to record.`,
48
- rubric: `Run method apply ${file} ${id} to see the case rubric to fix.`,
49
- script: `Run method apply ${file} ${id} to see the script change.`,
50
- none: 'No safe change was found.',
42
+ change: `Read the change, then run method apply ${file} ${id}. It does not publish.`,
43
+ none: 'No change passed the tests.',
51
44
  })[kind];
52
45
  const print = (value) => process.stdout.write(JSON.stringify(value, null, 2) + '\n');
53
46
  export const judgmentsLine = 'Method fixes this Method from your judgments. Record the person\'s reaction: method judge RUN_ID wrong --note "...".';
@@ -58,17 +51,15 @@ export const judgmentsLine = 'Method fixes this Method from your judgments. Reco
58
51
  * and saves a local proposal.
59
52
  */
60
53
  export async function improveCommand(args, clientFactory, run) {
61
- const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { case: { type: 'string' }, note: { type: 'string' }, step: { type: 'string' }, server: { type: 'string' } } });
54
+ const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { note: { type: 'string' }, step: { type: 'string' }, server: { type: 'string' } } });
62
55
  if (positionals.length !== 1)
63
- throw Error('Use method improve FILE [--case ID] [--note TEXT] [--step ID].');
56
+ throw Error('Use method improve FILE [--note TEXT] [--step ID].');
64
57
  const file = authoringPath(positionals[0]), document = readDocument(file), workflow = loadWorkflow(methodContent(document));
65
58
  if (values.step && !Object.hasOwn(workflow.steps, values.step))
66
59
  throw Error(`Step ${values.step} is not in ${positionals[0]}.`);
67
- if (values.case && !/^[a-z0-9][a-z0-9-]{0,79}$/.test(values.case))
68
- throw Error('Use a case ID: lowercase letters, digits, and hyphens.');
69
60
  if (values.note !== undefined && !values.note.trim())
70
61
  throw Error('Write the correction after --note.');
71
- const request = { ...(values.step ? { step_id: values.step } : {}), ...(values.note ? { note: values.note.trim() } : {}), ...(values.case ? { case_id: values.case } : {}) };
62
+ const request = { ...(values.step ? { step_id: values.step } : {}), ...(values.note ? { note: values.note.trim() } : {}) };
72
63
  const client = clientFactory(values.server ?? DEFAULT_SERVER);
73
64
  if (!client.token())
74
65
  await client.login();
@@ -119,7 +110,7 @@ export async function improveOnDevice(client, file, request, run) {
119
110
  const improve = await cachedImproveMethod(client);
120
111
  const runDir = join(methodCache(), 'runs', randomUUID());
121
112
  const inputs = join(runDir, 'improve-inputs.json');
122
- writePrivateJson(inputs, { method_file: file, case_id: request.case_id ?? '', note: request.note ?? '', step_id: request.step_id ?? '', grant_id: '', run_id: request.run_id ?? '', make_case: false });
113
+ writePrivateJson(inputs, { method_file: file, note: request.note ?? '', step_id: request.step_id ?? '', grant_id: '', judgment_id: '', run_id: request.run_id ?? '' });
123
114
  const runFile = run ?? (await import('./current-runtime.js')).runCurrentFile;
124
115
  process.stderr.write(`Improving ${file} on this computer. Its run content stays here.\n`);
125
116
  // The run prints its own result; send it to stderr so that stdout holds only the proposal.
@@ -38,7 +38,7 @@ export const commandHelp = {
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
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 makes Method fix the Method in the background: it applies a tested change that the person can undo, or changes nothing. Do not ask the person to confirm a judgment.", result: "One line, for example Recorded: wrong on step choose. Method is fixing it in the background. --json prints the judgment and fixing.", errors: "An unknown run, a run of a Method that is not in your account, or no sign-in.", example: "method judge 6f1c… wrong --step summary --note 'Name the customer in the first sentence.'", remote: true },
40
40
  changes: { usage: "method changes FILE [--json] [--server URL]", purpose: "List the changes that Method applied to a Method.", arguments: "Lists the changes that Method made from wrong judgments, newest first, and the local proposals applied on this computer. Commands that take a signed-in FILE merge a new change 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 local change.", example: "method changes task.method", remote: true },
41
- improve: { usage: "method improve FILE [--case ID] [--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 and a suggested case records. --step: improve only this step. --case: the case the change must pass. 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 },
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
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 },
43
43
  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
44
  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 },
@@ -71,7 +71,7 @@ Use method doctor to check the installed Node and configured tools.`, result: "W
71
71
  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
72
  test: { usage: "method test FILE [--case ID]... [--baseline OLD_FILE] [--new ID]... [--cases DIR] [--agent codex|claude]", 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
73
  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]... [--agent codex|claude]", 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.", 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.", errors: "A case that does not fail on the bad run, or does not pass on the passing run; 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.'" },
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] [--agent codex|claude]", 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
75
  "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
76
  "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
77
  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" },
@@ -300,10 +300,13 @@ const recipes = `# Recipes
300
300
  ## Corrections become cases
301
301
 
302
302
  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. Then read the run (result.json, events.jsonl) and fix the Method. Run it again and show the user the new result.
304
- 2. If the user wants the fix to stay, ask once: "Keep this as a rule: <the rule in plain words>?"
305
- 3. On yes, record it: method case new FILE --id ID --run BAD_RUN --passing-run NEW_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.
306
- 4. Run method test FILE. Every case must pass. method publish refuses a version that breaks a case.
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. Then read the run (result.json, events.jsonl).
304
+ 2. 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.
305
+ 3. Fix the Method. Run it again and show the user the new result.
306
+ 4. If the user wants the fix to stay, ask once: "Keep this as a rule: <the rule in plain words>?"
307
+ 5. On yes, record it: method case new FILE --id ID --run BAD_RUN --passing-run NEW_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.
308
+ 6. 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.
309
+ 7. Run method test FILE. Every case must pass. method publish refuses a version that breaks a case.
307
310
  For a one-time preference, edit and run again; a case is not needed.
308
311
 
309
312
  ## Checked on every run, or on every new version
@@ -184,17 +184,6 @@ export async function localRuns(file) {
184
184
  }
185
185
  return runs;
186
186
  }
187
- /** What the coding agent does for a proposal that is not a change to the Method document. runFolder is the folder of the suggested case's run, when it is on this computer. */
188
- function manualStep(file, proposal, runFolder) {
189
- const evidence = proposal.evidence ?? {};
190
- if (evidence.case_suggestion) {
191
- const c = evidence.case_suggestion, quote = (value) => JSON.stringify(value);
192
- return `Record the case: method case new ${file} --id ${c.id} --note ${quote(c.note)} --run ${runFolder ? quote(runFolder) : 'RUN_FOLDER'}${(c.rubric ?? []).map((r) => ` --rubric ${quote(r)}`).join('')}${runFolder ? '' : ', where RUN_FOLDER is the folder of the run that went wrong'}. Then run method improve ${file} --case ${c.id}, show the proposal, apply it, and run again.`;
193
- }
194
- if (evidence.script_change)
195
- return `Change the script, then run method test ${file}: ${evidence.script_change}`;
196
- return 'This proposal has no change to apply.';
197
- }
198
187
  const print = (value) => process.stdout.write(JSON.stringify(value, null, 2) + '\n');
199
188
  const changeLine = (change) => `${change.date.slice(0, 10)} ${change.version_id ?? 'not saved as a version'} ${change.cause}`;
200
189
  /** `method changes FILE`: the changes that Method applied to the Method, newest first, and those applied on this computer. */
@@ -246,13 +235,8 @@ export async function proposalCommand(args, clientFactory) {
246
235
  throw Error(`Proposal ${proposal.id} is ${proposal.status}.`);
247
236
  if (values.resolved)
248
237
  return print({ status: 'applied', file, proposal_id: proposal.id, ...await markApplied(client, file, proposal), next: `Run method test ${target}. Publish it when it is right.` });
249
- if (!proposal.proposed_workflow) {
250
- // The suggested case's run is a local folder (a local improvement) or an account run ID (an account improvement).
251
- const runId = proposal.evidence?.case_suggestion?.run_id;
252
- const runFolder = runId ? (await localRuns(file)).find(run => run.dir === runId || run.account_run_id === runId)?.dir : undefined;
253
- process.exitCode = 1;
254
- return print({ status: 'manual', file, proposal_id: proposal.id, kind: proposal.evidence?.kind ?? null, cause: proposal.cause, next: manualStep(target, proposal, runFolder) });
255
- }
238
+ if (!proposal.proposed_workflow)
239
+ return print({ status: 'none', file, proposal_id: proposal.id, cause: proposal.cause, next: 'No change passed the tests; there is nothing to apply.' });
256
240
  const result = await applyProposal(client, file, proposal);
257
241
  if (result.status === 'applied')
258
242
  return print({ ...result, file, proposal_id: proposal.id, cause: proposal.cause, next: `Run method test ${target}. Publish it when it is right.` });
@@ -179,7 +179,7 @@ export async function caseCommand(args) {
179
179
  const text = { type: "string" };
180
180
  const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { run: text, "passing-run": text, id: text, note: text, rubric: { type: "string", multiple: true }, ref: text, context: { type: "string", multiple: true }, expect: text,
181
181
  observations: text, redact: text, runs: text, "min-pass": text, author: text, reason: text, by: text, cases: text, workspace: text, agent: text,
182
- "retention-days": text, supersedes: { type: "string", multiple: true } } });
182
+ "retention-days": text, supersedes: { type: "string", multiple: true }, "accept-conflict": text } });
183
183
  const [action, file, id] = positionals;
184
184
  if (!file || !["new", "retire", "list"].includes(action ?? ""))
185
185
  throw Error("Use method case new|retire|list FILE. See method help case new.");
@@ -207,7 +207,7 @@ export async function caseCommand(args) {
207
207
  print(await createCase({ methodFile, runDir: values.run ? authoringPath(values.run) : undefined, passingRun: values["passing-run"] ? authoringPath(values["passing-run"]) : undefined,
208
208
  id: values.id, note: values.note, author: values.author ?? null, rubric: values.rubric ?? [], ref: values.ref, context: values.context ?? [], expect: json(values.expect) ?? [],
209
209
  observations: json(values.observations), redact: json(values.redact), runs: integer(values.runs, "--runs"), minPass: integer(values["min-pass"], "--min-pass"),
210
- retentionDays: integer(values["retention-days"], "--retention-days"), supersedes: values.supersedes ?? [], casesDir, config: prepared.config,
210
+ retentionDays: integer(values["retention-days"], "--retention-days"), supersedes: values.supersedes ?? [], casesDir, acceptConflict: values["accept-conflict"], config: prepared.config,
211
211
  options: { runOptions: { processPath: prepared.processPath, secrets: await declaredSecrets(methodFile), ...(prepared.classification ? { classification: prepared.classification } : {}), ...(prepared.hostedModels ? { hostedModels: prepared.hostedModels } : {}), ...(values.agent ? { agent: values.agent } : {}) } } }));
212
212
  }
213
213
  /**
@@ -14,7 +14,7 @@ import { readDocument } from './authoring.js';
14
14
  import { withInputsFrom } from './inputs-from.js';
15
15
  import { setupFailure } from './failure.js';
16
16
  // Older versions are listed only after their saved-package tests pass.
17
- const supportedPackageRuntimes = new Set([runtimeVersion, '0.12.0', '0.11.1', '0.11.0', '0.10.0', '0.9.5', '0.9.4', '0.9.3', '0.7.0', '0.7.1', '0.7.2', '0.8.0', '0.8.1', '0.8.2', '0.8.3']);
17
+ const supportedPackageRuntimes = new Set([runtimeVersion, '0.14.0', '0.12.0', '0.11.1', '0.11.0', '0.10.0', '0.9.5', '0.9.4', '0.9.3', '0.7.0', '0.7.1', '0.7.2', '0.8.0', '0.8.1', '0.8.2', '0.8.3']);
18
18
  /** A run whose steps all finished: completed, or unconfirmed (an external change could not be confirmed). */
19
19
  export const finishedRun = (status) => status === 'completed' || status === 'unconfirmed';
20
20
  export async function runSaved(saved, flags, client) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@method-ai-hq/sdk",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
4
  "description": "Build and run reusable .method files with checked steps and saved results.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,7 +35,7 @@
35
35
  "prepack": "npm --prefix ../.. run build"
36
36
  },
37
37
  "dependencies": {
38
- "@withmethod/runtime": "https://codeload.github.com/method-ai-hq/method-spec/tar.gz/54461cb387d8f10813db05bb0cea29c0a3a53504",
38
+ "@withmethod/runtime": "https://codeload.github.com/method-ai-hq/method-spec/tar.gz/5d9183f52b64af45d677a69d718bda9e1e0077ab",
39
39
  "fflate": "^0.8.3",
40
40
  "yaml": "^2.9.0",
41
41
  "zod": "^3.25.76"