cursor-sdd 1.0.3 → 1.0.4

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/README.md CHANGED
@@ -8,7 +8,13 @@ Cursor IDE 向けの Spec-Driven Development (SDD) テンプレート、ルー
8
8
  npm install cursor-sdd
9
9
  ```
10
10
 
11
- インストール時に自動的にプロジェクトの `.cursor/` フォルダにファイルがコピーされます。
11
+ インストール時に自動的にプロジェクトの `.cursor/` フォルダにファイルがコピーされます。対話可能な環境では「新規のPJを立ち上げる / 既存PJにアサインする」を選択できます。
12
+
13
+ ### モード指定
14
+
15
+ - 対話プロンプト: `npm install cursor-sdd` 実行時に `new` / `assign` を選択
16
+ - 非対話や CI: `npm install cursor-sdd --mode assign` または環境変数 `CURSOR_SDD_MODE=assign`
17
+ - 省略時デフォルト: `new`
12
18
 
13
19
  ### 手動セットアップ
14
20
 
@@ -33,10 +39,18 @@ Cursor IDE で以下のコマンドが使えるようになります:
33
39
  | `/check-design` | 設計のレビュー |
34
40
  | `/difference-check` | 差分チェック |
35
41
 
42
+ ### `/init` の使い分け
43
+
44
+ - **PJ全体を初期化**: `/init <プロジェクト説明>`
45
+ - **個別画面/機能を初期化**: `/init --feature billing-history <画面の説明>`
46
+ - `--feature` / `-f` で指定したキーが `.cursor/<PJ名>/<feature>` ディレクトリとして作成されます
47
+ - 以降の `/requirements` などは `<PJ名>/<feature>` を引数に渡してください(例: `/requirements my-project/billing-history`)
48
+
36
49
  ## 含まれるファイル
37
50
 
38
51
  ```
39
52
  .cursor/
53
+ ├── (assign 用の内容をコピーする場合は assign/ 配下がコピーされます)
40
54
  ├── commands/ # Cursor コマンド定義
41
55
  │ ├── init.md
42
56
  │ ├── requirements.md
@@ -63,6 +77,8 @@ Cursor IDE で以下のコマンドが使えるようになります:
63
77
  ├── design.md
64
78
  ├── tasks.md
65
79
  └── research.md
80
+
81
+ assign モード時に配布したいファイルはリポジトリ直下の `assign/` に配置してください(例: `assign/commands`, `assign/rules`, `assign/templates`)。
66
82
  ```
67
83
 
68
84
  ## ワークフロー
@@ -85,3 +101,4 @@ Cursor IDE で以下のコマンドが使えるようになります:
85
101
  MIT
86
102
 
87
103
  # cursor-sdd-package
104
+
@@ -0,0 +1,9 @@
1
+ # assign プロファイル
2
+
3
+ 既存プロジェクトにアサインする際のカスタム `.cursor` 一式をここに配置します。
4
+
5
+ - `assign/commands` … アサイン時専用のコマンド定義
6
+ - `assign/rules` … アサイン時専用のルール
7
+ - `assign/templates` … アサイン時専用のテンプレート
8
+
9
+ `npm install cursor-sdd --mode assign` あるいは対話選択で `assign` を選ぶと、このフォルダ配下が `.cursor/` にコピーされます。
@@ -0,0 +1,4 @@
1
+ # assign/commands
2
+
3
+ アサイン時専用の Cursor コマンド定義を置く場所です。
4
+ 既定の `/init` などとは別内容にしたい場合、このディレクトリを編集してください。
@@ -0,0 +1,3 @@
1
+ # assign/rules
2
+
3
+ アサイン時専用のルールを置く場所です。既存プロジェクトのガイドラインに合わせてここを編集してください。
@@ -0,0 +1,3 @@
1
+ # assign/templates
2
+
3
+ アサイン時専用のテンプレートを置く場所です。不要なら空のままでも構いません。
package/bin/setup.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
+ const readline = require('readline');
5
6
 
6
7
  const isAuto = process.argv.includes('--auto');
7
8
  const isForce = process.argv.includes('--force');
@@ -12,36 +13,34 @@ const packageRoot = path.resolve(__dirname, '..');
12
13
  // プロジェクトのルートを取得(node_modules の2つ上)
13
14
  function getProjectRoot() {
14
15
  let current = process.cwd();
15
-
16
+
16
17
  // npm install 経由の場合は INIT_CWD を使用
17
18
  if (process.env.INIT_CWD) {
18
19
  return process.env.INIT_CWD;
19
20
  }
20
-
21
+
21
22
  // node_modules から呼ばれた場合
22
23
  const nodeModulesIndex = __dirname.indexOf('node_modules');
23
24
  if (nodeModulesIndex !== -1) {
24
25
  return __dirname.substring(0, nodeModulesIndex);
25
26
  }
26
-
27
+
27
28
  return current;
28
29
  }
29
30
 
30
31
  const projectRoot = getProjectRoot();
31
32
  const targetDir = path.join(projectRoot, '.cursor');
32
33
 
33
- const folders = ['templates', 'rules', 'commands'];
34
-
35
34
  function copyRecursive(src, dest) {
36
35
  if (!fs.existsSync(src)) return;
37
-
36
+
38
37
  const stat = fs.statSync(src);
39
-
38
+
40
39
  if (stat.isDirectory()) {
41
40
  if (!fs.existsSync(dest)) {
42
41
  fs.mkdirSync(dest, { recursive: true });
43
42
  }
44
-
43
+
45
44
  for (const item of fs.readdirSync(src)) {
46
45
  copyRecursive(path.join(src, item), path.join(dest, item));
47
46
  }
@@ -56,24 +55,81 @@ function copyRecursive(src, dest) {
56
55
  }
57
56
  }
58
57
 
59
- function setup() {
58
+ function getArgValue(flag) {
59
+ const idx = process.argv.indexOf(flag);
60
+ if (idx === -1) return null;
61
+ const next = process.argv[idx + 1];
62
+ if (!next || next.startsWith('-')) return null;
63
+ return next;
64
+ }
65
+
66
+ function normalizeMode(value) {
67
+ if (!value) return null;
68
+ const lower = value.toLowerCase();
69
+ if (lower === 'assign' || lower === 'a') return 'assign';
70
+ if (lower === 'new' || lower === 'n') return 'new';
71
+ return null;
72
+ }
73
+
74
+ function shouldPromptForMode(explicitMode) {
75
+ return !explicitMode && process.stdout.isTTY && process.stdin.isTTY && !process.env.CI;
76
+ }
77
+
78
+ async function askMode() {
79
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
80
+ const answer = await new Promise((resolve) => {
81
+ rl.question('新規PJを立ち上げますか?既存PJにアサインしますか? [n]ew/[a]ssign (default: new): ', resolve);
82
+ });
83
+ rl.close();
84
+ return normalizeMode(answer) || 'new';
85
+ }
86
+
87
+ function resolveMode() {
88
+ const explicitMode = normalizeMode(getArgValue('--mode') || process.env.CURSOR_SDD_MODE);
89
+ if (explicitMode) return Promise.resolve(explicitMode);
90
+ if (shouldPromptForMode(explicitMode)) {
91
+ return askMode();
92
+ }
93
+ return Promise.resolve('new');
94
+ }
95
+
96
+ function getFolders(sourceRoot) {
97
+ if (!fs.existsSync(sourceRoot)) return [];
98
+ return fs
99
+ .readdirSync(sourceRoot)
100
+ .filter((item) => fs.statSync(path.join(sourceRoot, item)).isDirectory());
101
+ }
102
+
103
+ function setup({ mode, sourceRoot, folders }) {
60
104
  console.log('\n🚀 Setting up Cursor SDD...\n');
61
- console.log(`📁 Target: ${targetDir}\n`);
62
-
105
+ console.log(`📁 Target: ${targetDir}`);
106
+ console.log(`🎚️ Mode: ${mode}\n`);
107
+
108
+ // 自動実行時は既存の .cursor がある場合スキップ
109
+ if (isAuto && fs.existsSync(targetDir) && !isForce) {
110
+ console.log('ℹ️ .cursor already exists. Run `npx cursor-sdd --force` to overwrite.');
111
+ process.exit(0);
112
+ }
113
+
63
114
  // .cursor ディレクトリを作成
64
115
  if (!fs.existsSync(targetDir)) {
65
116
  fs.mkdirSync(targetDir, { recursive: true });
66
117
  }
67
-
118
+
119
+ if (!folders.length) {
120
+ console.log(`ℹ️ No folders to copy for mode: ${mode}.`);
121
+ return;
122
+ }
123
+
68
124
  // 各フォルダをコピー
69
125
  for (const folder of folders) {
70
- const src = path.join(packageRoot, folder);
126
+ const src = path.join(sourceRoot, folder);
71
127
  const dest = path.join(targetDir, folder);
72
-
128
+
73
129
  console.log(`📂 ${folder}/`);
74
130
  copyRecursive(src, dest);
75
131
  }
76
-
132
+
77
133
  console.log('\n✨ Cursor SDD setup complete!\n');
78
134
  console.log('Available commands:');
79
135
  console.log(' /init - Initialize project specs');
@@ -84,11 +140,14 @@ function setup() {
84
140
  console.log(' /status - Check status\n');
85
141
  }
86
142
 
87
- // 自動実行時は既存の .cursor がある場合スキップ
88
- if (isAuto && fs.existsSync(targetDir) && !isForce) {
89
- console.log('ℹ️ .cursor already exists. Run `npx cursor-sdd --force` to overwrite.');
90
- process.exit(0);
91
- }
143
+ (async () => {
144
+ const mode = await resolveMode();
145
+ const sourceRoot = mode === 'assign' ? path.join(packageRoot, 'assign') : packageRoot;
146
+ const folders = getFolders(sourceRoot);
92
147
 
93
- setup();
148
+ setup({ mode, sourceRoot, folders });
149
+ })().catch((err) => {
150
+ console.error(err);
151
+ process.exit(1);
152
+ });
94
153
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-sdd",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Cursor SDD (Spec-Driven Development) - AI-powered spec templates, rules and commands for Cursor IDE",
5
5
  "bin": {
6
6
  "cursor-sdd": "./bin/setup.js"
@@ -9,7 +9,8 @@
9
9
  "bin",
10
10
  "templates",
11
11
  "rules",
12
- "commands"
12
+ "commands",
13
+ "assign"
13
14
  ],
14
15
  "scripts": {
15
16
  "postinstall": "node bin/setup.js --auto"
@@ -1,5 +1,10 @@
1
1
  {
2
+ "project_name": "{{PROJECT_NAME}}",
2
3
  "feature_name": "{{FEATURE_NAME}}",
4
+ "feature_key": "{{FEATURE_KEY}}",
5
+ "feature_path": "{{FEATURE_PATH}}",
6
+ "mode": "{{INIT_MODE}}",
7
+ "project_summary": "{{PROJECT_DESCRIPTION}}",
3
8
  "created_at": "{{TIMESTAMP}}",
4
9
  "updated_at": "{{TIMESTAMP}}",
5
10
  "language": "ja",