@alessandroraffa/tangyr 0.9.0 → 0.10.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 +45 -36
- package/dist/index.js +544 -142
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,23 +21,16 @@ tangyr <command>
|
|
|
21
21
|
|
|
22
22
|
### Kit management
|
|
23
23
|
|
|
24
|
-
| Command
|
|
25
|
-
|
|
|
26
|
-
| `tangyr install --kit <name>`
|
|
27
|
-
| `tangyr
|
|
28
|
-
| `tangyr
|
|
29
|
-
| `tangyr
|
|
30
|
-
| `tangyr
|
|
31
|
-
| `tangyr
|
|
32
|
-
| `tangyr
|
|
33
|
-
|
|
34
|
-
### Authentication
|
|
35
|
-
|
|
36
|
-
| Command | Description |
|
|
37
|
-
| -------------------- | ------------------------------------- |
|
|
38
|
-
| `tangyr auth login` | Store a license key for the Full tier |
|
|
39
|
-
| `tangyr auth status` | Show current authentication state |
|
|
40
|
-
| `tangyr auth logout` | Remove stored credentials |
|
|
24
|
+
| Command | Description |
|
|
25
|
+
| ---------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| `tangyr install --kit <name>` | Install a kit resolved from configured search paths (`kitSearchPaths`/`kit` in `tangyr.config.yaml`) |
|
|
27
|
+
| `tangyr install --kit-path <path>` | Install a kit from an explicit kit root path |
|
|
28
|
+
| `tangyr uninstall` | Remove Tangyr from the target scope |
|
|
29
|
+
| `tangyr sync` | Synchronize installed kit to the latest version |
|
|
30
|
+
| `tangyr verify` | Check installation integrity against the manifest |
|
|
31
|
+
| `tangyr cleanup` | Remove managed artifacts; delegates to uninstall if manifest exists |
|
|
32
|
+
| `tangyr assess` | Pre-installation conflict analysis |
|
|
33
|
+
| `tangyr list` | Display kits discovered in configured search paths and any installed manifests |
|
|
41
34
|
|
|
42
35
|
### Environment
|
|
43
36
|
|
|
@@ -58,15 +51,16 @@ tangyr <command>
|
|
|
58
51
|
|
|
59
52
|
## Global flags
|
|
60
53
|
|
|
61
|
-
| Flag
|
|
62
|
-
|
|
|
63
|
-
| `--config <path>`
|
|
64
|
-
| `--kit-path <path>`
|
|
65
|
-
| `--dry-run`
|
|
66
|
-
| `--yes`
|
|
67
|
-
| `--verbose`
|
|
68
|
-
| `--quiet`
|
|
69
|
-
| `--no-color`
|
|
54
|
+
| Flag | Description |
|
|
55
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
56
|
+
| `--config <path>` | Use an explicit configuration file |
|
|
57
|
+
| `--kit-path <path>` | Use an explicit operating kit root |
|
|
58
|
+
| `--dry-run` | Report actions without modifying files |
|
|
59
|
+
| `--yes` | Skip confirmation prompts |
|
|
60
|
+
| `--verbose` | Detailed output |
|
|
61
|
+
| `--quiet` | Errors only |
|
|
62
|
+
| `--no-color` | Disable colors |
|
|
63
|
+
| `--claude-config-dir <path>` | Claude Code global root directory (repeatable). Overrides `claudeCodeGlobalPaths` in the config file. |
|
|
70
64
|
|
|
71
65
|
## Supported tools
|
|
72
66
|
|
|
@@ -83,14 +77,12 @@ tangyr <command>
|
|
|
83
77
|
# Initialize a configuration in your project
|
|
84
78
|
npx @alessandroraffa/tangyr init
|
|
85
79
|
|
|
86
|
-
# Install a kit (dry-run first
|
|
87
|
-
npx @alessandroraffa/tangyr install --kit
|
|
88
|
-
npx @alessandroraffa/tangyr install --kit
|
|
80
|
+
# Install a kit by path (preview with --dry-run first)
|
|
81
|
+
npx @alessandroraffa/tangyr install --kit-path ./path/to/operating-kit --dry-run
|
|
82
|
+
npx @alessandroraffa/tangyr install --kit-path ./path/to/operating-kit
|
|
89
83
|
|
|
90
|
-
# Check installation status
|
|
84
|
+
# Check installation status, then sync after a kit update
|
|
91
85
|
npx @alessandroraffa/tangyr status
|
|
92
|
-
|
|
93
|
-
# Synchronize after a kit update
|
|
94
86
|
npx @alessandroraffa/tangyr sync
|
|
95
87
|
```
|
|
96
88
|
|
|
@@ -103,9 +95,26 @@ Every write command accepts `--scope global` or `--scope project`. The CLI flag
|
|
|
103
95
|
| `global` | `~/.tangyr/manifest.json` | `~/.claude/` |
|
|
104
96
|
| `project` | `.tangyr/manifest.json` | repo root (`AGENTS.md`, `.claude/`) |
|
|
105
97
|
|
|
106
|
-
##
|
|
98
|
+
## Multiple Claude Code global roots
|
|
99
|
+
|
|
100
|
+
If you maintain more than one Claude Code account (e.g. `~/.claude` and `~/.claude-work`), you can keep them identical with a single sync operation.
|
|
101
|
+
|
|
102
|
+
**Via config** (`tangyr.config.yaml`):
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
claudeCodeGlobalPaths:
|
|
106
|
+
- ~/.claude
|
|
107
|
+
- ~/.claude-work
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Via CLI flag** (repeatable, overrides the config field):
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
tangyr sync --claude-config-dir ~/.claude --claude-config-dir ~/.claude-work
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Precedence: `--claude-config-dir` flag > `claudeCodeGlobalPaths` in config > default `~/.claude`.
|
|
107
117
|
|
|
108
|
-
-
|
|
109
|
-
- **Full** — requires license key, complete agent roster and all skills
|
|
118
|
+
Per-root artifacts (instructions, agents, skills, commands, rules, output-styles, hooks settings) are written under each root. Shared artifacts (`~/.claude.json` for MCP, `~/.agents/hooks` for hooks) are resolved from the platform default and written once regardless of how many roots are configured.
|
|
110
119
|
|
|
111
|
-
|
|
120
|
+
The manifest records the full set of roots written so that `verify` and `uninstall` operate on all of them.
|
package/dist/index.js
CHANGED
|
@@ -1368,8 +1368,8 @@ var require_command = __commonJS({
|
|
|
1368
1368
|
"use strict";
|
|
1369
1369
|
var EventEmitter = __require("events").EventEmitter;
|
|
1370
1370
|
var childProcess = __require("child_process");
|
|
1371
|
-
var
|
|
1372
|
-
var
|
|
1371
|
+
var path40 = __require("path");
|
|
1372
|
+
var fs38 = __require("fs");
|
|
1373
1373
|
var process5 = __require("process");
|
|
1374
1374
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1375
1375
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2363,7 +2363,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2363
2363
|
* @param {string} subcommandName
|
|
2364
2364
|
*/
|
|
2365
2365
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2366
|
-
if (
|
|
2366
|
+
if (fs38.existsSync(executableFile)) return;
|
|
2367
2367
|
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
2368
2368
|
const executableMissing = `'${executableFile}' does not exist
|
|
2369
2369
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2381,11 +2381,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2381
2381
|
let launchWithNode = false;
|
|
2382
2382
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2383
2383
|
function findFile(baseDir, baseName) {
|
|
2384
|
-
const localBin =
|
|
2385
|
-
if (
|
|
2386
|
-
if (sourceExt.includes(
|
|
2384
|
+
const localBin = path40.resolve(baseDir, baseName);
|
|
2385
|
+
if (fs38.existsSync(localBin)) return localBin;
|
|
2386
|
+
if (sourceExt.includes(path40.extname(baseName))) return void 0;
|
|
2387
2387
|
const foundExt = sourceExt.find(
|
|
2388
|
-
(ext) =>
|
|
2388
|
+
(ext) => fs38.existsSync(`${localBin}${ext}`)
|
|
2389
2389
|
);
|
|
2390
2390
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2391
2391
|
return void 0;
|
|
@@ -2397,21 +2397,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2397
2397
|
if (this._scriptPath) {
|
|
2398
2398
|
let resolvedScriptPath;
|
|
2399
2399
|
try {
|
|
2400
|
-
resolvedScriptPath =
|
|
2400
|
+
resolvedScriptPath = fs38.realpathSync(this._scriptPath);
|
|
2401
2401
|
} catch {
|
|
2402
2402
|
resolvedScriptPath = this._scriptPath;
|
|
2403
2403
|
}
|
|
2404
|
-
executableDir =
|
|
2405
|
-
|
|
2404
|
+
executableDir = path40.resolve(
|
|
2405
|
+
path40.dirname(resolvedScriptPath),
|
|
2406
2406
|
executableDir
|
|
2407
2407
|
);
|
|
2408
2408
|
}
|
|
2409
2409
|
if (executableDir) {
|
|
2410
2410
|
let localFile = findFile(executableDir, executableFile);
|
|
2411
2411
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2412
|
-
const legacyName =
|
|
2412
|
+
const legacyName = path40.basename(
|
|
2413
2413
|
this._scriptPath,
|
|
2414
|
-
|
|
2414
|
+
path40.extname(this._scriptPath)
|
|
2415
2415
|
);
|
|
2416
2416
|
if (legacyName !== this._name) {
|
|
2417
2417
|
localFile = findFile(
|
|
@@ -2422,7 +2422,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2422
2422
|
}
|
|
2423
2423
|
executableFile = localFile || executableFile;
|
|
2424
2424
|
}
|
|
2425
|
-
launchWithNode = sourceExt.includes(
|
|
2425
|
+
launchWithNode = sourceExt.includes(path40.extname(executableFile));
|
|
2426
2426
|
let proc;
|
|
2427
2427
|
if (process5.platform !== "win32") {
|
|
2428
2428
|
if (launchWithNode) {
|
|
@@ -3337,7 +3337,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3337
3337
|
* @return {Command}
|
|
3338
3338
|
*/
|
|
3339
3339
|
nameFromFilename(filename) {
|
|
3340
|
-
this._name =
|
|
3340
|
+
this._name = path40.basename(filename, path40.extname(filename));
|
|
3341
3341
|
return this;
|
|
3342
3342
|
}
|
|
3343
3343
|
/**
|
|
@@ -3351,9 +3351,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3351
3351
|
* @param {string} [path]
|
|
3352
3352
|
* @return {(string|null|Command)}
|
|
3353
3353
|
*/
|
|
3354
|
-
executableDir(
|
|
3355
|
-
if (
|
|
3356
|
-
this._executableDir =
|
|
3354
|
+
executableDir(path41) {
|
|
3355
|
+
if (path41 === void 0) return this._executableDir;
|
|
3356
|
+
this._executableDir = path41;
|
|
3357
3357
|
return this;
|
|
3358
3358
|
}
|
|
3359
3359
|
/**
|
|
@@ -3709,17 +3709,17 @@ var require_visit = __commonJS({
|
|
|
3709
3709
|
visit.BREAK = BREAK;
|
|
3710
3710
|
visit.SKIP = SKIP;
|
|
3711
3711
|
visit.REMOVE = REMOVE;
|
|
3712
|
-
function visit_(key, node, visitor,
|
|
3713
|
-
const ctrl = callVisitor(key, node, visitor,
|
|
3712
|
+
function visit_(key, node, visitor, path40) {
|
|
3713
|
+
const ctrl = callVisitor(key, node, visitor, path40);
|
|
3714
3714
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
3715
|
-
replaceNode(key,
|
|
3716
|
-
return visit_(key, ctrl, visitor,
|
|
3715
|
+
replaceNode(key, path40, ctrl);
|
|
3716
|
+
return visit_(key, ctrl, visitor, path40);
|
|
3717
3717
|
}
|
|
3718
3718
|
if (typeof ctrl !== "symbol") {
|
|
3719
3719
|
if (identity.isCollection(node)) {
|
|
3720
|
-
|
|
3720
|
+
path40 = Object.freeze(path40.concat(node));
|
|
3721
3721
|
for (let i = 0; i < node.items.length; ++i) {
|
|
3722
|
-
const ci2 = visit_(i, node.items[i], visitor,
|
|
3722
|
+
const ci2 = visit_(i, node.items[i], visitor, path40);
|
|
3723
3723
|
if (typeof ci2 === "number")
|
|
3724
3724
|
i = ci2 - 1;
|
|
3725
3725
|
else if (ci2 === BREAK)
|
|
@@ -3730,13 +3730,13 @@ var require_visit = __commonJS({
|
|
|
3730
3730
|
}
|
|
3731
3731
|
}
|
|
3732
3732
|
} else if (identity.isPair(node)) {
|
|
3733
|
-
|
|
3734
|
-
const ck = visit_("key", node.key, visitor,
|
|
3733
|
+
path40 = Object.freeze(path40.concat(node));
|
|
3734
|
+
const ck = visit_("key", node.key, visitor, path40);
|
|
3735
3735
|
if (ck === BREAK)
|
|
3736
3736
|
return BREAK;
|
|
3737
3737
|
else if (ck === REMOVE)
|
|
3738
3738
|
node.key = null;
|
|
3739
|
-
const cv = visit_("value", node.value, visitor,
|
|
3739
|
+
const cv = visit_("value", node.value, visitor, path40);
|
|
3740
3740
|
if (cv === BREAK)
|
|
3741
3741
|
return BREAK;
|
|
3742
3742
|
else if (cv === REMOVE)
|
|
@@ -3757,17 +3757,17 @@ var require_visit = __commonJS({
|
|
|
3757
3757
|
visitAsync.BREAK = BREAK;
|
|
3758
3758
|
visitAsync.SKIP = SKIP;
|
|
3759
3759
|
visitAsync.REMOVE = REMOVE;
|
|
3760
|
-
async function visitAsync_(key, node, visitor,
|
|
3761
|
-
const ctrl = await callVisitor(key, node, visitor,
|
|
3760
|
+
async function visitAsync_(key, node, visitor, path40) {
|
|
3761
|
+
const ctrl = await callVisitor(key, node, visitor, path40);
|
|
3762
3762
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
3763
|
-
replaceNode(key,
|
|
3764
|
-
return visitAsync_(key, ctrl, visitor,
|
|
3763
|
+
replaceNode(key, path40, ctrl);
|
|
3764
|
+
return visitAsync_(key, ctrl, visitor, path40);
|
|
3765
3765
|
}
|
|
3766
3766
|
if (typeof ctrl !== "symbol") {
|
|
3767
3767
|
if (identity.isCollection(node)) {
|
|
3768
|
-
|
|
3768
|
+
path40 = Object.freeze(path40.concat(node));
|
|
3769
3769
|
for (let i = 0; i < node.items.length; ++i) {
|
|
3770
|
-
const ci2 = await visitAsync_(i, node.items[i], visitor,
|
|
3770
|
+
const ci2 = await visitAsync_(i, node.items[i], visitor, path40);
|
|
3771
3771
|
if (typeof ci2 === "number")
|
|
3772
3772
|
i = ci2 - 1;
|
|
3773
3773
|
else if (ci2 === BREAK)
|
|
@@ -3778,13 +3778,13 @@ var require_visit = __commonJS({
|
|
|
3778
3778
|
}
|
|
3779
3779
|
}
|
|
3780
3780
|
} else if (identity.isPair(node)) {
|
|
3781
|
-
|
|
3782
|
-
const ck = await visitAsync_("key", node.key, visitor,
|
|
3781
|
+
path40 = Object.freeze(path40.concat(node));
|
|
3782
|
+
const ck = await visitAsync_("key", node.key, visitor, path40);
|
|
3783
3783
|
if (ck === BREAK)
|
|
3784
3784
|
return BREAK;
|
|
3785
3785
|
else if (ck === REMOVE)
|
|
3786
3786
|
node.key = null;
|
|
3787
|
-
const cv = await visitAsync_("value", node.value, visitor,
|
|
3787
|
+
const cv = await visitAsync_("value", node.value, visitor, path40);
|
|
3788
3788
|
if (cv === BREAK)
|
|
3789
3789
|
return BREAK;
|
|
3790
3790
|
else if (cv === REMOVE)
|
|
@@ -3811,23 +3811,23 @@ var require_visit = __commonJS({
|
|
|
3811
3811
|
}
|
|
3812
3812
|
return visitor;
|
|
3813
3813
|
}
|
|
3814
|
-
function callVisitor(key, node, visitor,
|
|
3814
|
+
function callVisitor(key, node, visitor, path40) {
|
|
3815
3815
|
if (typeof visitor === "function")
|
|
3816
|
-
return visitor(key, node,
|
|
3816
|
+
return visitor(key, node, path40);
|
|
3817
3817
|
if (identity.isMap(node))
|
|
3818
|
-
return visitor.Map?.(key, node,
|
|
3818
|
+
return visitor.Map?.(key, node, path40);
|
|
3819
3819
|
if (identity.isSeq(node))
|
|
3820
|
-
return visitor.Seq?.(key, node,
|
|
3820
|
+
return visitor.Seq?.(key, node, path40);
|
|
3821
3821
|
if (identity.isPair(node))
|
|
3822
|
-
return visitor.Pair?.(key, node,
|
|
3822
|
+
return visitor.Pair?.(key, node, path40);
|
|
3823
3823
|
if (identity.isScalar(node))
|
|
3824
|
-
return visitor.Scalar?.(key, node,
|
|
3824
|
+
return visitor.Scalar?.(key, node, path40);
|
|
3825
3825
|
if (identity.isAlias(node))
|
|
3826
|
-
return visitor.Alias?.(key, node,
|
|
3826
|
+
return visitor.Alias?.(key, node, path40);
|
|
3827
3827
|
return void 0;
|
|
3828
3828
|
}
|
|
3829
|
-
function replaceNode(key,
|
|
3830
|
-
const parent =
|
|
3829
|
+
function replaceNode(key, path40, node) {
|
|
3830
|
+
const parent = path40[path40.length - 1];
|
|
3831
3831
|
if (identity.isCollection(parent)) {
|
|
3832
3832
|
parent.items[key] = node;
|
|
3833
3833
|
} else if (identity.isPair(parent)) {
|
|
@@ -4435,10 +4435,10 @@ var require_Collection = __commonJS({
|
|
|
4435
4435
|
var createNode = require_createNode();
|
|
4436
4436
|
var identity = require_identity();
|
|
4437
4437
|
var Node = require_Node();
|
|
4438
|
-
function collectionFromPath(schema,
|
|
4438
|
+
function collectionFromPath(schema, path40, value) {
|
|
4439
4439
|
let v2 = value;
|
|
4440
|
-
for (let i =
|
|
4441
|
-
const k2 =
|
|
4440
|
+
for (let i = path40.length - 1; i >= 0; --i) {
|
|
4441
|
+
const k2 = path40[i];
|
|
4442
4442
|
if (typeof k2 === "number" && Number.isInteger(k2) && k2 >= 0) {
|
|
4443
4443
|
const a = [];
|
|
4444
4444
|
a[k2] = v2;
|
|
@@ -4457,7 +4457,7 @@ var require_Collection = __commonJS({
|
|
|
4457
4457
|
sourceObjects: /* @__PURE__ */ new Map()
|
|
4458
4458
|
});
|
|
4459
4459
|
}
|
|
4460
|
-
var isEmptyPath = (
|
|
4460
|
+
var isEmptyPath = (path40) => path40 == null || typeof path40 === "object" && !!path40[Symbol.iterator]().next().done;
|
|
4461
4461
|
var Collection = class extends Node.NodeBase {
|
|
4462
4462
|
constructor(type, schema) {
|
|
4463
4463
|
super(type);
|
|
@@ -4487,11 +4487,11 @@ var require_Collection = __commonJS({
|
|
|
4487
4487
|
* be a Pair instance or a `{ key, value }` object, which may not have a key
|
|
4488
4488
|
* that already exists in the map.
|
|
4489
4489
|
*/
|
|
4490
|
-
addIn(
|
|
4491
|
-
if (isEmptyPath(
|
|
4490
|
+
addIn(path40, value) {
|
|
4491
|
+
if (isEmptyPath(path40))
|
|
4492
4492
|
this.add(value);
|
|
4493
4493
|
else {
|
|
4494
|
-
const [key, ...rest] =
|
|
4494
|
+
const [key, ...rest] = path40;
|
|
4495
4495
|
const node = this.get(key, true);
|
|
4496
4496
|
if (identity.isCollection(node))
|
|
4497
4497
|
node.addIn(rest, value);
|
|
@@ -4505,8 +4505,8 @@ var require_Collection = __commonJS({
|
|
|
4505
4505
|
* Removes a value from the collection.
|
|
4506
4506
|
* @returns `true` if the item was found and removed.
|
|
4507
4507
|
*/
|
|
4508
|
-
deleteIn(
|
|
4509
|
-
const [key, ...rest] =
|
|
4508
|
+
deleteIn(path40) {
|
|
4509
|
+
const [key, ...rest] = path40;
|
|
4510
4510
|
if (rest.length === 0)
|
|
4511
4511
|
return this.delete(key);
|
|
4512
4512
|
const node = this.get(key, true);
|
|
@@ -4520,8 +4520,8 @@ var require_Collection = __commonJS({
|
|
|
4520
4520
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
4521
4521
|
* `true` (collections are always returned intact).
|
|
4522
4522
|
*/
|
|
4523
|
-
getIn(
|
|
4524
|
-
const [key, ...rest] =
|
|
4523
|
+
getIn(path40, keepScalar) {
|
|
4524
|
+
const [key, ...rest] = path40;
|
|
4525
4525
|
const node = this.get(key, true);
|
|
4526
4526
|
if (rest.length === 0)
|
|
4527
4527
|
return !keepScalar && identity.isScalar(node) ? node.value : node;
|
|
@@ -4539,8 +4539,8 @@ var require_Collection = __commonJS({
|
|
|
4539
4539
|
/**
|
|
4540
4540
|
* Checks if the collection includes a value with the key `key`.
|
|
4541
4541
|
*/
|
|
4542
|
-
hasIn(
|
|
4543
|
-
const [key, ...rest] =
|
|
4542
|
+
hasIn(path40) {
|
|
4543
|
+
const [key, ...rest] = path40;
|
|
4544
4544
|
if (rest.length === 0)
|
|
4545
4545
|
return this.has(key);
|
|
4546
4546
|
const node = this.get(key, true);
|
|
@@ -4550,8 +4550,8 @@ var require_Collection = __commonJS({
|
|
|
4550
4550
|
* Sets a value in this collection. For `!!set`, `value` needs to be a
|
|
4551
4551
|
* boolean to add/remove the item from the set.
|
|
4552
4552
|
*/
|
|
4553
|
-
setIn(
|
|
4554
|
-
const [key, ...rest] =
|
|
4553
|
+
setIn(path40, value) {
|
|
4554
|
+
const [key, ...rest] = path40;
|
|
4555
4555
|
if (rest.length === 0) {
|
|
4556
4556
|
this.set(key, value);
|
|
4557
4557
|
} else {
|
|
@@ -7063,9 +7063,9 @@ var require_Document = __commonJS({
|
|
|
7063
7063
|
this.contents.add(value);
|
|
7064
7064
|
}
|
|
7065
7065
|
/** Adds a value to the document. */
|
|
7066
|
-
addIn(
|
|
7066
|
+
addIn(path40, value) {
|
|
7067
7067
|
if (assertCollection(this.contents))
|
|
7068
|
-
this.contents.addIn(
|
|
7068
|
+
this.contents.addIn(path40, value);
|
|
7069
7069
|
}
|
|
7070
7070
|
/**
|
|
7071
7071
|
* Create a new `Alias` node, ensuring that the target `node` has the required anchor.
|
|
@@ -7140,14 +7140,14 @@ var require_Document = __commonJS({
|
|
|
7140
7140
|
* Removes a value from the document.
|
|
7141
7141
|
* @returns `true` if the item was found and removed.
|
|
7142
7142
|
*/
|
|
7143
|
-
deleteIn(
|
|
7144
|
-
if (Collection.isEmptyPath(
|
|
7143
|
+
deleteIn(path40) {
|
|
7144
|
+
if (Collection.isEmptyPath(path40)) {
|
|
7145
7145
|
if (this.contents == null)
|
|
7146
7146
|
return false;
|
|
7147
7147
|
this.contents = null;
|
|
7148
7148
|
return true;
|
|
7149
7149
|
}
|
|
7150
|
-
return assertCollection(this.contents) ? this.contents.deleteIn(
|
|
7150
|
+
return assertCollection(this.contents) ? this.contents.deleteIn(path40) : false;
|
|
7151
7151
|
}
|
|
7152
7152
|
/**
|
|
7153
7153
|
* Returns item at `key`, or `undefined` if not found. By default unwraps
|
|
@@ -7162,10 +7162,10 @@ var require_Document = __commonJS({
|
|
|
7162
7162
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
7163
7163
|
* `true` (collections are always returned intact).
|
|
7164
7164
|
*/
|
|
7165
|
-
getIn(
|
|
7166
|
-
if (Collection.isEmptyPath(
|
|
7165
|
+
getIn(path40, keepScalar) {
|
|
7166
|
+
if (Collection.isEmptyPath(path40))
|
|
7167
7167
|
return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
|
|
7168
|
-
return identity.isCollection(this.contents) ? this.contents.getIn(
|
|
7168
|
+
return identity.isCollection(this.contents) ? this.contents.getIn(path40, keepScalar) : void 0;
|
|
7169
7169
|
}
|
|
7170
7170
|
/**
|
|
7171
7171
|
* Checks if the document includes a value with the key `key`.
|
|
@@ -7176,10 +7176,10 @@ var require_Document = __commonJS({
|
|
|
7176
7176
|
/**
|
|
7177
7177
|
* Checks if the document includes a value at `path`.
|
|
7178
7178
|
*/
|
|
7179
|
-
hasIn(
|
|
7180
|
-
if (Collection.isEmptyPath(
|
|
7179
|
+
hasIn(path40) {
|
|
7180
|
+
if (Collection.isEmptyPath(path40))
|
|
7181
7181
|
return this.contents !== void 0;
|
|
7182
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
7182
|
+
return identity.isCollection(this.contents) ? this.contents.hasIn(path40) : false;
|
|
7183
7183
|
}
|
|
7184
7184
|
/**
|
|
7185
7185
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
@@ -7196,13 +7196,13 @@ var require_Document = __commonJS({
|
|
|
7196
7196
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
7197
7197
|
* boolean to add/remove the item from the set.
|
|
7198
7198
|
*/
|
|
7199
|
-
setIn(
|
|
7200
|
-
if (Collection.isEmptyPath(
|
|
7199
|
+
setIn(path40, value) {
|
|
7200
|
+
if (Collection.isEmptyPath(path40)) {
|
|
7201
7201
|
this.contents = value;
|
|
7202
7202
|
} else if (this.contents == null) {
|
|
7203
|
-
this.contents = Collection.collectionFromPath(this.schema, Array.from(
|
|
7203
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path40), value);
|
|
7204
7204
|
} else if (assertCollection(this.contents)) {
|
|
7205
|
-
this.contents.setIn(
|
|
7205
|
+
this.contents.setIn(path40, value);
|
|
7206
7206
|
}
|
|
7207
7207
|
}
|
|
7208
7208
|
/**
|
|
@@ -9159,9 +9159,9 @@ var require_cst_visit = __commonJS({
|
|
|
9159
9159
|
visit.BREAK = BREAK;
|
|
9160
9160
|
visit.SKIP = SKIP;
|
|
9161
9161
|
visit.REMOVE = REMOVE;
|
|
9162
|
-
visit.itemAtPath = (cst,
|
|
9162
|
+
visit.itemAtPath = (cst, path40) => {
|
|
9163
9163
|
let item = cst;
|
|
9164
|
-
for (const [field, index] of
|
|
9164
|
+
for (const [field, index] of path40) {
|
|
9165
9165
|
const tok = item?.[field];
|
|
9166
9166
|
if (tok && "items" in tok) {
|
|
9167
9167
|
item = tok.items[index];
|
|
@@ -9170,23 +9170,23 @@ var require_cst_visit = __commonJS({
|
|
|
9170
9170
|
}
|
|
9171
9171
|
return item;
|
|
9172
9172
|
};
|
|
9173
|
-
visit.parentCollection = (cst,
|
|
9174
|
-
const parent = visit.itemAtPath(cst,
|
|
9175
|
-
const field =
|
|
9173
|
+
visit.parentCollection = (cst, path40) => {
|
|
9174
|
+
const parent = visit.itemAtPath(cst, path40.slice(0, -1));
|
|
9175
|
+
const field = path40[path40.length - 1][0];
|
|
9176
9176
|
const coll = parent?.[field];
|
|
9177
9177
|
if (coll && "items" in coll)
|
|
9178
9178
|
return coll;
|
|
9179
9179
|
throw new Error("Parent collection not found");
|
|
9180
9180
|
};
|
|
9181
|
-
function _visit(
|
|
9182
|
-
let ctrl = visitor(item,
|
|
9181
|
+
function _visit(path40, item, visitor) {
|
|
9182
|
+
let ctrl = visitor(item, path40);
|
|
9183
9183
|
if (typeof ctrl === "symbol")
|
|
9184
9184
|
return ctrl;
|
|
9185
9185
|
for (const field of ["key", "value"]) {
|
|
9186
9186
|
const token = item[field];
|
|
9187
9187
|
if (token && "items" in token) {
|
|
9188
9188
|
for (let i = 0; i < token.items.length; ++i) {
|
|
9189
|
-
const ci2 = _visit(Object.freeze(
|
|
9189
|
+
const ci2 = _visit(Object.freeze(path40.concat([[field, i]])), token.items[i], visitor);
|
|
9190
9190
|
if (typeof ci2 === "number")
|
|
9191
9191
|
i = ci2 - 1;
|
|
9192
9192
|
else if (ci2 === BREAK)
|
|
@@ -9197,10 +9197,10 @@ var require_cst_visit = __commonJS({
|
|
|
9197
9197
|
}
|
|
9198
9198
|
}
|
|
9199
9199
|
if (typeof ctrl === "function" && field === "key")
|
|
9200
|
-
ctrl = ctrl(item,
|
|
9200
|
+
ctrl = ctrl(item, path40);
|
|
9201
9201
|
}
|
|
9202
9202
|
}
|
|
9203
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
9203
|
+
return typeof ctrl === "function" ? ctrl(item, path40) : ctrl;
|
|
9204
9204
|
}
|
|
9205
9205
|
exports2.visit = visit;
|
|
9206
9206
|
}
|
|
@@ -10485,14 +10485,14 @@ var require_parser = __commonJS({
|
|
|
10485
10485
|
case "scalar":
|
|
10486
10486
|
case "single-quoted-scalar":
|
|
10487
10487
|
case "double-quoted-scalar": {
|
|
10488
|
-
const
|
|
10488
|
+
const fs38 = this.flowScalar(this.type);
|
|
10489
10489
|
if (atNextItem || it2.value) {
|
|
10490
|
-
map.items.push({ start, key:
|
|
10490
|
+
map.items.push({ start, key: fs38, sep: [] });
|
|
10491
10491
|
this.onKeyLine = true;
|
|
10492
10492
|
} else if (it2.sep) {
|
|
10493
|
-
this.stack.push(
|
|
10493
|
+
this.stack.push(fs38);
|
|
10494
10494
|
} else {
|
|
10495
|
-
Object.assign(it2, { key:
|
|
10495
|
+
Object.assign(it2, { key: fs38, sep: [] });
|
|
10496
10496
|
this.onKeyLine = true;
|
|
10497
10497
|
}
|
|
10498
10498
|
return;
|
|
@@ -10620,13 +10620,13 @@ var require_parser = __commonJS({
|
|
|
10620
10620
|
case "scalar":
|
|
10621
10621
|
case "single-quoted-scalar":
|
|
10622
10622
|
case "double-quoted-scalar": {
|
|
10623
|
-
const
|
|
10623
|
+
const fs38 = this.flowScalar(this.type);
|
|
10624
10624
|
if (!it2 || it2.value)
|
|
10625
|
-
fc.items.push({ start: [], key:
|
|
10625
|
+
fc.items.push({ start: [], key: fs38, sep: [] });
|
|
10626
10626
|
else if (it2.sep)
|
|
10627
|
-
this.stack.push(
|
|
10627
|
+
this.stack.push(fs38);
|
|
10628
10628
|
else
|
|
10629
|
-
Object.assign(it2, { key:
|
|
10629
|
+
Object.assign(it2, { key: fs38, sep: [] });
|
|
10630
10630
|
return;
|
|
10631
10631
|
}
|
|
10632
10632
|
case "flow-map-end":
|
|
@@ -15011,7 +15011,10 @@ function normalizeConfig(config) {
|
|
|
15011
15011
|
kit: config.kit,
|
|
15012
15012
|
kitPath: config.kitPath,
|
|
15013
15013
|
kitSearchPaths: config.kitSearchPaths,
|
|
15014
|
-
scope: config.scope
|
|
15014
|
+
scope: config.scope,
|
|
15015
|
+
// claudeCodeGlobalPaths: pass through as-is; ~ expansion happens at
|
|
15016
|
+
// resolve time so relative entries are not forced absolute here
|
|
15017
|
+
claudeCodeGlobalPaths: config.claudeCodeGlobalPaths
|
|
15015
15018
|
};
|
|
15016
15019
|
}
|
|
15017
15020
|
function findConfigPath(explicitPath) {
|
|
@@ -15249,7 +15252,10 @@ function readManifest(scopePath) {
|
|
|
15249
15252
|
backupLocation: b.backupLocation,
|
|
15250
15253
|
integerSuffix: typeof b.integerSuffix === "number" ? b.integerSuffix : 0
|
|
15251
15254
|
})),
|
|
15252
|
-
managedLinks: Array.isArray(parsed.managedLinks) ? parsed.managedLinks : []
|
|
15255
|
+
managedLinks: Array.isArray(parsed.managedLinks) ? parsed.managedLinks : [],
|
|
15256
|
+
claudeCodeGlobalRoots: Array.isArray(parsed.claudeCodeGlobalRoots) ? parsed.claudeCodeGlobalRoots.filter(
|
|
15257
|
+
(r) => typeof r === "string"
|
|
15258
|
+
) : void 0
|
|
15253
15259
|
};
|
|
15254
15260
|
}
|
|
15255
15261
|
function readStrictManifest(scopePath) {
|
|
@@ -15637,6 +15643,66 @@ function resolveClineProfile(scope, projectRoot, mappings) {
|
|
|
15637
15643
|
mcp_file: r(slotMap, "mcp_file")
|
|
15638
15644
|
};
|
|
15639
15645
|
}
|
|
15646
|
+
var DEFAULT_CLAUDE_ROOT_RAW = "~/.claude";
|
|
15647
|
+
function resolveClaudeGlobalRoots(input) {
|
|
15648
|
+
const rawRoots = (input.flag?.length ?? 0) > 0 ? input.flag : (input.config?.length ?? 0) > 0 ? input.config : [DEFAULT_CLAUDE_ROOT_RAW];
|
|
15649
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15650
|
+
const roots = [];
|
|
15651
|
+
for (const raw of rawRoots) {
|
|
15652
|
+
const abs = path8.resolve(expandHome(raw));
|
|
15653
|
+
if (!seen.has(abs)) {
|
|
15654
|
+
seen.add(abs);
|
|
15655
|
+
roots.push(abs);
|
|
15656
|
+
}
|
|
15657
|
+
}
|
|
15658
|
+
return roots;
|
|
15659
|
+
}
|
|
15660
|
+
var UNDER_ROOT_SLOTS = /* @__PURE__ */ new Set([
|
|
15661
|
+
"instructions",
|
|
15662
|
+
"instructions_shim",
|
|
15663
|
+
"agents",
|
|
15664
|
+
"skills",
|
|
15665
|
+
"commands",
|
|
15666
|
+
"rules",
|
|
15667
|
+
"output_styles",
|
|
15668
|
+
"hooks_shared_file",
|
|
15669
|
+
"settings_shared_file"
|
|
15670
|
+
]);
|
|
15671
|
+
function resolveClaudeCodeProfileForRoot(root, mappings) {
|
|
15672
|
+
const slotMap = mappings.targets["claude-code"];
|
|
15673
|
+
if (!slotMap) {
|
|
15674
|
+
throw new Error(
|
|
15675
|
+
"platform-paths.yaml: missing claude-code target entry \u2014 run the loader validator first"
|
|
15676
|
+
);
|
|
15677
|
+
}
|
|
15678
|
+
const r = makeResolver("global", void 0, "claude-code");
|
|
15679
|
+
const defaultProfile = {
|
|
15680
|
+
instructions: r(slotMap, "instructions"),
|
|
15681
|
+
instructions_shim: r(slotMap, "instructions_shim"),
|
|
15682
|
+
agents: r(slotMap, "agents"),
|
|
15683
|
+
skills: r(slotMap, "skills"),
|
|
15684
|
+
commands: r(slotMap, "commands"),
|
|
15685
|
+
rules: r(slotMap, "rules"),
|
|
15686
|
+
output_styles: r(slotMap, "output_styles"),
|
|
15687
|
+
hooks_shared_file: r(slotMap, "hooks_shared_file"),
|
|
15688
|
+
settings_shared_file: r(slotMap, "settings_shared_file"),
|
|
15689
|
+
mcp_shared_file: r(slotMap, "mcp_shared_file"),
|
|
15690
|
+
hooks: r(slotMap, "hooks")
|
|
15691
|
+
};
|
|
15692
|
+
const defaultRoot = defaultProfile.agents !== void 0 ? path8.dirname(defaultProfile.agents) : path8.resolve(expandHome(DEFAULT_CLAUDE_ROOT_RAW));
|
|
15693
|
+
if (root === defaultRoot) {
|
|
15694
|
+
return defaultProfile;
|
|
15695
|
+
}
|
|
15696
|
+
const profile = { ...defaultProfile };
|
|
15697
|
+
for (const slot of UNDER_ROOT_SLOTS) {
|
|
15698
|
+
const slotValue = defaultProfile[slot];
|
|
15699
|
+
if (slotValue.startsWith(defaultRoot)) {
|
|
15700
|
+
const suffix = slotValue.slice(defaultRoot.length);
|
|
15701
|
+
profile[slot] = root + suffix;
|
|
15702
|
+
}
|
|
15703
|
+
}
|
|
15704
|
+
return profile;
|
|
15705
|
+
}
|
|
15640
15706
|
function makeResolver(scope, projectRoot, tool) {
|
|
15641
15707
|
return (slotMap, slot) => {
|
|
15642
15708
|
if (scope === "global") {
|
|
@@ -20144,6 +20210,10 @@ function removeManagedPath(managedPath) {
|
|
|
20144
20210
|
// src/commands/uninstall.ts
|
|
20145
20211
|
var codexManagedMcpStart = "# tangyr: managed mcp start";
|
|
20146
20212
|
var codexManagedMcpEnd = "# tangyr: managed mcp end";
|
|
20213
|
+
var SHARED_CLAUDE_SLOTS = /* @__PURE__ */ new Set([
|
|
20214
|
+
"mcp_shared_file",
|
|
20215
|
+
"hooks"
|
|
20216
|
+
]);
|
|
20147
20217
|
async function runUninstallCommand(options, logger) {
|
|
20148
20218
|
const scope = options.scope === "global" ? "global" : "project";
|
|
20149
20219
|
const scopePath = resolveScopePath(
|
|
@@ -20200,19 +20270,38 @@ async function runUninstallCommand(options, logger) {
|
|
|
20200
20270
|
return;
|
|
20201
20271
|
}
|
|
20202
20272
|
}
|
|
20273
|
+
const claudeRoots = scope === "global" && selectedTools.has("claude-code") ? resolveClaudeRootsForUninstall(manifest, runtime) : [];
|
|
20203
20274
|
let removedCount = 0;
|
|
20204
20275
|
for (const file of manifestArtifacts) {
|
|
20205
|
-
const
|
|
20206
|
-
|
|
20207
|
-
|
|
20208
|
-
|
|
20209
|
-
|
|
20210
|
-
|
|
20211
|
-
|
|
20212
|
-
|
|
20213
|
-
|
|
20276
|
+
const isClaudeGlobal = file.relativePath.startsWith("claude-code/") && scope === "global";
|
|
20277
|
+
if (isClaudeGlobal && claudeRoots.length > 0) {
|
|
20278
|
+
const paths = resolveManifestKeyToMultiPaths(
|
|
20279
|
+
file.relativePath,
|
|
20280
|
+
claudeRoots,
|
|
20281
|
+
runtime
|
|
20282
|
+
);
|
|
20283
|
+
for (const actualPath of paths) {
|
|
20284
|
+
if (actualPath && (fs18.existsSync(actualPath) || isSymlink(actualPath))) {
|
|
20285
|
+
removeManagedPath(actualPath);
|
|
20286
|
+
logger.verbose(` removed: ${actualPath}`);
|
|
20287
|
+
removedCount++;
|
|
20288
|
+
} else {
|
|
20289
|
+
logger.verbose(` skipped (not found): ${actualPath}`);
|
|
20290
|
+
}
|
|
20291
|
+
}
|
|
20214
20292
|
} else {
|
|
20215
|
-
|
|
20293
|
+
const actualPath = resolveManifestKeyToPath(
|
|
20294
|
+
file.relativePath,
|
|
20295
|
+
scope,
|
|
20296
|
+
runtime
|
|
20297
|
+
);
|
|
20298
|
+
if (actualPath && (fs18.existsSync(actualPath) || isSymlink(actualPath))) {
|
|
20299
|
+
removeManagedPath(actualPath);
|
|
20300
|
+
logger.verbose(` removed: ${actualPath}`);
|
|
20301
|
+
removedCount++;
|
|
20302
|
+
} else {
|
|
20303
|
+
logger.verbose(` skipped (not found): ${file.relativePath}`);
|
|
20304
|
+
}
|
|
20216
20305
|
}
|
|
20217
20306
|
}
|
|
20218
20307
|
if (runtime) {
|
|
@@ -20442,6 +20531,102 @@ function resolveClaudeCodePaths(runtime, scope, projectRoot) {
|
|
|
20442
20531
|
output_styles: path17.join(root, ".claude", "output-styles")
|
|
20443
20532
|
};
|
|
20444
20533
|
}
|
|
20534
|
+
function resolveClaudeRootsForUninstall(manifest, runtime) {
|
|
20535
|
+
if (Array.isArray(manifest.claudeCodeGlobalRoots) && manifest.claudeCodeGlobalRoots.length > 0) {
|
|
20536
|
+
return manifest.claudeCodeGlobalRoots;
|
|
20537
|
+
}
|
|
20538
|
+
if (runtime) {
|
|
20539
|
+
try {
|
|
20540
|
+
const mappings = loadMappings(
|
|
20541
|
+
runtime.sourceRoot,
|
|
20542
|
+
runtime.config,
|
|
20543
|
+
runtime.configDir
|
|
20544
|
+
);
|
|
20545
|
+
const profile = resolveTargetPaths({
|
|
20546
|
+
scope: "global",
|
|
20547
|
+
projectRoot: void 0,
|
|
20548
|
+
mappings: mappings.platformPaths
|
|
20549
|
+
})["claude-code"];
|
|
20550
|
+
return [path17.dirname(profile.agents)];
|
|
20551
|
+
} catch {
|
|
20552
|
+
}
|
|
20553
|
+
}
|
|
20554
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20555
|
+
return [path17.resolve(expandHome(`${home}/.claude`))];
|
|
20556
|
+
}
|
|
20557
|
+
function resolveManifestKeyToMultiPaths(key, claudeRoots, runtime) {
|
|
20558
|
+
if (claudeRoots.length === 0) {
|
|
20559
|
+
return [];
|
|
20560
|
+
}
|
|
20561
|
+
const afterPrefix = key.slice("claude-code/".length);
|
|
20562
|
+
const slotName = afterPrefix.split("/")[0];
|
|
20563
|
+
const subPath = afterPrefix.slice(slotName.length + 1) || void 0;
|
|
20564
|
+
const isShared = SHARED_CLAUDE_SLOTS.has(slotName);
|
|
20565
|
+
const rootsToResolve = isShared ? [claudeRoots[0]] : claudeRoots;
|
|
20566
|
+
if (!runtime) {
|
|
20567
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20568
|
+
const defaultRoot = path17.resolve(expandHome(`${home}/.claude`));
|
|
20569
|
+
return rootsToResolve.flatMap((root) => {
|
|
20570
|
+
const hardcoded = resolveHardcodedClaudeSlot(root, defaultRoot, slotName);
|
|
20571
|
+
if (!hardcoded) {
|
|
20572
|
+
return [];
|
|
20573
|
+
}
|
|
20574
|
+
return [subPath ? path17.join(hardcoded, subPath) : hardcoded];
|
|
20575
|
+
});
|
|
20576
|
+
}
|
|
20577
|
+
try {
|
|
20578
|
+
const mappings = loadMappings(
|
|
20579
|
+
runtime.sourceRoot,
|
|
20580
|
+
runtime.config,
|
|
20581
|
+
runtime.configDir
|
|
20582
|
+
);
|
|
20583
|
+
return rootsToResolve.flatMap((root) => {
|
|
20584
|
+
const profile = resolveClaudeCodeProfileForRoot(
|
|
20585
|
+
root,
|
|
20586
|
+
mappings.platformPaths
|
|
20587
|
+
);
|
|
20588
|
+
const effectiveSlot = slotName === "CLAUDE.md" ? "instructions" : slotName;
|
|
20589
|
+
const slotPath = profile[effectiveSlot] ?? null;
|
|
20590
|
+
if (!slotPath) {
|
|
20591
|
+
return [];
|
|
20592
|
+
}
|
|
20593
|
+
return [subPath ? path17.join(slotPath, subPath) : slotPath];
|
|
20594
|
+
});
|
|
20595
|
+
} catch {
|
|
20596
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20597
|
+
const defaultRoot = path17.resolve(expandHome(`${home}/.claude`));
|
|
20598
|
+
return rootsToResolve.flatMap((root) => {
|
|
20599
|
+
const hardcoded = resolveHardcodedClaudeSlot(root, defaultRoot, slotName);
|
|
20600
|
+
if (!hardcoded) {
|
|
20601
|
+
return [];
|
|
20602
|
+
}
|
|
20603
|
+
return [subPath ? path17.join(hardcoded, subPath) : hardcoded];
|
|
20604
|
+
});
|
|
20605
|
+
}
|
|
20606
|
+
}
|
|
20607
|
+
function resolveHardcodedClaudeSlot(root, defaultRoot, slotName) {
|
|
20608
|
+
const slotRelatives = {
|
|
20609
|
+
instructions: "CLAUDE.md",
|
|
20610
|
+
CLAUDE: "CLAUDE.md",
|
|
20611
|
+
"CLAUDE.md": "CLAUDE.md",
|
|
20612
|
+
instructions_shim: "CLAUDE.md",
|
|
20613
|
+
agents: "agents",
|
|
20614
|
+
skills: "skills",
|
|
20615
|
+
commands: "commands",
|
|
20616
|
+
rules: "rules",
|
|
20617
|
+
output_styles: "output-styles",
|
|
20618
|
+
hooks_shared_file: "settings.json",
|
|
20619
|
+
settings_shared_file: "settings.json"
|
|
20620
|
+
};
|
|
20621
|
+
const relative = slotRelatives[slotName];
|
|
20622
|
+
if (!relative) {
|
|
20623
|
+
return null;
|
|
20624
|
+
}
|
|
20625
|
+
if (root === defaultRoot) {
|
|
20626
|
+
return path17.join(root, relative);
|
|
20627
|
+
}
|
|
20628
|
+
return path17.join(root, relative);
|
|
20629
|
+
}
|
|
20445
20630
|
function resolveRuntimeIfAvailable(options, logger) {
|
|
20446
20631
|
try {
|
|
20447
20632
|
return resolveRuntime(options, import.meta.url);
|
|
@@ -26307,17 +26492,54 @@ function installForTool(tool, config, kitInfo, manifest, options, mappings, logg
|
|
|
26307
26492
|
if (tool !== "claude-code") {
|
|
26308
26493
|
throwUnrecognizedTarget(tool);
|
|
26309
26494
|
}
|
|
26310
|
-
const placements = getToolPlacements(
|
|
26311
|
-
tool,
|
|
26312
|
-
kitInfo,
|
|
26313
|
-
scope,
|
|
26314
|
-
projectRoot,
|
|
26315
|
-
mappings
|
|
26316
|
-
);
|
|
26317
|
-
let count = 0;
|
|
26318
26495
|
manifest.artifacts = manifest.artifacts.filter(
|
|
26319
26496
|
(file) => !file.relativePath.startsWith(`${tool}/`)
|
|
26320
26497
|
);
|
|
26498
|
+
const claudeRoots = scope === "global" && mappings ? resolveClaudeGlobalRoots({
|
|
26499
|
+
flag: options.claudeConfigDir,
|
|
26500
|
+
config: config.claudeCodeGlobalPaths
|
|
26501
|
+
}) : void 0;
|
|
26502
|
+
let count = 0;
|
|
26503
|
+
if (claudeRoots && claudeRoots.length > 0 && mappings) {
|
|
26504
|
+
for (const claudeRoot of claudeRoots) {
|
|
26505
|
+
if (claudeRoots.length > 1) {
|
|
26506
|
+
logger.info(` claude-code: installing to root ${claudeRoot}`);
|
|
26507
|
+
}
|
|
26508
|
+
const rootProfile = resolveClaudeCodeProfileForRoot(
|
|
26509
|
+
claudeRoot,
|
|
26510
|
+
mappings.platformPaths
|
|
26511
|
+
);
|
|
26512
|
+
const placements = getClaudeCodePlacementsFromProfile(
|
|
26513
|
+
kitInfo,
|
|
26514
|
+
rootProfile
|
|
26515
|
+
);
|
|
26516
|
+
count += applyClaudeCodePlacements(
|
|
26517
|
+
placements,
|
|
26518
|
+
manifest,
|
|
26519
|
+
skippedTargetPaths,
|
|
26520
|
+
logger
|
|
26521
|
+
);
|
|
26522
|
+
}
|
|
26523
|
+
manifest.claudeCodeGlobalRoots = claudeRoots;
|
|
26524
|
+
} else {
|
|
26525
|
+
const placements = getToolPlacements(
|
|
26526
|
+
tool,
|
|
26527
|
+
kitInfo,
|
|
26528
|
+
scope,
|
|
26529
|
+
projectRoot,
|
|
26530
|
+
mappings
|
|
26531
|
+
);
|
|
26532
|
+
count += applyClaudeCodePlacements(
|
|
26533
|
+
placements,
|
|
26534
|
+
manifest,
|
|
26535
|
+
skippedTargetPaths,
|
|
26536
|
+
logger
|
|
26537
|
+
);
|
|
26538
|
+
}
|
|
26539
|
+
return count;
|
|
26540
|
+
}
|
|
26541
|
+
function applyClaudeCodePlacements(placements, manifest, skippedTargetPaths, logger) {
|
|
26542
|
+
let count = 0;
|
|
26321
26543
|
for (const placement of placements) {
|
|
26322
26544
|
if (skippedTargetPaths.has(placement.destination)) {
|
|
26323
26545
|
logger.verbose(` skipped (operator choice): ${placement.destination}`);
|
|
@@ -26785,6 +27007,38 @@ function getClaudeCodePlacements(kitInfo, scope = "global", projectRoot, mapping
|
|
|
26785
27007
|
}
|
|
26786
27008
|
return placements;
|
|
26787
27009
|
}
|
|
27010
|
+
function getClaudeCodePlacementsFromProfile(kitInfo, profile) {
|
|
27011
|
+
const placements = [];
|
|
27012
|
+
const claudeMd = path31.join(kitInfo.path, "CLAUDE.md");
|
|
27013
|
+
if (fs30.existsSync(claudeMd)) {
|
|
27014
|
+
placements.push({
|
|
27015
|
+
source: claudeMd,
|
|
27016
|
+
destination: profile.instructions,
|
|
27017
|
+
manifestKey: "claude-code/CLAUDE.md",
|
|
27018
|
+
type: "symlink",
|
|
27019
|
+
isDir: false
|
|
27020
|
+
});
|
|
27021
|
+
}
|
|
27022
|
+
const componentDirMap = {
|
|
27023
|
+
agents: profile.agents,
|
|
27024
|
+
skills: profile.skills,
|
|
27025
|
+
commands: profile.commands,
|
|
27026
|
+
rules: profile.rules
|
|
27027
|
+
};
|
|
27028
|
+
for (const [component, targetPath] of Object.entries(componentDirMap)) {
|
|
27029
|
+
const componentDir = path31.join(kitInfo.path, component);
|
|
27030
|
+
if (fs30.existsSync(componentDir) && fs30.statSync(componentDir).isDirectory()) {
|
|
27031
|
+
placements.push({
|
|
27032
|
+
source: componentDir,
|
|
27033
|
+
destination: targetPath,
|
|
27034
|
+
manifestKey: `claude-code/${component}`,
|
|
27035
|
+
type: "symlink",
|
|
27036
|
+
isDir: true
|
|
27037
|
+
});
|
|
27038
|
+
}
|
|
27039
|
+
}
|
|
27040
|
+
return placements;
|
|
27041
|
+
}
|
|
26788
27042
|
function collectAllFiles(kitInfo) {
|
|
26789
27043
|
const files = /* @__PURE__ */ new Map();
|
|
26790
27044
|
const claudeMd = path31.join(kitInfo.path, "CLAUDE.md");
|
|
@@ -27017,18 +27271,13 @@ function runStatusCommand(options, logger) {
|
|
|
27017
27271
|
if (target !== "claude-code" && target !== "copilot" && target !== "codex") {
|
|
27018
27272
|
throwUnrecognizedTarget(target);
|
|
27019
27273
|
}
|
|
27020
|
-
const rows = target === "claude-code" ?
|
|
27021
|
-
|
|
27022
|
-
|
|
27023
|
-
|
|
27024
|
-
|
|
27025
|
-
|
|
27026
|
-
|
|
27027
|
-
).map(
|
|
27028
|
-
(artifact) => classifyDirectArtifact(artifact, config, sourceRoot)
|
|
27029
|
-
),
|
|
27030
|
-
...classifyClaudeSharedFiles(config, sourceRoot)
|
|
27031
|
-
] : target === "copilot" ? classifyCopilotFiles(config, sourceRoot, configDir) : classifyCodexFiles(config, sourceRoot, configDir);
|
|
27274
|
+
const rows = target === "claude-code" ? classifyClaudeCodeRows(
|
|
27275
|
+
sourceRoot,
|
|
27276
|
+
config,
|
|
27277
|
+
configDir,
|
|
27278
|
+
scope,
|
|
27279
|
+
projectRoot
|
|
27280
|
+
) : target === "copilot" ? classifyCopilotFiles(config, sourceRoot, configDir) : classifyCodexFiles(config, sourceRoot, configDir);
|
|
27032
27281
|
for (const row of rows) {
|
|
27033
27282
|
logger.info(`${row.component} ${row.className} ${row.path}`);
|
|
27034
27283
|
if (!row.conformant) {
|
|
@@ -27297,6 +27546,64 @@ function classifyDirectArtifact(artifact, config, sourceRoot) {
|
|
|
27297
27546
|
conformant: artifactClass === "managed-symlink" || artifactClass === "managed-compiled"
|
|
27298
27547
|
};
|
|
27299
27548
|
}
|
|
27549
|
+
function classifyClaudeCodeRows(sourceRoot, config, configDir, scope, projectRoot) {
|
|
27550
|
+
const extraRoots = [];
|
|
27551
|
+
if (scope === "global") {
|
|
27552
|
+
try {
|
|
27553
|
+
const scopePath = resolveScopePath(scope, projectRoot);
|
|
27554
|
+
const manifest = readManifest(scopePath);
|
|
27555
|
+
if (manifest && Array.isArray(manifest.claudeCodeGlobalRoots) && manifest.claudeCodeGlobalRoots.length > 1) {
|
|
27556
|
+
extraRoots.push(...manifest.claudeCodeGlobalRoots.slice(1));
|
|
27557
|
+
}
|
|
27558
|
+
} catch {
|
|
27559
|
+
}
|
|
27560
|
+
}
|
|
27561
|
+
const defaultArtifactRows = claudeDirectArtifacts(
|
|
27562
|
+
sourceRoot,
|
|
27563
|
+
config,
|
|
27564
|
+
configDir,
|
|
27565
|
+
scope,
|
|
27566
|
+
projectRoot
|
|
27567
|
+
).map((artifact) => classifyDirectArtifact(artifact, config, sourceRoot));
|
|
27568
|
+
const extraRootRows = [];
|
|
27569
|
+
if (extraRoots.length > 0) {
|
|
27570
|
+
try {
|
|
27571
|
+
const mappings = loadMappings(sourceRoot, config, configDir);
|
|
27572
|
+
for (const root of extraRoots) {
|
|
27573
|
+
const profile = resolveClaudeCodeProfileForRoot(
|
|
27574
|
+
root,
|
|
27575
|
+
mappings.platformPaths
|
|
27576
|
+
);
|
|
27577
|
+
for (const [component, targetPath] of Object.entries(profile)) {
|
|
27578
|
+
if (component.endsWith("_shared_file") || component === "hooks") {
|
|
27579
|
+
continue;
|
|
27580
|
+
}
|
|
27581
|
+
const sourceKey = claudeDirectComponentSourceKeys[component];
|
|
27582
|
+
if (!sourceKey || !targetPath) {
|
|
27583
|
+
continue;
|
|
27584
|
+
}
|
|
27585
|
+
extraRootRows.push(
|
|
27586
|
+
classifyDirectArtifact(
|
|
27587
|
+
{
|
|
27588
|
+
component: `${component.replace(/_/gu, "-")} [${root}]`,
|
|
27589
|
+
path: targetPath,
|
|
27590
|
+
sourceKey
|
|
27591
|
+
},
|
|
27592
|
+
config,
|
|
27593
|
+
sourceRoot
|
|
27594
|
+
)
|
|
27595
|
+
);
|
|
27596
|
+
}
|
|
27597
|
+
}
|
|
27598
|
+
} catch {
|
|
27599
|
+
}
|
|
27600
|
+
}
|
|
27601
|
+
return [
|
|
27602
|
+
...defaultArtifactRows,
|
|
27603
|
+
...extraRootRows,
|
|
27604
|
+
...classifyClaudeSharedFiles(config, sourceRoot)
|
|
27605
|
+
];
|
|
27606
|
+
}
|
|
27300
27607
|
function classifyClaudeSharedFiles(config, sourceRoot) {
|
|
27301
27608
|
const settingsPath = resolveTargetPath("~/.claude/settings.json");
|
|
27302
27609
|
const claudeJsonPath = resolveTargetPath("~/.claude.json");
|
|
@@ -28137,7 +28444,9 @@ function syncClaudeCode(config, sourceRoot, options, logger, mappings, manifest,
|
|
|
28137
28444
|
const scope = options.scope ?? "global";
|
|
28138
28445
|
const projectRoot = options.projectRoot;
|
|
28139
28446
|
let profile;
|
|
28140
|
-
if (mappings) {
|
|
28447
|
+
if (options.claudeRoot && mappings) {
|
|
28448
|
+
profile = resolveClaudeCodeProfileForRoot(options.claudeRoot, mappings);
|
|
28449
|
+
} else if (mappings) {
|
|
28141
28450
|
profile = resolveTargetPaths({
|
|
28142
28451
|
scope,
|
|
28143
28452
|
projectRoot,
|
|
@@ -28560,19 +28869,42 @@ function runSyncCommand(options, logger) {
|
|
|
28560
28869
|
const allMappings = mappings ?? (targets.includes("claude-code") ? loadMappings(sourceRoot, config, configDir) : void 0);
|
|
28561
28870
|
for (const target of targets) {
|
|
28562
28871
|
if (target === "claude-code") {
|
|
28563
|
-
|
|
28564
|
-
|
|
28565
|
-
|
|
28566
|
-
|
|
28567
|
-
|
|
28568
|
-
|
|
28569
|
-
|
|
28570
|
-
|
|
28571
|
-
|
|
28572
|
-
|
|
28573
|
-
|
|
28574
|
-
|
|
28575
|
-
|
|
28872
|
+
const claudeRoots = syncOptions.scope === "global" && allMappings ? resolveClaudeGlobalRoots({
|
|
28873
|
+
flag: options.claudeConfigDir,
|
|
28874
|
+
config: config.claudeCodeGlobalPaths
|
|
28875
|
+
}) : void 0;
|
|
28876
|
+
if (claudeRoots && claudeRoots.length > 1) {
|
|
28877
|
+
for (const claudeRoot of claudeRoots) {
|
|
28878
|
+
logger.info(` claude-code: syncing root ${claudeRoot}`);
|
|
28879
|
+
results.push(
|
|
28880
|
+
syncClaudeCode(
|
|
28881
|
+
config,
|
|
28882
|
+
sourceRoot,
|
|
28883
|
+
{ ...syncOptions, claudeRoot },
|
|
28884
|
+
logger,
|
|
28885
|
+
allMappings?.platformPaths,
|
|
28886
|
+
void 0,
|
|
28887
|
+
void 0,
|
|
28888
|
+
allMappings,
|
|
28889
|
+
lossReport
|
|
28890
|
+
)
|
|
28891
|
+
);
|
|
28892
|
+
}
|
|
28893
|
+
} else {
|
|
28894
|
+
results.push(
|
|
28895
|
+
syncClaudeCode(
|
|
28896
|
+
config,
|
|
28897
|
+
sourceRoot,
|
|
28898
|
+
syncOptions,
|
|
28899
|
+
logger,
|
|
28900
|
+
allMappings?.platformPaths,
|
|
28901
|
+
void 0,
|
|
28902
|
+
void 0,
|
|
28903
|
+
allMappings,
|
|
28904
|
+
lossReport
|
|
28905
|
+
)
|
|
28906
|
+
);
|
|
28907
|
+
}
|
|
28576
28908
|
} else if (target === "copilot") {
|
|
28577
28909
|
if (!mappings) {
|
|
28578
28910
|
throw new Error("Copilot mappings were not loaded");
|
|
@@ -29444,8 +29776,13 @@ function checkHumanOnlyCommandInvocation(kit) {
|
|
|
29444
29776
|
}
|
|
29445
29777
|
|
|
29446
29778
|
// src/commands/verify.ts
|
|
29779
|
+
import fs37 from "fs";
|
|
29780
|
+
import path39 from "path";
|
|
29447
29781
|
function runVerifyCommand(options, logger) {
|
|
29448
|
-
const { config, kitInfo } = resolveRuntime(
|
|
29782
|
+
const { config, configDir, sourceRoot, kitInfo } = resolveRuntime(
|
|
29783
|
+
options,
|
|
29784
|
+
import.meta.url
|
|
29785
|
+
);
|
|
29449
29786
|
const { scope, projectRoot } = resolveScopeContext(
|
|
29450
29787
|
options.scope,
|
|
29451
29788
|
config.scope,
|
|
@@ -29476,6 +29813,27 @@ function runVerifyCommand(options, logger) {
|
|
|
29476
29813
|
);
|
|
29477
29814
|
inconsistent = true;
|
|
29478
29815
|
}
|
|
29816
|
+
if (scope === "global" && Array.isArray(manifest.claudeCodeGlobalRoots) && manifest.claudeCodeGlobalRoots.length > 1) {
|
|
29817
|
+
try {
|
|
29818
|
+
const mappings = loadMappings(sourceRoot, config, configDir);
|
|
29819
|
+
const manifestKeys = manifest.artifacts.map((a) => a.relativePath);
|
|
29820
|
+
const rootFailures = verifyClaudeCodeGlobalRoots(
|
|
29821
|
+
manifest.claudeCodeGlobalRoots,
|
|
29822
|
+
manifestKeys,
|
|
29823
|
+
mappings.platformPaths
|
|
29824
|
+
);
|
|
29825
|
+
if (rootFailures.length > 0) {
|
|
29826
|
+
for (const failure of rootFailures) {
|
|
29827
|
+
logger.warn(failure);
|
|
29828
|
+
}
|
|
29829
|
+
inconsistent = true;
|
|
29830
|
+
}
|
|
29831
|
+
} catch (err) {
|
|
29832
|
+
logger.warn(
|
|
29833
|
+
`Multi-root verify skipped (mappings unavailable): ${err instanceof Error ? err.message : String(err)}`
|
|
29834
|
+
);
|
|
29835
|
+
}
|
|
29836
|
+
}
|
|
29479
29837
|
if (inconsistent) {
|
|
29480
29838
|
logger.error("Verification FAILED. Run 'tangyr sync' to update.");
|
|
29481
29839
|
process.exit(1);
|
|
@@ -29487,6 +29845,39 @@ function runVerifyCommand(options, logger) {
|
|
|
29487
29845
|
}
|
|
29488
29846
|
runStatusCommand(options, logger);
|
|
29489
29847
|
}
|
|
29848
|
+
function verifyClaudeCodeGlobalRoots(roots, manifestKeys, mappings) {
|
|
29849
|
+
const failures = [];
|
|
29850
|
+
const perRootSlots = [];
|
|
29851
|
+
for (const key of manifestKeys) {
|
|
29852
|
+
if (!key.startsWith("claude-code/")) {
|
|
29853
|
+
continue;
|
|
29854
|
+
}
|
|
29855
|
+
const afterPrefix = key.slice("claude-code/".length);
|
|
29856
|
+
const slotName = afterPrefix.split("/")[0];
|
|
29857
|
+
const subPath = afterPrefix.slice(slotName.length + 1) || void 0;
|
|
29858
|
+
if (SHARED_CLAUDE_SLOTS.has(slotName)) {
|
|
29859
|
+
continue;
|
|
29860
|
+
}
|
|
29861
|
+
const effectiveSlot = slotName === "CLAUDE.md" ? "instructions" : slotName;
|
|
29862
|
+
perRootSlots.push({ slot: effectiveSlot, subPath });
|
|
29863
|
+
}
|
|
29864
|
+
for (const root of roots) {
|
|
29865
|
+
const profile = resolveClaudeCodeProfileForRoot(root, mappings);
|
|
29866
|
+
for (const { slot, subPath } of perRootSlots) {
|
|
29867
|
+
const slotPath = profile[slot] ?? null;
|
|
29868
|
+
if (!slotPath) {
|
|
29869
|
+
continue;
|
|
29870
|
+
}
|
|
29871
|
+
const fullPath = subPath ? path39.join(slotPath, subPath) : slotPath;
|
|
29872
|
+
if (!fs37.existsSync(fullPath)) {
|
|
29873
|
+
failures.push(
|
|
29874
|
+
`claude-code/${slot}${subPath ? `/${subPath}` : ""} missing in root ${root}: ${fullPath}`
|
|
29875
|
+
);
|
|
29876
|
+
}
|
|
29877
|
+
}
|
|
29878
|
+
}
|
|
29879
|
+
return failures;
|
|
29880
|
+
}
|
|
29490
29881
|
|
|
29491
29882
|
// src/utils/logger.ts
|
|
29492
29883
|
var Logger = class {
|
|
@@ -29576,7 +29967,12 @@ function buildProgram() {
|
|
|
29576
29967
|
).option(
|
|
29577
29968
|
"-y, --yes",
|
|
29578
29969
|
"Skip interactive confirmation and apply non-interactive conflict handling"
|
|
29579
|
-
).option("-v, --verbose", "Emit detailed output").option("-q, --quiet", "Emit errors only").option("--no-color", "Disable colorized output")
|
|
29970
|
+
).option("-v, --verbose", "Emit detailed output").option("-q, --quiet", "Emit errors only").option("--no-color", "Disable colorized output").option(
|
|
29971
|
+
"--claude-config-dir <path>",
|
|
29972
|
+
"Claude Code global root directory (repeatable; overrides claudeCodeGlobalPaths in config)",
|
|
29973
|
+
(value, previous) => [...previous, value],
|
|
29974
|
+
[]
|
|
29975
|
+
);
|
|
29580
29976
|
program2.command("assess").description("Pre-installation conflict analysis").option("--tools <list>", "Comma-separated list of target tools to assess").option("--scope <scope>", "Installation scope: global or project").option("--json", "Emit JSON output").action((options) => {
|
|
29581
29977
|
const context = commandContext(program2);
|
|
29582
29978
|
const scope = options.scope === "global" || options.scope === "project" ? options.scope : void 0;
|
|
@@ -29585,7 +29981,10 @@ function buildProgram() {
|
|
|
29585
29981
|
context.logger
|
|
29586
29982
|
);
|
|
29587
29983
|
});
|
|
29588
|
-
program2.command("install").description("Install the Tangyr operating kit into the target scope").option("--kit <name>", "Name of the operating kit to install").option("--tools <list>", "Comma-separated list of target tools").option("--scope <scope>", "Installation scope: global or project").option(
|
|
29984
|
+
program2.command("install").description("Install the Tangyr operating kit into the target scope").option("--kit <name>", "Name of the operating kit to install").option("--tools <list>", "Comma-separated list of target tools").option("--scope <scope>", "Installation scope: global or project").option(
|
|
29985
|
+
"--ignore-errors",
|
|
29986
|
+
"Suppress exit code 3 when error-severity loss entries are present"
|
|
29987
|
+
).action(
|
|
29589
29988
|
async (options) => {
|
|
29590
29989
|
const context = commandContext(program2);
|
|
29591
29990
|
await runInstallCommand(
|
|
@@ -29622,7 +30021,10 @@ function buildProgram() {
|
|
|
29622
30021
|
context.logger
|
|
29623
30022
|
);
|
|
29624
30023
|
});
|
|
29625
|
-
program2.command("sync").description("Synchronize configured targets with the operating kit").option("--target <tool>", "Limit sync to a single target").option("--component <name>", "Limit sync to a single component type").option("--force", "Rewrite managed artifacts even when provenance matches").option("--loss-report <path>", "Write semantic loss report JSON to a file").option("--scope <scope>", "Installation scope: global or project").option(
|
|
30024
|
+
program2.command("sync").description("Synchronize configured targets with the operating kit").option("--target <tool>", "Limit sync to a single target").option("--component <name>", "Limit sync to a single component type").option("--force", "Rewrite managed artifacts even when provenance matches").option("--loss-report <path>", "Write semantic loss report JSON to a file").option("--scope <scope>", "Installation scope: global or project").option(
|
|
30025
|
+
"--ignore-errors",
|
|
30026
|
+
"Suppress exit code 3 when error-severity loss entries are present"
|
|
30027
|
+
).action(
|
|
29626
30028
|
(options) => {
|
|
29627
30029
|
const context = commandContext(program2);
|
|
29628
30030
|
const scope = options.scope === "global" || options.scope === "project" ? options.scope : void 0;
|