create-takt-sdd 0.1.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/cli.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { isLang } from "./i18n.js";
6
+ import { getMessages } from "./i18n.js";
7
+ import { install } from "./install.js";
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ const packageRoot = resolve(__dirname, "..");
11
+ function parseArgs(argv) {
12
+ const args = {
13
+ lang: "en",
14
+ force: false,
15
+ dryRun: false,
16
+ help: false,
17
+ version: false,
18
+ };
19
+ for (let i = 0; i < argv.length; i++) {
20
+ const arg = argv[i];
21
+ switch (arg) {
22
+ case "--lang": {
23
+ const value = argv[++i];
24
+ if (!value || !isLang(value)) {
25
+ console.error(`Error: --lang requires "en" or "ja". Got: ${value ?? "(empty)"}`);
26
+ process.exit(1);
27
+ }
28
+ args.lang = value;
29
+ break;
30
+ }
31
+ case "--force":
32
+ args.force = true;
33
+ break;
34
+ case "--dry-run":
35
+ args.dryRun = true;
36
+ break;
37
+ case "-h":
38
+ case "--help":
39
+ args.help = true;
40
+ break;
41
+ case "-v":
42
+ case "--version":
43
+ args.version = true;
44
+ break;
45
+ default:
46
+ console.error(`Unknown option: ${arg}`);
47
+ process.exit(1);
48
+ }
49
+ }
50
+ return args;
51
+ }
52
+ async function main() {
53
+ const args = parseArgs(process.argv.slice(2));
54
+ if (args.version) {
55
+ const pkg = JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf-8"));
56
+ console.log(pkg.version);
57
+ return;
58
+ }
59
+ if (args.help) {
60
+ const msg = getMessages(args.lang);
61
+ console.log(msg.helpText);
62
+ return;
63
+ }
64
+ await install({
65
+ lang: args.lang,
66
+ force: args.force,
67
+ dryRun: args.dryRun,
68
+ cwd: process.cwd(),
69
+ });
70
+ }
71
+ main().catch((err) => {
72
+ console.error(err);
73
+ process.exit(1);
74
+ });
package/dist/i18n.js ADDED
@@ -0,0 +1,71 @@
1
+ const en = {
2
+ downloading: "Downloading takt-sdd...",
3
+ installing: "Installing pieces and facets to .takt/...",
4
+ existsError: (cmd) => `.takt/ already exists. To overwrite, run:\n ${cmd} --force`,
5
+ complete: "Installation complete!",
6
+ dryRunHeader: "[dry-run] The following files would be installed:",
7
+ dryRunItem: (path) => ` ${path}`,
8
+ dryRunSkipped: "[dry-run] No files were written.",
9
+ taktNotFound: "Warning: takt is not installed. Install it first: https://github.com/nrslib/takt",
10
+ tarNotFound: "Error: tar command is required.",
11
+ archiveError: "Error: .takt/ not found in the downloaded archive.",
12
+ helpText: `Usage: npx create-takt-sdd [options]
13
+
14
+ Options:
15
+ --lang <en|ja> Message language (default: en)
16
+ --force Overwrite existing .takt/ directory
17
+ --dry-run Preview without writing files
18
+ -h, --help Show this help
19
+ -v, --version Show version`,
20
+ usageExamples: `
21
+ Installed to: .takt/
22
+
23
+ Usage:
24
+ takt -w sdd -t "description of requirements"
25
+
26
+ Run individual phases:
27
+ takt -w sdd-requirements -t "description of requirements"
28
+ takt -w sdd-design
29
+ takt -w sdd-tasks
30
+ takt -w sdd-impl
31
+ takt -w sdd-validate-impl`,
32
+ };
33
+ const ja = {
34
+ downloading: "takt-sdd をダウンロード中...",
35
+ installing: ".takt/ にピースとファセットをインストール中...",
36
+ existsError: (cmd) => `.takt/ が既に存在します。上書きするには以下を実行してください:\n ${cmd} --force`,
37
+ complete: "インストール完了!",
38
+ dryRunHeader: "[dry-run] 以下のファイルがインストールされます:",
39
+ dryRunItem: (path) => ` ${path}`,
40
+ dryRunSkipped: "[dry-run] ファイルは書き込まれませんでした。",
41
+ taktNotFound: "警告: takt がインストールされていません。先にインストールしてください: https://github.com/nrslib/takt",
42
+ tarNotFound: "エラー: tar コマンドが必要です。",
43
+ archiveError: "エラー: ダウンロードしたアーカイブに .takt/ が見つかりません。",
44
+ helpText: `使い方: npx create-takt-sdd [オプション]
45
+
46
+ オプション:
47
+ --lang <en|ja> メッセージ言語 (デフォルト: en)
48
+ --force 既存の .takt/ を上書き
49
+ --dry-run プレビューのみ(ファイル書き込みなし)
50
+ -h, --help ヘルプを表示
51
+ -v, --version バージョンを表示`,
52
+ usageExamples: `
53
+ インストール先: .takt/
54
+
55
+ 使い方:
56
+ takt -w sdd -t "要件の説明"
57
+
58
+ 各フェーズの個別実行:
59
+ takt -w sdd-requirements -t "要件の説明"
60
+ takt -w sdd-design
61
+ takt -w sdd-tasks
62
+ takt -w sdd-impl
63
+ takt -w sdd-validate-impl`,
64
+ };
65
+ const messages = { en, ja };
66
+ export function getMessages(lang) {
67
+ return messages[lang];
68
+ }
69
+ export function isLang(value) {
70
+ return value === "en" || value === "ja";
71
+ }
@@ -0,0 +1,149 @@
1
+ import { execSync } from "node:child_process";
2
+ import { cpSync, existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
3
+ import https from "node:https";
4
+ import { createWriteStream } from "node:fs";
5
+ import { mkdtempSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join, relative } from "node:path";
8
+ import { getMessages } from "./i18n.js";
9
+ const REPO = "j5ik2o/takt-sdd";
10
+ const BRANCH = "main";
11
+ const TARGET_DIR = ".takt";
12
+ const FACET_DIRS = [
13
+ "pieces",
14
+ "personas",
15
+ "policies",
16
+ "instructions",
17
+ "knowledge",
18
+ "output-contracts",
19
+ ];
20
+ function info(msg) {
21
+ console.log(`\x1b[1;34m==>\x1b[0m ${msg}`);
22
+ }
23
+ function warn(msg) {
24
+ console.log(`\x1b[1;33m==>\x1b[0m ${msg}`);
25
+ }
26
+ function errorExit(msg) {
27
+ console.error(`\x1b[1;31m==>\x1b[0m ${msg}`);
28
+ return process.exit(1);
29
+ }
30
+ function download(url, dest) {
31
+ return new Promise((resolve, reject) => {
32
+ const file = createWriteStream(dest);
33
+ const request = (targetUrl) => {
34
+ https
35
+ .get(targetUrl, (res) => {
36
+ if (res.statusCode === 301 || res.statusCode === 302) {
37
+ const location = res.headers.location;
38
+ if (location) {
39
+ request(location);
40
+ return;
41
+ }
42
+ }
43
+ if (res.statusCode !== 200) {
44
+ reject(new Error(`Download failed: HTTP ${res.statusCode}`));
45
+ return;
46
+ }
47
+ res.pipe(file);
48
+ file.on("finish", () => {
49
+ file.close();
50
+ resolve();
51
+ });
52
+ })
53
+ .on("error", reject);
54
+ };
55
+ request(url);
56
+ });
57
+ }
58
+ function collectFiles(dir, base) {
59
+ const results = [];
60
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
61
+ const full = join(dir, entry.name);
62
+ if (entry.isDirectory()) {
63
+ results.push(...collectFiles(full, base));
64
+ }
65
+ else {
66
+ results.push(relative(base, full));
67
+ }
68
+ }
69
+ return results;
70
+ }
71
+ export async function install(options) {
72
+ const msg = getMessages(options.lang);
73
+ const targetPath = join(options.cwd, TARGET_DIR);
74
+ // takt の存在チェック
75
+ try {
76
+ execSync("which takt", { stdio: "ignore" });
77
+ }
78
+ catch {
79
+ warn(msg.taktNotFound);
80
+ }
81
+ // tar の存在チェック
82
+ try {
83
+ execSync("which tar", { stdio: "ignore" });
84
+ }
85
+ catch {
86
+ errorExit(msg.tarNotFound);
87
+ }
88
+ // 既存ディレクトリチェック
89
+ if (existsSync(join(targetPath, "pieces")) && !options.force) {
90
+ errorExit(msg.existsError("npx create-takt-sdd"));
91
+ }
92
+ // ダウンロード
93
+ info(msg.downloading);
94
+ const tmpDir = mkdtempSync(join(tmpdir(), "takt-sdd-"));
95
+ const archivePath = join(tmpDir, "archive.tar.gz");
96
+ try {
97
+ const tarballUrl = `https://github.com/${REPO}/archive/refs/heads/${BRANCH}.tar.gz`;
98
+ await download(tarballUrl, archivePath);
99
+ execSync(`tar -xzf "${archivePath}" -C "${tmpDir}"`, { stdio: "ignore" });
100
+ const extractedDir = join(tmpDir, `takt-sdd-${BRANCH}`);
101
+ const extractedTakt = join(extractedDir, ".takt");
102
+ if (!existsSync(extractedTakt)) {
103
+ errorExit(msg.archiveError);
104
+ }
105
+ // dry-run: ファイル一覧のみ表示
106
+ if (options.dryRun) {
107
+ info(msg.dryRunHeader);
108
+ for (const dir of FACET_DIRS) {
109
+ const srcDir = join(extractedTakt, dir);
110
+ if (existsSync(srcDir)) {
111
+ for (const file of collectFiles(srcDir, extractedTakt)) {
112
+ console.log(msg.dryRunItem(join(TARGET_DIR, file)));
113
+ }
114
+ }
115
+ }
116
+ const gitignoreSrc = join(extractedTakt, ".gitignore");
117
+ if (existsSync(gitignoreSrc)) {
118
+ console.log(msg.dryRunItem(join(TARGET_DIR, ".gitignore")));
119
+ }
120
+ console.log("");
121
+ info(msg.dryRunSkipped);
122
+ return;
123
+ }
124
+ // インストール
125
+ info(msg.installing);
126
+ mkdirSync(targetPath, { recursive: true });
127
+ for (const dir of FACET_DIRS) {
128
+ const srcDir = join(extractedTakt, dir);
129
+ if (existsSync(srcDir)) {
130
+ const destDir = join(targetPath, dir);
131
+ if (existsSync(destDir)) {
132
+ rmSync(destDir, { recursive: true });
133
+ }
134
+ cpSync(srcDir, destDir, { recursive: true });
135
+ }
136
+ }
137
+ // .gitignore
138
+ const gitignoreSrc = join(extractedTakt, ".gitignore");
139
+ if (existsSync(gitignoreSrc)) {
140
+ cpSync(gitignoreSrc, join(targetPath, ".gitignore"));
141
+ }
142
+ info(msg.complete);
143
+ console.log(msg.usageExamples);
144
+ console.log("");
145
+ }
146
+ finally {
147
+ rmSync(tmpDir, { recursive: true, force: true });
148
+ }
149
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "create-takt-sdd",
3
+ "version": "0.1.0",
4
+ "description": "Installer for takt-sdd: Spec-Driven Development workflow for takt",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-takt-sdd": "./dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc && node scripts/add-shebang.js",
11
+ "prepare": "npm run build"
12
+ },
13
+ "keywords": [
14
+ "takt",
15
+ "sdd",
16
+ "spec-driven-development",
17
+ "ai",
18
+ "workflow"
19
+ ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/j5ik2o/takt-sdd"
23
+ },
24
+ "license": "MIT",
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "devDependencies": {
32
+ "@types/node": "^22.0.0",
33
+ "typescript": "^5.0.0"
34
+ }
35
+ }