fg-devkit 1.24.0 → 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 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 repo. Claude Code doesn't install them from
100
- the settings file on its own, so every developer runs it once per repo; commit `.claude/settings.json`
101
- the first time. Needs Claude Code and SSH access to `feelgood/fg-ai-rules`.
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`.
102
103
 
103
104
  Run full consumer setup in one command:
104
105
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fg-devkit",
3
- "version": "1.24.0",
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",
@@ -32,7 +32,7 @@
32
32
  "@intlify/unplugin-vue-i18n": "11.2.6",
33
33
  "@tanstack/vue-query-devtools": "6.3.3",
34
34
  "@types/node": "22.16.5",
35
- "fg-devkit": "1.24.0",
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",
@@ -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 and install them
580
+ setup-ai-rules Enable the fg-ai-rules Claude Code plugins in this repo, install them once per machine
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
@@ -5,12 +5,13 @@
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 this repo (`claude plugin install … --scope project`). Claude Code
9
- * does not install them from the settings file on its own, and without the install they don't
10
- * load at session start.
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
- * Safe to run again: it only changes what is missing. Commit .claude/settings.json afterwards so
13
- * teammates get the same plugins; each of them still runs this once.
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.
14
15
  *
15
16
  * Usage:
16
17
  * pnpm exec fg-devkit setup-ai-rules
@@ -38,6 +39,16 @@ function claude(args, { capture = false } = {}) {
38
39
  return { status: result.status ?? 1, stdout: result.stdout ?? '' }
39
40
  }
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
+
41
52
  function claudeJson(args) {
42
53
  const result = claude([...args, '--json'], { capture: true })
43
54
  if (result.status !== 0) return null
@@ -77,7 +88,10 @@ function updateSettings() {
77
88
  function printManualSteps() {
78
89
  console.log(`${TAG} Run these from the repo root, then restart Claude Code:`)
79
90
  console.log(` claude plugin marketplace add ${MARKETPLACE_REPO}`)
80
- for (const plugin of PLUGINS) console.log(` claude plugin install ${plugin}@${MARKETPLACE} --scope project`)
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
+ }
81
95
  }
82
96
 
83
97
  function main() {
@@ -106,21 +120,24 @@ function main() {
106
120
  }
107
121
 
108
122
  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
123
  let failed = false
117
124
  for (const plugin of PLUGINS) {
118
- if (isInstalledHere(plugin)) {
119
- console.log(`${TAG} ${plugin} is already installed for this repo.`)
120
- continue
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
121
140
  }
122
- console.log(`${TAG} Installing ${plugin}…`)
123
- if (claude(['plugin', 'install', `${plugin}@${MARKETPLACE}`, '--scope', 'project']).status !== 0) failed = true
124
141
  }
125
142
  if (failed) {
126
143
  printManualSteps()
@@ -128,7 +145,7 @@ function main() {
128
145
  }
129
146
 
130
147
  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.`)
148
+ if (settingsChanged) console.log(`${TAG} Commit .claude/settings.json so other repos and teammates get the same plugins.`)
132
149
  return 0
133
150
  }
134
151