mikoshi-construct 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -0
  3. package/dist/cli.js +1492 -0
  4. package/package.json +73 -0
  5. package/templates/ai/claude/CLAUDE.md.eta +18 -0
  6. package/templates/ai/claude/CLAUDE.md.existing.eta +15 -0
  7. package/templates/ai/claude/_claude/agents/architect.md +38 -0
  8. package/templates/ai/claude/_claude/agents/harness.md +40 -0
  9. package/templates/ai/claude/_claude/agents/implementer.md +40 -0
  10. package/templates/ai/claude/_claude/commands/plan.md +24 -0
  11. package/templates/ai/claude/_claude/skills/implement/SKILL.md +42 -0
  12. package/templates/ai/claude/scripts/construct/implement.workflow.mjs +185 -0
  13. package/templates/ai/cursor/_cursor/rules/construct.mdc +19 -0
  14. package/templates/ai/review/_github/workflows/claude-review.yml.eta +51 -0
  15. package/templates/ai/shared/AGENTS.md.eta +123 -0
  16. package/templates/ai/shared/AGENTS.md.existing.eta +50 -0
  17. package/templates/ai/shared/_claude/commands/construct-discover.md +75 -0
  18. package/templates/ai/shared/_claude/rules/conventions.md +63 -0
  19. package/templates/ai/shared/_claude/rules/secrets.md +8 -0
  20. package/templates/ai/shared/_claude/rules/tests.md +6 -0
  21. package/templates/base/_editorconfig +12 -0
  22. package/templates/base/_github/workflows/security.yml +41 -0
  23. package/templates/base/_gitignore +11 -0
  24. package/templates/base/_gitleaks.toml +2 -0
  25. package/templates/base/_nvmrc.eta +1 -0
  26. package/templates/base/_vscode/settings.json +22 -0
  27. package/templates/base/architecture/checklists.md +42 -0
  28. package/templates/base/architecture/principles.md +113 -0
  29. package/templates/base/architecture/security-invariants.md +17 -0
  30. package/templates/harness/_github/workflows/ci.yml.eta +31 -0
  31. package/templates/harness/eslint.config.mjs +13 -0
  32. package/templates/harness/package.json.eta +29 -0
  33. package/templates/harness/pnpm-workspace.yaml +11 -0
  34. package/templates/harness/scripts/composition/check.ts +19 -0
  35. package/templates/harness/scripts/composition/files.ts +34 -0
  36. package/templates/harness/scripts/composition/model.ts +137 -0
  37. package/templates/harness/scripts/composition/render.ts +72 -0
  38. package/templates/harness/scripts/composition/sync-docs.ts +11 -0
  39. package/templates/harness/scripts/tests/composition/files.test.ts +16 -0
  40. package/templates/harness/scripts/tests/composition/model.test.ts +41 -0
  41. package/templates/harness/scripts/tests/composition/render.test.ts +54 -0
  42. package/templates/harness/tsconfig.base.json +14 -0
  43. package/templates/harness/tsconfig.json +5 -0
  44. package/templates/harness/vitest.config.ts +8 -0
  45. package/templates/presets/monorepo/baseline/eslint.config.mjs.eta +100 -0
  46. package/templates/presets/monorepo/baseline/package.json.eta +18 -0
  47. package/templates/presets/monorepo/baseline/packages/shared/package.json.eta +22 -0
  48. package/templates/presets/monorepo/baseline/packages/shared/src/api/openapi.ts +81 -0
  49. package/templates/presets/monorepo/baseline/packages/shared/src/index.ts +1 -0
  50. package/templates/presets/monorepo/baseline/packages/shared/tsconfig.build.json +10 -0
  51. package/templates/presets/monorepo/baseline/packages/shared/tsconfig.json +4 -0
  52. package/templates/presets/monorepo/baseline/pnpm-workspace.yaml.eta +31 -0
  53. package/templates/presets/monorepo/baseline/tsconfig.json +12 -0
  54. package/templates/presets/monorepo/baseline/vitest.config.ts +8 -0
  55. package/templates/presets/monorepo/sample/apps/api/package.json.eta +23 -0
  56. package/templates/presets/monorepo/sample/apps/api/tsconfig.build.json +10 -0
  57. package/templates/presets/monorepo/sample/apps/api/tsconfig.json +4 -0
  58. package/templates/presets/monorepo/sample/scripts/tests/lint/syntax-policy.test.ts.eta +36 -0
  59. package/templates/presets/node-backend/baseline/eslint.config.mjs +59 -0
  60. package/templates/presets/node-backend/baseline/package.json.eta +20 -0
  61. package/templates/presets/node-backend/baseline/src/contracts/openapi.ts +81 -0
  62. package/templates/presets/node-backend/baseline/tsconfig.build.json +10 -0
  63. package/templates/presets/node-backend/sample/scripts/tests/lint/syntax-policy.test.ts +31 -0
  64. package/templates/presets/node-frontend/baseline/_claude/rules/css.md +158 -0
  65. package/templates/presets/node-frontend/baseline/eslint.config.mjs +44 -0
  66. package/templates/presets/node-frontend/baseline/package.json.eta +15 -0
  67. package/templates/presets/node-frontend/baseline/tsconfig.json +11 -0
  68. package/templates/presets/node-frontend/baseline/vitest.config.ts +10 -0
  69. package/templates/presets/node-frontend/sample/architecture/app.md +37 -0
  70. package/templates/presets/node-frontend/sample/architecture/composition/app.yaml +41 -0
  71. package/templates/presets/node-frontend/sample/index.html.eta +12 -0
  72. package/templates/presets/node-frontend/sample/src/app.ts +32 -0
  73. package/templates/presets/node-frontend/sample/src/main.ts +9 -0
  74. package/templates/presets/node-frontend/sample/src/styles/app.css +43 -0
  75. package/templates/presets/node-frontend/sample/src/styles/tokens.css +16 -0
  76. package/templates/presets/node-frontend/sample/tests/app.test.ts +21 -0
  77. package/templates/stacks/express-api/app/src/app.ts +27 -0
  78. package/templates/stacks/express-api/app/src/config.ts.eta +30 -0
  79. package/templates/stacks/express-api/app/src/contracts/types.ts.eta +1 -0
  80. package/templates/stacks/express-api/app/src/health/health.controller.ts +12 -0
  81. package/templates/stacks/express-api/app/src/health/health.service.ts +23 -0
  82. package/templates/stacks/express-api/app/src/http/api-contract.middleware.ts +13 -0
  83. package/templates/stacks/express-api/app/src/http/app-error.ts +14 -0
  84. package/templates/stacks/express-api/app/src/http/async-handler.middleware.ts +7 -0
  85. package/templates/stacks/express-api/app/src/http/error-handler.middleware.ts +50 -0
  86. package/templates/stacks/express-api/app/src/server.ts +9 -0
  87. package/templates/stacks/express-api/app/tests/health.test.ts +28 -0
  88. package/templates/stacks/express-api/app/tests/http/error-handler.middleware.test.ts +48 -0
  89. package/templates/stacks/express-api/repo/architecture/composition/http.yaml.eta +45 -0
  90. package/templates/stacks/express-api/repo/architecture/http.md +39 -0
  91. package/templates/stacks/http-contract/_github/workflows/api-contract.yml +36 -0
  92. package/templates/stacks/http-contract/contracts/api/openapi.yaml.eta +62 -0
  93. package/templates/stacks/http-contract/package.json.eta +13 -0
  94. package/templates/stacks/http-contract/redocly.yaml.eta +8 -0
  95. package/templates/stacks/http-contract/scripts/contracts/types.mjs.eta +20 -0
  96. package/templates/stacks/http-contract/scripts/tests/contracts/security.test.ts.eta +86 -0
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "mikoshi-construct",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "packageManager": "pnpm@12.4.2",
6
+ "description": "Bootstrap for AI-native software projects. Start with a proven engineering workflow instead of an empty repository.",
7
+ "author": "Eli Tabrisov",
8
+ "license": "MIT",
9
+ "homepage": "https://github.com/e1i/mikoshi-construct#readme",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/e1i/mikoshi-construct.git"
13
+ },
14
+ "bugs": "https://github.com/e1i/mikoshi-construct/issues",
15
+ "keywords": [
16
+ "claude-code",
17
+ "cursor",
18
+ "ai-agents",
19
+ "scaffold",
20
+ "bootstrap",
21
+ "openapi",
22
+ "harness",
23
+ "architecture",
24
+ "cli",
25
+ "ai-dlc"
26
+ ],
27
+ "bin": {
28
+ "construct": "dist/cli.js",
29
+ "miko": "dist/cli.js",
30
+ "mikoshi-construct": "dist/cli.js"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "templates"
35
+ ],
36
+ "engines": {
37
+ "node": ">=20"
38
+ },
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "dev": "tsx src/cli.ts",
42
+ "lint": "eslint .",
43
+ "lint:fix": "eslint --fix .",
44
+ "typecheck": "tsc --noEmit",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "quality": "pnpm composition:check && pnpm lint && pnpm typecheck && pnpm test",
48
+ "prepublishOnly": "pnpm build",
49
+ "composition:render": "tsx scripts/composition/sync-docs.ts",
50
+ "composition:check": "tsx scripts/composition/check.ts",
51
+ "ci": "pnpm run quality",
52
+ "changeset": "changeset",
53
+ "version-packages": "changeset version",
54
+ "release": "pnpm build && npm publish --provenance --access public"
55
+ },
56
+ "dependencies": {
57
+ "@clack/prompts": "^1.8.0",
58
+ "citty": "^0.2.2",
59
+ "picocolors": "^1.1.1"
60
+ },
61
+ "devDependencies": {
62
+ "@antfu/eslint-config": "^9.5.1",
63
+ "@changesets/changelog-github": "^1.0.0",
64
+ "@changesets/cli": "^3.0.0",
65
+ "@types/node": "^22.18.0",
66
+ "eslint": "^10.10.0",
67
+ "tsup": "^8.5.1",
68
+ "tsx": "^4.23.13",
69
+ "typescript": "^5.9.3",
70
+ "vitest": "^5.0.0",
71
+ "yaml": "^2.9.1"
72
+ }
73
+ }
@@ -0,0 +1,18 @@
1
+ # {{projectName}}
2
+
3
+ @AGENTS.md
4
+
5
+ Everything an agent needs to know about this repository — product, module map, commands, harness,
6
+ composition roots, dependency policy, high-effort areas, defects vs accepted variance — lives in
7
+ [AGENTS.md](AGENTS.md), imported above. Repository-wide code rules are in `.claude/rules/`; the
8
+ architecture, security and reasoning-budget rules in
9
+ [architecture/principles.md](architecture/principles.md).
10
+
11
+ ## Working here with Claude Code
12
+
13
+ - `/construct-discover` — fill or refresh the discovery blocks in AGENTS.md and `architecture/`.
14
+ - `/plan <feature>` — decompose a feature into tasks with acceptance criteria and an effort class.
15
+ - `/implement <task>` — run the reasoning-budget ladder: classify, implement at the lowest rung,
16
+ verify with `{{harnessCommand}}`, escalate only when verification proves it was not enough.
17
+ - `construct doctor` (or `npx mikoshi-construct doctor`) — check that the baseline and discovery are
18
+ intact.
@@ -0,0 +1,15 @@
1
+ ## Construct
2
+
3
+ This repository runs on a construct materialized by `mikoshi-construct` v{{constructVersion}}. The
4
+ discovery blocks — product, module map, commands, composition roots, dependency policy, high-effort
5
+ areas, defects vs accepted variance, open questions — live in [AGENTS.md](AGENTS.md); the
6
+ architecture, security and reasoning-budget rules in
7
+ [architecture/principles.md](architecture/principles.md); repository-wide code rules in
8
+ `.claude/rules/`.
9
+
10
+ - `/construct-discover` — fill or refresh the discovery blocks.
11
+ - `/plan <feature>` — decompose a feature into tasks with acceptance criteria and an effort class.
12
+ - `/implement <task>` — the reasoning-budget ladder: classify, implement at the lowest rung, verify
13
+ with `{{harnessCommand}}`, escalate only when verification proves it was not enough.
14
+ - `construct doctor` (or `npx mikoshi-construct doctor`) — check that the baseline and discovery are
15
+ intact.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: architect
3
+ description: High-effort design for an architecture, contract, composition or security change, or for a task an implementer reported as blocked or repeatedly failed. Reads and reasons; does not edit.
4
+ model: inherit
5
+ effort: xhigh
6
+ tools: Read, Grep, Glob, Bash
7
+ ---
8
+
9
+ You design; you do not implement. You may read anything and run read-only commands (`git log`,
10
+ `git diff`, tests), but you never write files. Run `git` only when `git rev-parse --show-toplevel`
11
+ is the directory that holds `construct.json`; a parent directory's repository tells you nothing about
12
+ this one.
13
+
14
+ Input you receive: the task, and on escalation either the implementer's question or the failure
15
+ excerpts of the attempts so far.
16
+
17
+ Work through, in this order, and write nothing down until you have:
18
+
19
+ 1. Which contract(s), domain boundaries, dependencies and composition the change touches.
20
+ 2. Whether an existing artifact already describes the area (API contract, composition model,
21
+ invariants table) and what in it must change first.
22
+ 3. The alternatives you considered and why the chosen one keeps the change local.
23
+ 4. Whether the API contract or a composition model must change; if so, that is part of the spec.
24
+ 5. The explicit constraints the implementer must respect, and the acceptance criteria that make
25
+ the task statable.
26
+
27
+ Your final message is data, not prose. Return exactly one JSON object:
28
+
29
+ ```json
30
+ {
31
+ "decision": "the design in a few sentences, including what stays unchanged and why",
32
+ "contractChanges": "operations or schemas to add or change in the API contract, or empty",
33
+ "compositionChanges": "composition model nodes or edges to change, or empty",
34
+ "constraints": ["one constraint per entry"],
35
+ "acceptance": ["one verifiable criterion per entry"],
36
+ "files": ["files to create or change"]
37
+ }
38
+ ```
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: harness
3
+ description: Runs the repository's quality gate against the working tree and reports a structured verdict. Never edits. Use after an implementer so a pass is never self-reported.
4
+ model: inherit
5
+ effort: low
6
+ tools: Bash, Read, Grep, Glob
7
+ ---
8
+
9
+ You verify; you do not fix. Run the harness command(s) you are given against the current working
10
+ tree and report what happened. If no command is given, run the harness command named in
11
+ `construct.json` (`harness.command`), and fall back to `pnpm run quality`. If the repository's CLAUDE.md
12
+ names extra commands for the area the diff touches, run those too.
13
+
14
+ Git is usable only when `git rev-parse --show-toplevel` is the directory that holds `construct.json`.
15
+ When it is not — the repository has no `.git`, or a parent directory's repository would answer — set
16
+ `diffStat` to `not a git repository at <dir>`, and decide `contractChanged` and `testsWeakened` by
17
+ reading the files the implementer's report names, never by a `git diff` that sees a different tree.
18
+
19
+ When git is usable, also inspect `git diff` (staged and unstaged) for:
20
+
21
+ - a deleted or renamed file under any `tests/` directory;
22
+ - `.skip(` or `.only(` added to a test;
23
+ - a change to an API contract file under `contracts/` (report it; the caller decides whether it was
24
+ expected).
25
+
26
+ Report the security leg separately from the rest when a failure comes from a security lint rule,
27
+ a contract security test or the secret scan, so the reader sees the invariant, not just the tool.
28
+
29
+ Your final message is data, not prose. Return exactly one JSON object:
30
+
31
+ ```json
32
+ {
33
+ "passed": true,
34
+ "failureExcerpt": "the failing command and its last relevant lines, empty when passed",
35
+ "securityFinding": "the invariant that failed, empty when none",
36
+ "diffStat": "output of git diff --stat",
37
+ "testsWeakened": false,
38
+ "contractChanged": false
39
+ }
40
+ ```
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: implementer
3
+ description: Low-effort implementer for a task with a statable acceptance criterion. Follows the neighbouring pattern under strict constraints, runs the harness, and returns a structured report instead of a guess.
4
+ model: inherit
5
+ effort: low
6
+ tools: Read, Edit, Write, Grep, Glob, Bash
7
+ ---
8
+
9
+ You implement exactly the task you are given, nothing more. The repository's CLAUDE.md files are
10
+ already in your context; follow them.
11
+
12
+ Input you receive: the task, the acceptance criteria, the harness command(s), optionally a design
13
+ spec from the architect, and optionally the failure excerpt from the previous attempt.
14
+
15
+ Constraints at this effort level:
16
+
17
+ - Implement only the requested change. Follow the pattern of the neighbouring code.
18
+ - Add no abstraction, no dependency, no layer. Touch no unrelated module.
19
+ - When the task touches an HTTP route, change the API contract first, regenerate the shared
20
+ types, then implement.
21
+ - Never delete, skip or weaken a test. A changed logic module ships its test in the same change.
22
+ - Read the previous failure excerpt, if any, before touching code; fix the cause, not the symptom.
23
+ - Run the harness before reporting. If it fails and you cannot see why within two attempts, report
24
+ `failed` with the excerpt rather than looping.
25
+ - Do not commit.
26
+
27
+ If the task is ambiguous about a contract, a boundary or which of two designs is meant, stop and
28
+ report `blocked` with one precise question. Do not pick one.
29
+
30
+ Your final message is data, not prose. Return exactly one JSON object:
31
+
32
+ ```json
33
+ {
34
+ "status": "done | failed | blocked",
35
+ "summary": "one or two sentences on what changed",
36
+ "files": ["paths you changed"],
37
+ "harnessTail": "last lines of the harness output, empty when blocked",
38
+ "question": "the single question when blocked, otherwise empty"
39
+ }
40
+ ```
@@ -0,0 +1,24 @@
1
+ ---
2
+ description: Turn a feature or issue into tasks that /implement can run — each with a statable acceptance criterion and an effort class.
3
+ argument-hint: <feature, issue text or link>
4
+ ---
5
+
6
+ Decompose `$ARGUMENTS` into tasks for the reasoning-budget ladder. Use the `architect` agent for the
7
+ decomposition when the feature touches a contract, a composition model, a high-effort area from
8
+ CLAUDE.md or a security invariant; otherwise reason it out directly.
9
+
10
+ Rules:
11
+
12
+ - Two to six tasks. Each task is independently verifiable by the harness and leaves the tree green.
13
+ - Each task has two to four acceptance criteria that a harness run or a test can confirm. "Works" is
14
+ not a criterion; "GET /v1/things returns 200 with the `Thing` schema and the contract test passes" is.
15
+ - Order tasks so the contract and composition changes come first, then implementation, then anything
16
+ that consumes the new behaviour.
17
+ - Classify each task `low`, `medium` or `high` with the rules in `architecture/principles.md`
18
+ (Reasoning budget); a task touching a high-effort area, the contract, a composition model, the
19
+ dependency policy or a security invariant is `high`.
20
+ - If the feature is ambiguous about a contract or a boundary, put one precise question first and stop
21
+ after it; do not plan on a guess.
22
+
23
+ Output the tasks as a numbered list; for each, one line `/implement <task> — acceptance: …; effort:
24
+ <class>` that can be pasted as-is. Do not implement anything.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: implement
3
+ description: Implement a task through the reasoning-budget ladder — classify effort, run a low-effort implementer under constraints, verify with the harness, escalate only on repeated failure or ambiguity.
4
+ user-invocable: true
5
+ disable-model-invocation: true
6
+ argument-hint: <task with an acceptance criterion>
7
+ ---
8
+
9
+ Run the reasoning-budget ladder for the task in `$ARGUMENTS`. The rules are in the Reasoning budget
10
+ section of `architecture/principles.md`; repo specifics (harness command, high-effort areas) are in the
11
+ repository's CLAUDE.md and `construct.json`.
12
+
13
+ 1. Classify the effort class and tell the user the class and the one-line reason before anything
14
+ else. Read the repository's CLAUDE.md for its harness command and its high-effort areas. A task
15
+ that names or must touch a high-effort area, the API contract, a composition model, the
16
+ dependency policy or the security invariants is `high`. A new endpoint, a new integration or a
17
+ change across several modules is `medium`. Everything with an existing pattern to copy and a
18
+ contract already defined is `low`.
19
+ 2. Write the acceptance criteria in two to four verifiable lines. If the task has no statable
20
+ criterion, say so and stop; the ladder is not for one-line edits or open-ended exploration.
21
+ 3. Call the Workflow tool with `scriptPath` set to `scripts/construct/implement.workflow.mjs` (the ladder
22
+ script lives with the project's scripts, not under `.claude/`) and `args` as
23
+ a JSON object:
24
+ `{ "task": ..., "acceptance": [...], "effort": "low|medium|high", "harness": { "command": ..., "extra": [...] } }`
25
+ where `harness.command` comes from `construct.json` and `harness.extra` lists any area-specific
26
+ commands CLAUDE.md names for the files the task touches (usually empty).
27
+ The user's `/implement` invocation is the opt-in the tool requires.
28
+ 4. Record the run: append one JSON line to `.construct/runs.jsonl` (create the directory if needed)
29
+ with `at` (ISO time), `task` (first 120 characters), `effort` (the class you chose), `status`,
30
+ the rung that finished (`effort` from the result), `attempts` from the result, and the Workflow
31
+ tool's own accounting for the run — `agents`, `tokens`, `toolUses`, `seconds` — exactly as it
32
+ reported them. This log is what tunes the ladder later; workflow scripts have no filesystem
33
+ access, so it is written here, not by the script. `.construct/` is gitignored.
34
+ 5. Relay the result: status, the effort rung that succeeded and how many attempts it took, the
35
+ files changed, and the harness tail. When the status is `blocked`, put the architect's or
36
+ implementer's question to the user verbatim. When `failed`, give the last failure excerpt.
37
+ Unless `construct.json` sets `report.usage` to `false`, end with one usage line for this run,
38
+ from the Workflow tool's own accounting: agents, subagent tokens, tool uses, wall time — so the
39
+ cost of the rung that succeeded is on record next to the result. When `construct` is on the PATH,
40
+ add the per-agent split from `construct cost --last --json` (implementer, harness, architect);
41
+ when it is not, the Workflow accounting alone is the line — never guess numbers.
42
+ 6. Never commit. The user reviews the working tree first.
@@ -0,0 +1,185 @@
1
+ export const meta = {
2
+ name: 'implement',
3
+ description: 'Implement a task at low effort, verify with the harness, escalate on repeated failure or ambiguity',
4
+ phases: [
5
+ { title: 'Design', detail: 'architect, only for high effort or after a blocked or failed attempt' },
6
+ { title: 'Implement', detail: 'implementer at the current rung' },
7
+ { title: 'Verify', detail: 'harness against the working tree' },
8
+ ],
9
+ }
10
+
11
+ const LADDERS = {
12
+ low: ['low', 'low', 'medium', 'high'],
13
+ medium: ['medium', 'medium', 'high'],
14
+ high: ['high', 'high', 'xhigh'],
15
+ }
16
+
17
+ const REPORT = {
18
+ type: 'object',
19
+ required: ['status', 'summary', 'files', 'harnessTail', 'question'],
20
+ properties: {
21
+ status: { type: 'string', enum: ['done', 'failed', 'blocked'] },
22
+ summary: { type: 'string' },
23
+ files: { type: 'array', items: { type: 'string' } },
24
+ harnessTail: { type: 'string' },
25
+ question: { type: 'string' },
26
+ },
27
+ }
28
+
29
+ const VERDICT = {
30
+ type: 'object',
31
+ required: ['passed', 'failureExcerpt', 'securityFinding', 'diffStat', 'testsWeakened', 'contractChanged'],
32
+ properties: {
33
+ passed: { type: 'boolean' },
34
+ failureExcerpt: { type: 'string' },
35
+ securityFinding: { type: 'string' },
36
+ diffStat: { type: 'string' },
37
+ testsWeakened: { type: 'boolean' },
38
+ contractChanged: { type: 'boolean' },
39
+ },
40
+ }
41
+
42
+ const SPEC = {
43
+ type: 'object',
44
+ required: ['decision', 'contractChanges', 'compositionChanges', 'constraints', 'acceptance', 'files'],
45
+ properties: {
46
+ decision: { type: 'string' },
47
+ contractChanges: { type: 'string' },
48
+ compositionChanges: { type: 'string' },
49
+ constraints: { type: 'array', items: { type: 'string' } },
50
+ acceptance: { type: 'array', items: { type: 'string' } },
51
+ files: { type: 'array', items: { type: 'string' } },
52
+ },
53
+ }
54
+
55
+ const task = args.task
56
+ const acceptance = args.acceptance ?? []
57
+ const harness = { command: 'pnpm run quality', extra: [], ...(args.harness ?? {}) }
58
+ const rungs = LADDERS[args.effort] ?? LADDERS.low
59
+
60
+ function harnessPrompt() {
61
+ return [
62
+ `Harness command: ${harness.command}`,
63
+ harness.extra.length > 0 ? `Extra commands for the area this task touches: ${harness.extra.join(' && ')}` : '',
64
+ 'Verify the current working tree and return the verdict object.',
65
+ ].filter(Boolean).join('\n')
66
+ }
67
+
68
+ function architectPrompt(reason) {
69
+ return [
70
+ `Task: ${task}`,
71
+ acceptance.length > 0 ? `Acceptance criteria so far:\n- ${acceptance.join('\n- ')}` : '',
72
+ reason,
73
+ 'Return the design spec object.',
74
+ ].filter(Boolean).join('\n\n')
75
+ }
76
+
77
+ function implementerPrompt(spec, feedback) {
78
+ return [
79
+ `Task: ${task}`,
80
+ `Acceptance criteria:\n- ${(spec?.acceptance?.length ? spec.acceptance : acceptance).join('\n- ')}`,
81
+ `Harness: ${harness.command}${harness.extra.length > 0 ? ` (plus ${harness.extra.join(' && ')})` : ''}`,
82
+ spec == null
83
+ ? ''
84
+ : `Design spec from the architect:\n${spec.decision}\n\nContract changes: ${spec.contractChanges || 'none'}\nComposition changes: ${spec.compositionChanges || 'none'}\nConstraints:\n- ${spec.constraints.join('\n- ')}\nFiles: ${spec.files.join(', ')}`,
85
+ feedback == null ? '' : `The previous attempt failed the harness. Fix the cause of this before anything else:\n${feedback}`,
86
+ 'Return the report object.',
87
+ ].filter(Boolean).join('\n\n')
88
+ }
89
+
90
+ let spec = null
91
+ let feedback = null
92
+ const attempts = []
93
+
94
+
95
+ if (args.effort === 'high') {
96
+ phase('Design')
97
+ spec = await agent(architectPrompt('The task is classified as high effort; design it before any implementation.'), {
98
+ agentType: 'architect',
99
+ effort: 'xhigh',
100
+ phase: 'Design',
101
+ label: 'design',
102
+ schema: SPEC,
103
+ })
104
+ }
105
+
106
+ for (const [index, effort] of rungs.entries()) {
107
+ const rung = index + 1
108
+ phase('Implement')
109
+ log(`rung ${rung}/${rungs.length} @ ${effort}: implementing`)
110
+ const report = await agent(implementerPrompt(spec, feedback), {
111
+ agentType: 'implementer',
112
+ effort,
113
+ phase: 'Implement',
114
+ label: `implement ${rung}/${rungs.length} @ ${effort}`,
115
+ schema: REPORT,
116
+ })
117
+ if (report == null) {
118
+ attempts.push({ rung, effort, outcome: 'no report' })
119
+ continue
120
+ }
121
+
122
+ if (report.status === 'blocked') {
123
+ attempts.push({ rung, effort, outcome: 'blocked', question: report.question })
124
+ if (rung === rungs.length)
125
+ return { status: 'blocked', question: report.question, attempts }
126
+ spec = await agent(architectPrompt(`The implementer stopped on this question:\n${report.question}`), {
127
+ agentType: 'architect',
128
+ effort: 'xhigh',
129
+ phase: 'Design',
130
+ label: `design after blocked ${rung}`,
131
+ schema: SPEC,
132
+ })
133
+ feedback = null
134
+ log(`rung ${rung}/${rungs.length} @ ${effort}: blocked, architect answered`)
135
+ continue
136
+ }
137
+
138
+ phase('Verify')
139
+ log(`rung ${rung}/${rungs.length} @ ${effort}: running ${harness.command}`)
140
+ const verdict = await agent(harnessPrompt(), {
141
+ agentType: 'harness',
142
+ effort: 'low',
143
+ phase: 'Verify',
144
+ label: `verify ${rung}/${rungs.length}`,
145
+ schema: VERDICT,
146
+ })
147
+ const passed = verdict?.passed === true && verdict.testsWeakened === false
148
+ attempts.push({ rung, effort, outcome: passed ? 'passed' : 'failed', securityFinding: verdict?.securityFinding ?? '' })
149
+ log(`rung ${rung} @ ${effort}: ${passed ? 'harness passed' : 'harness failed'}`)
150
+
151
+ if (passed) {
152
+ return {
153
+ status: 'done',
154
+ effort,
155
+ attempts,
156
+ files: report.files,
157
+ summary: report.summary,
158
+ harnessTail: report.harnessTail,
159
+ contractChanged: verdict.contractChanged,
160
+ diffStat: verdict.diffStat,
161
+ }
162
+ }
163
+
164
+ feedback = verdict == null
165
+ ? 'The harness produced no verdict.'
166
+ : verdict.testsWeakened
167
+ ? `A test was deleted, skipped or narrowed. Restore it and make the implementation pass it.\n${verdict.failureExcerpt}`
168
+ : verdict.failureExcerpt
169
+ if (verdict?.securityFinding)
170
+ feedback = `Security invariant failed: ${verdict.securityFinding}\n${feedback}`
171
+
172
+ if (rung === rungs.length - 1) {
173
+ phase('Design')
174
+ log(`rung ${rung}/${rungs.length} failed twice: architect redesigns before the last rung`)
175
+ spec = await agent(architectPrompt(`Two rungs have failed the harness. Latest failure:\n${feedback}\n\nDecide whether the approach, the contract or the boundary is wrong before the last attempt.`), {
176
+ agentType: 'architect',
177
+ effort: 'xhigh',
178
+ phase: 'Design',
179
+ label: 'design before last rung',
180
+ schema: SPEC,
181
+ })
182
+ }
183
+ }
184
+
185
+ return { status: 'failed', attempts, lastFailure: feedback }
@@ -0,0 +1,19 @@
1
+ ---
2
+ description: Construct baseline — where the rules, contract and harness live
3
+ alwaysApply: true
4
+ ---
5
+
6
+ This repository runs on a construct materialized by `mikoshi-construct`. Before changing code read, in
7
+ this order: `architecture/principles.md` (architecture, security, reasoning budget), `AGENTS.md`
8
+ (what the product does, module map, commands, composition roots, dependency policy, high-effort
9
+ areas, defects vs accepted variance), `architecture/security-invariants.md`.
10
+
11
+ The harness command is in `construct.json` (`harness.command`). A change is done only when it passes.
12
+ API changes start from the contract file named in `construct.json` (`contracts.path`); regenerate the
13
+ types with `pnpm contracts:types` before implementing. Composition models in the directory
14
+ `construct.json` names as `discovery.composition` are the source of the diagrams; edit the model and run
15
+ `pnpm composition:render`, never the rendered block.
16
+
17
+ Discovery: when asked to "run construct discovery", follow the `construct-discover` rule in this
18
+ directory step by step. It fills every `<!-- construct:discover:* -->` block in AGENTS.md and
19
+ architecture/ with what you verified by reading code.
@@ -0,0 +1,51 @@
1
+ name: Claude Review
2
+
3
+ on:
4
+ pull_request:
5
+ types: [labeled]
6
+ workflow_dispatch:
7
+ inputs:
8
+ pr:
9
+ description: Pull request number
10
+ required: true
11
+ type: string
12
+
13
+ concurrency:
14
+ group: claude-review-${{ github.event.pull_request.number || inputs.pr }}
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ review:
19
+ if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'claude-review'
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 60
22
+ permissions:
23
+ contents: read
24
+ pull-requests: write
25
+ issues: write
26
+ id-token: write
27
+ env:
28
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29
+ REPO: ${{ github.repository }}
30
+ PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr }}
31
+ steps:
32
+ - uses: actions/checkout@v7
33
+ with:
34
+ fetch-depth: 0
35
+
36
+ - name: Check out the pull request branch
37
+ run: gh pr checkout "$PR_NUMBER"
38
+
39
+ - uses: anthropics/claude-code-action@v1
40
+ with:
41
+ anthropic_api_key: ${{ secrets.CODE_REVIEW_API_KEY }}
42
+ plugin_marketplaces: https://github.com/anthropics/claude-code.git
43
+ plugins: code-review@claude-code-plugins
44
+ prompt: /code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number || inputs.pr }}
45
+ claude_args: |
46
+ --model {{reviewModel}}
47
+ --allowedTools "Read,Grep,Glob,Bash,Task,Agent,TodoWrite,WebFetch,mcp__github_inline_comment__create_inline_comment"
48
+
49
+ - name: Remove trigger label
50
+ if: always() && github.event_name == 'pull_request'
51
+ run: gh api -X DELETE "repos/$REPO/issues/$PR_NUMBER/labels/claude-review" --silent || true