@sunasteriskrnd/takumi 0.5.0 → 0.7.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.js +1830 -1400
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -11225,12 +11225,12 @@ function isSafeRelativeLayoutPath(value) {
|
|
|
11225
11225
|
return segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
11226
11226
|
}
|
|
11227
11227
|
function isValidKitType(value) {
|
|
11228
|
-
return value === "engineer";
|
|
11228
|
+
return value === "engineer" || value === "extras";
|
|
11229
11229
|
}
|
|
11230
|
-
var KitType, KitConfigSchema, KitLayoutSchema, TakumiPackageMetadataSchema, DEFAULT_KIT_LAYOUT, AVAILABLE_KITS, NEVER_COPY_PATTERNS, USER_CONFIG_PATTERNS, PROTECTED_PATTERNS;
|
|
11230
|
+
var KitType, KitConfigSchema, KitLayoutSchema, TakumiPackageMetadataSchema, DEFAULT_KIT_LAYOUT, AVAILABLE_KITS, BASE_KIT = "engineer", NEVER_COPY_PATTERNS, USER_CONFIG_PATTERNS, PROTECTED_PATTERNS;
|
|
11231
11231
|
var init_kit = __esm(() => {
|
|
11232
11232
|
init_zod();
|
|
11233
|
-
KitType = exports_external.enum(["engineer"]);
|
|
11233
|
+
KitType = exports_external.enum(["engineer", "extras"]);
|
|
11234
11234
|
KitConfigSchema = exports_external.object({
|
|
11235
11235
|
id: KitType,
|
|
11236
11236
|
name: exports_external.string(),
|
|
@@ -11256,6 +11256,13 @@ var init_kit = __esm(() => {
|
|
|
11256
11256
|
repo: "takumi",
|
|
11257
11257
|
owner: "sun-asterisk-internal",
|
|
11258
11258
|
description: "Engineering toolkit for building with Claude"
|
|
11259
|
+
},
|
|
11260
|
+
extras: {
|
|
11261
|
+
id: "extras",
|
|
11262
|
+
name: "Takumi Extras",
|
|
11263
|
+
repo: "takumi-extras",
|
|
11264
|
+
owner: "sun-asterisk-internal",
|
|
11265
|
+
description: "Extras agent kit"
|
|
11259
11266
|
}
|
|
11260
11267
|
};
|
|
11261
11268
|
NEVER_COPY_PATTERNS = [
|
|
@@ -11444,13 +11451,14 @@ var init_metadata = __esm(() => {
|
|
|
11444
11451
|
KitMetadataSchema = exports_external.object({
|
|
11445
11452
|
version: exports_external.string(),
|
|
11446
11453
|
installedAt: exports_external.string(),
|
|
11454
|
+
installState: exports_external.enum(["pending", "complete"]).optional(),
|
|
11447
11455
|
files: exports_external.array(TrackedFileSchema).optional(),
|
|
11448
11456
|
lastUpdateCheck: exports_external.string().optional(),
|
|
11449
11457
|
dismissedVersion: exports_external.string().optional(),
|
|
11450
11458
|
installedSettings: InstalledSettingsSchema.optional()
|
|
11451
11459
|
});
|
|
11452
11460
|
MultiKitMetadataSchema = exports_external.object({
|
|
11453
|
-
kits: exports_external.record(
|
|
11461
|
+
kits: exports_external.record(exports_external.string(), KitMetadataSchema).optional(),
|
|
11454
11462
|
scope: exports_external.enum(["local", "global"]).optional(),
|
|
11455
11463
|
name: exports_external.string().optional(),
|
|
11456
11464
|
version: exports_external.string().optional(),
|
|
@@ -12116,6 +12124,7 @@ __export(exports_types, {
|
|
|
12116
12124
|
DEFAULT_FOLDERS: () => DEFAULT_FOLDERS,
|
|
12117
12125
|
ConfigSchema: () => ConfigSchema,
|
|
12118
12126
|
CodingLevelSchema: () => CodingLevelSchema,
|
|
12127
|
+
BASE_KIT: () => BASE_KIT,
|
|
12119
12128
|
AuthenticationError: () => AuthenticationError,
|
|
12120
12129
|
ApiStatusOptionsSchema: () => ApiStatusOptionsSchema,
|
|
12121
12130
|
ApiSetupOptionsSchema: () => ApiSetupOptionsSchema,
|
|
@@ -34351,7 +34360,7 @@ __export(exports_monorepo_resolver, {
|
|
|
34351
34360
|
resolveMonorepoRoot: () => resolveMonorepoRoot
|
|
34352
34361
|
});
|
|
34353
34362
|
import { existsSync as existsSync44, readFileSync as readFileSync11 } from "node:fs";
|
|
34354
|
-
import { dirname as dirname25, join as
|
|
34363
|
+
import { dirname as dirname25, join as join93, resolve as resolve20 } from "node:path";
|
|
34355
34364
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
34356
34365
|
function parseMetadataAt(metadataPath) {
|
|
34357
34366
|
if (!existsSync44(metadataPath))
|
|
@@ -34368,7 +34377,7 @@ function parseMetadataAt(metadataPath) {
|
|
|
34368
34377
|
}
|
|
34369
34378
|
}
|
|
34370
34379
|
function readSourceDirFromPackageJson(candidateRoot) {
|
|
34371
|
-
const packageJsonPath =
|
|
34380
|
+
const packageJsonPath = join93(candidateRoot, "package.json");
|
|
34372
34381
|
if (!existsSync44(packageJsonPath))
|
|
34373
34382
|
return null;
|
|
34374
34383
|
try {
|
|
@@ -34381,7 +34390,7 @@ function readSourceDirFromPackageJson(candidateRoot) {
|
|
|
34381
34390
|
return null;
|
|
34382
34391
|
}
|
|
34383
34392
|
function tryReadAtCandidate(candidateRoot) {
|
|
34384
|
-
const flatMetadata = parseMetadataAt(
|
|
34393
|
+
const flatMetadata = parseMetadataAt(join93(candidateRoot, "metadata.json"));
|
|
34385
34394
|
if (flatMetadata) {
|
|
34386
34395
|
return {
|
|
34387
34396
|
root: candidateRoot,
|
|
@@ -34391,10 +34400,10 @@ function tryReadAtCandidate(candidateRoot) {
|
|
|
34391
34400
|
};
|
|
34392
34401
|
}
|
|
34393
34402
|
const sourceDir = readSourceDirFromPackageJson(candidateRoot) ?? "claude";
|
|
34394
|
-
const nestedMetadata = parseMetadataAt(
|
|
34403
|
+
const nestedMetadata = parseMetadataAt(join93(candidateRoot, sourceDir, "metadata.json"));
|
|
34395
34404
|
if (nestedMetadata) {
|
|
34396
34405
|
return {
|
|
34397
|
-
root:
|
|
34406
|
+
root: join93(candidateRoot, sourceDir),
|
|
34398
34407
|
repoRoot: candidateRoot,
|
|
34399
34408
|
version: nestedMetadata.version,
|
|
34400
34409
|
name: nestedMetadata.name
|
|
@@ -34554,9 +34563,9 @@ __export(exports_worktree_manager, {
|
|
|
34554
34563
|
});
|
|
34555
34564
|
import { existsSync as existsSync60 } from "node:fs";
|
|
34556
34565
|
import { readFile as readFile49, writeFile as writeFile36 } from "node:fs/promises";
|
|
34557
|
-
import { join as
|
|
34566
|
+
import { join as join120 } from "node:path";
|
|
34558
34567
|
async function createWorktree(projectDir, issueNumber, baseBranch) {
|
|
34559
|
-
const worktreePath =
|
|
34568
|
+
const worktreePath = join120(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
|
|
34560
34569
|
const branchName = `sk-watch/issue-${issueNumber}`;
|
|
34561
34570
|
await spawnAndCollect("git", ["fetch", "origin", baseBranch], projectDir).catch(() => {
|
|
34562
34571
|
logger.warning(`[worktree] Could not fetch origin/${baseBranch}, using local`);
|
|
@@ -34574,7 +34583,7 @@ async function createWorktree(projectDir, issueNumber, baseBranch) {
|
|
|
34574
34583
|
return worktreePath;
|
|
34575
34584
|
}
|
|
34576
34585
|
async function removeWorktree(projectDir, issueNumber) {
|
|
34577
|
-
const worktreePath =
|
|
34586
|
+
const worktreePath = join120(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
|
|
34578
34587
|
const branchName = `sk-watch/issue-${issueNumber}`;
|
|
34579
34588
|
try {
|
|
34580
34589
|
await spawnAndCollect("git", ["worktree", "remove", worktreePath, "--force"], projectDir);
|
|
@@ -34588,7 +34597,7 @@ async function listActiveWorktrees(projectDir) {
|
|
|
34588
34597
|
try {
|
|
34589
34598
|
const output2 = await spawnAndCollect("git", ["worktree", "list", "--porcelain"], projectDir);
|
|
34590
34599
|
const issueNumbers = [];
|
|
34591
|
-
const worktreePrefix =
|
|
34600
|
+
const worktreePrefix = join120(projectDir, WORKTREE_DIR, "issue-").replace(/\\/g, "/");
|
|
34592
34601
|
for (const line of output2.split(`
|
|
34593
34602
|
`)) {
|
|
34594
34603
|
if (line.startsWith("worktree ")) {
|
|
@@ -34616,7 +34625,7 @@ async function cleanupAllWorktrees(projectDir) {
|
|
|
34616
34625
|
await spawnAndCollect("git", ["worktree", "prune"], projectDir).catch(() => {});
|
|
34617
34626
|
}
|
|
34618
34627
|
async function ensureGitignore(projectDir) {
|
|
34619
|
-
const gitignorePath =
|
|
34628
|
+
const gitignorePath = join120(projectDir, ".gitignore");
|
|
34620
34629
|
try {
|
|
34621
34630
|
const content = existsSync60(gitignorePath) ? await readFile49(gitignorePath, "utf-8") : "";
|
|
34622
34631
|
if (!content.includes(".worktrees")) {
|
|
@@ -34723,7 +34732,7 @@ import { createHash as createHash8 } from "node:crypto";
|
|
|
34723
34732
|
import { existsSync as existsSync66, mkdirSync as mkdirSync4, readFileSync as readFileSync16, readdirSync as readdirSync10, statSync as statSync7 } from "node:fs";
|
|
34724
34733
|
import { rename as rename10, writeFile as writeFile38 } from "node:fs/promises";
|
|
34725
34734
|
import { homedir as homedir32 } from "node:os";
|
|
34726
|
-
import { basename as basename17, join as
|
|
34735
|
+
import { basename as basename17, join as join127 } from "node:path";
|
|
34727
34736
|
function getCachedContext(repoPath) {
|
|
34728
34737
|
const cachePath = getCacheFilePath(repoPath);
|
|
34729
34738
|
if (!existsSync66(cachePath))
|
|
@@ -34766,25 +34775,25 @@ function computeSourceHash(repoPath) {
|
|
|
34766
34775
|
}
|
|
34767
34776
|
function getDocSourcePaths(repoPath) {
|
|
34768
34777
|
const paths = [];
|
|
34769
|
-
const docsDir =
|
|
34778
|
+
const docsDir = join127(repoPath, "docs");
|
|
34770
34779
|
if (existsSync66(docsDir)) {
|
|
34771
34780
|
try {
|
|
34772
34781
|
const files = readdirSync10(docsDir);
|
|
34773
34782
|
for (const f4 of files) {
|
|
34774
34783
|
if (f4.endsWith(".md"))
|
|
34775
|
-
paths.push(
|
|
34784
|
+
paths.push(join127(docsDir, f4));
|
|
34776
34785
|
}
|
|
34777
34786
|
} catch {}
|
|
34778
34787
|
}
|
|
34779
|
-
const readme =
|
|
34788
|
+
const readme = join127(repoPath, "README.md");
|
|
34780
34789
|
if (existsSync66(readme))
|
|
34781
34790
|
paths.push(readme);
|
|
34782
|
-
const stylesDir =
|
|
34791
|
+
const stylesDir = join127(repoPath, "assets", "writing-styles");
|
|
34783
34792
|
if (existsSync66(stylesDir)) {
|
|
34784
34793
|
try {
|
|
34785
34794
|
const files = readdirSync10(stylesDir);
|
|
34786
34795
|
for (const f4 of files) {
|
|
34787
|
-
paths.push(
|
|
34796
|
+
paths.push(join127(stylesDir, f4));
|
|
34788
34797
|
}
|
|
34789
34798
|
} catch {}
|
|
34790
34799
|
}
|
|
@@ -34793,11 +34802,11 @@ function getDocSourcePaths(repoPath) {
|
|
|
34793
34802
|
function getCacheFilePath(repoPath) {
|
|
34794
34803
|
const repoName = basename17(repoPath).replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
34795
34804
|
const pathHash = createHash8("sha256").update(repoPath).digest("hex").slice(0, 8);
|
|
34796
|
-
return
|
|
34805
|
+
return join127(CACHE_DIR, `${repoName}-${pathHash}-context-cache.json`);
|
|
34797
34806
|
}
|
|
34798
34807
|
var CACHE_DIR, CACHE_TTL_MS3;
|
|
34799
34808
|
var init_context_cache_manager = __esm(() => {
|
|
34800
|
-
CACHE_DIR =
|
|
34809
|
+
CACHE_DIR = join127(homedir32(), ".sunagentkit", "cache");
|
|
34801
34810
|
CACHE_TTL_MS3 = 24 * 60 * 60 * 1000;
|
|
34802
34811
|
});
|
|
34803
34812
|
|
|
@@ -34978,7 +34987,7 @@ function extractContentFromResponse(response) {
|
|
|
34978
34987
|
// src/commands/content/phases/docs-summarizer.ts
|
|
34979
34988
|
import { execSync as execSync5 } from "node:child_process";
|
|
34980
34989
|
import { existsSync as existsSync67, readFileSync as readFileSync17, readdirSync as readdirSync11 } from "node:fs";
|
|
34981
|
-
import { join as
|
|
34990
|
+
import { join as join128 } from "node:path";
|
|
34982
34991
|
async function summarizeProjectDocs(repoPath, contentLogger) {
|
|
34983
34992
|
const rawContent = collectRawDocs(repoPath);
|
|
34984
34993
|
if (rawContent.total.length < 200) {
|
|
@@ -35032,12 +35041,12 @@ function collectRawDocs(repoPath) {
|
|
|
35032
35041
|
return capped;
|
|
35033
35042
|
};
|
|
35034
35043
|
const docsContent = [];
|
|
35035
|
-
const docsDir =
|
|
35044
|
+
const docsDir = join128(repoPath, "docs");
|
|
35036
35045
|
if (existsSync67(docsDir)) {
|
|
35037
35046
|
try {
|
|
35038
35047
|
const files = readdirSync11(docsDir).filter((f4) => f4.endsWith(".md")).sort();
|
|
35039
35048
|
for (const f4 of files) {
|
|
35040
|
-
const content = readCapped(
|
|
35049
|
+
const content = readCapped(join128(docsDir, f4), 5000);
|
|
35041
35050
|
if (content) {
|
|
35042
35051
|
docsContent.push(`### ${f4}
|
|
35043
35052
|
${content}`);
|
|
@@ -35051,21 +35060,21 @@ ${content}`);
|
|
|
35051
35060
|
let brand = "";
|
|
35052
35061
|
const brandCandidates = ["docs/brand-guidelines.md", "docs/design-guidelines.md"];
|
|
35053
35062
|
for (const p2 of brandCandidates) {
|
|
35054
|
-
brand = readCapped(
|
|
35063
|
+
brand = readCapped(join128(repoPath, p2), 3000);
|
|
35055
35064
|
if (brand)
|
|
35056
35065
|
break;
|
|
35057
35066
|
}
|
|
35058
35067
|
let styles3 = "";
|
|
35059
|
-
const stylesDir =
|
|
35068
|
+
const stylesDir = join128(repoPath, "assets", "writing-styles");
|
|
35060
35069
|
if (existsSync67(stylesDir)) {
|
|
35061
35070
|
try {
|
|
35062
35071
|
const files = readdirSync11(stylesDir).slice(0, 3);
|
|
35063
|
-
styles3 = files.map((f4) => readCapped(
|
|
35072
|
+
styles3 = files.map((f4) => readCapped(join128(stylesDir, f4), 1000)).filter(Boolean).join(`
|
|
35064
35073
|
|
|
35065
35074
|
`);
|
|
35066
35075
|
} catch {}
|
|
35067
35076
|
}
|
|
35068
|
-
const readme = readCapped(
|
|
35077
|
+
const readme = readCapped(join128(repoPath, "README.md"), 3000);
|
|
35069
35078
|
const total = [docs, brand, styles3, readme].join(`
|
|
35070
35079
|
`);
|
|
35071
35080
|
return { docs, brand, styles: styles3, readme, total };
|
|
@@ -35252,9 +35261,9 @@ IMPORTANT: Generate the image and output the path as JSON: {"imagePath": "/path/
|
|
|
35252
35261
|
import { execSync as execSync6 } from "node:child_process";
|
|
35253
35262
|
import { existsSync as existsSync68, mkdirSync as mkdirSync5, readdirSync as readdirSync12 } from "node:fs";
|
|
35254
35263
|
import { homedir as homedir33 } from "node:os";
|
|
35255
|
-
import { join as
|
|
35264
|
+
import { join as join129 } from "node:path";
|
|
35256
35265
|
async function generatePhoto(_content, context, config, platform10, contentId, contentLogger) {
|
|
35257
|
-
const mediaDir =
|
|
35266
|
+
const mediaDir = join129(config.contentDir.replace(/^~/, homedir33()), "media", String(contentId));
|
|
35258
35267
|
if (!existsSync68(mediaDir)) {
|
|
35259
35268
|
mkdirSync5(mediaDir, { recursive: true });
|
|
35260
35269
|
}
|
|
@@ -35279,7 +35288,7 @@ async function generatePhoto(_content, context, config, platform10, contentId, c
|
|
|
35279
35288
|
const imageFile = files.find((f4) => /\.(png|jpg|jpeg|webp)$/i.test(f4));
|
|
35280
35289
|
if (imageFile) {
|
|
35281
35290
|
const ext2 = imageFile.split(".").pop() ?? "png";
|
|
35282
|
-
return { path:
|
|
35291
|
+
return { path: join129(mediaDir, imageFile), ...dimensions, format: ext2 };
|
|
35283
35292
|
}
|
|
35284
35293
|
contentLogger.warn(`Photo generation produced no image for content ${contentId}`);
|
|
35285
35294
|
return null;
|
|
@@ -35369,7 +35378,7 @@ var init_content_creator = __esm(() => {
|
|
|
35369
35378
|
// src/commands/content/phases/content-logger.ts
|
|
35370
35379
|
import { createWriteStream as createWriteStream5, existsSync as existsSync69, mkdirSync as mkdirSync6, statSync as statSync8 } from "node:fs";
|
|
35371
35380
|
import { homedir as homedir34 } from "node:os";
|
|
35372
|
-
import { join as
|
|
35381
|
+
import { join as join130 } from "node:path";
|
|
35373
35382
|
|
|
35374
35383
|
class ContentLogger {
|
|
35375
35384
|
stream = null;
|
|
@@ -35377,7 +35386,7 @@ class ContentLogger {
|
|
|
35377
35386
|
logDir;
|
|
35378
35387
|
maxBytes;
|
|
35379
35388
|
constructor(maxBytes = 0) {
|
|
35380
|
-
this.logDir =
|
|
35389
|
+
this.logDir = join130(homedir34(), ".sunagentkit", "logs");
|
|
35381
35390
|
this.maxBytes = maxBytes;
|
|
35382
35391
|
}
|
|
35383
35392
|
init() {
|
|
@@ -35409,7 +35418,7 @@ class ContentLogger {
|
|
|
35409
35418
|
}
|
|
35410
35419
|
}
|
|
35411
35420
|
getLogPath() {
|
|
35412
|
-
return
|
|
35421
|
+
return join130(this.logDir, `content-${this.getDateStr()}.log`);
|
|
35413
35422
|
}
|
|
35414
35423
|
write(level, message) {
|
|
35415
35424
|
this.rotateIfNeeded();
|
|
@@ -35426,18 +35435,18 @@ class ContentLogger {
|
|
|
35426
35435
|
if (dateStr !== this.currentDate) {
|
|
35427
35436
|
this.close();
|
|
35428
35437
|
this.currentDate = dateStr;
|
|
35429
|
-
const logPath =
|
|
35438
|
+
const logPath = join130(this.logDir, `content-${dateStr}.log`);
|
|
35430
35439
|
this.stream = createWriteStream5(logPath, { flags: "a", mode: 384 });
|
|
35431
35440
|
return;
|
|
35432
35441
|
}
|
|
35433
35442
|
if (this.maxBytes > 0 && this.stream) {
|
|
35434
|
-
const logPath =
|
|
35443
|
+
const logPath = join130(this.logDir, `content-${this.currentDate}.log`);
|
|
35435
35444
|
try {
|
|
35436
35445
|
const stat16 = statSync8(logPath);
|
|
35437
35446
|
if (stat16.size >= this.maxBytes) {
|
|
35438
35447
|
this.close();
|
|
35439
35448
|
const suffix = Date.now();
|
|
35440
|
-
const rotatedPath =
|
|
35449
|
+
const rotatedPath = join130(this.logDir, `content-${this.currentDate}-${suffix}.log`);
|
|
35441
35450
|
import("node:fs/promises").then(({ rename: rename11 }) => rename11(logPath, rotatedPath).catch(() => {}));
|
|
35442
35451
|
this.stream = createWriteStream5(logPath, { flags: "w", mode: 384 });
|
|
35443
35452
|
}
|
|
@@ -35663,7 +35672,7 @@ function isNoiseCommit(title, author) {
|
|
|
35663
35672
|
// src/commands/content/phases/change-detector.ts
|
|
35664
35673
|
import { execSync as execSync8 } from "node:child_process";
|
|
35665
35674
|
import { existsSync as existsSync71, readFileSync as readFileSync18, readdirSync as readdirSync13, statSync as statSync9 } from "node:fs";
|
|
35666
|
-
import { join as
|
|
35675
|
+
import { join as join131 } from "node:path";
|
|
35667
35676
|
function detectCommits(repo, since) {
|
|
35668
35677
|
try {
|
|
35669
35678
|
const fetchUrl = sshToHttps(repo.remoteUrl);
|
|
@@ -35761,7 +35770,7 @@ function detectTags(repo, since) {
|
|
|
35761
35770
|
}
|
|
35762
35771
|
}
|
|
35763
35772
|
function detectCompletedPlans(repo, since) {
|
|
35764
|
-
const plansDir =
|
|
35773
|
+
const plansDir = join131(repo.path, "plans");
|
|
35765
35774
|
if (!existsSync71(plansDir))
|
|
35766
35775
|
return [];
|
|
35767
35776
|
const sinceMs = new Date(since).getTime();
|
|
@@ -35771,7 +35780,7 @@ function detectCompletedPlans(repo, since) {
|
|
|
35771
35780
|
for (const entry of entries) {
|
|
35772
35781
|
if (!entry.isDirectory())
|
|
35773
35782
|
continue;
|
|
35774
|
-
const planFile =
|
|
35783
|
+
const planFile = join131(plansDir, entry.name, "plan.md");
|
|
35775
35784
|
if (!existsSync71(planFile))
|
|
35776
35785
|
continue;
|
|
35777
35786
|
try {
|
|
@@ -35849,7 +35858,7 @@ function classifyCommit(event) {
|
|
|
35849
35858
|
// src/commands/content/phases/repo-discoverer.ts
|
|
35850
35859
|
import { execSync as execSync9 } from "node:child_process";
|
|
35851
35860
|
import { readdirSync as readdirSync14 } from "node:fs";
|
|
35852
|
-
import { join as
|
|
35861
|
+
import { join as join132 } from "node:path";
|
|
35853
35862
|
function discoverRepos2(cwd2) {
|
|
35854
35863
|
const repos = [];
|
|
35855
35864
|
if (isGitRepoRoot(cwd2)) {
|
|
@@ -35862,7 +35871,7 @@ function discoverRepos2(cwd2) {
|
|
|
35862
35871
|
for (const entry of entries) {
|
|
35863
35872
|
if (!entry.isDirectory() || entry.name.startsWith("."))
|
|
35864
35873
|
continue;
|
|
35865
|
-
const dirPath =
|
|
35874
|
+
const dirPath = join132(cwd2, entry.name);
|
|
35866
35875
|
if (isGitRepoRoot(dirPath)) {
|
|
35867
35876
|
const info = getRepoInfo(dirPath);
|
|
35868
35877
|
if (info)
|
|
@@ -36529,9 +36538,9 @@ var init_types3 = __esm(() => {
|
|
|
36529
36538
|
|
|
36530
36539
|
// src/commands/content/phases/state-manager.ts
|
|
36531
36540
|
import { readFile as readFile51, rename as rename11, writeFile as writeFile39 } from "node:fs/promises";
|
|
36532
|
-
import { join as
|
|
36541
|
+
import { join as join133 } from "node:path";
|
|
36533
36542
|
async function loadContentConfig(projectDir) {
|
|
36534
|
-
const configPath =
|
|
36543
|
+
const configPath = join133(projectDir, TAKUMI_CONFIG_FILE2);
|
|
36535
36544
|
try {
|
|
36536
36545
|
const raw = await readFile51(configPath, "utf-8");
|
|
36537
36546
|
const json = JSON.parse(raw);
|
|
@@ -36541,13 +36550,13 @@ async function loadContentConfig(projectDir) {
|
|
|
36541
36550
|
}
|
|
36542
36551
|
}
|
|
36543
36552
|
async function saveContentConfig(projectDir, config) {
|
|
36544
|
-
const configPath =
|
|
36553
|
+
const configPath = join133(projectDir, TAKUMI_CONFIG_FILE2);
|
|
36545
36554
|
const json = await readJsonSafe(configPath);
|
|
36546
36555
|
json.content = { ...json.content, ...config };
|
|
36547
36556
|
await atomicWrite3(configPath, json);
|
|
36548
36557
|
}
|
|
36549
36558
|
async function loadContentState(projectDir) {
|
|
36550
|
-
const configPath =
|
|
36559
|
+
const configPath = join133(projectDir, TAKUMI_CONFIG_FILE2);
|
|
36551
36560
|
try {
|
|
36552
36561
|
const raw = await readFile51(configPath, "utf-8");
|
|
36553
36562
|
const json = JSON.parse(raw);
|
|
@@ -36558,7 +36567,7 @@ async function loadContentState(projectDir) {
|
|
|
36558
36567
|
}
|
|
36559
36568
|
}
|
|
36560
36569
|
async function saveContentState(projectDir, state) {
|
|
36561
|
-
const configPath =
|
|
36570
|
+
const configPath = join133(projectDir, TAKUMI_CONFIG_FILE2);
|
|
36562
36571
|
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
|
36563
36572
|
for (const key of Object.keys(state.dailyPostCounts)) {
|
|
36564
36573
|
const dateStr = key.slice(-10);
|
|
@@ -36840,7 +36849,7 @@ var init_platform_setup_x = __esm(() => {
|
|
|
36840
36849
|
|
|
36841
36850
|
// src/commands/content/phases/setup-wizard.ts
|
|
36842
36851
|
import { existsSync as existsSync72 } from "node:fs";
|
|
36843
|
-
import { join as
|
|
36852
|
+
import { join as join134 } from "node:path";
|
|
36844
36853
|
async function runSetupWizard2(cwd2, contentLogger) {
|
|
36845
36854
|
console.log();
|
|
36846
36855
|
oe(import_picocolors40.default.bgCyan(import_picocolors40.default.white(" SK Content — Multi-Channel Content Engine ")));
|
|
@@ -36908,8 +36917,8 @@ async function showRepoSummary(cwd2) {
|
|
|
36908
36917
|
function detectBrandAssets(cwd2, contentLogger) {
|
|
36909
36918
|
const repos = discoverRepos2(cwd2);
|
|
36910
36919
|
for (const repo of repos) {
|
|
36911
|
-
const hasGuidelines = existsSync72(
|
|
36912
|
-
const hasStyles = existsSync72(
|
|
36920
|
+
const hasGuidelines = existsSync72(join134(repo.path, "docs", "brand-guidelines.md"));
|
|
36921
|
+
const hasStyles = existsSync72(join134(repo.path, "assets", "writing-styles"));
|
|
36913
36922
|
if (!hasGuidelines) {
|
|
36914
36923
|
f2.warning(`${repo.name}: No docs/brand-guidelines.md — content will use generic tone.`);
|
|
36915
36924
|
contentLogger.warn(`${repo.name}: missing docs/brand-guidelines.md`);
|
|
@@ -37051,9 +37060,9 @@ __export(exports_content_subcommands, {
|
|
|
37051
37060
|
});
|
|
37052
37061
|
import { existsSync as existsSync74, readFileSync as readFileSync19, unlinkSync as unlinkSync6 } from "node:fs";
|
|
37053
37062
|
import { homedir as homedir36 } from "node:os";
|
|
37054
|
-
import { join as
|
|
37063
|
+
import { join as join135 } from "node:path";
|
|
37055
37064
|
function isDaemonRunning() {
|
|
37056
|
-
const lockFile =
|
|
37065
|
+
const lockFile = join135(LOCK_DIR, `${LOCK_NAME2}.lock`);
|
|
37057
37066
|
if (!existsSync74(lockFile))
|
|
37058
37067
|
return { running: false, pid: null };
|
|
37059
37068
|
try {
|
|
@@ -37085,7 +37094,7 @@ async function startContent(options2) {
|
|
|
37085
37094
|
await contentCommand(options2);
|
|
37086
37095
|
}
|
|
37087
37096
|
async function stopContent() {
|
|
37088
|
-
const lockFile =
|
|
37097
|
+
const lockFile = join135(LOCK_DIR, `${LOCK_NAME2}.lock`);
|
|
37089
37098
|
if (!existsSync74(lockFile)) {
|
|
37090
37099
|
logger.info("Content daemon is not running.");
|
|
37091
37100
|
return;
|
|
@@ -37124,9 +37133,9 @@ async function statusContent() {
|
|
|
37124
37133
|
} catch {}
|
|
37125
37134
|
}
|
|
37126
37135
|
async function logsContent(options2) {
|
|
37127
|
-
const logDir =
|
|
37136
|
+
const logDir = join135(homedir36(), ".sunagentkit", "logs");
|
|
37128
37137
|
const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, "");
|
|
37129
|
-
const logPath =
|
|
37138
|
+
const logPath = join135(logDir, `content-${dateStr}.log`);
|
|
37130
37139
|
if (!existsSync74(logPath)) {
|
|
37131
37140
|
logger.info("No content logs found for today.");
|
|
37132
37141
|
return;
|
|
@@ -37158,13 +37167,13 @@ var init_content_subcommands = __esm(() => {
|
|
|
37158
37167
|
init_setup_wizard();
|
|
37159
37168
|
init_state_manager();
|
|
37160
37169
|
init_content_review_commands();
|
|
37161
|
-
LOCK_DIR =
|
|
37170
|
+
LOCK_DIR = join135(homedir36(), ".sunagentkit", "locks");
|
|
37162
37171
|
});
|
|
37163
37172
|
|
|
37164
37173
|
// src/commands/content/content-command.ts
|
|
37165
37174
|
import { existsSync as existsSync75, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
37166
37175
|
import { homedir as homedir37 } from "node:os";
|
|
37167
|
-
import { join as
|
|
37176
|
+
import { join as join136 } from "node:path";
|
|
37168
37177
|
async function contentCommand(options2) {
|
|
37169
37178
|
const cwd2 = process.cwd();
|
|
37170
37179
|
const contentLogger = new ContentLogger;
|
|
@@ -37342,8 +37351,8 @@ var init_content_command = __esm(() => {
|
|
|
37342
37351
|
init_publisher();
|
|
37343
37352
|
init_review_manager();
|
|
37344
37353
|
init_state_manager();
|
|
37345
|
-
LOCK_DIR2 =
|
|
37346
|
-
LOCK_FILE =
|
|
37354
|
+
LOCK_DIR2 = join136(homedir37(), ".sunagentkit", "locks");
|
|
37355
|
+
LOCK_FILE = join136(LOCK_DIR2, "sk-content.lock");
|
|
37347
37356
|
});
|
|
37348
37357
|
|
|
37349
37358
|
// src/commands/content/index.ts
|
|
@@ -48980,11 +48989,11 @@ async function detectMetadataFormat(claudeDir) {
|
|
|
48980
48989
|
};
|
|
48981
48990
|
}
|
|
48982
48991
|
if (parsed.name || parsed.version || parsed.files) {
|
|
48983
|
-
let detectedKit =
|
|
48992
|
+
let detectedKit = "engineer";
|
|
48984
48993
|
const nameToCheck = parsed.name || "";
|
|
48985
|
-
if (/\
|
|
48986
|
-
detectedKit = "
|
|
48987
|
-
} else {
|
|
48994
|
+
if (/\bextras\b/i.test(nameToCheck)) {
|
|
48995
|
+
detectedKit = "extras";
|
|
48996
|
+
} else if (/\bengineer\b/i.test(nameToCheck)) {
|
|
48988
48997
|
detectedKit = "engineer";
|
|
48989
48998
|
}
|
|
48990
48999
|
return { format: "legacy", metadata: parsed, detectedKit };
|
|
@@ -49278,28 +49287,32 @@ var import_fs_extra6 = __toESM(require_lib(), 1);
|
|
|
49278
49287
|
var import_picomatch = __toESM(require_picomatch2(), 1);
|
|
49279
49288
|
function findFileInMetadata(metadata, path3) {
|
|
49280
49289
|
if (!metadata)
|
|
49281
|
-
return null;
|
|
49290
|
+
return { tracked: null, ownerKit: null };
|
|
49282
49291
|
if (metadata.kits) {
|
|
49283
|
-
for (const kitMeta of Object.
|
|
49284
|
-
if (kitMeta?.files)
|
|
49285
|
-
|
|
49286
|
-
|
|
49287
|
-
|
|
49288
|
-
|
|
49292
|
+
for (const [kitName, kitMeta] of Object.entries(metadata.kits)) {
|
|
49293
|
+
if (!kitMeta?.files)
|
|
49294
|
+
continue;
|
|
49295
|
+
const found = kitMeta.files.find((f3) => f3.path === path3);
|
|
49296
|
+
if (found)
|
|
49297
|
+
return { tracked: found, ownerKit: kitName };
|
|
49289
49298
|
}
|
|
49290
49299
|
}
|
|
49291
49300
|
if (metadata.files) {
|
|
49292
49301
|
const found = metadata.files.find((f3) => f3.path === path3);
|
|
49293
49302
|
if (found)
|
|
49294
|
-
return found;
|
|
49303
|
+
return { tracked: found, ownerKit: null };
|
|
49295
49304
|
}
|
|
49296
|
-
return null;
|
|
49305
|
+
return { tracked: null, ownerKit: null };
|
|
49297
49306
|
}
|
|
49298
|
-
function shouldDeletePath(path3, metadata) {
|
|
49299
|
-
const tracked = findFileInMetadata(metadata, path3);
|
|
49307
|
+
function shouldDeletePath(path3, metadata, installingKit) {
|
|
49308
|
+
const { tracked, ownerKit } = findFileInMetadata(metadata, path3);
|
|
49300
49309
|
if (!tracked)
|
|
49301
49310
|
return true;
|
|
49302
|
-
|
|
49311
|
+
if (tracked.ownership === "user")
|
|
49312
|
+
return false;
|
|
49313
|
+
if (installingKit && ownerKit && ownerKit !== installingKit)
|
|
49314
|
+
return false;
|
|
49315
|
+
return true;
|
|
49303
49316
|
}
|
|
49304
49317
|
function collectFilesRecursively(dir, baseDir) {
|
|
49305
49318
|
const results = [];
|
|
@@ -49422,7 +49435,7 @@ async function updateMetadataAfterDeletion(claudeDir, deletedPaths) {
|
|
|
49422
49435
|
logger.debug("Failed to write updated metadata.json");
|
|
49423
49436
|
}
|
|
49424
49437
|
}
|
|
49425
|
-
async function handleDeletions(sourceMetadata, claudeDir) {
|
|
49438
|
+
async function handleDeletions(sourceMetadata, claudeDir, installingKit) {
|
|
49426
49439
|
const deletionPatterns = sourceMetadata.deletions || [];
|
|
49427
49440
|
if (deletionPatterns.length === 0) {
|
|
49428
49441
|
return { deletedPaths: [], preservedPaths: [], errors: [] };
|
|
@@ -49439,9 +49452,9 @@ async function handleDeletions(sourceMetadata, claudeDir) {
|
|
|
49439
49452
|
result.errors.push(path3);
|
|
49440
49453
|
continue;
|
|
49441
49454
|
}
|
|
49442
|
-
if (!shouldDeletePath(path3, userMetadata)) {
|
|
49455
|
+
if (!shouldDeletePath(path3, userMetadata, installingKit)) {
|
|
49443
49456
|
result.preservedPaths.push(path3);
|
|
49444
|
-
logger.verbose(`Preserved user
|
|
49457
|
+
logger.verbose(`Preserved file (cross-kit or user-owned): ${path3}`);
|
|
49445
49458
|
continue;
|
|
49446
49459
|
}
|
|
49447
49460
|
if (existsSync13(fullPath)) {
|
|
@@ -52510,14 +52523,17 @@ async function writeManifest(providerRoot, kitName, version3, scope, kitType, tr
|
|
|
52510
52523
|
logger.debug(`Could not read existing metadata: ${error}`);
|
|
52511
52524
|
}
|
|
52512
52525
|
}
|
|
52526
|
+
const existingKits = existingMetadata.kits || {};
|
|
52527
|
+
const otherKitsExist = Object.keys(existingKits).some((k2) => k2 !== kit);
|
|
52513
52528
|
const installedAt = new Date().toISOString();
|
|
52529
|
+
const existingKitMeta = existingKits[kit];
|
|
52514
52530
|
const kitMetadata = {
|
|
52531
|
+
...existingKitMeta ?? {},
|
|
52515
52532
|
version: version3,
|
|
52516
52533
|
installedAt,
|
|
52534
|
+
installState: "complete",
|
|
52517
52535
|
files: trackedFiles.length > 0 ? trackedFiles : undefined
|
|
52518
52536
|
};
|
|
52519
|
-
const existingKits = existingMetadata.kits || {};
|
|
52520
|
-
const otherKitsExist = Object.keys(existingKits).some((k2) => k2 !== kit);
|
|
52521
52537
|
const metadata = {
|
|
52522
52538
|
kits: {
|
|
52523
52539
|
...existingKits,
|
|
@@ -52548,6 +52564,69 @@ async function writeManifest(providerRoot, kitName, version3, scope, kitType, tr
|
|
|
52548
52564
|
}
|
|
52549
52565
|
}
|
|
52550
52566
|
}
|
|
52567
|
+
async function writeKitPending(providerRoot, kit, version3, scope) {
|
|
52568
|
+
const metadataPath = join28(providerRoot, "metadata.json");
|
|
52569
|
+
await import_fs_extra10.ensureFile(metadataPath);
|
|
52570
|
+
let release = null;
|
|
52571
|
+
try {
|
|
52572
|
+
release = await import_proper_lockfile4.lock(metadataPath, {
|
|
52573
|
+
retries: { retries: 5, minTimeout: 100, maxTimeout: 1000 },
|
|
52574
|
+
stale: 60000
|
|
52575
|
+
});
|
|
52576
|
+
logger.debug(`Acquired lock on ${metadataPath} for pending-state write`);
|
|
52577
|
+
const migrationResult = await migrateToMultiKit(providerRoot);
|
|
52578
|
+
if (!migrationResult.success) {
|
|
52579
|
+
logger.warning(`Metadata migration warning: ${migrationResult.error}`);
|
|
52580
|
+
}
|
|
52581
|
+
let existingMetadata = { kits: {} };
|
|
52582
|
+
if (await import_fs_extra10.pathExists(metadataPath)) {
|
|
52583
|
+
try {
|
|
52584
|
+
const content = await import_fs_extra10.readFile(metadataPath, "utf-8");
|
|
52585
|
+
const parsed = JSON.parse(content);
|
|
52586
|
+
if (parsed && typeof parsed === "object" && Object.keys(parsed).length > 0) {
|
|
52587
|
+
existingMetadata = parsed;
|
|
52588
|
+
}
|
|
52589
|
+
} catch (error) {
|
|
52590
|
+
logger.debug(`Could not read existing metadata: ${error}`);
|
|
52591
|
+
}
|
|
52592
|
+
}
|
|
52593
|
+
const existingKits = existingMetadata.kits || {};
|
|
52594
|
+
const pendingMetadata = {
|
|
52595
|
+
version: version3,
|
|
52596
|
+
installedAt: new Date().toISOString(),
|
|
52597
|
+
installState: "pending",
|
|
52598
|
+
files: []
|
|
52599
|
+
};
|
|
52600
|
+
const metadata = {
|
|
52601
|
+
...existingMetadata,
|
|
52602
|
+
kits: {
|
|
52603
|
+
...existingKits,
|
|
52604
|
+
[kit]: pendingMetadata
|
|
52605
|
+
},
|
|
52606
|
+
scope
|
|
52607
|
+
};
|
|
52608
|
+
const validated = MetadataSchema.parse(metadata);
|
|
52609
|
+
await import_fs_extra10.writeFile(metadataPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
52610
|
+
logger.debug(`Wrote pending-state entry for kit "${kit}"`);
|
|
52611
|
+
} finally {
|
|
52612
|
+
if (release) {
|
|
52613
|
+
await release();
|
|
52614
|
+
logger.debug(`Released lock on ${metadataPath}`);
|
|
52615
|
+
}
|
|
52616
|
+
}
|
|
52617
|
+
}
|
|
52618
|
+
async function findPendingKits(providerRoot) {
|
|
52619
|
+
const metadata = await readManifest(providerRoot);
|
|
52620
|
+
if (!metadata?.kits)
|
|
52621
|
+
return [];
|
|
52622
|
+
const pending = [];
|
|
52623
|
+
for (const [kitName, kitMeta] of Object.entries(metadata.kits)) {
|
|
52624
|
+
if (kitMeta?.installState === "pending") {
|
|
52625
|
+
pending.push(kitName);
|
|
52626
|
+
}
|
|
52627
|
+
}
|
|
52628
|
+
return pending;
|
|
52629
|
+
}
|
|
52551
52630
|
async function removeKitFromManifest(providerRoot, kit) {
|
|
52552
52631
|
const metadataPath = join28(providerRoot, "metadata.json");
|
|
52553
52632
|
if (!await import_fs_extra10.pathExists(metadataPath))
|
|
@@ -52749,6 +52828,12 @@ class ManifestWriter {
|
|
|
52749
52828
|
async writeManifest(providerRoot, kitName, version3, scope, kitType) {
|
|
52750
52829
|
return writeManifest(providerRoot, kitName, version3, scope, kitType, this.getTrackedFiles(), this.getUserConfigFiles());
|
|
52751
52830
|
}
|
|
52831
|
+
static async writeKitPending(providerRoot, kit, version3, scope) {
|
|
52832
|
+
return writeKitPending(providerRoot, kit, version3, scope);
|
|
52833
|
+
}
|
|
52834
|
+
static async findPendingKits(providerRoot) {
|
|
52835
|
+
return findPendingKits(providerRoot);
|
|
52836
|
+
}
|
|
52752
52837
|
static async readManifest(providerRoot) {
|
|
52753
52838
|
return readManifest(providerRoot);
|
|
52754
52839
|
}
|
|
@@ -53716,7 +53801,7 @@ async function handleMerge(ctx) {
|
|
|
53716
53801
|
}
|
|
53717
53802
|
try {
|
|
53718
53803
|
if (sourceMetadata?.deletions && sourceMetadata.deletions.length > 0) {
|
|
53719
|
-
const deletionResult = await handleDeletions(sourceMetadata, ctx.claudeDir);
|
|
53804
|
+
const deletionResult = await handleDeletions(sourceMetadata, ctx.claudeDir, ctx.kitType);
|
|
53720
53805
|
if (deletionResult.deletedPaths.length > 0) {
|
|
53721
53806
|
logger.info(`Removed ${deletionResult.deletedPaths.length} deprecated file(s)`);
|
|
53722
53807
|
for (const path4 of deletionResult.deletedPaths) {
|
|
@@ -55543,14 +55628,17 @@ async function handlePostInstall(ctx) {
|
|
|
55543
55628
|
}
|
|
55544
55629
|
}
|
|
55545
55630
|
const skillsDir = PathResolver.buildSkillsPath(ctx.resolvedDir, ctx.options.global);
|
|
55546
|
-
|
|
55547
|
-
|
|
55548
|
-
|
|
55549
|
-
|
|
55550
|
-
|
|
55551
|
-
|
|
55552
|
-
|
|
55553
|
-
|
|
55631
|
+
let installSkills = ctx.installSkills;
|
|
55632
|
+
if (ctx.isLastKit === undefined || ctx.isLastKit) {
|
|
55633
|
+
const depsResult = await promptAndInstallSkillsDeps({
|
|
55634
|
+
skillsDir,
|
|
55635
|
+
isNonInteractive: ctx.isNonInteractive,
|
|
55636
|
+
installSkills: ctx.installSkills,
|
|
55637
|
+
withSudo: ctx.options.withSudo,
|
|
55638
|
+
prompts: ctx.prompts
|
|
55639
|
+
});
|
|
55640
|
+
installSkills = depsResult.installSkills;
|
|
55641
|
+
}
|
|
55554
55642
|
if (!ctx.isNonInteractive) {
|
|
55555
55643
|
const { isGeminiInstalled: isGeminiInstalled2 } = await Promise.resolve().then(() => (init_package_installer(), exports_package_installer));
|
|
55556
55644
|
const { checkExistingGeminiConfig: checkExistingGeminiConfig2, findMcpConfigPath: findMcpConfigPath2, processGeminiMcpLinking: processGeminiMcpLinking2 } = await Promise.resolve().then(() => (init_gemini_mcp_linker(), exports_gemini_mcp_linker));
|
|
@@ -60490,6 +60578,8 @@ var codexInstaller = {
|
|
|
60490
60578
|
await copySkillsAuxFiles(sources.skills, codexSkillsDir);
|
|
60491
60579
|
await copySkillsScriptsDir(ctx.extractDir, codexSkillsDir);
|
|
60492
60580
|
auxCopySucceeded = true;
|
|
60581
|
+
ctx.codexSkillsDirSeen = codexSkillsDir;
|
|
60582
|
+
ctx.codexAuxCopiedAny = true;
|
|
60493
60583
|
} catch (error) {
|
|
60494
60584
|
logger.error(`[init/codex] skills aux copy failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
60495
60585
|
logger.warning("[init/codex] skipping skills-deps install (aux files unavailable)");
|
|
@@ -60502,9 +60592,12 @@ var codexInstaller = {
|
|
|
60502
60592
|
logger.debug(`[init/codex] project auto-registration skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
60503
60593
|
}
|
|
60504
60594
|
}
|
|
60505
|
-
|
|
60595
|
+
const isLastIteration = ctx.isLastKit === undefined || ctx.isLastKit;
|
|
60596
|
+
const depsTargetDir = codexSkillsDir ?? ctx.codexSkillsDirSeen;
|
|
60597
|
+
const auxAvailable = auxCopySucceeded || ctx.codexAuxCopiedAny === true;
|
|
60598
|
+
if (isLastIteration && depsTargetDir && auxAvailable) {
|
|
60506
60599
|
const depsResult = await promptAndInstallSkillsDeps({
|
|
60507
|
-
skillsDir:
|
|
60600
|
+
skillsDir: depsTargetDir,
|
|
60508
60601
|
isNonInteractive: ctx.isNonInteractive,
|
|
60509
60602
|
installSkills: ctx.installSkills,
|
|
60510
60603
|
withSudo: ctx.options.withSudo,
|
|
@@ -63269,10 +63362,170 @@ async function doctorCommand(options2 = {}) {
|
|
|
63269
63362
|
}
|
|
63270
63363
|
}
|
|
63271
63364
|
|
|
63272
|
-
// src/
|
|
63365
|
+
// src/commands/init/init-command.ts
|
|
63366
|
+
import { join as join102 } from "node:path";
|
|
63367
|
+
|
|
63368
|
+
// src/domains/ui/prompts.ts
|
|
63369
|
+
init_package_installer();
|
|
63370
|
+
init_logger();
|
|
63371
|
+
|
|
63372
|
+
// src/domains/ui/prompts/agent-selection-prompts.ts
|
|
63373
|
+
init_provider_registry();
|
|
63374
|
+
|
|
63375
|
+
// src/services/package-installer/agent-cli-detector.ts
|
|
63376
|
+
init_process_executor();
|
|
63377
|
+
var DETECT_TIMEOUT_MS = 5000;
|
|
63378
|
+
var BINARY_CANDIDATES = {
|
|
63379
|
+
"claude-code": process.platform === "win32" ? ["claude.exe", "claude.cmd", "claude"] : ["claude"],
|
|
63380
|
+
codex: process.platform === "win32" ? ["codex.exe", "codex.cmd", "codex"] : ["codex"]
|
|
63381
|
+
};
|
|
63382
|
+
async function probeBinary(candidates) {
|
|
63383
|
+
for (const bin of candidates) {
|
|
63384
|
+
const needsShell = process.platform === "win32" && /\.(cmd|bat)$/i.test(bin);
|
|
63385
|
+
try {
|
|
63386
|
+
const { stdout } = await execFileAsync5(bin, ["--version"], {
|
|
63387
|
+
timeout: DETECT_TIMEOUT_MS,
|
|
63388
|
+
encoding: "utf8",
|
|
63389
|
+
shell: needsShell
|
|
63390
|
+
});
|
|
63391
|
+
return typeof stdout === "string" ? stdout : String(stdout);
|
|
63392
|
+
} catch {}
|
|
63393
|
+
}
|
|
63394
|
+
return null;
|
|
63395
|
+
}
|
|
63396
|
+
function extractVersion(stdout) {
|
|
63397
|
+
const match2 = stdout.match(/(\d+\.\d+\.\d+(?:[-+][\w.]+)?)/);
|
|
63398
|
+
return match2 ? match2[1] : undefined;
|
|
63399
|
+
}
|
|
63400
|
+
async function detectAgentInstallations(providers2) {
|
|
63401
|
+
const probes = providers2.filter((p) => (p in BINARY_CANDIDATES)).map(async (provider) => {
|
|
63402
|
+
const candidates = BINARY_CANDIDATES[provider];
|
|
63403
|
+
if (!candidates) {
|
|
63404
|
+
return [provider, { installed: false }];
|
|
63405
|
+
}
|
|
63406
|
+
const stdout = await probeBinary(candidates);
|
|
63407
|
+
const detection = stdout ? { installed: true, version: extractVersion(stdout) } : { installed: false };
|
|
63408
|
+
return [provider, detection];
|
|
63409
|
+
});
|
|
63410
|
+
const entries = await Promise.all(probes);
|
|
63411
|
+
const result = {};
|
|
63412
|
+
for (const [provider, detection] of entries) {
|
|
63413
|
+
result[provider] = detection;
|
|
63414
|
+
}
|
|
63415
|
+
return result;
|
|
63416
|
+
}
|
|
63417
|
+
|
|
63418
|
+
// src/domains/ui/prompts/agent-selection-prompts.ts
|
|
63419
|
+
function buildDetectionNote(supported, detections) {
|
|
63420
|
+
const lines = supported.map((provider) => {
|
|
63421
|
+
const detection = detections[provider];
|
|
63422
|
+
const label = providers[provider]?.displayName ?? provider;
|
|
63423
|
+
if (detection?.installed) {
|
|
63424
|
+
const version3 = detection.version ? ` v${detection.version}` : "";
|
|
63425
|
+
return ` ✓ ${label}${version3} detected on PATH`;
|
|
63426
|
+
}
|
|
63427
|
+
return ` ✗ ${label} not detected on PATH`;
|
|
63428
|
+
});
|
|
63429
|
+
lines.push("");
|
|
63430
|
+
lines.push("Init writes config files regardless — detection only informs defaults.");
|
|
63431
|
+
return lines.join(`
|
|
63432
|
+
`);
|
|
63433
|
+
}
|
|
63434
|
+
function computeInitialSelection(supported, detections) {
|
|
63435
|
+
if (detections["claude-code"]?.installed)
|
|
63436
|
+
return ["claude-code"];
|
|
63437
|
+
const firstInstalled = supported.find((p) => detections[p]?.installed);
|
|
63438
|
+
return firstInstalled ? [firstInstalled] : [];
|
|
63439
|
+
}
|
|
63440
|
+
async function promptAgentSelection(supported) {
|
|
63441
|
+
const detections = await detectAgentInstallations(supported);
|
|
63442
|
+
note(buildDetectionNote(supported, detections), "Detected agent CLIs");
|
|
63443
|
+
const options2 = supported.map((provider) => {
|
|
63444
|
+
const detection = detections[provider];
|
|
63445
|
+
const label = providers[provider]?.displayName ?? provider;
|
|
63446
|
+
const hint = detection?.installed ? detection.version ? `installed v${detection.version}` : "installed" : "not detected — config will still be written";
|
|
63447
|
+
return { value: provider, label, hint };
|
|
63448
|
+
});
|
|
63449
|
+
const initialValues = computeInitialSelection(supported, detections);
|
|
63450
|
+
const selected = await ae({
|
|
63451
|
+
message: "Select agent(s) to initialize",
|
|
63452
|
+
options: options2,
|
|
63453
|
+
required: true,
|
|
63454
|
+
initialValues
|
|
63455
|
+
});
|
|
63456
|
+
if (lD(selected))
|
|
63457
|
+
return null;
|
|
63458
|
+
return selected;
|
|
63459
|
+
}
|
|
63460
|
+
// src/domains/ui/prompts/kit-prompts.ts
|
|
63461
|
+
init_logger();
|
|
63462
|
+
init_types2();
|
|
63463
|
+
async function selectKit(defaultKit, accessibleKits) {
|
|
63464
|
+
const kits = accessibleKits ?? Object.keys(AVAILABLE_KITS);
|
|
63465
|
+
const kit = await ie({
|
|
63466
|
+
message: "Select a Takumi:",
|
|
63467
|
+
options: kits.map((key) => ({
|
|
63468
|
+
value: key,
|
|
63469
|
+
label: AVAILABLE_KITS[key].name,
|
|
63470
|
+
hint: AVAILABLE_KITS[key].description
|
|
63471
|
+
})),
|
|
63472
|
+
initialValue: defaultKit
|
|
63473
|
+
});
|
|
63474
|
+
if (lD(kit)) {
|
|
63475
|
+
throw new Error("Kit selection cancelled");
|
|
63476
|
+
}
|
|
63477
|
+
return kit;
|
|
63478
|
+
}
|
|
63479
|
+
async function selectKits(accessibleKits, defaults2) {
|
|
63480
|
+
const kits = accessibleKits ?? Object.keys(AVAILABLE_KITS);
|
|
63481
|
+
const options2 = kits.map((key) => {
|
|
63482
|
+
const isBase = key === BASE_KIT;
|
|
63483
|
+
const cfg = AVAILABLE_KITS[key];
|
|
63484
|
+
return {
|
|
63485
|
+
value: key,
|
|
63486
|
+
label: isBase ? `${cfg.name} (base, required)` : cfg.name,
|
|
63487
|
+
hint: cfg.description
|
|
63488
|
+
};
|
|
63489
|
+
});
|
|
63490
|
+
const initialValues = defaults2 && defaults2.length > 0 ? Array.from(new Set([BASE_KIT, ...defaults2])).filter((k2) => kits.includes(k2)) : kits;
|
|
63491
|
+
const selected = await ae({
|
|
63492
|
+
message: "Select kit(s) to install:",
|
|
63493
|
+
options: options2,
|
|
63494
|
+
initialValues,
|
|
63495
|
+
required: true
|
|
63496
|
+
});
|
|
63497
|
+
if (lD(selected)) {
|
|
63498
|
+
throw new Error("Kit selection cancelled");
|
|
63499
|
+
}
|
|
63500
|
+
const result = selected;
|
|
63501
|
+
if (!result.includes(BASE_KIT) && kits.includes(BASE_KIT)) {
|
|
63502
|
+
logger.info(`"${AVAILABLE_KITS[BASE_KIT].name}" is the base kit and was added back automatically.`);
|
|
63503
|
+
result.unshift(BASE_KIT);
|
|
63504
|
+
}
|
|
63505
|
+
const idx = result.indexOf(BASE_KIT);
|
|
63506
|
+
if (idx > 0) {
|
|
63507
|
+
result.splice(idx, 1);
|
|
63508
|
+
result.unshift(BASE_KIT);
|
|
63509
|
+
}
|
|
63510
|
+
return result;
|
|
63511
|
+
}
|
|
63512
|
+
async function getDirectory(defaultDir = ".") {
|
|
63513
|
+
const dir = await te({
|
|
63514
|
+
message: "Enter target directory:",
|
|
63515
|
+
placeholder: `Press Enter for "${defaultDir}"`,
|
|
63516
|
+
validate: () => {
|
|
63517
|
+
return;
|
|
63518
|
+
}
|
|
63519
|
+
});
|
|
63520
|
+
if (lD(dir)) {
|
|
63521
|
+
throw new Error("Directory input cancelled");
|
|
63522
|
+
}
|
|
63523
|
+
const trimmed = (dir ?? "").trim();
|
|
63524
|
+
return trimmed.length > 0 ? trimmed : defaultDir;
|
|
63525
|
+
}
|
|
63526
|
+
// src/domains/versioning/version-selector.ts
|
|
63273
63527
|
init_auth_client();
|
|
63274
|
-
|
|
63275
|
-
import { join as join86 } from "node:path";
|
|
63528
|
+
init_github_client();
|
|
63276
63529
|
|
|
63277
63530
|
// src/domains/installation/release-source.ts
|
|
63278
63531
|
init_auth_client();
|
|
@@ -63383,900 +63636,198 @@ class WorkerSource {
|
|
|
63383
63636
|
}
|
|
63384
63637
|
}
|
|
63385
63638
|
|
|
63386
|
-
// src/domains/
|
|
63387
|
-
|
|
63388
|
-
|
|
63389
|
-
|
|
63390
|
-
|
|
63391
|
-
|
|
63392
|
-
return
|
|
63393
|
-
|
|
63394
|
-
|
|
63395
|
-
|
|
63396
|
-
|
|
63397
|
-
|
|
63639
|
+
// src/domains/installation/release-entry-adapter.ts
|
|
63640
|
+
function githubReleaseToEntry(release, kit = "engineer") {
|
|
63641
|
+
const expected = assetNameFor(kit);
|
|
63642
|
+
const expectedAsset = release.assets.find((a3) => a3.name === expected);
|
|
63643
|
+
const fallback2 = release.assets[0];
|
|
63644
|
+
const archive = expectedAsset?.name ?? fallback2?.name ?? expected;
|
|
63645
|
+
return {
|
|
63646
|
+
tag: release.tag_name,
|
|
63647
|
+
version: release.tag_name.replace(/^v/, ""),
|
|
63648
|
+
prerelease: release.prerelease,
|
|
63649
|
+
publishedAt: release.published_at ?? "",
|
|
63650
|
+
archive
|
|
63651
|
+
};
|
|
63398
63652
|
}
|
|
63399
|
-
function
|
|
63400
|
-
const normalized = normalizeVersion(version3);
|
|
63401
|
-
const [base, ...prereleaseParts] = normalized.split("-");
|
|
63653
|
+
function releaseEntryToGitHubRelease(entry, kit) {
|
|
63402
63654
|
return {
|
|
63403
|
-
|
|
63404
|
-
|
|
63655
|
+
id: 0,
|
|
63656
|
+
tag_name: entry.tag,
|
|
63657
|
+
name: entry.tag,
|
|
63658
|
+
draft: false,
|
|
63659
|
+
prerelease: entry.prerelease,
|
|
63660
|
+
published_at: entry.publishedAt || undefined,
|
|
63661
|
+
tarball_url: `https://github.com/${kit.owner}/${kit.repo}/archive/refs/tags/${entry.tag}.tar.gz`,
|
|
63662
|
+
zipball_url: `https://github.com/${kit.owner}/${kit.repo}/archive/refs/tags/${entry.tag}.zip`,
|
|
63663
|
+
assets: []
|
|
63405
63664
|
};
|
|
63406
63665
|
}
|
|
63407
|
-
|
|
63408
|
-
|
|
63409
|
-
|
|
63410
|
-
|
|
63411
|
-
|
|
63412
|
-
|
|
63666
|
+
|
|
63667
|
+
// src/domains/versioning/version-selector.ts
|
|
63668
|
+
init_logger();
|
|
63669
|
+
init_dist2();
|
|
63670
|
+
init_release_filter();
|
|
63671
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
63672
|
+
|
|
63673
|
+
// src/domains/versioning/selection/version-filter.ts
|
|
63674
|
+
var VERSION_PATTERN = /^v?\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/;
|
|
63675
|
+
function isValidVersionFormat(version3) {
|
|
63676
|
+
const trimmed = version3.trim();
|
|
63677
|
+
if (!trimmed)
|
|
63413
63678
|
return false;
|
|
63414
|
-
return
|
|
63415
|
-
}
|
|
63416
|
-
function versionsMatch(installed, latest) {
|
|
63417
|
-
return normalizeVersion(installed) === normalizeVersion(latest);
|
|
63679
|
+
return VERSION_PATTERN.test(trimmed);
|
|
63418
63680
|
}
|
|
63419
|
-
function
|
|
63420
|
-
|
|
63421
|
-
|
|
63422
|
-
const latest = normalizeVersion(latestVersion);
|
|
63423
|
-
if (isPrereleaseOfSameBase(current, latest)) {
|
|
63424
|
-
return false;
|
|
63425
|
-
}
|
|
63426
|
-
return import_compare_versions2.compareVersions(latest, current) > 0;
|
|
63427
|
-
} catch {
|
|
63428
|
-
return false;
|
|
63429
|
-
}
|
|
63681
|
+
function normalizeVersionTag(version3) {
|
|
63682
|
+
const trimmed = version3.trim();
|
|
63683
|
+
return trimmed.startsWith("v") ? trimmed : `v${trimmed}`;
|
|
63430
63684
|
}
|
|
63431
|
-
|
|
63432
|
-
|
|
63685
|
+
// src/domains/versioning/selection/selection-ui.ts
|
|
63686
|
+
init_environment();
|
|
63433
63687
|
init_logger();
|
|
63434
|
-
|
|
63435
|
-
var
|
|
63436
|
-
var DEFAULT_CACHE_TTL_MS = CACHE_TTL_HOURS * 60 * 60 * 1000;
|
|
63437
|
-
var MIN_CACHE_TTL_MS = 60 * 1000;
|
|
63438
|
-
var MAX_CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
63439
|
-
function parseCacheTtl() {
|
|
63440
|
-
const envValue = process.env.TAKUMI_SYNC_CACHE_TTL;
|
|
63441
|
-
if (!envValue) {
|
|
63442
|
-
return DEFAULT_CACHE_TTL_MS;
|
|
63443
|
-
}
|
|
63444
|
-
const parsed = Number.parseInt(envValue, 10);
|
|
63445
|
-
if (Number.isNaN(parsed) || parsed < 0) {
|
|
63446
|
-
logger.warning(`Invalid TAKUMI_SYNC_CACHE_TTL value "${envValue}", using default (${CACHE_TTL_HOURS}h)`);
|
|
63447
|
-
return DEFAULT_CACHE_TTL_MS;
|
|
63448
|
-
}
|
|
63449
|
-
const ttlMs = parsed * 1000;
|
|
63450
|
-
if (ttlMs < MIN_CACHE_TTL_MS) {
|
|
63451
|
-
logger.warning(`TAKUMI_SYNC_CACHE_TTL too low (${parsed}s), using minimum (60s)`);
|
|
63452
|
-
return MIN_CACHE_TTL_MS;
|
|
63453
|
-
}
|
|
63454
|
-
if (ttlMs > MAX_CACHE_TTL_MS) {
|
|
63455
|
-
logger.warning(`TAKUMI_SYNC_CACHE_TTL too high (${parsed}s), using maximum (7 days)`);
|
|
63456
|
-
return MAX_CACHE_TTL_MS;
|
|
63457
|
-
}
|
|
63458
|
-
return ttlMs;
|
|
63459
|
-
}
|
|
63460
|
-
var CACHE_TTL_MS = parseCacheTtl();
|
|
63461
|
-
var CACHE_FILENAME = "config-update-cache.json";
|
|
63688
|
+
init_dist2();
|
|
63689
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
63462
63690
|
|
|
63463
|
-
|
|
63464
|
-
|
|
63465
|
-
|
|
63466
|
-
|
|
63467
|
-
|
|
63468
|
-
|
|
63469
|
-
|
|
63470
|
-
|
|
63471
|
-
|
|
63472
|
-
|
|
63473
|
-
if (
|
|
63474
|
-
|
|
63475
|
-
|
|
63691
|
+
// src/domains/versioning/version-display.ts
|
|
63692
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
63693
|
+
|
|
63694
|
+
class VersionDisplayFormatter {
|
|
63695
|
+
static createBadges(release) {
|
|
63696
|
+
const badges = [];
|
|
63697
|
+
if (release.isLatestStable) {
|
|
63698
|
+
badges.push(import_picocolors18.default.bold(import_picocolors18.default.yellow("[latest]")));
|
|
63699
|
+
}
|
|
63700
|
+
if (release.prerelease || release.isLatestBeta) {
|
|
63701
|
+
if (release.isLatestBeta) {
|
|
63702
|
+
badges.push(import_picocolors18.default.bold(import_picocolors18.default.magenta("[beta]")));
|
|
63703
|
+
} else {
|
|
63704
|
+
badges.push(import_picocolors18.default.magenta("[prerelease]"));
|
|
63476
63705
|
}
|
|
63477
|
-
|
|
63478
|
-
|
|
63479
|
-
return null;
|
|
63706
|
+
} else if (!release.draft) {
|
|
63707
|
+
badges.push(import_picocolors18.default.blue("[stable]"));
|
|
63480
63708
|
}
|
|
63481
|
-
|
|
63482
|
-
|
|
63483
|
-
try {
|
|
63484
|
-
const cachePath = ConfigVersionChecker.getCacheFilePath(kitType, global3);
|
|
63485
|
-
const cacheDir = PathResolver.getCacheDir(global3);
|
|
63486
|
-
await mkdir22(cacheDir, { recursive: true });
|
|
63487
|
-
await writeFile29(cachePath, JSON.stringify(cache, null, 2));
|
|
63488
|
-
} catch (error) {
|
|
63489
|
-
logger.debug(`Cache write failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
63709
|
+
if (release.draft) {
|
|
63710
|
+
badges.push(import_picocolors18.default.gray("[draft]"));
|
|
63490
63711
|
}
|
|
63712
|
+
return badges.length > 0 ? ` ${badges.join(" ")}` : "";
|
|
63491
63713
|
}
|
|
63492
|
-
static
|
|
63493
|
-
const
|
|
63494
|
-
const
|
|
63495
|
-
|
|
63496
|
-
|
|
63497
|
-
const worker = new WorkerSource(getServerUrl(), kitType);
|
|
63498
|
-
const entry = await worker.fetchLatest();
|
|
63499
|
-
const version3 = entry.version.replace(/^v/, "");
|
|
63500
|
-
if (!version3 || version3.length > 256) {
|
|
63501
|
-
logger.debug(`Invalid version format from Worker: ${entry.version}`);
|
|
63502
|
-
return null;
|
|
63503
|
-
}
|
|
63504
|
-
const semverParts = version3.split(/[-+]/);
|
|
63505
|
-
const coreParts = semverParts[0].split(".");
|
|
63506
|
-
if (coreParts.length !== 3 || !coreParts.every((p) => /^\d+$/.test(p))) {
|
|
63507
|
-
logger.debug(`Invalid version format from Worker: ${entry.version}`);
|
|
63508
|
-
return null;
|
|
63509
|
-
}
|
|
63510
|
-
return { version: version3 };
|
|
63511
|
-
} catch (error) {
|
|
63512
|
-
if (error instanceof NotLoggedInError) {
|
|
63513
|
-
logger.debug("Skipping update check (not logged in)");
|
|
63514
|
-
return null;
|
|
63515
|
-
}
|
|
63516
|
-
const isLastAttempt = attempt === maxRetries - 1;
|
|
63517
|
-
if (isLastAttempt) {
|
|
63518
|
-
logger.debug(`Version check failed after ${maxRetries} attempts: ${error}`);
|
|
63519
|
-
return null;
|
|
63520
|
-
}
|
|
63521
|
-
const delay2 = baseBackoff * 2 ** attempt;
|
|
63522
|
-
await new Promise((resolve17) => setTimeout(resolve17, delay2));
|
|
63523
|
-
}
|
|
63524
|
-
}
|
|
63525
|
-
return null;
|
|
63714
|
+
static formatChoiceLabel(release) {
|
|
63715
|
+
const version3 = import_picocolors18.default.green(release.displayVersion);
|
|
63716
|
+
const badges = VersionDisplayFormatter.createBadges(release);
|
|
63717
|
+
const name = release.name || "Release";
|
|
63718
|
+
return `${version3}${badges} ${import_picocolors18.default.dim(name)}`;
|
|
63526
63719
|
}
|
|
63527
|
-
static
|
|
63528
|
-
const
|
|
63529
|
-
|
|
63530
|
-
|
|
63531
|
-
if (cache && now - cache.lastCheck < CACHE_TTL_MS) {
|
|
63532
|
-
const hasUpdates = isNewerVersion(normalizedCurrent, cache.latestVersion);
|
|
63533
|
-
return {
|
|
63534
|
-
hasUpdates,
|
|
63535
|
-
currentVersion: normalizedCurrent,
|
|
63536
|
-
latestVersion: cache.latestVersion,
|
|
63537
|
-
fromCache: true
|
|
63538
|
-
};
|
|
63720
|
+
static formatChoiceHint(release) {
|
|
63721
|
+
const parts = [];
|
|
63722
|
+
if (release.relativeTime && release.relativeTime !== "Unknown") {
|
|
63723
|
+
parts.push(release.relativeTime);
|
|
63539
63724
|
}
|
|
63540
|
-
|
|
63541
|
-
|
|
63542
|
-
|
|
63543
|
-
|
|
63544
|
-
|
|
63725
|
+
if (release.assetCount > 0) {
|
|
63726
|
+
const assetText = release.assetCount === 1 ? "asset" : "assets";
|
|
63727
|
+
parts.push(`${release.assetCount} ${assetText}`);
|
|
63728
|
+
}
|
|
63729
|
+
if (release.normalizedVersion !== release.displayVersion) {
|
|
63730
|
+
parts.push(`(${release.normalizedVersion})`);
|
|
63731
|
+
}
|
|
63732
|
+
return parts.length > 0 ? parts.join(", ") : "";
|
|
63733
|
+
}
|
|
63734
|
+
static createSpecialOptions(releases) {
|
|
63735
|
+
const options2 = [];
|
|
63736
|
+
const latestStable = releases.find((r2) => r2.isLatestStable && !r2.prerelease);
|
|
63737
|
+
if (latestStable) {
|
|
63738
|
+
options2.push({
|
|
63739
|
+
value: latestStable.tag_name,
|
|
63740
|
+
label: `${import_picocolors18.default.bold(import_picocolors18.default.green("Latest Stable"))} (${latestStable.displayVersion})`,
|
|
63741
|
+
hint: "recommended version",
|
|
63742
|
+
isLatest: true,
|
|
63743
|
+
isPrerelease: false
|
|
63545
63744
|
});
|
|
63546
|
-
const hasUpdates = isNewerVersion(normalizedCurrent, result.version);
|
|
63547
|
-
return {
|
|
63548
|
-
hasUpdates,
|
|
63549
|
-
currentVersion: normalizedCurrent,
|
|
63550
|
-
latestVersion: result.version,
|
|
63551
|
-
fromCache: false
|
|
63552
|
-
};
|
|
63553
63745
|
}
|
|
63554
|
-
|
|
63555
|
-
|
|
63556
|
-
|
|
63557
|
-
|
|
63558
|
-
|
|
63559
|
-
|
|
63560
|
-
|
|
63561
|
-
|
|
63746
|
+
const latestBeta = releases.find((r2) => r2.isLatestBeta || r2.prerelease && !r2.draft);
|
|
63747
|
+
if (latestBeta) {
|
|
63748
|
+
options2.push({
|
|
63749
|
+
value: latestBeta.tag_name,
|
|
63750
|
+
label: `${import_picocolors18.default.bold(import_picocolors18.default.magenta("Latest Beta"))} (${latestBeta.displayVersion})`,
|
|
63751
|
+
hint: "latest features, may be unstable",
|
|
63752
|
+
isLatest: false,
|
|
63753
|
+
isPrerelease: true
|
|
63754
|
+
});
|
|
63562
63755
|
}
|
|
63756
|
+
return options2;
|
|
63757
|
+
}
|
|
63758
|
+
static createSeparator() {
|
|
63563
63759
|
return {
|
|
63564
|
-
|
|
63565
|
-
|
|
63566
|
-
|
|
63567
|
-
|
|
63760
|
+
value: "separator",
|
|
63761
|
+
label: import_picocolors18.default.dim("─".repeat(50)),
|
|
63762
|
+
hint: undefined,
|
|
63763
|
+
isLatest: false,
|
|
63764
|
+
isPrerelease: false
|
|
63568
63765
|
};
|
|
63569
63766
|
}
|
|
63570
|
-
static
|
|
63571
|
-
|
|
63572
|
-
|
|
63573
|
-
|
|
63574
|
-
|
|
63575
|
-
|
|
63576
|
-
|
|
63577
|
-
|
|
63767
|
+
static createCancelOption() {
|
|
63768
|
+
return {
|
|
63769
|
+
value: "cancel",
|
|
63770
|
+
label: import_picocolors18.default.red("Cancel"),
|
|
63771
|
+
hint: "exit version selection",
|
|
63772
|
+
isLatest: false,
|
|
63773
|
+
isPrerelease: false
|
|
63774
|
+
};
|
|
63775
|
+
}
|
|
63776
|
+
static formatVersionChoice(release) {
|
|
63777
|
+
return {
|
|
63778
|
+
value: release.tag_name,
|
|
63779
|
+
label: VersionDisplayFormatter.formatChoiceLabel(release),
|
|
63780
|
+
hint: VersionDisplayFormatter.formatChoiceHint(release),
|
|
63781
|
+
isLatest: release.isLatestStable,
|
|
63782
|
+
isPrerelease: release.prerelease
|
|
63783
|
+
};
|
|
63784
|
+
}
|
|
63785
|
+
static formatReleasesToChoices(releases, includeSpecialOptions = true, limit = 30) {
|
|
63786
|
+
const choices = [];
|
|
63787
|
+
if (includeSpecialOptions) {
|
|
63788
|
+
const specialOptions = VersionDisplayFormatter.createSpecialOptions(releases);
|
|
63789
|
+
choices.push(...specialOptions);
|
|
63790
|
+
if (specialOptions.length > 0 && releases.length > 0) {
|
|
63791
|
+
choices.push(VersionDisplayFormatter.createSeparator());
|
|
63578
63792
|
}
|
|
63579
63793
|
}
|
|
63794
|
+
const limitedReleases = releases.slice(0, limit);
|
|
63795
|
+
for (const release of limitedReleases) {
|
|
63796
|
+
choices.push(VersionDisplayFormatter.formatVersionChoice(release));
|
|
63797
|
+
}
|
|
63798
|
+
if (includeSpecialOptions) {
|
|
63799
|
+
choices.push(VersionDisplayFormatter.createSeparator());
|
|
63800
|
+
choices.push(VersionDisplayFormatter.createCancelOption());
|
|
63801
|
+
}
|
|
63802
|
+
return choices;
|
|
63580
63803
|
}
|
|
63581
|
-
|
|
63582
|
-
|
|
63583
|
-
|
|
63584
|
-
|
|
63585
|
-
|
|
63586
|
-
var MAX_SYNC_FILE_SIZE = 10 * 1024 * 1024;
|
|
63587
|
-
var MAX_SYMLINK_DEPTH = 20;
|
|
63588
|
-
async function validateSymlinkChain(path8, basePath, maxDepth = MAX_SYMLINK_DEPTH) {
|
|
63589
|
-
let current = path8;
|
|
63590
|
-
let depth = 0;
|
|
63591
|
-
while (depth < maxDepth) {
|
|
63592
|
-
try {
|
|
63593
|
-
const stats = await lstat6(current);
|
|
63594
|
-
if (!stats.isSymbolicLink())
|
|
63595
|
-
break;
|
|
63596
|
-
const target = await readlink(current);
|
|
63597
|
-
const resolvedTarget = isAbsolute3(target) ? target : join87(current, "..", target);
|
|
63598
|
-
const normalizedTarget = normalize8(resolvedTarget);
|
|
63599
|
-
const rel = relative14(basePath, normalizedTarget);
|
|
63600
|
-
if (rel.startsWith("..") || isAbsolute3(rel)) {
|
|
63601
|
-
throw new Error(`Symlink chain escapes base directory at depth ${depth}: ${path8}`);
|
|
63804
|
+
static getDefaultChoiceIndex(choices) {
|
|
63805
|
+
for (let i = 0;i < choices.length; i++) {
|
|
63806
|
+
const choice = choices[i];
|
|
63807
|
+
if (choice.isLatest && !choice.isPrerelease) {
|
|
63808
|
+
return i;
|
|
63602
63809
|
}
|
|
63603
|
-
|
|
63604
|
-
|
|
63605
|
-
|
|
63606
|
-
|
|
63607
|
-
throw error;
|
|
63810
|
+
}
|
|
63811
|
+
for (let i = 0;i < choices.length; i++) {
|
|
63812
|
+
if (choices[i].value !== "separator") {
|
|
63813
|
+
return i;
|
|
63608
63814
|
}
|
|
63609
|
-
break;
|
|
63610
63815
|
}
|
|
63816
|
+
return 0;
|
|
63611
63817
|
}
|
|
63612
|
-
|
|
63613
|
-
|
|
63614
|
-
}
|
|
63615
|
-
}
|
|
63616
|
-
async function validateSyncPath(basePath, filePath) {
|
|
63617
|
-
if (!filePath || filePath.trim() === "") {
|
|
63618
|
-
throw new Error("Empty file path not allowed");
|
|
63619
|
-
}
|
|
63620
|
-
if (filePath.includes("\x00")) {
|
|
63621
|
-
throw new Error(`Invalid file path (null byte): ${filePath}`);
|
|
63622
|
-
}
|
|
63623
|
-
if (filePath.length > 1024) {
|
|
63624
|
-
throw new Error(`Path too long: ${filePath.slice(0, 50)}...`);
|
|
63625
|
-
}
|
|
63626
|
-
const normalized = normalize8(filePath);
|
|
63627
|
-
if (isAbsolute3(normalized)) {
|
|
63628
|
-
throw new Error(`Absolute paths not allowed: ${filePath}`);
|
|
63629
|
-
}
|
|
63630
|
-
if (normalized.startsWith("..") || normalized.includes("/../")) {
|
|
63631
|
-
throw new Error(`Path traversal not allowed: ${filePath}`);
|
|
63632
|
-
}
|
|
63633
|
-
const fullPath = join87(basePath, normalized);
|
|
63634
|
-
const rel = relative14(basePath, fullPath);
|
|
63635
|
-
if (rel.startsWith("..") || isAbsolute3(rel)) {
|
|
63636
|
-
throw new Error(`Path escapes base directory: ${filePath}`);
|
|
63637
|
-
}
|
|
63638
|
-
await validateSymlinkChain(fullPath, basePath);
|
|
63639
|
-
try {
|
|
63640
|
-
const resolvedBase = await realpath3(basePath);
|
|
63641
|
-
const resolvedFull = await realpath3(fullPath);
|
|
63642
|
-
const resolvedRel = relative14(resolvedBase, resolvedFull);
|
|
63643
|
-
if (resolvedRel.startsWith("..") || isAbsolute3(resolvedRel)) {
|
|
63644
|
-
throw new Error(`Symlink escapes base directory: ${filePath}`);
|
|
63645
|
-
}
|
|
63646
|
-
} catch (error) {
|
|
63647
|
-
if (error.code === "ENOENT") {
|
|
63648
|
-
const parentPath = join87(fullPath, "..");
|
|
63649
|
-
try {
|
|
63650
|
-
const resolvedBase = await realpath3(basePath);
|
|
63651
|
-
const resolvedParent = await realpath3(parentPath);
|
|
63652
|
-
const resolvedRel = relative14(resolvedBase, resolvedParent);
|
|
63653
|
-
if (resolvedRel.startsWith("..") || isAbsolute3(resolvedRel)) {
|
|
63654
|
-
throw new Error(`Parent symlink escapes base directory: ${filePath}`);
|
|
63655
|
-
}
|
|
63656
|
-
} catch (parentError) {
|
|
63657
|
-
if (parentError.code !== "ENOENT") {
|
|
63658
|
-
throw parentError;
|
|
63659
|
-
}
|
|
63660
|
-
}
|
|
63661
|
-
} else {
|
|
63662
|
-
throw error;
|
|
63663
|
-
}
|
|
63664
|
-
}
|
|
63665
|
-
return fullPath;
|
|
63666
|
-
}
|
|
63667
|
-
|
|
63668
|
-
class SyncEngine {
|
|
63669
|
-
static async createSyncPlan(trackedFiles, claudeDir, upstreamDir) {
|
|
63670
|
-
const plan = { autoUpdate: [], needsReview: [], skipped: [] };
|
|
63671
|
-
for (const file of trackedFiles) {
|
|
63672
|
-
if (file.ownership === "user") {
|
|
63673
|
-
plan.skipped.push(file);
|
|
63674
|
-
continue;
|
|
63675
|
-
}
|
|
63676
|
-
let upstreamPath;
|
|
63677
|
-
try {
|
|
63678
|
-
upstreamPath = await validateSyncPath(upstreamDir, file.path);
|
|
63679
|
-
} catch (error) {
|
|
63680
|
-
logger.warning(`Skipping invalid path: ${file.path}`);
|
|
63681
|
-
plan.skipped.push(file);
|
|
63682
|
-
continue;
|
|
63683
|
-
}
|
|
63684
|
-
try {
|
|
63685
|
-
await stat8(upstreamPath);
|
|
63686
|
-
} catch {
|
|
63687
|
-
plan.skipped.push(file);
|
|
63688
|
-
continue;
|
|
63689
|
-
}
|
|
63690
|
-
let localPath;
|
|
63691
|
-
try {
|
|
63692
|
-
localPath = await validateSyncPath(claudeDir, file.path);
|
|
63693
|
-
} catch (error) {
|
|
63694
|
-
logger.warning(`Skipping invalid local path: ${file.path}`);
|
|
63695
|
-
plan.skipped.push(file);
|
|
63696
|
-
continue;
|
|
63697
|
-
}
|
|
63698
|
-
try {
|
|
63699
|
-
await stat8(localPath);
|
|
63700
|
-
} catch {
|
|
63701
|
-
plan.autoUpdate.push(file);
|
|
63702
|
-
continue;
|
|
63703
|
-
}
|
|
63704
|
-
if (file.ownership === "takumi") {
|
|
63705
|
-
plan.autoUpdate.push(file);
|
|
63706
|
-
continue;
|
|
63707
|
-
}
|
|
63708
|
-
const currentChecksum = await OwnershipChecker.calculateChecksum(localPath);
|
|
63709
|
-
const baseChecksum = file.baseChecksum || file.checksum;
|
|
63710
|
-
if (currentChecksum === baseChecksum) {
|
|
63711
|
-
plan.autoUpdate.push(file);
|
|
63712
|
-
} else {
|
|
63713
|
-
plan.needsReview.push(file);
|
|
63714
|
-
}
|
|
63715
|
-
}
|
|
63716
|
-
return plan;
|
|
63717
|
-
}
|
|
63718
|
-
static generateHunks(currentContent, newContent, filename, contextLines = 3) {
|
|
63719
|
-
const patch = structuredPatch(filename, filename, currentContent, newContent, "", "", {
|
|
63720
|
-
context: contextLines
|
|
63721
|
-
});
|
|
63722
|
-
return patch.hunks.map((hunk) => ({
|
|
63723
|
-
oldStart: hunk.oldStart,
|
|
63724
|
-
oldLines: hunk.oldLines,
|
|
63725
|
-
newStart: hunk.newStart,
|
|
63726
|
-
newLines: hunk.newLines,
|
|
63727
|
-
lines: hunk.lines
|
|
63728
|
-
}));
|
|
63729
|
-
}
|
|
63730
|
-
static applyHunks(content, hunks, accepted) {
|
|
63731
|
-
const acceptedHunks = hunks.filter((_3, i) => accepted[i]);
|
|
63732
|
-
if (acceptedHunks.length === 0) {
|
|
63733
|
-
return content;
|
|
63734
|
-
}
|
|
63735
|
-
const patchStr = SyncEngine.buildUnifiedDiff(content, acceptedHunks);
|
|
63736
|
-
const result = applyPatch(content, patchStr);
|
|
63737
|
-
if (result === false) {
|
|
63738
|
-
try {
|
|
63739
|
-
return SyncEngine.applyHunksManually(content, acceptedHunks);
|
|
63740
|
-
} catch (fallbackError) {
|
|
63741
|
-
throw new Error(`Failed to apply ${acceptedHunks.length} hunk(s): patch and manual methods both failed. ` + `Manual error: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`);
|
|
63742
|
-
}
|
|
63743
|
-
}
|
|
63744
|
-
return result;
|
|
63745
|
-
}
|
|
63746
|
-
static buildUnifiedDiff(_content, hunks) {
|
|
63747
|
-
let diff = `--- a
|
|
63748
|
-
+++ b
|
|
63749
|
-
`;
|
|
63750
|
-
for (const hunk of hunks) {
|
|
63751
|
-
diff += `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@
|
|
63752
|
-
`;
|
|
63753
|
-
for (const line of hunk.lines) {
|
|
63754
|
-
diff += `${line}
|
|
63755
|
-
`;
|
|
63756
|
-
}
|
|
63757
|
-
}
|
|
63758
|
-
return diff;
|
|
63759
|
-
}
|
|
63760
|
-
static applyHunksManually(content, hunks) {
|
|
63761
|
-
const lines = content.split(`
|
|
63762
|
-
`);
|
|
63763
|
-
const sortedHunks = [...hunks].sort((a3, b3) => b3.oldStart - a3.oldStart);
|
|
63764
|
-
for (const hunk of sortedHunks) {
|
|
63765
|
-
const startIndex = hunk.oldStart - 1;
|
|
63766
|
-
if (startIndex < 0 || startIndex > lines.length) {
|
|
63767
|
-
logger.warning(`Hunk start ${hunk.oldStart} out of bounds (file has ${lines.length} lines)`);
|
|
63768
|
-
continue;
|
|
63769
|
-
}
|
|
63770
|
-
const newLines = [];
|
|
63771
|
-
let deleteCount = 0;
|
|
63772
|
-
for (const line of hunk.lines) {
|
|
63773
|
-
if (!line || line.length === 0) {
|
|
63774
|
-
continue;
|
|
63775
|
-
}
|
|
63776
|
-
const prefix = line[0];
|
|
63777
|
-
const lineContent = line.slice(1);
|
|
63778
|
-
if (prefix === "-") {
|
|
63779
|
-
deleteCount++;
|
|
63780
|
-
} else if (prefix === "+" || prefix === " ") {
|
|
63781
|
-
newLines.push(lineContent);
|
|
63782
|
-
}
|
|
63783
|
-
}
|
|
63784
|
-
if (startIndex + deleteCount > lines.length) {
|
|
63785
|
-
logger.warning(`Hunk would delete past EOF (start: ${startIndex}, delete: ${deleteCount}, lines: ${lines.length})`);
|
|
63786
|
-
continue;
|
|
63787
|
-
}
|
|
63788
|
-
lines.splice(startIndex, deleteCount, ...newLines);
|
|
63789
|
-
}
|
|
63790
|
-
return lines.join(`
|
|
63791
|
-
`);
|
|
63792
|
-
}
|
|
63793
|
-
static isBinaryFile(content) {
|
|
63794
|
-
if (content.length === 0) {
|
|
63795
|
-
return false;
|
|
63796
|
-
}
|
|
63797
|
-
const sampleSize = Math.min(content.length, 8192);
|
|
63798
|
-
let nonPrintableCount = 0;
|
|
63799
|
-
for (let i = 0;i < sampleSize; i++) {
|
|
63800
|
-
const code = content.charCodeAt(i);
|
|
63801
|
-
if (code === 0) {
|
|
63802
|
-
return true;
|
|
63803
|
-
}
|
|
63804
|
-
if (code < 32 && code !== 9 && code !== 10 && code !== 13) {
|
|
63805
|
-
nonPrintableCount++;
|
|
63806
|
-
}
|
|
63807
|
-
}
|
|
63808
|
-
return nonPrintableCount / sampleSize > 0.1;
|
|
63809
|
-
}
|
|
63810
|
-
static async loadFileContent(filePath) {
|
|
63811
|
-
try {
|
|
63812
|
-
const lstats = await lstat6(filePath);
|
|
63813
|
-
if (lstats.isSymbolicLink()) {
|
|
63814
|
-
throw new Error(`Symlink not allowed for sync: ${filePath}`);
|
|
63815
|
-
}
|
|
63816
|
-
if (lstats.size > MAX_SYNC_FILE_SIZE) {
|
|
63817
|
-
throw new Error(`File too large for sync (${Math.round(lstats.size / 1024 / 1024)}MB > ${MAX_SYNC_FILE_SIZE / 1024 / 1024}MB limit)`);
|
|
63818
|
-
}
|
|
63819
|
-
const buffer = await readFile39(filePath);
|
|
63820
|
-
if (buffer.includes(0)) {
|
|
63821
|
-
return { content: "", isBinary: true };
|
|
63822
|
-
}
|
|
63823
|
-
const content = buffer.toString("utf8");
|
|
63824
|
-
if (content.includes("�")) {
|
|
63825
|
-
return { content: "", isBinary: true };
|
|
63826
|
-
}
|
|
63827
|
-
if (SyncEngine.isBinaryFile(content)) {
|
|
63828
|
-
return { content: "", isBinary: true };
|
|
63829
|
-
}
|
|
63830
|
-
return { content, isBinary: false };
|
|
63831
|
-
} catch (error) {
|
|
63832
|
-
const errMsg = error instanceof Error ? error.message : "Unknown error";
|
|
63833
|
-
throw new Error(`Cannot read file for sync: ${filePath} - ${errMsg}`);
|
|
63834
|
-
}
|
|
63835
|
-
}
|
|
63836
|
-
}
|
|
63837
|
-
// src/domains/sync/deletion-path-filter.ts
|
|
63838
|
-
init_path_resolver();
|
|
63839
|
-
var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
63840
|
-
function filterDeletionPaths(trackedFiles, deletions) {
|
|
63841
|
-
if (!deletions || deletions.length === 0) {
|
|
63842
|
-
return trackedFiles;
|
|
63843
|
-
}
|
|
63844
|
-
const exactPaths = new Set;
|
|
63845
|
-
const globMatchers = [];
|
|
63846
|
-
for (const pattern of deletions) {
|
|
63847
|
-
if (PathResolver.isGlobPattern(pattern)) {
|
|
63848
|
-
globMatchers.push(import_picomatch2.default(pattern));
|
|
63849
|
-
} else {
|
|
63850
|
-
exactPaths.add(pattern);
|
|
63851
|
-
}
|
|
63852
|
-
}
|
|
63853
|
-
return trackedFiles.filter((file) => {
|
|
63854
|
-
if (exactPaths.has(file.path))
|
|
63855
|
-
return false;
|
|
63856
|
-
for (const matcher of globMatchers) {
|
|
63857
|
-
if (matcher(file.path))
|
|
63858
|
-
return false;
|
|
63859
|
-
}
|
|
63860
|
-
return true;
|
|
63861
|
-
});
|
|
63862
|
-
}
|
|
63863
|
-
// src/domains/sync/merge-ui.ts
|
|
63864
|
-
init_dist2();
|
|
63865
|
-
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
63866
|
-
var HUNK_SEPARATOR_WIDTH = 50;
|
|
63867
|
-
var EXTENDED_CONTEXT_LINES = 10;
|
|
63868
|
-
var MAX_LINE_DISPLAY_LENGTH = 120;
|
|
63869
|
-
function requireTTY() {
|
|
63870
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
63871
|
-
throw new Error("Interactive merge requires a TTY terminal. " + "Use --yes flag for non-interactive mode, or run in a terminal.");
|
|
63872
|
-
}
|
|
63873
|
-
}
|
|
63874
|
-
function truncateLine(line) {
|
|
63875
|
-
if (line.length <= MAX_LINE_DISPLAY_LENGTH)
|
|
63876
|
-
return line;
|
|
63877
|
-
return `${line.slice(0, MAX_LINE_DISPLAY_LENGTH - 3)}...`;
|
|
63878
|
-
}
|
|
63879
|
-
|
|
63880
|
-
class MergeUI {
|
|
63881
|
-
static async promptHunk(hunk, hunkIndex, totalHunks, _filename) {
|
|
63882
|
-
requireTTY();
|
|
63883
|
-
const lineRange = `${hunk.oldStart}-${hunk.oldStart + hunk.oldLines - 1}`;
|
|
63884
|
-
console.log(import_picocolors18.default.cyan(`
|
|
63885
|
-
Hunk ${hunkIndex + 1}/${totalHunks}: Lines ${lineRange}`));
|
|
63886
|
-
console.log(import_picocolors18.default.dim("─".repeat(HUNK_SEPARATOR_WIDTH)));
|
|
63887
|
-
for (const line of hunk.lines) {
|
|
63888
|
-
const displayLine = truncateLine(line);
|
|
63889
|
-
const prefix = line[0];
|
|
63890
|
-
if (prefix === "+") {
|
|
63891
|
-
console.log(import_picocolors18.default.green(displayLine));
|
|
63892
|
-
} else if (prefix === "-") {
|
|
63893
|
-
console.log(import_picocolors18.default.red(displayLine));
|
|
63894
|
-
} else {
|
|
63895
|
-
console.log(import_picocolors18.default.dim(displayLine));
|
|
63896
|
-
}
|
|
63897
|
-
}
|
|
63898
|
-
console.log(import_picocolors18.default.dim("─".repeat(HUNK_SEPARATOR_WIDTH)));
|
|
63899
|
-
const action = await ie({
|
|
63900
|
-
message: "Action?",
|
|
63901
|
-
options: [
|
|
63902
|
-
{ value: "accept", label: "[a]ccept - Apply this change" },
|
|
63903
|
-
{ value: "reject", label: "[r]eject - Keep current" },
|
|
63904
|
-
{ value: "view", label: "[v]iew - More context" },
|
|
63905
|
-
{ value: "skip", label: "[s]kip - Skip entire file" }
|
|
63906
|
-
]
|
|
63907
|
-
});
|
|
63908
|
-
if (lD(action)) {
|
|
63909
|
-
return "skip";
|
|
63910
|
-
}
|
|
63911
|
-
return action;
|
|
63912
|
-
}
|
|
63913
|
-
static showExtendedContext(currentContent, hunk, contextLines = EXTENDED_CONTEXT_LINES) {
|
|
63914
|
-
const lines = currentContent.split(`
|
|
63915
|
-
`);
|
|
63916
|
-
const startLine = Math.max(0, hunk.oldStart - 1 - contextLines);
|
|
63917
|
-
const endLine = Math.min(lines.length, hunk.oldStart + hunk.oldLines - 1 + contextLines);
|
|
63918
|
-
console.log(import_picocolors18.default.cyan(`
|
|
63919
|
-
Extended context (lines ${startLine + 1}-${endLine}):`));
|
|
63920
|
-
console.log(import_picocolors18.default.dim("─".repeat(HUNK_SEPARATOR_WIDTH)));
|
|
63921
|
-
for (let i = startLine;i < endLine; i++) {
|
|
63922
|
-
const lineNum = String(i + 1).padStart(4, " ");
|
|
63923
|
-
const isInHunk = i >= hunk.oldStart - 1 && i < hunk.oldStart - 1 + hunk.oldLines;
|
|
63924
|
-
const prefix = isInHunk ? import_picocolors18.default.yellow("*") : " ";
|
|
63925
|
-
console.log(`${import_picocolors18.default.dim(lineNum)} ${prefix} ${lines[i]}`);
|
|
63926
|
-
}
|
|
63927
|
-
console.log(import_picocolors18.default.dim("─".repeat(HUNK_SEPARATOR_WIDTH)));
|
|
63928
|
-
}
|
|
63929
|
-
static async mergeFile(filename, currentContent, _newContent, hunks) {
|
|
63930
|
-
console.log(import_picocolors18.default.bold(`
|
|
63931
|
-
━━━ ${filename} ━━━`));
|
|
63932
|
-
console.log(import_picocolors18.default.dim(`${hunks.length} change${hunks.length === 1 ? "" : "s"} to review
|
|
63933
|
-
`));
|
|
63934
|
-
const decisions = [];
|
|
63935
|
-
for (let i = 0;i < hunks.length; i++) {
|
|
63936
|
-
let action;
|
|
63937
|
-
do {
|
|
63938
|
-
action = await MergeUI.promptHunk(hunks[i], i, hunks.length, filename);
|
|
63939
|
-
if (action === "view") {
|
|
63940
|
-
MergeUI.showExtendedContext(currentContent, hunks[i]);
|
|
63941
|
-
}
|
|
63942
|
-
} while (action === "view");
|
|
63943
|
-
if (action === "skip") {
|
|
63944
|
-
return "skipped";
|
|
63945
|
-
}
|
|
63946
|
-
decisions.push(action === "accept");
|
|
63947
|
-
}
|
|
63948
|
-
const result = SyncEngine.applyHunks(currentContent, hunks, decisions);
|
|
63949
|
-
const applied = decisions.filter(Boolean).length;
|
|
63950
|
-
const rejected = decisions.length - applied;
|
|
63951
|
-
return { result, applied, rejected };
|
|
63952
|
-
}
|
|
63953
|
-
static displayMergeSummary(filename, applied, rejected) {
|
|
63954
|
-
if (applied > 0 && rejected > 0) {
|
|
63955
|
-
console.log(import_picocolors18.default.dim(` ${filename}: `) + import_picocolors18.default.green(`${applied} applied`) + import_picocolors18.default.dim(", ") + import_picocolors18.default.yellow(`${rejected} rejected`));
|
|
63956
|
-
} else if (applied > 0) {
|
|
63957
|
-
console.log(import_picocolors18.default.dim(` ${filename}: `) + import_picocolors18.default.green(`${applied} applied`));
|
|
63958
|
-
} else {
|
|
63959
|
-
console.log(import_picocolors18.default.dim(` ${filename}: `) + import_picocolors18.default.yellow(`${rejected} rejected`));
|
|
63960
|
-
}
|
|
63961
|
-
}
|
|
63962
|
-
static displaySkipped(filename) {
|
|
63963
|
-
console.log(import_picocolors18.default.dim(` ${filename}: `) + import_picocolors18.default.yellow("skipped"));
|
|
63964
|
-
}
|
|
63965
|
-
}
|
|
63966
|
-
// src/domains/sync/notification-display.ts
|
|
63967
|
-
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
63968
|
-
import { stdout } from "node:process";
|
|
63969
|
-
function createNotificationBox(borderColor, boxWidth) {
|
|
63970
|
-
const contentWidth = boxWidth - 2;
|
|
63971
|
-
const topBorder = borderColor(`╭${"─".repeat(contentWidth)}╮`);
|
|
63972
|
-
const bottomBorder = borderColor(`╰${"─".repeat(contentWidth)}╯`);
|
|
63973
|
-
const emptyLine = borderColor("│") + " ".repeat(contentWidth) + borderColor("│");
|
|
63974
|
-
const padLine = (text, visibleLen) => {
|
|
63975
|
-
const len = visibleLen ?? text.length;
|
|
63976
|
-
const displayText = len > contentWidth ? `${text.slice(0, contentWidth - 3)}...` : text;
|
|
63977
|
-
const actualLen = visibleLen ?? displayText.length;
|
|
63978
|
-
const totalPadding = contentWidth - actualLen;
|
|
63979
|
-
const leftPadding = Math.max(0, Math.floor(totalPadding / 2));
|
|
63980
|
-
const rightPadding = Math.max(0, totalPadding - leftPadding);
|
|
63981
|
-
return borderColor("│") + " ".repeat(leftPadding) + displayText + " ".repeat(rightPadding) + borderColor("│");
|
|
63982
|
-
};
|
|
63983
|
-
return { topBorder, bottomBorder, emptyLine, padLine };
|
|
63984
|
-
}
|
|
63985
|
-
function displayConfigUpdateNotification(currentVersion, latestVersion, isGlobal = false) {
|
|
63986
|
-
const displayCurrent = currentVersion.replace(/^v/, "");
|
|
63987
|
-
const displayLatest = latestVersion.replace(/^v/, "");
|
|
63988
|
-
const terminalWidth = stdout.columns || 80;
|
|
63989
|
-
const boxWidth = Math.min(52, terminalWidth - 4);
|
|
63990
|
-
const { topBorder, bottomBorder, emptyLine, padLine } = createNotificationBox(import_picocolors19.default.cyan, boxWidth);
|
|
63991
|
-
const headerText = import_picocolors19.default.bold(import_picocolors19.default.yellow("\uD83D\uDCE6 Config Updates Available"));
|
|
63992
|
-
const headerLen = "\uD83D\uDCE6 Config Updates Available".length;
|
|
63993
|
-
const versionText = `${import_picocolors19.default.dim(displayCurrent)} ${import_picocolors19.default.white("→")} ${import_picocolors19.default.green(import_picocolors19.default.bold(displayLatest))}`;
|
|
63994
|
-
const versionLen = displayCurrent.length + 3 + displayLatest.length;
|
|
63995
|
-
const updateCmd = isGlobal ? "tkm init -g --sync" : "tkm init --sync";
|
|
63996
|
-
const commandText = `Run: ${import_picocolors19.default.cyan(import_picocolors19.default.bold(updateCmd))}`;
|
|
63997
|
-
const commandLen = `Run: ${updateCmd}`.length;
|
|
63998
|
-
console.log("");
|
|
63999
|
-
console.log(topBorder);
|
|
64000
|
-
console.log(emptyLine);
|
|
64001
|
-
console.log(padLine(headerText, headerLen));
|
|
64002
|
-
console.log(padLine(versionText, versionLen));
|
|
64003
|
-
console.log(emptyLine);
|
|
64004
|
-
console.log(padLine(commandText, commandLen));
|
|
64005
|
-
console.log(emptyLine);
|
|
64006
|
-
console.log(bottomBorder);
|
|
64007
|
-
console.log("");
|
|
64008
|
-
}
|
|
64009
|
-
// src/domains/sync/passive-update-check.ts
|
|
64010
|
-
init_logger();
|
|
64011
|
-
async function maybeShowConfigUpdateNotification(claudeDir, global3) {
|
|
64012
|
-
try {
|
|
64013
|
-
const detection = await detectMetadataFormat(claudeDir);
|
|
64014
|
-
if (detection.format === "none" || !detection.metadata) {
|
|
64015
|
-
logger.debug("No metadata found, skipping update check");
|
|
64016
|
-
return false;
|
|
64017
|
-
}
|
|
64018
|
-
const kitsToCheck = [];
|
|
64019
|
-
if (detection.format === "multi-kit" && detection.metadata.kits) {
|
|
64020
|
-
for (const [kitType, kitInfo] of Object.entries(detection.metadata.kits)) {
|
|
64021
|
-
if (kitInfo?.version) {
|
|
64022
|
-
kitsToCheck.push({ kit: kitType, version: kitInfo.version });
|
|
64023
|
-
}
|
|
64024
|
-
}
|
|
64025
|
-
} else if (detection.format === "legacy" && detection.detectedKit && detection.metadata.version) {
|
|
64026
|
-
kitsToCheck.push({
|
|
64027
|
-
kit: detection.detectedKit,
|
|
64028
|
-
version: detection.metadata.version
|
|
64029
|
-
});
|
|
64030
|
-
}
|
|
64031
|
-
if (kitsToCheck.length === 0) {
|
|
64032
|
-
logger.debug("No kits with versions found, skipping update check");
|
|
64033
|
-
return false;
|
|
64034
|
-
}
|
|
64035
|
-
for (const { kit, version: version3 } of kitsToCheck) {
|
|
64036
|
-
const result = await ConfigVersionChecker.checkForUpdates(kit, version3, global3);
|
|
64037
|
-
if (result.hasUpdates) {
|
|
64038
|
-
displayConfigUpdateNotification(result.currentVersion, result.latestVersion, global3);
|
|
64039
|
-
return true;
|
|
64040
|
-
}
|
|
64041
|
-
}
|
|
64042
|
-
return false;
|
|
64043
|
-
} catch (error) {
|
|
64044
|
-
logger.debug(`Passive update check failed: ${error instanceof Error ? error.message : error}`);
|
|
64045
|
-
return false;
|
|
64046
|
-
}
|
|
64047
|
-
}
|
|
64048
|
-
// src/domains/ui/prompts.ts
|
|
64049
|
-
init_package_installer();
|
|
64050
|
-
init_logger();
|
|
64051
|
-
|
|
64052
|
-
// src/domains/ui/prompts/kit-prompts.ts
|
|
64053
|
-
init_types2();
|
|
64054
|
-
async function selectKit(defaultKit, accessibleKits) {
|
|
64055
|
-
const kits = accessibleKits ?? Object.keys(AVAILABLE_KITS);
|
|
64056
|
-
const kit = await ie({
|
|
64057
|
-
message: "Select a Takumi:",
|
|
64058
|
-
options: kits.map((key) => ({
|
|
64059
|
-
value: key,
|
|
64060
|
-
label: AVAILABLE_KITS[key].name,
|
|
64061
|
-
hint: AVAILABLE_KITS[key].description
|
|
64062
|
-
})),
|
|
64063
|
-
initialValue: defaultKit
|
|
64064
|
-
});
|
|
64065
|
-
if (lD(kit)) {
|
|
64066
|
-
throw new Error("Kit selection cancelled");
|
|
64067
|
-
}
|
|
64068
|
-
return kit;
|
|
64069
|
-
}
|
|
64070
|
-
async function getDirectory(defaultDir = ".") {
|
|
64071
|
-
const dir = await te({
|
|
64072
|
-
message: "Enter target directory:",
|
|
64073
|
-
placeholder: `Press Enter for "${defaultDir}"`,
|
|
64074
|
-
validate: () => {
|
|
64075
|
-
return;
|
|
64076
|
-
}
|
|
64077
|
-
});
|
|
64078
|
-
if (lD(dir)) {
|
|
64079
|
-
throw new Error("Directory input cancelled");
|
|
64080
|
-
}
|
|
64081
|
-
const trimmed = (dir ?? "").trim();
|
|
64082
|
-
return trimmed.length > 0 ? trimmed : defaultDir;
|
|
64083
|
-
}
|
|
64084
|
-
// src/domains/versioning/version-selector.ts
|
|
64085
|
-
init_auth_client();
|
|
64086
|
-
init_github_client();
|
|
64087
|
-
|
|
64088
|
-
// src/domains/installation/release-entry-adapter.ts
|
|
64089
|
-
function githubReleaseToEntry(release, kit = "engineer") {
|
|
64090
|
-
const expected = assetNameFor(kit);
|
|
64091
|
-
const expectedAsset = release.assets.find((a3) => a3.name === expected);
|
|
64092
|
-
const fallback2 = release.assets[0];
|
|
64093
|
-
const archive = expectedAsset?.name ?? fallback2?.name ?? expected;
|
|
64094
|
-
return {
|
|
64095
|
-
tag: release.tag_name,
|
|
64096
|
-
version: release.tag_name.replace(/^v/, ""),
|
|
64097
|
-
prerelease: release.prerelease,
|
|
64098
|
-
publishedAt: release.published_at ?? "",
|
|
64099
|
-
archive
|
|
64100
|
-
};
|
|
64101
|
-
}
|
|
64102
|
-
function releaseEntryToGitHubRelease(entry, kit) {
|
|
64103
|
-
return {
|
|
64104
|
-
id: 0,
|
|
64105
|
-
tag_name: entry.tag,
|
|
64106
|
-
name: entry.tag,
|
|
64107
|
-
draft: false,
|
|
64108
|
-
prerelease: entry.prerelease,
|
|
64109
|
-
published_at: entry.publishedAt || undefined,
|
|
64110
|
-
tarball_url: `https://github.com/${kit.owner}/${kit.repo}/archive/refs/tags/${entry.tag}.tar.gz`,
|
|
64111
|
-
zipball_url: `https://github.com/${kit.owner}/${kit.repo}/archive/refs/tags/${entry.tag}.zip`,
|
|
64112
|
-
assets: []
|
|
64113
|
-
};
|
|
64114
|
-
}
|
|
64115
|
-
|
|
64116
|
-
// src/domains/versioning/version-selector.ts
|
|
64117
|
-
init_logger();
|
|
64118
|
-
init_dist2();
|
|
64119
|
-
init_release_filter();
|
|
64120
|
-
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
64121
|
-
|
|
64122
|
-
// src/domains/versioning/selection/version-filter.ts
|
|
64123
|
-
var VERSION_PATTERN = /^v?\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/;
|
|
64124
|
-
function isValidVersionFormat(version3) {
|
|
64125
|
-
const trimmed = version3.trim();
|
|
64126
|
-
if (!trimmed)
|
|
64127
|
-
return false;
|
|
64128
|
-
return VERSION_PATTERN.test(trimmed);
|
|
64129
|
-
}
|
|
64130
|
-
function normalizeVersionTag(version3) {
|
|
64131
|
-
const trimmed = version3.trim();
|
|
64132
|
-
return trimmed.startsWith("v") ? trimmed : `v${trimmed}`;
|
|
64133
|
-
}
|
|
64134
|
-
// src/domains/versioning/selection/selection-ui.ts
|
|
64135
|
-
init_environment();
|
|
64136
|
-
init_logger();
|
|
64137
|
-
init_dist2();
|
|
64138
|
-
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
64139
|
-
|
|
64140
|
-
// src/domains/versioning/version-display.ts
|
|
64141
|
-
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
64142
|
-
|
|
64143
|
-
class VersionDisplayFormatter {
|
|
64144
|
-
static createBadges(release) {
|
|
64145
|
-
const badges = [];
|
|
64146
|
-
if (release.isLatestStable) {
|
|
64147
|
-
badges.push(import_picocolors20.default.bold(import_picocolors20.default.yellow("[latest]")));
|
|
64148
|
-
}
|
|
64149
|
-
if (release.prerelease || release.isLatestBeta) {
|
|
64150
|
-
if (release.isLatestBeta) {
|
|
64151
|
-
badges.push(import_picocolors20.default.bold(import_picocolors20.default.magenta("[beta]")));
|
|
64152
|
-
} else {
|
|
64153
|
-
badges.push(import_picocolors20.default.magenta("[prerelease]"));
|
|
64154
|
-
}
|
|
64155
|
-
} else if (!release.draft) {
|
|
64156
|
-
badges.push(import_picocolors20.default.blue("[stable]"));
|
|
64157
|
-
}
|
|
64158
|
-
if (release.draft) {
|
|
64159
|
-
badges.push(import_picocolors20.default.gray("[draft]"));
|
|
64160
|
-
}
|
|
64161
|
-
return badges.length > 0 ? ` ${badges.join(" ")}` : "";
|
|
64162
|
-
}
|
|
64163
|
-
static formatChoiceLabel(release) {
|
|
64164
|
-
const version3 = import_picocolors20.default.green(release.displayVersion);
|
|
64165
|
-
const badges = VersionDisplayFormatter.createBadges(release);
|
|
64166
|
-
const name = release.name || "Release";
|
|
64167
|
-
return `${version3}${badges} ${import_picocolors20.default.dim(name)}`;
|
|
64168
|
-
}
|
|
64169
|
-
static formatChoiceHint(release) {
|
|
64170
|
-
const parts = [];
|
|
64171
|
-
if (release.relativeTime && release.relativeTime !== "Unknown") {
|
|
64172
|
-
parts.push(release.relativeTime);
|
|
64173
|
-
}
|
|
64174
|
-
if (release.assetCount > 0) {
|
|
64175
|
-
const assetText = release.assetCount === 1 ? "asset" : "assets";
|
|
64176
|
-
parts.push(`${release.assetCount} ${assetText}`);
|
|
64177
|
-
}
|
|
64178
|
-
if (release.normalizedVersion !== release.displayVersion) {
|
|
64179
|
-
parts.push(`(${release.normalizedVersion})`);
|
|
64180
|
-
}
|
|
64181
|
-
return parts.length > 0 ? parts.join(", ") : "";
|
|
64182
|
-
}
|
|
64183
|
-
static createSpecialOptions(releases) {
|
|
64184
|
-
const options2 = [];
|
|
64185
|
-
const latestStable = releases.find((r2) => r2.isLatestStable && !r2.prerelease);
|
|
64186
|
-
if (latestStable) {
|
|
64187
|
-
options2.push({
|
|
64188
|
-
value: latestStable.tag_name,
|
|
64189
|
-
label: `${import_picocolors20.default.bold(import_picocolors20.default.green("Latest Stable"))} (${latestStable.displayVersion})`,
|
|
64190
|
-
hint: "recommended version",
|
|
64191
|
-
isLatest: true,
|
|
64192
|
-
isPrerelease: false
|
|
64193
|
-
});
|
|
64194
|
-
}
|
|
64195
|
-
const latestBeta = releases.find((r2) => r2.isLatestBeta || r2.prerelease && !r2.draft);
|
|
64196
|
-
if (latestBeta) {
|
|
64197
|
-
options2.push({
|
|
64198
|
-
value: latestBeta.tag_name,
|
|
64199
|
-
label: `${import_picocolors20.default.bold(import_picocolors20.default.magenta("Latest Beta"))} (${latestBeta.displayVersion})`,
|
|
64200
|
-
hint: "latest features, may be unstable",
|
|
64201
|
-
isLatest: false,
|
|
64202
|
-
isPrerelease: true
|
|
64203
|
-
});
|
|
64204
|
-
}
|
|
64205
|
-
return options2;
|
|
64206
|
-
}
|
|
64207
|
-
static createSeparator() {
|
|
64208
|
-
return {
|
|
64209
|
-
value: "separator",
|
|
64210
|
-
label: import_picocolors20.default.dim("─".repeat(50)),
|
|
64211
|
-
hint: undefined,
|
|
64212
|
-
isLatest: false,
|
|
64213
|
-
isPrerelease: false
|
|
64214
|
-
};
|
|
64215
|
-
}
|
|
64216
|
-
static createCancelOption() {
|
|
64217
|
-
return {
|
|
64218
|
-
value: "cancel",
|
|
64219
|
-
label: import_picocolors20.default.red("Cancel"),
|
|
64220
|
-
hint: "exit version selection",
|
|
64221
|
-
isLatest: false,
|
|
64222
|
-
isPrerelease: false
|
|
64223
|
-
};
|
|
64224
|
-
}
|
|
64225
|
-
static formatVersionChoice(release) {
|
|
64226
|
-
return {
|
|
64227
|
-
value: release.tag_name,
|
|
64228
|
-
label: VersionDisplayFormatter.formatChoiceLabel(release),
|
|
64229
|
-
hint: VersionDisplayFormatter.formatChoiceHint(release),
|
|
64230
|
-
isLatest: release.isLatestStable,
|
|
64231
|
-
isPrerelease: release.prerelease
|
|
64232
|
-
};
|
|
64233
|
-
}
|
|
64234
|
-
static formatReleasesToChoices(releases, includeSpecialOptions = true, limit = 30) {
|
|
64235
|
-
const choices = [];
|
|
64236
|
-
if (includeSpecialOptions) {
|
|
64237
|
-
const specialOptions = VersionDisplayFormatter.createSpecialOptions(releases);
|
|
64238
|
-
choices.push(...specialOptions);
|
|
64239
|
-
if (specialOptions.length > 0 && releases.length > 0) {
|
|
64240
|
-
choices.push(VersionDisplayFormatter.createSeparator());
|
|
64241
|
-
}
|
|
64242
|
-
}
|
|
64243
|
-
const limitedReleases = releases.slice(0, limit);
|
|
64244
|
-
for (const release of limitedReleases) {
|
|
64245
|
-
choices.push(VersionDisplayFormatter.formatVersionChoice(release));
|
|
64246
|
-
}
|
|
64247
|
-
if (includeSpecialOptions) {
|
|
64248
|
-
choices.push(VersionDisplayFormatter.createSeparator());
|
|
64249
|
-
choices.push(VersionDisplayFormatter.createCancelOption());
|
|
64250
|
-
}
|
|
64251
|
-
return choices;
|
|
64252
|
-
}
|
|
64253
|
-
static getDefaultChoiceIndex(choices) {
|
|
64254
|
-
for (let i = 0;i < choices.length; i++) {
|
|
64255
|
-
const choice = choices[i];
|
|
64256
|
-
if (choice.isLatest && !choice.isPrerelease) {
|
|
64257
|
-
return i;
|
|
64258
|
-
}
|
|
64259
|
-
}
|
|
64260
|
-
for (let i = 0;i < choices.length; i++) {
|
|
64261
|
-
if (choices[i].value !== "separator") {
|
|
64262
|
-
return i;
|
|
64263
|
-
}
|
|
64264
|
-
}
|
|
64265
|
-
return 0;
|
|
64266
|
-
}
|
|
64267
|
-
static isValidVersionChoice(value) {
|
|
64268
|
-
return value !== "separator" && value !== "cancel" && value.trim().length > 0;
|
|
63818
|
+
static isValidVersionChoice(value) {
|
|
63819
|
+
return value !== "separator" && value !== "cancel" && value.trim().length > 0;
|
|
64269
63820
|
}
|
|
64270
63821
|
static formatError(message, suggestion) {
|
|
64271
|
-
let output2 =
|
|
63822
|
+
let output2 = import_picocolors18.default.red(`Error: ${message}`);
|
|
64272
63823
|
if (suggestion) {
|
|
64273
63824
|
output2 += `
|
|
64274
|
-
${
|
|
63825
|
+
${import_picocolors18.default.dim(suggestion)}`;
|
|
64275
63826
|
}
|
|
64276
63827
|
return output2;
|
|
64277
63828
|
}
|
|
64278
63829
|
static formatSuccess(version3, kitName) {
|
|
64279
|
-
return `${
|
|
63830
|
+
return `${import_picocolors18.default.green("✓")} Selected ${import_picocolors18.default.bold(version3)} for ${import_picocolors18.default.bold(kitName)}`;
|
|
64280
63831
|
}
|
|
64281
63832
|
}
|
|
64282
63833
|
|
|
@@ -64286,7 +63837,7 @@ async function handleNoReleases(kit, allowManualEntry) {
|
|
|
64286
63837
|
This could be due to:
|
|
64287
63838
|
• No releases published yet
|
|
64288
63839
|
• Network connectivity issues
|
|
64289
|
-
• Repository access permissions`,
|
|
63840
|
+
• Repository access permissions`, import_picocolors19.default.yellow("No Releases Available"));
|
|
64290
63841
|
if (!allowManualEntry) {
|
|
64291
63842
|
throw new Error(`No releases available for ${kit.name}`);
|
|
64292
63843
|
}
|
|
@@ -64346,34 +63897,34 @@ async function createVersionPrompt(kit, choices, _defaultIndex, allowManualEntry
|
|
|
64346
63897
|
if (latestStable) {
|
|
64347
63898
|
clackChoices.push({
|
|
64348
63899
|
value: latestStable.tag_name,
|
|
64349
|
-
label: `${
|
|
63900
|
+
label: `${import_picocolors19.default.bold(import_picocolors19.default.green("Latest Stable"))} (${latestStable.displayVersion})`,
|
|
64350
63901
|
hint: "recommended"
|
|
64351
63902
|
});
|
|
64352
63903
|
}
|
|
64353
63904
|
if (allowManualEntry) {
|
|
64354
63905
|
clackChoices.push({
|
|
64355
63906
|
value: "manual-entry",
|
|
64356
|
-
label:
|
|
63907
|
+
label: import_picocolors19.default.cyan("↳ Enter Version Manually"),
|
|
64357
63908
|
hint: "for older versions"
|
|
64358
63909
|
});
|
|
64359
63910
|
}
|
|
64360
63911
|
clackChoices.push({
|
|
64361
63912
|
value: "cancel",
|
|
64362
|
-
label:
|
|
63913
|
+
label: import_picocolors19.default.red("✕ Cancel")
|
|
64363
63914
|
});
|
|
64364
63915
|
const versionChoices = choices.filter((choice) => choice.value !== "separator" && choice.value !== "cancel");
|
|
64365
63916
|
for (const choice of versionChoices) {
|
|
64366
63917
|
const isCurrentlyInstalled = currentVersion && (choice.value === currentVersion || choice.value === `v${currentVersion}`);
|
|
64367
|
-
const installedMarker = isCurrentlyInstalled ?
|
|
63918
|
+
const installedMarker = isCurrentlyInstalled ? import_picocolors19.default.cyan(" (installed)") : "";
|
|
64368
63919
|
clackChoices.push({
|
|
64369
63920
|
value: choice.value,
|
|
64370
63921
|
label: `${choice.label}${installedMarker}`,
|
|
64371
63922
|
hint: choice.hint
|
|
64372
63923
|
});
|
|
64373
63924
|
}
|
|
64374
|
-
const currentVersionHint = currentVersion ?
|
|
63925
|
+
const currentVersionHint = currentVersion ? import_picocolors19.default.dim(` (current: ${currentVersion})`) : "";
|
|
64375
63926
|
const selected = await ie({
|
|
64376
|
-
message: `Select version for ${
|
|
63927
|
+
message: `Select version for ${import_picocolors19.default.bold(kit.name)}${currentVersionHint}:`,
|
|
64377
63928
|
options: clackChoices,
|
|
64378
63929
|
initialValue: latestStable?.tag_name
|
|
64379
63930
|
});
|
|
@@ -64398,15 +63949,15 @@ async function createVersionPrompt(kit, choices, _defaultIndex, allowManualEntry
|
|
|
64398
63949
|
async function handleSelectionError(error, kit, allowManualEntry, retryCallback) {
|
|
64399
63950
|
logger.error(`Version selection error: ${error.message}`);
|
|
64400
63951
|
if (error.message.includes("401") || error.message.includes("403")) {
|
|
64401
|
-
le(VersionDisplayFormatter.formatError("Authentication failed", "Please check your GitHub token with: sk auth"),
|
|
63952
|
+
le(VersionDisplayFormatter.formatError("Authentication failed", "Please check your GitHub token with: sk auth"), import_picocolors19.default.red("Authentication Error"));
|
|
64402
63953
|
} else if (error.message.includes("404")) {
|
|
64403
|
-
le(VersionDisplayFormatter.formatError("Repository access denied", "Make sure you have access to the repository"),
|
|
63954
|
+
le(VersionDisplayFormatter.formatError("Repository access denied", "Make sure you have access to the repository"), import_picocolors19.default.red("Access Error"));
|
|
64404
63955
|
} else if (error.message.includes("rate limit") || error.message.includes("403")) {
|
|
64405
|
-
le(VersionDisplayFormatter.formatError("GitHub API rate limit exceeded", "Please wait a moment and try again"),
|
|
63956
|
+
le(VersionDisplayFormatter.formatError("GitHub API rate limit exceeded", "Please wait a moment and try again"), import_picocolors19.default.yellow("Rate Limited"));
|
|
64406
63957
|
} else if (error.message.includes("network") || error.message.includes("ENOTFOUND")) {
|
|
64407
|
-
le(VersionDisplayFormatter.formatError("Network connection failed", "Please check your internet connection"),
|
|
63958
|
+
le(VersionDisplayFormatter.formatError("Network connection failed", "Please check your internet connection"), import_picocolors19.default.yellow("Network Error"));
|
|
64408
63959
|
} else {
|
|
64409
|
-
le(VersionDisplayFormatter.formatError(error.message || "Unknown error occurred", "Please try again or contact support"),
|
|
63960
|
+
le(VersionDisplayFormatter.formatError(error.message || "Unknown error occurred", "Please try again or contact support"), import_picocolors19.default.red("Error"));
|
|
64410
63961
|
}
|
|
64411
63962
|
if (isNonInteractive()) {
|
|
64412
63963
|
logger.warning("Non-interactive mode: version selection failed, cannot retry");
|
|
@@ -64468,7 +64019,7 @@ class VersionSelector {
|
|
|
64468
64019
|
} = options2;
|
|
64469
64020
|
try {
|
|
64470
64021
|
const loadingSpinner = de();
|
|
64471
|
-
loadingSpinner.start(`Fetching versions for ${
|
|
64022
|
+
loadingSpinner.start(`Fetching versions for ${import_picocolors20.default.bold(kit.name)}...`);
|
|
64472
64023
|
let releases;
|
|
64473
64024
|
if (useGh) {
|
|
64474
64025
|
releases = await this.githubClient.listReleasesWithCache(kit, {
|
|
@@ -64730,6 +64281,9 @@ class PromptsManager {
|
|
|
64730
64281
|
async selectKit(defaultKit, accessibleKits) {
|
|
64731
64282
|
return selectKit(defaultKit, accessibleKits);
|
|
64732
64283
|
}
|
|
64284
|
+
async selectKits(accessibleKits, defaults2) {
|
|
64285
|
+
return selectKits(accessibleKits, defaults2);
|
|
64286
|
+
}
|
|
64733
64287
|
async selectVersion(versions, defaultVersion) {
|
|
64734
64288
|
return selectVersion(versions, defaultVersion);
|
|
64735
64289
|
}
|
|
@@ -64805,6 +64359,9 @@ class PromptsManager {
|
|
|
64805
64359
|
async promptSkillsInstallation() {
|
|
64806
64360
|
return promptSkillsInstallation();
|
|
64807
64361
|
}
|
|
64362
|
+
async promptAgentSelection(supported) {
|
|
64363
|
+
return promptAgentSelection(supported);
|
|
64364
|
+
}
|
|
64808
64365
|
showPackageInstallationResults(results) {
|
|
64809
64366
|
const successfulInstalls = [];
|
|
64810
64367
|
const failedInstalls = [];
|
|
@@ -64868,6 +64425,8 @@ class PromptsManager {
|
|
|
64868
64425
|
|
|
64869
64426
|
// src/commands/init/init-command.ts
|
|
64870
64427
|
init_logger();
|
|
64428
|
+
init_path_resolver();
|
|
64429
|
+
init_types2();
|
|
64871
64430
|
|
|
64872
64431
|
// src/commands/init/context-factory.ts
|
|
64873
64432
|
function createInitContext(rawOptions, prompts) {
|
|
@@ -64959,12 +64518,12 @@ import * as path8 from "node:path";
|
|
|
64959
64518
|
// src/domains/github/auth-prompt.ts
|
|
64960
64519
|
init_dist2();
|
|
64961
64520
|
init_github_auth();
|
|
64962
|
-
var
|
|
64521
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
64963
64522
|
async function promptForAuth() {
|
|
64964
64523
|
const hasGit = GitCloneManager.isGitInstalled();
|
|
64965
64524
|
const hasSshKeys = hasGit && GitCloneManager.hasSshKeys();
|
|
64966
64525
|
const hasGhCli = AuthManager.isGhCliInstalled();
|
|
64967
|
-
oe(
|
|
64526
|
+
oe(import_picocolors21.default.yellow("No GitHub authentication found"));
|
|
64968
64527
|
const options2 = [];
|
|
64969
64528
|
if (hasGit) {
|
|
64970
64529
|
const hint = hasSshKeys ? "SSH keys detected" : "Will use HTTPS";
|
|
@@ -65017,7 +64576,7 @@ async function promptForAuth() {
|
|
|
65017
64576
|
return { method: "cancel" };
|
|
65018
64577
|
}
|
|
65019
64578
|
if (typeof token === "string" && token.startsWith("github_pat_")) {
|
|
65020
|
-
le(
|
|
64579
|
+
le(import_picocolors21.default.yellow(`⚠️ Fine-grained PATs cannot access repos where you're a collaborator.
|
|
65021
64580
|
` + " If you encounter access issues, use a Classic PAT instead."));
|
|
65022
64581
|
}
|
|
65023
64582
|
return { method: "token", token };
|
|
@@ -65032,9 +64591,9 @@ init_github_client();
|
|
|
65032
64591
|
// src/domains/installation/download-manager.ts
|
|
65033
64592
|
init_environment();
|
|
65034
64593
|
init_logger();
|
|
65035
|
-
import { mkdir as
|
|
64594
|
+
import { mkdir as mkdir26, stat as stat10 } from "node:fs/promises";
|
|
65036
64595
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
65037
|
-
import { join as
|
|
64596
|
+
import { join as join91 } from "node:path";
|
|
65038
64597
|
|
|
65039
64598
|
// src/shared/temp-cleanup.ts
|
|
65040
64599
|
init_logger();
|
|
@@ -65052,13 +64611,13 @@ var import_ignore3 = __toESM(require_ignore(), 1);
|
|
|
65052
64611
|
init_logger();
|
|
65053
64612
|
init_output_manager();
|
|
65054
64613
|
import { createWriteStream as createWriteStream2, rmSync as rmSync3 } from "node:fs";
|
|
65055
|
-
import { mkdir as
|
|
65056
|
-
import { join as
|
|
64614
|
+
import { mkdir as mkdir22 } from "node:fs/promises";
|
|
64615
|
+
import { join as join86 } from "node:path";
|
|
65057
64616
|
|
|
65058
64617
|
// src/shared/progress-bar.ts
|
|
65059
64618
|
init_output_manager();
|
|
65060
64619
|
init_terminal_utils();
|
|
65061
|
-
var
|
|
64620
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
65062
64621
|
var BAR_CHARS = {
|
|
65063
64622
|
unicode: { filled: "█", empty: "░" },
|
|
65064
64623
|
ascii: { filled: "=", empty: "-" }
|
|
@@ -65116,7 +64675,7 @@ class ProgressBar {
|
|
|
65116
64675
|
this.clearLine();
|
|
65117
64676
|
if (message) {
|
|
65118
64677
|
const symbols = output.getSymbols();
|
|
65119
|
-
console.log(`${
|
|
64678
|
+
console.log(`${import_picocolors22.default.green(symbols.success)} ${message}`);
|
|
65120
64679
|
}
|
|
65121
64680
|
}
|
|
65122
64681
|
clearLine() {
|
|
@@ -65218,13 +64777,13 @@ init_types2();
|
|
|
65218
64777
|
// src/domains/installation/utils/path-security.ts
|
|
65219
64778
|
init_types2();
|
|
65220
64779
|
import { lstatSync as lstatSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
65221
|
-
import { relative as
|
|
64780
|
+
import { relative as relative14, resolve as resolve17 } from "node:path";
|
|
65222
64781
|
var MAX_EXTRACTION_SIZE = 500 * 1024 * 1024;
|
|
65223
64782
|
function isPathSafe(basePath, targetPath) {
|
|
65224
64783
|
const resolvedBase = resolve17(basePath);
|
|
65225
64784
|
try {
|
|
65226
|
-
const
|
|
65227
|
-
if (
|
|
64785
|
+
const stat8 = lstatSync3(targetPath);
|
|
64786
|
+
if (stat8.isSymbolicLink()) {
|
|
65228
64787
|
const realTarget = realpathSync2(targetPath);
|
|
65229
64788
|
if (!realTarget.startsWith(resolvedBase)) {
|
|
65230
64789
|
return false;
|
|
@@ -65232,7 +64791,7 @@ function isPathSafe(basePath, targetPath) {
|
|
|
65232
64791
|
}
|
|
65233
64792
|
} catch {}
|
|
65234
64793
|
const resolvedTarget = resolve17(targetPath);
|
|
65235
|
-
const relativePath =
|
|
64794
|
+
const relativePath = relative14(resolvedBase, resolvedTarget);
|
|
65236
64795
|
return !relativePath.startsWith("..") && !relativePath.startsWith("/") && resolvedTarget.startsWith(resolvedBase);
|
|
65237
64796
|
}
|
|
65238
64797
|
|
|
@@ -65263,8 +64822,8 @@ var MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
|
|
|
65263
64822
|
class FileDownloader {
|
|
65264
64823
|
async downloadAsset(asset, destDir) {
|
|
65265
64824
|
try {
|
|
65266
|
-
const destPath =
|
|
65267
|
-
await
|
|
64825
|
+
const destPath = join86(destDir, asset.name);
|
|
64826
|
+
await mkdir22(destDir, { recursive: true });
|
|
65268
64827
|
output.info(`Downloading ${asset.name} (${formatBytes(asset.size)})...`);
|
|
65269
64828
|
logger.verbose("Download details", {
|
|
65270
64829
|
url: asset.browser_download_url,
|
|
@@ -65348,8 +64907,8 @@ class FileDownloader {
|
|
|
65348
64907
|
}
|
|
65349
64908
|
async downloadFile(params) {
|
|
65350
64909
|
const { url, name, size, destDir, token } = params;
|
|
65351
|
-
const destPath =
|
|
65352
|
-
await
|
|
64910
|
+
const destPath = join86(destDir, name);
|
|
64911
|
+
await mkdir22(destDir, { recursive: true });
|
|
65353
64912
|
output.info(`Downloading ${name}${size ? ` (${formatBytes(size)})` : ""}...`);
|
|
65354
64913
|
const headers = {};
|
|
65355
64914
|
const isTarballUrl = /\/(tarball|zipball)\//.test(url);
|
|
@@ -65451,7 +65010,7 @@ init_logger();
|
|
|
65451
65010
|
init_types2();
|
|
65452
65011
|
import { constants as constants3 } from "node:fs";
|
|
65453
65012
|
import { access as access3, readdir as readdir25 } from "node:fs/promises";
|
|
65454
|
-
import { join as
|
|
65013
|
+
import { join as join87 } from "node:path";
|
|
65455
65014
|
async function validateExtraction(extractDir) {
|
|
65456
65015
|
try {
|
|
65457
65016
|
const entries = await readdir25(extractDir, { encoding: "utf8" });
|
|
@@ -65463,7 +65022,7 @@ async function validateExtraction(extractDir) {
|
|
|
65463
65022
|
const missingPaths = [];
|
|
65464
65023
|
for (const path8 of criticalPaths) {
|
|
65465
65024
|
try {
|
|
65466
|
-
await access3(
|
|
65025
|
+
await access3(join87(extractDir, path8), constants3.F_OK);
|
|
65467
65026
|
logger.debug(`Found: ${path8}`);
|
|
65468
65027
|
} catch {
|
|
65469
65028
|
logger.warning(`Expected path not found: ${path8}`);
|
|
@@ -65484,8 +65043,8 @@ async function validateExtraction(extractDir) {
|
|
|
65484
65043
|
|
|
65485
65044
|
// src/domains/installation/extraction/tar-extractor.ts
|
|
65486
65045
|
init_logger();
|
|
65487
|
-
import { copyFile as copyFile5, mkdir as
|
|
65488
|
-
import { join as
|
|
65046
|
+
import { copyFile as copyFile5, mkdir as mkdir24, readdir as readdir27, rm as rm9, stat as stat8 } from "node:fs/promises";
|
|
65047
|
+
import { join as join89 } from "node:path";
|
|
65489
65048
|
|
|
65490
65049
|
// node_modules/tar/dist/esm/index.min.js
|
|
65491
65050
|
import Kr from "events";
|
|
@@ -68697,8 +68256,8 @@ function decodeFilePath(path8) {
|
|
|
68697
68256
|
// src/domains/installation/utils/file-utils.ts
|
|
68698
68257
|
init_logger();
|
|
68699
68258
|
init_types2();
|
|
68700
|
-
import { copyFile as copyFile4, lstat as
|
|
68701
|
-
import { join as
|
|
68259
|
+
import { copyFile as copyFile4, lstat as lstat6, mkdir as mkdir23, readdir as readdir26 } from "node:fs/promises";
|
|
68260
|
+
import { join as join88, relative as relative15 } from "node:path";
|
|
68702
68261
|
async function withRetry2(fn2, retries = 3) {
|
|
68703
68262
|
for (let i = 0;i < retries; i++) {
|
|
68704
68263
|
try {
|
|
@@ -68717,12 +68276,12 @@ var isRetryable2 = (e2) => {
|
|
|
68717
68276
|
};
|
|
68718
68277
|
var delay2 = (ms2) => new Promise((r2) => setTimeout(r2, ms2));
|
|
68719
68278
|
async function moveDirectoryContents(sourceDir, destDir, shouldExclude, sizeTracker) {
|
|
68720
|
-
await
|
|
68279
|
+
await mkdir23(destDir, { recursive: true });
|
|
68721
68280
|
const entries = await readdir26(sourceDir, { encoding: "utf8" });
|
|
68722
68281
|
for (const entry of entries) {
|
|
68723
|
-
const sourcePath =
|
|
68724
|
-
const destPath =
|
|
68725
|
-
const relativePath =
|
|
68282
|
+
const sourcePath = join88(sourceDir, entry);
|
|
68283
|
+
const destPath = join88(destDir, entry);
|
|
68284
|
+
const relativePath = relative15(sourceDir, sourcePath);
|
|
68726
68285
|
if (!isPathSafe(destDir, destPath)) {
|
|
68727
68286
|
logger.warning(`Skipping unsafe path: ${relativePath}`);
|
|
68728
68287
|
throw new ExtractionError(`Path traversal attempt detected: ${relativePath}`);
|
|
@@ -68731,7 +68290,7 @@ async function moveDirectoryContents(sourceDir, destDir, shouldExclude, sizeTrac
|
|
|
68731
68290
|
logger.debug(`Excluding: ${relativePath}`);
|
|
68732
68291
|
continue;
|
|
68733
68292
|
}
|
|
68734
|
-
const entryStat = await
|
|
68293
|
+
const entryStat = await lstat6(sourcePath);
|
|
68735
68294
|
if (entryStat.isDirectory()) {
|
|
68736
68295
|
await copyDirectory(sourcePath, destPath, shouldExclude, sizeTracker);
|
|
68737
68296
|
} else if (entryStat.isFile()) {
|
|
@@ -68745,12 +68304,12 @@ async function moveDirectoryContents(sourceDir, destDir, shouldExclude, sizeTrac
|
|
|
68745
68304
|
}
|
|
68746
68305
|
}
|
|
68747
68306
|
async function copyDirectory(sourceDir, destDir, shouldExclude, sizeTracker) {
|
|
68748
|
-
await
|
|
68307
|
+
await mkdir23(destDir, { recursive: true });
|
|
68749
68308
|
const entries = await readdir26(sourceDir, { encoding: "utf8" });
|
|
68750
68309
|
for (const entry of entries) {
|
|
68751
|
-
const sourcePath =
|
|
68752
|
-
const destPath =
|
|
68753
|
-
const relativePath =
|
|
68310
|
+
const sourcePath = join88(sourceDir, entry);
|
|
68311
|
+
const destPath = join88(destDir, entry);
|
|
68312
|
+
const relativePath = relative15(sourceDir, sourcePath);
|
|
68754
68313
|
if (!isPathSafe(destDir, destPath)) {
|
|
68755
68314
|
logger.warning(`Skipping unsafe path: ${relativePath}`);
|
|
68756
68315
|
throw new ExtractionError(`Path traversal attempt detected: ${relativePath}`);
|
|
@@ -68759,7 +68318,7 @@ async function copyDirectory(sourceDir, destDir, shouldExclude, sizeTracker) {
|
|
|
68759
68318
|
logger.debug(`Excluding: ${relativePath}`);
|
|
68760
68319
|
continue;
|
|
68761
68320
|
}
|
|
68762
|
-
const entryStat = await
|
|
68321
|
+
const entryStat = await lstat6(sourcePath);
|
|
68763
68322
|
if (entryStat.isDirectory()) {
|
|
68764
68323
|
await copyDirectory(sourcePath, destPath, shouldExclude, sizeTracker);
|
|
68765
68324
|
} else if (entryStat.isFile()) {
|
|
@@ -68777,7 +68336,7 @@ async function copyDirectory(sourceDir, destDir, shouldExclude, sizeTracker) {
|
|
|
68777
68336
|
class TarExtractor {
|
|
68778
68337
|
async extract(archivePath, destDir, shouldExclude, sizeTracker) {
|
|
68779
68338
|
const tempExtractDir = `${destDir}-temp`;
|
|
68780
|
-
await
|
|
68339
|
+
await mkdir24(tempExtractDir, { recursive: true });
|
|
68781
68340
|
try {
|
|
68782
68341
|
await co({
|
|
68783
68342
|
file: archivePath,
|
|
@@ -68804,8 +68363,8 @@ class TarExtractor {
|
|
|
68804
68363
|
logger.debug(`Root entries: ${entries.join(", ")}`);
|
|
68805
68364
|
if (entries.length === 1) {
|
|
68806
68365
|
const rootEntry = entries[0];
|
|
68807
|
-
const rootPath =
|
|
68808
|
-
const rootStat = await
|
|
68366
|
+
const rootPath = join89(tempExtractDir, rootEntry);
|
|
68367
|
+
const rootStat = await stat8(rootPath);
|
|
68809
68368
|
if (rootStat.isDirectory()) {
|
|
68810
68369
|
const rootContents = await readdir27(rootPath, { encoding: "utf8" });
|
|
68811
68370
|
logger.debug(`Root directory '${rootEntry}' contains: ${rootContents.join(", ")}`);
|
|
@@ -68819,8 +68378,8 @@ class TarExtractor {
|
|
|
68819
68378
|
await moveDirectoryContents(tempExtractDir, destDir, shouldExclude, sizeTracker);
|
|
68820
68379
|
}
|
|
68821
68380
|
} else {
|
|
68822
|
-
await
|
|
68823
|
-
await copyFile5(rootPath,
|
|
68381
|
+
await mkdir24(destDir, { recursive: true });
|
|
68382
|
+
await copyFile5(rootPath, join89(destDir, rootEntry));
|
|
68824
68383
|
}
|
|
68825
68384
|
} else {
|
|
68826
68385
|
logger.debug("Multiple root entries - moving all");
|
|
@@ -68840,14 +68399,14 @@ class TarExtractor {
|
|
|
68840
68399
|
// src/domains/installation/extraction/zip-extractor.ts
|
|
68841
68400
|
init_logger();
|
|
68842
68401
|
import { createWriteStream as createWriteStream3 } from "node:fs";
|
|
68843
|
-
import { chmod as chmod3, copyFile as copyFile6, mkdir as
|
|
68844
|
-
import { dirname as dirname23, join as
|
|
68402
|
+
import { chmod as chmod3, copyFile as copyFile6, mkdir as mkdir25, readdir as readdir28, rm as rm10, stat as stat9 } from "node:fs/promises";
|
|
68403
|
+
import { dirname as dirname23, join as join90, resolve as resolve18 } from "node:path";
|
|
68845
68404
|
import { pipeline } from "node:stream/promises";
|
|
68846
68405
|
import yauzl from "yauzl-promise";
|
|
68847
68406
|
class ZipExtractor {
|
|
68848
68407
|
async extract(archivePath, destDir, shouldExclude, sizeTracker) {
|
|
68849
68408
|
const tempExtractDir = `${destDir}-temp`;
|
|
68850
|
-
await
|
|
68409
|
+
await mkdir25(tempExtractDir, { recursive: true });
|
|
68851
68410
|
try {
|
|
68852
68411
|
await this.extractToDir(archivePath, tempExtractDir);
|
|
68853
68412
|
logger.debug(`Extracted ZIP to temp: ${tempExtractDir}`);
|
|
@@ -68855,8 +68414,8 @@ class ZipExtractor {
|
|
|
68855
68414
|
logger.debug(`Root entries: ${entries.join(", ")}`);
|
|
68856
68415
|
if (entries.length === 1) {
|
|
68857
68416
|
const rootEntry = entries[0];
|
|
68858
|
-
const rootPath =
|
|
68859
|
-
const rootStat = await
|
|
68417
|
+
const rootPath = join90(tempExtractDir, rootEntry);
|
|
68418
|
+
const rootStat = await stat9(rootPath);
|
|
68860
68419
|
if (rootStat.isDirectory()) {
|
|
68861
68420
|
const rootContents = await readdir28(rootPath, { encoding: "utf8" });
|
|
68862
68421
|
logger.debug(`Root directory '${rootEntry}' contains: ${rootContents.join(", ")}`);
|
|
@@ -68870,8 +68429,8 @@ class ZipExtractor {
|
|
|
68870
68429
|
await moveDirectoryContents(tempExtractDir, destDir, shouldExclude, sizeTracker);
|
|
68871
68430
|
}
|
|
68872
68431
|
} else {
|
|
68873
|
-
await
|
|
68874
|
-
await copyFile6(rootPath,
|
|
68432
|
+
await mkdir25(destDir, { recursive: true });
|
|
68433
|
+
await copyFile6(rootPath, join90(destDir, rootEntry));
|
|
68875
68434
|
}
|
|
68876
68435
|
} else {
|
|
68877
68436
|
logger.debug("Multiple root entries - moving all");
|
|
@@ -68899,10 +68458,10 @@ class ZipExtractor {
|
|
|
68899
68458
|
throw new Error(`Unsafe zip entry path (zip-slip): ${name}`);
|
|
68900
68459
|
}
|
|
68901
68460
|
if (name.endsWith("/")) {
|
|
68902
|
-
await
|
|
68461
|
+
await mkdir25(outPath, { recursive: true });
|
|
68903
68462
|
continue;
|
|
68904
68463
|
}
|
|
68905
|
-
await
|
|
68464
|
+
await mkdir25(dirname23(outPath), { recursive: true });
|
|
68906
68465
|
const readStream = await entry.openReadStream();
|
|
68907
68466
|
await pipeline(readStream, createWriteStream3(outPath));
|
|
68908
68467
|
const unixMode = entry.externalFileAttributes >>> 16 & 511;
|
|
@@ -68961,7 +68520,7 @@ class DownloadManager {
|
|
|
68961
68520
|
return this.fileDownloader.downloadFile(params);
|
|
68962
68521
|
}
|
|
68963
68522
|
async extractArchive(archivePath, destDir, archiveType) {
|
|
68964
|
-
const archiveStats = await
|
|
68523
|
+
const archiveStats = await stat10(archivePath);
|
|
68965
68524
|
if (archiveStats.size > MAX_ARCHIVE_SIZE) {
|
|
68966
68525
|
throw new ExtractionError(`Archive exceeds ${formatBytes(MAX_ARCHIVE_SIZE)} limit: ${formatBytes(archiveStats.size)}`);
|
|
68967
68526
|
}
|
|
@@ -68975,7 +68534,7 @@ class DownloadManager {
|
|
|
68975
68534
|
try {
|
|
68976
68535
|
this.sizeTracker.reset();
|
|
68977
68536
|
const detectedType = archiveType || detectArchiveType(archivePath);
|
|
68978
|
-
await
|
|
68537
|
+
await mkdir26(destDir, { recursive: true });
|
|
68979
68538
|
if (detectedType === "tar.gz") {
|
|
68980
68539
|
await this.tarExtractor.extract(archivePath, destDir, this.shouldExclude, this.sizeTracker);
|
|
68981
68540
|
} else if (detectedType === "zip") {
|
|
@@ -69000,9 +68559,9 @@ class DownloadManager {
|
|
|
69000
68559
|
async createTempDir() {
|
|
69001
68560
|
const timestamp = Date.now();
|
|
69002
68561
|
const counter = DownloadManager.tempDirCounter++;
|
|
69003
|
-
const primaryTempDir =
|
|
68562
|
+
const primaryTempDir = join91(tmpdir3(), `takumi-${timestamp}-${counter}`);
|
|
69004
68563
|
try {
|
|
69005
|
-
await
|
|
68564
|
+
await mkdir26(primaryTempDir, { recursive: true });
|
|
69006
68565
|
logger.debug(`Created temp directory: ${primaryTempDir}`);
|
|
69007
68566
|
registerTempDir(primaryTempDir);
|
|
69008
68567
|
return primaryTempDir;
|
|
@@ -69017,9 +68576,9 @@ Solutions:
|
|
|
69017
68576
|
2. Set HOME environment variable
|
|
69018
68577
|
3. Try running from a different directory`);
|
|
69019
68578
|
}
|
|
69020
|
-
const fallbackTempDir =
|
|
68579
|
+
const fallbackTempDir = join91(homeDir, ".sunagentkit", "tmp", `takumi-${timestamp}-${counter}`);
|
|
69021
68580
|
try {
|
|
69022
|
-
await
|
|
68581
|
+
await mkdir26(fallbackTempDir, { recursive: true });
|
|
69023
68582
|
logger.debug(`Created temp directory (fallback): ${fallbackTempDir}`);
|
|
69024
68583
|
logger.warning(`Using fallback temp directory: ${fallbackTempDir}
|
|
69025
68584
|
(OS temp directory was not accessible)`);
|
|
@@ -69060,8 +68619,8 @@ function buildReleaseAllowlist(layout) {
|
|
|
69060
68619
|
async function ensureLayoutSourceDir(projectRoot, layout, strict) {
|
|
69061
68620
|
const sourceDir = path8.join(projectRoot, layout.sourceDir);
|
|
69062
68621
|
try {
|
|
69063
|
-
const
|
|
69064
|
-
if (!
|
|
68622
|
+
const stat11 = await fs11.promises.stat(sourceDir);
|
|
68623
|
+
if (!stat11.isDirectory()) {
|
|
69065
68624
|
throw new Error(`Expected source directory "${layout.sourceDir}" exists but is not a directory.`);
|
|
69066
68625
|
}
|
|
69067
68626
|
return sourceDir;
|
|
@@ -69302,8 +68861,8 @@ async function useLocalKitPath(kitPath, isMonorepo) {
|
|
|
69302
68861
|
output.section("Using local kit");
|
|
69303
68862
|
const absolutePath = path8.resolve(kitPath);
|
|
69304
68863
|
try {
|
|
69305
|
-
const
|
|
69306
|
-
if (!
|
|
68864
|
+
const stat11 = await fs11.promises.stat(absolutePath);
|
|
68865
|
+
if (!stat11.isDirectory()) {
|
|
69307
68866
|
throw new Error(`--kit-path must point to a directory, not a file.
|
|
69308
68867
|
|
|
69309
68868
|
Provided path: ${absolutePath}
|
|
@@ -69341,8 +68900,8 @@ Please verify the path exists and is accessible.`);
|
|
|
69341
68900
|
}
|
|
69342
68901
|
const claudeDir = path8.join(absolutePath, DEFAULT_KIT_LAYOUT.runtimeDir);
|
|
69343
68902
|
try {
|
|
69344
|
-
const
|
|
69345
|
-
if (!
|
|
68903
|
+
const stat11 = await fs11.promises.stat(claudeDir);
|
|
68904
|
+
if (!stat11.isDirectory()) {
|
|
69346
68905
|
logger.warning(`Warning: ${claudeDir} exists but is not a directory.
|
|
69347
68906
|
This may not be a valid Takumi installation.`);
|
|
69348
68907
|
}
|
|
@@ -69376,15 +68935,15 @@ async function extractLocalArchive(archivePath, exclude) {
|
|
|
69376
68935
|
const absolutePath = path8.resolve(archivePath);
|
|
69377
68936
|
validateArchiveFormat(absolutePath);
|
|
69378
68937
|
try {
|
|
69379
|
-
const
|
|
69380
|
-
if (!
|
|
68938
|
+
const stat11 = await fs11.promises.stat(absolutePath);
|
|
68939
|
+
if (!stat11.isFile()) {
|
|
69381
68940
|
throw new Error(`--archive must point to a file, not a directory.
|
|
69382
68941
|
|
|
69383
68942
|
Provided path: ${absolutePath}
|
|
69384
68943
|
|
|
69385
68944
|
If you meant to use an extracted kit directory, use --kit-path instead.`);
|
|
69386
68945
|
}
|
|
69387
|
-
if (
|
|
68946
|
+
if (stat11.size === 0) {
|
|
69388
68947
|
throw new Error(`Archive file is empty: ${absolutePath}
|
|
69389
68948
|
|
|
69390
68949
|
The file exists but contains no data. Please verify the archive is not corrupted.`);
|
|
@@ -69545,8 +69104,28 @@ async function handleDownload(ctx) {
|
|
|
69545
69104
|
// src/commands/init/phases/options-resolver.ts
|
|
69546
69105
|
init_logger();
|
|
69547
69106
|
init_types2();
|
|
69107
|
+
function parseKitFlag(raw) {
|
|
69108
|
+
const tokens = raw.split(",").map((s3) => s3.trim()).filter((s3) => s3.length > 0);
|
|
69109
|
+
if (tokens.length === 0)
|
|
69110
|
+
return null;
|
|
69111
|
+
if (tokens.length === 1 && tokens[0] === "all") {
|
|
69112
|
+
return Object.keys(AVAILABLE_KITS);
|
|
69113
|
+
}
|
|
69114
|
+
const seen = new Set;
|
|
69115
|
+
const out = [];
|
|
69116
|
+
for (const tok of tokens) {
|
|
69117
|
+
if (!isValidKitType(tok))
|
|
69118
|
+
return null;
|
|
69119
|
+
if (!seen.has(tok)) {
|
|
69120
|
+
seen.add(tok);
|
|
69121
|
+
out.push(tok);
|
|
69122
|
+
}
|
|
69123
|
+
}
|
|
69124
|
+
return out;
|
|
69125
|
+
}
|
|
69548
69126
|
async function resolveOptions(ctx) {
|
|
69549
69127
|
const explicitDir = ctx.rawOptions.dir !== undefined;
|
|
69128
|
+
const explicitAgents = Array.isArray(ctx.rawOptions.agents) && ctx.rawOptions.agents.length > 0;
|
|
69550
69129
|
const parsed = UpdateCommandOptionsSchema.parse(ctx.rawOptions);
|
|
69551
69130
|
const validOptions = {
|
|
69552
69131
|
kit: parsed.kit,
|
|
@@ -69589,8 +69168,6 @@ async function resolveOptions(ctx) {
|
|
|
69589
69168
|
` + "For other agents, run init without --sync to perform a fresh install.");
|
|
69590
69169
|
}
|
|
69591
69170
|
validOptions.agents = Array.from(new Set(validOptions.agents));
|
|
69592
|
-
} else {
|
|
69593
|
-
validOptions.agents = ["claude-code"];
|
|
69594
69171
|
}
|
|
69595
69172
|
ConfigManager.setGlobalFlag(validOptions.global);
|
|
69596
69173
|
if (validOptions.global) {
|
|
@@ -69635,10 +69212,52 @@ Example: takumi init --use-git --release v2.1.0`);
|
|
|
69635
69212
|
Choose one approach.`);
|
|
69636
69213
|
}
|
|
69637
69214
|
const isNonInteractive2 = validOptions.yes || !process.stdin.isTTY || process.env.CI === "true" || process.env.NON_INTERACTIVE === "true";
|
|
69215
|
+
if (validOptions.kit) {
|
|
69216
|
+
const parsed2 = parseKitFlag(validOptions.kit);
|
|
69217
|
+
if (!parsed2 || parsed2.length === 0) {
|
|
69218
|
+
throw new Error(`Invalid --kit value: "${validOptions.kit}".
|
|
69219
|
+
Valid kits: ${Object.keys(AVAILABLE_KITS).join(", ")}`);
|
|
69220
|
+
}
|
|
69221
|
+
if (!parsed2.includes(BASE_KIT)) {
|
|
69222
|
+
if (isNonInteractive2) {
|
|
69223
|
+
throw new Error(`The "${BASE_KIT}" kit is required when installing additional kits.
|
|
69224
|
+
Re-run with explicit base kit, e.g. --kit ${BASE_KIT} --kit ${parsed2.join(" --kit ")}`);
|
|
69225
|
+
}
|
|
69226
|
+
logger.info(`"${BASE_KIT}" is the required base kit — adding it automatically.`);
|
|
69227
|
+
parsed2.unshift(BASE_KIT);
|
|
69228
|
+
} else {
|
|
69229
|
+
const idx = parsed2.indexOf(BASE_KIT);
|
|
69230
|
+
if (idx > 0) {
|
|
69231
|
+
parsed2.splice(idx, 1);
|
|
69232
|
+
parsed2.unshift(BASE_KIT);
|
|
69233
|
+
}
|
|
69234
|
+
}
|
|
69235
|
+
validOptions.selectedKits = parsed2;
|
|
69236
|
+
}
|
|
69638
69237
|
if (validOptions.yes) {
|
|
69639
69238
|
logger.info("Running in non-interactive mode (--yes flag)");
|
|
69640
69239
|
}
|
|
69641
69240
|
const initialInstallSkills = validOptions.installSkills ? true : undefined;
|
|
69241
|
+
if (!explicitAgents) {
|
|
69242
|
+
if (isNonInteractive2) {
|
|
69243
|
+
validOptions.agents = ["claude-code"];
|
|
69244
|
+
} else {
|
|
69245
|
+
const supported = listSupportedProviders();
|
|
69246
|
+
const selected = await ctx.prompts.promptAgentSelection(supported);
|
|
69247
|
+
if (selected === null) {
|
|
69248
|
+
logger.warning("Agent selection cancelled");
|
|
69249
|
+
return {
|
|
69250
|
+
...ctx,
|
|
69251
|
+
options: validOptions,
|
|
69252
|
+
explicitDir,
|
|
69253
|
+
isNonInteractive: isNonInteractive2,
|
|
69254
|
+
installSkills: initialInstallSkills,
|
|
69255
|
+
cancelled: true
|
|
69256
|
+
};
|
|
69257
|
+
}
|
|
69258
|
+
validOptions.agents = selected;
|
|
69259
|
+
}
|
|
69260
|
+
}
|
|
69642
69261
|
return {
|
|
69643
69262
|
...ctx,
|
|
69644
69263
|
options: validOptions,
|
|
@@ -69648,8 +69267,8 @@ Choose one approach.`);
|
|
|
69648
69267
|
};
|
|
69649
69268
|
}
|
|
69650
69269
|
// src/commands/init/phases/selection-handler.ts
|
|
69651
|
-
import { mkdir as
|
|
69652
|
-
import { join as
|
|
69270
|
+
import { mkdir as mkdir27 } from "node:fs/promises";
|
|
69271
|
+
import { join as join95, resolve as resolve22 } from "node:path";
|
|
69653
69272
|
|
|
69654
69273
|
// src/commands/shared/agent-selector.ts
|
|
69655
69274
|
function selectAgents(opts = {}) {
|
|
@@ -69730,13 +69349,13 @@ async function runPreflightChecks() {
|
|
|
69730
69349
|
errorLines: []
|
|
69731
69350
|
};
|
|
69732
69351
|
try {
|
|
69733
|
-
const { stdout
|
|
69734
|
-
const match2 =
|
|
69352
|
+
const { stdout } = await execAsync7("gh --version", { timeout: GH_COMMAND_TIMEOUT_MS });
|
|
69353
|
+
const match2 = stdout.match(/(\d+\.\d+\.\d+)/);
|
|
69735
69354
|
if (!match2) {
|
|
69736
|
-
logger.debug(`GitHub CLI version not detected from output: ${
|
|
69355
|
+
logger.debug(`GitHub CLI version not detected from output: ${stdout.trim()}`);
|
|
69737
69356
|
result.ghInstalled = true;
|
|
69738
69357
|
result.errorLines.push("✗ GitHub CLI installed but version could not be detected");
|
|
69739
|
-
result.errorLines.push(` Output: ${
|
|
69358
|
+
result.errorLines.push(` Output: ${stdout.trim().slice(0, 100)}`);
|
|
69740
69359
|
result.errorLines.push(" Try running: gh --version");
|
|
69741
69360
|
return result;
|
|
69742
69361
|
}
|
|
@@ -69794,7 +69413,7 @@ async function runPreflightChecks() {
|
|
|
69794
69413
|
|
|
69795
69414
|
// src/domains/installation/fresh-installer.ts
|
|
69796
69415
|
import { existsSync as existsSync45, readdirSync as readdirSync4, rmSync as rmSync4, rmdirSync as rmdirSync2, unlinkSync as unlinkSync4 } from "node:fs";
|
|
69797
|
-
import { dirname as dirname26, join as
|
|
69416
|
+
import { dirname as dirname26, join as join94, resolve as resolve21 } from "node:path";
|
|
69798
69417
|
init_logger();
|
|
69799
69418
|
var import_fs_extra31 = __toESM(require_lib(), 1);
|
|
69800
69419
|
var TAKUMI_SUBDIRECTORIES = ["commands", "agents", "skills", "rules", "hooks"];
|
|
@@ -69866,7 +69485,7 @@ async function removeFilesByOwnership(claudeDir, analysis, includeModified) {
|
|
|
69866
69485
|
const filesToRemove = includeModified ? [...analysis.ckFiles, ...analysis.ckModifiedFiles] : analysis.ckFiles;
|
|
69867
69486
|
const filesToPreserve = includeModified ? analysis.userFiles : [...analysis.ckModifiedFiles, ...analysis.userFiles];
|
|
69868
69487
|
for (const file of filesToRemove) {
|
|
69869
|
-
const fullPath =
|
|
69488
|
+
const fullPath = join94(claudeDir, file.path);
|
|
69870
69489
|
try {
|
|
69871
69490
|
if (existsSync45(fullPath)) {
|
|
69872
69491
|
unlinkSync4(fullPath);
|
|
@@ -69891,7 +69510,7 @@ async function removeFilesByOwnership(claudeDir, analysis, includeModified) {
|
|
|
69891
69510
|
};
|
|
69892
69511
|
}
|
|
69893
69512
|
async function updateMetadataAfterFresh(claudeDir, removedFiles) {
|
|
69894
|
-
const metadataPath =
|
|
69513
|
+
const metadataPath = join94(claudeDir, "metadata.json");
|
|
69895
69514
|
if (!await import_fs_extra31.pathExists(metadataPath)) {
|
|
69896
69515
|
return;
|
|
69897
69516
|
}
|
|
@@ -69934,7 +69553,7 @@ async function removeSubdirectoriesFallback(claudeDir) {
|
|
|
69934
69553
|
const removedFiles = [];
|
|
69935
69554
|
let removedDirCount = 0;
|
|
69936
69555
|
for (const subdir of TAKUMI_SUBDIRECTORIES) {
|
|
69937
|
-
const subdirPath =
|
|
69556
|
+
const subdirPath = join94(claudeDir, subdir);
|
|
69938
69557
|
if (await import_fs_extra31.pathExists(subdirPath)) {
|
|
69939
69558
|
rmSync4(subdirPath, { recursive: true, force: true });
|
|
69940
69559
|
removedDirCount++;
|
|
@@ -69942,7 +69561,7 @@ async function removeSubdirectoriesFallback(claudeDir) {
|
|
|
69942
69561
|
logger.debug(`Removed subdirectory: ${subdir}/`);
|
|
69943
69562
|
}
|
|
69944
69563
|
}
|
|
69945
|
-
const metadataPath =
|
|
69564
|
+
const metadataPath = join94(claudeDir, "metadata.json");
|
|
69946
69565
|
if (await import_fs_extra31.pathExists(metadataPath)) {
|
|
69947
69566
|
unlinkSync4(metadataPath);
|
|
69948
69567
|
removedFiles.push("metadata.json");
|
|
@@ -69979,10 +69598,56 @@ async function handleFreshInstallation(claudeDir, prompts) {
|
|
|
69979
69598
|
if (result.preservedCount > 0) {
|
|
69980
69599
|
logger.verbose(`Preserved user files: ${result.preservedFiles.slice(0, 5).join(", ")}${result.preservedFiles.length > 5 ? ` and ${result.preservedFiles.length - 5} more` : ""}`);
|
|
69981
69600
|
}
|
|
69982
|
-
return true;
|
|
69983
|
-
} catch (error) {
|
|
69984
|
-
spinner.fail("Failed to remove Takumi files");
|
|
69985
|
-
throw new Error(`Failed to remove files: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
69601
|
+
return true;
|
|
69602
|
+
} catch (error) {
|
|
69603
|
+
spinner.fail("Failed to remove Takumi files");
|
|
69604
|
+
throw new Error(`Failed to remove files: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
69605
|
+
}
|
|
69606
|
+
}
|
|
69607
|
+
|
|
69608
|
+
// src/domains/versioning/checking/version-utils.ts
|
|
69609
|
+
var import_compare_versions2 = __toESM(require_umd(), 1);
|
|
69610
|
+
function isUpdateCheckDisabled() {
|
|
69611
|
+
return process.env.NO_UPDATE_NOTIFIER === "1" || process.env.NO_UPDATE_NOTIFIER === "true" || !process.stdout.isTTY;
|
|
69612
|
+
}
|
|
69613
|
+
function normalizeVersion(version3) {
|
|
69614
|
+
return version3.replace(/^v/i, "");
|
|
69615
|
+
}
|
|
69616
|
+
function isPrereleaseVersion(version3) {
|
|
69617
|
+
if (!version3)
|
|
69618
|
+
return false;
|
|
69619
|
+
return /-(beta|alpha|rc|dev)[.\d]/i.test(normalizeVersion(version3));
|
|
69620
|
+
}
|
|
69621
|
+
function parseVersionParts(version3) {
|
|
69622
|
+
const normalized = normalizeVersion(version3);
|
|
69623
|
+
const [base, ...prereleaseParts] = normalized.split("-");
|
|
69624
|
+
return {
|
|
69625
|
+
base,
|
|
69626
|
+
prerelease: prereleaseParts.length > 0 ? prereleaseParts.join("-") : null
|
|
69627
|
+
};
|
|
69628
|
+
}
|
|
69629
|
+
function isPrereleaseOfSameBase(currentVersion, latestVersion) {
|
|
69630
|
+
const current = parseVersionParts(currentVersion);
|
|
69631
|
+
const latest = parseVersionParts(latestVersion);
|
|
69632
|
+
if (!current.prerelease)
|
|
69633
|
+
return false;
|
|
69634
|
+
if (latest.prerelease !== null)
|
|
69635
|
+
return false;
|
|
69636
|
+
return current.base === latest.base;
|
|
69637
|
+
}
|
|
69638
|
+
function versionsMatch(installed, latest) {
|
|
69639
|
+
return normalizeVersion(installed) === normalizeVersion(latest);
|
|
69640
|
+
}
|
|
69641
|
+
function isNewerVersion(currentVersion, latestVersion) {
|
|
69642
|
+
try {
|
|
69643
|
+
const current = normalizeVersion(currentVersion);
|
|
69644
|
+
const latest = normalizeVersion(latestVersion);
|
|
69645
|
+
if (isPrereleaseOfSameBase(current, latest)) {
|
|
69646
|
+
return false;
|
|
69647
|
+
}
|
|
69648
|
+
return import_compare_versions2.compareVersions(latest, current) > 0;
|
|
69649
|
+
} catch {
|
|
69650
|
+
return false;
|
|
69986
69651
|
}
|
|
69987
69652
|
}
|
|
69988
69653
|
|
|
@@ -70052,275 +69717,950 @@ async function handleSelection(ctx) {
|
|
|
70052
69717
|
return { ...ctx, cancelled: true };
|
|
70053
69718
|
}
|
|
70054
69719
|
}
|
|
70055
|
-
let
|
|
70056
|
-
|
|
70057
|
-
if (kitOption) {
|
|
69720
|
+
let selectedKits = ctx.options.selectedKits;
|
|
69721
|
+
if (!selectedKits || selectedKits.length === 0) {
|
|
70058
69722
|
const allKitTypes = Object.keys(AVAILABLE_KITS);
|
|
70059
|
-
|
|
70060
|
-
|
|
70061
|
-
if (
|
|
70062
|
-
|
|
69723
|
+
const kitOption = ctx.options.kit || config.defaults?.kit;
|
|
69724
|
+
if (kitOption) {
|
|
69725
|
+
if (kitOption === "all") {
|
|
69726
|
+
selectedKits = accessibleKits ?? allKitTypes;
|
|
69727
|
+
} else if (!isValidKitType(kitOption)) {
|
|
69728
|
+
logger.error(`Invalid kit: ${kitOption}`);
|
|
69729
|
+
logger.info(`Valid kits: ${allKitTypes.join(", ")}`);
|
|
69730
|
+
return { ...ctx, cancelled: true };
|
|
69731
|
+
} else {
|
|
69732
|
+
selectedKits = [kitOption];
|
|
69733
|
+
}
|
|
69734
|
+
}
|
|
69735
|
+
if (!selectedKits || selectedKits.length === 0) {
|
|
69736
|
+
if (ctx.isNonInteractive) {
|
|
69737
|
+
if (!accessibleKits || accessibleKits.length === 0) {
|
|
69738
|
+
throw new Error("Kit must be specified via --kit flag in non-interactive mode (no accessible kits detected)");
|
|
69739
|
+
}
|
|
69740
|
+
selectedKits = [accessibleKits[0]];
|
|
69741
|
+
logger.info(`Auto-selected: ${AVAILABLE_KITS[selectedKits[0]].name}`);
|
|
69742
|
+
} else if (accessibleKits?.length === 1) {
|
|
69743
|
+
selectedKits = [accessibleKits[0]];
|
|
69744
|
+
logger.info(`Using ${AVAILABLE_KITS[selectedKits[0]].name} (only accessible kit)`);
|
|
69745
|
+
} else {
|
|
69746
|
+
selectedKits = await ctx.prompts.selectKits(accessibleKits);
|
|
69747
|
+
}
|
|
69748
|
+
}
|
|
69749
|
+
}
|
|
69750
|
+
if (accessibleKits) {
|
|
69751
|
+
const inaccessible = selectedKits.filter((k3) => !accessibleKits.includes(k3));
|
|
69752
|
+
if (inaccessible.length > 0) {
|
|
69753
|
+
logger.error(`No access to: ${inaccessible.map((k3) => AVAILABLE_KITS[k3].name).join(", ")}`);
|
|
69754
|
+
logger.info("Request access from your team lead or check your GitHub invitation");
|
|
69755
|
+
return { ...ctx, cancelled: true };
|
|
69756
|
+
}
|
|
69757
|
+
}
|
|
69758
|
+
if (selectedKits.includes(BASE_KIT)) {
|
|
69759
|
+
const idx = selectedKits.indexOf(BASE_KIT);
|
|
69760
|
+
if (idx > 0) {
|
|
69761
|
+
selectedKits.splice(idx, 1);
|
|
69762
|
+
selectedKits.unshift(BASE_KIT);
|
|
69763
|
+
}
|
|
69764
|
+
}
|
|
69765
|
+
const kitType = selectedKits[0];
|
|
69766
|
+
const kit = AVAILABLE_KITS[kitType];
|
|
69767
|
+
if (selectedKits.length > 1) {
|
|
69768
|
+
logger.info(`Selected kits: ${selectedKits.map((k3) => AVAILABLE_KITS[k3].name).join(", ")} (installed in order)`);
|
|
69769
|
+
} else {
|
|
69770
|
+
logger.info(`Selected kit: ${kit.name}`);
|
|
69771
|
+
}
|
|
69772
|
+
let targetDir;
|
|
69773
|
+
if (ctx.explicitDir) {
|
|
69774
|
+
targetDir = ctx.options.dir;
|
|
69775
|
+
logger.info(`Using explicit directory: ${targetDir}`);
|
|
69776
|
+
} else if (ctx.options.global) {
|
|
69777
|
+
const targetAgents = selectAgents({ explicit: ctx.options.agents });
|
|
69778
|
+
targetDir = resolveGlobalTargetDir(targetAgents);
|
|
69779
|
+
logger.info(`Using global kit directory: ${targetDir}`);
|
|
69780
|
+
} else {
|
|
69781
|
+
targetDir = config.defaults?.dir || ".";
|
|
69782
|
+
if (!config.defaults?.dir) {
|
|
69783
|
+
if (ctx.isNonInteractive) {
|
|
69784
|
+
logger.info("Using current directory as target");
|
|
69785
|
+
} else {
|
|
69786
|
+
targetDir = await ctx.prompts.getDirectory(targetDir);
|
|
69787
|
+
}
|
|
69788
|
+
}
|
|
69789
|
+
}
|
|
69790
|
+
const resolvedDir = resolve22(targetDir);
|
|
69791
|
+
logger.info(`Target directory: ${resolvedDir}`);
|
|
69792
|
+
if (!ctx.options.global && isLocalSameAsGlobal(resolvedDir)) {
|
|
69793
|
+
logger.warning("You're at HOME directory. Installing here modifies your GLOBAL Takumi.");
|
|
69794
|
+
if (!ctx.isNonInteractive) {
|
|
69795
|
+
const choice = await ctx.prompts.selectScope();
|
|
69796
|
+
if (choice === "cancel") {
|
|
69797
|
+
return { ...ctx, cancelled: true };
|
|
69798
|
+
}
|
|
69799
|
+
if (choice === "global") {
|
|
69800
|
+
logger.info("Proceeding with global installation");
|
|
69801
|
+
}
|
|
69802
|
+
if (choice === "different") {
|
|
69803
|
+
logger.info("Please run 'takumi init' from a project directory instead.");
|
|
69804
|
+
return { ...ctx, cancelled: true };
|
|
69805
|
+
}
|
|
69806
|
+
} else {
|
|
69807
|
+
logger.error("Cannot use local installation at HOME directory.");
|
|
69808
|
+
logger.info("Use -g/--global flag or run from a project directory.");
|
|
69809
|
+
return { ...ctx, cancelled: true };
|
|
69810
|
+
}
|
|
69811
|
+
}
|
|
69812
|
+
if (!await import_fs_extra32.pathExists(resolvedDir)) {
|
|
69813
|
+
if (ctx.options.global) {
|
|
69814
|
+
await mkdir27(resolvedDir, { recursive: true });
|
|
69815
|
+
logger.info(`Created global directory: ${resolvedDir}`);
|
|
69816
|
+
} else {
|
|
69817
|
+
logger.error(`Directory does not exist: ${resolvedDir}`);
|
|
69818
|
+
logger.info('Use "tkm new" to create a new project');
|
|
69819
|
+
return { ...ctx, cancelled: true };
|
|
69820
|
+
}
|
|
69821
|
+
}
|
|
69822
|
+
if (!ctx.options.fresh) {
|
|
69823
|
+
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
69824
|
+
const claudeDir = prefix ? join95(resolvedDir, prefix) : resolvedDir;
|
|
69825
|
+
try {
|
|
69826
|
+
const existingMetadata = await readManifest(claudeDir);
|
|
69827
|
+
if (existingMetadata?.kits) {
|
|
69828
|
+
const existingKitTypes = Object.keys(existingMetadata.kits);
|
|
69829
|
+
const otherKits = existingKitTypes.filter((k3) => k3 !== kitType);
|
|
69830
|
+
if (otherKits.length > 0) {
|
|
69831
|
+
const kitsRecord = existingMetadata.kits;
|
|
69832
|
+
const existingKitsDisplay = otherKits.map((k3) => `${k3}@${kitsRecord[k3]?.version || "unknown"}`).join(", ");
|
|
69833
|
+
if (!ctx.options.yes && !ctx.isNonInteractive) {
|
|
69834
|
+
try {
|
|
69835
|
+
const confirmAdd = await ctx.prompts.confirm(`${existingKitsDisplay} already installed. Add ${kit.name} alongside?`);
|
|
69836
|
+
if (!confirmAdd) {
|
|
69837
|
+
logger.warning("Multi-kit installation cancelled by user");
|
|
69838
|
+
return { ...ctx, cancelled: true };
|
|
69839
|
+
}
|
|
69840
|
+
logger.info(`Adding ${kit.name} alongside existing kit(s)`);
|
|
69841
|
+
} catch {
|
|
69842
|
+
logger.warning("Prompt cancelled or interrupted");
|
|
69843
|
+
return { ...ctx, cancelled: true };
|
|
69844
|
+
}
|
|
69845
|
+
} else {
|
|
69846
|
+
const reason = ctx.options.yes ? "(--yes flag)" : "(non-interactive mode)";
|
|
69847
|
+
logger.info(`Adding ${kit.name} alongside ${existingKitsDisplay} ${reason}`);
|
|
69848
|
+
}
|
|
69849
|
+
}
|
|
69850
|
+
}
|
|
69851
|
+
} catch (error) {
|
|
69852
|
+
logger.debug(`Metadata read skipped: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
69853
|
+
}
|
|
69854
|
+
}
|
|
69855
|
+
if (ctx.options.fresh) {
|
|
69856
|
+
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
69857
|
+
const claudeDir = prefix ? join95(resolvedDir, prefix) : resolvedDir;
|
|
69858
|
+
const canProceed = await handleFreshInstallation(claudeDir, ctx.prompts);
|
|
69859
|
+
if (!canProceed) {
|
|
69860
|
+
return { ...ctx, cancelled: true };
|
|
69861
|
+
}
|
|
69862
|
+
}
|
|
69863
|
+
const isOfflineMode = !!(ctx.options.kitPath || ctx.options.archive);
|
|
69864
|
+
const github = !isOfflineMode && useLegacyGh ? new GitHubClient : null;
|
|
69865
|
+
let selectedVersion = ctx.options.release;
|
|
69866
|
+
if (!selectedVersion && ctx.isNonInteractive && !ctx.options.yes && !isOfflineMode) {
|
|
69867
|
+
throw new Error("Non-interactive mode requires either: --release <tag> OR --yes (uses latest)");
|
|
69868
|
+
}
|
|
69869
|
+
if (!selectedVersion && ctx.options.yes && !isOfflineMode) {
|
|
69870
|
+
logger.info("Using latest stable version (--yes flag)");
|
|
69871
|
+
}
|
|
69872
|
+
if (!selectedVersion && !ctx.isNonInteractive && !isOfflineMode) {
|
|
69873
|
+
logger.info("Fetching available versions...");
|
|
69874
|
+
let currentVersion = null;
|
|
69875
|
+
try {
|
|
69876
|
+
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
69877
|
+
const claudeDir = prefix ? join95(resolvedDir, prefix) : resolvedDir;
|
|
69878
|
+
const existingMetadata = await readManifest(claudeDir);
|
|
69879
|
+
currentVersion = existingMetadata?.kits?.[kitType]?.version || null;
|
|
69880
|
+
if (currentVersion) {
|
|
69881
|
+
logger.debug(`Current installed version: ${currentVersion}`);
|
|
69882
|
+
}
|
|
69883
|
+
} catch {}
|
|
69884
|
+
try {
|
|
69885
|
+
const versionResult = await ctx.prompts.selectVersionEnhanced({
|
|
69886
|
+
kit,
|
|
69887
|
+
includePrereleases: ctx.options.beta,
|
|
69888
|
+
limit: 10,
|
|
69889
|
+
allowManualEntry: true,
|
|
69890
|
+
forceRefresh: ctx.options.refresh,
|
|
69891
|
+
currentVersion,
|
|
69892
|
+
useGh: useLegacyGh
|
|
69893
|
+
});
|
|
69894
|
+
if (!versionResult) {
|
|
69895
|
+
logger.warning("Version selection cancelled by user");
|
|
69896
|
+
return { ...ctx, cancelled: true };
|
|
69897
|
+
}
|
|
69898
|
+
selectedVersion = versionResult;
|
|
69899
|
+
logger.success(`Selected version: ${selectedVersion}`);
|
|
69900
|
+
} catch (error) {
|
|
69901
|
+
logger.error("Failed to fetch versions, using latest release");
|
|
69902
|
+
logger.debug(`Version selection error: ${error.message}`);
|
|
69903
|
+
selectedVersion = undefined;
|
|
69904
|
+
}
|
|
69905
|
+
}
|
|
69906
|
+
let release;
|
|
69907
|
+
if (isOfflineMode) {
|
|
69908
|
+
release = undefined;
|
|
69909
|
+
logger.verbose("Offline mode - skipping release fetch", {
|
|
69910
|
+
kitPath: ctx.options.kitPath,
|
|
69911
|
+
archive: ctx.options.archive
|
|
69912
|
+
});
|
|
69913
|
+
} else if (ctx.options.useGit && selectedVersion) {
|
|
69914
|
+
release = {
|
|
69915
|
+
id: 0,
|
|
69916
|
+
tag_name: selectedVersion,
|
|
69917
|
+
name: selectedVersion,
|
|
69918
|
+
draft: false,
|
|
69919
|
+
prerelease: selectedVersion.includes("-"),
|
|
69920
|
+
tarball_url: `https://github.com/${kit.owner}/${kit.repo}/archive/refs/tags/${selectedVersion}.tar.gz`,
|
|
69921
|
+
zipball_url: `https://github.com/${kit.owner}/${kit.repo}/archive/refs/tags/${selectedVersion}.zip`,
|
|
69922
|
+
assets: []
|
|
69923
|
+
};
|
|
69924
|
+
logger.verbose("Using git clone mode with tag", { tag: selectedVersion });
|
|
69925
|
+
} else if (selectedVersion && github) {
|
|
69926
|
+
release = await github.getReleaseByTag(kit, selectedVersion);
|
|
69927
|
+
} else if (github) {
|
|
69928
|
+
if (ctx.options.beta) {
|
|
69929
|
+
logger.info("Fetching latest beta release...");
|
|
69930
|
+
} else {
|
|
69931
|
+
logger.info("Fetching latest release...");
|
|
69932
|
+
}
|
|
69933
|
+
release = await github.getLatestRelease(kit, ctx.options.beta);
|
|
69934
|
+
if (release.prerelease) {
|
|
69935
|
+
logger.success(`Found beta: ${release.tag_name}`);
|
|
69936
|
+
} else {
|
|
69937
|
+
logger.success(`Found: ${release.tag_name}`);
|
|
69938
|
+
}
|
|
69939
|
+
} else if (selectedVersion) {
|
|
69940
|
+
const worker = new WorkerSource(getServerUrl(), kitType);
|
|
69941
|
+
const entry = await worker.fetchByTag(selectedVersion);
|
|
69942
|
+
release = releaseEntryToGitHubRelease(entry, kit);
|
|
69943
|
+
} else {
|
|
69944
|
+
if (ctx.options.beta) {
|
|
69945
|
+
logger.info("Fetching latest beta release...");
|
|
69946
|
+
} else {
|
|
69947
|
+
logger.info("Fetching latest release...");
|
|
69948
|
+
}
|
|
69949
|
+
const worker = new WorkerSource(getServerUrl(), kitType);
|
|
69950
|
+
const entry = await worker.fetchLatest(ctx.options.beta);
|
|
69951
|
+
release = releaseEntryToGitHubRelease(entry, kit);
|
|
69952
|
+
if (release.prerelease) {
|
|
69953
|
+
logger.success(`Found beta: ${release.tag_name}`);
|
|
69954
|
+
} else {
|
|
69955
|
+
logger.success(`Found: ${release.tag_name}`);
|
|
69956
|
+
}
|
|
69957
|
+
}
|
|
69958
|
+
if (ctx.options.force && !ctx.options.yes) {
|
|
69959
|
+
logger.info("--force has no effect without --yes (the version-match skip only applies in non-interactive mode)");
|
|
69960
|
+
}
|
|
69961
|
+
const releaseTag = release?.tag_name;
|
|
69962
|
+
if (ctx.options.yes && !ctx.options.fresh && !ctx.options.force && releaseTag && !isOfflineMode) {
|
|
69963
|
+
try {
|
|
69964
|
+
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
69965
|
+
const claudeDir = prefix ? join95(resolvedDir, prefix) : resolvedDir;
|
|
69966
|
+
const existingMetadata = await readManifest(claudeDir);
|
|
69967
|
+
const installedKitVersion = existingMetadata?.kits?.[kitType]?.version;
|
|
69968
|
+
if (installedKitVersion && versionsMatch(installedKitVersion, releaseTag)) {
|
|
69969
|
+
logger.success(`Already at latest version (${kitType}@${installedKitVersion}), skipping reinstall`);
|
|
70063
69970
|
return { ...ctx, cancelled: true };
|
|
70064
69971
|
}
|
|
70065
|
-
|
|
70066
|
-
logger.
|
|
70067
|
-
}
|
|
70068
|
-
|
|
70069
|
-
|
|
70070
|
-
|
|
70071
|
-
|
|
69972
|
+
} catch (error) {
|
|
69973
|
+
logger.verbose(`Metadata read failed, proceeding with installation: ${error instanceof Error ? error.message : "unknown"}`);
|
|
69974
|
+
}
|
|
69975
|
+
}
|
|
69976
|
+
const releasesByKit = {};
|
|
69977
|
+
if (release)
|
|
69978
|
+
releasesByKit[kitType] = release;
|
|
69979
|
+
const shouldPromptSecondaryVersions = selectedKits.length > 1 && !ctx.isNonInteractive && !ctx.options.yes && !isOfflineMode;
|
|
69980
|
+
if (shouldPromptSecondaryVersions) {
|
|
69981
|
+
for (const secondaryKit of selectedKits) {
|
|
69982
|
+
if (secondaryKit === kitType)
|
|
69983
|
+
continue;
|
|
69984
|
+
const kitConfig = AVAILABLE_KITS[secondaryKit];
|
|
69985
|
+
let currentSecondaryVersion = null;
|
|
69986
|
+
try {
|
|
69987
|
+
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
69988
|
+
const claudeDir = prefix ? join95(resolvedDir, prefix) : resolvedDir;
|
|
69989
|
+
const existingMetadata = await readManifest(claudeDir);
|
|
69990
|
+
currentSecondaryVersion = existingMetadata?.kits?.[secondaryKit]?.version || null;
|
|
69991
|
+
} catch {}
|
|
69992
|
+
let secondaryVersion;
|
|
69993
|
+
try {
|
|
69994
|
+
const versionResult = await ctx.prompts.selectVersionEnhanced({
|
|
69995
|
+
kit: kitConfig,
|
|
69996
|
+
includePrereleases: ctx.options.beta,
|
|
69997
|
+
limit: 10,
|
|
69998
|
+
allowManualEntry: true,
|
|
69999
|
+
forceRefresh: ctx.options.refresh,
|
|
70000
|
+
currentVersion: currentSecondaryVersion,
|
|
70001
|
+
useGh: useLegacyGh
|
|
70002
|
+
});
|
|
70003
|
+
if (!versionResult) {
|
|
70004
|
+
logger.warning(`Version selection cancelled for ${kitConfig.name}`);
|
|
70005
|
+
return { ...ctx, cancelled: true };
|
|
70006
|
+
}
|
|
70007
|
+
secondaryVersion = versionResult;
|
|
70008
|
+
logger.success(`Selected ${kitConfig.name}: ${secondaryVersion}`);
|
|
70009
|
+
} catch (error) {
|
|
70010
|
+
logger.error(`Failed to fetch versions for ${kitConfig.name}, using latest`);
|
|
70011
|
+
logger.debug(`Version selection error: ${error.message}`);
|
|
70012
|
+
secondaryVersion = undefined;
|
|
70013
|
+
}
|
|
70014
|
+
try {
|
|
70015
|
+
let secondaryRelease;
|
|
70016
|
+
if (useLegacyGh) {
|
|
70017
|
+
const ghClient = github ?? new GitHubClient;
|
|
70018
|
+
secondaryRelease = secondaryVersion ? await ghClient.getReleaseByTag(kitConfig, secondaryVersion) : await ghClient.getLatestRelease(kitConfig, ctx.options.beta);
|
|
70019
|
+
} else {
|
|
70020
|
+
const worker = new WorkerSource(getServerUrl(), secondaryKit);
|
|
70021
|
+
const entry = secondaryVersion ? await worker.fetchByTag(secondaryVersion) : await worker.fetchLatest(ctx.options.beta);
|
|
70022
|
+
secondaryRelease = releaseEntryToGitHubRelease(entry, kitConfig);
|
|
70023
|
+
}
|
|
70024
|
+
releasesByKit[secondaryKit] = secondaryRelease;
|
|
70025
|
+
} catch (error) {
|
|
70026
|
+
logger.warning(`Could not pre-fetch release for ${kitConfig.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
70027
|
+
}
|
|
70028
|
+
}
|
|
70029
|
+
}
|
|
70030
|
+
return {
|
|
70031
|
+
...ctx,
|
|
70032
|
+
kit,
|
|
70033
|
+
kitType,
|
|
70034
|
+
selectedKits,
|
|
70035
|
+
resolvedDir,
|
|
70036
|
+
release,
|
|
70037
|
+
selectedVersion,
|
|
70038
|
+
releasesByKit,
|
|
70039
|
+
accessibleKits
|
|
70040
|
+
};
|
|
70041
|
+
}
|
|
70042
|
+
async function resolveReleaseForKit(ctx, kitType) {
|
|
70043
|
+
const isOfflineMode = !!(ctx.options.kitPath || ctx.options.archive);
|
|
70044
|
+
if (isOfflineMode)
|
|
70045
|
+
return;
|
|
70046
|
+
const kit = AVAILABLE_KITS[kitType];
|
|
70047
|
+
if (ctx.options.useGit) {
|
|
70048
|
+
throw new Error(`--use-git is not supported for multi-kit installs (secondary kit: ${kitType}). Run kits individually.`);
|
|
70049
|
+
}
|
|
70050
|
+
if (ctx.options.useGh) {
|
|
70051
|
+
const github = new GitHubClient;
|
|
70052
|
+
const release = await github.getLatestRelease(kit, ctx.options.beta);
|
|
70053
|
+
return release;
|
|
70054
|
+
}
|
|
70055
|
+
const worker = new WorkerSource(getServerUrl(), kitType);
|
|
70056
|
+
const entry = await worker.fetchLatest(ctx.options.beta);
|
|
70057
|
+
return releaseEntryToGitHubRelease(entry, kit);
|
|
70058
|
+
}
|
|
70059
|
+
function resolveGlobalTargetDir(targetAgents) {
|
|
70060
|
+
const primary = targetAgents[0];
|
|
70061
|
+
if (primary) {
|
|
70062
|
+
const installer = getInstaller(primary);
|
|
70063
|
+
if (installer) {
|
|
70064
|
+
return installer.globalRoot();
|
|
70065
|
+
}
|
|
70066
|
+
}
|
|
70067
|
+
return getClaudeDir();
|
|
70068
|
+
}
|
|
70069
|
+
// src/commands/init/phases/sync-handler.ts
|
|
70070
|
+
import { copyFile as copyFile7, mkdir as mkdir29, open as open3, readFile as readFile41, rename as rename6, stat as stat12, unlink as unlink13, writeFile as writeFile31 } from "node:fs/promises";
|
|
70071
|
+
import { dirname as dirname27, join as join98, resolve as resolve23 } from "node:path";
|
|
70072
|
+
|
|
70073
|
+
// src/domains/sync/config-version-checker.ts
|
|
70074
|
+
init_auth_client();
|
|
70075
|
+
import { mkdir as mkdir28, readFile as readFile39, unlink as unlink12, writeFile as writeFile30 } from "node:fs/promises";
|
|
70076
|
+
import { join as join96 } from "node:path";
|
|
70077
|
+
init_logger();
|
|
70078
|
+
init_path_resolver();
|
|
70079
|
+
var CACHE_TTL_HOURS = 24;
|
|
70080
|
+
var DEFAULT_CACHE_TTL_MS = CACHE_TTL_HOURS * 60 * 60 * 1000;
|
|
70081
|
+
var MIN_CACHE_TTL_MS = 60 * 1000;
|
|
70082
|
+
var MAX_CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
70083
|
+
function parseCacheTtl() {
|
|
70084
|
+
const envValue = process.env.TAKUMI_SYNC_CACHE_TTL;
|
|
70085
|
+
if (!envValue) {
|
|
70086
|
+
return DEFAULT_CACHE_TTL_MS;
|
|
70087
|
+
}
|
|
70088
|
+
const parsed = Number.parseInt(envValue, 10);
|
|
70089
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
70090
|
+
logger.warning(`Invalid TAKUMI_SYNC_CACHE_TTL value "${envValue}", using default (${CACHE_TTL_HOURS}h)`);
|
|
70091
|
+
return DEFAULT_CACHE_TTL_MS;
|
|
70092
|
+
}
|
|
70093
|
+
const ttlMs = parsed * 1000;
|
|
70094
|
+
if (ttlMs < MIN_CACHE_TTL_MS) {
|
|
70095
|
+
logger.warning(`TAKUMI_SYNC_CACHE_TTL too low (${parsed}s), using minimum (60s)`);
|
|
70096
|
+
return MIN_CACHE_TTL_MS;
|
|
70097
|
+
}
|
|
70098
|
+
if (ttlMs > MAX_CACHE_TTL_MS) {
|
|
70099
|
+
logger.warning(`TAKUMI_SYNC_CACHE_TTL too high (${parsed}s), using maximum (7 days)`);
|
|
70100
|
+
return MAX_CACHE_TTL_MS;
|
|
70101
|
+
}
|
|
70102
|
+
return ttlMs;
|
|
70103
|
+
}
|
|
70104
|
+
var CACHE_TTL_MS = parseCacheTtl();
|
|
70105
|
+
var CACHE_FILENAME = "config-update-cache.json";
|
|
70106
|
+
|
|
70107
|
+
class ConfigVersionChecker {
|
|
70108
|
+
static getCacheFilePath(kitType, global3) {
|
|
70109
|
+
const cacheDir = PathResolver.getCacheDir(global3);
|
|
70110
|
+
return join96(cacheDir, `${kitType}-${CACHE_FILENAME}`);
|
|
70111
|
+
}
|
|
70112
|
+
static async loadCache(kitType, global3) {
|
|
70113
|
+
try {
|
|
70114
|
+
const cachePath = ConfigVersionChecker.getCacheFilePath(kitType, global3);
|
|
70115
|
+
const data = await readFile39(cachePath, "utf8");
|
|
70116
|
+
const parsed = JSON.parse(data);
|
|
70117
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.lastCheck !== "number" || typeof parsed.latestVersion !== "string" || !parsed.latestVersion || parsed.lastCheck < 0 || parsed.lastCheck > Date.now() + 7 * 24 * 60 * 60 * 1000) {
|
|
70118
|
+
logger.debug("Invalid cache structure, ignoring");
|
|
70119
|
+
return null;
|
|
70120
|
+
}
|
|
70121
|
+
return parsed;
|
|
70122
|
+
} catch {
|
|
70123
|
+
return null;
|
|
70124
|
+
}
|
|
70125
|
+
}
|
|
70126
|
+
static async saveCache(kitType, global3, cache) {
|
|
70127
|
+
try {
|
|
70128
|
+
const cachePath = ConfigVersionChecker.getCacheFilePath(kitType, global3);
|
|
70129
|
+
const cacheDir = PathResolver.getCacheDir(global3);
|
|
70130
|
+
await mkdir28(cacheDir, { recursive: true });
|
|
70131
|
+
await writeFile30(cachePath, JSON.stringify(cache, null, 2));
|
|
70132
|
+
} catch (error) {
|
|
70133
|
+
logger.debug(`Cache write failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
70134
|
+
}
|
|
70135
|
+
}
|
|
70136
|
+
static async fetchLatestVersion(kitType) {
|
|
70137
|
+
const maxRetries = 3;
|
|
70138
|
+
const baseBackoff = 1000;
|
|
70139
|
+
for (let attempt = 0;attempt < maxRetries; attempt++) {
|
|
70140
|
+
try {
|
|
70141
|
+
const worker = new WorkerSource(getServerUrl(), kitType);
|
|
70142
|
+
const entry = await worker.fetchLatest();
|
|
70143
|
+
const version3 = entry.version.replace(/^v/, "");
|
|
70144
|
+
if (!version3 || version3.length > 256) {
|
|
70145
|
+
logger.debug(`Invalid version format from Worker: ${entry.version}`);
|
|
70146
|
+
return null;
|
|
70147
|
+
}
|
|
70148
|
+
const semverParts = version3.split(/[-+]/);
|
|
70149
|
+
const coreParts = semverParts[0].split(".");
|
|
70150
|
+
if (coreParts.length !== 3 || !coreParts.every((p2) => /^\d+$/.test(p2))) {
|
|
70151
|
+
logger.debug(`Invalid version format from Worker: ${entry.version}`);
|
|
70152
|
+
return null;
|
|
70153
|
+
}
|
|
70154
|
+
return { version: version3 };
|
|
70155
|
+
} catch (error) {
|
|
70156
|
+
if (error instanceof NotLoggedInError) {
|
|
70157
|
+
logger.debug("Skipping update check (not logged in)");
|
|
70158
|
+
return null;
|
|
70159
|
+
}
|
|
70160
|
+
const isLastAttempt = attempt === maxRetries - 1;
|
|
70161
|
+
if (isLastAttempt) {
|
|
70162
|
+
logger.debug(`Version check failed after ${maxRetries} attempts: ${error}`);
|
|
70163
|
+
return null;
|
|
70164
|
+
}
|
|
70165
|
+
const delay3 = baseBackoff * 2 ** attempt;
|
|
70166
|
+
await new Promise((resolve23) => setTimeout(resolve23, delay3));
|
|
70167
|
+
}
|
|
70168
|
+
}
|
|
70169
|
+
return null;
|
|
70170
|
+
}
|
|
70171
|
+
static async checkForUpdates(kitType, currentVersion, global3 = false) {
|
|
70172
|
+
const normalizedCurrent = currentVersion.replace(/^v/, "");
|
|
70173
|
+
const cache = await ConfigVersionChecker.loadCache(kitType, global3);
|
|
70174
|
+
const now = Date.now();
|
|
70175
|
+
if (cache && now - cache.lastCheck < CACHE_TTL_MS) {
|
|
70176
|
+
const hasUpdates = isNewerVersion(normalizedCurrent, cache.latestVersion);
|
|
70177
|
+
return {
|
|
70178
|
+
hasUpdates,
|
|
70179
|
+
currentVersion: normalizedCurrent,
|
|
70180
|
+
latestVersion: cache.latestVersion,
|
|
70181
|
+
fromCache: true
|
|
70182
|
+
};
|
|
70183
|
+
}
|
|
70184
|
+
const result = await ConfigVersionChecker.fetchLatestVersion(kitType);
|
|
70185
|
+
if (result) {
|
|
70186
|
+
await ConfigVersionChecker.saveCache(kitType, global3, {
|
|
70187
|
+
lastCheck: now,
|
|
70188
|
+
latestVersion: result.version
|
|
70189
|
+
});
|
|
70190
|
+
const hasUpdates = isNewerVersion(normalizedCurrent, result.version);
|
|
70191
|
+
return {
|
|
70192
|
+
hasUpdates,
|
|
70193
|
+
currentVersion: normalizedCurrent,
|
|
70194
|
+
latestVersion: result.version,
|
|
70195
|
+
fromCache: false
|
|
70196
|
+
};
|
|
70197
|
+
}
|
|
70198
|
+
if (cache) {
|
|
70199
|
+
const hasUpdates = isNewerVersion(normalizedCurrent, cache.latestVersion);
|
|
70200
|
+
return {
|
|
70201
|
+
hasUpdates,
|
|
70202
|
+
currentVersion: normalizedCurrent,
|
|
70203
|
+
latestVersion: cache.latestVersion,
|
|
70204
|
+
fromCache: true
|
|
70205
|
+
};
|
|
70206
|
+
}
|
|
70207
|
+
return {
|
|
70208
|
+
hasUpdates: false,
|
|
70209
|
+
currentVersion: normalizedCurrent,
|
|
70210
|
+
latestVersion: normalizedCurrent,
|
|
70211
|
+
fromCache: false
|
|
70212
|
+
};
|
|
70213
|
+
}
|
|
70214
|
+
static async clearCache(kitType, global3 = false) {
|
|
70215
|
+
const cachePath = ConfigVersionChecker.getCacheFilePath(kitType, global3);
|
|
70216
|
+
try {
|
|
70217
|
+
await unlink12(cachePath);
|
|
70218
|
+
logger.debug(`Cleared sync cache for ${kitType}`);
|
|
70219
|
+
} catch (error) {
|
|
70220
|
+
if (error.code !== "ENOENT") {
|
|
70221
|
+
throw error;
|
|
70222
|
+
}
|
|
70223
|
+
}
|
|
70224
|
+
}
|
|
70225
|
+
}
|
|
70226
|
+
// src/domains/sync/sync-engine.ts
|
|
70227
|
+
import { lstat as lstat7, readFile as readFile40, readlink, realpath as realpath3, stat as stat11 } from "node:fs/promises";
|
|
70228
|
+
import { isAbsolute as isAbsolute3, join as join97, normalize as normalize8, relative as relative16 } from "node:path";
|
|
70229
|
+
init_logger();
|
|
70230
|
+
var MAX_SYNC_FILE_SIZE = 10 * 1024 * 1024;
|
|
70231
|
+
var MAX_SYMLINK_DEPTH = 20;
|
|
70232
|
+
async function validateSymlinkChain(path9, basePath, maxDepth = MAX_SYMLINK_DEPTH) {
|
|
70233
|
+
let current = path9;
|
|
70234
|
+
let depth = 0;
|
|
70235
|
+
while (depth < maxDepth) {
|
|
70236
|
+
try {
|
|
70237
|
+
const stats = await lstat7(current);
|
|
70238
|
+
if (!stats.isSymbolicLink())
|
|
70239
|
+
break;
|
|
70240
|
+
const target = await readlink(current);
|
|
70241
|
+
const resolvedTarget = isAbsolute3(target) ? target : join97(current, "..", target);
|
|
70242
|
+
const normalizedTarget = normalize8(resolvedTarget);
|
|
70243
|
+
const rel = relative16(basePath, normalizedTarget);
|
|
70244
|
+
if (rel.startsWith("..") || isAbsolute3(rel)) {
|
|
70245
|
+
throw new Error(`Symlink chain escapes base directory at depth ${depth}: ${path9}`);
|
|
70246
|
+
}
|
|
70247
|
+
current = normalizedTarget;
|
|
70248
|
+
depth++;
|
|
70249
|
+
} catch (error) {
|
|
70250
|
+
if (error instanceof Error && error.message.includes("Symlink chain")) {
|
|
70251
|
+
throw error;
|
|
70252
|
+
}
|
|
70253
|
+
break;
|
|
70254
|
+
}
|
|
70255
|
+
}
|
|
70256
|
+
if (depth >= maxDepth) {
|
|
70257
|
+
throw new Error(`Symlink chain too deep (>${maxDepth}): ${path9}`);
|
|
70258
|
+
}
|
|
70259
|
+
}
|
|
70260
|
+
async function validateSyncPath(basePath, filePath) {
|
|
70261
|
+
if (!filePath || filePath.trim() === "") {
|
|
70262
|
+
throw new Error("Empty file path not allowed");
|
|
70263
|
+
}
|
|
70264
|
+
if (filePath.includes("\x00")) {
|
|
70265
|
+
throw new Error(`Invalid file path (null byte): ${filePath}`);
|
|
70266
|
+
}
|
|
70267
|
+
if (filePath.length > 1024) {
|
|
70268
|
+
throw new Error(`Path too long: ${filePath.slice(0, 50)}...`);
|
|
70269
|
+
}
|
|
70270
|
+
const normalized = normalize8(filePath);
|
|
70271
|
+
if (isAbsolute3(normalized)) {
|
|
70272
|
+
throw new Error(`Absolute paths not allowed: ${filePath}`);
|
|
70273
|
+
}
|
|
70274
|
+
if (normalized.startsWith("..") || normalized.includes("/../")) {
|
|
70275
|
+
throw new Error(`Path traversal not allowed: ${filePath}`);
|
|
70276
|
+
}
|
|
70277
|
+
const fullPath = join97(basePath, normalized);
|
|
70278
|
+
const rel = relative16(basePath, fullPath);
|
|
70279
|
+
if (rel.startsWith("..") || isAbsolute3(rel)) {
|
|
70280
|
+
throw new Error(`Path escapes base directory: ${filePath}`);
|
|
70281
|
+
}
|
|
70282
|
+
await validateSymlinkChain(fullPath, basePath);
|
|
70283
|
+
try {
|
|
70284
|
+
const resolvedBase = await realpath3(basePath);
|
|
70285
|
+
const resolvedFull = await realpath3(fullPath);
|
|
70286
|
+
const resolvedRel = relative16(resolvedBase, resolvedFull);
|
|
70287
|
+
if (resolvedRel.startsWith("..") || isAbsolute3(resolvedRel)) {
|
|
70288
|
+
throw new Error(`Symlink escapes base directory: ${filePath}`);
|
|
70289
|
+
}
|
|
70290
|
+
} catch (error) {
|
|
70291
|
+
if (error.code === "ENOENT") {
|
|
70292
|
+
const parentPath = join97(fullPath, "..");
|
|
70293
|
+
try {
|
|
70294
|
+
const resolvedBase = await realpath3(basePath);
|
|
70295
|
+
const resolvedParent = await realpath3(parentPath);
|
|
70296
|
+
const resolvedRel = relative16(resolvedBase, resolvedParent);
|
|
70297
|
+
if (resolvedRel.startsWith("..") || isAbsolute3(resolvedRel)) {
|
|
70298
|
+
throw new Error(`Parent symlink escapes base directory: ${filePath}`);
|
|
70299
|
+
}
|
|
70300
|
+
} catch (parentError) {
|
|
70301
|
+
if (parentError.code !== "ENOENT") {
|
|
70302
|
+
throw parentError;
|
|
70303
|
+
}
|
|
70304
|
+
}
|
|
70305
|
+
} else {
|
|
70306
|
+
throw error;
|
|
70307
|
+
}
|
|
70308
|
+
}
|
|
70309
|
+
return fullPath;
|
|
70310
|
+
}
|
|
70311
|
+
|
|
70312
|
+
class SyncEngine {
|
|
70313
|
+
static async createSyncPlan(trackedFiles, claudeDir, upstreamDir) {
|
|
70314
|
+
const plan = { autoUpdate: [], needsReview: [], skipped: [] };
|
|
70315
|
+
for (const file of trackedFiles) {
|
|
70316
|
+
if (file.ownership === "user") {
|
|
70317
|
+
plan.skipped.push(file);
|
|
70318
|
+
continue;
|
|
70319
|
+
}
|
|
70320
|
+
let upstreamPath;
|
|
70321
|
+
try {
|
|
70322
|
+
upstreamPath = await validateSyncPath(upstreamDir, file.path);
|
|
70323
|
+
} catch (error) {
|
|
70324
|
+
logger.warning(`Skipping invalid path: ${file.path}`);
|
|
70325
|
+
plan.skipped.push(file);
|
|
70326
|
+
continue;
|
|
70327
|
+
}
|
|
70328
|
+
try {
|
|
70329
|
+
await stat11(upstreamPath);
|
|
70330
|
+
} catch {
|
|
70331
|
+
plan.skipped.push(file);
|
|
70332
|
+
continue;
|
|
70333
|
+
}
|
|
70334
|
+
let localPath;
|
|
70335
|
+
try {
|
|
70336
|
+
localPath = await validateSyncPath(claudeDir, file.path);
|
|
70337
|
+
} catch (error) {
|
|
70338
|
+
logger.warning(`Skipping invalid local path: ${file.path}`);
|
|
70339
|
+
plan.skipped.push(file);
|
|
70340
|
+
continue;
|
|
70341
|
+
}
|
|
70342
|
+
try {
|
|
70343
|
+
await stat11(localPath);
|
|
70344
|
+
} catch {
|
|
70345
|
+
plan.autoUpdate.push(file);
|
|
70346
|
+
continue;
|
|
70072
70347
|
}
|
|
70073
|
-
|
|
70074
|
-
|
|
70075
|
-
|
|
70076
|
-
|
|
70077
|
-
|
|
70348
|
+
if (file.ownership === "takumi") {
|
|
70349
|
+
plan.autoUpdate.push(file);
|
|
70350
|
+
continue;
|
|
70351
|
+
}
|
|
70352
|
+
const currentChecksum = await OwnershipChecker.calculateChecksum(localPath);
|
|
70353
|
+
const baseChecksum = file.baseChecksum || file.checksum;
|
|
70354
|
+
if (currentChecksum === baseChecksum) {
|
|
70355
|
+
plan.autoUpdate.push(file);
|
|
70356
|
+
} else {
|
|
70357
|
+
plan.needsReview.push(file);
|
|
70078
70358
|
}
|
|
70079
70359
|
}
|
|
70360
|
+
return plan;
|
|
70080
70361
|
}
|
|
70081
|
-
|
|
70082
|
-
|
|
70083
|
-
|
|
70084
|
-
|
|
70362
|
+
static generateHunks(currentContent, newContent, filename, contextLines = 3) {
|
|
70363
|
+
const patch = structuredPatch(filename, filename, currentContent, newContent, "", "", {
|
|
70364
|
+
context: contextLines
|
|
70365
|
+
});
|
|
70366
|
+
return patch.hunks.map((hunk) => ({
|
|
70367
|
+
oldStart: hunk.oldStart,
|
|
70368
|
+
oldLines: hunk.oldLines,
|
|
70369
|
+
newStart: hunk.newStart,
|
|
70370
|
+
newLines: hunk.newLines,
|
|
70371
|
+
lines: hunk.lines
|
|
70372
|
+
}));
|
|
70373
|
+
}
|
|
70374
|
+
static applyHunks(content, hunks, accepted) {
|
|
70375
|
+
const acceptedHunks = hunks.filter((_4, i) => accepted[i]);
|
|
70376
|
+
if (acceptedHunks.length === 0) {
|
|
70377
|
+
return content;
|
|
70378
|
+
}
|
|
70379
|
+
const patchStr = SyncEngine.buildUnifiedDiff(content, acceptedHunks);
|
|
70380
|
+
const result = applyPatch(content, patchStr);
|
|
70381
|
+
if (result === false) {
|
|
70382
|
+
try {
|
|
70383
|
+
return SyncEngine.applyHunksManually(content, acceptedHunks);
|
|
70384
|
+
} catch (fallbackError) {
|
|
70385
|
+
throw new Error(`Failed to apply ${acceptedHunks.length} hunk(s): patch and manual methods both failed. ` + `Manual error: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`);
|
|
70085
70386
|
}
|
|
70086
|
-
kitType = accessibleKits[0];
|
|
70087
|
-
logger.info(`Auto-selected: ${AVAILABLE_KITS[kitType].name}`);
|
|
70088
|
-
} else if (accessibleKits?.length === 1) {
|
|
70089
|
-
kitType = accessibleKits[0];
|
|
70090
|
-
logger.info(`Using ${AVAILABLE_KITS[kitType].name} (only accessible kit)`);
|
|
70091
|
-
} else {
|
|
70092
|
-
kitType = await ctx.prompts.selectKit(undefined, accessibleKits);
|
|
70093
70387
|
}
|
|
70388
|
+
return result;
|
|
70094
70389
|
}
|
|
70095
|
-
|
|
70096
|
-
|
|
70097
|
-
|
|
70098
|
-
|
|
70099
|
-
|
|
70100
|
-
|
|
70101
|
-
|
|
70102
|
-
|
|
70103
|
-
|
|
70104
|
-
|
|
70105
|
-
} else {
|
|
70106
|
-
targetDir = config.defaults?.dir || ".";
|
|
70107
|
-
if (!config.defaults?.dir) {
|
|
70108
|
-
if (ctx.isNonInteractive) {
|
|
70109
|
-
logger.info("Using current directory as target");
|
|
70110
|
-
} else {
|
|
70111
|
-
targetDir = await ctx.prompts.getDirectory(targetDir);
|
|
70390
|
+
static buildUnifiedDiff(_content, hunks) {
|
|
70391
|
+
let diff = `--- a
|
|
70392
|
+
+++ b
|
|
70393
|
+
`;
|
|
70394
|
+
for (const hunk of hunks) {
|
|
70395
|
+
diff += `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@
|
|
70396
|
+
`;
|
|
70397
|
+
for (const line of hunk.lines) {
|
|
70398
|
+
diff += `${line}
|
|
70399
|
+
`;
|
|
70112
70400
|
}
|
|
70113
70401
|
}
|
|
70402
|
+
return diff;
|
|
70114
70403
|
}
|
|
70115
|
-
|
|
70116
|
-
|
|
70117
|
-
|
|
70118
|
-
|
|
70119
|
-
|
|
70120
|
-
const
|
|
70121
|
-
if (
|
|
70122
|
-
|
|
70404
|
+
static applyHunksManually(content, hunks) {
|
|
70405
|
+
const lines = content.split(`
|
|
70406
|
+
`);
|
|
70407
|
+
const sortedHunks = [...hunks].sort((a3, b4) => b4.oldStart - a3.oldStart);
|
|
70408
|
+
for (const hunk of sortedHunks) {
|
|
70409
|
+
const startIndex = hunk.oldStart - 1;
|
|
70410
|
+
if (startIndex < 0 || startIndex > lines.length) {
|
|
70411
|
+
logger.warning(`Hunk start ${hunk.oldStart} out of bounds (file has ${lines.length} lines)`);
|
|
70412
|
+
continue;
|
|
70123
70413
|
}
|
|
70124
|
-
|
|
70125
|
-
|
|
70414
|
+
const newLines = [];
|
|
70415
|
+
let deleteCount = 0;
|
|
70416
|
+
for (const line of hunk.lines) {
|
|
70417
|
+
if (!line || line.length === 0) {
|
|
70418
|
+
continue;
|
|
70419
|
+
}
|
|
70420
|
+
const prefix = line[0];
|
|
70421
|
+
const lineContent = line.slice(1);
|
|
70422
|
+
if (prefix === "-") {
|
|
70423
|
+
deleteCount++;
|
|
70424
|
+
} else if (prefix === "+" || prefix === " ") {
|
|
70425
|
+
newLines.push(lineContent);
|
|
70426
|
+
}
|
|
70126
70427
|
}
|
|
70127
|
-
if (
|
|
70128
|
-
logger.
|
|
70129
|
-
|
|
70428
|
+
if (startIndex + deleteCount > lines.length) {
|
|
70429
|
+
logger.warning(`Hunk would delete past EOF (start: ${startIndex}, delete: ${deleteCount}, lines: ${lines.length})`);
|
|
70430
|
+
continue;
|
|
70130
70431
|
}
|
|
70131
|
-
|
|
70132
|
-
logger.error("Cannot use local installation at HOME directory.");
|
|
70133
|
-
logger.info("Use -g/--global flag or run from a project directory.");
|
|
70134
|
-
return { ...ctx, cancelled: true };
|
|
70432
|
+
lines.splice(startIndex, deleteCount, ...newLines);
|
|
70135
70433
|
}
|
|
70434
|
+
return lines.join(`
|
|
70435
|
+
`);
|
|
70136
70436
|
}
|
|
70137
|
-
|
|
70138
|
-
if (
|
|
70139
|
-
|
|
70140
|
-
logger.info(`Created global directory: ${resolvedDir}`);
|
|
70141
|
-
} else {
|
|
70142
|
-
logger.error(`Directory does not exist: ${resolvedDir}`);
|
|
70143
|
-
logger.info('Use "tkm new" to create a new project');
|
|
70144
|
-
return { ...ctx, cancelled: true };
|
|
70437
|
+
static isBinaryFile(content) {
|
|
70438
|
+
if (content.length === 0) {
|
|
70439
|
+
return false;
|
|
70145
70440
|
}
|
|
70146
|
-
|
|
70147
|
-
|
|
70148
|
-
|
|
70149
|
-
|
|
70150
|
-
|
|
70151
|
-
|
|
70152
|
-
|
|
70153
|
-
|
|
70154
|
-
|
|
70155
|
-
if (otherKits.length > 0) {
|
|
70156
|
-
const kitsRecord = existingMetadata.kits;
|
|
70157
|
-
const existingKitsDisplay = otherKits.map((k3) => `${k3}@${kitsRecord[k3]?.version || "unknown"}`).join(", ");
|
|
70158
|
-
if (!ctx.options.yes && !ctx.isNonInteractive) {
|
|
70159
|
-
try {
|
|
70160
|
-
const confirmAdd = await ctx.prompts.confirm(`${existingKitsDisplay} already installed. Add ${kit.name} alongside?`);
|
|
70161
|
-
if (!confirmAdd) {
|
|
70162
|
-
logger.warning("Multi-kit installation cancelled by user");
|
|
70163
|
-
return { ...ctx, cancelled: true };
|
|
70164
|
-
}
|
|
70165
|
-
logger.info(`Adding ${kit.name} alongside existing kit(s)`);
|
|
70166
|
-
} catch {
|
|
70167
|
-
logger.warning("Prompt cancelled or interrupted");
|
|
70168
|
-
return { ...ctx, cancelled: true };
|
|
70169
|
-
}
|
|
70170
|
-
} else {
|
|
70171
|
-
const reason = ctx.options.yes ? "(--yes flag)" : "(non-interactive mode)";
|
|
70172
|
-
logger.info(`Adding ${kit.name} alongside ${existingKitsDisplay} ${reason}`);
|
|
70173
|
-
}
|
|
70174
|
-
}
|
|
70441
|
+
const sampleSize = Math.min(content.length, 8192);
|
|
70442
|
+
let nonPrintableCount = 0;
|
|
70443
|
+
for (let i = 0;i < sampleSize; i++) {
|
|
70444
|
+
const code = content.charCodeAt(i);
|
|
70445
|
+
if (code === 0) {
|
|
70446
|
+
return true;
|
|
70447
|
+
}
|
|
70448
|
+
if (code < 32 && code !== 9 && code !== 10 && code !== 13) {
|
|
70449
|
+
nonPrintableCount++;
|
|
70175
70450
|
}
|
|
70176
|
-
} catch (error) {
|
|
70177
|
-
logger.debug(`Metadata read skipped: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
70178
|
-
}
|
|
70179
|
-
}
|
|
70180
|
-
if (ctx.options.fresh) {
|
|
70181
|
-
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
70182
|
-
const claudeDir = prefix ? join97(resolvedDir, prefix) : resolvedDir;
|
|
70183
|
-
const canProceed = await handleFreshInstallation(claudeDir, ctx.prompts);
|
|
70184
|
-
if (!canProceed) {
|
|
70185
|
-
return { ...ctx, cancelled: true };
|
|
70186
70451
|
}
|
|
70452
|
+
return nonPrintableCount / sampleSize > 0.1;
|
|
70187
70453
|
}
|
|
70188
|
-
|
|
70189
|
-
const github = !isOfflineMode && useLegacyGh ? new GitHubClient : null;
|
|
70190
|
-
let selectedVersion = ctx.options.release;
|
|
70191
|
-
if (!selectedVersion && ctx.isNonInteractive && !ctx.options.yes && !isOfflineMode) {
|
|
70192
|
-
throw new Error("Non-interactive mode requires either: --release <tag> OR --yes (uses latest)");
|
|
70193
|
-
}
|
|
70194
|
-
if (!selectedVersion && ctx.options.yes && !isOfflineMode) {
|
|
70195
|
-
logger.info("Using latest stable version (--yes flag)");
|
|
70196
|
-
}
|
|
70197
|
-
if (!selectedVersion && !ctx.isNonInteractive && !isOfflineMode) {
|
|
70198
|
-
logger.info("Fetching available versions...");
|
|
70199
|
-
let currentVersion = null;
|
|
70454
|
+
static async loadFileContent(filePath) {
|
|
70200
70455
|
try {
|
|
70201
|
-
const
|
|
70202
|
-
|
|
70203
|
-
|
|
70204
|
-
currentVersion = existingMetadata?.kits?.[kitType]?.version || null;
|
|
70205
|
-
if (currentVersion) {
|
|
70206
|
-
logger.debug(`Current installed version: ${currentVersion}`);
|
|
70456
|
+
const lstats = await lstat7(filePath);
|
|
70457
|
+
if (lstats.isSymbolicLink()) {
|
|
70458
|
+
throw new Error(`Symlink not allowed for sync: ${filePath}`);
|
|
70207
70459
|
}
|
|
70208
|
-
|
|
70209
|
-
|
|
70210
|
-
const versionResult = await ctx.prompts.selectVersionEnhanced({
|
|
70211
|
-
kit,
|
|
70212
|
-
includePrereleases: ctx.options.beta,
|
|
70213
|
-
limit: 10,
|
|
70214
|
-
allowManualEntry: true,
|
|
70215
|
-
forceRefresh: ctx.options.refresh,
|
|
70216
|
-
currentVersion,
|
|
70217
|
-
useGh: useLegacyGh
|
|
70218
|
-
});
|
|
70219
|
-
if (!versionResult) {
|
|
70220
|
-
logger.warning("Version selection cancelled by user");
|
|
70221
|
-
return { ...ctx, cancelled: true };
|
|
70460
|
+
if (lstats.size > MAX_SYNC_FILE_SIZE) {
|
|
70461
|
+
throw new Error(`File too large for sync (${Math.round(lstats.size / 1024 / 1024)}MB > ${MAX_SYNC_FILE_SIZE / 1024 / 1024}MB limit)`);
|
|
70222
70462
|
}
|
|
70223
|
-
|
|
70224
|
-
|
|
70463
|
+
const buffer = await readFile40(filePath);
|
|
70464
|
+
if (buffer.includes(0)) {
|
|
70465
|
+
return { content: "", isBinary: true };
|
|
70466
|
+
}
|
|
70467
|
+
const content = buffer.toString("utf8");
|
|
70468
|
+
if (content.includes("�")) {
|
|
70469
|
+
return { content: "", isBinary: true };
|
|
70470
|
+
}
|
|
70471
|
+
if (SyncEngine.isBinaryFile(content)) {
|
|
70472
|
+
return { content: "", isBinary: true };
|
|
70473
|
+
}
|
|
70474
|
+
return { content, isBinary: false };
|
|
70225
70475
|
} catch (error) {
|
|
70226
|
-
|
|
70227
|
-
|
|
70228
|
-
selectedVersion = undefined;
|
|
70476
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error";
|
|
70477
|
+
throw new Error(`Cannot read file for sync: ${filePath} - ${errMsg}`);
|
|
70229
70478
|
}
|
|
70230
70479
|
}
|
|
70231
|
-
|
|
70232
|
-
|
|
70233
|
-
|
|
70234
|
-
|
|
70235
|
-
|
|
70236
|
-
|
|
70237
|
-
|
|
70238
|
-
}
|
|
70239
|
-
|
|
70240
|
-
|
|
70241
|
-
|
|
70242
|
-
|
|
70243
|
-
|
|
70244
|
-
prerelease: selectedVersion.includes("-"),
|
|
70245
|
-
tarball_url: `https://github.com/${kit.owner}/${kit.repo}/archive/refs/tags/${selectedVersion}.tar.gz`,
|
|
70246
|
-
zipball_url: `https://github.com/${kit.owner}/${kit.repo}/archive/refs/tags/${selectedVersion}.zip`,
|
|
70247
|
-
assets: []
|
|
70248
|
-
};
|
|
70249
|
-
logger.verbose("Using git clone mode with tag", { tag: selectedVersion });
|
|
70250
|
-
} else if (selectedVersion && github) {
|
|
70251
|
-
release = await github.getReleaseByTag(kit, selectedVersion);
|
|
70252
|
-
} else if (github) {
|
|
70253
|
-
if (ctx.options.beta) {
|
|
70254
|
-
logger.info("Fetching latest beta release...");
|
|
70480
|
+
}
|
|
70481
|
+
// src/domains/sync/deletion-path-filter.ts
|
|
70482
|
+
init_path_resolver();
|
|
70483
|
+
var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
70484
|
+
function filterDeletionPaths(trackedFiles, deletions) {
|
|
70485
|
+
if (!deletions || deletions.length === 0) {
|
|
70486
|
+
return trackedFiles;
|
|
70487
|
+
}
|
|
70488
|
+
const exactPaths = new Set;
|
|
70489
|
+
const globMatchers = [];
|
|
70490
|
+
for (const pattern of deletions) {
|
|
70491
|
+
if (PathResolver.isGlobPattern(pattern)) {
|
|
70492
|
+
globMatchers.push(import_picomatch2.default(pattern));
|
|
70255
70493
|
} else {
|
|
70256
|
-
|
|
70494
|
+
exactPaths.add(pattern);
|
|
70257
70495
|
}
|
|
70258
|
-
|
|
70259
|
-
|
|
70260
|
-
|
|
70261
|
-
|
|
70262
|
-
|
|
70496
|
+
}
|
|
70497
|
+
return trackedFiles.filter((file) => {
|
|
70498
|
+
if (exactPaths.has(file.path))
|
|
70499
|
+
return false;
|
|
70500
|
+
for (const matcher of globMatchers) {
|
|
70501
|
+
if (matcher(file.path))
|
|
70502
|
+
return false;
|
|
70263
70503
|
}
|
|
70264
|
-
|
|
70265
|
-
|
|
70266
|
-
|
|
70267
|
-
|
|
70268
|
-
|
|
70269
|
-
|
|
70270
|
-
|
|
70271
|
-
|
|
70272
|
-
|
|
70504
|
+
return true;
|
|
70505
|
+
});
|
|
70506
|
+
}
|
|
70507
|
+
// src/domains/sync/merge-ui.ts
|
|
70508
|
+
init_dist2();
|
|
70509
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
70510
|
+
var HUNK_SEPARATOR_WIDTH = 50;
|
|
70511
|
+
var EXTENDED_CONTEXT_LINES = 10;
|
|
70512
|
+
var MAX_LINE_DISPLAY_LENGTH = 120;
|
|
70513
|
+
function requireTTY() {
|
|
70514
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
70515
|
+
throw new Error("Interactive merge requires a TTY terminal. " + "Use --yes flag for non-interactive mode, or run in a terminal.");
|
|
70516
|
+
}
|
|
70517
|
+
}
|
|
70518
|
+
function truncateLine(line) {
|
|
70519
|
+
if (line.length <= MAX_LINE_DISPLAY_LENGTH)
|
|
70520
|
+
return line;
|
|
70521
|
+
return `${line.slice(0, MAX_LINE_DISPLAY_LENGTH - 3)}...`;
|
|
70522
|
+
}
|
|
70523
|
+
|
|
70524
|
+
class MergeUI {
|
|
70525
|
+
static async promptHunk(hunk, hunkIndex, totalHunks, _filename) {
|
|
70526
|
+
requireTTY();
|
|
70527
|
+
const lineRange = `${hunk.oldStart}-${hunk.oldStart + hunk.oldLines - 1}`;
|
|
70528
|
+
console.log(import_picocolors23.default.cyan(`
|
|
70529
|
+
Hunk ${hunkIndex + 1}/${totalHunks}: Lines ${lineRange}`));
|
|
70530
|
+
console.log(import_picocolors23.default.dim("─".repeat(HUNK_SEPARATOR_WIDTH)));
|
|
70531
|
+
for (const line of hunk.lines) {
|
|
70532
|
+
const displayLine = truncateLine(line);
|
|
70533
|
+
const prefix = line[0];
|
|
70534
|
+
if (prefix === "+") {
|
|
70535
|
+
console.log(import_picocolors23.default.green(displayLine));
|
|
70536
|
+
} else if (prefix === "-") {
|
|
70537
|
+
console.log(import_picocolors23.default.red(displayLine));
|
|
70538
|
+
} else {
|
|
70539
|
+
console.log(import_picocolors23.default.dim(displayLine));
|
|
70540
|
+
}
|
|
70273
70541
|
}
|
|
70274
|
-
|
|
70275
|
-
const
|
|
70276
|
-
|
|
70277
|
-
|
|
70278
|
-
|
|
70279
|
-
|
|
70280
|
-
|
|
70542
|
+
console.log(import_picocolors23.default.dim("─".repeat(HUNK_SEPARATOR_WIDTH)));
|
|
70543
|
+
const action = await ie({
|
|
70544
|
+
message: "Action?",
|
|
70545
|
+
options: [
|
|
70546
|
+
{ value: "accept", label: "[a]ccept - Apply this change" },
|
|
70547
|
+
{ value: "reject", label: "[r]eject - Keep current" },
|
|
70548
|
+
{ value: "view", label: "[v]iew - More context" },
|
|
70549
|
+
{ value: "skip", label: "[s]kip - Skip entire file" }
|
|
70550
|
+
]
|
|
70551
|
+
});
|
|
70552
|
+
if (lD(action)) {
|
|
70553
|
+
return "skip";
|
|
70281
70554
|
}
|
|
70555
|
+
return action;
|
|
70282
70556
|
}
|
|
70283
|
-
|
|
70284
|
-
|
|
70557
|
+
static showExtendedContext(currentContent, hunk, contextLines = EXTENDED_CONTEXT_LINES) {
|
|
70558
|
+
const lines = currentContent.split(`
|
|
70559
|
+
`);
|
|
70560
|
+
const startLine = Math.max(0, hunk.oldStart - 1 - contextLines);
|
|
70561
|
+
const endLine = Math.min(lines.length, hunk.oldStart + hunk.oldLines - 1 + contextLines);
|
|
70562
|
+
console.log(import_picocolors23.default.cyan(`
|
|
70563
|
+
Extended context (lines ${startLine + 1}-${endLine}):`));
|
|
70564
|
+
console.log(import_picocolors23.default.dim("─".repeat(HUNK_SEPARATOR_WIDTH)));
|
|
70565
|
+
for (let i = startLine;i < endLine; i++) {
|
|
70566
|
+
const lineNum = String(i + 1).padStart(4, " ");
|
|
70567
|
+
const isInHunk = i >= hunk.oldStart - 1 && i < hunk.oldStart - 1 + hunk.oldLines;
|
|
70568
|
+
const prefix = isInHunk ? import_picocolors23.default.yellow("*") : " ";
|
|
70569
|
+
console.log(`${import_picocolors23.default.dim(lineNum)} ${prefix} ${lines[i]}`);
|
|
70570
|
+
}
|
|
70571
|
+
console.log(import_picocolors23.default.dim("─".repeat(HUNK_SEPARATOR_WIDTH)));
|
|
70285
70572
|
}
|
|
70286
|
-
|
|
70287
|
-
|
|
70288
|
-
|
|
70289
|
-
|
|
70290
|
-
|
|
70291
|
-
|
|
70292
|
-
|
|
70293
|
-
|
|
70294
|
-
|
|
70295
|
-
|
|
70573
|
+
static async mergeFile(filename, currentContent, _newContent, hunks) {
|
|
70574
|
+
console.log(import_picocolors23.default.bold(`
|
|
70575
|
+
━━━ ${filename} ━━━`));
|
|
70576
|
+
console.log(import_picocolors23.default.dim(`${hunks.length} change${hunks.length === 1 ? "" : "s"} to review
|
|
70577
|
+
`));
|
|
70578
|
+
const decisions = [];
|
|
70579
|
+
for (let i = 0;i < hunks.length; i++) {
|
|
70580
|
+
let action;
|
|
70581
|
+
do {
|
|
70582
|
+
action = await MergeUI.promptHunk(hunks[i], i, hunks.length, filename);
|
|
70583
|
+
if (action === "view") {
|
|
70584
|
+
MergeUI.showExtendedContext(currentContent, hunks[i]);
|
|
70585
|
+
}
|
|
70586
|
+
} while (action === "view");
|
|
70587
|
+
if (action === "skip") {
|
|
70588
|
+
return "skipped";
|
|
70296
70589
|
}
|
|
70297
|
-
|
|
70298
|
-
logger.verbose(`Metadata read failed, proceeding with installation: ${error instanceof Error ? error.message : "unknown"}`);
|
|
70590
|
+
decisions.push(action === "accept");
|
|
70299
70591
|
}
|
|
70592
|
+
const result = SyncEngine.applyHunks(currentContent, hunks, decisions);
|
|
70593
|
+
const applied = decisions.filter(Boolean).length;
|
|
70594
|
+
const rejected = decisions.length - applied;
|
|
70595
|
+
return { result, applied, rejected };
|
|
70300
70596
|
}
|
|
70301
|
-
|
|
70302
|
-
|
|
70303
|
-
|
|
70304
|
-
|
|
70305
|
-
|
|
70306
|
-
|
|
70307
|
-
|
|
70308
|
-
|
|
70597
|
+
static displayMergeSummary(filename, applied, rejected) {
|
|
70598
|
+
if (applied > 0 && rejected > 0) {
|
|
70599
|
+
console.log(import_picocolors23.default.dim(` ${filename}: `) + import_picocolors23.default.green(`${applied} applied`) + import_picocolors23.default.dim(", ") + import_picocolors23.default.yellow(`${rejected} rejected`));
|
|
70600
|
+
} else if (applied > 0) {
|
|
70601
|
+
console.log(import_picocolors23.default.dim(` ${filename}: `) + import_picocolors23.default.green(`${applied} applied`));
|
|
70602
|
+
} else {
|
|
70603
|
+
console.log(import_picocolors23.default.dim(` ${filename}: `) + import_picocolors23.default.yellow(`${rejected} rejected`));
|
|
70604
|
+
}
|
|
70605
|
+
}
|
|
70606
|
+
static displaySkipped(filename) {
|
|
70607
|
+
console.log(import_picocolors23.default.dim(` ${filename}: `) + import_picocolors23.default.yellow("skipped"));
|
|
70608
|
+
}
|
|
70609
|
+
}
|
|
70610
|
+
// src/domains/sync/notification-display.ts
|
|
70611
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
70612
|
+
import { stdout } from "node:process";
|
|
70613
|
+
function createNotificationBox(borderColor, boxWidth) {
|
|
70614
|
+
const contentWidth = boxWidth - 2;
|
|
70615
|
+
const topBorder = borderColor(`╭${"─".repeat(contentWidth)}╮`);
|
|
70616
|
+
const bottomBorder = borderColor(`╰${"─".repeat(contentWidth)}╯`);
|
|
70617
|
+
const emptyLine = borderColor("│") + " ".repeat(contentWidth) + borderColor("│");
|
|
70618
|
+
const padLine = (text, visibleLen) => {
|
|
70619
|
+
const len = visibleLen ?? text.length;
|
|
70620
|
+
const displayText = len > contentWidth ? `${text.slice(0, contentWidth - 3)}...` : text;
|
|
70621
|
+
const actualLen = visibleLen ?? displayText.length;
|
|
70622
|
+
const totalPadding = contentWidth - actualLen;
|
|
70623
|
+
const leftPadding = Math.max(0, Math.floor(totalPadding / 2));
|
|
70624
|
+
const rightPadding = Math.max(0, totalPadding - leftPadding);
|
|
70625
|
+
return borderColor("│") + " ".repeat(leftPadding) + displayText + " ".repeat(rightPadding) + borderColor("│");
|
|
70309
70626
|
};
|
|
70627
|
+
return { topBorder, bottomBorder, emptyLine, padLine };
|
|
70310
70628
|
}
|
|
70311
|
-
function
|
|
70312
|
-
|
|
70313
|
-
|
|
70314
|
-
|
|
70315
|
-
|
|
70316
|
-
|
|
70317
|
-
}
|
|
70629
|
+
function displayConfigUpdateNotification(entries, isGlobal = false) {
|
|
70630
|
+
if (entries.length === 0)
|
|
70631
|
+
return;
|
|
70632
|
+
const lines = entries.map(({ kit, currentVersion, latestVersion }) => {
|
|
70633
|
+
const cur = currentVersion.replace(/^v/, "");
|
|
70634
|
+
const latest = latestVersion.replace(/^v/, "");
|
|
70635
|
+
const text = `${import_picocolors24.default.bold(kit)}: ${import_picocolors24.default.dim(cur)} ${import_picocolors24.default.white("→")} ${import_picocolors24.default.green(import_picocolors24.default.bold(latest))}`;
|
|
70636
|
+
const visibleLen = kit.length + 2 + cur.length + 3 + latest.length;
|
|
70637
|
+
return { text, visibleLen };
|
|
70638
|
+
});
|
|
70639
|
+
const updateCmd = isGlobal ? "tkm init -g --sync" : "tkm init --sync";
|
|
70640
|
+
const commandText = `Run: ${import_picocolors24.default.cyan(import_picocolors24.default.bold(updateCmd))}`;
|
|
70641
|
+
const commandLen = `Run: ${updateCmd}`.length;
|
|
70642
|
+
const headerLabel = "\uD83D\uDCE6 Config Updates Available";
|
|
70643
|
+
const headerText = import_picocolors24.default.bold(import_picocolors24.default.yellow(headerLabel));
|
|
70644
|
+
const longestLine = Math.max(headerLabel.length, commandLen, ...lines.map((l2) => l2.visibleLen));
|
|
70645
|
+
const terminalWidth = stdout.columns || 80;
|
|
70646
|
+
const boxWidth = Math.min(Math.max(longestLine + 6, 52), terminalWidth - 4);
|
|
70647
|
+
const { topBorder, bottomBorder, emptyLine, padLine } = createNotificationBox(import_picocolors24.default.cyan, boxWidth);
|
|
70648
|
+
console.log("");
|
|
70649
|
+
console.log(topBorder);
|
|
70650
|
+
console.log(emptyLine);
|
|
70651
|
+
console.log(padLine(headerText, headerLabel.length));
|
|
70652
|
+
for (const { text, visibleLen } of lines) {
|
|
70653
|
+
console.log(padLine(text, visibleLen));
|
|
70318
70654
|
}
|
|
70319
|
-
|
|
70655
|
+
console.log(emptyLine);
|
|
70656
|
+
console.log(padLine(commandText, commandLen));
|
|
70657
|
+
console.log(emptyLine);
|
|
70658
|
+
console.log(bottomBorder);
|
|
70659
|
+
console.log("");
|
|
70320
70660
|
}
|
|
70661
|
+
// src/domains/sync/passive-update-check.ts
|
|
70662
|
+
init_logger();
|
|
70321
70663
|
// src/commands/init/phases/sync-handler.ts
|
|
70322
|
-
import { copyFile as copyFile7, mkdir as mkdir29, open as open3, readFile as readFile41, rename as rename6, stat as stat12, unlink as unlink13, writeFile as writeFile31 } from "node:fs/promises";
|
|
70323
|
-
import { dirname as dirname27, join as join98, resolve as resolve23 } from "node:path";
|
|
70324
70664
|
init_logger();
|
|
70325
70665
|
init_path_resolver();
|
|
70326
70666
|
var import_fs_extra33 = __toESM(require_lib(), 1);
|
|
@@ -70348,7 +70688,7 @@ async function handleSync(ctx) {
|
|
|
70348
70688
|
Run 'takumi init' to update.`, "Legacy Installation");
|
|
70349
70689
|
return { ...ctx, cancelled: true };
|
|
70350
70690
|
}
|
|
70351
|
-
let kitType = ctx.options.
|
|
70691
|
+
let kitType = ctx.options.selectedKits?.[0];
|
|
70352
70692
|
if (!kitType) {
|
|
70353
70693
|
const engineerMeta = await readKitManifest(claudeDir, "engineer");
|
|
70354
70694
|
if (engineerMeta) {
|
|
@@ -70385,7 +70725,13 @@ Run 'takumi init' to update.`, "Legacy Installation");
|
|
|
70385
70725
|
ctx.prompts.note(`You're on the latest version (${updateResult.currentVersion})`, "Already Up to Date");
|
|
70386
70726
|
return { ...ctx, cancelled: true };
|
|
70387
70727
|
}
|
|
70388
|
-
displayConfigUpdateNotification(
|
|
70728
|
+
displayConfigUpdateNotification([
|
|
70729
|
+
{
|
|
70730
|
+
kit: kitType,
|
|
70731
|
+
currentVersion: updateResult.currentVersion,
|
|
70732
|
+
latestVersion: updateResult.latestVersion
|
|
70733
|
+
}
|
|
70734
|
+
], ctx.options.global);
|
|
70389
70735
|
if (!ctx.isNonInteractive) {
|
|
70390
70736
|
const proceed = await ctx.prompts.confirm("Proceed with config sync?");
|
|
70391
70737
|
if (!proceed) {
|
|
@@ -71181,23 +71527,102 @@ async function executeInit(options2, prompts) {
|
|
|
71181
71527
|
ctx = await handleSelection(ctx);
|
|
71182
71528
|
if (ctx.cancelled)
|
|
71183
71529
|
return;
|
|
71184
|
-
ctx
|
|
71185
|
-
|
|
71530
|
+
if (ctx.resolvedDir && !isSyncMode) {
|
|
71531
|
+
try {
|
|
71532
|
+
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
71533
|
+
const claudeDir = prefix ? join102(ctx.resolvedDir, prefix) : ctx.resolvedDir;
|
|
71534
|
+
const pending = await ManifestWriter.findPendingKits(claudeDir);
|
|
71535
|
+
if (pending.length > 0) {
|
|
71536
|
+
logger.warning(`Previous install was interrupted for kit(s): ${pending.join(", ")}. Re-running install will overwrite the pending entry.`);
|
|
71537
|
+
}
|
|
71538
|
+
} catch (error) {
|
|
71539
|
+
logger.debug(`Pending-kit check skipped: ${error}`);
|
|
71540
|
+
}
|
|
71541
|
+
}
|
|
71542
|
+
const selectedKits = ctx.selectedKits && ctx.selectedKits.length > 0 ? ctx.selectedKits : ctx.kitType ? [ctx.kitType] : [];
|
|
71543
|
+
if (selectedKits.length === 0) {
|
|
71544
|
+
logger.error("No kits resolved for installation");
|
|
71186
71545
|
return;
|
|
71187
|
-
if (!isSyncMode) {
|
|
71188
|
-
ctx = await handleTransforms(ctx);
|
|
71189
|
-
if (ctx.cancelled)
|
|
71190
|
-
return;
|
|
71191
71546
|
}
|
|
71192
|
-
|
|
71193
|
-
|
|
71194
|
-
|
|
71547
|
+
const aggregatedResults = [];
|
|
71548
|
+
let lastCtx = ctx;
|
|
71549
|
+
for (let i = 0;i < selectedKits.length; i++) {
|
|
71550
|
+
const currentKit = selectedKits[i];
|
|
71551
|
+
const isLastKit = i === selectedKits.length - 1;
|
|
71552
|
+
if (i > 0) {
|
|
71553
|
+
let nextRelease = lastCtx.releasesByKit?.[currentKit];
|
|
71554
|
+
if (!nextRelease) {
|
|
71555
|
+
try {
|
|
71556
|
+
nextRelease = await resolveReleaseForKit(lastCtx, currentKit);
|
|
71557
|
+
} catch (error) {
|
|
71558
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71559
|
+
logger.error(`Failed to resolve release for ${currentKit}: ${message}`);
|
|
71560
|
+
aggregatedResults.push({
|
|
71561
|
+
provider: targetAgents[0] ?? "claude-code",
|
|
71562
|
+
success: false,
|
|
71563
|
+
cancelled: false,
|
|
71564
|
+
installedFiles: [],
|
|
71565
|
+
error: `Release fetch failed for kit "${currentKit}": ${message}`
|
|
71566
|
+
});
|
|
71567
|
+
continue;
|
|
71568
|
+
}
|
|
71569
|
+
}
|
|
71570
|
+
logger.info(`Installing kit ${i + 1}/${selectedKits.length}: ${AVAILABLE_KITS[currentKit].name}${nextRelease?.tag_name ? ` (${nextRelease.tag_name})` : ""}`);
|
|
71571
|
+
if (lastCtx.options.release && !lastCtx.releasesByKit?.[currentKit] && !lastCtx.options.kitPath && !lastCtx.options.archive) {
|
|
71572
|
+
logger.info(`Secondary kit "${currentKit}" uses latest channel — --release applies only to the primary kit.`);
|
|
71573
|
+
}
|
|
71574
|
+
const nextReleasesByKit = { ...lastCtx.releasesByKit ?? {} };
|
|
71575
|
+
if (nextRelease)
|
|
71576
|
+
nextReleasesByKit[currentKit] = nextRelease;
|
|
71577
|
+
lastCtx = {
|
|
71578
|
+
...lastCtx,
|
|
71579
|
+
kit: AVAILABLE_KITS[currentKit],
|
|
71580
|
+
kitType: currentKit,
|
|
71581
|
+
release: nextRelease,
|
|
71582
|
+
selectedVersion: nextRelease?.tag_name,
|
|
71583
|
+
releasesByKit: nextReleasesByKit,
|
|
71584
|
+
tempDir: undefined,
|
|
71585
|
+
extractDir: undefined,
|
|
71586
|
+
archivePath: undefined,
|
|
71587
|
+
isLastKit
|
|
71588
|
+
};
|
|
71589
|
+
} else {
|
|
71590
|
+
if (selectedKits.length > 1) {
|
|
71591
|
+
logger.info(`Installing kit ${i + 1}/${selectedKits.length}: ${AVAILABLE_KITS[currentKit].name}${lastCtx.release?.tag_name ? ` (${lastCtx.release.tag_name})` : ""}`);
|
|
71592
|
+
}
|
|
71593
|
+
lastCtx = { ...lastCtx, isLastKit };
|
|
71594
|
+
}
|
|
71595
|
+
if (lastCtx.resolvedDir && lastCtx.release && !isSyncMode) {
|
|
71596
|
+
try {
|
|
71597
|
+
const prefix = PathResolver.getPathPrefix(lastCtx.options.global);
|
|
71598
|
+
const claudeDir = prefix ? join102(lastCtx.resolvedDir, prefix) : lastCtx.resolvedDir;
|
|
71599
|
+
const scope = lastCtx.options.global ? "global" : "local";
|
|
71600
|
+
await ManifestWriter.writeKitPending(claudeDir, currentKit, lastCtx.release.tag_name, scope);
|
|
71601
|
+
} catch (error) {
|
|
71602
|
+
logger.debug(`writeKitPending skipped: ${error}`);
|
|
71603
|
+
}
|
|
71604
|
+
}
|
|
71605
|
+
lastCtx = await handleDownload(lastCtx);
|
|
71606
|
+
if (lastCtx.cancelled)
|
|
71607
|
+
return;
|
|
71608
|
+
if (!isSyncMode) {
|
|
71609
|
+
lastCtx = await handleTransforms(lastCtx);
|
|
71610
|
+
if (lastCtx.cancelled)
|
|
71611
|
+
return;
|
|
71612
|
+
}
|
|
71613
|
+
if (isSyncMode) {
|
|
71614
|
+
lastCtx = await executeSyncMerge(lastCtx);
|
|
71615
|
+
if (lastCtx.cancelled)
|
|
71616
|
+
return;
|
|
71617
|
+
}
|
|
71618
|
+
const kitResults = await dispatchInstallers(lastCtx, targetAgents);
|
|
71619
|
+
aggregatedResults.push(...kitResults);
|
|
71620
|
+
if (kitResults.some((r2) => r2.cancelled))
|
|
71195
71621
|
return;
|
|
71196
71622
|
}
|
|
71197
|
-
|
|
71623
|
+
ctx = lastCtx;
|
|
71624
|
+
const results = aggregatedResults;
|
|
71198
71625
|
ctx.installerResults = results;
|
|
71199
|
-
if (results.some((r2) => r2.cancelled))
|
|
71200
|
-
return;
|
|
71201
71626
|
if (targetAgents.length > 1) {
|
|
71202
71627
|
displayMultiInstallerSummary(results);
|
|
71203
71628
|
}
|
|
@@ -71229,13 +71654,14 @@ ${errorLines.join(`
|
|
|
71229
71654
|
return;
|
|
71230
71655
|
}
|
|
71231
71656
|
prompts.outro(`Project initialized successfully at ${ctx.resolvedDir}`);
|
|
71232
|
-
const
|
|
71233
|
-
|
|
71234
|
-
|
|
71235
|
-
|
|
71236
|
-
|
|
71237
|
-
|
|
71238
|
-
}
|
|
71657
|
+
const installedSummary = selectedKits.map((k3) => {
|
|
71658
|
+
const tag = ctx.releasesByKit?.[k3]?.tag_name;
|
|
71659
|
+
return tag ? `${k3}@${tag}` : k3;
|
|
71660
|
+
}).join(", ");
|
|
71661
|
+
const installedLine = installedSummary ? `Installed: ${installedSummary}.` : "Project initialized.";
|
|
71662
|
+
const protectedLine = ctx.customClaudeFiles.length > 0 ? "Your local files were left untouched — including .env and any custom files you added under .claude/." : "Your local files were left untouched — Takumi only writes inside .claude/ (and CLAUDE.md), so .env and other project files are never modified.";
|
|
71663
|
+
prompts.note(`${installedLine}
|
|
71664
|
+
${protectedLine}`, "Initialization complete");
|
|
71239
71665
|
}
|
|
71240
71666
|
async function initCommand(options2) {
|
|
71241
71667
|
const prompts = new PromptsManager;
|
|
@@ -71258,7 +71684,7 @@ async function initCommand(options2) {
|
|
|
71258
71684
|
import { existsSync as existsSync47 } from "node:fs";
|
|
71259
71685
|
import { readFile as readFile45, rm as rm13, unlink as unlink15 } from "node:fs/promises";
|
|
71260
71686
|
import { homedir as homedir25 } from "node:os";
|
|
71261
|
-
import { basename as basename12, join as
|
|
71687
|
+
import { basename as basename12, join as join103, resolve as resolve26 } from "node:path";
|
|
71262
71688
|
init_dist2();
|
|
71263
71689
|
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
71264
71690
|
init_logger();
|
|
@@ -71740,7 +72166,7 @@ async function executeDeleteAction(action, options2) {
|
|
|
71740
72166
|
async function processMetadataDeletions(skillSourcePath, installGlobally) {
|
|
71741
72167
|
if (!skillSourcePath)
|
|
71742
72168
|
return;
|
|
71743
|
-
const sourceMetadataPath =
|
|
72169
|
+
const sourceMetadataPath = join103(resolve26(skillSourcePath, ".."), "metadata.json");
|
|
71744
72170
|
if (!existsSync47(sourceMetadataPath))
|
|
71745
72171
|
return;
|
|
71746
72172
|
let sourceMetadata;
|
|
@@ -71753,11 +72179,11 @@ async function processMetadataDeletions(skillSourcePath, installGlobally) {
|
|
|
71753
72179
|
}
|
|
71754
72180
|
if (!sourceMetadata.deletions || sourceMetadata.deletions.length === 0)
|
|
71755
72181
|
return;
|
|
71756
|
-
const claudeDir = installGlobally ?
|
|
72182
|
+
const claudeDir = installGlobally ? join103(homedir25(), ".claude") : join103(process.cwd(), ".claude");
|
|
71757
72183
|
if (!existsSync47(claudeDir))
|
|
71758
72184
|
return;
|
|
71759
72185
|
try {
|
|
71760
|
-
const result = await handleDeletions(sourceMetadata, claudeDir);
|
|
72186
|
+
const result = await handleDeletions(sourceMetadata, claudeDir, undefined);
|
|
71761
72187
|
if (result.deletedPaths.length > 0) {
|
|
71762
72188
|
logger.verbose(`[migrate] Cleaned up ${result.deletedPaths.length} deprecated path(s): ${result.deletedPaths.join(", ")}`);
|
|
71763
72189
|
}
|
|
@@ -71901,8 +72327,8 @@ async function migrateCommand(options2) {
|
|
|
71901
72327
|
selectedProviders = Array.from(new Set(selectedProviders));
|
|
71902
72328
|
let installGlobally = options2.global ?? false;
|
|
71903
72329
|
if (options2.global === undefined && !options2.yes) {
|
|
71904
|
-
const projectTarget =
|
|
71905
|
-
const globalTarget =
|
|
72330
|
+
const projectTarget = join103(process.cwd(), ".claude");
|
|
72331
|
+
const globalTarget = join103(homedir25(), ".claude");
|
|
71906
72332
|
const scopeChoice = await ie({
|
|
71907
72333
|
message: "Installation scope",
|
|
71908
72334
|
options: [
|
|
@@ -71954,7 +72380,7 @@ async function migrateCommand(options2) {
|
|
|
71954
72380
|
}
|
|
71955
72381
|
const providerNames = selectedProviders.map((prov) => import_picocolors27.default.cyan(providers[prov].displayName)).join(", ");
|
|
71956
72382
|
f2.message(` Providers: ${providerNames}`);
|
|
71957
|
-
const targetDir = installGlobally ?
|
|
72383
|
+
const targetDir = installGlobally ? join103(homedir25(), ".claude") : join103(process.cwd(), ".claude");
|
|
71958
72384
|
f2.message(` Scope: ${installGlobally ? "Global" : "Project"} ${import_picocolors27.default.dim(`-> ${targetDir}`)}`);
|
|
71959
72385
|
const cmdProviders = getProvidersSupporting("commands");
|
|
71960
72386
|
const unsupportedCmd = selectedProviders.filter((pv) => !cmdProviders.includes(pv));
|
|
@@ -72464,7 +72890,7 @@ async function handleDirectorySetup(ctx) {
|
|
|
72464
72890
|
};
|
|
72465
72891
|
}
|
|
72466
72892
|
// src/commands/new/phases/project-creation.ts
|
|
72467
|
-
import { join as
|
|
72893
|
+
import { join as join104 } from "node:path";
|
|
72468
72894
|
init_github_client();
|
|
72469
72895
|
init_logger();
|
|
72470
72896
|
init_output_manager();
|
|
@@ -72618,7 +73044,7 @@ async function projectCreation(kit, resolvedDir, validOptions, isNonInteractive2
|
|
|
72618
73044
|
output.section("Installing");
|
|
72619
73045
|
logger.verbose("Installation target", { directory: resolvedDir });
|
|
72620
73046
|
const merger = new FileMerger;
|
|
72621
|
-
const claudeDir =
|
|
73047
|
+
const claudeDir = join104(resolvedDir, ".claude");
|
|
72622
73048
|
merger.setMultiKitContext(claudeDir, kit);
|
|
72623
73049
|
if (validOptions.exclude && validOptions.exclude.length > 0) {
|
|
72624
73050
|
merger.addIgnorePatterns(validOptions.exclude);
|
|
@@ -72671,10 +73097,10 @@ async function handleProjectCreation(ctx) {
|
|
|
72671
73097
|
};
|
|
72672
73098
|
}
|
|
72673
73099
|
// src/commands/new/phases/post-setup.ts
|
|
72674
|
-
import { join as
|
|
73100
|
+
import { join as join106 } from "node:path";
|
|
72675
73101
|
|
|
72676
73102
|
// src/domains/installation/setup-wizard.ts
|
|
72677
|
-
import { join as
|
|
73103
|
+
import { join as join105 } from "node:path";
|
|
72678
73104
|
init_logger();
|
|
72679
73105
|
init_dist2();
|
|
72680
73106
|
var import_fs_extra36 = __toESM(require_lib(), 1);
|
|
@@ -72754,7 +73180,7 @@ async function parseEnvFile(path9) {
|
|
|
72754
73180
|
}
|
|
72755
73181
|
}
|
|
72756
73182
|
async function checkGlobalConfig() {
|
|
72757
|
-
const globalEnvPath =
|
|
73183
|
+
const globalEnvPath = join105(getClaudeDir(), ".env");
|
|
72758
73184
|
if (!await import_fs_extra36.pathExists(globalEnvPath))
|
|
72759
73185
|
return false;
|
|
72760
73186
|
const env2 = await parseEnvFile(globalEnvPath);
|
|
@@ -72770,7 +73196,7 @@ async function runSetupWizard(options2) {
|
|
|
72770
73196
|
let globalEnv = {};
|
|
72771
73197
|
const hasGlobalConfig = !isGlobal && await checkGlobalConfig();
|
|
72772
73198
|
if (!isGlobal) {
|
|
72773
|
-
const globalEnvPath =
|
|
73199
|
+
const globalEnvPath = join105(getClaudeDir(), ".env");
|
|
72774
73200
|
if (await import_fs_extra36.pathExists(globalEnvPath)) {
|
|
72775
73201
|
globalEnv = await parseEnvFile(globalEnvPath);
|
|
72776
73202
|
}
|
|
@@ -72833,7 +73259,7 @@ async function runSetupWizard(options2) {
|
|
|
72833
73259
|
}
|
|
72834
73260
|
}
|
|
72835
73261
|
await generateEnvFile(targetDir, values);
|
|
72836
|
-
f2.success(`Configuration saved to ${
|
|
73262
|
+
f2.success(`Configuration saved to ${join105(targetDir, ".env")}`);
|
|
72837
73263
|
return true;
|
|
72838
73264
|
}
|
|
72839
73265
|
async function promptForAdditionalGeminiKeys(primaryKey) {
|
|
@@ -72936,9 +73362,9 @@ async function postSetup(resolvedDir, validOptions, isNonInteractive2, prompts)
|
|
|
72936
73362
|
withSudo: validOptions.withSudo
|
|
72937
73363
|
});
|
|
72938
73364
|
}
|
|
72939
|
-
const claudeDir =
|
|
73365
|
+
const claudeDir = join106(resolvedDir, ".claude");
|
|
72940
73366
|
await promptSetupWizardIfNeeded({
|
|
72941
|
-
envPath:
|
|
73367
|
+
envPath: join106(claudeDir, ".env"),
|
|
72942
73368
|
claudeDir,
|
|
72943
73369
|
isGlobal: false,
|
|
72944
73370
|
isNonInteractive: isNonInteractive2,
|
|
@@ -73015,11 +73441,11 @@ Example: tkm new --use-git --release v2.1.0`);
|
|
|
73015
73441
|
// src/commands/plan/plan-command.ts
|
|
73016
73442
|
init_output_manager();
|
|
73017
73443
|
import { existsSync as existsSync52, statSync as statSync5 } from "node:fs";
|
|
73018
|
-
import { dirname as dirname33, join as
|
|
73444
|
+
import { dirname as dirname33, join as join110, parse as parse2, resolve as resolve31 } from "node:path";
|
|
73019
73445
|
|
|
73020
73446
|
// src/commands/plan/plan-read-handlers.ts
|
|
73021
73447
|
import { existsSync as existsSync51, statSync as statSync4 } from "node:fs";
|
|
73022
|
-
import { basename as basename15, dirname as dirname32, join as
|
|
73448
|
+
import { basename as basename15, dirname as dirname32, join as join109, relative as relative20, resolve as resolve29 } from "node:path";
|
|
73023
73449
|
|
|
73024
73450
|
// src/domains/plan-parser/index.ts
|
|
73025
73451
|
import { dirname as dirname31 } from "node:path";
|
|
@@ -73400,12 +73826,12 @@ function parsePlanFile(planFilePath, options2) {
|
|
|
73400
73826
|
}
|
|
73401
73827
|
// src/domains/plan-parser/plan-scanner.ts
|
|
73402
73828
|
import { existsSync as existsSync48, readdirSync as readdirSync5 } from "node:fs";
|
|
73403
|
-
import { join as
|
|
73829
|
+
import { join as join107 } from "node:path";
|
|
73404
73830
|
function scanPlanDir(dir) {
|
|
73405
73831
|
if (!existsSync48(dir))
|
|
73406
73832
|
return [];
|
|
73407
73833
|
try {
|
|
73408
|
-
return readdirSync5(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
73834
|
+
return readdirSync5(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join107(dir, entry.name, "plan.md")).filter(existsSync48);
|
|
73409
73835
|
} catch {
|
|
73410
73836
|
return [];
|
|
73411
73837
|
}
|
|
@@ -73474,7 +73900,7 @@ function validatePlanFile(filePath, strict = false) {
|
|
|
73474
73900
|
var import_gray_matter7 = __toESM(require_gray_matter(), 1);
|
|
73475
73901
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "node:fs";
|
|
73476
73902
|
import { existsSync as existsSync50 } from "node:fs";
|
|
73477
|
-
import { basename as basename14, dirname as dirname30, join as
|
|
73903
|
+
import { basename as basename14, dirname as dirname30, join as join108 } from "node:path";
|
|
73478
73904
|
function phaseNameToFilename(id, name) {
|
|
73479
73905
|
const numMatch = /^(\d+)([a-z]*)$/i.exec(id);
|
|
73480
73906
|
const num = numMatch ? numMatch[1] : id;
|
|
@@ -73582,12 +74008,12 @@ function scaffoldPlan(options2) {
|
|
|
73582
74008
|
mkdirSync3(dir, { recursive: true });
|
|
73583
74009
|
const resolvedPhases = resolvePhaseIds(options2.phases);
|
|
73584
74010
|
const optionsWithResolved = { ...options2, phases: resolvedPhases };
|
|
73585
|
-
const planFile =
|
|
74011
|
+
const planFile = join108(dir, "plan.md");
|
|
73586
74012
|
writeFileSync5(planFile, generatePlanMd(optionsWithResolved), "utf8");
|
|
73587
74013
|
const phaseFiles = [];
|
|
73588
74014
|
for (const phase of resolvedPhases) {
|
|
73589
74015
|
const filename = phaseNameToFilename(phase.id, phase.name);
|
|
73590
|
-
const phaseFile =
|
|
74016
|
+
const phaseFile = join108(dir, filename);
|
|
73591
74017
|
writeFileSync5(phaseFile, generatePhaseTemplate(phase), "utf8");
|
|
73592
74018
|
phaseFiles.push(phaseFile);
|
|
73593
74019
|
}
|
|
@@ -73667,7 +74093,7 @@ function phaseNameFilenameFromTableRow(body, phaseId, planDir) {
|
|
|
73667
74093
|
continue;
|
|
73668
74094
|
const linkMatch = /\[([^\]]+)\]\(\.\/([^)]+)\)/.exec(row);
|
|
73669
74095
|
if (linkMatch)
|
|
73670
|
-
return
|
|
74096
|
+
return join108(planDir, linkMatch[2]);
|
|
73671
74097
|
}
|
|
73672
74098
|
return null;
|
|
73673
74099
|
}
|
|
@@ -73748,7 +74174,7 @@ function addPhase(planFile, name, afterId) {
|
|
|
73748
74174
|
`);
|
|
73749
74175
|
}
|
|
73750
74176
|
writeFileSync5(planFile, import_gray_matter7.default.stringify(updatedBody, frontmatter), "utf8");
|
|
73751
|
-
const phaseFilePath =
|
|
74177
|
+
const phaseFilePath = join108(planDir, filename);
|
|
73752
74178
|
writeFileSync5(phaseFilePath, generatePhaseTemplate({ id: phaseId, name }), "utf8");
|
|
73753
74179
|
return { phaseId, phaseFile: phaseFilePath };
|
|
73754
74180
|
}
|
|
@@ -73852,7 +74278,7 @@ async function handleValidate(target, options2) {
|
|
|
73852
74278
|
}
|
|
73853
74279
|
async function handleStatus(target, options2) {
|
|
73854
74280
|
const t = target ? resolve29(target) : null;
|
|
73855
|
-
const plansDir = t && existsSync51(t) && statSync4(t).isDirectory() && !existsSync51(
|
|
74281
|
+
const plansDir = t && existsSync51(t) && statSync4(t).isDirectory() && !existsSync51(join109(t, "plan.md")) ? t : null;
|
|
73856
74282
|
if (plansDir) {
|
|
73857
74283
|
const planFiles = scanPlanDir(plansDir);
|
|
73858
74284
|
if (planFiles.length === 0) {
|
|
@@ -74087,7 +74513,7 @@ function resolvePlanFile(target) {
|
|
|
74087
74513
|
const stat13 = statSync5(t);
|
|
74088
74514
|
if (stat13.isFile())
|
|
74089
74515
|
return t;
|
|
74090
|
-
const candidate =
|
|
74516
|
+
const candidate = join110(t, "plan.md");
|
|
74091
74517
|
if (existsSync52(candidate))
|
|
74092
74518
|
return candidate;
|
|
74093
74519
|
}
|
|
@@ -74095,7 +74521,7 @@ function resolvePlanFile(target) {
|
|
|
74095
74521
|
let dir = process.cwd();
|
|
74096
74522
|
const root = parse2(dir).root;
|
|
74097
74523
|
while (dir !== root) {
|
|
74098
|
-
const candidate =
|
|
74524
|
+
const candidate = join110(dir, "plan.md");
|
|
74099
74525
|
if (existsSync52(candidate))
|
|
74100
74526
|
return candidate;
|
|
74101
74527
|
dir = dirname33(dir);
|
|
@@ -74367,112 +74793,112 @@ var import_picocolors34 = __toESM(require_picocolors(), 1);
|
|
|
74367
74793
|
// src/commands/skills/agents.ts
|
|
74368
74794
|
import { existsSync as existsSync54 } from "node:fs";
|
|
74369
74795
|
import { homedir as homedir26 } from "node:os";
|
|
74370
|
-
import { join as
|
|
74796
|
+
import { join as join111 } from "node:path";
|
|
74371
74797
|
var home6 = homedir26();
|
|
74372
74798
|
var agents = {
|
|
74373
74799
|
"claude-code": {
|
|
74374
74800
|
name: "claude-code",
|
|
74375
74801
|
displayName: "Claude Code",
|
|
74376
74802
|
projectPath: ".claude/skills",
|
|
74377
|
-
globalPath:
|
|
74378
|
-
detect: async () => existsSync54(
|
|
74803
|
+
globalPath: join111(home6, ".claude/skills"),
|
|
74804
|
+
detect: async () => existsSync54(join111(home6, ".claude"))
|
|
74379
74805
|
},
|
|
74380
74806
|
cursor: {
|
|
74381
74807
|
name: "cursor",
|
|
74382
74808
|
displayName: "Cursor",
|
|
74383
74809
|
projectPath: ".cursor/skills",
|
|
74384
|
-
globalPath:
|
|
74385
|
-
detect: async () => existsSync54(
|
|
74810
|
+
globalPath: join111(home6, ".cursor/skills"),
|
|
74811
|
+
detect: async () => existsSync54(join111(home6, ".cursor"))
|
|
74386
74812
|
},
|
|
74387
74813
|
codex: {
|
|
74388
74814
|
name: "codex",
|
|
74389
74815
|
displayName: "Codex",
|
|
74390
74816
|
projectPath: ".codex/skills",
|
|
74391
|
-
globalPath:
|
|
74392
|
-
detect: async () => existsSync54(
|
|
74817
|
+
globalPath: join111(home6, ".codex/skills"),
|
|
74818
|
+
detect: async () => existsSync54(join111(home6, ".codex"))
|
|
74393
74819
|
},
|
|
74394
74820
|
opencode: {
|
|
74395
74821
|
name: "opencode",
|
|
74396
74822
|
displayName: "OpenCode",
|
|
74397
74823
|
projectPath: ".opencode/skills",
|
|
74398
|
-
globalPath:
|
|
74399
|
-
detect: async () => existsSync54(
|
|
74824
|
+
globalPath: join111(home6, ".config/opencode/skills"),
|
|
74825
|
+
detect: async () => existsSync54(join111(home6, ".config/opencode"))
|
|
74400
74826
|
},
|
|
74401
74827
|
goose: {
|
|
74402
74828
|
name: "goose",
|
|
74403
74829
|
displayName: "Goose",
|
|
74404
74830
|
projectPath: ".goose/skills",
|
|
74405
|
-
globalPath:
|
|
74406
|
-
detect: async () => existsSync54(
|
|
74831
|
+
globalPath: join111(home6, ".config/goose/skills"),
|
|
74832
|
+
detect: async () => existsSync54(join111(home6, ".config/goose"))
|
|
74407
74833
|
},
|
|
74408
74834
|
"gemini-cli": {
|
|
74409
74835
|
name: "gemini-cli",
|
|
74410
74836
|
displayName: "Gemini CLI",
|
|
74411
74837
|
projectPath: ".agents/skills",
|
|
74412
|
-
globalPath:
|
|
74413
|
-
detect: async () => existsSync54(
|
|
74838
|
+
globalPath: join111(home6, ".agents/skills"),
|
|
74839
|
+
detect: async () => existsSync54(join111(home6, ".gemini"))
|
|
74414
74840
|
},
|
|
74415
74841
|
antigravity: {
|
|
74416
74842
|
name: "antigravity",
|
|
74417
74843
|
displayName: "Antigravity",
|
|
74418
74844
|
projectPath: ".agent/skills",
|
|
74419
|
-
globalPath:
|
|
74420
|
-
detect: async () => existsSync54(
|
|
74845
|
+
globalPath: join111(home6, ".gemini/antigravity/skills"),
|
|
74846
|
+
detect: async () => existsSync54(join111(process.cwd(), ".agent")) || existsSync54(join111(home6, ".gemini/antigravity"))
|
|
74421
74847
|
},
|
|
74422
74848
|
"github-copilot": {
|
|
74423
74849
|
name: "github-copilot",
|
|
74424
74850
|
displayName: "GitHub Copilot",
|
|
74425
74851
|
projectPath: ".github/skills",
|
|
74426
|
-
globalPath:
|
|
74427
|
-
detect: async () => existsSync54(
|
|
74852
|
+
globalPath: join111(home6, ".copilot/skills"),
|
|
74853
|
+
detect: async () => existsSync54(join111(home6, ".copilot"))
|
|
74428
74854
|
},
|
|
74429
74855
|
amp: {
|
|
74430
74856
|
name: "amp",
|
|
74431
74857
|
displayName: "Amp",
|
|
74432
74858
|
projectPath: ".agents/skills",
|
|
74433
|
-
globalPath:
|
|
74434
|
-
detect: async () => existsSync54(
|
|
74859
|
+
globalPath: join111(home6, ".config/agents/skills"),
|
|
74860
|
+
detect: async () => existsSync54(join111(home6, ".config/amp"))
|
|
74435
74861
|
},
|
|
74436
74862
|
kilo: {
|
|
74437
74863
|
name: "kilo",
|
|
74438
74864
|
displayName: "Kilo Code",
|
|
74439
74865
|
projectPath: ".kilocode/skills",
|
|
74440
|
-
globalPath:
|
|
74441
|
-
detect: async () => existsSync54(
|
|
74866
|
+
globalPath: join111(home6, ".kilocode/skills"),
|
|
74867
|
+
detect: async () => existsSync54(join111(home6, ".kilocode"))
|
|
74442
74868
|
},
|
|
74443
74869
|
roo: {
|
|
74444
74870
|
name: "roo",
|
|
74445
74871
|
displayName: "Roo Code",
|
|
74446
74872
|
projectPath: ".roo/skills",
|
|
74447
|
-
globalPath:
|
|
74448
|
-
detect: async () => existsSync54(
|
|
74873
|
+
globalPath: join111(home6, ".roo/skills"),
|
|
74874
|
+
detect: async () => existsSync54(join111(home6, ".roo"))
|
|
74449
74875
|
},
|
|
74450
74876
|
windsurf: {
|
|
74451
74877
|
name: "windsurf",
|
|
74452
74878
|
displayName: "Windsurf",
|
|
74453
74879
|
projectPath: ".windsurf/skills",
|
|
74454
|
-
globalPath:
|
|
74455
|
-
detect: async () => existsSync54(
|
|
74880
|
+
globalPath: join111(home6, ".codeium/windsurf/skills"),
|
|
74881
|
+
detect: async () => existsSync54(join111(home6, ".codeium/windsurf"))
|
|
74456
74882
|
},
|
|
74457
74883
|
cline: {
|
|
74458
74884
|
name: "cline",
|
|
74459
74885
|
displayName: "Cline",
|
|
74460
74886
|
projectPath: ".cline/skills",
|
|
74461
|
-
globalPath:
|
|
74462
|
-
detect: async () => existsSync54(
|
|
74887
|
+
globalPath: join111(home6, ".cline/skills"),
|
|
74888
|
+
detect: async () => existsSync54(join111(home6, ".cline"))
|
|
74463
74889
|
},
|
|
74464
74890
|
openhands: {
|
|
74465
74891
|
name: "openhands",
|
|
74466
74892
|
displayName: "OpenHands",
|
|
74467
74893
|
projectPath: ".openhands/skills",
|
|
74468
|
-
globalPath:
|
|
74469
|
-
detect: async () => existsSync54(
|
|
74894
|
+
globalPath: join111(home6, ".openhands/skills"),
|
|
74895
|
+
detect: async () => existsSync54(join111(home6, ".openhands"))
|
|
74470
74896
|
}
|
|
74471
74897
|
};
|
|
74472
74898
|
function getInstallPath(skillName, agent, options2) {
|
|
74473
74899
|
const config = agents[agent];
|
|
74474
74900
|
const basePath = options2.global ? config.globalPath : config.projectPath;
|
|
74475
|
-
return
|
|
74901
|
+
return join111(basePath, skillName);
|
|
74476
74902
|
}
|
|
74477
74903
|
function isSkillInstalled(skillName, agent, options2) {
|
|
74478
74904
|
const installPath = getInstallPath(skillName, agent, options2);
|
|
@@ -74483,16 +74909,16 @@ function isSkillInstalled(skillName, agent, options2) {
|
|
|
74483
74909
|
import { existsSync as existsSync56 } from "node:fs";
|
|
74484
74910
|
import { cp as cp4, mkdir as mkdir31, rm as rm14, stat as stat13 } from "node:fs/promises";
|
|
74485
74911
|
import { homedir as homedir28 } from "node:os";
|
|
74486
|
-
import { dirname as dirname35, join as
|
|
74912
|
+
import { dirname as dirname35, join as join113, resolve as resolve33 } from "node:path";
|
|
74487
74913
|
|
|
74488
74914
|
// src/commands/skills/skills-registry.ts
|
|
74489
74915
|
init_zod();
|
|
74490
74916
|
import { existsSync as existsSync55 } from "node:fs";
|
|
74491
74917
|
import { mkdir as mkdir30, readFile as readFile47, writeFile as writeFile35 } from "node:fs/promises";
|
|
74492
74918
|
import { homedir as homedir27 } from "node:os";
|
|
74493
|
-
import { dirname as dirname34, join as
|
|
74919
|
+
import { dirname as dirname34, join as join112, sep as sep9 } from "node:path";
|
|
74494
74920
|
var home7 = homedir27();
|
|
74495
|
-
var REGISTRY_PATH2 =
|
|
74921
|
+
var REGISTRY_PATH2 = join112(home7, ".sunagentkit", "skill-registry.json");
|
|
74496
74922
|
var SkillInstallationSchema = exports_external.object({
|
|
74497
74923
|
skill: exports_external.string(),
|
|
74498
74924
|
agent: exports_external.string(),
|
|
@@ -74630,7 +75056,7 @@ async function syncRegistry() {
|
|
|
74630
75056
|
var LEGACY_SKILL_PATHS = {
|
|
74631
75057
|
"gemini-cli": {
|
|
74632
75058
|
project: ".gemini/skills",
|
|
74633
|
-
global:
|
|
75059
|
+
global: join113(homedir28(), ".gemini/skills")
|
|
74634
75060
|
}
|
|
74635
75061
|
};
|
|
74636
75062
|
function isSamePath3(path1, path22) {
|
|
@@ -74664,7 +75090,7 @@ async function cleanupLegacySkillPath(skillName, agent, global3) {
|
|
|
74664
75090
|
if (!legacy)
|
|
74665
75091
|
return;
|
|
74666
75092
|
const legacyBase = global3 ? legacy.global : legacy.project;
|
|
74667
|
-
const legacyPath =
|
|
75093
|
+
const legacyPath = join113(legacyBase, skillName);
|
|
74668
75094
|
if (!existsSync56(legacyPath))
|
|
74669
75095
|
return;
|
|
74670
75096
|
await rm14(legacyPath, { recursive: true, force: true });
|
|
@@ -74674,7 +75100,7 @@ async function cleanupLegacySkillPath(skillName, agent, global3) {
|
|
|
74674
75100
|
if (entry.skill === skillName && entry.agent === agent && entry.global === global3) {
|
|
74675
75101
|
if (entry.path === legacyPath) {
|
|
74676
75102
|
const newBase = global3 ? agents[agent].globalPath : agents[agent].projectPath;
|
|
74677
|
-
entry.path =
|
|
75103
|
+
entry.path = join113(newBase, skillName);
|
|
74678
75104
|
changed = true;
|
|
74679
75105
|
}
|
|
74680
75106
|
}
|
|
@@ -74759,7 +75185,7 @@ function getInstallPreview(skill, targetAgents, options2) {
|
|
|
74759
75185
|
// src/commands/skills/skills-uninstaller.ts
|
|
74760
75186
|
import { existsSync as existsSync57 } from "node:fs";
|
|
74761
75187
|
import { rm as rm15 } from "node:fs/promises";
|
|
74762
|
-
import { join as
|
|
75188
|
+
import { join as join114 } from "node:path";
|
|
74763
75189
|
async function uninstallSkillFromAgent(skill, agent, global3) {
|
|
74764
75190
|
const agentConfig = agents[agent];
|
|
74765
75191
|
const registry = await readRegistry();
|
|
@@ -74807,7 +75233,7 @@ async function uninstallSkillFromAgent(skill, agent, global3) {
|
|
|
74807
75233
|
async function forceUninstallSkill(skill, agent, global3) {
|
|
74808
75234
|
const agentConfig = agents[agent];
|
|
74809
75235
|
const basePath = global3 ? agentConfig.globalPath : agentConfig.projectPath;
|
|
74810
|
-
const path9 =
|
|
75236
|
+
const path9 = join114(basePath, skill);
|
|
74811
75237
|
if (!existsSync57(path9)) {
|
|
74812
75238
|
return {
|
|
74813
75239
|
skill,
|
|
@@ -75312,12 +75738,12 @@ init_logger();
|
|
|
75312
75738
|
// src/commands/telemetry/shared.ts
|
|
75313
75739
|
import { existsSync as existsSync58, readFileSync as readFileSync15, readdirSync as readdirSync6 } from "node:fs";
|
|
75314
75740
|
import { homedir as homedir29 } from "node:os";
|
|
75315
|
-
import { join as
|
|
75741
|
+
import { join as join115 } from "node:path";
|
|
75316
75742
|
init_token_store();
|
|
75317
|
-
var USER_CACHE_PATH =
|
|
75318
|
-
var EVENT_BUFFER_DIR =
|
|
75319
|
-
var RATE_STATE_PATH =
|
|
75320
|
-
var METADATA_PATH =
|
|
75743
|
+
var USER_CACHE_PATH = join115(homedir29(), ".claude", "sk-user.json");
|
|
75744
|
+
var EVENT_BUFFER_DIR = join115(homedir29(), ".claude", "sk-events");
|
|
75745
|
+
var RATE_STATE_PATH = join115(homedir29(), ".claude", "sk-rate-state.json");
|
|
75746
|
+
var METADATA_PATH = join115(homedir29(), ".claude", "metadata.json");
|
|
75321
75747
|
var TELEMETRY_HOOK_FIELD = "hooks.telemetry";
|
|
75322
75748
|
var TOKEN_PLACEHOLDER = "__INJECT_AT_RELEASE__";
|
|
75323
75749
|
function readUserCache() {
|
|
@@ -75404,7 +75830,7 @@ async function handleDisable() {
|
|
|
75404
75830
|
// src/commands/telemetry/phases/purge-local-handler.ts
|
|
75405
75831
|
init_logger();
|
|
75406
75832
|
import { existsSync as existsSync59, readdirSync as readdirSync7, unlinkSync as unlinkSync5 } from "node:fs";
|
|
75407
|
-
import { join as
|
|
75833
|
+
import { join as join116 } from "node:path";
|
|
75408
75834
|
function removeIfExists(path9) {
|
|
75409
75835
|
try {
|
|
75410
75836
|
if (!existsSync59(path9))
|
|
@@ -75424,7 +75850,7 @@ function removeBufferFiles() {
|
|
|
75424
75850
|
if (!file.endsWith(".jsonl"))
|
|
75425
75851
|
continue;
|
|
75426
75852
|
try {
|
|
75427
|
-
unlinkSync5(
|
|
75853
|
+
unlinkSync5(join116(EVENT_BUFFER_DIR, file));
|
|
75428
75854
|
count += 1;
|
|
75429
75855
|
} catch {}
|
|
75430
75856
|
}
|
|
@@ -75650,13 +76076,13 @@ async function detectInstallations() {
|
|
|
75650
76076
|
|
|
75651
76077
|
// src/commands/uninstall/removal-handler.ts
|
|
75652
76078
|
import { readdirSync as readdirSync9, rmSync as rmSync6 } from "node:fs";
|
|
75653
|
-
import { join as
|
|
76079
|
+
import { join as join118, resolve as resolve34, sep as sep10 } from "node:path";
|
|
75654
76080
|
init_logger();
|
|
75655
76081
|
var import_fs_extra38 = __toESM(require_lib(), 1);
|
|
75656
76082
|
|
|
75657
76083
|
// src/commands/uninstall/analysis-handler.ts
|
|
75658
76084
|
import { readdirSync as readdirSync8, rmSync as rmSync5 } from "node:fs";
|
|
75659
|
-
import { dirname as dirname36, join as
|
|
76085
|
+
import { dirname as dirname36, join as join117 } from "node:path";
|
|
75660
76086
|
init_logger();
|
|
75661
76087
|
var import_picocolors35 = __toESM(require_picocolors(), 1);
|
|
75662
76088
|
function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
|
|
@@ -75703,7 +76129,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
|
|
|
75703
76129
|
if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
|
|
75704
76130
|
const kitFiles = metadata.kits[kit].files || [];
|
|
75705
76131
|
for (const trackedFile of kitFiles) {
|
|
75706
|
-
const filePath =
|
|
76132
|
+
const filePath = join117(installation.path, trackedFile.path);
|
|
75707
76133
|
if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
|
|
75708
76134
|
result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
|
|
75709
76135
|
continue;
|
|
@@ -75733,7 +76159,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
|
|
|
75733
76159
|
return result;
|
|
75734
76160
|
}
|
|
75735
76161
|
for (const trackedFile of allTrackedFiles) {
|
|
75736
|
-
const filePath =
|
|
76162
|
+
const filePath = join117(installation.path, trackedFile.path);
|
|
75737
76163
|
const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
|
|
75738
76164
|
if (!ownershipResult.exists)
|
|
75739
76165
|
continue;
|
|
@@ -75830,7 +76256,7 @@ async function removeInstallations(installations, options2) {
|
|
|
75830
76256
|
let removedCount = 0;
|
|
75831
76257
|
let cleanedDirs = 0;
|
|
75832
76258
|
for (const item of analysis.toDelete) {
|
|
75833
|
-
const filePath =
|
|
76259
|
+
const filePath = join118(installation.path, item.path);
|
|
75834
76260
|
if (!await import_fs_extra38.pathExists(filePath))
|
|
75835
76261
|
continue;
|
|
75836
76262
|
if (!await isPathSafeToRemove(filePath, installation.path)) {
|
|
@@ -76032,7 +76458,7 @@ ${import_picocolors36.default.yellow("User modifications will be permanently del
|
|
|
76032
76458
|
// src/commands/update-cli.ts
|
|
76033
76459
|
init_takumi_config_manager();
|
|
76034
76460
|
import { exec as exec8, spawn as spawn2 } from "node:child_process";
|
|
76035
|
-
import { join as
|
|
76461
|
+
import { join as join119 } from "node:path";
|
|
76036
76462
|
import { promisify as promisify14 } from "node:util";
|
|
76037
76463
|
|
|
76038
76464
|
// src/domains/github/npm-registry.ts
|
|
@@ -76199,7 +76625,7 @@ var import_fs_extra39 = __toESM(require_lib(), 1);
|
|
|
76199
76625
|
// package.json
|
|
76200
76626
|
var package_default = {
|
|
76201
76627
|
name: "@sunasteriskrnd/takumi",
|
|
76202
|
-
version: "0.
|
|
76628
|
+
version: "0.7.0",
|
|
76203
76629
|
description: "CLI tool for bootstrapping and managing Takumi projects",
|
|
76204
76630
|
type: "module",
|
|
76205
76631
|
repository: {
|
|
@@ -76222,6 +76648,7 @@ var package_default = {
|
|
|
76222
76648
|
build: `bun build src/index.ts --outdir dist --target node --external @octokit/rest --external better-sqlite3 --external yauzl-promise --external @node-rs/crc32 && node -e "const fs=require('fs'),f='dist/index.js',c=fs.readFileSync(f,'utf-8');fs.writeFileSync(f,c.replace(/^#!.*\\n\\/\\/ @bun\\n/,''))"`,
|
|
76223
76649
|
"verify:package": "node scripts/prepublish-check.js",
|
|
76224
76650
|
test: "bun test",
|
|
76651
|
+
"test:ci": "NON_INTERACTIVE=true CI_SAFE_MODE=true bun test",
|
|
76225
76652
|
"test:integration": "CK_RUN_CLI_INTEGRATION=1 bun test tests/integration/cli.test.ts",
|
|
76226
76653
|
"test:watch": "bun test --watch",
|
|
76227
76654
|
"test:quick": "./scripts/dev-quick-start.sh test",
|
|
@@ -76383,7 +76810,7 @@ function selectKitForUpdate(params) {
|
|
|
76383
76810
|
};
|
|
76384
76811
|
}
|
|
76385
76812
|
async function readMetadataFile(claudeDir) {
|
|
76386
|
-
const metadataPath =
|
|
76813
|
+
const metadataPath = join119(claudeDir, "metadata.json");
|
|
76387
76814
|
try {
|
|
76388
76815
|
if (!await import_fs_extra39.pathExists(metadataPath)) {
|
|
76389
76816
|
return null;
|
|
@@ -76884,7 +77311,7 @@ init_logger();
|
|
|
76884
77311
|
import { existsSync as existsSync65 } from "node:fs";
|
|
76885
77312
|
import { rm as rm16 } from "node:fs/promises";
|
|
76886
77313
|
import { homedir as homedir31 } from "node:os";
|
|
76887
|
-
import { join as
|
|
77314
|
+
import { join as join126 } from "node:path";
|
|
76888
77315
|
var import_picocolors38 = __toESM(require_picocolors(), 1);
|
|
76889
77316
|
|
|
76890
77317
|
// src/commands/watch/phases/implementation-runner.ts
|
|
@@ -77394,7 +77821,7 @@ function spawnAndCollect3(command, args) {
|
|
|
77394
77821
|
|
|
77395
77822
|
// src/commands/watch/phases/issue-processor.ts
|
|
77396
77823
|
import { mkdir as mkdir32, writeFile as writeFile37 } from "node:fs/promises";
|
|
77397
|
-
import { join as
|
|
77824
|
+
import { join as join122 } from "node:path";
|
|
77398
77825
|
|
|
77399
77826
|
// src/commands/watch/phases/approval-detector.ts
|
|
77400
77827
|
init_logger();
|
|
@@ -77769,9 +78196,9 @@ async function checkAwaitingApproval(state, setup, options2, watchLog, projectDi
|
|
|
77769
78196
|
|
|
77770
78197
|
// src/commands/watch/phases/plan-dir-finder.ts
|
|
77771
78198
|
import { readdir as readdir32, stat as stat14 } from "node:fs/promises";
|
|
77772
|
-
import { join as
|
|
78199
|
+
import { join as join121 } from "node:path";
|
|
77773
78200
|
async function findRecentPlanDir(cwd2, issueNumber, watchLog) {
|
|
77774
|
-
const plansRoot =
|
|
78201
|
+
const plansRoot = join121(cwd2, "plans");
|
|
77775
78202
|
try {
|
|
77776
78203
|
const entries = await readdir32(plansRoot);
|
|
77777
78204
|
const tenMinAgo = Date.now() - 10 * 60 * 1000;
|
|
@@ -77780,14 +78207,14 @@ async function findRecentPlanDir(cwd2, issueNumber, watchLog) {
|
|
|
77780
78207
|
for (const entry of entries) {
|
|
77781
78208
|
if (entry === "watch" || entry === "reports" || entry === "visuals")
|
|
77782
78209
|
continue;
|
|
77783
|
-
const dirPath =
|
|
78210
|
+
const dirPath = join121(plansRoot, entry);
|
|
77784
78211
|
const dirStat = await stat14(dirPath);
|
|
77785
78212
|
if (!dirStat.isDirectory())
|
|
77786
78213
|
continue;
|
|
77787
78214
|
if (dirStat.mtimeMs < tenMinAgo)
|
|
77788
78215
|
continue;
|
|
77789
78216
|
try {
|
|
77790
|
-
await stat14(
|
|
78217
|
+
await stat14(join121(dirPath, "plan.md"));
|
|
77791
78218
|
} catch {
|
|
77792
78219
|
continue;
|
|
77793
78220
|
}
|
|
@@ -78018,13 +78445,13 @@ async function handlePlanGeneration(issue, state, config, setup, options2, watch
|
|
|
78018
78445
|
stats.plansCreated++;
|
|
78019
78446
|
const detectedPlanDir = await findRecentPlanDir(projectDir, issue.number, watchLog);
|
|
78020
78447
|
if (detectedPlanDir) {
|
|
78021
|
-
state.activeIssues[numStr].planPath =
|
|
78448
|
+
state.activeIssues[numStr].planPath = join122(detectedPlanDir, "plan.md");
|
|
78022
78449
|
watchLog.info(`Plan directory detected: ${detectedPlanDir}`);
|
|
78023
78450
|
} else {
|
|
78024
78451
|
try {
|
|
78025
|
-
const planDir =
|
|
78452
|
+
const planDir = join122(projectDir, "plans", "watch");
|
|
78026
78453
|
await mkdir32(planDir, { recursive: true });
|
|
78027
|
-
const planFilePath =
|
|
78454
|
+
const planFilePath = join122(planDir, `issue-${issue.number}-plan.md`);
|
|
78028
78455
|
await writeFile37(planFilePath, planResult.planText, "utf-8");
|
|
78029
78456
|
state.activeIssues[numStr].planPath = planFilePath;
|
|
78030
78457
|
watchLog.info(`Plan saved (fallback) to ${planFilePath}`);
|
|
@@ -78329,18 +78756,18 @@ init_logger();
|
|
|
78329
78756
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
78330
78757
|
import { existsSync as existsSync62 } from "node:fs";
|
|
78331
78758
|
import { readdir as readdir33, stat as stat15 } from "node:fs/promises";
|
|
78332
|
-
import { join as
|
|
78759
|
+
import { join as join123 } from "node:path";
|
|
78333
78760
|
async function scanForRepos(parentDir) {
|
|
78334
78761
|
const repos = [];
|
|
78335
78762
|
const entries = await readdir33(parentDir);
|
|
78336
78763
|
for (const entry of entries) {
|
|
78337
78764
|
if (entry.startsWith("."))
|
|
78338
78765
|
continue;
|
|
78339
|
-
const fullPath =
|
|
78766
|
+
const fullPath = join123(parentDir, entry);
|
|
78340
78767
|
const entryStat = await stat15(fullPath);
|
|
78341
78768
|
if (!entryStat.isDirectory())
|
|
78342
78769
|
continue;
|
|
78343
|
-
const gitDir =
|
|
78770
|
+
const gitDir = join123(fullPath, ".git");
|
|
78344
78771
|
if (!existsSync62(gitDir))
|
|
78345
78772
|
continue;
|
|
78346
78773
|
const result = spawnSync5("gh", ["repo", "view", "--json", "owner,name"], {
|
|
@@ -78367,7 +78794,7 @@ init_logger();
|
|
|
78367
78794
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
78368
78795
|
import { existsSync as existsSync63 } from "node:fs";
|
|
78369
78796
|
import { homedir as homedir30 } from "node:os";
|
|
78370
|
-
import { join as
|
|
78797
|
+
import { join as join124 } from "node:path";
|
|
78371
78798
|
async function validateSetup(cwd2) {
|
|
78372
78799
|
const workDir = cwd2 ?? process.cwd();
|
|
78373
78800
|
const ghVersion = spawnSync6("gh", ["--version"], { encoding: "utf-8", timeout: 1e4 });
|
|
@@ -78398,7 +78825,7 @@ Run this command from a directory with a GitHub remote.`);
|
|
|
78398
78825
|
} catch {
|
|
78399
78826
|
throw new Error(`Failed to parse repository info: ${ghRepo.stdout}`);
|
|
78400
78827
|
}
|
|
78401
|
-
const skillsPath =
|
|
78828
|
+
const skillsPath = join124(homedir30(), ".claude", "skills");
|
|
78402
78829
|
const skillsAvailable = existsSync63(skillsPath);
|
|
78403
78830
|
if (!skillsAvailable) {
|
|
78404
78831
|
logger.warning(`Takumi Engineer skills not found at ${skillsPath}`);
|
|
@@ -78417,7 +78844,7 @@ init_path_resolver();
|
|
|
78417
78844
|
import { createWriteStream as createWriteStream4, statSync as statSync6 } from "node:fs";
|
|
78418
78845
|
import { existsSync as existsSync64 } from "node:fs";
|
|
78419
78846
|
import { mkdir as mkdir34, rename as rename9 } from "node:fs/promises";
|
|
78420
|
-
import { join as
|
|
78847
|
+
import { join as join125 } from "node:path";
|
|
78421
78848
|
|
|
78422
78849
|
class WatchLogger {
|
|
78423
78850
|
logStream = null;
|
|
@@ -78425,7 +78852,7 @@ class WatchLogger {
|
|
|
78425
78852
|
logPath = null;
|
|
78426
78853
|
maxBytes;
|
|
78427
78854
|
constructor(logDir, maxBytes = 0) {
|
|
78428
|
-
this.logDir = logDir ??
|
|
78855
|
+
this.logDir = logDir ?? join125(PathResolver.getTakumiDir(), "logs");
|
|
78429
78856
|
this.maxBytes = maxBytes;
|
|
78430
78857
|
}
|
|
78431
78858
|
async init() {
|
|
@@ -78434,7 +78861,7 @@ class WatchLogger {
|
|
|
78434
78861
|
await mkdir34(this.logDir, { recursive: true });
|
|
78435
78862
|
}
|
|
78436
78863
|
const dateStr = formatDate(new Date);
|
|
78437
|
-
this.logPath =
|
|
78864
|
+
this.logPath = join125(this.logDir, `watch-${dateStr}.log`);
|
|
78438
78865
|
this.logStream = createWriteStream4(this.logPath, { flags: "a", mode: 384 });
|
|
78439
78866
|
} catch (error) {
|
|
78440
78867
|
logger.warning(`Cannot create watch log file: ${error instanceof Error ? error.message : "Unknown"}`);
|
|
@@ -78614,7 +79041,7 @@ async function watchCommand(options2) {
|
|
|
78614
79041
|
}
|
|
78615
79042
|
async function discoverRepos(options2, watchLog) {
|
|
78616
79043
|
const cwd2 = process.cwd();
|
|
78617
|
-
const isGitRepo = existsSync65(
|
|
79044
|
+
const isGitRepo = existsSync65(join126(cwd2, ".git"));
|
|
78618
79045
|
if (options2.force) {
|
|
78619
79046
|
await forceRemoveLock(watchLog);
|
|
78620
79047
|
}
|
|
@@ -78684,7 +79111,7 @@ async function resetState(state, projectDir, watchLog) {
|
|
|
78684
79111
|
watchLog.info(`Watch state reset (--force) for ${projectDir}`);
|
|
78685
79112
|
}
|
|
78686
79113
|
async function forceRemoveLock(watchLog) {
|
|
78687
|
-
const lockPath =
|
|
79114
|
+
const lockPath = join126(homedir31(), ".sunagentkit", "locks", `${LOCK_NAME}.lock`);
|
|
78688
79115
|
try {
|
|
78689
79116
|
await rm16(lockPath, { recursive: true, force: true });
|
|
78690
79117
|
watchLog.info("Removed existing lock file (--force)");
|
|
@@ -78728,13 +79155,13 @@ function sleep2(ms2) {
|
|
|
78728
79155
|
// src/cli/command-registry.ts
|
|
78729
79156
|
init_logger();
|
|
78730
79157
|
function registerCommands(cli) {
|
|
78731
|
-
cli.command("new", "Bootstrap a new Takumi project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to
|
|
79158
|
+
cli.command("new", "Bootstrap a new Takumi project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit(s) to install (engineer, extras). Repeat the flag to install multiple kits, e.g. --kit engineer --kit extras.").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--force", "Overwrite existing files without confirmation").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--opencode", "Install OpenCode CLI package (non-interactive mode)").option("--gemini", "Install Google Gemini CLI package (non-interactive mode)").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /sk: prefix to all slash commands by moving them to commands/sk/ subdirectory").option("--beta", "Show beta versions in selection prompt").option("--refresh", "Bypass release cache to fetch latest versions from GitHub").option("--docs-dir <name>", "Custom docs folder name (default: docs)").option("--plans-dir <name>", "Custom plans folder name (default: plans)").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("--use-git", "Use git clone instead of GitHub API (uses SSH/HTTPS credentials)").option("--archive <path>", "Use local archive file instead of downloading (zip/tar.gz)").option("--kit-path <path>", "Use local kit directory instead of downloading").option("--local", "Use local monorepo as kit source (auto-detects from CLI location)").option("--use-gh", "Force GitHub release source (bypass Worker R2 proxy; default uses Worker)").action(async (options2) => {
|
|
78732
79159
|
if (options2.exclude && !Array.isArray(options2.exclude)) {
|
|
78733
79160
|
options2.exclude = [options2.exclude];
|
|
78734
79161
|
}
|
|
78735
79162
|
await newCommand(options2);
|
|
78736
79163
|
});
|
|
78737
|
-
cli.command("init", "Initialize or update Takumi project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to
|
|
79164
|
+
cli.command("init", "Initialize or update Takumi project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit(s) to install (engineer, extras). Repeat the flag to install multiple kits, e.g. --kit engineer --kit extras.").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--only <pattern>", "Include only files matching glob pattern (can be used multiple times)").option("-g, --global", "Use platform-specific user configuration directory").option("--fresh", "Full reset: remove SK files, replace settings.json and CLAUDE.md, reinstall from scratch").option("--force", "Force reinstall even if already at latest version (use with --yes; re-onboards missing files without full reset)").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /sk: prefix to all slash commands by moving them to commands/sk/ subdirectory").option("--beta", "Show beta versions in selection prompt").option("--refresh", "Bypass release cache to fetch latest versions from GitHub").option("--dry-run", "Preview changes without applying them (requires --prefix)").option("--force-overwrite", "Override ownership protections and delete user-modified files (requires --prefix)").option("--force-overwrite-settings", "Fully replace settings.json instead of selective merge (destroys user customizations)").option("--skip-setup", "Skip interactive configuration wizard").option("--docs-dir <name>", "Custom docs folder name (default: docs)").option("--plans-dir <name>", "Custom plans folder name (default: plans)").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("--sync", "Sync config files from upstream with interactive hunk-by-hunk merge").option("--use-git", "Use git clone instead of GitHub API (uses SSH/HTTPS credentials)").option("--archive <path>", "Use local archive file instead of downloading (zip/tar.gz)").option("--kit-path <path>", "Use local kit directory instead of downloading").option("--local", "Use local monorepo as kit source (auto-detects from CLI location)").option("--use-gh", "Force GitHub release source (bypass Worker R2 proxy; default uses Worker)").option("-a, --agent <agents...>", "Target agents (claude-code, codex). Default: claude-code").action(async (options2) => {
|
|
78738
79165
|
if (options2.exclude && !Array.isArray(options2.exclude)) {
|
|
78739
79166
|
options2.exclude = [options2.exclude];
|
|
78740
79167
|
}
|
|
@@ -78747,6 +79174,9 @@ function registerCommands(cli) {
|
|
|
78747
79174
|
if (options2.agent) {
|
|
78748
79175
|
options2.agents = options2.agent;
|
|
78749
79176
|
}
|
|
79177
|
+
if (options2.kit && Array.isArray(options2.kit)) {
|
|
79178
|
+
options2.kit = options2.kit.join(",");
|
|
79179
|
+
}
|
|
78750
79180
|
await initCommand(options2);
|
|
78751
79181
|
});
|
|
78752
79182
|
cli.command("update", "Update Takumi CLI to the latest version").option("-r, --release <version>", "Update to a specific version").option("--check", "Check for updates without installing").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("-d, --dev", "Update to the latest dev version").option("--beta", "Alias for --dev (deprecated)").option("--registry <url>", "Custom npm registry URL").option("--kit <kit>", "[DEPRECATED] Use 'takumi init --kit <kit>' instead").option("-g, --global", "[DEPRECATED] Use 'takumi init --global' instead").action(async (options2) => {
|
|
@@ -78886,7 +79316,7 @@ function registerCommands(cli) {
|
|
|
78886
79316
|
|
|
78887
79317
|
// src/cli/version-display.ts
|
|
78888
79318
|
import { existsSync as existsSync77, readFileSync as readFileSync20 } from "node:fs";
|
|
78889
|
-
import { join as
|
|
79319
|
+
import { join as join138 } from "node:path";
|
|
78890
79320
|
init_help_banner();
|
|
78891
79321
|
// src/domains/versioning/checking/kit-version-checker.ts
|
|
78892
79322
|
init_github_client();
|
|
@@ -78898,14 +79328,14 @@ init_logger();
|
|
|
78898
79328
|
init_path_resolver();
|
|
78899
79329
|
import { existsSync as existsSync76 } from "node:fs";
|
|
78900
79330
|
import { mkdir as mkdir35, readFile as readFile52, writeFile as writeFile40 } from "node:fs/promises";
|
|
78901
|
-
import { join as
|
|
79331
|
+
import { join as join137 } from "node:path";
|
|
78902
79332
|
|
|
78903
79333
|
class VersionCacheManager {
|
|
78904
79334
|
static CACHE_FILENAME = "version-check.json";
|
|
78905
79335
|
static CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
78906
79336
|
static getCacheFile() {
|
|
78907
79337
|
const cacheDir = PathResolver.getCacheDir(false);
|
|
78908
|
-
return
|
|
79338
|
+
return join137(cacheDir, VersionCacheManager.CACHE_FILENAME);
|
|
78909
79339
|
}
|
|
78910
79340
|
static async load() {
|
|
78911
79341
|
const cacheFile = VersionCacheManager.getCacheFile();
|
|
@@ -79198,8 +79628,8 @@ async function displayVersion() {
|
|
|
79198
79628
|
const localSubdir = PROVIDER_LOCAL_SUBDIRS[provider];
|
|
79199
79629
|
if (!localSubdir)
|
|
79200
79630
|
continue;
|
|
79201
|
-
const localMeta =
|
|
79202
|
-
if (localMeta ===
|
|
79631
|
+
const localMeta = join138(process.cwd(), localSubdir, "metadata.json");
|
|
79632
|
+
if (localMeta === join138(inst.globalRoot(), "metadata.json"))
|
|
79203
79633
|
continue;
|
|
79204
79634
|
if (!existsSync77(localMeta))
|
|
79205
79635
|
continue;
|
|
@@ -79224,7 +79654,7 @@ async function displayVersion() {
|
|
|
79224
79654
|
}
|
|
79225
79655
|
const singleInstall = detectedGlobals.length === 1 && detectedGlobals[0]?.provider === "claude-code";
|
|
79226
79656
|
for (const { provider, path: installPath } of detectedGlobals) {
|
|
79227
|
-
const metadataPath =
|
|
79657
|
+
const metadataPath = join138(installPath, "metadata.json");
|
|
79228
79658
|
if (existsSync77(metadataPath)) {
|
|
79229
79659
|
try {
|
|
79230
79660
|
const rawMetadata = JSON.parse(readFileSync20(metadataPath, "utf-8"));
|