mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
refactor(ops): extract cmdRemove to src/cmd-remove.mjs
Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
9fef4c4f46
commit
7c8eeb939c
16 files changed
+350
-377
No files matched your search
@@ -0,0 +1,12 @@
|
||||
# Memory Update — refactor-agent — 2026-02-21
|
||||
|
||||
## Key findings
|
||||
- Extracted `cmdRemove` successfully.
|
||||
- `src/ops.mjs` was over 800 LOC, now lighter.
|
||||
|
||||
## Patterns / reusable knowledge
|
||||
- When extracting commands, remember to move or duplicate helper functions they depend on, or export them if shared.
|
||||
- Tests often need renaming and import updates.
|
||||
|
||||
## Warnings / gotchas
|
||||
- `test/ops-remove.test.mjs` was renamed to `test/cmd-remove.test.mjs`.
|
||||
@@ -0,0 +1,256 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import readline from 'node:readline/promises';
|
||||
import { stdin as input, stdout as output } from 'node:process';
|
||||
import { TORCH_HOST_SCRIPTS } from './constants.mjs';
|
||||
|
||||
/**
|
||||
* Detects the TORCH install directory within a project.
|
||||
*
|
||||
* Strategy:
|
||||
* 1. If a 'torch/' directory exists with a roster.json or bin/, it's the install dir.
|
||||
* 2. If the current directory's package.json has name "torch-lock", installed to '.'.
|
||||
* 3. Returns null if TORCH is not detected.
|
||||
*
|
||||
* @param {string} cwd - The project root directory
|
||||
* @returns {string|null} - The install directory name ('torch', '.', etc.) or null
|
||||
*/
|
||||
export function detectTorchInstallDir(cwd) {
|
||||
// Check for 'torch/' subdirectory with TORCH markers
|
||||
const torchSubdir = path.join(cwd, 'torch');
|
||||
if (fs.existsSync(torchSubdir)) {
|
||||
const hasRoster = fs.existsSync(path.join(torchSubdir, 'roster.json'));
|
||||
const hasBin = fs.existsSync(path.join(torchSubdir, 'bin'));
|
||||
if (hasRoster || hasBin) {
|
||||
return 'torch';
|
||||
}
|
||||
}
|
||||
|
||||
// Check if installed to current directory
|
||||
const pkgPath = path.join(cwd, 'package.json');
|
||||
if (fs.existsSync(pkgPath)) {
|
||||
try {
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
||||
if (pkg.name === 'torch-lock') {
|
||||
return '.';
|
||||
}
|
||||
} catch (_e) {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a directory or file if it exists, logging the action.
|
||||
*
|
||||
* @param {string} target - Absolute path to remove
|
||||
* @param {string} label - Human-readable label for log output
|
||||
* @param {Function} log - Logging function
|
||||
* @returns {boolean} - True if something was removed
|
||||
*/
|
||||
export function removeIfExists(target, label, log) {
|
||||
if (!fs.existsSync(target)) return false;
|
||||
|
||||
const stat = fs.statSync(target);
|
||||
if (stat.isDirectory()) {
|
||||
fs.rmSync(target, { recursive: true, force: true });
|
||||
} else {
|
||||
fs.unlinkSync(target);
|
||||
}
|
||||
log(` Removed ${label}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes TORCH torch:* scripts from the host package.json.
|
||||
*
|
||||
* @param {string} hostRoot - Project root containing package.json
|
||||
* @param {Function} log - Logging function
|
||||
* @returns {boolean} - True if any scripts were removed
|
||||
*/
|
||||
export function removeHostScripts(hostRoot, log) {
|
||||
const hostPkgPath = path.join(hostRoot, 'package.json');
|
||||
if (!fs.existsSync(hostPkgPath)) return false;
|
||||
|
||||
try {
|
||||
const raw = fs.readFileSync(hostPkgPath, 'utf8');
|
||||
const pkg = JSON.parse(raw);
|
||||
if (!pkg.scripts) return false;
|
||||
|
||||
let modified = false;
|
||||
for (const key of TORCH_HOST_SCRIPTS) {
|
||||
if (key in pkg.scripts) {
|
||||
delete pkg.scripts[key];
|
||||
log(` Removed script: "${key}"`);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
fs.writeFileSync(hostPkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
||||
log(' Updated package.json.');
|
||||
}
|
||||
return modified;
|
||||
} catch (e) {
|
||||
log(` Warning: Could not update package.json: ${e.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely removes TORCH from a project that installed it via
|
||||
* `npm install <torch-tarball> && npx torch-lock init`.
|
||||
*
|
||||
* What it removes:
|
||||
* 1. The torch/ install directory (or equivalent)
|
||||
* 2. torch-config.json at the project root
|
||||
* 3. .torch/ hidden directory (prompt history)
|
||||
* 4. .scheduler-memory/ directory (runtime memory store)
|
||||
* 5. task-logs/ directory (scheduler run logs)
|
||||
* 6. src/proposals/ directory (governance proposals)
|
||||
* 7. torch:* scripts from the host package.json
|
||||
* 8. The torch-lock npm package (node_modules/torch-lock)
|
||||
*
|
||||
* @param {boolean} force - If true, skip the confirmation prompt
|
||||
* @param {string} cwd - Working directory (project root)
|
||||
* @param {Object|null} mockAnswers - For testing: { confirm: true/false }
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function cmdRemove(force = false, cwd = process.cwd(), mockAnswers = null) {
|
||||
const log = console.log;
|
||||
const installDir = detectTorchInstallDir(cwd);
|
||||
|
||||
if (!installDir) {
|
||||
log('No TORCH installation detected in this directory.');
|
||||
log('Looked for:');
|
||||
log(' - A torch/ subdirectory containing roster.json or bin/');
|
||||
log(' - A package.json with name "torch-lock" (root install)');
|
||||
return;
|
||||
}
|
||||
|
||||
const isRootInstall = installDir === '.';
|
||||
const torchDir = path.resolve(cwd, installDir);
|
||||
|
||||
// Build a manifest of what will be removed
|
||||
const targets = [];
|
||||
|
||||
if (!isRootInstall && fs.existsSync(torchDir)) {
|
||||
targets.push({ path: torchDir, label: `${installDir}/ (TORCH install directory)` });
|
||||
}
|
||||
|
||||
const configPath = path.join(cwd, 'torch-config.json');
|
||||
if (fs.existsSync(configPath)) {
|
||||
targets.push({ path: configPath, label: 'torch-config.json' });
|
||||
}
|
||||
|
||||
const dotTorch = path.join(cwd, '.torch');
|
||||
if (fs.existsSync(dotTorch)) {
|
||||
targets.push({ path: dotTorch, label: '.torch/ (prompt history)' });
|
||||
}
|
||||
|
||||
const schedulerMemory = path.join(cwd, '.scheduler-memory');
|
||||
if (fs.existsSync(schedulerMemory)) {
|
||||
targets.push({ path: schedulerMemory, label: '.scheduler-memory/ (memory store)' });
|
||||
}
|
||||
|
||||
const taskLogs = path.join(cwd, 'task-logs');
|
||||
if (fs.existsSync(taskLogs)) {
|
||||
targets.push({ path: taskLogs, label: 'task-logs/ (scheduler logs)' });
|
||||
}
|
||||
|
||||
const proposals = path.join(cwd, 'src', 'proposals');
|
||||
if (fs.existsSync(proposals)) {
|
||||
targets.push({ path: proposals, label: 'src/proposals/ (governance proposals)' });
|
||||
}
|
||||
|
||||
// Check for host scripts
|
||||
const hostPkgPath = path.join(cwd, 'package.json');
|
||||
let hasHostScripts = false;
|
||||
if (fs.existsSync(hostPkgPath)) {
|
||||
try {
|
||||
const pkg = JSON.parse(fs.readFileSync(hostPkgPath, 'utf8'));
|
||||
if (pkg.scripts) {
|
||||
hasHostScripts = TORCH_HOST_SCRIPTS.some(key => key in pkg.scripts);
|
||||
}
|
||||
} catch (_e) { /* ignore */ }
|
||||
}
|
||||
|
||||
if (targets.length === 0 && !hasHostScripts) {
|
||||
log('Nothing to remove — no TORCH artifacts found.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show what will be removed
|
||||
log('\nThe following TORCH artifacts will be removed:\n');
|
||||
for (const t of targets) {
|
||||
log(` - ${t.label}`);
|
||||
}
|
||||
if (hasHostScripts) {
|
||||
log(' - torch:* scripts from package.json');
|
||||
}
|
||||
log(' - torch-lock npm package (via npm uninstall)');
|
||||
log('');
|
||||
|
||||
// Confirm unless --force
|
||||
if (!force) {
|
||||
let confirmed;
|
||||
|
||||
if (mockAnswers) {
|
||||
confirmed = mockAnswers.confirm;
|
||||
} else {
|
||||
const rl = readline.createInterface({ input, output });
|
||||
try {
|
||||
const answer = await rl.question('Proceed with removal? (yes/no): ');
|
||||
confirmed = answer.trim().toLowerCase() === 'yes';
|
||||
} finally {
|
||||
rl.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (!confirmed) {
|
||||
log('Removal cancelled.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log('\nRemoving TORCH...\n');
|
||||
|
||||
// 1. Remove directories and files
|
||||
for (const t of targets) {
|
||||
removeIfExists(t.path, t.label, log);
|
||||
}
|
||||
|
||||
// 2. Clean up empty src/ directory if proposals was the only thing in it
|
||||
const srcDir = path.join(cwd, 'src');
|
||||
if (fs.existsSync(srcDir)) {
|
||||
try {
|
||||
const entries = fs.readdirSync(srcDir);
|
||||
if (entries.length === 0) {
|
||||
fs.rmdirSync(srcDir);
|
||||
log(' Removed empty src/ directory');
|
||||
}
|
||||
} catch (_e) { /* leave it if not empty or permission error */ }
|
||||
}
|
||||
|
||||
// 3. Remove host package.json scripts
|
||||
if (hasHostScripts) {
|
||||
removeHostScripts(cwd, log);
|
||||
}
|
||||
|
||||
// 4. Uninstall the npm package
|
||||
log('\n Running npm uninstall torch-lock...');
|
||||
try {
|
||||
const { execSync } = await import('node:child_process');
|
||||
execSync('npm uninstall torch-lock', { cwd, stdio: 'pipe' });
|
||||
log(' Uninstalled torch-lock package.');
|
||||
} catch (e) {
|
||||
log(` Warning: npm uninstall torch-lock failed: ${e.message}`);
|
||||
log(' You may need to run "npm uninstall torch-lock" manually.');
|
||||
}
|
||||
|
||||
log('\nTORCH has been completely removed from this project.');
|
||||
log('If you used TORCH environment variables (NOSTR_LOCK_*, TORCH_*),');
|
||||
log('remember to remove them from your shell profile or CI configuration.');
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
agent: refactor-agent
|
||||
cadence: weekly
|
||||
session: 2026-02-21T20:22:19Z
|
||||
run-start: 2026-02-21T20:22:19Z
|
||||
prompt: src/prompts/weekly/refactor-agent.md
|
||||
---
|
||||
|
||||
# Context
|
||||
|
||||
## Goal
|
||||
Refactor `src/ops.mjs` to improve modularity.
|
||||
|
||||
## Scope
|
||||
Extract `cmdRemove` and related helpers into `src/cmd-remove.mjs`.
|
||||
|
||||
## Constraints
|
||||
- Preserve backward compatibility by re-exporting.
|
||||
- Add unit tests.
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
agent: refactor-agent
|
||||
cadence: weekly
|
||||
session: 2026-02-21T20:22:19Z
|
||||
run-start: 2026-02-21T20:22:19Z
|
||||
prompt: src/prompts/weekly/refactor-agent.md
|
||||
---
|
||||
|
||||
# Decisions
|
||||
|
||||
## Decision
|
||||
Extracted `cmdRemove` to `src/cmd-remove.mjs`.
|
||||
|
||||
## Rationale
|
||||
To adhere to separation of concerns and reduce the size of `src/ops.mjs` (>800 LOC).
|
||||
|
||||
## Decision
|
||||
Removed duplicate export of `mergeRelayList` in `src/lock-utils.mjs`.
|
||||
|
||||
## Rationale
|
||||
It caused syntax errors blocking execution.
|
||||
@@ -62,10 +62,6 @@ export function withTimeout(promise, timeoutMs, timeoutMessage = 'Operation time
|
||||
]);
|
||||
}
|
||||
|
||||
export function mergeRelayList(primaryRelays, fallbackRelays) {
|
||||
return [...new Set([...primaryRelays, ...fallbackRelays])];
|
||||
}
|
||||
|
||||
const MAX_RANDOM = 281474976710655; // 2**48 - 1
|
||||
|
||||
export function secureRandom() {
|
||||
|
||||
+2
-252
@@ -4,8 +4,9 @@ import { fileURLToPath } from 'node:url';
|
||||
import crypto from 'node:crypto';
|
||||
import readline from 'node:readline/promises';
|
||||
import { stdin as input, stdout as output } from 'node:process';
|
||||
import { DEFAULT_RELAYS, TORCH_HOST_SCRIPTS } from './constants.mjs';
|
||||
import { DEFAULT_RELAYS } from './constants.mjs';
|
||||
import { ensureDir } from './utils.mjs';
|
||||
export { cmdRemove } from './cmd-remove.mjs';
|
||||
|
||||
const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
||||
@@ -453,257 +454,6 @@ function injectScriptsIntoHost(hostRoot, installDirName) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the TORCH install directory within a project.
|
||||
*
|
||||
* Strategy:
|
||||
* 1. If a 'torch/' directory exists with a roster.json or bin/, it's the install dir.
|
||||
* 2. If the current directory's package.json has name "torch-lock", installed to '.'.
|
||||
* 3. Returns null if TORCH is not detected.
|
||||
*
|
||||
* @param {string} cwd - The project root directory
|
||||
* @returns {string|null} - The install directory name ('torch', '.', etc.) or null
|
||||
*/
|
||||
function detectTorchInstallDir(cwd) {
|
||||
// Check for 'torch/' subdirectory with TORCH markers
|
||||
const torchSubdir = path.join(cwd, 'torch');
|
||||
if (fs.existsSync(torchSubdir)) {
|
||||
const hasRoster = fs.existsSync(path.join(torchSubdir, 'roster.json'));
|
||||
const hasBin = fs.existsSync(path.join(torchSubdir, 'bin'));
|
||||
if (hasRoster || hasBin) {
|
||||
return 'torch';
|
||||
}
|
||||
}
|
||||
|
||||
// Check if installed to current directory
|
||||
const pkgPath = path.join(cwd, 'package.json');
|
||||
if (fs.existsSync(pkgPath)) {
|
||||
try {
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
||||
if (pkg.name === 'torch-lock') {
|
||||
return '.';
|
||||
}
|
||||
} catch (_e) {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a directory or file if it exists, logging the action.
|
||||
*
|
||||
* @param {string} target - Absolute path to remove
|
||||
* @param {string} label - Human-readable label for log output
|
||||
* @param {Function} log - Logging function
|
||||
* @returns {boolean} - True if something was removed
|
||||
*/
|
||||
function removeIfExists(target, label, log) {
|
||||
if (!fs.existsSync(target)) return false;
|
||||
|
||||
const stat = fs.statSync(target);
|
||||
if (stat.isDirectory()) {
|
||||
fs.rmSync(target, { recursive: true, force: true });
|
||||
} else {
|
||||
fs.unlinkSync(target);
|
||||
}
|
||||
log(` Removed ${label}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes TORCH torch:* scripts from the host package.json.
|
||||
*
|
||||
* @param {string} hostRoot - Project root containing package.json
|
||||
* @param {Function} log - Logging function
|
||||
* @returns {boolean} - True if any scripts were removed
|
||||
*/
|
||||
function removeHostScripts(hostRoot, log) {
|
||||
const hostPkgPath = path.join(hostRoot, 'package.json');
|
||||
if (!fs.existsSync(hostPkgPath)) return false;
|
||||
|
||||
try {
|
||||
const raw = fs.readFileSync(hostPkgPath, 'utf8');
|
||||
const pkg = JSON.parse(raw);
|
||||
if (!pkg.scripts) return false;
|
||||
|
||||
let modified = false;
|
||||
for (const key of TORCH_HOST_SCRIPTS) {
|
||||
if (key in pkg.scripts) {
|
||||
delete pkg.scripts[key];
|
||||
log(` Removed script: "${key}"`);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
fs.writeFileSync(hostPkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
||||
log(' Updated package.json.');
|
||||
}
|
||||
return modified;
|
||||
} catch (e) {
|
||||
log(` Warning: Could not update package.json: ${e.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely removes TORCH from a project that installed it via
|
||||
* `npm install <torch-tarball> && npx torch-lock init`.
|
||||
*
|
||||
* What it removes:
|
||||
* 1. The torch/ install directory (or equivalent)
|
||||
* 2. torch-config.json at the project root
|
||||
* 3. .torch/ hidden directory (prompt history)
|
||||
* 4. .scheduler-memory/ directory (runtime memory store)
|
||||
* 5. task-logs/ directory (scheduler run logs)
|
||||
* 6. src/proposals/ directory (governance proposals)
|
||||
* 7. torch:* scripts from the host package.json
|
||||
* 8. The torch-lock npm package (node_modules/torch-lock)
|
||||
*
|
||||
* @param {boolean} force - If true, skip the confirmation prompt
|
||||
* @param {string} cwd - Working directory (project root)
|
||||
* @param {Object|null} mockAnswers - For testing: { confirm: true/false }
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function cmdRemove(force = false, cwd = process.cwd(), mockAnswers = null) {
|
||||
const log = console.log;
|
||||
const installDir = detectTorchInstallDir(cwd);
|
||||
|
||||
if (!installDir) {
|
||||
log('No TORCH installation detected in this directory.');
|
||||
log('Looked for:');
|
||||
log(' - A torch/ subdirectory containing roster.json or bin/');
|
||||
log(' - A package.json with name "torch-lock" (root install)');
|
||||
return;
|
||||
}
|
||||
|
||||
const isRootInstall = installDir === '.';
|
||||
const torchDir = path.resolve(cwd, installDir);
|
||||
|
||||
// Build a manifest of what will be removed
|
||||
const targets = [];
|
||||
|
||||
if (!isRootInstall && fs.existsSync(torchDir)) {
|
||||
targets.push({ path: torchDir, label: `${installDir}/ (TORCH install directory)` });
|
||||
}
|
||||
|
||||
const configPath = path.join(cwd, 'torch-config.json');
|
||||
if (fs.existsSync(configPath)) {
|
||||
targets.push({ path: configPath, label: 'torch-config.json' });
|
||||
}
|
||||
|
||||
const dotTorch = path.join(cwd, '.torch');
|
||||
if (fs.existsSync(dotTorch)) {
|
||||
targets.push({ path: dotTorch, label: '.torch/ (prompt history)' });
|
||||
}
|
||||
|
||||
const schedulerMemory = path.join(cwd, '.scheduler-memory');
|
||||
if (fs.existsSync(schedulerMemory)) {
|
||||
targets.push({ path: schedulerMemory, label: '.scheduler-memory/ (memory store)' });
|
||||
}
|
||||
|
||||
const taskLogs = path.join(cwd, 'task-logs');
|
||||
if (fs.existsSync(taskLogs)) {
|
||||
targets.push({ path: taskLogs, label: 'task-logs/ (scheduler logs)' });
|
||||
}
|
||||
|
||||
const proposals = path.join(cwd, 'src', 'proposals');
|
||||
if (fs.existsSync(proposals)) {
|
||||
targets.push({ path: proposals, label: 'src/proposals/ (governance proposals)' });
|
||||
}
|
||||
|
||||
// Check for host scripts
|
||||
const hostPkgPath = path.join(cwd, 'package.json');
|
||||
let hasHostScripts = false;
|
||||
if (fs.existsSync(hostPkgPath)) {
|
||||
try {
|
||||
const pkg = JSON.parse(fs.readFileSync(hostPkgPath, 'utf8'));
|
||||
if (pkg.scripts) {
|
||||
hasHostScripts = TORCH_HOST_SCRIPTS.some(key => key in pkg.scripts);
|
||||
}
|
||||
} catch (_e) { /* ignore */ }
|
||||
}
|
||||
|
||||
if (targets.length === 0 && !hasHostScripts) {
|
||||
log('Nothing to remove — no TORCH artifacts found.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show what will be removed
|
||||
log('\nThe following TORCH artifacts will be removed:\n');
|
||||
for (const t of targets) {
|
||||
log(` - ${t.label}`);
|
||||
}
|
||||
if (hasHostScripts) {
|
||||
log(' - torch:* scripts from package.json');
|
||||
}
|
||||
log(' - torch-lock npm package (via npm uninstall)');
|
||||
log('');
|
||||
|
||||
// Confirm unless --force
|
||||
if (!force) {
|
||||
let confirmed;
|
||||
|
||||
if (mockAnswers) {
|
||||
confirmed = mockAnswers.confirm;
|
||||
} else {
|
||||
const rl = readline.createInterface({ input, output });
|
||||
try {
|
||||
const answer = await rl.question('Proceed with removal? (yes/no): ');
|
||||
confirmed = answer.trim().toLowerCase() === 'yes';
|
||||
} finally {
|
||||
rl.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (!confirmed) {
|
||||
log('Removal cancelled.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log('\nRemoving TORCH...\n');
|
||||
|
||||
// 1. Remove directories and files
|
||||
for (const t of targets) {
|
||||
removeIfExists(t.path, t.label, log);
|
||||
}
|
||||
|
||||
// 2. Clean up empty src/ directory if proposals was the only thing in it
|
||||
const srcDir = path.join(cwd, 'src');
|
||||
if (fs.existsSync(srcDir)) {
|
||||
try {
|
||||
const entries = fs.readdirSync(srcDir);
|
||||
if (entries.length === 0) {
|
||||
fs.rmdirSync(srcDir);
|
||||
log(' Removed empty src/ directory');
|
||||
}
|
||||
} catch (_e) { /* leave it if not empty or permission error */ }
|
||||
}
|
||||
|
||||
// 3. Remove host package.json scripts
|
||||
if (hasHostScripts) {
|
||||
removeHostScripts(cwd, log);
|
||||
}
|
||||
|
||||
// 4. Uninstall the npm package
|
||||
log('\n Running npm uninstall torch-lock...');
|
||||
try {
|
||||
const { execSync } = await import('node:child_process');
|
||||
execSync('npm uninstall torch-lock', { cwd, stdio: 'pipe' });
|
||||
log(' Uninstalled torch-lock package.');
|
||||
} catch (e) {
|
||||
log(` Warning: npm uninstall torch-lock failed: ${e.message}`);
|
||||
log(' You may need to run "npm uninstall torch-lock" manually.');
|
||||
}
|
||||
|
||||
log('\nTORCH has been completely removed from this project.');
|
||||
log('If you used TORCH environment variables (NOSTR_LOCK_*, TORCH_*),');
|
||||
log('remember to remove them from your shell profile or CI configuration.');
|
||||
}
|
||||
|
||||
export function cmdUpdate(force = false, cwd = process.cwd()) {
|
||||
// Update logic needs to know WHERE torch is installed.
|
||||
// We can look for torch directory? Or assume 'torch'?
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
agent: refactor-agent
|
||||
cadence: weekly
|
||||
session: 2026-02-21T20:22:19Z
|
||||
run-start: 2026-02-21T20:22:19Z
|
||||
prompt: src/prompts/weekly/refactor-agent.md
|
||||
---
|
||||
|
||||
# Test Log
|
||||
|
||||
- **Command**: `npm test`
|
||||
- **Result**: Passed (122 tests).
|
||||
|
||||
- **Command**: `node --test test/cmd-remove.test.mjs`
|
||||
- **Result**: Passed.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
agent: refactor-agent
|
||||
cadence: weekly
|
||||
session: 2026-02-21T20:22:19Z
|
||||
run-start: 2026-02-21T20:22:19Z
|
||||
prompt: src/prompts/weekly/refactor-agent.md
|
||||
---
|
||||
|
||||
# Todo
|
||||
|
||||
## Pending Tasks
|
||||
- None.
|
||||
|
||||
## Completed Tasks
|
||||
- [x] Extract `cmdRemove`.
|
||||
- [x] Update tests.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
agent: event-schema-agent
|
||||
status: failed
|
||||
date: 2026-02-14
|
||||
---
|
||||
|
||||
# Preflight Check Failed
|
||||
|
||||
The `event-schema-agent` requires `src/lib/eventSchemas.js` as the canonical source of truth for event builders and sanitizers. This file does not exist in the `torch-lock` repository.
|
||||
|
||||
## Findings
|
||||
|
||||
1. **Missing Source of Truth**: `src/lib/eventSchemas.js` is referenced in the agent prompt but is absent from the codebase.
|
||||
2. **Runtime Construction**: Event construction in this repository currently happens inline (e.g., in `src/nostr-lock.mjs`), which violates the pattern of using shared, canonical builders.
|
||||
3. **Action**: Aborting execution. No validator harness can be generated without the canonical schemas.
|
||||
|
||||
## Recommendations
|
||||
|
||||
- Create `src/lib/eventSchemas.js` to centralize event construction (e.g., for the lock event).
|
||||
- Update `event-schema-agent` prompt or repository documentation to reflect the actual location of schemas if they exist elsewhere.
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
agent: event-schema-agent
|
||||
status: failed
|
||||
---
|
||||
# Weekly Scheduler Run Failed
|
||||
|
||||
reason: Lock backend error
|
||||
|
||||
## Details
|
||||
- cadence: weekly
|
||||
- excluded: []
|
||||
- selected_agent: event-schema-agent
|
||||
- lock_command: `AGENT_PLATFORM=codex npm run lock:lock -- --agent event-schema-agent --cadence weekly`
|
||||
- lock_exit_code: 2
|
||||
@@ -1 +0,0 @@
|
||||
Preflight check failed: Required files `src/lib/eventSchemas.js`, `src/lib/clientFacade.js` (or defaultClient.js), and `js/dmDecryptor.js` are missing from the repository. This agent appears to be misconfigured for the current repository structure.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
agent: prompt-maintenance-agent
|
||||
status: failed
|
||||
---
|
||||
# Weekly Scheduler Run Failed
|
||||
|
||||
reason: Lock backend error
|
||||
|
||||
## Details
|
||||
- cadence: weekly
|
||||
- log_dir: task-logs/weekly/
|
||||
- branch_prefix: agents/weekly/
|
||||
- prompt_dir: src/prompts/weekly/
|
||||
- excluded: [bug-reproducer-agent, changelog-agent, dead-code-agent, feature-proposer-agent, frontend-console-debug-agent, fuzz-agent, perf-deepdive-agent, perf-optimization-agent, pr-review-agent]
|
||||
- selected_agent: prompt-maintenance-agent
|
||||
- lock_command: `AGENT_PLATFORM=codex npm run lock:lock -- --agent prompt-maintenance-agent --cadence weekly`
|
||||
- lock_exit_code: 2
|
||||
- next_action: Retry scheduler cycle after relay connectivity is restored.
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
agent: perf-optimization-agent
|
||||
cadence: weekly
|
||||
platform: claude
|
||||
status: failed
|
||||
timestamp: "2026-02-19T00:58:49Z"
|
||||
failure_class: backend_unavailable
|
||||
failure_category: lock_backend_error
|
||||
---
|
||||
|
||||
# Task Log — Failed
|
||||
|
||||
## Reason
|
||||
|
||||
Lock backend error
|
||||
|
||||
## Detail
|
||||
|
||||
Lock acquisition for `perf-optimization-agent` (weekly cadence) failed with exit code 2.
|
||||
All Nostr relays returned `permanent_validation_error`: "Received network error or non-101 status code."
|
||||
WebSocket connectivity to the relay pool is unavailable in this execution environment.
|
||||
|
||||
No prompt was executed. `lock:complete` was NOT published.
|
||||
|
||||
## Scheduler State
|
||||
|
||||
- **Selected agent:** perf-optimization-agent
|
||||
- **Selection algorithm:**
|
||||
- Roster source: `src/prompts/roster.json` (weekly key)
|
||||
- Latest log file: `2026-02-18T19-54-03Z__perf-deepdive-agent__completed.md`
|
||||
- Previous agent: `perf-deepdive-agent` (from filename convention, index 6)
|
||||
- start_index: `(6 + 1) mod 21 = 7`
|
||||
- Candidate at index 7: `perf-optimization-agent` — not in excluded set → selected
|
||||
- **Excluded set (from lock:check:weekly):** bug-reproducer-agent, changelog-agent, dead-code-agent, feature-proposer-agent, frontend-console-debug-agent, fuzz-agent, perf-deepdive-agent, test-coverage-agent, ui-ux-agent, weekly-synthesis-agent
|
||||
|
||||
## Lock Failure Metadata
|
||||
|
||||
- **lock_command:** `AGENT_PLATFORM=claude npm run lock:lock -- --agent perf-optimization-agent --cadence weekly`
|
||||
- **backend_category:** relay_publish_non_retryable
|
||||
- **lock_correlation_id:** c5e5d9fc-85f5-41ca-b4d2-3671f815d157
|
||||
- **lock_attempt_id:** 5403bebc-a9f5-4a30-a3dd-6df7af4ec213
|
||||
- **successCount:** 0 / 3
|
||||
- **requiredSuccesses:** 1
|
||||
- **relay_list:**
|
||||
- wss://nos.lol (publish:primary, reason=permanent_validation_error)
|
||||
- wss://relay.damus.io (publish:primary, reason=permanent_validation_error)
|
||||
- wss://relay.primal.net (publish:primary, reason=permanent_validation_error)
|
||||
- **lock_stderr_excerpt:** "Failed relay publish quorum in publish phase: 0/3 successful (required=1, timeout=15000ms, attempts=4)"
|
||||
|
||||
## Retry Guidance
|
||||
|
||||
1. Verify relay connectivity: `npm run lock:health -- --cadence weekly`
|
||||
2. Retry lock acquisition: `AGENT_PLATFORM=claude npm run lock:lock -- --agent perf-optimization-agent --cadence weekly`
|
||||
3. If all relays remain unhealthy, consult `docs/agent-handoffs/incidents/` for relay remediation guidance.
|
||||
4. See incident runbook: `docs/agent-handoffs/learnings/2026-02-15-relay-health-preflight-job.md`
|
||||
|
||||
## Prompt Not Executed
|
||||
|
||||
The selected prompt `src/prompts/weekly/perf-optimization-agent.md` was NOT executed.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
agent: pr-review-agent
|
||||
cadence: weekly
|
||||
run_start: 2026-02-19T05:06:16Z
|
||||
platform: linux
|
||||
failure_reason: Validation failed (Verify: 1, Lint: 0)
|
||||
---
|
||||
Run failed.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
agent: refactor-agent
|
||||
cadence: weekly
|
||||
platform: codex
|
||||
---
|
||||
|
||||
Run completed successfully.
|
||||
@@ -2,7 +2,8 @@ import { test, after } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { cmdInit, cmdRemove } from '../src/ops.mjs';
|
||||
import { cmdInit } from '../src/ops.mjs';
|
||||
import { cmdRemove } from '../src/cmd-remove.mjs';
|
||||
|
||||
const MOCK_CONFIG = {
|
||||
installDir: 'torch',
|
||||
Reference in new issue
Block a user