@openora/create 0.4.1-canary.34 → 0.4.1-canary.340

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 (98) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/generated/versions.d.ts +11 -0
  3. package/dist/generated/versions.d.ts.map +1 -0
  4. package/dist/generated/versions.js +11 -0
  5. package/dist/generated/versions.js.map +1 -0
  6. package/dist/index.js +48 -93
  7. package/dist/index.js.map +1 -1
  8. package/dist/render.d.ts +15 -0
  9. package/dist/render.d.ts.map +1 -0
  10. package/dist/render.js +125 -0
  11. package/dist/render.js.map +1 -0
  12. package/package.json +8 -6
  13. package/template/README.md.tpl +12 -7
  14. package/template/__dot__claude/settings.json.tpl +22 -12
  15. package/template/__dot__env.example +10 -0
  16. package/template/__dot__gitignore +6 -1
  17. package/template/__dot__rulesync/commands/adr.md +32 -0
  18. package/template/__dot__rulesync/commands/check.md +11 -10
  19. package/template/__dot__rulesync/commands/doctor.md.tpl +17 -0
  20. package/template/__dot__rulesync/commands/scaffold-module.md +3 -2
  21. package/template/__dot__rulesync/commands/scaffold-plugin.md +19 -3
  22. package/template/__dot__rulesync/commands/scaffold-route.md +1 -0
  23. package/template/__dot__rulesync/commands/start.md +2 -2
  24. package/template/__dot__rulesync/hooks/_shared.mjs +26 -13
  25. package/template/__dot__rulesync/hooks/guard-core.mjs.tpl +76 -28
  26. package/template/__dot__rulesync/hooks/guard-generated.mjs +16 -14
  27. package/template/__dot__rulesync/hooks/guard-mr.mjs +81 -0
  28. package/template/__dot__rulesync/hooks/guard-subagent.mjs +22 -6
  29. package/template/__dot__rulesync/hooks/post-edit.mjs.tpl +116 -0
  30. package/template/__dot__rulesync/hooks.json +27 -4
  31. package/template/__dot__rulesync/rules/conventions.md +114 -0
  32. package/template/__dot__rulesync/rules/db-conventions.md +6 -72
  33. package/template/__dot__rulesync/rules/e2e-conventions.md +52 -0
  34. package/template/__dot__rulesync/rules/frontend-conventions.md.tpl +35 -0
  35. package/template/__dot__rulesync/rules/oss-boundaries.md.tpl +78 -0
  36. package/template/__dot__rulesync/rules/overview.md +18 -73
  37. package/template/__dot__rulesync/skills/create-plugin/{SKILL.md → SKILL.md.tpl} +13 -24
  38. package/template/__dot__rulesync/skills/create-pr/SKILL.md.tpl +38 -0
  39. package/template/__dot__rulesync/skills/create-task/{SKILL.md → SKILL.md.tpl} +14 -22
  40. package/template/__dot__rulesync/skills/create-ui-module/SKILL.md.tpl +60 -0
  41. package/template/__dot__rulesync/skills/enhance-prompt/{SKILL.md → SKILL.md.tpl} +4 -4
  42. package/template/__dot__rulesync/skills/handoff/SKILL.md +95 -0
  43. package/template/__dot__rulesync/skills/review/SKILL.md.tpl +277 -0
  44. package/template/__dot__rulesync/skills/ship/SKILL.md.tpl +157 -0
  45. package/template/__dot__rulesync/skills/ship/handoff.md.tpl +48 -0
  46. package/template/__dot__rulesync/skills/ship/watch-pr.mjs +172 -0
  47. package/template/__dot__rulesync/skills/ui-verify/SKILL.md.tpl +120 -0
  48. package/template/__dot__rulesync/skills/ui-verify/layout-check.check.sh +58 -0
  49. package/template/__dot__rulesync/skills/ui-verify/layout-check.js +163 -0
  50. package/template/__dot__rulesync/subagents/builder.md.tpl +115 -0
  51. package/template/__dot__rulesync/subagents/compliance-reviewer.md +78 -0
  52. package/template/__dot__rulesync/subagents/debugger.md.tpl +86 -0
  53. package/template/__dot__rulesync/subagents/deployer.md.tpl +54 -0
  54. package/template/__dot__rulesync/subagents/expert.md +28 -34
  55. package/template/__dot__rulesync/subagents/planner.md.tpl +39 -0
  56. package/template/__dot__rulesync/subagents/quality-reviewer.md.tpl +112 -0
  57. package/template/__dot__rulesync/subagents/security-reviewer.md.tpl +85 -0
  58. package/template/__dot__rulesync/subagents/tester.md.tpl +102 -0
  59. package/template/__dot__rulesync/sync.json.tpl +26 -0
  60. package/template/apps/api/package.json.tpl +3 -4
  61. package/template/apps/api/src/extensions.config.ts.tpl +12 -0
  62. package/template/apps/api/src/main.ts.tpl +1 -4
  63. package/template/apps/api/src/seed.ts.tpl +8 -1
  64. package/template/docker-compose.yml +13 -0
  65. package/template/docs/agents/cross-repo.md.tpl +37 -0
  66. package/template/docs/agents/forge.md.tpl +54 -0
  67. package/template/docs/agents/issue-tracker.md.tpl +42 -0
  68. package/template/docs/standards/comments.md +16 -0
  69. package/template/docs/standards/database.md +77 -0
  70. package/template/docs/standards/enforcement.md +12 -0
  71. package/template/docs/standards/errors.md +30 -0
  72. package/template/docs/standards/frontend.md +60 -0
  73. package/template/docs/standards/functions.md +38 -0
  74. package/template/docs/standards/git-delivery.md +20 -0
  75. package/template/docs/standards/module-structure.md +37 -0
  76. package/template/docs/standards/testing.md +88 -0
  77. package/template/docs/standards/types.md +31 -0
  78. package/template/package.json.tpl +19 -13
  79. package/template/rulesync.jsonc +4 -4
  80. package/template/tools/oss-worktree.mjs.tpl +105 -0
  81. package/template/tools/review-precheck.mjs +531 -0
  82. package/template/tools/sync-agents.mjs +257 -0
  83. package/template/turbo/generators/config.ts +1 -1
  84. package/template/turbo.json +14 -2
  85. package/dist/generated/core-version.d.ts +0 -2
  86. package/dist/generated/core-version.d.ts.map +0 -1
  87. package/dist/generated/core-version.js +0 -2
  88. package/dist/generated/core-version.js.map +0 -1
  89. package/template/__dot__rulesync/hooks/post-edit.mjs +0 -57
  90. package/template/__dot__rulesync/skills/add-feature/SKILL.md +0 -113
  91. package/template/__dot__rulesync/skills/add-feature/handoff.md +0 -58
  92. package/template/__dot__rulesync/skills/code-review/SKILL.md +0 -111
  93. package/template/__dot__rulesync/skills/create-pr/SKILL.md +0 -55
  94. package/template/__dot__rulesync/subagents/builder.md +0 -93
  95. package/template/__dot__rulesync/subagents/debugger.md +0 -83
  96. package/template/__dot__rulesync/subagents/deployer.md +0 -66
  97. package/template/__dot__rulesync/subagents/qa.md +0 -88
  98. package/template/apps/api/src/migrate.ts.tpl +0 -46
package/dist/render.js ADDED
@@ -0,0 +1,125 @@
1
+ import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
2
+ import { basename, dirname, join, relative, sep } from 'node:path';
3
+ const MCP_COMMAND = 'node';
4
+ const MCP_ARGS = ['node_modules/@openora/mcp/dist/main.js'];
5
+ const SKIPPED_BASENAMES = new Set();
6
+ const USAGE = 'Usage: npm create @openora <target-dir> [--name <name>] [--no-telemetry]';
7
+ function readFlag(args, index) {
8
+ const arg = args[index] ?? '';
9
+ const eq = arg.indexOf('=');
10
+ if (eq !== -1) {
11
+ return { value: arg.slice(eq + 1), next: index };
12
+ }
13
+ return { value: args[index + 1], next: index + 1 };
14
+ }
15
+ export function parseArgs(argv) {
16
+ const args = argv.slice(2);
17
+ let target;
18
+ let name;
19
+ let telemetry = true;
20
+ for (let i = 0; i < args.length; i++) {
21
+ const arg = args[i];
22
+ if (arg === undefined) {
23
+ continue;
24
+ }
25
+ if (arg === '--name' || arg.startsWith('--name=')) {
26
+ const flag = readFlag(args, i);
27
+ name = flag.value;
28
+ i = flag.next;
29
+ }
30
+ else if (arg === '--no-telemetry') {
31
+ telemetry = false;
32
+ }
33
+ else if (!arg.startsWith('-') && !target) {
34
+ target = arg;
35
+ }
36
+ }
37
+ if (!target) {
38
+ throw new Error(`missing target directory.\n ${USAGE}`);
39
+ }
40
+ return name === undefined ? { target, telemetry } : { target, name, telemetry };
41
+ }
42
+ function isSet(flag) {
43
+ return flag !== undefined && ['1', 'true', 'yes'].includes(flag.trim().toLowerCase());
44
+ }
45
+ // Mirrors `isTelemetryEnabled` in core's server/runtime/telemetry.ts; change both together.
46
+ export function isTelemetryEnabled(telemetry, env) {
47
+ return (telemetry &&
48
+ !isSet(env['OPENORA_TELEMETRY_DISABLED']) &&
49
+ !isSet(env['DO_NOT_TRACK']) &&
50
+ env['CI'] === undefined &&
51
+ env['NODE_ENV'] !== 'test');
52
+ }
53
+ export function sanitizeName(raw) {
54
+ return (raw
55
+ .toLowerCase()
56
+ .replace(/[^a-z0-9-_]/g, '-')
57
+ .replace(/^-+|-+$/g, '') || 'igaming-app');
58
+ }
59
+ export function posix(path) {
60
+ return path.split(sep).join('/');
61
+ }
62
+ export function substitute(content, vars) {
63
+ return content.replace(/\{\{(\w+)\}\}/g, (match, key) => {
64
+ const value = vars[key];
65
+ return value === undefined ? match : value;
66
+ });
67
+ }
68
+ function walk(dir) {
69
+ const out = [];
70
+ for (const entry of readdirSync(dir)) {
71
+ const full = join(dir, entry);
72
+ if (statSync(full).isDirectory()) {
73
+ out.push(...walk(full));
74
+ }
75
+ else {
76
+ out.push(full);
77
+ }
78
+ }
79
+ return out;
80
+ }
81
+ export function undotSegment(segment) {
82
+ return segment.startsWith('__dot__') ? `.${segment.slice('__dot__'.length)}` : segment;
83
+ }
84
+ export function outputPath(templateRel) {
85
+ const mapped = templateRel.split(sep).map(undotSegment).join(sep);
86
+ return mapped.endsWith('.tpl') ? mapped.slice(0, -'.tpl'.length) : mapped;
87
+ }
88
+ export function emitTree(srcRoot, vars, targetDir) {
89
+ for (const file of walk(srcRoot)) {
90
+ if (SKIPPED_BASENAMES.has(basename(file))) {
91
+ continue;
92
+ }
93
+ const outRel = outputPath(relative(srcRoot, file));
94
+ const raw = readFileSync(file, 'utf8');
95
+ const content = file.endsWith('.tpl') ? substitute(raw, vars) : raw;
96
+ const outPath = join(targetDir, outRel);
97
+ mkdirSync(dirname(outPath), { recursive: true });
98
+ writeFileSync(outPath, content);
99
+ console.log(` + ${posix(outRel)}`);
100
+ }
101
+ }
102
+ // Operator coordinates the agent files reference. A fresh scaffold gets placeholders;
103
+ // the consumer fills them in once in .rulesync/sync.json and `pnpm sync:agents`
104
+ // re-renders every template-owned file from them.
105
+ export function buildVars(name, versions) {
106
+ return {
107
+ name,
108
+ ...versions,
109
+ mcpCommand: MCP_COMMAND,
110
+ mcpArgsJson: JSON.stringify(MCP_ARGS),
111
+ scope: `@${name}`,
112
+ trackerKey: 'KEY',
113
+ jiraSite: '<your-site>.atlassian.net',
114
+ jiraCloudId: '<your-jira-cloud-id>',
115
+ wikiSpace: '<your-wiki-space>',
116
+ teamChannel: '#<your-team-channel>',
117
+ gitRemotePath: '<group>/<repo>',
118
+ ossDir: '../openora',
119
+ // Only meaningful once the consumer links a local platform checkout; until then the
120
+ // guard-core hook simply never matches.
121
+ ossFromRoot: '../openora',
122
+ mrTarget: 'dev',
123
+ };
124
+ }
125
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openora/create",
3
- "version": "0.4.1-canary.34",
3
+ "version": "0.4.1-canary.340",
4
4
  "description": "npm initializer that scaffolds a headless Openora consumer app from the published @openora/* packages - no OSS checkout required",
5
5
  "keywords": [
6
6
  "ai-native",
@@ -28,16 +28,18 @@
28
28
  "access": "public"
29
29
  },
30
30
  "devDependencies": {
31
- "@types/node": "25.9.2",
31
+ "@types/node": "26.6.5",
32
32
  "typescript": "6.0.3",
33
- "@openora/core": "0.4.1-canary.34"
33
+ "vitest": "4.1.12",
34
+ "@openora/core": "0.4.1-canary.340"
34
35
  },
35
36
  "engines": {
36
37
  "node": ">=26"
37
38
  },
38
39
  "scripts": {
39
- "build": "node scripts/copy-template.mjs && node scripts/generate-core-version.mjs && tsc",
40
- "typecheck": "node scripts/generate-core-version.mjs && tsc --noEmit",
41
- "clean": "rm -rf dist template src/generated"
40
+ "build": "node scripts/copy-template.mjs && node scripts/generate-versions.mjs && tsc",
41
+ "check:types": "node scripts/generate-versions.mjs && tsc --noEmit",
42
+ "clean": "rm -rf dist template src/generated",
43
+ "test:unit": "vitest run"
42
44
  }
43
45
  }
@@ -13,7 +13,8 @@ in its own repo and talk to this api over HTTP via `@openora/react`.
13
13
  ```
14
14
  apps/
15
15
  api/ # Hono + oRPC API (:3001) - thin createApp entry + your extensions.config.ts
16
- .claude/agents/ # AI agents: builder, expert, qa
16
+ .rulesync/ # source of truth for agent rules, subagents, commands, skills
17
+ .claude/agents/ # generated AI agents
17
18
  turbo/generators/ # turbo gen: plugin, adapter
18
19
  ```
19
20
 
@@ -27,18 +28,20 @@ newest release; pin an exact version if you need reproducible installs.
27
28
 
28
29
  ```bash
29
30
  pnpm install # pulls @openora/* from npm
30
- pnpm setup:mcp # trust the MCP server + install the /start onboarding flow
31
- pnpm regen # regenerate OpenAPI + catalog + Drizzle client (after schema changes)
32
31
  cp .env.example .env # set DATABASE_URL + AUTH_SECRET
33
32
  pnpm db:migrate # apply the OSS schema to your database
34
33
  pnpm dev # api :3001
35
34
  ```
36
35
 
37
- After `pnpm setup:mcp`, restart your editor and run **`/start`** in Claude Code - it asks what
38
- you want to build and scaffolds it for you.
36
+ The `oss` MCP server is declared in `.mcp.json`. Approve it in your editor, restart, then run
37
+ **`/start`** in Claude Code - it asks what you want to build and scaffolds it for you.
39
38
 
40
39
  To pull the newest platform release, re-run `pnpm install` (the pin tracks the `latest` stable tag).
41
40
 
41
+ The API sends anonymous usage telemetry (a random instance id, the version, the Node.js runtime and
42
+ two active-player counts). Set `OPENORA_TELEMETRY_DISABLED=1` in `.env` to turn it off; every field
43
+ is listed at https://openora.ai/documentation/telemetry.
44
+
42
45
  ## Extending
43
46
 
44
47
  | Need | Command |
@@ -52,10 +55,12 @@ a DI token wins).
52
55
 
53
56
  ## AI agents
54
57
 
55
- `.claude/agents/` ships three agents scoped to this repo:
58
+ `.claude/agents/` ships agents scoped to this repo, generated from `.rulesync/subagents/`:
56
59
 
60
+ - `planner` - plan a ticket: the blocking questions, or a plan with risk-driven test cases
57
61
  - `builder` - configure extensions, swap adapters, write overlays, customize UI
58
62
  - `expert` - turn product asks into requirements + acceptance criteria
59
- - `qa` - write/run Playwright E2E tests and triage bugs
63
+ - `tester` - attack a built change against the running stack, return a verdict table, add the tests, triage bugs; once tests and review pass, record a video per AC on the final commit
64
+ - `debugger`, `deployer`, `quality-reviewer`, `security-reviewer`, `compliance-reviewer` - root-cause, packaging, review
60
65
 
61
66
  The `oss` MCP server (`.mcp.json`) gives them read-only inspection of the platform surface.
@@ -1,49 +1,59 @@
1
1
  {
2
2
  "enableAllProjectMcpServers": false,
3
3
  "enabledMcpjsonServers": ["oss"],
4
+ "env": {
5
+ "CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD": "1"
6
+ },
4
7
  "permissions": {
5
- "deny": [
6
- "Edit(./node_modules/**)",
7
- "Write(./node_modules/**)"
8
- ]
8
+ "deny": ["Edit(./node_modules/**)"],
9
+ "additionalDirectories": ["{{ossFromRoot}}"]
9
10
  },
10
11
  "hooks": {
11
12
  "PreToolUse": [
12
13
  {
13
- "matcher": "Bash|Edit|Write",
14
+ "matcher": "Bash|Edit|Write|MultiEdit|NotebookEdit",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "node \"${CLAUDE_PROJECT_DIR:-.}/.rulesync/hooks/guard-core.mjs\""
19
+ }
20
+ ]
21
+ },
22
+ {
23
+ "matcher": "Bash|Edit|Write|MultiEdit|NotebookEdit",
14
24
  "hooks": [
15
25
  {
16
26
  "type": "command",
17
- "command": "node .rulesync/hooks/guard-core.mjs"
27
+ "command": "node \"${CLAUDE_PROJECT_DIR:-.}/.rulesync/hooks/guard-generated.mjs\""
18
28
  }
19
29
  ]
20
30
  },
21
31
  {
22
- "matcher": "Edit|Write",
32
+ "matcher": "Bash",
23
33
  "hooks": [
24
34
  {
25
35
  "type": "command",
26
- "command": "node .rulesync/hooks/guard-generated.mjs"
36
+ "command": "node \"${CLAUDE_PROJECT_DIR:-.}/.rulesync/hooks/guard-mr.mjs\""
27
37
  }
28
38
  ]
29
39
  },
30
40
  {
31
- "matcher": "Task",
41
+ "matcher": "Task|Agent",
32
42
  "hooks": [
33
43
  {
34
44
  "type": "command",
35
- "command": "node .rulesync/hooks/guard-subagent.mjs"
45
+ "command": "node \"${CLAUDE_PROJECT_DIR:-.}/.rulesync/hooks/guard-subagent.mjs\""
36
46
  }
37
47
  ]
38
48
  }
39
49
  ],
40
50
  "PostToolUse": [
41
51
  {
42
- "matcher": "Edit|Write",
52
+ "matcher": "Edit|Write|MultiEdit|NotebookEdit",
43
53
  "hooks": [
44
54
  {
45
55
  "type": "command",
46
- "command": "node .rulesync/hooks/post-edit.mjs"
56
+ "command": "node \"${CLAUDE_PROJECT_DIR:-.}/.rulesync/hooks/post-edit.mjs\""
47
57
  }
48
58
  ]
49
59
  }
@@ -1,6 +1,13 @@
1
1
  # Database (Postgres). In dev link-mode this is typically the same local DB the
2
2
  # OSS checkout uses, so migrations run via `pnpm db:migrate` line up.
3
3
  DATABASE_URL=postgresql://postgres:postgres@localhost:5432/oss_igaming
4
+ # Optional pool tuning: connections per process (default 10) and how long a query waits for
5
+ # a free one before failing (default 5000 ms).
6
+ # DATABASE_POOL_MAX=10
7
+ # DATABASE_POOL_ACQUIRE_TIMEOUT_MS=5000
8
+ # Optional read replicas, comma-separated. Only reads that opt into `drizzle.replica`
9
+ # use them; everything else stays on DATABASE_URL. Each replica gets its own pool.
10
+ # DATABASE_REPLICA_URLS=
4
11
 
5
12
  # Auth secret (min 32 chars). Generate one: openssl rand -base64 32
6
13
  AUTH_SECRET=change-me-in-production-min-32-chars
@@ -10,6 +17,9 @@ AUTH_SECRET=change-me-in-production-min-32-chars
10
17
  # up -d` starts this alongside Postgres.
11
18
  REDIS_URL=redis://localhost:6379
12
19
 
20
+ # Anonymous usage telemetry is on by default: https://openora.ai/documentation/telemetry
21
+ # OPENORA_TELEMETRY_DISABLED=1
22
+
13
23
  # Ports
14
24
  PORT=3001
15
25
  CORS_ORIGINS=http://localhost:3000,http://localhost:3002
@@ -17,7 +17,7 @@ coverage
17
17
  .DS_Store
18
18
 
19
19
  # Generated by rulesync from .rulesync/ (single source of truth).
20
- # Regenerated on `pnpm install` (prepare) or `pnpm sync:agents`. Do not commit.
20
+ # Regenerated on `pnpm install` (prepare) or `pnpm gen:agents`. Do not commit.
21
21
  /AGENTS.md
22
22
  /CLAUDE.md
23
23
  /.mcp.json
@@ -25,7 +25,12 @@ coverage
25
25
  .codex/
26
26
  .claude/agents/
27
27
  .claude/commands/
28
+ .claude/worktrees/
29
+ .claude/reviews/
28
30
  .github/copilot-instructions.md
29
31
  .github/agents/
30
32
  .github/prompts/
31
33
  .github/hooks/
34
+
35
+ # Agent files rendered from the @openora/create consumer template on install.
36
+ # The block below is rewritten by tools/sync-agents.mjs - do not edit by hand.
@@ -0,0 +1,32 @@
1
+ ---
2
+ targets:
3
+ - '*'
4
+ description: 'Record an architectural decision in docs/adr/. Args: <title>. Numbers sequentially, follows the ADR-0001 format (Context / Decision / Consequences).'
5
+ ---
6
+
7
+ Create `docs/adr/NNNN-<kebab-slug>.md` where `NNNN` is the next number after the highest existing one (`ls docs/adr/`).
8
+
9
+ Structure (match `0001-modular-architecture.md`):
10
+
11
+ ```markdown
12
+ # ADR NNNN: <Title>
13
+
14
+ ## Context
15
+
16
+ <the problem/forces, 2-5 lines - why a decision was needed>
17
+
18
+ ## Decision
19
+
20
+ <what was decided, imperative and concrete; bullets for the specifics>
21
+
22
+ ## Consequences
23
+
24
+ <what becomes easier/harder; follow-ups it creates>
25
+ ```
26
+
27
+ Rules:
28
+
29
+ - Fill it from the current conversation/diff - state the decision actually made, not options considered. If the decision isn't settled, say so and ask instead of writing a speculative ADR.
30
+ - One ADR = one decision. Reference related ADRs by number.
31
+ - If the decision changes agent-facing conventions, also update the matching `.rulesync/rules/*` file and run `pnpm gen:agents`.
32
+ - Report the file path when done. Don't commit unless asked.
@@ -1,21 +1,22 @@
1
1
  ---
2
2
  targets:
3
3
  - '*'
4
- description: Run the local gate before a PR - typecheck + lint. There is no single `verify` script in this repo; this is it.
4
+ description: Run the fast local gate - typecheck + lint + unit tests. Use `pnpm verify` for the full pre-MR gate (adds format check, boundaries, build).
5
5
  ---
6
6
 
7
- This repo has no `verify` script - typecheck and lint are separate. Run them in order and stop at the first failure.
7
+ Run in order, stop at the first failure:
8
8
 
9
- 1. `pnpm typecheck` - `turbo run typecheck` across all apps.
10
- 2. `pnpm lint` - `oxlint .`.
9
+ 1. `pnpm check:types`
10
+ 2. `pnpm check:lint`
11
+ 3. `pnpm test:unit`
11
12
 
12
- If you have added a test runner (eg `vitest`), also run your test script.
13
-
14
- The generated agent files (AGENTS.md, CLAUDE.md, .codex/config.toml, Copilot mirrors) are gitignored and regenerated from `.rulesync/` on `pnpm install` - there is nothing to drift-check. If you changed agent instructions, edit `.rulesync/` and run `pnpm sync:agents`.
13
+ `pnpm verify` is the pre-MR gate: these three plus whatever else the repo wires into that script (format, boundaries, build). It also records the verified commit, which the `guard-mr` hook checks before an MR/PR is opened.
15
14
 
16
15
  After running:
17
16
 
18
- - All green: report ready for PR; list changed files (`git diff --name-only`).
19
- - typecheck/lint fails: show the location, propose a fix.
17
+ - All green: report ready; list changed files (`git diff --name-only`).
18
+ - A step fails: show the exact location, propose a fix. Never report ready while any step is red.
19
+
20
+ Agent files (`AGENTS.md`, `CLAUDE.md`, mirrors) are gitignored and generated from `.rulesync/` on install - nothing to drift-check. If you changed agent instructions, edit `.rulesync/` and run `pnpm gen:agents`.
20
21
 
21
- Do not touch `@openora/*` core or `node_modules` - this repo extends the platform from the outside only (overlay plugins, adapter rebindings, UI plugins, config). Never report ready for PR if any step fails.
22
+ Do not touch `@openora/*` core or `node_modules` - this repo extends the platform from the outside only (overlay plugins, adapter rebindings, UI plugins, config).
@@ -0,0 +1,17 @@
1
+ ---
2
+ targets:
3
+ - '*'
4
+ description: Diagnose the local dev environment - linked {{ossDir}}, node, postgres, ports, caches. Read-only; reports findings + the fix command for each.
5
+ ---
6
+
7
+ Run these checks and report a pass/fail line per item with the fix command for failures. Read-only - never apply fixes without being asked.
8
+
9
+ 1. **Node version**: `node -v` matches the major in `.nvmrc` (read the file, don't assume a number). Fix: `nvm use`.
10
+ 2. **OSS checkout linked** (only in repos that link a local platform checkout - a stock scaffold installs `@openora/*` from npm and skips this and the next check): `{{ossDir}}` exists and `node --input-type=module -e "import {createRequire} from 'node:module'; console.log(createRequire(process.cwd()+'/').resolve('@openora/core/react'))"` resolves. Fix: clone the OSS repo side by side at `{{ossDir}}`, run the repo's link script if it ships one (`pnpm link:oss`), then `pnpm install`.
11
+ 3. **OSS built**: the resolved path above points into a `dist/` that exists and is newer than `{{ossDir}}/packages/*/src`. Fix: rebuild the linked checkout with the repo's build script if it ships one (`pnpm build:oss`), otherwise build it from `{{ossDir}}`.
12
+ 4. **Database**: postgres reachable on :5432 (`nc -z localhost 5432`). Fix: `dev:infra` MCP tool (server `oss`) or the compose stack.
13
+ 5. **Services**: port 3001 (api), plus 3000 (web) and 3002 (backoffice) in repos that have those apps - report which are up. Fix: `pnpm dev`.
14
+ 6. **Stale bundler cache**: if this repo has a Next app and a build error persists after a fix, `rm -rf apps/*/.next` and rebuild (Turbopack caches across the link boundary).
15
+ 7. **Generated agent files**: `pnpm gen:agents` runs clean (regenerates from `.rulesync/`).
16
+
17
+ If everything passes but a build still fails, hand off to the `debugger` agent with the verbatim error - it owns the deeper resolution table (turbopack.root, symlinked tsconfig, externalDir).
@@ -1,5 +1,6 @@
1
1
  ---
2
- description: 'Generate a self-contained local add-on package (schema, service, router, plugin.ts, AGENTS.md) and register it. Args: <name>. Consumers rarely need this - prefer an overlay plugin.'
2
+ targets: ['*']
3
+ description: 'Generate a self-contained local add-on package (schema, service, router, plugin.ts) and register it. Args: <name>. Consumers rarely need this - prefer an overlay plugin.'
3
4
  ---
4
5
 
5
6
  > A consumer extends the platform with **overlay plugins** (`scaffold-plugin`) almost always. Reach
@@ -11,7 +12,7 @@ ships a buildable add-on - a `list` route wired contract -> router -> service ov
11
12
  so `/check` is green immediately. Then:
12
13
 
13
14
  1. `<name>/src/schema/index.ts` - Drizzle tables (`pgTable`, snake_case, `timestamp({ withTimezone:
14
- true })` per `db-conventions`).
15
+ true })` per `docs/standards/database.md`).
15
16
  2. `<name>/src/schemas/index.ts` - Zod input/output schemas; types via `z.infer`.
16
17
  3. `<name>/src/service/<name>.service.ts` - business logic; inject `DRIZZLE` + `EVENT_BUS`, no inline
17
18
  fetch/SQL.
@@ -1,5 +1,6 @@
1
1
  ---
2
- description: 'Generate an overlay extension under apps/api/src/extensions/<name>/. Adds routes, providers, event handlers, or MCP tools without touching @openora/* core. Args: <name>.'
2
+ targets: ['*']
3
+ description: 'Generate an overlay extension under apps/api/src/extensions/<name>/. Adds routes, providers, event handlers, agent tools, or agent action types without touching @openora/* core. Args: <name>.'
3
4
  ---
4
5
 
5
6
  Run `pnpm gen plugin $ARGUMENTS` in the repo root. For a vendor swap use
@@ -12,13 +13,28 @@ After the generator finishes, open `apps/api/src/extensions/<name>/plugin.ts` an
12
13
  compile + runtime).
13
14
  - `ctx.routers.add(namespace, (c) => router)` - mount oRPC routes (Zod input/output, no inline schemas).
14
15
  - `ctx.events.on(event, handler)` - subscribe to the typed `EventBus`.
15
- - `ctx.mcp.tool(definition)` - expose an MCP tool.
16
+ - `ctx.triggers.listen((trigger, payload, envelope) => ...)` - hear the domain events the operator
17
+ allow-listed as agent triggers (`agents.triggers.topics`). Delivery is at-least-once: dedupe on
18
+ `envelope.eventId`, and drop what is older than you care about by `envelope.occurredAt`, since a
19
+ topic listed again can replay its backlog. The payload is raw and may hold personal data, so
20
+ never hand it to a model. Enqueue a job and return: a slow listener delays the topic's other
21
+ handlers.
22
+ - `ctx.mcp.tool(defineMcpTool({...}), (c) => async (input, run) => output)` - register an agent tool,
23
+ a read a model makes during a run.
24
+ - `ctx.actions.register(defineActionType({...}), (c) => ({ precondition, execute }))` - register an
25
+ agent action type, a change a model may only propose. `execute` performs nothing on a replay
26
+ while its effect still holds, and answers `already_applied`.
27
+
28
+ Both are served only through the `MCP_KERNEL` token, which checks IAM, validates input, applies the
29
+ output allow-list and audits every tool call and action execution; a shape it cannot serve fails
30
+ boot with the fix. The one-argument `ctx.mcp.tool(definition)` still registers but the kernel does
31
+ not serve it.
16
32
 
17
33
  Then:
18
34
 
19
35
  1. Register the plugin in `apps/api/src/extensions.config.ts`. Last registration of a DI token wins -
20
36
  list an adapter swap AFTER the module that owns the default binding.
21
- 2. Tables go in the overlay's own `src/schema/index.ts` (follow `db-conventions`); run `pnpm db:migrate`.
37
+ 2. Tables go in the overlay's own `src/schema/index.ts` (follow `docs/standards/database.md`); run `pnpm db:migrate`.
22
38
  3. Audit every state-changing action (emit an event the `audit` add-on consumes, or `AUDIT_WRITER.record`).
23
39
  4. `/check` to confirm wiring compiles and boundary lint passes.
24
40
 
@@ -1,4 +1,5 @@
1
1
  ---
2
+ targets: ['*']
2
3
  description: 'Add an oRPC route stub to an overlay or local add-on. Args: <target> <GET|POST|PUT|PATCH|DELETE> <path>. Example: /scaffold-route promotions GET /active.'
3
4
  ---
4
5
 
@@ -6,6 +6,6 @@ description: First-run onboarding for this consumer igaming repo. Interview the
6
6
 
7
7
  Call the `start` MCP tool (server `oss`) with no arguments, then follow the script it returns exactly. If the user already described what they want to build, pass it as the `ask` argument.
8
8
 
9
- The returned script will have you: confirm the MCP server is connected, run a thorough requirements interview, call `enhance-intent`, then delegate the implementation to the `expert`, `builder`, and `qa` agents (via the Task tool). You gather requirements and orchestrate; you do not write feature code yourself, and you never modify `@openora/*` core.
9
+ The returned script will have you: confirm the MCP server is connected, run a thorough requirements interview, call `enhance-intent`, then delegate the implementation to the `expert`, `builder`, and `tester` agents (via the Task tool). You gather requirements and orchestrate; you do not write feature code yourself, and you never modify `@openora/*` core.
10
10
 
11
- If the `start` tool is not available, the `oss` MCP server is not connected - tell the user to run `pnpm setup:mcp` and restart the editor.
11
+ If the `start` tool is not available, the `oss` MCP server is not connected - tell the user to approve the `oss` server declared in `.mcp.json` (or run the repo's MCP setup script if it ships one) and restart the editor.
@@ -2,7 +2,10 @@
2
2
  //
3
3
  // One script serves Claude Code, Copilot CLI, Codex CLI, and Gemini CLI. Each
4
4
  // passes the tool call as JSON on stdin but with a slightly different shape:
5
- // Claude / Codex : { tool_name, tool_input: { command?, file_path? } }
5
+ // Claude : { tool_name, tool_input: { command?, file_path? } }
6
+ // Codex : same, except an edit is { tool_name: "apply_patch", tool_input: { command: "<patch>" } },
7
+ // the paths only in its "*** Add File: <path>" style headers; an Edit|Write
8
+ // matcher matches apply_patch
6
9
  // Copilot : { toolName, toolArgs: "<json-string>" | object }
7
10
  // Gemini : { toolName/tool_name, ... } (best-effort)
8
11
  //
@@ -13,6 +16,7 @@
13
16
 
14
17
  import { readFileSync } from 'node:fs';
15
18
 
19
+ /** Read the hook payload from stdin; {} on any error. */
16
20
  export function readPayload() {
17
21
  try {
18
22
  return JSON.parse(readFileSync(0, 'utf8') || '{}');
@@ -21,6 +25,7 @@ export function readPayload() {
21
25
  }
22
26
  }
23
27
 
28
+ /** Copilot's toolArgs may be a JSON string; normalize to an object. */
24
29
  function toolArgsObject(payload) {
25
30
  const a = payload.toolArgs;
26
31
  if (typeof a === 'string') {
@@ -33,27 +38,35 @@ function toolArgsObject(payload) {
33
38
  return a && typeof a === 'object' ? a : {};
34
39
  }
35
40
 
36
- export function extractCommand(payload) {
41
+ const PATCH_PATH = /^\*\*\* (?:Add File|Update File|Delete File|Move to): (.+)$/gm;
42
+
43
+ function rawCommand(payload) {
37
44
  const ti = payload.tool_input ?? {};
38
45
  const ca = toolArgsObject(payload);
39
46
  return String(ti.command ?? ca.command ?? '');
40
47
  }
41
48
 
42
- export function extractFilePath(payload) {
49
+ // Only the tool name tells a patch from a shell command: a Bash command can carry patch text too.
50
+ const isPatch = (payload) => (payload.tool_name ?? payload.toolName) === 'apply_patch';
51
+
52
+ /** The shell command string for this tool call, across tool shapes ('' if none or a patch). */
53
+ export function extractCommand(payload) {
54
+ return isPatch(payload) ? '' : rawCommand(payload);
55
+ }
56
+
57
+ /** Every file an edit/write tool call touches, across tool shapes ([] if none). */
58
+ export function extractFilePaths(payload) {
59
+ if (isPatch(payload)) {
60
+ return [...rawCommand(payload).matchAll(PATCH_PATH)].map((match) => match[1].trim());
61
+ }
43
62
  const ti = payload.tool_input ?? {};
44
63
  const ca = toolArgsObject(payload);
45
- return String(
46
- ti.file_path ??
47
- ti.path ??
48
- ca.path ??
49
- ca.filePath ??
50
- ca.file ??
51
- ca.targetFile ??
52
- ca.target_file ??
53
- '',
54
- );
64
+ const filePath =
65
+ ti.file_path ?? ti.path ?? ca.path ?? ca.filePath ?? ca.file ?? ca.targetFile ?? ca.target_file;
66
+ return filePath ? [String(filePath)] : [];
55
67
  }
56
68
 
69
+ /** Deny the tool call (works on Claude, Copilot, Codex, Gemini). */
57
70
  export function deny(message) {
58
71
  process.stderr.write(message.endsWith('\n') ? message : message + '\n');
59
72
  process.exit(2);