@zhuan-ai/zhuanspec 1.3.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/LICENSE +22 -0
- package/README.md +461 -0
- package/README.zh.md +434 -0
- package/bin/zhuanspec.js +3 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +356 -0
- package/dist/commands/artifact-workflow.d.ts +13 -0
- package/dist/commands/artifact-workflow.js +916 -0
- package/dist/commands/change.d.ts +35 -0
- package/dist/commands/change.js +277 -0
- package/dist/commands/completion.d.ts +72 -0
- package/dist/commands/completion.js +221 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +198 -0
- package/dist/commands/show.d.ts +14 -0
- package/dist/commands/show.js +132 -0
- package/dist/commands/spec.d.ts +15 -0
- package/dist/commands/spec.js +225 -0
- package/dist/commands/validate.d.ts +24 -0
- package/dist/commands/validate.js +294 -0
- package/dist/core/archive.d.ts +30 -0
- package/dist/core/archive.js +438 -0
- package/dist/core/artifact-graph/graph.d.ts +56 -0
- package/dist/core/artifact-graph/graph.js +141 -0
- package/dist/core/artifact-graph/index.d.ts +7 -0
- package/dist/core/artifact-graph/index.js +13 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +134 -0
- package/dist/core/artifact-graph/instruction-loader.js +180 -0
- package/dist/core/artifact-graph/resolver.d.ts +61 -0
- package/dist/core/artifact-graph/resolver.js +187 -0
- package/dist/core/artifact-graph/schema.d.ts +13 -0
- package/dist/core/artifact-graph/schema.js +108 -0
- package/dist/core/artifact-graph/state.d.ts +12 -0
- package/dist/core/artifact-graph/state.js +54 -0
- package/dist/core/artifact-graph/types.d.ts +45 -0
- package/dist/core/artifact-graph/types.js +43 -0
- package/dist/core/completions/command-registry.d.ts +7 -0
- package/dist/core/completions/command-registry.js +362 -0
- package/dist/core/completions/completion-provider.d.ts +60 -0
- package/dist/core/completions/completion-provider.js +102 -0
- package/dist/core/completions/factory.d.ts +51 -0
- package/dist/core/completions/factory.js +57 -0
- package/dist/core/completions/generators/zsh-generator.d.ts +58 -0
- package/dist/core/completions/generators/zsh-generator.js +319 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +136 -0
- package/dist/core/completions/installers/zsh-installer.js +449 -0
- package/dist/core/completions/types.d.ts +78 -0
- package/dist/core/completions/types.js +2 -0
- package/dist/core/config-schema.d.ts +76 -0
- package/dist/core/config-schema.js +200 -0
- package/dist/core/config.d.ts +16 -0
- package/dist/core/config.js +29 -0
- package/dist/core/configurators/agents.d.ts +8 -0
- package/dist/core/configurators/agents.js +15 -0
- package/dist/core/configurators/base.d.ts +7 -0
- package/dist/core/configurators/base.js +2 -0
- package/dist/core/configurators/claude.d.ts +8 -0
- package/dist/core/configurators/claude.js +15 -0
- package/dist/core/configurators/cline.d.ts +8 -0
- package/dist/core/configurators/cline.js +15 -0
- package/dist/core/configurators/codebuddy.d.ts +8 -0
- package/dist/core/configurators/codebuddy.js +15 -0
- package/dist/core/configurators/costrict.d.ts +8 -0
- package/dist/core/configurators/costrict.js +15 -0
- package/dist/core/configurators/iflow.d.ts +8 -0
- package/dist/core/configurators/iflow.js +15 -0
- package/dist/core/configurators/qoder.d.ts +30 -0
- package/dist/core/configurators/qoder.js +42 -0
- package/dist/core/configurators/qwen.d.ts +24 -0
- package/dist/core/configurators/qwen.js +37 -0
- package/dist/core/configurators/registry.d.ts +9 -0
- package/dist/core/configurators/registry.js +43 -0
- package/dist/core/configurators/slash/amazon-q.d.ts +9 -0
- package/dist/core/configurators/slash/amazon-q.js +46 -0
- package/dist/core/configurators/slash/antigravity.d.ts +9 -0
- package/dist/core/configurators/slash/antigravity.js +23 -0
- package/dist/core/configurators/slash/auggie.d.ts +9 -0
- package/dist/core/configurators/slash/auggie.js +31 -0
- package/dist/core/configurators/slash/base.d.ts +19 -0
- package/dist/core/configurators/slash/base.js +69 -0
- package/dist/core/configurators/slash/claude.d.ts +9 -0
- package/dist/core/configurators/slash/claude.js +37 -0
- package/dist/core/configurators/slash/cline.d.ts +9 -0
- package/dist/core/configurators/slash/cline.js +23 -0
- package/dist/core/configurators/slash/codebuddy.d.ts +9 -0
- package/dist/core/configurators/slash/codebuddy.js +37 -0
- package/dist/core/configurators/slash/codex.d.ts +14 -0
- package/dist/core/configurators/slash/codex.js +109 -0
- package/dist/core/configurators/slash/costrict.d.ts +9 -0
- package/dist/core/configurators/slash/costrict.js +31 -0
- package/dist/core/configurators/slash/crush.d.ts +9 -0
- package/dist/core/configurators/slash/crush.js +37 -0
- package/dist/core/configurators/slash/cursor.d.ts +9 -0
- package/dist/core/configurators/slash/cursor.js +37 -0
- package/dist/core/configurators/slash/factory.d.ts +10 -0
- package/dist/core/configurators/slash/factory.js +35 -0
- package/dist/core/configurators/slash/gemini.d.ts +9 -0
- package/dist/core/configurators/slash/gemini.js +22 -0
- package/dist/core/configurators/slash/github-copilot.d.ts +9 -0
- package/dist/core/configurators/slash/github-copilot.js +34 -0
- package/dist/core/configurators/slash/iflow.d.ts +9 -0
- package/dist/core/configurators/slash/iflow.js +37 -0
- package/dist/core/configurators/slash/kilocode.d.ts +9 -0
- package/dist/core/configurators/slash/kilocode.js +17 -0
- package/dist/core/configurators/slash/opencode.d.ts +12 -0
- package/dist/core/configurators/slash/opencode.js +72 -0
- package/dist/core/configurators/slash/qoder.d.ts +35 -0
- package/dist/core/configurators/slash/qoder.js +76 -0
- package/dist/core/configurators/slash/qwen.d.ts +32 -0
- package/dist/core/configurators/slash/qwen.js +49 -0
- package/dist/core/configurators/slash/registry.d.ts +8 -0
- package/dist/core/configurators/slash/registry.js +75 -0
- package/dist/core/configurators/slash/roocode.d.ts +9 -0
- package/dist/core/configurators/slash/roocode.js +23 -0
- package/dist/core/configurators/slash/toml-base.d.ts +10 -0
- package/dist/core/configurators/slash/toml-base.js +53 -0
- package/dist/core/configurators/slash/windsurf.d.ts +9 -0
- package/dist/core/configurators/slash/windsurf.js +23 -0
- package/dist/core/converters/json-converter.d.ts +6 -0
- package/dist/core/converters/json-converter.js +51 -0
- package/dist/core/global-config.d.ts +39 -0
- package/dist/core/global-config.js +115 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +3 -0
- package/dist/core/init.d.ts +60 -0
- package/dist/core/init.js +861 -0
- package/dist/core/list.d.ts +9 -0
- package/dist/core/list.js +171 -0
- package/dist/core/parsers/change-parser.d.ts +13 -0
- package/dist/core/parsers/change-parser.js +193 -0
- package/dist/core/parsers/markdown-parser.d.ts +22 -0
- package/dist/core/parsers/markdown-parser.js +187 -0
- package/dist/core/parsers/requirement-blocks.d.ts +37 -0
- package/dist/core/parsers/requirement-blocks.js +201 -0
- package/dist/core/project-config.d.ts +34 -0
- package/dist/core/project-config.js +79 -0
- package/dist/core/schemas/base.schema.d.ts +13 -0
- package/dist/core/schemas/base.schema.js +13 -0
- package/dist/core/schemas/change.schema.d.ts +73 -0
- package/dist/core/schemas/change.schema.js +31 -0
- package/dist/core/schemas/index.d.ts +4 -0
- package/dist/core/schemas/index.js +4 -0
- package/dist/core/schemas/spec.schema.d.ts +18 -0
- package/dist/core/schemas/spec.schema.js +15 -0
- package/dist/core/skill-discovery.d.ts +24 -0
- package/dist/core/skill-discovery.js +153 -0
- package/dist/core/specs-apply.d.ts +73 -0
- package/dist/core/specs-apply.js +384 -0
- package/dist/core/styles/palette.d.ts +7 -0
- package/dist/core/styles/palette.js +8 -0
- package/dist/core/templates/agents-root-stub.d.ts +2 -0
- package/dist/core/templates/agents-root-stub.js +17 -0
- package/dist/core/templates/agents-template.d.ts +2 -0
- package/dist/core/templates/agents-template.js +706 -0
- package/dist/core/templates/claude-template.d.ts +2 -0
- package/dist/core/templates/claude-template.js +2 -0
- package/dist/core/templates/cline-template.d.ts +2 -0
- package/dist/core/templates/cline-template.js +2 -0
- package/dist/core/templates/costrict-template.d.ts +2 -0
- package/dist/core/templates/costrict-template.js +2 -0
- package/dist/core/templates/index.d.ts +17 -0
- package/dist/core/templates/index.js +37 -0
- package/dist/core/templates/project-template.d.ts +8 -0
- package/dist/core/templates/project-template.js +32 -0
- package/dist/core/templates/skill-templates.d.ts +103 -0
- package/dist/core/templates/skill-templates.js +2131 -0
- package/dist/core/templates/slash-command-templates.d.ts +4 -0
- package/dist/core/templates/slash-command-templates.js +81 -0
- package/dist/core/update.d.ts +4 -0
- package/dist/core/update.js +88 -0
- package/dist/core/validation/constants.d.ts +34 -0
- package/dist/core/validation/constants.js +40 -0
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.js +2 -0
- package/dist/core/validation/validator.d.ts +33 -0
- package/dist/core/validation/validator.js +409 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.js +168 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/utils/change-metadata.d.ts +47 -0
- package/dist/utils/change-metadata.js +130 -0
- package/dist/utils/change-utils.d.ts +51 -0
- package/dist/utils/change-utils.js +100 -0
- package/dist/utils/file-system.d.ts +19 -0
- package/dist/utils/file-system.js +177 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/interactive.d.ts +18 -0
- package/dist/utils/interactive.js +21 -0
- package/dist/utils/item-discovery.d.ts +4 -0
- package/dist/utils/item-discovery.js +72 -0
- package/dist/utils/match.d.ts +3 -0
- package/dist/utils/match.js +22 -0
- package/dist/utils/shell-detection.d.ts +20 -0
- package/dist/utils/shell-detection.js +41 -0
- package/dist/utils/task-progress.d.ts +8 -0
- package/dist/utils/task-progress.js +36 -0
- package/package.json +81 -0
- package/schemas/spec-driven/schema.yaml +205 -0
- package/schemas/spec-driven/templates/design.md +19 -0
- package/schemas/spec-driven/templates/proposal.md +43 -0
- package/schemas/spec-driven/templates/spec.md +8 -0
- package/schemas/spec-driven/templates/tasks.md +25 -0
- package/schemas/tdd/schema.yaml +213 -0
- package/schemas/tdd/templates/docs.md +0 -0
- package/schemas/tdd/templates/implementation.md +11 -0
- package/schemas/tdd/templates/spec.md +11 -0
- package/schemas/tdd/templates/test.md +11 -0
- package/scripts/postinstall.js +147 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects the current user's shell based on environment variables
|
|
3
|
+
*
|
|
4
|
+
* @returns Detection result with supported shell and raw detected name
|
|
5
|
+
*/
|
|
6
|
+
export function detectShell() {
|
|
7
|
+
// Try SHELL environment variable first (Unix-like systems)
|
|
8
|
+
const shellPath = process.env.SHELL;
|
|
9
|
+
if (shellPath) {
|
|
10
|
+
const shellName = shellPath.toLowerCase();
|
|
11
|
+
if (shellName.includes('zsh')) {
|
|
12
|
+
return { shell: 'zsh', detected: 'zsh' };
|
|
13
|
+
}
|
|
14
|
+
if (shellName.includes('bash')) {
|
|
15
|
+
return { shell: 'bash', detected: 'bash' };
|
|
16
|
+
}
|
|
17
|
+
if (shellName.includes('fish')) {
|
|
18
|
+
return { shell: 'fish', detected: 'fish' };
|
|
19
|
+
}
|
|
20
|
+
// Shell detected but not supported
|
|
21
|
+
// Extract shell name from path (e.g., /bin/tcsh -> tcsh)
|
|
22
|
+
const match = shellPath.match(/\/([^/]+)$/);
|
|
23
|
+
const detectedName = match ? match[1] : shellPath;
|
|
24
|
+
return { shell: undefined, detected: detectedName };
|
|
25
|
+
}
|
|
26
|
+
// Check for PowerShell on Windows
|
|
27
|
+
// PSModulePath is a reliable PowerShell-specific environment variable
|
|
28
|
+
if (process.env.PSModulePath || process.platform === 'win32') {
|
|
29
|
+
const comspec = process.env.COMSPEC?.toLowerCase();
|
|
30
|
+
// If PSModulePath exists, we're definitely in PowerShell
|
|
31
|
+
if (process.env.PSModulePath) {
|
|
32
|
+
return { shell: 'powershell', detected: 'powershell' };
|
|
33
|
+
}
|
|
34
|
+
// On Windows without PSModulePath, we might be in cmd.exe
|
|
35
|
+
if (comspec?.includes('cmd.exe')) {
|
|
36
|
+
return { shell: undefined, detected: 'cmd.exe' };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { shell: undefined, detected: undefined };
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=shell-detection.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface TaskProgress {
|
|
2
|
+
total: number;
|
|
3
|
+
completed: number;
|
|
4
|
+
}
|
|
5
|
+
export declare function countTasksFromContent(content: string): TaskProgress;
|
|
6
|
+
export declare function getTaskProgressForChange(changesDir: string, changeName: string): Promise<TaskProgress>;
|
|
7
|
+
export declare function formatTaskStatus(progress: TaskProgress): string;
|
|
8
|
+
//# sourceMappingURL=task-progress.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
const TASK_PATTERN = /^[-*]\s+\[[\sx]\]/i;
|
|
4
|
+
const COMPLETED_TASK_PATTERN = /^[-*]\s+\[x\]/i;
|
|
5
|
+
export function countTasksFromContent(content) {
|
|
6
|
+
const lines = content.split('\n');
|
|
7
|
+
let total = 0;
|
|
8
|
+
let completed = 0;
|
|
9
|
+
for (const line of lines) {
|
|
10
|
+
if (line.match(TASK_PATTERN)) {
|
|
11
|
+
total++;
|
|
12
|
+
if (line.match(COMPLETED_TASK_PATTERN)) {
|
|
13
|
+
completed++;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return { total, completed };
|
|
18
|
+
}
|
|
19
|
+
export async function getTaskProgressForChange(changesDir, changeName) {
|
|
20
|
+
const tasksPath = path.join(changesDir, changeName, 'tasks.md');
|
|
21
|
+
try {
|
|
22
|
+
const content = await fs.readFile(tasksPath, 'utf-8');
|
|
23
|
+
return countTasksFromContent(content);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { total: 0, completed: 0 };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function formatTaskStatus(progress) {
|
|
30
|
+
if (progress.total === 0)
|
|
31
|
+
return 'No tasks';
|
|
32
|
+
if (progress.completed === progress.total)
|
|
33
|
+
return '✓ Complete';
|
|
34
|
+
return `${progress.completed}/${progress.total} tasks`;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=task-progress.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zhuan-ai/zhuanspec",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "AI-native system for spec-driven development",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"zhuanspec",
|
|
7
|
+
"specs",
|
|
8
|
+
"cli",
|
|
9
|
+
"ai",
|
|
10
|
+
"development"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/Fission-AI/OpenSpec",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/Fission-AI/OpenSpec"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "ZhuanSpec Contributors",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"zhuanspec": "./bin/zhuanspec.js"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"bin",
|
|
35
|
+
"schemas",
|
|
36
|
+
"scripts/postinstall.js",
|
|
37
|
+
"!dist/**/*.test.js",
|
|
38
|
+
"!dist/**/__tests__",
|
|
39
|
+
"!dist/**/*.map"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20.19.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@changesets/cli": "^2.27.7",
|
|
46
|
+
"@types/node": "^24.2.0",
|
|
47
|
+
"@vitest/ui": "^3.2.4",
|
|
48
|
+
"eslint": "^9.39.2",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"typescript-eslint": "^8.50.1",
|
|
51
|
+
"vitest": "^3.2.4"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@inquirer/core": "^10.2.2",
|
|
55
|
+
"@inquirer/prompts": "^7.8.0",
|
|
56
|
+
"chalk": "^5.5.0",
|
|
57
|
+
"commander": "^14.0.0",
|
|
58
|
+
"fast-glob": "^3.3.3",
|
|
59
|
+
"ora": "^8.2.0",
|
|
60
|
+
"yaml": "^2.8.2",
|
|
61
|
+
"zod": "^4.0.17"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"lint": "eslint src/",
|
|
65
|
+
"build": "node build.js",
|
|
66
|
+
"dev": "tsc --watch",
|
|
67
|
+
"dev:cli": "pnpm build && node bin/zhuanspec.js",
|
|
68
|
+
"test": "vitest run",
|
|
69
|
+
"test:watch": "vitest",
|
|
70
|
+
"test:ui": "vitest --ui",
|
|
71
|
+
"test:coverage": "vitest --coverage",
|
|
72
|
+
"test:postinstall": "node scripts/postinstall.js",
|
|
73
|
+
"postinstall": "node scripts/postinstall.js",
|
|
74
|
+
"check:pack-version": "node scripts/pack-version-check.mjs",
|
|
75
|
+
"diagnose:cursor": "node scripts/diagnose-cursor-commands.js",
|
|
76
|
+
"release": "pnpm run release:ci",
|
|
77
|
+
"release:ci": "pnpm run check:pack-version && pnpm exec changeset publish",
|
|
78
|
+
"release:local": "pnpm exec changeset version && pnpm run check:pack-version && pnpm exec changeset publish",
|
|
79
|
+
"changeset": "changeset"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
name: spec-driven
|
|
2
|
+
version: 1
|
|
3
|
+
description: 默认 OpenSpec 工作流 - proposal → specs → design → tasks
|
|
4
|
+
artifacts:
|
|
5
|
+
- id: proposal
|
|
6
|
+
generates: proposal.md
|
|
7
|
+
description: 概述变更的初始提案文档
|
|
8
|
+
template: proposal.md
|
|
9
|
+
instruction: |
|
|
10
|
+
**强制澄清检查点(必须首先执行)**:
|
|
11
|
+
|
|
12
|
+
**重要原则:先查阅后询问,避免过度询问,使用选项式交互**
|
|
13
|
+
|
|
14
|
+
在创建 proposal.md 之前,必须首先:
|
|
15
|
+
1. **查阅现有来源**:查阅现有规范(`zhuanspec/specs/`)、项目约定(`zhuanspec/project.md`)、代码库结构、上下文文件等,尝试从这些来源获取所需信息
|
|
16
|
+
2. **分析用户请求**:识别所有不确定或模糊的方面,但排除那些可以从现有来源确定的方面
|
|
17
|
+
|
|
18
|
+
需要检查的方面(仅在无法从现有来源确定时询问):
|
|
19
|
+
- 范围是否明确?(边界、包含/排除的内容)
|
|
20
|
+
- 技术选择是否明确?(框架、库、架构模式)
|
|
21
|
+
- 优先级是否明确?(如果有多需求)
|
|
22
|
+
- 验收标准是否明确?(如何判断完成)
|
|
23
|
+
- 上下文信息是否充分?(背景、约束、依赖)
|
|
24
|
+
- **实现细节是否明确**:代码改动位置、代码分层架构、实现方式、依赖关系等(仅在无法从代码库分析或现有规范中确定时询问)
|
|
25
|
+
|
|
26
|
+
如果发现任何模糊之处,且无法从现有来源确定:
|
|
27
|
+
- **必须使用选项式交互**:调用编辑器的结构化问答工具(如 Cursor 的 `AskQuestion`),将每个澄清问题转化为带预设选项的选择题
|
|
28
|
+
- 每个问题提供 2-5 个预设选项,末尾包含"其他"选项
|
|
29
|
+
- 尽量将多个问题合并到一次交互中(一次性展示)
|
|
30
|
+
- 必须等待用户选择答案,不能继续
|
|
31
|
+
- 如果用户选择"其他",再针对性追问细节
|
|
32
|
+
- **严禁**在不确定的情况下自行推测、假设或创建提案文件
|
|
33
|
+
- **严禁**要求用户手动输入大段文字来回答澄清问题
|
|
34
|
+
|
|
35
|
+
**何时不需要询问**:
|
|
36
|
+
- 信息已明确:用户请求中已经明确说明
|
|
37
|
+
- 可从规范获取:可以从现有规范中获取
|
|
38
|
+
- 可从项目约定获取:可以从项目约定中获取
|
|
39
|
+
- 可从代码库分析获取:可以通过分析代码库结构、现有模式获取
|
|
40
|
+
- 可从上下文获取:可以从上下文文件、相关变更中获取
|
|
41
|
+
|
|
42
|
+
只有在所有必要的模糊点都明确后,才能继续创建 proposal.md。
|
|
43
|
+
|
|
44
|
+
创建建立此变更原因的提案文档。
|
|
45
|
+
|
|
46
|
+
章节:
|
|
47
|
+
- **Why**:关于问题或机会的 1-2 句话。它解决了什么问题?为什么是现在?
|
|
48
|
+
- **What Changes**:变更的要点列表。具体说明新功能、修改或移除。用 **BREAKING** 标记破坏性更改。
|
|
49
|
+
- **Capabilities**:确定将创建或修改哪些规范:
|
|
50
|
+
- **New Capabilities**:列出正在引入的功能。每个都会成为新的 `specs/<name>/spec.md`。使用 kebab-case 名称(例如,`user-auth`、`data-export`)。
|
|
51
|
+
- **Modified Capabilities**:列出其 REQUIREMENTS 正在更改的现有功能。仅当规范级行为更改时(不仅仅是实现细节)才包含。每个都需要一个增量规范文件。检查 `openspec/specs/` 以获取现有规范名称。如果没有要求更改,则留空。
|
|
52
|
+
- **Skill Mapping**:运行 `zhuanspec skills list` 发现可用 skill,基于 skill description 进行语义匹配:
|
|
53
|
+
1. 仔细阅读每个 skill 的 description,理解其具体功能和适用场景
|
|
54
|
+
2. 只有当实现区域的实际功能与 skill description 明确匹配时才关联
|
|
55
|
+
3. 简单的代码修改(如枚举值增删)应匹配通用编码规范 skill,而非架构级 skill
|
|
56
|
+
4. 避免仅因模块名称或文件路径中的关键词而错误匹配
|
|
57
|
+
5. 在表格中说明匹配理由,确保映射合理性
|
|
58
|
+
此映射将在 tasks.md 中用于 @skill 标注。如果没有匹配 skill 则留空。
|
|
59
|
+
- **Impact**:受影响的代码、API、依赖项或系统。
|
|
60
|
+
|
|
61
|
+
重要提示:Capabilities 部分至关重要。它在提案和规范阶段之间创建契约。在填写之前,请研究现有规范。
|
|
62
|
+
此处列出的每个功能都需要一个相应的规范文件。
|
|
63
|
+
|
|
64
|
+
保持简洁(1-2 页)。专注于"为什么"而不是"如何" -
|
|
65
|
+
实现细节属于 design.md。
|
|
66
|
+
|
|
67
|
+
这是基础 - 规范、设计和任务都建立在此基础上。
|
|
68
|
+
requires: []
|
|
69
|
+
|
|
70
|
+
- id: specs
|
|
71
|
+
generates: "specs/**/*.md"
|
|
72
|
+
description: 变更的详细规范
|
|
73
|
+
template: spec.md
|
|
74
|
+
instruction: |
|
|
75
|
+
创建定义系统应该做什么的规范文件。
|
|
76
|
+
|
|
77
|
+
在 specs/<name>/spec.md 中为每个功能/特性区域创建一个规范文件。
|
|
78
|
+
|
|
79
|
+
增量操作(使用 ## 标题):
|
|
80
|
+
- **ADDED Requirements**:新功能
|
|
81
|
+
- **MODIFIED Requirements**:更改的行为 - 必须包含完整的更新内容
|
|
82
|
+
- **REMOVED Requirements**:已弃用的功能 - 必须包含 **Reason** 和 **Migration**
|
|
83
|
+
- **RENAMED Requirements**:仅名称更改 - 使用 FROM:/TO: 格式
|
|
84
|
+
|
|
85
|
+
格式要求:
|
|
86
|
+
- 每个要求:`### Requirement: <name>` 后跟描述
|
|
87
|
+
- 对规范性要求使用 SHALL/MUST(避免使用 should/may)
|
|
88
|
+
- 每个场景:`#### Scenario: <name>` 使用 WHEN/THEN 格式
|
|
89
|
+
- **关键**:场景必须使用恰好 4 个井号(`####`)。使用 3 个井号或项目符号将静默失败。
|
|
90
|
+
- 每个要求必须至少有一个场景。
|
|
91
|
+
|
|
92
|
+
MODIFIED 要求工作流:
|
|
93
|
+
1. 在 openspec/specs/<capability>/spec.md 中找到现有要求
|
|
94
|
+
2. 复制整个要求块(从 `### Requirement:` 到所有场景)
|
|
95
|
+
3. 粘贴到 `## MODIFIED Requirements` 下并编辑以反映新行为
|
|
96
|
+
4. 确保标题文本完全匹配(忽略空白)
|
|
97
|
+
|
|
98
|
+
常见陷阱:使用部分内容的 MODIFIED 会在归档时丢失详细信息。
|
|
99
|
+
如果在不更改现有行为的情况下添加新关注点,请改用 ADDED。
|
|
100
|
+
|
|
101
|
+
示例:
|
|
102
|
+
```
|
|
103
|
+
## ADDED Requirements
|
|
104
|
+
|
|
105
|
+
### Requirement: 用户可导出数据
|
|
106
|
+
系统 SHALL 允许用户以 CSV 格式导出其数据。
|
|
107
|
+
|
|
108
|
+
#### Scenario: 成功导出
|
|
109
|
+
- **WHEN** 用户点击"导出"按钮
|
|
110
|
+
- **THEN** 系统下载包含所有用户数据的 CSV 文件
|
|
111
|
+
|
|
112
|
+
## REMOVED Requirements
|
|
113
|
+
|
|
114
|
+
### Requirement: 旧版导出
|
|
115
|
+
**Reason**: 已被新的导出系统替代
|
|
116
|
+
**Migration**: 使用新的导出端点 /api/v2/export
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
规范应该是可测试的 - 每个场景都是一个潜在的测试用例。
|
|
120
|
+
requires:
|
|
121
|
+
- proposal
|
|
122
|
+
|
|
123
|
+
- id: design
|
|
124
|
+
generates: design.md
|
|
125
|
+
description: 包含实现细节的技术设计文档
|
|
126
|
+
template: design.md
|
|
127
|
+
instruction: |
|
|
128
|
+
创建解释如何实现变更的设计文档。
|
|
129
|
+
|
|
130
|
+
何时包含 design.md(仅在以下任何情况适用时创建):
|
|
131
|
+
- 横切变更(多个服务/模块)或新的架构模式
|
|
132
|
+
- 新的外部依赖或重要的数据模型更改
|
|
133
|
+
- 安全性、性能或迁移复杂性
|
|
134
|
+
- 在编码之前从技术决策中受益的模糊性
|
|
135
|
+
|
|
136
|
+
章节:
|
|
137
|
+
- **Context**:背景、当前状态、约束、利益相关者
|
|
138
|
+
- **Goals / Non-Goals**:此设计实现的内容和明确排除的内容
|
|
139
|
+
- **Decisions**:关键的技术选择及其理由(为什么选择 X 而不是 Y?)。包括为每个决策考虑的替代方案。
|
|
140
|
+
- **Risks / Trade-offs**:已知的限制,可能出错的事情。格式:[Risk] → Mitigation
|
|
141
|
+
- **Migration Plan**:部署步骤、回滚策略(如果适用)
|
|
142
|
+
- **Open Questions**:待解决的决定或未知数
|
|
143
|
+
|
|
144
|
+
专注于架构和方法,而不是逐行实现。
|
|
145
|
+
参考提案了解动机,参考规范了解要求。
|
|
146
|
+
|
|
147
|
+
好的设计文档解释了技术决策背后的"原因"。
|
|
148
|
+
requires:
|
|
149
|
+
- proposal
|
|
150
|
+
|
|
151
|
+
- id: tasks
|
|
152
|
+
generates: tasks.md
|
|
153
|
+
description: 从规范和设计派生的实施任务
|
|
154
|
+
template: tasks.md
|
|
155
|
+
instruction: |
|
|
156
|
+
创建分解实施工作的任务列表。
|
|
157
|
+
|
|
158
|
+
指导原则:
|
|
159
|
+
- 在 ## 编号标题下对相关任务进行分组
|
|
160
|
+
- 每个任务是一个复选框:- [ ] X.Y 任务描述
|
|
161
|
+
- 任务应该足够小,可以在一次会话中完成
|
|
162
|
+
- 按依赖关系排序任务(必须先做什么?)
|
|
163
|
+
|
|
164
|
+
技能标注(@skill 标签):
|
|
165
|
+
- 在创建 tasks.md 之前,先运行 `zhuanspec skills list --json` 获取当前环境中可用的 skill 列表及其 description
|
|
166
|
+
- **匹配原则**:仔细阅读每个 skill 的 description,基于任务的具体功能内容进行语义匹配
|
|
167
|
+
- 精确匹配:任务功能应与 skill description 明确对应
|
|
168
|
+
- 避免过度匹配:简单的代码修改(如增删枚举值、修改常量)只需通用编码规范 skill
|
|
169
|
+
- 避免关键词匹配:不要仅因文件名或路径包含某些词就匹配相关 skill
|
|
170
|
+
- 格式:在任务描述末尾添加 `@skill:<real-skill-name>`
|
|
171
|
+
- 支持多个 skill:`@skill:skill-1,skill-2`
|
|
172
|
+
- 使用 skill 的真实名称(即 SKILL.md 中的 name 字段),而非类别名
|
|
173
|
+
- 如果任务不需要特定 skill 或没有匹配的 skill,可以省略标签
|
|
174
|
+
- 在 apply 阶段,AI 应根据 @skill 标签直接调用对应的 skill
|
|
175
|
+
|
|
176
|
+
示例(假设环境中安装了 java-db-schema-standards、java-dao-standards 等 skill):
|
|
177
|
+
```
|
|
178
|
+
## 1. Setup
|
|
179
|
+
|
|
180
|
+
- [ ] 1.1 Create new module structure
|
|
181
|
+
- [ ] 1.2 Add dependencies to package.json
|
|
182
|
+
|
|
183
|
+
## 2. Core Implementation
|
|
184
|
+
|
|
185
|
+
- [ ] 2.1 Create database schema @skill:java-db-schema-standards
|
|
186
|
+
- [ ] 2.2 Implement DAO layer @skill:java-dao-standards,kf-backend-coding-standards
|
|
187
|
+
- [ ] 2.3 Implement RPC interface @skill:java-scf-rpc-usage-skill
|
|
188
|
+
|
|
189
|
+
## 3. Testing
|
|
190
|
+
|
|
191
|
+
- [ ] 3.1 Write unit tests @skill:generate-mockito-unit-test-skill
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
参考规范了解需要构建的内容,参考设计了解如何构建。
|
|
195
|
+
每个任务应该是可验证的 - 您知道何时完成。
|
|
196
|
+
requires:
|
|
197
|
+
- specs
|
|
198
|
+
- design
|
|
199
|
+
|
|
200
|
+
apply:
|
|
201
|
+
requires: [tasks]
|
|
202
|
+
tracks: tasks.md
|
|
203
|
+
instruction: |
|
|
204
|
+
阅读上下文文件,处理待处理的任务,在完成时标记为完成。
|
|
205
|
+
如果遇到阻碍或需要澄清,请暂停。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
## Why
|
|
2
|
+
|
|
3
|
+
<!-- 解释此变更的动机。它解决了什么问题?为什么是现在? -->
|
|
4
|
+
|
|
5
|
+
## What Changes
|
|
6
|
+
|
|
7
|
+
<!-- 描述将要更改的内容。具体说明新功能、修改或移除。 -->
|
|
8
|
+
|
|
9
|
+
## Capabilities
|
|
10
|
+
|
|
11
|
+
### New Capabilities
|
|
12
|
+
<!-- 正在引入的功能。将 <name> 替换为 kebab-case 标识符(例如,user-auth、data-export、api-rate-limiting)。每个都会创建 specs/<name>/spec.md -->
|
|
13
|
+
- `<name>`: <此功能涵盖内容的简要描述>
|
|
14
|
+
|
|
15
|
+
### Modified Capabilities
|
|
16
|
+
<!-- 其 REQUIREMENTS 正在更改的现有功能(不仅仅是实现)。
|
|
17
|
+
仅当规范级行为更改时,才在此处列出。每个都需要一个增量规范文件。
|
|
18
|
+
使用 zhuanspec/specs/ 中的现有规范名称。如果没有要求更改,则留空。 -->
|
|
19
|
+
- `<existing-name>`: <正在更改的要求>
|
|
20
|
+
|
|
21
|
+
## Skill Mapping
|
|
22
|
+
|
|
23
|
+
<!-- 重要:运行 `zhuanspec skills list` 查看可用 skill,基于 skill description 进行语义匹配。
|
|
24
|
+
匹配原则:
|
|
25
|
+
1. 仔细阅读每个 skill 的 description,理解其具体功能和适用场景
|
|
26
|
+
2. 只有当实现区域的实际功能与 skill description 明确匹配时才关联
|
|
27
|
+
3. 比如简单的代码修改(如枚举值增删)应匹配通用编码规范 skill,而非架构级 skill
|
|
28
|
+
4. 避免仅因模块名称或文件路径中的关键词而错误匹配
|
|
29
|
+
|
|
30
|
+
示例:
|
|
31
|
+
- 修改枚举值 → kf-backend-coding-standards(编码规范)
|
|
32
|
+
- 定义 RPC 接口 → java-scf-rpc-usage-skill(RPC 调用规范)
|
|
33
|
+
- 数据库建表 → java-db-schema-standards(数据库规范)
|
|
34
|
+
|
|
35
|
+
此映射将用于 tasks.md 中的 @skill 标注。如果没有匹配 skill 则留空。 -->
|
|
36
|
+
|
|
37
|
+
| 实现区域 | 实际功能 | 关联 Skill | 匹配理由 |
|
|
38
|
+
|---------|---------|-----------|---------|
|
|
39
|
+
| <!-- 模块/文件 --> | <!-- 具体功能 --> | <!-- skill 名称 --> | <!-- 为什么匹配 --> |
|
|
40
|
+
|
|
41
|
+
## Impact
|
|
42
|
+
|
|
43
|
+
<!-- 受影响的代码、API、依赖项、系统 -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## 1. <!-- 任务组名称 -->
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
@skill 标注指南:
|
|
5
|
+
1. 首先运行 `zhuanspec skills list --json` 获取可用 skill 列表及其 description
|
|
6
|
+
2. 仔细阅读每个 skill 的 description,理解其具体功能和适用场景
|
|
7
|
+
3. 基于任务的具体功能内容,匹配最相关的 skill
|
|
8
|
+
4. 匹配原则:
|
|
9
|
+
- 精确匹配:任务功能应与 skill description 明确对应
|
|
10
|
+
- 避免过度匹配:简单的代码修改(如增删枚举值、修改常量)只需通用编码规范 skill
|
|
11
|
+
- 避免关键词匹配:不要仅因文件名或路径包含某些词就匹配相关 skill
|
|
12
|
+
|
|
13
|
+
示例:
|
|
14
|
+
- [ ] 1.1 新增 AssBusinessDataEnum 枚举值 ASSEMBLY_MACHINE_FLAG → @skill:kf-backend-coding-standards
|
|
15
|
+
- [ ] 1.2 定义 SCF 接口 IAssQueryService → @skill:java-scf-rpc-usage-skill
|
|
16
|
+
- [ ] 1.3 创建数据库表 ass_order → @skill:java-db-schema-standards
|
|
17
|
+
-->
|
|
18
|
+
|
|
19
|
+
- [ ] 1.1 <!-- 任务描述 --> <!-- @skill:real-skill-name -->
|
|
20
|
+
- [ ] 1.2 <!-- 任务描述 -->
|
|
21
|
+
|
|
22
|
+
## 2. <!-- 任务组名称 -->
|
|
23
|
+
|
|
24
|
+
- [ ] 2.1 <!-- 任务描述 -->
|
|
25
|
+
- [ ] 2.2 <!-- 任务描述 -->
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
name: tdd
|
|
2
|
+
version: 1
|
|
3
|
+
description: Test-driven development workflow - tests → implementation → docs
|
|
4
|
+
artifacts:
|
|
5
|
+
- id: spec
|
|
6
|
+
generates: spec.md
|
|
7
|
+
description: Feature specification defining requirements
|
|
8
|
+
template: spec.md
|
|
9
|
+
instruction: |
|
|
10
|
+
Create the feature specification that defines WHAT to build.
|
|
11
|
+
|
|
12
|
+
Sections:
|
|
13
|
+
- **Feature**: Name and high-level description of the feature's purpose and user value
|
|
14
|
+
- **Requirements**: List of specific requirements. Use SHALL/MUST for normative language.
|
|
15
|
+
- **Acceptance Criteria**: Testable criteria in WHEN/THEN format
|
|
16
|
+
|
|
17
|
+
Format requirements:
|
|
18
|
+
- Each requirement should be specific and testable
|
|
19
|
+
- Use `#### Scenario: <name>` with WHEN/THEN format for acceptance criteria
|
|
20
|
+
- Define edge cases and error scenarios explicitly
|
|
21
|
+
- Every requirement MUST have at least one scenario
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
```
|
|
25
|
+
## Feature: User Authentication
|
|
26
|
+
|
|
27
|
+
Users can securely log into the application.
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
### Requirement: Password validation
|
|
32
|
+
The system SHALL validate passwords meet minimum security requirements.
|
|
33
|
+
|
|
34
|
+
#### Scenario: Valid password accepted
|
|
35
|
+
- **WHEN** password has 8+ chars, uppercase, lowercase, and number
|
|
36
|
+
- **THEN** password is accepted
|
|
37
|
+
|
|
38
|
+
#### Scenario: Weak password rejected
|
|
39
|
+
- **WHEN** password is less than 8 characters
|
|
40
|
+
- **THEN** system displays "Password too short" error
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This spec drives test creation - each scenario becomes a test case.
|
|
44
|
+
requires: []
|
|
45
|
+
|
|
46
|
+
- id: tests
|
|
47
|
+
generates: "tests/*.test.ts"
|
|
48
|
+
description: Test files written before implementation
|
|
49
|
+
template: test.md
|
|
50
|
+
instruction: |
|
|
51
|
+
Write tests BEFORE implementation (TDD red phase).
|
|
52
|
+
|
|
53
|
+
File naming:
|
|
54
|
+
- Create test files as `tests/<feature>.test.ts`
|
|
55
|
+
- One test file per feature/capability
|
|
56
|
+
- Use descriptive names matching the spec
|
|
57
|
+
|
|
58
|
+
Test structure:
|
|
59
|
+
- Use Given/When/Then format matching spec scenarios
|
|
60
|
+
- Group related tests with `describe()` blocks
|
|
61
|
+
- Each scenario from spec becomes at least one `it()` test
|
|
62
|
+
|
|
63
|
+
Coverage requirements:
|
|
64
|
+
- Cover each requirement from the spec
|
|
65
|
+
- Include happy path (success cases)
|
|
66
|
+
- Include edge cases (boundary conditions)
|
|
67
|
+
- Include error scenarios (invalid input, failures)
|
|
68
|
+
- Tests should fail initially (no implementation yet)
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
```typescript
|
|
72
|
+
describe('Password validation', () => {
|
|
73
|
+
it('accepts valid password with all requirements', () => {
|
|
74
|
+
// GIVEN a password meeting all requirements
|
|
75
|
+
const password = 'SecurePass1';
|
|
76
|
+
// WHEN validating
|
|
77
|
+
const result = validatePassword(password);
|
|
78
|
+
// THEN it should be accepted
|
|
79
|
+
expect(result.valid).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('rejects password shorter than 8 characters', () => {
|
|
83
|
+
// GIVEN a short password
|
|
84
|
+
const password = 'Short1';
|
|
85
|
+
// WHEN validating
|
|
86
|
+
const result = validatePassword(password);
|
|
87
|
+
// THEN it should be rejected with message
|
|
88
|
+
expect(result.valid).toBe(false);
|
|
89
|
+
expect(result.error).toBe('Password too short');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Follow the spec requirements exactly - tests verify the spec.
|
|
95
|
+
requires:
|
|
96
|
+
- spec
|
|
97
|
+
|
|
98
|
+
- id: implementation
|
|
99
|
+
generates: "src/*.ts"
|
|
100
|
+
description: Implementation code to pass the tests
|
|
101
|
+
template: implementation.md
|
|
102
|
+
instruction: |
|
|
103
|
+
Implement the feature to make tests pass (TDD green phase).
|
|
104
|
+
|
|
105
|
+
TDD workflow:
|
|
106
|
+
1. Run tests - confirm they fail (red)
|
|
107
|
+
2. Write minimal code to pass ONE test
|
|
108
|
+
3. Run tests - confirm that test passes (green)
|
|
109
|
+
4. Refactor if needed while keeping tests green
|
|
110
|
+
5. Repeat for next failing test
|
|
111
|
+
|
|
112
|
+
Implementation guidelines:
|
|
113
|
+
- Write minimal code to pass each test - no more, no less
|
|
114
|
+
- Run tests frequently to verify progress
|
|
115
|
+
- Keep functions small and focused
|
|
116
|
+
- Use clear, descriptive names
|
|
117
|
+
|
|
118
|
+
Code organization:
|
|
119
|
+
- Create source files in `src/<feature>.ts`
|
|
120
|
+
- Export public API clearly
|
|
121
|
+
- Keep implementation details private
|
|
122
|
+
- Add JSDoc comments for public functions
|
|
123
|
+
|
|
124
|
+
Example structure:
|
|
125
|
+
```typescript
|
|
126
|
+
/**
|
|
127
|
+
* Validates a password meets security requirements.
|
|
128
|
+
* @param password - The password to validate
|
|
129
|
+
* @returns Validation result with valid flag and optional error
|
|
130
|
+
*/
|
|
131
|
+
export function validatePassword(password: string): ValidationResult {
|
|
132
|
+
if (password.length < 8) {
|
|
133
|
+
return { valid: false, error: 'Password too short' };
|
|
134
|
+
}
|
|
135
|
+
// ... additional checks
|
|
136
|
+
return { valid: true };
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Don't over-engineer - implement only what tests require.
|
|
141
|
+
requires:
|
|
142
|
+
- tests
|
|
143
|
+
|
|
144
|
+
- id: docs
|
|
145
|
+
generates: "docs/*.md"
|
|
146
|
+
description: Documentation for the implemented feature
|
|
147
|
+
template: docs.md
|
|
148
|
+
instruction: |
|
|
149
|
+
Document the implemented feature.
|
|
150
|
+
|
|
151
|
+
Sections:
|
|
152
|
+
- **Overview**: What the feature does and why it exists (1-2 paragraphs)
|
|
153
|
+
- **Getting Started**: Quick start guide to use the feature immediately
|
|
154
|
+
- **Examples**: Code examples showing common use cases
|
|
155
|
+
- **Reference**: Detailed API documentation, configuration options
|
|
156
|
+
|
|
157
|
+
Guidelines:
|
|
158
|
+
- Write for the user, not the developer
|
|
159
|
+
- Start with the most common use case
|
|
160
|
+
- Include copy-pasteable code examples
|
|
161
|
+
- Document all configuration options with defaults
|
|
162
|
+
- Note any limitations, edge cases, or gotchas
|
|
163
|
+
- Link to related features or specs
|
|
164
|
+
|
|
165
|
+
Example structure:
|
|
166
|
+
```markdown
|
|
167
|
+
## Overview
|
|
168
|
+
|
|
169
|
+
Password validation ensures user passwords meet security requirements
|
|
170
|
+
before account creation or password changes.
|
|
171
|
+
|
|
172
|
+
## Getting Started
|
|
173
|
+
|
|
174
|
+
Import and use the validation function:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { validatePassword } from './password';
|
|
178
|
+
|
|
179
|
+
const result = validatePassword('MySecurePass1');
|
|
180
|
+
if (!result.valid) {
|
|
181
|
+
console.error(result.error);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Examples
|
|
186
|
+
|
|
187
|
+
### Basic validation
|
|
188
|
+
...
|
|
189
|
+
|
|
190
|
+
### Custom error handling
|
|
191
|
+
...
|
|
192
|
+
|
|
193
|
+
## Reference
|
|
194
|
+
|
|
195
|
+
### validatePassword(password)
|
|
196
|
+
|
|
197
|
+
| Parameter | Type | Description |
|
|
198
|
+
|-----------|------|-------------|
|
|
199
|
+
| password | string | The password to validate |
|
|
200
|
+
|
|
201
|
+
**Returns**: `{ valid: boolean, error?: string }`
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Reference the spec for requirements, implementation for details.
|
|
205
|
+
requires:
|
|
206
|
+
- implementation
|
|
207
|
+
|
|
208
|
+
apply:
|
|
209
|
+
requires: [tests]
|
|
210
|
+
tracks: null
|
|
211
|
+
instruction: |
|
|
212
|
+
Run tests to see failures. Implement minimal code to pass each test.
|
|
213
|
+
Refactor while keeping tests green.
|