pi-shepherd 0.1.1 → 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.
Files changed (58) hide show
  1. package/README.en.md +206 -83
  2. package/README.md +209 -88
  3. package/index.ts +262 -229
  4. package/package.json +60 -58
  5. package/rules.json +197 -218
  6. package/shepherd/compaction.ts +76 -0
  7. package/shepherd/conditions.ts +98 -0
  8. package/shepherd/ephemeral.ts +55 -52
  9. package/shepherd/git.ts +64 -0
  10. package/shepherd/index.ts +14 -0
  11. package/shepherd/line-count.ts +86 -86
  12. package/shepherd/message-end.ts +120 -0
  13. package/shepherd/rules-editor.ts +250 -135
  14. package/shepherd/rules-tool-helpers.ts +119 -0
  15. package/shepherd/rules-tool-list.ts +124 -0
  16. package/shepherd/rules-tool.ts +182 -99
  17. package/shepherd/rules-validate.ts +89 -44
  18. package/shepherd/rules.ts +366 -283
  19. package/shepherd/tool-event-types.ts +27 -14
  20. package/shepherd/tool-hooks.ts +165 -176
  21. package/shepherd/worktree-check.ts +130 -130
  22. package/tsconfig.json +21 -14
  23. package/node_modules/@pi-atelier/shared-utils/README.en.md +0 -182
  24. package/node_modules/@pi-atelier/shared-utils/README.md +0 -182
  25. package/node_modules/@pi-atelier/shared-utils/package.json +0 -51
  26. package/node_modules/@pi-atelier/shared-utils/src/__tests__/agents.test.ts +0 -120
  27. package/node_modules/@pi-atelier/shared-utils/src/__tests__/ephemeral.test.ts +0 -100
  28. package/node_modules/@pi-atelier/shared-utils/src/__tests__/file-lock.test.ts +0 -152
  29. package/node_modules/@pi-atelier/shared-utils/src/__tests__/filter-match.test.ts +0 -187
  30. package/node_modules/@pi-atelier/shared-utils/src/__tests__/memory-parser.test.ts +0 -170
  31. package/node_modules/@pi-atelier/shared-utils/src/__tests__/paths.test.ts +0 -126
  32. package/node_modules/@pi-atelier/shared-utils/src/__tests__/project-config-edge.test.ts +0 -138
  33. package/node_modules/@pi-atelier/shared-utils/src/__tests__/project-config.test.ts +0 -257
  34. package/node_modules/@pi-atelier/shared-utils/src/__tests__/project-tools-mcp.test.ts +0 -189
  35. package/node_modules/@pi-atelier/shared-utils/src/__tests__/project-tools.test.ts +0 -204
  36. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-backup-advanced.test.ts +0 -269
  37. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-backup-array.test.ts +0 -267
  38. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-backup.test.ts +0 -520
  39. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-read.test.ts +0 -116
  40. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-write.test.ts +0 -119
  41. package/node_modules/@pi-atelier/shared-utils/src/__tests__/tool-output.test.ts +0 -145
  42. package/node_modules/@pi-atelier/shared-utils/src/agents.ts +0 -39
  43. package/node_modules/@pi-atelier/shared-utils/src/ephemeral.ts +0 -42
  44. package/node_modules/@pi-atelier/shared-utils/src/file-lock.ts +0 -62
  45. package/node_modules/@pi-atelier/shared-utils/src/filter-match.ts +0 -100
  46. package/node_modules/@pi-atelier/shared-utils/src/index.ts +0 -71
  47. package/node_modules/@pi-atelier/shared-utils/src/memory-parser.ts +0 -96
  48. package/node_modules/@pi-atelier/shared-utils/src/paths.ts +0 -23
  49. package/node_modules/@pi-atelier/shared-utils/src/project-config.ts +0 -241
  50. package/node_modules/@pi-atelier/shared-utils/src/project-tools.ts +0 -191
  51. package/node_modules/@pi-atelier/shared-utils/src/settings-array.ts +0 -73
  52. package/node_modules/@pi-atelier/shared-utils/src/settings-backup-rollback.ts +0 -104
  53. package/node_modules/@pi-atelier/shared-utils/src/settings-backup-utils.ts +0 -75
  54. package/node_modules/@pi-atelier/shared-utils/src/settings-backup.ts +0 -172
  55. package/node_modules/@pi-atelier/shared-utils/src/settings.ts +0 -104
  56. package/node_modules/@pi-atelier/shared-utils/src/tool-output.ts +0 -149
  57. package/node_modules/@pi-atelier/shared-utils/tsconfig.json +0 -9
  58. package/node_modules/@pi-atelier/shared-utils/vitest.config.ts +0 -24
@@ -1,267 +0,0 @@
1
- /**
2
- * patchSettingsSectionWithBackup 数组操作测试
3
- *
4
- * 测试 packages/extensions/skills 等数组类型 section 的增删改操作。
5
- */
6
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
7
- import { tmpdir } from "node:os";
8
- import { join } from "node:path";
9
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
10
-
11
- const TEST_DIR = join(tmpdir(), `pi-settings-array-test-${process.pid}`);
12
- const TEST_SETTINGS = join(TEST_DIR, "settings.json");
13
- const TEST_BACKUP_DIR = join(TEST_DIR, "settings-backup");
14
-
15
- const { patchSettingsSectionWithBackup } = await import("../settings-backup");
16
-
17
- function writeTestSettings(content: Record<string, any>) {
18
- mkdirSync(TEST_DIR, { recursive: true });
19
- writeFileSync(TEST_SETTINGS, JSON.stringify(content, null, "\t"));
20
- }
21
-
22
- function readTestSettings(): Record<string, any> {
23
- return JSON.parse(readFileSync(TEST_SETTINGS, "utf-8"));
24
- }
25
-
26
- beforeEach(() => {
27
- mkdirSync(TEST_DIR, { recursive: true });
28
- mkdirSync(TEST_BACKUP_DIR, { recursive: true });
29
- });
30
-
31
- afterEach(() => {
32
- rmSync(TEST_DIR, { recursive: true, force: true });
33
- });
34
-
35
- // ── addItem:向数组添加元素 ─────────────────────────────
36
-
37
- describe("patchSettingsSectionWithBackup — 数组 addItem", () => {
38
- it("应向空数组添加元素", () => {
39
- writeTestSettings({ packages: [] });
40
-
41
- const result = patchSettingsSectionWithBackup(
42
- "packages",
43
- { addItem: { source: "git:github.com/catlain/pi-foo" } },
44
- [],
45
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
46
- );
47
-
48
- expect(result.config).toEqual([{ source: "git:github.com/catlain/pi-foo" }]);
49
- const written = readTestSettings();
50
- expect(written.packages).toEqual([{ source: "git:github.com/catlain/pi-foo" }]);
51
- });
52
-
53
- it("应向已有数组追加元素", () => {
54
- writeTestSettings({
55
- packages: [{ source: "git:github.com/catlain/pi-shepherd" }],
56
- });
57
-
58
- patchSettingsSectionWithBackup(
59
- "packages",
60
- { addItem: { source: "git:github.com/catlain/pi-foo" } },
61
- [],
62
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
63
- );
64
-
65
- const written = readTestSettings();
66
- expect(written.packages).toHaveLength(2);
67
- expect(written.packages[1]).toEqual({ source: "git:github.com/catlain/pi-foo" });
68
- });
69
-
70
- it("addItem 为对象时用 source 字段去重,已存在则不重复添加", () => {
71
- writeTestSettings({
72
- packages: [{ source: "git:github.com/catlain/pi-shepherd" }],
73
- });
74
-
75
- const result = patchSettingsSectionWithBackup(
76
- "packages",
77
- { addItem: { source: "git:github.com/catlain/pi-shepherd" } },
78
- [],
79
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
80
- );
81
-
82
- expect(result.config).toHaveLength(1);
83
- });
84
-
85
- it("addItem 为字符串时精确去重", () => {
86
- writeTestSettings({
87
- packages: ["npm:pi-tool-display"],
88
- });
89
-
90
- const result = patchSettingsSectionWithBackup(
91
- "packages",
92
- { addItem: "npm:pi-tool-display" },
93
- [],
94
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
95
- );
96
-
97
- expect(result.config).toHaveLength(1);
98
- });
99
- });
100
-
101
- // ── removeItem:从数组删除元素 ─────────────────────────────
102
-
103
- describe("patchSettingsSectionWithBackup — 数组 removeItem", () => {
104
- it("应按 source 字段匹配删除对象元素", () => {
105
- writeTestSettings({
106
- packages: [
107
- { source: "git:github.com/catlain/pi-shepherd" },
108
- { source: "git:github.com/catlain/pi-foo" },
109
- ],
110
- });
111
-
112
- const result = patchSettingsSectionWithBackup(
113
- "packages",
114
- { removeItem: { source: "git:github.com/catlain/pi-foo" } },
115
- [],
116
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
117
- );
118
-
119
- expect(result.config).toEqual([{ source: "git:github.com/catlain/pi-shepherd" }]);
120
- });
121
-
122
- it("应按字符串精确匹配删除", () => {
123
- writeTestSettings({
124
- packages: ["npm:pi-tool-display", "npm:pi-foo"],
125
- });
126
-
127
- const result = patchSettingsSectionWithBackup(
128
- "packages",
129
- { removeItem: "npm:pi-foo" },
130
- [],
131
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
132
- );
133
-
134
- expect(result.config).toEqual(["npm:pi-tool-display"]);
135
- });
136
-
137
- it("removeItem 匹配不到时不应修改数组", () => {
138
- writeTestSettings({
139
- packages: [{ source: "git:github.com/catlain/pi-shepherd" }],
140
- });
141
-
142
- const result = patchSettingsSectionWithBackup(
143
- "packages",
144
- { removeItem: { source: "git:github.com/catlain/nonexistent" } },
145
- [],
146
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
147
- );
148
-
149
- expect(result.config).toHaveLength(1);
150
- });
151
- });
152
-
153
- // ── replaceItem:替换数组中匹配的元素 ─────────────────────────
154
-
155
- describe("patchSettingsSectionWithBackup — 数组 replaceItem", () => {
156
- it("应按 source 匹配并替换整个对象元素", () => {
157
- writeTestSettings({
158
- packages: [
159
- { source: "git:github.com/catlain/pi-usage-stats" }, // 旧格式(字符串被修前)
160
- ],
161
- });
162
-
163
- const result = patchSettingsSectionWithBackup(
164
- "packages",
165
- {
166
- replaceItem: {
167
- match: { source: "git:github.com/catlain/pi-usage-stats" },
168
- replacement: { source: "git:github.com/catlain/pi-usage-stats", extensions: ["+index.ts"] },
169
- },
170
- },
171
- [],
172
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
173
- );
174
-
175
- expect(result.config).toEqual([
176
- { source: "git:github.com/catlain/pi-usage-stats", extensions: ["+index.ts"] },
177
- ]);
178
- });
179
-
180
- it("应按字符串精确匹配并替换", () => {
181
- writeTestSettings({
182
- packages: ["npm:old-package"],
183
- });
184
-
185
- const result = patchSettingsSectionWithBackup(
186
- "packages",
187
- {
188
- replaceItem: {
189
- match: "npm:old-package",
190
- replacement: { source: "npm:new-package" },
191
- },
192
- },
193
- [],
194
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
195
- );
196
-
197
- expect(result.config).toEqual([{ source: "npm:new-package" }]);
198
- });
199
-
200
- it("replaceItem 匹配不到时不应修改数组", () => {
201
- writeTestSettings({
202
- packages: [{ source: "git:github.com/catlain/pi-shepherd" }],
203
- });
204
-
205
- const result = patchSettingsSectionWithBackup(
206
- "packages",
207
- {
208
- replaceItem: {
209
- match: { source: "git:github.com/catlain/nonexistent" },
210
- replacement: { source: "git:github.com/catlain/something" },
211
- },
212
- },
213
- [],
214
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
215
- );
216
-
217
- expect(result.config).toEqual([{ source: "git:github.com/catlain/pi-shepherd" }]);
218
- });
219
- });
220
-
221
- // ── 创建备份 ─────────────────────────────────────────
222
-
223
- describe("patchSettingsSectionWithBackup — 数组操作应创建备份", () => {
224
- it("addItem 应创建备份", () => {
225
- writeTestSettings({ packages: [] });
226
-
227
- const result = patchSettingsSectionWithBackup(
228
- "packages",
229
- { addItem: { source: "git:github.com/catlain/pi-foo" } },
230
- [],
231
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR },
232
- );
233
-
234
- expect(result.backupPath).toBeDefined();
235
- expect(existsSync(result.backupPath!)).toBe(true);
236
- });
237
-
238
- it("removeItem 应创建备份", () => {
239
- writeTestSettings({ packages: [{ source: "git:github.com/catlain/pi-foo" }] });
240
-
241
- const result = patchSettingsSectionWithBackup(
242
- "packages",
243
- { removeItem: { source: "git:github.com/catlain/pi-foo" } },
244
- [],
245
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR },
246
- );
247
-
248
- expect(result.backupPath).toBeDefined();
249
- });
250
- });
251
-
252
- // ── 对象 section 保持不变 ─────────────────────────────────
253
-
254
- describe("patchSettingsSectionWithBackup — 对象操作不受影响", () => {
255
- it("对象 merge 仍然正常工作", () => {
256
- writeTestSettings({ context: { distillThreshold: 5000 } });
257
-
258
- const result = patchSettingsSectionWithBackup(
259
- "context",
260
- { processorThreshold: 3000 },
261
- { distillThreshold: 8000, processorThreshold: 5000 },
262
- { settingsPath: TEST_SETTINGS, backupDir: TEST_BACKUP_DIR, validate: false },
263
- );
264
-
265
- expect(result.config).toEqual({ distillThreshold: 5000, processorThreshold: 3000 });
266
- });
267
- });