silvermoon 0.2.0 → 0.2.1
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 +20 -16
- package/README.zh-CN.md +18 -15
- package/docs/getting-started.md +17 -7
- package/package.json +3 -3
- package/skills/silvermoon/SKILL.md +11 -5
- package/skills/silvermoon/references/adoption.md +14 -4
- package/src/adoption.js +345 -8
- package/src/response.js +51 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<!-- markdownlint-disable-next-line MD013 -->
|
|
5
|
-
<img src="https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@
|
|
5
|
+
<img src="https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/assets/silvermoon.svg" width="960" alt="Silvermoon, the artifact spirit of the project">
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
|
-
English | <a href="https://github.com/shazhou-ww/silvermoon/blob/
|
|
20
|
+
English | <a href="https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/README.zh-CN.md">简体中文</a>
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
23
|
# Silvermoon
|
|
@@ -35,11 +35,15 @@ silvermoon whats-next
|
|
|
35
35
|
silvermoon list-ideas
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
Silvermoon
|
|
39
|
-
`package.json
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
Silvermoon remains ecosystem-neutral when the repository root has no
|
|
39
|
+
`package.json`: non-npm projects do not need a package manager, a Silvermoon
|
|
40
|
+
dependency, or `node_modules`. If a root `package.json` exists, it must be valid
|
|
41
|
+
JSON and declare `devDependencies.silvermoon` as exactly `^<running-version>`.
|
|
42
|
+
The report supplies a package-manager-aware repair command. After installing
|
|
43
|
+
that root dependency, register its canonical skill from
|
|
44
|
+
`./node_modules/silvermoon/skills`; non-npm projects use the skill bundled with
|
|
45
|
+
the running installation. Readiness checks inspect the selected repository
|
|
46
|
+
snapshot and do not install dependencies or require `node_modules`.
|
|
43
47
|
|
|
44
48
|
Create `.silvermoon/config.yaml`:
|
|
45
49
|
|
|
@@ -77,7 +81,7 @@ silvermoon whats-next <idea> --language zh-CN
|
|
|
77
81
|
Describe the desired world in the generated `Idea.md`, review it, and approve
|
|
78
82
|
that exact revision. From then on, Silvermoon keeps the goal, implementation,
|
|
79
83
|
repository state, and real-world result connected. See
|
|
80
|
-
[Getting Started](https://github.com/shazhou-ww/silvermoon/blob/
|
|
84
|
+
[Getting Started](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/getting-started.md) for the complete first workflow.
|
|
81
85
|
Every command processes one ordered, versioned domain-message stream. Add
|
|
82
86
|
`--json` when a programmatic consumer needs its four deterministic projections:
|
|
83
87
|
`intention`, `observation`, `actions`, and `response`. Actions are side effects
|
|
@@ -86,7 +90,7 @@ Markdown renders only the self-contained response instead of exposing the
|
|
|
86
90
|
internal projections. For pre-commit validation, run
|
|
87
91
|
`silvermoon check --staged`: unlike the default `check` (committed `HEAD`), it
|
|
88
92
|
checks the index and exits `0` only when the project snapshot is valid. See the
|
|
89
|
-
[Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/
|
|
93
|
+
[Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/reference.md) for validation targets and exit
|
|
90
94
|
codes.
|
|
91
95
|
|
|
92
96
|
Add `--trace <file.trace.jsonl>` to any command to write one schema-versioned
|
|
@@ -105,7 +109,7 @@ actionable phase's snapshot-bound, repository-owned Markdown in
|
|
|
105
109
|
`response.guidance`; `observation.guidance` carries its provenance without
|
|
106
110
|
content. `create-idea` can return preparing guidance. This guidance is
|
|
107
111
|
additive, never a fourth contract or decision fact, and `check` validates the
|
|
108
|
-
complete fixed directory. See [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/
|
|
112
|
+
complete fixed directory. See [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/core-concepts.md).
|
|
109
113
|
|
|
110
114
|
## Why Silvermoon
|
|
111
115
|
|
|
@@ -135,7 +139,7 @@ sessions, Agents, and hosting platforms.
|
|
|
135
139
|
<tr>
|
|
136
140
|
<td width="160" align="center" valign="top">
|
|
137
141
|
<!-- markdownlint-disable-next-line MD013 -->
|
|
138
|
-
<img src="https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@
|
|
142
|
+
<img src="https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/assets/silvermoon-avatar.svg" width="128" alt="Line portrait of Silvermoon, the project's artifact spirit">
|
|
139
143
|
</td>
|
|
140
144
|
<td valign="top">
|
|
141
145
|
Silvermoon is named after a character in <em>A Record of a Mortal's
|
|
@@ -183,13 +187,13 @@ the project, not to a fleeting conversation.
|
|
|
183
187
|
|
|
184
188
|
## Further Reading
|
|
185
189
|
|
|
186
|
-
- [Getting Started](https://github.com/shazhou-ww/silvermoon/blob/
|
|
190
|
+
- [Getting Started](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/getting-started.md) — installation, configuration,
|
|
187
191
|
and the first idea.
|
|
188
|
-
- [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/
|
|
192
|
+
- [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/core-concepts.md) — project ownership, the three
|
|
189
193
|
worlds, revisions, and decision boundaries.
|
|
190
|
-
- [Operating Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/
|
|
194
|
+
- [Operating Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/operations.md) — navigation, creation,
|
|
191
195
|
publication, hygiene, and continuation.
|
|
192
|
-
- [Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/
|
|
196
|
+
- [Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/reference.md) — storage, derived state, CLI
|
|
193
197
|
reports, validation targets, and schemas.
|
|
194
|
-
- [Maintaining Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/
|
|
198
|
+
- [Maintaining Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/maintaining.md) — development checks,
|
|
195
199
|
documentation ownership, and release guidance.
|
package/README.zh-CN.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<!-- markdownlint-disable-next-line MD013 -->
|
|
5
|
-
<img src="https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@
|
|
5
|
+
<img src="https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/assets/silvermoon.svg" width="960" alt="Silvermoon,项目的器灵">
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
|
-
<a href="https://github.com/shazhou-ww/silvermoon/blob/
|
|
9
|
+
<a href="https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/README.md">English</a> | 简体中文
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
# Silvermoon(银月)
|
|
@@ -23,10 +23,13 @@ silvermoon whats-next
|
|
|
23
23
|
silvermoon list-ideas
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Silvermoon
|
|
27
|
-
Silvermoon dependency 或 `node_modules
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
Silvermoon 对非 npm 仓库保持生态中立:当仓库根目录没有 `package.json` 时,无需
|
|
27
|
+
package manager、Silvermoon dependency 或 `node_modules`。如果根目录存在
|
|
28
|
+
`package.json`,它必须是有效 JSON,并在 `devDependencies.silvermoon` 中逐字声明
|
|
29
|
+
`^<当前运行版本>`。首次报告会按项目 package manager 提供修复命令。安装根依赖后,
|
|
30
|
+
从 `./node_modules/silvermoon/skills` 注册 canonical skill;非 npm 项目继续使用当前
|
|
31
|
+
运行实例随附的 skill。整备检查只读取所选 repository snapshot,不会安装依赖,也不要求
|
|
32
|
+
存在 `node_modules`。
|
|
30
33
|
|
|
31
34
|
创建 `.silvermoon/config.yaml`:
|
|
32
35
|
|
|
@@ -60,14 +63,14 @@ silvermoon whats-next <idea> --language zh-CN
|
|
|
60
63
|
|
|
61
64
|
在生成的 `Idea.md` 中描述想要抵达的世界,审阅并批准这个精确 revision。此后,
|
|
62
65
|
Silvermoon 会让目标、实现、仓库状态与现实结果始终相连。完整的首次工作流见英文
|
|
63
|
-
[Getting Started](https://github.com/shazhou-ww/silvermoon/blob/
|
|
66
|
+
[Getting Started](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/getting-started.md)。
|
|
64
67
|
每个命令都处理一条有序且带版本的 domain-message stream。程序消费者可使用
|
|
65
68
|
`--json` 获取四个确定性 projection:`intention`、`observation`、`actions` 与
|
|
66
69
|
`response`。`actions` 只记录已经尝试的副作用,未来工作只位于
|
|
67
70
|
`response.nextSteps`。默认 Markdown 只渲染自足的 response,不暴露内部
|
|
68
71
|
projection。提交前使用 `silvermoon check --staged` 验证 index;无参数 `check`
|
|
69
72
|
只验证已提交的 `HEAD`,不能替代提交前检查。只有项目快照有效时检查才以退出码
|
|
70
|
-
`0` 放行,详见英文 [Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/
|
|
73
|
+
`0` 放行,详见英文 [Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/reference.md)。
|
|
71
74
|
|
|
72
75
|
任何命令都可以添加 `--trace <file.trace.jsonl>`,写出一条 schema-versioned
|
|
73
76
|
JSONL timeline。`domain` channel 保存经过 allowlist 的 message 摘要,
|
|
@@ -82,7 +85,7 @@ repository 内的 `*.trace.jsonl` 会被忽略。trace 文件采用缓冲写入
|
|
|
82
85
|
与 snapshot 绑定的 repository-owned Markdown;`observation.guidance` 仅保留
|
|
83
86
|
provenance。`create-idea` 可返回 preparing guidance。这些 guidance 只能追加
|
|
84
87
|
约束,不是第四份 contract 或 decision fact;`check` 会验证完整的固定目录。详见英文
|
|
85
|
-
[Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/
|
|
88
|
+
[Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/core-concepts.md)。
|
|
86
89
|
|
|
87
90
|
## 为什么需要 Silvermoon
|
|
88
91
|
|
|
@@ -105,7 +108,7 @@ provenance。`create-idea` 可返回 preparing guidance。这些 guidance 只能
|
|
|
105
108
|
<tr>
|
|
106
109
|
<td width="160" align="center" valign="top">
|
|
107
110
|
<!-- markdownlint-disable-next-line MD013 -->
|
|
108
|
-
<img src="https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@
|
|
111
|
+
<img src="https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/assets/silvermoon-avatar.svg" width="128" alt="项目器灵银月的线稿头像">
|
|
109
112
|
</td>
|
|
110
113
|
<td valign="top">
|
|
111
114
|
Silvermoon 得名于《凡人修仙传》中的银月。她来自灵界的银月狼族,是玲珑公主分裂出的
|
|
@@ -145,8 +148,8 @@ provenance。`create-idea` 可返回 preparing guidance。这些 guidance 只能
|
|
|
145
148
|
|
|
146
149
|
## 延伸阅读
|
|
147
150
|
|
|
148
|
-
- [Getting Started](https://github.com/shazhou-ww/silvermoon/blob/
|
|
149
|
-
- [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/
|
|
150
|
-
- [Operating Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/
|
|
151
|
-
- [Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/
|
|
152
|
-
- [Maintaining Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/
|
|
151
|
+
- [Getting Started](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/getting-started.md) — 安装、配置与第一个 idea。
|
|
152
|
+
- [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/core-concepts.md) — 项目归属、三重世界、revision 与决策边界。
|
|
153
|
+
- [Operating Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/operations.md) — 导航、创建、发布、仓库卫生与持续推进。
|
|
154
|
+
- [Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/reference.md) — 存储、派生状态、CLI report、验证 target 与 schema。
|
|
155
|
+
- [Maintaining Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/maintaining.md) — 开发检查、文档职责与发布指南。
|
package/docs/getting-started.md
CHANGED
|
@@ -20,18 +20,28 @@ silvermoon whats-next
|
|
|
20
20
|
# or: npx silvermoon@<version> whats-next
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
For repositories without a root `package.json`, the setup report gives the
|
|
24
|
+
exact path for the running installation. Use it to register the bundled
|
|
25
|
+
canonical skill through the universal `skills` target:
|
|
25
26
|
|
|
26
27
|
```sh
|
|
27
28
|
npx skills add <path-to-running-silvermoon>/skills --skill silvermoon --agent universal --yes --copy
|
|
28
29
|
```
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
the
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
If the repository root contains `package.json`, Silvermoon requires valid JSON
|
|
32
|
+
and an exact `devDependencies.silvermoon` value of `^<running-version>`.
|
|
33
|
+
Follow the setup report's package-manager-specific command, then install the
|
|
34
|
+
root dependencies and register the project-local canonical skill:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npx skills add ./node_modules/silvermoon/skills --skill silvermoon --agent universal --yes --copy
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Only the tracked root manifest is part of readiness; installed dependencies
|
|
41
|
+
and `node_modules` are not required for snapshot checks. Silvermoon checks the
|
|
42
|
+
registration at `.agents/skills/silvermoon` against its running canonical
|
|
43
|
+
skill, and never mutates the manifest, package-manager files, dependencies, or
|
|
44
|
+
skill registration itself. Non-npm repositories remain free of npm setup.
|
|
35
45
|
|
|
36
46
|
## Configure The Shared Primary
|
|
37
47
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silvermoon",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "The project spirit that checks repository state and answers what's next.",
|
|
5
5
|
"packageManager": "pnpm@11.22.0",
|
|
6
6
|
"type": "module",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"mdast-util-to-markdown": "^2.1.2",
|
|
67
67
|
"yaml": "^2.9.1"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
70
|
-
"readme": "<!-- markdownlint-disable-file MD041 -->\n\n<p align=\"center\">\n <!-- markdownlint-disable-next-line MD013 -->\n <img src=\"https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@27700c428509c3152d5eb96e90a8e1a4a150c6cb/assets/silvermoon.svg\" width=\"960\" alt=\"Silvermoon, the artifact spirit of the project\">\n</p>\n\n<p align=\"center\">\n <!-- markdownlint-disable-next-line MD013 -->\n <a href=\"https://npmx.dev/package/silvermoon\"><img src=\"https://img.shields.io/npm/v/silvermoon?style=flat-square\" alt=\"npm version\"></a>\n <!-- markdownlint-disable-next-line MD013 -->\n <a href=\"https://npmx.dev/package/silvermoon\"><img src=\"https://img.shields.io/node/v/silvermoon?style=flat-square\" alt=\"Node.js version\"></a>\n <!-- markdownlint-disable-next-line MD013 -->\n <a href=\"https://npmx.dev/package/silvermoon\"><img src=\"https://img.shields.io/npm/unpacked-size/silvermoon?style=flat-square\" alt=\"npm unpacked size\"></a>\n <!-- markdownlint-disable-next-line MD013 -->\n <a href=\"https://github.com/shazhou-ww/silvermoon/actions/workflows/ci.yml?query=branch%3Amain\"><img src=\"https://img.shields.io/github/actions/workflow/status/shazhou-ww/silvermoon/ci.yml?branch=main&style=flat-square&label=CI\" alt=\"CI status\"></a>\n</p>\n\n<p align=\"center\">\n English | <a href=\"https://github.com/shazhou-ww/silvermoon/blob/27700c428509c3152d5eb96e90a8e1a4a150c6cb/README.zh-CN.md\">简体中文</a>\n</p>\n\n# Silvermoon\n\n**The artifact spirit of the project.**\n\n## Quick Start\n\nSilvermoon needs Node.js 22 or newer and Git access to the repository's primary\nbranch.\n\n```sh\nnpm install --global silvermoon\nsilvermoon whats-next\nsilvermoon list-ideas\n```\n\nSilvermoon is ecosystem-neutral: the target repository does not need\n`package.json`, a package manager, a Silvermoon dependency, or `node_modules`.\nThe first report lists every applicable setup step in order, including the\nexact `npx skills add ... --agent universal` command that registers the\ncanonical skill at `.agents/skills/silvermoon`.\n\nCreate `.silvermoon/config.yaml`:\n\n```yaml\nversion: 1\nprimaryRepository: https://github.com/example/repository.git\nprimaryBranch: main\npreferredLanguage: en\n```\n\n`preferredLanguage` is optional. A specific idea can override it with\n`language` in `status.yaml`, and `create-idea --language <tag>` can set that\noverride when the idea is created. Otherwise Silvermoon checks\n`~/.config/silvermoon/config.yaml` and finally defaults to `en-US`.\nThese stored values choose the language for project-owned content. To change\nonly one command's built-in output, use `whats-next --language en-US|zh-CN` or\n`check --language en-US|zh-CN`; the temporary override is never persisted.\n\nUse `list-ideas` when you only need the current local inventory. It defaults to\nactive ideas and supports repeatable `--state`, `--all`, literal `--query`,\nRFC 3339 `--created-since` / `--created-before`, `--sort`, and `--limit`.\nUnlike lifecycle navigation, this read-only query uses the valid worktree\nsnapshot without checking branch, upstream, cleanliness, network, or primary\nsynchronization.\n\nThen create an idea and let the registered Silvermoon skill guide one safe\nnext action at a time:\n\n```sh\nsilvermoon create-idea\nsilvermoon whats-next <idea>\nsilvermoon whats-next <idea> --language zh-CN\n```\n\nDescribe the desired world in the generated `Idea.md`, review it, and approve\nthat exact revision. From then on, Silvermoon keeps the goal, implementation,\nrepository state, and real-world result connected. See\n[Getting Started](https://github.com/shazhou-ww/silvermoon/blob/27700c428509c3152d5eb96e90a8e1a4a150c6cb/docs/getting-started.md) for the complete first workflow.\nEvery command processes one ordered, versioned domain-message stream. Add\n`--json` when a programmatic consumer needs its four deterministic projections:\n`intention`, `observation`, `actions`, and `response`. Actions are side effects\nalready attempted; future work belongs only to `response.nextSteps`. Default\nMarkdown renders only the self-contained response instead of exposing the\ninternal projections. For pre-commit validation, run\n`silvermoon check --staged`: unlike the default `check` (committed `HEAD`), it\nchecks the index and exits `0` only when the project snapshot is valid. See the\n[Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/27700c428509c3152d5eb96e90a8e1a4a150c6cb/docs/reference.md) for validation targets and exit\ncodes.\n\nAdd `--trace <file.trace.jsonl>` to any command to write one schema-versioned\nJSONL timeline. Its `domain` channel contains allowlisted message summaries,\nand its `telemetry` channel contains paired performance spans; both share one\ntrace ID and global sequence. Trace output never includes guidance or file\ncontent, Git arguments/output, environment data, or credentials. If the\nargument does not end with the exact lowercase `.trace.jsonl` suffix,\nSilvermoon appends it. Repository-local `*.trace.jsonl` files are ignored.\nTrace files are buffered, created only after command work completes, and never\noverwrite an existing file.\n\nProjects may optionally add `.silvermoon/guidance/preparing.md`,\n`implementing.md`, and `deploying.md`. Silvermoon returns only the current\nactionable phase's snapshot-bound, repository-owned Markdown in\n`response.guidance`; `observation.guidance` carries its provenance without\ncontent. `create-idea` can return preparing guidance. This guidance is\nadditive, never a fourth contract or decision fact, and `check` validates the\ncomplete fixed directory. See [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/27700c428509c3152d5eb96e90a8e1a4a150c6cb/docs/core-concepts.md).\n\n## Why Silvermoon\n\nLong-running Agent work usually asks people to carry too much invisible state.\nSomeone must remember what was intended, whether the code still matches the\ntask, and which conversation or machine knows the latest truth. Silvermoon\nmakes those concerns part of the project instead.\n\nIt lets people step back from continuous supervision and return at the\ndecisions that belong to them: approving the intended outcome, accepting the\nimplementation, and accepting that the result is true in the outside world.\nBetween those boundaries, an Agent can inspect repository facts and continue\nthe highest-priority safe action.\n\nIt also refuses to let a detached task card declare success. Current artifacts\nand explicit decisions jointly determine state, so a changed goal or\nimplementation naturally invalidates conclusions that depended on the older\nrevision. Because those facts live in Git, work can continue across devices,\nsessions, Agents, and hosting platforms.\n\n## The Project's Artifact Spirit\n\n> Fellow Daoist, you wouldn't want your lifebound project to be without an\n> artifact spirit, would you?\n\n<table>\n <tr>\n <td width=\"160\" align=\"center\" valign=\"top\">\n <!-- markdownlint-disable-next-line MD013 -->\n <img src=\"https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@27700c428509c3152d5eb96e90a8e1a4a150c6cb/assets/silvermoon-avatar.svg\" width=\"128\" alt=\"Line portrait of Silvermoon, the project's artifact spirit\">\n </td>\n <td valign=\"top\">\n Silvermoon is named after a character in <em>A Record of a Mortal's\n Journey to Immortality</em>. She comes from the Silvermoon Wolf Clan in\n the Spirit Realm and is one of the split souls of Ling Long. After losing\n part of her memory in the human realm, she lives as an artifact spirit\n first in a wolf-headed jade scepter and later in Han Li's Bamboo\n Cloudswarm Swords.\n <br><br>\n That image fits this project: Silvermoon does not belong to one operator\n or one chat. It lives with the project's artifacts, understands their\n state, and helps each companion find what comes next. The biography is\n inspiration, not a prerequisite for using the tool.\n </td>\n </tr>\n</table>\n\nWatch *A Record of a Mortal's Journey to Immortality*:\n\n<!-- markdownlint-disable-next-line MD013 -->\n- YouTube: [Episode 150: Overseas Turmoil 26](https://www.youtube.com/watch?v=GJgezoCBIHM \"A Record of a Mortal's Journey to Immortality — Episode 150: Overseas Turmoil 26\")\n<!-- markdownlint-disable-next-line MD013 -->\n- Bilibili: [Episode 150: Overseas Turmoil 26](https://www.bilibili.com/bangumi/play/ep1231558 \"A Record of a Mortal's Journey to Immortality — Episode 150: Overseas Turmoil 26\")\n\n## From Ideal To Real\n\nA project is more than a task list or a transcript. It is an ideal becoming\nreal through three nested worlds.\n\n**Ideal World (道心)** names the outcome worth pursuing. **Inner World (内景)**\ngives that intent shape in repository artifacts. **Outer World (现世)** asks\nwhether the shaped work is true where it must actually operate.\n\n> 道心立意,内景成形,现世验真。\n\nThe worlds nest from the inside out. Implementation contains the ideal it\nserves, and deployment contains both. This is an engineering relationship, not\njust a metaphor: when an inner world changes, conclusions from an outer world\nno longer have the same foundation and must be proven again.\n\nHumans own approval and acceptance. Agents own continuation: they read the\ncontracts, the ledger, and Git facts; preserve concurrent work; execute one\nsafe action; and reobserve only after something changes. The context belongs to\nthe project, not to a fleeting conversation.\n\n## Further Reading\n\n- [Getting Started](https://github.com/shazhou-ww/silvermoon/blob/27700c428509c3152d5eb96e90a8e1a4a150c6cb/docs/getting-started.md) — installation, configuration,\n and the first idea.\n- [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/27700c428509c3152d5eb96e90a8e1a4a150c6cb/docs/core-concepts.md) — project ownership, the three\n worlds, revisions, and decision boundaries.\n- [Operating Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/27700c428509c3152d5eb96e90a8e1a4a150c6cb/docs/operations.md) — navigation, creation,\n publication, hygiene, and continuation.\n- [Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/27700c428509c3152d5eb96e90a8e1a4a150c6cb/docs/reference.md) — storage, derived state, CLI\n reports, validation targets, and schemas.\n- [Maintaining Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/27700c428509c3152d5eb96e90a8e1a4a150c6cb/docs/maintaining.md) — development checks,\n documentation ownership, and release guidance.\n",
|
|
69
|
+
"gitHead": "9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c",
|
|
70
|
+
"readme": "<!-- markdownlint-disable-file MD041 -->\n\n<p align=\"center\">\n <!-- markdownlint-disable-next-line MD013 -->\n <img src=\"https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/assets/silvermoon.svg\" width=\"960\" alt=\"Silvermoon, the artifact spirit of the project\">\n</p>\n\n<p align=\"center\">\n <!-- markdownlint-disable-next-line MD013 -->\n <a href=\"https://npmx.dev/package/silvermoon\"><img src=\"https://img.shields.io/npm/v/silvermoon?style=flat-square\" alt=\"npm version\"></a>\n <!-- markdownlint-disable-next-line MD013 -->\n <a href=\"https://npmx.dev/package/silvermoon\"><img src=\"https://img.shields.io/node/v/silvermoon?style=flat-square\" alt=\"Node.js version\"></a>\n <!-- markdownlint-disable-next-line MD013 -->\n <a href=\"https://npmx.dev/package/silvermoon\"><img src=\"https://img.shields.io/npm/unpacked-size/silvermoon?style=flat-square\" alt=\"npm unpacked size\"></a>\n <!-- markdownlint-disable-next-line MD013 -->\n <a href=\"https://github.com/shazhou-ww/silvermoon/actions/workflows/ci.yml?query=branch%3Amain\"><img src=\"https://img.shields.io/github/actions/workflow/status/shazhou-ww/silvermoon/ci.yml?branch=main&style=flat-square&label=CI\" alt=\"CI status\"></a>\n</p>\n\n<p align=\"center\">\n English | <a href=\"https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/README.zh-CN.md\">简体中文</a>\n</p>\n\n# Silvermoon\n\n**The artifact spirit of the project.**\n\n## Quick Start\n\nSilvermoon needs Node.js 22 or newer and Git access to the repository's primary\nbranch.\n\n```sh\nnpm install --global silvermoon\nsilvermoon whats-next\nsilvermoon list-ideas\n```\n\nSilvermoon remains ecosystem-neutral when the repository root has no\n`package.json`: non-npm projects do not need a package manager, a Silvermoon\ndependency, or `node_modules`. If a root `package.json` exists, it must be valid\nJSON and declare `devDependencies.silvermoon` as exactly `^<running-version>`.\nThe report supplies a package-manager-aware repair command. After installing\nthat root dependency, register its canonical skill from\n`./node_modules/silvermoon/skills`; non-npm projects use the skill bundled with\nthe running installation. Readiness checks inspect the selected repository\nsnapshot and do not install dependencies or require `node_modules`.\n\nCreate `.silvermoon/config.yaml`:\n\n```yaml\nversion: 1\nprimaryRepository: https://github.com/example/repository.git\nprimaryBranch: main\npreferredLanguage: en\n```\n\n`preferredLanguage` is optional. A specific idea can override it with\n`language` in `status.yaml`, and `create-idea --language <tag>` can set that\noverride when the idea is created. Otherwise Silvermoon checks\n`~/.config/silvermoon/config.yaml` and finally defaults to `en-US`.\nThese stored values choose the language for project-owned content. To change\nonly one command's built-in output, use `whats-next --language en-US|zh-CN` or\n`check --language en-US|zh-CN`; the temporary override is never persisted.\n\nUse `list-ideas` when you only need the current local inventory. It defaults to\nactive ideas and supports repeatable `--state`, `--all`, literal `--query`,\nRFC 3339 `--created-since` / `--created-before`, `--sort`, and `--limit`.\nUnlike lifecycle navigation, this read-only query uses the valid worktree\nsnapshot without checking branch, upstream, cleanliness, network, or primary\nsynchronization.\n\nThen create an idea and let the registered Silvermoon skill guide one safe\nnext action at a time:\n\n```sh\nsilvermoon create-idea\nsilvermoon whats-next <idea>\nsilvermoon whats-next <idea> --language zh-CN\n```\n\nDescribe the desired world in the generated `Idea.md`, review it, and approve\nthat exact revision. From then on, Silvermoon keeps the goal, implementation,\nrepository state, and real-world result connected. See\n[Getting Started](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/getting-started.md) for the complete first workflow.\nEvery command processes one ordered, versioned domain-message stream. Add\n`--json` when a programmatic consumer needs its four deterministic projections:\n`intention`, `observation`, `actions`, and `response`. Actions are side effects\nalready attempted; future work belongs only to `response.nextSteps`. Default\nMarkdown renders only the self-contained response instead of exposing the\ninternal projections. For pre-commit validation, run\n`silvermoon check --staged`: unlike the default `check` (committed `HEAD`), it\nchecks the index and exits `0` only when the project snapshot is valid. See the\n[Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/reference.md) for validation targets and exit\ncodes.\n\nAdd `--trace <file.trace.jsonl>` to any command to write one schema-versioned\nJSONL timeline. Its `domain` channel contains allowlisted message summaries,\nand its `telemetry` channel contains paired performance spans; both share one\ntrace ID and global sequence. Trace output never includes guidance or file\ncontent, Git arguments/output, environment data, or credentials. If the\nargument does not end with the exact lowercase `.trace.jsonl` suffix,\nSilvermoon appends it. Repository-local `*.trace.jsonl` files are ignored.\nTrace files are buffered, created only after command work completes, and never\noverwrite an existing file.\n\nProjects may optionally add `.silvermoon/guidance/preparing.md`,\n`implementing.md`, and `deploying.md`. Silvermoon returns only the current\nactionable phase's snapshot-bound, repository-owned Markdown in\n`response.guidance`; `observation.guidance` carries its provenance without\ncontent. `create-idea` can return preparing guidance. This guidance is\nadditive, never a fourth contract or decision fact, and `check` validates the\ncomplete fixed directory. See [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/core-concepts.md).\n\n## Why Silvermoon\n\nLong-running Agent work usually asks people to carry too much invisible state.\nSomeone must remember what was intended, whether the code still matches the\ntask, and which conversation or machine knows the latest truth. Silvermoon\nmakes those concerns part of the project instead.\n\nIt lets people step back from continuous supervision and return at the\ndecisions that belong to them: approving the intended outcome, accepting the\nimplementation, and accepting that the result is true in the outside world.\nBetween those boundaries, an Agent can inspect repository facts and continue\nthe highest-priority safe action.\n\nIt also refuses to let a detached task card declare success. Current artifacts\nand explicit decisions jointly determine state, so a changed goal or\nimplementation naturally invalidates conclusions that depended on the older\nrevision. Because those facts live in Git, work can continue across devices,\nsessions, Agents, and hosting platforms.\n\n## The Project's Artifact Spirit\n\n> Fellow Daoist, you wouldn't want your lifebound project to be without an\n> artifact spirit, would you?\n\n<table>\n <tr>\n <td width=\"160\" align=\"center\" valign=\"top\">\n <!-- markdownlint-disable-next-line MD013 -->\n <img src=\"https://cdn.jsdelivr.net/gh/shazhou-ww/silvermoon@9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/assets/silvermoon-avatar.svg\" width=\"128\" alt=\"Line portrait of Silvermoon, the project's artifact spirit\">\n </td>\n <td valign=\"top\">\n Silvermoon is named after a character in <em>A Record of a Mortal's\n Journey to Immortality</em>. She comes from the Silvermoon Wolf Clan in\n the Spirit Realm and is one of the split souls of Ling Long. After losing\n part of her memory in the human realm, she lives as an artifact spirit\n first in a wolf-headed jade scepter and later in Han Li's Bamboo\n Cloudswarm Swords.\n <br><br>\n That image fits this project: Silvermoon does not belong to one operator\n or one chat. It lives with the project's artifacts, understands their\n state, and helps each companion find what comes next. The biography is\n inspiration, not a prerequisite for using the tool.\n </td>\n </tr>\n</table>\n\nWatch *A Record of a Mortal's Journey to Immortality*:\n\n<!-- markdownlint-disable-next-line MD013 -->\n- YouTube: [Episode 150: Overseas Turmoil 26](https://www.youtube.com/watch?v=GJgezoCBIHM \"A Record of a Mortal's Journey to Immortality — Episode 150: Overseas Turmoil 26\")\n<!-- markdownlint-disable-next-line MD013 -->\n- Bilibili: [Episode 150: Overseas Turmoil 26](https://www.bilibili.com/bangumi/play/ep1231558 \"A Record of a Mortal's Journey to Immortality — Episode 150: Overseas Turmoil 26\")\n\n## From Ideal To Real\n\nA project is more than a task list or a transcript. It is an ideal becoming\nreal through three nested worlds.\n\n**Ideal World (道心)** names the outcome worth pursuing. **Inner World (内景)**\ngives that intent shape in repository artifacts. **Outer World (现世)** asks\nwhether the shaped work is true where it must actually operate.\n\n> 道心立意,内景成形,现世验真。\n\nThe worlds nest from the inside out. Implementation contains the ideal it\nserves, and deployment contains both. This is an engineering relationship, not\njust a metaphor: when an inner world changes, conclusions from an outer world\nno longer have the same foundation and must be proven again.\n\nHumans own approval and acceptance. Agents own continuation: they read the\ncontracts, the ledger, and Git facts; preserve concurrent work; execute one\nsafe action; and reobserve only after something changes. The context belongs to\nthe project, not to a fleeting conversation.\n\n## Further Reading\n\n- [Getting Started](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/getting-started.md) — installation, configuration,\n and the first idea.\n- [Core Concepts](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/core-concepts.md) — project ownership, the three\n worlds, revisions, and decision boundaries.\n- [Operating Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/operations.md) — navigation, creation,\n publication, hygiene, and continuation.\n- [Technical Reference](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/reference.md) — storage, derived state, CLI\n reports, validation targets, and schemas.\n- [Maintaining Silvermoon](https://github.com/shazhou-ww/silvermoon/blob/9f6b61b1dfb05a0fb1e5c3eb77ad0a16105ffd0c/docs/maintaining.md) — development checks,\n documentation ownership, and release guidance.\n",
|
|
71
71
|
"readmeFilename": "README.md"
|
|
72
72
|
}
|
|
@@ -45,11 +45,17 @@ override.
|
|
|
45
45
|
|
|
46
46
|
`whats-next` may fetch and inspect but never changes files, branches, index, or
|
|
47
47
|
refs. `create-idea` requires the configured primary branch and upstream plus a
|
|
48
|
-
clean worktree; it does not fetch or compare ancestry.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
clean worktree; it does not fetch or compare ancestry. A repository without a
|
|
49
|
+
root `package.json` needs no package manager, Silvermoon dependency, or
|
|
50
|
+
`node_modules`. When the root manifest exists, it must be valid JSON and declare
|
|
51
|
+
`devDependencies.silvermoon` as exactly `^<running-version>`; follow the
|
|
52
|
+
reported package-manager-specific remediation. Register npm projects' skill
|
|
53
|
+
from `./node_modules/silvermoon/skills` after installing root dependencies.
|
|
54
|
+
Other repositories use the skill bundled with the running installation.
|
|
55
|
+
Snapshot checks do not require installed dependencies, and Silvermoon reports
|
|
56
|
+
setup problems without modifying project files or registering skills. Register
|
|
57
|
+
the canonical skill at `.agents/skills/silvermoon` with the supported
|
|
58
|
+
`npx skills add` universal target.
|
|
53
59
|
|
|
54
60
|
`check` validates only a project snapshot; it does not navigate ideas or check
|
|
55
61
|
repository synchronization. Default `check` validates committed `HEAD`;
|
|
@@ -15,10 +15,20 @@ when a programmatic consumer needs the complete
|
|
|
15
15
|
The command may fetch after local readiness passes, but it does not move the
|
|
16
16
|
worktree, index, branches, or named refs.
|
|
17
17
|
|
|
18
|
-
The target repository may use any language or build ecosystem.
|
|
19
|
-
|
|
20
|
-
`node_modules`.
|
|
21
|
-
|
|
18
|
+
The target repository may use any language or build ecosystem. If it has no
|
|
19
|
+
root `package.json`, it does not need a package manager, a Silvermoon
|
|
20
|
+
dependency, or `node_modules`. If a root manifest exists, Silvermoon requires
|
|
21
|
+
valid JSON and `devDependencies.silvermoon` exactly equal to
|
|
22
|
+
`^<running-version>`. Follow the reported package-manager-specific command.
|
|
23
|
+
After installing root dependencies, register the canonical skill from the
|
|
24
|
+
project-local package:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npx skills add ./node_modules/silvermoon/skills --skill silvermoon --agent universal --yes --copy
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Non-npm repositories register the skill bundled with their running Silvermoon
|
|
31
|
+
installation:
|
|
22
32
|
|
|
23
33
|
```sh
|
|
24
34
|
npx skills add <path-to-running-silvermoon>/skills --skill silvermoon --agent universal --yes --copy
|
package/src/adoption.js
CHANGED
|
@@ -17,6 +17,22 @@ const packageRoot = fileURLToPath(new URL("..", import.meta.url));
|
|
|
17
17
|
const packageJson = JSON.parse(await readFile(resolve(packageRoot, "package.json"), "utf8"));
|
|
18
18
|
const packagedSkillsRoot = resolve(packageRoot, "skills");
|
|
19
19
|
const packagedSkill = resolve(packagedSkillsRoot, "silvermoon");
|
|
20
|
+
const localSkillsRoot = "./node_modules/silvermoon/skills";
|
|
21
|
+
const dependencySections = [
|
|
22
|
+
"dependencies",
|
|
23
|
+
"devDependencies",
|
|
24
|
+
"peerDependencies",
|
|
25
|
+
"optionalDependencies",
|
|
26
|
+
];
|
|
27
|
+
const lockfileManagers = new Map([
|
|
28
|
+
["package-lock.json", "npm"],
|
|
29
|
+
["npm-shrinkwrap.json", "npm"],
|
|
30
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
31
|
+
["yarn.lock", "yarn"],
|
|
32
|
+
["bun.lock", "bun"],
|
|
33
|
+
["bun.lockb", "bun"],
|
|
34
|
+
]);
|
|
35
|
+
const semverPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
|
|
20
36
|
const utf8Decoder = new TextDecoder("utf-8", {
|
|
21
37
|
fatal: true,
|
|
22
38
|
ignoreBOM: true,
|
|
@@ -24,6 +40,320 @@ const utf8Decoder = new TextDecoder("utf-8", {
|
|
|
24
40
|
|
|
25
41
|
export const SILVERMOON_VERSION = packageJson.version;
|
|
26
42
|
export const REPOSITORY_SKILL_PATH = ".agents/skills/silvermoon";
|
|
43
|
+
const expectedDependency = typeof SILVERMOON_VERSION === "string"
|
|
44
|
+
&& semverPattern.test(SILVERMOON_VERSION)
|
|
45
|
+
? `^${SILVERMOON_VERSION}`
|
|
46
|
+
: null;
|
|
47
|
+
|
|
48
|
+
function isRecord(value) {
|
|
49
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isSilvermoonSourceCheckout(root, manifest) {
|
|
53
|
+
const repositoryUrl = manifest.repository?.url;
|
|
54
|
+
if (
|
|
55
|
+
resolve(root) !== resolve(packageRoot)
|
|
56
|
+
|| manifest.name !== "silvermoon"
|
|
57
|
+
|| typeof repositoryUrl !== "string"
|
|
58
|
+
) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const normalizedUrl = repositoryUrl
|
|
62
|
+
.replace(/^git\+/, "")
|
|
63
|
+
.replace(/\.git$/, "");
|
|
64
|
+
return normalizedUrl === "https://github.com/shazhou-ww/silvermoon";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function workspaceRoot(manifest) {
|
|
68
|
+
if (!Object.hasOwn(manifest, "workspaces")) return false;
|
|
69
|
+
const workspaces = manifest.workspaces;
|
|
70
|
+
const packages = Array.isArray(workspaces)
|
|
71
|
+
? workspaces
|
|
72
|
+
: isRecord(workspaces)
|
|
73
|
+
? workspaces.packages
|
|
74
|
+
: null;
|
|
75
|
+
if (!Array.isArray(packages) || !packages.every((item) => typeof item === "string")) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return packages.length > 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function packageManagerDescriptor(manager, workspace, reason = null) {
|
|
82
|
+
if (reason || !manager || workspace === null) {
|
|
83
|
+
return {
|
|
84
|
+
manager: null,
|
|
85
|
+
reason: reason ?? (workspace === null
|
|
86
|
+
? "The root workspaces field is not in a recognized form."
|
|
87
|
+
: "The package manager could not be determined safely."),
|
|
88
|
+
workspace,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
manager,
|
|
93
|
+
reason: null,
|
|
94
|
+
workspace,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function detectPackageManager(root, manifest) {
|
|
99
|
+
const workspace = workspaceRoot(manifest);
|
|
100
|
+
if (Object.hasOwn(manifest, "packageManager")) {
|
|
101
|
+
const declaration = manifest.packageManager;
|
|
102
|
+
const match = typeof declaration === "string"
|
|
103
|
+
? /^(npm|pnpm|yarn|bun)@(.+)$/.exec(declaration)
|
|
104
|
+
: null;
|
|
105
|
+
if (!match || !semverPattern.test(match[2])) {
|
|
106
|
+
return packageManagerDescriptor(
|
|
107
|
+
null,
|
|
108
|
+
workspace,
|
|
109
|
+
`The root packageManager value ${JSON.stringify(declaration)} is unknown or invalid.`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return packageManagerDescriptor(match[1], workspace);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const detected = [];
|
|
116
|
+
const uncertain = [];
|
|
117
|
+
for (const [filename, manager] of lockfileManagers) {
|
|
118
|
+
try {
|
|
119
|
+
const metadata = await lstat(resolve(root, filename));
|
|
120
|
+
if (metadata.isFile()) detected.push({ filename, manager });
|
|
121
|
+
else uncertain.push(filename);
|
|
122
|
+
} catch (caught) {
|
|
123
|
+
if (caught.code !== "ENOENT") uncertain.push(filename);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (uncertain.length > 0) {
|
|
127
|
+
return packageManagerDescriptor(
|
|
128
|
+
null,
|
|
129
|
+
workspace,
|
|
130
|
+
`Cannot safely inspect root lockfile path(s): ${uncertain.join(", ")}.`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
const managers = [...new Set(detected.map(({ manager }) => manager))];
|
|
134
|
+
if (managers.length > 1) {
|
|
135
|
+
return packageManagerDescriptor(
|
|
136
|
+
null,
|
|
137
|
+
workspace,
|
|
138
|
+
`Conflicting root lockfiles identify ${managers.join(", ")}.`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return packageManagerDescriptor(managers[0] ?? "npm", workspace);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function dependencyInstallCommand(manager, workspace, version = SILVERMOON_VERSION) {
|
|
145
|
+
const packageSpecifier = `silvermoon@^${version}`;
|
|
146
|
+
if (manager === "npm") {
|
|
147
|
+
return { executable: "npm", args: ["install", "--save-dev", packageSpecifier] };
|
|
148
|
+
}
|
|
149
|
+
if (manager === "pnpm") {
|
|
150
|
+
return {
|
|
151
|
+
executable: "pnpm",
|
|
152
|
+
args: [
|
|
153
|
+
"add",
|
|
154
|
+
"--save-dev",
|
|
155
|
+
packageSpecifier,
|
|
156
|
+
...(workspace ? ["--workspace-root"] : []),
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (manager === "yarn") {
|
|
161
|
+
return {
|
|
162
|
+
executable: "yarn",
|
|
163
|
+
args: [
|
|
164
|
+
"add",
|
|
165
|
+
packageSpecifier,
|
|
166
|
+
"--dev",
|
|
167
|
+
...(workspace ? ["--ignore-workspace-root-check"] : []),
|
|
168
|
+
],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (manager === "bun") {
|
|
172
|
+
return { executable: "bun", args: ["add", packageSpecifier, "--dev"] };
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function renderCommand({ executable, args }) {
|
|
178
|
+
return [executable, ...args]
|
|
179
|
+
.map((argument) => argument.includes("^") || argument.includes(" ")
|
|
180
|
+
? `"${argument.replaceAll('"', '\\"')}"`
|
|
181
|
+
: argument)
|
|
182
|
+
.join(" ");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function renderDependencyCommand(manager, workspace, version = SILVERMOON_VERSION) {
|
|
186
|
+
const command = dependencyInstallCommand(manager, workspace, version);
|
|
187
|
+
return command ? renderCommand(command) : null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function dependencyInstruction(packageManager) {
|
|
191
|
+
const command = packageManager.manager
|
|
192
|
+
? renderDependencyCommand(
|
|
193
|
+
packageManager.manager,
|
|
194
|
+
packageManager.workspace,
|
|
195
|
+
SILVERMOON_VERSION,
|
|
196
|
+
)
|
|
197
|
+
: null;
|
|
198
|
+
if (!expectedDependency) {
|
|
199
|
+
return `The running Silvermoon version ${JSON.stringify(SILVERMOON_VERSION)} is not a supported SemVer version; repair the package before declaring its dependency.`;
|
|
200
|
+
}
|
|
201
|
+
if (!command) {
|
|
202
|
+
return `Do not guess a package-manager command. ${packageManager.reason} Resolve the root package-manager/workspace configuration, then add silvermoon@${expectedDependency} to the root devDependencies.`;
|
|
203
|
+
}
|
|
204
|
+
return `From the repository root, run \`${command}\` to set root devDependencies.silvermoon to ${expectedDependency}.`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function skillInstruction(skill, packageManager) {
|
|
208
|
+
const registration = `npx skills add "${skill}" --skill silvermoon --agent universal --yes --copy`;
|
|
209
|
+
if (skill === packagedSkillsRoot) {
|
|
210
|
+
return `Run \`${registration}\`.`;
|
|
211
|
+
}
|
|
212
|
+
const install = packageManager.manager
|
|
213
|
+
? `run \`${packageManager.manager} install\``
|
|
214
|
+
: "install dependencies with the resolved project package manager";
|
|
215
|
+
return `After you add the required Silvermoon devDependency and ${install} at the repository root, run \`${registration}\`.`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export async function inspectNpmProject(
|
|
219
|
+
snapshotRoot,
|
|
220
|
+
repositoryRoot,
|
|
221
|
+
{ readManifest = readFile } = {},
|
|
222
|
+
) {
|
|
223
|
+
const manifestPath = resolve(snapshotRoot, "package.json");
|
|
224
|
+
let metadata;
|
|
225
|
+
try {
|
|
226
|
+
metadata = await lstat(manifestPath);
|
|
227
|
+
} catch (caught) {
|
|
228
|
+
if (caught.code === "ENOENT") return { npmProject: false, findings: [] };
|
|
229
|
+
return {
|
|
230
|
+
npmProject: true,
|
|
231
|
+
findings: [{
|
|
232
|
+
priority: 35,
|
|
233
|
+
problem: {
|
|
234
|
+
type: "npm-manifest-unreadable",
|
|
235
|
+
summary: `Cannot inspect the root package.json: ${caught.message}`,
|
|
236
|
+
},
|
|
237
|
+
instruction: "Repair access to the root package.json, then rerun Silvermoon.",
|
|
238
|
+
}],
|
|
239
|
+
manifest: null,
|
|
240
|
+
packageManager: { manager: null, reason: "The root manifest could not be read.", workspace: null },
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
if (!metadata.isFile()) {
|
|
244
|
+
return {
|
|
245
|
+
npmProject: true,
|
|
246
|
+
findings: [{
|
|
247
|
+
priority: 35,
|
|
248
|
+
problem: {
|
|
249
|
+
type: "npm-manifest-invalid",
|
|
250
|
+
summary: "The root package.json must be a regular file.",
|
|
251
|
+
},
|
|
252
|
+
instruction: "Replace the root package.json path with a readable regular JSON manifest.",
|
|
253
|
+
}],
|
|
254
|
+
manifest: null,
|
|
255
|
+
packageManager: { manager: null, reason: "The root manifest is not a regular file.", workspace: null },
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let manifest;
|
|
260
|
+
try {
|
|
261
|
+
manifest = JSON.parse(await readManifest(manifestPath, "utf8"));
|
|
262
|
+
} catch (caught) {
|
|
263
|
+
const invalidJson = caught instanceof SyntaxError;
|
|
264
|
+
return {
|
|
265
|
+
npmProject: true,
|
|
266
|
+
findings: [{
|
|
267
|
+
priority: 35,
|
|
268
|
+
problem: {
|
|
269
|
+
type: invalidJson ? "npm-manifest-invalid" : "npm-manifest-unreadable",
|
|
270
|
+
summary: invalidJson
|
|
271
|
+
? `The root package.json is not valid JSON: ${caught.message}`
|
|
272
|
+
: `Cannot read the root package.json: ${caught.message}`,
|
|
273
|
+
},
|
|
274
|
+
instruction: "Repair the root package.json as a readable JSON object, then rerun Silvermoon.",
|
|
275
|
+
}],
|
|
276
|
+
manifest: null,
|
|
277
|
+
packageManager: { manager: null, reason: "The root manifest could not be parsed.", workspace: null },
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
if (!isRecord(manifest)) {
|
|
281
|
+
return {
|
|
282
|
+
npmProject: true,
|
|
283
|
+
findings: [{
|
|
284
|
+
priority: 35,
|
|
285
|
+
problem: {
|
|
286
|
+
type: "npm-manifest-invalid",
|
|
287
|
+
summary: "The root package.json must contain a JSON object.",
|
|
288
|
+
},
|
|
289
|
+
instruction: "Replace the root package.json contents with a valid JSON object.",
|
|
290
|
+
}],
|
|
291
|
+
manifest: null,
|
|
292
|
+
packageManager: { manager: null, reason: "The root manifest is not a JSON object.", workspace: null },
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const packageManager = await detectPackageManager(snapshotRoot, manifest);
|
|
297
|
+
const findings = [];
|
|
298
|
+
const sourceCheckout = isSilvermoonSourceCheckout(repositoryRoot, manifest);
|
|
299
|
+
if (!expectedDependency) {
|
|
300
|
+
findings.push({
|
|
301
|
+
priority: 35,
|
|
302
|
+
problem: {
|
|
303
|
+
type: "npm-runtime-version-invalid",
|
|
304
|
+
summary: `The running Silvermoon version ${JSON.stringify(SILVERMOON_VERSION)} is not a supported SemVer version.`,
|
|
305
|
+
},
|
|
306
|
+
instruction: "Use a Silvermoon package with a valid SemVer version before adopting this npm project.",
|
|
307
|
+
});
|
|
308
|
+
} else if (!sourceCheckout) {
|
|
309
|
+
const declarations = dependencySections
|
|
310
|
+
.filter((section) => isRecord(manifest[section]) && Object.hasOwn(manifest[section], "silvermoon"));
|
|
311
|
+
const hasDevDependency = declarations.includes("devDependencies");
|
|
312
|
+
const actual = isRecord(manifest.devDependencies)
|
|
313
|
+
? manifest.devDependencies.silvermoon
|
|
314
|
+
: undefined;
|
|
315
|
+
if (!hasDevDependency) {
|
|
316
|
+
findings.push({
|
|
317
|
+
priority: 35,
|
|
318
|
+
problem: {
|
|
319
|
+
type: declarations.length > 0
|
|
320
|
+
? "npm-dependency-wrong-section"
|
|
321
|
+
: "npm-dependency-missing",
|
|
322
|
+
summary: declarations.length > 0
|
|
323
|
+
? "The root Silvermoon dependency must be declared in devDependencies."
|
|
324
|
+
: `The root devDependencies.silvermoon must be ${expectedDependency}.`,
|
|
325
|
+
},
|
|
326
|
+
instruction: dependencyInstruction(packageManager),
|
|
327
|
+
});
|
|
328
|
+
} else if (actual !== expectedDependency) {
|
|
329
|
+
findings.push({
|
|
330
|
+
priority: 35,
|
|
331
|
+
problem: {
|
|
332
|
+
type: "npm-dependency-version-mismatch",
|
|
333
|
+
summary: `The root devDependencies.silvermoon must be exactly ${expectedDependency}; found ${JSON.stringify(actual)}.`,
|
|
334
|
+
},
|
|
335
|
+
instruction: dependencyInstruction(packageManager),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
if (declarations.length > 1) {
|
|
339
|
+
findings.push({
|
|
340
|
+
priority: 36,
|
|
341
|
+
problem: {
|
|
342
|
+
type: "npm-dependency-duplicate",
|
|
343
|
+
summary: `Silvermoon is declared in multiple root dependency sections: ${declarations.join(", ")}.`,
|
|
344
|
+
},
|
|
345
|
+
instruction: `Remove duplicate Silvermoon declarations and keep only devDependencies.silvermoon at ${expectedDependency}.`,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
npmProject: true,
|
|
351
|
+
findings,
|
|
352
|
+
manifest,
|
|
353
|
+
packageManager,
|
|
354
|
+
sourceCheckout,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
27
357
|
|
|
28
358
|
function canonicalSkillBytes(content) {
|
|
29
359
|
if (content.includes(0)) return content;
|
|
@@ -93,9 +423,6 @@ async function inspectSkill(contentRoot) {
|
|
|
93
423
|
type: "canonical-skill-missing",
|
|
94
424
|
summary: `Missing canonical Silvermoon skill at ${REPOSITORY_SKILL_PATH}.`,
|
|
95
425
|
},
|
|
96
|
-
instruction:
|
|
97
|
-
`Run \`npx skills add "${packagedSkillsRoot}" --skill silvermoon `
|
|
98
|
-
+ "--agent universal --yes --copy`.",
|
|
99
426
|
};
|
|
100
427
|
}
|
|
101
428
|
if (!metadata.isDirectory() && !metadata.isSymbolicLink()) {
|
|
@@ -137,9 +464,6 @@ async function inspectSkill(contentRoot) {
|
|
|
137
464
|
`${REPOSITORY_SKILL_PATH} does not match the canonical skill bundled `
|
|
138
465
|
+ `with Silvermoon ${SILVERMOON_VERSION}.`,
|
|
139
466
|
},
|
|
140
|
-
instruction:
|
|
141
|
-
`Review local skill changes, then run \`npx skills add "${packagedSkillsRoot}" `
|
|
142
|
-
+ "--skill silvermoon --agent universal --yes --copy`.",
|
|
143
467
|
};
|
|
144
468
|
}
|
|
145
469
|
return { ok: true, digest: actualDigest };
|
|
@@ -180,16 +504,29 @@ export async function inspectAdoption({
|
|
|
180
504
|
});
|
|
181
505
|
}
|
|
182
506
|
|
|
507
|
+
const npm = await traceAsync(
|
|
508
|
+
"npm-project.inspect",
|
|
509
|
+
{},
|
|
510
|
+
() => inspectNpmProject(snapshotRoot, repositoryRoot),
|
|
511
|
+
);
|
|
512
|
+
findings.push(...npm.findings);
|
|
513
|
+
const skillSource = npm.npmProject && !npm.sourceCheckout
|
|
514
|
+
? localSkillsRoot
|
|
515
|
+
: packagedSkillsRoot;
|
|
183
516
|
const skill = await traceAsync(
|
|
184
517
|
"skill.inspect",
|
|
185
518
|
{},
|
|
186
|
-
() => inspectSkill(snapshotRoot),
|
|
519
|
+
() => inspectSkill(snapshotRoot, skillSource),
|
|
187
520
|
);
|
|
188
521
|
if (!skill.ok) {
|
|
189
522
|
findings.push({
|
|
190
523
|
priority: 40,
|
|
191
524
|
problem: skill.problem,
|
|
192
|
-
instruction:
|
|
525
|
+
instruction: skillInstruction(skillSource, npm.packageManager ?? {
|
|
526
|
+
manager: null,
|
|
527
|
+
reason: "The root package manager is unknown.",
|
|
528
|
+
workspace: null,
|
|
529
|
+
}),
|
|
193
530
|
});
|
|
194
531
|
}
|
|
195
532
|
findings.sort((left, right) => left.priority - right.priority);
|
package/src/response.js
CHANGED
|
@@ -297,8 +297,59 @@ function responseTitle(response) {
|
|
|
297
297
|
return localize(response.language, english, chinese);
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
function markdownTableCell(value) {
|
|
301
|
+
return String(value ?? "-")
|
|
302
|
+
.replaceAll("\\", "\\\\")
|
|
303
|
+
.replaceAll("|", "\\|")
|
|
304
|
+
.replaceAll(/\r?\n/g, " ");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function renderIdeaList(response, language) {
|
|
308
|
+
const headers = [
|
|
309
|
+
localize(language, "ID", "ID"),
|
|
310
|
+
localize(language, "Alias", "Alias"),
|
|
311
|
+
localize(language, "State", "状态"),
|
|
312
|
+
localize(language, "Created", "创建时间"),
|
|
313
|
+
localize(language, "Title", "标题"),
|
|
314
|
+
];
|
|
315
|
+
const counts = Object.entries(response.inventory.counts)
|
|
316
|
+
.map(([state, count]) => `${state}=${count}`)
|
|
317
|
+
.join(", ");
|
|
318
|
+
const lines = [
|
|
319
|
+
`${response.summary} ${localize(language, "Counts", "计数")}: ${counts}.`,
|
|
320
|
+
"",
|
|
321
|
+
`| ${headers.join(" | ")} |`,
|
|
322
|
+
`| ${headers.map(() => "---").join(" | ")} |`,
|
|
323
|
+
];
|
|
324
|
+
for (const idea of response.items) {
|
|
325
|
+
lines.push(
|
|
326
|
+
`| ${[
|
|
327
|
+
idea.id,
|
|
328
|
+
idea.alias,
|
|
329
|
+
idea.state,
|
|
330
|
+
idea.createdAt,
|
|
331
|
+
idea.title,
|
|
332
|
+
].map(markdownTableCell).join(" | ")} |`,
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
if (response.items.length === 0) {
|
|
336
|
+
lines.push(
|
|
337
|
+
"",
|
|
338
|
+
localize(language, "No ideas matched.", "没有匹配的 idea。"),
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
return lines.join("\n");
|
|
342
|
+
}
|
|
343
|
+
|
|
300
344
|
export function renderResponse(response) {
|
|
301
345
|
const language = response.language ?? DEFAULT_LANGUAGE;
|
|
346
|
+
if (response.kind === "idea-list") {
|
|
347
|
+
return [
|
|
348
|
+
`## ${responseTitle(response)}`,
|
|
349
|
+
"",
|
|
350
|
+
renderIdeaList(response, language),
|
|
351
|
+
].join("\n");
|
|
352
|
+
}
|
|
302
353
|
const lines = [`## ${responseTitle(response)}`, "", response.summary];
|
|
303
354
|
|
|
304
355
|
if (response.inventory) {
|