want-a-init 0.0.0-stage → 0.2.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 +21 -0
- package/README.md +158 -2
- package/cordis.patch.yml +8 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.js +272 -0
- package/package.json +42 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 want-a-init contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,159 @@
|
|
|
1
|
-
#
|
|
1
|
+
# want-a-init
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
DeepSeek Harness 的**模型驱动** `/init` 命令:让 agent 自己读你的仓库,写出一份**仓库特有、可验证、不膨胀**的 `AGENTS.md`——并把"持续维护它"变成 agent 运行时的常驻意识,而不是一次性仪式。
|
|
4
|
+
|
|
5
|
+
[](package.json)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](cordis.patch.yml)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 为什么不是"又一个 AGENTS.md 模板生成器"
|
|
12
|
+
|
|
13
|
+
模板生成器只会产出通用建议("写测试"、"保持代码整洁"),这类文字对未来会话零价值——任何一个称职的模型本来就会做。`AGENTS.md` 真正值钱的只有三类东西:
|
|
14
|
+
|
|
15
|
+
1. **本仓库独有的命令**——构建顺序、单测怎么单跑、必须先生成再类型检查的那种顺序依赖;
|
|
16
|
+
2. **不读代码就会踩的坑**——生成物不能手改、需要哪个常驻服务、快照什么时候会漂;
|
|
17
|
+
3. **边界与所有权**——哪个目录是核心、哪个包不能反向依赖。
|
|
18
|
+
|
|
19
|
+
`/init` 的全部设计都围绕"只留这三类、其余全删"。它有骨架但**不写死模板**:骨架只是提出该回答的问题,答案必须来自仓库证据。
|
|
20
|
+
|
|
21
|
+
## 快速开始
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
# 从 GitHub 安装
|
|
25
|
+
dsh plugin --profile web add github:mexiaosqwq/want-a-init
|
|
26
|
+
|
|
27
|
+
# 或从本地 checkout 安装
|
|
28
|
+
dsh plugin --profile web add /path/to/want-a-init
|
|
29
|
+
|
|
30
|
+
dsh web # 重启后生效
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
然后在任意会话里:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
/init
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
agent 会在**同一回合**内只读调查仓库、写入 `AGENTS.md`、回报改了什么、删了什么陈旧内容。写盘范围绝对锁死在这一个文件:源码、配置、`docs/`、`CLAUDE.md`、子包/子目录的 `AGENTS.md` 一律不改,自由文本也无法给这些文件开后门(子包的 `AGENTS.md` 只作为**上下文**读取,不写)。
|
|
40
|
+
|
|
41
|
+
> **本地 `link:` 安装前置条件**:仓库内必须先 `pnpm install`。否则插件解析不到 `@deepseek-ai/dsh-llm`,`/init` 根本不会出现在客户端(见[排错](#排错))。
|
|
42
|
+
|
|
43
|
+
## 用法
|
|
44
|
+
|
|
45
|
+
| 输入 | 模式 | 行为 |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `/init` | 默认合并 | 已有文件→原地改进:保留仍然正确的规则,删掉陈旧/通用内容,与当前代码对齐 |
|
|
48
|
+
| `/init force` | 覆盖 | 先列出**将被丢弃**的既有条目;git 已跟踪→同回合重建;未跟踪(含尚未 `git init` 的目录,视同最严格情形)→**等用户确认**;文件不存在→跳过护栏直接创建 |
|
|
49
|
+
| `/init minimal` | 裁剪 | 只留生存必需的事实:Project 身份、精确 Commands(含顺序)、关键 Pitfalls;Layout 压到 2–5 行;可省略 Architecture / Docs / Maintenance |
|
|
50
|
+
| `/init detailed` | 展开 | 允许展开模块边界、数据/控制流、测试前置与夹具、环境搭建、可复用的 playbook——但每条仍须挣得自己的上下文成本 |
|
|
51
|
+
| `/init 只补 Commands,别动其他` | 自由文本 | 选项之外的任何输入都会**原样**交给模型解释,不对整句做模式猜测——包括"要不要更精简""要不要覆盖"这类意图,都由模型读语义后决定,不用正则去嗅 |
|
|
52
|
+
|
|
53
|
+
几处刻意设计:
|
|
54
|
+
|
|
55
|
+
- **选项解析是严格的**:只有输入(忽略空白与逗号后)**全部由** `force` / `minimal` / `detailed` 组成才当选项处理。所以「别 force」这类否定句不会误触发——它整句作为自由文本交给模型。
|
|
56
|
+
- **`minimal` 与 `detailed` 同时给出**:`minimal` 胜出,回复里会明确提示冲突。
|
|
57
|
+
- **模式差异在"授权",不在提示词长度**:默认 / `minimal` / `detailed` 三档提示词几乎等长(61 / 62 / 61 行),换掉的是整块 `Detail level:` 授权。默认档面向"该写的都写、别灌水",`minimal` 才是明确授权砍段。
|
|
58
|
+
- **自由文本不走正则嗅探**:`buildPrompt` 全文只在**一处**用正则——把用户输入里的 `</user_guidance>` 中和掉,防止分隔符被从内部逃逸。像 `/init minimal,重点写测试` 这种混合输入不会被提升成 `minimal` 模式(`Detail level:` 块会如实显示 `default`),但解释句里已经写明"要精简就压 Layout、砍 Architecture/Docs/Maintenance",模型照办即可。少一条正则、少一条分支、少一层误判。
|
|
59
|
+
- **/init 不自己写文件**:命令只负责把调研指令注入为上下文(`source.kind != 'user'`,客户端渲染成折叠的 context 行而非人类气泡),写盘由 agent 完成。中途取消则什么都不投递。
|
|
60
|
+
|
|
61
|
+
## 产出形态
|
|
62
|
+
|
|
63
|
+
八个可选段落,**没有可验证内容的段落直接省略**,不留占位符:
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
# <项目名>
|
|
67
|
+
|
|
68
|
+
## Project 一行身份 + 技术栈 + 真实入口 + 包边界
|
|
69
|
+
## Layout 高密度注释地图:同模式文件合并成一条带计数(tests/ ← 31 *.test.ts)
|
|
70
|
+
## Docs 文档路由表:<何时读> -> <路径>(<回答什么问题>)
|
|
71
|
+
## Commands 精确命令:安装/构建/测试(全量+单跑)/lint/类型检查/生成/部署(标注先后)
|
|
72
|
+
## Architecture 核心模块、数据流、所有权边界、非显然的不变量
|
|
73
|
+
## Conventions 仓库特有规则与"禁止"项(原因不显然时才附一句)
|
|
74
|
+
## Pitfalls 反复出现的失败模式(或触发词索引,指向专门的 pitfalls 文档)
|
|
75
|
+
## Maintenance 未来 agent 如何原地更新、如何反向清理
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
骨架是**地板不是天花板**:仓库若有值得单独阅读时刻的知识(发布 playbook、被否决过的方案列表),可以加段。但"给会话偏好或一次性指令开段"被明确禁止。
|
|
79
|
+
|
|
80
|
+
## 反膨胀机制
|
|
81
|
+
|
|
82
|
+
这是本插件与"让模型随便写写"的关键差别,也是常驻维护段与命令提示词**共用同一份规则字符串**(`DISTILL_RULE`)的原因。规则形状是**白名单兜底 + 排除项 + 开放式归因禁令 + 两条硬护栏**,不是一张谁都能绕过去的固定清单:
|
|
83
|
+
|
|
84
|
+
1. **只收这五类持久事实**:精确命令、架构、关键不变量/阈值、反复踩的坑(写成"什么坏了 -> 为什么 -> 怎么修")、可复用的流程/QA/协作规则。写不出这五类之一的,本就不该进文件。
|
|
85
|
+
2. **排除项靠白名单兜底**:一次性任务指令、临时绕行、事故流水账、工作日志一律不收。真正封死"换马甲"的是第 1 条的 `keep only`——不在五类持久事实里的本就不该进文件,不必把排除项做成无穷清单。
|
|
86
|
+
3. **归因标签全家桶封禁**:`(用户要求)` / `(user requested)` / `(用户决定)` 只是三个最常见马甲,规则里明写 **"or any equivalent label"**,并额外封死**日期戳**与**用户原话引用**。只封两个标签等于给模型留了换马甲的出口。规则本身写成紧凑祈使句即可。
|
|
87
|
+
4. **无实证即丢弃**:指不出具体文件、配置、命令或已观察到的故障来支撑一条结论,就整条删掉;同时**禁止脑补技术理由**——封死标签还不够,得给模型一个合法的放弃出口,否则它会为了保住一条偏好而现编因果。
|
|
88
|
+
5. **外部红线例外**:既有条目若涉及部署安全、外部接口兼容、线上环境边界,即使本地代码与单测无法佐证,也**保留并向用户确认**,绝不因"本地没验证到"就静默删除。把「对话偏好」和「生产不变量」精确切开,误删比留着几条垃圾危险得多。
|
|
89
|
+
6. **近期记忆降级为候选池**:会话记录和 `git diff` 只当作**低信任候选**——必须对着磁盘现状复核,剥掉任务细节,只放行跨任务的持久事实。上下文被压缩过也不会让幻觉得到官方认证。
|
|
90
|
+
|
|
91
|
+
> 第 1–5 条管**入库标准**,第 6 条管**输入来源**:来源可疑不等于内容错误,来源可信也不等于内容该进文件——两者是独立的闸门,都要过。
|
|
92
|
+
|
|
93
|
+
配套的两条写作纪律:**一个事实只有一个家**(详情写最具体的段,其它处只指路),**段内按使用频率排序且保持稳定**(让它是有序清单,不是堆积物)。
|
|
94
|
+
|
|
95
|
+
此外,这条规则字符串是**每轮都要付费**的:它被内联进常驻维护段,所以措辞本身也是成本。改它时先问"删掉这句,模型会不会写坏东西"——不会就删。
|
|
96
|
+
|
|
97
|
+
## 常驻维护段
|
|
98
|
+
|
|
99
|
+
除了一次性命令,插件还会往系统提示注入 `agents-md-maintenance` 段落(当前约 1.6k 字符,`order: 650`),让每个会话都带着维护意识跑:
|
|
100
|
+
|
|
101
|
+
- **前置阀门**:工作区**没有** `AGENTS.md` 时不得新建(除非用户明确要求或跑 `/init`);
|
|
102
|
+
- **默认动作是"不编辑"**:多数任务对 `AGENTS.md` 的改动数应为 0;
|
|
103
|
+
- **只在任务收尾评估**,且只在出现"可复用且经代码验证"的教训时才原地更新,并在回复里说明改了哪段;
|
|
104
|
+
- **裁剪是条件动作,不是常驻命令**:只有本次任务本就决定更新 `AGENTS.md`、或某条旧规则直接误导了当前任务 / 指向刚删改的文件时才顺带清理;平时不主动发起整份清理(那是 `/init` 的活)。顺手剥掉残留的归因标签、日期戳、原话尾巴(**剥标签、留规则**);
|
|
105
|
+
- **文档路由**:写进仓库的长期文档按 Layout 安放,给它自描述头部,并在**收尾时**补上 `Docs` 路由行(不在任务中途写,避免文档被改名/删除后留下死链)。
|
|
106
|
+
|
|
107
|
+
规则文字(`DISTILL_RULE`)由这段与 `/init` 提示词共用,因此它同时是**每轮都在付的上下文成本**——扩写它之前先确认新句子真的拦得住某类坏输出。
|
|
108
|
+
|
|
109
|
+
段落位置 `order: 650` 落在宿主 `SECTION_ORDERS` 的 `TEAM_POLICY`(600) 与 `PTC_ONLY`(800) 之间——在全部策略块之后、首个工具段之前,是"指导"而非最高优先级指令。
|
|
110
|
+
|
|
111
|
+
## 开发
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
pnpm install
|
|
115
|
+
bash scripts/build.sh # 存在性检查 + 跑全部自测
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
完整验证链(改了 `lib/index.js` 就必须全跑):
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
node --check lib/index.js
|
|
122
|
+
node scripts/parse-options.test.mjs # 选项解析严格性、否定句不误触发、空输入=默认档
|
|
123
|
+
node scripts/build-prompt.test.mjs # 五模式提示词 + 分隔符逃逸防护 + 分支暂停理由
|
|
124
|
+
node scripts/apply-smoke.test.mjs # 真依赖链:注册→投递→段落→enabled 门控→cwd 回退/取消
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```text
|
|
128
|
+
want-a-init/
|
|
129
|
+
├─ lib/index.js ← 全部运行时逻辑(唯一该手改的源码)
|
|
130
|
+
├─ lib/index.d.ts ← 类型镜像,必须与 7 个导出保持一致
|
|
131
|
+
├─ scripts/ ← build.sh + 3 个自测
|
|
132
|
+
├─ cordis.patch.yml ← loader row(id: init-command)
|
|
133
|
+
└─ package.json / pnpm-lock.yaml ← lockfile 入库保可复现安装
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`lib/index.js` 导出 7 个名字,`lib/index.d.ts` 必须逐一镜像:`name` · `inject` · `Config` · `SOURCE_KIND` · `parseOptions` · `buildPrompt` · `apply`。其中 `parseOptions` / `buildPrompt` 是公开的纯函数,可直接 import 做自定义流程或断言测试。
|
|
137
|
+
|
|
138
|
+
面向 agent 的仓库知识(命令、架构细节、坑位)在 [`AGENTS.md`](AGENTS.md),改代码前先读它。
|
|
139
|
+
|
|
140
|
+
## 排错
|
|
141
|
+
|
|
142
|
+
| 症状 | 原因 | 处置 |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| `/init` 不出现在客户端 | 本地 `link:` 安装但没跑 `pnpm install`,解析不到 `@deepseek-ai/dsh-llm` | 仓库内 `pnpm install` 后重启 |
|
|
145
|
+
| 命令在、但系统提示没有维护段 | `config.enabled: false`,或 bundle 未真正挂载 | 查 `Config` inspect 的 `include:init-command` |
|
|
146
|
+
| 段落重复/行为像是旧版本 | `profiles/web/cordis.patch.yml` 里手写的 `init-command` 行遮蔽了 bundle | 删掉该行后重启 |
|
|
147
|
+
| 改完代码没生效 | 宿主按进程缓存插件 | 重启 `dsh web` |
|
|
148
|
+
| 新建文件报 `EACCES ... link '*.tmp'` | 宿主 fs-local 降级路径缺失(与本插件无关) | 跑 `dsh-fix` 后重启 |
|
|
149
|
+
|
|
150
|
+
## 非目标
|
|
151
|
+
|
|
152
|
+
- **不写 `CLAUDE.md`**,也不创建任何替代性指令文件;只维护 `AGENTS.md`。
|
|
153
|
+
- **不替你决定仓库长什么样**:没有可验证依据的段落就省略,宁可短。
|
|
154
|
+
- **不动你的源码、配置和文档**:`/init` 的写盘范围就是那一个文件。
|
|
155
|
+
- **不做一次性报告的存档库**:审计、周报、工作日志不属于 `AGENTS.md`。
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
[MIT](LICENSE)
|
package/cordis.patch.yml
ADDED
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import type z from '@deepseek-ai/schemastery'
|
|
3
|
+
|
|
4
|
+
export declare const name = 'init-command'
|
|
5
|
+
export declare const inject: string[]
|
|
6
|
+
|
|
7
|
+
/** Producer kind of the /init injected-context message (never `user`). */
|
|
8
|
+
export declare const SOURCE_KIND: string
|
|
9
|
+
|
|
10
|
+
export interface InitOptions {
|
|
11
|
+
force: boolean
|
|
12
|
+
minimal: boolean
|
|
13
|
+
detailed: boolean
|
|
14
|
+
/** true when both minimal and detailed were given (pure-options mode only) */
|
|
15
|
+
conflict: boolean
|
|
16
|
+
/** true when rawInput is non-empty free-form guidance rather than bare option tokens */
|
|
17
|
+
naturalLanguage: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export declare function parseOptions(rawInput: unknown): InitOptions
|
|
21
|
+
export declare function buildPrompt(cwd: string, rawInput: unknown): string
|
|
22
|
+
|
|
23
|
+
/** Plugin config — defaults live in the schemastery schema (`enabled` defaults to true). */
|
|
24
|
+
export interface InitConfig {
|
|
25
|
+
/** Gate the persistent agents-md-maintenance system-prompt section (default true). */
|
|
26
|
+
enabled: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export declare const Config: z<InitConfig>
|
|
30
|
+
export declare function apply(ctx: Context, config?: Partial<InitConfig>): void
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* want-a-init — model-driven /init command for DeepSeek Harness.
|
|
3
|
+
*
|
|
4
|
+
* Injects a focused prompt into the agent so the model analyzes the project
|
|
5
|
+
* and writes/updates a concise, high-signal AGENTS.md. Only AGENTS.md is
|
|
6
|
+
* maintained; CLAUDE.md is intentionally not created.
|
|
7
|
+
*
|
|
8
|
+
* Options (rawInput): bare option tokens (force | minimal | detailed, any
|
|
9
|
+
* order, comma/space separated) are parsed strictly; anything else is treated
|
|
10
|
+
* as free-form guidance and passed to the model verbatim — the model, not a
|
|
11
|
+
* regex, interprets it, so negations like "别 force" cannot misfire.
|
|
12
|
+
*/
|
|
13
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
14
|
+
import z from '@deepseek-ai/schemastery'
|
|
15
|
+
|
|
16
|
+
export const name = 'init-command'
|
|
17
|
+
export const inject = ['commands']
|
|
18
|
+
|
|
19
|
+
// Plugin config (0.2 contract: static Config, defaults in the schema).
|
|
20
|
+
// `enabled` gates the persistent agents-md-maintenance system-prompt section;
|
|
21
|
+
// the /init command itself stays available either way.
|
|
22
|
+
export const Config = z.object({
|
|
23
|
+
enabled: z.boolean().default(true),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// Single source of truth for rules that appear in more than one prompt
|
|
27
|
+
// (command prompt + persistent maintenance section) so wording cannot drift.
|
|
28
|
+
// Both strings sit on the per-round token budget: DISTILL_RULE is inlined into
|
|
29
|
+
// the persistent section, so every word is paid by every future session. Keep
|
|
30
|
+
// them terse, and never move one-off `/init` guidance into them.
|
|
31
|
+
const ONLY_AGENTS_MD = 'Only AGENTS.md is created or updated; never create or substitute CLAUDE.md or any other instruction file.'
|
|
32
|
+
|
|
33
|
+
const DISTILL_RULE =
|
|
34
|
+
'Distill, don\'t transcribe: keep only durable cross-task truths — exact commands, architecture, key invariants/thresholds, recurring pitfalls ("what breaks -> why -> fix"), and durable workflow/QA/collaboration rules. ' +
|
|
35
|
+
'Drop one-off instructions, temporary workarounds, incident play-by-play, and work logs. ' +
|
|
36
|
+
'Write terse standalone imperatives in their matching section; never attach provenance tags ("(user requested)", "(用户要求)", "(用户决定)" or any equivalent label), date stamps, or verbatim user quotes, and never invent a technical reason. ' +
|
|
37
|
+
'No evidence, no rule: if no file, config, command, or observed failure backs it, drop the claim. ' +
|
|
38
|
+
'Never prune an entry justified by deployment safety, external-contract compatibility, or production boundaries — keep it and flag it for confirmation.'
|
|
39
|
+
|
|
40
|
+
// Referenced from both the force branch and the free-form guidance branch, so a
|
|
41
|
+
// user asking for a regenerate in prose gets the same guardrail spelled out
|
|
42
|
+
// instead of a dangling "see above".
|
|
43
|
+
const FORCE_GUARDRAIL =
|
|
44
|
+
'Overwrite guardrail: before replacing an existing AGENTS.md, list the existing entries that will be discarded (one line each). If AGENTS.md is tracked by git, you may overwrite it in the same turn. If it is NOT tracked by git (including non-git directories), present the discard list and wait for explicit user confirmation before writing.'
|
|
45
|
+
|
|
46
|
+
export function parseOptions(rawInput) {
|
|
47
|
+
const raw = String(rawInput || '')
|
|
48
|
+
const tokens = raw.toLowerCase().split(/[^a-z]+/).filter(Boolean)
|
|
49
|
+
const known = ['force', 'minimal', 'detailed']
|
|
50
|
+
// Pure-options mode only when the input (ignoring whitespace/commas) is made
|
|
51
|
+
// of known option words; otherwise it is free-form guidance for the model.
|
|
52
|
+
const stripped = raw.replace(/[\s,,、]+/g, '')
|
|
53
|
+
const pure = stripped.length > 0 && /^[a-z]+$/i.test(stripped) && tokens.every(t => known.includes(t))
|
|
54
|
+
return {
|
|
55
|
+
force: pure && tokens.includes('force'),
|
|
56
|
+
minimal: pure && tokens.includes('minimal'),
|
|
57
|
+
detailed: pure && tokens.includes('detailed'),
|
|
58
|
+
conflict: pure && tokens.includes('minimal') && tokens.includes('detailed'),
|
|
59
|
+
// Empty input is plain default mode, NOT free-form guidance — otherwise the
|
|
60
|
+
// prompt would quote an empty user message back at the model.
|
|
61
|
+
naturalLanguage: !pure && stripped.length > 0,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function buildPrompt(cwd, rawInput) {
|
|
66
|
+
const { force, minimal, detailed, naturalLanguage } = parseOptions(rawInput)
|
|
67
|
+
|
|
68
|
+
const mode = force ? 'overwrite' : 'merge-or-create'
|
|
69
|
+
|
|
70
|
+
// The guidance block is delimited so it cannot outrank the hard rules; any
|
|
71
|
+
// closing-tag lookalike inside the user's text is neutralized so the
|
|
72
|
+
// delimiter cannot be escaped from within.
|
|
73
|
+
const guidance = String(rawInput ?? '').replace(/<\s*\/\s*user_guidance\s*>/gi, '[/user_guidance]')
|
|
74
|
+
|
|
75
|
+
// Only the branches that actually introduce a guardrail may cite it.
|
|
76
|
+
const pauseReasons = [
|
|
77
|
+
'the repository is empty or has no discoverable project signals',
|
|
78
|
+
...(force || naturalLanguage ? ['an untracked AGENTS.md triggers the overwrite guardrail'] : []),
|
|
79
|
+
...(naturalLanguage ? ['`<user_guidance>` leaves the overwrite intent genuinely ambiguous'] : []),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
const detailGuidance = minimal
|
|
83
|
+
? [
|
|
84
|
+
'Detail level: minimal (target ~40 lines or fewer).',
|
|
85
|
+
'- Keep only high-leverage survival facts an agent would get wrong without help: Project identity, exact Commands (with order), and critical Pitfalls.',
|
|
86
|
+
'- Compress Layout to 2-5 lines highlighting real entrypoints/boundaries (skip full trees). Omit Architecture, Docs, and Maintenance unless strictly required to prevent mistakes.',
|
|
87
|
+
]
|
|
88
|
+
: detailed
|
|
89
|
+
? [
|
|
90
|
+
'Detail level: detailed (comprehensive onboarding reference, still zero fluff — every line must earn its context cost).',
|
|
91
|
+
'- Expand verified module boundaries, key invariants, test prerequisites/fixtures, environment setup, and recurring workflow/QA playbooks where warranted.',
|
|
92
|
+
]
|
|
93
|
+
: [
|
|
94
|
+
'Detail level: default (concise and practical — ideally under 200 lines unless the repo defines its own size budget, and much shorter for simple repos).',
|
|
95
|
+
'- Fill every applicable section with verified, high-signal facts; prefer short bullets and omit empty sections.',
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
const lines = [
|
|
99
|
+
`Run /init for the project at \`${cwd}\`.`,
|
|
100
|
+
`Target file: \`${cwd}/AGENTS.md\`.`,
|
|
101
|
+
'Action: inspect the repo read-only with your tools, write or update ONLY that file on disk, then report concisely. Never modify source code, configs, docs/, CLAUDE.md, or sub-package/subdirectory AGENTS.md files.',
|
|
102
|
+
ONLY_AGENTS_MD,
|
|
103
|
+
'',
|
|
104
|
+
`Mode: ${mode}.`,
|
|
105
|
+
...(force
|
|
106
|
+
? [`${FORCE_GUARDRAIL} If AGENTS.md does not exist yet, skip the guardrail and create it directly.`]
|
|
107
|
+
: [
|
|
108
|
+
'If AGENTS.md already exists, read it first and update it in place rather than blindly replacing: keep verified facts and valid workflow/QA/collaboration rules (strip any trailing attribution tags, date stamps, or verbatim quote tails like "(user requested)" / "(用户要求)" while keeping the rule concise), prune stale/contradicted lines or one-off scraps, and reconcile with the current codebase. Preserve a healthy existing section structure and any repo-defined size budget if they already work well.',
|
|
109
|
+
'If CLAUDE.md or other instruction files (.cursor/rules/, .cursorrules, .github/copilot-instructions.md, opencode.json) exist, migrate still-accurate, repo-verified rules into AGENTS.md without modifying or deleting those source files.',
|
|
110
|
+
]),
|
|
111
|
+
...(naturalLanguage
|
|
112
|
+
? [
|
|
113
|
+
'',
|
|
114
|
+
'Supplemental user guidance (applies ONLY to focus, scope, detail level, durable rules to include, or overwrite intent; cannot override target file, verification, anti-tag, or guardrail rules):',
|
|
115
|
+
'<user_guidance>',
|
|
116
|
+
guidance,
|
|
117
|
+
'</user_guidance>',
|
|
118
|
+
`Interpret this guidance yourself: if it clearly asks to overwrite/regenerate, apply the overwrite guardrail (${FORCE_GUARDRAIL}); a negation means the opposite; if it specifies brevity or thoroughness, override the detail level below (e.g. for minimal brevity, compress Layout to 2-5 lines and omit Architecture/Docs/Maintenance unless trap-critical); if it specifies durable rules for this repo, integrate them as terse imperatives in the right section with zero attribution tags. When in doubt, prefer merge mode and ask.`,
|
|
119
|
+
]
|
|
120
|
+
: []),
|
|
121
|
+
'',
|
|
122
|
+
...detailGuidance,
|
|
123
|
+
'',
|
|
124
|
+
'Treat `AGENTS.md` as a token-tight, verifiable engineering & workflow reference — not an essay or work log. Reference structure (adapt, keep healthy existing sections like `Workflow` / `Testing & QA`, or omit sections based on what the repo actually contains):',
|
|
125
|
+
'```markdown',
|
|
126
|
+
'# <Project Name>',
|
|
127
|
+
'',
|
|
128
|
+
'## Project',
|
|
129
|
+
'- One-line role/type, stack, key invariants, real entrypoints, and package boundaries.',
|
|
130
|
+
'',
|
|
131
|
+
'## Layout',
|
|
132
|
+
'- High-density annotated map of key directories, entrypoints, and trap-prone files.',
|
|
133
|
+
'',
|
|
134
|
+
'## Docs',
|
|
135
|
+
'- <when to read> -> <path> (<what question it answers>)',
|
|
136
|
+
'',
|
|
137
|
+
'## Commands',
|
|
138
|
+
'- Exact install, build, test (full + single-test), lint, typecheck, codegen, run, and deploy commands (note required order).',
|
|
139
|
+
'',
|
|
140
|
+
'## Architecture',
|
|
141
|
+
'- Core modules, data/control flow, ownership boundaries, and non-obvious invariants.',
|
|
142
|
+
'',
|
|
143
|
+
'## Conventions',
|
|
144
|
+
'- Repo-specific coding/boundary rules and "do not" rules (add a brief reason only when non-obvious).',
|
|
145
|
+
'',
|
|
146
|
+
'## Pitfalls',
|
|
147
|
+
'- Recurring failure modes (or compact trigger-word index pointing to a dedicated pitfalls doc).',
|
|
148
|
+
'',
|
|
149
|
+
'## Maintenance',
|
|
150
|
+
'- Update in place when a reusable fact or durable rule emerges; keep entries ordered (most-used first) and prune stale/one-off lines.',
|
|
151
|
+
'```',
|
|
152
|
+
'',
|
|
153
|
+
'Structure & formatting rules (follow these when shaping the file above):',
|
|
154
|
+
'- Floor, not a ceiling: omit any section with no verifiable or durable content (never leave TODOs or `<...>` placeholders). Keep or add sections when a distinct class of repo knowledge needs its own reading moment (e.g. `Workflow`, `Testing & QA`, a release playbook, or rejected architectural approaches). Do not inject generic `Docs`-routing instructions into `Maintenance` if the repo has no `Docs` table.',
|
|
155
|
+
'- Progressive disclosure & index pattern: for large repos, keep root `AGENTS.md` lean by placing compact indexes or anchor lists that point into `docs/` or subdirectory `AGENTS.md` files (e.g. `src/client/AGENTS.md`) rather than duplicating long details inline. Read existing subdirectory `AGENTS.md` files for context and link to them without editing them.',
|
|
156
|
+
'- Layout craft: group homogeneous files into one directory line with a count (e.g. "tests/ <- 31 *.test.ts (node --test)"); list an individual file only when it is an entrypoint, core module, or real trap. Tree glyphs (`├─ └─`) are recommended for multi-directory repos (and may live under `Layout` or inside `Project`); for tiny repos or `minimal` mode, a compact 2-5 line summary suffices.',
|
|
157
|
+
'- Docs routing: scan whatever long-lived documentation directories or root docs actually exist (`docs/`, `doc/`, `adr/`, root `ARCHITECTURE.md` / `RUNBOOK.md`, etc.); skip `README*`, `CHANGELOG*`, and one-off audits, reports, or scratch notes. Omit `Docs` if no qualifying long-lived docs exist.',
|
|
158
|
+
'',
|
|
159
|
+
'Investigation workflow (verify repository facts with your tools):',
|
|
160
|
+
'1. Inspect codebase evidence: root manifests, README*, workspace config, lockfiles; build/test/lint/typecheck/formatter/codegen/CI/pre-commit configs (including single-test and single-package commands); existing instruction files; source entrypoints and core directories; actual long-lived docs; and recent `git log`, `git status`, `git diff`.',
|
|
161
|
+
'2. Check recent context safely: session memory may be compacted or stale. Re-verify every technical claim from recent session history or diffs against files currently on disk before writing; discard one-off debugging details, and integrate durable workflow rules as terse imperatives without attribution tags.',
|
|
162
|
+
'',
|
|
163
|
+
'Distillation & quality bar:',
|
|
164
|
+
DISTILL_RULE,
|
|
165
|
+
'- Phrase rules as executable decision tests, not adjectives (bad: "keep tests stable"; good: "after editing schema.prisma, run `pnpm codegen` before typecheck or generated types drift").',
|
|
166
|
+
'- One fact, one home: write the full detail in the most specific section and point to it elsewhere instead of restating. Order entries deliberately (most-used or most-critical first) and keep that order stable.',
|
|
167
|
+
'- Exclude generic software advice, obvious language conventions, speculative claims, unverified technical details, and bare unannotated file dumps.',
|
|
168
|
+
'',
|
|
169
|
+
'Execution & reporting:',
|
|
170
|
+
`- Once core project identity and commands are verified, write \`${cwd}/AGENTS.md\` directly (omitting unverifiable sections) rather than stalling on minor missing facts.`,
|
|
171
|
+
`- Pause BEFORE writing only if: ${pauseReasons.map((r, i) => `(${i + 1}) ${r}`).join(', or ')}.`,
|
|
172
|
+
'- Report concisely what was created/updated, what stale or token-wasting content was pruned, and (if applicable) 1-3 targeted open questions that could not be answered from code.',
|
|
173
|
+
]
|
|
174
|
+
return lines.join('\n')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Producer identity for the injected context message. A source kind that is not
|
|
178
|
+
// `user` is what keeps the /init order out of the human turn: the client renders
|
|
179
|
+
// it as a collapsed context row (turn trigger when it wakes the driver) instead
|
|
180
|
+
// of a prompt the user appears to have typed.
|
|
181
|
+
export const SOURCE_KIND = 'want-a-init'
|
|
182
|
+
|
|
183
|
+
const INIT_SUMMARY = '/init: analyzing the project to create or update AGENTS.md'
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Deliver the analysis order as injected context.
|
|
187
|
+
*
|
|
188
|
+
* `agent.send(message, 'next-step', true)` routes the item to the next-step
|
|
189
|
+
* inbox and wakes the driver, so the model runs on it without a human turn
|
|
190
|
+
* owning the prompt. `agent.inject()` would not wake an idle driver, and
|
|
191
|
+
* `agent.followup()` would open a turn presented as a user message. The
|
|
192
|
+
* followup fallback covers runtimes that predate `send`.
|
|
193
|
+
*/
|
|
194
|
+
function deliver(agent, message) {
|
|
195
|
+
if (typeof agent.send === 'function') agent.send(message, 'next-step', true)
|
|
196
|
+
else agent.followup(message)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Resolve the session's live working directory.
|
|
201
|
+
*
|
|
202
|
+
* `session.header.cwd` is only the creation-time directory; a session may have
|
|
203
|
+
* changed it since (`working-directory/change`), so prefer the owning service
|
|
204
|
+
* and fall back to the header when it is not composed.
|
|
205
|
+
*/
|
|
206
|
+
async function resolveWorkingDirectory(ctx, agent, signal) {
|
|
207
|
+
const service = typeof ctx.get === 'function' ? ctx.get('workingDirectory') : undefined
|
|
208
|
+
if (typeof service?.ensure !== 'function') return agent.session.header.cwd
|
|
209
|
+
try {
|
|
210
|
+
return await service.ensure(agent, signal)
|
|
211
|
+
} catch (error) {
|
|
212
|
+
if (signal?.aborted) throw error
|
|
213
|
+
ctx.logger?.warn?.('want-a-init: working directory lookup failed: %o', error)
|
|
214
|
+
return agent.session.header.cwd
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function apply(ctx, config = {}) {
|
|
219
|
+
ctx.commands.register({
|
|
220
|
+
name: 'init',
|
|
221
|
+
description: 'Ask the agent to analyze the current project and generate/update AGENTS.md. Options: force, minimal, detailed.',
|
|
222
|
+
input: { hint: 'optional: force | minimal | detailed, or free-form guidance' },
|
|
223
|
+
async handler(invocation) {
|
|
224
|
+
const { agent, signal } = invocation
|
|
225
|
+
if (signal?.aborted) return { kind: 'error', text: 'Init cancelled.' }
|
|
226
|
+
|
|
227
|
+
const cwd = await resolveWorkingDirectory(ctx, agent, signal)
|
|
228
|
+
if (!cwd) {
|
|
229
|
+
return { kind: 'error', text: 'No session working directory available.' }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const opts = parseOptions(invocation.rawInput)
|
|
233
|
+
const prompt = buildPrompt(cwd, invocation.rawInput)
|
|
234
|
+
|
|
235
|
+
deliver(agent, createUserMessage({
|
|
236
|
+
content: [{ type: 'text', text: prompt }],
|
|
237
|
+
source: { kind: SOURCE_KIND, form: 'notice', summary: INIT_SUMMARY },
|
|
238
|
+
}))
|
|
239
|
+
|
|
240
|
+
let text = 'Init started: I’m analyzing the project and will create/update AGENTS.md.'
|
|
241
|
+
if (opts.conflict) text += ' Note: both minimal and detailed were given — proceeding with minimal.'
|
|
242
|
+
if (opts.naturalLanguage) text += ' Your input will be interpreted as guidance for this run.'
|
|
243
|
+
|
|
244
|
+
return { kind: 'success', text }
|
|
245
|
+
},
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
// Persistent agent awareness: whenever the system prompt is assembled, remind
|
|
249
|
+
// the agent that AGENTS.md is a living, repo-specific knowledge base it should
|
|
250
|
+
// keep filling in and summarizing. This makes the maintenance behavior part of
|
|
251
|
+
// the agent runtime, not just the one-shot /init command.
|
|
252
|
+
// This text is paid by EVERY round of every session, so it is the tightest
|
|
253
|
+
// prose in the plugin: each sentence must justify its own tokens or it goes.
|
|
254
|
+
// Gated by config.enabled (default true). Section order 650 lands between the
|
|
255
|
+
// host's TEAM_POLICY (600) and PTC_ONLY (800): after every policy block, before
|
|
256
|
+
// the first tool section — guidance, not the highest-priority instruction.
|
|
257
|
+
if (config.enabled === false) return
|
|
258
|
+
ctx.inject(['systemPrompt'], (scope) => {
|
|
259
|
+
scope.systemPrompt.section({
|
|
260
|
+
name: 'agents-md-maintenance',
|
|
261
|
+
order: 650,
|
|
262
|
+
text:
|
|
263
|
+
'AGENTS.md maintenance: act ONLY if the workspace already has an AGENTS.md (never create one mid-workflow unless asked or /init runs). ' +
|
|
264
|
+
'Default to NO EDIT — most tasks need zero updates. ' +
|
|
265
|
+
DISTILL_RULE + ' ' +
|
|
266
|
+
'Evaluate only at task end; update in place only for a genuinely reusable fact or durable repo rule, and note the updated section in your reply. ' +
|
|
267
|
+
'Prune only when already editing (or when an entry actively misleads or points to a deleted path): remove stale facts, dead links, one-off scraps, and any trailing attribution/date/quote tags (strip the tag, keep valid rules). Keep order (most-used first; one fact, one home). ' +
|
|
268
|
+
'For new long-lived docs kept in the repo, place per Layout, start with a self-describing header, and add "<when to read> -> <path> (<answers what>)" to Docs before finishing. ' +
|
|
269
|
+
ONLY_AGENTS_MD,
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "want-a-init",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Model-driven /init command for DeepSeek Harness: analyzes the current project and generates/updates a high-signal AGENTS.md.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"lib",
|
|
18
|
+
"cordis.patch.yml",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "bash scripts/build.sh"
|
|
24
|
+
},
|
|
25
|
+
"dsh": {
|
|
26
|
+
"bundle": {
|
|
27
|
+
"patch": "./cordis.patch.yml"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"dsh-plugin",
|
|
32
|
+
"deepseek-harness",
|
|
33
|
+
"init",
|
|
34
|
+
"agents.md",
|
|
35
|
+
"claude-code"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@deepseek-ai/cordis": "~4.1.0",
|
|
40
|
+
"@deepseek-ai/dsh-commands": "^0.2.2-rc.1",
|
|
41
|
+
"@deepseek-ai/dsh-llm": "^0.2.2-rc.1",
|
|
42
|
+
"@deepseek-ai/schemastery": "^3.19.0"
|
|
43
|
+
}
|
|
44
|
+
}
|