nano-spec 1.1.2 → 1.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/README.md +17 -12
- package/bin/nanospec.js +3 -1
- package/dist/commands/new.js +13 -1
- package/dist/commands/new.test.js +18 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +85 -69
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +27 -0
- package/dist/static/commands/command-templates.test.js +11 -1
- package/dist/static/commands/command-templates.test.ts +15 -1
- package/dist/static/commands/spec.init.toml +23 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,8 +12,8 @@ npm install -g nano-spec
|
|
|
12
12
|
# 2) 初始化(默认交互式选择 AI 工具)
|
|
13
13
|
nanospec init
|
|
14
14
|
|
|
15
|
-
# 3)
|
|
16
|
-
nanospec new
|
|
15
|
+
# 3) 创建任务(可直接回车使用默认名“待命名”)
|
|
16
|
+
nanospec new
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
在 AI 工具中按顺序执行:
|
|
@@ -22,18 +22,23 @@ nanospec new "优化登录流程"
|
|
|
22
22
|
- `/spec.2-plan`:生成实施方案与任务拆解
|
|
23
23
|
- `/spec.3-execute`:执行任务并更新状态
|
|
24
24
|
|
|
25
|
+
`/init` 使用建议(已初始化项目):
|
|
26
|
+
|
|
27
|
+
- 直接给出任务目标:`/init 创建用户认证功能`
|
|
28
|
+
- 避免只输入 `/init` 或“创建任务”这类泛化描述,否则应先补充任务名与一句话目标
|
|
29
|
+
|
|
25
30
|
## 核心命令
|
|
26
31
|
|
|
27
|
-
| 命令 | 说明 |
|
|
28
|
-
|
|
29
|
-
| `nanospec init` | 初始化项目结构与命令模板 |
|
|
30
|
-
| `nanospec new [name]` |
|
|
31
|
-
| `nanospec switch [name]` | 切换当前任务 |
|
|
32
|
-
| `nanospec status` | 查看当前任务状态 |
|
|
33
|
-
| `nanospec sync [--adapter <name>]` | 同步命令到 AI 工具目录 |
|
|
34
|
-
| `nanospec preset list/install/uninstall` | 预设包管理 |
|
|
35
|
-
| `nanospec config` | 查看当前配置 |
|
|
36
|
-
| `nanospec config get/set/unset/list` | 读写配置(支持 `--global`) |
|
|
32
|
+
| 命令 | 简写 | 说明 |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `nanospec init` | `nanospec i` | 初始化项目结构与命令模板 |
|
|
35
|
+
| `nanospec new [name]` | `nanospec n [name]` | 创建任务目录并设为当前任务;不带 `name` 时进入交互输入(默认“待命名”) |
|
|
36
|
+
| `nanospec switch [name]` | `nanospec s [name]` | 切换当前任务 |
|
|
37
|
+
| `nanospec status` | `nanospec st` | 查看当前任务状态 |
|
|
38
|
+
| `nanospec sync [--adapter <name>]` | `nanospec sy [--adapter <name>]` | 同步命令到 AI 工具目录 |
|
|
39
|
+
| `nanospec preset list/install/uninstall` | `nanospec p ls/add/rm` | 预设包管理 |
|
|
40
|
+
| `nanospec config` | `nanospec c` | 查看当前配置 |
|
|
41
|
+
| `nanospec config get/set/unset/list` | `nanospec c g/s/u/ls` | 读写配置(支持 `--global`) |
|
|
37
42
|
|
|
38
43
|
## 工作流概览
|
|
39
44
|
|
package/bin/nanospec.js
CHANGED
package/dist/commands/new.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
3
4
|
import { loadConfig } from '../config/config.js';
|
|
4
5
|
import { setCurrentTask } from '../config/task-pointer.js';
|
|
5
6
|
export async function newTask(name) {
|
|
@@ -11,7 +12,18 @@ export async function newTask(name) {
|
|
|
11
12
|
return;
|
|
12
13
|
}
|
|
13
14
|
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
14
|
-
|
|
15
|
+
let taskName = name?.trim();
|
|
16
|
+
if (!taskName) {
|
|
17
|
+
const result = await inquirer.prompt([
|
|
18
|
+
{
|
|
19
|
+
type: 'input',
|
|
20
|
+
name: 'taskName',
|
|
21
|
+
message: '任务名称:',
|
|
22
|
+
default: '待命名',
|
|
23
|
+
},
|
|
24
|
+
]);
|
|
25
|
+
taskName = result.taskName?.trim() || '待命名';
|
|
26
|
+
}
|
|
15
27
|
const dirName = `${date}-${taskName}`;
|
|
16
28
|
const taskDir = join(nanospecDir, dirName);
|
|
17
29
|
if (existsSync(taskDir)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, rmSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
3
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
5
|
import { newTask } from './new.js';
|
|
5
6
|
describe('new command', () => {
|
|
@@ -32,10 +33,12 @@ describe('new command', () => {
|
|
|
32
33
|
it('应该创建带日期戳的任务目录', async () => {
|
|
33
34
|
// 先初始化项目
|
|
34
35
|
mkdirSync(nanospecDir, { recursive: true });
|
|
36
|
+
const promptSpy = vi.spyOn(inquirer, 'prompt');
|
|
35
37
|
await newTask('用户认证功能');
|
|
36
38
|
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
37
39
|
const taskDir = join(nanospecDir, `${date}-用户认证功能`);
|
|
38
40
|
expect(existsSync(taskDir)).toBe(true);
|
|
41
|
+
expect(promptSpy).not.toHaveBeenCalled();
|
|
39
42
|
});
|
|
40
43
|
it('应该创建任务目录结构(brief.md、assets/、outputs/)', async () => {
|
|
41
44
|
mkdirSync(nanospecDir, { recursive: true });
|
|
@@ -57,10 +60,25 @@ describe('new command', () => {
|
|
|
57
60
|
});
|
|
58
61
|
it('应该使用"待命名"作为默认名称', async () => {
|
|
59
62
|
mkdirSync(nanospecDir, { recursive: true });
|
|
63
|
+
const promptSpy = vi
|
|
64
|
+
.spyOn(inquirer, 'prompt')
|
|
65
|
+
.mockResolvedValue({ taskName: '' });
|
|
60
66
|
await newTask();
|
|
61
67
|
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
62
68
|
const taskDir = join(nanospecDir, `${date}-待命名`);
|
|
63
69
|
expect(existsSync(taskDir)).toBe(true);
|
|
70
|
+
expect(promptSpy).toHaveBeenCalledOnce();
|
|
71
|
+
});
|
|
72
|
+
it('应该在无参时进入交互模式并使用用户输入名称', async () => {
|
|
73
|
+
mkdirSync(nanospecDir, { recursive: true });
|
|
74
|
+
const promptSpy = vi
|
|
75
|
+
.spyOn(inquirer, 'prompt')
|
|
76
|
+
.mockResolvedValue({ taskName: '登录体验优化' });
|
|
77
|
+
await newTask();
|
|
78
|
+
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
79
|
+
const taskDir = join(nanospecDir, `${date}-登录体验优化`);
|
|
80
|
+
expect(existsSync(taskDir)).toBe(true);
|
|
81
|
+
expect(promptSpy).toHaveBeenCalledOnce();
|
|
64
82
|
});
|
|
65
83
|
it('应该在目录已存在时显示警告', async () => {
|
|
66
84
|
mkdirSync(nanospecDir, { recursive: true });
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -7,72 +7,88 @@ import { showStatus } from './commands/status.js';
|
|
|
7
7
|
import { listPresets, installPreset, uninstallPreset } from './commands/preset.js';
|
|
8
8
|
import { syncCommands } from './commands/sync.js';
|
|
9
9
|
import { config } from './commands/config.js';
|
|
10
|
-
|
|
11
|
-
program
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
10
|
+
export function createProgram() {
|
|
11
|
+
const program = new Command();
|
|
12
|
+
program
|
|
13
|
+
.name('nanospec')
|
|
14
|
+
.description('nanospec - Spec 驱动开发工作流')
|
|
15
|
+
.version('1.0.0');
|
|
16
|
+
program
|
|
17
|
+
.command('init')
|
|
18
|
+
.alias('i')
|
|
19
|
+
.description('初始化 nanospec 项目结构')
|
|
20
|
+
.option('--ai <tool>', 'AI 工具类型(非交互式快速初始化)')
|
|
21
|
+
.option('-f, --force', '强制覆盖已存在的文件')
|
|
22
|
+
.action((options) => init(options));
|
|
23
|
+
program
|
|
24
|
+
.command('new [name]')
|
|
25
|
+
.alias('n')
|
|
26
|
+
.description('创建新的任务目录')
|
|
27
|
+
.action((name) => newTask(name));
|
|
28
|
+
program
|
|
29
|
+
.command('switch [name]')
|
|
30
|
+
.alias('s')
|
|
31
|
+
.description('切换当前任务')
|
|
32
|
+
.action((name) => switchTask(name));
|
|
33
|
+
program
|
|
34
|
+
.command('status')
|
|
35
|
+
.alias('st')
|
|
36
|
+
.description('显示当前状态')
|
|
37
|
+
.action(() => showStatus());
|
|
38
|
+
const presetCmd = program
|
|
39
|
+
.command('preset')
|
|
40
|
+
.alias('p')
|
|
41
|
+
.description('预设包管理');
|
|
42
|
+
presetCmd
|
|
43
|
+
.command('list')
|
|
44
|
+
.alias('ls')
|
|
45
|
+
.description('列出所有可用预设')
|
|
46
|
+
.action(() => listPresets());
|
|
47
|
+
presetCmd
|
|
48
|
+
.command('install [name]')
|
|
49
|
+
.alias('add')
|
|
50
|
+
.description('安装预设(不指定名称时使用交互式选择)')
|
|
51
|
+
.action((name) => installPreset(name));
|
|
52
|
+
presetCmd
|
|
53
|
+
.command('uninstall <name>')
|
|
54
|
+
.alias('rm')
|
|
55
|
+
.description('卸载预设')
|
|
56
|
+
.action((name) => uninstallPreset(name));
|
|
57
|
+
program
|
|
58
|
+
.command('sync')
|
|
59
|
+
.alias('sy')
|
|
60
|
+
.description('同步命令到 AI 工具')
|
|
61
|
+
.option('--adapter <name>', '指定 AI 工具')
|
|
62
|
+
.action((options) => syncCommands(options));
|
|
63
|
+
const configCmd = program
|
|
64
|
+
.command('config')
|
|
65
|
+
.alias('c')
|
|
66
|
+
.description('配置管理');
|
|
67
|
+
configCmd
|
|
68
|
+
.command('get <key>')
|
|
69
|
+
.alias('g')
|
|
70
|
+
.description('获取配置值')
|
|
71
|
+
.option('-g, --global', '操作全局配置')
|
|
72
|
+
.action((key, options) => config('get', key, undefined, options));
|
|
73
|
+
configCmd
|
|
74
|
+
.command('set <key> <value>')
|
|
75
|
+
.alias('s')
|
|
76
|
+
.description('设置配置')
|
|
77
|
+
.option('-g, --global', '操作全局配置')
|
|
78
|
+
.action((key, value, options) => config('set', key, value, options));
|
|
79
|
+
configCmd
|
|
80
|
+
.command('unset <key>')
|
|
81
|
+
.alias('u')
|
|
82
|
+
.description('删除配置项')
|
|
83
|
+
.option('-g, --global', '操作全局配置')
|
|
84
|
+
.action((key, options) => config('unset', key, undefined, options));
|
|
85
|
+
configCmd
|
|
86
|
+
.command('list')
|
|
87
|
+
.alias('ls')
|
|
88
|
+
.description('列出所有配置项')
|
|
89
|
+
.option('-g, --global', '操作全局配置')
|
|
90
|
+
.action((options) => config(undefined, undefined, undefined, { ...options, list: true }));
|
|
91
|
+
configCmd
|
|
92
|
+
.action((options) => config(undefined, undefined, undefined, options));
|
|
93
|
+
return program;
|
|
94
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createProgram } from './index.js';
|
|
3
|
+
describe('cli command registration', () => {
|
|
4
|
+
it('should register aliases for top-level commands', () => {
|
|
5
|
+
const program = createProgram();
|
|
6
|
+
const expectedAliases = [
|
|
7
|
+
['init', 'i'],
|
|
8
|
+
['new', 'n'],
|
|
9
|
+
['switch', 's'],
|
|
10
|
+
['status', 'st'],
|
|
11
|
+
['preset', 'p'],
|
|
12
|
+
['sync', 'sy'],
|
|
13
|
+
['config', 'c'],
|
|
14
|
+
];
|
|
15
|
+
for (const [name, alias] of expectedAliases) {
|
|
16
|
+
const command = program.commands.find((cmd) => cmd.name() === name);
|
|
17
|
+
expect(command, `missing command: ${name}`).toBeDefined();
|
|
18
|
+
expect(command?.aliases()).toContain(alias);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
it('should register alias for switch command as s', () => {
|
|
22
|
+
const program = createProgram();
|
|
23
|
+
const switchCommand = program.commands.find((cmd) => cmd.name() === 'switch');
|
|
24
|
+
expect(switchCommand).toBeDefined();
|
|
25
|
+
expect(switchCommand?.aliases()).toContain('s');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -14,8 +14,18 @@ describe('command templates', () => {
|
|
|
14
14
|
it('should use nanospec commands in init and run templates', () => {
|
|
15
15
|
const initTemplate = readFileSync(join(__dirname, 'spec.init.toml'), 'utf-8');
|
|
16
16
|
const runTemplate = readFileSync(join(__dirname, 'spec.run.toml'), 'utf-8');
|
|
17
|
-
expect(initTemplate).toContain('nanospec init');
|
|
18
17
|
expect(initTemplate).toContain('nanospec new');
|
|
19
18
|
expect(runTemplate).toContain('nanospec new');
|
|
20
19
|
});
|
|
20
|
+
it('should keep init template focused on task creation without environment detection', () => {
|
|
21
|
+
const initTemplate = readFileSync(join(__dirname, 'spec.init.toml'), 'utf-8');
|
|
22
|
+
expect(initTemplate).not.toContain('Step 1: 检测环境');
|
|
23
|
+
expect(initTemplate).not.toContain('nanospec init --ai cursor');
|
|
24
|
+
expect(initTemplate).toContain('执行创建任务流程');
|
|
25
|
+
});
|
|
26
|
+
it('should require clarification when task name cannot be extracted in init template', () => {
|
|
27
|
+
const initTemplate = readFileSync(join(__dirname, 'spec.init.toml'), 'utf-8');
|
|
28
|
+
expect(initTemplate).toContain('如果无法明确提取任务名');
|
|
29
|
+
expect(initTemplate).toContain('不执行 `nanospec new`');
|
|
30
|
+
});
|
|
21
31
|
});
|
|
@@ -19,8 +19,22 @@ describe('command templates', () => {
|
|
|
19
19
|
const initTemplate = readFileSync(join(__dirname, 'spec.init.toml'), 'utf-8');
|
|
20
20
|
const runTemplate = readFileSync(join(__dirname, 'spec.run.toml'), 'utf-8');
|
|
21
21
|
|
|
22
|
-
expect(initTemplate).toContain('nanospec init');
|
|
23
22
|
expect(initTemplate).toContain('nanospec new');
|
|
24
23
|
expect(runTemplate).toContain('nanospec new');
|
|
25
24
|
});
|
|
25
|
+
|
|
26
|
+
it('should keep init template focused on task creation without environment detection', () => {
|
|
27
|
+
const initTemplate = readFileSync(join(__dirname, 'spec.init.toml'), 'utf-8');
|
|
28
|
+
|
|
29
|
+
expect(initTemplate).not.toContain('Step 1: 检测环境');
|
|
30
|
+
expect(initTemplate).not.toContain('nanospec init --ai cursor');
|
|
31
|
+
expect(initTemplate).toContain('执行创建任务流程');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should require clarification when task name cannot be extracted in init template', () => {
|
|
35
|
+
const initTemplate = readFileSync(join(__dirname, 'spec.init.toml'), 'utf-8');
|
|
36
|
+
|
|
37
|
+
expect(initTemplate).toContain('如果无法明确提取任务名');
|
|
38
|
+
expect(initTemplate).toContain('不执行 `nanospec new`');
|
|
39
|
+
});
|
|
26
40
|
});
|
|
@@ -1,44 +1,29 @@
|
|
|
1
1
|
# Command: spec.init
|
|
2
|
-
# Description:
|
|
2
|
+
# Description: 创建任务 - 从用户意图创建任务并填写 brief
|
|
3
3
|
# Category: nanospec
|
|
4
4
|
# Version: 1
|
|
5
5
|
|
|
6
|
-
description = "
|
|
6
|
+
description = "创建任务 - 从用户意图创建任务并填写 brief"
|
|
7
7
|
|
|
8
8
|
prompt = """
|
|
9
|
-
# /init -
|
|
9
|
+
# /init - 创建任务
|
|
10
10
|
|
|
11
11
|
> 遵循 `<config_dir>/AGENTS.md` 通用规范
|
|
12
12
|
|
|
13
13
|
## Role
|
|
14
14
|
|
|
15
|
-
你是"
|
|
15
|
+
你是"任务创建助手":从用户输入中提取任务名称和意图,创建任务并填写 brief。
|
|
16
16
|
|
|
17
17
|
## Objective
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
1. **未初始化**:执行 `nanospec init` 初始化目录结构
|
|
21
|
-
2. **已初始化**:执行 `nanospec new` 创建任务,并根据用户意图填写 brief.md
|
|
22
|
-
|
|
23
|
-
## Decision Protocol
|
|
24
|
-
|
|
25
|
-
### Step 1: 检测环境
|
|
26
|
-
|
|
27
|
-
检查 `<config_dir>/AGENTS.md` 是否存在:
|
|
28
|
-
- **不存在** → 执行初始化流程
|
|
29
|
-
- **存在** → 执行创建任务流程
|
|
30
|
-
|
|
31
|
-
### Step 2A: 初始化流程(未初始化时)
|
|
32
|
-
|
|
33
|
-
1. 执行 shell 命令:`nanospec init --ai cursor`
|
|
34
|
-
2. 确认生成了 `<config_dir>/AGENTS.md`
|
|
35
|
-
3. 提示用户初始化完成,可以开始创建任务
|
|
36
|
-
|
|
37
|
-
### Step 2B: 创建任务流程(已初始化时)
|
|
38
|
-
|
|
19
|
+
执行创建任务流程:
|
|
39
20
|
1. 从用户输入中提取任务名称和意图
|
|
40
|
-
2.
|
|
41
|
-
|
|
21
|
+
2. 对任务名称做有效性检查:
|
|
22
|
+
- 任务名应当和要做的事情直接相关(功能、页面、模块、问题)
|
|
23
|
+
- 禁止使用泛化命名:`初始化或创建任务`、`新任务`、`临时任务`、`todo` 等
|
|
24
|
+
- 如果无法明确提取任务名,先向用户提问后再继续,不得直接执行 `nanospec new`
|
|
25
|
+
3. 执行 shell 命令:`nanospec new "<任务名称>"`
|
|
26
|
+
4. 根据用户意图,填写 `<specs_dir>/<task_name>/brief.md`:
|
|
42
27
|
- 背景:用户的原始描述
|
|
43
28
|
- 目标:从意图中提炼的目标
|
|
44
29
|
- 约束:如有提及则填写,否则留空待补充
|
|
@@ -46,17 +31,14 @@ prompt = """
|
|
|
46
31
|
## Input
|
|
47
32
|
|
|
48
33
|
用户的自然语言描述,例如:
|
|
49
|
-
- "初始化 nano-spec"
|
|
50
34
|
- "创建一个用户认证功能"
|
|
51
35
|
- "我要做一个支持暗黑模式的设置页面"
|
|
36
|
+
- "/init 创建用户认证功能(支持邮箱+验证码登录)"
|
|
37
|
+
- "/init 修复订单页在移动端按钮遮挡问题"
|
|
52
38
|
|
|
53
39
|
## Output
|
|
54
40
|
|
|
55
|
-
###
|
|
56
|
-
|
|
57
|
-
执行命令并报告结果。
|
|
58
|
-
|
|
59
|
-
### 创建任务场景
|
|
41
|
+
### 创建任务
|
|
60
42
|
|
|
61
43
|
1. 执行 `nanospec new` 命令
|
|
62
44
|
2. 填写 brief.md,格式:
|
|
@@ -75,10 +57,16 @@ prompt = """
|
|
|
75
57
|
|
|
76
58
|
3. 提示用户下一步使用 `/{{cmd_prefix}}.1-spec` 生成规格说明
|
|
77
59
|
|
|
60
|
+
### 信息不足场景
|
|
61
|
+
|
|
62
|
+
1. 不执行 `nanospec new`
|
|
63
|
+
2. 先向用户提一个简短问题:请提供任务名称和一句话目标
|
|
64
|
+
3. 得到补充后再继续创建任务
|
|
65
|
+
|
|
78
66
|
## Checklist
|
|
79
67
|
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
68
|
+
- 已提取并校验任务名
|
|
69
|
+
- 执行了 `nanospec new`
|
|
70
|
+
- brief.md 已填写用户意图
|
|
83
71
|
|
|
84
72
|
"""
|
package/package.json
CHANGED