@dyoshikawa/tsumiki
Version:
A CLI tool for install tsumiki commands
310 lines (304 loc) • 20.1 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// src/cli.ts
var import_commander = require("commander");
// src/commands/gitignore.tsx
var path = __toESM(require("path"), 1);
var import_node_url = require("url");
var import_fs_extra = __toESM(require("fs-extra"), 1);
var import_ink = require("ink");
var import_react = __toESM(require("react"), 1);
var import_meta = {};
var GitignoreComponent = () => {
const [status, setStatus] = (0, import_react.useState)("starting");
const [addedRules, setAddedRules] = (0, import_react.useState)([]);
const [skippedRules, setSkippedRules] = (0, import_react.useState)([]);
const [error, setError] = (0, import_react.useState)(null);
(0, import_react.useEffect)(() => {
const performGitignoreUpdate = async () => {
try {
setStatus("checking");
const currentDir = process.cwd();
const gitignorePath = path.join(currentDir, ".gitignore");
const __filename = (0, import_node_url.fileURLToPath)(import_meta.url);
const __dirname = path.dirname(__filename);
const tsumikiDir = path.join(__dirname, "commands");
const files = await import_fs_extra.default.readdir(tsumikiDir);
const targetFiles = files.filter(
(file) => file.endsWith(".md") || file.endsWith(".sh")
);
const rulesToAdd = targetFiles.map(
(file) => `.claude/commands/${file}`
);
let gitignoreContent = "";
let gitignoreExists = false;
try {
gitignoreContent = await import_fs_extra.default.readFile(gitignorePath, "utf-8");
gitignoreExists = true;
} catch {
gitignoreExists = false;
}
const existingLines = gitignoreContent.split("\n").map((line) => line.trim());
const rulesToActuallyAdd = [];
const rulesAlreadyExist = [];
for (const rule of rulesToAdd) {
if (existingLines.includes(rule)) {
rulesAlreadyExist.push(rule);
} else {
rulesToActuallyAdd.push(rule);
}
}
if (rulesToActuallyAdd.length === 0) {
setSkippedRules(rulesAlreadyExist);
setStatus("skipped");
setTimeout(() => {
process.exit(0);
}, 2e3);
return;
}
setStatus("updating");
let newContent = gitignoreContent;
if (gitignoreExists && gitignoreContent.length > 0 && !gitignoreContent.endsWith("\n")) {
newContent += "\n";
}
if (gitignoreExists && gitignoreContent.length > 0) {
newContent += "\n# Tsumiki command templates\n";
} else {
newContent = "# Tsumiki command templates\n";
}
for (const rule of rulesToActuallyAdd) {
newContent += `${rule}
`;
}
await import_fs_extra.default.writeFile(gitignorePath, newContent);
setAddedRules(rulesToActuallyAdd);
setSkippedRules(rulesAlreadyExist);
setStatus("completed");
setTimeout(() => {
process.exit(0);
}, 2e3);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
setError(errorMessage);
setStatus("error");
setTimeout(() => {
process.exit(1);
}, 3e3);
}
};
performGitignoreUpdate();
}, []);
if (status === "starting") {
return /* @__PURE__ */ import_react.default.createElement(import_ink.Box, null, /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { color: "cyan" }, "\u{1F680} .gitignore \u306E\u66F4\u65B0\u3092\u958B\u59CB\u3057\u307E\u3059..."));
}
if (status === "checking") {
return /* @__PURE__ */ import_react.default.createElement(import_ink.Box, null, /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { color: "yellow" }, "\u{1F4CB} .gitignore \u30D5\u30A1\u30A4\u30EB\u3092\u30C1\u30A7\u30C3\u30AF\u4E2D..."));
}
if (status === "updating") {
return /* @__PURE__ */ import_react.default.createElement(import_ink.Box, null, /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { color: "blue" }, "\u270F\uFE0F .gitignore \u3092\u66F4\u65B0\u4E2D..."));
}
if (status === "error") {
return /* @__PURE__ */ import_react.default.createElement(import_ink.Box, { flexDirection: "column" }, /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { color: "red" }, "\u274C \u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F:"), /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { color: "red" }, error));
}
if (status === "skipped") {
return /* @__PURE__ */ import_react.default.createElement(import_ink.Box, { flexDirection: "column" }, /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { color: "yellow" }, "\u23ED\uFE0F \u3059\u3079\u3066\u306E\u30EB\u30FC\u30EB\u304C\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059"), /* @__PURE__ */ import_react.default.createElement(import_ink.Newline, null), /* @__PURE__ */ import_react.default.createElement(import_ink.Text, null, "\u65E2\u5B58\u306E\u30EB\u30FC\u30EB:"), skippedRules.map((rule) => /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { key: rule, color: "gray" }, "\u2022 ", rule)), /* @__PURE__ */ import_react.default.createElement(import_ink.Newline, null), /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { color: "cyan" }, ".gitignore \u306E\u66F4\u65B0\u306F\u4E0D\u8981\u3067\u3057\u305F"));
}
if (status === "completed") {
return /* @__PURE__ */ import_react.default.createElement(import_ink.Box, { flexDirection: "column" }, /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { color: "green" }, "\u2705 .gitignore \u306E\u66F4\u65B0\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F!"), /* @__PURE__ */ import_react.default.createElement(import_ink.Newline, null), addedRules.length > 0 && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_ink.Text, null, "\u8FFD\u52A0\u3055\u308C\u305F\u30EB\u30FC\u30EB (", addedRules.length, "\u500B):"), addedRules.map((rule) => /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { key: rule, color: "green" }, "\u2022 ", rule))), skippedRules.length > 0 && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_ink.Text, null, "\u65E2\u5B58\u306E\u30EB\u30FC\u30EB (", skippedRules.length, "\u500B):"), skippedRules.map((rule) => /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { key: rule, color: "gray" }, "\u2022 ", rule))), /* @__PURE__ */ import_react.default.createElement(import_ink.Newline, null), /* @__PURE__ */ import_react.default.createElement(import_ink.Text, { color: "cyan" }, "Tsumiki \u306E\u30B3\u30DE\u30F3\u30C9\u30D5\u30A1\u30A4\u30EB\u304C Git \u304B\u3089\u7121\u8996\u3055\u308C\u308B\u3088\u3046\u306B\u306A\u308A\u307E\u3057\u305F"));
}
return null;
};
var gitignoreCommand = () => {
(0, import_ink.render)(import_react.default.createElement(GitignoreComponent));
};
// src/commands/install.tsx
var path2 = __toESM(require("path"), 1);
var import_node_url2 = require("url");
var import_fs_extra2 = __toESM(require("fs-extra"), 1);
var import_ink2 = require("ink");
var import_react2 = __toESM(require("react"), 1);
var import_meta2 = {};
var InstallComponent = () => {
const [status, setStatus] = (0, import_react2.useState)("starting");
const [copiedFiles, setCopiedFiles] = (0, import_react2.useState)([]);
const [error, setError] = (0, import_react2.useState)(null);
(0, import_react2.useEffect)(() => {
const performInstall = async () => {
try {
setStatus("checking");
const currentDir = process.cwd();
const targetDir = path2.join(currentDir, ".claude", "commands");
const __filename = (0, import_node_url2.fileURLToPath)(import_meta2.url);
const __dirname = path2.dirname(__filename);
const tsumikiDir = path2.join(__dirname, "commands");
await import_fs_extra2.default.ensureDir(targetDir);
setStatus("copying");
const files = await import_fs_extra2.default.readdir(tsumikiDir);
const targetFiles = files.filter(
(file) => file.endsWith(".md") || file.endsWith(".sh")
);
const copiedFilesList = [];
for (const file of targetFiles) {
const sourcePath = path2.join(tsumikiDir, file);
const targetPath = path2.join(targetDir, file);
await import_fs_extra2.default.copy(sourcePath, targetPath);
copiedFilesList.push(file);
}
setCopiedFiles(copiedFilesList);
setStatus("completed");
setTimeout(() => {
process.exit(0);
}, 2e3);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
setError(errorMessage);
setStatus("error");
setTimeout(() => {
process.exit(1);
}, 3e3);
}
};
performInstall();
}, []);
if (status === "starting") {
return /* @__PURE__ */ import_react2.default.createElement(import_ink2.Box, null, /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "cyan" }, "\u{1F680} Tsumiki \u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3092\u958B\u59CB\u3057\u307E\u3059..."));
}
if (status === "checking") {
return /* @__PURE__ */ import_react2.default.createElement(import_ink2.Box, null, /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "yellow" }, "\u{1F4CB} \u74B0\u5883\u3092\u30C1\u30A7\u30C3\u30AF\u4E2D..."));
}
if (status === "copying") {
return /* @__PURE__ */ import_react2.default.createElement(import_ink2.Box, null, /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "blue" }, "\u{1F4DD} \u30B3\u30DE\u30F3\u30C9\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3092\u30B3\u30D4\u30FC\u4E2D..."));
}
if (status === "error") {
return /* @__PURE__ */ import_react2.default.createElement(import_ink2.Box, { flexDirection: "column" }, /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "red" }, "\u274C \u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F:"), /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "red" }, error));
}
if (status === "completed") {
return /* @__PURE__ */ import_react2.default.createElement(import_ink2.Box, { flexDirection: "column" }, /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "green" }, "\u2705 \u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F!"), /* @__PURE__ */ import_react2.default.createElement(import_ink2.Newline, null), /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, null, "\u30B3\u30D4\u30FC\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB (", copiedFiles.length, "\u500B):"), copiedFiles.map((file) => /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { key: file, color: "gray" }, " ", "\u2022 ", file)), /* @__PURE__ */ import_react2.default.createElement(import_ink2.Newline, null), /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "cyan" }, "Claude Code\u3067\u4EE5\u4E0B\u306E\u3088\u3046\u306B\u30B3\u30DE\u30F3\u30C9\u3092\u4F7F\u7528\u3067\u304D\u307E\u3059:"), /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "white" }, " /tdd-requirements"), /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "white" }, " /kairo-design"), /* @__PURE__ */ import_react2.default.createElement(import_ink2.Text, { color: "white" }, " ..."));
}
return null;
};
var installCommand = () => {
(0, import_ink2.render)(import_react2.default.createElement(InstallComponent));
};
// src/commands/uninstall.tsx
var path3 = __toESM(require("path"), 1);
var import_node_url3 = require("url");
var import_fs_extra3 = __toESM(require("fs-extra"), 1);
var import_ink3 = require("ink");
var import_react3 = __toESM(require("react"), 1);
var import_meta3 = {};
var UninstallComponent = () => {
const [status, setStatus] = (0, import_react3.useState)("starting");
const [removedFiles, setRemovedFiles] = (0, import_react3.useState)([]);
const [error, setError] = (0, import_react3.useState)(null);
(0, import_react3.useEffect)(() => {
const performUninstall = async () => {
try {
setStatus("checking");
const currentDir = process.cwd();
const targetDir = path3.join(currentDir, ".claude", "commands");
const dirExists = await import_fs_extra3.default.pathExists(targetDir);
if (!dirExists) {
setStatus("not_found");
setTimeout(() => {
process.exit(0);
}, 2e3);
return;
}
const __filename = (0, import_node_url3.fileURLToPath)(import_meta3.url);
const __dirname = path3.dirname(__filename);
const tsumikiDir = path3.join(__dirname, "commands");
const tsumikiFiles = await import_fs_extra3.default.readdir(tsumikiDir);
const tsumikiTargetFiles = tsumikiFiles.filter(
(file) => file.endsWith(".md") || file.endsWith(".sh")
);
setStatus("removing");
const installedFiles = await import_fs_extra3.default.readdir(targetDir);
const removedFilesList = [];
for (const file of installedFiles) {
if (tsumikiTargetFiles.includes(file)) {
const filePath = path3.join(targetDir, file);
await import_fs_extra3.default.remove(filePath);
removedFilesList.push(file);
}
}
const remainingFiles = await import_fs_extra3.default.readdir(targetDir);
if (remainingFiles.length === 0) {
await import_fs_extra3.default.rmdir(targetDir);
const claudeDir = path3.dirname(targetDir);
const claudeFiles = await import_fs_extra3.default.readdir(claudeDir);
if (claudeFiles.length === 0) {
await import_fs_extra3.default.rmdir(claudeDir);
}
}
setRemovedFiles(removedFilesList);
setStatus("completed");
setTimeout(() => {
process.exit(0);
}, 2e3);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
setError(errorMessage);
setStatus("error");
setTimeout(() => {
process.exit(1);
}, 3e3);
}
};
performUninstall();
}, []);
if (status === "starting") {
return /* @__PURE__ */ import_react3.default.createElement(import_ink3.Box, null, /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "cyan" }, "\u{1F5D1}\uFE0F Tsumiki \u30A2\u30F3\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3092\u958B\u59CB\u3057\u307E\u3059..."));
}
if (status === "checking") {
return /* @__PURE__ */ import_react3.default.createElement(import_ink3.Box, null, /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "yellow" }, "\u{1F4CB} \u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u72B6\u6CC1\u3092\u30C1\u30A7\u30C3\u30AF\u4E2D..."));
}
if (status === "removing") {
return /* @__PURE__ */ import_react3.default.createElement(import_ink3.Box, null, /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "blue" }, "\u{1F5D1}\uFE0F \u30B3\u30DE\u30F3\u30C9\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3092\u524A\u9664\u4E2D..."));
}
if (status === "not_found") {
return /* @__PURE__ */ import_react3.default.createElement(import_ink3.Box, { flexDirection: "column" }, /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "yellow" }, "\u26A0\uFE0F .claude/commands \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093"), /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "gray" }, "Tsumiki\u306F\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u3066\u3044\u306A\u3044\u3088\u3046\u3067\u3059\u3002"));
}
if (status === "error") {
return /* @__PURE__ */ import_react3.default.createElement(import_ink3.Box, { flexDirection: "column" }, /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "red" }, "\u274C \u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F:"), /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "red" }, error));
}
if (status === "completed") {
if (removedFiles.length === 0) {
return /* @__PURE__ */ import_react3.default.createElement(import_ink3.Box, { flexDirection: "column" }, /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "yellow" }, "\u26A0\uFE0F \u524A\u9664\u5BFE\u8C61\u306E\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F"), /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "gray" }, "Tsumiki\u306E\u30B3\u30DE\u30F3\u30C9\u306F\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u3066\u3044\u306A\u3044\u3088\u3046\u3067\u3059\u3002"));
}
return /* @__PURE__ */ import_react3.default.createElement(import_ink3.Box, { flexDirection: "column" }, /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "green" }, "\u2705 \u30A2\u30F3\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F!"), /* @__PURE__ */ import_react3.default.createElement(import_ink3.Newline, null), /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, null, "\u524A\u9664\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB (", removedFiles.length, "\u500B):"), removedFiles.map((file) => /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { key: file, color: "gray" }, " ", "\u2022 ", file)), /* @__PURE__ */ import_react3.default.createElement(import_ink3.Newline, null), /* @__PURE__ */ import_react3.default.createElement(import_ink3.Text, { color: "cyan" }, "Tsumiki\u306EClaude Code\u30B3\u30DE\u30F3\u30C9\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u304C\u524A\u9664\u3055\u308C\u307E\u3057\u305F\u3002"));
}
return null;
};
var uninstallCommand = () => {
(0, import_ink3.render)(import_react3.default.createElement(UninstallComponent));
};
// src/cli.ts
var program = new import_commander.Command();
program.name("tsumiki").description("CLI tool for installing Claude Code command templates").version("1.0.0");
program.command("install").description("Install Claude Code command templates to .claude/commands/").action(installCommand);
program.command("uninstall").description("Uninstall Claude Code command templates from .claude/commands/").action(uninstallCommand);
program.command("gitignore").description("Add commands/*.{md,sh} to .gitignore file").action(gitignoreCommand);
program.parse();