Skip to content

Commit 02e910b

Browse files
feat(init): bundle contract templates into project root
1 parent 159bb88 commit 02e910b

7 files changed

Lines changed: 61 additions & 8 deletions

File tree

TMLSPEC-cli/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules/
2+
contract/
23
dist/
34
*.log
45
npm-debug.log*

TMLSPEC-cli/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ TMLSPEC CLI 是一个轻量初始化工具,用于向当前工作区注入项
77
- 安装 `tml-spec` 命令
88
- 执行 `tml-spec init` 选择目标工具
99
- 向对应工具目录下的 `tml-spec/` 子目录注入 `project``requirement` 两个命令文件
10+
- 将打包内置的 `contract/` 模板目录复制到目标项目根目录
1011

1112
## 快速开始
1213

@@ -46,7 +47,8 @@ tml-spec init
4647
- 决定是否覆盖已有文件。
4748

4849
7. 初始化完成后,到对应工具目录下的 `tml-spec/` 子目录使用生成的 `project``requirement` 命令文件。
49-
8. 在模型会话输入框中输入/tml-spec xxx调用对应的命令
50+
8. 在目标项目根目录查看自动复制的 `contract/` 模板目录。
51+
9. 在模型会话输入框中输入/tml-spec xxx调用对应的命令
5052

5153
### 方式二:在仓库中本地开发和调试
5254

@@ -109,6 +111,7 @@ registry=https://registry.npmjs.org/
109111

110112
## 生成结果
111113

114+
- `contract/`(复制到目标项目根目录)
112115
- `.claude/commands/tml-spec/project.md`
113116
- `.claude/commands/tml-spec/requirement.md`
114117
- `~/.codex/prompts/tml-spec-project.md`

TMLSPEC-cli/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

TMLSPEC-cli/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
{
22
"name": "@tml/tmlspec-cli",
3-
"version": "0.2.2",
3+
"version": "0.3.0",
44
"description": "Inject project and requirement command files for team workflows.",
55
"type": "module",
66
"bin": {
77
"tml-spec": "bin/tml-spec.js"
88
},
99
"files": [
1010
"bin",
11+
"contract",
1112
"dist",
1213
"src/core/commands",
1314
"README.md"
1415
],
1516
"scripts": {
17+
"prebuild": "node ./scripts/sync-contract.mjs",
1618
"build": "tsc -p tsconfig.json",
1719
"dev": "tsx src/cli/index.ts",
20+
"prepack": "node ./scripts/sync-contract.mjs",
1821
"start": "node ./bin/tml-spec.js"
1922
},
2023
"keywords": [
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs';
2+
import path from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
5+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
6+
const packageRoot = path.resolve(currentDir, '..');
7+
const sourceDir = path.resolve(packageRoot, '..', 'contract');
8+
const targetDir = path.join(packageRoot, 'contract');
9+
10+
if (!existsSync(sourceDir)) {
11+
throw new Error(`Contract source directory does not exist: ${sourceDir}`);
12+
}
13+
14+
rmSync(targetDir, { recursive: true, force: true });
15+
mkdirSync(packageRoot, { recursive: true });
16+
cpSync(sourceDir, targetDir, { recursive: true });
17+
18+
console.log(`Synchronized contract directory to ${targetDir}`);

TMLSPEC-cli/src/core/init.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import path from 'node:path';
2+
import { fileURLToPath } from 'node:url';
23
import { checkbox, confirm, input } from '@inquirer/prompts';
34
import { getAdapter } from './adapters/index.js';
45
import { TOOL_OPTIONS } from './catalog.js';
56
import type { InitAnswers, ToolId } from './types.js';
6-
import { writeTextFile } from '../utils/fs.js';
7+
import { copyDirectory, writeTextFile } from '../utils/fs.js';
8+
9+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
10+
const packageRoot = path.resolve(currentDir, '../..');
11+
const bundledContractDirectory = path.join(packageRoot, 'contract');
712

813
interface InitOverrides {
914
projectRoot?: string;
@@ -91,16 +96,21 @@ export async function runInit(overrides: InitOverrides = {}): Promise<void> {
9196
}
9297
}
9398

99+
const contractTargetPath = path.join(answers.projectRoot, 'contract');
100+
const contractCopied = await copyDirectory(bundledContractDirectory, contractTargetPath, answers.force);
101+
94102
const relativeRoot = path.relative(process.cwd(), answers.projectRoot) || '.';
95103
console.log('TML Spec 命令已初始化。');
96104
console.log(`项目根目录: ${relativeRoot}`);
97105
console.log(`已选择工具: ${answers.tools.join(', ')}`);
98106
console.log(`写入文件数: ${writtenFiles}`);
99107
console.log(`跳过文件数: ${skippedFiles}`);
108+
console.log(`contract 模板目录: ${contractCopied ? '已写入项目根目录' : '已存在,未覆盖'}`);
100109
console.log('下一步:');
101110
console.log('1. 在你的 IDE 中打开生成的命令或 prompt 文件。');
102-
console.log('2. 使用 tml-spec 命名空间下的 project 命令处理项目级文档工作。');
103-
console.log('3. 使用 tml-spec 命名空间下的 requirement 命令处理需求级工作,并路由到 openspec。');
111+
console.log('2. 在项目根目录下查看 contract 模板目录,并按需补充团队规范。');
112+
console.log('3. 使用 tml-spec 命名空间下的 project 命令处理项目级文档工作。');
113+
console.log('4. 使用 tml-spec 命名空间下的 requirement 命令处理需求级工作,并路由到 openspec。');
104114
}
105115

106116
export function parseInitOverrides(options: {

TMLSPEC-cli/src/utils/fs.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { access, mkdir, writeFile } from 'node:fs/promises';
1+
import { access, cp, mkdir, rm, writeFile } from 'node:fs/promises';
22
import path from 'node:path';
33

44
export async function ensureDirectory(dirPath: string): Promise<void> {
@@ -22,4 +22,22 @@ export async function writeTextFile(filePath: string, content: string, overwrite
2222
await ensureDirectory(path.dirname(filePath));
2323
await writeFile(filePath, content, 'utf8');
2424
return true;
25+
}
26+
27+
export async function copyDirectory(sourcePath: string, targetPath: string, overwrite = true): Promise<boolean> {
28+
if (!await pathExists(sourcePath)) {
29+
throw new Error(`Source directory does not exist: ${sourcePath}`);
30+
}
31+
32+
if (!overwrite && await pathExists(targetPath)) {
33+
return false;
34+
}
35+
36+
if (overwrite && await pathExists(targetPath)) {
37+
await rm(targetPath, { recursive: true, force: true });
38+
}
39+
40+
await ensureDirectory(path.dirname(targetPath));
41+
await cp(sourcePath, targetPath, { recursive: true, force: overwrite });
42+
return true;
2543
}

0 commit comments

Comments
 (0)