origem 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +31 -25
  2. package/dist/index.js +278 -272
  3. package/package.json +12 -5
package/dist/index.js CHANGED
@@ -22,14 +22,14 @@ function yamlScalar(value) {
22
22
  return /[:#"'{}\[\],&*?|<>=!%@`]/.test(v) ? JSON.stringify(v) : v;
23
23
  }
24
24
  var DEFAULT_SECTIONS = [
25
- { type: "instruction", title: "Instru\xE7\xF5es" },
26
- { type: "spec", title: "Especifica\xE7\xF5es (requisitos)" },
25
+ { type: "instruction", title: "Instructions" },
26
+ { type: "spec", title: "Specs (requirements)" },
27
27
  { type: "boundary", title: "Limites (Sempre / Perguntar antes / Nunca)" },
28
- { type: "rule", title: "Regras & Instru\xE7\xF5es" },
29
- { type: "memory", title: "Mem\xF3rias & Contexto" },
28
+ { type: "rule", title: "Rules & Instructions" },
29
+ { type: "memory", title: "Memory & Context" },
30
30
  { type: "knowledge", title: "Conhecimento" },
31
31
  { type: "prompt", title: "Prompts" },
32
- { type: "integration", title: "Integra\xE7\xF5es" }
32
+ { type: "integration", title: "Integrations" }
33
33
  ];
34
34
  function renderMarkdown(brain, sections = DEFAULT_SECTIONS) {
35
35
  const lines = [];
@@ -38,7 +38,7 @@ function renderMarkdown(brain, sections = DEFAULT_SECTIONS) {
38
38
  lines.push(`
39
39
  > ${brain.brain.description}`);
40
40
  lines.push(`
41
- _Configura\xE7\xE3o gerada pelo Origem. Adote as regras, mem\xF3rias e conhecimento abaixo como contexto operacional._`);
41
+ _Configuration generated by Origem. Adopt the rules, memory and knowledge below as your operating context._`);
42
42
  for (const section of sections) {
43
43
  const group = byType(brain.cards, section.type);
44
44
  if (group.length === 0)
@@ -61,16 +61,16 @@ ${card.content}`);
61
61
 
62
62
  // ../origem-adapters/dist/adapters.js
63
63
  var GENERIC_SECTIONS = [
64
- { type: "instruction", title: "Instru\xE7\xF5es" },
65
- { type: "spec", title: "Especifica\xE7\xF5es (requisitos)" },
64
+ { type: "instruction", title: "Instructions" },
65
+ { type: "spec", title: "Specs (requirements)" },
66
66
  { type: "boundary", title: "Limites (Sempre / Perguntar antes / Nunca)" },
67
- { type: "rule", title: "Regras & Padr\xF5es" },
68
- { type: "memory", title: "Mem\xF3rias & Contexto" },
67
+ { type: "rule", title: "Rules & Standards" },
68
+ { type: "memory", title: "Memory & Context" },
69
69
  { type: "knowledge", title: "Conhecimento" },
70
70
  { type: "skill", title: "Skills / Capacidades" },
71
71
  { type: "agent", title: "Subagentes" },
72
72
  { type: "prompt", title: "Prompts & Comandos" },
73
- { type: "integration", title: "Integra\xE7\xF5es" }
73
+ { type: "integration", title: "Integrations" }
74
74
  ];
75
75
  function toAgentsMd(brain) {
76
76
  return [
@@ -84,13 +84,13 @@ function toAgentsMd(brain) {
84
84
  ];
85
85
  }
86
86
  var CLAUDE_SECTIONS = [
87
- { type: "instruction", title: "Instru\xE7\xF5es" },
88
- { type: "spec", title: "Especifica\xE7\xF5es (requisitos)" },
87
+ { type: "instruction", title: "Instructions" },
88
+ { type: "spec", title: "Specs (requirements)" },
89
89
  { type: "boundary", title: "Limites (Sempre / Perguntar antes / Nunca)" },
90
- { type: "rule", title: "Regras & Padr\xF5es" },
91
- { type: "memory", title: "Mem\xF3rias & Contexto" },
90
+ { type: "rule", title: "Rules & Standards" },
91
+ { type: "memory", title: "Memory & Context" },
92
92
  { type: "knowledge", title: "Conhecimento" },
93
- { type: "integration", title: "Integra\xE7\xF5es" }
93
+ { type: "integration", title: "Integrations" }
94
94
  ];
95
95
  function toClaudeMd(brain) {
96
96
  return [
@@ -348,14 +348,14 @@ var TARGET_CAPABILITIES = [
348
348
  mcp: "none"
349
349
  // vira .mcp.json
350
350
  },
351
- note: "Instru\xE7\xF5es can\xF4nicas. Prompt/skill/agent/harness/mcp viram arquivos pr\xF3prios do Claude."
351
+ note: "Canonical instructions. Prompt/skill/agent/harness/mcp become their own Claude files."
352
352
  },
353
353
  {
354
354
  id: "claude-settings",
355
355
  label: "Claude Code \xB7 settings.json",
356
356
  path: ".claude/settings.json",
357
357
  support: onlyNative("harness"),
358
- note: "Permiss\xF5es, env, hooks e modelo. Privilegiado (executa na m\xE1quina)."
358
+ note: "Permissions, env, hooks and model. Privileged (runs on your machine)."
359
359
  },
360
360
  {
361
361
  id: "claude-skills",
@@ -380,14 +380,14 @@ var TARGET_CAPABILITIES = [
380
380
  label: "MCP servers \xB7 .mcp.json",
381
381
  path: ".mcp.json",
382
382
  support: onlyNative("mcp"),
383
- note: "Port\xE1vel em conceito; o CAMINHO varia por ferramenta (Cursor: .cursor/mcp.json, VS Code: .vscode/mcp.json)."
383
+ note: "Portable in concept; the PATH varies per tool (Cursor: .cursor/mcp.json, VS Code: .vscode/mcp.json)."
384
384
  },
385
385
  {
386
386
  id: "agents",
387
387
  label: "AGENTS.md (Codex, Windsurf, Zed, Aider\u2026)",
388
388
  path: "AGENTS.md",
389
389
  support: ALL_DEGRADED,
390
- note: "Padr\xE3o convergido de instru\xE7\xF5es. Sem conceito nativo de skills/subagentes \u2192 viram documenta\xE7\xE3o."
390
+ note: "The converged instruction standard. No native skills/subagents concept \u2192 they become documentation."
391
391
  },
392
392
  {
393
393
  id: "gemini",
@@ -418,7 +418,7 @@ var TARGET_CAPABILITIES = [
418
418
  skill: "none",
419
419
  agent: "none"
420
420
  },
421
- note: "Nosso adapter gera s\xF3 regras (.mdc). Cursor tamb\xE9m l\xEA AGENTS.md e .cursor/mcp.json (roadmap)."
421
+ note: "Our adapter emits rules only (.mdc). Cursor also reads AGENTS.md and .cursor/mcp.json (roadmap)."
422
422
  }
423
423
  ];
424
424
  function onlyNative(type) {
@@ -597,36 +597,36 @@ async function fetchBrain(apiUrl, key) {
597
597
  try {
598
598
  res = await fetch(url);
599
599
  } catch (e) {
600
- throw new Error(`N\xE3o consegui conectar em ${base}: ${e.message}`);
600
+ throw new Error(`Could not reach ${base}: ${e.message}`);
601
601
  }
602
602
  if (res.status === 401) {
603
- throw new Error("Token inv\xE1lido, expirado ou revogado. Gere um novo link no Origem.");
603
+ throw new Error("Token is invalid, expired or revoked. Run `origem connect` again.");
604
604
  }
605
605
  if (res.status === 403) {
606
- throw new Error("Token sem permiss\xE3o de leitura.");
606
+ throw new Error("Token has no read permission.");
607
607
  }
608
608
  if (!res.ok) {
609
- throw new Error(`Falha ao puxar o Brain (HTTP ${res.status}).`);
609
+ throw new Error(`Failed to pull the harness (HTTP ${res.status}).`);
610
610
  }
611
611
  const payload = await res.json();
612
612
  const secret = process.env.ORIGEM_SIGNING_SECRET;
613
613
  if (payload.signature) {
614
614
  if (!secret) {
615
615
  warn(
616
- "O payload vem assinado, mas ORIGEM_SIGNING_SECRET n\xE3o est\xE1 definido \u2014 n\xE3o \xE9 poss\xEDvel verificar a origem. Config privilegiada ser\xE1 bloqueada."
616
+ "The payload is signed but ORIGEM_SIGNING_SECRET is not set \u2014 its origin cannot be verified. Privileged config will be blocked."
617
617
  );
618
618
  payload.__unverified = true;
619
619
  } else {
620
620
  const { signature, ...rest } = payload;
621
621
  if (!verifyPayload(rest, signature, secret)) {
622
622
  throw new Error(
623
- "Assinatura inv\xE1lida \u2014 o Brain pode ter sido adulterado. Abortando."
623
+ "Invalid signature \u2014 the harness may have been tampered with. Aborting."
624
624
  );
625
625
  }
626
626
  }
627
627
  } else if (secret) {
628
628
  warn(
629
- "ORIGEM_SIGNING_SECRET est\xE1 definido, mas o payload N\xC3O vem assinado. Config privilegiada ser\xE1 bloqueada."
629
+ "ORIGEM_SIGNING_SECRET is set but the payload is NOT signed. Privileged config will be blocked."
630
630
  );
631
631
  payload.__unverified = true;
632
632
  }
@@ -640,7 +640,7 @@ function resolveKey(flag) {
640
640
  const key = flag || process.env.ORIGEM_TOKEN || readConfig().token || "";
641
641
  if (!key) {
642
642
  throw new Error(
643
- "Token ausente. Rode `origem login --key <token>` uma vez (grava em ~/.origem/config.json), ou passe --key / ORIGEM_TOKEN. Gere o link em 'Conectar com IA' no app Origem."
643
+ "No token. Run `origem connect` once \u2014 it stores the credential in ~/.origem/config.json \u2014 or pass --key / ORIGEM_TOKEN."
644
644
  );
645
645
  }
646
646
  return key;
@@ -655,20 +655,20 @@ async function probeToken(apiUrl, key) {
655
655
  signal: AbortSignal.timeout(1e4)
656
656
  });
657
657
  } catch (e) {
658
- throw new Error(`N\xE3o consegui falar com ${apiUrl}: ${e.message}`);
658
+ throw new Error(`Could not reach ${apiUrl}: ${e.message}`);
659
659
  }
660
- if (res.status === 401) throw new Error("Token inv\xE1lido, expirado ou revogado.");
660
+ if (res.status === 401) throw new Error("Token is invalid, expired or revoked.");
661
661
  if (res.status === 403) {
662
662
  throw new Error(
663
- "Token sem escopo de escrita. O watch e o hook precisam de um link de escrita (ingest\xE3o)."
663
+ "Token has no write scope. Watch and hooks need a write-scoped token."
664
664
  );
665
665
  }
666
666
  if (res.status === 404) {
667
667
  throw new Error(
668
- `Esta API n\xE3o tem o endpoint /run (HTTP 404). Ela precisa estar publicada com o m\xF3dulo de runs.`
668
+ `This API has no /run endpoint (HTTP 404). It must be deployed with the runs module.`
669
669
  );
670
670
  }
671
- if (!res.ok) throw new Error(`Falha na verifica\xE7\xE3o (HTTP ${res.status}).`);
671
+ if (!res.ok) throw new Error(`Verification failed (HTTP ${res.status}).`);
672
672
  }
673
673
 
674
674
  // src/fsutil.ts
@@ -701,7 +701,7 @@ function ensureDir(abs) {
701
701
  function wrapBlock(content) {
702
702
  const body = content.replace(/\n+$/, "");
703
703
  return `${MB_START}
704
- <!-- Gerado pelo Origem (origem apply). N\xE3o edite dentro do bloco. -->
704
+ <!-- Generated by Origem. Do not edit inside this block. -->
705
705
  ${body}
706
706
  ${MB_END}
707
707
  `;
@@ -767,7 +767,7 @@ function diskManagedHash(abs, strategy, jsonKeys) {
767
767
  }
768
768
  }
769
769
  function unwrapBlock(block) {
770
- return block.replace(MB_START, "").replace(MB_END, "").replace(/^\s*<!-- Gerado pelo Origem[^\n]*-->\n?/, "").replace(/^\n+/, "").replace(/\n+$/, "");
770
+ return block.replace(MB_START, "").replace(MB_END, "").replace(/^\s*<!-- (?:Generated by|Gerado pelo) Origem[^\n]*-->\n?/, "").replace(/^\n+/, "").replace(/\n+$/, "");
771
771
  }
772
772
  function writeTarget(abs, strategy, content) {
773
773
  ensureDir(abs);
@@ -847,19 +847,19 @@ function buildPlan(brain, scope, targets, lock) {
847
847
  return { items, skipped };
848
848
  }
849
849
  function actionLabel(a) {
850
- if (a === "create") return color.green("criar ");
851
- if (a === "update") return color.yellow("atualizar");
850
+ if (a === "create") return color.green("create ");
851
+ if (a === "update") return color.yellow("update ");
852
852
  return color.dim("ok ");
853
853
  }
854
854
  function printPlan(items, skipped) {
855
- info(color.bold("\nPlano:"));
855
+ info(color.bold("\nPlan:"));
856
856
  for (const it of items) {
857
- const risk = it.risk === "privileged" ? color.red(" [privilegiado]") : "";
858
- const drift = it.driftedLocally ? color.yellow(" (editado localmente)") : "";
857
+ const risk = it.risk === "privileged" ? color.red(" [privileged]") : "";
858
+ const drift = it.driftedLocally ? color.yellow(" (edited locally)") : "";
859
859
  info(` ${actionLabel(it.action)} ${it.output.path}${risk}${drift}`);
860
860
  }
861
861
  for (const s of skipped) {
862
- info(` ${color.dim("pular ")} ${s.path} ${color.dim("(n\xE3o se aplica a este escopo)")}`);
862
+ info(` ${color.dim("skip ")} ${s.path} ${color.dim("(does not apply to this scope)")}`);
863
863
  }
864
864
  }
865
865
  async function runApply(opts) {
@@ -874,8 +874,8 @@ async function runApply(opts) {
874
874
  const orphans = (lock?.files ?? []).filter((f) => !currentPaths.has(f.path));
875
875
  printPlan(items, skipped);
876
876
  if (orphans.length) {
877
- info(color.bold("\nRemover (n\xE3o est\xE3o mais no Brain):"));
878
- for (const o of orphans) info(` ${color.red("remover ")} ${o.path}`);
877
+ info(color.bold("\nRemove (no longer in the harness):"));
878
+ for (const o of orphans) info(` ${color.red("remove ")} ${o.path}`);
879
879
  }
880
880
  if (dryRun) {
881
881
  info(color.dim("\n(dry-run \u2014 nada foi escrito.)"));
@@ -885,18 +885,18 @@ async function runApply(opts) {
885
885
  if (privileged.length > 0) {
886
886
  if (unverified) {
887
887
  warn(
888
- "Config privilegiada bloqueada: o payload n\xE3o p\xF4de ser verificado (assinatura ausente/ inv\xE1lida)."
888
+ "Privileged config blocked: the payload could not be verified (missing or invalid signature)."
889
889
  );
890
890
  allowPrivileged = false;
891
891
  } else if (yes) {
892
892
  allowPrivileged = true;
893
893
  } else if (!process.stdin.isTTY) {
894
- warn("Sem terminal interativo \u2014 pulando config privilegiada. Use --yes para aplicar.");
894
+ warn("No interactive terminal \u2014 skipping privileged config. Use --yes to apply it.");
895
895
  allowPrivileged = false;
896
896
  } else {
897
- info(color.bold("\n\u26A0 Config privilegiada (executa comandos/segredos na sua m\xE1quina):"));
897
+ info(color.bold("\n\u26A0 Privileged config (runs commands / holds secrets on your machine):"));
898
898
  for (const p of privileged) info(` \u2022 ${p.output.path}`);
899
- allowPrivileged = await confirm("Aplicar tamb\xE9m esses arquivos privilegiados?");
899
+ allowPrivileged = await confirm("Apply these privileged files too?");
900
900
  }
901
901
  }
902
902
  for (const o of orphans) {
@@ -933,32 +933,32 @@ async function runApply(opts) {
933
933
  files: [...nextEntries.values()]
934
934
  });
935
935
  ok(
936
- `Aplicado: ${written} arquivo(s) escrito(s), ${orphans.length} removido(s). Estado em ${color.cyan(".origem/lock.json")}.`
936
+ `Applied: ${written} file(s) written, ${orphans.length} removed. State in ${color.cyan(".origem/lock.json")}.`
937
937
  );
938
938
  if (!allowPrivileged && privileged.length > 0) {
939
- warn(`${privileged.length} arquivo(s) privilegiado(s) N\xC3O aplicado(s).`);
939
+ warn(`${privileged.length} privileged file(s) NOT applied.`);
940
940
  }
941
941
  }
942
942
  function runStatus(brain, scope, targets) {
943
943
  const baseDir = process.cwd();
944
944
  const lock = readLock(baseDir);
945
945
  if (!lock) {
946
- info("N\xE3o inicializado neste diret\xF3rio. Rode `origem apply`.");
946
+ info("Not initialised here. Run `origem connect`.");
947
947
  return;
948
948
  }
949
949
  const remoteRev = brain.origem?.revision ?? null;
950
950
  const localRev = lock.revision;
951
- info(color.bold(`Brain: ${lock.brainName ?? lock.brainId ?? "?"}`));
951
+ info(color.bold(`Harness: ${lock.brainName ?? lock.brainId ?? "?"}`));
952
952
  info(
953
- `Revis\xE3o local: ${localRev ?? "?"} \xB7 remota: ${remoteRev ?? "?"} ` + (remoteRev !== null && localRev !== remoteRev ? color.yellow("(desatualizado \u2014 rode `origem apply`)") : color.green("(em dia)"))
953
+ `Local revision: ${localRev ?? "?"} \xB7 remote: ${remoteRev ?? "?"} ` + (remoteRev !== null && localRev !== remoteRev ? color.yellow("(out of date \u2014 run `origem connect`)") : color.green("(up to date)"))
954
954
  );
955
955
  const { items } = buildPlan(brain, scope, targets, lock);
956
- info(color.bold("\nArquivos:"));
956
+ info(color.bold("\nFiles:"));
957
957
  for (const it of items) {
958
958
  const parts = [];
959
959
  if (it.action !== "unchanged") parts.push(color.yellow(it.action));
960
- if (it.driftedLocally) parts.push(color.yellow("editado localmente"));
961
- if (parts.length === 0) parts.push(color.green("em dia"));
960
+ if (it.driftedLocally) parts.push(color.yellow("edited locally"));
961
+ if (parts.length === 0) parts.push(color.green("up to date"));
962
962
  info(` ${it.output.path} \u2014 ${parts.join(", ")}`);
963
963
  }
964
964
  }
@@ -966,7 +966,7 @@ function runRemove(scope) {
966
966
  const baseDir = process.cwd();
967
967
  const lock = readLock(baseDir);
968
968
  if (!lock) {
969
- info("Nada para remover (sem .origem/lock.json).");
969
+ info("Nothing to remove (no .origem/lock.json).");
970
970
  return;
971
971
  }
972
972
  for (const f of lock.files) {
@@ -974,7 +974,7 @@ function runRemove(scope) {
974
974
  if (abs) removeTarget(abs, f.strategy, f.jsonKeys);
975
975
  }
976
976
  deleteLock(baseDir);
977
- ok(`Removidos ${lock.files.length} arquivo(s) do Origem e limpo o lock.`);
977
+ ok(`Removed ${lock.files.length} Origem file(s) and cleared the lock.`);
978
978
  }
979
979
 
980
980
  // src/hook.ts
@@ -1078,7 +1078,7 @@ var HOOK_ENTRIES = (bin, args) => ({
1078
1078
  type: "command",
1079
1079
  command: `${bin} hook stop${args}`,
1080
1080
  timeout: 15,
1081
- statusMessage: "Reportando ao Origem\u2026"
1081
+ statusMessage: "Reporting to Origem\u2026"
1082
1082
  }
1083
1083
  ]
1084
1084
  }
@@ -1097,22 +1097,22 @@ async function hookInstall(opts) {
1097
1097
  const target = join4(process.cwd(), ".claude", file);
1098
1098
  const args = (opts.key ? ` --key ${shellQuote(opts.key)}` : "") + (opts.apiUrl ? ` --api-url ${shellQuote(opts.apiUrl)}` : "");
1099
1099
  const snippet = HOOK_ENTRIES(opts.bin, args);
1100
- info(`${color.bold("Vai escrever em:")} ${target}`);
1100
+ info(`${color.bold("Will write to:")} ${target}`);
1101
1101
  info(JSON.stringify(snippet, null, 2));
1102
1102
  info(
1103
1103
  color.yellow(
1104
- "\nIsto \xE9 config privilegiada: os hooks rodam um comando na sua m\xE1quina a cada\nturno, e o `stop` pode CONTINUAR a sess\xE3o com um prompt vindo do site.\nQuem escrever na fila do Brain dirige esta sess\xE3o."
1104
+ "\nThis is privileged config: the hooks run a command on your machine every\nturn, and `stop` can CONTINUE the session with a prompt coming from the site.\nWhoever can write to the queue drives this session."
1105
1105
  )
1106
1106
  );
1107
1107
  if (opts.key && !opts.local) {
1108
1108
  info(
1109
1109
  color.yellow(
1110
- "O token vai ficar GRAVADO neste arquivo. Se ele for versionado, use --local\n(.claude/settings.local.json, ignorado pelo git) ou exporte ORIGEM_TOKEN."
1110
+ "The token will be STORED in this file. If it is committed, use --local\n(.claude/settings.local.json, which git ignores) or export ORIGEM_TOKEN."
1111
1111
  )
1112
1112
  );
1113
1113
  }
1114
- if (!opts.yes && !await confirm("Instalar os hooks?")) {
1115
- warn("Nada foi escrito.");
1114
+ if (!opts.yes && !await confirm("Install the hooks?")) {
1115
+ warn("Nothing was written.");
1116
1116
  return;
1117
1117
  }
1118
1118
  let existing = {};
@@ -1120,25 +1120,25 @@ async function hookInstall(opts) {
1120
1120
  try {
1121
1121
  existing = JSON.parse(readFileSync4(target, "utf8"));
1122
1122
  } catch {
1123
- throw new Error(`${target} existe mas n\xE3o \xE9 JSON v\xE1lido \u2014 corrija antes de instalar.`);
1123
+ throw new Error(`${target} exists but is not valid JSON \u2014 fix it before installing.`);
1124
1124
  }
1125
1125
  }
1126
1126
  const merged = mergeHooks(existing, snippet.hooks);
1127
1127
  mkdirSync4(dirname4(target), { recursive: true });
1128
1128
  writeFileSync4(target, `${JSON.stringify(merged, null, 2)}
1129
1129
  `);
1130
- ok(`Hooks instalados em ${target}`);
1130
+ ok(`Hooks installed in ${target}`);
1131
1131
  if (opts.key || opts.apiUrl) {
1132
1132
  const p = saveConfig({
1133
1133
  ...opts.key ? { token: opts.key } : {},
1134
1134
  ...opts.apiUrl ? { apiUrl: opts.apiUrl } : {}
1135
1135
  });
1136
- info(color.dim(`Credenciais tamb\xE9m gravadas em ${p} \u2014 \`origem watch\` j\xE1 roda sem flag.`));
1136
+ info(color.dim(`Credentials also stored in ${p} \u2014 \`origem watch\` now runs with no flags.`));
1137
1137
  }
1138
1138
  if (!opts.key) {
1139
1139
  info(
1140
1140
  color.dim(
1141
- "Sem --key: o hook usa ORIGEM_TOKEN do ambiente do processo do Claude Code.\nNuma sess\xE3o j\xE1 aberta isso n\xE3o pega \u2014 reinicie o Claude com a vari\xE1vel exportada,\nou reinstale com --key."
1141
+ "Without --key the hook uses ORIGEM_TOKEN from the Claude Code process environment.\nIn an already-open session that will not apply \u2014 restart Claude with the variable\nexported, or reinstall with --key."
1142
1142
  )
1143
1143
  );
1144
1144
  }
@@ -1226,7 +1226,7 @@ function resolveClaudeBin(flag) {
1226
1226
  const found = candidates().find(isExecutable);
1227
1227
  if (!found) {
1228
1228
  throw new Error(
1229
- "N\xE3o achei o bin\xE1rio do Claude Code. Passe --claude <caminho> (no VS Code fica em ~/.vscode/extensions/anthropic.claude-code-*/resources/native-binary/claude)."
1229
+ "Could not find the Claude Code binary. Pass --claude <path> (no VS Code fica em ~/.vscode/extensions/anthropic.claude-code-*/resources/native-binary/claude)."
1230
1230
  );
1231
1231
  }
1232
1232
  saveConfig({ claudeBin: found });
@@ -1266,27 +1266,27 @@ function runClaude(bin, opts, prompt, resumeId) {
1266
1266
  }
1267
1267
  async function runWatch(opts) {
1268
1268
  const bin = resolveClaudeBin(opts.claudeBin);
1269
- info(`${color.bold("origem watch")} \u2014 plant\xE3o local`);
1269
+ info(`${color.bold("origem watch")} \u2014 local runner`);
1270
1270
  info(` fila: ${opts.apiUrl}/run`);
1271
- info(` diret\xF3rio: ${opts.cwd}`);
1271
+ info(` directory: ${opts.cwd}`);
1272
1272
  info(` claude: ${bin}`);
1273
1273
  info(opts.once ? " modo: uma passada" : ` intervalo: ${Math.round(opts.intervalMs / 1e3)}s`);
1274
1274
  info(
1275
1275
  color.yellow(
1276
- "\nEnquanto isto roda, quem escrever na fila do Brain FAZ o Claude Code rodar\nnesta m\xE1quina, neste diret\xF3rio. O que trafega \xE9 prompt, e as permiss\xF5es do\nprojeto continuam valendo \u2014 mas o gatilho passa a ser remoto."
1276
+ "\nWhile this runs, whoever writes to the queue MAKES Claude Code run on this\nmachine, in this directory. What travels is a prompt, and Claude Code\nprojeto continuam valendo \u2014 mas o gatilho passa a ser remoto."
1277
1277
  )
1278
1278
  );
1279
1279
  if (opts.permissionMode === "bypassPermissions") {
1280
- warn("--permission-mode bypassPermissions desliga TODA checagem. N\xE3o use com fila remota.");
1280
+ warn("--permission-mode bypassPermissions turns OFF every check. Do not use it with a remote queue.");
1281
1281
  }
1282
- if (!opts.dryRun && !opts.once && !opts.yes && !await confirm("\nIniciar o plant\xE3o?")) {
1282
+ if (!opts.dryRun && !opts.once && !opts.yes && !await confirm("\nStart the runner?")) {
1283
1283
  warn("Cancelado.");
1284
1284
  return;
1285
1285
  }
1286
1286
  let stop = false;
1287
1287
  process.on("SIGINT", () => {
1288
1288
  stop = true;
1289
- info("\nEncerrando o plant\xE3o\u2026");
1289
+ info("\nShutting the runner down\u2026");
1290
1290
  });
1291
1291
  let quiet = 0;
1292
1292
  while (!stop) {
@@ -1309,18 +1309,18 @@ async function runWatch(opts) {
1309
1309
  await sleep(opts.intervalMs);
1310
1310
  continue;
1311
1311
  }
1312
- const alvo = command.targetSessionId ? `retomando a sess\xE3o ${command.targetSessionId}` : "sess\xE3o nova";
1312
+ const alvo = command.targetSessionId ? `resuming session ${command.targetSessionId}` : "new session";
1313
1313
  ok(`Comando recebido (${alvo})`);
1314
1314
  info(` ${color.dim(command.prompt)}`);
1315
1315
  if (opts.dryRun) {
1316
- info(color.dim(" --dry-run: n\xE3o executei."));
1316
+ info(color.dim(" --dry-run: did not execute."));
1317
1317
  if (opts.once) return;
1318
1318
  await sleep(opts.intervalMs);
1319
1319
  continue;
1320
1320
  }
1321
1321
  const code = await runClaude(bin, opts, command.prompt, command.targetSessionId);
1322
- if (code === 0) ok("Comando conclu\xEDdo.");
1323
- else warn(`Claude Code saiu com c\xF3digo ${code}.`);
1322
+ if (code === 0) ok("Command finished.");
1323
+ else warn(`Claude Code exited with code ${code}.`);
1324
1324
  if (opts.once) return;
1325
1325
  }
1326
1326
  }
@@ -1330,24 +1330,24 @@ import { rmSync as rmSync2, existsSync as existsSync6 } from "node:fs";
1330
1330
  async function runLogin(opts) {
1331
1331
  const key = opts.key || process.env.ORIGEM_TOKEN;
1332
1332
  if (!key) {
1333
- throw new Error("Passe --key <token>. Gere o link de escrita em 'Conectar com IA' no Origem.");
1333
+ throw new Error("Pass --key <token>, or just run `origem connect`.");
1334
1334
  }
1335
- info(`Verificando o token em ${color.cyan(opts.apiUrl)}\u2026`);
1335
+ info(`Checking the token against ${color.cyan(opts.apiUrl)}\u2026`);
1336
1336
  await probeToken(opts.apiUrl, key);
1337
1337
  const p = saveConfig({ token: key, apiUrl: opts.apiUrl });
1338
- ok(`Token v\xE1lido, com escopo de escrita. Gravado em ${p} (0600).`);
1339
- info(color.dim("Agora `origem watch` roda sem flag nenhuma."));
1338
+ ok(`Token is valid, with write scope. Stored in ${p} (0600).`);
1339
+ info(color.dim("`origem watch` now runs with no flags."));
1340
1340
  }
1341
1341
  function runLogout() {
1342
1342
  const p = configPath();
1343
1343
  if (!existsSync6(p)) {
1344
- warn("Nada gravado.");
1344
+ warn("Nothing stored.");
1345
1345
  return;
1346
1346
  }
1347
1347
  const { claudeBin } = readConfig();
1348
1348
  rmSync2(p);
1349
1349
  if (claudeBin) saveConfig({ claudeBin });
1350
- ok(`Credenciais removidas de ${p}.`);
1350
+ ok(`Credentials removed from ${p}.`);
1351
1351
  }
1352
1352
 
1353
1353
  // src/connect.ts
@@ -1363,102 +1363,102 @@ import { dirname as dirname5, join as join6 } from "node:path";
1363
1363
  var SKILL_PATH = join6(".claude", "skills", "origem", "SKILL.md");
1364
1364
  var SKILL_MD = `---
1365
1365
  name: origem
1366
- description: Sincroniza e inspeciona o harness deste reposit\xF3rio, que \xE9 gerenciado pelo Origem. Use quando o usu\xE1rio pedir para atualizar/sincronizar/puxar o harness ou a config da IA, perguntar de onde v\xEAm CLAUDE.md ou .claude/*, quiser saber se a config local divergiu do Origem, ou pedir para mudar uma regra, skill, subagente ou permiss\xE3o de forma permanente.
1366
+ description: Syncs and inspects this repository's harness, which is managed by Origem. Use when the user asks to update/sync/pull the harness or the AI config, asks where CLAUDE.md or .claude/* come from, wants to know whether the local config drifted from Origem, or asks to change a rule, skill, subagent or permission permanently.
1367
1367
  ---
1368
1368
 
1369
- # Harness gerenciado pelo Origem
1369
+ # Harness managed by Origem
1370
1370
 
1371
- Os arquivos de configura\xE7\xE3o deste reposit\xF3rio \u2014 \`CLAUDE.md\`, \`.claude/settings.json\`,
1372
- \`.claude/skills/\`, \`.claude/agents/\`, \`.claude/commands/\` e \`.mcp.json\` \u2014 n\xE3o foram
1373
- escritos \xE0 m\xE3o aqui. Eles s\xE3o materializados a partir de um harness guardado no Origem,
1374
- e o invent\xE1rio do que foi escrito est\xE1 em \`.origem/lock.json\`.
1371
+ This repository's configuration files \u2014 \`CLAUDE.md\`, \`.claude/settings.json\`,
1372
+ \`.claude/skills/\`, \`.claude/agents/\`, \`.claude/commands/\` and \`.mcp.json\` \u2014 were not
1373
+ written by hand here. They are materialised from a harness stored in Origem, and the
1374
+ inventory of what was written lives in \`.origem/lock.json\`.
1375
1375
 
1376
- ## O que isso muda no seu comportamento
1376
+ ## What this changes about your behaviour
1377
1377
 
1378
- **Mudan\xE7a permanente vai para o Origem, n\xE3o para o arquivo.** Se o usu\xE1rio pedir uma regra
1379
- nova, uma skill nova, uma permiss\xE3o diferente ou qualquer coisa que deva valer nas pr\xF3ximas
1380
- sess\xF5es, editar o arquivo local funciona at\xE9 o pr\xF3ximo \`origem connect\` \u2014 e some ali.
1381
- Diga isso ao usu\xE1rio e ofere\xE7a registrar a mudan\xE7a no Origem.
1378
+ **A permanent change goes to Origem, not to the file.** If the user asks for a new rule,
1379
+ a new skill, a different permission, or anything that should hold in future sessions,
1380
+ editing the local file works until the next \`origem connect\` \u2014 and disappears there.
1381
+ Say so, and offer to record the change in Origem.
1382
1382
 
1383
- **Um arquivo listado no lock \xE9 territ\xF3rio do Origem.** Antes de editar \`CLAUDE.md\` ou
1384
- qualquer coisa sob \`.claude/\`, confira \`.origem/lock.json\`. Arquivos fora dessa lista s\xE3o
1385
- do projeto e voc\xEA edita normalmente.
1383
+ **A file listed in the lock is Origem's territory.** Before editing \`CLAUDE.md\` or
1384
+ anything under \`.claude/\`, check \`.origem/lock.json\`. Files outside that list belong to
1385
+ the project and you edit them normally.
1386
1386
 
1387
- ## Comandos
1387
+ ## Commands
1388
1388
 
1389
- | Comando | O que faz |
1389
+ | Command | What it does |
1390
1390
  |---|---|
1391
- | \`npx origem status\` | Mostra o que mudaria e se h\xE1 edi\xE7\xE3o local divergente. \xC9 a leitura segura \u2014 n\xE3o escreve nada. |
1392
- | \`npx origem connect\` | Autoriza (navegador), puxa a vers\xE3o mais nova e reescreve os arquivos. \xC9 tamb\xE9m o comando de sincronizar. |
1393
- | \`npx origem apply\` | Reaplica com a credencial j\xE1 gravada, sem passar pelo navegador. |
1394
- | \`npx origem apply --dry-run\` | S\xF3 imprime o plano. |
1395
- | \`npx origem remove\` | Reverte tudo que o Origem escreveu, usando o lock. |
1396
- | \`npx origem propose\` | Sugere uma mudan\xE7a no harness. **N\xE3o escreve nada** \u2014 fica pendente at\xE9 a pessoa aceitar no site. |
1397
- | \`npx origem spec\` | Mostra os specs abertos e em que est\xE1gio cada um est\xE1. |
1398
- | \`npx origem doctor\` | Compara o harness com este reposit\xF3rio e aponta o que divergiu. |
1399
- | \`npx origem bootstrap\` | L\xEA o reposit\xF3rio e prop\xF5e um harness inteiro (para projeto sem harness). |
1391
+ | \`npx origem status\` | What would change, and whether a managed block was edited locally. The safe read \u2014 writes nothing. |
1392
+ | \`npx origem connect\` | Authorises (browser), pulls the newest version and rewrites the files. Also the sync command. |
1393
+ | \`npx origem apply\` | Re-applies with the stored credential, without the browser. |
1394
+ | \`npx origem apply --dry-run\` | Prints the plan only. |
1395
+ | \`npx origem remove\` | Reverts everything Origem wrote, using the lock. |
1396
+ | \`npx origem propose\` | Suggests a change to the harness. **Writes nothing** \u2014 it stays pending until someone accepts it. |
1397
+ | \`npx origem spec\` | Open specs and the stage each one is in. |
1398
+ | \`npx origem doctor\` | Compares the harness against this repository and reports what drifted. |
1399
+ | \`npx origem bootstrap\` | Reads the repository and proposes a whole harness (for a project without one). |
1400
1400
 
1401
- ## No come\xE7o de uma tarefa
1401
+ ## At the start of a task
1402
1402
 
1403
- Rode \`npx origem spec\`. Se houver spec aberto relacionado ao que o usu\xE1rio pediu, use os
1404
- crit\xE9rios de aceite dele como defini\xE7\xE3o de pronto, e diga em que est\xE1gio o trabalho est\xE1.
1405
- Se n\xE3o houver, siga normalmente \u2014 trabalho avulso \xE9 leg\xEDtimo.
1403
+ Run \`npx origem spec\`. If there is an open spec related to what the user asked, use its
1404
+ acceptance criteria as the definition of done, and say which stage the work is in. If
1405
+ there is none, carry on \u2014 standalone work is legitimate.
1406
1406
 
1407
- ## Quando o comando que voc\xEA rodou falhar por n\xE3o existir
1407
+ ## When a command you ran fails because it does not exist
1408
1408
 
1409
- Rode \`npx origem doctor\`. Ele compara o que o harness afirma com o que o reposit\xF3rio
1410
- declara e costuma encontrar a causa (script renomeado, gerenciador de pacotes errado).
1411
- Corrigir o harness evita que o pr\xF3ximo turno tropece no mesmo lugar.
1409
+ Run \`npx origem doctor\`. It compares what the harness claims against what the repository
1410
+ declares and usually finds the cause (renamed script, wrong package manager). Fixing the
1411
+ harness stops the next turn from tripping on the same thing.
1412
1412
 
1413
- ## Quando propor uma mudan\xE7a
1413
+ ## When to propose a change
1414
1414
 
1415
- Se durante a sess\xE3o o usu\xE1rio corrigir voc\xEA sobre a mesma coisa mais de uma vez, ou estabelecer
1416
- uma conven\xE7\xE3o que deve valer nas pr\xF3ximas sess\xF5es, proponha:
1415
+ If during the session the user corrects you about the same thing more than once, or
1416
+ establishes a convention that should hold in future sessions, propose it:
1417
1417
 
1418
1418
  \`\`\`
1419
1419
  npx origem propose \\
1420
1420
  --type rule \\
1421
- --title "Terraform: apply \xE9 sempre manual" \\
1422
- --why "O usu\xE1rio me corrigiu 3x sobre isto nesta sess\xE3o." \\
1423
- --content "Nunca rode \`terraform apply\` sem mostrar o plan antes."
1421
+ --title "Terraform: apply is always manual" \\
1422
+ --why "The user corrected me on this 3 times in this session." \\
1423
+ --content "Never run \`terraform apply\` without showing the plan first."
1424
1424
  \`\`\`
1425
1425
 
1426
- Compartimentos v\xE1lidos: \`instruction\`, \`rule\`, \`boundary\`, \`memory\`, \`knowledge\`,
1426
+ Valid compartments: \`instruction\`, \`rule\`, \`boundary\`, \`memory\`, \`knowledge\`,
1427
1427
  \`skill\`, \`agent\`, \`prompt\`, \`harness\`, \`mcp\`, \`integration\`.
1428
1428
 
1429
- Regras para propor bem:
1430
- - **\`--why\` \xE9 o que a pessoa l\xEA para decidir.** Cite o que aconteceu na sess\xE3o, n\xE3o uma
1431
- justificativa gen\xE9rica.
1432
- - Proponha **uma coisa por vez**. Duas regras juntas viram uma decis\xE3o dif\xEDcil.
1433
- - N\xE3o proponha o que j\xE1 est\xE1 no harness \u2014 leia antes.
1434
- - Para alterar uma pe\xE7a existente, passe \`--card <id>\` em vez de criar uma nova.
1429
+ Rules for proposing well:
1430
+ - **\`--why\` is what the reviewer reads to decide.** Cite what happened in the session,
1431
+ not a generic justification.
1432
+ - Propose **one thing at a time**. Two rules together make one hard decision.
1433
+ - Do not propose what is already in the harness \u2014 read it first.
1434
+ - To change an existing piece, pass \`--card <id>\` instead of creating a new one.
1435
1435
 
1436
- ## Quando o usu\xE1rio pedir para sincronizar
1436
+ ## When the user asks to sync
1437
1437
 
1438
- Rode \`npx origem status\` primeiro e mostre o resultado. Se houver edi\xE7\xE3o local dentro de um
1439
- bloco gerenciado, avise **antes** de aplicar \u2014 o apply sobrescreve esse bloco.
1438
+ Run \`npx origem status\` first and show the result. If there is a local edit inside a
1439
+ managed block, warn **before** applying \u2014 the apply overwrites that block.
1440
1440
 
1441
- ## Limites
1441
+ ## Limits
1442
1442
 
1443
- N\xE3o edite \`.origem/lock.json\` na m\xE3o: ele \xE9 o invent\xE1rio que o \`origem remove\` usa para
1444
- saber o que reverter, e um lock inconsistente deixa arquivos \xF3rf\xE3os no projeto.
1443
+ Do not edit \`.origem/lock.json\` by hand: it is the inventory \`origem remove\` uses to
1444
+ know what to revert, and an inconsistent lock leaves orphan files in the project.
1445
1445
  `;
1446
1446
  function installOrigemSkill(baseDir, lock) {
1447
1447
  const fromHarness = (lock?.files ?? []).some(
1448
1448
  (f) => f.path.replace(/\\/g, "/") === SKILL_PATH.replace(/\\/g, "/")
1449
1449
  );
1450
1450
  if (fromHarness) {
1451
- info(color.dim(`skill do Origem: veio do pr\xF3prio harness, mantida como est\xE1`));
1451
+ info(color.dim("Origem skill: it comes from the harness itself, left as is"));
1452
1452
  return;
1453
1453
  }
1454
1454
  const abs = join6(baseDir, SKILL_PATH);
1455
1455
  if (existsSync7(abs) && readFileSync5(abs, "utf8") === SKILL_MD) {
1456
- info(color.dim(`skill do Origem j\xE1 atualizada em ${SKILL_PATH}`));
1456
+ info(color.dim(`Origem skill already up to date at ${SKILL_PATH}`));
1457
1457
  return;
1458
1458
  }
1459
1459
  mkdirSync5(dirname5(abs), { recursive: true });
1460
1460
  writeFileSync5(abs, SKILL_MD, "utf8");
1461
- ok(`skill do Origem instalada em ${SKILL_PATH}`);
1461
+ ok(`Origem skill installed at ${SKILL_PATH}`);
1462
1462
  }
1463
1463
 
1464
1464
  // src/connect.ts
@@ -1494,7 +1494,7 @@ async function resolveEndpoints(apiUrl) {
1494
1494
  }
1495
1495
  }
1496
1496
  throw new Error(
1497
- `N\xE3o encontrei os endpoints OAuth em ${base}. Confira a URL da API (--api-url ou ORIGEM_API_URL) e se ela est\xE1 publicada com o m\xF3dulo de OAuth.`
1497
+ `No OAuth endpoints found at ${base}. Check the API URL (--api-url or ORIGEM_API_URL) and that it is deployed with the OAuth module.`
1498
1498
  );
1499
1499
  }
1500
1500
  function page(title, body, accent) {
@@ -1518,18 +1518,18 @@ async function startCallbackServer(state) {
1518
1518
  const gotState = url.searchParams.get("state");
1519
1519
  if (error) {
1520
1520
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1521
- res.end(page("Autoriza\xE7\xE3o negada", "Pode fechar esta aba e voltar ao terminal.", "#b3392e"));
1522
- fail?.(new Error(`Autoriza\xE7\xE3o negada (${error}).`));
1521
+ res.end(page("Authorisation denied", "You can close this tab and go back to the terminal.", "#b3392e"));
1522
+ fail?.(new Error(`Authorisation denied (${error}).`));
1523
1523
  return;
1524
1524
  }
1525
1525
  if (!code || gotState !== state) {
1526
1526
  res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
1527
- res.end(page("Retorno inv\xE1lido", "Recomece com `origem connect`.", "#b3392e"));
1528
- fail?.(new Error("Retorno do navegador inv\xE1lido (state n\xE3o confere)."));
1527
+ res.end(page("Invalid callback", "Start again with `origem connect`.", "#b3392e"));
1528
+ fail?.(new Error("Invalid callback from the browser (state mismatch)."));
1529
1529
  return;
1530
1530
  }
1531
1531
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1532
- res.end(page("Conectado", "Pode fechar esta aba \u2014 o resto acontece no terminal.", "#2540d8"));
1532
+ res.end(page("Connected", "You can close this tab \u2014 the rest happens in the terminal.", "#2540d8"));
1533
1533
  settle?.({ code });
1534
1534
  });
1535
1535
  await new Promise((resolve, reject) => {
@@ -1537,7 +1537,7 @@ async function startCallbackServer(state) {
1537
1537
  server.listen(0, "127.0.0.1", resolve);
1538
1538
  });
1539
1539
  const timer = setTimeout(() => {
1540
- fail?.(new Error("Tempo esgotado esperando a autoriza\xE7\xE3o no navegador (5 min)."));
1540
+ fail?.(new Error("Timed out waiting for authorisation in the browser (5 min)."));
1541
1541
  }, CALLBACK_TIMEOUT_MS);
1542
1542
  timer.unref?.();
1543
1543
  return {
@@ -1576,10 +1576,10 @@ async function registerClient(endpoint, redirectUri) {
1576
1576
  signal: AbortSignal.timeout(15e3)
1577
1577
  });
1578
1578
  if (!res.ok) {
1579
- throw new Error(`N\xE3o consegui registrar o cliente OAuth (HTTP ${res.status}).`);
1579
+ throw new Error(`Could not register the OAuth client (HTTP ${res.status}).`);
1580
1580
  }
1581
1581
  const data = await res.json();
1582
- if (!data.client_id) throw new Error("Registro OAuth devolveu resposta sem client_id.");
1582
+ if (!data.client_id) throw new Error("OAuth registration returned no client_id.");
1583
1583
  return data;
1584
1584
  }
1585
1585
  async function exchangeCode(endpoint, input) {
@@ -1598,12 +1598,12 @@ async function exchangeCode(endpoint, input) {
1598
1598
  const data = await res.json().catch(() => ({}));
1599
1599
  if (!res.ok || !data.access_token) {
1600
1600
  throw new Error(
1601
- `Troca do c\xF3digo falhou (HTTP ${res.status}): ${data.error_description || data.error || "resposta sem access_token"}`
1601
+ `Code exchange failed (HTTP ${res.status}): ${data.error_description || data.error || "response had no access_token"}`
1602
1602
  );
1603
1603
  }
1604
1604
  if (!(data.scope ?? "").split(/\s+/).includes("write")) {
1605
1605
  warn(
1606
- "O token veio sem escopo de escrita \u2014 a sess\xE3o n\xE3o vai conseguir reportar turnos. Refa\xE7a o connect e confirme a permiss\xE3o na tela de autoriza\xE7\xE3o."
1606
+ "The token came back without write scope \u2014 the session will not be able to report turns. Run connect again and confirm the permission on the authorisation screen."
1607
1607
  );
1608
1608
  }
1609
1609
  return data.access_token;
@@ -1626,10 +1626,13 @@ async function runConnect(opts) {
1626
1626
  authorizeUrl.searchParams.set("code_challenge_method", "S256");
1627
1627
  authorizeUrl.searchParams.set("state", state);
1628
1628
  authorizeUrl.searchParams.set("scope", "read write");
1629
- info(`${color.cyan("\u2192")} abrindo o navegador para autorizar\u2026 ${color.dim(redirectUri)}`);
1630
- info(color.dim(` se nada abrir, cole no navegador:
1629
+ info(`${color.cyan("\u2192")} opening your browser to authorise\u2026`);
1630
+ info(color.dim(` if nothing opens, paste this in your browser:
1631
1631
  ${authorizeUrl.toString()}`));
1632
1632
  openBrowser(authorizeUrl.toString());
1633
+ info(
1634
+ color.bold("\n Waiting for you to authorise. Keep this terminal open.") + color.dim("\n It is not frozen \u2014 it is listening on 127.0.0.1 for the browser to come back.\n")
1635
+ );
1633
1636
  const { code } = await server.waitForCode;
1634
1637
  token = await exchangeCode(endpoints.token, {
1635
1638
  code,
@@ -1641,11 +1644,11 @@ async function runConnect(opts) {
1641
1644
  server.close();
1642
1645
  }
1643
1646
  saveConfig({ token, apiUrl });
1644
- ok("autorizado \u2014 credencial gravada em ~/.origem/config.json (0600)");
1647
+ ok("authorised \u2014 credential stored in ~/.origem/config.json (0600)");
1645
1648
  const brain = await fetchBrain(apiUrl, token);
1646
1649
  brain.__apiUrl = apiUrl;
1647
1650
  info(
1648
- `${color.cyan("\u2192")} harness ${color.bold(brain.brain?.name ?? "(sem nome)")} \u2014 ${brain.cards?.length ?? 0} pe\xE7as`
1651
+ `${color.cyan("\u2192")} harness ${color.bold(brain.brain?.name ?? "(unnamed)")} \u2014 ${brain.cards?.length ?? 0} pieces`
1649
1652
  );
1650
1653
  await runApply({
1651
1654
  brain,
@@ -1656,7 +1659,7 @@ async function runConnect(opts) {
1656
1659
  });
1657
1660
  installOrigemSkill(process.cwd(), readLock(process.cwd()));
1658
1661
  if (opts.noHooks) {
1659
- info(color.dim("hooks pulados (--no-hooks): a sess\xE3o n\xE3o vai reportar turnos ao Origem."));
1662
+ info(color.dim("hooks skipped (--no-hooks): the session will not report turns to Origem."));
1660
1663
  } else {
1661
1664
  info("");
1662
1665
  await hookInstall({
@@ -1669,8 +1672,8 @@ async function runConnect(opts) {
1669
1672
  });
1670
1673
  }
1671
1674
  info("");
1672
- ok(color.bold("ligado."));
1673
- info(color.dim("rode `origem connect` de novo a qualquer momento para sincronizar."));
1675
+ ok(color.bold("connected."));
1676
+ info(color.dim("run `origem connect` again any time to sync."));
1674
1677
  }
1675
1678
 
1676
1679
  // src/propose.ts
@@ -1690,14 +1693,14 @@ var PROPOSABLE = [
1690
1693
  ];
1691
1694
  async function runPropose(opts) {
1692
1695
  if (!PROPOSABLE.includes(opts.type)) {
1693
- throw new Error(`--type inv\xE1lido: ${opts.type}. Use um de: ${PROPOSABLE.join(", ")}`);
1696
+ throw new Error(`Invalid --type: ${opts.type}. Use one of: ${PROPOSABLE.join(", ")}`);
1694
1697
  }
1695
1698
  if (!opts.rationale?.trim()) {
1696
- throw new Error("--why \xE9 obrigat\xF3rio: diga por que est\xE1 propondo (\xE9 o que a pessoa l\xEA pra decidir).");
1699
+ throw new Error("--why is required: say why you are proposing it \u2014 that is what the reviewer reads.");
1697
1700
  }
1698
1701
  const content = opts.file ? readFileSync6(opts.file, "utf8") : opts.content ?? "";
1699
1702
  if (!content.trim()) {
1700
- throw new Error("Conte\xFAdo vazio. Passe --content <texto> ou --file <caminho>.");
1703
+ throw new Error("Empty content. Pass --content <text> or --file <path>.");
1701
1704
  }
1702
1705
  const base = opts.apiUrl.replace(/\/$/, "");
1703
1706
  const res = await fetch(`${base}/propose?k=${encodeURIComponent(opts.key)}`, {
@@ -1714,28 +1717,30 @@ async function runPropose(opts) {
1714
1717
  signal: AbortSignal.timeout(15e3)
1715
1718
  });
1716
1719
  const data = await res.json().catch(() => ({}));
1717
- if (res.status === 403) throw new Error("Token sem escopo de escrita \u2014 refa\xE7a o `origem connect`.");
1718
- if (res.status === 429) throw new Error(data.error ?? "Rate limit ou fila de propostas cheia.");
1720
+ if (res.status === 403) throw new Error("Token has no write scope \u2014 run `origem connect` again.");
1721
+ if (res.status === 429) throw new Error(data.error ?? "Rate limited, or the proposal queue is full.");
1719
1722
  if (!res.ok || !data.proposalId) {
1720
- throw new Error(data.error ?? `Falha ao propor (HTTP ${res.status}).`);
1723
+ throw new Error(data.error ?? `Failed to propose (HTTP ${res.status}).`);
1721
1724
  }
1722
- ok(`proposta enviada: ${color.bold(opts.title)}`);
1723
- info(color.dim("Ela fica pendente at\xE9 ser aceita no Origem \u2014 nada foi escrito no harness."));
1725
+ ok(`proposal sent: ${color.bold(opts.title)}`);
1726
+ info(color.dim("It stays pending until accepted in Origem \u2014 nothing was written to the harness."));
1724
1727
  }
1725
1728
 
1726
1729
  // src/spec.ts
1727
1730
  var STAGES = ["requisito", "design", "implementacao", "verificacao"];
1728
1731
  async function runSpec(opts) {
1729
1732
  const base = opts.apiUrl.replace(/\/$/, "");
1730
- const res = await fetch(`${base}/specs?k=${encodeURIComponent(opts.key)}`, {
1731
- signal: AbortSignal.timeout(15e3)
1732
- });
1733
+ const q = `?k=${encodeURIComponent(opts.key)}`;
1734
+ let res = await fetch(`${base}/api/specs${q}`, { signal: AbortSignal.timeout(15e3) });
1735
+ if (res.status === 404) {
1736
+ res = await fetch(`${base}/specsFeed${q}`, { signal: AbortSignal.timeout(15e3) });
1737
+ }
1733
1738
  const data = await res.json().catch(() => ({}));
1734
- if (res.status === 401) throw new Error("Token inv\xE1lido \u2014 refa\xE7a o `origem connect`.");
1735
- if (!res.ok) throw new Error(data.error ?? `Falha ao ler os specs (HTTP ${res.status}).`);
1739
+ if (res.status === 401) throw new Error("Invalid token \u2014 run `origem connect` again.");
1740
+ if (!res.ok) throw new Error(data.error ?? `Failed to read the specs (HTTP ${res.status}).`);
1736
1741
  const specs = data.specs ?? [];
1737
1742
  if (specs.length === 0) {
1738
- ok("nenhum spec aberto \u2014 o trabalho desta sess\xE3o \xE9 avulso.");
1743
+ ok("no open spec \u2014 this session is standalone work.");
1739
1744
  return;
1740
1745
  }
1741
1746
  for (const s of specs) {
@@ -1754,7 +1759,7 @@ ${color.bold(s.title)} ${color.dim(`(${s.id})`)}`);
1754
1759
  info(
1755
1760
  color.dim(
1756
1761
  `
1757
- ${specs.length} spec(s) aberto(s). Ao concluir um est\xE1gio, reporte com \`origem hook stop\` (o hook j\xE1 faz isso) \u2014 o board avan\xE7a sozinho.`
1762
+ ${specs.length} open spec(s). When you close a stage, it is reported by \`origem hook stop\` (the hook does it) \u2014 the board advances on its own.`
1758
1763
  )
1759
1764
  );
1760
1765
  }
@@ -1887,15 +1892,15 @@ function detectDrift(brain, repo) {
1887
1892
  if (declared.has(name)) continue;
1888
1893
  findings.push({
1889
1894
  severity: "critical",
1890
- message: `O harness manda rodar "${name}", que n\xE3o existe no projeto.`,
1891
- detail: `Dispon\xEDveis: ${[...declared].slice(0, 12).join(", ") || "(nenhum)"}`
1895
+ message: `The harness tells you to run "${name}", which does not exist in this project.`,
1896
+ detail: `Available: ${[...declared].slice(0, 12).join(", ") || "(none)"}`
1892
1897
  });
1893
1898
  }
1894
1899
  const testScript = repo.scripts.find((s) => /^(test|tests|check|verify)$/.test(s.name));
1895
1900
  if (testScript && !/\btest/.test(text)) {
1896
1901
  findings.push({
1897
1902
  severity: "critical",
1898
- message: "O projeto tem comando de teste e o harness n\xE3o menciona nenhum.",
1903
+ message: "The project has a test command and the harness mentions none.",
1899
1904
  detail: `${testScript.from}: ${testScript.name} \u2192 ${testScript.command}`
1900
1905
  });
1901
1906
  }
@@ -1905,24 +1910,24 @@ function detectDrift(brain, repo) {
1905
1910
  if (wrong.length > 0 && !new RegExp(`\\b${repo.packageManager}\\b`).test(text)) {
1906
1911
  findings.push({
1907
1912
  severity: "warning",
1908
- message: `O harness usa ${wrong.join("/")}, mas o lockfile \xE9 de ${repo.packageManager}.`,
1909
- detail: "Rodar o gerenciador errado reescreve o lock e quebra o CI."
1913
+ message: `The harness uses ${wrong.join("/")}, but the lockfile is ${repo.packageManager}.`,
1914
+ detail: "Running the wrong package manager rewrites the lock and breaks CI."
1910
1915
  });
1911
1916
  }
1912
1917
  }
1913
1918
  if (repo.hasIac && !/\b(nunca|never|terraform|aprova|confirm)/.test(text)) {
1914
1919
  findings.push({
1915
1920
  severity: "warning",
1916
- message: "O reposit\xF3rio tem infraestrutura como c\xF3digo e o harness n\xE3o declara limites.",
1917
- detail: "Um `apply` sem gate \xE9 a diferen\xE7a entre um erro e um incidente."
1921
+ message: "This repo has infrastructure as code and the harness declares no boundaries.",
1922
+ detail: "An ungated `apply` is the difference between a mistake and an incident."
1918
1923
  });
1919
1924
  }
1920
1925
  for (const lang of repo.languages.slice(0, 3)) {
1921
1926
  if (!text.includes(lang)) {
1922
1927
  findings.push({
1923
1928
  severity: "warning",
1924
- message: `O projeto \xE9 ${lang} e o harness n\xE3o menciona a linguagem.`,
1925
- detail: "Sem isso, conven\xE7\xF5es de estilo e ferramentas ficam por conta do palpite."
1929
+ message: `This project is ${lang} and the harness never mentions the language.`,
1930
+ detail: "Without that, style conventions and tooling are left to guesswork."
1926
1931
  });
1927
1932
  break;
1928
1933
  }
@@ -1933,18 +1938,18 @@ async function runDoctor(opts) {
1933
1938
  const brain = await fetchBrain(opts.apiUrl, opts.key);
1934
1939
  const repo = readRepo(process.cwd());
1935
1940
  info(
1936
- `${color.bold(repo.name)} \u2014 ${repo.languages.join(", ") || "linguagem indefinida"}${repo.packageManager ? ` \xB7 ${repo.packageManager}` : ""} \xB7 ${repo.scripts.length} comando(s)`
1941
+ `${color.bold(repo.name)} \u2014 ${repo.languages.join(", ") || "unknown language"}${repo.packageManager ? ` \xB7 ${repo.packageManager}` : ""} \xB7 ${repo.scripts.length} command(s)`
1937
1942
  );
1938
- info(color.dim(`harness: ${brain.brain?.name ?? "?"} \u2014 ${brain.cards?.length ?? 0} pe\xE7as
1943
+ info(color.dim(`harness: ${brain.brain?.name ?? "?"} \u2014 ${brain.cards?.length ?? 0} pieces
1939
1944
  `));
1940
1945
  const findings = detectDrift(brain, repo);
1941
1946
  if (findings.length === 0) {
1942
- ok("nenhum drift entre o harness e o reposit\xF3rio.");
1947
+ ok("no drift between the harness and this repository.");
1943
1948
  return;
1944
1949
  }
1945
1950
  for (const f of findings) {
1946
1951
  if (f.severity === "critical") {
1947
- console.error(`${color.red("\u2717 cr\xEDtico")} ${f.message}`);
1952
+ console.error(`${color.red("\u2717 critical")} ${f.message}`);
1948
1953
  } else {
1949
1954
  warn(f.message);
1950
1955
  }
@@ -1953,7 +1958,7 @@ async function runDoctor(opts) {
1953
1958
  info(
1954
1959
  color.dim(
1955
1960
  `
1956
- ${findings.length} achado(s). Corrija no Origem (a mudan\xE7a vale nas pr\xF3ximas sess\xF5es) ou proponha com \`origem propose\`.`
1961
+ ${findings.length} finding(s). Fix it in Origem (the change applies to every future session) or send it with \`origem propose\`.`
1957
1962
  )
1958
1963
  );
1959
1964
  }
@@ -1968,42 +1973,42 @@ function buildEntries(repo, commits) {
1968
1973
  const identity = [
1969
1974
  `# ${repo.name}`,
1970
1975
  "",
1971
- repo.languages.length ? `Projeto em ${repo.languages.join(", ")}.` : "",
1972
- repo.packageManager ? `Gerenciador de pacotes: **${repo.packageManager}** \u2014 use este, n\xE3o outro.` : "",
1976
+ repo.languages.length ? `A ${repo.languages.join(", ")} project.` : "",
1977
+ repo.packageManager ? `Package manager: **${repo.packageManager}** \u2014 use this one, not another.` : "",
1973
1978
  "",
1974
- "## Comandos",
1979
+ "## Commands",
1975
1980
  ""
1976
1981
  ];
1977
1982
  const cmd = (label, s) => s ? `- **${label}:** \`${s.from === "Makefile" ? s.command : `${pm} run ${s.name}`}\`` : "";
1978
- identity.push(cmd("build", build), cmd("testes", test), cmd("dev", dev));
1983
+ identity.push(cmd("build", build), cmd("tests", test), cmd("dev", dev));
1979
1984
  const others = repo.scripts.filter((s) => ![build, test, dev].includes(s)).slice(0, 8).map((s) => `- \`${s.from === "Makefile" ? s.command : `${pm} run ${s.name}`}\` \u2014 ${s.command}`);
1980
- if (others.length) identity.push("", "### Outros", "", ...others);
1985
+ if (others.length) identity.push("", "### Other", "", ...others);
1981
1986
  entries.push({
1982
1987
  type: "instruction",
1983
- title: `${repo.name} \u2014 vis\xE3o e comandos`,
1988
+ title: `${repo.name} \u2014 overview and commands`,
1984
1989
  content: identity.filter(Boolean).join("\n"),
1985
1990
  tags: ["bootstrap"]
1986
1991
  });
1987
1992
  const boundaries = [];
1988
1993
  if (repo.hasIac) {
1989
1994
  boundaries.push(
1990
- "- Nunca rode `terraform apply` sem mostrar o plan e obter confirma\xE7\xE3o.",
1991
- "- Sempre pe\xE7a confirma\xE7\xE3o antes de qualquer opera\xE7\xE3o que destrua recurso."
1995
+ "- Never run `terraform apply` without showing the plan and getting confirmation.",
1996
+ "- Always ask for confirmation before any operation that destroys a resource."
1992
1997
  );
1993
1998
  }
1994
1999
  if (repo.packageManager) {
1995
2000
  boundaries.push(
1996
- `- Nunca rode outro gerenciador que n\xE3o \`${repo.packageManager}\` \u2014 reescreve o lockfile e quebra o CI.`
2001
+ `- Never run a package manager other than \`${repo.packageManager}\` \u2014 it rewrites the lockfile and breaks CI.`
1997
2002
  );
1998
2003
  }
1999
2004
  if (repo.hasCi) {
2000
- boundaries.push("- Perguntar antes de alterar workflows em `.github/workflows/`.");
2005
+ boundaries.push("- Ask before changing workflows under `.github/workflows/`.");
2001
2006
  }
2002
2007
  if (boundaries.length) {
2003
2008
  entries.push({
2004
2009
  type: "boundary",
2005
- title: "Limites do projeto",
2006
- content: ["Sempre / Perguntar antes / Nunca:", "", ...boundaries].join("\n"),
2010
+ title: "Project boundaries",
2011
+ content: ["Always / Ask first / Never:", "", ...boundaries].join("\n"),
2007
2012
  tags: ["bootstrap"]
2008
2013
  });
2009
2014
  }
@@ -2011,11 +2016,11 @@ function buildEntries(repo, commits) {
2011
2016
  if (commits.length >= 10 && conventional.length / commits.length > 0.6) {
2012
2017
  entries.push({
2013
2018
  type: "rule",
2014
- title: "Conven\xE7\xE3o de commits",
2019
+ title: "Commit convention",
2015
2020
  content: [
2016
- `Este reposit\xF3rio usa Conventional Commits \u2014 ${conventional.length} dos ${commits.length} commits recentes seguem o padr\xE3o.`,
2021
+ `This repository uses Conventional Commits \u2014 ${conventional.length} of the last ${commits.length} commits follow it.`,
2017
2022
  "",
2018
- "Formato: `tipo(escopo): descri\xE7\xE3o`, ex.: `fix(auth): corrige refresh do token`."
2023
+ "Format: `type(scope): description`, e.g. `fix(auth): correct token refresh`."
2019
2024
  ].join("\n"),
2020
2025
  tags: ["bootstrap"]
2021
2026
  });
@@ -2036,23 +2041,23 @@ async function runBootstrap(opts) {
2036
2041
  const commits = recentCommitSubjects(baseDir);
2037
2042
  const entries = buildEntries(repo, commits);
2038
2043
  info(
2039
- `${color.bold(repo.name)} \u2014 ${repo.languages.join(", ") || "linguagem indefinida"}${repo.packageManager ? ` \xB7 ${repo.packageManager}` : ""}`
2044
+ `${color.bold(repo.name)} \u2014 ${repo.languages.join(", ") || "unknown language"}${repo.packageManager ? ` \xB7 ${repo.packageManager}` : ""}`
2040
2045
  );
2041
- info(`${repo.scripts.length} comando(s) \xB7 ${repo.docs.length} doc(s) \xB7 ${commits.length} commits lidos
2046
+ info(`${repo.scripts.length} command(s) \xB7 ${repo.docs.length} doc(s) \xB7 ${commits.length} commits read
2042
2047
  `);
2043
- info(color.bold("Vai propor:"));
2048
+ info(color.bold("Will propose:"));
2044
2049
  for (const e of entries) {
2045
2050
  const preview = e.content.split("\n").find((l) => l.trim() && !l.startsWith("#")) ?? "";
2046
2051
  info(` ${color.cyan(e.type.padEnd(12))} ${e.title}`);
2047
2052
  info(color.dim(` ${preview.slice(0, 76)}`));
2048
2053
  }
2049
2054
  if (opts.dryRun) {
2050
- info(color.dim("\n--dry-run: nada foi enviado."));
2055
+ info(color.dim("\n--dry-run: nothing was sent."));
2051
2056
  return;
2052
2057
  }
2053
2058
  if (!opts.yes && !await confirm(`
2054
- Enviar ${entries.length} proposta(s)?`)) {
2055
- warn("Nada foi enviado.");
2059
+ Send ${entries.length} proposal(s)?`)) {
2060
+ warn("Nothing was sent.");
2056
2061
  return;
2057
2062
  }
2058
2063
  const base = opts.apiUrl.replace(/\/$/, "");
@@ -2063,73 +2068,74 @@ Enviar ${entries.length} proposta(s)?`)) {
2063
2068
  signal: AbortSignal.timeout(3e4)
2064
2069
  });
2065
2070
  const data = await res.json().catch(() => ({}));
2066
- if (res.status === 403) throw new Error("Token sem escopo de escrita \u2014 refa\xE7a o `origem connect`.");
2067
- if (!res.ok) throw new Error(data.error ?? `Falha ao propor (HTTP ${res.status}).`);
2068
- ok(`${data.proposed} pe\xE7a(s) propostas como um lote.`);
2069
- info(color.dim("Aceite ou descarte o lote no Origem \u2014 nada foi escrito no harness ainda."));
2071
+ if (res.status === 403) throw new Error("Token has no write scope \u2014 run `origem connect` again.");
2072
+ if (!res.ok) throw new Error(data.error ?? `Failed to propose (HTTP ${res.status}).`);
2073
+ ok(`${data.proposed} piece(s) proposed as one batch.`);
2074
+ info(color.dim("Accept or discard the batch in Origem \u2014 nothing was written to the harness yet."));
2070
2075
  }
2071
2076
 
2072
2077
  // src/index.ts
2073
- var HELP = `${color.bold("origem")} \u2014 materializa um Brain do Origem na config da sua IA.
2078
+ var HELP = `${color.bold("origem")} \u2014 connects a repository to your Origem harness.
2074
2079
 
2075
- ${color.bold("Uso:")}
2076
- origem connect [op\xE7\xF5es] Conecta este reposit\xF3rio a um harness (autoriza no
2077
- navegador, aplica em disco, instala skill + hooks).
2078
- Rodar de novo sincroniza.
2079
- origem spec Mostra os specs abertos e em que est\xE1gio est\xE3o
2080
- origem doctor Compara o harness com este reposit\xF3rio (drift)
2081
- origem bootstrap L\xEA o reposit\xF3rio e prop\xF5e um harness inteiro
2082
- origem propose [op\xE7\xF5es] Sugere uma mudan\xE7a no harness (fica pendente at\xE9
2083
- ser aceita no site; n\xE3o escreve nada)
2084
- origem apply [op\xE7\xF5es] Puxa o Brain e escreve os arquivos de config
2085
- origem status [op\xE7\xF5es] Mostra o que mudaria e se h\xE1 edi\xE7\xF5es locais
2086
- origem remove [op\xE7\xF5es] Reverte tudo que o Origem escreveu (usa o lock)
2087
- origem login --key <t> Grava token + API em ~/.origem/config.json (0600)
2088
- origem logout Remove as credenciais gravadas
2089
- origem watch [op\xE7\xF5es] Plant\xE3o local: executa comandos da fila mesmo sem sess\xE3o aberta
2090
- origem hook install Instala os hooks que ligam a sess\xE3o ao Origem
2091
- origem hook start (uso interno) marca o in\xEDcio do turno
2092
- origem hook stop (uso interno) reporta o turno e busca comando
2093
- origem help Esta ajuda
2080
+ ${color.bold("Usage:")}
2081
+ origem connect [options] Connect this repo to a harness (authorises in the
2082
+ browser, writes the files, installs skill + hooks).
2083
+ Run it again to sync.
2084
+ origem spec Open specs and the stage each one is in
2085
+ origem doctor Compare the harness against this repo (drift)
2086
+ origem bootstrap Read the repo and propose a whole harness
2087
+ origem propose [options] Suggest one change (stays pending until accepted
2088
+ on the site; writes nothing)
2089
+ origem apply [options] Pull the harness and write the config files
2090
+ origem status [options] What would change, and whether you edited a block
2091
+ origem remove [options] Revert everything Origem wrote (uses the lock)
2092
+ origem login --key <t> Store token + API in ~/.origem/config.json (0600)
2093
+ origem logout Remove the stored credentials
2094
+ origem watch [options] Local runner: executes queued commands with no session open
2095
+ origem hook install Install the hooks that link the session to Origem
2096
+ origem hook start (internal) marks the start of a turn
2097
+ origem hook stop (internal) reports the turn and fetches a command
2098
+ origem help This help
2094
2099
 
2095
- ${color.bold("Op\xE7\xF5es:")}
2096
- -k, --key <token> Token de capacidade (ou env ORIGEM_TOKEN)
2097
- --api-url <url> Base da API (ou env ORIGEM_API_URL)
2098
- --scope <s> project (padr\xE3o) | user (~/.claude)
2099
- --dry-run S\xF3 mostra o plano; n\xE3o escreve
2100
- -y, --yes Aplica config privilegiada sem perguntar
2101
- --targets a,b Alvos espec\xEDficos (ex: claude,claude-settings,mcp)
2102
- --all Todos os alvos (AGENTS.md, Cursor, Gemini, Copilot\u2026)
2103
- --finish (hook stop) fecha o run em vez de s\xF3 reportar o turno
2104
- --quiet (hook stop) n\xE3o escreve erro no stderr
2105
- --bin <cmd> (hook install) comando gravado no settings.json
2106
- --local (hook install) grava em .claude/settings.local.json
2107
- --once (watch) uma passada s\xF3 e sai (bom pra cron)
2108
- --no-hooks (connect) n\xE3o instala os hooks de sess\xE3o
2109
- --type <t> (propose) compartimento: rule, boundary, memory, skill\u2026
2110
- --title <t> (propose) t\xEDtulo da pe\xE7a
2111
- --content <t> (propose) conte\xFAdo, ou use --file <caminho>
2112
- --why <t> (propose) por que est\xE1 propondo \u2014 obrigat\xF3rio
2113
- --card <id> (propose) altera uma pe\xE7a existente em vez de criar
2114
- --interval <s> (watch) segundos entre consultas \xE0 fila (padr\xE3o 5)
2115
- --claude <path> (watch) bin\xE1rio do Claude Code (padr\xE3o: descobre sozinho)
2116
- --cwd <dir> (watch) diret\xF3rio onde executar (padr\xE3o: o atual)
2117
- --permission-mode (watch) repassado ao Claude Code
2100
+ ${color.bold("Options:")}
2101
+ -k, --key <token> Capability token (or env ORIGEM_TOKEN)
2102
+ --api-url <url> API base URL (or env ORIGEM_API_URL)
2103
+ --scope <s> project (default) | user (~/.claude)
2104
+ --dry-run Print the plan only; write nothing
2105
+ -y, --yes Apply privileged config without asking
2106
+ --targets a,b Specific targets (e.g. claude,claude-settings,mcp)
2107
+ --all Every target (AGENTS.md, Cursor, Gemini, Copilot\u2026)
2108
+ --finish (hook stop) close the run instead of just reporting
2109
+ --quiet (hook stop) do not write errors to stderr
2110
+ --bin <cmd> (hook install) command stored in settings.json
2111
+ --local (hook install) write to .claude/settings.local.json
2112
+ --once (watch) a single pass, then exit (good for cron)
2113
+ --no-hooks (connect) do not install the session hooks
2114
+ --type <t> (propose) compartment: rule, boundary, memory, skill\u2026
2115
+ --title <t> (propose) title of the piece
2116
+ --content <t> (propose) content, or use --file <path>
2117
+ --why <t> (propose) why you are proposing it \u2014 required
2118
+ --card <id> (propose) change an existing piece instead of creating
2119
+ --interval <s> (watch) seconds between queue polls (default 5)
2120
+ --claude <path> (watch) Claude Code binary (default: auto-detect)
2121
+ --cwd <dir> (watch) directory to run in (default: current)
2122
+ --permission-mode (watch) passed through to Claude Code
2118
2123
 
2119
- ${color.bold("Alvos padr\xE3o (Claude Code):")} ${CLAUDE_TARGETS.join(", ")}
2120
- ${color.bold("Todos:")} ${ALL_TARGETS.join(", ")}
2124
+ ${color.bold("Default targets (Claude Code):")} ${CLAUDE_TARGETS.join(", ")}
2125
+ ${color.bold("All:")} ${ALL_TARGETS.join(", ")}
2121
2126
 
2122
- ${color.bold("Come\xE7ando:")} \`origem connect\` \xE9 o \xFAnico comando necess\xE1rio \u2014 ele autoriza no
2123
- navegador (OAuth + PKCE, sem token colado), grava a credencial, materializa o harness
2124
- e instala a skill. \`login\`/\`apply\`/\`hook install\` continuam dispon\xEDveis pra quem
2125
- precisa dos passos separados.
2127
+ ${color.bold("Getting started:")} \`origem connect\` is the only command you need \u2014 it
2128
+ authorises in the browser (OAuth + PKCE, no token to paste), stores the credential,
2129
+ writes the harness to disk and installs the skill. \`login\`/\`apply\`/\`hook install\`
2130
+ are still there for whoever needs the steps apart.
2126
2131
 
2127
- ${color.bold("Controle pelo site:")} com os hooks instalados, cada turno \xE9 reportado ao
2128
- Origem e a resposta pode trazer o pr\xF3ximo prompt da fila \u2014 a sess\xE3o continua sozinha.
2129
- O que trafega \xE9 PROMPT, nunca shell: quem decide o que roda \xE9 a permiss\xE3o do Claude.
2132
+ ${color.bold("Driving from the site:")} with the hooks installed, every turn is reported to
2133
+ Origem and the reply may carry the next prompt from the queue \u2014 the session continues on
2134
+ its own. What travels back is a PROMPT, never shell: what runs is still decided by
2135
+ Claude Code's permission layer.
2130
2136
 
2131
- ${color.bold("Confian\xE7a:")} arquivos privilegiados (settings.json com hooks/env, .mcp.json
2132
- com command) exigem confirma\xE7\xE3o. Defina ORIGEM_SIGNING_SECRET pra exigir assinatura.
2137
+ ${color.bold("Trust:")} privileged files (settings.json with hooks/env, .mcp.json with
2138
+ command) require confirmation. Set ORIGEM_SIGNING_SECRET to require a signed payload.
2133
2139
  `;
2134
2140
  function parseArgs(argv) {
2135
2141
  const cmd = argv[0] && !argv[0].startsWith("-") ? argv[0] : "help";
@@ -2162,7 +2168,7 @@ function parseArgs(argv) {
2162
2168
  break;
2163
2169
  case "--scope": {
2164
2170
  const s = rest[++i];
2165
- if (s !== "project" && s !== "user") throw new Error(`--scope inv\xE1lido: ${s}`);
2171
+ if (s !== "project" && s !== "user") throw new Error(`Invalid --scope: ${s}`);
2166
2172
  flags.scope = s;
2167
2173
  break;
2168
2174
  }
@@ -2193,7 +2199,7 @@ function parseArgs(argv) {
2193
2199
  break;
2194
2200
  case "--interval": {
2195
2201
  const n = Number(rest[++i]);
2196
- if (!Number.isFinite(n) || n < 2) throw new Error("--interval deve ser >= 2 (segundos)");
2202
+ if (!Number.isFinite(n) || n < 2) throw new Error("--interval must be >= 2 (seconds)");
2197
2203
  flags.interval = n;
2198
2204
  break;
2199
2205
  }
@@ -2234,7 +2240,7 @@ function parseArgs(argv) {
2234
2240
  flags.permissionMode = rest[++i];
2235
2241
  break;
2236
2242
  default:
2237
- throw new Error(`Op\xE7\xE3o desconhecida: ${a}`);
2243
+ throw new Error(`Unknown option: ${a}`);
2238
2244
  }
2239
2245
  }
2240
2246
  return { cmd, flags };
@@ -2262,7 +2268,7 @@ async function main() {
2262
2268
  }
2263
2269
  case "propose": {
2264
2270
  if (!flags.type || !flags.title) {
2265
- throw new Error("Use: origem propose --type <compartimento> --title <t\xEDtulo> --content <texto> --why <motivo>");
2271
+ throw new Error("Usage: origem propose --type <compartment> --title <title> --content <text> --why <reason>");
2266
2272
  }
2267
2273
  await runPropose({
2268
2274
  apiUrl: resolveApiUrl(flags.apiUrl),
@@ -2355,7 +2361,7 @@ async function main() {
2355
2361
  quiet: flags.quiet
2356
2362
  });
2357
2363
  } else {
2358
- err("Use: origem hook install | origem hook start | origem hook stop");
2364
+ err("Usage: origem hook install | origem hook start | origem hook stop");
2359
2365
  process.exitCode = 1;
2360
2366
  }
2361
2367
  } catch (e) {
@@ -2365,7 +2371,7 @@ async function main() {
2365
2371
  break;
2366
2372
  }
2367
2373
  default:
2368
- err(`Comando desconhecido: ${cmd}`);
2374
+ err(`Unknown command: ${cmd}`);
2369
2375
  info(HELP);
2370
2376
  process.exitCode = 1;
2371
2377
  }