diff --git a/AGENTS.md b/AGENTS.md index 71904f4d..2252920b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -692,3 +692,8 @@ Please read these documents next. And if you need to create new nostr kinds please keep the logic centralized there in `nostrEventSchemas.js` and the `nostr-event-schemas.md` up to date. **End of AGENTS.md** + +## TORCH Memory Integration +You have access to the TORCH memory system. +1. READ: Check `.scheduler-memory/latest/${cadence}/memories.md` for past learnings. +2. WRITE: Before exiting, save new insights to `memory-update.md` so future runs can learn from this session. diff --git a/CLAUDE.md b/CLAUDE.md index d74ca3e9..f20400a5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -636,3 +636,8 @@ Validation: Must have `title` + at least one of `url` or `magnet`. 5. **Token-first styling** — no raw colors, no inline styles 6. **Keep magnets raw** — decode only at playback time 7. **Document rollback steps** — especially for playback changes + +## TORCH Memory Integration +You have access to the TORCH memory system. +1. READ: Check `.scheduler-memory/latest/${cadence}/memories.md` for past learnings. +2. WRITE: Before exiting, save new insights to `memory-update.md` so future runs can learn from this session. diff --git a/package-lock.json b/package-lock.json index 4295b0d1..ea915d67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "nostr-tools": "^2.19.4", "postcss": "^8.5.6", "tailwindcss": "^3.4.19", - "torch-lock": "https://github.com/PR0M3TH3AN/TORCH/archive/b5023799d34ee0f7064c2d0f0591aef22eaa2288.tar.gz", + "torch-lock": "https://github.com/PR0M3TH3AN/TORCH/archive/1e3f88e.tar.gz", "ws": "^8.19.0" }, "devDependencies": { @@ -6106,8 +6106,8 @@ }, "node_modules/torch-lock": { "version": "0.1.0", - "resolved": "https://github.com/PR0M3TH3AN/TORCH/archive/b5023799d34ee0f7064c2d0f0591aef22eaa2288.tar.gz", - "integrity": "sha512-HpA3zjTulnNR8WRTfE+4SzGKsHlVMu+wqloFXU3Zotm3z2+YC+ucZPjkBzZ6hgGCM1NS2vCiwnNW8Xwdt2X7cQ==", + "resolved": "https://github.com/PR0M3TH3AN/TORCH/archive/1e3f88e.tar.gz", + "integrity": "sha512-iAA5mMXf0BT6NsAUprd4QldSJtT3NayfN7eORCGkRI+v3hNV9lJtMOkfu2xAlERKk9wvV8GBSMNjYF8R6KsQTw==", "license": "MIT", "dependencies": { "nostr-tools": "2.23.1", diff --git a/package.json b/package.json index 7e50f9fb..2e4aa53b 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "nostr-tools": "^2.19.4", "postcss": "^8.5.6", "tailwindcss": "^3.4.19", - "torch-lock": "https://github.com/PR0M3TH3AN/TORCH/archive/b5023799d34ee0f7064c2d0f0591aef22eaa2288.tar.gz", + "torch-lock": "https://github.com/PR0M3TH3AN/TORCH/archive/1e3f88e.tar.gz", "ws": "^8.19.0" } } diff --git a/torch/README.md b/torch/README.md index bec9b94c..b81388d9 100644 --- a/torch/README.md +++ b/torch/README.md @@ -33,6 +33,8 @@ npx --no-install torch-lock check --cadence daily npx --no-install torch-lock dashboard --port 4173 --host 127.0.0.1 ``` +`torch-lock init` and `torch-lock update` now auto-upsert the `## TORCH Memory Integration` block into root-level `AGENTS.md` and `CLAUDE.md` (and create `AGENTS.md` if neither file exists). + > TORCH is distributed from GitHub tarballs and is not currently published to the npm registry. > Use pinned commit tarballs (`archive/.tar.gz`) for repeatable installs. Avoid `refs/heads/main.tar.gz` for production rollouts. @@ -119,6 +121,9 @@ npx --no-install torch-lock init # Update TORCH configuration and scripts npx --no-install torch-lock update +# Validate setup and print actionable fixes +npx --no-install torch-lock doctor + # Remove TORCH completely from this project npx --no-install torch-lock remove diff --git a/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/context/CONTEXT_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/context/CONTEXT_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-02-25-181Z/src/context/CONTEXT_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-02-25-181Z/src/context/CONTEXT_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/context/CONTEXT_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/context/CONTEXT_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-02-25-181Z/src/context/CONTEXT_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-02-25-181Z/src/context/CONTEXT_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/decisions/DECISIONS_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/decisions/DECISIONS_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-02-25-181Z/src/decisions/DECISIONS_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-02-25-181Z/src/decisions/DECISIONS_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/decisions/DECISIONS_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/decisions/DECISIONS_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-02-25-181Z/src/decisions/DECISIONS_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-02-25-181Z/src/decisions/DECISIONS_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-02-25-181Z/src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-02-25-181Z/src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-02-25-181Z/src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-02-25-181Z/src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/todo/TODO_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/todo/TODO_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-02-25-181Z/src/todo/TODO_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-02-25-181Z/src/todo/TODO_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/todo/TODO_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-02-25-181Z/src/todo/TODO_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-02-25-181Z/src/todo/TODO_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-02-25-181Z/src/todo/TODO_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/context/CONTEXT_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/context/CONTEXT_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-17-56-006Z/src/context/CONTEXT_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-17-56-006Z/src/context/CONTEXT_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/context/CONTEXT_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/context/CONTEXT_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-17-56-006Z/src/context/CONTEXT_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-17-56-006Z/src/context/CONTEXT_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/decisions/DECISIONS_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/decisions/DECISIONS_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-17-56-006Z/src/decisions/DECISIONS_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-17-56-006Z/src/decisions/DECISIONS_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/decisions/DECISIONS_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/decisions/DECISIONS_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-17-56-006Z/src/decisions/DECISIONS_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-17-56-006Z/src/decisions/DECISIONS_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-17-56-006Z/src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-17-56-006Z/src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-17-56-006Z/src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-17-56-006Z/src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/todo/TODO_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/todo/TODO_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-17-56-006Z/src/todo/TODO_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-17-56-006Z/src/todo/TODO_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/todo/TODO_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-17-56-006Z/src/todo/TODO_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-17-56-006Z/src/todo/TODO_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-17-56-006Z/src/todo/TODO_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/context/CONTEXT_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/context/CONTEXT_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-19-02-007Z/src/context/CONTEXT_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-19-02-007Z/src/context/CONTEXT_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/context/CONTEXT_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/context/CONTEXT_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-19-02-007Z/src/context/CONTEXT_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-19-02-007Z/src/context/CONTEXT_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/decisions/DECISIONS_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/decisions/DECISIONS_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-19-02-007Z/src/decisions/DECISIONS_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-19-02-007Z/src/decisions/DECISIONS_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/decisions/DECISIONS_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/decisions/DECISIONS_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-19-02-007Z/src/decisions/DECISIONS_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-19-02-007Z/src/decisions/DECISIONS_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-19-02-007Z/src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-19-02-007Z/src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-19-02-007Z/src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-19-02-007Z/src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md diff --git a/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/todo/TODO_2026-02-15T01:03:31Z.md b/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/todo/TODO_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-19-02-007Z/src/todo/TODO_2026-02-15T01:03:31Z.md rename to torch/_backups/backup_2026-02-25T20-19-02-007Z/src/todo/TODO_2026-02-15T01-03-31Z.md diff --git a/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/todo/TODO_2026-02-22T16:59:21Z.md b/torch/_backups/backup_2026-02-25T20-19-02-007Z/src/todo/TODO_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/_backups/backup_2026-02-25T20-19-02-007Z/src/todo/TODO_2026-02-22T16:59:21Z.md rename to torch/_backups/backup_2026-02-25T20-19-02-007Z/src/todo/TODO_2026-02-22T16-59-21Z.md diff --git a/torch/landing/index.html b/torch/landing/index.html index 17def050..4ad26972 100644 --- a/torch/landing/index.html +++ b/torch/landing/index.html @@ -938,9 +938,9 @@

Copy this happy-path prompt to onboard TORCH:

-

Requires Node.js 22+.

+

Requires Node.js 22+. `init` also hooks TORCH memory instructions into AGENTS.md / CLAUDE.md.

- Hey Jules, can you install and setup TORCH into this repo? Here is the install command: + Hey Jules, can you install and setup TORCH into this repo in one pass? Please run this command: npm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx --no-install torch-lock init
-
-

Hook memory into your existing agents

-

- Add these instructions to your agent's system prompt (e.g. in AGENTS.md or CLAUDE.md) to enable the TORCH memory system. -

- -
- ## TORCH Memory Integration -You have access to the TORCH memory system. -1. READ: Check `.scheduler-memory/latest/${cadence}/memories.md` for past learnings. -2. WRITE: Before exiting, save new insights to `memory-update.md` so future runs can learn from this session. - -
-
-

Scheduler Meta Prompts

@@ -1637,21 +1616,19 @@ You have access to the TORCH memory system. // Update prompt text const installCmd = document.getElementById('install-cmd'); let promptText = ''; - - if (agent === 'jules') { - promptText = "Hey Jules, can you install and setup TORCH into this repo? Here is the install command:\n\nnpm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx --no-install torch-lock init"; - } else if (agent === 'codex') { - promptText = "Hey Codex, can you install and setup TORCH into this repo? Here is the install command:\n\nnpm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx --no-install torch-lock init"; - } else if (agent === 'opencode') { - promptText = "Hey OpenCode, can you install and setup TORCH into this repo? Here is the install command:\n\nnpm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx --no-install torch-lock init"; - } else if (agent === 'claude') { - promptText = "Hey Claude, can you install and setup TORCH into this repo? Here is the install command:\n\nnpm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx --no-install torch-lock init"; - } else if (agent === 'gemini') { - promptText = "Hey Gemini, can you install and setup TORCH into this repo? Here is the install command:\n\nnpm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx --no-install torch-lock init"; - } else if (agent === 'qwen') { - promptText = "Hey Qwen, can you install and setup TORCH into this repo? Here is the install command:\n\nnpm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx --no-install torch-lock init"; - } else if (agent === 'antigravity') { - promptText = "Hey Antigravity, can you install and setup TORCH into this repo? Here is the install command:\n\nnpm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx --no-install torch-lock init"; + const agentLabelById = { + jules: 'Jules', + codex: 'Codex', + opencode: 'OpenCode', + claude: 'Claude', + gemini: 'Gemini', + qwen: 'Qwen', + antigravity: 'Antigravity', + }; + const installCommand = 'npm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx --no-install torch-lock init'; + const agentLabel = agentLabelById[agent]; + if (agentLabel) { + promptText = `Hey ${agentLabel}, can you install and setup TORCH into this repo in one pass? Please run this command:\n\n${installCommand}`; } if (installCmd) { diff --git a/torch/scheduler-flow.md b/torch/scheduler-flow.md index a1f831a7..444c7f9e 100644 --- a/torch/scheduler-flow.md +++ b/torch/scheduler-flow.md @@ -26,6 +26,8 @@ Every agent prompt invoked by the schedulers (daily/weekly) MUST enforce this co - `docs/agent-handoffs/README.md` - Recent notes in `docs/agent-handoffs/learnings/` and `docs/agent-handoffs/incidents/` 2. **Update run artifacts** in `src/context/`, `src/todo/`, `src/decisions/`, and `src/test_logs/` during the run, or explicitly document why each artifact update is not needed for that run. + - When creating filenames with ``, use the scheduler-provided `SCHEDULER_RUN_TIMESTAMP` value when available. + - Required filename-safe format: `YYYY-MM-DDTHH-MM-SSZ` (for example `2026-02-15T01-03-31Z`); never use `:` in filenames. 3. **Capture reusable failures and unresolved issues**: - Record reusable failures in `docs/agent-handoffs/incidents/` - Record active unresolved reproducible items in `KNOWN_ISSUES.md` diff --git a/torch/scripts/agent/run-scheduler-cycle.mjs b/torch/scripts/agent/run-scheduler-cycle.mjs index 1df3b64d..ae80c4a3 100644 --- a/torch/scripts/agent/run-scheduler-cycle.mjs +++ b/torch/scripts/agent/run-scheduler-cycle.mjs @@ -889,6 +889,7 @@ async function main() { schedulerRunState.lock_deferral = null; await writeRunState(runStatePath, schedulerRunState); const runArtifactSince = new Date().toISOString(); + const runArtifactTimestamp = ts(); const runStartMs = Date.parse(runArtifactSince); const outputChunks = []; @@ -904,6 +905,7 @@ async function main() { SCHEDULER_CADENCE: cadence, SCHEDULER_PROMPT_PATH: promptPath, SCHEDULER_MEMORY_FILE: memoryFile, + SCHEDULER_RUN_TIMESTAMP: runArtifactTimestamp, }; if (schedulerConfig.memoryPolicy.retrieveCommand) { diff --git a/torch/scripts/agent/verify-run-artifacts.mjs b/torch/scripts/agent/verify-run-artifacts.mjs index a70cf6df..bb453411 100644 --- a/torch/scripts/agent/verify-run-artifacts.mjs +++ b/torch/scripts/agent/verify-run-artifacts.mjs @@ -133,6 +133,14 @@ function getMatchingArtifacts(files, { expectedPrefix, session }) { return files.filter((file) => file.name.startsWith(expectedPrefix)); } +function findInvalidFilenameCharacters(files) { + // Windows-invalid filename characters: <>:"/\|?* + const invalidCharRe = /[<>:"/\\|?*]/; + return files + .filter((file) => invalidCharRe.test(file.name)) + .map((file) => file.name); +} + async function hasValidArtifactStructure(files, artifact) { for (const file of files) { const content = await fs.readFile(file.filePath, 'utf8').catch(() => ''); @@ -336,6 +344,11 @@ async function main() { continue; } + const invalidFilenames = findInvalidFilenameCharacters(matchingArtifacts); + if (invalidFilenames.length) { + missing.push(`${artifact.dir}: invalid filename characters for cross-platform sync in ${invalidFilenames.join(', ')}`); + } + const hasValidStructure = await hasValidArtifactStructure(matchingArtifacts, artifact); if (!hasValidStructure) { missing.push(`${artifact.dir}: ${artifact.structureHint}`); diff --git a/torch/src/cmd-doctor.mjs b/torch/src/cmd-doctor.mjs new file mode 100644 index 00000000..27320f2b --- /dev/null +++ b/torch/src/cmd-doctor.mjs @@ -0,0 +1,232 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { detectTorchInstallDir } from './cmd-remove.mjs'; + +const MEMORY_HEADING = '## TORCH Memory Integration'; +const MEMORY_READ_LINE = '`.scheduler-memory/latest/${cadence}/memories.md`'; +const MEMORY_WRITE_LINE = '`memory-update.md`'; + +function parseMajorNodeVersion(version) { + const major = Number.parseInt(String(version).split('.')[0], 10); + return Number.isFinite(major) ? major : null; +} + +function createCheck(id, status, summary, fix = null) { + return { id, status, summary, fix }; +} + +function hasMemoryHook(content) { + return content.includes(MEMORY_HEADING) + && content.includes(MEMORY_READ_LINE) + && content.includes(MEMORY_WRITE_LINE); +} + +/** + * Runs setup and install diagnostics for a TORCH-enabled repository. + * + * @param {Object} [opts] + * @param {string} [opts.cwd] + * @param {string} [opts.nodeVersion] + * @returns {{ + * ok: boolean, + * summary: { passed: number, warned: number, failed: number }, + * checks: Array<{ id: string, status: 'pass'|'warn'|'fail', summary: string, fix: string|null }> + * }} + */ +export function runDoctorChecks(opts = {}) { + const cwd = opts.cwd || process.cwd(); + const nodeVersion = opts.nodeVersion || process.versions.node; + const checks = []; + + const nodeMajor = parseMajorNodeVersion(nodeVersion); + if (nodeMajor !== null && nodeMajor >= 22) { + checks.push(createCheck('node-version', 'pass', `Node.js ${nodeVersion} meets requirement (>= 22).`)); + } else { + checks.push(createCheck( + 'node-version', + 'fail', + `Node.js ${nodeVersion} is below requirement (>= 22).`, + 'Install Node.js 22+ and rerun `npx --no-install torch-lock doctor`.', + )); + } + + const installDir = detectTorchInstallDir(cwd); + if (!installDir) { + checks.push(createCheck( + 'install-detection', + 'fail', + 'No TORCH installation detected.', + 'Run `npm install --force && npx --no-install torch-lock init`.', + )); + } else { + const installLabel = installDir === '.' ? 'root install (.)' : `${installDir}/`; + checks.push(createCheck('install-detection', 'pass', `Detected TORCH install at ${installLabel}.`)); + } + + const configPath = path.join(cwd, 'torch-config.json'); + let config = null; + if (!fs.existsSync(configPath)) { + checks.push(createCheck( + 'torch-config', + 'fail', + 'Missing torch-config.json in project root.', + 'Run `npx --no-install torch-lock init --force` or `npx --no-install torch-lock update --force`.', + )); + } else { + try { + config = JSON.parse(fs.readFileSync(configPath, 'utf8')); + checks.push(createCheck('torch-config', 'pass', 'torch-config.json exists and parses as JSON.')); + } catch (error) { + checks.push(createCheck( + 'torch-config', + 'fail', + `torch-config.json is invalid JSON: ${error.message}`, + 'Fix JSON syntax or regenerate with `npx --no-install torch-lock update --force`.', + )); + } + } + + if (config) { + const dailyHandoff = config?.scheduler?.handoffCommandByCadence?.daily; + const weeklyHandoff = config?.scheduler?.handoffCommandByCadence?.weekly; + if (dailyHandoff && weeklyHandoff) { + checks.push(createCheck('handoff-config', 'pass', 'Scheduler handoff commands are configured for daily and weekly.')); + } else { + checks.push(createCheck( + 'handoff-config', + 'fail', + 'Scheduler handoff command is missing for daily and/or weekly cadence.', + 'Run `npx --no-install torch-lock update --force` to rehydrate scheduler config.', + )); + } + } + + if (installDir) { + const installRoot = path.resolve(cwd, installDir); + const expectedPaths = [ + path.join(installRoot, 'roster.json'), + path.join(installRoot, 'prompts', 'daily'), + path.join(installRoot, 'prompts', 'weekly'), + path.join(installRoot, 'scripts', 'agent', 'run-selected-prompt.mjs'), + ]; + const missing = expectedPaths.filter((targetPath) => !fs.existsSync(targetPath)); + if (missing.length === 0) { + checks.push(createCheck('install-files', 'pass', 'Core TORCH install files are present.')); + } else { + const missingRel = missing.map((targetPath) => path.relative(cwd, targetPath)).join(', '); + checks.push(createCheck( + 'install-files', + 'fail', + `Missing install artifacts: ${missingRel}`, + 'Run `npx --no-install torch-lock update --force` to repair copied files.', + )); + } + + const nodeModulesPath = path.join(installRoot, 'node_modules'); + if (fs.existsSync(nodeModulesPath)) { + checks.push(createCheck('install-deps', 'pass', `${path.relative(cwd, nodeModulesPath)} exists.`)); + } else { + checks.push(createCheck( + 'install-deps', + 'warn', + `${path.relative(cwd, nodeModulesPath)} is missing; TORCH package dependencies may not be installed yet.`, + `Run \`npm install --prefix ${installDir === '.' ? '.' : installDir}\`.`, + )); + } + } + + const promptFiles = ['AGENTS.md', 'CLAUDE.md'] + .map((name) => ({ name, filePath: path.join(cwd, name) })) + .filter((entry) => fs.existsSync(entry.filePath)); + + if (promptFiles.length === 0) { + checks.push(createCheck( + 'memory-hook', + 'warn', + 'No AGENTS.md or CLAUDE.md found in repository root.', + 'Run `npx --no-install torch-lock update --force` to create/update agent prompt hooks.', + )); + } else { + const withHook = []; + const withoutHook = []; + for (const entry of promptFiles) { + const content = fs.readFileSync(entry.filePath, 'utf8'); + if (hasMemoryHook(content)) { + withHook.push(entry.name); + } else { + withoutHook.push(entry.name); + } + } + + if (withHook.length > 0 && withoutHook.length === 0) { + checks.push(createCheck('memory-hook', 'pass', `Memory hook present in: ${withHook.join(', ')}.`)); + } else if (withHook.length > 0) { + checks.push(createCheck( + 'memory-hook', + 'warn', + `Memory hook present in ${withHook.join(', ')} but missing in ${withoutHook.join(', ')}.`, + 'Run `npx --no-install torch-lock update --force` to upsert missing prompt hooks.', + )); + } else { + checks.push(createCheck( + 'memory-hook', + 'fail', + `Memory hook missing from: ${withoutHook.join(', ')}.`, + 'Run `npx --no-install torch-lock update --force` to inject TORCH memory integration.', + )); + } + } + + const summary = { + passed: checks.filter((check) => check.status === 'pass').length, + warned: checks.filter((check) => check.status === 'warn').length, + failed: checks.filter((check) => check.status === 'fail').length, + }; + + return { + ok: summary.failed === 0, + summary, + checks, + }; +} + +/** + * Prints a human-readable setup diagnostic report. + * + * @param {Object} [opts] + * @param {string} [opts.cwd] + * @param {boolean} [opts.json] + * @param {Function} [opts.log] + * @returns {ReturnType} + */ +export function cmdDoctor(opts = {}) { + const { + cwd = process.cwd(), + json = false, + log = console.log, + } = opts; + + const report = runDoctorChecks({ cwd, nodeVersion: opts.nodeVersion }); + if (json) { + log(JSON.stringify(report, null, 2)); + return report; + } + + log('TORCH Doctor Report'); + log(`Root: ${cwd}`); + for (const check of report.checks) { + const badge = check.status === 'pass' ? '[PASS]' : check.status === 'warn' ? '[WARN]' : '[FAIL]'; + log(`${badge} ${check.id}: ${check.summary}`); + } + + log(''); + log(`Summary: ${report.summary.passed} passed, ${report.summary.warned} warned, ${report.summary.failed} failed.`); + if (report.summary.warned > 0 || report.summary.failed > 0) { + log('Recommended fixes:'); + for (const check of report.checks.filter((item) => item.status !== 'pass' && item.fix)) { + log(`- ${check.fix}`); + } + } + + return report; +} diff --git a/torch/src/constants.mjs b/torch/src/constants.mjs index be12c6b1..143aae3d 100644 --- a/torch/src/constants.mjs +++ b/torch/src/constants.mjs @@ -45,6 +45,7 @@ Commands: list [--cadence ] Print active lock table health --cadence Probe relay websocket + publish/read health dashboard [--port ] [--host ] Serve the dashboard (default: ${DEFAULT_DASHBOARD_PORT}) + doctor [--json] Validate local TORCH setup and print fixes init [--force] Initialize torch/ directory in current project update [--force] Update torch/ configuration (backups, merges) remove [--force] Remove all TORCH files and configuration from project diff --git a/torch/src/context/CONTEXT_2026-02-15T01:03:31Z.md b/torch/src/context/CONTEXT_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/src/context/CONTEXT_2026-02-15T01:03:31Z.md rename to torch/src/context/CONTEXT_2026-02-15T01-03-31Z.md diff --git a/torch/src/context/CONTEXT_2026-02-22T16:59:21Z.md b/torch/src/context/CONTEXT_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/src/context/CONTEXT_2026-02-22T16:59:21Z.md rename to torch/src/context/CONTEXT_2026-02-22T16-59-21Z.md diff --git a/torch/src/decisions/DECISIONS_2026-02-15T01:03:31Z.md b/torch/src/decisions/DECISIONS_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/src/decisions/DECISIONS_2026-02-15T01:03:31Z.md rename to torch/src/decisions/DECISIONS_2026-02-15T01-03-31Z.md diff --git a/torch/src/decisions/DECISIONS_2026-02-22T16:59:21Z.md b/torch/src/decisions/DECISIONS_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/src/decisions/DECISIONS_2026-02-22T16:59:21Z.md rename to torch/src/decisions/DECISIONS_2026-02-22T16-59-21Z.md diff --git a/torch/src/lib.mjs b/torch/src/lib.mjs index 280f439c..0ebb1e6c 100644 --- a/torch/src/lib.mjs +++ b/torch/src/lib.mjs @@ -44,6 +44,7 @@ import { cmdRollback } from './cmd-rollback.mjs'; import { cmdCheck } from './cmd-check.mjs'; import { cmdList } from './cmd-list.mjs'; import { cmdComplete } from './cmd-complete.mjs'; +import { cmdDoctor } from './cmd-doctor.mjs'; useWebSocketImplementation(WebSocket); @@ -308,6 +309,12 @@ const COMMAND_HANDLERS = { dashboard: async (args) => { await cmdDashboard(args.port, args.host); }, + doctor: async (args) => { + const report = cmdDoctor({ json: args.json }); + if (!report.ok) { + throw new ExitError(2, 'Doctor found setup failures'); + } + }, init: async (args) => { await cmdInit(args.force); }, diff --git a/torch/src/ops.mjs b/torch/src/ops.mjs index 9383d570..567e2a9b 100644 --- a/torch/src/ops.mjs +++ b/torch/src/ops.mjs @@ -54,6 +54,12 @@ const APP_DIRS = ['src', 'bin', 'dashboard', 'landing', 'assets', 'scripts']; /** Individual files to sync from package root to install directory. */ const APP_FILES = ['package.json', 'build.mjs', 'README.md', 'torch-config.example.json', 'TORCH.md', 'eslint.config.mjs']; +const MEMORY_PROMPT_FILES = ['AGENTS.md', 'CLAUDE.md']; +const MEMORY_INTEGRATION_HEADING = '## TORCH Memory Integration'; +const MEMORY_INTEGRATION_BLOCK = `${MEMORY_INTEGRATION_HEADING} +You have access to the TORCH memory system. +1. READ: Check \`.scheduler-memory/latest/\${cadence}/memories.md\` for past learnings. +2. WRITE: Before exiting, save new insights to \`memory-update.md\` so future runs can learn from this session.`; /** * Resolves the absolute paths for the installation. @@ -478,6 +484,61 @@ function injectHostScriptsIfNeeded(paths, installDir) { } } +/** + * Upserts the TORCH memory integration section inside a system prompt file. + * + * @param {string} content - Existing file content. + * @returns {string} Updated file content. + */ +function upsertMemoryIntegrationBlock(content) { + if (content.includes(MEMORY_INTEGRATION_BLOCK)) { + return content; + } + + const escapedHeading = MEMORY_INTEGRATION_HEADING.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const sectionRegex = new RegExp(`(^|\\n)${escapedHeading}\\n[\\s\\S]*?(?=\\n##\\s|$)`, 'm'); + + if (sectionRegex.test(content)) { + return content.replace(sectionRegex, (_match, prefix) => `${prefix}${MEMORY_INTEGRATION_BLOCK}`); + } + + const trimmed = content.trimEnd(); + if (!trimmed) { + return `${MEMORY_INTEGRATION_BLOCK}\n`; + } + return `${trimmed}\n\n${MEMORY_INTEGRATION_BLOCK}\n`; +} + +/** + * Ensures AGENTS/CLAUDE system prompt files include TORCH memory integration. + * If neither file exists, creates AGENTS.md with the integration block. + * + * @param {string} root - Host project root. + */ +function ensureMemoryPromptHook(root) { + const existingTargets = MEMORY_PROMPT_FILES.filter((file) => fs.existsSync(path.join(root, file))); + + if (existingTargets.length === 0) { + const defaultPath = path.join(root, 'AGENTS.md'); + fs.writeFileSync(defaultPath, `${MEMORY_INTEGRATION_BLOCK}\n`, 'utf8'); + console.log(`Created ${path.relative(root, defaultPath)} with TORCH memory integration.`); + return; + } + + for (const file of existingTargets) { + const filePath = path.join(root, file); + const current = fs.readFileSync(filePath, 'utf8'); + const next = upsertMemoryIntegrationBlock(current); + + if (next !== current) { + fs.writeFileSync(filePath, next, 'utf8'); + console.log(`Updated ${path.relative(root, filePath)} with TORCH memory integration.`); + } else { + console.log(`TORCH memory integration already present in ${path.relative(root, filePath)}.`); + } + } +} + /** * Main entry point for `torch-lock init`. * @@ -505,6 +566,7 @@ export async function cmdInit(force = false, cwd = process.cwd(), mockAnswers = configureTorch(cwd, paths, installDir, namespace, relays, hashtag); const dashboardUrl = createDashboardLinkFile(paths, namespace, relays, hashtag); injectHostScriptsIfNeeded(paths, installDir); + ensureMemoryPromptHook(paths.root); console.log('\nInitialization complete.'); console.log('You can now customize the files in ' + path.relative(cwd, paths.torchDir) + '/'); @@ -712,5 +774,6 @@ export function cmdUpdate(force = false, cwd = process.cwd()) { } } + ensureMemoryPromptHook(paths.root); console.log('\nUpdate complete.'); } diff --git a/torch/src/prompts/scheduler-flow.md b/torch/src/prompts/scheduler-flow.md index 70c4bb04..e79d8047 100644 --- a/torch/src/prompts/scheduler-flow.md +++ b/torch/src/prompts/scheduler-flow.md @@ -26,6 +26,8 @@ Every agent prompt invoked by the schedulers (daily/weekly) MUST enforce this co - `docs/agent-handoffs/README.md` - Recent notes in `docs/agent-handoffs/learnings/` and `docs/agent-handoffs/incidents/` 2. **Update run artifacts** in `src/context/`, `src/todo/`, `src/decisions/`, and `src/test_logs/` during the run, or explicitly document why each artifact update is not needed for that run. + - When creating filenames with ``, use the scheduler-provided `SCHEDULER_RUN_TIMESTAMP` value when available. + - Required filename-safe format: `YYYY-MM-DDTHH-MM-SSZ` (for example `2026-02-15T01-03-31Z`); never use `:` in filenames. 3. **Capture reusable failures and unresolved issues**: - Record reusable failures in `docs/agent-handoffs/incidents/` - Record active unresolved reproducible items in `KNOWN_ISSUES.md` diff --git a/torch/src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md b/torch/src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/src/test_logs/TEST_LOG_2026-02-15T01:03:31Z.md rename to torch/src/test_logs/TEST_LOG_2026-02-15T01-03-31Z.md diff --git a/torch/src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md b/torch/src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/src/test_logs/TEST_LOG_2026-02-22T16:59:21Z.md rename to torch/src/test_logs/TEST_LOG_2026-02-22T16-59-21Z.md diff --git a/torch/src/todo/TODO_2026-02-15T01:03:31Z.md b/torch/src/todo/TODO_2026-02-15T01-03-31Z.md similarity index 100% rename from torch/src/todo/TODO_2026-02-15T01:03:31Z.md rename to torch/src/todo/TODO_2026-02-15T01-03-31Z.md diff --git a/torch/src/todo/TODO_2026-02-22T16:59:21Z.md b/torch/src/todo/TODO_2026-02-22T16-59-21Z.md similarity index 100% rename from torch/src/todo/TODO_2026-02-22T16:59:21Z.md rename to torch/src/todo/TODO_2026-02-22T16-59-21Z.md