registry  /  loomctl  /  0.17.0

loomctl@0.17.0

Orchestrate a fleet of real Claude Code agents on your Claude subscription (Pro/Max), not per-token API bills — durable sessions, multi-agent orchestration, all local-first on your own machine.

AI Security Review

scanned 2d ago · by lpm-firewall-ai

Review flagged AI-agent configuration or capability changes. This remains warn-only unless evidence shows foreign-agent hijack through preinstall/install/postinstall, hidden persistence, exfiltration, remote code execution, or other concrete malicious behavior.

Static reason
One or more suspicious static signals were detected.; previous stored version diff introduced dangerous source
Trigger
User runs `loom` commands, starts a Loom session, or invokes `loom service install`.
Impact
Can alter Claude project trust/settings and execute dependency lifecycle scripts in Loom-created worktrees; no unconsented npm-install attack was found.
Mechanism
Claude-agent orchestration with project configuration, local service registration, and worktree dependency provisioning.
Rationale
The package is not malicious by the stated boundary because it has no lifecycle trigger or concrete stealth attack chain. Its explicit runtime AI-agent control, project configuration writes, and worktree installs create a real dangerous-capability risk that should be warned.
Evidence
package.jsonbin/loom.mjsbin/service.mjsdist/pty/claude-config.jsdist/pty/claude-settings.jsdist/skills/inject.jsdist/git/worktrees.jsdist/orchestration/usage-status.js~/.claude.json~/.claude/.credentials.json<project>/.claude/skills<project>/.git/info/exclude~/.config/systemd/user/loom.service~/Library/LaunchAgents/com.loom.daemon.plist~/.loom/service/Loom.xml
Network endpoints2
127.0.0.1api.anthropic.com/api/oauth/usage

Decision evidence

public snapshot
AI called this Suspicious at 87.0% confidence as Dangerous Capability with low false-positive risk.
Evidence for warning
  • `dist/pty/claude-config.js` writes trust flags to `~/.claude.json` before spawned sessions.
  • `dist/skills/inject.js` copies Loom skills into project `.claude/skills` and updates `.git/info/exclude`.
  • `dist/orchestration/usage-status.js` reads `~/.claude/.credentials.json` and sends its OAuth token to Anthropic's usage endpoint.
  • `dist/git/worktrees.js` runs detected package-manager installs/builds inside Loom-created user worktrees.
  • `bin/service.mjs` creates per-user autostart artifacts only via explicit `loom service install`.
Evidence against
  • `package.json` contains no `preinstall`, `install`, `postinstall`, or `prepare` lifecycle hook.
  • `bin/loom.mjs` only contacts the local daemon at `127.0.0.1` for management.
  • Credential use is package-aligned usage polling to `https://api.anthropic.com/api/oauth/usage`, not a third-party endpoint.
  • No eval, remote-payload download/execution, or hidden install-time mutation was confirmed.
Behavioral surface
Source
ChildProcessCryptoDynamicRequireEnvironmentVarsFilesystemNetworkShellWebSocket
Supply chain
HighEntropyStringsMinifiedObfuscatedUrlStrings
ManifestNo manifest risk signals triggered.
scanned 128 file(s), 3.78 MB of source, external domains: 127.0.0.1, api.anthropic.com, api.telegram.org, github.com, react.dev, registry.npmjs.org, schemas.microsoft.com, www.w3.org

Source & flagged code

9 flagged · loading source
bin/loom.mjsView file
22import http from "node:http"; L23: import { spawn, spawnSync } from "node:child_process"; L24: import { CHANNELS, isValidChannel, installSpecFor, readChannel, writeChannel } from "./update-config.mjs";
High
Child Process

Package source references child process execution.

bin/loom.mjsView on unpkg · L22
449// no shell-injection surface. L450: const r = spawnSync("npm", ["i", "-g", spec], { stdio: "inherit", shell: true }); L451: if (r.error || r.status !== 0) {
High
Shell

Package source references shell execution.

bin/loom.mjsView on unpkg · L449
21import fs from "node:fs"; L22: import http from "node:http"; L23: import { spawn, spawnSync } from "node:child_process"; L24: import { CHANNELS, isValidChannel, installSpecFor, readChannel, writeChannel } from "./update-config.mjs"; ... L33: // to resize it after the fact). Never overrides an operator's own explicit setting. L34: if (!process.env.UV_THREADPOOL_SIZE) process.env.UV_THREADPOOL_SIZE = "16"; L35:
High
Same File Env Network Execution

A single source file combines environment access, network access, and code or shell execution; review context before blocking.

bin/loom.mjsView on unpkg · L21
270// readiness. L271: await import(pathToFileURL(daemonEntry).href); L272:
Medium
Dynamic Require

Package source references dynamic require/import behavior.

bin/loom.mjsView on unpkg · L270
dist/mcp/repo-read.jsView file
62for (let i = 0; i < s.length; i++) L63: if (s.charCodeAt(i) === 0) L64: return true;
Medium
Unsafe Vm Context

Package source executes code through a VM context API.

dist/mcp/repo-read.jsView on unpkg · L62
bin/service.mjsView file
10// - macOS : a launchd LaunchAgent plist (RunAtLoad + KeepAlive) under ~/Library/LaunchAgents, L11: // loaded via `launchctl`. L12: // - Windows: a Task Scheduler logon task (schtasks /create /xml) running at logon — no admin / no ... L22: import path from "node:path"; L23: import { spawnSync } from "node:child_process"; L24: ... L74: // launchd LaunchAgent plist. RunAtLoad starts it when the agent is loaded at login; KeepAlive restarts L75: // it if it exits (the keep-alive). Stdout/stderr go to a log under LOOM_HOME so a background boot stays L76: // debuggable. ... L118: return `<?xml version="1.0" encoding="UTF-16"?> L119: <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> L120: <RegistrationInfo>
Medium
Install Persistence

Source writes installer persistence such as shell profile or service configuration.

bin/service.mjsView on unpkg · L10
dist/git/worktrees.jsView file
92/** L93: * Run a BOUNDED, NON-INTERACTIVE `pnpm install --frozen-lockfile --prefer-offline` in `worktreePath`, L94: * killing the child if it exceeds `timeoutMs`. ASYNC (child_process.spawn, NOT spawnSync) on purpose: L95: * createWorktree is awaited on the worker-spawn hot path, and a synchronous spawnSync would freeze the
High
Runtime Package Install

Package source invokes a package manager install command at runtime.

dist/git/worktrees.jsView on unpkg · L92
assets/python/synthesize.pyView file
path = assets/python/synthesize.py kind = build_helper sizeBytes = 11311 magicHex = [redacted]
Medium
Ships Build Helper

Package ships non-JavaScript build or shell helper files.

assets/python/synthesize.pyView on unpkg
dist/pty/host.jsView file
matchType = previous_version_dangerous_delta matchedPackage = loomctl@0.15.0 matchedIdentity = npm:bG9vbWN0bA:0.15.0 similarity = 0.505 summary = stored previous version shares package body but lacks this dangerous source file
Critical
Previous Version Dangerous Delta

This package version adds a dangerous source file absent from the previous stored version; route for source-aware review.

dist/pty/host.jsView on unpkg

Findings

1 Critical4 High7 Medium4 Low
CriticalPrevious Version Dangerous Deltadist/pty/host.js
HighChild Processbin/loom.mjs
HighShellbin/loom.mjs
HighSame File Env Network Executionbin/loom.mjs
HighRuntime Package Installdist/git/worktrees.js
MediumDynamic Requirebin/loom.mjs
MediumUnsafe Vm Contextdist/mcp/repo-read.js
MediumNetwork
MediumEnvironment Vars
MediumInstall Persistencebin/service.mjs
MediumShips Build Helperassets/python/synthesize.py
MediumStructural Risk Force Deep Review
LowFilesystem
LowObfuscated
LowHigh Entropy Strings
LowUrl Strings