@sunasteriskrnd/takumi 1.0.0-dev.46 → 1.0.0-dev.48

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.
Files changed (2) hide show
  1. package/dist/index.js +178 -114
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -19815,7 +19815,7 @@ var package_default;
19815
19815
  var init_package = __esm(() => {
19816
19816
  package_default = {
19817
19817
  name: "@sunasteriskrnd/takumi",
19818
- version: "1.0.0-dev.46",
19818
+ version: "1.0.0-dev.48",
19819
19819
  description: "CLI tool for bootstrapping and managing Takumi projects",
19820
19820
  type: "module",
19821
19821
  repository: {
@@ -27299,6 +27299,48 @@ var require_ignore = __commonJS((exports, module) => {
27299
27299
  }
27300
27300
  });
27301
27301
 
27302
+ // src/domains/migration/release-manifest.ts
27303
+ import { join as join11 } from "node:path";
27304
+ function normalizeClaudeReleaseManifestPath(relativePath) {
27305
+ const normalized = relativePath.replace(/\\/g, "/").replace(/^\.\//, "");
27306
+ return normalized.startsWith(".claude/") ? normalized.slice(".claude/".length) : normalized;
27307
+ }
27308
+
27309
+ class ReleaseManifestLoader {
27310
+ static async load(extractDir) {
27311
+ const manifestPath = join11(extractDir, "release-manifest.json");
27312
+ try {
27313
+ const content = await import_fs_extra8.readFile(manifestPath, "utf-8");
27314
+ const parsed = JSON.parse(content);
27315
+ return ReleaseManifestSchema.parse(parsed);
27316
+ } catch (error) {
27317
+ logger.debug(`Release manifest not found or invalid: ${error}`);
27318
+ return null;
27319
+ }
27320
+ }
27321
+ static findFile(manifest, relativePath) {
27322
+ const normalizedPath = normalizeClaudeReleaseManifestPath(relativePath);
27323
+ return manifest.files.find((file) => normalizeClaudeReleaseManifestPath(file.path) === normalizedPath);
27324
+ }
27325
+ }
27326
+ var import_fs_extra8, ReleaseManifestFileSchema, ReleaseManifestSchema;
27327
+ var init_release_manifest = __esm(() => {
27328
+ init_logger();
27329
+ init_zod();
27330
+ import_fs_extra8 = __toESM(require_lib(), 1);
27331
+ ReleaseManifestFileSchema = exports_external.object({
27332
+ path: exports_external.string(),
27333
+ checksum: exports_external.string().regex(/^[a-f0-9]{64}$/),
27334
+ size: exports_external.number(),
27335
+ lastModified: exports_external.string().datetime({ offset: true }).optional()
27336
+ });
27337
+ ReleaseManifestSchema = exports_external.object({
27338
+ version: exports_external.string(),
27339
+ generatedAt: exports_external.string(),
27340
+ files: exports_external.array(ReleaseManifestFileSchema)
27341
+ });
27342
+ });
27343
+
27302
27344
  // node_modules/semver/internal/constants.js
27303
27345
  var require_constants4 = __commonJS((exports, module) => {
27304
27346
  var SEMVER_SPEC_VERSION = "2.0.0";
@@ -29095,7 +29137,7 @@ class SelectiveMerger {
29095
29137
  this.manifestMap = new Map;
29096
29138
  if (manifest) {
29097
29139
  for (const file of manifest.files) {
29098
- this.manifestMap.set(file.path, file);
29140
+ this.manifestMap.set(normalizeClaudeReleaseManifestPath(file.path), file);
29099
29141
  }
29100
29142
  }
29101
29143
  }
@@ -29104,25 +29146,26 @@ class SelectiveMerger {
29104
29146
  this.installingKit = installingKit;
29105
29147
  }
29106
29148
  async shouldCopyFile(destPath, relativePath) {
29149
+ const providerRelativePath = normalizeClaudeReleaseManifestPath(relativePath);
29107
29150
  let destStat;
29108
29151
  try {
29109
29152
  destStat = await stat2(destPath);
29110
29153
  } catch {
29111
29154
  if (this.claudeDir && this.installingKit) {
29112
- const installed = await findFileInInstalledKits(this.claudeDir, relativePath, this.installingKit);
29155
+ const installed = await findFileInInstalledKits(this.claudeDir, providerRelativePath, this.installingKit);
29113
29156
  if (installed.exists) {
29114
29157
  logger.debug(`File ${relativePath} tracked by ${installed.ownerKit} but missing on disk`);
29115
29158
  }
29116
29159
  }
29117
29160
  return { changed: true, reason: "new" };
29118
29161
  }
29119
- const manifestEntry = this.manifestMap.get(relativePath);
29162
+ const manifestEntry = this.manifestMap.get(providerRelativePath);
29120
29163
  if (!manifestEntry) {
29121
29164
  logger.debug(`No manifest entry for ${relativePath}, will copy`);
29122
29165
  return { changed: true, reason: "new" };
29123
29166
  }
29124
29167
  if (this.claudeDir && this.installingKit) {
29125
- const installed = await findFileInInstalledKits(this.claudeDir, relativePath, this.installingKit);
29168
+ const installed = await findFileInInstalledKits(this.claudeDir, providerRelativePath, this.installingKit);
29126
29169
  if (installed.exists && installed.checksum && installed.ownerKit) {
29127
29170
  if (installed.checksum === manifestEntry.checksum) {
29128
29171
  logger.debug(`Shared identical: ${relativePath} (owned by ${installed.ownerKit})`);
@@ -29137,7 +29180,7 @@ class SelectiveMerger {
29137
29180
  const incomingTimestamp = manifestEntry.lastModified ?? null;
29138
29181
  const existingTimestamp = installed.sourceTimestamp;
29139
29182
  const conflictBase = {
29140
- relativePath,
29183
+ relativePath: providerRelativePath,
29141
29184
  incomingKit: this.installingKit,
29142
29185
  existingKit: installed.ownerKit,
29143
29186
  incomingTimestamp,
@@ -29253,6 +29296,7 @@ class SelectiveMerger {
29253
29296
  }
29254
29297
  var import_semver;
29255
29298
  var init_selective_merger = __esm(() => {
29299
+ init_release_manifest();
29256
29300
  init_manifest_reader();
29257
29301
  init_ownership_checker();
29258
29302
  init_logger();
@@ -29261,7 +29305,7 @@ var init_selective_merger = __esm(() => {
29261
29305
 
29262
29306
  // src/domains/installation/merger/file-scanner.ts
29263
29307
  import { relative as relative3 } from "node:path";
29264
- import { join as join11 } from "node:path";
29308
+ import { join as join12 } from "node:path";
29265
29309
 
29266
29310
  class FileScanner {
29267
29311
  includeMatchers = [];
@@ -29283,12 +29327,12 @@ class FileScanner {
29283
29327
  }
29284
29328
  async getFiles(dir, baseDir = dir) {
29285
29329
  const files = [];
29286
- const entries = await import_fs_extra8.readdir(dir, { encoding: "utf8" });
29330
+ const entries = await import_fs_extra9.readdir(dir, { encoding: "utf8" });
29287
29331
  for (const entry of entries) {
29288
- const fullPath = join11(dir, entry);
29332
+ const fullPath = join12(dir, entry);
29289
29333
  const relativePath = relative3(baseDir, fullPath);
29290
29334
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
29291
- const stats = await import_fs_extra8.lstat(fullPath);
29335
+ const stats = await import_fs_extra9.lstat(fullPath);
29292
29336
  if (stats.isSymbolicLink()) {
29293
29337
  logger.warning(`Skipping symbolic link: ${normalizedRelativePath}`);
29294
29338
  continue;
@@ -29316,10 +29360,10 @@ class FileScanner {
29316
29360
  return files;
29317
29361
  }
29318
29362
  }
29319
- var import_fs_extra8, import_ignore, import_picomatch2;
29363
+ var import_fs_extra9, import_ignore, import_picomatch2;
29320
29364
  var init_file_scanner = __esm(() => {
29321
29365
  init_logger();
29322
- import_fs_extra8 = __toESM(require_lib(), 1);
29366
+ import_fs_extra9 = __toESM(require_lib(), 1);
29323
29367
  import_ignore = __toESM(require_ignore(), 1);
29324
29368
  import_picomatch2 = __toESM(require_picomatch2(), 1);
29325
29369
  });
@@ -29475,8 +29519,8 @@ var init_shared = __esm(() => {
29475
29519
 
29476
29520
  // src/domains/config/installed-settings-tracker.ts
29477
29521
  import { existsSync as existsSync5 } from "node:fs";
29478
- import { mkdir, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
29479
- import { dirname as dirname2, join as join12 } from "node:path";
29522
+ import { mkdir, readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
29523
+ import { dirname as dirname2, join as join13 } from "node:path";
29480
29524
 
29481
29525
  class InstalledSettingsTracker {
29482
29526
  projectDir;
@@ -29489,9 +29533,9 @@ class InstalledSettingsTracker {
29489
29533
  }
29490
29534
  getCkJsonPath() {
29491
29535
  if (this.isGlobal) {
29492
- return join12(this.projectDir, TAKUMI_JSON_FILE);
29536
+ return join13(this.projectDir, TAKUMI_JSON_FILE);
29493
29537
  }
29494
- return join12(this.projectDir, ".claude", TAKUMI_JSON_FILE);
29538
+ return join13(this.projectDir, ".claude", TAKUMI_JSON_FILE);
29495
29539
  }
29496
29540
  async loadInstalledSettings() {
29497
29541
  const ckJsonPath = this.getCkJsonPath();
@@ -29499,7 +29543,7 @@ class InstalledSettingsTracker {
29499
29543
  return { hooks: [], mcpServers: [] };
29500
29544
  }
29501
29545
  try {
29502
- const content = await readFile6(ckJsonPath, "utf-8");
29546
+ const content = await readFile7(ckJsonPath, "utf-8");
29503
29547
  const data = JSON.parse(content);
29504
29548
  const installed = data.kits?.[this.kitName]?.installedSettings;
29505
29549
  if (installed) {
@@ -29516,7 +29560,7 @@ class InstalledSettingsTracker {
29516
29560
  try {
29517
29561
  let data = {};
29518
29562
  if (existsSync5(ckJsonPath)) {
29519
- const content = await readFile6(ckJsonPath, "utf-8");
29563
+ const content = await readFile7(ckJsonPath, "utf-8");
29520
29564
  data = JSON.parse(content);
29521
29565
  }
29522
29566
  if (!data.kits) {
@@ -29965,16 +30009,16 @@ var init_merge_engine = __esm(() => {
29965
30009
 
29966
30010
  // src/domains/config/merger/file-io.ts
29967
30011
  import { randomUUID } from "node:crypto";
29968
- import { dirname as dirname3, join as join13 } from "node:path";
30012
+ import { dirname as dirname3, join as join14 } from "node:path";
29969
30013
  function stripBOM(content) {
29970
30014
  return content.charCodeAt(0) === 65279 ? content.slice(1) : content;
29971
30015
  }
29972
30016
  async function readSettingsFile(filePath) {
29973
30017
  try {
29974
- if (!await import_fs_extra9.pathExists(filePath)) {
30018
+ if (!await import_fs_extra10.pathExists(filePath)) {
29975
30019
  return null;
29976
30020
  }
29977
- const rawContent = await import_fs_extra9.readFile(filePath, "utf-8");
30021
+ const rawContent = await import_fs_extra10.readFile(filePath, "utf-8");
29978
30022
  const content = stripBOM(rawContent);
29979
30023
  const parsed = JSON.parse(content);
29980
30024
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -29989,14 +30033,14 @@ async function readSettingsFile(filePath) {
29989
30033
  }
29990
30034
  async function atomicWriteFile(filePath, content) {
29991
30035
  const dir = dirname3(filePath);
29992
- const tempPath = join13(dir, `.settings-${randomUUID()}.tmp`);
30036
+ const tempPath = join14(dir, `.settings-${randomUUID()}.tmp`);
29993
30037
  try {
29994
- await import_fs_extra9.writeFile(tempPath, content, "utf-8");
29995
- await import_fs_extra9.rename(tempPath, filePath);
30038
+ await import_fs_extra10.writeFile(tempPath, content, "utf-8");
30039
+ await import_fs_extra10.rename(tempPath, filePath);
29996
30040
  } catch (error) {
29997
30041
  try {
29998
- if (await import_fs_extra9.pathExists(tempPath)) {
29999
- await import_fs_extra9.unlink(tempPath);
30042
+ if (await import_fs_extra10.pathExists(tempPath)) {
30043
+ await import_fs_extra10.unlink(tempPath);
30000
30044
  }
30001
30045
  } catch {}
30002
30046
  throw error;
@@ -30006,10 +30050,10 @@ async function writeSettingsFile(filePath, settings) {
30006
30050
  const content = JSON.stringify(settings, null, 2);
30007
30051
  await atomicWriteFile(filePath, content);
30008
30052
  }
30009
- var import_fs_extra9;
30053
+ var import_fs_extra10;
30010
30054
  var init_file_io = __esm(() => {
30011
30055
  init_logger();
30012
- import_fs_extra9 = __toESM(require_lib(), 1);
30056
+ import_fs_extra10 = __toESM(require_lib(), 1);
30013
30057
  });
30014
30058
 
30015
30059
  // src/domains/config/merger/index.ts
@@ -30079,7 +30123,7 @@ class SettingsProcessor {
30079
30123
  }
30080
30124
  async processSettingsJson(sourceFile, destFile) {
30081
30125
  try {
30082
- const sourceContent = await import_fs_extra10.readFile(sourceFile, "utf-8");
30126
+ const sourceContent = await import_fs_extra11.readFile(sourceFile, "utf-8");
30083
30127
  let transformedSource = sourceContent;
30084
30128
  if (this.isGlobal) {
30085
30129
  const homeVar = '"$HOME"';
@@ -30093,7 +30137,7 @@ class SettingsProcessor {
30093
30137
  logger.debug("Transformed paths to $CLAUDE_PROJECT_DIR/.claude/ in settings.json for local installation");
30094
30138
  }
30095
30139
  }
30096
- const destExists = await import_fs_extra10.pathExists(destFile);
30140
+ const destExists = await import_fs_extra11.pathExists(destFile);
30097
30141
  if (destExists && !this.forceOverwriteSettings) {
30098
30142
  await this.selectiveMergeSettings(transformedSource, destFile);
30099
30143
  } else {
@@ -30114,13 +30158,13 @@ class SettingsProcessor {
30114
30158
  }
30115
30159
  } catch {
30116
30160
  const formattedContent = this.formatJsonContent(transformedSource);
30117
- await import_fs_extra10.writeFile(destFile, formattedContent, "utf-8");
30161
+ await import_fs_extra11.writeFile(destFile, formattedContent, "utf-8");
30118
30162
  }
30119
30163
  await this.injectTeamHooksIfSupported(destFile);
30120
30164
  }
30121
30165
  } catch (error) {
30122
30166
  logger.error(`Failed to process settings.json: ${error}`);
30123
- await import_fs_extra10.copy(sourceFile, destFile, { overwrite: true });
30167
+ await import_fs_extra11.copy(sourceFile, destFile, { overwrite: true });
30124
30168
  }
30125
30169
  }
30126
30170
  async selectiveMergeSettings(transformedSourceContent, destFile) {
@@ -30130,7 +30174,7 @@ class SettingsProcessor {
30130
30174
  } catch {
30131
30175
  logger.warning("Failed to parse source settings.json, falling back to overwrite");
30132
30176
  const formattedContent = this.formatJsonContent(transformedSourceContent);
30133
- await import_fs_extra10.writeFile(destFile, formattedContent, "utf-8");
30177
+ await import_fs_extra11.writeFile(destFile, formattedContent, "utf-8");
30134
30178
  return;
30135
30179
  }
30136
30180
  let destSettings;
@@ -30322,7 +30366,7 @@ class SettingsProcessor {
30322
30366
  }
30323
30367
  async readAndNormalizeGlobalSettings(destFile) {
30324
30368
  try {
30325
- const content = await import_fs_extra10.readFile(destFile, "utf-8");
30369
+ const content = await import_fs_extra11.readFile(destFile, "utf-8");
30326
30370
  if (!content.trim())
30327
30371
  return null;
30328
30372
  const homeVar = "$HOME";
@@ -30527,17 +30571,17 @@ class SettingsProcessor {
30527
30571
  }
30528
30572
  }
30529
30573
  }
30530
- var import_fs_extra10, import_semver2;
30574
+ var import_fs_extra11, import_semver2;
30531
30575
  var init_settings_processor = __esm(() => {
30532
30576
  init_installed_settings_tracker();
30533
30577
  init_settings_merger();
30534
30578
  init_logger();
30535
- import_fs_extra10 = __toESM(require_lib(), 1);
30579
+ import_fs_extra11 = __toESM(require_lib(), 1);
30536
30580
  import_semver2 = __toESM(require_semver2(), 1);
30537
30581
  });
30538
30582
 
30539
30583
  // src/domains/installation/merger/copy-executor.ts
30540
- import { dirname as dirname4, join as join14, relative as relative4 } from "node:path";
30584
+ import { dirname as dirname4, join as join15, relative as relative4 } from "node:path";
30541
30585
  async function withRetry(fn, retries = 3) {
30542
30586
  for (let i = 0;i < retries; i++) {
30543
30587
  try {
@@ -30612,8 +30656,8 @@ class CopyExecutor {
30612
30656
  for (const file of files) {
30613
30657
  const relativePath = relative4(sourceDir, file);
30614
30658
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
30615
- const destPath = join14(destDir, relativePath);
30616
- if (await import_fs_extra11.pathExists(destPath)) {
30659
+ const destPath = join15(destDir, relativePath);
30660
+ if (await import_fs_extra12.pathExists(destPath)) {
30617
30661
  if (this.fileScanner.shouldNeverCopy(normalizedRelativePath)) {
30618
30662
  logger.debug(`Security-sensitive file exists but won't be overwritten: ${normalizedRelativePath}`);
30619
30663
  continue;
@@ -30634,14 +30678,14 @@ class CopyExecutor {
30634
30678
  for (const file of files) {
30635
30679
  const relativePath = relative4(sourceDir, file);
30636
30680
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
30637
- const destPath = join14(destDir, relativePath);
30681
+ const destPath = join15(destDir, relativePath);
30638
30682
  if (this.fileScanner.shouldNeverCopy(normalizedRelativePath)) {
30639
30683
  logger.debug(`Skipping security-sensitive file: ${normalizedRelativePath}`);
30640
30684
  skippedCount++;
30641
30685
  continue;
30642
30686
  }
30643
30687
  if (this.userConfigChecker.ignores(normalizedRelativePath)) {
30644
- const fileExists = await import_fs_extra11.pathExists(destPath);
30688
+ const fileExists = await import_fs_extra12.pathExists(destPath);
30645
30689
  if (fileExists) {
30646
30690
  logger.debug(`Preserving user config: ${normalizedRelativePath}`);
30647
30691
  skippedCount++;
@@ -30672,7 +30716,7 @@ class CopyExecutor {
30672
30716
  continue;
30673
30717
  }
30674
30718
  }
30675
- await withRetry(() => import_fs_extra11.copy(file, destPath, { overwrite: true }));
30719
+ await withRetry(() => import_fs_extra12.copy(file, destPath, { overwrite: true }));
30676
30720
  this.trackInstalledFile(normalizedRelativePath);
30677
30721
  copiedCount++;
30678
30722
  }
@@ -30718,7 +30762,7 @@ class CopyExecutor {
30718
30762
  }
30719
30763
  }
30720
30764
  }
30721
- var import_fs_extra11, import_ignore2, isRetryable = (e2) => {
30765
+ var import_fs_extra12, import_ignore2, isRetryable = (e2) => {
30722
30766
  const code = e2.code ?? "";
30723
30767
  return ["EBUSY", "EPERM", "EACCES"].includes(code);
30724
30768
  }, delay = (ms) => new Promise((r2) => setTimeout(r2, ms));
@@ -30728,7 +30772,7 @@ var init_copy_executor = __esm(() => {
30728
30772
  init_selective_merger();
30729
30773
  init_file_scanner();
30730
30774
  init_settings_processor();
30731
- import_fs_extra11 = __toESM(require_lib(), 1);
30775
+ import_fs_extra12 = __toESM(require_lib(), 1);
30732
30776
  import_ignore2 = __toESM(require_ignore(), 1);
30733
30777
  });
30734
30778
 
@@ -30826,7 +30870,7 @@ var init_file_merger = __esm(() => {
30826
30870
 
30827
30871
  // src/shared/kit-layout.ts
30828
30872
  import { existsSync as existsSync6, readFileSync as readFileSync3 } from "node:fs";
30829
- import { join as join15 } from "node:path";
30873
+ import { join as join16 } from "node:path";
30830
30874
  function uniquePaths(paths) {
30831
30875
  return [...new Set(paths)];
30832
30876
  }
@@ -30839,7 +30883,7 @@ function findFirstExistingPath(paths) {
30839
30883
  return null;
30840
30884
  }
30841
30885
  function resolveKitLayout(projectRoot) {
30842
- const packageJsonPath = join15(projectRoot, "package.json");
30886
+ const packageJsonPath = join16(projectRoot, "package.json");
30843
30887
  if (!existsSync6(packageJsonPath)) {
30844
30888
  return DEFAULT_KIT_LAYOUT;
30845
30889
  }
@@ -30856,8 +30900,8 @@ function resolveKitLayout(projectRoot) {
30856
30900
  function getProjectLayoutCandidates(projectRoot, subPath) {
30857
30901
  const layout = resolveKitLayout(projectRoot);
30858
30902
  return uniquePaths([
30859
- join15(projectRoot, layout.sourceDir, subPath),
30860
- join15(projectRoot, DEFAULT_KIT_LAYOUT.sourceDir, subPath)
30903
+ join16(projectRoot, layout.sourceDir, subPath),
30904
+ join16(projectRoot, DEFAULT_KIT_LAYOUT.sourceDir, subPath)
30861
30905
  ]);
30862
30906
  }
30863
30907
  function findExistingProjectLayoutPath(projectRoot, subPath) {
@@ -30866,9 +30910,9 @@ function findExistingProjectLayoutPath(projectRoot, subPath) {
30866
30910
  function getProjectConfigCandidates(projectRoot) {
30867
30911
  const layout = resolveKitLayout(projectRoot);
30868
30912
  return uniquePaths([
30869
- join15(projectRoot, "CLAUDE.md"),
30870
- join15(projectRoot, layout.sourceDir, "CLAUDE.md"),
30871
- join15(projectRoot, DEFAULT_KIT_LAYOUT.sourceDir, "CLAUDE.md")
30913
+ join16(projectRoot, "CLAUDE.md"),
30914
+ join16(projectRoot, layout.sourceDir, "CLAUDE.md"),
30915
+ join16(projectRoot, DEFAULT_KIT_LAYOUT.sourceDir, "CLAUDE.md")
30872
30916
  ]);
30873
30917
  }
30874
30918
  function findExistingProjectConfigPath(projectRoot) {
@@ -34352,21 +34396,21 @@ var require_gray_matter = __commonJS((exports, module) => {
34352
34396
  });
34353
34397
 
34354
34398
  // src/domains/installers/shared/skills-discovery.ts
34355
- import { readFile as readFile9, readdir as readdir3, stat as stat3 } from "node:fs/promises";
34399
+ import { readFile as readFile10, readdir as readdir3, stat as stat3 } from "node:fs/promises";
34356
34400
  import { homedir as homedir5 } from "node:os";
34357
- import { basename, dirname as dirname5, join as join16 } from "node:path";
34401
+ import { basename, dirname as dirname5, join as join17 } from "node:path";
34358
34402
  function getSkillSourcePath() {
34359
- const bundledRoot = join16(process.cwd(), "node_modules", "takumi-engineer");
34403
+ const bundledRoot = join17(process.cwd(), "node_modules", "takumi-engineer");
34360
34404
  return findFirstExistingPath([
34361
- join16(bundledRoot, "skills"),
34405
+ join17(bundledRoot, "skills"),
34362
34406
  ...getProjectLayoutCandidates(bundledRoot, "skills"),
34363
34407
  ...getProjectLayoutCandidates(process.cwd(), "skills"),
34364
- join16(home, ".claude/skills")
34408
+ join17(home, ".claude/skills")
34365
34409
  ]);
34366
34410
  }
34367
34411
  async function hasSkillMd(dir) {
34368
34412
  try {
34369
- const skillPath = join16(dir, "SKILL.md");
34413
+ const skillPath = join17(dir, "SKILL.md");
34370
34414
  const stats = await stat3(skillPath);
34371
34415
  return stats.isFile();
34372
34416
  } catch {
@@ -34375,7 +34419,7 @@ async function hasSkillMd(dir) {
34375
34419
  }
34376
34420
  async function parseSkillMd(skillMdPath) {
34377
34421
  try {
34378
- const content = await readFile9(skillMdPath, "utf-8");
34422
+ const content = await readFile10(skillMdPath, "utf-8");
34379
34423
  const { data } = import_gray_matter.default(content);
34380
34424
  const skillDir = dirname5(skillMdPath);
34381
34425
  const dirName = skillDir.split(/[/\\]/).pop() || "";
@@ -34423,7 +34467,7 @@ async function readWorkflowSlashNames(workflowsDir) {
34423
34467
  return names;
34424
34468
  }
34425
34469
  async function readSiblingWorkflowNames(skillsDir) {
34426
- return readWorkflowSlashNames(join16(dirname5(skillsDir), "workflows"));
34470
+ return readWorkflowSlashNames(join17(dirname5(skillsDir), "workflows"));
34427
34471
  }
34428
34472
  async function discoverSkills(sourcePath) {
34429
34473
  const skills = [];
@@ -34443,9 +34487,9 @@ async function discoverSkills(sourcePath) {
34443
34487
  logger.verbose(`Skipping skill "${entry.name}": shadowed by workflow with the same name`);
34444
34488
  continue;
34445
34489
  }
34446
- const skillDir = join16(searchPath, entry.name);
34490
+ const skillDir = join17(searchPath, entry.name);
34447
34491
  if (await hasSkillMd(skillDir)) {
34448
- const skill = await parseSkillMd(join16(skillDir, "SKILL.md"));
34492
+ const skill = await parseSkillMd(join17(skillDir, "SKILL.md"));
34449
34493
  if (skill && !seenNames.has(skill.name)) {
34450
34494
  skills.push(skill);
34451
34495
  seenNames.add(skill.name);
@@ -34465,43 +34509,6 @@ var init_skills_discovery = __esm(() => {
34465
34509
  WORKFLOW_ENTRY_EXTENSIONS = [".js", ".mjs", ".cjs"];
34466
34510
  });
34467
34511
 
34468
- // src/domains/migration/release-manifest.ts
34469
- import { join as join17 } from "node:path";
34470
-
34471
- class ReleaseManifestLoader {
34472
- static async load(extractDir) {
34473
- const manifestPath = join17(extractDir, "release-manifest.json");
34474
- try {
34475
- const content = await import_fs_extra12.readFile(manifestPath, "utf-8");
34476
- const parsed = JSON.parse(content);
34477
- return ReleaseManifestSchema.parse(parsed);
34478
- } catch (error) {
34479
- logger.debug(`Release manifest not found or invalid: ${error}`);
34480
- return null;
34481
- }
34482
- }
34483
- static findFile(manifest, relativePath) {
34484
- return manifest.files.find((f3) => f3.path === relativePath);
34485
- }
34486
- }
34487
- var import_fs_extra12, ReleaseManifestFileSchema, ReleaseManifestSchema;
34488
- var init_release_manifest = __esm(() => {
34489
- init_logger();
34490
- init_zod();
34491
- import_fs_extra12 = __toESM(require_lib(), 1);
34492
- ReleaseManifestFileSchema = exports_external.object({
34493
- path: exports_external.string(),
34494
- checksum: exports_external.string().regex(/^[a-f0-9]{64}$/),
34495
- size: exports_external.number(),
34496
- lastModified: exports_external.string().datetime({ offset: true }).optional()
34497
- });
34498
- ReleaseManifestSchema = exports_external.object({
34499
- version: exports_external.string(),
34500
- generatedAt: exports_external.string(),
34501
- files: exports_external.array(ReleaseManifestFileSchema)
34502
- });
34503
- });
34504
-
34505
34512
  // src/domains/migration/legacy-migration.ts
34506
34513
  import { readdir as readdir4, stat as stat4 } from "node:fs/promises";
34507
34514
  import { join as join18, relative as relative5 } from "node:path";
@@ -35362,6 +35369,28 @@ var init_commands_prefix = __esm(() => {
35362
35369
  };
35363
35370
  });
35364
35371
 
35372
+ // src/domains/installers/claude-code/ownership-resolver.ts
35373
+ function createClaudeOwnershipResolver(releaseManifest) {
35374
+ return (installedPath) => {
35375
+ const providerRelativePath = normalizeClaudeReleaseManifestPath(installedPath);
35376
+ if (providerRelativePath === "settings.json" || userConfigMatcher.ignores(providerRelativePath)) {
35377
+ return { ownership: "user" };
35378
+ }
35379
+ const entry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
35380
+ return {
35381
+ ownership: entry ? "takumi" : "user",
35382
+ sourceTimestamp: entry?.lastModified
35383
+ };
35384
+ };
35385
+ }
35386
+ var import_ignore3, userConfigMatcher;
35387
+ var init_ownership_resolver = __esm(() => {
35388
+ init_release_manifest();
35389
+ init_types2();
35390
+ import_ignore3 = __toESM(require_ignore(), 1);
35391
+ userConfigMatcher = import_ignore3.default().add(USER_CONFIG_PATTERNS);
35392
+ });
35393
+
35365
35394
  // src/domains/ui/ownership-display.ts
35366
35395
  var exports_ownership_display = {};
35367
35396
  __export(exports_ownership_display, {
@@ -35619,13 +35648,7 @@ async function handleMerge(ctx) {
35619
35648
  logger.debug(`Cleanup of deprecated files failed: ${error}`);
35620
35649
  }
35621
35650
  const installedFiles = merger.getAllInstalledFiles();
35622
- const resolver = (installedPath) => {
35623
- const entry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
35624
- return {
35625
- ownership: entry ? "takumi" : "user",
35626
- sourceTimestamp: entry?.lastModified
35627
- };
35628
- };
35651
+ const resolver = createClaudeOwnershipResolver(releaseManifest);
35629
35652
  const filesToTrack = buildFileTrackingList({
35630
35653
  installedFiles,
35631
35654
  providerRoot: ctx.claudeDir,
@@ -35659,6 +35682,7 @@ var init_merge_handler = __esm(() => {
35659
35682
  init_commands_prefix();
35660
35683
  init_logger();
35661
35684
  init_output_manager();
35685
+ init_ownership_resolver();
35662
35686
  import_fs_extra18 = __toESM(require_lib(), 1);
35663
35687
  });
35664
35688
 
@@ -52646,7 +52670,7 @@ import { promises as fs13 } from "node:fs";
52646
52670
  import { basename as basename12, extname as extname5, resolve as resolve16 } from "node:path";
52647
52671
 
52648
52672
  // src/domains/artifact/folder-walk.ts
52649
- var import_ignore3 = __toESM(require_ignore(), 1);
52673
+ var import_ignore4 = __toESM(require_ignore(), 1);
52650
52674
  import { promises as fs12 } from "node:fs";
52651
52675
  import { join as join72, relative as relative14 } from "node:path";
52652
52676
  var MAX_FILE_BYTES = 20 * 1024 * 1024;
@@ -52661,7 +52685,7 @@ class FolderLimitError extends Error {
52661
52685
  }
52662
52686
  }
52663
52687
  async function buildIgnoreMatcher(rootDir) {
52664
- const ig = import_ignore3.default();
52688
+ const ig = import_ignore4.default();
52665
52689
  for (const file of IGNORE_FILES) {
52666
52690
  try {
52667
52691
  ig.add(await fs12.readFile(join72(rootDir, file), "utf8"));
@@ -53591,6 +53615,44 @@ function tryOpenBrowser(target) {
53591
53615
  init_logger();
53592
53616
  import * as http from "node:http";
53593
53617
 
53618
+ // src/domains/sessions/analytics-daily-by-model.ts
53619
+ function buildDailyByModel(aggregates, oldestAllowedMs) {
53620
+ const byDayModel = new Map;
53621
+ for (const agg of aggregates) {
53622
+ const ms = startedMs(agg?.startedAt);
53623
+ if (ms === null)
53624
+ continue;
53625
+ const perModel = agg?.tokensByModel;
53626
+ if (!perModel || typeof perModel !== "object")
53627
+ continue;
53628
+ const date = dayKey(ms);
53629
+ if (Date.parse(`${date}T00:00:00.000Z`) < oldestAllowedMs)
53630
+ continue;
53631
+ let byModel = byDayModel.get(date);
53632
+ if (!byModel) {
53633
+ byModel = new Map;
53634
+ byDayModel.set(date, byModel);
53635
+ }
53636
+ for (const [model, mtok] of Object.entries(perModel)) {
53637
+ let acc = byModel.get(model);
53638
+ if (!acc) {
53639
+ acc = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
53640
+ byModel.set(model, acc);
53641
+ }
53642
+ accumulateTokens(acc, normalizeTokens(mtok));
53643
+ }
53644
+ }
53645
+ const rows = [];
53646
+ for (const [date, byModel] of byDayModel) {
53647
+ for (const [model, tokens] of byModel) {
53648
+ if (sumTokens(tokens) > 0)
53649
+ rows.push({ date, model, tokens });
53650
+ }
53651
+ }
53652
+ rows.sort((a3, b3) => a3.date.localeCompare(b3.date) || a3.model.localeCompare(b3.model));
53653
+ return rows;
53654
+ }
53655
+
53594
53656
  // src/domains/sessions/analytics.ts
53595
53657
  var DAY_MS = 24 * 60 * 60 * 1000;
53596
53658
  var PER_DAY_MAX = 371;
@@ -53704,6 +53766,7 @@ function foldAnalytics(aggregates, now, range) {
53704
53766
  const rankedModels = [...tokensByModel.entries()].map(([label, tokens]) => ({ label, tokens, total: sumTokens(tokens) })).filter((m2) => m2.total > 0).sort((a3, b3) => b3.total - a3.total || a3.label.localeCompare(b3.label)).slice(0, TOP_PROJECTS);
53705
53767
  const byModel = rankedModels.map(({ label, total }) => ({ label, tokens: total }));
53706
53768
  const byModelDetailed = rankedModels.map(({ label, tokens }) => ({ label, tokens }));
53769
+ const dailyByModel = buildDailyByModel(aggregates, oldestAllowedMs);
53707
53770
  const cacheDenom = totals.cacheRead + totals.input;
53708
53771
  const cacheHitRate = cacheDenom > 0 ? totals.cacheRead / cacheDenom : 0;
53709
53772
  return {
@@ -53725,7 +53788,8 @@ function foldAnalytics(aggregates, now, range) {
53725
53788
  byProject,
53726
53789
  daily,
53727
53790
  byModel,
53728
- byModelDetailed
53791
+ byModelDetailed,
53792
+ dailyByModel
53729
53793
  },
53730
53794
  window: {
53731
53795
  earliest: earliestMs === null ? null : new Date(earliestMs).toISOString(),
@@ -69558,7 +69622,7 @@ function detectBroadGlob(pattern, pathHint) {
69558
69622
  }
69559
69623
 
69560
69624
  // src/domains/hooks/handlers/guard-breadth-scout/check-ignore.ts
69561
- var import_ignore4 = __toESM(require_ignore(), 1);
69625
+ var import_ignore5 = __toESM(require_ignore(), 1);
69562
69626
  import { existsSync as existsSync45, readFileSync as readFileSync13 } from "node:fs";
69563
69627
  import { dirname as dirname24, join as join93 } from "node:path";
69564
69628
  var BUILTIN_HEAVY_DIR_LINES = [
@@ -69619,7 +69683,7 @@ function cleanLines(lines) {
69619
69683
  }
69620
69684
  function buildIgnoreConfig(lines, sourceLabel) {
69621
69685
  const cleaned = cleanLines(lines);
69622
- const ig = import_ignore4.default().add(cleaned);
69686
+ const ig = import_ignore5.default().add(cleaned);
69623
69687
  const patterns = cleaned.filter((line) => !line.startsWith("!"));
69624
69688
  return { ig, patterns, sourceLabel };
69625
69689
  }
@@ -69659,7 +69723,7 @@ function findMatchingIgnoreRule(candidatePath, config) {
69659
69723
  let matched = null;
69660
69724
  for (const pattern of config.patterns) {
69661
69725
  try {
69662
- if (import_ignore4.default().add(pattern).ignores(rel))
69726
+ if (import_ignore5.default().add(pattern).ignores(rel))
69663
69727
  matched = pattern;
69664
69728
  } catch {}
69665
69729
  }
@@ -77277,7 +77341,7 @@ function registerTempDir(dir) {
77277
77341
 
77278
77342
  // src/domains/installation/download-manager.ts
77279
77343
  init_types2();
77280
- var import_ignore5 = __toESM(require_ignore(), 1);
77344
+ var import_ignore6 = __toESM(require_ignore(), 1);
77281
77345
 
77282
77346
  // src/domains/installation/download/file-downloader.ts
77283
77347
  init_logger();
@@ -81172,11 +81236,11 @@ class DownloadManager {
81172
81236
  ig;
81173
81237
  userExcludePatterns = [];
81174
81238
  constructor() {
81175
- this.ig = import_ignore5.default().add(EXCLUDE_PATTERNS);
81239
+ this.ig = import_ignore6.default().add(EXCLUDE_PATTERNS);
81176
81240
  }
81177
81241
  setExcludePatterns(patterns) {
81178
81242
  this.userExcludePatterns = patterns;
81179
- this.ig = import_ignore5.default().add([...EXCLUDE_PATTERNS, ...this.userExcludePatterns]);
81243
+ this.ig = import_ignore6.default().add([...EXCLUDE_PATTERNS, ...this.userExcludePatterns]);
81180
81244
  if (patterns.length > 0) {
81181
81245
  logger.info(`Added ${patterns.length} custom exclude pattern(s)`);
81182
81246
  patterns.forEach((p2) => logger.debug(` - ${p2}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunasteriskrnd/takumi",
3
- "version": "1.0.0-dev.46",
3
+ "version": "1.0.0-dev.48",
4
4
  "description": "CLI tool for bootstrapping and managing Takumi projects",
5
5
  "type": "module",
6
6
  "repository": {