create-takt-sdd 0.1.0 → 0.1.3

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 CHANGED
@@ -15,6 +15,8 @@ function parseArgs(argv) {
15
15
  dryRun: false,
16
16
  help: false,
17
17
  version: false,
18
+ tag: undefined,
19
+ withoutSkills: false,
18
20
  };
19
21
  for (let i = 0; i < argv.length; i++) {
20
22
  const arg = argv[i];
@@ -28,12 +30,24 @@ function parseArgs(argv) {
28
30
  args.lang = value;
29
31
  break;
30
32
  }
33
+ case "--tag": {
34
+ const value = argv[++i];
35
+ if (!value) {
36
+ console.error('Error: --tag requires a value (e.g. "latest", "0.1.0")');
37
+ process.exit(1);
38
+ }
39
+ args.tag = value;
40
+ break;
41
+ }
31
42
  case "--force":
32
43
  args.force = true;
33
44
  break;
34
45
  case "--dry-run":
35
46
  args.dryRun = true;
36
47
  break;
48
+ case "--without-skills":
49
+ args.withoutSkills = true;
50
+ break;
37
51
  case "-h":
38
52
  case "--help":
39
53
  args.help = true;
@@ -65,6 +79,8 @@ async function main() {
65
79
  lang: args.lang,
66
80
  force: args.force,
67
81
  dryRun: args.dryRun,
82
+ tag: args.tag,
83
+ withoutSkills: args.withoutSkills,
68
84
  cwd: process.cwd(),
69
85
  });
70
86
  }
@@ -0,0 +1,2 @@
1
+ // Auto-generated by scripts/embed-takt-ref.js — do not edit
2
+ export const TAKT_REF_HASH = "78dead335de8ae6d5477eac82c581ce8273ccf35";
package/dist/i18n.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const en = {
2
2
  downloading: "Downloading takt-sdd...",
3
+ downloadingVersion: (tag) => `Downloading takt-sdd ${tag}...`,
3
4
  installing: "Installing pieces and facets to .takt/...",
4
5
  existsError: (cmd) => `.takt/ already exists. To overwrite, run:\n ${cmd} --force`,
5
6
  complete: "Installation complete!",
@@ -9,29 +10,43 @@ const en = {
9
10
  taktNotFound: "Warning: takt is not installed. Install it first: https://github.com/nrslib/takt",
10
11
  tarNotFound: "Error: tar command is required.",
11
12
  archiveError: "Error: .takt/ not found in the downloaded archive.",
13
+ scriptsAdded: (count) => `Added ${count} npm scripts to package.json`,
14
+ scriptsSkipped: (keys) => `Skipped existing scripts: ${keys.join(", ")}`,
15
+ scriptsCreated: "Created package.json with npm scripts",
16
+ installingSkills: "Installing takt skills to .agent/skills/...",
17
+ skillInstalled: (name) => `Installed skill: ${name}`,
18
+ skillSymlinked: (name, target) => `Symlinked ${target}/${name} -> .agent/skills/${name}`,
19
+ downloadingTaktRefs: "Downloading takt builtins to references/takt/...",
20
+ taktRefsInstalled: "Installed takt references (builtins, docs)",
21
+ taktRefsSkipped: "Takt references already exist, skipping",
22
+ taktRefsError: "Warning: Failed to download takt references. Skills may not find style guides.",
12
23
  helpText: `Usage: npx create-takt-sdd [options]
13
24
 
14
25
  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`,
26
+ --tag <version> Version to install ("latest", "0.2.0", default: installer version)
27
+ --lang <en|ja> Message language (default: en)
28
+ --force Overwrite existing .takt/ directory
29
+ --dry-run Preview without writing files
30
+ --without-skills Skip installing takt skills to .agent/skills/
31
+ -h, --help Show this help
32
+ -v, --version Show version`,
20
33
  usageExamples: `
21
34
  Installed to: .takt/
22
35
 
23
36
  Usage:
24
- takt -w sdd -t "description of requirements"
37
+ npm run sdd -- "description of requirements"
25
38
 
26
39
  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`,
40
+ npm run sdd:requirements -- "description of requirements"
41
+ npm run sdd:design -- "feature={feature}"
42
+ npm run sdd:validate-design -- "feature={feature}"
43
+ npm run sdd:tasks -- "feature={feature}"
44
+ npm run sdd:impl -- "feature={feature}"
45
+ npm run sdd:validate-impl -- "feature={feature}"`,
32
46
  };
33
47
  const ja = {
34
48
  downloading: "takt-sdd をダウンロード中...",
49
+ downloadingVersion: (tag) => `takt-sdd ${tag} をダウンロード中...`,
35
50
  installing: ".takt/ にピースとファセットをインストール中...",
36
51
  existsError: (cmd) => `.takt/ が既に存在します。上書きするには以下を実行してください:\n ${cmd} --force`,
37
52
  complete: "インストール完了!",
@@ -41,26 +56,39 @@ const ja = {
41
56
  taktNotFound: "警告: takt がインストールされていません。先にインストールしてください: https://github.com/nrslib/takt",
42
57
  tarNotFound: "エラー: tar コマンドが必要です。",
43
58
  archiveError: "エラー: ダウンロードしたアーカイブに .takt/ が見つかりません。",
59
+ scriptsAdded: (count) => `package.json に ${count} 個の npm scripts を追加しました`,
60
+ scriptsSkipped: (keys) => `既存のスクリプトをスキップしました: ${keys.join(", ")}`,
61
+ scriptsCreated: "npm scripts 付きの package.json を作成しました",
62
+ installingSkills: ".agent/skills/ に takt スキルをインストール中...",
63
+ skillInstalled: (name) => `スキルをインストールしました: ${name}`,
64
+ skillSymlinked: (name, target) => `シンボリックリンク作成: ${target}/${name} -> .agent/skills/${name}`,
65
+ downloadingTaktRefs: "references/takt/ に takt ビルトインをダウンロード中...",
66
+ taktRefsInstalled: "takt リファレンスをインストールしました(builtins, docs)",
67
+ taktRefsSkipped: "takt リファレンスは既に存在するためスキップしました",
68
+ taktRefsError: "警告: takt リファレンスのダウンロードに失敗しました。スキルがスタイルガイドを参照できない可能性があります。",
44
69
  helpText: `使い方: npx create-takt-sdd [オプション]
45
70
 
46
71
  オプション:
47
- --lang <en|ja> メッセージ言語 (デフォルト: en)
48
- --force 既存の .takt/ を上書き
49
- --dry-run プレビューのみ(ファイル書き込みなし)
50
- -h, --help ヘルプを表示
51
- -v, --version バージョンを表示`,
72
+ --tag <version> インストールするバージョン ("latest", "0.2.0", デフォルト: インストーラのバージョン)
73
+ --lang <en|ja> メッセージ言語 (デフォルト: en)
74
+ --force 既存の .takt/ を上書き
75
+ --dry-run プレビューのみ(ファイル書き込みなし)
76
+ --without-skills takt スキルのインストールをスキップ
77
+ -h, --help ヘルプを表示
78
+ -v, --version バージョンを表示`,
52
79
  usageExamples: `
53
80
  インストール先: .takt/
54
81
 
55
82
  使い方:
56
- takt -w sdd -t "要件の説明"
83
+ npm run sdd -- "要件の説明"
57
84
 
58
85
  各フェーズの個別実行:
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`,
86
+ npm run sdd:requirements -- "要件の説明"
87
+ npm run sdd:design -- "feature={feature}"
88
+ npm run sdd:validate-design -- "feature={feature}"
89
+ npm run sdd:tasks -- "feature={feature}"
90
+ npm run sdd:impl -- "feature={feature}"
91
+ npm run sdd:validate-impl -- "feature={feature}"`,
64
92
  };
65
93
  const messages = { en, ja };
66
94
  export function getMessages(lang) {
package/dist/install.js CHANGED
@@ -1,13 +1,22 @@
1
1
  import { execSync } from "node:child_process";
2
- import { cpSync, existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
2
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, symlinkSync, statSync, writeFileSync } from "node:fs";
3
3
  import https from "node:https";
4
4
  import { createWriteStream } from "node:fs";
5
5
  import { mkdtempSync } from "node:fs";
6
6
  import { tmpdir } from "node:os";
7
- import { join, relative } from "node:path";
7
+ import { dirname, join, relative, resolve } from "node:path";
8
+ import { fileURLToPath } from "node:url";
8
9
  import { getMessages } from "./i18n.js";
10
+ import { TAKT_REF_HASH } from "./generated/takt-ref.js";
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ function getInstallerVersion() {
14
+ const pkgPath = resolve(__dirname, "..", "package.json");
15
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
16
+ return pkg.version;
17
+ }
9
18
  const REPO = "j5ik2o/takt-sdd";
10
- const BRANCH = "main";
19
+ const TAKT_REPO = "nrslib/takt";
11
20
  const TARGET_DIR = ".takt";
12
21
  const FACET_DIRS = [
13
22
  "pieces",
@@ -17,6 +26,28 @@ const FACET_DIRS = [
17
26
  "knowledge",
18
27
  "output-contracts",
19
28
  ];
29
+ const TAKT_SKILLS = [
30
+ "takt-analyze",
31
+ "takt-facet",
32
+ "takt-optimize",
33
+ "takt-piece",
34
+ ];
35
+ const SKILL_SYMLINK_TARGETS = [
36
+ ".claude/skills",
37
+ ".codex/skills",
38
+ ];
39
+ const SDD_SCRIPTS = {
40
+ "sdd": "takt --pipeline --skip-git --create-worktree no -w sdd -t",
41
+ "sdd:requirements": "takt --pipeline --skip-git --create-worktree no -w sdd-requirements -t",
42
+ "sdd:validate-gap": "takt --pipeline --skip-git --create-worktree no -w sdd-validate-gap -t",
43
+ "sdd:design": "takt --pipeline --skip-git --create-worktree no -w sdd-design -t",
44
+ "sdd:validate-design": "takt --pipeline --skip-git --create-worktree no -w sdd-validate-design -t",
45
+ "sdd:tasks": "takt --pipeline --skip-git --create-worktree no -w sdd-tasks -t",
46
+ "sdd:impl": "takt --pipeline --skip-git --create-worktree no -w sdd-impl -t",
47
+ "sdd:validate-impl": "takt --pipeline --skip-git --create-worktree no -w sdd-validate-impl -t",
48
+ "steering": "takt --pipeline --skip-git --create-worktree no -w steering -t",
49
+ "steering:custom": "takt --pipeline --skip-git --create-worktree no -w steering-custom -t",
50
+ };
20
51
  function info(msg) {
21
52
  console.log(`\x1b[1;34m==>\x1b[0m ${msg}`);
22
53
  }
@@ -27,6 +58,50 @@ function errorExit(msg) {
27
58
  console.error(`\x1b[1;31m==>\x1b[0m ${msg}`);
28
59
  return process.exit(1);
29
60
  }
61
+ function fetchJson(url) {
62
+ return new Promise((resolve, reject) => {
63
+ const request = (targetUrl) => {
64
+ https
65
+ .get(targetUrl, { headers: { "User-Agent": "create-takt-sdd" } }, (res) => {
66
+ if (res.statusCode === 301 || res.statusCode === 302) {
67
+ const location = res.headers.location;
68
+ if (location) {
69
+ request(location);
70
+ return;
71
+ }
72
+ }
73
+ if (res.statusCode !== 200) {
74
+ reject(new Error(`Fetch failed: HTTP ${res.statusCode}`));
75
+ return;
76
+ }
77
+ let data = "";
78
+ res.on("data", (chunk) => { data += chunk.toString(); });
79
+ res.on("end", () => resolve(data));
80
+ })
81
+ .on("error", reject);
82
+ };
83
+ request(url);
84
+ });
85
+ }
86
+ async function fetchLatestTag() {
87
+ const data = await fetchJson(`https://api.github.com/repos/${REPO}/releases/latest`);
88
+ const release = JSON.parse(data);
89
+ const tagName = release.tag_name;
90
+ if (!tagName) {
91
+ throw new Error("No releases found");
92
+ }
93
+ return tagName;
94
+ }
95
+ function resolveTag(tagOption, installerVersion) {
96
+ if (tagOption === undefined) {
97
+ return `v${installerVersion}`;
98
+ }
99
+ if (tagOption === "latest") {
100
+ return fetchLatestTag();
101
+ }
102
+ // Accept both "v0.1.0" and "0.1.0"
103
+ return tagOption.startsWith("v") ? tagOption : `v${tagOption}`;
104
+ }
30
105
  function download(url, dest) {
31
106
  return new Promise((resolve, reject) => {
32
107
  const file = createWriteStream(dest);
@@ -94,10 +169,14 @@ export async function install(options) {
94
169
  const tmpDir = mkdtempSync(join(tmpdir(), "takt-sdd-"));
95
170
  const archivePath = join(tmpDir, "archive.tar.gz");
96
171
  try {
97
- const tarballUrl = `https://github.com/${REPO}/archive/refs/heads/${BRANCH}.tar.gz`;
172
+ const installerVersion = getInstallerVersion();
173
+ const tag = await resolveTag(options.tag, installerVersion);
174
+ const version = tag.startsWith("v") ? tag.slice(1) : tag;
175
+ info(msg.downloadingVersion(tag));
176
+ const tarballUrl = `https://github.com/${REPO}/archive/refs/tags/${tag}.tar.gz`;
98
177
  await download(tarballUrl, archivePath);
99
178
  execSync(`tar -xzf "${archivePath}" -C "${tmpDir}"`, { stdio: "ignore" });
100
- const extractedDir = join(tmpDir, `takt-sdd-${BRANCH}`);
179
+ const extractedDir = join(tmpDir, `takt-sdd-${version}`);
101
180
  const extractedTakt = join(extractedDir, ".takt");
102
181
  if (!existsSync(extractedTakt)) {
103
182
  errorExit(msg.archiveError);
@@ -106,16 +185,25 @@ export async function install(options) {
106
185
  if (options.dryRun) {
107
186
  info(msg.dryRunHeader);
108
187
  for (const dir of FACET_DIRS) {
109
- const srcDir = join(extractedTakt, dir);
188
+ const srcDir = join(extractedTakt, options.lang, dir);
110
189
  if (existsSync(srcDir)) {
111
- for (const file of collectFiles(srcDir, extractedTakt)) {
190
+ for (const file of collectFiles(srcDir, join(extractedTakt, options.lang))) {
112
191
  console.log(msg.dryRunItem(join(TARGET_DIR, file)));
113
192
  }
114
193
  }
115
194
  }
116
- const gitignoreSrc = join(extractedTakt, ".gitignore");
117
- if (existsSync(gitignoreSrc)) {
118
- console.log(msg.dryRunItem(join(TARGET_DIR, ".gitignore")));
195
+ if (!options.withoutSkills) {
196
+ for (const skill of TAKT_SKILLS) {
197
+ const skillSrc = join(extractedDir, ".agent", "skills", skill);
198
+ if (existsSync(skillSrc)) {
199
+ for (const file of collectFiles(skillSrc, join(extractedDir, ".agent", "skills"))) {
200
+ console.log(msg.dryRunItem(join(".agent", "skills", file)));
201
+ }
202
+ for (const target of SKILL_SYMLINK_TARGETS) {
203
+ console.log(msg.dryRunItem(`${target}/${skill} -> ../../.agent/skills/${skill}`));
204
+ }
205
+ }
206
+ }
119
207
  }
120
208
  console.log("");
121
209
  info(msg.dryRunSkipped);
@@ -125,7 +213,7 @@ export async function install(options) {
125
213
  info(msg.installing);
126
214
  mkdirSync(targetPath, { recursive: true });
127
215
  for (const dir of FACET_DIRS) {
128
- const srcDir = join(extractedTakt, dir);
216
+ const srcDir = join(extractedTakt, options.lang, dir);
129
217
  if (existsSync(srcDir)) {
130
218
  const destDir = join(targetPath, dir);
131
219
  if (existsSync(destDir)) {
@@ -134,10 +222,116 @@ export async function install(options) {
134
222
  cpSync(srcDir, destDir, { recursive: true });
135
223
  }
136
224
  }
137
- // .gitignore
138
- const gitignoreSrc = join(extractedTakt, ".gitignore");
139
- if (existsSync(gitignoreSrc)) {
140
- cpSync(gitignoreSrc, join(targetPath, ".gitignore"));
225
+ // .gitignore は takt が初回実行時に自動配置するため、インストーラでは生成しない
226
+ // takt スキルのインストール
227
+ const agentSkillsDir = join(options.cwd, ".agent", "skills");
228
+ const extractedSkillsDir = join(extractedDir, ".agent", "skills");
229
+ if (!options.withoutSkills && existsSync(extractedSkillsDir)) {
230
+ info(msg.installingSkills);
231
+ mkdirSync(agentSkillsDir, { recursive: true });
232
+ for (const skill of TAKT_SKILLS) {
233
+ const skillSrc = join(extractedSkillsDir, skill);
234
+ if (!existsSync(skillSrc))
235
+ continue;
236
+ const skillDest = join(agentSkillsDir, skill);
237
+ if (existsSync(skillDest)) {
238
+ rmSync(skillDest, { recursive: true });
239
+ }
240
+ cpSync(skillSrc, skillDest, { recursive: true });
241
+ info(msg.skillInstalled(skill));
242
+ }
243
+ // .claude/skills/ と .codex/skills/ にシンボリックリンクを作成
244
+ for (const target of SKILL_SYMLINK_TARGETS) {
245
+ const targetDir = join(options.cwd, target);
246
+ mkdirSync(targetDir, { recursive: true });
247
+ for (const skill of TAKT_SKILLS) {
248
+ if (!existsSync(join(agentSkillsDir, skill)))
249
+ continue;
250
+ const linkPath = join(targetDir, skill);
251
+ if (existsSync(linkPath)) {
252
+ rmSync(linkPath, { recursive: true });
253
+ }
254
+ symlinkSync(`../../.agent/skills/${skill}`, linkPath);
255
+ info(msg.skillSymlinked(skill, target));
256
+ }
257
+ }
258
+ }
259
+ // takt リファレンスのダウンロード(スキルが参照するbuiltins等)
260
+ if (!options.withoutSkills) {
261
+ const refsDir = join(options.cwd, "references", "takt");
262
+ if (!existsSync(join(refsDir, "builtins"))) {
263
+ info(msg.downloadingTaktRefs);
264
+ const taktTmpDir = mkdtempSync(join(tmpdir(), "takt-refs-"));
265
+ try {
266
+ const taktArchive = join(taktTmpDir, "takt.tar.gz");
267
+ const taktTarball = `https://github.com/${TAKT_REPO}/archive/${TAKT_REF_HASH}.tar.gz`;
268
+ await download(taktTarball, taktArchive);
269
+ execSync(`tar -xzf "${taktArchive}" -C "${taktTmpDir}"`, { stdio: "ignore" });
270
+ // takt-{hash}/ ディレクトリを探す
271
+ const taktExtracted = readdirSync(taktTmpDir).find((d) => d.startsWith("takt-") && statSync(join(taktTmpDir, d)).isDirectory());
272
+ if (taktExtracted) {
273
+ const taktRoot = join(taktTmpDir, taktExtracted);
274
+ mkdirSync(refsDir, { recursive: true });
275
+ // builtins/ をコピー
276
+ const builtinsSrc = join(taktRoot, "builtins");
277
+ if (existsSync(builtinsSrc)) {
278
+ cpSync(builtinsSrc, join(refsDir, "builtins"), { recursive: true });
279
+ }
280
+ // docs/faceted-prompting.ja.md をコピー
281
+ const fpSrc = join(taktRoot, "docs", "faceted-prompting.ja.md");
282
+ if (existsSync(fpSrc)) {
283
+ mkdirSync(join(refsDir, "docs"), { recursive: true });
284
+ cpSync(fpSrc, join(refsDir, "docs", "faceted-prompting.ja.md"));
285
+ }
286
+ info(msg.taktRefsInstalled);
287
+ }
288
+ else {
289
+ warn(msg.taktRefsError);
290
+ }
291
+ }
292
+ catch {
293
+ warn(msg.taktRefsError);
294
+ }
295
+ finally {
296
+ rmSync(taktTmpDir, { recursive: true, force: true });
297
+ }
298
+ }
299
+ else {
300
+ info(msg.taktRefsSkipped);
301
+ }
302
+ }
303
+ // package.json に npm scripts を追加
304
+ const pkgPath = join(options.cwd, "package.json");
305
+ if (existsSync(pkgPath)) {
306
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
307
+ const scripts = pkg.scripts ?? {};
308
+ const added = [];
309
+ const skipped = [];
310
+ for (const [key, value] of Object.entries(SDD_SCRIPTS)) {
311
+ if (scripts[key] !== undefined) {
312
+ skipped.push(key);
313
+ }
314
+ else {
315
+ scripts[key] = value;
316
+ added.push(key);
317
+ }
318
+ }
319
+ pkg.scripts = scripts;
320
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
321
+ if (added.length > 0) {
322
+ info(msg.scriptsAdded(added.length));
323
+ }
324
+ if (skipped.length > 0) {
325
+ warn(msg.scriptsSkipped(skipped));
326
+ }
327
+ }
328
+ else {
329
+ const pkg = {
330
+ private: true,
331
+ scripts: { ...SDD_SCRIPTS },
332
+ };
333
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
334
+ info(msg.scriptsCreated);
141
335
  }
142
336
  info(msg.complete);
143
337
  console.log(msg.usageExamples);
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "create-takt-sdd",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Installer for takt-sdd: Spec-Driven Development workflow for takt",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-takt-sdd": "./dist/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "build": "tsc && node scripts/add-shebang.js",
10
+ "build": "node scripts/embed-takt-ref.js && tsc && node scripts/add-shebang.js",
11
11
  "prepare": "npm run build"
12
12
  },
13
13
  "keywords": [
@@ -21,7 +21,7 @@
21
21
  "type": "git",
22
22
  "url": "https://github.com/j5ik2o/takt-sdd"
23
23
  },
24
- "license": "MIT",
24
+ "license": "(MIT OR Apache-2.0)",
25
25
  "engines": {
26
26
  "node": ">=18"
27
27
  },