@devflow-tools/cli 0.16.0 → 0.16.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/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +266 -5
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/server.js +1 -1
- package/dist/commands/start.js +1 -1
- package/dist/lib/plugin-setup.d.ts +5 -6
- package/dist/lib/plugin-setup.d.ts.map +1 -1
- package/dist/lib/plugin-setup.js +132 -27
- package/dist/lib/plugin-setup.js.map +1 -1
- package/dist/lib/with-server.js +1 -1
- package/dist/plugin-files/.claude-plugin/plugin.json +1 -1
- package/dist/plugin-files/dist/hooks/hook-client.js +5 -5
- package/dist/plugin-files/dist/hooks/hook-daemon.js +11 -11
- package/dist/plugin-files/dist/hooks/post-tool-use-failure.js +2 -2
- package/dist/plugin-files/dist/hooks/post-tool-use.js +3 -3
- package/dist/plugin-files/dist/hooks/pre-compact.js +2 -2
- package/dist/plugin-files/dist/hooks/pre-tool-use.js +2 -2
- package/dist/plugin-files/dist/hooks/session-end.js +2 -2
- package/dist/plugin-files/dist/hooks/stop.js +2 -2
- package/dist/plugin-files/dist/hooks/user-prompt-submit.js +3 -2
- package/dist/plugin-files/dist/skills/devflow:react/SKILL.md +16 -3
- package/dist/plugin-files/hooks/session-start +0 -10
- package/dist/plugin-files/hooks/stop +3 -2
- package/dist/plugin-files/package.json +9 -8
- package/dist/plugin-files/skills/animation/SKILL.md +38 -0
- package/dist/plugin-files/skills/context/SKILL.md +63 -0
- package/dist/plugin-files/skills/css/SKILL.md +38 -0
- package/dist/plugin-files/skills/devflow/SKILL.md +20 -0
- package/dist/plugin-files/skills/docker/SKILL.md +42 -0
- package/dist/plugin-files/skills/doctor/SKILL.md +17 -0
- package/dist/plugin-files/skills/electron/SKILL.md +42 -0
- package/dist/plugin-files/skills/git/SKILL.md +42 -0
- package/dist/plugin-files/skills/graph/SKILL.md +55 -0
- package/dist/plugin-files/skills/graphql/SKILL.md +41 -0
- package/dist/plugin-files/skills/knowledge/SKILL.md +55 -0
- package/dist/plugin-files/skills/memory/SKILL.md +54 -0
- package/dist/plugin-files/skills/nest/SKILL.md +41 -0
- package/dist/plugin-files/skills/nextjs/SKILL.md +38 -0
- package/dist/plugin-files/skills/performance/SKILL.md +42 -0
- package/dist/plugin-files/skills/react/SKILL.md +63 -0
- package/dist/plugin-files/skills/start/SKILL.md +50 -0
- package/dist/plugin-files/skills/tailwind/SKILL.md +38 -0
- package/dist/plugin-files/skills/taro/SKILL.md +41 -0
- package/dist/plugin-files/skills/ui-layout/SKILL.md +38 -0
- package/dist/plugin-files/skills/vue/SKILL.md +45 -0
- package/dist/plugin-files/skills/workflow/SKILL.md +55 -0
- package/package.json +27 -27
- /package/dist/plugin-files/{hooks.json → hooks/hooks.json} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAyDA,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AACtD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EACA,WAAW,GACX,SAAS,GACT,SAAS,GACT,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,mBAAmB,GACnB,UAAU,GACV,UAAU,GACV,WAAW,GACX,cAAc,GACd,kBAAkB,GAClB,WAAW,GACX,iBAAiB,GACjB,kBAAkB,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,eAAe;IACvB,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;IACrC,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACnC,cAAc,CAAC,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACzC;AAED,UAAU,cAAc;IACtB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;IACtC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC;IAChD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -108,6 +108,13 @@ function checkIsolation(project, dependencies) {
|
|
|
108
108
|
ok: true,
|
|
109
109
|
severity: "info",
|
|
110
110
|
});
|
|
111
|
+
metrics.push({
|
|
112
|
+
key: "hook_daemon_registration",
|
|
113
|
+
value: { activeProjectRegistrations: 0 },
|
|
114
|
+
target: "0 when idle or 1 while active",
|
|
115
|
+
ok: true,
|
|
116
|
+
severity: "info",
|
|
117
|
+
});
|
|
111
118
|
}
|
|
112
119
|
else {
|
|
113
120
|
try {
|
|
@@ -144,6 +151,13 @@ function checkIsolation(project, dependencies) {
|
|
|
144
151
|
ok: true,
|
|
145
152
|
severity: "info",
|
|
146
153
|
});
|
|
154
|
+
metrics.push({
|
|
155
|
+
key: "hook_daemon_registration",
|
|
156
|
+
value: { activeProjectRegistrations: projectEntries.length },
|
|
157
|
+
target: "0 when idle or 1 while active",
|
|
158
|
+
ok: projectEntries.length <= 1,
|
|
159
|
+
severity: "warning",
|
|
160
|
+
});
|
|
147
161
|
const registeredSocketPaths = projectEntries.map((entry) => getDaemonSocketPath(entry.rootPath));
|
|
148
162
|
metrics.push({
|
|
149
163
|
key: "registered_daemon_socket_paths",
|
|
@@ -176,6 +190,13 @@ function checkIsolation(project, dependencies) {
|
|
|
176
190
|
ok: false,
|
|
177
191
|
severity: "warning",
|
|
178
192
|
});
|
|
193
|
+
metrics.push({
|
|
194
|
+
key: "hook_daemon_registration",
|
|
195
|
+
value: "unreadable daemon registry",
|
|
196
|
+
target: "valid project registration",
|
|
197
|
+
ok: false,
|
|
198
|
+
severity: "warning",
|
|
199
|
+
});
|
|
179
200
|
}
|
|
180
201
|
}
|
|
181
202
|
return { name: "Isolation", status: sectionStatus(metrics), metrics };
|
|
@@ -184,7 +205,9 @@ const REQUIRED_SCHEMA = {
|
|
|
184
205
|
distill_leases: ["event_ids", "expires_at", "status"],
|
|
185
206
|
observations: ["title"],
|
|
186
207
|
session_events: ["created_at", "id", "processed"],
|
|
208
|
+
sessions: ["created_at", "id", "project_root", "status"],
|
|
187
209
|
};
|
|
210
|
+
const SESSION_STALE_MS = 60 * 60 * 1000;
|
|
188
211
|
function checkRequiredSchema(db) {
|
|
189
212
|
const tableRows = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all();
|
|
190
213
|
const tables = new Set(tableRows.map((row) => row.name));
|
|
@@ -340,6 +363,39 @@ function checkDistill(project, dependencies) {
|
|
|
340
363
|
ok: staleUnleased === 0,
|
|
341
364
|
severity: "error",
|
|
342
365
|
});
|
|
366
|
+
const pendingEvents = doctorCount(openedDb, "SELECT COUNT(*) count FROM session_events WHERE processed = 0");
|
|
367
|
+
metrics.push({
|
|
368
|
+
key: "pending_memory_events",
|
|
369
|
+
value: pendingEvents,
|
|
370
|
+
target: "actively leased or younger than 5 minutes",
|
|
371
|
+
ok: staleUnleased === 0,
|
|
372
|
+
severity: "error",
|
|
373
|
+
});
|
|
374
|
+
const orphanEvents = doctorCount(openedDb, `
|
|
375
|
+
SELECT COUNT(*) count
|
|
376
|
+
FROM session_events event
|
|
377
|
+
LEFT JOIN sessions session ON session.id = event.session_id
|
|
378
|
+
WHERE TRIM(event.session_id) = '' OR session.id IS NULL
|
|
379
|
+
`);
|
|
380
|
+
metrics.push({
|
|
381
|
+
key: "orphan_memory_events",
|
|
382
|
+
value: orphanEvents,
|
|
383
|
+
target: 0,
|
|
384
|
+
ok: orphanEvents === 0,
|
|
385
|
+
severity: "error",
|
|
386
|
+
});
|
|
387
|
+
const staleSessions = doctorCount(openedDb, `
|
|
388
|
+
SELECT COUNT(*) count
|
|
389
|
+
FROM sessions
|
|
390
|
+
WHERE status = 'active' AND created_at < ?
|
|
391
|
+
`, now - SESSION_STALE_MS);
|
|
392
|
+
metrics.push({
|
|
393
|
+
key: "stale_memory_sessions",
|
|
394
|
+
value: staleSessions,
|
|
395
|
+
target: 0,
|
|
396
|
+
ok: staleSessions === 0,
|
|
397
|
+
severity: "error",
|
|
398
|
+
});
|
|
343
399
|
const duplicates = openedDb.prepare(`
|
|
344
400
|
SELECT COUNT(*) AS group_count, COALESCE(SUM(title_count), 0) AS row_count
|
|
345
401
|
FROM (
|
|
@@ -386,6 +442,13 @@ function doctorOpenDatabase(path, dependencies) {
|
|
|
386
442
|
function doctorTableExists(db, table) {
|
|
387
443
|
return Boolean(db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1").get(table));
|
|
388
444
|
}
|
|
445
|
+
function doctorTableColumns(db, table) {
|
|
446
|
+
if (!doctorTableExists(db, table))
|
|
447
|
+
return new Set();
|
|
448
|
+
return new Set(db.prepare(`PRAGMA table_info(${table})`).all()
|
|
449
|
+
.map((column) => column.name)
|
|
450
|
+
.filter((name) => typeof name === "string"));
|
|
451
|
+
}
|
|
389
452
|
function doctorCount(db, sql, ...params) {
|
|
390
453
|
const row = db.prepare(sql).get(...params);
|
|
391
454
|
return Number(row?.count ?? 0);
|
|
@@ -574,10 +637,69 @@ function checkTelemetry(project, strict, dependencies) {
|
|
|
574
637
|
? doctorCount(db, "SELECT COUNT(*) count FROM telemetry_failures WHERE created_at >= ?", cutoff)
|
|
575
638
|
: 0;
|
|
576
639
|
metrics.push({ key: "telemetry_failures_24h", value: failures, target: 0, ok: failures === 0, severity: strictSeverity });
|
|
640
|
+
const sessionColumns = doctorTableColumns(db, "sessions");
|
|
641
|
+
const toolEventColumns = doctorTableColumns(db, "tool_call_events");
|
|
642
|
+
const executionColumns = doctorTableColumns(db, "skill_executions");
|
|
643
|
+
const sessionSchemaReady = ["id", "project_root", "started_at", "status"]
|
|
644
|
+
.every((column) => sessionColumns.has(column));
|
|
645
|
+
metrics.push({
|
|
646
|
+
key: "telemetry_session_schema",
|
|
647
|
+
value: sessionSchemaReady ? "canonical" : "sessions table or identity columns missing",
|
|
648
|
+
target: "canonical sessions table",
|
|
649
|
+
ok: sessionSchemaReady,
|
|
650
|
+
severity: "error",
|
|
651
|
+
});
|
|
652
|
+
if (sessionSchemaReady) {
|
|
653
|
+
const recentSessionStarts = doctorCount(db, `
|
|
654
|
+
SELECT COUNT(*) count FROM sessions
|
|
655
|
+
WHERE project_root = ? AND started_at >= ?
|
|
656
|
+
`, project, cutoff);
|
|
657
|
+
metrics.push({
|
|
658
|
+
key: "session_start_evidence_24h",
|
|
659
|
+
value: recentSessionStarts,
|
|
660
|
+
target: "present after a Claude SessionStart",
|
|
661
|
+
ok: true,
|
|
662
|
+
severity: "info",
|
|
663
|
+
});
|
|
664
|
+
const staleSessions = doctorCount(db, `
|
|
665
|
+
SELECT COUNT(*) count FROM sessions
|
|
666
|
+
WHERE project_root = ? AND status = 'running' AND started_at < ?
|
|
667
|
+
`, project, (dependencies.now ?? Date.now)() - SESSION_STALE_MS);
|
|
668
|
+
metrics.push({
|
|
669
|
+
key: "stale_telemetry_sessions",
|
|
670
|
+
value: staleSessions,
|
|
671
|
+
target: 0,
|
|
672
|
+
ok: staleSessions === 0,
|
|
673
|
+
severity: "error",
|
|
674
|
+
});
|
|
675
|
+
let orphanToolEvents = 0;
|
|
676
|
+
if (toolEventColumns.has("session_id")) {
|
|
677
|
+
orphanToolEvents = doctorCount(db, `
|
|
678
|
+
SELECT COUNT(*) count
|
|
679
|
+
FROM tool_call_events event
|
|
680
|
+
LEFT JOIN sessions session ON session.id = event.session_id
|
|
681
|
+
WHERE TRIM(COALESCE(event.session_id, '')) = '' OR session.id IS NULL
|
|
682
|
+
`);
|
|
683
|
+
}
|
|
684
|
+
let orphanExecutions = 0;
|
|
685
|
+
if (executionColumns.has("session_id")) {
|
|
686
|
+
orphanExecutions = doctorCount(db, `
|
|
687
|
+
SELECT COUNT(*) count
|
|
688
|
+
FROM skill_executions execution
|
|
689
|
+
LEFT JOIN sessions session ON session.id = execution.session_id
|
|
690
|
+
WHERE TRIM(COALESCE(execution.session_id, '')) = '' OR session.id IS NULL
|
|
691
|
+
`);
|
|
692
|
+
}
|
|
693
|
+
metrics.push({
|
|
694
|
+
key: "orphan_telemetry_events",
|
|
695
|
+
value: { toolCalls: orphanToolEvents, skillExecutions: orphanExecutions },
|
|
696
|
+
target: { toolCalls: 0, skillExecutions: 0 },
|
|
697
|
+
ok: orphanToolEvents === 0 && orphanExecutions === 0,
|
|
698
|
+
severity: "error",
|
|
699
|
+
});
|
|
700
|
+
}
|
|
577
701
|
if (doctorTableExists(db, "tool_call_events")) {
|
|
578
|
-
|
|
579
|
-
.map(column => column.name);
|
|
580
|
-
if (eventColumns.includes("failure_category")) {
|
|
702
|
+
if (toolEventColumns.has("failure_category")) {
|
|
581
703
|
const rows = db.prepare(`
|
|
582
704
|
SELECT failure_category category, COUNT(*) count
|
|
583
705
|
FROM tool_call_events
|
|
@@ -2575,7 +2697,8 @@ function marketplaceSourceRoot(sourcePath) {
|
|
|
2575
2697
|
join(sourcePath, "plugins", "claude-code"),
|
|
2576
2698
|
]) {
|
|
2577
2699
|
const root = canonicalDirectory(candidate);
|
|
2578
|
-
if (root && existsSync(join(root, "hooks.json"))
|
|
2700
|
+
if (root && (existsSync(join(root, "hooks", "hooks.json"))
|
|
2701
|
+
|| existsSync(join(root, "hooks.json"))))
|
|
2579
2702
|
return root;
|
|
2580
2703
|
}
|
|
2581
2704
|
return null;
|
|
@@ -3266,7 +3389,11 @@ function inspectLegacyRoot(project, homePath, target, inspection) {
|
|
|
3266
3389
|
enqueueCommand(command);
|
|
3267
3390
|
}
|
|
3268
3391
|
if (target.inspectPluginConfig || !target.commands) {
|
|
3269
|
-
const
|
|
3392
|
+
const conventionalConfigPath = join(root, "hooks", "hooks.json");
|
|
3393
|
+
const legacyConfigPath = join(root, "hooks.json");
|
|
3394
|
+
const configPath = existsSync(legacyConfigPath)
|
|
3395
|
+
? legacyConfigPath
|
|
3396
|
+
: conventionalConfigPath;
|
|
3270
3397
|
if (!existsSync(configPath)) {
|
|
3271
3398
|
inspection.missing.push(configPath);
|
|
3272
3399
|
enqueue(join(root, "hooks", "session-start"));
|
|
@@ -3395,6 +3522,111 @@ function inspectLegacyRoot(project, homePath, target, inspection) {
|
|
|
3395
3522
|
}
|
|
3396
3523
|
}
|
|
3397
3524
|
}
|
|
3525
|
+
function installedPluginVersionsByRoot(dependencies) {
|
|
3526
|
+
const installedPluginsPath = dependencies.installedPluginsPath
|
|
3527
|
+
?? join(dependencies.homePath ?? homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
3528
|
+
const versions = new Map();
|
|
3529
|
+
if (!existsSync(installedPluginsPath))
|
|
3530
|
+
return new Map();
|
|
3531
|
+
try {
|
|
3532
|
+
const manifest = asRecord(parseJsonFile(installedPluginsPath));
|
|
3533
|
+
const plugins = asRecord(manifest?.plugins);
|
|
3534
|
+
if (!plugins)
|
|
3535
|
+
return new Map();
|
|
3536
|
+
for (const entries of Object.values(plugins)) {
|
|
3537
|
+
if (!Array.isArray(entries))
|
|
3538
|
+
continue;
|
|
3539
|
+
for (const entry of entries) {
|
|
3540
|
+
const record = asRecord(entry);
|
|
3541
|
+
if (typeof record?.installPath !== "string" || typeof record.version !== "string")
|
|
3542
|
+
continue;
|
|
3543
|
+
const root = canonicalDirectory(record.installPath);
|
|
3544
|
+
if (!root)
|
|
3545
|
+
continue;
|
|
3546
|
+
const rootVersions = versions.get(root) ?? new Set();
|
|
3547
|
+
rootVersions.add(record.version);
|
|
3548
|
+
versions.set(root, rootVersions);
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
catch {
|
|
3553
|
+
return new Map();
|
|
3554
|
+
}
|
|
3555
|
+
return new Map([...versions].map(([root, values]) => [root, [...values].sort()]));
|
|
3556
|
+
}
|
|
3557
|
+
function readPluginVersion(path) {
|
|
3558
|
+
try {
|
|
3559
|
+
const metadata = asRecord(parseJsonFile(path));
|
|
3560
|
+
return typeof metadata?.version === "string" ? metadata.version : undefined;
|
|
3561
|
+
}
|
|
3562
|
+
catch {
|
|
3563
|
+
return undefined;
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
function inspectPluginLifecycle(targets, dependencies) {
|
|
3567
|
+
const inspection = {
|
|
3568
|
+
layoutIssues: [],
|
|
3569
|
+
versionIssues: [],
|
|
3570
|
+
versionRecords: [],
|
|
3571
|
+
sessionStartIssues: [],
|
|
3572
|
+
daemonIssues: [],
|
|
3573
|
+
};
|
|
3574
|
+
const installedVersions = installedPluginVersionsByRoot(dependencies);
|
|
3575
|
+
for (const target of targets) {
|
|
3576
|
+
const root = target.root;
|
|
3577
|
+
const packagePath = join(root, "package.json");
|
|
3578
|
+
const manifestPath = join(root, ".claude-plugin", "plugin.json");
|
|
3579
|
+
const hasPackageMetadata = existsSync(packagePath) || existsSync(manifestPath);
|
|
3580
|
+
if (!hasPackageMetadata)
|
|
3581
|
+
continue;
|
|
3582
|
+
const conventionalConfigPath = join(root, "hooks", "hooks.json");
|
|
3583
|
+
const legacyConfigPath = join(root, "hooks.json");
|
|
3584
|
+
if (!existsSync(conventionalConfigPath)) {
|
|
3585
|
+
inspection.layoutIssues.push({ root, issue: "missing hooks/hooks.json" });
|
|
3586
|
+
}
|
|
3587
|
+
if (existsSync(legacyConfigPath)) {
|
|
3588
|
+
inspection.layoutIssues.push({ root, issue: "retired root hooks.json is present" });
|
|
3589
|
+
}
|
|
3590
|
+
const activeConfigPath = existsSync(conventionalConfigPath)
|
|
3591
|
+
? conventionalConfigPath
|
|
3592
|
+
: legacyConfigPath;
|
|
3593
|
+
try {
|
|
3594
|
+
const config = asRecord(parseJsonFile(activeConfigPath));
|
|
3595
|
+
const hooks = asRecord(config?.hooks);
|
|
3596
|
+
const commands = [];
|
|
3597
|
+
collectCommandStrings(hooks?.SessionStart, commands);
|
|
3598
|
+
if (!commands.some((command) => /(?:^|[/\\])session-start(?:\s|$|["'])/.test(command))) {
|
|
3599
|
+
inspection.sessionStartIssues.push({ root, issue: "SessionStart wrapper is not registered" });
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
catch {
|
|
3603
|
+
inspection.sessionStartIssues.push({ root, issue: "hook configuration is unreadable" });
|
|
3604
|
+
}
|
|
3605
|
+
const packageVersion = readPluginVersion(packagePath);
|
|
3606
|
+
const manifestVersion = readPluginVersion(manifestPath);
|
|
3607
|
+
const registryVersions = installedVersions.get(root) ?? [];
|
|
3608
|
+
const versionRecord = { root, packageVersion, manifestVersion, registryVersions };
|
|
3609
|
+
inspection.versionRecords.push(versionRecord);
|
|
3610
|
+
if (!packageVersion
|
|
3611
|
+
|| !manifestVersion
|
|
3612
|
+
|| packageVersion !== manifestVersion
|
|
3613
|
+
|| registryVersions.some((version) => version !== packageVersion)) {
|
|
3614
|
+
inspection.versionIssues.push(versionRecord);
|
|
3615
|
+
}
|
|
3616
|
+
const sessionStartPath = join(root, "hooks", "session-start");
|
|
3617
|
+
const daemonPath = join(root, "dist", "hooks", "hook-daemon.js");
|
|
3618
|
+
try {
|
|
3619
|
+
const sessionStart = readFileSync(sessionStartPath, "utf8");
|
|
3620
|
+
if (!existsSync(daemonPath) || !sessionStart.includes("hook-daemon.js")) {
|
|
3621
|
+
inspection.daemonIssues.push({ root, issue: "SessionStart does not register the packaged hook daemon" });
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
catch {
|
|
3625
|
+
inspection.daemonIssues.push({ root, issue: "SessionStart wrapper or hook daemon is missing" });
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
return inspection;
|
|
3629
|
+
}
|
|
3398
3630
|
function checkLegacyPaths(project, dependencies) {
|
|
3399
3631
|
const selection = selectActiveLegacyTargets(project, dependencies);
|
|
3400
3632
|
const inspection = {
|
|
@@ -3438,6 +3670,7 @@ function checkLegacyPaths(project, dependencies) {
|
|
|
3438
3670
|
const resolutionMethod = artifactResolutionFailed && !selection.method.endsWith("-unresolved")
|
|
3439
3671
|
? `${selection.method}-unresolved`
|
|
3440
3672
|
: selection.method;
|
|
3673
|
+
const lifecycle = inspectPluginLifecycle(selection.targets, dependencies);
|
|
3441
3674
|
const metrics = [
|
|
3442
3675
|
{
|
|
3443
3676
|
key: "active_plugin_resolution",
|
|
@@ -3458,6 +3691,34 @@ function checkLegacyPaths(project, dependencies) {
|
|
|
3458
3691
|
ok: true,
|
|
3459
3692
|
severity: "info",
|
|
3460
3693
|
},
|
|
3694
|
+
{
|
|
3695
|
+
key: "plugin_hook_layout",
|
|
3696
|
+
value: lifecycle.layoutIssues,
|
|
3697
|
+
target: "hooks/hooks.json present and root hooks.json absent",
|
|
3698
|
+
ok: lifecycle.layoutIssues.length === 0,
|
|
3699
|
+
severity: "error",
|
|
3700
|
+
},
|
|
3701
|
+
{
|
|
3702
|
+
key: "plugin_version_parity",
|
|
3703
|
+
value: lifecycle.versionRecords,
|
|
3704
|
+
target: "manifest, package, and installed registry versions match",
|
|
3705
|
+
ok: lifecycle.versionIssues.length === 0,
|
|
3706
|
+
severity: "error",
|
|
3707
|
+
},
|
|
3708
|
+
{
|
|
3709
|
+
key: "session_start_hook_registration",
|
|
3710
|
+
value: lifecycle.sessionStartIssues,
|
|
3711
|
+
target: [],
|
|
3712
|
+
ok: lifecycle.sessionStartIssues.length === 0,
|
|
3713
|
+
severity: "error",
|
|
3714
|
+
},
|
|
3715
|
+
{
|
|
3716
|
+
key: "hook_daemon_artifact_registration",
|
|
3717
|
+
value: lifecycle.daemonIssues,
|
|
3718
|
+
target: [],
|
|
3719
|
+
ok: lifecycle.daemonIssues.length === 0,
|
|
3720
|
+
severity: "error",
|
|
3721
|
+
},
|
|
3461
3722
|
{
|
|
3462
3723
|
key: "malformed_active_settings",
|
|
3463
3724
|
value: selection.malformedSettings,
|