@genesislcap/genx 15.23.0 → 15.23.1-agent-rules.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/agent-rules/manifest.json +13 -0
- package/agent-rules/report-upstream-bug.md +249 -0
- package/dist/commands/agent-rules.d.ts +6 -0
- package/dist/commands/agent-rules.d.ts.map +1 -0
- package/dist/commands/agent-rules.js +62 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -0
- package/dist/index.js +17 -0
- package/dist/utils/agent-rules.d.ts +62 -0
- package/dist/utils/agent-rules.d.ts.map +1 -0
- package/dist/utils/agent-rules.js +124 -0
- package/package.json +13 -9
package/README.md
CHANGED
|
@@ -26,6 +26,30 @@ To enable this module in your application, follow the steps below.
|
|
|
26
26
|
|
|
27
27
|
## [API Docs](./docs/api/index.md)
|
|
28
28
|
|
|
29
|
+
## Agent rules
|
|
30
|
+
|
|
31
|
+
`genx agent-rules` installs the shared AI agent rules that ship with this package
|
|
32
|
+
([`agent-rules/`](./agent-rules)) into a consumer project, and wires them into whichever assistants
|
|
33
|
+
that project uses.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx genx agent-rules # install and wire up detected assistants
|
|
37
|
+
npx genx agent-rules --tools claude,cursor,gemini # wire up specific assistants
|
|
38
|
+
npx genx agent-rules --check # CI: fail when installed rules are stale
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The rule markdown is copied to `docs/agent-rules/` and referenced from a managed block in
|
|
42
|
+
`CLAUDE.md`, `GEMINI.md`, or `AGENTS.md`, and from a generated `.cursor/rules/<rule>.mdc` for
|
|
43
|
+
Cursor. Content outside the managed block is never touched. Installed copies are managed — re-run
|
|
44
|
+
the command after `genx upgrade` to pick up rule changes, and propose edits in
|
|
45
|
+
[foundation-ui](https://github.com/genesislcap/foundation-ui) rather than locally.
|
|
46
|
+
|
|
47
|
+
Currently shipped:
|
|
48
|
+
|
|
49
|
+
| Rule | Purpose |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `report-upstream-bug.md` | File a bug in `genesislcap/foundation-ui` — with the human's confirmation — instead of quietly working around it in the consumer project. |
|
|
52
|
+
|
|
29
53
|
## Event type codegen
|
|
30
54
|
|
|
31
55
|
When `eventTypes.enabled` is set in `genx.config.*` or `package.json` (`genx` / `genesis` keys), `genx build` and `genx generate event-types` emit typed `EventDetailsMap` from Kotlin handler DTOs and generated table DAOs. See [@genesislcap/event-type-codegen](../event-type-codegen/README.md) for configuration, limitations, and migration.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"targetDir": "docs/agent-rules",
|
|
4
|
+
"rules": [
|
|
5
|
+
{
|
|
6
|
+
"file": "report-upstream-bug.md",
|
|
7
|
+
"name": "report-upstream-bug",
|
|
8
|
+
"title": "Report Foundation UI bugs upstream",
|
|
9
|
+
"description": "File a bug upstream in genesislcap/foundation-ui instead of working around it locally. Use whenever a defect is traced into a @genesislcap/* package - a stack frame in node_modules/@genesislcap, or behaviour contradicting the package's docs or types - or before writing any monkey-patch, patch-package entry, vendored copy, version pin, or cast that exists to route around upstream behaviour.",
|
|
10
|
+
"cursorDescription": "- When a bug is traced into a @genesislcap package, When considering a workaround for upstream behaviour, When patching node_modules or pinning a @genesislcap version"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
<!-- Managed by `genx agent-rules` (@genesislcap/genx). Do not edit this copy — it is overwritten on every run. Propose changes in genesislcap/foundation-ui. -->
|
|
2
|
+
|
|
3
|
+
# Report Foundation UI bugs upstream (don't silently work around them)
|
|
4
|
+
|
|
5
|
+
> **Purpose**: When a bug in this project turns out to live inside a `@genesislcap/*` package,
|
|
6
|
+
> the fix belongs in [`genesislcap/foundation-ui`](https://github.com/genesislcap/foundation-ui),
|
|
7
|
+
> not in a local patch. A workaround that is never reported keeps every other consumer project
|
|
8
|
+
> broken and quietly becomes permanent. This rule makes "raise it upstream" the default path and
|
|
9
|
+
> a local workaround the deliberate, approved, clearly-marked exception.
|
|
10
|
+
|
|
11
|
+
## When this rule applies
|
|
12
|
+
|
|
13
|
+
Apply it as soon as any of these is true:
|
|
14
|
+
|
|
15
|
+
- A stack frame, breakpoint, or console error points into `node_modules/@genesislcap/**`.
|
|
16
|
+
- The behaviour of a `@genesislcap/*` package contradicts its own docs, types, JSDoc, or
|
|
17
|
+
README — including a `CustomEvent<unknown>` where a typed detail is documented.
|
|
18
|
+
- You are about to write, or have caught yourself considering, any of these:
|
|
19
|
+
- a monkey-patch, prototype override, or `patch-package` entry against a `@genesislcap/*` package;
|
|
20
|
+
- a copy of an upstream component/function into this repo so a few lines can be changed;
|
|
21
|
+
- `as any` / `@ts-expect-error` / `// eslint-disable` purely to route around a wrong upstream type;
|
|
22
|
+
- a CSS override fighting an upstream style (`!important` into a shadow part, etc.);
|
|
23
|
+
- pinning or downgrading a `@genesislcap/*` version to dodge a regression;
|
|
24
|
+
- a `setTimeout`, extra re-render, retry, or "nudge" that exists only to compensate for
|
|
25
|
+
upstream timing.
|
|
26
|
+
|
|
27
|
+
It does **not** apply when the cause is on this side: wrong usage of a documented API, a version
|
|
28
|
+
mismatch between `@genesislcap/*` packages, missing peer dependencies, or local configuration.
|
|
29
|
+
Step 1 exists to tell those apart.
|
|
30
|
+
|
|
31
|
+
## Non-negotiables
|
|
32
|
+
|
|
33
|
+
1. **The upstream issue is filed before local code is changed** to route around the bug.
|
|
34
|
+
2. **Never** modify anything under `node_modules/` (or add a `patch-package` patch, or vendor a
|
|
35
|
+
copy of upstream source) without explicit human approval.
|
|
36
|
+
3. **Never** run `gh issue create` unprompted. Draft the issue, show it to the human, wait for a
|
|
37
|
+
clear yes, then file it. Filing an issue is an outward-facing action on a shared repo.
|
|
38
|
+
4. **Any workaround needs explicit human approval** — present the options and the trade-offs, do
|
|
39
|
+
not pick one and start editing (see Step 6).
|
|
40
|
+
5. **Be honest about what you verified.** Separate "confirmed by running it" from "suspected".
|
|
41
|
+
A confidently-wrong root cause wastes more upstream time than no root cause at all.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Step 1 — Rule out the boring causes
|
|
46
|
+
|
|
47
|
+
Do this before writing a single line of the issue.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# What is actually installed (the lockfile, not the range in package.json)?
|
|
51
|
+
npm ls @genesislcap/<package> # or: pnpm why @genesislcap/<package>
|
|
52
|
+
|
|
53
|
+
# Is it the latest? Is there a newer patch or a prerelease dist-tag?
|
|
54
|
+
npm view @genesislcap/<package> version
|
|
55
|
+
npm view @genesislcap/<package> dist-tags
|
|
56
|
+
|
|
57
|
+
# Are the @genesislcap/* packages on matching versions? Mixed majors/minors cause
|
|
58
|
+
# symptoms that look exactly like upstream bugs but are dependency drift.
|
|
59
|
+
npm ls --all 2>/dev/null | grep @genesislcap
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then confirm the calling code follows the documented API — read the package's own `dist/dts/*.d.ts`
|
|
63
|
+
and README rather than assuming. If the answer is "we were holding it wrong", fix it locally and
|
|
64
|
+
stop here; this rule is done.
|
|
65
|
+
|
|
66
|
+
## Step 2 — Check it isn't already known or already fixed
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Open AND closed issues — a closed one may name the release that fixed it.
|
|
70
|
+
gh issue list --repo genesislcap/foundation-ui --state all --search "<keywords>" --limit 20
|
|
71
|
+
|
|
72
|
+
# Same for PRs — a fix may be merged but not yet released.
|
|
73
|
+
gh pr list --repo genesislcap/foundation-ui --state all --search "<keywords>" --limit 20
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Outcomes:
|
|
77
|
+
|
|
78
|
+
- **An open issue matches** → don't file a duplicate. Add a comment with your new evidence
|
|
79
|
+
(your version, your repro, your environment) after the human approves, and link it in your report.
|
|
80
|
+
- **A closed issue matches and a newer version exists** → the fix is likely released. Recommend the
|
|
81
|
+
upgrade instead of an issue.
|
|
82
|
+
- **Nothing matches** → continue to Step 3.
|
|
83
|
+
|
|
84
|
+
## Step 3 — Collect the evidence
|
|
85
|
+
|
|
86
|
+
An upstream maintainer needs to reproduce this without access to your app. Gather:
|
|
87
|
+
|
|
88
|
+
| What | How |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| Root-cause package + exact version | The package the broken code lives in, e.g. `@genesislcap/foundation-comms@15.3.1` |
|
|
91
|
+
| Symptom package + exact version | Where it *appears* to break, if different, e.g. `@genesislcap/grid-pro@15.3.1` |
|
|
92
|
+
| The offending code | Real path + quoted snippet from `node_modules/@genesislcap/<pkg>/dist/...` |
|
|
93
|
+
| Minimal reproduction | Numbered steps. Say plainly whether you executed them or are proposing them |
|
|
94
|
+
| Expected vs actual | Both stated as observable behaviour, not as internal state |
|
|
95
|
+
| Environment | Framework + version (React 19, Angular, etc.), bundler, browser, and relevant third-party versions (`ag-grid-community`, `@microsoft/fast-element`, …) |
|
|
96
|
+
| Evidence | Console output, network/websocket trace, screenshots — trimmed to the relevant lines |
|
|
97
|
+
| Workaround status | What unblocks the app today (even if it's "nothing yet") |
|
|
98
|
+
|
|
99
|
+
Redact anything client-confidential: hostnames, credentials, real user data, and business-specific
|
|
100
|
+
resource names. Replace them with placeholders (`SOME_DATASERVER_QUERY`) — a repro does not need
|
|
101
|
+
them to be real.
|
|
102
|
+
|
|
103
|
+
## Step 4 — Draft the issue
|
|
104
|
+
|
|
105
|
+
Title: a specific, searchable one-liner that names the mechanism, not just the symptom. No
|
|
106
|
+
conventional-commit prefix.
|
|
107
|
+
|
|
108
|
+
- Good: `sendForStream() defers DATA_LOGON via DOM.queueUpdate — StrictMode double-mount discards it, leaving every dataserver grid empty`
|
|
109
|
+
- Bad: `Grid is empty`
|
|
110
|
+
|
|
111
|
+
Body — use this structure (it mirrors
|
|
112
|
+
[#2442](https://github.com/genesislcap/foundation-ui/issues/2442), a good reference report):
|
|
113
|
+
|
|
114
|
+
````markdown
|
|
115
|
+
## Package + version
|
|
116
|
+
|
|
117
|
+
Root cause: **`@genesislcap/<pkg>@<version>`**
|
|
118
|
+
Visible symptom in: **`@genesislcap/<pkg>@<version>`** (if different)
|
|
119
|
+
|
|
120
|
+
From the consumer's `package.json`:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
"@genesislcap/<pkg>": "<version>"
|
|
124
|
+
"<relevant third-party dep>": "<version>"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Summary
|
|
128
|
+
|
|
129
|
+
One paragraph: the mechanism and its user-visible consequence.
|
|
130
|
+
|
|
131
|
+
## Reproduction
|
|
132
|
+
|
|
133
|
+
State up front whether this was confirmed end-to-end or is a proposed reduction.
|
|
134
|
+
|
|
135
|
+
1. …
|
|
136
|
+
2. …
|
|
137
|
+
3. Observe: …
|
|
138
|
+
|
|
139
|
+
## Expected vs Actual
|
|
140
|
+
|
|
141
|
+
**Expected**
|
|
142
|
+
- …
|
|
143
|
+
|
|
144
|
+
**Actual**
|
|
145
|
+
- …
|
|
146
|
+
|
|
147
|
+
## Root cause
|
|
148
|
+
|
|
149
|
+
`<pkg>/dist/<path>`:
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
// the offending snippet, quoted
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Explain why this is wrong. If parts are inferred rather than observed, say so explicitly.
|
|
156
|
+
|
|
157
|
+
## Impact
|
|
158
|
+
|
|
159
|
+
Who and what is affected — one app, every consumer using this component, dev-only, production.
|
|
160
|
+
|
|
161
|
+
## Workaround in place downstream
|
|
162
|
+
|
|
163
|
+
What the consumer project is doing right now to stay unblocked, or "none".
|
|
164
|
+
|
|
165
|
+
## Environment
|
|
166
|
+
|
|
167
|
+
- Framework: …
|
|
168
|
+
- Browser / Node: …
|
|
169
|
+
- Other relevant versions: …
|
|
170
|
+
````
|
|
171
|
+
|
|
172
|
+
Show the draft to the human. Do not file it yet.
|
|
173
|
+
|
|
174
|
+
## Step 5 — File it (after the human says yes)
|
|
175
|
+
|
|
176
|
+
Write the body to a temp file first — passing markdown inline mangles backticks and special
|
|
177
|
+
characters:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
gh issue create \
|
|
181
|
+
--repo genesislcap/foundation-ui \
|
|
182
|
+
--title "<title>" \
|
|
183
|
+
--body-file /tmp/upstream-issue.md \
|
|
184
|
+
--label "Bug" \
|
|
185
|
+
--label "<area label>"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Pick area labels from the repo's own list — `gh label list --repo genesislcap/foundation-ui --limit 100`.
|
|
189
|
+
Common ones:
|
|
190
|
+
|
|
191
|
+
| Area | Label |
|
|
192
|
+
| --- | --- |
|
|
193
|
+
| grid-pro, data-grid, rapid-grid-pro | `Grids` |
|
|
194
|
+
| foundation-comms, datasources, websockets | `COMMS`, `Datasource` |
|
|
195
|
+
| design system, FAST, rapid components | `Design System`, `Rapid` |
|
|
196
|
+
| foundation-forms / criteria / filters | `Forms`, `Criteria` |
|
|
197
|
+
| foundation-layout, golden/dynamic layout | `Layout` |
|
|
198
|
+
| foundation-entity-management | `Entity Management` |
|
|
199
|
+
| React/Angular/Vue wrappers, typings | `Frontends Compatibility` |
|
|
200
|
+
| build, bundling, genx/CLI | `Build`, `Rollup & Webpack`, `CLI & GENX` |
|
|
201
|
+
| production-blocking | `High Priority`, or `CRITICAL` if the app is down |
|
|
202
|
+
|
|
203
|
+
Then delete the temp file, and report the issue URL back to the human.
|
|
204
|
+
|
|
205
|
+
## Step 6 — Workaround: only after the issue exists, only with approval
|
|
206
|
+
|
|
207
|
+
Filing the issue does not unblock this app. Once the issue URL exists:
|
|
208
|
+
|
|
209
|
+
1. **Present the options**, with the cost of each — e.g. upgrade/downgrade, use a different
|
|
210
|
+
documented API, local subclass/override, wait for the upstream fix, `patch-package`.
|
|
211
|
+
Include "do nothing yet" when it is viable.
|
|
212
|
+
2. **Wait for an explicit choice.** Do not start editing consumer code before that.
|
|
213
|
+
3. **Once approved**, keep it minimal and make it self-deleting on upgrade:
|
|
214
|
+
- Mark every touched site with a marker comment carrying the issue URL and the removal condition:
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
// UPSTREAM-WORKAROUND(genesislcap/foundation-ui#2442):
|
|
218
|
+
// foundation-comms defers DATA_LOGON via DOM.queueUpdate, so a StrictMode remount drops it.
|
|
219
|
+
// Remove when: @genesislcap/foundation-comms ships the fix (> 15.3.1) and this app upgrades.
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- Prefer wrapping over forking: a subclass, a decorator, or an adapter at one boundary beats a
|
|
223
|
+
vendored copy of upstream source scattered across the codebase.
|
|
224
|
+
- Never leave the workaround as the only record of the bug. The marker must name the issue.
|
|
225
|
+
4. **Log it** in a single tracking file the team can grep at upgrade time (create it if absent —
|
|
226
|
+
`docs/upstream-workarounds.md`):
|
|
227
|
+
|
|
228
|
+
````markdown
|
|
229
|
+
| Issue | Package + version | What we do locally | Remove when |
|
|
230
|
+
| --- | --- | --- | --- |
|
|
231
|
+
| genesislcap/foundation-ui#2442 | foundation-comms@15.3.1 | ... | fix released and adopted |
|
|
232
|
+
````
|
|
233
|
+
|
|
234
|
+
## Step 7 — Offer the upstream fix
|
|
235
|
+
|
|
236
|
+
If the fix is small and you can see it, say so in the issue and offer a PR. `foundation-ui` accepts
|
|
237
|
+
contributions from consumer teams — see its
|
|
238
|
+
[CONTRIBUTING.md](https://github.com/genesislcap/foundation-ui/blob/master/CONTRIBUTING.md) for
|
|
239
|
+
setup, commit format, and the PR template. A pre-release build can be cut from a branch so this app
|
|
240
|
+
can validate the fix before it lands (`@genesislcap/<pkg>@<dist-tag>`); ask in the issue.
|
|
241
|
+
|
|
242
|
+
## Never
|
|
243
|
+
|
|
244
|
+
- Edit `node_modules/` directly, or add a `patch-package` patch, without approval — and never
|
|
245
|
+
without an issue link in the patch's commit message.
|
|
246
|
+
- Present a suspected root cause as confirmed.
|
|
247
|
+
- Paste client-confidential data (hostnames, credentials, real records) into a shared repo.
|
|
248
|
+
- Delete, skip, or weaken a test to make an upstream bug invisible.
|
|
249
|
+
- Ship a workaround with no marker comment and no issue reference.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-rules.d.ts","sourceRoot":"","sources":["../../src/commands/agent-rules.ts"],"names":[],"mappings":"yBAuCE,eAAsB,EACtB,UAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO;AAFnD,wBAoDE"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const build_kit_1 = require("@genesislcap/build-kit");
|
|
7
|
+
const consola_1 = tslib_1.__importDefault(require("consola"));
|
|
8
|
+
const agent_rules_1 = require("../utils/agent-rules");
|
|
9
|
+
/** The packaged rules live at the root of this package, alongside `bin` and `dist`. */
|
|
10
|
+
const packagedRulesDir = () => (0, node_path_1.resolve)(__dirname, '../../agent-rules');
|
|
11
|
+
const loadManifest = (rulesDir) => JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(rulesDir, 'manifest.json'), 'utf8'));
|
|
12
|
+
const readRuleContents = (rulesDir, manifest) => manifest.rules.reduce((contents, rule) => {
|
|
13
|
+
contents[rule.file] = (0, node_fs_1.readFileSync)((0, node_path_1.join)(rulesDir, rule.file), 'utf8');
|
|
14
|
+
return contents;
|
|
15
|
+
}, {});
|
|
16
|
+
const resolveTools = (dir, requested) => {
|
|
17
|
+
if (!requested) {
|
|
18
|
+
return (0, agent_rules_1.detectTools)(dir);
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return (0, agent_rules_1.parseTools)(requested);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
// A typo in --tools is a user error, not a bug: report it without a stack trace.
|
|
25
|
+
consola_1.default.error(error.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
exports.default = (...args_1) => tslib_1.__awaiter(void 0, [...args_1], void 0, function* (folder = process.cwd(), options = {}) {
|
|
30
|
+
const dir = (0, node_path_1.resolve)(folder);
|
|
31
|
+
const rulesDir = packagedRulesDir();
|
|
32
|
+
const manifest = loadManifest(rulesDir);
|
|
33
|
+
const ruleContents = readRuleContents(rulesDir, manifest);
|
|
34
|
+
const tools = resolveTools(dir, options.tools);
|
|
35
|
+
if (!tools.length) {
|
|
36
|
+
consola_1.default.warn(`No assistant detected in ${dir}. The rule files will be installed, but nothing will be wired up. Pass --tools claude,cursor,gemini,agents to wire one explicitly.`);
|
|
37
|
+
}
|
|
38
|
+
const planned = (0, agent_rules_1.planFiles)(dir, manifest, tools, ruleContents, (path) => (0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
39
|
+
const drift = (0, agent_rules_1.findDrift)(dir, planned);
|
|
40
|
+
if (options.check) {
|
|
41
|
+
if (drift.length) {
|
|
42
|
+
consola_1.default.error(`Agent rules are out of date in ${dir}:`);
|
|
43
|
+
drift.forEach((file) => consola_1.default.log((0, build_kit_1.white)(` ${(0, build_kit_1.bold)(file.path)} ${(0, node_fs_1.existsSync)((0, node_path_1.join)(dir, file.path)) ? 'differs' : 'missing'}`)));
|
|
44
|
+
consola_1.default.info('Run `genx agent-rules` to update.');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
consola_1.default.success(`Agent rules are up to date (${manifest.rules.length} rule(s)).`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!drift.length) {
|
|
51
|
+
consola_1.default.info('Agent rules already up to date. Nothing to do.');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
drift.forEach((file) => {
|
|
55
|
+
const path = (0, node_path_1.join)(dir, file.path);
|
|
56
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
57
|
+
(0, node_fs_1.writeFileSync)(path, file.contents, 'utf8');
|
|
58
|
+
consola_1.default.log((0, build_kit_1.white)(` ${(0, build_kit_1.bold)(file.path)}`));
|
|
59
|
+
});
|
|
60
|
+
consola_1.default.success(`Installed ${manifest.rules.length} agent rule(s)${tools.length ? ` for: ${tools.join(', ')}` : ''}.`);
|
|
61
|
+
consola_1.default.info('Commit the changes so the whole team gets them.');
|
|
62
|
+
});
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export declare const commands: {
|
|
2
|
+
agentRules: () => Promise<(folder?: string, options?: {
|
|
3
|
+
tools?: string;
|
|
4
|
+
check?: boolean;
|
|
5
|
+
}) => Promise<void>>;
|
|
2
6
|
clean: () => Promise<(paths?: string[]) => Promise<void>>;
|
|
3
7
|
generateEventTypes: () => Promise<(folder?: string, options?: {
|
|
4
8
|
fromMetadata?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,QAAQ;;aAYgoB,CAAC;aAAe,CAAC;;;;oBAVvpB,CAAA;;;;;;;CAOd,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,QAAQ,CAAC"}
|
package/dist/commands/index.js
CHANGED
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.commands = void 0;
|
|
37
37
|
// lazy loading command definitions
|
|
38
38
|
exports.commands = {
|
|
39
|
+
agentRules: () => Promise.resolve().then(() => __importStar(require('./agent-rules'))).then((r) => r.default),
|
|
39
40
|
clean: () => Promise.resolve().then(() => __importStar(require('./clean'))).then((r) => r.default),
|
|
40
41
|
generateEventTypes: () => Promise.resolve().then(() => __importStar(require('./generate-event-types'))).then((r) => r.default),
|
|
41
42
|
run: () => Promise.resolve().then(() => __importStar(require('./run'))).then((r) => r.default),
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,23 @@ cli
|
|
|
42
42
|
consola_1.consola.error(`Unknown generate type "${type}". Supported: event-types`);
|
|
43
43
|
process.exit(1);
|
|
44
44
|
}));
|
|
45
|
+
cli
|
|
46
|
+
.command('agent-rules [folder]', 'Install shared AI agent rules into a project')
|
|
47
|
+
.option('-t, --tools <list>', 'Comma-separated assistants to wire: claude, cursor, gemini, agents (defaults to those detected)')
|
|
48
|
+
.option('-c, --check', 'Verify the installed rules are up to date, exit 1 otherwise (for CI)')
|
|
49
|
+
.action((folder, options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
50
|
+
const action = yield commands_1.commands.agentRules();
|
|
51
|
+
yield action(folder, options);
|
|
52
|
+
})).example(`
|
|
53
|
+
${(0, build_kit_1.bold)('genx agent-rules')}
|
|
54
|
+
Install the shared rules into docs/agent-rules and wire up every assistant detected in the project
|
|
55
|
+
|
|
56
|
+
${(0, build_kit_1.bold)('genx agent-rules --tools claude,cursor')}
|
|
57
|
+
Wire up Claude Code and Cursor explicitly
|
|
58
|
+
|
|
59
|
+
${(0, build_kit_1.bold)('genx agent-rules --check')}
|
|
60
|
+
Fail if the installed rules are missing or out of date (use in CI)
|
|
61
|
+
`);
|
|
45
62
|
cli
|
|
46
63
|
.command('clean [...paths]', 'Delete specified paths (defaults to dist folder)')
|
|
47
64
|
.action((paths) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shareable agent rules are markdown files that ship with this package and are installed into a
|
|
3
|
+
* consumer project by `genx agent-rules`. The rule body is tool-agnostic; per-assistant wiring is
|
|
4
|
+
* generated from the manifest below.
|
|
5
|
+
*/
|
|
6
|
+
export interface AgentRule {
|
|
7
|
+
/** File name of the rule markdown, relative to the packaged `agent-rules` folder. */
|
|
8
|
+
file: string;
|
|
9
|
+
/** Slug used for generated file names, e.g. the Cursor `.mdc`. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Human readable title, used in logs and generated pointers. */
|
|
12
|
+
title: string;
|
|
13
|
+
/** When the rule applies. Used as the Claude skill / pointer description. */
|
|
14
|
+
description: string;
|
|
15
|
+
/** Cursor's trigger phrasing for the `.mdc` front matter. */
|
|
16
|
+
cursorDescription: string;
|
|
17
|
+
}
|
|
18
|
+
export interface AgentRulesManifest {
|
|
19
|
+
version: number;
|
|
20
|
+
/** Where the rule markdown is installed inside the consumer project. */
|
|
21
|
+
targetDir: string;
|
|
22
|
+
rules: AgentRule[];
|
|
23
|
+
}
|
|
24
|
+
export declare const AGENT_TOOLS: readonly ["claude", "cursor", "gemini", "agents"];
|
|
25
|
+
export type AgentTool = (typeof AGENT_TOOLS)[number];
|
|
26
|
+
/** Entry file each assistant reads, for the tools wired via a managed block. */
|
|
27
|
+
export declare const TOOL_ENTRY_FILES: Record<Exclude<AgentTool, 'cursor'>, string>;
|
|
28
|
+
export declare const MANAGED_BEGIN = "<!-- BEGIN genx agent-rules -->";
|
|
29
|
+
export declare const MANAGED_END = "<!-- END genx agent-rules -->";
|
|
30
|
+
export interface PlannedFile {
|
|
31
|
+
/** Path relative to the consumer project root. */
|
|
32
|
+
path: string;
|
|
33
|
+
contents: string;
|
|
34
|
+
}
|
|
35
|
+
export type ExistsFn = (path: string) => boolean;
|
|
36
|
+
export type ReadFn = (path: string) => string;
|
|
37
|
+
/**
|
|
38
|
+
* Replaces the genx-managed block in `content`, or appends one when absent. Everything outside the
|
|
39
|
+
* markers is left alone — these files belong to the consumer project, not to us.
|
|
40
|
+
*/
|
|
41
|
+
export declare const upsertManagedBlock: (content: string, body: string) => string;
|
|
42
|
+
/** Import line understood by Claude Code (`CLAUDE.md`) and Gemini CLI (`GEMINI.md`). */
|
|
43
|
+
export declare const renderImportBlock: (rules: AgentRule[], targetDir: string) => string;
|
|
44
|
+
/** Plain pointer for assistants without an import syntax (AGENTS.md, Copilot, Windsurf, …). */
|
|
45
|
+
export declare const renderPointerBlock: (rules: AgentRule[], targetDir: string) => string;
|
|
46
|
+
/** A fully generated Cursor rule that defers to the shared markdown. */
|
|
47
|
+
export declare const renderCursorRule: (rule: AgentRule, targetDir: string) => string;
|
|
48
|
+
/**
|
|
49
|
+
* Which assistants a project already uses. Wiring an assistant nobody uses is just litter, so we
|
|
50
|
+
* only touch what we can see evidence of.
|
|
51
|
+
*/
|
|
52
|
+
export declare const detectTools: (dir: string, exists?: ExistsFn) => AgentTool[];
|
|
53
|
+
/** Parses and validates a `--tools` value. Throws on anything unknown rather than guessing. */
|
|
54
|
+
export declare const parseTools: (value: string) => AgentTool[];
|
|
55
|
+
/**
|
|
56
|
+
* Everything the command intends to write, as (path, contents) pairs. Kept separate from the
|
|
57
|
+
* writing itself so `--check` can compare against disk without touching it.
|
|
58
|
+
*/
|
|
59
|
+
export declare const planFiles: (dir: string, manifest: AgentRulesManifest, tools: AgentTool[], ruleContents: Record<string, string>, read: ReadFn, exists?: ExistsFn) => PlannedFile[];
|
|
60
|
+
/** Planned files whose contents differ from what is on disk. */
|
|
61
|
+
export declare const findDrift: (dir: string, planned: PlannedFile[], read?: ReadFn, exists?: ExistsFn) => PlannedFile[];
|
|
62
|
+
//# sourceMappingURL=agent-rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-rules.d.ts","sourceRoot":"","sources":["../../src/utils/agent-rules.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,mDAAoD,CAAC;AAC7E,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,CAIzE,CAAC;AAEF,eAAO,MAAM,aAAa,oCAAoC,CAAC;AAC/D,eAAO,MAAM,WAAW,kCAAkC,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;AACjD,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,EAAE,MAAM,MAAM,KAAG,MAgBlE,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,GAAI,OAAO,SAAS,EAAE,EAAE,WAAW,MAAM,KAAG,MAI5D,CAAC;AAEf,+FAA+F;AAC/F,eAAO,MAAM,kBAAkB,GAAI,OAAO,SAAS,EAAE,EAAE,WAAW,MAAM,KAAG,MAI7D,CAAC;AAEf,wEAAwE;AACxE,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,EAAE,WAAW,MAAM,KAAG,MAarE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,SAAQ,QAAqB,KAAG,SAAS,EAQjF,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,SAAS,EAYnD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,EACX,UAAU,kBAAkB,EAC5B,OAAO,SAAS,EAAE,EAClB,cAAc,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,MAAM,MAAM,EACZ,SAAQ,QAAqB,KAC5B,WAAW,EA6Bb,CAAC;AAEF,gEAAgE;AAChE,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,EACX,SAAS,WAAW,EAAE,EACtB,OAAM,MAA6C,EACnD,SAAQ,QAAqB,KAC5B,WAAW,EAIV,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findDrift = exports.planFiles = exports.parseTools = exports.detectTools = exports.renderCursorRule = exports.renderPointerBlock = exports.renderImportBlock = exports.upsertManagedBlock = exports.MANAGED_END = exports.MANAGED_BEGIN = exports.TOOL_ENTRY_FILES = exports.AGENT_TOOLS = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
exports.AGENT_TOOLS = ['claude', 'cursor', 'gemini', 'agents'];
|
|
7
|
+
/** Entry file each assistant reads, for the tools wired via a managed block. */
|
|
8
|
+
exports.TOOL_ENTRY_FILES = {
|
|
9
|
+
claude: 'CLAUDE.md',
|
|
10
|
+
gemini: 'GEMINI.md',
|
|
11
|
+
agents: 'AGENTS.md',
|
|
12
|
+
};
|
|
13
|
+
exports.MANAGED_BEGIN = '<!-- BEGIN genx agent-rules -->';
|
|
14
|
+
exports.MANAGED_END = '<!-- END genx agent-rules -->';
|
|
15
|
+
/**
|
|
16
|
+
* Replaces the genx-managed block in `content`, or appends one when absent. Everything outside the
|
|
17
|
+
* markers is left alone — these files belong to the consumer project, not to us.
|
|
18
|
+
*/
|
|
19
|
+
const upsertManagedBlock = (content, body) => {
|
|
20
|
+
const block = `${exports.MANAGED_BEGIN}\n${body.trim()}\n${exports.MANAGED_END}`;
|
|
21
|
+
const begin = content.indexOf(exports.MANAGED_BEGIN);
|
|
22
|
+
const end = content.indexOf(exports.MANAGED_END);
|
|
23
|
+
if (begin !== -1 && end > begin) {
|
|
24
|
+
const before = content.slice(0, begin);
|
|
25
|
+
const after = content.slice(end + exports.MANAGED_END.length);
|
|
26
|
+
return `${before}${block}${after}`;
|
|
27
|
+
}
|
|
28
|
+
if (!content.trim()) {
|
|
29
|
+
return `${block}\n`;
|
|
30
|
+
}
|
|
31
|
+
return `${content.replace(/\s*$/, '')}\n\n${block}\n`;
|
|
32
|
+
};
|
|
33
|
+
exports.upsertManagedBlock = upsertManagedBlock;
|
|
34
|
+
/** Import line understood by Claude Code (`CLAUDE.md`) and Gemini CLI (`GEMINI.md`). */
|
|
35
|
+
const renderImportBlock = (rules, targetDir) => [
|
|
36
|
+
'<!-- Installed by `genx agent-rules`. Re-run it to update; edits inside this block are lost. -->',
|
|
37
|
+
...rules.map((rule) => `@${targetDir}/${rule.file}`),
|
|
38
|
+
].join('\n');
|
|
39
|
+
exports.renderImportBlock = renderImportBlock;
|
|
40
|
+
/** Plain pointer for assistants without an import syntax (AGENTS.md, Copilot, Windsurf, …). */
|
|
41
|
+
const renderPointerBlock = (rules, targetDir) => [
|
|
42
|
+
'<!-- Installed by `genx agent-rules`. Re-run it to update; edits inside this block are lost. -->',
|
|
43
|
+
...rules.map((rule) => `- ${rule.description} Read and follow \`${targetDir}/${rule.file}\`.`),
|
|
44
|
+
].join('\n');
|
|
45
|
+
exports.renderPointerBlock = renderPointerBlock;
|
|
46
|
+
/** A fully generated Cursor rule that defers to the shared markdown. */
|
|
47
|
+
const renderCursorRule = (rule, targetDir) => `---
|
|
48
|
+
description: ${rule.cursorDescription}
|
|
49
|
+
alwaysApply: false
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
<!-- Installed by \`genx agent-rules\`. Re-run it to update; local edits are lost. -->
|
|
53
|
+
|
|
54
|
+
# ${rule.title}
|
|
55
|
+
|
|
56
|
+
@${targetDir}/${rule.file}
|
|
57
|
+
|
|
58
|
+
If this reference does not resolve, open \`${targetDir}/${rule.file}\` and follow it directly.
|
|
59
|
+
`;
|
|
60
|
+
exports.renderCursorRule = renderCursorRule;
|
|
61
|
+
/**
|
|
62
|
+
* Which assistants a project already uses. Wiring an assistant nobody uses is just litter, so we
|
|
63
|
+
* only touch what we can see evidence of.
|
|
64
|
+
*/
|
|
65
|
+
const detectTools = (dir, exists = node_fs_1.existsSync) => {
|
|
66
|
+
const markers = {
|
|
67
|
+
claude: ['CLAUDE.md', '.claude'],
|
|
68
|
+
cursor: ['.cursor', '.cursorrules'],
|
|
69
|
+
gemini: ['GEMINI.md', '.gemini'],
|
|
70
|
+
agents: ['AGENTS.md'],
|
|
71
|
+
};
|
|
72
|
+
return exports.AGENT_TOOLS.filter((tool) => markers[tool].some((marker) => exists((0, node_path_1.join)(dir, marker))));
|
|
73
|
+
};
|
|
74
|
+
exports.detectTools = detectTools;
|
|
75
|
+
/** Parses and validates a `--tools` value. Throws on anything unknown rather than guessing. */
|
|
76
|
+
const parseTools = (value) => {
|
|
77
|
+
const requested = value
|
|
78
|
+
.split(',')
|
|
79
|
+
.map((tool) => tool.trim().toLowerCase())
|
|
80
|
+
.filter(Boolean);
|
|
81
|
+
const unknown = requested.filter((tool) => !exports.AGENT_TOOLS.includes(tool));
|
|
82
|
+
if (unknown.length) {
|
|
83
|
+
throw new Error(`Unknown agent tool(s): ${unknown.join(', ')}. Supported: ${exports.AGENT_TOOLS.join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
return exports.AGENT_TOOLS.filter((tool) => requested.includes(tool));
|
|
86
|
+
};
|
|
87
|
+
exports.parseTools = parseTools;
|
|
88
|
+
/**
|
|
89
|
+
* Everything the command intends to write, as (path, contents) pairs. Kept separate from the
|
|
90
|
+
* writing itself so `--check` can compare against disk without touching it.
|
|
91
|
+
*/
|
|
92
|
+
const planFiles = (dir, manifest, tools, ruleContents, read, exists = node_fs_1.existsSync) => {
|
|
93
|
+
const { targetDir, rules } = manifest;
|
|
94
|
+
const planned = rules.map((rule) => ({
|
|
95
|
+
path: `${targetDir}/${rule.file}`,
|
|
96
|
+
contents: ruleContents[rule.file],
|
|
97
|
+
}));
|
|
98
|
+
tools.forEach((tool) => {
|
|
99
|
+
if (tool === 'cursor') {
|
|
100
|
+
rules.forEach((rule) => {
|
|
101
|
+
planned.push({
|
|
102
|
+
path: `.cursor/rules/${rule.name}.mdc`,
|
|
103
|
+
contents: (0, exports.renderCursorRule)(rule, targetDir),
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const entryFile = exports.TOOL_ENTRY_FILES[tool];
|
|
109
|
+
const entryPath = (0, node_path_1.join)(dir, entryFile);
|
|
110
|
+
const current = exists(entryPath) ? read(entryPath) : '';
|
|
111
|
+
const body = tool === 'agents'
|
|
112
|
+
? (0, exports.renderPointerBlock)(rules, targetDir)
|
|
113
|
+
: (0, exports.renderImportBlock)(rules, targetDir);
|
|
114
|
+
planned.push({ path: entryFile, contents: (0, exports.upsertManagedBlock)(current, body) });
|
|
115
|
+
});
|
|
116
|
+
return planned;
|
|
117
|
+
};
|
|
118
|
+
exports.planFiles = planFiles;
|
|
119
|
+
/** Planned files whose contents differ from what is on disk. */
|
|
120
|
+
const findDrift = (dir, planned, read = (path) => (0, node_fs_1.readFileSync)(path, 'utf8'), exists = node_fs_1.existsSync) => planned.filter((file) => {
|
|
121
|
+
const path = (0, node_path_1.join)(dir, file.path);
|
|
122
|
+
return !exists(path) || read(path) !== file.contents;
|
|
123
|
+
});
|
|
124
|
+
exports.findDrift = findDrift;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genesislcap/genx",
|
|
3
3
|
"description": "Genx CLI",
|
|
4
|
-
"version": "15.23.
|
|
4
|
+
"version": "15.23.1-agent-rules.1",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.0.0"
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"build": "npm run clean && tsc -b ./tsconfig.json",
|
|
11
11
|
"clean": "rimraf dist temp tsconfig.tsbuildinfo",
|
|
12
12
|
"dev": "tsc -b ./tsconfig.json -w",
|
|
13
|
+
"test": "genx test",
|
|
13
14
|
"lint": "genx lint -l ox",
|
|
14
15
|
"lint:fix": "genx lint -l ox --fix"
|
|
15
16
|
},
|
|
@@ -17,18 +18,21 @@
|
|
|
17
18
|
"genx": "./bin/genx"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"@genesislcap/build-kit": "15.23.
|
|
21
|
-
"@genesislcap/eslint-stylelint-builder": "15.23.
|
|
22
|
-
"@genesislcap/event-type-codegen": "15.23.
|
|
23
|
-
"@genesislcap/rollup-builder": "15.23.
|
|
24
|
-
"@genesislcap/ts-builder": "15.23.
|
|
25
|
-
"@genesislcap/uvu-playwright-builder": "15.23.
|
|
26
|
-
"@genesislcap/vite-builder": "15.23.
|
|
27
|
-
"@genesislcap/webpack-builder": "15.23.
|
|
21
|
+
"@genesislcap/build-kit": "15.23.1-agent-rules.1",
|
|
22
|
+
"@genesislcap/eslint-stylelint-builder": "15.23.1-agent-rules.1",
|
|
23
|
+
"@genesislcap/event-type-codegen": "15.23.1-agent-rules.1",
|
|
24
|
+
"@genesislcap/rollup-builder": "15.23.1-agent-rules.1",
|
|
25
|
+
"@genesislcap/ts-builder": "15.23.1-agent-rules.1",
|
|
26
|
+
"@genesislcap/uvu-playwright-builder": "15.23.1-agent-rules.1",
|
|
27
|
+
"@genesislcap/vite-builder": "15.23.1-agent-rules.1",
|
|
28
|
+
"@genesislcap/webpack-builder": "15.23.1-agent-rules.1",
|
|
28
29
|
"cac": "^6.7.14",
|
|
29
30
|
"consola": "^3.0.2",
|
|
30
31
|
"serve-handler": "^6.1.5"
|
|
31
32
|
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@genesislcap/foundation-testing": "15.23.1-agent-rules.1"
|
|
35
|
+
},
|
|
32
36
|
"repository": {
|
|
33
37
|
"type": "git",
|
|
34
38
|
"url": "git+https://github.com/genesislcap/foundation-ui.git",
|