@sunasteriskrnd/takumi 0.11.0 → 0.12.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.
Files changed (2) hide show
  1. package/dist/index.js +191 -29
  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: "0.11.0",
19818
+ version: "0.12.0",
19819
19819
  description: "CLI tool for bootstrapping and managing Takumi projects",
19820
19820
  type: "module",
19821
19821
  repository: {
@@ -39263,7 +39263,7 @@ async function installSkillsDependencies(skillsDir, options2 = {}) {
39263
39263
  let effectiveSkillsDir = skillsDir;
39264
39264
  if (!existsSync8(scriptPath)) {
39265
39265
  const { homedir: homedir6 } = await import("node:os");
39266
- const globalSkillsDir = join38(homedir6(), ".claude", "skills");
39266
+ const globalSkillsDir = process.env.TKM_GLOBAL_SKILLS_DIR ?? join38(homedir6(), ".claude", "skills");
39267
39267
  const globalScriptPath = join38(globalSkillsDir, scriptName);
39268
39268
  if (existsSync8(globalScriptPath)) {
39269
39269
  logger.debug(`Local install script not found, using global: ${globalScriptPath}`);
@@ -51332,6 +51332,82 @@ var init_auth_command_help = __esm(() => {
51332
51332
  };
51333
51333
  });
51334
51334
 
51335
+ // src/domains/help/commands/artifact-command-help.ts
51336
+ var artifactCommandHelp;
51337
+ var init_artifact_command_help = __esm(() => {
51338
+ artifactCommandHelp = {
51339
+ name: "artifact",
51340
+ description: "Upload, download, and delete Takumi artifacts (upload|download|delete)",
51341
+ usage: "tkm artifact <upload <file|dir> | download <uuid|url> | delete <uuid|url>>",
51342
+ examples: [
51343
+ {
51344
+ command: "tkm artifact upload ./report.html --title 'Weekly Report'",
51345
+ description: "Upload a file (or directory) as a new artifact and print its share URL"
51346
+ },
51347
+ {
51348
+ command: "tkm artifact download <uuid|url> -o ./out --ver 3",
51349
+ description: "Download a specific version's files into a directory"
51350
+ },
51351
+ {
51352
+ command: "tkm artifact delete <uuid|url> --ver 2",
51353
+ description: "Delete ONLY version 2 (soft delete; other versions stay). A pasted URL with ?v=2 does the same"
51354
+ }
51355
+ ],
51356
+ optionGroups: [
51357
+ {
51358
+ title: "Actions",
51359
+ options: [
51360
+ {
51361
+ flags: "upload <file|dir>",
51362
+ description: "Upload a file or directory as a new artifact (or new version with --id)"
51363
+ },
51364
+ {
51365
+ flags: "download <uuid|url>",
51366
+ description: "Download an artifact's files (latest or --ver <n>)"
51367
+ },
51368
+ {
51369
+ flags: "delete <uuid|url>",
51370
+ description: "Soft-delete the whole artifact, or one version with --ver <n>"
51371
+ }
51372
+ ]
51373
+ },
51374
+ {
51375
+ title: "Options",
51376
+ options: [
51377
+ {
51378
+ flags: "--id <uuid|url>",
51379
+ description: "(upload) Existing artifact to overwrite with a new version"
51380
+ },
51381
+ {
51382
+ flags: "--title <title>",
51383
+ description: "(upload) Display title for the artifact (max 200 chars)"
51384
+ },
51385
+ {
51386
+ flags: "-m, --message <message>",
51387
+ description: "(upload) Short note describing this version's change (max 100 chars)"
51388
+ },
51389
+ {
51390
+ flags: "-o, --output <dir>",
51391
+ description: "(download) Output directory; default: slug(title) or uuid in CWD"
51392
+ },
51393
+ {
51394
+ flags: "--ver <n>",
51395
+ description: "(download) Version to download | (delete) version to delete. ?v=<n> in a pasted URL works too; --ver wins when both given"
51396
+ },
51397
+ {
51398
+ flags: "--force",
51399
+ description: "(download) Allow writing into a non-empty directory"
51400
+ },
51401
+ {
51402
+ flags: "-y, --yes",
51403
+ description: "(delete) Skip the confirmation prompt"
51404
+ }
51405
+ ]
51406
+ }
51407
+ ]
51408
+ };
51409
+ });
51410
+
51335
51411
  // src/domains/help/commands/index.ts
51336
51412
  var init_commands2 = __esm(() => {
51337
51413
  init_init_command_help();
@@ -51341,6 +51417,7 @@ var init_commands2 = __esm(() => {
51341
51417
  init_versions_command_help();
51342
51418
  init_config_command_help();
51343
51419
  init_auth_command_help();
51420
+ init_artifact_command_help();
51344
51421
  init_common_options();
51345
51422
  });
51346
51423
 
@@ -51359,7 +51436,8 @@ var init_help_commands = __esm(() => {
51359
51436
  versions: versionsCommandHelp,
51360
51437
  doctor: doctorCommandHelp,
51361
51438
  uninstall: uninstallCommandHelp,
51362
- auth: authCommandHelp
51439
+ auth: authCommandHelp,
51440
+ artifact: artifactCommandHelp
51363
51441
  };
51364
51442
  });
51365
51443
 
@@ -53547,6 +53625,27 @@ async function remove9(token, uuid) {
53547
53625
  throw new ArtifactApiError(res.status, `Delete failed (HTTP ${res.status}): ${msg}`);
53548
53626
  }
53549
53627
  }
53628
+ async function removeVersion(token, uuid, version3) {
53629
+ const base = getServerUrl();
53630
+ const res = await fetch(`${base}/api/artifacts/${encodeURIComponent(uuid)}/versions/${encodeURIComponent(String(version3))}`, {
53631
+ method: "DELETE",
53632
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }
53633
+ });
53634
+ if (!res.ok) {
53635
+ const msg = await parseErrorMessage(res);
53636
+ throw new ArtifactApiError(res.status, `Delete version failed (HTTP ${res.status}): ${msg}`);
53637
+ }
53638
+ try {
53639
+ return await res.json();
53640
+ } catch {
53641
+ return {
53642
+ ok: true,
53643
+ deleted_version: undefined,
53644
+ live_version: undefined,
53645
+ shared_version: undefined
53646
+ };
53647
+ }
53648
+ }
53550
53649
  async function getManifest(token, uuid, version3) {
53551
53650
  const base = getServerUrl();
53552
53651
  const qs = version3 !== undefined ? `?v=${encodeURIComponent(String(version3))}` : "";
@@ -53680,6 +53779,17 @@ function parseArtifactRef(input) {
53680
53779
  const candidate = m2?.[1] ?? "";
53681
53780
  return UUID_V4_RE.test(candidate) ? candidate.toLowerCase() : null;
53682
53781
  }
53782
+ function extractVersionFromRef(ref) {
53783
+ try {
53784
+ const url = new URL(ref.trim());
53785
+ const v2 = url.searchParams.get("v");
53786
+ if (v2 !== null) {
53787
+ const n = Number.parseInt(v2, 10);
53788
+ return Number.isFinite(n) && n > 0 ? n : undefined;
53789
+ }
53790
+ } catch {}
53791
+ return;
53792
+ }
53683
53793
  function warnIfHostMismatch(input) {
53684
53794
  let inputUrl;
53685
53795
  try {
@@ -53710,6 +53820,11 @@ async function artifactDelete(ref, opts) {
53710
53820
  const token = await getToken(opts);
53711
53821
  if (!token)
53712
53822
  return;
53823
+ const version3 = opts.version !== undefined ? opts.version : extractVersionFromRef(ref);
53824
+ if (version3 !== undefined) {
53825
+ await deleteVersion(uuid, version3, token, opts);
53826
+ return;
53827
+ }
53713
53828
  if (!opts.yes) {
53714
53829
  const confirmed = await se({
53715
53830
  message: `Delete artifact ${uuid}? This cannot be undone.`,
@@ -53730,6 +53845,36 @@ async function artifactDelete(ref, opts) {
53730
53845
  await removeEntry(uuid);
53731
53846
  console.log(`Deleted artifact ${uuid}.`);
53732
53847
  }
53848
+ async function deleteVersion(uuid, version3, token, opts) {
53849
+ if (!opts.yes) {
53850
+ const confirmed = await se({
53851
+ message: `Delete version ${version3} of artifact ${uuid}? This cannot be undone.`,
53852
+ initialValue: false
53853
+ });
53854
+ if (lD(confirmed) || !confirmed) {
53855
+ console.log("Cancelled.");
53856
+ return;
53857
+ }
53858
+ }
53859
+ try {
53860
+ const result = await removeVersion(token, uuid, version3);
53861
+ if (result.live_version !== undefined) {
53862
+ console.log(`Deleted version ${version3} of artifact ${uuid}. Live version is now ${result.live_version}.`);
53863
+ } else {
53864
+ console.log(`Deleted version ${version3} of artifact ${uuid}.`);
53865
+ }
53866
+ } catch (err) {
53867
+ if (err instanceof ArtifactApiError && isCannotDeleteLastVersion(err)) {
53868
+ console.error(`Error: cannot delete version ${version3} — it is the last remaining version. ` + `Delete the whole artifact instead: tkm artifact delete ${uuid}`);
53869
+ } else {
53870
+ printApiError(err);
53871
+ }
53872
+ process.exitCode = 1;
53873
+ }
53874
+ }
53875
+ function isCannotDeleteLastVersion(err) {
53876
+ return err.status === 409 && err.message.includes("cannot_delete_last_version");
53877
+ }
53733
53878
 
53734
53879
  // src/commands/artifact/download-command.ts
53735
53880
  import { promises as fs11 } from "node:fs";
@@ -53797,6 +53942,11 @@ var EXT_TO_MIME = {
53797
53942
  html: "text/html",
53798
53943
  htm: "text/html",
53799
53944
  md: "text/markdown",
53945
+ mmd: "text/vnd.mermaid",
53946
+ yaml: "application/yaml",
53947
+ yml: "application/yaml",
53948
+ csv: "text/csv",
53949
+ tsv: "text/tab-separated-values",
53800
53950
  txt: "text/plain",
53801
53951
  css: "text/css",
53802
53952
  js: "text/javascript",
@@ -53905,17 +54055,6 @@ function deriveSlug(title, uuid) {
53905
54055
  const slug = (title ?? "").trim().replace(/[/\\:*?"<>|.]/g, " ").split("").map((c2) => c2.charCodeAt(0) < 32 ? " " : c2).join("").replace(/\s+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "").slice(0, 80);
53906
54056
  return slug || uuid;
53907
54057
  }
53908
- function extractVersionFromRef(ref) {
53909
- try {
53910
- const url = new URL(ref.trim());
53911
- const v2 = url.searchParams.get("v");
53912
- if (v2 !== null) {
53913
- const n = Number.parseInt(v2, 10);
53914
- return Number.isFinite(n) && n > 0 ? n : undefined;
53915
- }
53916
- } catch {}
53917
- return;
53918
- }
53919
54058
  async function artifactDownload(ref, opts) {
53920
54059
  const uuid = parseArtifactRef(ref);
53921
54060
  if (!uuid) {
@@ -54005,9 +54144,12 @@ import { promises as fs13 } from "node:fs";
54005
54144
  import { basename as basename12, extname as extname5, resolve as resolve16 } from "node:path";
54006
54145
 
54007
54146
  // src/domains/artifact/folder-walk.ts
54147
+ var import_ignore3 = __toESM(require_ignore(), 1);
54008
54148
  import { promises as fs12 } from "node:fs";
54009
54149
  import { join as join72, relative as relative14 } from "node:path";
54010
54150
  var MAX_FILE_BYTES = 20 * 1024 * 1024;
54151
+ var IGNORE_FILES = [".gitignore", ".tkmignore"];
54152
+ var HARD_IGNORES = [".git", ".gitignore", ".tkmignore"];
54011
54153
 
54012
54154
  class FolderLimitError extends Error {
54013
54155
  constructor(message) {
@@ -54015,9 +54157,20 @@ class FolderLimitError extends Error {
54015
54157
  this.name = "FolderLimitError";
54016
54158
  }
54017
54159
  }
54160
+ async function buildIgnoreMatcher(rootDir) {
54161
+ const ig = import_ignore3.default();
54162
+ for (const file of IGNORE_FILES) {
54163
+ try {
54164
+ ig.add(await fs12.readFile(join72(rootDir, file), "utf8"));
54165
+ } catch {}
54166
+ }
54167
+ ig.add(HARD_IGNORES);
54168
+ return ig;
54169
+ }
54018
54170
  async function walkFolder(rootDir) {
54171
+ const ig = await buildIgnoreMatcher(rootDir);
54019
54172
  const entries = [];
54020
- await walk(rootDir, rootDir, entries);
54173
+ await walk(rootDir, rootDir, ig, entries);
54021
54174
  entries.sort((a3, b3) => a3.relPath.localeCompare(b3.relPath));
54022
54175
  const oversized = entries.find((e2) => e2.size > MAX_FILE_BYTES);
54023
54176
  if (oversized) {
@@ -54026,7 +54179,7 @@ async function walkFolder(rootDir) {
54026
54179
  }
54027
54180
  return entries;
54028
54181
  }
54029
- async function walk(rootDir, dir, out) {
54182
+ async function walk(rootDir, dir, ig, out) {
54030
54183
  let dirents;
54031
54184
  try {
54032
54185
  dirents = await fs12.readdir(dir, { withFileTypes: true });
@@ -54038,11 +54191,15 @@ async function walk(rootDir, dir, out) {
54038
54191
  continue;
54039
54192
  const name = String(dirent.name);
54040
54193
  const absPath = join72(dir, name);
54194
+ const relPath = relative14(rootDir, absPath).split("\\").join("/");
54041
54195
  if (dirent.isDirectory()) {
54042
- await walk(rootDir, absPath, out);
54196
+ if (ig.ignores(`${relPath}/`))
54197
+ continue;
54198
+ await walk(rootDir, absPath, ig, out);
54043
54199
  } else if (dirent.isFile()) {
54200
+ if (ig.ignores(relPath))
54201
+ continue;
54044
54202
  const stat8 = await fs12.stat(absPath);
54045
- const relPath = relative14(rootDir, absPath).split("\\").join("/");
54046
54203
  out.push({ absPath, relPath, size: stat8.size });
54047
54204
  }
54048
54205
  }
@@ -54306,16 +54463,16 @@ async function artifactCommand(action, target, opts) {
54306
54463
  break;
54307
54464
  case "download":
54308
54465
  if (!target)
54309
- return usageError("tkm artifact download <uuid|url> [-o <dir>] [-v <n>] [--force]");
54466
+ return usageError("tkm artifact download <uuid|url> [-o <dir>] [--ver <n>] [--force]");
54310
54467
  await artifactDownload(target, opts);
54311
54468
  break;
54312
54469
  case "delete":
54313
54470
  if (!target)
54314
- return usageError("tkm artifact delete <uuid|url>");
54471
+ return usageError("tkm artifact delete <uuid|url> [--ver <n>]");
54315
54472
  await artifactDelete(target, opts);
54316
54473
  break;
54317
54474
  case undefined:
54318
- usageError("tkm artifact <upload <file> | download <uuid|url> | delete <uuid|url>>");
54475
+ usageError("tkm artifact <upload <file> | download <uuid|url> | delete <uuid|url> [--ver <n>]>");
54319
54476
  break;
54320
54477
  default:
54321
54478
  console.error(`Unknown artifact action: ${action}. Available: upload, download, delete`);
@@ -60842,7 +60999,7 @@ function detectBroadGlob(pattern, pathHint) {
60842
60999
  }
60843
61000
 
60844
61001
  // src/domains/hooks/handlers/guard-breadth-scout/check-ignore.ts
60845
- var import_ignore3 = __toESM(require_ignore(), 1);
61002
+ var import_ignore4 = __toESM(require_ignore(), 1);
60846
61003
  import { existsSync as existsSync44, readFileSync as readFileSync13 } from "node:fs";
60847
61004
  import { dirname as dirname24, join as join93 } from "node:path";
60848
61005
  var BUILTIN_HEAVY_DIR_LINES = [
@@ -60903,7 +61060,7 @@ function cleanLines(lines) {
60903
61060
  }
60904
61061
  function buildIgnoreConfig(lines, sourceLabel) {
60905
61062
  const cleaned = cleanLines(lines);
60906
- const ig = import_ignore3.default().add(cleaned);
61063
+ const ig = import_ignore4.default().add(cleaned);
60907
61064
  const patterns = cleaned.filter((line) => !line.startsWith("!"));
60908
61065
  return { ig, patterns, sourceLabel };
60909
61066
  }
@@ -60943,7 +61100,7 @@ function findMatchingIgnoreRule(candidatePath, config) {
60943
61100
  let matched = null;
60944
61101
  for (const pattern of config.patterns) {
60945
61102
  try {
60946
- if (import_ignore3.default().add(pattern).ignores(rel))
61103
+ if (import_ignore4.default().add(pattern).ignores(rel))
60947
61104
  matched = pattern;
60948
61105
  } catch {}
60949
61106
  }
@@ -68150,7 +68307,7 @@ function registerTempDir(dir) {
68150
68307
 
68151
68308
  // src/domains/installation/download-manager.ts
68152
68309
  init_types2();
68153
- var import_ignore4 = __toESM(require_ignore(), 1);
68310
+ var import_ignore5 = __toESM(require_ignore(), 1);
68154
68311
 
68155
68312
  // src/domains/installation/download/file-downloader.ts
68156
68313
  init_logger();
@@ -72045,11 +72202,11 @@ class DownloadManager {
72045
72202
  ig;
72046
72203
  userExcludePatterns = [];
72047
72204
  constructor() {
72048
- this.ig = import_ignore4.default().add(EXCLUDE_PATTERNS);
72205
+ this.ig = import_ignore5.default().add(EXCLUDE_PATTERNS);
72049
72206
  }
72050
72207
  setExcludePatterns(patterns) {
72051
72208
  this.userExcludePatterns = patterns;
72052
- this.ig = import_ignore4.default().add([...EXCLUDE_PATTERNS, ...this.userExcludePatterns]);
72209
+ this.ig = import_ignore5.default().add([...EXCLUDE_PATTERNS, ...this.userExcludePatterns]);
72053
72210
  if (patterns.length > 0) {
72054
72211
  logger.info(`Added ${patterns.length} custom exclude pattern(s)`);
72055
72212
  patterns.forEach((p2) => logger.debug(` - ${p2}`));
@@ -77653,8 +77810,13 @@ function registerCommands(cli) {
77653
77810
  cli.command("api [action] [service] [path]", "Interact with Takumi API and proxy services").option("--method <method>", "HTTP method for proxy requests (default: GET)").option("--body <json>", "Request body as JSON string (proxy only)").option("--query <json>", "Query params as JSON string (proxy only)").option("--key <key>", "API key to use (setup only)").option("--force", "Force re-setup even if key exists (setup only)").option("--json", "Output raw JSON instead of formatted display").option("--locale <locale>", "Locale for vidcap summary/caption (default: en)").option("--max-results <n>", "Max results for vidcap search").option("--second <s>", "Timestamp in seconds for vidcap screenshot").option("--order <order>", "Sort order for vidcap comments (time/relevance)").option("--format <fmt>", "Summary format for reviewweb (bullet/paragraph)").option("--max-length <n>", "Max summary length for reviewweb").option("--instructions <text>", "Extraction instructions for reviewweb extract").option("--template <json>", "JSON template for reviewweb extract").option("--type <type>", "Link type filter for reviewweb links (web/image/file/all)").option("--country <code>", "Country code for reviewweb SEO commands").action(async (action, service, path12, options2) => {
77654
77811
  await apiCommand(action, service, path12, options2);
77655
77812
  });
77656
- cli.command("artifact [action] [target]", "Manage Takumi artifacts (upload <file> | download <uuid|url> | delete <uuid|url>)").option("--id <uuid|url>", "Existing artifact (UUID or viewer URL) to overwrite on upload").option("--title <title>", "Display title for the artifact (max 200 chars)").option("-m, --message <message>", "Short note describing this version's change (max 100 chars)").option("-o, --output <dir>", "(download) Output directory; default: slug(title) or uuid in CWD").option("-v, --version <n>", "(download) Download a specific version number").option("--force", "(download) Allow writing into a non-empty directory").option("-y, --yes", "Skip delete confirmation prompt").action(async (action, target, opts) => {
77657
- const version3 = opts.version !== undefined ? Number.parseInt(opts.version, 10) : undefined;
77813
+ cli.command("artifact [action] [target]", "Manage Takumi artifacts (upload <file> | download <uuid|url> | delete <uuid|url>)").option("--id <uuid|url>", "Existing artifact (UUID or viewer URL) to overwrite on upload").option("--title <title>", "Display title for the artifact (max 200 chars)").option("-m, --message <message>", "Short note describing this version's change (max 100 chars)").option("-o, --output <dir>", "(download) Output directory; default: slug(title) or uuid in CWD").option("--ver <n>", "Version number (download: download it; delete: delete only that version)").option("--force", "(download) Allow writing into a non-empty directory").option("-y, --yes", "Skip delete confirmation prompt").action(async (action, target, opts) => {
77814
+ const version3 = opts.ver !== undefined ? Number.parseInt(String(opts.ver), 10) : undefined;
77815
+ if (version3 !== undefined && (!Number.isInteger(version3) || version3 < 1)) {
77816
+ console.error(`Invalid version: ${opts.ver}. Use a positive integer, e.g. --ver 2`);
77817
+ process.exitCode = 1;
77818
+ return;
77819
+ }
77658
77820
  await artifactCommand(action, target, { ...opts, version: version3 });
77659
77821
  });
77660
77822
  cli.command("auth [action]", "Sign in/out, refresh, and check Takumi session (login|logout|refresh|status)").option("--json", "Machine-readable JSON output (status, refresh)").option("-f, --force", "Force a token refresh even when the current token is still valid (refresh only)").action(async (action, options2 = {}) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunasteriskrnd/takumi",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "CLI tool for bootstrapping and managing Takumi projects",
5
5
  "type": "module",
6
6
  "repository": {