create-takt-sdd 0.1.2 → 0.2.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 CHANGED
@@ -16,6 +16,8 @@ function parseArgs(argv) {
16
16
  help: false,
17
17
  version: false,
18
18
  tag: undefined,
19
+ withoutSkills: false,
20
+ refsPath: "references/takt",
19
21
  };
20
22
  for (let i = 0; i < argv.length; i++) {
21
23
  const arg = argv[i];
@@ -44,6 +46,18 @@ function parseArgs(argv) {
44
46
  case "--dry-run":
45
47
  args.dryRun = true;
46
48
  break;
49
+ case "--without-skills":
50
+ args.withoutSkills = true;
51
+ break;
52
+ case "--refs-path": {
53
+ const value = argv[++i];
54
+ if (!value) {
55
+ console.error('Error: --refs-path requires a value (e.g. "references/takt")');
56
+ process.exit(1);
57
+ }
58
+ args.refsPath = value;
59
+ break;
60
+ }
47
61
  case "-h":
48
62
  case "--help":
49
63
  args.help = true;
@@ -76,6 +90,8 @@ async function main() {
76
90
  force: args.force,
77
91
  dryRun: args.dryRun,
78
92
  tag: args.tag,
93
+ withoutSkills: args.withoutSkills,
94
+ refsPath: args.refsPath,
79
95
  cwd: process.cwd(),
80
96
  });
81
97
  }
@@ -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
@@ -7,21 +7,30 @@ const en = {
7
7
  dryRunHeader: "[dry-run] The following files would be installed:",
8
8
  dryRunItem: (path) => ` ${path}`,
9
9
  dryRunSkipped: "[dry-run] No files were written.",
10
- taktNotFound: "Warning: takt is not installed. Install it first: https://github.com/nrslib/takt",
11
10
  tarNotFound: "Error: tar command is required.",
12
11
  archiveError: "Error: .takt/ not found in the downloaded archive.",
13
12
  scriptsAdded: (count) => `Added ${count} npm scripts to package.json`,
14
13
  scriptsSkipped: (keys) => `Skipped existing scripts: ${keys.join(", ")}`,
15
- scriptsCreated: "Created package.json with npm scripts",
14
+ scriptsCreated: "Created package.json with npm scripts and devDependencies",
15
+ depsAdded: (keys) => `Added devDependencies: ${keys.join(", ")}`,
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: (refsPath) => `Downloading takt builtins to ${refsPath}/...`,
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.",
16
23
  helpText: `Usage: npx create-takt-sdd [options]
17
24
 
18
25
  Options:
19
- --tag <version> Version to install ("latest", "0.2.0", default: installer version)
20
- --lang <en|ja> Message language (default: en)
21
- --force Overwrite existing .takt/ directory
22
- --dry-run Preview without writing files
23
- -h, --help Show this help
24
- -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
+ --refs-path <path> Path for takt references (default: references/takt)
32
+ -h, --help Show this help
33
+ -v, --version Show version`,
25
34
  usageExamples: `
26
35
  Installed to: .takt/
27
36
 
@@ -45,21 +54,30 @@ const ja = {
45
54
  dryRunHeader: "[dry-run] 以下のファイルがインストールされます:",
46
55
  dryRunItem: (path) => ` ${path}`,
47
56
  dryRunSkipped: "[dry-run] ファイルは書き込まれませんでした。",
48
- taktNotFound: "警告: takt がインストールされていません。先にインストールしてください: https://github.com/nrslib/takt",
49
57
  tarNotFound: "エラー: tar コマンドが必要です。",
50
58
  archiveError: "エラー: ダウンロードしたアーカイブに .takt/ が見つかりません。",
51
59
  scriptsAdded: (count) => `package.json に ${count} 個の npm scripts を追加しました`,
52
60
  scriptsSkipped: (keys) => `既存のスクリプトをスキップしました: ${keys.join(", ")}`,
53
- scriptsCreated: "npm scripts 付きの package.json を作成しました",
61
+ scriptsCreated: "npm scripts と devDependencies 付きの package.json を作成しました",
62
+ depsAdded: (keys) => `devDependencies を追加しました: ${keys.join(", ")}`,
63
+ installingSkills: ".agent/skills/ に takt スキルをインストール中...",
64
+ skillInstalled: (name) => `スキルをインストールしました: ${name}`,
65
+ skillSymlinked: (name, target) => `シンボリックリンク作成: ${target}/${name} -> .agent/skills/${name}`,
66
+ downloadingTaktRefs: (refsPath) => `${refsPath}/ に takt ビルトインをダウンロード中...`,
67
+ taktRefsInstalled: "takt リファレンスをインストールしました(builtins, docs)",
68
+ taktRefsSkipped: "takt リファレンスは既に存在するためスキップしました",
69
+ taktRefsError: "警告: takt リファレンスのダウンロードに失敗しました。スキルがスタイルガイドを参照できない可能性があります。",
54
70
  helpText: `使い方: npx create-takt-sdd [オプション]
55
71
 
56
72
  オプション:
57
- --tag <version> インストールするバージョン ("latest", "0.2.0", デフォルト: インストーラのバージョン)
58
- --lang <en|ja> メッセージ言語 (デフォルト: en)
59
- --force 既存の .takt/ を上書き
60
- --dry-run プレビューのみ(ファイル書き込みなし)
61
- -h, --help ヘルプを表示
62
- -v, --version バージョンを表示`,
73
+ --tag <version> インストールするバージョン ("latest", "0.2.0", デフォルト: インストーラのバージョン)
74
+ --lang <en|ja> メッセージ言語 (デフォルト: en)
75
+ --force 既存の .takt/ を上書き
76
+ --dry-run プレビューのみ(ファイル書き込みなし)
77
+ --without-skills takt スキルのインストールをスキップ
78
+ --refs-path <path> takt リファレンスのパス(デフォルト: references/takt)
79
+ -h, --help ヘルプを表示
80
+ -v, --version バージョンを表示`,
63
81
  usageExamples: `
64
82
  インストール先: .takt/
65
83
 
package/dist/install.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { execSync } from "node:child_process";
2
- import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } 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";
@@ -7,6 +7,7 @@ import { tmpdir } from "node:os";
7
7
  import { dirname, join, relative, resolve } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { getMessages } from "./i18n.js";
10
+ import { TAKT_REF_HASH } from "./generated/takt-ref.js";
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = dirname(__filename);
12
13
  function getInstallerVersion() {
@@ -15,7 +16,9 @@ function getInstallerVersion() {
15
16
  return pkg.version;
16
17
  }
17
18
  const REPO = "j5ik2o/takt-sdd";
19
+ const TAKT_REPO = "nrslib/takt";
18
20
  const TARGET_DIR = ".takt";
21
+ const DEFAULT_REFS_PATH = "references/takt";
19
22
  const FACET_DIRS = [
20
23
  "pieces",
21
24
  "personas",
@@ -24,6 +27,19 @@ const FACET_DIRS = [
24
27
  "knowledge",
25
28
  "output-contracts",
26
29
  ];
30
+ const TAKT_SKILLS = [
31
+ "takt-analyze",
32
+ "takt-facet",
33
+ "takt-optimize",
34
+ "takt-piece",
35
+ ];
36
+ const SKILL_SYMLINK_TARGETS = [
37
+ ".claude/skills",
38
+ ".codex/skills",
39
+ ];
40
+ const SDD_DEV_DEPENDENCIES = {
41
+ "takt": "^0.20.0",
42
+ };
27
43
  const SDD_SCRIPTS = {
28
44
  "sdd": "takt --pipeline --skip-git --create-worktree no -w sdd -t",
29
45
  "sdd:requirements": "takt --pipeline --skip-git --create-worktree no -w sdd-requirements -t",
@@ -134,13 +150,6 @@ function collectFiles(dir, base) {
134
150
  export async function install(options) {
135
151
  const msg = getMessages(options.lang);
136
152
  const targetPath = join(options.cwd, TARGET_DIR);
137
- // takt の存在チェック
138
- try {
139
- execSync("which takt", { stdio: "ignore" });
140
- }
141
- catch {
142
- warn(msg.taktNotFound);
143
- }
144
153
  // tar の存在チェック
145
154
  try {
146
155
  execSync("which tar", { stdio: "ignore" });
@@ -173,16 +182,27 @@ export async function install(options) {
173
182
  if (options.dryRun) {
174
183
  info(msg.dryRunHeader);
175
184
  for (const dir of FACET_DIRS) {
176
- const srcDir = join(extractedTakt, dir);
185
+ const srcDir = join(extractedTakt, options.lang, dir);
177
186
  if (existsSync(srcDir)) {
178
- for (const file of collectFiles(srcDir, extractedTakt)) {
187
+ for (const file of collectFiles(srcDir, join(extractedTakt, options.lang))) {
179
188
  console.log(msg.dryRunItem(join(TARGET_DIR, file)));
180
189
  }
181
190
  }
182
191
  }
183
- const gitignoreSrc = join(extractedTakt, ".gitignore");
184
- if (existsSync(gitignoreSrc)) {
185
- console.log(msg.dryRunItem(join(TARGET_DIR, ".gitignore")));
192
+ if (!options.withoutSkills) {
193
+ for (const skill of TAKT_SKILLS) {
194
+ const skillSrc = join(extractedDir, ".agent", "skills", skill);
195
+ if (existsSync(skillSrc)) {
196
+ for (const file of collectFiles(skillSrc, join(extractedDir, ".agent", "skills"))) {
197
+ console.log(msg.dryRunItem(join(".agent", "skills", file)));
198
+ }
199
+ for (const target of SKILL_SYMLINK_TARGETS) {
200
+ console.log(msg.dryRunItem(`${target}/${skill} -> ../../.agent/skills/${skill}`));
201
+ }
202
+ }
203
+ }
204
+ console.log(msg.dryRunItem(`${options.refsPath}/builtins/`));
205
+ console.log(msg.dryRunItem(`${options.refsPath}/docs/`));
186
206
  }
187
207
  console.log("");
188
208
  info(msg.dryRunSkipped);
@@ -192,7 +212,7 @@ export async function install(options) {
192
212
  info(msg.installing);
193
213
  mkdirSync(targetPath, { recursive: true });
194
214
  for (const dir of FACET_DIRS) {
195
- const srcDir = join(extractedTakt, dir);
215
+ const srcDir = join(extractedTakt, options.lang, dir);
196
216
  if (existsSync(srcDir)) {
197
217
  const destDir = join(targetPath, dir);
198
218
  if (existsSync(destDir)) {
@@ -201,12 +221,94 @@ export async function install(options) {
201
221
  cpSync(srcDir, destDir, { recursive: true });
202
222
  }
203
223
  }
204
- // .gitignore
205
- const gitignoreSrc = join(extractedTakt, ".gitignore");
206
- if (existsSync(gitignoreSrc)) {
207
- cpSync(gitignoreSrc, join(targetPath, ".gitignore"));
224
+ // .gitignore は takt が初回実行時に自動配置するため、インストーラでは生成しない
225
+ // takt スキルのインストール
226
+ const agentSkillsDir = join(options.cwd, ".agent", "skills");
227
+ const extractedSkillsDir = join(extractedDir, ".agent", "skills");
228
+ if (!options.withoutSkills && existsSync(extractedSkillsDir)) {
229
+ info(msg.installingSkills);
230
+ mkdirSync(agentSkillsDir, { recursive: true });
231
+ for (const skill of TAKT_SKILLS) {
232
+ const skillSrc = join(extractedSkillsDir, skill);
233
+ if (!existsSync(skillSrc))
234
+ continue;
235
+ const skillDest = join(agentSkillsDir, skill);
236
+ if (existsSync(skillDest)) {
237
+ rmSync(skillDest, { recursive: true });
238
+ }
239
+ cpSync(skillSrc, skillDest, { recursive: true });
240
+ // SKILL.md 内の references/takt パスを置換
241
+ if (options.refsPath !== DEFAULT_REFS_PATH) {
242
+ const skillMd = join(skillDest, "SKILL.md");
243
+ if (existsSync(skillMd)) {
244
+ const content = readFileSync(skillMd, "utf-8");
245
+ const updated = content.replaceAll(DEFAULT_REFS_PATH, options.refsPath);
246
+ writeFileSync(skillMd, updated, "utf-8");
247
+ }
248
+ }
249
+ info(msg.skillInstalled(skill));
250
+ }
251
+ // .claude/skills/ と .codex/skills/ にシンボリックリンクを作成
252
+ for (const target of SKILL_SYMLINK_TARGETS) {
253
+ const targetDir = join(options.cwd, target);
254
+ mkdirSync(targetDir, { recursive: true });
255
+ for (const skill of TAKT_SKILLS) {
256
+ if (!existsSync(join(agentSkillsDir, skill)))
257
+ continue;
258
+ const linkPath = join(targetDir, skill);
259
+ if (existsSync(linkPath)) {
260
+ rmSync(linkPath, { recursive: true });
261
+ }
262
+ symlinkSync(`../../.agent/skills/${skill}`, linkPath);
263
+ info(msg.skillSymlinked(skill, target));
264
+ }
265
+ }
208
266
  }
209
- // package.json に npm scripts を追加
267
+ // takt リファレンスのダウンロード(スキルが参照するbuiltins等)
268
+ if (!options.withoutSkills) {
269
+ const refsDir = join(options.cwd, options.refsPath);
270
+ if (!existsSync(join(refsDir, "builtins"))) {
271
+ info(msg.downloadingTaktRefs(options.refsPath));
272
+ const taktTmpDir = mkdtempSync(join(tmpdir(), "takt-refs-"));
273
+ try {
274
+ const taktArchive = join(taktTmpDir, "takt.tar.gz");
275
+ const taktTarball = `https://github.com/${TAKT_REPO}/archive/${TAKT_REF_HASH}.tar.gz`;
276
+ await download(taktTarball, taktArchive);
277
+ execSync(`tar -xzf "${taktArchive}" -C "${taktTmpDir}"`, { stdio: "ignore" });
278
+ // takt-{hash}/ ディレクトリを探す
279
+ const taktExtracted = readdirSync(taktTmpDir).find((d) => d.startsWith("takt-") && statSync(join(taktTmpDir, d)).isDirectory());
280
+ if (taktExtracted) {
281
+ const taktRoot = join(taktTmpDir, taktExtracted);
282
+ mkdirSync(refsDir, { recursive: true });
283
+ // builtins/ をコピー
284
+ const builtinsSrc = join(taktRoot, "builtins");
285
+ if (existsSync(builtinsSrc)) {
286
+ cpSync(builtinsSrc, join(refsDir, "builtins"), { recursive: true });
287
+ }
288
+ // docs/faceted-prompting.ja.md をコピー
289
+ const fpSrc = join(taktRoot, "docs", "faceted-prompting.ja.md");
290
+ if (existsSync(fpSrc)) {
291
+ mkdirSync(join(refsDir, "docs"), { recursive: true });
292
+ cpSync(fpSrc, join(refsDir, "docs", "faceted-prompting.ja.md"));
293
+ }
294
+ info(msg.taktRefsInstalled);
295
+ }
296
+ else {
297
+ warn(msg.taktRefsError);
298
+ }
299
+ }
300
+ catch {
301
+ warn(msg.taktRefsError);
302
+ }
303
+ finally {
304
+ rmSync(taktTmpDir, { recursive: true, force: true });
305
+ }
306
+ }
307
+ else {
308
+ info(msg.taktRefsSkipped);
309
+ }
310
+ }
311
+ // package.json に npm scripts と devDependencies を追加
210
312
  const pkgPath = join(options.cwd, "package.json");
211
313
  if (existsSync(pkgPath)) {
212
314
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
@@ -223,6 +325,15 @@ export async function install(options) {
223
325
  }
224
326
  }
225
327
  pkg.scripts = scripts;
328
+ const devDeps = pkg.devDependencies ?? {};
329
+ const depsAdded = [];
330
+ for (const [key, value] of Object.entries(SDD_DEV_DEPENDENCIES)) {
331
+ if (devDeps[key] === undefined) {
332
+ devDeps[key] = value;
333
+ depsAdded.push(key);
334
+ }
335
+ }
336
+ pkg.devDependencies = devDeps;
226
337
  writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
227
338
  if (added.length > 0) {
228
339
  info(msg.scriptsAdded(added.length));
@@ -230,11 +341,15 @@ export async function install(options) {
230
341
  if (skipped.length > 0) {
231
342
  warn(msg.scriptsSkipped(skipped));
232
343
  }
344
+ if (depsAdded.length > 0) {
345
+ info(msg.depsAdded(depsAdded));
346
+ }
233
347
  }
234
348
  else {
235
349
  const pkg = {
236
350
  private: true,
237
351
  scripts: { ...SDD_SCRIPTS },
352
+ devDependencies: { ...SDD_DEV_DEPENDENCIES },
238
353
  };
239
354
  writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
240
355
  info(msg.scriptsCreated);
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "create-takt-sdd",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
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": [