cortad 0.3.1-rc.5 → 0.3.1-rc.6

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/lib/cli.mjs CHANGED
@@ -36,7 +36,7 @@ export async function main(argv, { root = process.cwd(), env = process.env, stdo
36
36
  const children = [];
37
37
  const pending = { read: () => readPending(project), write: (p) => writePending(project, p) };
38
38
  // The key is read at every call: the MCP process outlives a connect that writes it after the process started.
39
- const writes = { mark: (id) => markRun(writesDirOf(homeOf(project)), id), putBack: (id) => putBack(writesDirOf(homeOf(project)), root, id) };
39
+ const writes = { mark: (id) => markRun(writesDirOf(homeOf(project)), id), putBack: () => putBack(writesDirOf(homeOf(project)), root) };
40
40
  const verbs = makeVerbs({
41
41
  api: `${origin.origin}/api`,
42
42
  root,
package/lib/verbs.mjs CHANGED
@@ -114,7 +114,7 @@ export function makeVerbs({ api, token, fetchImpl = fetch, startApp, pending, ro
114
114
  }
115
115
  // The run is over: what the app wrote into its folder during it is put back, said before the next step.
116
116
  let back = "";
117
- if (writes && finished(res.data)) { try { back = writes.putBack(id); } catch { back = ""; } }
117
+ if (writes && finished(res.data)) { try { back = writes.putBack(); } catch { back = ""; } }
118
118
  return { text: back ? withBack(runText(res.data), back) : runText(res.data), data: res.data };
119
119
  };
120
120
 
package/lib/writes.mjs CHANGED
@@ -8,19 +8,23 @@ import { dirname, join, resolve, sep } from "node:path";
8
8
 
9
9
  export const writesDirOf = (home) => join(home, "writes");
10
10
 
11
- // A run has started: from now until it ends, the hook records what the app writes.
12
- export function markRun(dir, jobId) {
11
+ // A run has started: from now until it ends, the hook records what the app writes. A record that
12
+ // is already open is kept when `keep` is set: an app started again mid-run keeps the copies made
13
+ // before it went down.
14
+ export function markRun(dir, jobId, { keep = false } = {}) {
15
+ if (keep && existsSync(join(dir, "run"))) return;
13
16
  try { rmSync(dir, { recursive: true, force: true }); } catch { /* fresh */ }
14
17
  mkdirSync(dir, { recursive: true, mode: 0o700 });
15
18
  writeFileSync(join(dir, "run"), String(jobId), { mode: 0o600 });
16
19
  }
17
20
 
18
- // The run has ended: every file it wrote is put back, and the line says which. Nothing happens
19
- // for a run that is not the one marked, so an old id read late never undoes a newer run's work.
20
- export function putBack(dir, root, jobId) {
21
+ // The run has ended: every file written since the record began is put back, and the line says
22
+ // which. The record begins when the command starts the app (the first run starts itself on the
23
+ // server's side, so no verb marks it) and again at every run a verb starts, so what a person wrote
24
+ // between two runs is dropped from the record rather than undone.
25
+ export function putBack(dir, root) {
21
26
  let rows = [];
22
27
  try {
23
- if (readFileSync(join(dir, "run"), "utf8").trim() !== String(jobId)) return "";
24
28
  rows = readFileSync(join(dir, "written.jsonl"), "utf8").split("\n").filter(Boolean)
25
29
  .map((line) => { try { return JSON.parse(line); } catch { return null; } }).filter(Boolean);
26
30
  } catch { /* the run wrote nothing */ }
package/local.mjs CHANGED
@@ -33,6 +33,7 @@ import { listingUrl } from "./lib/listing.mjs";
33
33
  import { installPlan, missingDependency, startPlan, workspaces } from "./lib/start.mjs";
34
34
  import { openSwitches } from "./lib/switches.mjs";
35
35
  import { keepData } from "./lib/data.mjs";
36
+ import { markRun, writesDirOf } from "./lib/writes.mjs";
36
37
 
37
38
  const argv = process.argv.slice(2);
38
39
  // The two faces a coding agent uses after the first connect (lib/cli.mjs): the MCP server the
@@ -768,6 +769,9 @@ function dataOnce(started) {
768
769
 
769
770
  async function start(waitMs) {
770
771
  const { cmd, lifted } = await freed(launched.cmd, launched.lifted ?? {});
772
+ // The record of what the app writes opens with the app: the first run starts itself on the
773
+ // server's side, with no verb here to mark it, and a restart mid-run keeps the record open.
774
+ try { markRun(writesDirOf(homeOf(projectOf(root))), "session", { keep: true }); } catch { /* the app's writes go unrecorded */ }
771
775
  child = spawn("/bin/sh", ["-c", cmd], { cwd: appDir, env: { ...envExports(envFiles), ...process.env, ...lifted, ...dataOnce(true).env, ...(capture ? capture.env(process.env) : {}), FORCE_COLOR: "0", ...(pinned.bin ? { PATH: `${pinned.bin}:${process.env.PATH ?? ""}` } : {}) }, stdio: ["ignore", "pipe", "pipe"], detached: true });
772
776
  const mine = child;
773
777
  let seen = "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortad",
3
- "version": "0.3.1-rc.5",
3
+ "version": "0.3.1-rc.6",
4
4
  "description": "Connects the AI app on your machine to Cortad for test conversations, and gives your coding agent the MCP and skill to run them. No dependencies.",
5
5
  "bin": {
6
6
  "cortad": "local.mjs"