@shirayner/ace 0.1.7 → 0.2.0-SNAPSHOT

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 CHANGED
@@ -31,17 +31,27 @@ ACE 是一个**AI 开发环境配置工具**,基于 Claude Code 官方最佳
31
31
  - 📝 **规范驱动工作流** — OpenSpec 集成的需求管理体系
32
32
  - 🧩 **跨会话记忆系统** — 持久化的开发者画像与项目记忆
33
33
 
34
+ ### 视频教程
35
+
36
+ 从下载安装到实际使用的完整演示:
37
+
38
+ ![ACE 使用示例](assets/ace-demo.gif)
39
+
40
+ 动图速度过快,另有示例视频:[ACE 使用示例](assets/ace-demo.mp4)
41
+
42
+ ---
43
+
44
+ ## ✨ 一分钟速览
45
+
46
+ ### 安装
47
+
34
48
  ```bash
35
49
  # 一键安装,即刻拥有专业级 AI 开发环境
36
50
  npm install -g @shirayner/ace
37
- ace init
38
51
  ```
39
52
 
40
- ---
41
53
 
42
- ## ✨ 一分钟速览
43
-
44
- ### 初始化向导
54
+ ### 初始化
45
55
 
46
56
  ```bash
47
57
  $ ace init
@@ -74,38 +84,64 @@ $ ace init
74
84
 
75
85
  ### Spec Coding 完整流程
76
86
 
87
+ 进入工作目录
88
+
77
89
  ```bash
78
90
  # 进入工作目录
79
91
  $ mkdir my-project
80
92
  $ cd my-project
81
93
 
94
+ ```
95
+
96
+ Spec 初始化
97
+
98
+ ```bash
82
99
  # 执行 aspec 初始化
83
100
  $ ace spec init
84
101
  ✓ aspec 工作流已初始化
85
102
  Done! 规范驱动开发已就绪。
86
103
 
87
- # 在 Claude Code 中体验三命令开发流程:
104
+ ```
105
+
106
+ Spec驱动开发
107
+
108
+ ```bash
109
+ # 在 Claude Code 中体验Spec开发流程:
110
+ # 输入 /opsx:proposal 命令后,一路交互式澄清、确认,然后需求完成,Spec归档
88
111
  $ claude
89
112
 
90
113
  > /opsx:proposal 帮我实现用户积分系统
91
114
 
92
- Claude:
93
- 【需求澄清】积分获取规则?消费规则?过期策略?→ 3 个问题确认
94
- 【创建提案】proposal.md
95
- 【技术澄清】并发扣减方案?积分流水存储?→ 2 个问题确认
96
- 【确定方案】design.md + tasks.md(8 个可执行任务)
115
+ Claude: 【需求澄清】对需求不确定的地方提出疑问?→ 请求人工澄清
116
+ 人工:选择选项,或者进行纠正,然后点击submit
117
+
118
+ Claude: 【需求对齐】Claude输出自己对本需求的理解 → 请求人工确认
119
+ 人工:选择确认,或者纠正信息,然后点击submit
120
+
121
+ -- 人工确认之后,Cluade会创建提案
122
+
123
+ Claude: 【技术设计澄清】对技术不确定的地方提出疑问 → 请求人工确认
124
+ 人工:选择确认,或者纠正信息,然后点击submit
125
+
126
+ Claude: 【技术设计对齐】Claude输出自己对本需求的技术方案设计的理解 → 请求人工确认
127
+ 人工:选择确认,或者纠正信息,然后点击submit
128
+
129
+ -- 人工确认之后,Cluade会创建Design
130
+
131
+ Claude: 【Design审批并创建Tasks】Claude请求人工确认Design设计,然后创建Tasks → 请求人工审批
132
+ 人工:选择确认,或者纠正信息,然后点击submit
133
+
134
+ -- 人工确认之后,Cluade会创建tasks
97
135
 
98
- > /opsx:apply
136
+ Claude: 【执行】Claude 请求按规划的任务进行代码实现 → 请求人工审批
137
+ 人工:选择确认,或者纠正信息,然后点击submit
99
138
 
100
- Claude:
101
- 按 tasks.md 逐项实现,每步验证
102
- ✓ 所有任务完成,测试通过
139
+ -- 人工确认之后,Cluade会进行代码实现,然后进行经验收集
103
140
 
104
- > /opsx:archive
141
+ Claude: 【归档同步】Claude 会请求进行归档同步→ 请求人工审批
142
+ 人工:选择确认,或者纠正信息,然后点击submit
105
143
 
106
- Claude:
107
- spec 归档,收敛检查
108
- ✓ 归档完成
144
+ -- 人工确认之后,Cluade会对Spec进行归档同步
109
145
  ```
110
146
 
111
147
  ### 健康检查
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shirayner/ace",
3
- "version": "0.1.7",
3
+ "version": "0.2.0-SNAPSHOT",
4
4
  "description": "AI Coding Environment - One command to set up your Claude Code harness",
5
5
  "bin": {
6
6
  "ace": "./bin/ace.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ace",
3
- "version": "0.1.3",
3
+ "version": "0.2.0-SNAPSHOT",
4
4
  "description": "AI Coding Environment - Skills and commands for Claude Code",
5
5
  "author": {
6
6
  "name": "shirayner"
@@ -27,10 +27,10 @@ export async function doctorCommand() {
27
27
  const ruleFiles = await fs.readdir(templateRulesDir);
28
28
  for (const file of ruleFiles) {
29
29
  if (!file.endsWith('.md')) continue;
30
- checks.push(await check(`rules/ace/${file}`, fs.pathExists(path.join(CLAUDE_DIR, rulesDir, file))));
30
+ checks.push(await check(`ace/rules/${file}`, fs.pathExists(path.join(CLAUDE_DIR, rulesDir, file))));
31
31
  }
32
32
  } catch {
33
- checks.push({ name: 'rules/ace/ directory', ok: false });
33
+ checks.push({ name: 'ace/rules/ directory', ok: false });
34
34
  }
35
35
  }
36
36
 
@@ -91,14 +91,21 @@ export async function doctorCommand() {
91
91
  checks.push({ name: 'settings.json parseable', ok: false });
92
92
  }
93
93
 
94
- // 8. Validate CLAUDE.md @references
94
+ // 8. Validate CLAUDE.md references (both @refs and path-index style)
95
95
  try {
96
96
  const claudeMd = await fs.readFile(path.join(CLAUDE_DIR, 'CLAUDE.md'), 'utf-8');
97
- const refs = claudeMd.match(/@~?\/?\.?claude\/[^\s)]+/g) || [];
98
- for (const ref of refs) {
97
+ // Check @references (legacy format)
98
+ const atRefs = claudeMd.match(/@~?\/?\.?claude\/[^\s)]+/g) || [];
99
+ for (const ref of atRefs) {
99
100
  const refPath = ref.replace(/^@/, '').replace(/^~/, process.env.HOME || process.env.USERPROFILE);
100
101
  checks.push(await check(`@ref: ${path.basename(refPath)}`, fs.pathExists(refPath)));
101
102
  }
103
+ // Check path-index style references (new format: lines starting with "- ~/.claude/...")
104
+ const pathRefs = claudeMd.match(/(?:^|\n)-\s+(~\/.claude\/[^\s—]+)/g) || [];
105
+ for (const match of pathRefs) {
106
+ const refPath = match.replace(/(?:^|\n)-\s+/, '').replace(/^~/, process.env.HOME || process.env.USERPROFILE);
107
+ checks.push(await check(`path: ${path.basename(refPath)}`, fs.pathExists(refPath)));
108
+ }
102
109
  } catch {
103
110
  checks.push({ name: 'CLAUDE.md readable', ok: false });
104
111
  }
@@ -91,6 +91,11 @@ export async function initCommand(options) {
91
91
 
92
92
  s.start('Installing...');
93
93
 
94
+ // Prepare: migrate legacy directory structure if needed
95
+ if (!options.dryRun) {
96
+ await installer.prepare();
97
+ }
98
+
94
99
  for (const componentName of components) {
95
100
  const component = COMPONENTS[componentName];
96
101
  if (!component) continue;
@@ -149,7 +154,7 @@ export async function initCommand(options) {
149
154
  '',
150
155
  'Customize',
151
156
  ' Change role edit ~/.claude/memory/user_profile.md',
152
- ' Adjust rules edit ~/.claude/rules/ace/',
157
+ ' Adjust rules edit ~/.claude/ace/rules/',
153
158
  ' Safety guards edit ~/.claude/hookify.ace.*.local.md',
154
159
  ' Verify setup ace doctor',
155
160
  ].join('\n'),
@@ -224,8 +229,8 @@ async function buildInstallPreview(installer, components) {
224
229
  try {
225
230
  const existing = await fs.readFile(path.join(installer.targetDir, item.dest), 'utf-8');
226
231
  const template = await fs.readFile(path.join(installer.templatesDir, item.src), 'utf-8');
227
- const { added } = mergeClaudeMd(existing, template);
228
- item.detail = added.length > 0 ? `adds ${added.length} new @references` : 'up to date';
232
+ const { content } = mergeClaudeMd(existing, template);
233
+ item.detail = content !== existing ? 'will update managed section' : 'up to date';
229
234
  } catch {
230
235
  item.detail = 'will merge';
231
236
  }
@@ -30,16 +30,35 @@ export async function uninstallCommand(options) {
30
30
  const skipped = [];
31
31
  const errors = [];
32
32
 
33
- // 1. Remove rules/ace/ directory
33
+ // 1. Remove ace/rules/ directory (and legacy rules/ace/ if still present)
34
34
  const spinner1 = ora('Removing rules...').start();
35
35
  try {
36
- const rulesDir = path.join(CLAUDE_DIR, 'rules', 'ace');
37
- if (await fs.pathExists(rulesDir)) {
38
- await fs.remove(rulesDir);
39
- removed.push('rules/ace/');
40
- } else {
41
- skipped.push('rules/ace/ (not found)');
36
+ const newRulesDir = path.join(CLAUDE_DIR, 'ace', 'rules');
37
+ const newTeamDir = path.join(CLAUDE_DIR, 'ace', 'team');
38
+ const aceDir = path.join(CLAUDE_DIR, 'ace');
39
+ const legacyRulesDir = path.join(CLAUDE_DIR, 'rules', 'ace');
40
+
41
+ if (await fs.pathExists(newRulesDir)) {
42
+ await fs.remove(newRulesDir);
43
+ removed.push('ace/rules/');
44
+ }
45
+ if (await fs.pathExists(newTeamDir)) {
46
+ await fs.remove(newTeamDir);
47
+ removed.push('ace/team/');
48
+ }
49
+ // Remove ace/ parent if empty
50
+ if (await fs.pathExists(aceDir)) {
51
+ const remaining = await fs.readdir(aceDir);
52
+ if (remaining.length === 0) {
53
+ await fs.remove(aceDir);
54
+ }
42
55
  }
56
+ // Also clean legacy directory if still exists
57
+ if (await fs.pathExists(legacyRulesDir)) {
58
+ await fs.remove(legacyRulesDir);
59
+ removed.push('rules/ace/ (legacy)');
60
+ }
61
+
43
62
  spinner1.succeed('rules removed');
44
63
  } catch (err) {
45
64
  spinner1.fail('rules removal failed');
@@ -126,17 +145,31 @@ export async function uninstallCommand(options) {
126
145
  await fs.remove(claudeBackup);
127
146
  removed.push('CLAUDE.md (restored pre-ace backup)');
128
147
  } else if (await fs.pathExists(claudeMdPath)) {
129
- // Fallback: surgically remove ace @references
148
+ // Fallback: surgically remove ace @references and managed section
130
149
  const content = await fs.readFile(claudeMdPath, 'utf-8');
131
- const lines = content.split('\n');
132
- const filtered = lines.filter(line => !line.includes('@~/.claude/rules/ace/'));
133
- const cleaned = filtered.join('\n')
150
+ let cleaned = content;
151
+ // Remove the entire managed section if present
152
+ const managedStart = '<!-- ace:managed:start -->';
153
+ const managedEnd = '<!-- ace:managed:end -->';
154
+ const startIdx = cleaned.indexOf(managedStart);
155
+ const endIdx = cleaned.indexOf(managedEnd);
156
+ if (startIdx !== -1 && endIdx !== -1) {
157
+ cleaned = cleaned.slice(0, startIdx) + cleaned.slice(endIdx + managedEnd.length);
158
+ }
159
+ // Remove any remaining ace @references (legacy or new format)
160
+ const lines = cleaned.split('\n');
161
+ const filtered = lines.filter(line =>
162
+ !line.includes('@~/.claude/rules/ace/') &&
163
+ !line.includes('@~/.claude/ace/') &&
164
+ !line.includes('hookify.ace.')
165
+ );
166
+ cleaned = filtered.join('\n')
134
167
  .replace(/\n## Added by ace\n*/g, '\n')
135
168
  .replace(/\n{3,}/g, '\n\n')
136
169
  .trim() + '\n';
137
170
  if (cleaned !== content) {
138
171
  await fs.writeFile(claudeMdPath, cleaned, 'utf-8');
139
- removed.push('CLAUDE.md ace @references (surgical)');
172
+ removed.push('CLAUDE.md ace content (surgical)');
140
173
  }
141
174
  }
142
175
 
@@ -58,10 +58,12 @@ export const SPEC_TEMPLATE_FILES = [
58
58
 
59
59
  /**
60
60
  * Patterns for files owned by ACE - these are overwritten directly on init without prompting.
61
- * Used to identify ACE-owned content in rules/, hooks/, and hookify/.
61
+ * Used to identify ACE-owned content in ace/, hooks/, and hookify/.
62
62
  */
63
63
  export const ACE_OWNED_PATTERNS = [
64
- /^rules\/ace\//, // rules/ace/*.md
64
+ /^ace\/rules\//, // ace/rules/*.md (v2.0+)
65
+ /^ace\/team\//, // ace/team/*.md (v2.0+)
66
+ /^rules\/ace\//, // rules/ace/*.md (legacy, for migration detection)
65
67
  /^hooks\/ace\./, // hooks/ace.*.sh
66
68
  /^hookify\.ace\./, // hookify.ace.*.local.md
67
69
  ];
@@ -77,12 +79,12 @@ export function isAceOwnedFile(relativePath) {
77
79
 
78
80
  /**
79
81
  * Check if an @reference path is owned by ACE.
80
- * @param {string} refPath - Reference path like '~/.claude/rules/ace/thinking.md' or '~/.claude/hooks/ace.java-compile-check.sh'
82
+ * @param {string} refPath - Reference path like '@~/.claude/rules/ace/thinking.md' or '~/.claude/hooks/ace.java-compile-check.sh'
81
83
  * @returns {boolean}
82
84
  */
83
85
  export function isAceOwnedRef(refPath) {
84
- // Remove the ~/.claude/ prefix if present
85
- const relativePath = refPath.replace(/^~\/\.claude\//, '');
86
+ // Remove the @~/.claude/ or ~/.claude/ prefix if present
87
+ const relativePath = refPath.replace(/^@?~\/\.claude\//, '');
86
88
  return isAceOwnedFile(relativePath);
87
89
  }
88
90
 
@@ -98,7 +100,7 @@ export const COMPONENTS = {
98
100
  rules: {
99
101
  description: 'Cognitive & code quality rules',
100
102
  required: true,
101
- rulesDir: 'rules/ace',
103
+ rulesDir: 'ace/rules',
102
104
  },
103
105
  plugin: {
104
106
  description: 'Ace plugin (skills: auto-goal, coding, skill-creator, skill-optimize; commands: report)',
@@ -94,6 +94,7 @@ export class Installer {
94
94
  async run() {
95
95
  if (!this.dryRun) {
96
96
  await fs.ensureDir(this.targetDir);
97
+ await this.prepare();
97
98
  }
98
99
 
99
100
  for (const componentName of this.components) {
@@ -114,6 +115,57 @@ export class Installer {
114
115
  return this.results;
115
116
  }
116
117
 
118
+ /**
119
+ * Prepare the target directory: migrate legacy structure and ensure new layout.
120
+ * Call this before installComponent() if not using run().
121
+ */
122
+ async prepare() {
123
+ await fs.ensureDir(this.targetDir);
124
+ await this.migrateFromLegacy();
125
+ await this.ensureAceStructure();
126
+ }
127
+
128
+ /**
129
+ * Migrate from legacy directory structure (rules/ace/) to new (ace/rules/).
130
+ * Only runs if old directory exists.
131
+ */
132
+ async migrateFromLegacy() {
133
+ const legacyDir = path.join(this.targetDir, 'rules', 'ace');
134
+ const newDir = path.join(this.targetDir, 'ace', 'rules');
135
+
136
+ if (!await fs.pathExists(legacyDir)) return;
137
+
138
+ // Move contents from legacy to new location
139
+ await fs.ensureDir(newDir);
140
+ const files = await fs.readdir(legacyDir);
141
+ for (const file of files) {
142
+ const src = path.join(legacyDir, file);
143
+ const dest = path.join(newDir, file);
144
+ await fs.move(src, dest, { overwrite: true });
145
+ }
146
+
147
+ // Remove empty legacy directory
148
+ await fs.remove(legacyDir);
149
+ // Clean up parent if empty
150
+ const rulesParent = path.join(this.targetDir, 'rules');
151
+ if (await fs.pathExists(rulesParent)) {
152
+ const remaining = await fs.readdir(rulesParent);
153
+ if (remaining.length === 0) {
154
+ await fs.remove(rulesParent);
155
+ }
156
+ }
157
+
158
+ this.results.merged.push({ file: 'ace/rules/ (migrated from rules/ace/)' });
159
+ }
160
+
161
+ /**
162
+ * Ensure the ace/ namespace directory structure exists.
163
+ */
164
+ async ensureAceStructure() {
165
+ await fs.ensureDir(path.join(this.targetDir, 'ace', 'rules'));
166
+ await fs.ensureDir(path.join(this.targetDir, 'ace', 'team'));
167
+ }
168
+
117
169
  async installComponent(name, component) {
118
170
  if (component.isPlugin) {
119
171
  await this.installPlugin();
@@ -330,15 +382,16 @@ export class Installer {
330
382
  async mergeClaudeMdFile(srcPath, destPath, fileSpec) {
331
383
  const existing = await fs.readFile(destPath, 'utf-8');
332
384
  const template = await fs.readFile(srcPath, 'utf-8');
333
- const { content, added } = mergeClaudeMd(existing, template);
385
+ const { content, added, removed } = mergeClaudeMd(existing, template);
334
386
 
335
- if (added.length === 0) {
387
+ // Skip if content is unchanged (no refs added/removed, managed section identical)
388
+ if (content === existing) {
336
389
  this.results.skipped.push(fileSpec.dest);
337
390
  return;
338
391
  }
339
392
 
340
393
  if (this.dryRun) {
341
- !this.quiet && console.log(chalk.cyan(` [dry-run] Would merge CLAUDE.md, adding ${added.length} references`));
394
+ !this.quiet && console.log(chalk.cyan(` [dry-run] Would merge CLAUDE.md`));
342
395
  this.results.merged.push({ file: fileSpec.dest, added });
343
396
  return;
344
397
  }
@@ -346,7 +399,7 @@ export class Installer {
346
399
  await backupPreInstall(destPath);
347
400
  await backupFile(destPath);
348
401
  await fs.writeFile(destPath, content, 'utf-8');
349
- this.results.merged.push({ file: fileSpec.dest, added });
402
+ this.results.merged.push({ file: fileSpec.dest, added, removed });
350
403
  }
351
404
 
352
405
  async mergeSettingsJsonFile(srcPath, destPath, fileSpec) {
@@ -60,28 +60,40 @@ function mergeWithMarkers(existingContent, templateContent) {
60
60
  let result = replaceManagedSection(existingContent, templateManaged);
61
61
 
62
62
  // Clean up any obsolete ACE refs outside the managed section
63
+ // This includes old @~/.claude/rules/ace/ refs AND hookify @refs
63
64
  const removed = [];
64
65
  const lines = result.split('\n');
65
66
  const cleanedLines = lines.map(line => {
66
67
  const refs = extractRefs(line);
67
68
  const hasObsoleteAceRef = refs.some(ref => {
68
- // Check if this is an ACE-owned ref that's NOT in the new template
69
69
  if (isAceOwnedRef(ref)) {
70
70
  const refWithAt = `@${ref}`;
71
71
  if (!templateRefs.includes(refWithAt)) {
72
72
  removed.push(ref);
73
- return true; // This line has an obsolete ref
73
+ return true;
74
74
  }
75
75
  }
76
76
  return false;
77
77
  });
78
78
 
79
- // Return null to mark for removal, otherwise keep line
79
+ // Also remove lines with hookify @ references (these should not be in CLAUDE.md)
80
+ const hasHookifyRef = refs.some(ref => /hookify\.ace\./.test(ref));
81
+ if (hasHookifyRef) {
82
+ const refBare = refs.find(ref => /hookify\.ace\./.test(ref));
83
+ if (refBare) removed.push(refBare);
84
+ return null;
85
+ }
86
+
80
87
  return hasObsoleteAceRef ? null : line;
81
88
  }).filter(line => line !== null);
82
89
 
83
90
  result = cleanedLines.join('\n');
84
91
 
92
+ // Clean up empty "## Added by ace" section if all its refs were removed
93
+ result = result.replace(/\n## Added by ace\n*(?=\n|$)/g, '\n');
94
+ // Normalize multiple blank lines
95
+ result = result.replace(/\n{3,}/g, '\n\n');
96
+
85
97
  // Get the new refs that were added (in the managed section)
86
98
  const existingRefs = extractRefs(existingContent);
87
99
  const added = templateRefs
@@ -1,21 +1,31 @@
1
- # 全局配置索引
1
+ # 交互语言
2
+ 始终使用中文与用户交互。所有回复、解释、总结使用中文;代码和技术标识符保持英文。
2
3
 
3
4
  <!-- ace:managed:start -->
4
- ## 核心原则
5
- - @~/.claude/rules/ace/thinking.md - 深度思考原则(序验深广辨简)
5
+ # ACE 配置
6
6
 
7
- ## 代码规范
8
- - @~/.claude/rules/ace/clean-code.md - Clean Code 核心原则(始终加载)
9
- - @~/.claude/rules/ace/code-quality.md - 代码质量标准(编辑代码文件时加载)
7
+ ## 核心原则(始终适用)
10
8
 
11
- ## 工作流规则
12
- - @~/.claude/rules/ace/reporting.md - 报告输出规则
13
- - @~/.claude/rules/ace/task-recovery.md - 任务恢复规则
14
- - @~/.claude/rules/ace/context-hygiene.md - 上下文卫生与 Compaction 保护
9
+ **深度思考** — 理解先于规划,规划先于行动。用事实闭环,不以假设收尾。多问一层为什么,追问前提、追问替代、追问问题本身。在系统中定位局部。主动找反证,复杂度是负债。
15
10
 
16
- ## 质量控制
17
- - @~/.claude/rules/ace/memory-policy.md - Memory 质量策略
11
+ **Clean Code** — 意图清晰(命名即意图)、单一职责(一个理由改变)、最小 Surprise(做读者期望的事)、DRY(知识只表达一次)、简洁胜于复杂(KISS/YAGNI)、渐进改进(离开时更干净)。
18
12
 
19
- ## 交互规则
20
- - @~/.claude/rules/ace/interactive-clarify.md - 交互式澄清规则
13
+ **优先级** — 正确性 > 可读性 > 清晰 > 简单 > 显式。
14
+
15
+ ## 编码规则(编辑代码前,先阅读对应规则文件)
16
+ - ~/.claude/ace/rules/code-quality.md — 代码质量标准(函数/命名/结构/SOLID 检查清单)
17
+ - ~/.claude/ace/rules/clean-code.md — Clean Code 详细原则与反模式速查
18
+
19
+ ## 工作流规则(对应场景时参考)
20
+ - ~/.claude/ace/rules/context-hygiene.md — 上下文卫生与压缩保护(长任务时阅读)
21
+ - ~/.claude/ace/rules/task-recovery.md — 任务恢复流程(用户说"继续"时阅读)
22
+ - ~/.claude/ace/rules/reporting.md — 报告输出规则(生成报告前阅读)
23
+ - ~/.claude/ace/rules/memory-policy.md — 记忆质量策略(保存记忆前阅读)
24
+ - ~/.claude/ace/rules/interactive-clarify.md — 交互式澄清规则(需要提问时阅读)
25
+
26
+ ## 安全策略
27
+ 安全由外部机制保障,不占用上下文 token:
28
+ - settings.json deny 规则 → 硬拦截 rm -rf、sudo 等
29
+ - Shell hooks → 进程级检查(编译、类型检查)
30
+ - Hookify 插件 → 模式匹配拦截(危险命令、敏感文件)
21
31
  <!-- ace:managed:end -->
@@ -12,6 +12,11 @@
12
12
 
13
13
  **深** — 多问一层为什么。表面问题是深层问题的症状。抵达机制层和根因层才算理解。
14
14
 
15
+ 苏格拉底式追问:
16
+ - 追问前提:"这个假设成立吗?依据是什么?"
17
+ - 追问替代:"还有没有其他解释?"
18
+ - 追问问题本身:"这是正确的问题吗?问题框架对吗?"
19
+
15
20
  **广** — 在系统中定位局部。一切都有上下文、依赖和边界。改动一处,影响传向何方?
16
21
 
17
22
  **辨** — 主动为自己的结论找反证。方案都有代价,判断都有前提。区分事实、推断与猜测,对不确定性诚实。
File without changes