engineering-loop 1.0.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 +45 -0
- package/dist/adapters/aider.d.ts +2 -0
- package/dist/adapters/aider.js +36 -0
- package/dist/adapters/aider.js.map +1 -0
- package/dist/adapters/claude.d.ts +2 -0
- package/dist/adapters/claude.js +42 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex.d.ts +2 -0
- package/dist/adapters/codex.js +37 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/cursor.d.ts +2 -0
- package/dist/adapters/cursor.js +36 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/shared.d.ts +6 -0
- package/dist/adapters/shared.js +44 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/types.d.ts +7 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/commands/doctor.d.ts +10 -0
- package/dist/commands/doctor.js +53 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +12 -0
- package/dist/commands/init.js +46 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/defaultConfig.d.ts +4 -0
- package/dist/defaultConfig.js +43 -0
- package/dist/defaultConfig.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/paths.d.ts +3 -0
- package/dist/paths.js +11 -0
- package/dist/paths.js.map +1 -0
- package/dist/schema.d.ts +5 -0
- package/dist/schema.js +25 -0
- package/dist/schema.js.map +1 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
- package/schema/engineering-loop.schema.json +130 -0
- package/templates/memory/ARCHITECTURE.md +13 -0
- package/templates/memory/DECISIONS.md +12 -0
- package/templates/memory/KNOWLEDGE.md +8 -0
- package/templates/memory/PROJECT.md +17 -0
- package/templates/memory/ROADMAP.md +13 -0
- package/templates/memory/TASKS.md +13 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Engineering Loop Standard Contributors
|
|
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
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# engineering-loop
|
|
2
|
+
|
|
3
|
+
CLI for the [Engineering Loop Standard](../../README.md): scaffolds
|
|
4
|
+
`engineering-loop.json`, persistent `docs/memory/`, and generated adapters
|
|
5
|
+
for Claude Code, Cursor, Aider, and Codex/Gemini.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx engineering-loop init
|
|
11
|
+
npx engineering-loop init --adapters claude,cursor --name my-app
|
|
12
|
+
npx engineering-loop doctor
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Commands
|
|
16
|
+
|
|
17
|
+
### `init`
|
|
18
|
+
|
|
19
|
+
| Flag | Description | Default |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `-d, --dir <path>` | Target directory | current directory |
|
|
22
|
+
| `-n, --name <name>` | Project name written into `engineering-loop.json` | directory name |
|
|
23
|
+
| `-a, --adapters <list>` | Comma-separated adapter ids (`claude,cursor,aider,codex,gemini`) | `claude,cursor,aider,codex` |
|
|
24
|
+
| `-f, --force` | Overwrite existing files | `false` |
|
|
25
|
+
|
|
26
|
+
### `doctor`
|
|
27
|
+
|
|
28
|
+
| Flag | Description | Default |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `-d, --dir <path>` | Project directory to validate | current directory |
|
|
31
|
+
|
|
32
|
+
Exits non-zero if `engineering-loop.json` is missing, invalid against
|
|
33
|
+
[the schema](../../engineering-loop.schema.json), or a required memory file
|
|
34
|
+
is missing.
|
|
35
|
+
|
|
36
|
+
## Development
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install
|
|
40
|
+
npm run build # compiles src/ -> dist/, syncs the schema first
|
|
41
|
+
npm test # vitest
|
|
42
|
+
npm run lint # tsc --noEmit
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
See [../../docs/ADAPTERS.md](../../docs/ADAPTERS.md) to add a new adapter.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { LOOP_PHASES, MANIFESTO_PRINCIPLES, renderCommandsBlock, renderLanguagePolicyLine, renderMemorySection, } from "./shared.js";
|
|
2
|
+
function render(config) {
|
|
3
|
+
const name = config.project.name;
|
|
4
|
+
const manifesto = MANIFESTO_PRINCIPLES.map((p) => `* ${p}`).join("\n");
|
|
5
|
+
const phases = LOOP_PHASES.map((p, i) => `${i + 1}. ${p}`).join("\n");
|
|
6
|
+
const commands = renderCommandsBlock(config);
|
|
7
|
+
return `# Conventions — ${name}
|
|
8
|
+
|
|
9
|
+
Pass this file to Aider with \`--read CONVENTIONS.md\` (or add it to
|
|
10
|
+
\`.aider.conf.yml\`). Generated by the ELS Aider adapter from
|
|
11
|
+
\`engineering-loop.json\`.
|
|
12
|
+
|
|
13
|
+
## Engineering principles
|
|
14
|
+
|
|
15
|
+
${manifesto}
|
|
16
|
+
|
|
17
|
+
## Language policy
|
|
18
|
+
|
|
19
|
+
${renderLanguagePolicyLine(config)}
|
|
20
|
+
|
|
21
|
+
## Loop to follow for non-trivial changes
|
|
22
|
+
|
|
23
|
+
${phases}
|
|
24
|
+
|
|
25
|
+
## Project memory
|
|
26
|
+
|
|
27
|
+
${renderMemorySection(config)}
|
|
28
|
+
${commands.length > 0 ? `\n## Verification commands\n\n${commands.join("\n")}\n` : ""}`;
|
|
29
|
+
}
|
|
30
|
+
export const aiderAdapter = {
|
|
31
|
+
id: "aider",
|
|
32
|
+
label: "Aider",
|
|
33
|
+
outputPath: "CONVENTIONS.md",
|
|
34
|
+
render,
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=aider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aider.js","sourceRoot":"","sources":["../../src/adapters/aider.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,SAAS,MAAM,CAAC,MAA6B;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;IACjC,MAAM,SAAS,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE7C,OAAO,mBAAmB,IAAI;;;;;;;;EAQ9B,SAAS;;;;EAIT,wBAAwB,CAAC,MAAM,CAAC;;;;EAIhC,MAAM;;;;EAIN,mBAAmB,CAAC,MAAM,CAAC;EAC3B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iCAAiC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACxF,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAY;IACnC,EAAE,EAAE,OAAO;IACX,KAAK,EAAE,OAAO;IACd,UAAU,EAAE,gBAAgB;IAC5B,MAAM;CACP,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { LOOP_PHASES, MANIFESTO_PRINCIPLES, renderCommandsBlock, renderLanguagePolicyLine, renderMemorySection, } from "./shared.js";
|
|
2
|
+
function render(config) {
|
|
3
|
+
const name = config.project.name;
|
|
4
|
+
const manifesto = MANIFESTO_PRINCIPLES.map((p, i) => `${i + 1}. **${p}**`).join("\n");
|
|
5
|
+
const phases = LOOP_PHASES.map((p, i) => `${i + 1}. ${p}`).join("\n");
|
|
6
|
+
const commands = renderCommandsBlock(config);
|
|
7
|
+
return `# CLAUDE.md - ${name}
|
|
8
|
+
|
|
9
|
+
Generated by the Engineering Loop Standard (ELS) Claude Code adapter. This
|
|
10
|
+
file is the binding contract for Claude Code working in this repository. See
|
|
11
|
+
\`engineering-loop.json\` for the machine-readable source of truth this file
|
|
12
|
+
is generated from.
|
|
13
|
+
|
|
14
|
+
## Agentic Engineering Manifesto
|
|
15
|
+
|
|
16
|
+
${manifesto}
|
|
17
|
+
|
|
18
|
+
## Language policy
|
|
19
|
+
|
|
20
|
+
${renderLanguagePolicyLine(config)} This applies even when the human author
|
|
21
|
+
writes to Claude in a different language.
|
|
22
|
+
|
|
23
|
+
## The 10-phase engineering loop
|
|
24
|
+
|
|
25
|
+
For any non-trivial change (feature, bugfix, refactor, infra), work through:
|
|
26
|
+
|
|
27
|
+
${phases}
|
|
28
|
+
|
|
29
|
+
## Persistent memory
|
|
30
|
+
|
|
31
|
+
${renderMemorySection(config)}
|
|
32
|
+
|
|
33
|
+
${commands.length > 0 ? `## Commands\n\n${commands.join("\n")}\n` : ""}
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
export const claudeAdapter = {
|
|
37
|
+
id: "claude",
|
|
38
|
+
label: "Claude Code",
|
|
39
|
+
outputPath: "CLAUDE.md",
|
|
40
|
+
render,
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/adapters/claude.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,SAAS,MAAM,CAAC,MAA6B;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;IACjC,MAAM,SAAS,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE7C,OAAO,iBAAiB,IAAI;;;;;;;;;EAS5B,SAAS;;;;EAIT,wBAAwB,CAAC,MAAM,CAAC;;;;;;;EAOhC,MAAM;;;;EAIN,mBAAmB,CAAC,MAAM,CAAC;;EAE3B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;CACrE,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAY;IACpC,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,aAAa;IACpB,UAAU,EAAE,WAAW;IACvB,MAAM;CACP,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { LOOP_PHASES, MANIFESTO_PRINCIPLES, renderCommandsBlock, renderLanguagePolicyLine, renderMemorySection, } from "./shared.js";
|
|
2
|
+
function render(config) {
|
|
3
|
+
const name = config.project.name;
|
|
4
|
+
const manifesto = MANIFESTO_PRINCIPLES.map((p, i) => `${i + 1}. ${p}`).join("\n");
|
|
5
|
+
const phases = LOOP_PHASES.map((p, i) => `${i + 1}. ${p}`).join("\n");
|
|
6
|
+
const commands = renderCommandsBlock(config);
|
|
7
|
+
return `# AGENTS.md — ${name}
|
|
8
|
+
|
|
9
|
+
Read by Codex, Gemini, and any other agent that honors the AGENTS.md
|
|
10
|
+
convention. Generated by the ELS adapter from \`engineering-loop.json\`.
|
|
11
|
+
|
|
12
|
+
## Manifesto
|
|
13
|
+
|
|
14
|
+
${manifesto}
|
|
15
|
+
|
|
16
|
+
## Language policy
|
|
17
|
+
|
|
18
|
+
${renderLanguagePolicyLine(config)}
|
|
19
|
+
|
|
20
|
+
## Engineering loop
|
|
21
|
+
|
|
22
|
+
For any non-trivial change, work through:
|
|
23
|
+
|
|
24
|
+
${phases}
|
|
25
|
+
|
|
26
|
+
## Memory
|
|
27
|
+
|
|
28
|
+
${renderMemorySection(config)}
|
|
29
|
+
${commands.length > 0 ? `\n## Commands\n\n${commands.join("\n")}\n` : ""}`;
|
|
30
|
+
}
|
|
31
|
+
export const codexAdapter = {
|
|
32
|
+
id: "codex",
|
|
33
|
+
label: "Codex / Gemini",
|
|
34
|
+
outputPath: "AGENTS.md",
|
|
35
|
+
render,
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/adapters/codex.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,SAAS,MAAM,CAAC,MAA6B;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;IACjC,MAAM,SAAS,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClF,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE7C,OAAO,iBAAiB,IAAI;;;;;;;EAO5B,SAAS;;;;EAIT,wBAAwB,CAAC,MAAM,CAAC;;;;;;EAMhC,MAAM;;;;EAIN,mBAAmB,CAAC,MAAM,CAAC;EAC3B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,oBAAoB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAY;IACnC,EAAE,EAAE,OAAO;IACX,KAAK,EAAE,gBAAgB;IACvB,UAAU,EAAE,WAAW;IACvB,MAAM;CACP,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { LOOP_PHASES, MANIFESTO_PRINCIPLES, renderCommandsBlock, renderLanguagePolicyLine, renderMemorySection, } from "./shared.js";
|
|
2
|
+
function render(config) {
|
|
3
|
+
const name = config.project.name;
|
|
4
|
+
const manifesto = MANIFESTO_PRINCIPLES.map((p) => `- ${p}`).join("\n");
|
|
5
|
+
const phases = LOOP_PHASES.map((p, i) => `${i + 1}. ${p}`).join("\n");
|
|
6
|
+
const commands = renderCommandsBlock(config);
|
|
7
|
+
return `# ${name} — Engineering Loop Standard rules
|
|
8
|
+
|
|
9
|
+
Generated by the ELS Cursor adapter from \`engineering-loop.json\`.
|
|
10
|
+
|
|
11
|
+
## Principles
|
|
12
|
+
|
|
13
|
+
${manifesto}
|
|
14
|
+
|
|
15
|
+
## Language policy
|
|
16
|
+
|
|
17
|
+
${renderLanguagePolicyLine(config)}
|
|
18
|
+
|
|
19
|
+
## Loop
|
|
20
|
+
|
|
21
|
+
Work through this sequence for any non-trivial change:
|
|
22
|
+
|
|
23
|
+
${phases}
|
|
24
|
+
|
|
25
|
+
## Memory
|
|
26
|
+
|
|
27
|
+
${renderMemorySection(config)}
|
|
28
|
+
${commands.length > 0 ? `\n## Commands\n\n${commands.join("\n")}\n` : ""}`;
|
|
29
|
+
}
|
|
30
|
+
export const cursorAdapter = {
|
|
31
|
+
id: "cursor",
|
|
32
|
+
label: "Cursor",
|
|
33
|
+
outputPath: ".cursorrules",
|
|
34
|
+
render,
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/adapters/cursor.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,SAAS,MAAM,CAAC,MAA6B;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;IACjC,MAAM,SAAS,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE7C,OAAO,KAAK,IAAI;;;;;;EAMhB,SAAS;;;;EAIT,wBAAwB,CAAC,MAAM,CAAC;;;;;;EAMhC,MAAM;;;;EAIN,mBAAmB,CAAC,MAAM,CAAC;EAC3B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,oBAAoB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAY;IACpC,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,QAAQ;IACf,UAAU,EAAE,cAAc;IAC1B,MAAM;CACP,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { aiderAdapter } from "./aider.js";
|
|
2
|
+
import { claudeAdapter } from "./claude.js";
|
|
3
|
+
import { codexAdapter } from "./codex.js";
|
|
4
|
+
import { cursorAdapter } from "./cursor.js";
|
|
5
|
+
export const ADAPTERS = {
|
|
6
|
+
claude: claudeAdapter,
|
|
7
|
+
cursor: cursorAdapter,
|
|
8
|
+
aider: aiderAdapter,
|
|
9
|
+
codex: codexAdapter,
|
|
10
|
+
// Gemini currently shares the AGENTS.md convention with Codex.
|
|
11
|
+
gemini: codexAdapter,
|
|
12
|
+
};
|
|
13
|
+
export function resolveAdapters(ids) {
|
|
14
|
+
const seen = new Set();
|
|
15
|
+
const adapters = [];
|
|
16
|
+
for (const id of ids) {
|
|
17
|
+
const adapter = ADAPTERS[id];
|
|
18
|
+
if (!adapter) {
|
|
19
|
+
throw new Error(`Unknown adapter id: ${id}`);
|
|
20
|
+
}
|
|
21
|
+
if (seen.has(adapter.outputPath))
|
|
22
|
+
continue;
|
|
23
|
+
seen.add(adapter.outputPath);
|
|
24
|
+
adapters.push(adapter);
|
|
25
|
+
}
|
|
26
|
+
return adapters;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,CAAC,MAAM,QAAQ,GAA+B;IAClD,MAAM,EAAE,aAAa;IACrB,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,+DAA+D;IAC/D,MAAM,EAAE,YAAY;CACrB,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,GAAgB;IAC9C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,OAAO,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC;YAAE,SAAS;QAC3C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC7B,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { EngineeringLoopConfig } from "../types.js";
|
|
2
|
+
export declare const MANIFESTO_PRINCIPLES: string[];
|
|
3
|
+
export declare const LOOP_PHASES: string[];
|
|
4
|
+
export declare function renderLanguagePolicyLine(config: EngineeringLoopConfig): string;
|
|
5
|
+
export declare function renderCommandsBlock(config: EngineeringLoopConfig): string[];
|
|
6
|
+
export declare function renderMemorySection(config: EngineeringLoopConfig): string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const MANIFESTO_PRINCIPLES = [
|
|
2
|
+
"Understand before acting. Never write code without full contextual knowledge.",
|
|
3
|
+
"Research before building. Search the repository for existing patterns before creating new ones.",
|
|
4
|
+
"Plan before execution. Draft a clear, step-by-step plan and assess risks before changing files.",
|
|
5
|
+
"Verify before completing. Run tests, linters, and type checks. An unverified change is an incomplete change.",
|
|
6
|
+
"Document as you go. Project memory must always reflect current reality.",
|
|
7
|
+
"Leave the codebase better than you found it. Maintain continuous refactoring and clean code principles.",
|
|
8
|
+
];
|
|
9
|
+
export const LOOP_PHASES = [
|
|
10
|
+
"Context Retrieval",
|
|
11
|
+
"Repository Research",
|
|
12
|
+
"Implementation Plan",
|
|
13
|
+
"Risk & Impact Assessment",
|
|
14
|
+
"Implementation",
|
|
15
|
+
"Automated Testing & Verification",
|
|
16
|
+
"Self-Healing & Bug Fixing",
|
|
17
|
+
"Refactoring",
|
|
18
|
+
"Memory & Documentation Update",
|
|
19
|
+
"Task Summary",
|
|
20
|
+
];
|
|
21
|
+
export function renderLanguagePolicyLine(config) {
|
|
22
|
+
const policy = config.language_policy;
|
|
23
|
+
if (!policy)
|
|
24
|
+
return "All code, comments, and documentation must be written in English.";
|
|
25
|
+
const parts = Object.entries(policy)
|
|
26
|
+
.filter((entry) => typeof entry[1] === "string")
|
|
27
|
+
.map(([key, value]) => `${key}=${value}`);
|
|
28
|
+
return `Required languages by artifact: ${parts.join(", ")}.`;
|
|
29
|
+
}
|
|
30
|
+
export function renderCommandsBlock(config) {
|
|
31
|
+
const commands = config.commands ?? {};
|
|
32
|
+
const lines = [];
|
|
33
|
+
for (const [key, value] of Object.entries(commands)) {
|
|
34
|
+
if (value)
|
|
35
|
+
lines.push(`- ${key}: \`${value}\``);
|
|
36
|
+
}
|
|
37
|
+
return lines;
|
|
38
|
+
}
|
|
39
|
+
export function renderMemorySection(config) {
|
|
40
|
+
const dir = config.memory.directory;
|
|
41
|
+
const files = config.memory.required_files.join(", ");
|
|
42
|
+
return `Persistent project memory lives in \`${dir}/\` (${files}). Read it during Phase 1 (Context Retrieval) and update whatever it makes stale during Phase 9 (Memory & Documentation Update).`;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/adapters/shared.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,+EAA+E;IAC/E,iGAAiG;IACjG,iGAAiG;IACjG,8GAA8G;IAC9G,yEAAyE;IACzE,yGAAyG;CAC1G,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,mBAAmB;IACnB,qBAAqB;IACrB,qBAAqB;IACrB,0BAA0B;IAC1B,gBAAgB;IAChB,kCAAkC;IAClC,2BAA2B;IAC3B,aAAa;IACb,+BAA+B;IAC/B,cAAc;CACf,CAAC;AAEF,MAAM,UAAU,wBAAwB,CAAC,MAA6B;IACpE,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC;IACtC,IAAI,CAAC,MAAM;QAAE,OAAO,mEAAmE,CAAC;IACxF,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;SACjC,MAAM,CAAC,CAAC,KAAK,EAA6B,EAAE,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC;SAC1E,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC;IAC5C,OAAO,mCAAmC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAA6B;IAC/D,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;IACvC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAA6B;IAC/D,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;IACpC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,wCAAwC,GAAG,QAAQ,KAAK,kIAAkI,CAAC;AACpM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { validateConfig } from "../schema.js";
|
|
5
|
+
export async function runDoctor(dir) {
|
|
6
|
+
const checks = [];
|
|
7
|
+
const configPath = join(dir, "engineering-loop.json");
|
|
8
|
+
if (!existsSync(configPath)) {
|
|
9
|
+
checks.push({
|
|
10
|
+
name: "engineering-loop.json exists",
|
|
11
|
+
passed: false,
|
|
12
|
+
detail: `not found at ${configPath}`,
|
|
13
|
+
});
|
|
14
|
+
return { ok: false, checks };
|
|
15
|
+
}
|
|
16
|
+
checks.push({ name: "engineering-loop.json exists", passed: true });
|
|
17
|
+
let raw;
|
|
18
|
+
let parsed;
|
|
19
|
+
try {
|
|
20
|
+
raw = await readFile(configPath, "utf-8");
|
|
21
|
+
parsed = JSON.parse(raw);
|
|
22
|
+
checks.push({ name: "engineering-loop.json is valid JSON", passed: true });
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
checks.push({
|
|
26
|
+
name: "engineering-loop.json is valid JSON",
|
|
27
|
+
passed: false,
|
|
28
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
29
|
+
});
|
|
30
|
+
return { ok: false, checks };
|
|
31
|
+
}
|
|
32
|
+
const { valid, errors } = validateConfig(parsed);
|
|
33
|
+
checks.push({
|
|
34
|
+
name: "engineering-loop.json conforms to schema",
|
|
35
|
+
passed: valid,
|
|
36
|
+
detail: valid ? undefined : errors.join("; "),
|
|
37
|
+
});
|
|
38
|
+
if (valid) {
|
|
39
|
+
const config = parsed;
|
|
40
|
+
const memoryDir = join(dir, config.memory.directory);
|
|
41
|
+
for (const file of config.memory.required_files) {
|
|
42
|
+
const filePath = join(memoryDir, file);
|
|
43
|
+
const exists = existsSync(filePath);
|
|
44
|
+
checks.push({
|
|
45
|
+
name: `memory file present: ${config.memory.directory}/${file}`,
|
|
46
|
+
passed: exists,
|
|
47
|
+
detail: exists ? undefined : `missing at ${filePath}`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { ok: checks.every((c) => c.passed), checks };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAc9C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAC;IAEtD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,8BAA8B;YACpC,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,gBAAgB,UAAU,EAAE;SACrC,CAAC,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC/B,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,8BAA8B,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpE,IAAI,GAAW,CAAC;IAChB,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC1C,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,qCAAqC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,qCAAqC;YAC3C,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC/D,CAAC,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,CAAC,IAAI,CAAC;QACV,IAAI,EAAE,0CAA0C;QAChD,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;KAC9C,CAAC,CAAC;IAEH,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,MAAM,GAAG,MAA+B,CAAC;QAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACrD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,wBAAwB,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,IAAI,EAAE;gBAC/D,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,QAAQ,EAAE;aACtD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AdapterId } from "../types.js";
|
|
2
|
+
export interface InitOptions {
|
|
3
|
+
dir: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
adapters?: AdapterId[];
|
|
6
|
+
force?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface InitResult {
|
|
9
|
+
created: string[];
|
|
10
|
+
skipped: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function runInit(options: InitOptions): Promise<InitResult>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { copyFile, mkdir, readdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
import { resolveAdapters } from "../adapters/index.js";
|
|
5
|
+
import { buildDefaultConfig } from "../defaultConfig.js";
|
|
6
|
+
import { TEMPLATES_DIR } from "../paths.js";
|
|
7
|
+
async function writeIfAllowed(path, contents, force, result) {
|
|
8
|
+
if (!force && existsSync(path)) {
|
|
9
|
+
result.skipped.push(path);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
await writeFile(path, contents, "utf-8");
|
|
13
|
+
result.created.push(path);
|
|
14
|
+
}
|
|
15
|
+
export async function runInit(options) {
|
|
16
|
+
const targetDir = options.dir;
|
|
17
|
+
const projectName = options.name ?? basename(targetDir);
|
|
18
|
+
const result = { created: [], skipped: [] };
|
|
19
|
+
await mkdir(targetDir, { recursive: true });
|
|
20
|
+
const config = buildDefaultConfig(projectName);
|
|
21
|
+
if (options.adapters && options.adapters.length > 0) {
|
|
22
|
+
config.adapters = options.adapters;
|
|
23
|
+
}
|
|
24
|
+
const configPath = join(targetDir, "engineering-loop.json");
|
|
25
|
+
await writeIfAllowed(configPath, `${JSON.stringify(config, null, 2)}\n`, Boolean(options.force), result);
|
|
26
|
+
const memoryDir = join(targetDir, config.memory.directory);
|
|
27
|
+
await mkdir(memoryDir, { recursive: true });
|
|
28
|
+
const templateFiles = await readdir(TEMPLATES_DIR);
|
|
29
|
+
for (const file of templateFiles) {
|
|
30
|
+
const destination = join(memoryDir, file);
|
|
31
|
+
if (!options.force && existsSync(destination)) {
|
|
32
|
+
result.skipped.push(destination);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
await copyFile(join(TEMPLATES_DIR, file), destination);
|
|
36
|
+
result.created.push(destination);
|
|
37
|
+
}
|
|
38
|
+
const adapterIds = config.adapters ?? [];
|
|
39
|
+
const adapters = resolveAdapters(adapterIds);
|
|
40
|
+
for (const adapter of adapters) {
|
|
41
|
+
const outputPath = join(targetDir, adapter.outputPath);
|
|
42
|
+
await writeIfAllowed(outputPath, adapter.render(config), Boolean(options.force), result);
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const DEFAULT_MEMORY_FILES = [
|
|
2
|
+
"PROJECT.md",
|
|
3
|
+
"ARCHITECTURE.md",
|
|
4
|
+
"ROADMAP.md",
|
|
5
|
+
"TASKS.md",
|
|
6
|
+
"DECISIONS.md",
|
|
7
|
+
"KNOWLEDGE.md",
|
|
8
|
+
];
|
|
9
|
+
export const DEFAULT_ADAPTERS = ["claude", "cursor", "aider", "codex"];
|
|
10
|
+
export function buildDefaultConfig(projectName) {
|
|
11
|
+
return {
|
|
12
|
+
$schema: "./engineering-loop.schema.json",
|
|
13
|
+
version: "1.0.0",
|
|
14
|
+
project: {
|
|
15
|
+
name: projectName,
|
|
16
|
+
},
|
|
17
|
+
memory: {
|
|
18
|
+
directory: "docs/memory",
|
|
19
|
+
required_files: [...DEFAULT_MEMORY_FILES],
|
|
20
|
+
},
|
|
21
|
+
pipeline: {
|
|
22
|
+
pre_code: ["research", "plan"],
|
|
23
|
+
post_code: ["lint", "test", "refactor", "update_docs"],
|
|
24
|
+
},
|
|
25
|
+
commands: {
|
|
26
|
+
test: "npm test",
|
|
27
|
+
lint: "npm run lint",
|
|
28
|
+
build: "npm run build",
|
|
29
|
+
},
|
|
30
|
+
language_policy: {
|
|
31
|
+
code: "en",
|
|
32
|
+
comments: "en",
|
|
33
|
+
docs: "en",
|
|
34
|
+
commits: "en",
|
|
35
|
+
},
|
|
36
|
+
agents: {
|
|
37
|
+
allow_auto_fix: true,
|
|
38
|
+
max_repair_iterations: 3,
|
|
39
|
+
},
|
|
40
|
+
adapters: [...DEFAULT_ADAPTERS],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=defaultConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaultConfig.js","sourceRoot":"","sources":["../src/defaultConfig.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,YAAY;IACZ,iBAAiB;IACjB,YAAY;IACZ,UAAU;IACV,cAAc;IACd,cAAc;CACf,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAEpF,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IACpD,OAAO;QACL,OAAO,EAAE,gCAAgC;QACzC,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE;YACP,IAAI,EAAE,WAAW;SAClB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,aAAa;YACxB,cAAc,EAAE,CAAC,GAAG,oBAAoB,CAAC;SAC1C;QACD,QAAQ,EAAE;YACR,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC;YAC9B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,CAAC;SACvD;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,eAAe;SACvB;QACD,eAAe,EAAE;YACf,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI;SACd;QACD,MAAM,EAAE;YACN,cAAc,EAAE,IAAI;YACpB,qBAAqB,EAAE,CAAC;SACzB;QACD,QAAQ,EAAE,CAAC,GAAG,gBAAgB,CAAC;KAChC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { runDoctor } from "./commands/doctor.js";
|
|
6
|
+
import { runInit } from "./commands/init.js";
|
|
7
|
+
import { PACKAGE_ROOT } from "./paths.js";
|
|
8
|
+
const pkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, "package.json"), "utf-8"));
|
|
9
|
+
const VALID_ADAPTER_IDS = ["claude", "cursor", "aider", "codex", "gemini"];
|
|
10
|
+
function parseAdapters(value) {
|
|
11
|
+
const ids = value.split(",").map((s) => s.trim());
|
|
12
|
+
for (const id of ids) {
|
|
13
|
+
if (!VALID_ADAPTER_IDS.includes(id)) {
|
|
14
|
+
throw new Error(`Unknown adapter "${id}". Valid adapters: ${VALID_ADAPTER_IDS.join(", ")}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return ids;
|
|
18
|
+
}
|
|
19
|
+
const program = new Command();
|
|
20
|
+
program
|
|
21
|
+
.name("engineering-loop")
|
|
22
|
+
.description("Scaffold and validate the Engineering Loop Standard in a project")
|
|
23
|
+
.version(pkg.version);
|
|
24
|
+
program
|
|
25
|
+
.command("init")
|
|
26
|
+
.description("Create engineering-loop.json, docs/memory/, and generate agent adapters")
|
|
27
|
+
.option("-d, --dir <path>", "target directory", process.cwd())
|
|
28
|
+
.option("-n, --name <name>", "project name (defaults to the directory name)")
|
|
29
|
+
.option("-a, --adapters <list>", "comma-separated adapters to generate (claude,cursor,aider,codex,gemini)")
|
|
30
|
+
.option("-f, --force", "overwrite existing files", false)
|
|
31
|
+
.action(async (opts) => {
|
|
32
|
+
try {
|
|
33
|
+
const adapters = opts.adapters ? parseAdapters(opts.adapters) : undefined;
|
|
34
|
+
const result = await runInit({
|
|
35
|
+
dir: opts.dir,
|
|
36
|
+
name: opts.name,
|
|
37
|
+
adapters,
|
|
38
|
+
force: opts.force,
|
|
39
|
+
});
|
|
40
|
+
for (const file of result.created) {
|
|
41
|
+
console.log(`✔ Created ${file}`);
|
|
42
|
+
}
|
|
43
|
+
for (const file of result.skipped) {
|
|
44
|
+
console.log(`– Skipped ${file} (already exists, use --force to overwrite)`);
|
|
45
|
+
}
|
|
46
|
+
console.log("\nEngineering Loop ready.");
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
program
|
|
54
|
+
.command("doctor")
|
|
55
|
+
.description("Validate that a project conforms to the Engineering Loop Standard")
|
|
56
|
+
.option("-d, --dir <path>", "project directory", process.cwd())
|
|
57
|
+
.action(async (opts) => {
|
|
58
|
+
const report = await runDoctor(opts.dir);
|
|
59
|
+
for (const check of report.checks) {
|
|
60
|
+
const icon = check.passed ? "✔" : "✗";
|
|
61
|
+
const detail = check.detail ? ` (${check.detail})` : "";
|
|
62
|
+
console.log(`${icon} ${check.name}${detail}`);
|
|
63
|
+
}
|
|
64
|
+
if (!report.ok) {
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
program.parseAsync(process.argv);
|
|
69
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.d.ts
ADDED
package/dist/paths.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
// This file must stay directly under src/ (compiling to dist/paths.js one
|
|
4
|
+
// level below the package root) so PACKAGE_ROOT resolves correctly whether
|
|
5
|
+
// running compiled (dist/paths.js) or via tsx in dev (src/paths.ts) — both
|
|
6
|
+
// sit exactly one directory below the package root.
|
|
7
|
+
const thisFileDir = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
export const PACKAGE_ROOT = dirname(thisFileDir);
|
|
9
|
+
export const TEMPLATES_DIR = join(PACKAGE_ROOT, "templates", "memory");
|
|
10
|
+
export const SCHEMA_PATH = join(PACKAGE_ROOT, "schema", "engineering-loop.schema.json");
|
|
11
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,0EAA0E;AAC1E,2EAA2E;AAC3E,2EAA2E;AAC3E,oDAAoD;AACpD,MAAM,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5D,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;AAEjD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;AACvE,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,8BAA8B,CAAC,CAAC"}
|
package/dist/schema.d.ts
ADDED
package/dist/schema.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
3
|
+
import { SCHEMA_PATH } from "./paths.js";
|
|
4
|
+
let compiledValidator;
|
|
5
|
+
let lastErrors;
|
|
6
|
+
function getValidator() {
|
|
7
|
+
if (!compiledValidator) {
|
|
8
|
+
const schema = JSON.parse(readFileSync(SCHEMA_PATH, "utf-8"));
|
|
9
|
+
const ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
10
|
+
const validate = ajv.compile(schema);
|
|
11
|
+
compiledValidator = (data) => {
|
|
12
|
+
const ok = validate(data);
|
|
13
|
+
lastErrors = validate.errors;
|
|
14
|
+
return ok;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return compiledValidator;
|
|
18
|
+
}
|
|
19
|
+
export function validateConfig(data) {
|
|
20
|
+
const validate = getValidator();
|
|
21
|
+
const valid = validate(data);
|
|
22
|
+
const errors = (lastErrors ?? []).map((err) => `${err.instancePath || "/"} ${err.message ?? "is invalid"}`);
|
|
23
|
+
return { valid, errors };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,IAAI,iBAA2D,CAAC;AAChE,IAAI,UAA4C,CAAC;AAEjD,SAAS,YAAY;IACnB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9D,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACrC,iBAAiB,GAAG,CAAC,IAAa,EAAE,EAAE;YACpC,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC1B,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;YAC7B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAOD,MAAM,UAAU,cAAc,CAAC,IAAa;IAC1C,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CACnC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,YAAY,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,IAAI,YAAY,EAAE,CACrE,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface EngineeringLoopConfig {
|
|
2
|
+
$schema?: string;
|
|
3
|
+
version: string;
|
|
4
|
+
project: {
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
architecture_style?: string;
|
|
8
|
+
};
|
|
9
|
+
memory: {
|
|
10
|
+
directory: string;
|
|
11
|
+
required_files: string[];
|
|
12
|
+
};
|
|
13
|
+
pipeline?: {
|
|
14
|
+
pre_code?: string[];
|
|
15
|
+
post_code?: string[];
|
|
16
|
+
};
|
|
17
|
+
commands?: {
|
|
18
|
+
test?: string;
|
|
19
|
+
lint?: string;
|
|
20
|
+
build?: string;
|
|
21
|
+
format?: string;
|
|
22
|
+
[key: string]: string | undefined;
|
|
23
|
+
};
|
|
24
|
+
language_policy?: {
|
|
25
|
+
code?: string;
|
|
26
|
+
comments?: string;
|
|
27
|
+
docs?: string;
|
|
28
|
+
commits?: string;
|
|
29
|
+
[key: string]: string | undefined;
|
|
30
|
+
};
|
|
31
|
+
agents?: {
|
|
32
|
+
allow_auto_fix?: boolean;
|
|
33
|
+
max_repair_iterations?: number;
|
|
34
|
+
};
|
|
35
|
+
adapters?: AdapterId[];
|
|
36
|
+
}
|
|
37
|
+
export type AdapterId = "claude" | "cursor" | "aider" | "codex" | "gemini";
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "engineering-loop",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI for the Engineering Loop Standard: scaffold engineering-loop.json, persistent project memory, and multi-tool AI agent adapters.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"engineering-loop": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"templates",
|
|
15
|
+
"schema"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"prebuild": "node scripts/sync-schema.mjs",
|
|
22
|
+
"build": "tsc -p tsconfig.json",
|
|
23
|
+
"pretest": "node scripts/sync-schema.mjs",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
26
|
+
"dev": "tsx src/index.ts"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"ajv": "^8.14.0",
|
|
30
|
+
"commander": "^12.1.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.14.0",
|
|
34
|
+
"tsx": "^4.16.0",
|
|
35
|
+
"typescript": "^5.5.0",
|
|
36
|
+
"vitest": "^1.6.0"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"engineering-loop",
|
|
40
|
+
"ai-agents",
|
|
41
|
+
"claude-code",
|
|
42
|
+
"cursor",
|
|
43
|
+
"aider",
|
|
44
|
+
"codex",
|
|
45
|
+
"specification",
|
|
46
|
+
"cli"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://engineering-loop.org/schema/v1.json",
|
|
4
|
+
"title": "Engineering Loop Standard Configuration",
|
|
5
|
+
"description": "Formal schema for engineering-loop.json, the single source of truth an Engineering Loop Standard adapter is generated from.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["version", "project", "memory"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"$schema": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Pointer back to this schema, for editor validation."
|
|
13
|
+
},
|
|
14
|
+
"version": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Version of the Engineering Loop Standard this config conforms to, in semver.",
|
|
17
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
18
|
+
},
|
|
19
|
+
"project": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"required": ["name"],
|
|
22
|
+
"additionalProperties": false,
|
|
23
|
+
"properties": {
|
|
24
|
+
"name": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"minLength": 1
|
|
27
|
+
},
|
|
28
|
+
"description": {
|
|
29
|
+
"type": "string"
|
|
30
|
+
},
|
|
31
|
+
"architecture_style": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "Free-text label, e.g. modular-monolith, microservices, monorepo, cli-tool."
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"memory": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"required": ["directory", "required_files"],
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"properties": {
|
|
42
|
+
"directory": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "Path, relative to the project root, holding persistent memory files.",
|
|
45
|
+
"default": "docs/memory"
|
|
46
|
+
},
|
|
47
|
+
"required_files": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"description": "Filenames that must exist inside the memory directory.",
|
|
50
|
+
"items": { "type": "string" },
|
|
51
|
+
"minItems": 1,
|
|
52
|
+
"default": [
|
|
53
|
+
"PROJECT.md",
|
|
54
|
+
"ARCHITECTURE.md",
|
|
55
|
+
"ROADMAP.md",
|
|
56
|
+
"TASKS.md",
|
|
57
|
+
"DECISIONS.md",
|
|
58
|
+
"KNOWLEDGE.md"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"pipeline": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"description": "Ordered phase names run before and after code changes. Phase names are free-text so tools can extend them.",
|
|
66
|
+
"additionalProperties": false,
|
|
67
|
+
"properties": {
|
|
68
|
+
"pre_code": {
|
|
69
|
+
"type": "array",
|
|
70
|
+
"items": { "type": "string" },
|
|
71
|
+
"default": ["research", "plan"]
|
|
72
|
+
},
|
|
73
|
+
"post_code": {
|
|
74
|
+
"type": "array",
|
|
75
|
+
"items": { "type": "string" },
|
|
76
|
+
"default": ["lint", "test", "refactor", "update_docs"]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"commands": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"description": "Shell commands an agent should run for each verification step.",
|
|
83
|
+
"additionalProperties": { "type": "string" },
|
|
84
|
+
"properties": {
|
|
85
|
+
"test": { "type": "string" },
|
|
86
|
+
"lint": { "type": "string" },
|
|
87
|
+
"build": { "type": "string" },
|
|
88
|
+
"format": { "type": "string" }
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"language_policy": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"description": "ISO 639-1 language codes required for each artifact category.",
|
|
94
|
+
"additionalProperties": { "type": "string", "pattern": "^[a-z]{2}$" },
|
|
95
|
+
"properties": {
|
|
96
|
+
"code": { "type": "string", "pattern": "^[a-z]{2}$", "default": "en" },
|
|
97
|
+
"comments": { "type": "string", "pattern": "^[a-z]{2}$", "default": "en" },
|
|
98
|
+
"docs": { "type": "string", "pattern": "^[a-z]{2}$", "default": "en" },
|
|
99
|
+
"commits": { "type": "string", "pattern": "^[a-z]{2}$", "default": "en" }
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"agents": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"description": "Behavioral limits applied to any agent following this config.",
|
|
105
|
+
"additionalProperties": false,
|
|
106
|
+
"properties": {
|
|
107
|
+
"allow_auto_fix": {
|
|
108
|
+
"type": "boolean",
|
|
109
|
+
"description": "Whether an agent may automatically repair failing tests/lint without asking first.",
|
|
110
|
+
"default": true
|
|
111
|
+
},
|
|
112
|
+
"max_repair_iterations": {
|
|
113
|
+
"type": "integer",
|
|
114
|
+
"description": "Upper bound on Phase 7 (Self-Healing) retry loops before an agent must stop and ask for help.",
|
|
115
|
+
"minimum": 1,
|
|
116
|
+
"default": 3
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"adapters": {
|
|
121
|
+
"type": "array",
|
|
122
|
+
"description": "Adapters to generate when running `engineering-loop init` or `engineering-loop sync`.",
|
|
123
|
+
"items": {
|
|
124
|
+
"type": "string",
|
|
125
|
+
"enum": ["claude", "cursor", "aider", "codex", "gemini"]
|
|
126
|
+
},
|
|
127
|
+
"default": ["claude", "cursor", "aider", "codex"]
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
<!-- High-level diagram or description of major components. -->
|
|
6
|
+
|
|
7
|
+
## Data flow
|
|
8
|
+
|
|
9
|
+
<!-- How a request/task moves through the system. -->
|
|
10
|
+
|
|
11
|
+
## Design decisions
|
|
12
|
+
|
|
13
|
+
<!-- Link to DECISIONS.md for the "why" behind non-obvious choices. -->
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Decisions
|
|
2
|
+
|
|
3
|
+
Architecture Decision Records. Newest first.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ADR-001: <title>
|
|
8
|
+
|
|
9
|
+
**Date:** <YYYY-MM-DD>
|
|
10
|
+
**Status:** Proposed | Accepted | Superseded
|
|
11
|
+
|
|
12
|
+
<!-- What was decided, what alternatives were considered, and why this one won. -->
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Project
|
|
2
|
+
|
|
3
|
+
## What this is
|
|
4
|
+
|
|
5
|
+
<!-- One or two sentences: what does this project do, and for whom? -->
|
|
6
|
+
|
|
7
|
+
## Tech stack
|
|
8
|
+
|
|
9
|
+
<!-- Languages, frameworks, datastores, notable infrastructure. -->
|
|
10
|
+
|
|
11
|
+
## Language policy
|
|
12
|
+
|
|
13
|
+
<!-- Should generally match the `language_policy` block in engineering-loop.json. -->
|
|
14
|
+
|
|
15
|
+
## Current status
|
|
16
|
+
|
|
17
|
+
<!-- One line pointing to ROADMAP.md / TASKS.md for detail. -->
|