@przeprogramowani/10x-cli 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +561 -137
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2271,7 +2271,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
2271
2271
|
// package.json
|
|
2272
2272
|
var package_default = {
|
|
2273
2273
|
name: "@przeprogramowani/10x-cli",
|
|
2274
|
-
version: "0.
|
|
2274
|
+
version: "0.6.0",
|
|
2275
2275
|
description: "Open-source CLI for 10xDevs course content",
|
|
2276
2276
|
repository: {
|
|
2277
2277
|
type: "git",
|
|
@@ -2324,6 +2324,9 @@ var package_default = {
|
|
|
2324
2324
|
}
|
|
2325
2325
|
};
|
|
2326
2326
|
|
|
2327
|
+
// node_modules/@clack/prompts/dist/index.mjs
|
|
2328
|
+
import { stripVTControlCharacters as T2 } from "node:util";
|
|
2329
|
+
|
|
2327
2330
|
// node_modules/@clack/core/dist/index.mjs
|
|
2328
2331
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
2329
2332
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
@@ -2833,6 +2836,21 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
2833
2836
|
}
|
|
2834
2837
|
} }).prompt();
|
|
2835
2838
|
};
|
|
2839
|
+
var ye = (s = "", n = "") => {
|
|
2840
|
+
const t = `
|
|
2841
|
+
${s}
|
|
2842
|
+
`.split(`
|
|
2843
|
+
`), i = T2(n).length, r2 = Math.max(t.reduce((o, l2) => {
|
|
2844
|
+
const $2 = T2(l2);
|
|
2845
|
+
return $2.length > o ? $2.length : o;
|
|
2846
|
+
}, 0), i) + 2, c2 = t.map((o) => `${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(o)}${" ".repeat(r2 - T2(o).length)}${import_picocolors2.default.gray(a)}`).join(`
|
|
2847
|
+
`);
|
|
2848
|
+
process.stdout.write(`${import_picocolors2.default.gray(a)}
|
|
2849
|
+
${import_picocolors2.default.green(S2)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(N2.repeat(Math.max(r2 - i - 1, 1)) + re)}
|
|
2850
|
+
${c2}
|
|
2851
|
+
${import_picocolors2.default.gray(ie + N2.repeat(r2 + 2) + ne)}
|
|
2852
|
+
`);
|
|
2853
|
+
};
|
|
2836
2854
|
var ve = (s = "") => {
|
|
2837
2855
|
process.stdout.write(`${import_picocolors2.default.gray(m2)} ${import_picocolors2.default.red(s)}
|
|
2838
2856
|
|
|
@@ -2927,6 +2945,20 @@ function readAuth() {
|
|
|
2927
2945
|
return null;
|
|
2928
2946
|
}
|
|
2929
2947
|
}
|
|
2948
|
+
function writeJsonAtomic(path, data, opts = {}) {
|
|
2949
|
+
const tmp = `${path}.tmp`;
|
|
2950
|
+
rmSync(tmp, { force: true });
|
|
2951
|
+
const body = `${JSON.stringify(data, null, 2)}
|
|
2952
|
+
`;
|
|
2953
|
+
const writeOpts = opts.mode !== undefined ? { mode: opts.mode } : undefined;
|
|
2954
|
+
writeFileSync(tmp, body, writeOpts);
|
|
2955
|
+
if (opts.mode !== undefined && process.platform !== "win32") {
|
|
2956
|
+
try {
|
|
2957
|
+
chmodSync(tmp, opts.mode);
|
|
2958
|
+
} catch {}
|
|
2959
|
+
}
|
|
2960
|
+
renameSync(tmp, path);
|
|
2961
|
+
}
|
|
2930
2962
|
function saveAuth(data) {
|
|
2931
2963
|
const dir = configDir();
|
|
2932
2964
|
if (!existsSync(dir)) {
|
|
@@ -2937,17 +2969,7 @@ function saveAuth(data) {
|
|
|
2937
2969
|
chmodSync(dir, 448);
|
|
2938
2970
|
} catch {}
|
|
2939
2971
|
}
|
|
2940
|
-
|
|
2941
|
-
const tmp = `${file}.tmp`;
|
|
2942
|
-
rmSync(tmp, { force: true });
|
|
2943
|
-
writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
|
|
2944
|
-
`, { mode: 384 });
|
|
2945
|
-
if (process.platform !== "win32") {
|
|
2946
|
-
try {
|
|
2947
|
-
chmodSync(tmp, 384);
|
|
2948
|
-
} catch {}
|
|
2949
|
-
}
|
|
2950
|
-
renameSync(tmp, file);
|
|
2972
|
+
writeJsonAtomic(authFilePath(), data, { mode: 384 });
|
|
2951
2973
|
}
|
|
2952
2974
|
function deleteAuth() {
|
|
2953
2975
|
const file = authFilePath();
|
|
@@ -2967,6 +2989,9 @@ function readToolConfig() {
|
|
|
2967
2989
|
const parsed = JSON.parse(raw);
|
|
2968
2990
|
if (typeof parsed.tool !== "string")
|
|
2969
2991
|
return null;
|
|
2992
|
+
if (parsed.acknowledgedOrphans !== undefined && (!Array.isArray(parsed.acknowledgedOrphans) || !parsed.acknowledgedOrphans.every((x2) => typeof x2 === "string"))) {
|
|
2993
|
+
delete parsed.acknowledgedOrphans;
|
|
2994
|
+
}
|
|
2970
2995
|
return parsed;
|
|
2971
2996
|
} catch {
|
|
2972
2997
|
return null;
|
|
@@ -2977,9 +3002,7 @@ function saveToolConfig(config) {
|
|
|
2977
3002
|
if (!existsSync(dir)) {
|
|
2978
3003
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2979
3004
|
}
|
|
2980
|
-
|
|
2981
|
-
writeFileSync(file, `${JSON.stringify(config, null, 2)}
|
|
2982
|
-
`);
|
|
3005
|
+
writeJsonAtomic(toolConfigPath(), config);
|
|
2983
3006
|
}
|
|
2984
3007
|
|
|
2985
3008
|
// src/lib/api-client.ts
|
|
@@ -3917,7 +3940,71 @@ function parseModuleRef(raw) {
|
|
|
3917
3940
|
return module;
|
|
3918
3941
|
}
|
|
3919
3942
|
|
|
3943
|
+
// src/lib/tool-detect.ts
|
|
3944
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
3945
|
+
import { join as join4 } from "node:path";
|
|
3946
|
+
|
|
3947
|
+
// src/lib/manifest.ts
|
|
3948
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3949
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
3950
|
+
var MANIFEST_FILENAME = ".10x-cli-manifest.json";
|
|
3951
|
+
var CLI_PACKAGE_NAME = "@przeprogramowani/10x-cli";
|
|
3952
|
+
function readManifest(dir) {
|
|
3953
|
+
const manifestPath = join3(dir, MANIFEST_FILENAME);
|
|
3954
|
+
if (!existsSync3(manifestPath))
|
|
3955
|
+
return null;
|
|
3956
|
+
let raw;
|
|
3957
|
+
try {
|
|
3958
|
+
raw = readFileSync2(manifestPath, "utf8");
|
|
3959
|
+
} catch {
|
|
3960
|
+
return null;
|
|
3961
|
+
}
|
|
3962
|
+
let parsed;
|
|
3963
|
+
try {
|
|
3964
|
+
parsed = JSON.parse(raw);
|
|
3965
|
+
} catch {
|
|
3966
|
+
return null;
|
|
3967
|
+
}
|
|
3968
|
+
if (!isManifest(parsed))
|
|
3969
|
+
return null;
|
|
3970
|
+
return parsed;
|
|
3971
|
+
}
|
|
3972
|
+
function writeManifest(dir, manifest) {
|
|
3973
|
+
if (!existsSync3(dir)) {
|
|
3974
|
+
mkdirSync3(dir, { recursive: true });
|
|
3975
|
+
}
|
|
3976
|
+
const manifestPath = join3(dir, MANIFEST_FILENAME);
|
|
3977
|
+
mkdirSync3(dirname2(manifestPath), { recursive: true });
|
|
3978
|
+
writeFileSync2(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
3979
|
+
`);
|
|
3980
|
+
}
|
|
3981
|
+
function isManifest(value) {
|
|
3982
|
+
if (typeof value !== "object" || value === null)
|
|
3983
|
+
return false;
|
|
3984
|
+
const v2 = value;
|
|
3985
|
+
if (v2["package"] !== CLI_PACKAGE_NAME)
|
|
3986
|
+
return false;
|
|
3987
|
+
if (typeof v2["version"] !== "string")
|
|
3988
|
+
return false;
|
|
3989
|
+
if (typeof v2["lastApplied"] !== "string")
|
|
3990
|
+
return false;
|
|
3991
|
+
if (typeof v2["lessonId"] !== "string")
|
|
3992
|
+
return false;
|
|
3993
|
+
if (typeof v2["course"] !== "string")
|
|
3994
|
+
return false;
|
|
3995
|
+
const files = v2["files"];
|
|
3996
|
+
if (typeof files !== "object" || files === null)
|
|
3997
|
+
return false;
|
|
3998
|
+
const f2 = files;
|
|
3999
|
+
return isStringArray(f2["skills"]) && isStringArray(f2["prompts"]) && isStringArray(f2["configs"]);
|
|
4000
|
+
}
|
|
4001
|
+
function isStringArray(value) {
|
|
4002
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
4003
|
+
}
|
|
4004
|
+
|
|
3920
4005
|
// src/lib/tool-profile.ts
|
|
4006
|
+
var SENTINEL_BEGIN = "<!-- BEGIN @przeprogramowani/10x-cli -->";
|
|
4007
|
+
var SENTINEL_END = "<!-- END @przeprogramowani/10x-cli -->";
|
|
3921
4008
|
var PROFILES = {
|
|
3922
4009
|
"claude-code": {
|
|
3923
4010
|
toolId: "claude-code",
|
|
@@ -3927,8 +4014,8 @@ var PROFILES = {
|
|
|
3927
4014
|
configPath: (n) => `.claude/config-templates/${n}`,
|
|
3928
4015
|
rulesFile: "CLAUDE.md",
|
|
3929
4016
|
manifestDir: ".claude",
|
|
3930
|
-
sentinelBegin:
|
|
3931
|
-
sentinelEnd:
|
|
4017
|
+
sentinelBegin: SENTINEL_BEGIN,
|
|
4018
|
+
sentinelEnd: SENTINEL_END
|
|
3932
4019
|
},
|
|
3933
4020
|
cursor: {
|
|
3934
4021
|
toolId: "cursor",
|
|
@@ -3938,8 +4025,8 @@ var PROFILES = {
|
|
|
3938
4025
|
configPath: (n) => `.cursor/config-templates/${n}`,
|
|
3939
4026
|
rulesFile: ".cursor/rules/10x-course.mdc",
|
|
3940
4027
|
manifestDir: ".cursor",
|
|
3941
|
-
sentinelBegin:
|
|
3942
|
-
sentinelEnd:
|
|
4028
|
+
sentinelBegin: SENTINEL_BEGIN,
|
|
4029
|
+
sentinelEnd: SENTINEL_END
|
|
3943
4030
|
},
|
|
3944
4031
|
copilot: {
|
|
3945
4032
|
toolId: "copilot",
|
|
@@ -3949,8 +4036,8 @@ var PROFILES = {
|
|
|
3949
4036
|
configPath: (n) => `.github/config-templates/${n}`,
|
|
3950
4037
|
rulesFile: ".github/copilot-instructions.md",
|
|
3951
4038
|
manifestDir: ".github",
|
|
3952
|
-
sentinelBegin:
|
|
3953
|
-
sentinelEnd:
|
|
4039
|
+
sentinelBegin: SENTINEL_BEGIN,
|
|
4040
|
+
sentinelEnd: SENTINEL_END
|
|
3954
4041
|
},
|
|
3955
4042
|
codex: {
|
|
3956
4043
|
toolId: "codex",
|
|
@@ -3960,8 +4047,8 @@ var PROFILES = {
|
|
|
3960
4047
|
configPath: (n) => `.agents/config-templates/${n}`,
|
|
3961
4048
|
rulesFile: "AGENTS.md",
|
|
3962
4049
|
manifestDir: ".agents",
|
|
3963
|
-
sentinelBegin:
|
|
3964
|
-
sentinelEnd:
|
|
4050
|
+
sentinelBegin: SENTINEL_BEGIN,
|
|
4051
|
+
sentinelEnd: SENTINEL_END
|
|
3965
4052
|
},
|
|
3966
4053
|
generic: {
|
|
3967
4054
|
toolId: "generic",
|
|
@@ -3971,116 +4058,143 @@ var PROFILES = {
|
|
|
3971
4058
|
configPath: (n) => `.ai/config-templates/${n}`,
|
|
3972
4059
|
rulesFile: "AGENTS.md",
|
|
3973
4060
|
manifestDir: ".ai",
|
|
3974
|
-
sentinelBegin:
|
|
3975
|
-
sentinelEnd:
|
|
4061
|
+
sentinelBegin: SENTINEL_BEGIN,
|
|
4062
|
+
sentinelEnd: SENTINEL_END
|
|
3976
4063
|
}
|
|
3977
4064
|
};
|
|
3978
4065
|
var DEFAULT_TOOL = "claude-code";
|
|
3979
4066
|
|
|
3980
|
-
// src/lib/tool-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
if (
|
|
3991
|
-
|
|
4067
|
+
// src/lib/tool-detect.ts
|
|
4068
|
+
function detectTools(projectRoot) {
|
|
4069
|
+
const hit = (rel) => existsSync4(join4(projectRoot, rel));
|
|
4070
|
+
const signals = [];
|
|
4071
|
+
if (hit(".claude/" + MANIFEST_FILENAME)) {
|
|
4072
|
+
signals.push({
|
|
4073
|
+
profileId: "claude-code",
|
|
4074
|
+
confidence: "strong",
|
|
4075
|
+
reason: ".claude/.10x-cli-manifest.json"
|
|
4076
|
+
});
|
|
4077
|
+
} else if (hit(".claude")) {
|
|
4078
|
+
signals.push({ profileId: "claude-code", confidence: "medium", reason: ".claude/ directory" });
|
|
4079
|
+
} else if (hit("CLAUDE.md")) {
|
|
4080
|
+
signals.push({ profileId: "claude-code", confidence: "weak", reason: "CLAUDE.md" });
|
|
4081
|
+
}
|
|
4082
|
+
if (hit(".cursor/" + MANIFEST_FILENAME)) {
|
|
4083
|
+
signals.push({
|
|
4084
|
+
profileId: "cursor",
|
|
4085
|
+
confidence: "strong",
|
|
4086
|
+
reason: ".cursor/.10x-cli-manifest.json"
|
|
4087
|
+
});
|
|
4088
|
+
} else if (hit(".cursor/rules")) {
|
|
4089
|
+
signals.push({ profileId: "cursor", confidence: "strong", reason: ".cursor/rules/" });
|
|
4090
|
+
} else if (hit(".cursor")) {
|
|
4091
|
+
signals.push({ profileId: "cursor", confidence: "medium", reason: ".cursor/ directory" });
|
|
4092
|
+
}
|
|
4093
|
+
if (hit(".github/" + MANIFEST_FILENAME)) {
|
|
4094
|
+
signals.push({
|
|
4095
|
+
profileId: "copilot",
|
|
4096
|
+
confidence: "strong",
|
|
4097
|
+
reason: ".github/.10x-cli-manifest.json"
|
|
4098
|
+
});
|
|
4099
|
+
} else if (hit(".github/copilot-instructions.md")) {
|
|
4100
|
+
signals.push({
|
|
4101
|
+
profileId: "copilot",
|
|
4102
|
+
confidence: "strong",
|
|
4103
|
+
reason: ".github/copilot-instructions.md"
|
|
4104
|
+
});
|
|
4105
|
+
} else if (hit(".github/skills") || hit(".github/prompts")) {
|
|
4106
|
+
signals.push({
|
|
4107
|
+
profileId: "copilot",
|
|
4108
|
+
confidence: "medium",
|
|
4109
|
+
reason: ".github/skills/ or .github/prompts/"
|
|
4110
|
+
});
|
|
3992
4111
|
}
|
|
3993
|
-
if (
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
label: profile.displayName,
|
|
3999
|
-
hint: profile.toolId === DEFAULT_TOOL ? "default" : undefined
|
|
4000
|
-
})),
|
|
4001
|
-
initialValue: DEFAULT_TOOL
|
|
4112
|
+
if (hit(".agents/" + MANIFEST_FILENAME)) {
|
|
4113
|
+
signals.push({
|
|
4114
|
+
profileId: "codex",
|
|
4115
|
+
confidence: "strong",
|
|
4116
|
+
reason: ".agents/.10x-cli-manifest.json"
|
|
4002
4117
|
});
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
}
|
|
4007
|
-
|
|
4008
|
-
|
|
4118
|
+
} else if (hit(".agents")) {
|
|
4119
|
+
signals.push({ profileId: "codex", confidence: "strong", reason: ".agents/ directory" });
|
|
4120
|
+
} else if (hit("AGENTS.md") && !hit(".ai")) {
|
|
4121
|
+
signals.push({ profileId: "codex", confidence: "medium", reason: "AGENTS.md" });
|
|
4122
|
+
}
|
|
4123
|
+
if (hit(".ai/" + MANIFEST_FILENAME)) {
|
|
4124
|
+
signals.push({
|
|
4125
|
+
profileId: "generic",
|
|
4126
|
+
confidence: "strong",
|
|
4127
|
+
reason: ".ai/.10x-cli-manifest.json"
|
|
4128
|
+
});
|
|
4129
|
+
} else if (hit(".ai")) {
|
|
4130
|
+
signals.push({ profileId: "generic", confidence: "strong", reason: ".ai/ directory" });
|
|
4131
|
+
} else if (hit("AGENTS.md") && !hit(".agents")) {
|
|
4132
|
+
signals.push({ profileId: "generic", confidence: "weak", reason: "AGENTS.md (generic fallback)" });
|
|
4009
4133
|
}
|
|
4010
|
-
return
|
|
4134
|
+
return rankSignals(signals);
|
|
4135
|
+
}
|
|
4136
|
+
var CONFIDENCE_ORDER = { strong: 3, medium: 2, weak: 1 };
|
|
4137
|
+
var PROFILE_ORDER = ["claude-code", "cursor", "copilot", "codex", "generic"];
|
|
4138
|
+
function rankSignals(signals) {
|
|
4139
|
+
return [...signals].sort((a2, b2) => {
|
|
4140
|
+
const c2 = CONFIDENCE_ORDER[b2.confidence] - CONFIDENCE_ORDER[a2.confidence];
|
|
4141
|
+
if (c2 !== 0)
|
|
4142
|
+
return c2;
|
|
4143
|
+
return PROFILE_ORDER.indexOf(a2.profileId) - PROFILE_ORDER.indexOf(b2.profileId);
|
|
4144
|
+
});
|
|
4145
|
+
}
|
|
4146
|
+
function topDetectedProfile(signals) {
|
|
4147
|
+
const top = signals[0];
|
|
4148
|
+
if (!top)
|
|
4149
|
+
return null;
|
|
4150
|
+
return PROFILES[top.profileId] ?? null;
|
|
4011
4151
|
}
|
|
4012
4152
|
|
|
4013
|
-
// src/lib/
|
|
4153
|
+
// src/lib/tool-switch.ts
|
|
4014
4154
|
import {
|
|
4015
|
-
existsSync as
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4155
|
+
existsSync as existsSync6,
|
|
4156
|
+
lstatSync,
|
|
4157
|
+
mkdirSync as mkdirSync5,
|
|
4158
|
+
readFileSync as readFileSync5,
|
|
4159
|
+
renameSync as renameSync2,
|
|
4160
|
+
rmdirSync,
|
|
4161
|
+
rmSync as rmSync3,
|
|
4162
|
+
statSync,
|
|
4163
|
+
writeFileSync as writeFileSync4
|
|
4020
4164
|
} from "node:fs";
|
|
4021
|
-
import { dirname as
|
|
4165
|
+
import { dirname as dirname4, join as join6 } from "node:path";
|
|
4022
4166
|
|
|
4023
|
-
// src/lib/
|
|
4024
|
-
import {
|
|
4025
|
-
|
|
4026
|
-
var MANIFEST_FILENAME = ".10x-cli-manifest.json";
|
|
4027
|
-
var CLI_PACKAGE_NAME = "@przeprogramowani/10x-cli";
|
|
4028
|
-
function readManifest(dir) {
|
|
4029
|
-
const manifestPath = join3(dir, MANIFEST_FILENAME);
|
|
4030
|
-
if (!existsSync3(manifestPath))
|
|
4031
|
-
return null;
|
|
4032
|
-
let raw;
|
|
4167
|
+
// src/lib/fs-utils.ts
|
|
4168
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
4169
|
+
function readFileOrNull(path) {
|
|
4033
4170
|
try {
|
|
4034
|
-
|
|
4171
|
+
return readFileSync3(path, "utf8");
|
|
4035
4172
|
} catch {
|
|
4036
4173
|
return null;
|
|
4037
4174
|
}
|
|
4038
|
-
let parsed;
|
|
4039
|
-
try {
|
|
4040
|
-
parsed = JSON.parse(raw);
|
|
4041
|
-
} catch {
|
|
4042
|
-
return null;
|
|
4043
|
-
}
|
|
4044
|
-
if (!isManifest(parsed))
|
|
4045
|
-
return null;
|
|
4046
|
-
return parsed;
|
|
4047
|
-
}
|
|
4048
|
-
function writeManifest(dir, manifest) {
|
|
4049
|
-
if (!existsSync3(dir)) {
|
|
4050
|
-
mkdirSync3(dir, { recursive: true });
|
|
4051
|
-
}
|
|
4052
|
-
const manifestPath = join3(dir, MANIFEST_FILENAME);
|
|
4053
|
-
mkdirSync3(dirname2(manifestPath), { recursive: true });
|
|
4054
|
-
writeFileSync2(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
4055
|
-
`);
|
|
4056
|
-
}
|
|
4057
|
-
function isManifest(value) {
|
|
4058
|
-
if (typeof value !== "object" || value === null)
|
|
4059
|
-
return false;
|
|
4060
|
-
const v2 = value;
|
|
4061
|
-
if (v2["package"] !== CLI_PACKAGE_NAME)
|
|
4062
|
-
return false;
|
|
4063
|
-
if (typeof v2["version"] !== "string")
|
|
4064
|
-
return false;
|
|
4065
|
-
if (typeof v2["lastApplied"] !== "string")
|
|
4066
|
-
return false;
|
|
4067
|
-
if (typeof v2["lessonId"] !== "string")
|
|
4068
|
-
return false;
|
|
4069
|
-
if (typeof v2["course"] !== "string")
|
|
4070
|
-
return false;
|
|
4071
|
-
const files = v2["files"];
|
|
4072
|
-
if (typeof files !== "object" || files === null)
|
|
4073
|
-
return false;
|
|
4074
|
-
const f2 = files;
|
|
4075
|
-
return isStringArray(f2["skills"]) && isStringArray(f2["prompts"]) && isStringArray(f2["configs"]);
|
|
4076
|
-
}
|
|
4077
|
-
function isStringArray(value) {
|
|
4078
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
4079
4175
|
}
|
|
4080
4176
|
|
|
4081
4177
|
// src/lib/sentinel-migration.ts
|
|
4082
4178
|
var OLD_BEGIN = "<!-- BEGIN @przeprogramowani/10x-toolkit -->";
|
|
4083
4179
|
var OLD_END = "<!-- END @przeprogramowani/10x-toolkit -->";
|
|
4180
|
+
function removeRulesBlockWithMarkers(existing, begin, end) {
|
|
4181
|
+
const beginIdx = existing.indexOf(begin);
|
|
4182
|
+
const endIdx = existing.indexOf(end);
|
|
4183
|
+
if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) {
|
|
4184
|
+
return { content: existing, removed: false };
|
|
4185
|
+
}
|
|
4186
|
+
const beforeRaw = existing.slice(0, beginIdx);
|
|
4187
|
+
const afterRaw = existing.slice(endIdx + end.length);
|
|
4188
|
+
const before = beforeRaw.replace(/[\r\n]*$/, "");
|
|
4189
|
+
const after = afterRaw.replace(/^[\r\n]*/, "");
|
|
4190
|
+
const joiner = before.length > 0 && after.length > 0 ? `
|
|
4191
|
+
|
|
4192
|
+
` : "";
|
|
4193
|
+
const combined = before + joiner + after;
|
|
4194
|
+
const content = combined.length === 0 ? "" : combined.replace(/[\r\n]*$/, `
|
|
4195
|
+
`);
|
|
4196
|
+
return { content, removed: true };
|
|
4197
|
+
}
|
|
4084
4198
|
function applyRulesBlockWithMarkers(existingContent, rulesBody, beginMarker, endMarker) {
|
|
4085
4199
|
for (const marker of [OLD_BEGIN, OLD_END, beginMarker, endMarker]) {
|
|
4086
4200
|
if (rulesBody.includes(marker)) {
|
|
@@ -4128,6 +4242,14 @@ function stripBlock(content, begin, end, label, warnings) {
|
|
|
4128
4242
|
}
|
|
4129
4243
|
|
|
4130
4244
|
// src/lib/writer.ts
|
|
4245
|
+
import {
|
|
4246
|
+
existsSync as existsSync5,
|
|
4247
|
+
mkdirSync as mkdirSync4,
|
|
4248
|
+
readFileSync as readFileSync4,
|
|
4249
|
+
rmSync as rmSync2,
|
|
4250
|
+
writeFileSync as writeFileSync3
|
|
4251
|
+
} from "node:fs";
|
|
4252
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
4131
4253
|
var CLI_VERSION = package_default.version;
|
|
4132
4254
|
var DEFAULT_COURSE = "10xdevs3";
|
|
4133
4255
|
function applyBundle(bundle, projectRoot, options = {}) {
|
|
@@ -4135,7 +4257,7 @@ function applyBundle(bundle, projectRoot, options = {}) {
|
|
|
4135
4257
|
const partial = options.partial === true;
|
|
4136
4258
|
const course = options.course ?? DEFAULT_COURSE;
|
|
4137
4259
|
const profile = options.profile ?? PROFILES[DEFAULT_TOOL];
|
|
4138
|
-
const manifestDir =
|
|
4260
|
+
const manifestDir = join5(projectRoot, profile.manifestDir);
|
|
4139
4261
|
const prevManifest = readManifest(manifestDir);
|
|
4140
4262
|
for (const skill of bundle.skills)
|
|
4141
4263
|
assertSafeName(skill.name, "skill");
|
|
@@ -4144,7 +4266,7 @@ function applyBundle(bundle, projectRoot, options = {}) {
|
|
|
4144
4266
|
for (const config of bundle.configs)
|
|
4145
4267
|
assertSafeName(config.name, "config");
|
|
4146
4268
|
const skills = bundle.skills.map((skill) => {
|
|
4147
|
-
const target =
|
|
4269
|
+
const target = join5(projectRoot, profile.skillPath(skill.name));
|
|
4148
4270
|
const action = computeFileAction(target, skill.content);
|
|
4149
4271
|
if (!dryRun && action !== "unchanged") {
|
|
4150
4272
|
writeFileAt(target, skill.content);
|
|
@@ -4152,14 +4274,14 @@ function applyBundle(bundle, projectRoot, options = {}) {
|
|
|
4152
4274
|
return { name: skill.name, path: target, action };
|
|
4153
4275
|
});
|
|
4154
4276
|
const prompts = bundle.prompts.map((prompt) => {
|
|
4155
|
-
const target =
|
|
4277
|
+
const target = join5(projectRoot, profile.promptPath(prompt.name));
|
|
4156
4278
|
const action = computeFileAction(target, prompt.content);
|
|
4157
4279
|
if (!dryRun && action !== "unchanged") {
|
|
4158
4280
|
writeFileAt(target, prompt.content);
|
|
4159
4281
|
}
|
|
4160
4282
|
return { name: prompt.name, path: target, action };
|
|
4161
4283
|
});
|
|
4162
|
-
const rulesFilePath =
|
|
4284
|
+
const rulesFilePath = join5(projectRoot, profile.rulesFile);
|
|
4163
4285
|
const existingRules = readFileOrEmpty(rulesFilePath);
|
|
4164
4286
|
let rulesAction;
|
|
4165
4287
|
if (bundle.rules.length === 0) {
|
|
@@ -4181,8 +4303,8 @@ function applyBundle(bundle, projectRoot, options = {}) {
|
|
|
4181
4303
|
}
|
|
4182
4304
|
}
|
|
4183
4305
|
const configs = bundle.configs.map((config) => {
|
|
4184
|
-
const target =
|
|
4185
|
-
const action =
|
|
4306
|
+
const target = join5(projectRoot, profile.configPath(config.name));
|
|
4307
|
+
const action = existsSync5(target) ? "skipped" : "created";
|
|
4186
4308
|
if (!dryRun && action === "created") {
|
|
4187
4309
|
writeFileAt(target, config.content);
|
|
4188
4310
|
}
|
|
@@ -4221,21 +4343,21 @@ function applyBundle(bundle, projectRoot, options = {}) {
|
|
|
4221
4343
|
};
|
|
4222
4344
|
}
|
|
4223
4345
|
function computeFileAction(filePath, newContent) {
|
|
4224
|
-
if (!
|
|
4346
|
+
if (!existsSync5(filePath))
|
|
4225
4347
|
return "created";
|
|
4226
4348
|
let current;
|
|
4227
4349
|
try {
|
|
4228
|
-
current =
|
|
4350
|
+
current = readFileSync4(filePath, "utf8");
|
|
4229
4351
|
} catch {
|
|
4230
4352
|
return "updated";
|
|
4231
4353
|
}
|
|
4232
4354
|
return current === newContent ? "unchanged" : "updated";
|
|
4233
4355
|
}
|
|
4234
4356
|
function readFileOrEmpty(filePath) {
|
|
4235
|
-
if (!
|
|
4357
|
+
if (!existsSync5(filePath))
|
|
4236
4358
|
return "";
|
|
4237
4359
|
try {
|
|
4238
|
-
return
|
|
4360
|
+
return readFileSync4(filePath, "utf8");
|
|
4239
4361
|
} catch {
|
|
4240
4362
|
return "";
|
|
4241
4363
|
}
|
|
@@ -4257,7 +4379,7 @@ function computeRemovals(prevManifest, bundle, profile, projectRoot) {
|
|
|
4257
4379
|
continue;
|
|
4258
4380
|
if (!isSafeName(skillName))
|
|
4259
4381
|
continue;
|
|
4260
|
-
const skillTarget =
|
|
4382
|
+
const skillTarget = join5(projectRoot, profile.skillPath(skillName));
|
|
4261
4383
|
removed.skills.push({ name: skillName, path: dirname3(skillTarget) });
|
|
4262
4384
|
}
|
|
4263
4385
|
for (const promptFile of prevManifest.files.prompts) {
|
|
@@ -4268,7 +4390,7 @@ function computeRemovals(prevManifest, bundle, profile, projectRoot) {
|
|
|
4268
4390
|
const promptName = promptFile.replace(/\.md$/, "");
|
|
4269
4391
|
removed.prompts.push({
|
|
4270
4392
|
name: promptFile,
|
|
4271
|
-
path:
|
|
4393
|
+
path: join5(projectRoot, profile.promptPath(promptName))
|
|
4272
4394
|
});
|
|
4273
4395
|
}
|
|
4274
4396
|
for (const configFile of prevManifest.files.configs) {
|
|
@@ -4278,23 +4400,33 @@ function computeRemovals(prevManifest, bundle, profile, projectRoot) {
|
|
|
4278
4400
|
continue;
|
|
4279
4401
|
removed.configs.push({
|
|
4280
4402
|
name: configFile,
|
|
4281
|
-
path:
|
|
4403
|
+
path: join5(projectRoot, profile.configPath(configFile))
|
|
4282
4404
|
});
|
|
4283
4405
|
}
|
|
4284
4406
|
return removed;
|
|
4285
4407
|
}
|
|
4286
|
-
function
|
|
4408
|
+
function findOrphanedManifests(projectRoot, currentProfile) {
|
|
4409
|
+
const out = [];
|
|
4287
4410
|
for (const profile of Object.values(PROFILES)) {
|
|
4288
4411
|
if (profile.toolId === currentProfile.toolId)
|
|
4289
4412
|
continue;
|
|
4290
|
-
const
|
|
4291
|
-
if (
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4413
|
+
const manifestPath = join5(projectRoot, profile.manifestDir, MANIFEST_FILENAME);
|
|
4414
|
+
if (!existsSync5(manifestPath))
|
|
4415
|
+
continue;
|
|
4416
|
+
const manifest = readManifest(join5(projectRoot, profile.manifestDir));
|
|
4417
|
+
if (!manifest)
|
|
4418
|
+
continue;
|
|
4419
|
+
out.push({ profile, manifestPath, manifest });
|
|
4296
4420
|
}
|
|
4297
|
-
return
|
|
4421
|
+
return out;
|
|
4422
|
+
}
|
|
4423
|
+
function detectOrphanedArtifacts(projectRoot, currentProfile) {
|
|
4424
|
+
const first = findOrphanedManifests(projectRoot, currentProfile)[0];
|
|
4425
|
+
if (!first)
|
|
4426
|
+
return null;
|
|
4427
|
+
return `Found existing 10x artifacts in ${first.profile.manifestDir}/ from ${first.profile.displayName}.
|
|
4428
|
+
Manually remove ${first.profile.manifestDir}/ if you no longer need it.
|
|
4429
|
+
Your new artifacts will be written to ${currentProfile.manifestDir}/`;
|
|
4298
4430
|
}
|
|
4299
4431
|
function assertSafeName(name, kind) {
|
|
4300
4432
|
if (!isSafeName(name)) {
|
|
@@ -4322,6 +4454,296 @@ function isSafeName(name) {
|
|
|
4322
4454
|
return true;
|
|
4323
4455
|
}
|
|
4324
4456
|
|
|
4457
|
+
// src/lib/tool-switch.ts
|
|
4458
|
+
function migrateArtifacts(projectRoot, orphan, newProfile) {
|
|
4459
|
+
const summary = {
|
|
4460
|
+
action: "migrated",
|
|
4461
|
+
oldToolId: orphan.profile.toolId,
|
|
4462
|
+
movedOrRemoved: { skills: [], prompts: [], configs: [] },
|
|
4463
|
+
sentinelStripped: false,
|
|
4464
|
+
skipped: []
|
|
4465
|
+
};
|
|
4466
|
+
const oldProfile = orphan.profile;
|
|
4467
|
+
for (const skillName of orphan.manifest.files.skills) {
|
|
4468
|
+
if (!isSafeName(skillName)) {
|
|
4469
|
+
summary.skipped.push({ path: skillName, reason: "unsafe name in manifest" });
|
|
4470
|
+
continue;
|
|
4471
|
+
}
|
|
4472
|
+
const from = join6(projectRoot, oldProfile.skillPath(skillName));
|
|
4473
|
+
const to = join6(projectRoot, newProfile.skillPath(skillName));
|
|
4474
|
+
if (moveIfSafe(from, to, summary.skipped)) {
|
|
4475
|
+
summary.movedOrRemoved.skills.push(skillName);
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
for (const promptFile of orphan.manifest.files.prompts) {
|
|
4479
|
+
if (!isSafeName(promptFile)) {
|
|
4480
|
+
summary.skipped.push({ path: promptFile, reason: "unsafe name in manifest" });
|
|
4481
|
+
continue;
|
|
4482
|
+
}
|
|
4483
|
+
const promptName = promptFile.replace(/\.md$/, "");
|
|
4484
|
+
const from = join6(projectRoot, oldProfile.promptPath(promptName));
|
|
4485
|
+
const to = join6(projectRoot, newProfile.promptPath(promptName));
|
|
4486
|
+
if (moveIfSafe(from, to, summary.skipped)) {
|
|
4487
|
+
summary.movedOrRemoved.prompts.push(promptFile);
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
for (const configFile of orphan.manifest.files.configs) {
|
|
4491
|
+
if (!isSafeName(configFile)) {
|
|
4492
|
+
summary.skipped.push({ path: configFile, reason: "unsafe name in manifest" });
|
|
4493
|
+
continue;
|
|
4494
|
+
}
|
|
4495
|
+
const from = join6(projectRoot, oldProfile.configPath(configFile));
|
|
4496
|
+
const to = join6(projectRoot, newProfile.configPath(configFile));
|
|
4497
|
+
if (moveIfSafe(from, to, summary.skipped)) {
|
|
4498
|
+
summary.movedOrRemoved.configs.push(configFile);
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
summary.sentinelStripped = stripSentinelFromRulesFile(projectRoot, oldProfile);
|
|
4502
|
+
rmSync3(orphan.manifestPath, { force: true });
|
|
4503
|
+
tryRemoveEmptyDir(join6(projectRoot, oldProfile.manifestDir));
|
|
4504
|
+
return summary;
|
|
4505
|
+
}
|
|
4506
|
+
function deleteArtifacts(projectRoot, orphan) {
|
|
4507
|
+
const summary = {
|
|
4508
|
+
action: "deleted",
|
|
4509
|
+
oldToolId: orphan.profile.toolId,
|
|
4510
|
+
movedOrRemoved: { skills: [], prompts: [], configs: [] },
|
|
4511
|
+
sentinelStripped: false,
|
|
4512
|
+
skipped: []
|
|
4513
|
+
};
|
|
4514
|
+
const oldProfile = orphan.profile;
|
|
4515
|
+
for (const skillName of orphan.manifest.files.skills) {
|
|
4516
|
+
if (!isSafeName(skillName)) {
|
|
4517
|
+
summary.skipped.push({ path: skillName, reason: "unsafe name in manifest" });
|
|
4518
|
+
continue;
|
|
4519
|
+
}
|
|
4520
|
+
const skillFile = join6(projectRoot, oldProfile.skillPath(skillName));
|
|
4521
|
+
const skillDir = dirname4(skillFile);
|
|
4522
|
+
rmSync3(skillFile, { force: true });
|
|
4523
|
+
tryRemoveEmptyDir(skillDir);
|
|
4524
|
+
summary.movedOrRemoved.skills.push(skillName);
|
|
4525
|
+
}
|
|
4526
|
+
for (const promptFile of orphan.manifest.files.prompts) {
|
|
4527
|
+
if (!isSafeName(promptFile)) {
|
|
4528
|
+
summary.skipped.push({ path: promptFile, reason: "unsafe name in manifest" });
|
|
4529
|
+
continue;
|
|
4530
|
+
}
|
|
4531
|
+
const promptName = promptFile.replace(/\.md$/, "");
|
|
4532
|
+
rmSync3(join6(projectRoot, oldProfile.promptPath(promptName)), { force: true });
|
|
4533
|
+
summary.movedOrRemoved.prompts.push(promptFile);
|
|
4534
|
+
}
|
|
4535
|
+
for (const configFile of orphan.manifest.files.configs) {
|
|
4536
|
+
if (!isSafeName(configFile)) {
|
|
4537
|
+
summary.skipped.push({ path: configFile, reason: "unsafe name in manifest" });
|
|
4538
|
+
continue;
|
|
4539
|
+
}
|
|
4540
|
+
rmSync3(join6(projectRoot, oldProfile.configPath(configFile)), { force: true });
|
|
4541
|
+
summary.movedOrRemoved.configs.push(configFile);
|
|
4542
|
+
}
|
|
4543
|
+
summary.sentinelStripped = stripSentinelFromRulesFile(projectRoot, oldProfile);
|
|
4544
|
+
rmSync3(orphan.manifestPath, { force: true });
|
|
4545
|
+
const manifestDirAbs = join6(projectRoot, oldProfile.manifestDir);
|
|
4546
|
+
tryRemoveEmptyDir(join6(manifestDirAbs, "skills"));
|
|
4547
|
+
tryRemoveEmptyDir(join6(manifestDirAbs, "prompts"));
|
|
4548
|
+
tryRemoveEmptyDir(join6(manifestDirAbs, "config-templates"));
|
|
4549
|
+
tryRemoveEmptyDir(manifestDirAbs);
|
|
4550
|
+
return summary;
|
|
4551
|
+
}
|
|
4552
|
+
function moveIfSafe(from, to, skipped) {
|
|
4553
|
+
if (!existsSync6(from)) {
|
|
4554
|
+
return false;
|
|
4555
|
+
}
|
|
4556
|
+
if (lstatSync(from).isSymbolicLink()) {
|
|
4557
|
+
skipped.push({ path: from, reason: "source is a symlink" });
|
|
4558
|
+
return false;
|
|
4559
|
+
}
|
|
4560
|
+
if (existsSync6(to)) {
|
|
4561
|
+
if (!filesAreIdentical(from, to)) {
|
|
4562
|
+
skipped.push({
|
|
4563
|
+
path: to,
|
|
4564
|
+
reason: "destination already exists with different content"
|
|
4565
|
+
});
|
|
4566
|
+
return false;
|
|
4567
|
+
}
|
|
4568
|
+
rmSync3(from, { force: true });
|
|
4569
|
+
return true;
|
|
4570
|
+
}
|
|
4571
|
+
mkdirSync5(dirname4(to), { recursive: true });
|
|
4572
|
+
try {
|
|
4573
|
+
renameSync2(from, to);
|
|
4574
|
+
} catch (err) {
|
|
4575
|
+
const code = err?.code;
|
|
4576
|
+
if (code !== "EXDEV")
|
|
4577
|
+
throw err;
|
|
4578
|
+
const bytes = readFileSync5(from);
|
|
4579
|
+
const tmp = `${to}.tmp`;
|
|
4580
|
+
writeFileSync4(tmp, bytes);
|
|
4581
|
+
renameSync2(tmp, to);
|
|
4582
|
+
try {
|
|
4583
|
+
rmSync3(from, { force: true });
|
|
4584
|
+
} catch {
|
|
4585
|
+
skipped.push({
|
|
4586
|
+
path: from,
|
|
4587
|
+
reason: "copied to destination but could not remove source"
|
|
4588
|
+
});
|
|
4589
|
+
return true;
|
|
4590
|
+
}
|
|
4591
|
+
}
|
|
4592
|
+
return true;
|
|
4593
|
+
}
|
|
4594
|
+
function stripSentinelFromRulesFile(projectRoot, oldProfile) {
|
|
4595
|
+
const rulesPath = join6(projectRoot, oldProfile.rulesFile);
|
|
4596
|
+
if (!existsSync6(rulesPath))
|
|
4597
|
+
return false;
|
|
4598
|
+
const existing = readFileOrNull(rulesPath);
|
|
4599
|
+
if (existing === null)
|
|
4600
|
+
return false;
|
|
4601
|
+
const { content, removed } = removeRulesBlockWithMarkers(existing, oldProfile.sentinelBegin, oldProfile.sentinelEnd);
|
|
4602
|
+
if (!removed)
|
|
4603
|
+
return false;
|
|
4604
|
+
if (content.trim().length === 0) {
|
|
4605
|
+
rmSync3(rulesPath, { force: true });
|
|
4606
|
+
} else {
|
|
4607
|
+
writeFileSync4(rulesPath, content);
|
|
4608
|
+
}
|
|
4609
|
+
return true;
|
|
4610
|
+
}
|
|
4611
|
+
function filesAreIdentical(a2, b2) {
|
|
4612
|
+
try {
|
|
4613
|
+
const aStat = statSync(a2);
|
|
4614
|
+
const bStat = statSync(b2);
|
|
4615
|
+
if (aStat.size !== bStat.size)
|
|
4616
|
+
return false;
|
|
4617
|
+
return readFileSync5(a2).equals(readFileSync5(b2));
|
|
4618
|
+
} catch {
|
|
4619
|
+
return false;
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
function tryRemoveEmptyDir(dir) {
|
|
4623
|
+
if (!existsSync6(dir))
|
|
4624
|
+
return;
|
|
4625
|
+
try {
|
|
4626
|
+
rmdirSync(dir);
|
|
4627
|
+
} catch {}
|
|
4628
|
+
}
|
|
4629
|
+
|
|
4630
|
+
// src/lib/tool-prompt.ts
|
|
4631
|
+
async function resolveToolProfile(flagOverride, projectRoot = process.cwd()) {
|
|
4632
|
+
const profile = await resolveProfileOnly(flagOverride, projectRoot);
|
|
4633
|
+
await handleToolSwitch(projectRoot, profile);
|
|
4634
|
+
return profile;
|
|
4635
|
+
}
|
|
4636
|
+
async function resolveProfileOnly(flagOverride, projectRoot) {
|
|
4637
|
+
if (flagOverride) {
|
|
4638
|
+
const profile = PROFILES[flagOverride];
|
|
4639
|
+
if (!profile) {
|
|
4640
|
+
throw new Error(`Unknown tool '${flagOverride}'. Supported: ${Object.keys(PROFILES).join(", ")}`);
|
|
4641
|
+
}
|
|
4642
|
+
return profile;
|
|
4643
|
+
}
|
|
4644
|
+
const config = readToolConfig();
|
|
4645
|
+
if (config?.tool && PROFILES[config.tool]) {
|
|
4646
|
+
return PROFILES[config.tool];
|
|
4647
|
+
}
|
|
4648
|
+
if (process.stdout.isTTY) {
|
|
4649
|
+
const signals = detectTools(projectRoot);
|
|
4650
|
+
const detected = topDetectedProfile(signals);
|
|
4651
|
+
const top = signals[0];
|
|
4652
|
+
if (top && detected) {
|
|
4653
|
+
ye(`Detected: ${detected.displayName} (${top.reason})`);
|
|
4654
|
+
}
|
|
4655
|
+
const initial = detected?.toolId ?? DEFAULT_TOOL;
|
|
4656
|
+
const choice = await de({
|
|
4657
|
+
message: "Which AI coding tool do you use?",
|
|
4658
|
+
options: Object.values(PROFILES).map((profile) => ({
|
|
4659
|
+
value: profile.toolId,
|
|
4660
|
+
label: profile.displayName,
|
|
4661
|
+
hint: profile.toolId === initial ? "default" : undefined
|
|
4662
|
+
})),
|
|
4663
|
+
initialValue: initial
|
|
4664
|
+
});
|
|
4665
|
+
if (BD(choice)) {
|
|
4666
|
+
ve("Using default (Claude Code).");
|
|
4667
|
+
return PROFILES[DEFAULT_TOOL];
|
|
4668
|
+
}
|
|
4669
|
+
saveToolConfig({ ...config ?? {}, tool: choice });
|
|
4670
|
+
return PROFILES[choice];
|
|
4671
|
+
}
|
|
4672
|
+
return PROFILES[DEFAULT_TOOL];
|
|
4673
|
+
}
|
|
4674
|
+
async function handleToolSwitch(projectRoot, newProfile) {
|
|
4675
|
+
if (!process.stdout.isTTY)
|
|
4676
|
+
return;
|
|
4677
|
+
const cfg = readToolConfig();
|
|
4678
|
+
const acknowledged = new Set(cfg?.acknowledgedOrphans ?? []);
|
|
4679
|
+
const orphans = findOrphanedManifests(projectRoot, newProfile).filter((o) => !acknowledged.has(o.profile.toolId));
|
|
4680
|
+
if (orphans.length === 0)
|
|
4681
|
+
return;
|
|
4682
|
+
for (const orphan of orphans) {
|
|
4683
|
+
const action = await de({
|
|
4684
|
+
message: `Found 10x artifacts from ${orphan.profile.displayName} in ${orphan.profile.manifestDir}/. What should we do?`,
|
|
4685
|
+
options: [
|
|
4686
|
+
{
|
|
4687
|
+
value: "migrate",
|
|
4688
|
+
label: `Migrate to ${newProfile.manifestDir}/`,
|
|
4689
|
+
hint: "recommended"
|
|
4690
|
+
},
|
|
4691
|
+
{
|
|
4692
|
+
value: "delete",
|
|
4693
|
+
label: `Remove 10x artifacts from ${orphan.profile.manifestDir}/`
|
|
4694
|
+
},
|
|
4695
|
+
{ value: "keep", label: "Keep both (don't ask again)" }
|
|
4696
|
+
],
|
|
4697
|
+
initialValue: "migrate"
|
|
4698
|
+
});
|
|
4699
|
+
if (BD(action))
|
|
4700
|
+
continue;
|
|
4701
|
+
if (action === "migrate") {
|
|
4702
|
+
const summary = migrateArtifacts(projectRoot, orphan, newProfile);
|
|
4703
|
+
printMigrationSummary(summary, newProfile);
|
|
4704
|
+
} else if (action === "delete") {
|
|
4705
|
+
const summary = deleteArtifacts(projectRoot, orphan);
|
|
4706
|
+
printMigrationSummary(summary, newProfile);
|
|
4707
|
+
} else {
|
|
4708
|
+
const existing = readToolConfig();
|
|
4709
|
+
const nextAcks = [
|
|
4710
|
+
...existing?.acknowledgedOrphans ?? [],
|
|
4711
|
+
orphan.profile.toolId
|
|
4712
|
+
];
|
|
4713
|
+
saveToolConfig({
|
|
4714
|
+
...existing ?? {},
|
|
4715
|
+
tool: existing?.tool ?? newProfile.toolId,
|
|
4716
|
+
acknowledgedOrphans: nextAcks
|
|
4717
|
+
});
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
function printMigrationSummary(summary, newProfile) {
|
|
4722
|
+
const { skills, prompts, configs } = summary.movedOrRemoved;
|
|
4723
|
+
const verb = summary.action === "migrated" ? "Moved" : "Removed";
|
|
4724
|
+
const target = summary.action === "migrated" ? `to ${newProfile.manifestDir}/` : "";
|
|
4725
|
+
const parts = [];
|
|
4726
|
+
if (skills.length)
|
|
4727
|
+
parts.push(`${skills.length} skill${skills.length === 1 ? "" : "s"}`);
|
|
4728
|
+
if (prompts.length)
|
|
4729
|
+
parts.push(`${prompts.length} prompt${prompts.length === 1 ? "" : "s"}`);
|
|
4730
|
+
if (configs.length)
|
|
4731
|
+
parts.push(`${configs.length} config${configs.length === 1 ? "" : "s"}`);
|
|
4732
|
+
const lines = [];
|
|
4733
|
+
if (parts.length > 0) {
|
|
4734
|
+
lines.push(`${verb} ${parts.join(", ")}${target ? " " + target : ""}.`);
|
|
4735
|
+
}
|
|
4736
|
+
if (summary.sentinelStripped) {
|
|
4737
|
+
lines.push(`Removed 10x block from the old rules file.`);
|
|
4738
|
+
}
|
|
4739
|
+
for (const s of summary.skipped) {
|
|
4740
|
+
lines.push(`Skipped ${s.path} (${s.reason}).`);
|
|
4741
|
+
}
|
|
4742
|
+
if (lines.length > 0)
|
|
4743
|
+
ye(lines.join(`
|
|
4744
|
+
`));
|
|
4745
|
+
}
|
|
4746
|
+
|
|
4325
4747
|
// src/commands/get.ts
|
|
4326
4748
|
var ARTIFACT_TYPES = ["skills", "prompts", "rules", "configs"];
|
|
4327
4749
|
function isArtifactType(s) {
|
|
@@ -4351,7 +4773,7 @@ async function runGet(ctx, ref, options) {
|
|
|
4351
4773
|
}
|
|
4352
4774
|
const auth = await requireAuth(ctx);
|
|
4353
4775
|
const course = options.course ?? DEFAULT_COURSE2;
|
|
4354
|
-
const profile = await resolveToolProfile(options.tool);
|
|
4776
|
+
const profile = await resolveToolProfile(options.tool, process.cwd());
|
|
4355
4777
|
const lang = options.lang ?? readToolConfig()?.lang ?? "en";
|
|
4356
4778
|
if (options.lang) {
|
|
4357
4779
|
const existing = readToolConfig();
|
|
@@ -4371,9 +4793,11 @@ async function runGet(ctx, ref, options) {
|
|
|
4371
4793
|
if (isFallback && contentLang) {
|
|
4372
4794
|
verbose(ctx, `${lang.toUpperCase()} not available for ${parsed.lessonId}, showing ${contentLang.toUpperCase()}.`);
|
|
4373
4795
|
}
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4796
|
+
if (!process.stdout.isTTY) {
|
|
4797
|
+
const orphanWarning = detectOrphanedArtifacts(process.cwd(), profile);
|
|
4798
|
+
if (orphanWarning)
|
|
4799
|
+
verbose(ctx, orphanWarning);
|
|
4800
|
+
}
|
|
4377
4801
|
const isFiltered = options.type !== undefined;
|
|
4378
4802
|
const bundle = filterBundle(ctx, result.data, options);
|
|
4379
4803
|
const writeResult = applyBundle(bundle, process.cwd(), {
|