# 创建 Autos 技能包

<!-- autos.skill.namespace: autos.skills.skillCreator -->
<!-- autos.skill.description: 用于创建 Autos 技能包知识与能力的“元技能包” ; 当你要新建、修改、评审任何技能包，或者需要确定技能包结构与规范细节时，先加载本技能包 -->
<!-- autos.skill.version: 0.1.0 -->
<!-- autos.skill.minAutos: 3.5 -->

## 为什么 Autos 不需要重型技能包

先理解 Autos 的环境，再决定技能包要做多“厚”。

- Autos = aardio + 大量内置/标准/扩展库 + 一个会写 aardio 的 AI。
- **aardio 的库列表本身就是一张微型技能路由表**：看到“PPT”想到 COM、看到“压缩”想到 zlib/sevenZip、看到“HTTP”想到 web.rest、看到“网页界面”想到 web.view。
- 只要 AI 会把已有的通用知识（VBA、SQL、HTTP、JS、正则……）翻译成 aardio 库调用，很多任务**不需要任何技能包**就能完成。

所以技能包的价值**不是**“教 AI 它本来就会的东西”，而是：

> 把某类任务“第一次做时要踩的坑、要查的库、要试的语法、要确认的可行性”这些**前期研究成本**，一次性做完并固化下来，让下次对话时 AI 直接拿现成结论与代码上手，省掉重复的查库 / 看源码 / 反复调试等过程。

一句话：**技能包 = 预研成果的结晶 + 精准的知识路由，不是大百科。**

## 技能包就是一个 aardio 扩展库

约定（务必遵守）：

- 命名空间统一在 `autos.skills.*`，例如 `autos.skills.powerPoint`。
- 所有技能包都位于 aardio 公共库目录，物理目录为 `~\lib\autos\skills\<name>\` 。在 aardio 中库的命名空间与库的物理路径是一致的，例如  `autos.skills.skillCreator`位于 `~/lib/autos/skills/skillCreator/_.aardio`。
- 在 aardio 中可以直接用  `~` 字符开始的路径表示开发环境所在目录（即 aardio.exe 所在目录）。
- 主库 `_.aardio`：应至少暴露 `version` 与 `minAutos` 两个字符串字段（默认模板已生成）；需要时再加少量**稳定、已验证、可溯源**的脚手架函数。
- 提示词固定在：`~\lib\autos\skills\<name>\.res\skill.md`。
- `.res\skill.md` 第一行必须是一级标题，其后可带元数据注释：

      # 标题

      <!-- autos.skill.namespace: autos.skills.<name> -->
      <!-- autos.skill.description: <description>  -->
      <!-- autos.skill.version: 0.1.0 -->
      <!-- autos.skill.minAutos: 3.5 -->

      所有 Autos 技能包都必须包含以上 4 个元数据字段。可选用 `<!-- autos.skill.disabled: true -->` 禁用/启用技能包。允许增加自定义 `autos.skill.*` 扩展元数据，但除非 `autos.skills` 明确支持，否则不要让核心流程依赖自定义字段。

      - 元数据之前必须有且仅有一个格式为 `# 标题` 的标题行，这个标题必须简明扼要一句话说明技能包是做什么的，其主要作用是明确标注 skill.md 的用途（应当避免 skill.md 被插入系统提示词以后标题误导 AI 或存在歧义）
      - 每个有效元数据声明必须独立一行（行首不能有空白缩进），并且不允许包含换行
      - 所有元数据必须连续，中间不应用其他内容或者空行
      - 全部元数据之后必须至少有一个非空行
      - 前面有空白缩进的元数据将被忽略
      - 元数据 `autos.skill.description` 必须用两三句话简洁、清晰、明确地说明技能包的用途，Autos 将根据技能包元数据字段  `autos.skill.namespace` 与 `autos.skill.description` 的值自动建立 AI Agent 技能包路由表，因此技能包的命名必须强烈地暗示技能包的用途，技能包的描述必须说明技能包是什么用途，什么时候应该用它

- `skill.md` **不会自动注入系统提示词**。Autos 启动时只会根据元数据生成很短的技能包路由表；只有 AI 判断任务需要并主动调用 `load_skill("autos.skills.<name>")` 时，该技能包的 `skill.md` 才会被读取并注入当前上下文。
- AI 通过识别技能包元数据避免重复加载同一个技能包（跳过所有 markdown 格式代码段内的元数据示例）。

技能包可按需增加：

- 子库（如 `data.aardio`、`advisor.aardio`），放**确实需要代码固化**的逻辑。
- `.res\knowledge\*.md`：少量高质量规则/清单，配合 skill.md 里的“知识路由表”按需读取。
- `.res\data\`：必须本地化的关键数据（raw 原始件 / processed 清洗件）。

---

## skill.md 写作原则（最重要）

skill.md 是**按需加载的薄提示词**：它不会自动进入每次对话的系统提示词，但一旦加载就会占用当前上下文。因此不必因为几 KB 内容过度焦虑，也不应无节制堆资料。每写一段都先问：**“AI 自己知道吗？”**

- AI 已知的通用知识（语言语法、对象模型、算法常识）→ **不写**，最多一句话提示“复用你已有的 X 知识”。
- AI 不知道、或容易出错、需要实测才能确认的 → **才写**，并尽量给“拿来即用”的最小可运行片段。

一份好的 skill.md 通常包含：

1. **技能定位与边界**：能做什么、不做什么、不替代什么（如官方/权威来源）。
2. **核心原则**：处理这类任务的首选路线（例如“优先 COM；只读资源时可解 ZIP”）。
3. **知识路由表 / 入口**：什么问题→读哪个文件、用哪个库、调哪个函数。路由优先于内置全文搜索。
4. **现成代码入口**：经过实测的最小示例，标出关键坑（常量、owner 参数、编码等）。示例代码应优先使用局部别名简化命名空间，例如 `var ps = autos.skills.photoshop;`，避免反复书写很长的 `autos.skills.xxx`。
5. **可溯源的封装说明**：每个关键函数背后的核心技术、返回对象类型、可继续使用的既有知识。例如 `getApp(false)` 返回的是 `Photoshop.Application` COM 对象，底层来自 `com.TryGetObject / com.GetOrCreateObject`，后续可继续使用 Photoshop COM / ExtendScript / Action Manager 知识。
6. **数据可用性说明**：哪些数据已本地化、哪些需用户提供或实时获取。
7. **默认工作流**：3~6 步即可。

### 示例代码写法：短别名 + 可溯源

技能包示例应优先使用短局部别名，降低阅读与修改成本：

```aardio
import autos.skills.photoshop;

var ps = autos.skills.photoshop;
var psApp,err = ps.getApp(false);
var name,err = ps.getActiveDocumentName();
```

但简化不能把技术路线藏成黑盒。skill.md 必须明确说明：

- 这个短别名指向哪个完整命名空间。
- 关键函数底层调用了什么 aardio 库 / 系统接口 / 第三方对象。
- 返回值到底是什么类型，尤其是 COM、.NET、浏览器 WebView、数据库连接、文件句柄等可继续深入操作的对象。
- AI 后续可以复用哪些已有知识，而不是只能死记技能包函数名。

好封装应当是“给 AI 一把钥匙”，而不是“把门锁包进黑盒”。

不要做的事：

- 不要把大表/大数据塞进 skill.md（污染上下文、过期快）。
- 不要为每个技能重复造“搜索库”——Autos 已有 `search_text_in_dir` 工具与路由表。
- 不做复杂 RAG、不堆大量 UI、不长篇复述 API 文档。

---

## 预研流程：先把坑踩完，再写进技能包

写技能包前，**以真实可运行为准**完成预研，避免把没验证过的代码写进提示词：

1. 想清楚首选技术路线（哪个库/接口），列出 2~3 个候选与降级方案。
2. 用 `loadcodex` / `loadcodex_clean` **实测最小链路**（创建对象、跑通核心调用、确认返回值）。
   - 改了库文件后，用 `loadcodex_clean`（干净线程）重新加载，避免缓存。
3. 把验证通过的最小片段、关键常量、易错点，凝练进 skill.md 的“现成代码入口”。
4. 用 `util.testRunner` 给主库脚手架函数补几条断言，保证可导入、可调用。

经验教训（已踩，务必记住）：
- `call(fn, owner, ...)` 的第 2 个参数是 **owner**；给普通函数传参时通常写 `call(fn, null, arg1, arg2)`，否则形参错位。
- 在 `namespace` 内部访问全局库要加 `..` 前缀：`..io`、`..fsys`、`..table`、`..string`（`string/table/io` 等虽是内置库，但在命名空间里仍需 `..`）。`self.成员` 指当前命名空间成员（`self.` 前缀应当省略，必须用于区分关键词，例如 `self.namespace`）。
- `namespace` 是关键字；返回表里要用 `namespace` 当键名必须写成 `{["namespace"]="值"}` 或者 `{"namespace":"值"}`
- 创建技能包时 `name` 是命名空间末段短名称，必须是单个仅包含字母数字的合法标识符（例如 `powerPoint`），最多包含一个点号
- 技能包内所有不是 *.aardio 代码文件或命名空间的资源子目录首字符必须是 `.` ，例如 `.res`，autos.skills.list() 查找子技能包时将会自动跳过名称以 `.` 开头的目录
- 使用 `io.libpath("autos.skills.尚未创建的技能包")`会回退到应用目录。
  可通过`..io.joinpath( "~\lib\autos\skills\",name)` 或者 `..autos.skills.getDir(name)` 获取。
- 列目录用 `var files,folders = fsys.list(root)` 简单可靠。
- aardio 模式匹配用 `\a \w` 而非 `%a %w`；标识符校验：`^[\a_][\w_]*$`。
- 临时/演示技能包不要进入正式路由表；测试后删除，或写入 `<!-- autos.skill.disabled: true -->` 禁用，避免污染 AI 选技能。

---

## 本技能包提供的脚手架（已验证）

```aardio
import autos.skills;
import autos.skills.skillCreator;

// 1. 新建技能包脚手架：生成 _.aardio + .res\skill.md
var info,err = autos.skills.skillCreator("mySkill123",{
    title = "技能包 skill.md 的标题";
    description = "用于构建技能包路由表的技能包描述（三两句、简明扼要）";
    version = "0.1.0";
    minAutos = "3.6";
    overwrite = true;  // 允许覆盖已有技能包
});
// info 包含字段: { name; namespace; skillDir; resDir; mainPath; skillMarkdownPath }

// 2. 列出已安装的有效技能包（含 .res\skill.md 才算数）
var skills = autos.skills.list();

// 3. 路径与读取
autos.skills.getDir(); // 技能包根目录，完整路径
autos.skills.getDir("mySkill"); // 库目录，完整路径
autos.skills.getResDir("mySkill"); // 获取技能包的资源目录（`.res` 目录）路径
autos.skills.getMarkdownPath("mySkill"); // 获取技能包的 skill.md 路径
autos.skills.getMarkdown("mySkill"); // 获取技能包的 skill.md
autos.skills.buildMarkdown(name,title,description,version,minAutos); // 仅生成 skill.md 的默认文本
autos.skills.buildMainCode(name,title,description,version,minAutos); // 仅生成技能包扩展库的默认主代码

// 4. 诊断与验收：创建或修改技能包后必须跑一次
var report = autos.skills.validate("mySkill123");
if(!report.ok){
    return report; // 查看 errors / warnings 后修正
}

var all = autos.skills.doctor(); // 汇总诊断当前有效技能包

// 5. 临时/演示技能包不要污染路由表：测试后删除，或禁用
autos.skills.setDisabled("mySkill123",true);
```

`autos.skills.skillCreator` 默认产出一份**默认技能包模板**，你应在此基础上填充内容、代码与工作流。

---

## 创建技能包的默认工作流

1. 明确技能包的需求与约束
2. 规划与设计技能包的结构、内容、指导原则
3. 调用 `autos.skills.skillCreator(name, {...})` 生成骨架
4. 预研并实测首选技术路线，踩完坑
5. 把验证结论写进 `.res\skill.md`：薄、准、可执行、带路由
6. 只把“必须代码化”的逻辑放进主库或子库；用 `util.testRunner` 进行测试
7. 用 `autos.skills.validate(name)` 诊断单个技能包，确认元数据、namespace、主库编译等关键链路无错误
8. 用 `autos.skills.doctor()` / `autos.skills.list()` / `autos.skills.buildRoutePrompt()` 复核整体路由表，确认没有临时技能、重复前缀、空描述等污染
9. 用 `load_skill` 复核：站在“下一个 AI”的视角读一遍，能否不再重复预研就上手？不行就继续精简/补关键点

## 分而冶之

对于较复杂的技能包，可采取分而冶之的策略，与用户进行多轮互动，而不是在一次对话中完成所有任务。正确的步骤是：

1. 谋定而后动：先分析需求，做好策划与方案，然后先回复方案让用户确认。
2. 分而冶之：把任务分成多个阶段，每完成一个阶段以后，先回复用户并等待用户确认下一步的任务。
3. 权衡得失，及时纠偏：在向前推进的过程中，如果发现潜在问题，或者查觉到方向错误，或者探索更好的想法与思路，可先回复用户并等待确认。但也不要过多地原地犹豫，权衡得失合理选择并保持高效与正确的节奏是最重要的。
4. 精益求精：在每个技能包开发完成以后，可以事先征求用户确认是否一轮复查 review 。