ccprofiles-core 0.8.0 → 0.8.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/dist/discovery.d.ts +12 -17
- package/dist/discovery.js +26 -26
- package/package.json +1 -1
package/dist/discovery.d.ts
CHANGED
|
@@ -24,24 +24,19 @@ export interface LiveProfile {
|
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Pull a comparable installed version out of each entry of installed_plugins.json's `plugins` map.
|
|
27
|
+
* The value is an ARRAY of install records (one per scope), not an object.
|
|
27
28
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* merely a prefix of the entry's own sha. Otherwise use the full `gitCommitSha`, which is the one
|
|
40
|
-
* canonical spelling every profile agrees on.
|
|
41
|
-
*
|
|
42
|
-
* ponytail: takes the first scope's version when a plugin is installed at several scopes at once.
|
|
43
|
-
* Per-scope drift is a different (rarer) bug than the cross-profile drift this exists to catch;
|
|
44
|
-
* widen to Record<id, Record<scope, version>> if that ever bites.
|
|
29
|
+
* ONLY a real release `version` counts. `gitCommitSha` looks like a tempting fallback for the
|
|
30
|
+
* versionless plugins (Claude Code writes `"version": "unknown"` for those) but it is unusable as
|
|
31
|
+
* an identity, and comparing it produced drift that `fix` could never clear:
|
|
32
|
+
* - it is the sha of the whole MARKETPLACE repo, not of the plugin — two profiles whose plugin
|
|
33
|
+
* files are byte-identical still hold different shas if they installed at different times
|
|
34
|
+
* - Claude Code never refreshes it when `claude plugin update` finds nothing to do, so the stale
|
|
35
|
+
* value outlives every update. Seen on a real machine: context7 at 205b6e0b in one profile and
|
|
36
|
+
* e14e8fe2 in three others, identical file contents, identical marketplace snapshot, and
|
|
37
|
+
* `clp fix` looping forever because update kept (correctly) no-opping.
|
|
38
|
+
* A versionless plugin therefore contributes no drift signal at all. Detecting nothing beats
|
|
39
|
+
* detecting noise that has no remedy.
|
|
45
40
|
*/
|
|
46
41
|
export declare function readInstalledPluginVersions(plugins: Record<string, unknown>): Record<string, string>;
|
|
47
42
|
export declare function discoverProfiles(home: string): Promise<LiveProfile[]>;
|
package/dist/discovery.js
CHANGED
|
@@ -2,38 +2,38 @@ import { access, readdir, readFile, readlink } from 'node:fs/promises';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { readCodexMcpServers } from './codex.js';
|
|
4
4
|
import { logicalLinkEntry } from './links.js';
|
|
5
|
+
/**
|
|
6
|
+
* ccprofiles manages a profile's USER-scope plugins. A project-scope install belongs to some project
|
|
7
|
+
* directory, not to the profile, and `claude plugin update/uninstall` only ever act on user scope —
|
|
8
|
+
* counting one made those calls fail with `Plugin "x" is not installed at scope user`. Entries
|
|
9
|
+
* predating the `scope` field are treated as user.
|
|
10
|
+
*/
|
|
11
|
+
function userScopeEntries(entries) {
|
|
12
|
+
return (Array.isArray(entries) ? entries : [entries])
|
|
13
|
+
.filter((e) => e && (e.scope ?? 'user') === 'user');
|
|
14
|
+
}
|
|
5
15
|
/**
|
|
6
16
|
* Pull a comparable installed version out of each entry of installed_plugins.json's `plugins` map.
|
|
17
|
+
* The value is an ARRAY of install records (one per scope), not an object.
|
|
7
18
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* merely a prefix of the entry's own sha. Otherwise use the full `gitCommitSha`, which is the one
|
|
20
|
-
* canonical spelling every profile agrees on.
|
|
21
|
-
*
|
|
22
|
-
* ponytail: takes the first scope's version when a plugin is installed at several scopes at once.
|
|
23
|
-
* Per-scope drift is a different (rarer) bug than the cross-profile drift this exists to catch;
|
|
24
|
-
* widen to Record<id, Record<scope, version>> if that ever bites.
|
|
19
|
+
* ONLY a real release `version` counts. `gitCommitSha` looks like a tempting fallback for the
|
|
20
|
+
* versionless plugins (Claude Code writes `"version": "unknown"` for those) but it is unusable as
|
|
21
|
+
* an identity, and comparing it produced drift that `fix` could never clear:
|
|
22
|
+
* - it is the sha of the whole MARKETPLACE repo, not of the plugin — two profiles whose plugin
|
|
23
|
+
* files are byte-identical still hold different shas if they installed at different times
|
|
24
|
+
* - Claude Code never refreshes it when `claude plugin update` finds nothing to do, so the stale
|
|
25
|
+
* value outlives every update. Seen on a real machine: context7 at 205b6e0b in one profile and
|
|
26
|
+
* e14e8fe2 in three others, identical file contents, identical marketplace snapshot, and
|
|
27
|
+
* `clp fix` looping forever because update kept (correctly) no-opping.
|
|
28
|
+
* A versionless plugin therefore contributes no drift signal at all. Detecting nothing beats
|
|
29
|
+
* detecting noise that has no remedy.
|
|
25
30
|
*/
|
|
26
31
|
export function readInstalledPluginVersions(plugins) {
|
|
27
32
|
const out = {};
|
|
28
33
|
for (const [id, entries] of Object.entries(plugins)) {
|
|
29
|
-
for (const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const ver = typeof version === 'string' && version && version !== 'unknown' ? version : null;
|
|
33
|
-
const isShaSpelling = !!ver && !!sha && sha.startsWith(ver);
|
|
34
|
-
const v = ver && !isShaSpelling ? ver : sha ?? ver;
|
|
35
|
-
if (v) {
|
|
36
|
-
out[id] = v;
|
|
34
|
+
for (const { version } of userScopeEntries(entries)) {
|
|
35
|
+
if (typeof version === 'string' && version && version !== 'unknown') {
|
|
36
|
+
out[id] = version;
|
|
37
37
|
break;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -129,7 +129,7 @@ export async function discoverProfiles(home) {
|
|
|
129
129
|
try {
|
|
130
130
|
const ip = JSON.parse(await readFile(join(dir, 'plugins', 'installed_plugins.json'), 'utf8'));
|
|
131
131
|
if (ip && typeof ip.plugins === 'object' && ip.plugins !== null) {
|
|
132
|
-
installedPlugins.push(...Object.
|
|
132
|
+
installedPlugins.push(...Object.entries(ip.plugins).filter(([, v]) => userScopeEntries(v).length).map(([id]) => id));
|
|
133
133
|
installedPluginVersions = readInstalledPluginVersions(ip.plugins);
|
|
134
134
|
}
|
|
135
135
|
}
|
package/package.json
CHANGED