compounding-loop 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +93 -3
- package/bin/loop.js +2 -0
- package/dist/cli/dashboard.js +52 -0
- package/dist/cli/dashboard.js.map +1 -0
- package/dist/cli/gh.js +29 -0
- package/dist/cli/gh.js.map +1 -0
- package/dist/cli/index.js +144 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.js +72 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/loops.js +72 -0
- package/dist/cli/loops.js.map +1 -0
- package/dist/cli/new.js +54 -0
- package/dist/cli/new.js.map +1 -0
- package/dist/cli/run.js +81 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/core/blocked.js +42 -0
- package/dist/core/blocked.js.map +1 -0
- package/dist/core/brief.js +91 -0
- package/dist/core/brief.js.map +1 -0
- package/dist/core/gh.js +2 -0
- package/dist/core/gh.js.map +1 -0
- package/dist/core/init.js +59 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/repo.js +81 -0
- package/dist/core/repo.js.map +1 -0
- package/dist/core/rounds.js +50 -0
- package/dist/core/rounds.js.map +1 -0
- package/dist/core/runPrompt.js +31 -0
- package/dist/core/runPrompt.js.map +1 -0
- package/dist/core/status.js +21 -0
- package/dist/core/status.js.map +1 -0
- package/dist/core/table.js +24 -0
- package/dist/core/table.js.map +1 -0
- package/dist/core/template.js +29 -0
- package/dist/core/template.js.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/server/data.js +36 -0
- package/dist/server/data.js.map +1 -0
- package/dist/server/events.js +105 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/five-sites.json +3071 -0
- package/dist/server/health.js +133 -0
- package/dist/server/health.js.map +1 -0
- package/dist/server/index.js +134 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/loops.js +142 -0
- package/dist/server/loops.js.map +1 -0
- package/dist/server/settings.js +25 -0
- package/dist/server/settings.js.map +1 -0
- package/dist/server/static-export.js +28 -0
- package/dist/server/static-export.js.map +1 -0
- package/dist/server/static.js +80 -0
- package/dist/server/static.js.map +1 -0
- package/dist/web/assets/index-BDdtnG33.js +49 -0
- package/dist/web/assets/index-D3-N8DN4.css +2 -0
- package/dist/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/web/favicon.svg +1 -0
- package/dist/web/index.html +14 -0
- package/dist/web/logo.svg +1 -0
- package/method/COMPOUNDING_LOOP.md +569 -0
- package/method/example/K-0001.md +16 -0
- package/method/knowledge-index.md +5 -0
- package/method/operating-card.md +40 -0
- package/method/retro.md +10 -0
- package/method/reviewer-prompt.md +13 -0
- package/method/stuck-protocol.md +12 -0
- package/method/task-template.md +41 -0
- package/package.json +71 -4
- package/plugin/.claude-plugin/plugin.json +7 -0
- package/plugin/commands/handoff.md +10 -0
- package/plugin/commands/loop-init.md +13 -0
- package/plugin/commands/retro.md +11 -0
- package/plugin/commands/stuck.md +10 -0
- package/runners/github-actions/loop.yml +51 -0
- package/runners/local/README.md +8 -0
- package/runners/local/crontab.txt +6 -0
- package/runners/local/task-scheduler.xml +32 -0
- package/runners/routine/README.md +19 -0
- package/runners/routine/prompt.md +13 -0
- package/templates/chrome-extension/CLAUDE.md +18 -0
- package/templates/chrome-extension/IDEA.md +22 -0
- package/templates/chrome-extension/extension/background.js +5 -0
- package/templates/chrome-extension/extension/manifest.json +9 -0
- package/templates/chrome-extension/extension/popup.css +3 -0
- package/templates/chrome-extension/extension/popup.html +15 -0
- package/templates/chrome-extension/extension/popup.js +16 -0
- package/templates/chrome-extension/gitignore +6 -0
- package/templates/chrome-extension/package.json +12 -0
- package/templates/chrome-extension/playwright.config.js +22 -0
- package/templates/chrome-extension/scripts/check.mjs +51 -0
- package/templates/chrome-extension/scripts/test.mjs +59 -0
- package/templates/chrome-extension/tests/popup.spec.js +37 -0
- package/templates/static-site/.ai/index.md +5 -0
- package/templates/static-site/.ai/log.md +3 -0
- package/templates/static-site/.ai/loop-prompt.md +10 -0
- package/templates/static-site/.ai/task.md +27 -0
- package/templates/static-site/.gitattributes +1 -0
- package/templates/static-site/CLAUDE.md +113 -0
- package/templates/static-site/IDEA.md +36 -0
- package/templates/static-site/gitignore +6 -0
- package/templates/static-site/netlify.toml +14 -0
- package/templates/static-site/package.json +14 -0
- package/templates/static-site/playwright.config.js +27 -0
- package/templates/static-site/scripts/check.mjs +188 -0
- package/templates/static-site/scripts/serve.mjs +44 -0
- package/templates/static-site/scripts/test.mjs +59 -0
- package/templates/static-site/site/index.html +23 -0
- package/templates/static-site/site/robots.txt +4 -0
- package/templates/static-site/site/sitemap.xml +4 -0
- package/templates/static-site/tests/pages.spec.js +21 -0
- package/templates/static-site/vercel.json +19 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Minntooos
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,4 +1,94 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Compounding Loop
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
**Write the brief, check back tomorrow.** Unattended, verified Claude Code build loops with a local dashboard.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx compounding-loop init
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
Most agent tools show a demo. This one ships with proof: the method in this repo built five live sites, 215 pages in all, with nobody watching, and made **verification** the product. A round only counts when the repo's own tests pass and a reviewer that did not write the code has checked the diff.
|
|
12
|
+
|
|
13
|
+
## Proof
|
|
14
|
+
|
|
15
|
+
Five calculator sites, each built by scheduled Claude Code runs working from a written brief. Numbers come from the repo data in [`demo/five-sites.json`](demo/five-sites.json).
|
|
16
|
+
|
|
17
|
+
| Site | Pages | Tests passing | Commits | Rounds | Elapsed* |
|
|
18
|
+
|---|---:|---:|---:|---:|---:|
|
|
19
|
+
| [powerstationruntime.netlify.app](https://powerstationruntime.netlify.app) | 55 | 278 | 112 | 5 / 5 | 22 h |
|
|
20
|
+
| [clearpoolmath.netlify.app](https://clearpoolmath.netlify.app) | 40 | 543 | 83 | 5 / 5 | 22 h |
|
|
21
|
+
| [yarnsums.netlify.app](https://yarnsums.netlify.app) | 40 | 419 | 109 | 5 / 5 | 19 h |
|
|
22
|
+
| [quiltcalculators.netlify.app](https://quiltcalculators.netlify.app) | 43 | 452 | 77 | 5 / 5 | 18 h |
|
|
23
|
+
| [mathtank.netlify.app](https://mathtank.netlify.app) | 37 | 433 | 83 | 5 / 5 | 18 h |
|
|
24
|
+
| **Total** | **215** | **2,125** | **464** | | |
|
|
25
|
+
|
|
26
|
+
\*Wall-clock time from the first to the last commit, rounded. It is not the time a human spent: that was writing five briefs.
|
|
27
|
+
|
|
28
|
+
## How it works
|
|
29
|
+
|
|
30
|
+
```mermaid
|
|
31
|
+
flowchart LR
|
|
32
|
+
B[Brief] --> T[task.md contract]
|
|
33
|
+
T --> R[Scheduled run]
|
|
34
|
+
R --> U[One unit of work]
|
|
35
|
+
U --> C{npm test}
|
|
36
|
+
C -- red --> X[Log under Tried, discard]
|
|
37
|
+
C -- green --> V[Reviewer subagent]
|
|
38
|
+
V --> P[Push]
|
|
39
|
+
P --> K[Retro: knowledge]
|
|
40
|
+
K --> R
|
|
41
|
+
R -. stuck .-> Q[BLOCKED.md: your inbox]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
1. **Brief and contract.** You write what "done" means as commands that can be checked.
|
|
45
|
+
2. **Scheduled runs.** A routine, a GitHub Action or `loop run` wakes up, reads its task file and does one unit at a time.
|
|
46
|
+
3. **Checks decide.** The agent never declares itself done; `npm test` does.
|
|
47
|
+
4. **Reviewer gate.** A fresh subagent attacks the diff before it is pushed.
|
|
48
|
+
5. **Compounding.** Every run writes what it learned into a small indexed knowledge base the next run reads first.
|
|
49
|
+
6. **Escalation.** When it is stuck it writes `BLOCKED.md`. You answer from the dashboard inbox or `loop answer`.
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
npx compounding-loop new static-site my-site # a new loop repo from a template
|
|
55
|
+
cd my-site
|
|
56
|
+
npx compounding-loop run # one round now
|
|
57
|
+
npx compounding-loop dashboard # the local dashboard
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Already have a repo? `npx compounding-loop init` adds the method to it without overwriting anything. Details: [Getting started](docs/getting-started.md).
|
|
61
|
+
|
|
62
|
+
## Just loop the agent?
|
|
63
|
+
|
|
64
|
+
An honest comparison with `while true; do claude -p "keep going"; done`:
|
|
65
|
+
|
|
66
|
+
| | Plain loop | Compounding Loop |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| Knows when it is done | When the model says so | When your checks pass |
|
|
69
|
+
| Reviews its own work | Same context, same blind spots | A fresh reviewer must find no blockers |
|
|
70
|
+
| Survives context limits | Starts cold every time | Writes a handoff the next run resumes from |
|
|
71
|
+
| Learns from mistakes | No | Notes and a retro, indexed |
|
|
72
|
+
| Overlapping runs | Collide on the same files | Session lock |
|
|
73
|
+
| Stuck for hours | Burns budget | Run budget, then `BLOCKED.md` |
|
|
74
|
+
| You can see what happened | `git log` | Fleet dashboard and inbox |
|
|
75
|
+
|
|
76
|
+
If your task is one afternoon of work and you are at the keyboard, a plain loop is fine. This pays off for work that runs for days.
|
|
77
|
+
|
|
78
|
+
## Docs
|
|
79
|
+
|
|
80
|
+
[Getting started](docs/getting-started.md) · [Concepts](docs/concepts.md) · [Runners](docs/runners.md) · [Dashboard](docs/dashboard.md) · [Templates](docs/templates.md) · [FAQ](docs/faq.md) · [Troubleshooting](docs/troubleshooting.md) · [Contributing](CONTRIBUTING.md)
|
|
81
|
+
|
|
82
|
+
## FAQ
|
|
83
|
+
|
|
84
|
+
**What does it cost?** The tool is free and MIT licensed. Your Claude Code plan pays for the runs; the budget (`Run: N / LIMIT`) caps how many happen.
|
|
85
|
+
|
|
86
|
+
**Is it safe?** The dashboard binds to loopback, there is no telemetry and no call home, and tokens come from your environment or `gh` and are never stored. The loop works on branches or `main` as you configure; read [SECURITY.md](SECURITY.md).
|
|
87
|
+
|
|
88
|
+
**What won't it do?** It will not publish, spend money, create accounts or change repo settings on its own, and it stops and asks (`BLOCKED.md`) rather than guess when the brief contradicts reality.
|
|
89
|
+
|
|
90
|
+
**Does it need a GitHub login?** Only to create a private repo with `loop new`; `--dry-run` and every test work offline.
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT. See [LICENSE](LICENSE).
|
package/bin/loop.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export const DEFAULT_PORT = 4321;
|
|
5
|
+
/** Loads the server lazily so every other command works before (or without) the dashboard being built. */
|
|
6
|
+
async function loadStartServer() {
|
|
7
|
+
const specifier = '../server/index.js';
|
|
8
|
+
try {
|
|
9
|
+
const mod = (await import(specifier));
|
|
10
|
+
if (typeof mod.startServer === 'function')
|
|
11
|
+
return mod.startServer;
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
// Only "the server file itself is missing" is expected; a missing dependency inside it must surface as is.
|
|
15
|
+
const serverMissing = error instanceof Error && /Cannot find module|ERR_MODULE_NOT_FOUND/.test(error.message) && /server[\\/]index/.test(error.message);
|
|
16
|
+
if (!serverMissing)
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
throw new Error('The dashboard server is not available in this build (src/server/index.ts must export startServer).');
|
|
20
|
+
}
|
|
21
|
+
/** Command and arguments that open a URL in the default browser; always an argument array, never a shell string. */
|
|
22
|
+
export function browserCommand(url, platform = process.platform) {
|
|
23
|
+
if (platform === 'darwin')
|
|
24
|
+
return ['open', [url]];
|
|
25
|
+
if (platform === 'win32')
|
|
26
|
+
return ['rundll32', ['url.dll,FileProtocolHandler', url]];
|
|
27
|
+
return ['xdg-open', [url]];
|
|
28
|
+
}
|
|
29
|
+
function openBrowser(url) {
|
|
30
|
+
const [command, args] = browserCommand(url);
|
|
31
|
+
// Best effort: a missing opener must not stop the server, the URL is printed anyway.
|
|
32
|
+
execFile(command, args, () => undefined).on('error', () => undefined);
|
|
33
|
+
}
|
|
34
|
+
/** Starts the local dashboard server and (unless told not to) opens the browser. */
|
|
35
|
+
export async function runDashboard(options, deps = {}) {
|
|
36
|
+
if (!Number.isInteger(options.port) || options.port < 0 || options.port > 65535) {
|
|
37
|
+
throw new Error(`"${options.port}" is not a valid port (0-65535).`);
|
|
38
|
+
}
|
|
39
|
+
const start = deps.start ?? (await loadStartServer());
|
|
40
|
+
const server = await start({ port: options.port, demo: options.demo, projectsDir: path.resolve(options.projectsDir) }).catch((error) => {
|
|
41
|
+
if (error.code === 'EADDRINUSE')
|
|
42
|
+
throw new Error(`Port ${options.port} is already in use. Pick another with --port.`);
|
|
43
|
+
throw error;
|
|
44
|
+
});
|
|
45
|
+
(deps.log ?? console.log)(`Dashboard${options.demo ? ' (demo)' : ''}: ${server.url}`);
|
|
46
|
+
if (options.open)
|
|
47
|
+
(deps.open ?? openBrowser)(server.url);
|
|
48
|
+
return server;
|
|
49
|
+
}
|
|
50
|
+
/** True once the server lane has shipped `startServer`; lets tests run the real thing only when it exists. */
|
|
51
|
+
export const serverShipped = (root) => existsSync(path.join(root, 'src', 'server', 'index.ts'));
|
|
52
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/cli/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAS7B,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC;AAEjC,0GAA0G;AAC1G,KAAK,UAAU,eAAe;IAC5B,MAAM,SAAS,GAAG,oBAAoB,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAkC,CAAC;QACvE,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,UAAU;YAAE,OAAO,GAAG,CAAC,WAAW,CAAC;IACpE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,2GAA2G;QAC3G,MAAM,aAAa,GAAG,KAAK,YAAY,KAAK,IAAI,yCAAyC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxJ,IAAI,CAAC,aAAa;YAAE,MAAM,KAAK,CAAC;IAClC,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,oGAAoG,CAAC,CAAC;AACxH,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,QAAQ,GAAoB,OAAO,CAAC,QAAQ;IACtF,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAClD,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,CAAC,UAAU,EAAE,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC,CAAC;IACpF,OAAO,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAC5C,qFAAqF;IACrF,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AACxE,CAAC;AAeD,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAyB,EAAE,IAAI,GAAkB,EAAE;IACpF,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,GAAG,KAAK,EAAE,CAAC;QAChF,MAAM,IAAI,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,kCAAkC,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,eAAe,EAAE,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QAC9I,IAAK,KAA2B,CAAC,IAAI,KAAK,YAAY;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,OAAO,CAAC,IAAI,+CAA+C,CAAC,CAAC;QAC7I,MAAM,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IACH,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;IACtF,IAAI,OAAO,CAAC,IAAI;QAAE,CAAC,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,8GAA8G;AAC9G,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC"}
|
package/dist/cli/gh.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
/** Runs a command and rethrows with its stderr, not the raw command line execFile puts in the message. */
|
|
5
|
+
async function run(command, args, cwd) {
|
|
6
|
+
try {
|
|
7
|
+
return (await execFileAsync(command, args, { cwd })).stdout;
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
const stderr = error.stderr;
|
|
11
|
+
const detail = typeof stderr === 'string' && stderr.trim() ? stderr.trim() : error instanceof Error ? error.message : String(error);
|
|
12
|
+
throw new Error(`\`${command} ${args[0] ?? ''}\` failed: ${detail}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Uses the user's own `gh` login; no token is read, stored or logged here. */
|
|
16
|
+
export const realGh = {
|
|
17
|
+
async createRepoFromFolder(folder, name, options) {
|
|
18
|
+
await run('git', ['init', '-q'], folder);
|
|
19
|
+
await run('git', ['checkout', '-q', '-B', 'main'], folder);
|
|
20
|
+
await run('git', ['add', '-A'], folder);
|
|
21
|
+
// Fresh machines often have no git identity; fall back only when none is configured.
|
|
22
|
+
const hasIdentity = await run('git', ['config', 'user.email'], folder).then((out) => out.trim() !== '', () => false);
|
|
23
|
+
const identity = hasIdentity ? [] : ['-c', 'user.name=Compounding Loop', '-c', 'user.email=loop@users.noreply.github.com'];
|
|
24
|
+
await run('git', [...identity, 'commit', '-qm', 'Start from the Compounding Loop template'], folder);
|
|
25
|
+
await run('gh', ['repo', 'create', name, options.private ? '--private' : '--public', '--source', '.', '--push'], folder);
|
|
26
|
+
return (await run('gh', ['repo', 'view', '--json', 'url', '--jq', '.url'], folder)).trim();
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=gh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gh.js","sourceRoot":"","sources":["../../src/cli/gh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,0GAA0G;AAC1G,KAAK,UAAU,GAAG,CAAC,OAAe,EAAE,IAAc,EAAE,GAAW;IAC7D,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAC9D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAI,KAA8B,CAAC,MAAM,CAAC;QACtD,MAAM,MAAM,GAAG,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpI,MAAM,IAAI,KAAK,CAAC,KAAK,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,cAAc,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,MAAM,MAAM,GAAO;IACxB,KAAK,CAAC,oBAAoB,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO;QAC9C,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QAC3D,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACxC,qFAAqF;QACrF,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;QACrH,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,4BAA4B,EAAE,IAAI,EAAE,0CAA0C,CAAC,CAAC;QAC3H,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,0CAA0C,CAAC,EAAE,MAAM,CAAC,CAAC;QACrG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;QACzH,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7F,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { DEFAULT_PORT, runDashboard } from './dashboard.js';
|
|
5
|
+
import { realGh } from './gh.js';
|
|
6
|
+
import { runInit } from './init.js';
|
|
7
|
+
import { runAnswer, runNextRound, runStatus } from './loops.js';
|
|
8
|
+
import { runNew } from './new.js';
|
|
9
|
+
import { runRound } from './run.js';
|
|
10
|
+
const pkg = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
|
|
11
|
+
// Commands are added by the core lane: init, new, run, status, next-round, answer, dashboard.
|
|
12
|
+
export const program = new Command('loop')
|
|
13
|
+
.description('Unattended, verified Claude Code build loops. Write the brief, check back tomorrow.')
|
|
14
|
+
.version(pkg.version);
|
|
15
|
+
program
|
|
16
|
+
.command('init')
|
|
17
|
+
.description('Add the Compounding Loop method to a repo (idempotent; never overwrites without --force)')
|
|
18
|
+
.argument('[dir]', 'target repo folder', '.')
|
|
19
|
+
.option('--force', 'skip the brief check and overwrite existing kit files')
|
|
20
|
+
.option('--dry-run', 'show what would be written without writing')
|
|
21
|
+
.action(async (dir, options) => {
|
|
22
|
+
const result = await runInit(path.resolve(dir), { force: Boolean(options.force), dryRun: Boolean(options.dryRun) });
|
|
23
|
+
if (result.refused) {
|
|
24
|
+
console.error(result.refused);
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
for (const action of result.actions)
|
|
29
|
+
console.log(`${action.kind.padEnd(9)} ${action.dest}${action.reason ? ` (${action.reason})` : ''}`);
|
|
30
|
+
if (result.missing.includes('method/operating-card.md'))
|
|
31
|
+
console.error('warning: the Operating Card was NOT installed, so CLAUDE.md is unchanged.');
|
|
32
|
+
for (const rel of result.missing)
|
|
33
|
+
console.error(`warning: ${rel} is not in this package yet; used a built-in fallback or skipped it`);
|
|
34
|
+
});
|
|
35
|
+
program
|
|
36
|
+
.command('new')
|
|
37
|
+
.description('Create a new loop repo from a template (private GitHub repo unless --dry-run)')
|
|
38
|
+
.argument('<template>', 'template name, e.g. static-site')
|
|
39
|
+
.argument('[name]', 'folder and repo name (defaults to the template name)')
|
|
40
|
+
.option('--dry-run', 'write the folder locally and never touch GitHub')
|
|
41
|
+
.option('--public', 'create the GitHub repo as public (default: private)')
|
|
42
|
+
.action(async (template, name, options) => {
|
|
43
|
+
try {
|
|
44
|
+
const result = await runNew(template, name ?? template, {
|
|
45
|
+
parentDir: process.cwd(), dryRun: Boolean(options.dryRun), publicRepo: Boolean(options.public), gh: realGh,
|
|
46
|
+
});
|
|
47
|
+
console.log(`Created ${result.dir}${result.repoUrl ? `\nGitHub: ${result.repoUrl}` : ''}`);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
const fail = (error) => {
|
|
55
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
};
|
|
58
|
+
program
|
|
59
|
+
.command('status')
|
|
60
|
+
.description('Fleet table for one or more loop folders')
|
|
61
|
+
.argument('[dirs...]', 'loop folders', ['.'])
|
|
62
|
+
.action(async (dirs) => {
|
|
63
|
+
try {
|
|
64
|
+
console.log(await runStatus(dirs.map((d) => path.resolve(d))));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
fail(error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
program
|
|
71
|
+
.command('next-round')
|
|
72
|
+
.description('After DONE.md: archive it as .ai/done-vN.md and write the next round\'s task.md from its "next 10" list')
|
|
73
|
+
.argument('[dir]', 'loop folder', '.')
|
|
74
|
+
.option('--dry-run', 'report what would happen without writing')
|
|
75
|
+
.action(async (dir, options) => {
|
|
76
|
+
try {
|
|
77
|
+
const result = await runNextRound(path.resolve(dir), { dryRun: Boolean(options.dryRun) });
|
|
78
|
+
console.log(`${options.dryRun ? 'Would archive' : 'Archived'} DONE.md as .ai/${result.archivedAs}; next round has ${result.units} units.`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
fail(error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
program
|
|
85
|
+
.command('answer')
|
|
86
|
+
.description('Answer a BLOCKED.md: record the answer, remove the file, commit')
|
|
87
|
+
.argument('[answer]', 'your answer; omit with --accept to take the agent\'s best guess')
|
|
88
|
+
.option('--accept', 'accept the best guess in BLOCKED.md')
|
|
89
|
+
.option('--dir <dir>', 'loop folder', '.')
|
|
90
|
+
.option('--dry-run', 'show the question and answer without writing')
|
|
91
|
+
.action(async (answer, options) => {
|
|
92
|
+
try {
|
|
93
|
+
if (!options.accept && !answer)
|
|
94
|
+
throw new Error('Pass an answer, or --accept to take the best guess.');
|
|
95
|
+
const result = await runAnswer(path.resolve(options.dir), options.accept ? 'accept' : (answer ?? ''), { dryRun: Boolean(options.dryRun) });
|
|
96
|
+
console.log(`Question: ${result.question}\nAnswer: ${result.answer}${options.dryRun ? '\n(dry run: nothing written)' : '\nCommitted. Push to restart the loop.'}`);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
fail(error);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
program
|
|
103
|
+
.command('run')
|
|
104
|
+
.description('Run one build round now with Claude Code, using the same prompt the scheduler uses')
|
|
105
|
+
.argument('[dir]', 'loop folder', '.')
|
|
106
|
+
.option('--dry-run', 'print the prompt and stop; never starts claude')
|
|
107
|
+
.option('--skip-permissions', 'pass --dangerously-skip-permissions to claude (you accept the risk)')
|
|
108
|
+
.option('--model <model>', 'model for this round')
|
|
109
|
+
.action(async (dir, options) => {
|
|
110
|
+
try {
|
|
111
|
+
const result = await runRound(path.resolve(dir), {
|
|
112
|
+
dryRun: Boolean(options.dryRun), skipPermissions: Boolean(options.skipPermissions), ...(options.model && { model: options.model }),
|
|
113
|
+
});
|
|
114
|
+
if (result.skipped)
|
|
115
|
+
console.log(result.skipped);
|
|
116
|
+
else if (options.dryRun)
|
|
117
|
+
console.log(`Prompt from ${result.source}:\n\n${result.prompt}`);
|
|
118
|
+
if (result.exitCode)
|
|
119
|
+
process.exitCode = result.exitCode;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
fail(error);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
program
|
|
126
|
+
.command('dashboard')
|
|
127
|
+
.description('Start the local dashboard and open it in your browser')
|
|
128
|
+
.option('--demo', 'show the built-in snapshot of five real loops')
|
|
129
|
+
.option('--port <port>', 'port to listen on', String(DEFAULT_PORT))
|
|
130
|
+
.option('--no-open', 'do not open the browser')
|
|
131
|
+
.option('--projects <dir>', 'folder that holds your loops', '.')
|
|
132
|
+
.action(async (options) => {
|
|
133
|
+
try {
|
|
134
|
+
const server = await runDashboard({ demo: Boolean(options.demo), port: Number(options.port), open: options.open, projectsDir: options.projects });
|
|
135
|
+
for (const signal of ['SIGINT', 'SIGTERM'])
|
|
136
|
+
process.once(signal, () => void Promise.resolve(server.close()).finally(() => process.exit(0)));
|
|
137
|
+
// The server keeps the process alive; Ctrl+C stops it.
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
fail(error);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
await program.parseAsync();
|
|
144
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { briefBlocker, planInit } from '../core/init.js';
|
|
5
|
+
/** Root of the installed package (or this checkout): two levels above `dist/cli` or `src/cli`. */
|
|
6
|
+
export const packageRoot = fileURLToPath(new URL('../../', import.meta.url));
|
|
7
|
+
const FALLBACK_INDEX = '# Knowledge index\n\nOne line per entry in `.ai/knowledge/`: `K-0001 · trigger (error text, file path or topic) · one-line lesson`.\n\n<!-- empty: the loop adds entries as it learns -->\n';
|
|
8
|
+
const FALLBACK_LOG = '# Log\n\nOne line per finished task: `date · task · runs used · repeat errors · result`.\n';
|
|
9
|
+
const FALLBACK_TASK = '# TASK: Build v1 of IDEA.md\n\nRun: 0 / 30\n\n## Contract\n- Goal:\n- Done when:\n- Constraints:\n- Out of scope:\n\n## Handoff\n(each run ends by writing: where it stopped, the exact next step, anything half-done)\n';
|
|
10
|
+
async function readIfExists(file) {
|
|
11
|
+
try {
|
|
12
|
+
return await readFile(file, 'utf8');
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Loads the method files `loop init` installs from the package's `method/` folder. */
|
|
19
|
+
export async function loadKit(root = packageRoot) {
|
|
20
|
+
const missing = [];
|
|
21
|
+
const read = async (rel, fallback) => {
|
|
22
|
+
const text = await readIfExists(path.join(root, rel));
|
|
23
|
+
if (text === undefined)
|
|
24
|
+
missing.push(rel);
|
|
25
|
+
return text ?? fallback;
|
|
26
|
+
};
|
|
27
|
+
const card = await read('method/operating-card.md');
|
|
28
|
+
const method = await read('method/COMPOUNDING_LOOP.md');
|
|
29
|
+
const task = await read('method/task-template.md', FALLBACK_TASK);
|
|
30
|
+
const index = await read('method/knowledge-index.md', FALLBACK_INDEX);
|
|
31
|
+
const files = [
|
|
32
|
+
{ dest: '.ai/index.md', content: index ?? FALLBACK_INDEX, keep: true },
|
|
33
|
+
{ dest: '.ai/log.md', content: FALLBACK_LOG, keep: true },
|
|
34
|
+
{ dest: '.ai/task.md', content: task ?? FALLBACK_TASK, keep: true },
|
|
35
|
+
{ dest: '.ai/knowledge/.gitkeep', content: '', keep: true },
|
|
36
|
+
{ dest: '.ai/plans/.gitkeep', content: '', keep: true },
|
|
37
|
+
];
|
|
38
|
+
if (method !== undefined)
|
|
39
|
+
files.push({ dest: '.ai/method.md', content: method });
|
|
40
|
+
return { files, card, missing };
|
|
41
|
+
}
|
|
42
|
+
export async function runInit(target, options, kit) {
|
|
43
|
+
const loaded = kit ?? (await loadKit());
|
|
44
|
+
const brief = await readIfExists(path.join(target, 'IDEA.md'));
|
|
45
|
+
const blocker = options.force ? undefined : briefBlocker(brief);
|
|
46
|
+
if (blocker)
|
|
47
|
+
return { actions: [], missing: loaded.missing, refused: blocker };
|
|
48
|
+
const existing = new Map();
|
|
49
|
+
for (const dest of [...loaded.files.map((f) => f.dest), 'CLAUDE.md', 'AGENTS.md']) {
|
|
50
|
+
const text = await readIfExists(path.join(target, ...dest.split('/')));
|
|
51
|
+
if (text !== undefined)
|
|
52
|
+
existing.set(dest, text);
|
|
53
|
+
}
|
|
54
|
+
let actions;
|
|
55
|
+
try {
|
|
56
|
+
actions = planInit({ kit: loaded.files, card: loaded.card, existing, force: options.force });
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return { actions: [], missing: loaded.missing, refused: error instanceof Error ? error.message : String(error) };
|
|
60
|
+
}
|
|
61
|
+
if (!options.dryRun) {
|
|
62
|
+
for (const action of actions) {
|
|
63
|
+
if (action.content === undefined)
|
|
64
|
+
continue;
|
|
65
|
+
const file = path.join(target, ...action.dest.split('/'));
|
|
66
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
67
|
+
await writeFile(file, action.content);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { actions, missing: loaded.missing };
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { readdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { addAnswerToTask, BUDGET_EXTENSION, extendRunBudget, formatAnswer, isBudgetBlock, parseBlocked } from '../core/blocked.js';
|
|
6
|
+
import { readLoopFacts, readRunCounter } from '../core/repo.js';
|
|
7
|
+
import { buildNextRoundTask, extractNextItems, nextDoneFileName } from '../core/rounds.js';
|
|
8
|
+
import { formatFleet } from '../core/table.js';
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
const git = (cwd, ...args) => execFileAsync('git', args, { cwd });
|
|
11
|
+
/** `loop status`: one row per folder. */
|
|
12
|
+
export async function runStatus(dirs, now = new Date()) {
|
|
13
|
+
const rows = [];
|
|
14
|
+
for (const dir of dirs) {
|
|
15
|
+
const run = await readRunCounter(dir);
|
|
16
|
+
rows.push({ name: path.basename(path.resolve(dir)), facts: await readLoopFacts(dir), ...(run && { run }) });
|
|
17
|
+
}
|
|
18
|
+
return formatFleet(rows, now);
|
|
19
|
+
}
|
|
20
|
+
/** `loop next-round`: archive DONE.md as `.ai/done-vN.md` and write the next task.md from its "next 10" list. */
|
|
21
|
+
export async function runNextRound(dir, options) {
|
|
22
|
+
const donePath = path.join(dir, 'DONE.md');
|
|
23
|
+
const doneText = await readFile(donePath, 'utf8').catch(() => undefined);
|
|
24
|
+
if (doneText === undefined)
|
|
25
|
+
throw new Error('No DONE.md here: this loop has not finished a round.');
|
|
26
|
+
const items = extractNextItems(doneText);
|
|
27
|
+
if (items.length === 0)
|
|
28
|
+
throw new Error('DONE.md has no "next 10" list to turn into the next round.');
|
|
29
|
+
const aiDir = path.join(dir, '.ai');
|
|
30
|
+
const archivedAs = nextDoneFileName(await readdir(aiDir).catch(() => []));
|
|
31
|
+
const round = Number(/\d+/.exec(archivedAs)?.[0] ?? '1') + 1;
|
|
32
|
+
const previous = await readRunCounter(dir);
|
|
33
|
+
if (!options.dryRun) {
|
|
34
|
+
await rename(donePath, path.join(aiDir, archivedAs));
|
|
35
|
+
await writeFile(path.join(aiDir, 'task.md'), buildNextRoundTask(items, round, previous?.limit));
|
|
36
|
+
await rm(path.join(aiDir, 'session.lock'), { force: true });
|
|
37
|
+
}
|
|
38
|
+
return { archivedAs, units: items.length };
|
|
39
|
+
}
|
|
40
|
+
/** `loop answer`: record the owner's answer in task.md, remove BLOCKED.md, and commit so the next run restarts. */
|
|
41
|
+
export async function runAnswer(dir, answer, options) {
|
|
42
|
+
const blockedPath = path.join(dir, 'BLOCKED.md');
|
|
43
|
+
const text = await readFile(blockedPath, 'utf8').catch(() => undefined);
|
|
44
|
+
if (text === undefined)
|
|
45
|
+
throw new Error('No BLOCKED.md here: nothing to answer.');
|
|
46
|
+
const note = parseBlocked(text);
|
|
47
|
+
const budget = isBudgetBlock(text);
|
|
48
|
+
const acceptedGuess = note.bestGuess || (budget ? `Continue: ${BUDGET_EXTENSION} more runs.` : '');
|
|
49
|
+
const finalAnswer = answer === 'accept' ? acceptedGuess : answer;
|
|
50
|
+
if (!finalAnswer.trim())
|
|
51
|
+
throw new Error('BLOCKED.md has no best guess to accept; pass an answer instead.');
|
|
52
|
+
if (!options.dryRun) {
|
|
53
|
+
// Fail before changing any file when this is not a git repo.
|
|
54
|
+
await git(dir, 'rev-parse', '--git-dir').catch(() => {
|
|
55
|
+
throw new Error('This folder is not a git repository, so the answer cannot be committed.');
|
|
56
|
+
});
|
|
57
|
+
const taskPath = path.join(dir, '.ai', 'task.md');
|
|
58
|
+
const task = await readFile(taskPath, 'utf8').catch(() => '');
|
|
59
|
+
const date = (options.now ?? new Date()).toISOString().slice(0, 10);
|
|
60
|
+
const withAnswer = addAnswerToTask(task, formatAnswer(note, finalAnswer, date));
|
|
61
|
+
// Without a higher limit the next run would hit the budget and block again at once.
|
|
62
|
+
await writeFile(taskPath, budget ? extendRunBudget(withAnswer) : withAnswer);
|
|
63
|
+
await rm(blockedPath);
|
|
64
|
+
// Only our two files: the user's other staged or unstaged work stays out of this commit.
|
|
65
|
+
const blockedTracked = (await git(dir, 'ls-files', '--', 'BLOCKED.md')).stdout.trim() !== '';
|
|
66
|
+
const paths = ['.ai/task.md', ...(blockedTracked ? ['BLOCKED.md'] : [])];
|
|
67
|
+
await git(dir, 'add', '-A', '--', ...paths);
|
|
68
|
+
await git(dir, '-c', 'user.name=Compounding Loop', '-c', 'user.email=loop@users.noreply.github.com', 'commit', '-qm', 'Answer BLOCKED.md and restart the loop', '--', ...paths);
|
|
69
|
+
}
|
|
70
|
+
return { question: note.question, bestGuess: note.bestGuess, answer: finalAnswer };
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=loops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loops.js","sourceRoot":"","sources":["../../src/cli/loops.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnI,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,WAAW,EAAiB,MAAM,kBAAkB,CAAC;AAE9D,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,GAAG,IAAc,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AAEpF,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc,EAAE,GAAG,GAAS,IAAI,IAAI,EAAE;IACpE,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9G,CAAC;IACD,OAAO,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAChC,CAAC;AAOD,iHAAiH;AACjH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,OAA4B;IAC1E,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACzE,IAAI,QAAQ,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IACpG,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IACtG,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1E,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;QACrD,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QAChG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AAC7C,CAAC;AASD,mHAAmH;AACnH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,MAAyB,EACzB,OAAwC;IAExC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACxE,IAAI,IAAI,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAClF,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,gBAAgB,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACnG,MAAM,WAAW,GAAG,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IACjE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IAC5G,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,6DAA6D;QAC7D,MAAM,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAClD,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;QAC7F,CAAC,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9D,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACpE,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC;QAChF,oFAAoF;QACpF,MAAM,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QAC7E,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC;QACtB,yFAAyF;QACzF,MAAM,cAAc,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QAC7F,MAAM,KAAK,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzE,MAAM,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC;QAC5C,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,4BAA4B,EAAE,IAAI,EAAE,0CAA0C,EAAE,QAAQ,EAAE,KAAK,EAAE,wCAAwC,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC;IAClL,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AACrF,CAAC"}
|
package/dist/cli/new.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { cp, mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fillPlaceholders, hostLabel, isTextFile, TEMPLATE_IGNORED, templateTargetName, validateProjectName } from '../core/template.js';
|
|
4
|
+
import { packageRoot, runInit } from './init.js';
|
|
5
|
+
export async function listTemplates(root = packageRoot) {
|
|
6
|
+
const entries = await readdir(path.join(root, 'templates'), { withFileTypes: true }).catch(() => []);
|
|
7
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
8
|
+
}
|
|
9
|
+
/** Copies `source` to `dest`, filling `{{placeholders}}` in text files. */
|
|
10
|
+
async function copyTemplate(source, dest, vars) {
|
|
11
|
+
await mkdir(dest, { recursive: true });
|
|
12
|
+
for (const entry of await readdir(source, { withFileTypes: true })) {
|
|
13
|
+
if (TEMPLATE_IGNORED.has(entry.name))
|
|
14
|
+
continue;
|
|
15
|
+
const from = path.join(source, entry.name);
|
|
16
|
+
const to = path.join(dest, templateTargetName(entry.name));
|
|
17
|
+
if (entry.isDirectory())
|
|
18
|
+
await copyTemplate(from, to, vars);
|
|
19
|
+
else if (isTextFile(templateTargetName(entry.name)))
|
|
20
|
+
await writeFile(to, fillPlaceholders(await readFile(from, 'utf8'), vars));
|
|
21
|
+
else
|
|
22
|
+
await cp(from, to);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function runNew(template, name, options) {
|
|
26
|
+
const root = options.root ?? packageRoot;
|
|
27
|
+
const nameError = validateProjectName(name);
|
|
28
|
+
if (nameError)
|
|
29
|
+
throw new Error(nameError);
|
|
30
|
+
const available = await listTemplates(root);
|
|
31
|
+
if (!available.includes(template)) {
|
|
32
|
+
throw new Error(`Unknown template "${template}". Available: ${available.join(', ') || 'none'}.`);
|
|
33
|
+
}
|
|
34
|
+
const dir = path.join(options.parentDir, name);
|
|
35
|
+
if (await stat(dir).then(() => true, () => false))
|
|
36
|
+
throw new Error(`${dir} already exists; pick another name.`);
|
|
37
|
+
await copyTemplate(path.join(root, 'templates', template), dir, { name, template, host: hostLabel(name) });
|
|
38
|
+
// The brief is written after creation (dashboard wizard or by hand), so the brief gate is skipped here.
|
|
39
|
+
const init = await runInit(dir, { force: true, dryRun: false });
|
|
40
|
+
if (init.refused)
|
|
41
|
+
throw new Error(init.refused);
|
|
42
|
+
if (options.dryRun)
|
|
43
|
+
return { dir };
|
|
44
|
+
try {
|
|
45
|
+
const repoUrl = await options.gh.createRepoFromFolder(dir, name, { private: !options.publicRepo });
|
|
46
|
+
return { dir, repoUrl };
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
// The folder is regenerable from the template; leaving it would make a retry fail with "already exists".
|
|
50
|
+
await rm(dir, { recursive: true, force: true });
|
|
51
|
+
throw new Error(`Could not create the GitHub repo (is \`gh auth login\` done, and is the name free?). The folder was removed.\n${error instanceof Error ? error.message : String(error)}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/cli/new.ts"],"names":[],"mappings":"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"}
|