@curdx/flow 4.0.0 → 4.0.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/CHANGELOG.md +12 -0
- package/dist/index.mjs +83 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@curdx/flow` are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/) and the project follows [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 4.0.1 — 2026-04-27
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **Migration cleanup is now exhaustive.** v4.0.0's auto-migration for the `ralph-specum` → `curdx-flow` rename only invoked `claude plugin uninstall`, which leaves substantial residue when the marketplace's plugin id has been renamed (the CLI can't resolve the legacy id and bails). The installer now manually purges every leftover artifact for legacy slugs (`ralph-specum@curdx-flow`, `ralph-specum@smart-ralph`):
|
|
10
|
+
- `~/.claude/settings.json` → removes `enabledPlugins[<legacyId>]`
|
|
11
|
+
- `~/.claude/plugins/installed_plugins.json` → removes `plugins[<legacyId>]`
|
|
12
|
+
- `~/.claude/plugins/cache/<marketplace>/<name>/` → recursive remove
|
|
13
|
+
- `~/.claude/plugins/data/<name>-<marketplace>/` → recursive remove
|
|
14
|
+
- Marketplace registrations (`known_marketplaces.json`, `extraKnownMarketplaces`) are deliberately left alone — those are user-managed.
|
|
15
|
+
- Implementation lives in `src/runner/legacy-cleanup.ts::purgeLegacyPluginArtifacts`. Idempotent and safe: every step swallows ENOENT silently and reports JSON / IO errors via the install task log without failing the flow.
|
|
16
|
+
|
|
5
17
|
## 4.0.0 — 2026-04-27
|
|
6
18
|
|
|
7
19
|
### Breaking
|
package/dist/index.mjs
CHANGED
|
@@ -520,6 +520,78 @@ var frontendDesign = {
|
|
|
520
520
|
};
|
|
521
521
|
var frontend_design_default = frontendDesign;
|
|
522
522
|
|
|
523
|
+
// src/runner/legacy-cleanup.ts
|
|
524
|
+
import { promises as fs2 } from "fs";
|
|
525
|
+
import path2 from "path";
|
|
526
|
+
import os2 from "os";
|
|
527
|
+
async function purgeLegacyPluginArtifacts(legacyId, ctx) {
|
|
528
|
+
const at = legacyId.indexOf("@");
|
|
529
|
+
if (at <= 0 || at === legacyId.length - 1) return;
|
|
530
|
+
const name = legacyId.slice(0, at);
|
|
531
|
+
const marketplace = legacyId.slice(at + 1);
|
|
532
|
+
const home = os2.homedir();
|
|
533
|
+
const settingsPath = path2.join(home, ".claude", "settings.json");
|
|
534
|
+
const installedPath = path2.join(home, ".claude", "plugins", "installed_plugins.json");
|
|
535
|
+
const cacheDir = path2.join(home, ".claude", "plugins", "cache", marketplace, name);
|
|
536
|
+
const dataDir = path2.join(home, ".claude", "plugins", "data", `${name}-${marketplace}`);
|
|
537
|
+
let removedAny = false;
|
|
538
|
+
removedAny = await deleteJsonKey(settingsPath, ["enabledPlugins", legacyId], ctx) || removedAny;
|
|
539
|
+
removedAny = await deleteJsonKey(installedPath, ["plugins", legacyId], ctx) || removedAny;
|
|
540
|
+
removedAny = await rmDir(cacheDir, ctx) || removedAny;
|
|
541
|
+
removedAny = await rmDir(dataDir, ctx) || removedAny;
|
|
542
|
+
if (removedAny) {
|
|
543
|
+
ctx.log.message(`Purged legacy artifacts for ${legacyId}.`);
|
|
544
|
+
clearStateCache();
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async function deleteJsonKey(filePath, keyPath, ctx) {
|
|
548
|
+
let raw;
|
|
549
|
+
try {
|
|
550
|
+
raw = await fs2.readFile(filePath, "utf8");
|
|
551
|
+
} catch (err) {
|
|
552
|
+
if (err.code === "ENOENT") return false;
|
|
553
|
+
ctx.log.message(`Skip purge of ${filePath}: ${err.message}`);
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
let json;
|
|
557
|
+
try {
|
|
558
|
+
json = JSON.parse(raw);
|
|
559
|
+
} catch (err) {
|
|
560
|
+
ctx.log.message(`Skip purge of ${filePath}: invalid JSON (${err.message})`);
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
let cursor = json;
|
|
564
|
+
for (let i = 0; i < keyPath.length - 1; i++) {
|
|
565
|
+
const next = cursor?.[keyPath[i]];
|
|
566
|
+
if (!next || typeof next !== "object") return false;
|
|
567
|
+
cursor = next;
|
|
568
|
+
}
|
|
569
|
+
const finalKey = keyPath[keyPath.length - 1];
|
|
570
|
+
if (!cursor || !(finalKey in cursor)) return false;
|
|
571
|
+
delete cursor[finalKey];
|
|
572
|
+
try {
|
|
573
|
+
await fs2.writeFile(filePath, JSON.stringify(json, null, 2) + "\n", "utf8");
|
|
574
|
+
return true;
|
|
575
|
+
} catch (err) {
|
|
576
|
+
ctx.log.message(`Failed to rewrite ${filePath}: ${err.message}`);
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
async function rmDir(dirPath, ctx) {
|
|
581
|
+
try {
|
|
582
|
+
await fs2.access(dirPath);
|
|
583
|
+
} catch {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
await fs2.rm(dirPath, { recursive: true, force: true });
|
|
588
|
+
return true;
|
|
589
|
+
} catch (err) {
|
|
590
|
+
ctx.log.message(`Failed to remove ${dirPath}: ${err.message}`);
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
523
595
|
// src/registry/plugins/curdx-flow.ts
|
|
524
596
|
var PLUGIN_ID5 = "curdx-flow@curdx";
|
|
525
597
|
var PLUGIN_NAME3 = "curdx-flow";
|
|
@@ -528,10 +600,12 @@ var MARKETPLACE_SOURCE4 = "curdx/curdx-flow";
|
|
|
528
600
|
var LEGACY_PLUGIN_IDS = ["ralph-specum@curdx-flow", "ralph-specum@smart-ralph"];
|
|
529
601
|
async function uninstallLegacyIfPresent(ctx) {
|
|
530
602
|
for (const legacyId of LEGACY_PLUGIN_IDS) {
|
|
531
|
-
|
|
603
|
+
const installed = await isPluginInstalled(legacyId);
|
|
604
|
+
if (installed) {
|
|
532
605
|
ctx.log.message(`Removing legacy plugin ${legacyId} (renamed to ${PLUGIN_ID5})\u2026`);
|
|
533
606
|
await uninstallPluginById(legacyId, ctx);
|
|
534
607
|
}
|
|
608
|
+
await purgeLegacyPluginArtifacts(legacyId, ctx);
|
|
535
609
|
}
|
|
536
610
|
}
|
|
537
611
|
var curdxFlow = {
|
|
@@ -669,15 +743,15 @@ function findPkg(id) {
|
|
|
669
743
|
}
|
|
670
744
|
|
|
671
745
|
// src/runner/claudeMd.ts
|
|
672
|
-
import { promises as
|
|
673
|
-
import
|
|
674
|
-
import
|
|
746
|
+
import { promises as fs3 } from "fs";
|
|
747
|
+
import path3 from "path";
|
|
748
|
+
import os3 from "os";
|
|
675
749
|
import * as p3 from "@clack/prompts";
|
|
676
750
|
var BEGIN_MARKER = "<!-- BEGIN @curdx/flow v1 -->";
|
|
677
751
|
var END_MARKER = "<!-- END @curdx/flow v1 -->";
|
|
678
752
|
var BLOCK_RE = /<!-- BEGIN @curdx\/flow v\d+[^>]*-->[\s\S]*?<!-- END @curdx\/flow v\d+ -->/;
|
|
679
753
|
function claudeMdPath() {
|
|
680
|
-
return
|
|
754
|
+
return path3.join(os3.homedir(), ".claude", "CLAUDE.md");
|
|
681
755
|
}
|
|
682
756
|
function renderItemLine(item) {
|
|
683
757
|
let line = `- ${item.name}`;
|
|
@@ -786,7 +860,7 @@ async function syncClaudeMd(opts) {
|
|
|
786
860
|
let existing = "";
|
|
787
861
|
let existed = true;
|
|
788
862
|
try {
|
|
789
|
-
existing = await
|
|
863
|
+
existing = await fs3.readFile(file, "utf8");
|
|
790
864
|
} catch (err) {
|
|
791
865
|
if (err.code === "ENOENT") {
|
|
792
866
|
existed = false;
|
|
@@ -808,10 +882,10 @@ async function syncClaudeMd(opts) {
|
|
|
808
882
|
if (next === existing) {
|
|
809
883
|
return { status: "unchanged", path: file };
|
|
810
884
|
}
|
|
811
|
-
await
|
|
885
|
+
await fs3.mkdir(path3.dirname(file), { recursive: true });
|
|
812
886
|
const tmp = `${file}.tmp.${process.pid}`;
|
|
813
|
-
await
|
|
814
|
-
await
|
|
887
|
+
await fs3.writeFile(tmp, next, "utf8");
|
|
888
|
+
await fs3.rename(tmp, file);
|
|
815
889
|
if (!existed) return { status: "created", path: file };
|
|
816
890
|
if (hadBlock && items.length === 0) return { status: "removed", path: file };
|
|
817
891
|
return { status: "updated", path: file };
|