loop-engineering-harness 1.0.0 → 1.0.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.
- package/README.md +34 -2
- package/bin/cli.js +19 -2
- package/package.json +2 -1
- package/src/graphify.js +55 -0
- package/template/.claude/hooks/__pycache__/harness_lib.cpython-313.pyc +0 -0
- package/template/.claude/hooks/__pycache__/loop_lib.cpython-313.pyc +0 -0
- package/template/.claude/hooks/__pycache__/req_lib.cpython-313.pyc +0 -0
- package/template/.claude/hooks/__pycache__/version_lib.cpython-313.pyc +0 -0
package/README.md
CHANGED
|
@@ -17,17 +17,45 @@ commands — plus **graphify** (deterministic code knowledge graph) and **Langfu
|
|
|
17
17
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
20
|
+
Published on npm as [`loop-engineering-harness`](https://www.npmjs.com/package/loop-engineering-harness).
|
|
21
|
+
Zero runtime dependencies — just Node ≥ 16.
|
|
22
|
+
|
|
23
|
+
**Run without installing (recommended):**
|
|
24
|
+
|
|
20
25
|
```bash
|
|
21
26
|
npx loop-engineering-harness detect . # dry run — show exactly what will change
|
|
22
27
|
npx loop-engineering-harness init . # install (prints a plan, asks to confirm)
|
|
23
28
|
npx loop-engineering-harness uninstall . # remove everything; keeps your specs/
|
|
24
29
|
```
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
**Or install globally** to get the `loop-engineering-harness` / `agent-harness` commands on your PATH:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g loop-engineering-harness
|
|
35
|
+
|
|
36
|
+
loop-engineering-harness detect .
|
|
37
|
+
loop-engineering-harness init .
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> **No npm needed?** Install straight from GitHub:
|
|
27
41
|
> ```bash
|
|
28
42
|
> npx github:AakashRajendran0926/Loop-Engineering init .
|
|
29
43
|
> ```
|
|
30
44
|
|
|
45
|
+
### After install — build the knowledge base
|
|
46
|
+
|
|
47
|
+
`init` installs **graphify** (PyPI `graphifyy`, via uv/pipx/pip) because retrieval
|
|
48
|
+
is graph-first. Then, inside Claude Code, build the graph for your **existing**
|
|
49
|
+
codebase once:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
/graphify . # indexes this repo into graphify-out/ (the knowledge base)
|
|
53
|
+
/feature <description> # /feature also auto-builds the graph on first run if you skip the step above
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Skip the graphify install with `init . --no-graphify` (the `/graphify` skill
|
|
57
|
+
self-installs on first use either way).
|
|
58
|
+
|
|
31
59
|
Brownfield-safe by construction:
|
|
32
60
|
|
|
33
61
|
- **`CLAUDE.md`** gains a delimited `<!-- harness:begin -->…<!-- harness:end -->`
|
|
@@ -140,6 +168,10 @@ Escalations **park** (with a question for a human) instead of stalling the queue
|
|
|
140
168
|
a parked feature resumes from artifacts in a brand-new session — recover-from-
|
|
141
169
|
compaction and resume-from-parking are the same mechanism.
|
|
142
170
|
|
|
171
|
+
> Commands below assume a global install (`npm install -g loop-engineering-harness`).
|
|
172
|
+
> Without one, prefix each with `npx loop-engineering-harness …`. The
|
|
173
|
+
> `loop-engineering-harness` and `agent-harness` commands are interchangeable.
|
|
174
|
+
|
|
143
175
|
```bash
|
|
144
176
|
agent-harness queue add "add order cancellation with refunds"
|
|
145
177
|
agent-harness queue add "nightly CSV export" --auto-approve # headless self-approve
|
|
@@ -252,7 +284,7 @@ npm test # node tests/run.js — 84 checks across extract-deps determinis
|
|
|
252
284
|
|
|
253
285
|
- **Node ≥ 16** (installer + loop driver; zero runtime dependencies)
|
|
254
286
|
- **Python 3.8+** on PATH (`python`, `python3`, or `py`) for hooks/scripts
|
|
255
|
-
- **graphify** for graph-first retrieval — `/graphify` self-installs it on first run
|
|
287
|
+
- **graphify** for graph-first retrieval — `init` installs it (PyPI `graphifyy`); `/graphify` also self-installs it on first run
|
|
256
288
|
- **Claude Code** (for the `claude -p` headless loop) and, optionally, **Langfuse**
|
|
257
289
|
|
|
258
290
|
## Reversibility
|
package/bin/cli.js
CHANGED
|
@@ -10,17 +10,19 @@ const readline = require('readline');
|
|
|
10
10
|
const { detect, formatReport } = require('../src/detect');
|
|
11
11
|
const { install } = require('../src/install');
|
|
12
12
|
const { uninstall } = require('../src/uninstall');
|
|
13
|
+
const { ensureGraphify } = require('../src/graphify');
|
|
13
14
|
const loop = require('../src/loop');
|
|
14
15
|
const { computeMetrics, formatMetrics } = require('../src/metrics');
|
|
15
16
|
|
|
16
17
|
function parseArgs(argv) {
|
|
17
|
-
const out = { _: [], yes: false, python: null, dryRun: false, once: false, autoApprove: false };
|
|
18
|
+
const out = { _: [], yes: false, python: null, dryRun: false, once: false, autoApprove: false, graphify: true };
|
|
18
19
|
for (let i = 0; i < argv.length; i++) {
|
|
19
20
|
const a = argv[i];
|
|
20
21
|
if (a === '--yes' || a === '-y') out.yes = true;
|
|
21
22
|
else if (a === '--dry-run') out.dryRun = true;
|
|
22
23
|
else if (a === '--once') out.once = true;
|
|
23
24
|
else if (a === '--auto-approve') out.autoApprove = true;
|
|
25
|
+
else if (a === '--no-graphify') out.graphify = false;
|
|
24
26
|
else if (a === '--python') out.python = argv[++i];
|
|
25
27
|
else if (a.startsWith('--python=')) out.python = a.slice(9);
|
|
26
28
|
else out._.push(a);
|
|
@@ -57,6 +59,7 @@ async function main() {
|
|
|
57
59
|
' agent-harness metrics Per-feature burndown + tokens-per-task',
|
|
58
60
|
'',
|
|
59
61
|
'Flags: --yes | -y skip confirmation --python <cmd> force interpreter',
|
|
62
|
+
' --no-graphify skip installing graphify at init',
|
|
60
63
|
' --once one feature then stop --auto-approve headless self-approval',
|
|
61
64
|
' --dry-run plan the loop, run nothing',
|
|
62
65
|
].join('\n'));
|
|
@@ -140,7 +143,21 @@ async function main() {
|
|
|
140
143
|
console.log(`Installed. CLAUDE.md: ${manifest.claudeMd}; ` +
|
|
141
144
|
`${manifest.filesAdded.length} files added` +
|
|
142
145
|
(manifest.filesRenamed.length ? `, ${manifest.filesRenamed.length} collision(s) renamed to *.harness.*` : '') +
|
|
143
|
-
|
|
146
|
+
`.`);
|
|
147
|
+
|
|
148
|
+
// Retrieval is graph-first — install graphify now (best-effort; the
|
|
149
|
+
// /graphify skill self-installs on first use if this is skipped or fails).
|
|
150
|
+
if (args.graphify) {
|
|
151
|
+
console.log('\nInstalling graphify (graph-first retrieval)...');
|
|
152
|
+
const g = ensureGraphify({ python: args.python });
|
|
153
|
+
console.log(' ' + g.message);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log('\nNext steps:');
|
|
157
|
+
console.log(' 1. Build the knowledge base for this project: /graphify .');
|
|
158
|
+
console.log(' (existing/brownfield repos: this indexes your codebase into graphify-out/;');
|
|
159
|
+
console.log(' /feature also auto-builds it on first run if you skip this)');
|
|
160
|
+
console.log(' 2. Start a feature: /feature <description>');
|
|
144
161
|
return 0;
|
|
145
162
|
}
|
|
146
163
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loop-engineering-harness",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Loop Engineering — a reversible, observable multi-agent Claude Code harness for brownfield codebases. Policy lives in exit codes and artifacts on disk, not in prompts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"type": "commonjs",
|
|
15
15
|
"bin": {
|
|
16
|
+
"loop-engineering-harness": "bin/cli.js",
|
|
16
17
|
"agent-harness": "bin/cli.js",
|
|
17
18
|
"loop-engineering": "bin/cli.js"
|
|
18
19
|
},
|
package/src/graphify.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Best-effort installer for graphify (PyPI package `graphifyy`, imported as
|
|
3
|
+
// `graphify`). Retrieval in the harness is graph-first, so we install the CLI at
|
|
4
|
+
// `init` time rather than waiting for the first `/graphify` to self-install it.
|
|
5
|
+
// Non-fatal by design: a failure here never blocks the harness install — the
|
|
6
|
+
// `/graphify` skill still self-installs on first use.
|
|
7
|
+
|
|
8
|
+
const { spawnSync } = require('child_process');
|
|
9
|
+
const m = require('./merge');
|
|
10
|
+
|
|
11
|
+
const PKG = 'graphifyy';
|
|
12
|
+
|
|
13
|
+
function has(cmd) {
|
|
14
|
+
const probe = process.platform === 'win32' ? 'where' : 'which';
|
|
15
|
+
const r = spawnSync(probe, [cmd], { stdio: 'ignore' });
|
|
16
|
+
return r.status === 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function importable(python) {
|
|
20
|
+
const r = spawnSync(python, ['-c', 'import graphify'], { stdio: 'ignore' });
|
|
21
|
+
return r.status === 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Returns { status, via, message }.
|
|
25
|
+
// status: 'already' | 'installed' | 'failed' | 'skipped'
|
|
26
|
+
function ensureGraphify(opts) {
|
|
27
|
+
opts = opts || {};
|
|
28
|
+
const python = opts.python || m.detectPython();
|
|
29
|
+
const run = opts.run || ((cmd, cmdArgs) => spawnSync(cmd, cmdArgs, { stdio: 'inherit' }));
|
|
30
|
+
|
|
31
|
+
if (python && importable(python)) {
|
|
32
|
+
return { status: 'already', via: python, message: 'graphify already installed' };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Prefer isolated tool installs (uv → pipx), fall back to pip into the
|
|
36
|
+
// detected interpreter — mirrors what the /graphify skill does on first run.
|
|
37
|
+
const attempts = [];
|
|
38
|
+
if (has('uv')) attempts.push({ via: 'uv', cmd: 'uv', args: ['tool', 'install', '--upgrade', PKG] });
|
|
39
|
+
if (has('pipx')) attempts.push({ via: 'pipx', cmd: 'pipx', args: ['install', PKG] });
|
|
40
|
+
if (python) attempts.push({ via: 'pip', cmd: python, args: ['-m', 'pip', 'install', '--upgrade', PKG] });
|
|
41
|
+
|
|
42
|
+
if (attempts.length === 0) {
|
|
43
|
+
return { status: 'skipped', via: null, message: 'no uv/pipx/python found — skipped graphify install' };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const a of attempts) {
|
|
47
|
+
const r = run(a.cmd, a.args);
|
|
48
|
+
if (r && r.status === 0) {
|
|
49
|
+
return { status: 'installed', via: a.via, message: `graphify installed via ${a.via}` };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { status: 'failed', via: null, message: 'graphify install failed — /graphify will self-install on first use' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { ensureGraphify, PKG };
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|