@alessandroraffa/tangyr 1.0.4 → 3.0.0
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 +2 -2
- package/dist/index.js +76 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -342,9 +342,9 @@ In `tangyr-kit.yaml`, `discipline` is optional: declare it to opt into the corpu
|
|
|
342
342
|
|
|
343
343
|
## Conflict policy
|
|
344
344
|
|
|
345
|
-
Before writing, each destination is classified and resolved according to `onConflict` in `tangyr.config.yaml`: `ask` (default; prompts, and resolves to
|
|
345
|
+
Before writing, each destination is classified and resolved according to `onConflict` in `tangyr.config.yaml`: `ask` (default; prompts, and resolves to **skip** when there is no one to ask), `skip`, `backup`, or `overwrite`.
|
|
346
346
|
|
|
347
|
-
`--yes` supplies the confirmation a non-interactive run needs; it does **not** override the posture. Under `--yes`, `skip` leaves conflicting destinations unchanged and says which, `overwrite` replaces without a backup, and `backup` copies the existing file — or the existing directory, recursively — into `.tangyr/backups/<timestamp>/` and records it in the manifest so `uninstall` can put it back. A symlink is
|
|
347
|
+
`--yes` supplies the confirmation a non-interactive run needs; it does **not** override the posture. Under `--yes`, `skip` leaves conflicting destinations unchanged and says which, `overwrite` replaces without a backup, `ask` leaves the destination alone and names the postures that replace, and `backup` copies the existing file — or the existing directory, recursively — into `.tangyr/backups/<timestamp>/` and records it in the manifest so `uninstall` can put it back. A symlink is backed up as a symlink and restored as one: its target is untouched either way, but the link itself is the operator's artifact — often the one committed with the project — so replacing it without a record left nothing to put back.
|
|
348
348
|
|
|
349
349
|
One decision is taken per destination, not per target, so a file several targets write — `AGENTS.md` is claimed by four — is backed up once.
|
|
350
350
|
|
package/dist/index.js
CHANGED
|
@@ -21980,6 +21980,25 @@ function realPathOrInput(filePath) {
|
|
|
21980
21980
|
return filePath;
|
|
21981
21981
|
}
|
|
21982
21982
|
}
|
|
21983
|
+
function clearSymlinkAtDestination(destination) {
|
|
21984
|
+
let stat;
|
|
21985
|
+
try {
|
|
21986
|
+
stat = fs23.lstatSync(destination);
|
|
21987
|
+
} catch {
|
|
21988
|
+
return null;
|
|
21989
|
+
}
|
|
21990
|
+
if (!stat.isSymbolicLink()) {
|
|
21991
|
+
return null;
|
|
21992
|
+
}
|
|
21993
|
+
let target = null;
|
|
21994
|
+
try {
|
|
21995
|
+
target = fs23.readlinkSync(destination);
|
|
21996
|
+
} catch {
|
|
21997
|
+
target = null;
|
|
21998
|
+
}
|
|
21999
|
+
fs23.unlinkSync(destination);
|
|
22000
|
+
return target ?? "(unreadable link target)";
|
|
22001
|
+
}
|
|
21983
22002
|
|
|
21984
22003
|
// src/core/artifact-state.ts
|
|
21985
22004
|
import fs24 from "fs";
|
|
@@ -22304,13 +22323,11 @@ async function runUninstallCommand(options, logger) {
|
|
|
22304
22323
|
}
|
|
22305
22324
|
}
|
|
22306
22325
|
if (fullUninstall) {
|
|
22307
|
-
const newestFirst = [...manifest.backups].reverse();
|
|
22308
22326
|
const restored = /* @__PURE__ */ new Set();
|
|
22309
|
-
|
|
22327
|
+
const kept = [];
|
|
22328
|
+
for (const backup of manifest.backups) {
|
|
22310
22329
|
if (restored.has(backup.originalPath)) {
|
|
22311
|
-
|
|
22312
|
-
` superseded by a newer backup, not restored: ${backup.backupLocation}`
|
|
22313
|
-
);
|
|
22330
|
+
kept.push(backup);
|
|
22314
22331
|
continue;
|
|
22315
22332
|
}
|
|
22316
22333
|
restored.add(backup.originalPath);
|
|
@@ -22322,6 +22339,19 @@ async function runUninstallCommand(options, logger) {
|
|
|
22322
22339
|
);
|
|
22323
22340
|
}
|
|
22324
22341
|
}
|
|
22342
|
+
if (kept.length > 0) {
|
|
22343
|
+
logger.warn(
|
|
22344
|
+
`
|
|
22345
|
+
${String(kept.length)} later backup(s) were NOT restored: the earliest copy of each location is the one that comes back, because it is the content from before the first install.`
|
|
22346
|
+
);
|
|
22347
|
+
for (const backup of kept) {
|
|
22348
|
+
logger.warn(` ${backup.originalPath}`);
|
|
22349
|
+
logger.warn(` later copy left at: ${backup.backupLocation}`);
|
|
22350
|
+
}
|
|
22351
|
+
logger.warn(
|
|
22352
|
+
" The manifest is removed below, so nothing will reference these copies afterwards. Move what you need out of the backups directory now."
|
|
22353
|
+
);
|
|
22354
|
+
}
|
|
22325
22355
|
const manifestPath = path24.join(scopePath, "manifest.json");
|
|
22326
22356
|
if (fs27.existsSync(manifestPath)) {
|
|
22327
22357
|
fs27.unlinkSync(manifestPath);
|
|
@@ -26598,6 +26628,11 @@ function syncDirectComponent(config, sourceRoot, options, logger, directComponen
|
|
|
26598
26628
|
};
|
|
26599
26629
|
} else if (artifactClass === "managed-symlink") {
|
|
26600
26630
|
fs36.unlinkSync(destination);
|
|
26631
|
+
} else if (artifactClass === "managed-compiled") {
|
|
26632
|
+
logger.warn(
|
|
26633
|
+
`${destination}: replacing another target's compiled instructions file with a link to the kit. A target that appends its rules here (opencode has no rules slot of its own) does not get them. Give it its own instructions path, or disable it.`
|
|
26634
|
+
);
|
|
26635
|
+
fs36.unlinkSync(destination);
|
|
26601
26636
|
}
|
|
26602
26637
|
ensureDir(path34.dirname(destination));
|
|
26603
26638
|
createRelativeSymlink(source, destination, directComponent.type, {
|
|
@@ -28591,6 +28626,12 @@ function writeCompiledArtifact(component, destination, content, expectedHash, co
|
|
|
28591
28626
|
return { component, status: "conflict", path: destination };
|
|
28592
28627
|
}
|
|
28593
28628
|
ensureDir(path37.dirname(destination));
|
|
28629
|
+
const clearedLink = clearSymlinkAtDestination(destination);
|
|
28630
|
+
if (clearedLink !== null) {
|
|
28631
|
+
logger.verbose(
|
|
28632
|
+
` ${destination}: replaced a symlink to ${clearedLink} with generated content`
|
|
28633
|
+
);
|
|
28634
|
+
}
|
|
28594
28635
|
fs38.writeFileSync(destination, content);
|
|
28595
28636
|
return { component, status: "success", path: destination };
|
|
28596
28637
|
}
|
|
@@ -29000,6 +29041,12 @@ function writeInstructionsFile(component, destination, content, contentHash, con
|
|
|
29000
29041
|
return { component, status: "conflict", path: destination };
|
|
29001
29042
|
}
|
|
29002
29043
|
ensureDir(path39.dirname(destination));
|
|
29044
|
+
const clearedLink = clearSymlinkAtDestination(destination);
|
|
29045
|
+
if (clearedLink !== null) {
|
|
29046
|
+
logger.verbose(
|
|
29047
|
+
` ${destination}: replaced a symlink to ${clearedLink} with generated content`
|
|
29048
|
+
);
|
|
29049
|
+
}
|
|
29003
29050
|
fs39.writeFileSync(destination, content);
|
|
29004
29051
|
return { component, status: "success", path: destination };
|
|
29005
29052
|
}
|
|
@@ -29192,6 +29239,12 @@ function writeCompiledArtifact2(component, destination, content, expectedHash, c
|
|
|
29192
29239
|
return { component, status: "conflict", path: destination };
|
|
29193
29240
|
}
|
|
29194
29241
|
ensureDir(path39.dirname(destination));
|
|
29242
|
+
const clearedLink = clearSymlinkAtDestination(destination);
|
|
29243
|
+
if (clearedLink !== null) {
|
|
29244
|
+
logger.verbose(
|
|
29245
|
+
` ${destination}: replaced a symlink to ${clearedLink} with generated content`
|
|
29246
|
+
);
|
|
29247
|
+
}
|
|
29195
29248
|
fs39.writeFileSync(destination, content);
|
|
29196
29249
|
return { component, status: "success", path: destination };
|
|
29197
29250
|
}
|
|
@@ -29827,6 +29880,12 @@ function writeCompiledArtifact3(component, destination, content, expectedHash, c
|
|
|
29827
29880
|
return { component, status: "conflict", path: destination };
|
|
29828
29881
|
}
|
|
29829
29882
|
ensureDir(path40.dirname(destination));
|
|
29883
|
+
const clearedLink = clearSymlinkAtDestination(destination);
|
|
29884
|
+
if (clearedLink !== null) {
|
|
29885
|
+
logger.verbose(
|
|
29886
|
+
` ${destination}: replaced a symlink to ${clearedLink} with generated content`
|
|
29887
|
+
);
|
|
29888
|
+
}
|
|
29830
29889
|
fs40.writeFileSync(destination, content);
|
|
29831
29890
|
return { component, status: "success", path: destination };
|
|
29832
29891
|
}
|
|
@@ -30302,6 +30361,12 @@ function writeCompiledArtifact4(component, destination, content, expectedHash, c
|
|
|
30302
30361
|
return { component, status: "conflict", path: destination };
|
|
30303
30362
|
}
|
|
30304
30363
|
ensureDir(path41.dirname(destination));
|
|
30364
|
+
const clearedLink = clearSymlinkAtDestination(destination);
|
|
30365
|
+
if (clearedLink !== null) {
|
|
30366
|
+
logger.verbose(
|
|
30367
|
+
` ${destination}: replaced a symlink to ${clearedLink} with generated content`
|
|
30368
|
+
);
|
|
30369
|
+
}
|
|
30305
30370
|
fs41.writeFileSync(destination, content);
|
|
30306
30371
|
return { component, status: "success", path: destination };
|
|
30307
30372
|
}
|
|
@@ -30795,6 +30860,12 @@ function writeCompiledArtifact5(component, destination, content, expectedHash, c
|
|
|
30795
30860
|
return { component, status: "conflict", path: destination };
|
|
30796
30861
|
}
|
|
30797
30862
|
ensureDir(path42.dirname(destination));
|
|
30863
|
+
const clearedLink = clearSymlinkAtDestination(destination);
|
|
30864
|
+
if (clearedLink !== null) {
|
|
30865
|
+
logger.verbose(
|
|
30866
|
+
` ${destination}: replaced a symlink to ${clearedLink} with generated content`
|
|
30867
|
+
);
|
|
30868
|
+
}
|
|
30798
30869
|
fs42.writeFileSync(destination, content);
|
|
30799
30870
|
return { component, status: "success", path: destination };
|
|
30800
30871
|
}
|