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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/dist/adapters/aider.d.ts +2 -0
  4. package/dist/adapters/aider.js +36 -0
  5. package/dist/adapters/aider.js.map +1 -0
  6. package/dist/adapters/claude.d.ts +2 -0
  7. package/dist/adapters/claude.js +42 -0
  8. package/dist/adapters/claude.js.map +1 -0
  9. package/dist/adapters/codex.d.ts +2 -0
  10. package/dist/adapters/codex.js +37 -0
  11. package/dist/adapters/codex.js.map +1 -0
  12. package/dist/adapters/cursor.d.ts +2 -0
  13. package/dist/adapters/cursor.js +36 -0
  14. package/dist/adapters/cursor.js.map +1 -0
  15. package/dist/adapters/index.d.ts +5 -0
  16. package/dist/adapters/index.js +28 -0
  17. package/dist/adapters/index.js.map +1 -0
  18. package/dist/adapters/shared.d.ts +6 -0
  19. package/dist/adapters/shared.js +44 -0
  20. package/dist/adapters/shared.js.map +1 -0
  21. package/dist/adapters/types.d.ts +7 -0
  22. package/dist/adapters/types.js +2 -0
  23. package/dist/adapters/types.js.map +1 -0
  24. package/dist/commands/doctor.d.ts +10 -0
  25. package/dist/commands/doctor.js +53 -0
  26. package/dist/commands/doctor.js.map +1 -0
  27. package/dist/commands/init.d.ts +12 -0
  28. package/dist/commands/init.js +46 -0
  29. package/dist/commands/init.js.map +1 -0
  30. package/dist/defaultConfig.d.ts +4 -0
  31. package/dist/defaultConfig.js +43 -0
  32. package/dist/defaultConfig.js.map +1 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.js +69 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/paths.d.ts +3 -0
  37. package/dist/paths.js +11 -0
  38. package/dist/paths.js.map +1 -0
  39. package/dist/schema.d.ts +5 -0
  40. package/dist/schema.js +25 -0
  41. package/dist/schema.js.map +1 -0
  42. package/dist/types.d.ts +37 -0
  43. package/dist/types.js +2 -0
  44. package/dist/types.js.map +1 -0
  45. package/package.json +48 -0
  46. package/schema/engineering-loop.schema.json +130 -0
  47. package/templates/memory/ARCHITECTURE.md +13 -0
  48. package/templates/memory/DECISIONS.md +12 -0
  49. package/templates/memory/KNOWLEDGE.md +8 -0
  50. package/templates/memory/PROJECT.md +17 -0
  51. package/templates/memory/ROADMAP.md +13 -0
  52. 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,2 @@
1
+ import type { Adapter } from "./types.js";
2
+ export declare const aiderAdapter: 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,2 @@
1
+ import type { Adapter } from "./types.js";
2
+ export declare const claudeAdapter: Adapter;
@@ -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,2 @@
1
+ import type { Adapter } from "./types.js";
2
+ export declare const codexAdapter: Adapter;
@@ -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,2 @@
1
+ import type { Adapter } from "./types.js";
2
+ export declare const cursorAdapter: 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 `# ${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,5 @@
1
+ import type { AdapterId } from "../types.js";
2
+ import type { Adapter } from "./types.js";
3
+ export declare const ADAPTERS: Record<AdapterId, Adapter>;
4
+ export declare function resolveAdapters(ids: AdapterId[]): Adapter[];
5
+ export type { Adapter } from "./types.js";
@@ -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,7 @@
1
+ import type { AdapterId, EngineeringLoopConfig } from "../types.js";
2
+ export interface Adapter {
3
+ id: AdapterId;
4
+ label: string;
5
+ outputPath: string;
6
+ render(config: EngineeringLoopConfig): string;
7
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ export interface DoctorCheck {
2
+ name: string;
3
+ passed: boolean;
4
+ detail?: string;
5
+ }
6
+ export interface DoctorReport {
7
+ ok: boolean;
8
+ checks: DoctorCheck[];
9
+ }
10
+ export declare function runDoctor(dir: string): Promise<DoctorReport>;
@@ -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,4 @@
1
+ import type { AdapterId, EngineeringLoopConfig } from "./types.js";
2
+ export declare const DEFAULT_MEMORY_FILES: string[];
3
+ export declare const DEFAULT_ADAPTERS: AdapterId[];
4
+ export declare function buildDefaultConfig(projectName: string): EngineeringLoopConfig;
@@ -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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
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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"}
@@ -0,0 +1,3 @@
1
+ export declare const PACKAGE_ROOT: string;
2
+ export declare const TEMPLATES_DIR: string;
3
+ export declare const SCHEMA_PATH: string;
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"}
@@ -0,0 +1,5 @@
1
+ export interface ValidationResult {
2
+ valid: boolean;
3
+ errors: string[];
4
+ }
5
+ export declare function validateConfig(data: unknown): ValidationResult;
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"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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,8 @@
1
+ # Knowledge
2
+
3
+ Domain insights and lessons that aren't obvious from reading the code.
4
+
5
+ <!--
6
+ - One bullet per insight.
7
+ - Prefer "why" over "what" — the code already shows what.
8
+ -->
@@ -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. -->
@@ -0,0 +1,13 @@
1
+ # Roadmap
2
+
3
+ ## Now
4
+
5
+ <!-- What's actively planned for the current milestone. -->
6
+
7
+ ## Next
8
+
9
+ <!-- What's planned after that. -->
10
+
11
+ ## Later / unscheduled
12
+
13
+ <!-- Ideas not yet committed to a milestone. -->
@@ -0,0 +1,13 @@
1
+ # Tasks
2
+
3
+ ## Active
4
+
5
+ <!-- What's being worked on right now, by whom. -->
6
+
7
+ ## Backlog
8
+
9
+ <!-- See ROADMAP.md for direction; this is day-to-day state. -->
10
+
11
+ ## Done (recent)
12
+
13
+ <!-- Short log of recently completed tasks, newest first. -->