@@ -9,5 +9,4 @@ import { resolveApiUrl, resolveToken } from "../lib/config.js"; | ||
| .command("init") | ||
| .description("Wire Vruum into every detected AI coding harness (MCP + skills + instructions)") | ||
| .option("--project-dir <dir>", "project root for instruction files", process.cwd()) | ||
| .action(async (opts) => { | ||
| .description("Wire Vruum into every detected AI coding harness (MCP + skills)") | ||
| .action(async () => { | ||
| const token = await resolveToken(); | ||
@@ -23,3 +22,2 @@ if (!token) { | ||
| token, | ||
| projectRoot: opts.projectDir, | ||
| }; | ||
@@ -35,3 +33,2 @@ const results = []; | ||
| ["skills", (o) => adapter.linkSkills(o)], | ||
| ["instructions", (o) => adapter.writeInstructions(o)], | ||
| ]; | ||
@@ -38,0 +35,0 @@ const stepResults = []; |
@@ -60,9 +60,2 @@ /** | ||
| }, | ||
| async writeInstructions() { | ||
| return { | ||
| step: "instructions", | ||
| status: "deferred", | ||
| detail: "AGENTS.md/CLAUDE.md emitter lands in VRU-469b", | ||
| }; | ||
| }, | ||
| }; |
@@ -144,9 +144,2 @@ /** | ||
| }, | ||
| async writeInstructions() { | ||
| return { | ||
| step: "instructions", | ||
| status: "deferred", | ||
| detail: "AGENTS.md/CLAUDE.md emitter lands in VRU-469b", | ||
| }; | ||
| }, | ||
| }; |
@@ -72,9 +72,2 @@ /** | ||
| }, | ||
| async writeInstructions() { | ||
| return { | ||
| step: "instructions", | ||
| status: "deferred", | ||
| detail: "AGENTS.md/CLAUDE.md emitter lands in VRU-469b", | ||
| }; | ||
| }, | ||
| }; |
+34
-23
@@ -1,23 +0,4 @@ | ||
| /** | ||
| * Leaf helpers shared by `lib/harness.ts` and every per-harness adapter in this | ||
| * directory. This module imports ONLY node builtins — nothing adapter-related. | ||
| * | ||
| * Why a leaf: `harness.ts` registers the adapters (value imports), and an | ||
| * adapter needs `harnessHome`/`pathExists`/`isFile` plus the command runner. If | ||
| * those lived in `harness.ts`, an adapter's value import would close a runtime | ||
| * ESM cycle (`harness.ts → *.adapter.ts → harness.ts`) with a TDZ crash when an | ||
| * adapter module loads first (its own unit test). Putting them here breaks the | ||
| * back-edge: every side imports runtime helpers from `core`, and adapters pull | ||
| * the `HarnessAdapter`/`InitOptions`/`StepResult` *types* from `harness.ts` via | ||
| * `import type` (erased at runtime → no cycle). The graph stays a clean DAG. | ||
| * | ||
| * Consolidates the duplicate leaf helpers the parallel adapter branches each | ||
| * grew (`harnesses/core.ts` and `lib/harness-paths.ts`) into one module — the | ||
| * union of `harnessHome` + `pathExists` + `isFile` + the command-runner seam. | ||
| */ | ||
| import { execFile } from "node:child_process"; | ||
| import { promises as fs } from "node:fs"; | ||
| import { homedir } from "node:os"; | ||
| import { promisify } from "node:util"; | ||
| const execFileP = promisify(execFile); | ||
| import crossSpawn from "cross-spawn"; | ||
| /** Home dir adapters resolve harness configs under (override in tests). */ | ||
@@ -47,5 +28,35 @@ export function harnessHome() { | ||
| } | ||
| const defaultRunner = async (cmd, args) => { | ||
| await execFileP(cmd, args); | ||
| }; | ||
| /** | ||
| * Run a command with Windows-aware executable resolution. | ||
| * | ||
| * npm installs `npx`/`npm` as `.cmd` shims on Windows. Node's shell-free | ||
| * `execFile`/`spawn` does not resolve those through PATHEXT, while enabling a | ||
| * shell globally would make argument escaping both fragile and unsafe. The | ||
| * cross-spawn parser resolves the shim and escapes its arguments before it | ||
| * delegates to Node's child-process API. | ||
| */ | ||
| export async function runSpawnCommand(cmd, args, spawn = crossSpawn) { | ||
| await new Promise((resolve, reject) => { | ||
| const child = spawn(cmd, args, { stdio: ["ignore", "ignore", "pipe"] }); | ||
| const stderrStream = child.stderr; | ||
| let stderr = ""; | ||
| stderrStream.setEncoding("utf8"); | ||
| stderrStream.on("data", (chunk) => { | ||
| stderr += chunk; | ||
| }); | ||
| child.once("error", reject); | ||
| child.once("close", (code, signal) => { | ||
| if (code === 0) { | ||
| resolve(); | ||
| return; | ||
| } | ||
| const reason = code === null | ||
| ? `${cmd} exited due to signal ${String(signal)}` | ||
| : `${cmd} exited with code ${code}`; | ||
| const detail = stderr.trim(); | ||
| reject(new Error(detail ? `${reason}: ${detail}` : reason)); | ||
| }); | ||
| }); | ||
| } | ||
| const defaultRunner = runSpawnCommand; | ||
| let runner = defaultRunner; | ||
@@ -52,0 +63,0 @@ /** Run an external command through the current runner (override in tests). */ |
@@ -115,9 +115,2 @@ /** | ||
| }, | ||
| async writeInstructions() { | ||
| return { | ||
| step: "instructions", | ||
| status: "deferred", | ||
| detail: "AGENTS.md/instructions emitter lands in VRU-469b", | ||
| }; | ||
| }, | ||
| }; |
@@ -131,9 +131,2 @@ /** | ||
| }, | ||
| async writeInstructions() { | ||
| return { | ||
| step: "instructions", | ||
| status: "deferred", | ||
| detail: "AGENTS.md emitter lands in VRU-469b", | ||
| }; | ||
| }, | ||
| }; |
@@ -21,5 +21,4 @@ /** | ||
| * | ||
| * `linkSkills`/`writeInstructions` are `deferred`, mirroring the Claude Code | ||
| * reference adapter (OpenCode reads `.agents/skills` natively — placement is | ||
| * settled in VRU-470; the AGENTS.md emitter is VRU-469b). | ||
| * `linkSkills` is deferred (OpenCode reads `.agents/skills` natively — | ||
| * placement is settled in VRU-470). | ||
| * | ||
@@ -111,9 +110,2 @@ * Import discipline: runtime helpers come from `./core.js`; types come from | ||
| }, | ||
| async writeInstructions() { | ||
| return { | ||
| step: "instructions", | ||
| status: "deferred", | ||
| detail: "AGENTS.md emitter lands in VRU-469b", | ||
| }; | ||
| }, | ||
| }; |
@@ -13,4 +13,4 @@ /** | ||
| * | ||
| * Scope: MCP registration only. `linkSkills` / `writeInstructions` are deferred | ||
| * (VRU-470 / VRU-469b), matching the reference adapter's posture. | ||
| * Scope: MCP registration only. `linkSkills` is deferred (VRU-470), matching | ||
| * the reference adapter's posture. | ||
| * | ||
@@ -120,9 +120,2 @@ * Import discipline: runtime helpers come from `./core.js`; types come from | ||
| }, | ||
| async writeInstructions() { | ||
| return { | ||
| step: "instructions", | ||
| status: "deferred", | ||
| detail: "AGENTS.md emitter lands in VRU-469b", | ||
| }; | ||
| }, | ||
| }; |
@@ -6,3 +6,3 @@ /** | ||
| * installed, register the Vruum MCP server (Bearer-authed — no interactive | ||
| * OAuth), link the portable skill set, and write the agent instruction files. | ||
| * OAuth) and link the portable skill set. | ||
| * | ||
@@ -23,8 +23,8 @@ * `HarnessAdapter` is the extension point: adding a harness is adding one file | ||
| * | ||
| * Scope note: MCP registration is fully implemented. `linkSkills` / | ||
| * `writeInstructions` are interface-complete but their bodies are deferred — | ||
| * the published `@vruum/cli` ships no skills (those are the `@vruum/skills` | ||
| * package, and the portable SKILL.md placement is settled in VRU-470), and the | ||
| * AGENTS.md/CLAUDE.md emitter is VRU-469b. They return a `deferred` step so each | ||
| * adapter is a faithful, end-to-end-runnable shape today. | ||
| * MCP registration is implemented for every adapter. Skill installation is | ||
| * implemented where the harness has a stable skill directory; unsupported | ||
| * placements return a `deferred` skills step. Project instruction files are | ||
| * deliberately outside `vruum init`: silently editing a repository's | ||
| * AGENTS.md/CLAUDE.md would conflict with user-owned agent instructions and | ||
| * duplicate the installed Vruum skills. | ||
| */ | ||
@@ -31,0 +31,0 @@ import { claudeCode } from "../harnesses/claude.adapter.js"; |
+4
-2
| { | ||
| "name": "@vruum/cli", | ||
| "version": "0.2.3", | ||
| "version": "0.3.0", | ||
| "description": "Headless CLI for the Vruum revenue platform.", | ||
@@ -51,5 +51,7 @@ "type": "module", | ||
| "dependencies": { | ||
| "commander": "^12.1.0" | ||
| "commander": "^12.1.0", | ||
| "cross-spawn": "^7.0.6" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/cross-spawn": "^6.0.6", | ||
| "@types/node": "^20.14.0", | ||
@@ -56,0 +58,0 @@ "@yao-pkg/pkg": "^6.20.0", |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
1
-50%162936
-1%2
100%6
20%3704
-1.12%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added