fg-devkit 1.23.1 → 1.24.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 +15 -0
- package/configs/dependency-policy.vue-workspace.json +6 -6
- package/package.json +3 -5
- package/scaffold/vue/package.json +5 -5
- package/scripts/fg-devkit.mjs +18 -8
- package/scripts/setup-ai-rules.mjs +152 -0
- package/scripts/sync-node-toolchain.mjs +0 -1
- package/scripts/sync-pnpm-toolchain.mjs +0 -1
- package/.cursor/rules/core-coding-standards.mdc +0 -291
- package/scripts/sync-core-coding-standards.mjs +0 -62
package/README.md
CHANGED
|
@@ -89,12 +89,27 @@ To set up Lefthook git hooks:
|
|
|
89
89
|
pnpm exec fgd setup-lefthook
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
To use the shared AI rules from [feelgood/fg-ai-rules](https://github.com/feelgood/fg-ai-rules) in Claude Code:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pnpm exec fgd setup-ai-rules
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
It enables the `fg-core` and `fg-frontend` plugins in `.claude/settings.json`, adds the `fg` marketplace
|
|
99
|
+
to Claude Code if it is missing, and installs the plugins once for the user, disabled at user scope. The
|
|
100
|
+
repo's `.claude/settings.json` then switches them on, so every repo that commits that file gets the rules
|
|
101
|
+
and other repos don't. Each developer runs it once on their machine; commit `.claude/settings.json` in
|
|
102
|
+
each repo. Needs Claude Code and SSH access to `feelgood/fg-ai-rules`.
|
|
103
|
+
|
|
92
104
|
Run full consumer setup in one command:
|
|
93
105
|
|
|
94
106
|
```bash
|
|
95
107
|
pnpm exec fgd full-consumer-setup
|
|
96
108
|
```
|
|
97
109
|
|
|
110
|
+
It syncs the consumer scripts, Lefthook, configs and dependency policy, runs `pnpm install`, and finishes with
|
|
111
|
+
`setup-ai-rules`. Without Claude Code that last step only prints a warning.
|
|
112
|
+
|
|
98
113
|
## Dependency policy update workflow
|
|
99
114
|
|
|
100
115
|
Check outdated policy versions:
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@lexical/link": "0.52.0",
|
|
19
19
|
"@piwikpro/vue-piwik-pro": "2.0.0",
|
|
20
20
|
"@sentry/vue": "11.7.0",
|
|
21
|
-
"@tanstack/vue-query": "5.104.
|
|
21
|
+
"@tanstack/vue-query": "5.104.3",
|
|
22
22
|
"@types/leaflet": "1.9.22",
|
|
23
23
|
"@unhead/vue": "2.0.5",
|
|
24
24
|
"@vue-leaflet/vue-leaflet": "0.10.1",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"lodash-es": "4.18.1",
|
|
39
39
|
"markdown-it": "15.0.2",
|
|
40
40
|
"pinia": "4.1.0",
|
|
41
|
-
"playwright": "1.64.
|
|
41
|
+
"playwright": "1.64.1",
|
|
42
42
|
"quill": "2.0.3",
|
|
43
43
|
"randexp": "0.5.3",
|
|
44
44
|
"register-service-worker": "1.7.2",
|
|
@@ -56,14 +56,14 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@chromatic-com/storybook": "5.5.1",
|
|
58
58
|
"@eslint/js": "9.39.1",
|
|
59
|
-
"@intlify/unplugin-vue-i18n": "11.2.
|
|
60
|
-
"@playwright/test": "1.64.
|
|
59
|
+
"@intlify/unplugin-vue-i18n": "11.2.6",
|
|
60
|
+
"@playwright/test": "1.64.1",
|
|
61
61
|
"@storybook/addon-a11y": "10.6.2",
|
|
62
62
|
"@storybook/addon-docs": "10.6.2",
|
|
63
63
|
"@storybook/addon-vitest": "10.6.2",
|
|
64
64
|
"@storybook/vue3": "10.6.2",
|
|
65
65
|
"@storybook/vue3-vite": "10.6.2",
|
|
66
|
-
"@tanstack/vue-query-devtools": "6.3.
|
|
66
|
+
"@tanstack/vue-query-devtools": "6.3.3",
|
|
67
67
|
"@tsconfig/node22": "22.0.6",
|
|
68
68
|
"@types/lodash-es": "4.17.12",
|
|
69
69
|
"@types/node": "22.16.5",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"@tailwindcss/vite": "4.3.3",
|
|
93
93
|
"@vitejs/plugin-vue": "6.0.10",
|
|
94
94
|
"axios": "1.20.0",
|
|
95
|
-
"dotenv": "18.0.
|
|
95
|
+
"dotenv": "18.0.8",
|
|
96
96
|
"qrcode.vue": "3.11.0",
|
|
97
97
|
"tailwindcss": "4.3.3",
|
|
98
98
|
"vite": "8.3.4",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fg-devkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.1",
|
|
4
4
|
"description": "Shared configs, scripts, and DX tooling for Vue / TypeScript frontend repos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -26,8 +26,7 @@
|
|
|
26
26
|
"./release-notes/format": "./release-notes/format.ts",
|
|
27
27
|
"./release-notes/types": "./release-notes/types.ts",
|
|
28
28
|
"./configs/cliff.toml": "./configs/cliff.toml",
|
|
29
|
-
"./configs/cliff.user.toml": "./configs/cliff.user.toml"
|
|
30
|
-
"./cursor-rules/core-coding-standards.mdc": "./.cursor/rules/core-coding-standards.mdc"
|
|
29
|
+
"./configs/cliff.user.toml": "./configs/cliff.user.toml"
|
|
31
30
|
},
|
|
32
31
|
"files": [
|
|
33
32
|
"configs",
|
|
@@ -36,8 +35,7 @@
|
|
|
36
35
|
"!**/node_modules",
|
|
37
36
|
"!**/node_modules/**",
|
|
38
37
|
"scripts",
|
|
39
|
-
"tsconfig.json"
|
|
40
|
-
".cursor/rules/core-coding-standards.mdc"
|
|
38
|
+
"tsconfig.json"
|
|
41
39
|
],
|
|
42
40
|
"engines": {
|
|
43
41
|
"node": ">=22.13.0"
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@tailwindcss/vite": "4.3.3",
|
|
19
|
-
"@tanstack/vue-query": "5.104.
|
|
19
|
+
"@tanstack/vue-query": "5.104.3",
|
|
20
20
|
"axios": "1.20.0",
|
|
21
21
|
"content-disposition-header": "0.6.0",
|
|
22
22
|
"date-fns": "4.4.0",
|
|
23
|
-
"dotenv": "18.0.
|
|
23
|
+
"dotenv": "18.0.8",
|
|
24
24
|
"fds-vue-core": "8.11.0",
|
|
25
25
|
"pinia": "4.1.0",
|
|
26
26
|
"qrcode.vue": "3.11.0",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"vue-router": "5.4.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@intlify/unplugin-vue-i18n": "11.2.
|
|
33
|
-
"@tanstack/vue-query-devtools": "6.3.
|
|
32
|
+
"@intlify/unplugin-vue-i18n": "11.2.6",
|
|
33
|
+
"@tanstack/vue-query-devtools": "6.3.3",
|
|
34
34
|
"@types/node": "22.16.5",
|
|
35
|
-
"fg-devkit": "1.
|
|
35
|
+
"fg-devkit": "1.24.1",
|
|
36
36
|
"@vitejs/plugin-vue": "6.0.10",
|
|
37
37
|
"tailwindcss": "4.3.3",
|
|
38
38
|
"tsx": "4.23.15",
|
package/scripts/fg-devkit.mjs
CHANGED
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
* pnpm exec fg-devkit sync-tooling-configs (tooling sync only; overwrites config files)
|
|
24
24
|
* pnpm exec fg-devkit sync-dependency-check-suppressions-commit [--all]
|
|
25
25
|
* pnpm exec fg-devkit sync-vscode-settings
|
|
26
|
-
* pnpm exec fg-devkit sync-core-coding-standards
|
|
27
26
|
* pnpm exec fg-devkit sync-consumer-scripts
|
|
28
27
|
* pnpm exec fg-devkit setup-lefthook
|
|
29
28
|
* pnpm exec fg-devkit full-consumer-setup
|
|
@@ -165,6 +164,7 @@ function runSetupLefthook() {
|
|
|
165
164
|
* - Step 3: sync project configs
|
|
166
165
|
* - Step 2: sync dependency policy
|
|
167
166
|
* - Then run pnpm install
|
|
167
|
+
* - Set up the fg-ai-rules Claude Code plugins (a failure only warns, e.g. without Claude Code)
|
|
168
168
|
*/
|
|
169
169
|
function runFullConsumerSetup() {
|
|
170
170
|
console.log('[fg-devkit] Running full consumer sync and setup')
|
|
@@ -184,7 +184,13 @@ function runFullConsumerSetup() {
|
|
|
184
184
|
const step2 = runNode('sync-dependency-policy.mjs')
|
|
185
185
|
if (step2 !== 0) return step2
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
const installStep = runPnpmInstall()
|
|
188
|
+
if (installStep !== 0) return installStep
|
|
189
|
+
|
|
190
|
+
if (runNode('setup-ai-rules.mjs') !== 0) {
|
|
191
|
+
console.warn('[fg-devkit] AI rules were not set up. Run "fgd setup-ai-rules" when Claude Code is available.')
|
|
192
|
+
}
|
|
193
|
+
return 0
|
|
188
194
|
}
|
|
189
195
|
|
|
190
196
|
/** Maintainer flow: run full repo vulnerability audit in fg-devkit root. */
|
|
@@ -471,7 +477,6 @@ function runSyncConfigTarget(target) {
|
|
|
471
477
|
if (target === 'prettier') return syncFileFromTemplate('configs/prettier.sync-stub.js', 'prettier.config.js')
|
|
472
478
|
if (target === 'tsconfig') return syncFileFromTemplate('configs/tsconfig.app.sync.json', 'tsconfig.json')
|
|
473
479
|
if (target === 'dependency-check') return runNode('sync-dependency-check-suppressions-commit.mjs')
|
|
474
|
-
if (target === 'cursor') return runNode('sync-core-coding-standards.mjs')
|
|
475
480
|
if (target === 'vue-inspector') return runNode('sync-vite-plugin-vue-inspector.mjs')
|
|
476
481
|
if (target === 'all') return runNode('sync-project-configs.mjs')
|
|
477
482
|
console.error(`[fg-devkit] Unknown config sync target: ${target}`)
|
|
@@ -504,9 +509,9 @@ const DIRECT_COMMANDS = {
|
|
|
504
509
|
'sync-tooling-configs': 'sync-tooling-configs.mjs',
|
|
505
510
|
'sync-dependency-check-suppressions-commit': 'sync-dependency-check-suppressions-commit.mjs',
|
|
506
511
|
'sync-vscode-settings': 'sync-vscode-settings.mjs',
|
|
507
|
-
'sync-core-coding-standards': 'sync-core-coding-standards.mjs',
|
|
508
512
|
'sync-vite-plugin-vue-inspector': 'sync-vite-plugin-vue-inspector.mjs',
|
|
509
513
|
'sync-consumer-scripts': 'sync-consumer-scripts.mjs',
|
|
514
|
+
'setup-ai-rules': 'setup-ai-rules.mjs',
|
|
510
515
|
'sync-release-notes-config': 'sync-release-notes-config.mjs',
|
|
511
516
|
'generate-release-notes': 'generate-release-notes.mjs',
|
|
512
517
|
'verify-release-notes': 'verify-release-notes.mjs',
|
|
@@ -569,11 +574,11 @@ Commands:
|
|
|
569
574
|
sync-dependency-check-suppressions-commit [--all]
|
|
570
575
|
Write the dependency-check suppressions that match pnpm audit findings and auto-commit (--all: copy every entry)
|
|
571
576
|
sync-vscode-settings Copy shared VS Code settings into .vscode/settings.json
|
|
572
|
-
sync-core-coding-standards Copy .cursor core coding standards with consumer package name
|
|
573
577
|
sync-vite-plugin-vue-inspector Add/pin vite-plugin-vue-inspector, wire vite.config, install, commit
|
|
574
578
|
sync-consumer-scripts Enforce standard fgd scripts in consumer package.json
|
|
575
579
|
setup-lefthook Write lefthook.yml and install the git hooks
|
|
576
|
-
|
|
580
|
+
setup-ai-rules Enable the fg-ai-rules Claude Code plugins in this repo, install them once per machine
|
|
581
|
+
full-consumer-setup Run: scripts -> lefthook -> step 3 -> step 2 -> pnpm install -> AI rules
|
|
577
582
|
create-release [release|candidate] [patch|minor|major] [--skip-release-notes]
|
|
578
583
|
Stable release or candidate (X.Y.Z-rc.N); omit = interactive menu
|
|
579
584
|
(alias: createRelease)
|
|
@@ -1061,7 +1066,6 @@ async function runInteractiveMenu() {
|
|
|
1061
1066
|
value: 'dependency-check',
|
|
1062
1067
|
description: 'Write the suppressions that match pnpm audit findings',
|
|
1063
1068
|
},
|
|
1064
|
-
{ name: 'Cursor rules', value: 'cursor', description: 'Write .cursor/rules/core-coding-standards.mdc' },
|
|
1065
1069
|
{
|
|
1066
1070
|
name: autoCommit('Vue inspector'),
|
|
1067
1071
|
value: 'vue-inspector',
|
|
@@ -1086,10 +1090,15 @@ async function runInteractiveMenu() {
|
|
|
1086
1090
|
value: 'lefthook',
|
|
1087
1091
|
description: 'Write lefthook.yml and install the git hooks',
|
|
1088
1092
|
},
|
|
1093
|
+
{
|
|
1094
|
+
name: 'AI rules',
|
|
1095
|
+
value: 'ai-rules',
|
|
1096
|
+
description: 'Enable the fg-ai-rules plugins in .claude/settings.json and install them for Claude Code',
|
|
1097
|
+
},
|
|
1089
1098
|
{
|
|
1090
1099
|
name: 'Full setup',
|
|
1091
1100
|
value: 'full-consumer-setup',
|
|
1092
|
-
description: 'Sync scripts, Lefthook, configs and dependency policy, then
|
|
1101
|
+
description: 'Sync scripts, Lefthook, configs and dependency policy, pnpm install, then set up AI rules',
|
|
1093
1102
|
},
|
|
1094
1103
|
{ name: 'Cancel', value: 'cancel' },
|
|
1095
1104
|
],
|
|
@@ -1097,6 +1106,7 @@ async function runInteractiveMenu() {
|
|
|
1097
1106
|
loop: false,
|
|
1098
1107
|
})
|
|
1099
1108
|
if (choice === 'cancel') continue
|
|
1109
|
+
if (choice === 'ai-rules') return runSelectedAction(() => runNode('setup-ai-rules.mjs'))
|
|
1100
1110
|
return runSelectedAction(() => (choice === 'lefthook' ? runSetupLefthook() : runFullConsumerSetup()))
|
|
1101
1111
|
}
|
|
1102
1112
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Set up the shared AI rules from feelgood/fg-ai-rules for Claude Code in the current repo:
|
|
4
|
+
*
|
|
5
|
+
* 1. Add the `fg` marketplace and enable fg-core and fg-frontend in .claude/settings.json,
|
|
6
|
+
* keeping every other setting.
|
|
7
|
+
* 2. Add the marketplace to Claude Code on this machine if it is missing.
|
|
8
|
+
* 3. Install the plugins once for the user (`--scope user`) and disable them at user scope.
|
|
9
|
+
* Claude Code doesn't install plugins from a repo's settings file on its own, and a
|
|
10
|
+
* project-scope install only covers one repo. A user-scope install covers every repo, and
|
|
11
|
+
* disabling it at user scope leaves the repo's .claude/settings.json to switch it on.
|
|
12
|
+
*
|
|
13
|
+
* Safe to run again: it only changes what is missing. Once a developer has run it in any repo,
|
|
14
|
+
* every other repo that commits the same .claude/settings.json works without running it again.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* pnpm exec fg-devkit setup-ai-rules
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { spawnSync } from 'node:child_process'
|
|
21
|
+
import fs from 'node:fs'
|
|
22
|
+
import path from 'node:path'
|
|
23
|
+
|
|
24
|
+
const TAG = '[setup-ai-rules]'
|
|
25
|
+
const MARKETPLACE = 'fg'
|
|
26
|
+
const MARKETPLACE_REPO = 'feelgood/fg-ai-rules'
|
|
27
|
+
const PLUGINS = ['fg-core', 'fg-frontend']
|
|
28
|
+
|
|
29
|
+
const projectRoot = fs.realpathSync(process.cwd())
|
|
30
|
+
const settingsPath = path.join(projectRoot, '.claude', 'settings.json')
|
|
31
|
+
|
|
32
|
+
function claude(args, { capture = false } = {}) {
|
|
33
|
+
const result = spawnSync('claude', args, {
|
|
34
|
+
cwd: projectRoot,
|
|
35
|
+
encoding: 'utf8',
|
|
36
|
+
stdio: capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',
|
|
37
|
+
})
|
|
38
|
+
if (result.error) return { status: 1, stdout: '', missing: result.error.code === 'ENOENT' }
|
|
39
|
+
return { status: result.status ?? 1, stdout: result.stdout ?? '' }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Last JSON line of a command that prints one result line with --json. */
|
|
43
|
+
function claudeJsonLine(args) {
|
|
44
|
+
const lines = claude([...args, '--json'], { capture: true }).stdout.trim().split('\n')
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(lines.at(-1))
|
|
47
|
+
} catch {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function claudeJson(args) {
|
|
53
|
+
const result = claude([...args, '--json'], { capture: true })
|
|
54
|
+
if (result.status !== 0) return null
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(result.stdout)
|
|
57
|
+
} catch {
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Add the marketplace and enabled plugins to .claude/settings.json. Returns true when it changed. */
|
|
63
|
+
function updateSettings() {
|
|
64
|
+
let settings = {}
|
|
65
|
+
if (fs.existsSync(settingsPath)) {
|
|
66
|
+
try {
|
|
67
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'))
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error(`${TAG} Could not parse ${settingsPath}: ${err.message}`)
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const before = JSON.stringify(settings)
|
|
74
|
+
settings.extraKnownMarketplaces = {
|
|
75
|
+
...settings.extraKnownMarketplaces,
|
|
76
|
+
[MARKETPLACE]: { source: { source: 'github', repo: MARKETPLACE_REPO }, autoUpdate: true },
|
|
77
|
+
}
|
|
78
|
+
settings.enabledPlugins = {
|
|
79
|
+
...settings.enabledPlugins,
|
|
80
|
+
...Object.fromEntries(PLUGINS.map((plugin) => [`${plugin}@${MARKETPLACE}`, true])),
|
|
81
|
+
}
|
|
82
|
+
if (JSON.stringify(settings) === before) return false
|
|
83
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true })
|
|
84
|
+
fs.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`)
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function printManualSteps() {
|
|
89
|
+
console.log(`${TAG} Run these from the repo root, then restart Claude Code:`)
|
|
90
|
+
console.log(` claude plugin marketplace add ${MARKETPLACE_REPO}`)
|
|
91
|
+
for (const plugin of PLUGINS) {
|
|
92
|
+
console.log(` claude plugin install ${plugin}@${MARKETPLACE} --scope user`)
|
|
93
|
+
console.log(` claude plugin disable ${plugin}@${MARKETPLACE} --scope user`)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function main() {
|
|
98
|
+
if (!fs.existsSync(path.join(projectRoot, 'package.json'))) {
|
|
99
|
+
console.error(`${TAG} Run from the repo root (no package.json in ${projectRoot}).`)
|
|
100
|
+
return 1
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const settingsChanged = updateSettings()
|
|
104
|
+
console.log(`${TAG} ${settingsChanged ? 'Updated' : 'Already up to date:'} .claude/settings.json`)
|
|
105
|
+
|
|
106
|
+
const marketplaces = claudeJson(['plugin', 'marketplace', 'list'])
|
|
107
|
+
if (marketplaces === null) {
|
|
108
|
+
const missing = claude(['--version'], { capture: true }).missing
|
|
109
|
+
console.error(`${TAG} ${missing ? 'Claude Code (claude) is not installed.' : 'Could not read Claude Code plugins.'}`)
|
|
110
|
+
printManualSteps()
|
|
111
|
+
return 1
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!marketplaces.some((marketplace) => marketplace.name === MARKETPLACE)) {
|
|
115
|
+
console.log(`${TAG} Adding the ${MARKETPLACE} marketplace (${MARKETPLACE_REPO})…`)
|
|
116
|
+
if (claude(['plugin', 'marketplace', 'add', MARKETPLACE_REPO]).status !== 0) {
|
|
117
|
+
console.error(`${TAG} Could not add the marketplace. Check that you can clone ${MARKETPLACE_REPO} over SSH.`)
|
|
118
|
+
return 1
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const installed = claudeJson(['plugin', 'list']) ?? []
|
|
123
|
+
let failed = false
|
|
124
|
+
for (const plugin of PLUGINS) {
|
|
125
|
+
const id = `${plugin}@${MARKETPLACE}`
|
|
126
|
+
if (installed.some((entry) => entry.id === id && entry.scope === 'user')) {
|
|
127
|
+
console.log(`${TAG} ${plugin} is already installed.`)
|
|
128
|
+
} else {
|
|
129
|
+
console.log(`${TAG} Installing ${plugin}…`)
|
|
130
|
+
if (claude(['plugin', 'install', id, '--scope', 'user']).status !== 0) {
|
|
131
|
+
failed = true
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Off at user scope, so only repos whose .claude/settings.json enables it load it.
|
|
136
|
+
const disabled = claudeJsonLine(['plugin', 'disable', id, '--scope', 'user'])
|
|
137
|
+
if (!disabled || (disabled.outcome !== 'ok' && !disabled.alreadyInGoalState)) {
|
|
138
|
+
console.error(`${TAG} Could not disable ${plugin} at user scope: ${disabled?.message ?? 'unknown error'}`)
|
|
139
|
+
failed = true
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (failed) {
|
|
143
|
+
printManualSteps()
|
|
144
|
+
return 1
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log(`${TAG} Done. Restart Claude Code to load the rules.`)
|
|
148
|
+
if (settingsChanged) console.log(`${TAG} Commit .claude/settings.json so other repos and teammates get the same plugins.`)
|
|
149
|
+
return 0
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
process.exit(main())
|
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Core coding standards for Vue / fds-vue-core frontend projects
|
|
3
|
-
alwaysApply: true
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Core Coding Standards
|
|
7
|
-
|
|
8
|
-
## Keep changes focused
|
|
9
|
-
|
|
10
|
-
- Make the smallest safe change that solves the request.
|
|
11
|
-
- Avoid unrelated refactors in the same task.
|
|
12
|
-
- Preserve existing behavior unless a behavior change is requested.
|
|
13
|
-
|
|
14
|
-
## Code comments
|
|
15
|
-
|
|
16
|
-
- Do not add comments in code unless the user explicitly asks for them.
|
|
17
|
-
- Prefer self-explanatory naming and structure over inline comments.
|
|
18
|
-
- When updating existing code, do not add new comments or expand existing ones unless requested.
|
|
19
|
-
- Exception: update comments only when they are required to keep an existing comment accurate after a behavior change in the same task.
|
|
20
|
-
|
|
21
|
-
## Communication efficiency
|
|
22
|
-
|
|
23
|
-
- Do not waste tokens: keep responses concise, high-signal, and scoped to the request.
|
|
24
|
-
- Prefer short summaries over long explanations unless deeper detail is explicitly requested.
|
|
25
|
-
|
|
26
|
-
## Evidence-first implementation
|
|
27
|
-
|
|
28
|
-
- Never guess or hallucinate APIs, component props, or framework behavior.
|
|
29
|
-
- Verify implementation details against authoritative sources first (official docs, project code, and library types/source).
|
|
30
|
-
- If documentation is missing or ambiguous, state uncertainty explicitly and choose the safest validated approach.
|
|
31
|
-
|
|
32
|
-
## Match project conventions
|
|
33
|
-
|
|
34
|
-
- Follow established naming, file structure, and component patterns.
|
|
35
|
-
- Reuse existing utilities and query/composable patterns before adding new ones.
|
|
36
|
-
- Keep TypeScript types explicit at API boundaries.
|
|
37
|
-
|
|
38
|
-
## Strict TypeScript
|
|
39
|
-
|
|
40
|
-
- Keep TypeScript and Vue template typing strict (`strict: true`, `strictTemplates: true`).
|
|
41
|
-
- Do not introduce `any`; use `unknown` for unsafe input and narrow before use.
|
|
42
|
-
- Type API boundaries explicitly (request payloads, response DTOs, mutation variables, callback contracts).
|
|
43
|
-
- Prefer named reusable types under `src/types/<domain>/` for shared domain shapes; re-export from `src/types/index.ts`. Do not keep shared interfaces in views, stores, or query files.
|
|
44
|
-
- Avoid non-null assertions unless guarded by `enabled` checks or equivalent runtime guarantees.
|
|
45
|
-
|
|
46
|
-
## API boundaries
|
|
47
|
-
|
|
48
|
-
- Components and views should not call low-level HTTP/API clients directly for feature data fetching.
|
|
49
|
-
- Centralize HTTP and endpoint wiring in a dedicated API layer (for example `src/api/endpoints/*` or the convention used in the repo).
|
|
50
|
-
- Consume server state through query/mutation hooks (for example `src/queries/*` or equivalent), not ad-hoc fetches in UI code.
|
|
51
|
-
|
|
52
|
-
## Query strings
|
|
53
|
-
|
|
54
|
-
- Build query strings with `URLSearchParams` (or a shared helper that wraps it).
|
|
55
|
-
- Do not concatenate `key=value` fragments with `&`.
|
|
56
|
-
- Let `URLSearchParams` encode values. Do not interpolate untrusted or user-entered values into URLs.
|
|
57
|
-
- `toString()` has no leading `?`. Prefix it when appending to a path, and skip empty values instead of emitting empty keys.
|
|
58
|
-
- For list views, derive the query string in the view from route filter state (`requestQueryParams` computed). The API layer should accept the query string, not reconstruct filters.
|
|
59
|
-
|
|
60
|
-
## State-management boundaries
|
|
61
|
-
|
|
62
|
-
- Use TanStack Query for server state and caching. Do not add Vuex for new work.
|
|
63
|
-
- Use Pinia only for client/app state that is not owned by the server (for example `src/pinia/*`).
|
|
64
|
-
- Do not duplicate the same state across the query cache and Pinia unless the project explicitly documents that pattern.
|
|
65
|
-
- When a project is migrating Vuex → Query/Pinia, follow that repo’s local migration rules (if present) instead of expanding Vuex.
|
|
66
|
-
|
|
67
|
-
## TanStack Query server-state conventions
|
|
68
|
-
|
|
69
|
-
- Use TanStack Query for server state; avoid duplicating server data in ad-hoc local state.
|
|
70
|
-
- Keep API calls in the API layer and expose query/mutation hooks from a dedicated hooks folder.
|
|
71
|
-
- Build query keys as stable arrays of serializable primitives; when using refs, key by `.value` in a computed key.
|
|
72
|
-
- Prefer shared query key constants/factories to keep invalidation and cache updates consistent.
|
|
73
|
-
- Add `enabled` guards for required IDs/paths to prevent invalid requests.
|
|
74
|
-
- Use `placeholderData: keepPreviousData` for paginated/filter-driven views to avoid UI flicker.
|
|
75
|
-
- Set `staleTime` intentionally per resource; keep `refetchOnWindowFocus` off unless fresh-on-focus behavior is required.
|
|
76
|
-
- On mutation success, update cache intentionally (`setQueryData`) and/or invalidate the narrowest matching keys.
|
|
77
|
-
- Respect shared query client retry behavior and only override retries when endpoint-specific behavior is needed.
|
|
78
|
-
|
|
79
|
-
### Query loading UI
|
|
80
|
-
|
|
81
|
-
TanStack Query v5 flags (do not treat them as interchangeable):
|
|
82
|
-
|
|
83
|
-
| Flag | Meaning |
|
|
84
|
-
| --- | --- |
|
|
85
|
-
| `isPending` | No cached data yet (`status === 'pending'`) |
|
|
86
|
-
| `isFetching` | Any in-flight request, including background refetch |
|
|
87
|
-
| `isLoading` | First fetch in flight (`isPending && isFetching`) |
|
|
88
|
-
|
|
89
|
-
Rules:
|
|
90
|
-
|
|
91
|
-
1. **Content-blocking / full-page spinner** — use `isLoading` (or combine several `isLoading*` flags). Prefer `isLoading` over bare `isPending` when the query can be `enabled: false` (lazy/disabled queries stay `isPending` without fetching).
|
|
92
|
-
2. **Do not use bare `isFetching` for blocking UI** — it re-shows the full loader on every background refetch. Use `isFetching` only for subtle “refreshing” indicators while keeping content visible.
|
|
93
|
-
3. **Paginated / `keepPreviousData` lists** — show a hard loader only when there is nothing useful to render:
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
const isLoading = computed(() => isPending.value || (isFetching.value && !data.value))
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
4. **Multiple queries for one screen** — combine with OR into one (or purpose-prefixed) computed:
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
const isLoading = computed(() => isLoadingDetails.value || isLoadingList.value)
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
5. **No passthrough aliases** — do not wrap a single flag (`const loading = computed(() => isFetching.value)`). Destructure and rename instead:
|
|
106
|
-
|
|
107
|
-
```ts
|
|
108
|
-
const { data, isLoading, error } = useGetDetails(id)
|
|
109
|
-
// template: v-show="isLoading"
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
6. **Naming** — local query-derived booleans use the `is*` prefix: `isLoading`, `isLoadingUnits`, `isLoadingHistory`. Keep FDS/component prop names as-is (`:loading="isLoading"`). Mutations: rename `isPending` to intent (`isSaving`, `isSubmitting`, `isUploading`).
|
|
113
|
-
|
|
114
|
-
- TanStack Query boolean flags (`isPending`, `isSuccess`, `isError`) are typed `Ref<false> | Ref<true>`. `vue-tsc` unwraps that union in templates to `boolean`; the Vue language plugin may not.
|
|
115
|
-
- Narrow in script (`const isPending = mutation.isPending as Ref<boolean>`), then bind without `.value` (`:loading="isPending"`). Do not use `.value` in templates (fails `vue-tsc`) and do not add a wrapping `computed` only to satisfy the editor.
|
|
116
|
-
|
|
117
|
-
## Mutation cache behavior
|
|
118
|
-
|
|
119
|
-
- Every mutation should define post-success cache behavior.
|
|
120
|
-
- Use `invalidateQueries` for stale collections and `setQueryData` for direct entity updates.
|
|
121
|
-
- Invalidate the narrowest query keys that match changed resources.
|
|
122
|
-
|
|
123
|
-
## Vue Router conventions
|
|
124
|
-
|
|
125
|
-
- Organize route definitions in modules (for example `src/router/modules/*`) composed from a single router entry (for example `src/router/index.ts`), matching the repo layout.
|
|
126
|
-
- Every user-facing route should define `meta.title`; provide breadcrumb metadata for detail/flow routes when needed.
|
|
127
|
-
- Use shared route guards for auth/anonymous access control instead of duplicating checks in views.
|
|
128
|
-
- Prefer `props: true` and typed route params in components instead of reading raw params everywhere.
|
|
129
|
-
- Prefer named-route navigation for internal flows (`{ name, params, query }`) and reserve raw path pushes for simple/static redirects.
|
|
130
|
-
- Preserve existing query params intentionally when updating filters, pagination, or tab state.
|
|
131
|
-
- Returning to a view whose filters live in the URL: call `usePreviousLocation` from `fds-vue-core` in setup. `router.push('/path')` drops that query. Browser back keeps it.
|
|
132
|
-
- Use `hrefFor` on `router-link` and `FdsTabsItem`, and `goTo` on back buttons, only when the target is that same view. Leave other navigations unchanged.
|
|
133
|
-
- `hrefFor(path)` reuses the previous history entry, including `_search`, `_page`, and filters, when its pathname matches. Otherwise it returns the path only.
|
|
134
|
-
- `goTo(fallback, matchPaths?)` calls `router.back()` when the previous entry matches, so scroll is kept. Otherwise it pushes the fallback path.
|
|
135
|
-
- Pass `matchPaths` when several paths count as the same place. Without it, only the fallback path is compared.
|
|
136
|
-
|
|
137
|
-
```ts
|
|
138
|
-
import { usePreviousLocation } from 'fds-vue-core'
|
|
139
|
-
|
|
140
|
-
const { hrefFor, goTo } = usePreviousLocation()
|
|
141
|
-
|
|
142
|
-
const goBack = () => {
|
|
143
|
-
goTo('/services/J4', ['/services/J4', '/services'])
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
```vue
|
|
148
|
-
<router-link :to="hrefFor(breadcrumb.path)">
|
|
149
|
-
{{ breadcrumb.name }}
|
|
150
|
-
</router-link>
|
|
151
|
-
<FdsTabsItem :to="hrefFor(tab.path)" />
|
|
152
|
-
<FdsButtonMinor :text="t('common.back')" icon="left" @click="goBack" />
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
- Lazy-load route components with dynamic imports to keep initial bundles smaller.
|
|
156
|
-
- Name organisation route params by domain meaning, not generic `id`:
|
|
157
|
-
- top org: `:topClientNumber`
|
|
158
|
-
- unit/sub-org: `:unitClientNumber`
|
|
159
|
-
- Prefer `/organisations/:topClientNumber/units/:unitClientNumber` over `:id` / `:clino`.
|
|
160
|
-
- Keep route param names aligned with component props when using `props: true`.
|
|
161
|
-
|
|
162
|
-
## Vue and UI expectations
|
|
163
|
-
|
|
164
|
-
- Prefer clear, readable templates over compact but hard-to-follow expressions.
|
|
165
|
-
- Keep components focused; extract repeated UI blocks when duplication grows.
|
|
166
|
-
- Keep localization text in language files instead of hardcoded UI strings.
|
|
167
|
-
|
|
168
|
-
## FDS-first component usage
|
|
169
|
-
|
|
170
|
-
- Use `fds-vue-core` components as the default choice for UI building blocks.
|
|
171
|
-
- Multi-step wizards use `FdsWizard` from `fds-vue-core`. Follow the project’s wizard rule file when one exists (for example `.cursor/rules/fds-wizard.md`).
|
|
172
|
-
- Before adding custom UI, check whether an FDS component already solves the use case.
|
|
173
|
-
- Prefer extending composition around FDS components over re-implementing design-system behavior.
|
|
174
|
-
- Keep visual consistency by matching FDS variants, spacing patterns, and interaction styles.
|
|
175
|
-
|
|
176
|
-
## Asset creation policy
|
|
177
|
-
|
|
178
|
-
- Do not create icons, images, illustrations, or placeholder content unless explicitly requested.
|
|
179
|
-
- Assume `fds-vue-core` provides the required visual assets and use those first.
|
|
180
|
-
- If a required asset appears missing, clearly point out the gap instead of inventing a replacement.
|
|
181
|
-
|
|
182
|
-
## FDS typography (fds-vue-core)
|
|
183
|
-
|
|
184
|
-
- For headings, use `FdsH1`, `FdsH2`, and `FdsH3` (not raw `h1/h2/h3`).
|
|
185
|
-
- `FdsH1/FdsH2/FdsH3` use the `text` prop for content and support `size` (`sm|md|lg|xl|responsive`).
|
|
186
|
-
- When layout spacing needs to be neutralized, reset heading spacing with `class="m-0"` (and add explicit margins as needed).
|
|
187
|
-
- Use `FdsLead` for intro/lead paragraphs, `FdsMeta` for metadata/secondary text, and `FdsLabel` for short labels.
|
|
188
|
-
- `FdsLead`, `FdsMeta`, and `FdsLabel` render slot content; prefer these over custom styled spans for text semantics.
|
|
189
|
-
|
|
190
|
-
## Tailwind (v4+)
|
|
191
|
-
|
|
192
|
-
- Use Tailwind v4 patterns and utilities (for example `tailwindcss@4` with `@tailwindcss/vite` where applicable).
|
|
193
|
-
- Prefer utility classes in templates; only add custom CSS when utilities cannot express the design.
|
|
194
|
-
- Follow existing FDS token/preset styling (`fds-vue-core` tokens and preset) instead of inventing parallel design tokens.
|
|
195
|
-
- Always use Tailwind/FDS design tokens for colors, spacing, typography, radius, and shadows.
|
|
196
|
-
- Never invent ad-hoc color values (for example hex/rgb/hsl literals) when a tokenized utility exists.
|
|
197
|
-
- Keep spacing and typography explicit in class names; avoid hidden styling in deep selectors.
|
|
198
|
-
- Use `!` utility overrides sparingly and only when a component default cannot be changed safely.
|
|
199
|
-
- For Tailwind v4 important modifiers, use postfix-important utility syntax (for example `grid-cols-1!`) instead of prefix syntax (for example `!grid-cols-1`).
|
|
200
|
-
- Stack repeated items with parent `flex flex-col gap-*` (or `space-y-*`). Do not use `mb-* last:mb-0` or `last-of-type:mb-*` — those variants often miss the last `v-for` item (extra Vue/FDS nodes) and trip Tailwind conflict lint.
|
|
201
|
-
|
|
202
|
-
## Figma MCP implementation workflow
|
|
203
|
-
|
|
204
|
-
- When using Figma MCP, implement the design as specified (layout, spacing, typography, states, and interactions).
|
|
205
|
-
- Treat the Figma file as source of truth for UI behavior unless product/technical constraints are explicitly documented.
|
|
206
|
-
- Primary choice for implementation is `fds-vue-core` components that best match the design intent.
|
|
207
|
-
- Only create custom UI when no suitable FDS component exists; keep custom parts minimal and design-token based.
|
|
208
|
-
- If any Figma detail cannot be implemented exactly, document the deviation and choose the closest FDS-aligned solution.
|
|
209
|
-
|
|
210
|
-
## i18n and content
|
|
211
|
-
|
|
212
|
-
- Do not hardcode user-facing copy in templates or script logic; use `t(...)` keys.
|
|
213
|
-
- Add or update locale entries under the project’s language folder (for example `src/lang/*.json`) when introducing new text.
|
|
214
|
-
- Keep translation keys descriptive and grouped by screen/feature.
|
|
215
|
-
- Avoid storing i18n keys inside data/definition arrays (e.g. `i18nKey`) and translating later via dynamic keys or large switch statements. Prefer building definitions with already-translated `label: t('...')` (often as a `computed`) so types stay strict and templates stay simple.
|
|
216
|
-
|
|
217
|
-
## Formatting and locale consistency
|
|
218
|
-
|
|
219
|
-
- Prefer shared formatting utilities for date/number/currency output instead of ad-hoc inline formatting.
|
|
220
|
-
- Use Swedish locale conventions (`sv-SE`) unless a feature explicitly requires another locale.
|
|
221
|
-
|
|
222
|
-
## Accessibility baseline
|
|
223
|
-
|
|
224
|
-
- Use semantic HTML and accessible FDS components first; avoid click-only `div`/`span` patterns.
|
|
225
|
-
- Ensure form controls have proper labels and required ARIA attributes when needed.
|
|
226
|
-
- All interactive elements must be keyboard reachable and show visible focus states.
|
|
227
|
-
- Provide meaningful accessible names for icon-only actions and links.
|
|
228
|
-
|
|
229
|
-
## Async UI states
|
|
230
|
-
|
|
231
|
-
- For data-driven screens, always handle loading, empty, error, and success states explicitly.
|
|
232
|
-
- Use consistent FDS-based feedback patterns for loaders, alerts, and empty-state messaging.
|
|
233
|
-
|
|
234
|
-
## Error handling UX
|
|
235
|
-
|
|
236
|
-
- Do not swallow errors silently; surface actionable feedback to users.
|
|
237
|
-
- Use consistent FDS feedback patterns for API failures (alerts/toasts/inline messages).
|
|
238
|
-
- Keep technical error details out of user-facing copy unless explicitly required.
|
|
239
|
-
|
|
240
|
-
## Performance guardrails
|
|
241
|
-
|
|
242
|
-
- Avoid unnecessary reactive churn (redundant watchers/computed chains in hot paths).
|
|
243
|
-
- Memoize expensive derived values and keep render-time computations lightweight.
|
|
244
|
-
- Keep route-level and heavy feature components lazy-loaded.
|
|
245
|
-
|
|
246
|
-
## Security and data handling
|
|
247
|
-
|
|
248
|
-
- Never commit or expose secrets, tokens, or sensitive identifiers in code, logs, or UI.
|
|
249
|
-
- Validate and sanitize untrusted values from route/query params before use.
|
|
250
|
-
- Type and validate outgoing payloads to prevent accidental over-posting.
|
|
251
|
-
|
|
252
|
-
## Testing expectations
|
|
253
|
-
|
|
254
|
-
- Do not add new automated tests unless the user explicitly asks for them.
|
|
255
|
-
- Update existing tests when your change requires it (for example broken assertions, renamed APIs, or changed behavior covered by current tests).
|
|
256
|
-
- When new tests are requested, add them close to the affected feature.
|
|
257
|
-
- Keep tests behavior-focused and avoid brittle implementation-detail assertions.
|
|
258
|
-
- When no new tests are requested, rely on lint, typecheck, and manual verification of critical flows where practical.
|
|
259
|
-
|
|
260
|
-
## CI/CD
|
|
261
|
-
|
|
262
|
-
- Treat the repository’s CI configuration as the source of truth (`Jenkinsfile`, GitHub Actions, or equivalent).
|
|
263
|
-
- Prefer frozen lockfile installs in CI (for example `pnpm install --frozen-lockfile --prefer-offline`) so CI matches local installs.
|
|
264
|
-
- Keep build, lint, typecheck, and test stages deterministic; document any project-specific stages (security scans, i18n download, Docker publish) in the repo README or a dedicated rule file.
|
|
265
|
-
|
|
266
|
-
## Quality checks before finishing
|
|
267
|
-
|
|
268
|
-
- Run relevant checks for touched code (lint and typecheck when practical).
|
|
269
|
-
- Run and update existing tests when your change affects them; add new tests only when the user explicitly requests them.
|
|
270
|
-
- Fix issues introduced by your change before handing off.
|
|
271
|
-
- Call out anything you could not verify.
|
|
272
|
-
|
|
273
|
-
## Definition of done
|
|
274
|
-
|
|
275
|
-
- Lint and type checks pass for touched code.
|
|
276
|
-
- UI matches Figma/FDS expectations and uses tokenized styling.
|
|
277
|
-
- i18n keys, async states, and cache behavior are handled for changed features.
|
|
278
|
-
- Note any constraints, deviations, or unverified parts in handoff.
|
|
279
|
-
|
|
280
|
-
## Legacy/deprecation policy
|
|
281
|
-
|
|
282
|
-
- For new work, follow the current preferred patterns in this document.
|
|
283
|
-
- Do not expand usage of known legacy patterns when a supported modern pattern exists.
|
|
284
|
-
- If migration is out of scope, isolate legacy touchpoints and avoid spreading them.
|
|
285
|
-
|
|
286
|
-
## Example: API data safety
|
|
287
|
-
|
|
288
|
-
```ts
|
|
289
|
-
// Prefer typed fallbacks when API data may be missing.
|
|
290
|
-
const total = response?.stats?.total ?? 0
|
|
291
|
-
```
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Syncs .cursor/rules/core-coding-standards.mdc into the consumer repo.
|
|
4
|
-
* Rewrites frontmatter description to include the consumer package name.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* node node_modules/fg-devkit/scripts/sync-core-coding-standards.mjs
|
|
8
|
-
* pnpm exec fg-devkit sync-core-coding-standards
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import fs from 'node:fs'
|
|
12
|
-
import path from 'node:path'
|
|
13
|
-
import { findFgDevkitPackageRoot } from './lib/common.mjs'
|
|
14
|
-
|
|
15
|
-
const projectRoot = process.cwd()
|
|
16
|
-
|
|
17
|
-
function getConsumerPackageName() {
|
|
18
|
-
const pkgPath = path.join(projectRoot, 'package.json')
|
|
19
|
-
if (!fs.existsSync(pkgPath)) {
|
|
20
|
-
return 'consumer-project'
|
|
21
|
-
}
|
|
22
|
-
try {
|
|
23
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
24
|
-
if (typeof pkg.name === 'string' && pkg.name.trim() !== '') {
|
|
25
|
-
return pkg.name.trim()
|
|
26
|
-
}
|
|
27
|
-
} catch {
|
|
28
|
-
// fall through to default
|
|
29
|
-
}
|
|
30
|
-
return 'consumer-project'
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const fgRoot = findFgDevkitPackageRoot(projectRoot)
|
|
34
|
-
if (!fgRoot) {
|
|
35
|
-
console.error(
|
|
36
|
-
'[sync-core-coding-standards] Could not find fg-devkit (install fg-devkit or a package that bundles it, e.g. fds-vue-core).',
|
|
37
|
-
)
|
|
38
|
-
process.exit(1)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const sourcePath = path.join(fgRoot, '.cursor', 'rules', 'core-coding-standards.mdc')
|
|
42
|
-
if (!fs.existsSync(sourcePath)) {
|
|
43
|
-
console.error('[sync-core-coding-standards] Missing source rule in fg-devkit package.')
|
|
44
|
-
process.exit(1)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const destinationPath = path.join(projectRoot, '.cursor', 'rules', 'core-coding-standards.mdc')
|
|
48
|
-
const packageName = getConsumerPackageName()
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const content = fs.readFileSync(sourcePath, 'utf8')
|
|
52
|
-
const updated = content.replace(
|
|
53
|
-
/^description:\s*.*$/m,
|
|
54
|
-
`description: Core coding standards for ${packageName}`,
|
|
55
|
-
)
|
|
56
|
-
fs.mkdirSync(path.dirname(destinationPath), { recursive: true })
|
|
57
|
-
fs.writeFileSync(destinationPath, updated, 'utf8')
|
|
58
|
-
console.log(`[sync-core-coding-standards] Updated ${destinationPath}`)
|
|
59
|
-
} catch (err) {
|
|
60
|
-
console.error(`[sync-core-coding-standards] ${err.message}`)
|
|
61
|
-
process.exit(1)
|
|
62
|
-
}
|