@ecc-hgy/ae 0.2.0 → 0.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/README.md +13 -9
- package/package.json +1 -1
- package/src/cli.js +3 -4
- package/src/commands/init.js +5 -61
- package/src/commands/setup.js +2 -10
- package/src/skeleton.js +0 -84
- package/src/utils/report.js +2 -3
- package/templates/entries/AGENTS.md +17 -14
- package/templates/entries/CLAUDE.md +3 -19
- package/templates/ae-rules.md +0 -17
package/README.md
CHANGED
|
@@ -10,7 +10,6 @@ Agentic Engineering is a lightweight personal software lifecycle method: SDD rec
|
|
|
10
10
|
```bash
|
|
11
11
|
cd /your/project
|
|
12
12
|
npx @ecc-hgy/ae setup
|
|
13
|
-
# Optional: edit config/ae-rules.md
|
|
14
13
|
npx @ecc-hgy/ae init "one-line project goal"
|
|
15
14
|
```
|
|
16
15
|
|
|
@@ -18,23 +17,27 @@ npx @ecc-hgy/ae init "one-line project goal"
|
|
|
18
17
|
|
|
19
18
|
- `skills/` -> `.claude/skills/`
|
|
20
19
|
- `skills/` -> `.codex/skills/`
|
|
21
|
-
- `templates/ae-rules.md` -> `config/ae-rules.md`
|
|
22
20
|
|
|
23
|
-
`ae init` creates the platform-neutral project skeleton
|
|
21
|
+
`ae init` creates the platform-neutral project skeleton and renders entry files. Project-specific rules live directly in `AGENTS.md`.
|
|
24
22
|
|
|
25
23
|
## CLI
|
|
26
24
|
|
|
27
25
|
```bash
|
|
28
26
|
ae setup
|
|
29
27
|
ae init "one-line project goal"
|
|
30
|
-
ae init "goal" --rules /path/to/rules.md
|
|
31
28
|
ae --help
|
|
32
29
|
ae --version
|
|
33
30
|
```
|
|
34
31
|
|
|
35
|
-
Both commands are idempotent. They create missing files, skip existing files, and never overwrite
|
|
32
|
+
Both commands are idempotent. They create missing files, skip existing files, and never overwrite existing entry files. There is no `--force` mode.
|
|
36
33
|
|
|
37
|
-
`ae init` requires `ae setup` to have run first. This keeps
|
|
34
|
+
`ae init` requires `ae setup` to have run first. This keeps asset installation explicit before skeleton creation.
|
|
35
|
+
|
|
36
|
+
## Single Source Of Truth
|
|
37
|
+
|
|
38
|
+
- `AGENTS.md` is the canonical project instructions file.
|
|
39
|
+
- `CLAUDE.md` is a short pointer to `AGENTS.md`; do not maintain a separate copy there.
|
|
40
|
+
- All project-specific rules live in `AGENTS.md` under `## Project-Specific Rules`.
|
|
38
41
|
|
|
39
42
|
## Platform Scope
|
|
40
43
|
|
|
@@ -90,7 +93,7 @@ The two hard gates are:
|
|
|
90
93
|
bin/ npm executable entry
|
|
91
94
|
src/ zero-dependency CLI implementation
|
|
92
95
|
skills/ Selected Agentic Engineering skills
|
|
93
|
-
templates/ Entry file templates
|
|
96
|
+
templates/ Entry file templates
|
|
94
97
|
docs/ User-facing methodology docs
|
|
95
98
|
design/ Design records for this repository
|
|
96
99
|
tests/ node:test coverage for CLI behavior
|
|
@@ -100,8 +103,9 @@ tests/ node:test coverage for CLI behavior
|
|
|
100
103
|
|
|
101
104
|
1. Stage 2: npm CLI asset distribution with `init` and `ingest-mother`. Superseded by v0.2.
|
|
102
105
|
2. v0.2: split CLI into `setup` + `init`, remove slash command assets, and move rule sync into `ae init`.
|
|
103
|
-
3.
|
|
104
|
-
4.
|
|
106
|
+
3. v0.3: remove rules injection; make `AGENTS.md` the single source of truth and `CLAUDE.md` a pointer.
|
|
107
|
+
4. Stage 3: design `ae update` for skill/template upgrades and implement `ae index-rebuild`.
|
|
108
|
+
5. Add CI and Windows path validation before broader distribution.
|
|
105
109
|
|
|
106
110
|
## Limits
|
|
107
111
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -5,9 +5,9 @@ import { packageRoot } from './utils/paths.js';
|
|
|
5
5
|
const HELP = `Usage: ae <command> [options]
|
|
6
6
|
|
|
7
7
|
Commands:
|
|
8
|
-
setup Install skills to .claude/.codex
|
|
9
|
-
init [goal] Create spec/ skeleton
|
|
10
|
-
|
|
8
|
+
setup Install skills to .claude/.codex.
|
|
9
|
+
init [goal] Create spec/ skeleton and render entry files.
|
|
10
|
+
Requires \`ae setup\` to have run.
|
|
11
11
|
|
|
12
12
|
Options:
|
|
13
13
|
-h, --help Show help
|
|
@@ -15,7 +15,6 @@ Options:
|
|
|
15
15
|
|
|
16
16
|
Typical workflow:
|
|
17
17
|
ae setup
|
|
18
|
-
[optional] edit config/ae-rules.md
|
|
19
18
|
ae init "<one-line project goal>"`;
|
|
20
19
|
|
|
21
20
|
export async function run(argv = []) {
|
package/src/commands/init.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { mkdir,
|
|
1
|
+
import { mkdir, stat } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { createDirectories, renderEntryFiles
|
|
3
|
+
import { createDirectories, renderEntryFiles } from '../skeleton.js';
|
|
4
4
|
import { templatesPath } from '../utils/paths.js';
|
|
5
5
|
import { formatInitReport } from '../utils/report.js';
|
|
6
6
|
|
|
@@ -10,9 +10,7 @@ const HELP = `Usage: ae init [goal] [options]
|
|
|
10
10
|
|
|
11
11
|
Options:
|
|
12
12
|
--target <path> Initialize this project directory. Defaults to cwd.
|
|
13
|
-
--
|
|
14
|
-
Defaults to <target>/config/ae-rules.md when present.
|
|
15
|
-
--dry-run Show what would be created or synced without writing files.
|
|
13
|
+
--dry-run Show what would be created without writing files.
|
|
16
14
|
-h, --help Show help`;
|
|
17
15
|
|
|
18
16
|
export async function run(argv = []) {
|
|
@@ -51,16 +49,10 @@ export async function run(argv = []) {
|
|
|
51
49
|
created.push(...entries.created);
|
|
52
50
|
skipped.push(...entries.skipped);
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
console.log(formatInitReport({ created, skipped, target, rules, dryRun: options.dryRun }));
|
|
52
|
+
console.log(formatInitReport({ created, skipped, target, dryRun: options.dryRun }));
|
|
57
53
|
return 0;
|
|
58
54
|
} catch (error) {
|
|
59
|
-
|
|
60
|
-
console.error(`AE init failed: ${error.message}`);
|
|
61
|
-
} else {
|
|
62
|
-
console.error(`AE init failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
63
|
-
}
|
|
55
|
+
console.error(`AE init failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
64
56
|
process.exitCode = 1;
|
|
65
57
|
return 1;
|
|
66
58
|
}
|
|
@@ -69,7 +61,6 @@ export async function run(argv = []) {
|
|
|
69
61
|
function parseArgs(argv) {
|
|
70
62
|
const options = {
|
|
71
63
|
target: undefined,
|
|
72
|
-
rules: undefined,
|
|
73
64
|
goalParts: [],
|
|
74
65
|
dryRun: false,
|
|
75
66
|
help: false,
|
|
@@ -88,13 +79,6 @@ function parseArgs(argv) {
|
|
|
88
79
|
}
|
|
89
80
|
options.target = value;
|
|
90
81
|
index += 1;
|
|
91
|
-
} else if (arg === '--rules') {
|
|
92
|
-
const value = argv[index + 1];
|
|
93
|
-
if (!value) {
|
|
94
|
-
throw new Error('--rules requires a path');
|
|
95
|
-
}
|
|
96
|
-
options.rules = value;
|
|
97
|
-
index += 1;
|
|
98
82
|
} else if (arg.startsWith('-')) {
|
|
99
83
|
throw new Error(`Unknown option for init: ${arg}`);
|
|
100
84
|
} else {
|
|
@@ -104,7 +88,6 @@ function parseArgs(argv) {
|
|
|
104
88
|
|
|
105
89
|
return {
|
|
106
90
|
target: options.target,
|
|
107
|
-
rules: options.rules,
|
|
108
91
|
dryRun: options.dryRun,
|
|
109
92
|
help: options.help,
|
|
110
93
|
goal: options.goalParts.length > 0 ? options.goalParts.join(' ') : undefined,
|
|
@@ -134,41 +117,6 @@ async function hasInstalledAssets(target) {
|
|
|
134
117
|
);
|
|
135
118
|
}
|
|
136
119
|
|
|
137
|
-
async function syncRules(target, options) {
|
|
138
|
-
const rulesPath = options.rules
|
|
139
|
-
? path.resolve(process.cwd(), options.rules)
|
|
140
|
-
: path.join(target, 'config', 'ae-rules.md');
|
|
141
|
-
|
|
142
|
-
const rulesInfo = await statMaybe(rulesPath);
|
|
143
|
-
if (!rulesInfo) {
|
|
144
|
-
if (options.rules) {
|
|
145
|
-
throw new Error(`rules file does not exist: ${rulesPath}`);
|
|
146
|
-
}
|
|
147
|
-
return 'skipped (no rules file)';
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (rulesInfo.isDirectory()) {
|
|
151
|
-
throw new Error(`expected a file, got directory: ${rulesPath}`);
|
|
152
|
-
}
|
|
153
|
-
if (!rulesInfo.isFile()) {
|
|
154
|
-
throw new Error(`expected a file: ${rulesPath}`);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const rulesContent = await readFile(rulesPath, 'utf8');
|
|
158
|
-
const agents = path.join(target, 'AGENTS.md');
|
|
159
|
-
const claude = path.join(target, 'CLAUDE.md');
|
|
160
|
-
const agentsExists = await exists(agents);
|
|
161
|
-
const claudeExists = await exists(claude);
|
|
162
|
-
|
|
163
|
-
if (options.dryRun && (!agentsExists || !claudeExists)) {
|
|
164
|
-
return 'would sync';
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
await syncRulesBlock(agents, rulesContent, { dryRun: options.dryRun });
|
|
168
|
-
await syncRulesBlock(claude, rulesContent, { dryRun: options.dryRun });
|
|
169
|
-
return options.dryRun ? 'would sync' : 'synced';
|
|
170
|
-
}
|
|
171
|
-
|
|
172
120
|
async function statMaybe(value) {
|
|
173
121
|
try {
|
|
174
122
|
return await stat(value);
|
|
@@ -180,10 +128,6 @@ async function statMaybe(value) {
|
|
|
180
128
|
}
|
|
181
129
|
}
|
|
182
130
|
|
|
183
|
-
async function exists(value) {
|
|
184
|
-
return Boolean(await statMaybe(value));
|
|
185
|
-
}
|
|
186
|
-
|
|
187
131
|
async function isDirectory(value) {
|
|
188
132
|
const current = await statMaybe(value);
|
|
189
133
|
return Boolean(current?.isDirectory());
|
package/src/commands/setup.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdir, stat } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
import { skillsPath
|
|
3
|
+
import { copyTreeIdempotent } from '../utils/copy.js';
|
|
4
|
+
import { skillsPath } from '../utils/paths.js';
|
|
5
5
|
import { formatSetupReport } from '../utils/report.js';
|
|
6
6
|
|
|
7
7
|
const HELP = `Usage: ae setup [options]
|
|
@@ -45,14 +45,6 @@ export async function run(argv = []) {
|
|
|
45
45
|
skipped.push(...result.skipped.map((item) => toDisplayPath(path.join(group.prefix, item))));
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const rulesResult = await copyFileIdempotent(
|
|
49
|
-
templatesPath('ae-rules.md'),
|
|
50
|
-
path.join(target, 'config', 'ae-rules.md'),
|
|
51
|
-
{ dryRun: options.dryRun },
|
|
52
|
-
);
|
|
53
|
-
created.push(...rulesResult.created.map((item) => toDisplayPath(path.join('config', item))));
|
|
54
|
-
skipped.push(...rulesResult.skipped.map((item) => toDisplayPath(path.join('config', item))));
|
|
55
|
-
|
|
56
48
|
console.log(formatSetupReport({ created, skipped, target, dryRun: options.dryRun }));
|
|
57
49
|
return 0;
|
|
58
50
|
} catch (error) {
|
package/src/skeleton.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
export const RULES_BEGIN = '<!-- AE:RULES:BEGIN -->';
|
|
5
|
-
export const RULES_END = '<!-- AE:RULES:END -->';
|
|
6
|
-
const RULES_COMMENT = '<!-- 由 ae init 从 config/ae-rules.md 或 --rules 指定文件同步,勿手改 -->';
|
|
7
4
|
const DEFAULT_DRY_RUN = false;
|
|
8
5
|
|
|
9
6
|
const SKELETON_DIRS = [
|
|
@@ -32,13 +29,6 @@ const ENTRY_FILES = [
|
|
|
32
29
|
path.join('spec', 'INDEX.md'),
|
|
33
30
|
];
|
|
34
31
|
|
|
35
|
-
export class RulesMarkerError extends Error {
|
|
36
|
-
constructor(message) {
|
|
37
|
-
super(message);
|
|
38
|
-
this.name = 'RulesMarkerError';
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
32
|
export async function createDirectories(target, options = {}) {
|
|
43
33
|
const dryRun = Boolean(options.dryRun ?? DEFAULT_DRY_RUN);
|
|
44
34
|
const created = [];
|
|
@@ -112,82 +102,12 @@ export async function renderEntryFiles(target, templateRoot, options = {}) {
|
|
|
112
102
|
return { created, skipped, updated };
|
|
113
103
|
}
|
|
114
104
|
|
|
115
|
-
export async function syncRulesBlock(targetFile, rulesContent, options = {}) {
|
|
116
|
-
const dryRun = Boolean(options.dryRun ?? DEFAULT_DRY_RUN);
|
|
117
|
-
const content = await readFile(targetFile, 'utf8');
|
|
118
|
-
const ranges = findRulesRanges(content, targetFile);
|
|
119
|
-
const block = buildRulesBlock(rulesContent);
|
|
120
|
-
const created = [];
|
|
121
|
-
const skipped = [];
|
|
122
|
-
const updated = [];
|
|
123
|
-
const display = path.basename(targetFile);
|
|
124
|
-
|
|
125
|
-
if (ranges.length === 0) {
|
|
126
|
-
const next = content.endsWith('\n') ? `${content}\n${block}` : `${content}\n\n${block}`;
|
|
127
|
-
if (next === content) {
|
|
128
|
-
skipped.push(display);
|
|
129
|
-
return { created, skipped, updated };
|
|
130
|
-
}
|
|
131
|
-
created.push(`${display}#AE:RULES`);
|
|
132
|
-
if (!dryRun) {
|
|
133
|
-
await writeFile(targetFile, next);
|
|
134
|
-
}
|
|
135
|
-
return { created, skipped, updated };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const [{ start, end }] = ranges;
|
|
139
|
-
const next = `${content.slice(0, start)}${block}${content.slice(end)}`;
|
|
140
|
-
|
|
141
|
-
if (next === content) {
|
|
142
|
-
skipped.push(`${display}#AE:RULES`);
|
|
143
|
-
return { created, skipped, updated };
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
updated.push(`${display}#AE:RULES`);
|
|
147
|
-
if (!dryRun) {
|
|
148
|
-
await writeFile(targetFile, next);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return { created, skipped, updated };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function buildRulesBlock(rulesContent) {
|
|
155
|
-
const normalized = rulesContent.endsWith('\n') ? rulesContent : `${rulesContent}\n`;
|
|
156
|
-
return `${RULES_BEGIN}\n${RULES_COMMENT}\n${normalized}${RULES_END}\n`;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
105
|
function renderTemplate(template, replacements) {
|
|
160
106
|
return template
|
|
161
107
|
.replaceAll('{{PROJECT_NAME}}', replacements.projectName)
|
|
162
108
|
.replaceAll('{{PROJECT_GOAL}}', replacements.projectGoal);
|
|
163
109
|
}
|
|
164
110
|
|
|
165
|
-
function findRulesRanges(content, targetFile) {
|
|
166
|
-
const beginMatches = [...content.matchAll(new RegExp(escapeRegExp(RULES_BEGIN), 'g'))];
|
|
167
|
-
const endMatches = [...content.matchAll(new RegExp(escapeRegExp(RULES_END), 'g'))];
|
|
168
|
-
|
|
169
|
-
if (beginMatches.length !== endMatches.length || beginMatches.length > 1) {
|
|
170
|
-
throw new RulesMarkerError(`malformed AE:RULES markers in ${targetFile}`);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (beginMatches.length === 0) {
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const start = beginMatches[0].index;
|
|
178
|
-
const end = endMatches[0].index + RULES_END.length;
|
|
179
|
-
if (start > endMatches[0].index) {
|
|
180
|
-
throw new RulesMarkerError(`malformed AE:RULES markers in ${targetFile}`);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
let rangeEnd = end;
|
|
184
|
-
if (content.slice(rangeEnd, rangeEnd + 1) === '\n') {
|
|
185
|
-
rangeEnd += 1;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return [{ start, end: rangeEnd }];
|
|
189
|
-
}
|
|
190
|
-
|
|
191
111
|
async function exists(value) {
|
|
192
112
|
try {
|
|
193
113
|
await stat(value);
|
|
@@ -203,7 +123,3 @@ async function exists(value) {
|
|
|
203
123
|
function normalizeRelative(value) {
|
|
204
124
|
return value.split(path.sep).join('/');
|
|
205
125
|
}
|
|
206
|
-
|
|
207
|
-
function escapeRegExp(value) {
|
|
208
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
209
|
-
}
|
package/src/utils/report.js
CHANGED
|
@@ -5,17 +5,16 @@ export function formatSetupReport({ created, skipped, target, dryRun }) {
|
|
|
5
5
|
`- ${installedLabel}: ${created.length} (${summarize(created)})`,
|
|
6
6
|
`- Skipped: ${skipped.length} (${summarize(skipped)})`,
|
|
7
7
|
`- Target: ${target}`,
|
|
8
|
-
`- Next:
|
|
8
|
+
`- Next: run \`ae init "<goal>"\`.`,
|
|
9
9
|
].join('\n');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function formatInitReport({ created, skipped, target,
|
|
12
|
+
export function formatInitReport({ created, skipped, target, dryRun }) {
|
|
13
13
|
const createdLabel = dryRun ? 'Would create' : 'Created';
|
|
14
14
|
return [
|
|
15
15
|
dryRun ? 'AE init dry run complete' : 'AE init complete',
|
|
16
16
|
`- ${createdLabel}: ${created.length} (${summarize(created)})`,
|
|
17
17
|
`- Skipped: ${skipped.length} (${summarize(skipped)})`,
|
|
18
|
-
`- Rules: ${rules}`,
|
|
19
18
|
`- Target: ${target}`,
|
|
20
19
|
].join('\n');
|
|
21
20
|
}
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
#
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
|
-
{{PROJECT_GOAL}}
|
|
3
|
+
> {{PROJECT_GOAL}}
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Project-Specific Rules
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<!-- 在这里写本项目的稳定规则,例如:
|
|
8
|
+
- 代码风格 / 注释语言
|
|
9
|
+
- 提交规范
|
|
10
|
+
- 协作偏好
|
|
11
|
+
这些规则会被 Claude Code / Codex 在每次 session 自动加载。
|
|
12
|
+
未来新规则直接追加到本段,不需要其他工具命令。 -->
|
|
8
13
|
|
|
9
|
-
##
|
|
14
|
+
## Stack
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
- After writing or changing anything under `spec/needs/<need-name>/`, update `spec/INDEX.md`.
|
|
13
|
-
- If `spec/INDEX.md` cannot be updated confidently, run the future `index-rebuild` command or leave an explicit handoff note.
|
|
14
|
-
- Do not write `prd.md` before requirement alignment.
|
|
15
|
-
- Do not fix a bug before expected behavior is aligned.
|
|
16
|
+
TODO: 简述项目技术栈与运行环境。
|
|
16
17
|
|
|
17
|
-
##
|
|
18
|
+
## Structure
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
TODO: 简述目录结构,标注关键入口。
|
|
21
|
+
|
|
22
|
+
## Operating Guidelines
|
|
23
|
+
|
|
24
|
+
TODO: 简述代码、测试、提交、PR 的操作规范。
|
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Always read [AGENTS.md](./AGENTS.md) at the start of every session before doing any work.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Keep this file short because every agent session loads it. Do trivial work directly. For non-trivial product, architecture, implementation, or debug work, first load the relevant local guidance or skill, then proceed.
|
|
8
|
-
|
|
9
|
-
## Spec Landing Rules
|
|
10
|
-
|
|
11
|
-
- SDD owns what to build; TDD owns how to prove it works.
|
|
12
|
-
- After writing or changing anything under `spec/needs/<need-name>/`, update `spec/INDEX.md`.
|
|
13
|
-
- If `spec/INDEX.md` cannot be updated confidently, run the future `index-rebuild` command or leave an explicit handoff note.
|
|
14
|
-
- Do not write `prd.md` before requirement alignment.
|
|
15
|
-
- Do not fix a bug before expected behavior is aligned.
|
|
16
|
-
|
|
17
|
-
## Project Specific Rules
|
|
18
|
-
|
|
19
|
-
<!-- AE:PROJECT:BEGIN -->
|
|
20
|
-
<!-- Add project-specific rules here. `ae init` manages AE:RULES separately. -->
|
|
21
|
-
<!-- AE:PROJECT:END -->
|
|
5
|
+
This project's instructions, rules, structure, and operating guidelines are all defined in `AGENTS.md`.
|
package/templates/ae-rules.md
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Project-Specific Rules
|
|
2
|
-
|
|
3
|
-
<!-- 这个文件在 `ae init` 时会被读取,内容会注入到 AGENTS.md 与 CLAUDE.md 的
|
|
4
|
-
AE:RULES block 中。可以在 `ae setup` 之后、`ae init` 之前编辑。 -->
|
|
5
|
-
|
|
6
|
-
## 通用约束
|
|
7
|
-
|
|
8
|
-
- 例如:所有代码注释使用中文
|
|
9
|
-
- 例如:提交信息遵循 Conventional Commits
|
|
10
|
-
|
|
11
|
-
## 项目栈
|
|
12
|
-
|
|
13
|
-
- 例如:Node.js 18+,TypeScript strict mode
|
|
14
|
-
|
|
15
|
-
## 协作偏好
|
|
16
|
-
|
|
17
|
-
- 例如:小步提交,每个 commit 必须包含测试
|