fix(linkedin-studio): anchor analytics root on plugin marker + surface npm install
Wave 2 / Step 4 of the remediation plan (docs/remediation/plan.md). PRIMARY (the real fresh-clone failure): - scripts/analytics/node_modules is gitignored, so a fresh clone has neither tsx nor csv-parse. Surface an idempotent `npm install --silent` prerequisite at point-of-use in report.md (Step 1b) and import.md (Step 4). DEVIATION FROM PLAN (correction-in-scope, to satisfy the plan's own Verify gate): - The plan assumed prepending `npm install` was sufficient. Verified it is NOT: the commands invoke the CLI with an absolute src/cli.ts path but from the user's arbitrary CWD, and `node --import tsx` resolves the `tsx` specifier relative to CWD, not the script. There is no global tsx, so the call still fails with ERR_MODULE_NOT_FOUND from any CWD other than scripts/analytics. - Complete fix: invoke the locally-installed tsx by its absolute node_modules/.bin/tsx path in all CLI calls (report.md x10, import.md x3), so they resolve from any working directory once the install above has run. Verified: 0 ERR_MODULE_NOT_FOUND running `report` from /tmp. SECONDARY (latent correctness / hardening): - Add findPluginRoot(): walks up to the dir holding .claude-plugin/plugin.json and anchors getAnalyticsRoot() on it, falling back to the legacy 4-up count. MEASURED that ../../../../ already resolved to the plugin root from BOTH src/utils and build/utils (both 4 levels deep), so the plan's "src-vs-build depth miscalibration" premise was false — this is correct-by-construction hardening (survives a future source move), not a live-bug fix. - Reconcile cli.ts usage/help text: `node build/cli.js` -> `node --import tsx src/cli.ts` (the real runtime). - Fix report.md troubleshooting: "Verify tsx is available" -> the actual install command on ERR_MODULE_NOT_FOUND. Test-first: scripts/analytics/tests/storage-root.test.ts (red on missing findPluginRoot export, green after). Full suite 106/106, tsc --noEmit clean, structural lint 0 failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bd9a289dec
commit
e4db4cf5e8
5 changed files with 159 additions and 29 deletions
|
|
@ -6,18 +6,46 @@ import type { AnalyticsBatch, WeeklyReport, MonthlyReport, PostAnalytics } from
|
|||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* Get the analytics root directory from environment or default location
|
||||
* Default is assets/analytics relative to the plugin root
|
||||
* Walk up from `startDir` until a directory containing `.claude-plugin/plugin.json`
|
||||
* is found. Returns that directory, or null if no marker is found before the
|
||||
* filesystem root.
|
||||
*
|
||||
* Anchoring on the plugin marker keeps the analytics root correct by
|
||||
* construction regardless of whether this module runs from `src/utils/`
|
||||
* (under tsx) or `build/utils/` (compiled) — and survives a future source
|
||||
* move that a hardcoded "../../../../" count would silently break.
|
||||
*/
|
||||
export function findPluginRoot(startDir: string): string | null {
|
||||
let dir = resolve(startDir);
|
||||
|
||||
// dirname() of the filesystem root returns the root itself; stop when it
|
||||
// no longer changes.
|
||||
for (;;) {
|
||||
if (existsSync(join(dir, ".claude-plugin", "plugin.json"))) {
|
||||
return dir;
|
||||
}
|
||||
const parent = dirname(dir);
|
||||
if (parent === dir) {
|
||||
return null;
|
||||
}
|
||||
dir = parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the analytics root directory from environment or default location.
|
||||
* Default is assets/analytics under the plugin root (the dir holding
|
||||
* .claude-plugin/plugin.json). The ANALYTICS_ROOT env override is the test seam.
|
||||
*/
|
||||
export function getAnalyticsRoot(): string {
|
||||
if (process.env.ANALYTICS_ROOT) {
|
||||
return resolve(process.env.ANALYTICS_ROOT);
|
||||
}
|
||||
|
||||
// Build output is at: scripts/analytics/build/utils/storage.js
|
||||
// Plugin root is 4 levels up: ../../../../
|
||||
// Then assets/analytics from there
|
||||
const pluginRoot = resolve(__dirname, "../../../../");
|
||||
// Anchor on the .claude-plugin/plugin.json marker. Fall back to the legacy
|
||||
// 4-levels-up count (scripts/analytics/{src,build}/utils -> plugin root) only
|
||||
// if no marker is found (e.g. an unusual extraction without the manifest).
|
||||
const pluginRoot = findPluginRoot(__dirname) ?? resolve(__dirname, "../../../../");
|
||||
return join(pluginRoot, "assets", "analytics");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue