@alessandroraffa/tangyr 0.19.0 → 0.20.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/dist/index.js +922 -486
- package/mappings/hook-events.yaml +97 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1373,7 +1373,7 @@ var require_command = __commonJS({
|
|
|
1373
1373
|
var EventEmitter = __require("events").EventEmitter;
|
|
1374
1374
|
var childProcess = __require("child_process");
|
|
1375
1375
|
var path45 = __require("path");
|
|
1376
|
-
var
|
|
1376
|
+
var fs45 = __require("fs");
|
|
1377
1377
|
var process5 = __require("process");
|
|
1378
1378
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1379
1379
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2367,7 +2367,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2367
2367
|
* @param {string} subcommandName
|
|
2368
2368
|
*/
|
|
2369
2369
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2370
|
-
if (
|
|
2370
|
+
if (fs45.existsSync(executableFile)) return;
|
|
2371
2371
|
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";
|
|
2372
2372
|
const executableMissing = `'${executableFile}' does not exist
|
|
2373
2373
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2386,10 +2386,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2386
2386
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2387
2387
|
function findFile(baseDir, baseName) {
|
|
2388
2388
|
const localBin = path45.resolve(baseDir, baseName);
|
|
2389
|
-
if (
|
|
2389
|
+
if (fs45.existsSync(localBin)) return localBin;
|
|
2390
2390
|
if (sourceExt.includes(path45.extname(baseName))) return void 0;
|
|
2391
2391
|
const foundExt = sourceExt.find(
|
|
2392
|
-
(ext) =>
|
|
2392
|
+
(ext) => fs45.existsSync(`${localBin}${ext}`)
|
|
2393
2393
|
);
|
|
2394
2394
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2395
2395
|
return void 0;
|
|
@@ -2401,7 +2401,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2401
2401
|
if (this._scriptPath) {
|
|
2402
2402
|
let resolvedScriptPath;
|
|
2403
2403
|
try {
|
|
2404
|
-
resolvedScriptPath =
|
|
2404
|
+
resolvedScriptPath = fs45.realpathSync(this._scriptPath);
|
|
2405
2405
|
} catch {
|
|
2406
2406
|
resolvedScriptPath = this._scriptPath;
|
|
2407
2407
|
}
|
|
@@ -10489,14 +10489,14 @@ var require_parser = __commonJS({
|
|
|
10489
10489
|
case "scalar":
|
|
10490
10490
|
case "single-quoted-scalar":
|
|
10491
10491
|
case "double-quoted-scalar": {
|
|
10492
|
-
const
|
|
10492
|
+
const fs45 = this.flowScalar(this.type);
|
|
10493
10493
|
if (atNextItem || it2.value) {
|
|
10494
|
-
map.items.push({ start, key:
|
|
10494
|
+
map.items.push({ start, key: fs45, sep: [] });
|
|
10495
10495
|
this.onKeyLine = true;
|
|
10496
10496
|
} else if (it2.sep) {
|
|
10497
|
-
this.stack.push(
|
|
10497
|
+
this.stack.push(fs45);
|
|
10498
10498
|
} else {
|
|
10499
|
-
Object.assign(it2, { key:
|
|
10499
|
+
Object.assign(it2, { key: fs45, sep: [] });
|
|
10500
10500
|
this.onKeyLine = true;
|
|
10501
10501
|
}
|
|
10502
10502
|
return;
|
|
@@ -10624,13 +10624,13 @@ var require_parser = __commonJS({
|
|
|
10624
10624
|
case "scalar":
|
|
10625
10625
|
case "single-quoted-scalar":
|
|
10626
10626
|
case "double-quoted-scalar": {
|
|
10627
|
-
const
|
|
10627
|
+
const fs45 = this.flowScalar(this.type);
|
|
10628
10628
|
if (!it2 || it2.value)
|
|
10629
|
-
fc.items.push({ start: [], key:
|
|
10629
|
+
fc.items.push({ start: [], key: fs45, sep: [] });
|
|
10630
10630
|
else if (it2.sep)
|
|
10631
|
-
this.stack.push(
|
|
10631
|
+
this.stack.push(fs45);
|
|
10632
10632
|
else
|
|
10633
|
-
Object.assign(it2, { key:
|
|
10633
|
+
Object.assign(it2, { key: fs45, sep: [] });
|
|
10634
10634
|
return;
|
|
10635
10635
|
}
|
|
10636
10636
|
case "flow-map-end":
|
|
@@ -12551,22 +12551,22 @@ var require_parse_async = __commonJS({
|
|
|
12551
12551
|
const index = 0;
|
|
12552
12552
|
const blocksize = opts.blocksize || 40960;
|
|
12553
12553
|
const parser = new TOMLParser();
|
|
12554
|
-
return new Promise((resolve,
|
|
12555
|
-
setImmediate(parseAsyncNext, index, blocksize, resolve,
|
|
12554
|
+
return new Promise((resolve, reject2) => {
|
|
12555
|
+
setImmediate(parseAsyncNext, index, blocksize, resolve, reject2);
|
|
12556
12556
|
});
|
|
12557
|
-
function parseAsyncNext(index2, blocksize2, resolve,
|
|
12557
|
+
function parseAsyncNext(index2, blocksize2, resolve, reject2) {
|
|
12558
12558
|
if (index2 >= str.length) {
|
|
12559
12559
|
try {
|
|
12560
12560
|
return resolve(parser.finish());
|
|
12561
12561
|
} catch (err) {
|
|
12562
|
-
return
|
|
12562
|
+
return reject2(prettyError(err, str));
|
|
12563
12563
|
}
|
|
12564
12564
|
}
|
|
12565
12565
|
try {
|
|
12566
12566
|
parser.parse(str.slice(index2, index2 + blocksize2));
|
|
12567
|
-
setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2, resolve,
|
|
12567
|
+
setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2, resolve, reject2);
|
|
12568
12568
|
} catch (err) {
|
|
12569
|
-
|
|
12569
|
+
reject2(prettyError(err, str));
|
|
12570
12570
|
}
|
|
12571
12571
|
}
|
|
12572
12572
|
}
|
|
@@ -12590,7 +12590,7 @@ var require_parse_stream = __commonJS({
|
|
|
12590
12590
|
function parseReadable(stm) {
|
|
12591
12591
|
const parser = new TOMLParser();
|
|
12592
12592
|
stm.setEncoding("utf8");
|
|
12593
|
-
return new Promise((resolve,
|
|
12593
|
+
return new Promise((resolve, reject2) => {
|
|
12594
12594
|
let readable;
|
|
12595
12595
|
let ended = false;
|
|
12596
12596
|
let errored = false;
|
|
@@ -12600,12 +12600,12 @@ var require_parse_stream = __commonJS({
|
|
|
12600
12600
|
try {
|
|
12601
12601
|
resolve(parser.finish());
|
|
12602
12602
|
} catch (err) {
|
|
12603
|
-
|
|
12603
|
+
reject2(err);
|
|
12604
12604
|
}
|
|
12605
12605
|
}
|
|
12606
12606
|
function error(err) {
|
|
12607
12607
|
errored = true;
|
|
12608
|
-
|
|
12608
|
+
reject2(err);
|
|
12609
12609
|
}
|
|
12610
12610
|
stm.once("end", finish);
|
|
12611
12611
|
stm.once("error", error);
|
|
@@ -14307,12 +14307,12 @@ var PromisePolyfill = class extends Promise {
|
|
|
14307
14307
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
|
|
14308
14308
|
static withResolver() {
|
|
14309
14309
|
let resolve;
|
|
14310
|
-
let
|
|
14310
|
+
let reject2;
|
|
14311
14311
|
const promise = new Promise((res, rej) => {
|
|
14312
14312
|
resolve = res;
|
|
14313
|
-
|
|
14313
|
+
reject2 = rej;
|
|
14314
14314
|
});
|
|
14315
|
-
return { promise, resolve, reject };
|
|
14315
|
+
return { promise, resolve, reject: reject2 };
|
|
14316
14316
|
}
|
|
14317
14317
|
};
|
|
14318
14318
|
|
|
@@ -14349,10 +14349,10 @@ function createPrompt(view) {
|
|
|
14349
14349
|
output
|
|
14350
14350
|
});
|
|
14351
14351
|
const screen = new ScreenManager(rl);
|
|
14352
|
-
const { promise, resolve, reject } = PromisePolyfill.withResolver();
|
|
14353
|
-
const cancel = () =>
|
|
14352
|
+
const { promise, resolve, reject: reject2 } = PromisePolyfill.withResolver();
|
|
14353
|
+
const cancel = () => reject2(new CancelPromptError());
|
|
14354
14354
|
if (signal) {
|
|
14355
|
-
const abort = () =>
|
|
14355
|
+
const abort = () => reject2(new AbortPromptError({ cause: signal.reason }));
|
|
14356
14356
|
if (signal.aborted) {
|
|
14357
14357
|
abort();
|
|
14358
14358
|
return Object.assign(promise, { cancel });
|
|
@@ -14361,9 +14361,9 @@ function createPrompt(view) {
|
|
|
14361
14361
|
cleanups.add(() => signal.removeEventListener("abort", abort));
|
|
14362
14362
|
}
|
|
14363
14363
|
cleanups.add(onExit((code, signal2) => {
|
|
14364
|
-
|
|
14364
|
+
reject2(new ExitPromptError(`User force closed the prompt with ${code} ${signal2}`));
|
|
14365
14365
|
}));
|
|
14366
|
-
const sigint = () =>
|
|
14366
|
+
const sigint = () => reject2(new ExitPromptError(`User force closed the prompt with SIGINT`));
|
|
14367
14367
|
rl.on("SIGINT", sigint);
|
|
14368
14368
|
cleanups.add(() => rl.removeListener("SIGINT", sigint));
|
|
14369
14369
|
return withHooks(rl, (cycle) => {
|
|
@@ -14397,7 +14397,7 @@ function createPrompt(view) {
|
|
|
14397
14397
|
screen.render(content, bottomContent);
|
|
14398
14398
|
effectScheduler.run();
|
|
14399
14399
|
} catch (error) {
|
|
14400
|
-
|
|
14400
|
+
reject2(error);
|
|
14401
14401
|
}
|
|
14402
14402
|
effectsSettled = true;
|
|
14403
14403
|
if (pendingDone !== null) {
|
|
@@ -15667,7 +15667,11 @@ function vscodeUserDataDir() {
|
|
|
15667
15667
|
return expandHome(process.env.VSCODE_USER_DATA_DIR);
|
|
15668
15668
|
}
|
|
15669
15669
|
if (process.env.VSCODE_PORTABLE) {
|
|
15670
|
-
return path7.join(
|
|
15670
|
+
return path7.join(
|
|
15671
|
+
expandHome(process.env.VSCODE_PORTABLE),
|
|
15672
|
+
"user-data",
|
|
15673
|
+
"User"
|
|
15674
|
+
);
|
|
15671
15675
|
}
|
|
15672
15676
|
const homeDir = canonicalHomeDir();
|
|
15673
15677
|
if (process.platform === "darwin") {
|
|
@@ -16154,6 +16158,25 @@ var KIT_COMPONENTS = [
|
|
|
16154
16158
|
"rules",
|
|
16155
16159
|
"hooks"
|
|
16156
16160
|
];
|
|
16161
|
+
var DEFAULT_ENTRYPOINT = "ORIENTATION.md";
|
|
16162
|
+
var LEGACY_ENTRYPOINT = "CLAUDE.md";
|
|
16163
|
+
function resolveEntrypoint(kit) {
|
|
16164
|
+
const declared = kit.entrypoint;
|
|
16165
|
+
if (declared !== void 0) {
|
|
16166
|
+
const file = path9.join(kit.path, declared);
|
|
16167
|
+
return fs8.existsSync(file) ? { file, name: declared, legacy: false } : null;
|
|
16168
|
+
}
|
|
16169
|
+
const canonical = path9.join(kit.path, DEFAULT_ENTRYPOINT);
|
|
16170
|
+
if (fs8.existsSync(canonical)) {
|
|
16171
|
+
return { file: canonical, name: DEFAULT_ENTRYPOINT, legacy: false };
|
|
16172
|
+
}
|
|
16173
|
+
const legacy = path9.join(kit.path, LEGACY_ENTRYPOINT);
|
|
16174
|
+
if (fs8.existsSync(legacy)) {
|
|
16175
|
+
return { file: legacy, name: LEGACY_ENTRYPOINT, legacy: true };
|
|
16176
|
+
}
|
|
16177
|
+
return null;
|
|
16178
|
+
}
|
|
16179
|
+
var LEGACY_ENTRYPOINT_NOTICE = `This kit has no '${DEFAULT_ENTRYPOINT}' and no 'entrypoint' in tangyr-kit.yaml, so '${LEGACY_ENTRYPOINT}' was used. '${DEFAULT_ENTRYPOINT}' is the contract's canonical orientation document; '${LEGACY_ENTRYPOINT}' is one of the names targets translate it to, and a kit that ships only the translation is naming a target rather than itself. To fix: rename '${LEGACY_ENTRYPOINT}' to '${DEFAULT_ENTRYPOINT}', or add "entrypoint: ${LEGACY_ENTRYPOINT}" to tangyr-kit.yaml to keep the name deliberately.`;
|
|
16157
16180
|
function discoverKits(searchPaths) {
|
|
16158
16181
|
const kits = [];
|
|
16159
16182
|
for (const searchPath of searchPaths) {
|
|
@@ -16198,6 +16221,7 @@ function loadKit(kitPath) {
|
|
|
16198
16221
|
version: manifestResult.version,
|
|
16199
16222
|
description: manifestResult.description,
|
|
16200
16223
|
archetype: manifestResult.archetype,
|
|
16224
|
+
...manifestResult.entrypoint !== void 0 ? { entrypoint: manifestResult.entrypoint } : {},
|
|
16201
16225
|
discipline: manifestResult.discipline,
|
|
16202
16226
|
components
|
|
16203
16227
|
};
|
|
@@ -17845,7 +17869,7 @@ async function runCacheClearCommand(options, logger) {
|
|
|
17845
17869
|
}
|
|
17846
17870
|
|
|
17847
17871
|
// src/commands/cleanup.ts
|
|
17848
|
-
import
|
|
17872
|
+
import fs25 from "fs";
|
|
17849
17873
|
import path22 from "path";
|
|
17850
17874
|
|
|
17851
17875
|
// node_modules/glob/dist/esm/index.min.js
|
|
@@ -20992,7 +21016,7 @@ function removeManagedEntries(filePath, parser, serializer, removeManaged, logge
|
|
|
20992
21016
|
}
|
|
20993
21017
|
|
|
20994
21018
|
// src/commands/uninstall.ts
|
|
20995
|
-
import
|
|
21019
|
+
import fs24 from "fs";
|
|
20996
21020
|
import path21 from "path";
|
|
20997
21021
|
|
|
20998
21022
|
// src/core/managed-sources.ts
|
|
@@ -21043,10 +21067,11 @@ function hooksSourceExists(sourceRoot, config) {
|
|
|
21043
21067
|
}
|
|
21044
21068
|
return fs20.readdirSync(hooksDir).some((f) => f.endsWith(".md") && !f.startsWith("."));
|
|
21045
21069
|
}
|
|
21046
|
-
function hooksSourceLabel(config) {
|
|
21047
|
-
|
|
21048
|
-
|
|
21070
|
+
function hooksSourceLabel(sourceRoot, config) {
|
|
21071
|
+
const dir = normalizeManagedSourcePath(
|
|
21072
|
+
config.source.hooks.replace(/\\/gu, "/")
|
|
21049
21073
|
);
|
|
21074
|
+
return fs20.existsSync(resolveHooksSourceFile(sourceRoot, config)) ? path19.posix.join(dir, "hooks.json") : `${dir}/`;
|
|
21050
21075
|
}
|
|
21051
21076
|
function resolveMcpSourceFile(sourceRoot, config) {
|
|
21052
21077
|
return path19.resolve(sourceRoot, config.source.mcp);
|
|
@@ -21060,44 +21085,148 @@ function normalizeManagedSourcePath(rawPath) {
|
|
|
21060
21085
|
}
|
|
21061
21086
|
|
|
21062
21087
|
// src/compiler/hook-components.ts
|
|
21063
|
-
|
|
21064
|
-
import fs21 from "fs";
|
|
21088
|
+
import fs22 from "fs";
|
|
21065
21089
|
import path20 from "path";
|
|
21090
|
+
|
|
21091
|
+
// src/compiler/loss-report.ts
|
|
21092
|
+
import fs21 from "fs";
|
|
21093
|
+
function createLossReport() {
|
|
21094
|
+
return {
|
|
21095
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21096
|
+
targets: {},
|
|
21097
|
+
summary: {},
|
|
21098
|
+
informational: []
|
|
21099
|
+
};
|
|
21100
|
+
}
|
|
21101
|
+
function addInformational(report, target, component, source, severity, detail) {
|
|
21102
|
+
report.informational.push({ target, component, source, severity, detail });
|
|
21103
|
+
}
|
|
21104
|
+
function addLoss(report, target, component, loss) {
|
|
21105
|
+
const targetReport = report.targets[target] ?? {};
|
|
21106
|
+
const componentReport = targetReport[component] ?? [];
|
|
21107
|
+
componentReport.push(loss);
|
|
21108
|
+
targetReport[component] = componentReport;
|
|
21109
|
+
report.targets[target] = targetReport;
|
|
21110
|
+
const targetSummary = report.summary[target] ?? {
|
|
21111
|
+
info: 0,
|
|
21112
|
+
warning: 0,
|
|
21113
|
+
error: 0
|
|
21114
|
+
};
|
|
21115
|
+
for (const field of loss.fields) {
|
|
21116
|
+
targetSummary[field.severity] += 1;
|
|
21117
|
+
}
|
|
21118
|
+
report.summary[target] = targetSummary;
|
|
21119
|
+
}
|
|
21120
|
+
var lossSeverityByEntryType = {
|
|
21121
|
+
"command-as-skill": "info",
|
|
21122
|
+
"rule-as-instruction": "info",
|
|
21123
|
+
"rule-mode-downgrade": "warning",
|
|
21124
|
+
"rule-path-scoping": "warning",
|
|
21125
|
+
"hook-event-drop": "warning",
|
|
21126
|
+
// A declaration the parser refuses installs nothing, anywhere, and used to
|
|
21127
|
+
// do so silently. Error severity is what makes install and sync exit 3.
|
|
21128
|
+
"hook-declaration-invalid": "error",
|
|
21129
|
+
// Both source forms present means one of them is inert and nothing says
|
|
21130
|
+
// which. Error severity, because the CLI cannot tell an author which half
|
|
21131
|
+
// of their kit it is ignoring without them deciding first.
|
|
21132
|
+
"hook-source-ambiguous": "error",
|
|
21133
|
+
// A warning, not an error: the hooks that do reach the target are correct,
|
|
21134
|
+
// and the remedy is a choice about scope rather than a defect in the kit.
|
|
21135
|
+
"hook-script-path-unresolved": "warning",
|
|
21136
|
+
"hook-as-plugin": "error",
|
|
21137
|
+
"instructions-shim": "info",
|
|
21138
|
+
"permission-coarsening": "error",
|
|
21139
|
+
"provider-not-representable": "error",
|
|
21140
|
+
"provider-wire-protocol": "error",
|
|
21141
|
+
"model-not-representable": "error",
|
|
21142
|
+
"persona-not-representable": "error",
|
|
21143
|
+
"settings-not-representable": "warning"
|
|
21144
|
+
};
|
|
21145
|
+
function resolveLossSeverity(kind, name, action, explicitSeverity) {
|
|
21146
|
+
if (explicitSeverity) {
|
|
21147
|
+
return explicitSeverity;
|
|
21148
|
+
}
|
|
21149
|
+
return lossSeverityByEntryType[action];
|
|
21150
|
+
}
|
|
21151
|
+
function writeLossReport(filePath, report) {
|
|
21152
|
+
if (!filePath) {
|
|
21153
|
+
return;
|
|
21154
|
+
}
|
|
21155
|
+
fs21.writeFileSync(filePath, `${JSON.stringify(report, null, 2)}
|
|
21156
|
+
`);
|
|
21157
|
+
}
|
|
21158
|
+
|
|
21159
|
+
// src/compiler/hook-components.ts
|
|
21160
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
21161
|
+
function reject(filePath, field, reason) {
|
|
21162
|
+
return { ok: false, rejection: { file: filePath, field, reason } };
|
|
21163
|
+
}
|
|
21066
21164
|
function parseHookDeclaration(filePath) {
|
|
21067
21165
|
let raw;
|
|
21068
21166
|
try {
|
|
21069
|
-
raw =
|
|
21167
|
+
raw = fs22.readFileSync(filePath, "utf8");
|
|
21070
21168
|
} catch {
|
|
21071
|
-
return
|
|
21169
|
+
return reject(filePath, "<file>", "the file could not be read");
|
|
21072
21170
|
}
|
|
21073
21171
|
const fm = extractFrontmatter(raw);
|
|
21074
21172
|
if (!fm) {
|
|
21075
|
-
return
|
|
21173
|
+
return reject(
|
|
21174
|
+
filePath,
|
|
21175
|
+
"<frontmatter>",
|
|
21176
|
+
"no YAML frontmatter was found; a hook declaration opens with a '---' block carrying name, description and events"
|
|
21177
|
+
);
|
|
21076
21178
|
}
|
|
21077
|
-
const { name, description, events } = fm;
|
|
21179
|
+
const { name, description, events, command } = fm;
|
|
21078
21180
|
if (typeof name !== "string" || !name.trim()) {
|
|
21079
|
-
return
|
|
21181
|
+
return reject(filePath, "name", "expected a non-empty string");
|
|
21080
21182
|
}
|
|
21081
21183
|
if (typeof description !== "string" || !description.trim()) {
|
|
21082
|
-
return
|
|
21184
|
+
return reject(filePath, "description", "expected a non-empty string");
|
|
21185
|
+
}
|
|
21186
|
+
if (command !== void 0 && (typeof command !== "string" || !command.trim())) {
|
|
21187
|
+
return reject(
|
|
21188
|
+
filePath,
|
|
21189
|
+
"command",
|
|
21190
|
+
"expected a non-empty string naming the command the harness runs; omit the field entirely to accept the synthesized default"
|
|
21191
|
+
);
|
|
21083
21192
|
}
|
|
21084
21193
|
if (!Array.isArray(events) || events.length === 0) {
|
|
21085
|
-
return
|
|
21194
|
+
return reject(
|
|
21195
|
+
filePath,
|
|
21196
|
+
"events",
|
|
21197
|
+
'expected a non-empty list of event objects, e.g. "- event: PreToolUse\\n timeout: 10\\n statusMessage: \u2026". A list of plain event names is the most common mistake and is rejected: each entry must be an object'
|
|
21198
|
+
);
|
|
21086
21199
|
}
|
|
21087
21200
|
const parsedEvents = [];
|
|
21088
|
-
for (const entry of events) {
|
|
21201
|
+
for (const [index, entry] of events.entries()) {
|
|
21089
21202
|
if (!entry || typeof entry !== "object") {
|
|
21090
|
-
return
|
|
21203
|
+
return reject(
|
|
21204
|
+
filePath,
|
|
21205
|
+
`events[${index}]`,
|
|
21206
|
+
`expected an object with 'event', 'timeout' and 'statusMessage'; found ${typeof entry === "string" ? `the bare string "${entry}"` : typeof entry}`
|
|
21207
|
+
);
|
|
21091
21208
|
}
|
|
21092
21209
|
const ev = entry;
|
|
21093
21210
|
if (typeof ev.event !== "string" || !ev.event.trim()) {
|
|
21094
|
-
return
|
|
21211
|
+
return reject(
|
|
21212
|
+
filePath,
|
|
21213
|
+
`events[${index}].event`,
|
|
21214
|
+
"expected a non-empty string naming the harness event"
|
|
21215
|
+
);
|
|
21095
21216
|
}
|
|
21096
21217
|
if (typeof ev.timeout !== "number") {
|
|
21097
|
-
return
|
|
21218
|
+
return reject(
|
|
21219
|
+
filePath,
|
|
21220
|
+
`events[${index}].timeout`,
|
|
21221
|
+
"expected a number, in seconds"
|
|
21222
|
+
);
|
|
21098
21223
|
}
|
|
21099
21224
|
if (typeof ev.statusMessage !== "string" || !ev.statusMessage.trim()) {
|
|
21100
|
-
return
|
|
21225
|
+
return reject(
|
|
21226
|
+
filePath,
|
|
21227
|
+
`events[${index}].statusMessage`,
|
|
21228
|
+
"expected a non-empty string shown while the hook runs"
|
|
21229
|
+
);
|
|
21101
21230
|
}
|
|
21102
21231
|
const parsed = {
|
|
21103
21232
|
event: ev.event,
|
|
@@ -21110,15 +21239,23 @@ function parseHookDeclaration(filePath) {
|
|
|
21110
21239
|
parsedEvents.push(parsed);
|
|
21111
21240
|
}
|
|
21112
21241
|
return {
|
|
21113
|
-
|
|
21114
|
-
|
|
21115
|
-
|
|
21242
|
+
ok: true,
|
|
21243
|
+
declaration: {
|
|
21244
|
+
name: name.trim(),
|
|
21245
|
+
description: description.trim(),
|
|
21246
|
+
...typeof command === "string" ? { command: command.trim() } : {},
|
|
21247
|
+
events: parsedEvents
|
|
21248
|
+
}
|
|
21116
21249
|
};
|
|
21117
21250
|
}
|
|
21118
|
-
|
|
21119
|
-
|
|
21251
|
+
var GLOBAL_HOOK_SCRIPTS_DIR = "~/.agents/hooks/scripts";
|
|
21252
|
+
function synthesizedHookCommand(name, scriptsDir = GLOBAL_HOOK_SCRIPTS_DIR) {
|
|
21253
|
+
return `npx tsx ${scriptsDir}/${name}.ts`;
|
|
21120
21254
|
}
|
|
21121
|
-
function
|
|
21255
|
+
function resolveHookCommand(declaration, scriptsDir) {
|
|
21256
|
+
return declaration.command ?? synthesizedHookCommand(declaration.name, scriptsDir);
|
|
21257
|
+
}
|
|
21258
|
+
function synthesizeHooksJson(declarations, scriptsDir) {
|
|
21122
21259
|
const hooks = {};
|
|
21123
21260
|
for (const decl of declarations) {
|
|
21124
21261
|
for (const ev of decl.events) {
|
|
@@ -21129,7 +21266,7 @@ function synthesizeHooksJson(declarations) {
|
|
|
21129
21266
|
hooks: [
|
|
21130
21267
|
{
|
|
21131
21268
|
type: "command",
|
|
21132
|
-
command:
|
|
21269
|
+
command: resolveHookCommand(decl, scriptsDir),
|
|
21133
21270
|
timeout: ev.timeout,
|
|
21134
21271
|
statusMessage: ev.statusMessage
|
|
21135
21272
|
}
|
|
@@ -21148,35 +21285,56 @@ function synthesizeHooksJson(declarations) {
|
|
|
21148
21285
|
}
|
|
21149
21286
|
return { hooks };
|
|
21150
21287
|
}
|
|
21151
|
-
function loadSourceHooks(hooksDir, hookNames) {
|
|
21288
|
+
function loadSourceHooks(hooksDir, hookNames, scriptsDir) {
|
|
21152
21289
|
const hooksJsonPath = path20.join(hooksDir, "hooks.json");
|
|
21153
|
-
if (
|
|
21154
|
-
const raw =
|
|
21290
|
+
if (fs22.existsSync(hooksJsonPath)) {
|
|
21291
|
+
const raw = fs22.readFileSync(hooksJsonPath, "utf8");
|
|
21155
21292
|
return JSON.parse(raw);
|
|
21156
21293
|
}
|
|
21157
21294
|
const declarations = loadHookDeclarations(hooksDir, hookNames);
|
|
21158
|
-
return synthesizeHooksJson(declarations);
|
|
21295
|
+
return synthesizeHooksJson(declarations, scriptsDir);
|
|
21159
21296
|
}
|
|
21160
21297
|
function loadHookDeclarations(hooksDir, hookNames) {
|
|
21161
21298
|
let names;
|
|
21162
21299
|
if (hookNames && hookNames.length > 0) {
|
|
21163
21300
|
names = hookNames;
|
|
21164
21301
|
} else {
|
|
21165
|
-
if (!
|
|
21302
|
+
if (!fs22.existsSync(hooksDir) || !fs22.statSync(hooksDir).isDirectory()) {
|
|
21166
21303
|
return [];
|
|
21167
21304
|
}
|
|
21168
|
-
names =
|
|
21305
|
+
names = fs22.readdirSync(hooksDir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => f.slice(0, -3)).sort();
|
|
21169
21306
|
}
|
|
21170
21307
|
const declarations = [];
|
|
21171
21308
|
for (const name of names) {
|
|
21172
21309
|
const filePath = path20.join(hooksDir, `${name}.md`);
|
|
21173
|
-
const
|
|
21174
|
-
if (
|
|
21175
|
-
declarations.push(
|
|
21310
|
+
const result = parseHookDeclaration(filePath);
|
|
21311
|
+
if (result.ok) {
|
|
21312
|
+
declarations.push(result.declaration);
|
|
21176
21313
|
}
|
|
21177
21314
|
}
|
|
21178
21315
|
return declarations;
|
|
21179
21316
|
}
|
|
21317
|
+
function collectHookRejections(hooksDir, hookNames) {
|
|
21318
|
+
if (fs22.existsSync(path20.join(hooksDir, "hooks.json"))) {
|
|
21319
|
+
return [];
|
|
21320
|
+
}
|
|
21321
|
+
if (!fs22.existsSync(hooksDir) || !fs22.statSync(hooksDir).isDirectory()) {
|
|
21322
|
+
return [];
|
|
21323
|
+
}
|
|
21324
|
+
const names = hookNames && hookNames.length > 0 ? hookNames : fs22.readdirSync(hooksDir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => f.slice(0, -3)).sort();
|
|
21325
|
+
const rejections = [];
|
|
21326
|
+
for (const name of names) {
|
|
21327
|
+
const filePath = path20.join(hooksDir, `${name}.md`);
|
|
21328
|
+
if (!fs22.existsSync(filePath)) {
|
|
21329
|
+
continue;
|
|
21330
|
+
}
|
|
21331
|
+
const result = parseHookDeclaration(filePath);
|
|
21332
|
+
if (!result.ok) {
|
|
21333
|
+
rejections.push(result.rejection);
|
|
21334
|
+
}
|
|
21335
|
+
}
|
|
21336
|
+
return rejections;
|
|
21337
|
+
}
|
|
21180
21338
|
function extractFrontmatter(content) {
|
|
21181
21339
|
if (!content.startsWith("---\n")) {
|
|
21182
21340
|
return null;
|
|
@@ -21197,22 +21355,179 @@ function extractFrontmatter(content) {
|
|
|
21197
21355
|
}
|
|
21198
21356
|
return parsed;
|
|
21199
21357
|
}
|
|
21358
|
+
function reportHookRejections(report, targets, hooksDir, hookNames) {
|
|
21359
|
+
const rejections = collectHookRejections(hooksDir, hookNames);
|
|
21360
|
+
for (const rejection of rejections) {
|
|
21361
|
+
const name = path20.basename(rejection.file, ".md");
|
|
21362
|
+
for (const target of targets) {
|
|
21363
|
+
addLoss(report, target, "hooks", {
|
|
21364
|
+
source: `hooks/${path20.basename(rejection.file)}`,
|
|
21365
|
+
fields: [
|
|
21366
|
+
{
|
|
21367
|
+
field: rejection.field,
|
|
21368
|
+
action: "hook-declaration-invalid",
|
|
21369
|
+
severity: "error",
|
|
21370
|
+
detail: `Hook '${name}' was not installed: its declaration was rejected at '${rejection.field}' \u2014 ${rejection.reason}. To fix: correct that field in this file, then re-run. Until then this hook reaches no target.`
|
|
21371
|
+
}
|
|
21372
|
+
]
|
|
21373
|
+
});
|
|
21374
|
+
}
|
|
21375
|
+
}
|
|
21376
|
+
return rejections;
|
|
21377
|
+
}
|
|
21378
|
+
function declarationFileNames(hooksDir) {
|
|
21379
|
+
if (!fs22.existsSync(hooksDir) || !fs22.statSync(hooksDir).isDirectory()) {
|
|
21380
|
+
return [];
|
|
21381
|
+
}
|
|
21382
|
+
return fs22.readdirSync(hooksDir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).sort();
|
|
21383
|
+
}
|
|
21384
|
+
function detectHooksSourceForm(hooksDir) {
|
|
21385
|
+
const hasJson = fs22.existsSync(path20.join(hooksDir, "hooks.json"));
|
|
21386
|
+
const hasDeclarations = declarationFileNames(hooksDir).length > 0;
|
|
21387
|
+
if (hasJson && hasDeclarations) return { kind: "both" };
|
|
21388
|
+
if (hasJson) return { kind: "hooks-json" };
|
|
21389
|
+
if (hasDeclarations) return { kind: "declarations" };
|
|
21390
|
+
return { kind: "absent" };
|
|
21391
|
+
}
|
|
21392
|
+
function registrations(hooksJson) {
|
|
21393
|
+
const out = /* @__PURE__ */ new Set();
|
|
21394
|
+
if (!hooksJson || typeof hooksJson !== "object") return out;
|
|
21395
|
+
const hooks = hooksJson.hooks;
|
|
21396
|
+
if (!hooks || typeof hooks !== "object") return out;
|
|
21397
|
+
for (const [event, entries] of Object.entries(
|
|
21398
|
+
hooks
|
|
21399
|
+
)) {
|
|
21400
|
+
if (!Array.isArray(entries)) continue;
|
|
21401
|
+
for (const entry of entries) {
|
|
21402
|
+
if (!entry || typeof entry !== "object") continue;
|
|
21403
|
+
const e = entry;
|
|
21404
|
+
const matcher = typeof e.matcher === "string" ? e.matcher : "*";
|
|
21405
|
+
const inner = Array.isArray(e.hooks) ? e.hooks : [];
|
|
21406
|
+
for (const h of inner) {
|
|
21407
|
+
if (!h || typeof h !== "object") continue;
|
|
21408
|
+
const command = h.command;
|
|
21409
|
+
out.add(`${event} [${matcher}] ${String(command)}`);
|
|
21410
|
+
}
|
|
21411
|
+
}
|
|
21412
|
+
}
|
|
21413
|
+
return out;
|
|
21414
|
+
}
|
|
21415
|
+
function describeHooksSourceConflict(hooksDir, hookNames) {
|
|
21416
|
+
if (detectHooksSourceForm(hooksDir).kind !== "both") {
|
|
21417
|
+
return null;
|
|
21418
|
+
}
|
|
21419
|
+
const jsonPath = path20.join(hooksDir, "hooks.json");
|
|
21420
|
+
const declared = declarationFileNames(hooksDir);
|
|
21421
|
+
let divergence = "";
|
|
21422
|
+
try {
|
|
21423
|
+
const fromJson = registrations(
|
|
21424
|
+
JSON.parse(fs22.readFileSync(jsonPath, "utf8"))
|
|
21425
|
+
);
|
|
21426
|
+
const fromDeclarations = registrations(
|
|
21427
|
+
synthesizeHooksJson(loadHookDeclarations(hooksDir, hookNames))
|
|
21428
|
+
);
|
|
21429
|
+
const onlyJson = [...fromJson].filter((r) => !fromDeclarations.has(r));
|
|
21430
|
+
const onlyDeclarations = [...fromDeclarations].filter(
|
|
21431
|
+
(r) => !fromJson.has(r)
|
|
21432
|
+
);
|
|
21433
|
+
if (onlyJson.length === 0 && onlyDeclarations.length === 0) {
|
|
21434
|
+
divergence = " The two forms currently register the same hooks, so nothing is being lost yet \u2014 but only one of them is read, and the next edit to the other will be silent.";
|
|
21435
|
+
} else {
|
|
21436
|
+
const lines = [
|
|
21437
|
+
...onlyJson.map((r) => ` shipped, not declared: ${r}`),
|
|
21438
|
+
...onlyDeclarations.map((r) => ` declared, not shipped: ${r}`)
|
|
21439
|
+
];
|
|
21440
|
+
divergence = " The two forms disagree. Only hooks.json is read, so:\n" + lines.join("\n");
|
|
21441
|
+
}
|
|
21442
|
+
} catch (error) {
|
|
21443
|
+
divergence = ` The two forms could not be compared: ${error instanceof Error ? error.message : String(error)}.`;
|
|
21444
|
+
}
|
|
21445
|
+
return `This kit declares its hooks twice: '${jsonPath}' and ${declared.length} declaration file${declared.length === 1 ? "" : "s"} (${declared.join(", ")}) in the same directory. A kit must write one form or the other.${divergence}
|
|
21446
|
+
To fix: keep the declarations and delete hooks.json \u2014 the declarations are the documented form, they carry the description and the per-event statusMessage, and they are what 'tangyr validate' checks. Before deleting, fold anything above that hooks.json ships and the declarations do not into the matching hooks/<name>.md, and add every hook name to 'components.hooks' in tangyr-kit.yaml. To keep hooks.json instead, delete the .md files \u2014 but note that no target then receives a hook description, and the manifest's hook list stops being checked against anything.`;
|
|
21447
|
+
}
|
|
21448
|
+
function loadKitHooksSource(sourceRoot, config, scriptsDir) {
|
|
21449
|
+
if (!hooksSourceExists(sourceRoot, config)) {
|
|
21450
|
+
return null;
|
|
21451
|
+
}
|
|
21452
|
+
const hooksDir = resolveHooksSourceDir(sourceRoot, config);
|
|
21453
|
+
const hooks = loadSourceHooks(
|
|
21454
|
+
hooksDir,
|
|
21455
|
+
resolveKitHookNames(sourceRoot),
|
|
21456
|
+
scriptsDir
|
|
21457
|
+
);
|
|
21458
|
+
const form = detectHooksSourceForm(hooksDir);
|
|
21459
|
+
return {
|
|
21460
|
+
hooks,
|
|
21461
|
+
path: form.kind === "declarations" ? hooksDir : path20.join(hooksDir, "hooks.json"),
|
|
21462
|
+
label: hooksSourceLabel(sourceRoot, config),
|
|
21463
|
+
// Hashed over the compiled input rather than over one file's bytes: the
|
|
21464
|
+
// declaration form has no single file to hash, and what provenance needs
|
|
21465
|
+
// to identify is what was compiled.
|
|
21466
|
+
hash: computeBytesHash(Buffer.from(JSON.stringify(hooks), "utf8"))
|
|
21467
|
+
};
|
|
21468
|
+
}
|
|
21469
|
+
function reportHooksSourceConflict(report, targets, hooksDir, hookNames) {
|
|
21470
|
+
const message = describeHooksSourceConflict(hooksDir, hookNames);
|
|
21471
|
+
if (!message) {
|
|
21472
|
+
return null;
|
|
21473
|
+
}
|
|
21474
|
+
for (const target of targets) {
|
|
21475
|
+
addLoss(report, target, "hooks", {
|
|
21476
|
+
source: `${path20.basename(hooksDir)}/`,
|
|
21477
|
+
fields: [
|
|
21478
|
+
{
|
|
21479
|
+
field: "hooks.json",
|
|
21480
|
+
action: "hook-source-ambiguous",
|
|
21481
|
+
severity: "error",
|
|
21482
|
+
detail: message
|
|
21483
|
+
}
|
|
21484
|
+
]
|
|
21485
|
+
});
|
|
21486
|
+
}
|
|
21487
|
+
return message;
|
|
21488
|
+
}
|
|
21489
|
+
function reportProjectScopeScriptPath(report, targets, hooksDir, hookNames) {
|
|
21490
|
+
const affected = targets.filter((t) => t !== "claude-code");
|
|
21491
|
+
if (affected.length === 0) {
|
|
21492
|
+
return [];
|
|
21493
|
+
}
|
|
21494
|
+
const synthesized = loadHookDeclarations(hooksDir, hookNames).filter(
|
|
21495
|
+
(d) => d.command === void 0
|
|
21496
|
+
);
|
|
21497
|
+
if (synthesized.length === 0) {
|
|
21498
|
+
return [];
|
|
21499
|
+
}
|
|
21500
|
+
for (const target of affected) {
|
|
21501
|
+
addLoss(report, target, "hooks", {
|
|
21502
|
+
source: `${path20.basename(hooksDir)}/`,
|
|
21503
|
+
fields: [
|
|
21504
|
+
{
|
|
21505
|
+
field: "command",
|
|
21506
|
+
action: "hook-script-path-unresolved",
|
|
21507
|
+
severity: "warning",
|
|
21508
|
+
detail: `The ${synthesized.length} hook${synthesized.length === 1 ? "" : "s"} that take the synthesized command name '${GLOBAL_HOOK_SCRIPTS_DIR}/<name>.ts', which a project-scope install does not write. Only the claude-code adapter delivers hook scripts into a project, and only its own commands are resolved against the project. To fix, choose one: install this kit at global scope, where the path is correct; install claude-code alongside ${target} so the scripts reach the project, and give each hook an explicit 'command' pointing at them; or give each hook a 'command' of its own. Until then these hooks do not run under ${target}.`
|
|
21509
|
+
}
|
|
21510
|
+
]
|
|
21511
|
+
});
|
|
21512
|
+
}
|
|
21513
|
+
return affected;
|
|
21514
|
+
}
|
|
21200
21515
|
|
|
21201
21516
|
// src/core/remove.ts
|
|
21202
|
-
import
|
|
21517
|
+
import fs23 from "fs";
|
|
21203
21518
|
function removeManagedPath(managedPath) {
|
|
21204
21519
|
let stat;
|
|
21205
21520
|
try {
|
|
21206
|
-
stat =
|
|
21521
|
+
stat = fs23.lstatSync(managedPath);
|
|
21207
21522
|
} catch {
|
|
21208
21523
|
return;
|
|
21209
21524
|
}
|
|
21210
21525
|
if (stat.isSymbolicLink() || stat.isFile()) {
|
|
21211
|
-
|
|
21526
|
+
fs23.unlinkSync(managedPath);
|
|
21212
21527
|
return;
|
|
21213
21528
|
}
|
|
21214
21529
|
if (stat.isDirectory()) {
|
|
21215
|
-
|
|
21530
|
+
fs23.rmSync(managedPath, { recursive: true, force: true });
|
|
21216
21531
|
}
|
|
21217
21532
|
}
|
|
21218
21533
|
|
|
@@ -21320,7 +21635,7 @@ async function runUninstallCommand(options, logger) {
|
|
|
21320
21635
|
runtime
|
|
21321
21636
|
);
|
|
21322
21637
|
for (const actualPath of paths) {
|
|
21323
|
-
if (actualPath && (
|
|
21638
|
+
if (actualPath && (fs24.existsSync(actualPath) || isSymlink(actualPath))) {
|
|
21324
21639
|
removeManagedPath(actualPath);
|
|
21325
21640
|
logger.verbose(` removed: ${actualPath}`);
|
|
21326
21641
|
removedCount++;
|
|
@@ -21334,7 +21649,7 @@ async function runUninstallCommand(options, logger) {
|
|
|
21334
21649
|
scope,
|
|
21335
21650
|
runtime
|
|
21336
21651
|
);
|
|
21337
|
-
if (actualPath && (
|
|
21652
|
+
if (actualPath && (fs24.existsSync(actualPath) || isSymlink(actualPath))) {
|
|
21338
21653
|
removeManagedPath(actualPath);
|
|
21339
21654
|
logger.verbose(` removed: ${actualPath}`);
|
|
21340
21655
|
removedCount++;
|
|
@@ -21368,14 +21683,14 @@ async function runUninstallCommand(options, logger) {
|
|
|
21368
21683
|
restoreBackup(backup, logger);
|
|
21369
21684
|
}
|
|
21370
21685
|
const manifestPath = path21.join(scopePath, "manifest.json");
|
|
21371
|
-
if (
|
|
21372
|
-
|
|
21686
|
+
if (fs24.existsSync(manifestPath)) {
|
|
21687
|
+
fs24.unlinkSync(manifestPath);
|
|
21373
21688
|
logger.verbose(` removed manifest: ${manifestPath}`);
|
|
21374
21689
|
}
|
|
21375
21690
|
try {
|
|
21376
|
-
const remaining =
|
|
21691
|
+
const remaining = fs24.readdirSync(scopePath);
|
|
21377
21692
|
if (remaining.length === 0) {
|
|
21378
|
-
|
|
21693
|
+
fs24.rmdirSync(scopePath);
|
|
21379
21694
|
logger.verbose(` removed empty scope directory: ${scopePath}`);
|
|
21380
21695
|
}
|
|
21381
21696
|
} catch {
|
|
@@ -21619,9 +21934,9 @@ function resolveAllClaudeRoots(manifest, runtime, claudeConfigDirs) {
|
|
|
21619
21934
|
})
|
|
21620
21935
|
].map((root) => path21.resolve(root))
|
|
21621
21936
|
);
|
|
21622
|
-
if (home &&
|
|
21937
|
+
if (home && fs24.existsSync(home)) {
|
|
21623
21938
|
try {
|
|
21624
|
-
for (const entry of
|
|
21939
|
+
for (const entry of fs24.readdirSync(home, { withFileTypes: true })) {
|
|
21625
21940
|
if (entry.isDirectory() && entry.name.startsWith(".claude-")) {
|
|
21626
21941
|
roots.add(path21.resolve(home, entry.name));
|
|
21627
21942
|
}
|
|
@@ -21748,7 +22063,7 @@ function addCompiledGlobPlans(artifacts, pattern, expectedTarget, component) {
|
|
|
21748
22063
|
for (const artifactPath of ts(pattern).sort()) {
|
|
21749
22064
|
let marker = null;
|
|
21750
22065
|
try {
|
|
21751
|
-
marker = extractProvenanceMarker(
|
|
22066
|
+
marker = extractProvenanceMarker(fs24.readFileSync(artifactPath, "utf8"));
|
|
21752
22067
|
} catch {
|
|
21753
22068
|
continue;
|
|
21754
22069
|
}
|
|
@@ -21768,7 +22083,7 @@ function addCompiledGlobPlans(artifacts, pattern, expectedTarget, component) {
|
|
|
21768
22083
|
function addManagedSymlinkChildren(artifacts, directory, runtime, component) {
|
|
21769
22084
|
let entries;
|
|
21770
22085
|
try {
|
|
21771
|
-
entries =
|
|
22086
|
+
entries = fs24.readdirSync(directory, { withFileTypes: true });
|
|
21772
22087
|
} catch {
|
|
21773
22088
|
return;
|
|
21774
22089
|
}
|
|
@@ -21958,12 +22273,8 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
|
|
|
21958
22273
|
expectedTarget: "copilot"
|
|
21959
22274
|
}
|
|
21960
22275
|
);
|
|
21961
|
-
const
|
|
21962
|
-
|
|
21963
|
-
runtime.config
|
|
21964
|
-
);
|
|
21965
|
-
const hooksSource = hooksSourceLabel(runtime.config);
|
|
21966
|
-
if (fs23.existsSync(hooksSourcePath)) {
|
|
22276
|
+
const hooksSource = hooksSourceLabel(runtime.sourceRoot, runtime.config);
|
|
22277
|
+
if (hooksSourceExists(runtime.sourceRoot, runtime.config)) {
|
|
21967
22278
|
artifacts.push({
|
|
21968
22279
|
component: "copilot hooks",
|
|
21969
22280
|
path: path21.join(copilotPaths.hooks, "tangyr-managed.json"),
|
|
@@ -21977,7 +22288,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
|
|
|
21977
22288
|
runtime.config
|
|
21978
22289
|
);
|
|
21979
22290
|
const mcpSource = mcpSourceLabel(runtime.config);
|
|
21980
|
-
if (
|
|
22291
|
+
if (fs24.existsSync(mcpSourcePath)) {
|
|
21981
22292
|
artifacts.push({
|
|
21982
22293
|
component: "copilot mcp",
|
|
21983
22294
|
path: copilotPaths.mcpSharedFile,
|
|
@@ -22038,12 +22349,8 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
|
|
|
22038
22349
|
kind: "direct"
|
|
22039
22350
|
});
|
|
22040
22351
|
}
|
|
22041
|
-
const
|
|
22042
|
-
|
|
22043
|
-
runtime.config
|
|
22044
|
-
);
|
|
22045
|
-
const hooksSource = hooksSourceLabel(runtime.config);
|
|
22046
|
-
if (fs23.existsSync(hooksSourcePath)) {
|
|
22352
|
+
const hooksSource = hooksSourceLabel(runtime.sourceRoot, runtime.config);
|
|
22353
|
+
if (hooksSourceExists(runtime.sourceRoot, runtime.config)) {
|
|
22047
22354
|
artifacts.push({
|
|
22048
22355
|
component: "codex hooks",
|
|
22049
22356
|
path: codexPaths.hooksSharedFile,
|
|
@@ -22056,7 +22363,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
|
|
|
22056
22363
|
runtime.config
|
|
22057
22364
|
);
|
|
22058
22365
|
const mcpSource = mcpSourceLabel(runtime.config);
|
|
22059
|
-
if (
|
|
22366
|
+
if (fs24.existsSync(mcpSourcePath)) {
|
|
22060
22367
|
artifacts.push({
|
|
22061
22368
|
component: "codex mcp",
|
|
22062
22369
|
path: codexPaths.mcpSharedFile,
|
|
@@ -22133,7 +22440,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
|
|
|
22133
22440
|
runtime.config
|
|
22134
22441
|
);
|
|
22135
22442
|
const mcpSource = mcpSourceLabel(runtime.config);
|
|
22136
|
-
if (
|
|
22443
|
+
if (fs24.existsSync(mcpSourcePath)) {
|
|
22137
22444
|
artifacts.push({
|
|
22138
22445
|
component: "opencode config",
|
|
22139
22446
|
path: opencodePaths.configDoc,
|
|
@@ -22214,7 +22521,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
|
|
|
22214
22521
|
runtime.config
|
|
22215
22522
|
);
|
|
22216
22523
|
const mcpSource = mcpSourceLabel(runtime.config);
|
|
22217
|
-
if (
|
|
22524
|
+
if (fs24.existsSync(mcpSourcePath)) {
|
|
22218
22525
|
artifacts.push({
|
|
22219
22526
|
component: "cursor mcp",
|
|
22220
22527
|
path: cursorPaths.mcpFile,
|
|
@@ -22294,7 +22601,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
|
|
|
22294
22601
|
runtime.config
|
|
22295
22602
|
);
|
|
22296
22603
|
const mcpSource = mcpSourceLabel(runtime.config);
|
|
22297
|
-
if (
|
|
22604
|
+
if (fs24.existsSync(mcpSourcePath)) {
|
|
22298
22605
|
artifacts.push({
|
|
22299
22606
|
component: "cline mcp",
|
|
22300
22607
|
path: clinePaths.mcpFile,
|
|
@@ -22376,12 +22683,12 @@ function isKitManagedArtifact(artifact, runtime) {
|
|
|
22376
22683
|
return artifactClass === "managed-symlink" || artifactClass === "managed-stale" || artifactClass === "managed-compiled";
|
|
22377
22684
|
}
|
|
22378
22685
|
if (artifact.kind === "compiled") {
|
|
22379
|
-
if (!
|
|
22686
|
+
if (!fs24.existsSync(artifact.path)) {
|
|
22380
22687
|
return false;
|
|
22381
22688
|
}
|
|
22382
22689
|
try {
|
|
22383
22690
|
const marker = extractProvenanceMarker(
|
|
22384
|
-
|
|
22691
|
+
fs24.readFileSync(artifact.path, "utf8")
|
|
22385
22692
|
);
|
|
22386
22693
|
return marker?.target === artifact.expectedTarget && marker?.source === artifact.expectedSource;
|
|
22387
22694
|
} catch {
|
|
@@ -22389,7 +22696,7 @@ function isKitManagedArtifact(artifact, runtime) {
|
|
|
22389
22696
|
}
|
|
22390
22697
|
}
|
|
22391
22698
|
if (artifact.kind === "recorded") {
|
|
22392
|
-
return
|
|
22699
|
+
return fs24.existsSync(artifact.path) || isSymlink(artifact.path);
|
|
22393
22700
|
}
|
|
22394
22701
|
if (artifact.kind === "kit-pointer") {
|
|
22395
22702
|
return isKitPointer(artifact);
|
|
@@ -22398,11 +22705,11 @@ function isKitManagedArtifact(artifact, runtime) {
|
|
|
22398
22705
|
return isLegacyOperatingGuideCopy(artifact.path);
|
|
22399
22706
|
}
|
|
22400
22707
|
if (artifact.kind === "codex-mcp") {
|
|
22401
|
-
if (!
|
|
22708
|
+
if (!fs24.existsSync(artifact.path)) {
|
|
22402
22709
|
return false;
|
|
22403
22710
|
}
|
|
22404
22711
|
const section = readManagedTomlSection(
|
|
22405
|
-
|
|
22712
|
+
fs24.readFileSync(artifact.path, "utf8")
|
|
22406
22713
|
);
|
|
22407
22714
|
const marker = section ? extractProvenanceMarker(section) : null;
|
|
22408
22715
|
return marker?.target === artifact.expectedTarget && marker?.source === artifact.expectedSource;
|
|
@@ -22428,16 +22735,16 @@ function isKitManagedArtifact(artifact, runtime) {
|
|
|
22428
22735
|
return hasManagedCodexHooks(parsed, artifact.expectedSource);
|
|
22429
22736
|
}
|
|
22430
22737
|
function removeManagedCompiledArtifact(artifact, logger) {
|
|
22431
|
-
if (!
|
|
22738
|
+
if (!fs24.existsSync(artifact.path)) {
|
|
22432
22739
|
return false;
|
|
22433
22740
|
}
|
|
22434
22741
|
const marker = extractProvenanceMarker(
|
|
22435
|
-
|
|
22742
|
+
fs24.readFileSync(artifact.path, "utf8")
|
|
22436
22743
|
);
|
|
22437
22744
|
if (marker?.target !== artifact.expectedTarget || marker?.source !== artifact.expectedSource) {
|
|
22438
22745
|
return false;
|
|
22439
22746
|
}
|
|
22440
|
-
|
|
22747
|
+
fs24.rmSync(artifact.path, { force: true });
|
|
22441
22748
|
logger.verbose(` removed ${artifact.component}: ${artifact.path}`);
|
|
22442
22749
|
return true;
|
|
22443
22750
|
}
|
|
@@ -22508,12 +22815,12 @@ function removeManagedJsonArtifact(artifact, logger) {
|
|
|
22508
22815
|
if (!parsed || !hasManagedRootMetadata(parsed, artifact.expectedTarget)) {
|
|
22509
22816
|
return false;
|
|
22510
22817
|
}
|
|
22511
|
-
|
|
22818
|
+
fs24.rmSync(artifact.path, { force: true });
|
|
22512
22819
|
logger.verbose(` removed ${artifact.component}: ${artifact.path}`);
|
|
22513
22820
|
return true;
|
|
22514
22821
|
}
|
|
22515
22822
|
function removeRecordedArtifact(artifact, logger) {
|
|
22516
|
-
if (!
|
|
22823
|
+
if (!fs24.existsSync(artifact.path) && !isSymlink(artifact.path)) {
|
|
22517
22824
|
return false;
|
|
22518
22825
|
}
|
|
22519
22826
|
removeManagedPath(artifact.path);
|
|
@@ -22521,10 +22828,10 @@ function removeRecordedArtifact(artifact, logger) {
|
|
|
22521
22828
|
return true;
|
|
22522
22829
|
}
|
|
22523
22830
|
function removeExactManagedFile(artifact, logger) {
|
|
22524
|
-
if (!
|
|
22831
|
+
if (!fs24.existsSync(artifact.path)) {
|
|
22525
22832
|
return false;
|
|
22526
22833
|
}
|
|
22527
|
-
|
|
22834
|
+
fs24.rmSync(artifact.path, { force: true });
|
|
22528
22835
|
logger.verbose(` removed ${artifact.component}: ${artifact.path}`);
|
|
22529
22836
|
return true;
|
|
22530
22837
|
}
|
|
@@ -22558,10 +22865,10 @@ function removeManagedCodexHooksArtifact(artifact, logger) {
|
|
|
22558
22865
|
return true;
|
|
22559
22866
|
}
|
|
22560
22867
|
function removeManagedCodexMcpArtifact(artifact, logger) {
|
|
22561
|
-
if (!
|
|
22868
|
+
if (!fs24.existsSync(artifact.path)) {
|
|
22562
22869
|
return false;
|
|
22563
22870
|
}
|
|
22564
|
-
const content =
|
|
22871
|
+
const content = fs24.readFileSync(artifact.path, "utf8");
|
|
22565
22872
|
const managedSection = readManagedTomlSection(content);
|
|
22566
22873
|
if (!managedSection) {
|
|
22567
22874
|
return false;
|
|
@@ -22576,9 +22883,9 @@ function removeManagedCodexMcpArtifact(artifact, logger) {
|
|
|
22576
22883
|
codexManagedMcpEnd
|
|
22577
22884
|
);
|
|
22578
22885
|
if (cleaned.trim().length === 0) {
|
|
22579
|
-
|
|
22886
|
+
fs24.rmSync(artifact.path, { force: true });
|
|
22580
22887
|
} else {
|
|
22581
|
-
|
|
22888
|
+
fs24.writeFileSync(artifact.path, cleaned);
|
|
22582
22889
|
}
|
|
22583
22890
|
logger.verbose(` removed ${artifact.component}: ${artifact.path}`);
|
|
22584
22891
|
return true;
|
|
@@ -22771,11 +23078,11 @@ function managedArtifactPlanPriority(artifact) {
|
|
|
22771
23078
|
return 2;
|
|
22772
23079
|
}
|
|
22773
23080
|
function readJsonObject2(filePath) {
|
|
22774
|
-
if (!
|
|
23081
|
+
if (!fs24.existsSync(filePath)) {
|
|
22775
23082
|
return null;
|
|
22776
23083
|
}
|
|
22777
23084
|
try {
|
|
22778
|
-
const parsed = JSON.parse(
|
|
23085
|
+
const parsed = JSON.parse(fs24.readFileSync(filePath, "utf8"));
|
|
22779
23086
|
return isRecord(parsed) ? parsed : null;
|
|
22780
23087
|
} catch {
|
|
22781
23088
|
return null;
|
|
@@ -22823,22 +23130,22 @@ function removeLegacyClaudeSharedPermission(value) {
|
|
|
22823
23130
|
return Object.keys(cleaned).length === 0 ? void 0 : cleaned;
|
|
22824
23131
|
}
|
|
22825
23132
|
function isKitPointer(artifact) {
|
|
22826
|
-
if (!artifact.expectedPath || !
|
|
23133
|
+
if (!artifact.expectedPath || !fs24.existsSync(artifact.path)) {
|
|
22827
23134
|
return false;
|
|
22828
23135
|
}
|
|
22829
23136
|
try {
|
|
22830
|
-
const recordedPath =
|
|
23137
|
+
const recordedPath = fs24.readFileSync(artifact.path, "utf8").trim();
|
|
22831
23138
|
return recordedPath.length > 0 && path21.resolve(recordedPath) === path21.resolve(artifact.expectedPath);
|
|
22832
23139
|
} catch {
|
|
22833
23140
|
return false;
|
|
22834
23141
|
}
|
|
22835
23142
|
}
|
|
22836
23143
|
function isLegacyOperatingGuideCopy(filePath) {
|
|
22837
|
-
if (!
|
|
23144
|
+
if (!fs24.existsSync(filePath)) {
|
|
22838
23145
|
return false;
|
|
22839
23146
|
}
|
|
22840
23147
|
try {
|
|
22841
|
-
return
|
|
23148
|
+
return fs24.readFileSync(filePath, "utf8").startsWith(legacyOperatingGuideSignature);
|
|
22842
23149
|
} catch {
|
|
22843
23150
|
return false;
|
|
22844
23151
|
}
|
|
@@ -22860,10 +23167,10 @@ function matchesManagedMetadata(metadata, artifact) {
|
|
|
22860
23167
|
}
|
|
22861
23168
|
function writeJsonOrRemove(filePath, parsed) {
|
|
22862
23169
|
if (isEmptyJsonValue(parsed)) {
|
|
22863
|
-
|
|
23170
|
+
fs24.rmSync(filePath, { force: true });
|
|
22864
23171
|
return;
|
|
22865
23172
|
}
|
|
22866
|
-
|
|
23173
|
+
fs24.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
22867
23174
|
`);
|
|
22868
23175
|
}
|
|
22869
23176
|
function isEmptyJsonValue(value) {
|
|
@@ -22989,20 +23296,20 @@ function removeDelimitedManagedSection(content, startMarker, endMarker) {
|
|
|
22989
23296
|
return `${content.slice(0, start)}${content.slice(removalEnd)}`;
|
|
22990
23297
|
}
|
|
22991
23298
|
function restoreBackup(backup, logger) {
|
|
22992
|
-
if (!
|
|
23299
|
+
if (!fs24.existsSync(backup.backupLocation)) {
|
|
22993
23300
|
logger.warn(` backup not found: ${backup.backupLocation}`);
|
|
22994
23301
|
return;
|
|
22995
23302
|
}
|
|
22996
23303
|
const destDir = path21.dirname(backup.originalPath);
|
|
22997
|
-
|
|
22998
|
-
|
|
23304
|
+
fs24.mkdirSync(destDir, { recursive: true });
|
|
23305
|
+
fs24.renameSync(backup.backupLocation, backup.originalPath);
|
|
22999
23306
|
logger.verbose(
|
|
23000
23307
|
` restored: ${backup.backupLocation} \u2192 ${backup.originalPath}`
|
|
23001
23308
|
);
|
|
23002
23309
|
}
|
|
23003
23310
|
function isSymlink(p) {
|
|
23004
23311
|
try {
|
|
23005
|
-
return
|
|
23312
|
+
return fs24.lstatSync(p).isSymbolicLink();
|
|
23006
23313
|
} catch {
|
|
23007
23314
|
return false;
|
|
23008
23315
|
}
|
|
@@ -23076,7 +23383,7 @@ function cleanupClaudeCode(config, sourceRoot, configDir, logger, scope) {
|
|
|
23076
23383
|
)) {
|
|
23077
23384
|
const artifactClass = classifyArtifact(artifact.path, config, sourceRoot);
|
|
23078
23385
|
if (artifactClass === "managed-symlink" || artifactClass === "managed-compiled" || artifactClass === "managed-stale") {
|
|
23079
|
-
|
|
23386
|
+
fs25.rmSync(artifact.path, { recursive: true, force: true });
|
|
23080
23387
|
logger.info(`${artifact.component}: removed ${artifact.path}`);
|
|
23081
23388
|
}
|
|
23082
23389
|
}
|
|
@@ -23289,7 +23596,7 @@ function removeManagedValueByKey2(value, keyName, keyValue) {
|
|
|
23289
23596
|
function removeManagedDirectPath(artifactPath, config, sourceRoot, component, logger) {
|
|
23290
23597
|
const artifactClass = classifyArtifact(artifactPath, config, sourceRoot);
|
|
23291
23598
|
if (artifactClass === "managed-symlink" || artifactClass === "managed-compiled" || artifactClass === "managed-stale") {
|
|
23292
|
-
|
|
23599
|
+
fs25.rmSync(artifactPath, { recursive: true, force: true });
|
|
23293
23600
|
logger.info(`${component}: removed ${artifactPath}`);
|
|
23294
23601
|
}
|
|
23295
23602
|
}
|
|
@@ -23299,30 +23606,30 @@ function removeManagedCompiledFiles(dir, pattern, expectedTarget, logger) {
|
|
|
23299
23606
|
}
|
|
23300
23607
|
}
|
|
23301
23608
|
function removeManagedCompiledFile(filePath, expectedTarget, logger) {
|
|
23302
|
-
if (!
|
|
23609
|
+
if (!fs25.existsSync(filePath)) {
|
|
23303
23610
|
return;
|
|
23304
23611
|
}
|
|
23305
|
-
const marker = extractProvenanceMarker(
|
|
23612
|
+
const marker = extractProvenanceMarker(fs25.readFileSync(filePath, "utf8"));
|
|
23306
23613
|
if (marker?.target === expectedTarget) {
|
|
23307
|
-
|
|
23614
|
+
fs25.rmSync(filePath, { force: true });
|
|
23308
23615
|
logger.info(`removed ${filePath}`);
|
|
23309
23616
|
}
|
|
23310
23617
|
}
|
|
23311
23618
|
function removeManagedJsonFile(filePath, markerKey, logger) {
|
|
23312
|
-
if (!
|
|
23619
|
+
if (!fs25.existsSync(filePath)) {
|
|
23313
23620
|
return;
|
|
23314
23621
|
}
|
|
23315
23622
|
const parsed = readJsonObject(filePath);
|
|
23316
23623
|
if (isRecord(parsed[markerKey])) {
|
|
23317
|
-
|
|
23624
|
+
fs25.rmSync(filePath, { force: true });
|
|
23318
23625
|
logger.info(`removed ${filePath}`);
|
|
23319
23626
|
}
|
|
23320
23627
|
}
|
|
23321
23628
|
function removeCodexManagedTomlSection(filePath, logger) {
|
|
23322
|
-
if (!
|
|
23629
|
+
if (!fs25.existsSync(filePath)) {
|
|
23323
23630
|
return;
|
|
23324
23631
|
}
|
|
23325
|
-
const content =
|
|
23632
|
+
const content = fs25.readFileSync(filePath, "utf8");
|
|
23326
23633
|
const next = removeDelimitedManagedSection2(
|
|
23327
23634
|
content,
|
|
23328
23635
|
codexManagedMcpStart2,
|
|
@@ -23331,7 +23638,7 @@ function removeCodexManagedTomlSection(filePath, logger) {
|
|
|
23331
23638
|
if (next === content) {
|
|
23332
23639
|
return;
|
|
23333
23640
|
}
|
|
23334
|
-
|
|
23641
|
+
fs25.writeFileSync(filePath, next);
|
|
23335
23642
|
logger.info(`mcp: removed managed section from ${filePath}`);
|
|
23336
23643
|
}
|
|
23337
23644
|
function removeDelimitedManagedSection2(content, startMarker, endMarker) {
|
|
@@ -23403,7 +23710,7 @@ function toVerbosity(value) {
|
|
|
23403
23710
|
}
|
|
23404
23711
|
|
|
23405
23712
|
// src/commands/detect.ts
|
|
23406
|
-
import
|
|
23713
|
+
import fs26 from "fs";
|
|
23407
23714
|
import os5 from "os";
|
|
23408
23715
|
import path23 from "path";
|
|
23409
23716
|
import { spawnSync } from "child_process";
|
|
@@ -23444,7 +23751,7 @@ var bundledCursorExecutablePathsByPlatform = {
|
|
|
23444
23751
|
};
|
|
23445
23752
|
function detectTargets(logger, dependencies = {}) {
|
|
23446
23753
|
const spawnSyncImpl = dependencies.spawnSyncImpl ?? defaultSpawnSync;
|
|
23447
|
-
const pathExists = dependencies.pathExists ??
|
|
23754
|
+
const pathExists = dependencies.pathExists ?? fs26.existsSync;
|
|
23448
23755
|
const homeDirectory = dependencies.homeDirectory ?? os5.homedir();
|
|
23449
23756
|
const platform = dependencies.platform ?? process.platform;
|
|
23450
23757
|
const locator = platform === "win32" ? "where.exe" : "which";
|
|
@@ -23479,8 +23786,8 @@ function detectsCopilot(logger, dependencies = {}) {
|
|
|
23479
23786
|
return detectCopilot(logger, dependencies);
|
|
23480
23787
|
}
|
|
23481
23788
|
function detectCopilot(logger, dependencies = {}) {
|
|
23482
|
-
const pathExists = dependencies.pathExists ??
|
|
23483
|
-
const readFile = dependencies.readFile ?? ((filePath) =>
|
|
23789
|
+
const pathExists = dependencies.pathExists ?? fs26.existsSync;
|
|
23790
|
+
const readFile = dependencies.readFile ?? ((filePath) => fs26.readFileSync(filePath, "utf8"));
|
|
23484
23791
|
const platform = dependencies.platform ?? process.platform;
|
|
23485
23792
|
if (detectEditorExtension(
|
|
23486
23793
|
"Copilot",
|
|
@@ -23508,8 +23815,8 @@ function detectCopilot(logger, dependencies = {}) {
|
|
|
23508
23815
|
}
|
|
23509
23816
|
function detectEditorExtension(label, matchesExtension, logger, dependencies = {}) {
|
|
23510
23817
|
const spawnSyncImpl = dependencies.spawnSyncImpl ?? defaultSpawnSync;
|
|
23511
|
-
const pathExists = dependencies.pathExists ??
|
|
23512
|
-
const readDirectory = dependencies.readDirectory ??
|
|
23818
|
+
const pathExists = dependencies.pathExists ?? fs26.existsSync;
|
|
23819
|
+
const readDirectory = dependencies.readDirectory ?? fs26.readdirSync;
|
|
23513
23820
|
const homeDirectory = dependencies.homeDirectory ?? os5.homedir();
|
|
23514
23821
|
const platform = dependencies.platform ?? process.platform;
|
|
23515
23822
|
const commandCandidates = [
|
|
@@ -23620,45 +23927,55 @@ function logDetectionError(attemptedCommand, error, logger) {
|
|
|
23620
23927
|
}
|
|
23621
23928
|
|
|
23622
23929
|
// src/commands/doctor.ts
|
|
23623
|
-
import
|
|
23930
|
+
import fs28 from "fs";
|
|
23624
23931
|
import path25 from "path";
|
|
23625
23932
|
|
|
23626
23933
|
// src/utils/fs.ts
|
|
23627
|
-
import
|
|
23934
|
+
import fs27 from "fs";
|
|
23628
23935
|
import path24 from "path";
|
|
23629
23936
|
function ensureDir(dirPath) {
|
|
23630
|
-
|
|
23937
|
+
fs27.mkdirSync(dirPath, { recursive: true });
|
|
23631
23938
|
}
|
|
23632
23939
|
function createRelativeSymlink(source, destination, type, options) {
|
|
23633
23940
|
const absoluteSource = path24.resolve(source);
|
|
23634
|
-
const relative = path24.relative(
|
|
23941
|
+
const relative = path24.relative(
|
|
23942
|
+
realPathOrInput(path24.dirname(destination)),
|
|
23943
|
+
absoluteSource
|
|
23944
|
+
);
|
|
23635
23945
|
if (process.platform === "win32" && type === "dir") {
|
|
23636
|
-
|
|
23946
|
+
fs27.symlinkSync(absoluteSource, destination, "junction");
|
|
23637
23947
|
return;
|
|
23638
23948
|
}
|
|
23639
23949
|
if (process.platform === "win32" && type === "file") {
|
|
23640
23950
|
try {
|
|
23641
|
-
|
|
23951
|
+
fs27.symlinkSync(relative, destination, "file");
|
|
23642
23952
|
} catch (error) {
|
|
23643
23953
|
if (!hasErrorCode(error, "EPERM")) {
|
|
23644
23954
|
throw error;
|
|
23645
23955
|
}
|
|
23646
23956
|
if (!options?.provenanceMarker) {
|
|
23647
|
-
throw new Error(
|
|
23957
|
+
throw new Error(
|
|
23958
|
+
`${destination}: file symlink unavailable on Windows and no provenance marker was provided.`
|
|
23959
|
+
);
|
|
23648
23960
|
}
|
|
23649
|
-
createManagedCopy(
|
|
23961
|
+
createManagedCopy(
|
|
23962
|
+
source,
|
|
23963
|
+
destination,
|
|
23964
|
+
options.provenanceMarker,
|
|
23965
|
+
options.logger
|
|
23966
|
+
);
|
|
23650
23967
|
options.logger?.warn(
|
|
23651
23968
|
`${destination}: file symlink unavailable on Windows \u2014 using managed copy with provenance metadata (behavioral contract C-09)`
|
|
23652
23969
|
);
|
|
23653
23970
|
}
|
|
23654
23971
|
return;
|
|
23655
23972
|
}
|
|
23656
|
-
|
|
23973
|
+
fs27.symlinkSync(relative, destination, type);
|
|
23657
23974
|
}
|
|
23658
23975
|
function createManagedCopy(source, destination, provenanceMarker, _logger) {
|
|
23659
23976
|
ensureDir(path24.dirname(destination));
|
|
23660
|
-
const content =
|
|
23661
|
-
|
|
23977
|
+
const content = fs27.readFileSync(source, "utf8");
|
|
23978
|
+
fs27.writeFileSync(destination, `${provenanceMarker}
|
|
23662
23979
|
${content}`);
|
|
23663
23980
|
}
|
|
23664
23981
|
function hasErrorCode(error, expectedCode) {
|
|
@@ -23666,7 +23983,7 @@ function hasErrorCode(error, expectedCode) {
|
|
|
23666
23983
|
}
|
|
23667
23984
|
function realPathOrInput(filePath) {
|
|
23668
23985
|
try {
|
|
23669
|
-
return
|
|
23986
|
+
return fs27.realpathSync(filePath);
|
|
23670
23987
|
} catch {
|
|
23671
23988
|
return filePath;
|
|
23672
23989
|
}
|
|
@@ -23679,7 +23996,7 @@ function runDoctorCommand(options, logger) {
|
|
|
23679
23996
|
import.meta.url
|
|
23680
23997
|
);
|
|
23681
23998
|
const findings = [];
|
|
23682
|
-
if (!
|
|
23999
|
+
if (!fs28.existsSync(configPath)) {
|
|
23683
24000
|
findings.push(`${configPath}: missing`);
|
|
23684
24001
|
}
|
|
23685
24002
|
const hooksJsonPath = path25.join(
|
|
@@ -23687,9 +24004,9 @@ function runDoctorCommand(options, logger) {
|
|
|
23687
24004
|
config.source.hooks,
|
|
23688
24005
|
"hooks.json"
|
|
23689
24006
|
);
|
|
23690
|
-
if (
|
|
24007
|
+
if (fs28.existsSync(hooksJsonPath)) {
|
|
23691
24008
|
try {
|
|
23692
|
-
JSON.parse(
|
|
24009
|
+
JSON.parse(fs28.readFileSync(hooksJsonPath, "utf8"));
|
|
23693
24010
|
} catch (err) {
|
|
23694
24011
|
findings.push(
|
|
23695
24012
|
`hooks/hooks.json: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -23764,9 +24081,9 @@ function checkClaudeCode(options, findings, config, sourceRoot, configDir, logge
|
|
|
23764
24081
|
/* @__PURE__ */ new Set([path25.resolve(artifact.path)]),
|
|
23765
24082
|
config
|
|
23766
24083
|
)) {
|
|
23767
|
-
|
|
24084
|
+
fs28.unlinkSync(artifact.path);
|
|
23768
24085
|
ensureDir(path25.dirname(artifact.path));
|
|
23769
|
-
const symlinkType =
|
|
24086
|
+
const symlinkType = fs28.statSync(sourcePath).isDirectory() ? "dir" : "file";
|
|
23770
24087
|
createRelativeSymlink(sourcePath, artifact.path, symlinkType, {
|
|
23771
24088
|
logger,
|
|
23772
24089
|
provenanceMarker: symlinkType === "file" && artifact.sourceKey ? formatProvenanceMarker(
|
|
@@ -23779,7 +24096,7 @@ function checkClaudeCode(options, findings, config, sourceRoot, configDir, logge
|
|
|
23779
24096
|
logger.info(`${artifact.component}: recreated managed symlink`);
|
|
23780
24097
|
}
|
|
23781
24098
|
}
|
|
23782
|
-
if (options.fix && !
|
|
24099
|
+
if (options.fix && !fs28.existsSync(path25.dirname(artifact.path))) {
|
|
23783
24100
|
ensureDir(path25.dirname(artifact.path));
|
|
23784
24101
|
}
|
|
23785
24102
|
checkParentWritablePath(artifact.path, artifact.component, findings);
|
|
@@ -23841,24 +24158,24 @@ function checkParentWritablePath(targetPath, label, findings) {
|
|
|
23841
24158
|
checkWritablePath(path25.dirname(targetPath), `${label} parent`, findings);
|
|
23842
24159
|
}
|
|
23843
24160
|
function checkWritablePath(checkPath, label, findings) {
|
|
23844
|
-
if (!
|
|
24161
|
+
if (!fs28.existsSync(checkPath)) {
|
|
23845
24162
|
findings.push(`${label}: path does not exist`);
|
|
23846
24163
|
return;
|
|
23847
24164
|
}
|
|
23848
24165
|
try {
|
|
23849
|
-
|
|
24166
|
+
fs28.accessSync(checkPath, fs28.constants.R_OK | fs28.constants.W_OK);
|
|
23850
24167
|
} catch {
|
|
23851
24168
|
findings.push(`${label}: path is not readable and writable`);
|
|
23852
24169
|
}
|
|
23853
24170
|
}
|
|
23854
24171
|
function walkHookScripts(dir) {
|
|
23855
|
-
if (!
|
|
24172
|
+
if (!fs28.existsSync(dir)) {
|
|
23856
24173
|
return [];
|
|
23857
24174
|
}
|
|
23858
|
-
return
|
|
24175
|
+
return fs28.readdirSync(dir).map((entry) => path25.join(dir, entry)).filter((entryPath) => fs28.statSync(entryPath).isFile());
|
|
23859
24176
|
}
|
|
23860
24177
|
function fixManagedHookScriptModes(scriptRoot, sourceRoot, logger) {
|
|
23861
|
-
if (!
|
|
24178
|
+
if (!fs28.existsSync(scriptRoot)) {
|
|
23862
24179
|
return;
|
|
23863
24180
|
}
|
|
23864
24181
|
if (hasSymlinkAncestor(scriptRoot, resolveTargetPath("~/.agents")) || isInside(realPathOrInput2(scriptRoot), realPathOrInput2(sourceRoot))) {
|
|
@@ -23868,7 +24185,7 @@ function fixManagedHookScriptModes(scriptRoot, sourceRoot, logger) {
|
|
|
23868
24185
|
return;
|
|
23869
24186
|
}
|
|
23870
24187
|
for (const scriptPath of walkHookScripts(scriptRoot)) {
|
|
23871
|
-
|
|
24188
|
+
fs28.chmodSync(scriptPath, 493);
|
|
23872
24189
|
}
|
|
23873
24190
|
}
|
|
23874
24191
|
function hasSymlinkAncestor(targetPath, stopAt) {
|
|
@@ -23885,7 +24202,7 @@ function hasSymlinkAncestor(targetPath, stopAt) {
|
|
|
23885
24202
|
...segments.slice(0, index + 1)
|
|
23886
24203
|
);
|
|
23887
24204
|
try {
|
|
23888
|
-
return
|
|
24205
|
+
return fs28.lstatSync(candidate).isSymbolicLink();
|
|
23889
24206
|
} catch {
|
|
23890
24207
|
return false;
|
|
23891
24208
|
}
|
|
@@ -23897,14 +24214,14 @@ function isInside(childPath, parentPath) {
|
|
|
23897
24214
|
}
|
|
23898
24215
|
function realPathOrInput2(filePath) {
|
|
23899
24216
|
try {
|
|
23900
|
-
return
|
|
24217
|
+
return fs28.realpathSync(filePath);
|
|
23901
24218
|
} catch {
|
|
23902
24219
|
return filePath;
|
|
23903
24220
|
}
|
|
23904
24221
|
}
|
|
23905
24222
|
|
|
23906
24223
|
// src/commands/init.ts
|
|
23907
|
-
import
|
|
24224
|
+
import fs29 from "fs";
|
|
23908
24225
|
import path26 from "path";
|
|
23909
24226
|
var import_yaml7 = __toESM(require_dist(), 1);
|
|
23910
24227
|
async function runInitCommand(_options, logger) {
|
|
@@ -23920,16 +24237,18 @@ async function runInitCommand(_options, logger) {
|
|
|
23920
24237
|
"rules",
|
|
23921
24238
|
"hooks"
|
|
23922
24239
|
].filter(
|
|
23923
|
-
(relativePath) =>
|
|
24240
|
+
(relativePath) => fs29.existsSync(path26.join(sourceRoot, relativePath))
|
|
23924
24241
|
).length;
|
|
23925
24242
|
const detectedTargets = detectTargets(logger);
|
|
23926
24243
|
const enabledTargets = await dist_default4({
|
|
23927
24244
|
message: `Detected ${componentCount} source components. Select targets`,
|
|
23928
|
-
choices: Object.entries(detectedTargets).map(
|
|
23929
|
-
name,
|
|
23930
|
-
|
|
23931
|
-
|
|
23932
|
-
|
|
24245
|
+
choices: Object.entries(detectedTargets).map(
|
|
24246
|
+
([name, detected]) => ({
|
|
24247
|
+
name,
|
|
24248
|
+
value: name,
|
|
24249
|
+
checked: detected
|
|
24250
|
+
})
|
|
24251
|
+
)
|
|
23933
24252
|
});
|
|
23934
24253
|
const onConflict = await dist_default8({
|
|
23935
24254
|
message: "Conflict policy",
|
|
@@ -23939,7 +24258,7 @@ async function runInitCommand(_options, logger) {
|
|
|
23939
24258
|
}))
|
|
23940
24259
|
});
|
|
23941
24260
|
const configPath = path26.join(sourceRoot, "tangyr.config.yaml");
|
|
23942
|
-
if (
|
|
24261
|
+
if (fs29.existsSync(configPath)) {
|
|
23943
24262
|
const overwrite = await dist_default5({
|
|
23944
24263
|
message: `${configPath} exists. Overwrite?`,
|
|
23945
24264
|
default: false
|
|
@@ -23949,7 +24268,7 @@ async function runInitCommand(_options, logger) {
|
|
|
23949
24268
|
return;
|
|
23950
24269
|
}
|
|
23951
24270
|
}
|
|
23952
|
-
|
|
24271
|
+
fs29.writeFileSync(
|
|
23953
24272
|
configPath,
|
|
23954
24273
|
(0, import_yaml7.stringify)({
|
|
23955
24274
|
schema: 1,
|
|
@@ -23992,11 +24311,11 @@ function toConflictPolicy2(value) {
|
|
|
23992
24311
|
}
|
|
23993
24312
|
|
|
23994
24313
|
// src/commands/install.ts
|
|
23995
|
-
import
|
|
24314
|
+
import fs38 from "fs";
|
|
23996
24315
|
import path37 from "path";
|
|
23997
24316
|
|
|
23998
24317
|
// src/core/channel-state.ts
|
|
23999
|
-
import
|
|
24318
|
+
import fs30 from "fs";
|
|
24000
24319
|
import path27 from "path";
|
|
24001
24320
|
var CHANNEL_STATE_FILENAME = ".tangyr-channels.json";
|
|
24002
24321
|
function stateFilePath(cacheRoot) {
|
|
@@ -24009,7 +24328,7 @@ function readState(cacheRoot) {
|
|
|
24009
24328
|
const filePath = stateFilePath(cacheRoot);
|
|
24010
24329
|
let raw;
|
|
24011
24330
|
try {
|
|
24012
|
-
raw =
|
|
24331
|
+
raw = fs30.readFileSync(filePath, "utf8");
|
|
24013
24332
|
} catch (err) {
|
|
24014
24333
|
if (err.code === "ENOENT") {
|
|
24015
24334
|
return { state: {} };
|
|
@@ -24056,13 +24375,13 @@ function writeChannelSequence(params) {
|
|
|
24056
24375
|
[key]: { sequence, version, seenAt: now.toISOString() }
|
|
24057
24376
|
};
|
|
24058
24377
|
const filePath = stateFilePath(cacheRoot);
|
|
24059
|
-
|
|
24378
|
+
fs30.mkdirSync(path27.dirname(filePath), { recursive: true, mode: 448 });
|
|
24060
24379
|
const tempPath = `${filePath}.${process.pid}.tmp`;
|
|
24061
|
-
|
|
24380
|
+
fs30.writeFileSync(tempPath, `${JSON.stringify(state, null, 2)}
|
|
24062
24381
|
`, {
|
|
24063
24382
|
mode: 384
|
|
24064
24383
|
});
|
|
24065
|
-
|
|
24384
|
+
fs30.renameSync(tempPath, filePath);
|
|
24066
24385
|
}
|
|
24067
24386
|
function assertChannelNotRolledBack(params) {
|
|
24068
24387
|
const { originUrl, kit, channel, seen, seenVersion, incoming } = params;
|
|
@@ -24078,7 +24397,7 @@ var CHANNEL_POINTER_TYPE = "tangyr.channel-pointer.v1";
|
|
|
24078
24397
|
var DEFAULT_CHANNEL = "stable";
|
|
24079
24398
|
|
|
24080
24399
|
// src/core/materialize.ts
|
|
24081
|
-
import
|
|
24400
|
+
import fs31 from "fs";
|
|
24082
24401
|
import path28 from "path";
|
|
24083
24402
|
|
|
24084
24403
|
// src/core/integrity.ts
|
|
@@ -24489,10 +24808,10 @@ function verifyBundleFileHashes(bundle) {
|
|
|
24489
24808
|
function writeStagedFiles(stagingDir, decoded) {
|
|
24490
24809
|
for (const [rawPath, bytes] of decoded) {
|
|
24491
24810
|
const target = sanitizeBundleFilePath(rawPath, stagingDir);
|
|
24492
|
-
|
|
24811
|
+
fs31.mkdirSync(path28.dirname(target), { recursive: true, mode: 448 });
|
|
24493
24812
|
let existingStat;
|
|
24494
24813
|
try {
|
|
24495
|
-
existingStat =
|
|
24814
|
+
existingStat = fs31.lstatSync(target);
|
|
24496
24815
|
} catch (error) {
|
|
24497
24816
|
if (error.code !== "ENOENT") {
|
|
24498
24817
|
throw error;
|
|
@@ -24503,7 +24822,7 @@ function writeStagedFiles(stagingDir, decoded) {
|
|
|
24503
24822
|
`Refusing to write "${rawPath}" \u2014 a symbolic link already exists at the target path.`
|
|
24504
24823
|
);
|
|
24505
24824
|
}
|
|
24506
|
-
|
|
24825
|
+
fs31.writeFileSync(target, bytes, { mode: 384, flag: "wx" });
|
|
24507
24826
|
}
|
|
24508
24827
|
}
|
|
24509
24828
|
function verifyWarmCacheEntry(entryDir, reference, keys, now) {
|
|
@@ -24559,14 +24878,14 @@ async function materializeRemoteKit(params) {
|
|
|
24559
24878
|
writeStagedFiles(stagingDir, decoded);
|
|
24560
24879
|
writeBundleDocument(stagingDir, bundle);
|
|
24561
24880
|
writeCompletionMarker(stagingDir, reference);
|
|
24562
|
-
if (replace &&
|
|
24563
|
-
|
|
24881
|
+
if (replace && fs31.existsSync(entryDir)) {
|
|
24882
|
+
fs31.rmSync(entryDir, { recursive: true, force: true });
|
|
24564
24883
|
}
|
|
24565
24884
|
publishCacheEntry(stagingDir, entryDir);
|
|
24566
24885
|
return entryDir;
|
|
24567
24886
|
} catch (error) {
|
|
24568
24887
|
if (stagingDir !== void 0) {
|
|
24569
|
-
|
|
24888
|
+
fs31.rmSync(stagingDir, { recursive: true, force: true });
|
|
24570
24889
|
}
|
|
24571
24890
|
throw error;
|
|
24572
24891
|
}
|
|
@@ -24902,65 +25221,13 @@ function sleep(ms2) {
|
|
|
24902
25221
|
// src/adapters/codex.ts
|
|
24903
25222
|
var TOML3 = __toESM(require_toml(), 1);
|
|
24904
25223
|
import crypto5 from "crypto";
|
|
24905
|
-
import
|
|
25224
|
+
import fs33 from "fs";
|
|
24906
25225
|
import path31 from "path";
|
|
24907
25226
|
|
|
24908
25227
|
// src/compiler/agents.ts
|
|
24909
25228
|
var TOML = __toESM(require_toml(), 1);
|
|
24910
25229
|
var import_yaml8 = __toESM(require_dist(), 1);
|
|
24911
25230
|
import path29 from "path";
|
|
24912
|
-
|
|
24913
|
-
// src/compiler/loss-report.ts
|
|
24914
|
-
function createLossReport() {
|
|
24915
|
-
return {
|
|
24916
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24917
|
-
targets: {},
|
|
24918
|
-
summary: {},
|
|
24919
|
-
informational: []
|
|
24920
|
-
};
|
|
24921
|
-
}
|
|
24922
|
-
function addInformational(report, target, component, source, severity, detail) {
|
|
24923
|
-
report.informational.push({ target, component, source, severity, detail });
|
|
24924
|
-
}
|
|
24925
|
-
function addLoss(report, target, component, loss) {
|
|
24926
|
-
const targetReport = report.targets[target] ?? {};
|
|
24927
|
-
const componentReport = targetReport[component] ?? [];
|
|
24928
|
-
componentReport.push(loss);
|
|
24929
|
-
targetReport[component] = componentReport;
|
|
24930
|
-
report.targets[target] = targetReport;
|
|
24931
|
-
const targetSummary = report.summary[target] ?? {
|
|
24932
|
-
info: 0,
|
|
24933
|
-
warning: 0,
|
|
24934
|
-
error: 0
|
|
24935
|
-
};
|
|
24936
|
-
for (const field of loss.fields) {
|
|
24937
|
-
targetSummary[field.severity] += 1;
|
|
24938
|
-
}
|
|
24939
|
-
report.summary[target] = targetSummary;
|
|
24940
|
-
}
|
|
24941
|
-
var lossSeverityByEntryType = {
|
|
24942
|
-
"command-as-skill": "info",
|
|
24943
|
-
"rule-as-instruction": "info",
|
|
24944
|
-
"rule-mode-downgrade": "warning",
|
|
24945
|
-
"rule-path-scoping": "warning",
|
|
24946
|
-
"hook-event-drop": "warning",
|
|
24947
|
-
"hook-as-plugin": "error",
|
|
24948
|
-
"instructions-shim": "info",
|
|
24949
|
-
"permission-coarsening": "error",
|
|
24950
|
-
"provider-not-representable": "error",
|
|
24951
|
-
"provider-wire-protocol": "error",
|
|
24952
|
-
"model-not-representable": "error",
|
|
24953
|
-
"persona-not-representable": "error",
|
|
24954
|
-
"settings-not-representable": "warning"
|
|
24955
|
-
};
|
|
24956
|
-
function resolveLossSeverity(kind, name, action, explicitSeverity) {
|
|
24957
|
-
if (explicitSeverity) {
|
|
24958
|
-
return explicitSeverity;
|
|
24959
|
-
}
|
|
24960
|
-
return lossSeverityByEntryType[action];
|
|
24961
|
-
}
|
|
24962
|
-
|
|
24963
|
-
// src/compiler/agents.ts
|
|
24964
25231
|
var writeCapabilityClasses = /* @__PURE__ */ new Set(["filesystem-write", "shell-execution"]);
|
|
24965
25232
|
var copilotToolAliasesByClass = {
|
|
24966
25233
|
delegation: "agent",
|
|
@@ -25380,12 +25647,37 @@ function compileCursorHooks(sourceHooksJson, mappings, lossReport, sourceLabel)
|
|
|
25380
25647
|
let dropped = 0;
|
|
25381
25648
|
for (const [sourceEvent, payload] of Object.entries(sourceHooks)) {
|
|
25382
25649
|
const mapping = normalizeHookEventRule(
|
|
25383
|
-
targetMapping.source_to_target[sourceEvent]
|
|
25650
|
+
targetMapping.source_to_target[sourceEvent],
|
|
25651
|
+
"cursor",
|
|
25652
|
+
sourceEvent
|
|
25384
25653
|
);
|
|
25385
25654
|
if (mapping.status === "mapped" && mapping.target_event) {
|
|
25386
|
-
const
|
|
25387
|
-
|
|
25655
|
+
const extracted = extractHookCommand(payload);
|
|
25656
|
+
if (!extracted.command) {
|
|
25657
|
+
droppedEvents.push(sourceEvent);
|
|
25658
|
+
addHookLoss(
|
|
25659
|
+
lossReport,
|
|
25660
|
+
"cursor",
|
|
25661
|
+
sourceEvent,
|
|
25662
|
+
sourceLabel,
|
|
25663
|
+
"error",
|
|
25664
|
+
`${sourceEvent}: no command could be read from the source entry, so nothing was written for Cursor. To fix: check that the hook declares a command \u2014 'tangyr validate' names the file and the field.`
|
|
25665
|
+
);
|
|
25666
|
+
dropped += 1;
|
|
25667
|
+
continue;
|
|
25668
|
+
}
|
|
25669
|
+
hooks[mapping.target_event] = { command: extracted.command };
|
|
25388
25670
|
translated += 1;
|
|
25671
|
+
if (extracted.matcher) {
|
|
25672
|
+
addInformational(
|
|
25673
|
+
lossReport,
|
|
25674
|
+
"cursor",
|
|
25675
|
+
"hooks",
|
|
25676
|
+
sourceLabel,
|
|
25677
|
+
"warning",
|
|
25678
|
+
`${sourceEvent}: the source scopes this hook to '${extracted.matcher}', and Cursor's hook entry carries no matcher. It will run for every tool call on this event, not only the matched ones.`
|
|
25679
|
+
);
|
|
25680
|
+
}
|
|
25389
25681
|
continue;
|
|
25390
25682
|
}
|
|
25391
25683
|
droppedEvents.push(sourceEvent);
|
|
@@ -25504,7 +25796,9 @@ function compileCopilotHooks(sourceHooksJson, mappings, lossReport, sourceLabel)
|
|
|
25504
25796
|
}
|
|
25505
25797
|
for (const [sourceEvent, payload] of Object.entries(sourceHooks)) {
|
|
25506
25798
|
const mapping = normalizeHookEventRule(
|
|
25507
|
-
targetMapping.source_to_target[sourceEvent]
|
|
25799
|
+
targetMapping.source_to_target[sourceEvent],
|
|
25800
|
+
"copilot",
|
|
25801
|
+
sourceEvent
|
|
25508
25802
|
);
|
|
25509
25803
|
if (mapping.status === "mapped" && mapping.target_event) {
|
|
25510
25804
|
hooks[mapping.target_event] = payload;
|
|
@@ -25539,11 +25833,35 @@ function compileCopilotHooks(sourceHooksJson, mappings, lossReport, sourceLabel)
|
|
|
25539
25833
|
}
|
|
25540
25834
|
return { payload: { hooks }, translated, dropped, droppedEvents };
|
|
25541
25835
|
}
|
|
25542
|
-
function
|
|
25836
|
+
function extractHookCommand(payload) {
|
|
25837
|
+
const entries = Array.isArray(payload) ? payload : [payload];
|
|
25838
|
+
for (const entry of entries) {
|
|
25839
|
+
if (!isRecord(entry)) continue;
|
|
25840
|
+
const matcher = typeof entry.matcher === "string" && entry.matcher ? { matcher: entry.matcher } : {};
|
|
25841
|
+
if (typeof entry.command === "string" && entry.command) {
|
|
25842
|
+
return { command: entry.command, ...matcher };
|
|
25843
|
+
}
|
|
25844
|
+
const inner = Array.isArray(entry.hooks) ? entry.hooks : [];
|
|
25845
|
+
for (const hook of inner) {
|
|
25846
|
+
if (isRecord(hook) && typeof hook.command === "string" && hook.command) {
|
|
25847
|
+
return { command: hook.command, ...matcher };
|
|
25848
|
+
}
|
|
25849
|
+
}
|
|
25850
|
+
}
|
|
25851
|
+
return { command: "" };
|
|
25852
|
+
}
|
|
25853
|
+
function normalizeHookEventRule(value, target, sourceEvent) {
|
|
25543
25854
|
if (typeof value === "string") {
|
|
25544
25855
|
return { status: "mapped", target_event: value };
|
|
25545
25856
|
}
|
|
25546
|
-
|
|
25857
|
+
if (value) {
|
|
25858
|
+
return value;
|
|
25859
|
+
}
|
|
25860
|
+
return {
|
|
25861
|
+
status: "unmapped",
|
|
25862
|
+
severity: "warning",
|
|
25863
|
+
reason: `mappings/hook-events.yaml has no entry for '${sourceEvent}' under target '${target}', so it was dropped without a decision behind it. To fix: add '${sourceEvent}' to that file \u2014 to source_events, and to every target's source_to_target with either a target_event or a stated reason for dropping it.`
|
|
25864
|
+
};
|
|
25547
25865
|
}
|
|
25548
25866
|
function compileCodexHooks(sourceHooksJson, mappings, lossReport, sourceLabel) {
|
|
25549
25867
|
const targetMapping = mappings.hookEvents.targets.codex;
|
|
@@ -25557,7 +25875,9 @@ function compileCodexHooks(sourceHooksJson, mappings, lossReport, sourceLabel) {
|
|
|
25557
25875
|
let dropped = 0;
|
|
25558
25876
|
for (const [sourceEvent, payload] of Object.entries(sourceHooks)) {
|
|
25559
25877
|
const mapping = normalizeHookEventRule(
|
|
25560
|
-
targetMapping.source_to_target[sourceEvent]
|
|
25878
|
+
targetMapping.source_to_target[sourceEvent],
|
|
25879
|
+
"codex",
|
|
25880
|
+
sourceEvent
|
|
25561
25881
|
);
|
|
25562
25882
|
if (mapping.status === "mapped" && mapping.target_event) {
|
|
25563
25883
|
hooks[mapping.target_event] = markCodexManaged(payload, sourceLabel);
|
|
@@ -25890,7 +26210,7 @@ function compileOpenCodeMcp(sourceMcpJson, lossReport, sourceLabel) {
|
|
|
25890
26210
|
// src/compiler/rules.ts
|
|
25891
26211
|
import path30 from "path";
|
|
25892
26212
|
var import_yaml9 = __toESM(require_dist(), 1);
|
|
25893
|
-
import
|
|
26213
|
+
import fs32 from "fs";
|
|
25894
26214
|
function compileRules(sourceRulesDir, target, _mappings, lossReport) {
|
|
25895
26215
|
if (target === "copilot") {
|
|
25896
26216
|
addLoss(lossReport, "copilot", "rules", {
|
|
@@ -25960,7 +26280,7 @@ ${parsed.body.trim()}`);
|
|
|
25960
26280
|
return { agentsAppendix: appendixParts.join("\n\n"), dotRulesFiles };
|
|
25961
26281
|
}
|
|
25962
26282
|
function parseRule(ruleFile) {
|
|
25963
|
-
const content =
|
|
26283
|
+
const content = fs32.readFileSync(ruleFile, "utf8");
|
|
25964
26284
|
if (!content.startsWith("---\n")) {
|
|
25965
26285
|
return { mode: "guidance", body: content };
|
|
25966
26286
|
}
|
|
@@ -26011,7 +26331,7 @@ ${parsed.body.trim()}`);
|
|
|
26011
26331
|
function compileCursorRules(sourceRulesDir, _lossReport) {
|
|
26012
26332
|
const dotRulesFiles = [];
|
|
26013
26333
|
for (const ruleFile of ts(path30.join(sourceRulesDir, "*.md")).sort()) {
|
|
26014
|
-
const content =
|
|
26334
|
+
const content = fs32.readFileSync(ruleFile, "utf8");
|
|
26015
26335
|
const name = path30.basename(ruleFile, ".md");
|
|
26016
26336
|
let frontmatterData = {};
|
|
26017
26337
|
let body = content;
|
|
@@ -26058,7 +26378,7 @@ ${body}`;
|
|
|
26058
26378
|
function compileClineRules(sourceRulesDir, lossReport) {
|
|
26059
26379
|
const dotRulesFiles = [];
|
|
26060
26380
|
for (const ruleFile of ts(path30.join(sourceRulesDir, "*.md")).sort()) {
|
|
26061
|
-
const content =
|
|
26381
|
+
const content = fs32.readFileSync(ruleFile, "utf8");
|
|
26062
26382
|
const name = path30.basename(ruleFile, ".md");
|
|
26063
26383
|
let frontmatterData = {};
|
|
26064
26384
|
let body = content;
|
|
@@ -26283,7 +26603,7 @@ function buildProviderTomlSection(config, sourceRoot, mappings, scope, lossRepor
|
|
|
26283
26603
|
const cp = mappings.modelTiers.custom_provider;
|
|
26284
26604
|
if (!cp) return "";
|
|
26285
26605
|
const agentsDir = path31.join(sourceRoot, config.source.agents);
|
|
26286
|
-
if (!
|
|
26606
|
+
if (!fs33.existsSync(agentsDir)) return "";
|
|
26287
26607
|
const agentFiles = ts(path31.join(agentsDir, "*.md")).sort();
|
|
26288
26608
|
const hasProviderAgent = agentFiles.some((agentFile) => {
|
|
26289
26609
|
const { data } = parseMarkdownFrontmatter(agentFile);
|
|
@@ -26379,7 +26699,7 @@ function syncRules(config, sourceRoot, paths, rules, options, logger, manifest,
|
|
|
26379
26699
|
function syncInstructions(config, sourceRoot, paths, agentsAppendix, options, logger, manifest, scopePath) {
|
|
26380
26700
|
const source = path31.join(sourceRoot, config.source.instructions);
|
|
26381
26701
|
const destination = resolveTargetPath(paths.instructions);
|
|
26382
|
-
const composed = `${
|
|
26702
|
+
const composed = `${fs33.readFileSync(source, "utf8").trim()}
|
|
26383
26703
|
|
|
26384
26704
|
--- tangyr managed rules ---
|
|
26385
26705
|
${agentsAppendix}`.trim();
|
|
@@ -26414,7 +26734,7 @@ ${composed}
|
|
|
26414
26734
|
function syncSkills(config, sourceRoot, paths, options, logger, manifest, scopePath) {
|
|
26415
26735
|
const source = path31.resolve(sourceRoot, config.source.skills);
|
|
26416
26736
|
const destination = resolveTargetPath(paths.skills);
|
|
26417
|
-
if (!
|
|
26737
|
+
if (!fs33.existsSync(source)) {
|
|
26418
26738
|
return skippedMissingSource("skills", source);
|
|
26419
26739
|
}
|
|
26420
26740
|
if (options.dryRun) {
|
|
@@ -26430,12 +26750,12 @@ function syncSkills(config, sourceRoot, paths, options, logger, manifest, scopeP
|
|
|
26430
26750
|
};
|
|
26431
26751
|
}
|
|
26432
26752
|
if (artifactClass === "managed-stale" && isOurStaleSymlink(destination, /* @__PURE__ */ new Set([destination]), config)) {
|
|
26433
|
-
|
|
26753
|
+
fs33.unlinkSync(destination);
|
|
26434
26754
|
logger.verbose(`Skills: created skills path ${destination}`);
|
|
26435
26755
|
} else if (artifactClass === "unmanaged-conflict" && !handleConflict(destination, config, options, logger, manifest, scopePath)) {
|
|
26436
26756
|
return { component: "skills", status: "conflict", path: destination };
|
|
26437
26757
|
} else if (artifactClass === "managed-symlink") {
|
|
26438
|
-
|
|
26758
|
+
fs33.unlinkSync(destination);
|
|
26439
26759
|
}
|
|
26440
26760
|
ensureDir(path31.dirname(destination));
|
|
26441
26761
|
createRelativeSymlink(source, destination, "dir");
|
|
@@ -26444,7 +26764,7 @@ function syncSkills(config, sourceRoot, paths, options, logger, manifest, scopeP
|
|
|
26444
26764
|
}
|
|
26445
26765
|
function syncAgents(config, sourceRoot, paths, mappings, extensions, options, lossReport, logger, manifest, scopePath) {
|
|
26446
26766
|
const sourceAgentsRoot = path31.join(sourceRoot, config.source.agents);
|
|
26447
|
-
if (!
|
|
26767
|
+
if (!fs33.existsSync(sourceAgentsRoot)) {
|
|
26448
26768
|
return [skippedMissingSource("agents", sourceAgentsRoot)];
|
|
26449
26769
|
}
|
|
26450
26770
|
const sourceAgents = ts(path31.join(sourceAgentsRoot, "*.md")).sort();
|
|
@@ -26486,14 +26806,16 @@ function syncHooks(config, sourceRoot, paths, mappings, options, lossReport, log
|
|
|
26486
26806
|
logger.warn(message);
|
|
26487
26807
|
return { component: "hooks", status: "skipped", path: message };
|
|
26488
26808
|
}
|
|
26489
|
-
const
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
|
|
26809
|
+
const hooksSource = loadKitHooksSource(sourceRoot, config);
|
|
26810
|
+
if (!hooksSource) {
|
|
26811
|
+
return skippedMissingSource(
|
|
26812
|
+
"hooks",
|
|
26813
|
+
resolveHooksSourceDir(sourceRoot, config)
|
|
26814
|
+
);
|
|
26493
26815
|
}
|
|
26494
|
-
const
|
|
26816
|
+
const { path: source, label: sourceLabel } = hooksSource;
|
|
26495
26817
|
const compiled = compileHooks(
|
|
26496
|
-
|
|
26818
|
+
hooksSource.hooks,
|
|
26497
26819
|
"codex",
|
|
26498
26820
|
mappings,
|
|
26499
26821
|
lossReport,
|
|
@@ -26513,7 +26835,7 @@ function syncHooks(config, sourceRoot, paths, mappings, options, lossReport, log
|
|
|
26513
26835
|
destination,
|
|
26514
26836
|
hostHooks,
|
|
26515
26837
|
stringifyJson2,
|
|
26516
|
-
!
|
|
26838
|
+
!fs33.existsSync(destination),
|
|
26517
26839
|
logger
|
|
26518
26840
|
);
|
|
26519
26841
|
return {
|
|
@@ -26526,7 +26848,7 @@ function syncMcp(config, sourceRoot, paths, options, lossReport, logger, provide
|
|
|
26526
26848
|
const source = resolveMcpSourceFile(sourceRoot, config);
|
|
26527
26849
|
const sourceLabel = mcpSourceLabel(config);
|
|
26528
26850
|
const destination = resolveTargetPath(paths.mcp_shared_file);
|
|
26529
|
-
if (!
|
|
26851
|
+
if (!fs33.existsSync(source)) {
|
|
26530
26852
|
if (providerToml && !options.dryRun) {
|
|
26531
26853
|
const changed2 = writeTomlManagedSection(
|
|
26532
26854
|
destination,
|
|
@@ -26541,7 +26863,7 @@ function syncMcp(config, sourceRoot, paths, options, lossReport, logger, provide
|
|
|
26541
26863
|
}
|
|
26542
26864
|
return skippedMissingSource("mcp", source);
|
|
26543
26865
|
}
|
|
26544
|
-
const sourceMcpJson = JSON.parse(
|
|
26866
|
+
const sourceMcpJson = JSON.parse(fs33.readFileSync(source, "utf8"));
|
|
26545
26867
|
const compiled = compileMcp(sourceMcpJson, "codex", lossReport, sourceLabel);
|
|
26546
26868
|
const managedContent = providerToml ? `${compiled.trimEnd()}${providerToml}` : compiled;
|
|
26547
26869
|
if (options.dryRun) {
|
|
@@ -26564,11 +26886,11 @@ function writeCompiledArtifact(component, destination, content, expectedHash, co
|
|
|
26564
26886
|
return { component, status: "conflict", path: destination };
|
|
26565
26887
|
}
|
|
26566
26888
|
ensureDir(path31.dirname(destination));
|
|
26567
|
-
|
|
26889
|
+
fs33.writeFileSync(destination, content);
|
|
26568
26890
|
return { component, status: "success", path: destination };
|
|
26569
26891
|
}
|
|
26570
26892
|
function writeTomlManagedSection(destination, managedContent, logger) {
|
|
26571
|
-
const current =
|
|
26893
|
+
const current = fs33.existsSync(destination) ? fs33.readFileSync(destination, "utf8") : "";
|
|
26572
26894
|
const isFirstWrite = current.length > 0 && !current.includes(managedMcpStart);
|
|
26573
26895
|
if (isFirstWrite) {
|
|
26574
26896
|
logger.warn(
|
|
@@ -26585,7 +26907,7 @@ ${managedMcpEnd}
|
|
|
26585
26907
|
return false;
|
|
26586
26908
|
}
|
|
26587
26909
|
ensureDir(path31.dirname(destination));
|
|
26588
|
-
|
|
26910
|
+
fs33.writeFileSync(destination, next.trimStart());
|
|
26589
26911
|
return true;
|
|
26590
26912
|
}
|
|
26591
26913
|
function removeManagedTomlSection(content) {
|
|
@@ -26612,16 +26934,16 @@ function handleConflict(destination, config, options, logger, manifest, scopePat
|
|
|
26612
26934
|
backupFile(destination, manifest);
|
|
26613
26935
|
writeManifest(scopePath, manifest);
|
|
26614
26936
|
} else {
|
|
26615
|
-
|
|
26937
|
+
fs33.renameSync(destination, `${destination}.bak.${Date.now()}`);
|
|
26616
26938
|
}
|
|
26617
26939
|
return true;
|
|
26618
26940
|
}
|
|
26619
|
-
|
|
26941
|
+
fs33.rmSync(destination, { recursive: true, force: true });
|
|
26620
26942
|
return true;
|
|
26621
26943
|
}
|
|
26622
26944
|
function isCompiledCurrent(destination, expectedContent, expectedHash) {
|
|
26623
26945
|
try {
|
|
26624
|
-
const current =
|
|
26946
|
+
const current = fs33.readFileSync(destination, "utf8");
|
|
26625
26947
|
const marker = extractProvenanceMarker(current);
|
|
26626
26948
|
return expectedHash ? marker?.hash === expectedHash && marker.target === "codex" && current === expectedContent : current === expectedContent;
|
|
26627
26949
|
} catch {
|
|
@@ -26640,16 +26962,16 @@ function shouldSkipComponent(options, component) {
|
|
|
26640
26962
|
}
|
|
26641
26963
|
function isSymlinkTo(linkPath, source) {
|
|
26642
26964
|
try {
|
|
26643
|
-
return
|
|
26965
|
+
return fs33.realpathSync(linkPath) === fs33.realpathSync(source);
|
|
26644
26966
|
} catch {
|
|
26645
26967
|
return false;
|
|
26646
26968
|
}
|
|
26647
26969
|
}
|
|
26648
26970
|
function readJsonObject3(filePath) {
|
|
26649
|
-
if (!
|
|
26971
|
+
if (!fs33.existsSync(filePath)) {
|
|
26650
26972
|
return {};
|
|
26651
26973
|
}
|
|
26652
|
-
const parsed = JSON.parse(
|
|
26974
|
+
const parsed = JSON.parse(fs33.readFileSync(filePath, "utf8"));
|
|
26653
26975
|
return isRecord4(parsed) ? parsed : {};
|
|
26654
26976
|
}
|
|
26655
26977
|
function stringifyJson2(value) {
|
|
@@ -26665,7 +26987,7 @@ function hashString(value) {
|
|
|
26665
26987
|
|
|
26666
26988
|
// src/adapters/copilot.ts
|
|
26667
26989
|
import crypto6 from "crypto";
|
|
26668
|
-
import
|
|
26990
|
+
import fs34 from "fs";
|
|
26669
26991
|
import path33 from "path";
|
|
26670
26992
|
|
|
26671
26993
|
// src/compiler/commands.ts
|
|
@@ -26850,7 +27172,7 @@ function syncCopilot(config, sourceRoot, mappings, extensions, options, lossRepo
|
|
|
26850
27172
|
function syncProviderLoss(config, sourceRoot, mappings, lossReport) {
|
|
26851
27173
|
if (!mappings.modelTiers.custom_provider) return;
|
|
26852
27174
|
const agentsDir = path33.join(sourceRoot, config.source.agents);
|
|
26853
|
-
if (!
|
|
27175
|
+
if (!fs34.existsSync(agentsDir)) return;
|
|
26854
27176
|
for (const agentFile of ts(path33.join(agentsDir, "*.md")).sort()) {
|
|
26855
27177
|
const { data } = parseMarkdownFrontmatter(agentFile);
|
|
26856
27178
|
const hasProvider = typeof data.provider === "string" || typeof data.provider === "object" && data.provider !== null && Object.keys(data.provider).length > 0;
|
|
@@ -26881,10 +27203,10 @@ function syncCopilotInstructions(config, sourceRoot, options, lossReport, logger
|
|
|
26881
27203
|
return [{ component: "instructions", status: "skipped", path: message }];
|
|
26882
27204
|
}
|
|
26883
27205
|
const source = path33.resolve(sourceRoot, config.source.instructions);
|
|
26884
|
-
if (!
|
|
27206
|
+
if (!fs34.existsSync(source)) {
|
|
26885
27207
|
return [skippedMissingSource2("instructions", source)];
|
|
26886
27208
|
}
|
|
26887
|
-
const rawContent =
|
|
27209
|
+
const rawContent = fs34.readFileSync(source, "utf8");
|
|
26888
27210
|
const contentHash = hashString2(rawContent);
|
|
26889
27211
|
const marker = formatProvenanceMarker(
|
|
26890
27212
|
"markdown",
|
|
@@ -26951,7 +27273,7 @@ function writeInstructionsFile(component, destination, content, contentHash, con
|
|
|
26951
27273
|
return { component, status: "conflict", path: destination };
|
|
26952
27274
|
}
|
|
26953
27275
|
ensureDir(path33.dirname(destination));
|
|
26954
|
-
|
|
27276
|
+
fs34.writeFileSync(destination, content);
|
|
26955
27277
|
return { component, status: "success", path: destination };
|
|
26956
27278
|
}
|
|
26957
27279
|
function hashString2(value) {
|
|
@@ -26960,7 +27282,7 @@ function hashString2(value) {
|
|
|
26960
27282
|
function syncSkills2(config, sourceRoot, paths, options, logger, manifest, scopePath) {
|
|
26961
27283
|
const source = path33.resolve(sourceRoot, config.source.skills);
|
|
26962
27284
|
const destination = resolveTargetPath(paths.skills);
|
|
26963
|
-
if (!
|
|
27285
|
+
if (!fs34.existsSync(source)) {
|
|
26964
27286
|
return skippedMissingSource2("skills", source);
|
|
26965
27287
|
}
|
|
26966
27288
|
if (options.dryRun) {
|
|
@@ -26976,11 +27298,11 @@ function syncSkills2(config, sourceRoot, paths, options, logger, manifest, scope
|
|
|
26976
27298
|
};
|
|
26977
27299
|
}
|
|
26978
27300
|
if (artifactClass === "managed-stale" && isOurStaleSymlink(destination, /* @__PURE__ */ new Set([destination]), config)) {
|
|
26979
|
-
|
|
27301
|
+
fs34.unlinkSync(destination);
|
|
26980
27302
|
} else if (artifactClass === "unmanaged-conflict" && !handleConflict2(destination, config, options, logger, manifest, scopePath)) {
|
|
26981
27303
|
return { component: "skills", status: "conflict", path: destination };
|
|
26982
27304
|
} else if (artifactClass === "managed-symlink") {
|
|
26983
|
-
|
|
27305
|
+
fs34.unlinkSync(destination);
|
|
26984
27306
|
}
|
|
26985
27307
|
ensureDir(path33.dirname(destination));
|
|
26986
27308
|
createRelativeSymlink(source, destination, "dir");
|
|
@@ -26988,7 +27310,7 @@ function syncSkills2(config, sourceRoot, paths, options, logger, manifest, scope
|
|
|
26988
27310
|
}
|
|
26989
27311
|
function syncAgents2(config, sourceRoot, paths, mappings, extensions, options, lossReport, logger, manifest, scopePath) {
|
|
26990
27312
|
const sourceAgentsRoot = path33.join(sourceRoot, config.source.agents);
|
|
26991
|
-
if (!
|
|
27313
|
+
if (!fs34.existsSync(sourceAgentsRoot)) {
|
|
26992
27314
|
return [skippedMissingSource2("agents", sourceAgentsRoot)];
|
|
26993
27315
|
}
|
|
26994
27316
|
const sourceAgents = ts(path33.join(sourceAgentsRoot, "*.md")).sort();
|
|
@@ -27025,7 +27347,7 @@ function syncAgents2(config, sourceRoot, paths, mappings, extensions, options, l
|
|
|
27025
27347
|
}
|
|
27026
27348
|
function syncCommands(config, sourceRoot, paths, options, logger, manifest, scopePath) {
|
|
27027
27349
|
const sourceCommandsRoot = path33.join(sourceRoot, config.source.commands);
|
|
27028
|
-
if (!
|
|
27350
|
+
if (!fs34.existsSync(sourceCommandsRoot)) {
|
|
27029
27351
|
return [skippedMissingSource2("commands", sourceCommandsRoot)];
|
|
27030
27352
|
}
|
|
27031
27353
|
const sourceCommands = ts(path33.join(sourceCommandsRoot, "*.md")).sort();
|
|
@@ -27057,18 +27379,20 @@ function syncCommands(config, sourceRoot, paths, options, logger, manifest, scop
|
|
|
27057
27379
|
});
|
|
27058
27380
|
}
|
|
27059
27381
|
function syncHooks2(config, sourceRoot, paths, mappings, options, lossReport, logger) {
|
|
27060
|
-
const source = resolveHooksSourceFile(sourceRoot, config);
|
|
27061
|
-
const sourceLabel = hooksSourceLabel(config);
|
|
27062
27382
|
const destination = path33.join(
|
|
27063
27383
|
resolveTargetPath(paths.hooks),
|
|
27064
27384
|
"tangyr-managed.json"
|
|
27065
27385
|
);
|
|
27066
|
-
|
|
27067
|
-
|
|
27386
|
+
const hooksSource = loadKitHooksSource(sourceRoot, config);
|
|
27387
|
+
if (!hooksSource) {
|
|
27388
|
+
return skippedMissingSource2(
|
|
27389
|
+
"hooks",
|
|
27390
|
+
resolveHooksSourceDir(sourceRoot, config)
|
|
27391
|
+
);
|
|
27068
27392
|
}
|
|
27069
|
-
const
|
|
27393
|
+
const { path: source, label: sourceLabel, hash: sourceHash } = hooksSource;
|
|
27070
27394
|
const compiled = compileHooks(
|
|
27071
|
-
|
|
27395
|
+
hooksSource.hooks,
|
|
27072
27396
|
"copilot",
|
|
27073
27397
|
mappings,
|
|
27074
27398
|
lossReport,
|
|
@@ -27077,7 +27401,7 @@ function syncHooks2(config, sourceRoot, paths, mappings, options, lossReport, lo
|
|
|
27077
27401
|
const payload = {
|
|
27078
27402
|
_tangyr: {
|
|
27079
27403
|
source: sourceLabel,
|
|
27080
|
-
hash:
|
|
27404
|
+
hash: sourceHash,
|
|
27081
27405
|
target: "copilot"
|
|
27082
27406
|
},
|
|
27083
27407
|
...compiled.payload
|
|
@@ -27092,7 +27416,7 @@ function syncHooks2(config, sourceRoot, paths, mappings, options, lossReport, lo
|
|
|
27092
27416
|
"hooks",
|
|
27093
27417
|
destination,
|
|
27094
27418
|
serialized,
|
|
27095
|
-
|
|
27419
|
+
sourceHash,
|
|
27096
27420
|
config,
|
|
27097
27421
|
sourceRoot,
|
|
27098
27422
|
options,
|
|
@@ -27103,10 +27427,10 @@ function syncMcp2(config, sourceRoot, paths, options, lossReport, logger) {
|
|
|
27103
27427
|
const source = resolveMcpSourceFile(sourceRoot, config);
|
|
27104
27428
|
const sourceLabel = mcpSourceLabel(config);
|
|
27105
27429
|
const destination = resolveTargetPath(paths.mcp_shared_file);
|
|
27106
|
-
if (!
|
|
27430
|
+
if (!fs34.existsSync(source)) {
|
|
27107
27431
|
return skippedMissingSource2("mcp", source);
|
|
27108
27432
|
}
|
|
27109
|
-
const sourceMcpJson = JSON.parse(
|
|
27433
|
+
const sourceMcpJson = JSON.parse(fs34.readFileSync(source, "utf8"));
|
|
27110
27434
|
const compiled = compileMcp(
|
|
27111
27435
|
sourceMcpJson,
|
|
27112
27436
|
"copilot",
|
|
@@ -27137,12 +27461,12 @@ function writeCompiledArtifact2(component, destination, content, expectedHash, c
|
|
|
27137
27461
|
return { component, status: "conflict", path: destination };
|
|
27138
27462
|
}
|
|
27139
27463
|
ensureDir(path33.dirname(destination));
|
|
27140
|
-
|
|
27464
|
+
fs34.writeFileSync(destination, content);
|
|
27141
27465
|
return { component, status: "success", path: destination };
|
|
27142
27466
|
}
|
|
27143
27467
|
function isCompiledCurrent2(destination, expectedContent, expectedHash) {
|
|
27144
27468
|
try {
|
|
27145
|
-
const current =
|
|
27469
|
+
const current = fs34.readFileSync(destination, "utf8");
|
|
27146
27470
|
const marker = extractProvenanceMarker(current);
|
|
27147
27471
|
return expectedHash ? marker?.hash === expectedHash && marker.target === "copilot" && current === expectedContent : current === expectedContent;
|
|
27148
27472
|
} catch {
|
|
@@ -27165,11 +27489,11 @@ function handleConflict2(destination, config, options, logger, manifest, scopePa
|
|
|
27165
27489
|
backupFile(destination, manifest);
|
|
27166
27490
|
writeManifest(scopePath, manifest);
|
|
27167
27491
|
} else {
|
|
27168
|
-
|
|
27492
|
+
fs34.renameSync(destination, `${destination}.bak.${Date.now()}`);
|
|
27169
27493
|
}
|
|
27170
27494
|
return true;
|
|
27171
27495
|
}
|
|
27172
|
-
|
|
27496
|
+
fs34.rmSync(destination, { recursive: true, force: true });
|
|
27173
27497
|
return true;
|
|
27174
27498
|
}
|
|
27175
27499
|
function shouldSkipComponent2(options, component) {
|
|
@@ -27193,14 +27517,14 @@ function cleanupLegacyAgents(paths, options, logger) {
|
|
|
27193
27517
|
outcomes.push({ component: "agents", status: "dry-run", path: filePath });
|
|
27194
27518
|
continue;
|
|
27195
27519
|
}
|
|
27196
|
-
|
|
27520
|
+
fs34.rmSync(filePath, { force: true });
|
|
27197
27521
|
outcomes.push({ component: "agents", status: "success", path: filePath });
|
|
27198
27522
|
}
|
|
27199
27523
|
return outcomes;
|
|
27200
27524
|
}
|
|
27201
27525
|
function isCopilotManagedCompiledFile(filePath) {
|
|
27202
27526
|
try {
|
|
27203
|
-
return extractProvenanceMarker(
|
|
27527
|
+
return extractProvenanceMarker(fs34.readFileSync(filePath, "utf8"))?.target === "copilot";
|
|
27204
27528
|
} catch {
|
|
27205
27529
|
return false;
|
|
27206
27530
|
}
|
|
@@ -27214,7 +27538,7 @@ function skippedMissingSource2(component, source) {
|
|
|
27214
27538
|
}
|
|
27215
27539
|
function isSymlinkTo2(linkPath, source) {
|
|
27216
27540
|
try {
|
|
27217
|
-
return
|
|
27541
|
+
return fs34.realpathSync(linkPath) === fs34.realpathSync(source);
|
|
27218
27542
|
} catch {
|
|
27219
27543
|
return false;
|
|
27220
27544
|
}
|
|
@@ -27222,7 +27546,7 @@ function isSymlinkTo2(linkPath, source) {
|
|
|
27222
27546
|
|
|
27223
27547
|
// src/adapters/cursor.ts
|
|
27224
27548
|
import crypto7 from "crypto";
|
|
27225
|
-
import
|
|
27549
|
+
import fs35 from "fs";
|
|
27226
27550
|
import path34 from "path";
|
|
27227
27551
|
function syncCursor(config, sourceRoot, mappings, extensions, options, lossReport, logger, manifest, scopePath) {
|
|
27228
27552
|
const scope = options.scope ?? "global";
|
|
@@ -27376,7 +27700,7 @@ function syncCursor(config, sourceRoot, mappings, extensions, options, lossRepor
|
|
|
27376
27700
|
function syncInstructions2(config, sourceRoot, paths, options, lossReport, logger, manifest, scopePath) {
|
|
27377
27701
|
const source = path34.join(sourceRoot, config.source.instructions);
|
|
27378
27702
|
const destination = resolveTargetPath(paths.instructions);
|
|
27379
|
-
const baseContent =
|
|
27703
|
+
const baseContent = fs35.existsSync(source) ? fs35.readFileSync(source, "utf8").trim() : "";
|
|
27380
27704
|
const personaContent = extractPrimaryPersonaContent(
|
|
27381
27705
|
config,
|
|
27382
27706
|
sourceRoot,
|
|
@@ -27415,7 +27739,7 @@ ${composed}
|
|
|
27415
27739
|
}
|
|
27416
27740
|
function extractPrimaryPersonaContent(config, sourceRoot, lossReport) {
|
|
27417
27741
|
const agentsDir = path34.join(sourceRoot, config.source.agents);
|
|
27418
|
-
if (!
|
|
27742
|
+
if (!fs35.existsSync(agentsDir)) {
|
|
27419
27743
|
return null;
|
|
27420
27744
|
}
|
|
27421
27745
|
const agentFiles = ts(path34.join(agentsDir, "*.md")).sort();
|
|
@@ -27459,7 +27783,7 @@ function extractPrimaryPersonaContent(config, sourceRoot, lossReport) {
|
|
|
27459
27783
|
}
|
|
27460
27784
|
function syncAgents3(config, sourceRoot, paths, mappings, extensions, options, lossReport, logger, manifest, scopePath) {
|
|
27461
27785
|
const sourceAgentsRoot = path34.join(sourceRoot, config.source.agents);
|
|
27462
|
-
if (!
|
|
27786
|
+
if (!fs35.existsSync(sourceAgentsRoot)) {
|
|
27463
27787
|
return [skippedMissingSource3("agents", sourceAgentsRoot)];
|
|
27464
27788
|
}
|
|
27465
27789
|
const sourceAgents = ts(path34.join(sourceAgentsRoot, "*.md")).sort();
|
|
@@ -27509,7 +27833,7 @@ function syncAgents3(config, sourceRoot, paths, mappings, extensions, options, l
|
|
|
27509
27833
|
}
|
|
27510
27834
|
function syncRules2(config, sourceRoot, paths, mappings, options, lossReport, logger, manifest, scopePath) {
|
|
27511
27835
|
const sourceRulesDir = path34.join(sourceRoot, config.source.rules);
|
|
27512
|
-
if (!
|
|
27836
|
+
if (!fs35.existsSync(sourceRulesDir)) {
|
|
27513
27837
|
return [skippedMissingSource3("rules", sourceRulesDir)];
|
|
27514
27838
|
}
|
|
27515
27839
|
const compiled = compileRules(sourceRulesDir, "cursor", mappings, lossReport);
|
|
@@ -27540,7 +27864,7 @@ function syncRules2(config, sourceRoot, paths, mappings, options, lossReport, lo
|
|
|
27540
27864
|
}
|
|
27541
27865
|
function syncCommands2(config, sourceRoot, paths, options, logger, manifest, scopePath) {
|
|
27542
27866
|
const sourceCommandsRoot = path34.join(sourceRoot, config.source.commands);
|
|
27543
|
-
if (!
|
|
27867
|
+
if (!fs35.existsSync(sourceCommandsRoot)) {
|
|
27544
27868
|
return [skippedMissingSource3("commands", sourceCommandsRoot)];
|
|
27545
27869
|
}
|
|
27546
27870
|
const sourceCommands = ts(path34.join(sourceCommandsRoot, "*.md")).sort();
|
|
@@ -27548,7 +27872,7 @@ function syncCommands2(config, sourceRoot, paths, options, logger, manifest, sco
|
|
|
27548
27872
|
return sourceCommands.map((commandFile) => {
|
|
27549
27873
|
const baseName = path34.basename(commandFile, ".md");
|
|
27550
27874
|
const destination = path34.join(skillsRoot, baseName, "SKILL.md");
|
|
27551
|
-
const commandContent =
|
|
27875
|
+
const commandContent = fs35.readFileSync(commandFile, "utf8");
|
|
27552
27876
|
const relativeSource = `commands/${path34.basename(commandFile)}`;
|
|
27553
27877
|
const marker = formatProvenanceMarker(
|
|
27554
27878
|
"markdown",
|
|
@@ -27589,7 +27913,7 @@ ${commandContent.trim()}
|
|
|
27589
27913
|
}
|
|
27590
27914
|
function syncSkills3(config, sourceRoot, options, logger, manifest, scopePath, component = "skills", destination = "", asSymlink = false) {
|
|
27591
27915
|
const source = path34.resolve(sourceRoot, config.source.skills);
|
|
27592
|
-
if (!
|
|
27916
|
+
if (!fs35.existsSync(source)) {
|
|
27593
27917
|
return skippedMissingSource3(component, source);
|
|
27594
27918
|
}
|
|
27595
27919
|
if (options.dryRun) {
|
|
@@ -27603,18 +27927,18 @@ function syncSkills3(config, sourceRoot, options, logger, manifest, scopePath, c
|
|
|
27603
27927
|
return { component, status: "skipped-current", path: destination };
|
|
27604
27928
|
}
|
|
27605
27929
|
if (artifactClass === "managed-stale" && isOurStaleSymlink(destination, /* @__PURE__ */ new Set([destination]), config)) {
|
|
27606
|
-
|
|
27930
|
+
fs35.unlinkSync(destination);
|
|
27607
27931
|
} else if (artifactClass === "unmanaged-conflict" && !handleConflict3(destination, config, options, logger, manifest, scopePath)) {
|
|
27608
27932
|
return { component, status: "conflict", path: destination };
|
|
27609
27933
|
} else if (artifactClass === "managed-symlink") {
|
|
27610
|
-
|
|
27934
|
+
fs35.unlinkSync(destination);
|
|
27611
27935
|
}
|
|
27612
27936
|
ensureDir(path34.dirname(destination));
|
|
27613
27937
|
createRelativeSymlink(source, destination, "dir");
|
|
27614
27938
|
logger.verbose(`Skills: created shared skills symlink ${destination}`);
|
|
27615
27939
|
return { component, status: "success", path: destination };
|
|
27616
27940
|
}
|
|
27617
|
-
if (
|
|
27941
|
+
if (fs35.existsSync(destination)) {
|
|
27618
27942
|
return { component, status: "skipped-current", path: destination };
|
|
27619
27943
|
}
|
|
27620
27944
|
ensureDir(destination);
|
|
@@ -27622,10 +27946,11 @@ function syncSkills3(config, sourceRoot, options, logger, manifest, scopePath, c
|
|
|
27622
27946
|
return { component, status: "success", path: destination };
|
|
27623
27947
|
}
|
|
27624
27948
|
function syncHooks3(config, sourceRoot, paths, mappings, options, lossReport, logger) {
|
|
27625
|
-
const
|
|
27626
|
-
const
|
|
27949
|
+
const hooksSource = loadKitHooksSource(sourceRoot, config);
|
|
27950
|
+
const source = hooksSource?.path ?? resolveHooksSourceDir(sourceRoot, config);
|
|
27951
|
+
const sourceLabel = hooksSource?.label ?? hooksSourceLabel(sourceRoot, config);
|
|
27627
27952
|
const destination = resolveTargetPath(paths.hooks_file);
|
|
27628
|
-
const sourceHooksJson =
|
|
27953
|
+
const sourceHooksJson = hooksSource?.hooks ?? { hooks: {} };
|
|
27629
27954
|
const compiled = compileHooks(
|
|
27630
27955
|
sourceHooksJson,
|
|
27631
27956
|
"cursor",
|
|
@@ -27640,17 +27965,17 @@ function syncHooks3(config, sourceRoot, paths, mappings, options, lossReport, lo
|
|
|
27640
27965
|
const hooksFileContent = `${JSON.stringify({ version: 1, hooks: compiled.payload }, null, 2)}
|
|
27641
27966
|
`;
|
|
27642
27967
|
ensureDir(path34.dirname(destination));
|
|
27643
|
-
|
|
27968
|
+
fs35.writeFileSync(destination, hooksFileContent);
|
|
27644
27969
|
return { component: "hooks", status: "success", path: destination };
|
|
27645
27970
|
}
|
|
27646
27971
|
function syncMcp3(config, sourceRoot, paths, options, lossReport, logger, manifest, scopePath) {
|
|
27647
27972
|
const source = resolveMcpSourceFile(sourceRoot, config);
|
|
27648
27973
|
const sourceLabel = mcpSourceLabel(config);
|
|
27649
27974
|
const destination = resolveTargetPath(paths.mcp_file);
|
|
27650
|
-
if (!
|
|
27975
|
+
if (!fs35.existsSync(source)) {
|
|
27651
27976
|
return skippedMissingSource3("mcp", source);
|
|
27652
27977
|
}
|
|
27653
|
-
const sourceMcpJson = JSON.parse(
|
|
27978
|
+
const sourceMcpJson = JSON.parse(fs35.readFileSync(source, "utf8"));
|
|
27654
27979
|
const mcpJson = compileMcp(sourceMcpJson, "cursor", lossReport, sourceLabel);
|
|
27655
27980
|
if (options.dryRun) {
|
|
27656
27981
|
logger.info(`[dry-run] mcp: compile ${source} -> ${destination}`);
|
|
@@ -27702,7 +28027,7 @@ function syncPermissions(config, paths, options, _lossReport, logger, manifest,
|
|
|
27702
28027
|
}
|
|
27703
28028
|
function syncProviderPin(config, sourceRoot, lossReport) {
|
|
27704
28029
|
const agentsDir = path34.join(sourceRoot, config.source.agents);
|
|
27705
|
-
if (!
|
|
28030
|
+
if (!fs35.existsSync(agentsDir)) return;
|
|
27706
28031
|
for (const agentFile of ts(path34.join(agentsDir, "*.md")).sort()) {
|
|
27707
28032
|
const { data } = parseMarkdownFrontmatter(agentFile);
|
|
27708
28033
|
const hasCustomProvider = typeof data.provider === "string" || typeof data.provider === "object" && data.provider !== null && Object.keys(data.provider).length > 0;
|
|
@@ -27735,7 +28060,7 @@ function writeCompiledArtifact3(component, destination, content, expectedHash, c
|
|
|
27735
28060
|
return { component, status: "conflict", path: destination };
|
|
27736
28061
|
}
|
|
27737
28062
|
ensureDir(path34.dirname(destination));
|
|
27738
|
-
|
|
28063
|
+
fs35.writeFileSync(destination, content);
|
|
27739
28064
|
return { component, status: "success", path: destination };
|
|
27740
28065
|
}
|
|
27741
28066
|
function handleConflict3(destination, config, options, logger, manifest, scopePath) {
|
|
@@ -27754,16 +28079,16 @@ function handleConflict3(destination, config, options, logger, manifest, scopePa
|
|
|
27754
28079
|
backupFile(destination, manifest);
|
|
27755
28080
|
writeManifest(scopePath, manifest);
|
|
27756
28081
|
} else {
|
|
27757
|
-
|
|
28082
|
+
fs35.renameSync(destination, `${destination}.bak.${Date.now()}`);
|
|
27758
28083
|
}
|
|
27759
28084
|
return true;
|
|
27760
28085
|
}
|
|
27761
|
-
|
|
28086
|
+
fs35.rmSync(destination, { recursive: true, force: true });
|
|
27762
28087
|
return true;
|
|
27763
28088
|
}
|
|
27764
28089
|
function isCompiledCurrent3(destination, expectedContent, expectedHash) {
|
|
27765
28090
|
try {
|
|
27766
|
-
const current =
|
|
28091
|
+
const current = fs35.readFileSync(destination, "utf8");
|
|
27767
28092
|
const marker = extractProvenanceMarker(current);
|
|
27768
28093
|
return expectedHash ? marker?.hash === expectedHash && marker.target === "cursor" && current === expectedContent : current === expectedContent;
|
|
27769
28094
|
} catch {
|
|
@@ -27782,7 +28107,7 @@ function shouldSkipComponent3(options, component) {
|
|
|
27782
28107
|
}
|
|
27783
28108
|
function isSymlinkTo3(linkPath, source) {
|
|
27784
28109
|
try {
|
|
27785
|
-
return
|
|
28110
|
+
return fs35.realpathSync(linkPath) === fs35.realpathSync(source);
|
|
27786
28111
|
} catch {
|
|
27787
28112
|
return false;
|
|
27788
28113
|
}
|
|
@@ -27793,7 +28118,7 @@ function hashString3(value) {
|
|
|
27793
28118
|
|
|
27794
28119
|
// src/adapters/cline.ts
|
|
27795
28120
|
import crypto8 from "crypto";
|
|
27796
|
-
import
|
|
28121
|
+
import fs36 from "fs";
|
|
27797
28122
|
import path35 from "path";
|
|
27798
28123
|
function syncCline(config, sourceRoot, mappings, extensions, options, lossReport, logger, manifest, scopePath) {
|
|
27799
28124
|
const scope = options.scope ?? "global";
|
|
@@ -27929,7 +28254,7 @@ function syncInstructions3(config, sourceRoot, paths, scope, options, lossReport
|
|
|
27929
28254
|
}
|
|
27930
28255
|
const source = path35.join(sourceRoot, config.source.instructions);
|
|
27931
28256
|
const destination = resolveTargetPath(paths.instructions);
|
|
27932
|
-
const baseContent =
|
|
28257
|
+
const baseContent = fs36.existsSync(source) ? fs36.readFileSync(source, "utf8").trim() : "";
|
|
27933
28258
|
const marker = formatProvenanceMarker(
|
|
27934
28259
|
"markdown",
|
|
27935
28260
|
path35.basename(config.source.instructions),
|
|
@@ -27958,7 +28283,7 @@ ${baseContent}
|
|
|
27958
28283
|
}
|
|
27959
28284
|
function syncAgents4(config, sourceRoot, mappings, extensions, options, lossReport, logger) {
|
|
27960
28285
|
const sourceAgentsRoot = path35.join(sourceRoot, config.source.agents);
|
|
27961
|
-
if (!
|
|
28286
|
+
if (!fs36.existsSync(sourceAgentsRoot)) {
|
|
27962
28287
|
return [skippedMissingSource4("agents", sourceAgentsRoot)];
|
|
27963
28288
|
}
|
|
27964
28289
|
const sourceAgents = ts(path35.join(sourceAgentsRoot, "*.md")).sort();
|
|
@@ -27978,7 +28303,7 @@ function syncAgents4(config, sourceRoot, mappings, extensions, options, lossRepo
|
|
|
27978
28303
|
}
|
|
27979
28304
|
function syncRules3(config, sourceRoot, paths, mappings, options, lossReport, logger, manifest, scopePath) {
|
|
27980
28305
|
const sourceRulesDir = path35.join(sourceRoot, config.source.rules);
|
|
27981
|
-
if (!
|
|
28306
|
+
if (!fs36.existsSync(sourceRulesDir)) {
|
|
27982
28307
|
return [skippedMissingSource4("rules", sourceRulesDir)];
|
|
27983
28308
|
}
|
|
27984
28309
|
const compiled = compileRules(sourceRulesDir, "cline", mappings, lossReport);
|
|
@@ -28009,7 +28334,7 @@ function syncRules3(config, sourceRoot, paths, mappings, options, lossReport, lo
|
|
|
28009
28334
|
}
|
|
28010
28335
|
function syncCommands3(config, sourceRoot, paths, options, logger, manifest, scopePath) {
|
|
28011
28336
|
const sourceCommandsRoot = path35.join(sourceRoot, config.source.commands);
|
|
28012
|
-
if (!
|
|
28337
|
+
if (!fs36.existsSync(sourceCommandsRoot)) {
|
|
28013
28338
|
return [skippedMissingSource4("commands", sourceCommandsRoot)];
|
|
28014
28339
|
}
|
|
28015
28340
|
const sourceCommands = ts(path35.join(sourceCommandsRoot, "*.md")).sort();
|
|
@@ -28017,7 +28342,7 @@ function syncCommands3(config, sourceRoot, paths, options, logger, manifest, sco
|
|
|
28017
28342
|
return sourceCommands.map((commandFile) => {
|
|
28018
28343
|
const baseName = path35.basename(commandFile);
|
|
28019
28344
|
const destination = path35.join(workflowsRoot, baseName);
|
|
28020
|
-
const commandContent =
|
|
28345
|
+
const commandContent = fs36.readFileSync(commandFile, "utf8");
|
|
28021
28346
|
const relativeSource = `commands/${path35.basename(commandFile)}`;
|
|
28022
28347
|
const marker = formatProvenanceMarker(
|
|
28023
28348
|
"markdown",
|
|
@@ -28055,7 +28380,7 @@ ${commandContent.trim()}
|
|
|
28055
28380
|
function syncSkills4(config, sourceRoot, paths, options, logger, manifest, scopePath) {
|
|
28056
28381
|
const source = path35.resolve(sourceRoot, config.source.skills);
|
|
28057
28382
|
const destination = resolveTargetPath(paths.skills);
|
|
28058
|
-
if (!
|
|
28383
|
+
if (!fs36.existsSync(source)) {
|
|
28059
28384
|
return skippedMissingSource4("skills", source);
|
|
28060
28385
|
}
|
|
28061
28386
|
if (options.dryRun) {
|
|
@@ -28075,7 +28400,7 @@ function syncSkills4(config, sourceRoot, paths, options, logger, manifest, scope
|
|
|
28075
28400
|
return { component: "skills", status: "conflict", path: destination };
|
|
28076
28401
|
}
|
|
28077
28402
|
} else if (artifactClass === "managed-symlink") {
|
|
28078
|
-
|
|
28403
|
+
fs36.unlinkSync(destination);
|
|
28079
28404
|
}
|
|
28080
28405
|
ensureDir(path35.dirname(destination));
|
|
28081
28406
|
createRelativeSymlink(source, destination, "dir");
|
|
@@ -28083,9 +28408,10 @@ function syncSkills4(config, sourceRoot, paths, options, logger, manifest, scope
|
|
|
28083
28408
|
return { component: "skills", status: "success", path: destination };
|
|
28084
28409
|
}
|
|
28085
28410
|
function syncHooks4(config, sourceRoot, mappings, options, lossReport, logger) {
|
|
28086
|
-
const
|
|
28087
|
-
const
|
|
28088
|
-
const
|
|
28411
|
+
const hooksSource = loadKitHooksSource(sourceRoot, config);
|
|
28412
|
+
const source = hooksSource?.path ?? resolveHooksSourceDir(sourceRoot, config);
|
|
28413
|
+
const sourceLabel = hooksSource?.label ?? hooksSourceLabel(sourceRoot, config);
|
|
28414
|
+
const sourceHooksJson = hooksSource?.hooks ?? { hooks: {} };
|
|
28089
28415
|
compileHooks(sourceHooksJson, "cline", mappings, lossReport, sourceLabel);
|
|
28090
28416
|
if (options.dryRun) {
|
|
28091
28417
|
logger.info(`[dry-run] hooks: record hook-as-plugin loss for ${source}`);
|
|
@@ -28100,10 +28426,10 @@ function syncMcp4(config, sourceRoot, paths, options, lossReport, logger, manife
|
|
|
28100
28426
|
const source = resolveMcpSourceFile(sourceRoot, config);
|
|
28101
28427
|
const sourceLabel = mcpSourceLabel(config);
|
|
28102
28428
|
const destination = resolveTargetPath(paths.mcp_file);
|
|
28103
|
-
if (!
|
|
28429
|
+
if (!fs36.existsSync(source)) {
|
|
28104
28430
|
return skippedMissingSource4("mcp", source);
|
|
28105
28431
|
}
|
|
28106
|
-
const sourceMcpJson = JSON.parse(
|
|
28432
|
+
const sourceMcpJson = JSON.parse(fs36.readFileSync(source, "utf8"));
|
|
28107
28433
|
const mcpJson = compileMcp(sourceMcpJson, "cline", lossReport, sourceLabel);
|
|
28108
28434
|
if (options.dryRun) {
|
|
28109
28435
|
logger.info(`[dry-run] mcp: compile ${source} -> ${destination}`);
|
|
@@ -28147,7 +28473,7 @@ function syncSettings(lossReport) {
|
|
|
28147
28473
|
}
|
|
28148
28474
|
function syncProviderAndModel(config, sourceRoot, lossReport) {
|
|
28149
28475
|
const agentsDir = path35.join(sourceRoot, config.source.agents);
|
|
28150
|
-
if (!
|
|
28476
|
+
if (!fs36.existsSync(agentsDir)) return;
|
|
28151
28477
|
for (const agentFile of ts(path35.join(agentsDir, "*.md")).sort()) {
|
|
28152
28478
|
const { data } = parseMarkdownFrontmatter(agentFile);
|
|
28153
28479
|
const relativeSource = `agents/${path35.basename(agentFile)}`;
|
|
@@ -28198,7 +28524,7 @@ function writeCompiledArtifact4(component, destination, content, expectedHash, c
|
|
|
28198
28524
|
return { component, status: "conflict", path: destination };
|
|
28199
28525
|
}
|
|
28200
28526
|
ensureDir(path35.dirname(destination));
|
|
28201
|
-
|
|
28527
|
+
fs36.writeFileSync(destination, content);
|
|
28202
28528
|
return { component, status: "success", path: destination };
|
|
28203
28529
|
}
|
|
28204
28530
|
function handleConflict4(destination, config, options, logger, manifest, scopePath) {
|
|
@@ -28217,16 +28543,16 @@ function handleConflict4(destination, config, options, logger, manifest, scopePa
|
|
|
28217
28543
|
backupFile(destination, manifest);
|
|
28218
28544
|
writeManifest(scopePath, manifest);
|
|
28219
28545
|
} else {
|
|
28220
|
-
|
|
28546
|
+
fs36.renameSync(destination, `${destination}.bak.${Date.now()}`);
|
|
28221
28547
|
}
|
|
28222
28548
|
return true;
|
|
28223
28549
|
}
|
|
28224
|
-
|
|
28550
|
+
fs36.rmSync(destination, { recursive: true, force: true });
|
|
28225
28551
|
return true;
|
|
28226
28552
|
}
|
|
28227
28553
|
function isCompiledCurrent4(destination, expectedContent, expectedHash) {
|
|
28228
28554
|
try {
|
|
28229
|
-
const current =
|
|
28555
|
+
const current = fs36.readFileSync(destination, "utf8");
|
|
28230
28556
|
const marker = extractProvenanceMarker(current);
|
|
28231
28557
|
return expectedHash ? marker?.hash === expectedHash && marker.target === "cline" && current === expectedContent : current === expectedContent;
|
|
28232
28558
|
} catch {
|
|
@@ -28245,7 +28571,7 @@ function shouldSkipComponent4(options, component) {
|
|
|
28245
28571
|
}
|
|
28246
28572
|
function isSymlinkTo4(linkPath, source) {
|
|
28247
28573
|
try {
|
|
28248
|
-
return
|
|
28574
|
+
return fs36.realpathSync(linkPath) === fs36.realpathSync(source);
|
|
28249
28575
|
} catch {
|
|
28250
28576
|
return false;
|
|
28251
28577
|
}
|
|
@@ -28256,7 +28582,7 @@ function hashString4(value) {
|
|
|
28256
28582
|
|
|
28257
28583
|
// src/adapters/opencode.ts
|
|
28258
28584
|
import crypto9 from "crypto";
|
|
28259
|
-
import
|
|
28585
|
+
import fs37 from "fs";
|
|
28260
28586
|
import os6 from "os";
|
|
28261
28587
|
import path36 from "path";
|
|
28262
28588
|
var import_yaml11 = __toESM(require_dist(), 1);
|
|
@@ -28380,7 +28706,7 @@ function syncOpenCode(config, sourceRoot, mappings, extensions, options, lossRep
|
|
|
28380
28706
|
function syncInstructions4(config, sourceRoot, paths, agentsAppendix, options, logger, manifest, scopePath) {
|
|
28381
28707
|
const source = path36.join(sourceRoot, config.source.instructions);
|
|
28382
28708
|
const destination = resolveTargetPath(paths.instructions);
|
|
28383
|
-
const baseContent =
|
|
28709
|
+
const baseContent = fs37.existsSync(source) ? fs37.readFileSync(source, "utf8").trim() : "";
|
|
28384
28710
|
const composed = agentsAppendix ? `${baseContent}
|
|
28385
28711
|
|
|
28386
28712
|
--- tangyr managed rules ---
|
|
@@ -28415,7 +28741,7 @@ ${composed}
|
|
|
28415
28741
|
}
|
|
28416
28742
|
function syncAgents5(config, sourceRoot, paths, mappings, extensions, options, lossReport, logger, manifest, scopePath) {
|
|
28417
28743
|
const sourceAgentsRoot = path36.join(sourceRoot, config.source.agents);
|
|
28418
|
-
if (!
|
|
28744
|
+
if (!fs37.existsSync(sourceAgentsRoot)) {
|
|
28419
28745
|
return [skippedMissingSource5("agents", sourceAgentsRoot)];
|
|
28420
28746
|
}
|
|
28421
28747
|
const sourceAgents = ts(path36.join(sourceAgentsRoot, "*.md")).sort();
|
|
@@ -28488,7 +28814,7 @@ function applyProviderModelBinding(compiled, mappings) {
|
|
|
28488
28814
|
}
|
|
28489
28815
|
function syncCommands4(config, sourceRoot, paths, options, logger, manifest, scopePath) {
|
|
28490
28816
|
const sourceCommandsRoot = path36.join(sourceRoot, config.source.commands);
|
|
28491
|
-
if (!
|
|
28817
|
+
if (!fs37.existsSync(sourceCommandsRoot)) {
|
|
28492
28818
|
return [skippedMissingSource5("commands", sourceCommandsRoot)];
|
|
28493
28819
|
}
|
|
28494
28820
|
const sourceCommands = ts(path36.join(sourceCommandsRoot, "*.md")).sort();
|
|
@@ -28551,7 +28877,7 @@ ${body.trimEnd()}
|
|
|
28551
28877
|
}
|
|
28552
28878
|
function syncSkills5(config, sourceRoot, _paths, options, logger, manifest, scopePath, component = "skills", destination = "") {
|
|
28553
28879
|
const source = path36.resolve(sourceRoot, config.source.skills);
|
|
28554
|
-
if (!
|
|
28880
|
+
if (!fs37.existsSync(source)) {
|
|
28555
28881
|
return skippedMissingSource5(component, source);
|
|
28556
28882
|
}
|
|
28557
28883
|
if (options.dryRun) {
|
|
@@ -28563,11 +28889,11 @@ function syncSkills5(config, sourceRoot, _paths, options, logger, manifest, scop
|
|
|
28563
28889
|
return { component, status: "skipped-current", path: destination };
|
|
28564
28890
|
}
|
|
28565
28891
|
if (artifactClass === "managed-stale" && isOurStaleSymlink(destination, /* @__PURE__ */ new Set([destination]), config)) {
|
|
28566
|
-
|
|
28892
|
+
fs37.unlinkSync(destination);
|
|
28567
28893
|
} else if (artifactClass === "unmanaged-conflict" && !handleConflict5(destination, config, options, logger, manifest, scopePath)) {
|
|
28568
28894
|
return { component, status: "conflict", path: destination };
|
|
28569
28895
|
} else if (artifactClass === "managed-symlink") {
|
|
28570
|
-
|
|
28896
|
+
fs37.unlinkSync(destination);
|
|
28571
28897
|
}
|
|
28572
28898
|
ensureDir(path36.dirname(destination));
|
|
28573
28899
|
createRelativeSymlink(source, destination, "dir");
|
|
@@ -28578,13 +28904,13 @@ function syncMcp5(config, sourceRoot, paths, mappings, options, lossReport, logg
|
|
|
28578
28904
|
const source = resolveMcpSourceFile(sourceRoot, config);
|
|
28579
28905
|
const sourceLabel = mcpSourceLabel(config);
|
|
28580
28906
|
const destination = resolveTargetPath(paths.config_doc);
|
|
28581
|
-
if (!
|
|
28907
|
+
if (!fs37.existsSync(source)) {
|
|
28582
28908
|
if (!options.dryRun) {
|
|
28583
28909
|
syncConfigDoc(paths, {}, lossReport, {}, mappings);
|
|
28584
28910
|
}
|
|
28585
28911
|
return skippedMissingSource5("mcp", source);
|
|
28586
28912
|
}
|
|
28587
|
-
const sourceMcpJson = JSON.parse(
|
|
28913
|
+
const sourceMcpJson = JSON.parse(fs37.readFileSync(source, "utf8"));
|
|
28588
28914
|
const mcpJson = compileMcp(
|
|
28589
28915
|
sourceMcpJson,
|
|
28590
28916
|
"opencode",
|
|
@@ -28613,9 +28939,10 @@ function syncPermissions2(config, _sourceRoot, paths, options, lossReport, logge
|
|
|
28613
28939
|
return { component: "settings", status: "success", path: destination };
|
|
28614
28940
|
}
|
|
28615
28941
|
function syncHooks5(config, sourceRoot, mappings, options, lossReport) {
|
|
28616
|
-
const
|
|
28617
|
-
const
|
|
28618
|
-
const
|
|
28942
|
+
const hooksSource = loadKitHooksSource(sourceRoot, config);
|
|
28943
|
+
const source = hooksSource?.path ?? resolveHooksSourceDir(sourceRoot, config);
|
|
28944
|
+
const sourceLabel = hooksSource?.label ?? hooksSourceLabel(sourceRoot, config);
|
|
28945
|
+
const sourceHooksJson = hooksSource?.hooks ?? { hooks: {} };
|
|
28619
28946
|
compileHooks(sourceHooksJson, "opencode", mappings, lossReport, sourceLabel);
|
|
28620
28947
|
const message = "hooks: Skip \u2014 OpenCode exposes hooks only as plugin code (hook-as-plugin loss recorded)";
|
|
28621
28948
|
return { component: "hooks", status: "skipped", path: message };
|
|
@@ -28649,9 +28976,9 @@ function syncProviderBlock(mappings) {
|
|
|
28649
28976
|
function syncConfigDoc(paths, mcpEntries, _lossReport, permissionMap, mappings) {
|
|
28650
28977
|
const destination = resolveTargetPath(paths.config_doc);
|
|
28651
28978
|
let existing = {};
|
|
28652
|
-
if (
|
|
28979
|
+
if (fs37.existsSync(destination)) {
|
|
28653
28980
|
try {
|
|
28654
|
-
existing = JSON.parse(
|
|
28981
|
+
existing = JSON.parse(fs37.readFileSync(destination, "utf8"));
|
|
28655
28982
|
} catch {
|
|
28656
28983
|
existing = {};
|
|
28657
28984
|
}
|
|
@@ -28677,7 +29004,7 @@ function syncConfigDoc(paths, mcpEntries, _lossReport, permissionMap, mappings)
|
|
|
28677
29004
|
}
|
|
28678
29005
|
}
|
|
28679
29006
|
ensureDir(path36.dirname(destination));
|
|
28680
|
-
|
|
29007
|
+
fs37.writeFileSync(destination, `${JSON.stringify(config, null, 2)}
|
|
28681
29008
|
`);
|
|
28682
29009
|
}
|
|
28683
29010
|
function writeCompiledArtifact5(component, destination, content, expectedHash, config, sourceRoot, options, logger, manifest, scopePath) {
|
|
@@ -28689,7 +29016,7 @@ function writeCompiledArtifact5(component, destination, content, expectedHash, c
|
|
|
28689
29016
|
return { component, status: "conflict", path: destination };
|
|
28690
29017
|
}
|
|
28691
29018
|
ensureDir(path36.dirname(destination));
|
|
28692
|
-
|
|
29019
|
+
fs37.writeFileSync(destination, content);
|
|
28693
29020
|
return { component, status: "success", path: destination };
|
|
28694
29021
|
}
|
|
28695
29022
|
function handleConflict5(destination, config, options, logger, manifest, scopePath) {
|
|
@@ -28708,16 +29035,16 @@ function handleConflict5(destination, config, options, logger, manifest, scopePa
|
|
|
28708
29035
|
backupFile(destination, manifest);
|
|
28709
29036
|
writeManifest(scopePath, manifest);
|
|
28710
29037
|
} else {
|
|
28711
|
-
|
|
29038
|
+
fs37.renameSync(destination, `${destination}.bak.${Date.now()}`);
|
|
28712
29039
|
}
|
|
28713
29040
|
return true;
|
|
28714
29041
|
}
|
|
28715
|
-
|
|
29042
|
+
fs37.rmSync(destination, { recursive: true, force: true });
|
|
28716
29043
|
return true;
|
|
28717
29044
|
}
|
|
28718
29045
|
function isCompiledCurrent5(destination, expectedContent, expectedHash) {
|
|
28719
29046
|
try {
|
|
28720
|
-
const current =
|
|
29047
|
+
const current = fs37.readFileSync(destination, "utf8");
|
|
28721
29048
|
const marker = extractProvenanceMarker(current);
|
|
28722
29049
|
return expectedHash ? marker?.hash === expectedHash && marker.target === "opencode" && current === expectedContent : current === expectedContent;
|
|
28723
29050
|
} catch {
|
|
@@ -28736,7 +29063,7 @@ function shouldSkipComponent5(options, component) {
|
|
|
28736
29063
|
}
|
|
28737
29064
|
function isSymlinkTo5(linkPath, source) {
|
|
28738
29065
|
try {
|
|
28739
|
-
return
|
|
29066
|
+
return fs37.realpathSync(linkPath) === fs37.realpathSync(source);
|
|
28740
29067
|
} catch {
|
|
28741
29068
|
return false;
|
|
28742
29069
|
}
|
|
@@ -28745,7 +29072,7 @@ function hashString5(value) {
|
|
|
28745
29072
|
return `sha256:${crypto9.createHash("sha256").update(value).digest("hex")}`;
|
|
28746
29073
|
}
|
|
28747
29074
|
function parseCommandFile(commandFile) {
|
|
28748
|
-
const content =
|
|
29075
|
+
const content = fs37.readFileSync(commandFile, "utf8");
|
|
28749
29076
|
if (!content.startsWith("---\n")) {
|
|
28750
29077
|
return { data: {}, body: content };
|
|
28751
29078
|
}
|
|
@@ -28765,10 +29092,10 @@ function parseCommandFile(commandFile) {
|
|
|
28765
29092
|
// src/commands/install.ts
|
|
28766
29093
|
function isBackupableFile(targetPath) {
|
|
28767
29094
|
try {
|
|
28768
|
-
const lstat =
|
|
29095
|
+
const lstat = fs38.lstatSync(targetPath);
|
|
28769
29096
|
if (lstat.isDirectory()) return false;
|
|
28770
29097
|
if (lstat.isSymbolicLink()) {
|
|
28771
|
-
const stat =
|
|
29098
|
+
const stat = fs38.statSync(targetPath);
|
|
28772
29099
|
return stat.isFile();
|
|
28773
29100
|
}
|
|
28774
29101
|
return lstat.isFile();
|
|
@@ -28780,8 +29107,8 @@ function backupConflictingTarget(targetPath, scopePath) {
|
|
|
28780
29107
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/gu, "").replace("T", "T").replace(/\.\d+Z$/u, "Z");
|
|
28781
29108
|
const relativeToRoot = path37.relative("/", targetPath).replace(/^\.\.\/+/gu, "");
|
|
28782
29109
|
const backupPath = path37.join(scopePath, "backups", ts2, relativeToRoot);
|
|
28783
|
-
|
|
28784
|
-
|
|
29110
|
+
fs38.mkdirSync(path37.dirname(backupPath), { recursive: true });
|
|
29111
|
+
fs38.copyFileSync(targetPath, backupPath);
|
|
28785
29112
|
return backupPath;
|
|
28786
29113
|
}
|
|
28787
29114
|
async function runInstallCommand(options, logger) {
|
|
@@ -28837,6 +29164,10 @@ async function runInstallCommand(options, logger) {
|
|
|
28837
29164
|
logger.info(
|
|
28838
29165
|
`Installing kit '${kitInfo.name}' (${kitInfo.archetype}) into ${scope} scope for: ${tools.join(", ")}`
|
|
28839
29166
|
);
|
|
29167
|
+
const resolvedEntrypoint = resolveEntrypoint(kitInfo);
|
|
29168
|
+
if (resolvedEntrypoint?.legacy) {
|
|
29169
|
+
logger.warn(LEGACY_ENTRYPOINT_NOTICE);
|
|
29170
|
+
}
|
|
28840
29171
|
const scopePath = resolveScopePath(
|
|
28841
29172
|
scope,
|
|
28842
29173
|
scope === "project" ? process.cwd() : void 0
|
|
@@ -28951,7 +29282,7 @@ ACCEPTED CONFLICTS (backup-and-replace applied to ${conflictingFindings.length}
|
|
|
28951
29282
|
logger.info(` source: ${finding.tool}`);
|
|
28952
29283
|
logger.info(` action: backup-and-replace`);
|
|
28953
29284
|
logger.info(` backup: ${backupLocation}`);
|
|
28954
|
-
} else if (
|
|
29285
|
+
} else if (fs38.existsSync(finding.path)) {
|
|
28955
29286
|
logger.info(` - ${finding.path}`);
|
|
28956
29287
|
logger.info(` source: ${finding.tool}`);
|
|
28957
29288
|
logger.info(
|
|
@@ -29066,6 +29397,30 @@ Installation complete: ${installedCount} files installed.`);
|
|
|
29066
29397
|
logger.info(`Kit: ${kitInfo.name} (archetype: ${kitInfo.archetype})`);
|
|
29067
29398
|
logger.info(`Scope: ${scope}`);
|
|
29068
29399
|
logger.info(`Tools: ${tools.join(", ")}`);
|
|
29400
|
+
reportHookRejections(
|
|
29401
|
+
installLossReport,
|
|
29402
|
+
tools,
|
|
29403
|
+
path37.resolve(kitInfo.path, "hooks"),
|
|
29404
|
+
kitInfo.components.hooks.declared
|
|
29405
|
+
);
|
|
29406
|
+
const hooksConflict = reportHooksSourceConflict(
|
|
29407
|
+
installLossReport,
|
|
29408
|
+
tools,
|
|
29409
|
+
path37.resolve(kitInfo.path, "hooks"),
|
|
29410
|
+
kitInfo.components.hooks.declared
|
|
29411
|
+
);
|
|
29412
|
+
if (hooksConflict) {
|
|
29413
|
+
logger.warn(hooksConflict);
|
|
29414
|
+
}
|
|
29415
|
+
if (scope === "project") {
|
|
29416
|
+
reportProjectScopeScriptPath(
|
|
29417
|
+
installLossReport,
|
|
29418
|
+
tools,
|
|
29419
|
+
path37.resolve(kitInfo.path, "hooks"),
|
|
29420
|
+
kitInfo.components.hooks.declared
|
|
29421
|
+
);
|
|
29422
|
+
}
|
|
29423
|
+
writeLossReport(options.lossReport, installLossReport);
|
|
29069
29424
|
exitOnErrorSeverityLoss(installLossReport, logger, {
|
|
29070
29425
|
ignoreErrors: options.ignoreErrors ?? false
|
|
29071
29426
|
});
|
|
@@ -29205,12 +29560,12 @@ function applyClaudeCodePlacements(placements, manifest, skippedTargetPaths, log
|
|
|
29205
29560
|
continue;
|
|
29206
29561
|
}
|
|
29207
29562
|
const destDir = path37.dirname(placement.destination);
|
|
29208
|
-
|
|
29563
|
+
fs38.mkdirSync(destDir, { recursive: true });
|
|
29209
29564
|
if (placement.type === "symlink") {
|
|
29210
|
-
if (
|
|
29565
|
+
if (fs38.existsSync(placement.destination) || isSymlink2(placement.destination)) {
|
|
29211
29566
|
removeManagedPath(placement.destination);
|
|
29212
29567
|
}
|
|
29213
|
-
|
|
29568
|
+
fs38.symlinkSync(
|
|
29214
29569
|
placement.source,
|
|
29215
29570
|
placement.destination,
|
|
29216
29571
|
placement.isDir ? "dir" : "file"
|
|
@@ -29219,12 +29574,12 @@ function applyClaudeCodePlacements(placements, manifest, skippedTargetPaths, log
|
|
|
29219
29574
|
` symlink: ${placement.source} \u2192 ${placement.destination}`
|
|
29220
29575
|
);
|
|
29221
29576
|
} else {
|
|
29222
|
-
|
|
29577
|
+
fs38.copyFileSync(placement.source, placement.destination);
|
|
29223
29578
|
logger.verbose(` copy: ${placement.source} \u2192 ${placement.destination}`);
|
|
29224
29579
|
}
|
|
29225
29580
|
addArtifactToManifest(manifest, {
|
|
29226
29581
|
relativePath: placement.manifestKey,
|
|
29227
|
-
hash:
|
|
29582
|
+
hash: fs38.statSync(placement.source).isDirectory() ? `dir:${placement.source}` : computeSourceHash(placement.source),
|
|
29228
29583
|
origin: "tangyr-managed"
|
|
29229
29584
|
});
|
|
29230
29585
|
count++;
|
|
@@ -29636,12 +29991,12 @@ function getClaudeCodePlacements(kitInfo, scope = "global", projectRoot, mapping
|
|
|
29636
29991
|
};
|
|
29637
29992
|
}
|
|
29638
29993
|
const placements = [];
|
|
29639
|
-
const
|
|
29640
|
-
if (
|
|
29994
|
+
const entrypoint = resolveEntrypoint(kitInfo);
|
|
29995
|
+
if (entrypoint) {
|
|
29641
29996
|
placements.push({
|
|
29642
|
-
source:
|
|
29997
|
+
source: entrypoint.file,
|
|
29643
29998
|
destination: resolvedPaths.instructions,
|
|
29644
|
-
manifestKey:
|
|
29999
|
+
manifestKey: `claude-code/${entrypoint.name}`,
|
|
29645
30000
|
type: "symlink",
|
|
29646
30001
|
isDir: false
|
|
29647
30002
|
});
|
|
@@ -29654,7 +30009,7 @@ function getClaudeCodePlacements(kitInfo, scope = "global", projectRoot, mapping
|
|
|
29654
30009
|
};
|
|
29655
30010
|
for (const [component, targetPath] of Object.entries(componentDirMap)) {
|
|
29656
30011
|
const componentDir = path37.join(kitInfo.path, component);
|
|
29657
|
-
if (
|
|
30012
|
+
if (fs38.existsSync(componentDir) && fs38.statSync(componentDir).isDirectory()) {
|
|
29658
30013
|
placements.push({
|
|
29659
30014
|
source: componentDir,
|
|
29660
30015
|
destination: targetPath,
|
|
@@ -29668,12 +30023,12 @@ function getClaudeCodePlacements(kitInfo, scope = "global", projectRoot, mapping
|
|
|
29668
30023
|
}
|
|
29669
30024
|
function getClaudeCodePlacementsFromProfile(kitInfo, profile) {
|
|
29670
30025
|
const placements = [];
|
|
29671
|
-
const
|
|
29672
|
-
if (
|
|
30026
|
+
const entrypoint = resolveEntrypoint(kitInfo);
|
|
30027
|
+
if (entrypoint) {
|
|
29673
30028
|
placements.push({
|
|
29674
|
-
source:
|
|
30029
|
+
source: entrypoint.file,
|
|
29675
30030
|
destination: profile.instructions,
|
|
29676
|
-
manifestKey:
|
|
30031
|
+
manifestKey: `claude-code/${entrypoint.name}`,
|
|
29677
30032
|
type: "symlink",
|
|
29678
30033
|
isDir: false
|
|
29679
30034
|
});
|
|
@@ -29686,7 +30041,7 @@ function getClaudeCodePlacementsFromProfile(kitInfo, profile) {
|
|
|
29686
30041
|
};
|
|
29687
30042
|
for (const [component, targetPath] of Object.entries(componentDirMap)) {
|
|
29688
30043
|
const componentDir = path37.join(kitInfo.path, component);
|
|
29689
|
-
if (
|
|
30044
|
+
if (fs38.existsSync(componentDir) && fs38.statSync(componentDir).isDirectory()) {
|
|
29690
30045
|
placements.push({
|
|
29691
30046
|
source: componentDir,
|
|
29692
30047
|
destination: targetPath,
|
|
@@ -29700,9 +30055,9 @@ function getClaudeCodePlacementsFromProfile(kitInfo, profile) {
|
|
|
29700
30055
|
}
|
|
29701
30056
|
function collectAllFiles(kitInfo) {
|
|
29702
30057
|
const files = /* @__PURE__ */ new Map();
|
|
29703
|
-
const
|
|
29704
|
-
if (
|
|
29705
|
-
files.set(
|
|
30058
|
+
const entrypoint = resolveEntrypoint(kitInfo);
|
|
30059
|
+
if (entrypoint) {
|
|
30060
|
+
files.set(entrypoint.name, entrypoint.file);
|
|
29706
30061
|
}
|
|
29707
30062
|
for (const [component, reconciliation] of Object.entries(
|
|
29708
30063
|
kitInfo.components
|
|
@@ -29719,7 +30074,7 @@ function collectAllFiles(kitInfo) {
|
|
|
29719
30074
|
}
|
|
29720
30075
|
function isSymlink2(p) {
|
|
29721
30076
|
try {
|
|
29722
|
-
return
|
|
30077
|
+
return fs38.lstatSync(p).isSymbolicLink();
|
|
29723
30078
|
} catch {
|
|
29724
30079
|
return false;
|
|
29725
30080
|
}
|
|
@@ -29854,10 +30209,13 @@ function withDirectKit(kits, directKitPath) {
|
|
|
29854
30209
|
|
|
29855
30210
|
// src/commands/probe.ts
|
|
29856
30211
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
29857
|
-
import
|
|
30212
|
+
import fs39 from "fs";
|
|
29858
30213
|
import path39 from "path";
|
|
29859
30214
|
function runProbeCommand(options, logger) {
|
|
29860
|
-
const { config, configDir, sourceRoot } = resolveRuntime(
|
|
30215
|
+
const { config, configDir, sourceRoot } = resolveRuntime(
|
|
30216
|
+
options,
|
|
30217
|
+
import.meta.url
|
|
30218
|
+
);
|
|
29861
30219
|
const platformPaths = readYamlObject(
|
|
29862
30220
|
path39.join(
|
|
29863
30221
|
resolveConfiguredPath(config.mappings, configDir, sourceRoot, "mappings"),
|
|
@@ -29885,7 +30243,9 @@ function runProbeCommand(options, logger) {
|
|
|
29885
30243
|
console.log(JSON.stringify(rows, null, 2));
|
|
29886
30244
|
} else {
|
|
29887
30245
|
for (const row of rows) {
|
|
29888
|
-
logger.info(
|
|
30246
|
+
logger.info(
|
|
30247
|
+
`${row.target} ${row.key} ${row.exists ? "exists" : "missing"} ${row.path}`
|
|
30248
|
+
);
|
|
29889
30249
|
}
|
|
29890
30250
|
}
|
|
29891
30251
|
}
|
|
@@ -29893,15 +30253,26 @@ function toProbeRow(target, key, rawPath) {
|
|
|
29893
30253
|
const resolved = resolveTargetPath(rawPath);
|
|
29894
30254
|
let accessible = false;
|
|
29895
30255
|
try {
|
|
29896
|
-
|
|
30256
|
+
fs39.accessSync(resolved);
|
|
29897
30257
|
accessible = true;
|
|
29898
30258
|
} catch {
|
|
29899
30259
|
accessible = false;
|
|
29900
30260
|
}
|
|
29901
|
-
return {
|
|
30261
|
+
return {
|
|
30262
|
+
target,
|
|
30263
|
+
key,
|
|
30264
|
+
path: resolved,
|
|
30265
|
+
exists: fs39.existsSync(resolved),
|
|
30266
|
+
accessible
|
|
30267
|
+
};
|
|
29902
30268
|
}
|
|
29903
30269
|
function codexRuntimeProbeRows() {
|
|
29904
|
-
const rows = commandProbeRows(
|
|
30270
|
+
const rows = commandProbeRows(
|
|
30271
|
+
"codex",
|
|
30272
|
+
"binary",
|
|
30273
|
+
process.platform === "win32" ? "where.exe" : "which",
|
|
30274
|
+
["codex"]
|
|
30275
|
+
);
|
|
29905
30276
|
if (process.platform !== "linux") {
|
|
29906
30277
|
return rows;
|
|
29907
30278
|
}
|
|
@@ -29909,7 +30280,7 @@ function codexRuntimeProbeRows() {
|
|
|
29909
30280
|
["binary_snap", "/snap/bin/codex"],
|
|
29910
30281
|
["binary_flatpak", "/var/lib/flatpak/exports/bin/codex"]
|
|
29911
30282
|
]) {
|
|
29912
|
-
if (
|
|
30283
|
+
if (fs39.existsSync(candidatePath)) {
|
|
29913
30284
|
rows.push(toProbeRow("codex", key, candidatePath));
|
|
29914
30285
|
}
|
|
29915
30286
|
}
|
|
@@ -29925,7 +30296,7 @@ function commandProbeRows(target, key, command, args) {
|
|
|
29925
30296
|
|
|
29926
30297
|
// src/commands/status.ts
|
|
29927
30298
|
import crypto10 from "crypto";
|
|
29928
|
-
import
|
|
30299
|
+
import fs40 from "fs";
|
|
29929
30300
|
import path40 from "path";
|
|
29930
30301
|
function runStatusCommand(options, logger) {
|
|
29931
30302
|
const { config, configDir, sourceRoot } = resolveRuntime(
|
|
@@ -30010,7 +30381,7 @@ function classifyCodexFiles(config, sourceRoot, configDir) {
|
|
|
30010
30381
|
mappings,
|
|
30011
30382
|
createLossReport()
|
|
30012
30383
|
);
|
|
30013
|
-
const instructionsSource =
|
|
30384
|
+
const instructionsSource = fs40.readFileSync(path40.join(sourceRoot, config.source.instructions), "utf8").trim();
|
|
30014
30385
|
const composedInstructions = `${instructionsSource}
|
|
30015
30386
|
|
|
30016
30387
|
--- tangyr managed rules ---
|
|
@@ -30075,11 +30446,11 @@ ${rules.agentsAppendix}`.trim();
|
|
|
30075
30446
|
config,
|
|
30076
30447
|
sourceRoot
|
|
30077
30448
|
),
|
|
30078
|
-
|
|
30449
|
+
fs40.existsSync(hooksSource) ? classifyCodexHooksFile(resolveTargetPath(codexPaths.hooksSharedFile)) : notConfiguredRow(
|
|
30079
30450
|
"hooks",
|
|
30080
30451
|
resolveTargetPath(codexPaths.hooksSharedFile)
|
|
30081
30452
|
),
|
|
30082
|
-
|
|
30453
|
+
fs40.existsSync(mcpSource) ? classifyCompiledFile(
|
|
30083
30454
|
"mcp",
|
|
30084
30455
|
resolveTargetPath(codexPaths.mcpSharedFile),
|
|
30085
30456
|
"codex",
|
|
@@ -30099,7 +30470,7 @@ function classifyCodexHooksFile(filePath) {
|
|
|
30099
30470
|
conformant: true
|
|
30100
30471
|
};
|
|
30101
30472
|
}
|
|
30102
|
-
if (!
|
|
30473
|
+
if (!fs40.existsSync(filePath)) {
|
|
30103
30474
|
return {
|
|
30104
30475
|
component: "hooks",
|
|
30105
30476
|
className: "missing",
|
|
@@ -30182,7 +30553,7 @@ function classifyCopilotFiles(config, sourceRoot, configDir) {
|
|
|
30182
30553
|
config,
|
|
30183
30554
|
sourceRoot
|
|
30184
30555
|
),
|
|
30185
|
-
|
|
30556
|
+
fs40.existsSync(hooksSource) ? classifyCompiledFile(
|
|
30186
30557
|
"hooks",
|
|
30187
30558
|
path40.join(
|
|
30188
30559
|
resolveTargetPath(copilotPaths.hooks),
|
|
@@ -30199,7 +30570,7 @@ function classifyCopilotFiles(config, sourceRoot, configDir) {
|
|
|
30199
30570
|
"tangyr-managed.json"
|
|
30200
30571
|
)
|
|
30201
30572
|
),
|
|
30202
|
-
|
|
30573
|
+
fs40.existsSync(mcpSource) ? classifyCompiledFile(
|
|
30203
30574
|
"mcp",
|
|
30204
30575
|
resolveTargetPath(copilotPaths.mcpSharedFile),
|
|
30205
30576
|
"copilot",
|
|
@@ -30374,14 +30745,14 @@ function classifyMcp(filePath, sourcePath) {
|
|
|
30374
30745
|
}
|
|
30375
30746
|
function isSymlinkCurrent(linkPath, sourcePath) {
|
|
30376
30747
|
try {
|
|
30377
|
-
return
|
|
30748
|
+
return fs40.realpathSync(linkPath) === fs40.realpathSync(sourcePath);
|
|
30378
30749
|
} catch {
|
|
30379
30750
|
return false;
|
|
30380
30751
|
}
|
|
30381
30752
|
}
|
|
30382
30753
|
function isCompiledCurrent6(filePath, sourcePath) {
|
|
30383
30754
|
try {
|
|
30384
|
-
const marker = extractProvenanceMarker(
|
|
30755
|
+
const marker = extractProvenanceMarker(fs40.readFileSync(filePath, "utf8"));
|
|
30385
30756
|
return marker?.hash === computeSourceHash(sourcePath) && marker.target === "claude-code";
|
|
30386
30757
|
} catch {
|
|
30387
30758
|
return false;
|
|
@@ -30411,7 +30782,7 @@ function classifyCompiledFile(component, filePath, target, expectedHash, config,
|
|
|
30411
30782
|
}
|
|
30412
30783
|
function readCompiledContent(filePath) {
|
|
30413
30784
|
try {
|
|
30414
|
-
return
|
|
30785
|
+
return fs40.readFileSync(filePath, "utf8");
|
|
30415
30786
|
} catch {
|
|
30416
30787
|
return null;
|
|
30417
30788
|
}
|
|
@@ -30442,7 +30813,7 @@ function hashString6(value) {
|
|
|
30442
30813
|
}
|
|
30443
30814
|
|
|
30444
30815
|
// src/commands/sync.ts
|
|
30445
|
-
import
|
|
30816
|
+
import fs42 from "fs";
|
|
30446
30817
|
import path42 from "path";
|
|
30447
30818
|
|
|
30448
30819
|
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
@@ -30941,7 +31312,7 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
30941
31312
|
var source_default = chalk;
|
|
30942
31313
|
|
|
30943
31314
|
// src/adapters/claude-code.ts
|
|
30944
|
-
import
|
|
31315
|
+
import fs41 from "fs";
|
|
30945
31316
|
import path41 from "path";
|
|
30946
31317
|
|
|
30947
31318
|
// src/core/shim.ts
|
|
@@ -31080,7 +31451,14 @@ function syncClaudeCode(config, sourceRoot, options, logger, mappings, manifest,
|
|
|
31080
31451
|
)
|
|
31081
31452
|
);
|
|
31082
31453
|
outcomes.push(
|
|
31083
|
-
syncHooks6(
|
|
31454
|
+
syncHooks6(
|
|
31455
|
+
config,
|
|
31456
|
+
sourceRoot,
|
|
31457
|
+
options,
|
|
31458
|
+
logger,
|
|
31459
|
+
profile.hooks_shared_file,
|
|
31460
|
+
profile.hooks
|
|
31461
|
+
)
|
|
31084
31462
|
);
|
|
31085
31463
|
}
|
|
31086
31464
|
if (!shouldSkipComponent6(options, "mcp")) {
|
|
@@ -31107,7 +31485,7 @@ function syncClaudeCode(config, sourceRoot, options, logger, mappings, manifest,
|
|
|
31107
31485
|
function syncProviderLoss2(config, sourceRoot, mappings, lossReport) {
|
|
31108
31486
|
if (!mappings.modelTiers.custom_provider) return;
|
|
31109
31487
|
const agentsDir = path41.join(sourceRoot, config.source.agents);
|
|
31110
|
-
if (!
|
|
31488
|
+
if (!fs41.existsSync(agentsDir)) return;
|
|
31111
31489
|
for (const agentFile of ts(path41.join(agentsDir, "*.md")).sort()) {
|
|
31112
31490
|
const { data } = parseMarkdownFrontmatter(agentFile);
|
|
31113
31491
|
const hasProvider = typeof data.provider === "string" || typeof data.provider === "object" && data.provider !== null && Object.keys(data.provider).length > 0;
|
|
@@ -31137,19 +31515,19 @@ function shouldSkipComponent6(options, component) {
|
|
|
31137
31515
|
function applyClaudeCodeShimUpgrade(shimPath, agentsMdPath, manifest, scopePath, logger) {
|
|
31138
31516
|
const expectedBodyHash = shimBodyHash();
|
|
31139
31517
|
const shimContent = buildAgentsMdShim(shimPath, agentsMdPath, "claude-code");
|
|
31140
|
-
if (!
|
|
31518
|
+
if (!fs41.existsSync(shimPath)) {
|
|
31141
31519
|
manifest.artifacts.push({
|
|
31142
31520
|
relativePath: path41.relative(scopePath, shimPath),
|
|
31143
31521
|
hash: expectedBodyHash,
|
|
31144
31522
|
origin: "tangyr-managed"
|
|
31145
31523
|
});
|
|
31146
31524
|
writeManifest(scopePath, manifest);
|
|
31147
|
-
|
|
31148
|
-
|
|
31525
|
+
fs41.mkdirSync(path41.dirname(shimPath), { recursive: true });
|
|
31526
|
+
fs41.writeFileSync(shimPath, shimContent);
|
|
31149
31527
|
logger?.verbose(`instructions-shim: wrote @AGENTS.md shim -> ${shimPath}`);
|
|
31150
31528
|
return;
|
|
31151
31529
|
}
|
|
31152
|
-
const existing =
|
|
31530
|
+
const existing = fs41.readFileSync(shimPath, "utf8");
|
|
31153
31531
|
const firstNonEmptyLine = existing.split("\n").find((l) => l.trim().length > 0) ?? "";
|
|
31154
31532
|
const marker = extractProvenanceMarker(firstNonEmptyLine);
|
|
31155
31533
|
if (marker !== null && marker.hash === expectedBodyHash) {
|
|
@@ -31160,7 +31538,7 @@ function applyClaudeCodeShimUpgrade(shimPath, agentsMdPath, manifest, scopePath,
|
|
|
31160
31538
|
}
|
|
31161
31539
|
backupFile(shimPath, manifest);
|
|
31162
31540
|
writeManifest(scopePath, manifest);
|
|
31163
|
-
|
|
31541
|
+
fs41.writeFileSync(shimPath, shimContent);
|
|
31164
31542
|
manifest.artifacts.push({
|
|
31165
31543
|
relativePath: path41.relative(scopePath, shimPath),
|
|
31166
31544
|
hash: expectedBodyHash,
|
|
@@ -31174,7 +31552,7 @@ function syncDirectComponent(config, sourceRoot, options, logger, directComponen
|
|
|
31174
31552
|
config.source[directComponent.sourceKey]
|
|
31175
31553
|
);
|
|
31176
31554
|
const destination = directComponent.destination;
|
|
31177
|
-
if (!
|
|
31555
|
+
if (!fs41.existsSync(source)) {
|
|
31178
31556
|
return skippedMissingSource6(directComponent.component, source);
|
|
31179
31557
|
}
|
|
31180
31558
|
if (options.dryRun) {
|
|
@@ -31196,7 +31574,7 @@ function syncDirectComponent(config, sourceRoot, options, logger, directComponen
|
|
|
31196
31574
|
};
|
|
31197
31575
|
}
|
|
31198
31576
|
if (artifactClass === "managed-stale" && isOurStaleSymlink(destination, /* @__PURE__ */ new Set([destination]), config)) {
|
|
31199
|
-
|
|
31577
|
+
fs41.unlinkSync(destination);
|
|
31200
31578
|
} else if (artifactClass === "unmanaged-conflict" && !handleConflict6(destination, config, options, logger, manifest, scopePath)) {
|
|
31201
31579
|
return {
|
|
31202
31580
|
component: directComponent.component,
|
|
@@ -31204,7 +31582,7 @@ function syncDirectComponent(config, sourceRoot, options, logger, directComponen
|
|
|
31204
31582
|
path: destination
|
|
31205
31583
|
};
|
|
31206
31584
|
} else if (artifactClass === "managed-symlink") {
|
|
31207
|
-
|
|
31585
|
+
fs41.unlinkSync(destination);
|
|
31208
31586
|
}
|
|
31209
31587
|
ensureDir(path41.dirname(destination));
|
|
31210
31588
|
createRelativeSymlink(source, destination, directComponent.type, {
|
|
@@ -31224,7 +31602,7 @@ function syncDirectComponent(config, sourceRoot, options, logger, directComponen
|
|
|
31224
31602
|
}
|
|
31225
31603
|
function syncClaudeCodeSkills(config, sourceRoot, options, logger, destination, manifest, scopePath) {
|
|
31226
31604
|
const source = path41.resolve(sourceRoot, config.source.skills);
|
|
31227
|
-
if (!
|
|
31605
|
+
if (!fs41.existsSync(source)) {
|
|
31228
31606
|
return [skippedMissingSource6("skills", source)];
|
|
31229
31607
|
}
|
|
31230
31608
|
const declaredNames = resolveKitSkillNames(sourceRoot);
|
|
@@ -31279,7 +31657,7 @@ function syncClaudeCodeSkills(config, sourceRoot, options, logger, destination,
|
|
|
31279
31657
|
function syncSingleSkill(config, sourceRoot, options, logger, source, destination, name, manifest, scopePath) {
|
|
31280
31658
|
const skillSource = path41.join(source, name);
|
|
31281
31659
|
const skillDestination = path41.join(destination, name);
|
|
31282
|
-
if (!
|
|
31660
|
+
if (!fs41.existsSync(skillSource)) {
|
|
31283
31661
|
return skippedMissingSource6("skills", skillSource);
|
|
31284
31662
|
}
|
|
31285
31663
|
const artifactClass = classifyArtifact(skillDestination, config, sourceRoot);
|
|
@@ -31291,7 +31669,7 @@ function syncSingleSkill(config, sourceRoot, options, logger, source, destinatio
|
|
|
31291
31669
|
};
|
|
31292
31670
|
}
|
|
31293
31671
|
if (artifactClass === "managed-stale" && isOurStaleSymlink(skillDestination, /* @__PURE__ */ new Set([skillDestination]), config)) {
|
|
31294
|
-
|
|
31672
|
+
fs41.unlinkSync(skillDestination);
|
|
31295
31673
|
} else if (artifactClass === "unmanaged-conflict" && !handleConflict6(
|
|
31296
31674
|
skillDestination,
|
|
31297
31675
|
config,
|
|
@@ -31302,26 +31680,28 @@ function syncSingleSkill(config, sourceRoot, options, logger, source, destinatio
|
|
|
31302
31680
|
)) {
|
|
31303
31681
|
return { component: "skills", status: "conflict", path: skillDestination };
|
|
31304
31682
|
} else if (artifactClass === "managed-symlink") {
|
|
31305
|
-
|
|
31683
|
+
fs41.unlinkSync(skillDestination);
|
|
31306
31684
|
}
|
|
31307
31685
|
createRelativeSymlink(skillSource, skillDestination, "dir");
|
|
31308
31686
|
return { component: "skills", status: "success", path: skillDestination };
|
|
31309
31687
|
}
|
|
31310
31688
|
function migrateWholesaleSkillsSymlink(source, destination, declaredNames, logger) {
|
|
31311
31689
|
const foreignNames = foreignSkillNames(source, declaredNames);
|
|
31312
|
-
|
|
31690
|
+
fs41.unlinkSync(destination);
|
|
31313
31691
|
ensureDir(destination);
|
|
31314
31692
|
for (const name of foreignNames) {
|
|
31315
31693
|
const from = path41.join(source, name);
|
|
31316
31694
|
const to = path41.join(destination, name);
|
|
31317
|
-
if (
|
|
31695
|
+
if (fs41.existsSync(to)) {
|
|
31318
31696
|
logger.warn(
|
|
31319
|
-
`skills migration: ${to} already exists \u2014 skipping
|
|
31697
|
+
`skills migration: ${to} already exists \u2014 skipping ${from} to avoid overwrite. Nothing in the kit was changed.`
|
|
31320
31698
|
);
|
|
31321
31699
|
continue;
|
|
31322
31700
|
}
|
|
31323
|
-
|
|
31324
|
-
logger.
|
|
31701
|
+
fs41.cpSync(from, to, { recursive: true, verbatimSymlinks: true });
|
|
31702
|
+
logger.warn(
|
|
31703
|
+
`skills migration: '${name}' is present under the kit's skills/ but is not declared in its manifest. Copied ${from} -> ${to} so the harness can read it. The original is left in the kit deliberately: this command does not modify a kit. Remove ${from} yourself if it does not belong there, or declare it in tangyr-kit.yaml if it does.`
|
|
31704
|
+
);
|
|
31325
31705
|
}
|
|
31326
31706
|
}
|
|
31327
31707
|
function foreignSkillNames(source, declaredNames) {
|
|
@@ -31332,21 +31712,21 @@ function foreignSkillNames(source, declaredNames) {
|
|
|
31332
31712
|
return listSkillDirNames(source).filter((name) => !declaredSet.has(name));
|
|
31333
31713
|
}
|
|
31334
31714
|
function listSkillDirNames(dir) {
|
|
31335
|
-
if (!
|
|
31715
|
+
if (!fs41.existsSync(dir)) {
|
|
31336
31716
|
return [];
|
|
31337
31717
|
}
|
|
31338
|
-
return
|
|
31718
|
+
return fs41.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
|
|
31339
31719
|
}
|
|
31340
31720
|
function isSymlinkPath(filePath) {
|
|
31341
31721
|
try {
|
|
31342
|
-
return
|
|
31722
|
+
return fs41.lstatSync(filePath).isSymbolicLink();
|
|
31343
31723
|
} catch {
|
|
31344
31724
|
return false;
|
|
31345
31725
|
}
|
|
31346
31726
|
}
|
|
31347
31727
|
function syncHookScriptsBridge(config, sourceRoot, options, logger, destination, manifest, scopePath) {
|
|
31348
31728
|
const source = path41.resolve(sourceRoot, config.source.hooks);
|
|
31349
|
-
if (!
|
|
31729
|
+
if (!fs41.existsSync(source)) {
|
|
31350
31730
|
return skippedMissingSource6("hooks", source);
|
|
31351
31731
|
}
|
|
31352
31732
|
if (options.dryRun) {
|
|
@@ -31358,17 +31738,25 @@ function syncHookScriptsBridge(config, sourceRoot, options, logger, destination,
|
|
|
31358
31738
|
return { component: "hooks", status: "skipped-current", path: destination };
|
|
31359
31739
|
}
|
|
31360
31740
|
if (artifactClass === "managed-stale" && isOurStaleSymlink(destination, /* @__PURE__ */ new Set([destination]), config)) {
|
|
31361
|
-
|
|
31741
|
+
fs41.unlinkSync(destination);
|
|
31362
31742
|
} else if (artifactClass === "unmanaged-conflict" && !handleConflict6(destination, config, options, logger, manifest, scopePath)) {
|
|
31363
31743
|
return { component: "hooks", status: "conflict", path: destination };
|
|
31364
31744
|
} else if (artifactClass === "managed-symlink") {
|
|
31365
|
-
|
|
31745
|
+
fs41.unlinkSync(destination);
|
|
31366
31746
|
}
|
|
31367
31747
|
ensureDir(path41.dirname(destination));
|
|
31368
31748
|
createRelativeSymlink(source, destination, "dir");
|
|
31369
31749
|
return { component: "hooks", status: "success", path: destination };
|
|
31370
31750
|
}
|
|
31371
|
-
function
|
|
31751
|
+
function hookScriptsDir(scriptsBridge) {
|
|
31752
|
+
const globalBridge = resolveTargetPath("~/.agents/hooks");
|
|
31753
|
+
if (path41.resolve(scriptsBridge) === path41.resolve(globalBridge)) {
|
|
31754
|
+
return GLOBAL_HOOK_SCRIPTS_DIR;
|
|
31755
|
+
}
|
|
31756
|
+
const relative = path41.relative(process.cwd(), scriptsBridge);
|
|
31757
|
+
return relative.startsWith("..") || path41.isAbsolute(relative) ? path41.join(scriptsBridge, "scripts") : `"$CLAUDE_PROJECT_DIR"/${path41.posix.join(relative.split(path41.sep).join("/"), "scripts")}`;
|
|
31758
|
+
}
|
|
31759
|
+
function syncHooks6(config, sourceRoot, options, logger, destination, scriptsBridge) {
|
|
31372
31760
|
const hooksDir = resolveHooksSourceDir(sourceRoot, config);
|
|
31373
31761
|
if (!hooksSourceExists(sourceRoot, config)) {
|
|
31374
31762
|
return skippedMissingSource6("hooks", hooksDir);
|
|
@@ -31380,14 +31768,15 @@ function syncHooks6(config, sourceRoot, options, logger, destination) {
|
|
|
31380
31768
|
const settings = readJsonObject4(destination);
|
|
31381
31769
|
const sourceHooks = loadSourceHooks(
|
|
31382
31770
|
hooksDir,
|
|
31383
|
-
resolveKitHookNames(sourceRoot)
|
|
31771
|
+
resolveKitHookNames(sourceRoot),
|
|
31772
|
+
hookScriptsDir(scriptsBridge)
|
|
31384
31773
|
);
|
|
31385
31774
|
settings.hooks = markManaged(sourceHooks.hooks ?? {});
|
|
31386
31775
|
const changed = writeSharedFile(
|
|
31387
31776
|
destination,
|
|
31388
31777
|
settings,
|
|
31389
31778
|
stringifyJson3,
|
|
31390
|
-
!
|
|
31779
|
+
!fs41.existsSync(destination),
|
|
31391
31780
|
logger
|
|
31392
31781
|
);
|
|
31393
31782
|
return {
|
|
@@ -31398,7 +31787,7 @@ function syncHooks6(config, sourceRoot, options, logger, destination) {
|
|
|
31398
31787
|
}
|
|
31399
31788
|
function syncMcp6(config, sourceRoot, options, logger, destination) {
|
|
31400
31789
|
const source = path41.resolve(sourceRoot, config.source.mcp);
|
|
31401
|
-
if (!
|
|
31790
|
+
if (!fs41.existsSync(source)) {
|
|
31402
31791
|
return skippedMissingSource6("mcp", source);
|
|
31403
31792
|
}
|
|
31404
31793
|
if (options.dryRun) {
|
|
@@ -31423,7 +31812,7 @@ function syncMcp6(config, sourceRoot, options, logger, destination) {
|
|
|
31423
31812
|
destination,
|
|
31424
31813
|
hostConfig,
|
|
31425
31814
|
stringifyJson3,
|
|
31426
|
-
!
|
|
31815
|
+
!fs41.existsSync(destination),
|
|
31427
31816
|
logger
|
|
31428
31817
|
);
|
|
31429
31818
|
return {
|
|
@@ -31434,7 +31823,7 @@ function syncMcp6(config, sourceRoot, options, logger, destination) {
|
|
|
31434
31823
|
}
|
|
31435
31824
|
function syncSettings2(config, sourceRoot, options, logger, destination) {
|
|
31436
31825
|
const source = path41.resolve(sourceRoot, config.source.settings);
|
|
31437
|
-
if (!
|
|
31826
|
+
if (!fs41.existsSync(source)) {
|
|
31438
31827
|
return skippedMissingSource6("settings", source);
|
|
31439
31828
|
}
|
|
31440
31829
|
if (options.dryRun) {
|
|
@@ -31452,7 +31841,7 @@ function syncSettings2(config, sourceRoot, options, logger, destination) {
|
|
|
31452
31841
|
destination,
|
|
31453
31842
|
settings,
|
|
31454
31843
|
stringifyJson3,
|
|
31455
|
-
!
|
|
31844
|
+
!fs41.existsSync(destination),
|
|
31456
31845
|
logger
|
|
31457
31846
|
);
|
|
31458
31847
|
return {
|
|
@@ -31477,11 +31866,11 @@ function handleConflict6(destination, config, options, logger, manifest, scopePa
|
|
|
31477
31866
|
backupFile(destination, manifest);
|
|
31478
31867
|
writeManifest(scopePath, manifest);
|
|
31479
31868
|
} else {
|
|
31480
|
-
|
|
31869
|
+
fs41.renameSync(destination, `${destination}.bak.${Date.now()}`);
|
|
31481
31870
|
}
|
|
31482
31871
|
return true;
|
|
31483
31872
|
}
|
|
31484
|
-
|
|
31873
|
+
fs41.rmSync(destination, { recursive: true, force: true });
|
|
31485
31874
|
return true;
|
|
31486
31875
|
}
|
|
31487
31876
|
function skippedMissingSource6(component, source) {
|
|
@@ -31493,16 +31882,16 @@ function skippedMissingSource6(component, source) {
|
|
|
31493
31882
|
}
|
|
31494
31883
|
function isSymlinkTo6(linkPath, source) {
|
|
31495
31884
|
try {
|
|
31496
|
-
return
|
|
31885
|
+
return fs41.realpathSync(linkPath) === fs41.realpathSync(source);
|
|
31497
31886
|
} catch {
|
|
31498
31887
|
return false;
|
|
31499
31888
|
}
|
|
31500
31889
|
}
|
|
31501
31890
|
function readJsonObject4(filePath) {
|
|
31502
|
-
if (!
|
|
31891
|
+
if (!fs41.existsSync(filePath)) {
|
|
31503
31892
|
return {};
|
|
31504
31893
|
}
|
|
31505
|
-
const parsed = JSON.parse(
|
|
31894
|
+
const parsed = JSON.parse(fs41.readFileSync(filePath, "utf8"));
|
|
31506
31895
|
return isRecord5(parsed) ? parsed : {};
|
|
31507
31896
|
}
|
|
31508
31897
|
function markManaged(value) {
|
|
@@ -31698,6 +32087,31 @@ async function runSyncCommand(options, logger) {
|
|
|
31698
32087
|
throwUnrecognizedTarget(target);
|
|
31699
32088
|
}
|
|
31700
32089
|
}
|
|
32090
|
+
if (kitInfo) {
|
|
32091
|
+
reportHookRejections(
|
|
32092
|
+
lossReport,
|
|
32093
|
+
targets,
|
|
32094
|
+
path42.resolve(kitInfo.path, "hooks"),
|
|
32095
|
+
kitInfo.components.hooks.declared
|
|
32096
|
+
);
|
|
32097
|
+
const hooksConflict = reportHooksSourceConflict(
|
|
32098
|
+
lossReport,
|
|
32099
|
+
targets,
|
|
32100
|
+
path42.resolve(kitInfo.path, "hooks"),
|
|
32101
|
+
kitInfo.components.hooks.declared
|
|
32102
|
+
);
|
|
32103
|
+
if (hooksConflict) {
|
|
32104
|
+
logger.warn(hooksConflict);
|
|
32105
|
+
}
|
|
32106
|
+
if (syncOptions.scope === "project") {
|
|
32107
|
+
reportProjectScopeScriptPath(
|
|
32108
|
+
lossReport,
|
|
32109
|
+
targets,
|
|
32110
|
+
path42.resolve(kitInfo.path, "hooks"),
|
|
32111
|
+
kitInfo.components.hooks.declared
|
|
32112
|
+
);
|
|
32113
|
+
}
|
|
32114
|
+
}
|
|
31701
32115
|
writeLossReport(options.lossReport, lossReport);
|
|
31702
32116
|
printOutcomeTable(results);
|
|
31703
32117
|
if (kitInfo && kitSync && !options.dryRun) {
|
|
@@ -31764,13 +32178,6 @@ function colorStatus(status) {
|
|
|
31764
32178
|
}
|
|
31765
32179
|
return source_default.yellow(status);
|
|
31766
32180
|
}
|
|
31767
|
-
function writeLossReport(filePath, report) {
|
|
31768
|
-
if (!filePath) {
|
|
31769
|
-
return;
|
|
31770
|
-
}
|
|
31771
|
-
fs41.writeFileSync(filePath, `${JSON.stringify(report, null, 2)}
|
|
31772
|
-
`);
|
|
31773
|
-
}
|
|
31774
32181
|
function prepareKitManifest(scope, projectRoot, kitInfo) {
|
|
31775
32182
|
const scopePath = resolveScopePath(scope, projectRoot);
|
|
31776
32183
|
const manifest = readManifest(scopePath) ?? createEmptyManifest(kitInfo.archetype, kitInfo.kitFormat, scope);
|
|
@@ -31810,7 +32217,7 @@ function updateManifest(kitInfo, kitSync) {
|
|
|
31810
32217
|
}
|
|
31811
32218
|
function resolveKitFilePath(kitPath, relativePath) {
|
|
31812
32219
|
const fullPath = path42.join(kitPath, relativePath);
|
|
31813
|
-
return
|
|
32220
|
+
return fs42.existsSync(fullPath) ? fullPath : null;
|
|
31814
32221
|
}
|
|
31815
32222
|
function cleanupRemovedManagedArtifacts(config, mappings, targets, options, removedSourcePaths, logger) {
|
|
31816
32223
|
if (removedSourcePaths.length === 0 || !mappings) {
|
|
@@ -31857,7 +32264,9 @@ function componentForRemovedSource(relativePath, config) {
|
|
|
31857
32264
|
if (relativePath.startsWith("rules/")) {
|
|
31858
32265
|
return "rules";
|
|
31859
32266
|
}
|
|
31860
|
-
if (relativePath
|
|
32267
|
+
if (relativePath.startsWith(
|
|
32268
|
+
`${normalizeManagedSourcePath(config.source.hooks.replace(/\\/gu, "/"))}/`
|
|
32269
|
+
)) {
|
|
31861
32270
|
return "hooks";
|
|
31862
32271
|
}
|
|
31863
32272
|
if (relativePath === mcpSourceLabel(config)) {
|
|
@@ -32126,17 +32535,17 @@ function cleanupRemovedClineArtifact(relativePath, paths, logger) {
|
|
|
32126
32535
|
}
|
|
32127
32536
|
}
|
|
32128
32537
|
function removeManagedCompiledArtifact2(artifactPath, expectedTarget, expectedSource, logger) {
|
|
32129
|
-
if (!
|
|
32538
|
+
if (!fs42.existsSync(artifactPath)) {
|
|
32130
32539
|
return;
|
|
32131
32540
|
}
|
|
32132
|
-
if (!
|
|
32541
|
+
if (!fs42.statSync(artifactPath).isFile()) {
|
|
32133
32542
|
return;
|
|
32134
32543
|
}
|
|
32135
|
-
const marker = extractProvenanceMarker(
|
|
32544
|
+
const marker = extractProvenanceMarker(fs42.readFileSync(artifactPath, "utf8"));
|
|
32136
32545
|
if (marker?.source !== expectedSource || marker.target !== expectedTarget) {
|
|
32137
32546
|
return;
|
|
32138
32547
|
}
|
|
32139
|
-
|
|
32548
|
+
fs42.rmSync(artifactPath, { force: true });
|
|
32140
32549
|
logger.verbose(` removed stale target artifact: ${artifactPath}`);
|
|
32141
32550
|
}
|
|
32142
32551
|
function removeManagedCopilotMcpArtifact2(artifactPath, expectedSource, logger) {
|
|
@@ -32167,10 +32576,10 @@ function removeManagedCodexHooksArtifact2(artifactPath, expectedSource, logger)
|
|
|
32167
32576
|
logger.verbose(` removed stale target artifact: ${artifactPath}`);
|
|
32168
32577
|
}
|
|
32169
32578
|
function removeManagedCodexMcpArtifact2(artifactPath, expectedSource, logger) {
|
|
32170
|
-
if (!
|
|
32579
|
+
if (!fs42.existsSync(artifactPath)) {
|
|
32171
32580
|
return;
|
|
32172
32581
|
}
|
|
32173
|
-
const content =
|
|
32582
|
+
const content = fs42.readFileSync(artifactPath, "utf8");
|
|
32174
32583
|
const startMarker = "# tangyr: managed mcp start";
|
|
32175
32584
|
const endMarker = "# tangyr: managed mcp end";
|
|
32176
32585
|
const startIndex = content.indexOf(startMarker);
|
|
@@ -32187,19 +32596,19 @@ function removeManagedCodexMcpArtifact2(artifactPath, expectedSource, logger) {
|
|
|
32187
32596
|
const after = content.slice(endIndex + endMarker.length).trimStart();
|
|
32188
32597
|
const cleaned = [before, after].filter((entry) => entry.length > 0).join("\n\n");
|
|
32189
32598
|
if (cleaned.length === 0) {
|
|
32190
|
-
|
|
32599
|
+
fs42.rmSync(artifactPath, { force: true });
|
|
32191
32600
|
} else {
|
|
32192
|
-
|
|
32601
|
+
fs42.writeFileSync(artifactPath, `${cleaned}
|
|
32193
32602
|
`);
|
|
32194
32603
|
}
|
|
32195
32604
|
logger.verbose(` removed stale target artifact: ${artifactPath}`);
|
|
32196
32605
|
}
|
|
32197
32606
|
function readJsonObject5(filePath) {
|
|
32198
|
-
if (!
|
|
32607
|
+
if (!fs42.existsSync(filePath)) {
|
|
32199
32608
|
return null;
|
|
32200
32609
|
}
|
|
32201
32610
|
try {
|
|
32202
|
-
const parsed = JSON.parse(
|
|
32611
|
+
const parsed = JSON.parse(fs42.readFileSync(filePath, "utf8"));
|
|
32203
32612
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
32204
32613
|
} catch {
|
|
32205
32614
|
return null;
|
|
@@ -32213,10 +32622,10 @@ function readJsonMetadata(value) {
|
|
|
32213
32622
|
}
|
|
32214
32623
|
function writeJsonOrRemove2(filePath, parsed) {
|
|
32215
32624
|
if (isEmptyJsonValue2(parsed)) {
|
|
32216
|
-
|
|
32625
|
+
fs42.rmSync(filePath, { force: true });
|
|
32217
32626
|
return;
|
|
32218
32627
|
}
|
|
32219
|
-
|
|
32628
|
+
fs42.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
32220
32629
|
`);
|
|
32221
32630
|
}
|
|
32222
32631
|
function isEmptyJsonValue2(value) {
|
|
@@ -32276,7 +32685,7 @@ function removeManagedHookEntries(value, expectedSource) {
|
|
|
32276
32685
|
}
|
|
32277
32686
|
|
|
32278
32687
|
// src/commands/validate.ts
|
|
32279
|
-
import
|
|
32688
|
+
import fs43 from "fs";
|
|
32280
32689
|
import path43 from "path";
|
|
32281
32690
|
var SUPPORTED_KIT_FORMATS2 = [1];
|
|
32282
32691
|
var SUPPORTED_DISCIPLINE_VERSIONS = [
|
|
@@ -32359,6 +32768,8 @@ function validateKit(kitInfo) {
|
|
|
32359
32768
|
violations.push(...checkComponentFrontmatter(kitInfo));
|
|
32360
32769
|
violations.push(...checkReservedRootNames(kitInfo));
|
|
32361
32770
|
violations.push(...checkHumanOnlyCommandInvocation(kitInfo));
|
|
32771
|
+
violations.push(...checkHookDeclarations(kitInfo));
|
|
32772
|
+
collect(violations, checkHooksSourceForm(kitInfo));
|
|
32362
32773
|
return {
|
|
32363
32774
|
conformant: violations.length === 0,
|
|
32364
32775
|
violations,
|
|
@@ -32372,7 +32783,7 @@ function checkSkillSubfolderNames(kit) {
|
|
|
32372
32783
|
const skillDir = path43.join(kit.path, "skills", name);
|
|
32373
32784
|
let entries;
|
|
32374
32785
|
try {
|
|
32375
|
-
entries =
|
|
32786
|
+
entries = fs43.readdirSync(skillDir, { withFileTypes: true });
|
|
32376
32787
|
} catch {
|
|
32377
32788
|
continue;
|
|
32378
32789
|
}
|
|
@@ -32389,6 +32800,28 @@ function checkSkillSubfolderNames(kit) {
|
|
|
32389
32800
|
}
|
|
32390
32801
|
return notices;
|
|
32391
32802
|
}
|
|
32803
|
+
function checkHooksSourceForm(kit) {
|
|
32804
|
+
const hooksDir = path43.join(kit.path, "hooks");
|
|
32805
|
+
const message = describeHooksSourceConflict(
|
|
32806
|
+
hooksDir,
|
|
32807
|
+
kit.components.hooks.declared
|
|
32808
|
+
);
|
|
32809
|
+
return message ? {
|
|
32810
|
+
code: "hooks-source-ambiguous",
|
|
32811
|
+
location: "hooks/",
|
|
32812
|
+
message
|
|
32813
|
+
} : null;
|
|
32814
|
+
}
|
|
32815
|
+
function checkHookDeclarations(kit) {
|
|
32816
|
+
const hooksDir = path43.join(kit.path, "hooks");
|
|
32817
|
+
return collectHookRejections(hooksDir, kit.components.hooks.declared).map(
|
|
32818
|
+
(r) => ({
|
|
32819
|
+
code: "hook-declaration-invalid",
|
|
32820
|
+
location: `hooks/${path43.basename(r.file)}`,
|
|
32821
|
+
message: `hooks: '${path43.basename(r.file, ".md")}' was rejected at '${r.field}' \u2014 ${r.reason}. Until this is fixed the hook is not installed on any target; install and sync report it as an error-severity loss and exit 3. To fix: correct the field named above in this file.`
|
|
32822
|
+
})
|
|
32823
|
+
);
|
|
32824
|
+
}
|
|
32392
32825
|
function collect(violations, v2) {
|
|
32393
32826
|
if (v2) violations.push(v2);
|
|
32394
32827
|
}
|
|
@@ -32403,16 +32836,19 @@ function checkKitFormatSupported(kit) {
|
|
|
32403
32836
|
return null;
|
|
32404
32837
|
}
|
|
32405
32838
|
function checkEntrypointExists(kit) {
|
|
32406
|
-
const
|
|
32839
|
+
const declared = kit.entrypoint;
|
|
32840
|
+
const entrypoint = declared ?? DEFAULT_ENTRYPOINT;
|
|
32407
32841
|
const entrypointPath = path43.join(kit.path, entrypoint);
|
|
32408
|
-
if (
|
|
32409
|
-
return
|
|
32410
|
-
code: "entrypoint-missing",
|
|
32411
|
-
location: entrypoint,
|
|
32412
|
-
message: `entrypoint '${entrypoint}' does not exist at the kit root`
|
|
32413
|
-
};
|
|
32842
|
+
if (fs43.existsSync(entrypointPath)) {
|
|
32843
|
+
return null;
|
|
32414
32844
|
}
|
|
32415
|
-
|
|
32845
|
+
const legacyPresent = declared === void 0 && fs43.existsSync(path43.join(kit.path, LEGACY_ENTRYPOINT));
|
|
32846
|
+
const remedy = legacyPresent ? `This kit has '${LEGACY_ENTRYPOINT}' but not '${DEFAULT_ENTRYPOINT}'. Until now validate looked for '${LEGACY_ENTRYPOINT}' unconditionally; it now reads the manifest's 'entrypoint', defaulting to '${DEFAULT_ENTRYPOINT}' \u2014 the contract's canonical orientation document, of which '${LEGACY_ENTRYPOINT}' is a target translation. To fix: either add "entrypoint: ${LEGACY_ENTRYPOINT}" to tangyr-kit.yaml to keep the current filename, or rename '${LEGACY_ENTRYPOINT}' to '${DEFAULT_ENTRYPOINT}'.` : declared === void 0 ? `No 'entrypoint' is declared in tangyr-kit.yaml, so '${DEFAULT_ENTRYPOINT}' is expected at the kit root. To fix: create it, or declare the file you use with "entrypoint: <filename>".` : `tangyr-kit.yaml declares "entrypoint: ${declared}", and no such file exists at the kit root. To fix: create it, or correct the declaration.`;
|
|
32847
|
+
return {
|
|
32848
|
+
code: "entrypoint-missing",
|
|
32849
|
+
location: entrypoint,
|
|
32850
|
+
message: `entrypoint '${entrypoint}' does not exist at the kit root. ${remedy}`
|
|
32851
|
+
};
|
|
32416
32852
|
}
|
|
32417
32853
|
function checkDisciplineVersionSupported(kit) {
|
|
32418
32854
|
if (!SUPPORTED_DISCIPLINE_VERSIONS.includes(kit.discipline.version)) {
|
|
@@ -32463,7 +32899,7 @@ function checkComponentFrontmatter(kit) {
|
|
|
32463
32899
|
for (const name of kit.components[cat].declared) {
|
|
32464
32900
|
if (missingSet.has(name)) continue;
|
|
32465
32901
|
const filePath = cat === "skills" ? path43.join(kit.path, cat, name, "SKILL.md") : path43.join(kit.path, cat, `${name}.md`);
|
|
32466
|
-
if (!
|
|
32902
|
+
if (!fs43.existsSync(filePath)) continue;
|
|
32467
32903
|
const relPath = path43.relative(kit.path, filePath);
|
|
32468
32904
|
try {
|
|
32469
32905
|
const { data } = parseMarkdownFrontmatter(filePath);
|
|
@@ -32489,10 +32925,10 @@ function checkComponentFrontmatter(kit) {
|
|
|
32489
32925
|
}
|
|
32490
32926
|
function checkReservedRootNames(kit) {
|
|
32491
32927
|
const violations = [];
|
|
32492
|
-
if (!
|
|
32928
|
+
if (!fs43.existsSync(kit.path) || !fs43.statSync(kit.path).isDirectory()) {
|
|
32493
32929
|
return violations;
|
|
32494
32930
|
}
|
|
32495
|
-
const entries =
|
|
32931
|
+
const entries = fs43.readdirSync(kit.path, { withFileTypes: true });
|
|
32496
32932
|
for (const entry of entries) {
|
|
32497
32933
|
if (RESERVED_ROOT_NAMES.includes(entry.name)) {
|
|
32498
32934
|
violations.push({
|
|
@@ -32510,7 +32946,7 @@ function checkHumanOnlyCommandInvocation(kit) {
|
|
|
32510
32946
|
for (const name of kit.components.commands.declared) {
|
|
32511
32947
|
if (missingSet.has(name)) continue;
|
|
32512
32948
|
const filePath = path43.join(kit.path, "commands", `${name}.md`);
|
|
32513
|
-
if (!
|
|
32949
|
+
if (!fs43.existsSync(filePath)) continue;
|
|
32514
32950
|
let data;
|
|
32515
32951
|
try {
|
|
32516
32952
|
({ data } = parseMarkdownFrontmatter(filePath));
|
|
@@ -32532,7 +32968,7 @@ function checkHumanOnlyCommandInvocation(kit) {
|
|
|
32532
32968
|
}
|
|
32533
32969
|
|
|
32534
32970
|
// src/commands/verify.ts
|
|
32535
|
-
import
|
|
32971
|
+
import fs44 from "fs";
|
|
32536
32972
|
import path44 from "path";
|
|
32537
32973
|
function runVerifyCommand(options, logger) {
|
|
32538
32974
|
const { config, configDir, sourceRoot, kitInfo } = resolveRuntime(
|
|
@@ -32625,7 +33061,7 @@ function verifyClaudeCodeGlobalRoots(roots, manifestKeys, mappings) {
|
|
|
32625
33061
|
continue;
|
|
32626
33062
|
}
|
|
32627
33063
|
const fullPath = subPath ? path44.join(slotPath, subPath) : slotPath;
|
|
32628
|
-
if (!
|
|
33064
|
+
if (!fs44.existsSync(fullPath)) {
|
|
32629
33065
|
failures.push(
|
|
32630
33066
|
`claude-code/${slot}${subPath ? `/${subPath}` : ""} missing in root ${root}: ${fullPath}`
|
|
32631
33067
|
);
|
|
@@ -32755,7 +33191,7 @@ function buildProgram() {
|
|
|
32755
33191
|
).option(
|
|
32756
33192
|
"--json",
|
|
32757
33193
|
`Emit {"degraded": true} on stderr only when this resolution fell back to a warm cache after a network failure; otherwise this flag has no effect and output is unchanged \u2014 unlike --json on other commands, which always replaces the command's entire output.`
|
|
32758
|
-
).option("--tools <list>", "Comma-separated list of target tools").option("--scope <scope>", "Installation scope: global or project").option(
|
|
33194
|
+
).option("--tools <list>", "Comma-separated list of target tools").option("--loss-report <path>", "Write semantic loss report JSON to a file").option("--scope <scope>", "Installation scope: global or project").option(
|
|
32759
33195
|
"--ignore-errors",
|
|
32760
33196
|
"Suppress exit code 3 when error-severity loss entries are present"
|
|
32761
33197
|
).action(
|