fg-devkit 1.24.0 → 1.24.2
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 +5 -4
- package/package.json +1 -1
- package/scaffold/vue/package.json +1 -1
- package/scripts/fg-devkit.mjs +4 -4
- package/scripts/setup-ai-rules.mjs +58 -20
package/README.md
CHANGED
|
@@ -96,9 +96,10 @@ pnpm exec fgd setup-ai-rules
|
|
|
96
96
|
```
|
|
97
97
|
|
|
98
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 for the
|
|
100
|
-
|
|
101
|
-
|
|
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. When `.claude/settings.json` changed it commits that file on its own
|
|
102
|
+
(`chore(repo): enable fg-ai-rules Claude Code plugins`). Each developer runs it once on their machine. Needs Claude Code and SSH access to `feelgood/fg-ai-rules`.
|
|
102
103
|
|
|
103
104
|
Run full consumer setup in one command:
|
|
104
105
|
|
|
@@ -107,7 +108,7 @@ pnpm exec fgd full-consumer-setup
|
|
|
107
108
|
```
|
|
108
109
|
|
|
109
110
|
It syncs the consumer scripts, Lefthook, configs and dependency policy, runs `pnpm install`, and finishes with
|
|
110
|
-
`setup-ai-rules`. Without Claude Code that last step only prints a warning.
|
|
111
|
+
`setup-ai-rules`, which commits `.claude/settings.json`. Without Claude Code that last step only prints a warning.
|
|
111
112
|
|
|
112
113
|
## Dependency policy update workflow
|
|
113
114
|
|
package/package.json
CHANGED
package/scripts/fg-devkit.mjs
CHANGED
|
@@ -577,7 +577,7 @@ Commands:
|
|
|
577
577
|
sync-vite-plugin-vue-inspector Add/pin vite-plugin-vue-inspector, wire vite.config, install, commit
|
|
578
578
|
sync-consumer-scripts Enforce standard fgd scripts in consumer package.json
|
|
579
579
|
setup-lefthook Write lefthook.yml and install the git hooks
|
|
580
|
-
setup-ai-rules Enable the fg-ai-rules Claude Code plugins in this repo
|
|
580
|
+
setup-ai-rules Enable the fg-ai-rules Claude Code plugins in this repo, install them once per machine, commit
|
|
581
581
|
full-consumer-setup Run: scripts -> lefthook -> step 3 -> step 2 -> pnpm install -> AI rules
|
|
582
582
|
create-release [release|candidate] [patch|minor|major] [--skip-release-notes]
|
|
583
583
|
Stable release or candidate (X.Y.Z-rc.N); omit = interactive menu
|
|
@@ -1091,14 +1091,14 @@ async function runInteractiveMenu() {
|
|
|
1091
1091
|
description: 'Write lefthook.yml and install the git hooks',
|
|
1092
1092
|
},
|
|
1093
1093
|
{
|
|
1094
|
-
name: 'AI rules',
|
|
1094
|
+
name: autoCommit('AI rules'),
|
|
1095
1095
|
value: 'ai-rules',
|
|
1096
|
-
description: 'Enable the fg-ai-rules plugins in .claude/settings.json
|
|
1096
|
+
description: 'Enable the fg-ai-rules plugins in .claude/settings.json, install them for Claude Code and commit the file',
|
|
1097
1097
|
},
|
|
1098
1098
|
{
|
|
1099
1099
|
name: 'Full setup',
|
|
1100
1100
|
value: 'full-consumer-setup',
|
|
1101
|
-
description: 'Sync scripts, Lefthook, configs and dependency policy, pnpm install, then set up AI rules',
|
|
1101
|
+
description: 'Sync scripts, Lefthook, configs and dependency policy, pnpm install, then set up AI rules (commits .claude/settings.json)',
|
|
1102
1102
|
},
|
|
1103
1103
|
{ name: 'Cancel', value: 'cancel' },
|
|
1104
1104
|
],
|
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
* 1. Add the `fg` marketplace and enable fg-core and fg-frontend in .claude/settings.json,
|
|
6
6
|
* keeping every other setting.
|
|
7
7
|
* 2. Add the marketplace to Claude Code on this machine if it is missing.
|
|
8
|
-
* 3. Install the plugins for
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* 4. Commit .claude/settings.json when it changed (only that file).
|
|
14
|
+
*
|
|
15
|
+
* Safe to run again: it only changes what is missing. Once a developer has run it in any repo,
|
|
16
|
+
* every other repo that commits the same .claude/settings.json works without running it again.
|
|
14
17
|
*
|
|
15
18
|
* Usage:
|
|
16
19
|
* pnpm exec fg-devkit setup-ai-rules
|
|
@@ -20,13 +23,18 @@ import { spawnSync } from 'node:child_process'
|
|
|
20
23
|
import fs from 'node:fs'
|
|
21
24
|
import path from 'node:path'
|
|
22
25
|
|
|
26
|
+
import { fileDiffersFromHead, runGit } from './lib/common.mjs'
|
|
27
|
+
|
|
23
28
|
const TAG = '[setup-ai-rules]'
|
|
24
29
|
const MARKETPLACE = 'fg'
|
|
25
30
|
const MARKETPLACE_REPO = 'feelgood/fg-ai-rules'
|
|
26
31
|
const PLUGINS = ['fg-core', 'fg-frontend']
|
|
27
32
|
|
|
28
33
|
const projectRoot = fs.realpathSync(process.cwd())
|
|
29
|
-
const
|
|
34
|
+
const SETTINGS_FILE = '.claude/settings.json'
|
|
35
|
+
const COMMIT_MESSAGE = 'chore(repo): enable fg-ai-rules Claude Code plugins'
|
|
36
|
+
const settingsPath = path.join(projectRoot, SETTINGS_FILE)
|
|
37
|
+
const GIT = { cwd: projectRoot, tag: TAG }
|
|
30
38
|
|
|
31
39
|
function claude(args, { capture = false } = {}) {
|
|
32
40
|
const result = spawnSync('claude', args, {
|
|
@@ -38,6 +46,16 @@ function claude(args, { capture = false } = {}) {
|
|
|
38
46
|
return { status: result.status ?? 1, stdout: result.stdout ?? '' }
|
|
39
47
|
}
|
|
40
48
|
|
|
49
|
+
/** Last JSON line of a command that prints one result line with --json. */
|
|
50
|
+
function claudeJsonLine(args) {
|
|
51
|
+
const lines = claude([...args, '--json'], { capture: true }).stdout.trim().split('\n')
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(lines.at(-1))
|
|
54
|
+
} catch {
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
41
59
|
function claudeJson(args) {
|
|
42
60
|
const result = claude([...args, '--json'], { capture: true })
|
|
43
61
|
if (result.status !== 0) return null
|
|
@@ -74,10 +92,27 @@ function updateSettings() {
|
|
|
74
92
|
return true
|
|
75
93
|
}
|
|
76
94
|
|
|
95
|
+
/** Commit .claude/settings.json on its own when it differs from HEAD. */
|
|
96
|
+
function commitSettings() {
|
|
97
|
+
if (runGit(['rev-parse', '--is-inside-work-tree'], { ...GIT, capture: true }).status !== 0) {
|
|
98
|
+
console.log(`${TAG} Not a git repository; commit ${SETTINGS_FILE} yourself.`)
|
|
99
|
+
return 0
|
|
100
|
+
}
|
|
101
|
+
if (!fileDiffersFromHead(SETTINGS_FILE, GIT)) return 0
|
|
102
|
+
const status =
|
|
103
|
+
runGit(['add', '--', SETTINGS_FILE], GIT).status ||
|
|
104
|
+
runGit(['commit', '-m', COMMIT_MESSAGE, '--', SETTINGS_FILE], GIT).status
|
|
105
|
+
if (status !== 0) console.error(`${TAG} Could not commit ${SETTINGS_FILE}.`)
|
|
106
|
+
return status
|
|
107
|
+
}
|
|
108
|
+
|
|
77
109
|
function printManualSteps() {
|
|
78
110
|
console.log(`${TAG} Run these from the repo root, then restart Claude Code:`)
|
|
79
111
|
console.log(` claude plugin marketplace add ${MARKETPLACE_REPO}`)
|
|
80
|
-
for (const plugin of PLUGINS)
|
|
112
|
+
for (const plugin of PLUGINS) {
|
|
113
|
+
console.log(` claude plugin install ${plugin}@${MARKETPLACE} --scope user`)
|
|
114
|
+
console.log(` claude plugin disable ${plugin}@${MARKETPLACE} --scope user`)
|
|
115
|
+
}
|
|
81
116
|
}
|
|
82
117
|
|
|
83
118
|
function main() {
|
|
@@ -106,29 +141,32 @@ function main() {
|
|
|
106
141
|
}
|
|
107
142
|
|
|
108
143
|
const installed = claudeJson(['plugin', 'list']) ?? []
|
|
109
|
-
const isInstalledHere = (plugin) =>
|
|
110
|
-
installed.some(
|
|
111
|
-
(entry) =>
|
|
112
|
-
entry.id === `${plugin}@${MARKETPLACE}` &&
|
|
113
|
-
(entry.scope === 'user' || (entry.projectPath && fs.realpathSync(entry.projectPath) === projectRoot)),
|
|
114
|
-
)
|
|
115
|
-
|
|
116
144
|
let failed = false
|
|
117
145
|
for (const plugin of PLUGINS) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
146
|
+
const id = `${plugin}@${MARKETPLACE}`
|
|
147
|
+
if (installed.some((entry) => entry.id === id && entry.scope === 'user')) {
|
|
148
|
+
console.log(`${TAG} ${plugin} is already installed.`)
|
|
149
|
+
} else {
|
|
150
|
+
console.log(`${TAG} Installing ${plugin}…`)
|
|
151
|
+
if (claude(['plugin', 'install', id, '--scope', 'user']).status !== 0) {
|
|
152
|
+
failed = true
|
|
153
|
+
continue
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Off at user scope, so only repos whose .claude/settings.json enables it load it.
|
|
157
|
+
const disabled = claudeJsonLine(['plugin', 'disable', id, '--scope', 'user'])
|
|
158
|
+
if (!disabled || (disabled.outcome !== 'ok' && !disabled.alreadyInGoalState)) {
|
|
159
|
+
console.error(`${TAG} Could not disable ${plugin} at user scope: ${disabled?.message ?? 'unknown error'}`)
|
|
160
|
+
failed = true
|
|
121
161
|
}
|
|
122
|
-
console.log(`${TAG} Installing ${plugin}…`)
|
|
123
|
-
if (claude(['plugin', 'install', `${plugin}@${MARKETPLACE}`, '--scope', 'project']).status !== 0) failed = true
|
|
124
162
|
}
|
|
125
163
|
if (failed) {
|
|
126
164
|
printManualSteps()
|
|
127
165
|
return 1
|
|
128
166
|
}
|
|
129
167
|
|
|
168
|
+
if (commitSettings() !== 0) return 1
|
|
130
169
|
console.log(`${TAG} Done. Restart Claude Code to load the rules.`)
|
|
131
|
-
if (settingsChanged) console.log(`${TAG} Commit .claude/settings.json so teammates get the same plugins.`)
|
|
132
170
|
return 0
|
|
133
171
|
}
|
|
134
172
|
|