create-agentic-dev-env 0.2.4 → 0.2.6

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/bin/create.js CHANGED
@@ -20,8 +20,10 @@ fs.cpSync(path.join(__dirname, '..', 'template'), dest, { recursive: true })
20
20
  // npm publish 會剝掉 dot 開頭的檔案/目錄,template 內以無點名稱存放,複製後改名
21
21
  fs.renameSync(path.join(dest, 'gitignore'), path.join(dest, '.gitignore'))
22
22
  fs.renameSync(path.join(dest, 'dot-claude'), path.join(dest, '.claude'))
23
- // add-service 主要使用場景在 ADE repo 內,symlink 進 .claude/skills/ 讓它在本 repo 也可觸發(單一真相在 skills/)
24
- fs.symlinkSync(path.join('..', '..', 'skills', 'ade-add-service'), path.join(dest, '.claude', 'skills', 'ade-add-service'), 'dir')
23
+ // 這些 skill ADE repo 內也要可觸發,symlink 進 .claude/skills/(單一真相在 skills/)
24
+ for (const s of ['ade-add-service', 'ade-add-skill']) {
25
+ fs.symlinkSync(path.join('..', '..', 'skills', s), path.join(dest, '.claude', 'skills', s), 'dir')
26
+ }
25
27
  for (const rel of fs.readdirSync(dest, { recursive: true })) {
26
28
  const p = path.join(dest, rel)
27
29
  if (!fs.statSync(p).isFile()) continue
package/lib/runner.js CHANGED
@@ -8,10 +8,12 @@ const END = '<!-- ADE:END -->'
8
8
 
9
9
  function run(cmd, pkgDir) {
10
10
  try {
11
- if (cmd === 'init') init(process.cwd(), pkgDir)
12
- else if (cmd === 'update') update(process.cwd())
11
+ const i = process.argv.indexOf('--workspaces')
12
+ const ws = i !== -1 ? process.argv[i + 1] : undefined
13
+ if (cmd === 'init') init(process.cwd(), pkgDir, ws)
14
+ else if (cmd === 'update') update(process.cwd(), ws)
13
15
  else {
14
- console.error('Usage: init | update')
16
+ console.error('Usage: init | update [--workspaces <path>]')
15
17
  process.exit(1)
16
18
  }
17
19
  } catch (e) {
@@ -20,15 +22,15 @@ function run(cmd, pkgDir) {
20
22
  }
21
23
  }
22
24
 
23
- function init(cwd, srcDir) {
25
+ function init(cwd, srcDir, ws) {
24
26
  if (fs.existsSync(path.join(cwd, '.ade.json'))) {
25
27
  throw new Error('Already initialized here; run update instead')
26
28
  }
27
- install(cwd, srcDir)
29
+ install(cwd, srcDir, ws)
28
30
  console.log('ADE init done')
29
31
  }
30
32
 
31
- function update(cwd) {
33
+ function update(cwd, ws) {
32
34
  const cfgPath = path.join(cwd, '.ade.json')
33
35
  if (!fs.existsSync(cfgPath)) throw new Error('Not initialized here; run init first')
34
36
  const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'))
@@ -40,7 +42,7 @@ function update(cwd) {
40
42
  try {
41
43
  execSync(`git clone --depth 1 "${cfg.source}" "${path.join(tmp, 'repo')}"`, { stdio: 'inherit' })
42
44
  removeManaged(cwd)
43
- install(cwd, path.join(tmp, 'repo'))
45
+ install(cwd, path.join(tmp, 'repo'), ws)
44
46
  } finally {
45
47
  fs.rmSync(tmp, { recursive: true, force: true })
46
48
  }
@@ -57,7 +59,7 @@ function removeManaged(cwd) {
57
59
  }
58
60
  }
59
61
 
60
- function install(cwd, srcDir) {
62
+ function install(cwd, srcDir, wsOverride) {
61
63
  // 先驗證再動手:update 只清理 ade- 前綴,非前綴 skill 裝了就清不掉
62
64
  const srcSkills = path.join(srcDir, 'skills')
63
65
  const skillDirs = fs.existsSync(srcSkills)
@@ -90,15 +92,20 @@ function install(cwd, srcDir) {
90
92
  }
91
93
  fs.writeFileSync(claudePath, md)
92
94
 
93
- fs.mkdirSync(path.join(cwd, 'workspaces'), { recursive: true })
94
- const giPath = path.join(cwd, '.gitignore')
95
- const gi = fs.existsSync(giPath) ? fs.readFileSync(giPath, 'utf8') : ''
96
- if (!gi.split('\n').some((l) => l.trim().replace(/\/$/, '') === 'workspaces')) {
97
- fs.writeFileSync(giPath, (gi ? gi.trimEnd() + '\n' : '') + 'workspaces/\n')
98
- }
99
-
100
95
  const prevPath = path.join(cwd, '.ade.json')
101
96
  const prev = fs.existsSync(prevPath) ? JSON.parse(fs.readFileSync(prevPath, 'utf8')) : {}
97
+
98
+ // 作業區可指向既有的 repo 存放資料夾(--workspaces 或 .ade.json 的 workspaces),避免重複 clone
99
+ const ws = wsOverride || prev.workspaces || 'workspaces'
100
+ fs.mkdirSync(path.resolve(cwd, ws), { recursive: true })
101
+ const rel = path.relative(cwd, path.resolve(cwd, ws)).split(path.sep).join('/')
102
+ if (rel && !rel.startsWith('..') && !path.isAbsolute(rel)) {
103
+ const giPath = path.join(cwd, '.gitignore')
104
+ const gi = fs.existsSync(giPath) ? fs.readFileSync(giPath, 'utf8') : ''
105
+ if (!gi.split('\n').some((l) => l.trim().replace(/\/$/, '') === rel)) {
106
+ fs.writeFileSync(giPath, (gi ? gi.trimEnd() + '\n' : '') + rel + '/\n')
107
+ }
108
+ }
102
109
  let source = null
103
110
  try {
104
111
  const pkg = JSON.parse(fs.readFileSync(path.join(srcDir, 'package.json'), 'utf8'))
@@ -114,7 +121,7 @@ function install(cwd, srcDir) {
114
121
  } catch {}
115
122
  fs.writeFileSync(
116
123
  prevPath,
117
- JSON.stringify({ source: source || prev.source || null, commit }, null, 2) + '\n'
124
+ JSON.stringify({ source: source || prev.source || null, commit, workspaces: ws }, null, 2) + '\n'
118
125
  )
119
126
  if (!source && !prev.source) {
120
127
  console.warn('Warning: repository.url is not set in the ADE repo package.json; update will not work — set source in .ade.json manually')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agentic-dev-env",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Scaffold and manage team Agentic Dev Environment (ADE) knowledge repos",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,8 +40,8 @@ init 會在當前目錄建立:
40
40
 
41
41
  - `CLAUDE.md` 的 `<!-- ADE:BEGIN/END -->` managed 區段(原有內容不動)
42
42
  - `.claude/ade/knowledge/` 知識庫副本、`.claude/skills/ade-*/` skills
43
- - `workspaces/`(agent clone 服務 repo 的作業區,自動加入 .gitignore)
44
- - `.ade.json`(記錄來源與版本)
43
+ - 作業區(agent clone 服務 repo 的位置;預設 `workspaces/` 並自動加入 .gitignore。已有固定放 repo 的資料夾時用 `init --workspaces <path>` 指向它,可為相對或絕對路徑,已下載的 repo 直接沿用不重 clone;路徑在本目錄外就不動 .gitignore
44
+ - `.ade.json`(設定檔:`source` 來源、`commit` 版本、`workspaces` 作業區路徑。直接編輯即可改設定,改 `workspaces` 後不需重跑任何指令)
45
45
 
46
46
  之後同指令改跑 `update` 拉取最新知識(update 會直接 clone 最新版,不受 dlx 快取影響)。
47
47
 
@@ -53,8 +53,8 @@ knowledge/
53
53
  ├── process/ # 團隊流程知識
54
54
  ├── specs/ # 當前功能規格,持續迭代的真相來源
55
55
  └── prd/ # 一次開發一檔的需求文件,歷史文件不迭代
56
- skills/ # init 時注入工作目錄的 .claude/skills/(五支,見下方 Skills 一覽)
57
- .claude/skills/ # 在本 repo 內工作用的 skills(三支+ade-add-service 的 symlink,見下方 Skills 一覽)
56
+ skills/ # init 時注入工作目錄的 .claude/skills/(六支,見下方 Skills 一覽)
57
+ .claude/skills/ # 在本 repo 內工作用的 skills(三支+add-service、add-skill 的 symlink,見下方 Skills 一覽)
58
58
  claude-md/ # CLAUDE.md managed 區段的內容
59
59
  ```
60
60
 
@@ -70,6 +70,8 @@ claude-md/ # CLAUDE.md managed 區段的內容
70
70
 
71
71
  - **`ade-spec-audit`** — spec 的定期健檢。PRD 流程只覆蓋「計畫內」的開發,hotfix 和直接改 code 的計畫外變更會讓 spec 悄悄失真——這支 skill 補上這條偵測路徑。觸發後它逐份 spec 對照相關服務的實作(缺的 repo 會先 clone),找出「行為已變、功能已移除、實作有但 spec 沒記載」的漂移,產出清單讓人確認該修 spec 還是該修 code(漂移不一定是文件錯,也可能是實作偏離了規格),確認後開 PR 修正。建議在 release 後或定期執行。
72
72
 
73
+ - **`ade-add-skill`** — 為 ADE 生態新增 skill 的 meta-skill。使用者說「新增 skill」「把這個做成 skill」時觸發。它先問使用對象再決定放置位置:消費端工作目錄用 → `skills/ade-*`(init/update 注入);本 ADE repo 內用 → `.claude/skills/ade-*`;兩邊都用 → 放 `skills/` 加 symlink。並落實命名規則(`ade-` 前綴)、context 紀律與 README 同步。**本 ADE repo 內也可用**(`.claude/skills/` 有 symlink)。
74
+
73
75
  - **`ade-add-process`** — 為團隊建立或修改流程慣例的 meta-skill。使用者說「以後都這樣做」「定一個慣例」時觸發。它依三層機制選載體:無條件約束 → `claude-md/section.md` 加一行指標;有觸發時機的程序 → 新增一支 `ade-` 前綴 skill;細節 → `process/` 一主題一檔。並執行 context 紀律:常駐層只寫「何時做+去哪看」(參考技巧)、常駐規則超過 10 行時新增前必須與使用者確認取捨。最後走 `ade-contribute` 流程開 PR。
74
76
 
75
77
  ### 在本 ADE repo 內工作用的 skills(PO/維護者在本 repo 開 Claude Code 使用)
@@ -6,7 +6,7 @@
6
6
  ### Session 開始時
7
7
 
8
8
  - **檢查知識新鮮度**:讀 `.ade.json`,執行 `git ls-remote <source> HEAD`,若 hash 與 `commit` 不符,提醒使用者執行 update 後再繼續(勿自行修改 managed 內容)
9
- - Session 一律從本目錄(hub 根)開啟;在 `workspaces/<service>/` 內開啟會失去 ade skills
9
+ - Session 一律從本目錄(hub 根)開啟;在作業區的服務 repo 內開啟會失去 ade skills
10
10
 
11
11
  ### 知識分層
12
12
 
@@ -16,7 +16,7 @@
16
16
 
17
17
  1. 讀 `.claude/ade/knowledge/services/index.md`(全服務總覽)定位目標服務
18
18
  2. 讀 `.claude/ade/knowledge/services/<service>.yaml` 取得定位、repo、技術棧、依賴關係
19
- 3. `workspaces/<service>/` 不存在,依 `repo.url` / `repo.branch` clone 到 `workspaces/<service>/`
19
+ 3. 服務 repo 放在作業區(路徑見 `.ade.json` 的 `workspaces`,預設 `workspaces/`);`<作業區>/<service>/` 已存在就直接用,不存在才依 `repo.url` / `repo.branch` clone 進去
20
20
  4. 讀服務 repo 自身的 README/CLAUDE.md/AGENTS.md 完成安裝、啟動、測試;跨服務流程慣例見 `knowledge/process/`,功能規格見 `knowledge/specs/`
21
21
 
22
22
  ### 知識維護
@@ -19,6 +19,6 @@
19
19
 
20
20
  ## Agent 執行時
21
21
 
22
- - `workspaces/` 下任何服務 repo commit 都遵守本慣例
22
+ - 在作業區(`.ade.json` `workspaces` 路徑)下任何服務 repo commit 都遵守本慣例
23
23
  - 一個 commit 一件事;混雜多個意圖時拆開
24
24
  - 服務 repo 自己的 CLAUDE.md/AGENTS.md 若另有 commit 規範,以服務 repo 為準(分層規則見 `../README.md`)
@@ -28,7 +28,7 @@ description: 為團隊建立或修改流程慣例並落地到 ADE 知識庫。
28
28
 
29
29
  1. 依 `ade-contribute` skill 流程 clone ADE repo、建分支(含查重:同一流程已有 issue/PR 就別重開)
30
30
  2. 寫 `knowledge/process/<主題>.md`(細節層,kebab-case 檔名)
31
- 3. 需要 skill 的:在 `skills/` 下建立,**目錄名必須 `ade-` 前綴**——runner 只管理此前綴,非前綴會被拒裝
31
+ 3. 需要 skill 的:依 `ade-add-skill` skill 建立(它管使用對象選擇、放置位置與命名規則)
32
32
  4. 需要常駐行的:在 `claude-md/section.md` 適當小節加一行指標
33
33
  5. 更新 `process/README.md` 的主題索引
34
34
  6. 依 `ade-contribute` 慣例開 PR;merge 後各工作目錄 update 即生效
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: ade-add-skill
3
+ description: 為 ADE 生態新增 skill(供 ADE repo 內或消費端工作目錄使用)。使用者說「新增 skill」「建一支 skill」「把這個做成 skill」「這個 skill 消費端也要能用」時使用。
4
+ ---
5
+
6
+ # 新增 skill
7
+
8
+ 1. **判斷所在位置**:repo 根有 `knowledge/services/` → 你在 ADE repo 內,直接編輯本 repo 檔案;只有 `.claude/ade/knowledge/` → 你在工作目錄,依 `ade-contribute` skill 的流程 clone ADE repo 並建立分支(絕不直接改 `.claude/ade/` 副本)
9
+ 2. **選使用對象**——這決定放哪,不確定就問使用者:
10
+ - **消費端工作目錄用** → `skills/ade-<name>/`。init/update 會注入各工作目錄的 `.claude/skills/`;**目錄名必須 `ade-` 前綴**,runner 只管理此前綴,非前綴會被拒裝
11
+ - **ADE repo 內用**(PO/維護者)→ `.claude/skills/ade-<name>/`
12
+ - **兩邊都用** → 放 `skills/ade-<name>/`,再建 symlink:`ln -s ../../skills/ade-<name> .claude/skills/ade-<name>`(單一真相在 `skills/`,維護只改一份)
13
+ 3. 寫 `SKILL.md`:frontmatter 的 `name` 與目錄同名;`description` 寫觸發語(使用者會說什麼、什麼情境該觸發);body 遵守 context 紀律——只寫「何時做+去哪看」,超過一頁的細節拆到 `knowledge/process/` 檔並連結(禁止孤兒:細節檔必須被引用)
14
+ 4. skill 內需要「開 PR 回 ADE repo」的動作一律寫「依 `ade-contribute` 流程」,不要重複實作回流機制
15
+ 5. 更新本 repo `README.md` 的 Skills 一覽
16
+ 6. 收尾:ADE repo 內 → 照一般 git 慣例 commit;工作目錄 → 依 `ade-contribute` 流程開 PR
@@ -12,7 +12,7 @@ description: 功能開發完成後,核對 spec 與實作是否一致,移除
12
12
  1. 確認這次開發對應的 PRD 與受影響 spec(從使用者、branch 或 PR 上下文取得;不確定就問)
13
13
  2. 依 `ade-contribute` skill 的流程 clone ADE repo——**核對與修改都以這份 fresh clone 為唯一基準**。工作目錄的 `.claude/ade/` 副本可能過期(例如 PO merge 了 prd-to-spec 之後沒人跑過 update,本地根本沒有那些標記),只能當導航用
14
14
  3. 找出本次 PRD 的標記:在 clone 的 spec 上先用 `grep -n "🚧" <spec>` 列出**全部**標記行(寬鬆匹配,連格式變體一起抓),再逐行看 PRD 檔名判斷歸屬——只處理含本次 PRD 檔名的行,其他 PRD 的標記與其描述的內容一律不碰
15
- 4. 逐項核對:對照 `workspaces/` 下的實際實作,檢查每個屬於本次 PRD 的 `🚧` 區塊
15
+ 4. 逐項核對:對照作業區(`.ade.json` `workspaces` 路徑)下的實際實作,檢查每個屬於本次 PRD 的 `🚧` 區塊
16
16
  - 已實作且行為一致 → 移除該標記行(整行刪除,內容保留)
17
17
  - 實作與 spec 不符 → 以**實作為準**修改 spec 內容,並記下差異
18
18
  - 沒做的項目 → 保留標記,記下
@@ -11,7 +11,7 @@ spec 平時只靠 PRD 流程更新;hotfix 與計畫外變更會讓 spec 悄悄
11
11
 
12
12
  1. **先確保副本最新**:執行 update(或確認 `.ade.json` 的 commit 與遠端 HEAD 一致)——拿過期的 spec 副本去比對會誤報漂移
13
13
  2. 列出 `.claude/ade/knowledge/specs/` 下的 spec;範圍大時請使用者指定優先巡檢的部分(建議:最近有 release 的服務相關)
14
- 3. 對每份 spec 找出涉及的服務(文內連結與 `services/index.md`),缺的 repo 依服務檔 clone `workspaces/`
14
+ 3. 對每份 spec 找出涉及的服務(文內連結與 `services/index.md`),缺的 repo 依服務檔 clone 進作業區(`.ade.json` `workspaces` 路徑)
15
15
  4. 逐項對照實作與 spec 敘述,記錄不一致:行為已變、功能已移除、實作有但 spec 未記載
16
16
  - `🚧 尚未實作` 區塊屬「已定案未開發」,不算漂移,跳過
17
17
  5. 向使用者報告漂移清單,確認哪些該修 spec(也可能是實作錯了該修 code)