pi-harness-runtime 1.1.422 → 1.1.423

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 (134) hide show
  1. package/index.ts +32 -1
  2. package/package.json +1 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/connection-runtime/package.json +1 -1
  21. package/packages/context-compiler/package.json +1 -1
  22. package/packages/context-discovery/package.json +1 -1
  23. package/packages/context-manager/package.json +1 -1
  24. package/packages/contracts/package.json +1 -1
  25. package/packages/cookie-sanitizer/package.json +1 -1
  26. package/packages/cost-optimizer/package.json +1 -1
  27. package/packages/dependency-analyzer/package.json +1 -1
  28. package/packages/django-plugin/package.json +1 -1
  29. package/packages/doc-generator/package.json +1 -1
  30. package/packages/durable-review/package.json +1 -1
  31. package/packages/e2e-tester/package.json +1 -1
  32. package/packages/evaluation-engine/package.json +1 -1
  33. package/packages/evaluation-runner/package.json +1 -1
  34. package/packages/event-bus/package.json +1 -1
  35. package/packages/event-sink/package.json +1 -1
  36. package/packages/event-store/package.json +1 -1
  37. package/packages/experience-replay/package.json +1 -1
  38. package/packages/feedback-collector/package.json +1 -1
  39. package/packages/file-copy-helper/package.json +1 -1
  40. package/packages/framework-detector/package.json +1 -1
  41. package/packages/framework-plugin-sdk/package.json +1 -1
  42. package/packages/frappe-plugin/package.json +1 -1
  43. package/packages/generic-web-plugin/package.json +1 -1
  44. package/packages/health-monitor/package.json +1 -1
  45. package/packages/intent-analyzer/package.json +1 -1
  46. package/packages/jev-judge/package.json +1 -1
  47. package/packages/knowledge-graph/package.json +1 -1
  48. package/packages/knowledge-retrieval/package.json +1 -1
  49. package/packages/laravel-plugin/package.json +1 -1
  50. package/packages/learning-engine/package.json +1 -1
  51. package/packages/logging/package.json +1 -1
  52. package/packages/mcp-adapter/package.json +1 -1
  53. package/packages/memory-engine/package.json +1 -1
  54. package/packages/milestone-manager/package.json +1 -1
  55. package/packages/model-registry/package.json +1 -1
  56. package/packages/nextjs-plugin/package.json +1 -1
  57. package/packages/notification/package.json +1 -1
  58. package/packages/observability/package.json +1 -1
  59. package/packages/okf-indexer/package.json +1 -1
  60. package/packages/paperclip-integration/package.json +1 -1
  61. package/packages/performance-optimizer/package.json +1 -1
  62. package/packages/permission-mode/package.json +1 -1
  63. package/packages/privilege-broker/package.json +1 -1
  64. package/packages/project-analyzer/package.json +1 -1
  65. package/packages/project-bootstrap/package.json +1 -1
  66. package/packages/projection-engine/package.json +1 -1
  67. package/packages/prompt-compiler/package.json +1 -1
  68. package/packages/prompt-versioning/package.json +1 -1
  69. package/packages/provider-adapter-sdk/package.json +1 -1
  70. package/packages/provider-router/package.json +1 -1
  71. package/packages/provider-selector/package.json +1 -1
  72. package/packages/providers/package.json +1 -1
  73. package/packages/qdrant-skills/package.json +1 -1
  74. package/packages/quota-manager/package.json +1 -1
  75. package/packages/rate-limiter/package.json +1 -1
  76. package/packages/react-vite-plugin/package.json +1 -1
  77. package/packages/relay/package.json +1 -1
  78. package/packages/release-manager/package.json +1 -1
  79. package/packages/requirement-compiler/package.json +1 -1
  80. package/packages/runtime/package.json +1 -1
  81. package/packages/scheduler/package.json +1 -1
  82. package/packages/scheduler-adapter/package.json +1 -1
  83. package/packages/session/package.json +1 -1
  84. package/packages/session-api/package.json +1 -1
  85. package/packages/session-export/package.json +1 -1
  86. package/packages/shared-context/package.json +1 -1
  87. package/packages/skill-mcp-client/package.json +1 -1
  88. package/packages/skill-registry/package.json +1 -1
  89. package/packages/skills/dist/index.d.ts.map +1 -1
  90. package/packages/skills/dist/index.js +12 -4
  91. package/packages/skills/dist/index.js.map +1 -1
  92. package/packages/skills/dist/sanitize-rules.d.ts +93 -0
  93. package/packages/skills/dist/sanitize-rules.d.ts.map +1 -0
  94. package/packages/skills/dist/sanitize-rules.js +173 -0
  95. package/packages/skills/dist/sanitize-rules.js.map +1 -0
  96. package/packages/skills/dist/sanitize.d.ts +13 -2
  97. package/packages/skills/dist/sanitize.d.ts.map +1 -1
  98. package/packages/skills/dist/sanitize.js +14 -48
  99. package/packages/skills/dist/sanitize.js.map +1 -1
  100. package/packages/skills/dist/vector-schema.d.ts +7 -1
  101. package/packages/skills/dist/vector-schema.d.ts.map +1 -1
  102. package/packages/skills/dist/vector-schema.js +10 -5
  103. package/packages/skills/dist/vector-schema.js.map +1 -1
  104. package/packages/skills/package.json +1 -1
  105. package/packages/skills/src/index.ts +12 -5
  106. package/packages/skills/src/sanitize-rules.ts +218 -0
  107. package/packages/skills/src/sanitize.ts +26 -56
  108. package/packages/skills/src/vector-schema.ts +22 -14
  109. package/packages/skills/test/remote-skills.test.ts +33 -6
  110. package/packages/skills/test/sanitize.test.ts +163 -44
  111. package/packages/sprint-planner/package.json +1 -1
  112. package/packages/subscription-engine/package.json +1 -1
  113. package/packages/task-compiler/package.json +1 -1
  114. package/packages/telegram/package.json +1 -1
  115. package/packages/tencentdb-memory/package.json +1 -1
  116. package/packages/tencentdb-sync/package.json +1 -1
  117. package/packages/test-data-generator/package.json +1 -1
  118. package/packages/test-generator/package.json +1 -1
  119. package/packages/todo-bd-sync/package.json +1 -1
  120. package/packages/token-estimation/package.json +1 -1
  121. package/packages/token-optimizer/package.json +1 -1
  122. package/packages/tooling-preference/package.json +1 -1
  123. package/packages/trajectory/package.json +1 -1
  124. package/packages/tui/package.json +1 -1
  125. package/packages/two-phase-review/package.json +1 -1
  126. package/packages/types/package.json +1 -1
  127. package/packages/voice-api/package.json +1 -1
  128. package/packages/webmcp-tester/package.json +1 -1
  129. package/packages/wiki/package.json +1 -1
  130. package/packages/workflow-events/package.json +1 -1
  131. package/packages/workspace-scanner/package.json +1 -1
  132. package/packages/worktree/package.json +1 -1
  133. package/packages/write-review/package.json +1 -1
  134. package/scripts/sync-to-qdrant.ts +5 -9
@@ -13,15 +13,40 @@
13
13
  * network nor replaces a process-global module.
14
14
  */
15
15
 
16
- import { describe, expect, it } from "bun:test";
16
+ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
17
+ import { fileURLToPath } from "node:url";
17
18
 
18
19
  import {
19
20
  createSkillRegistry,
20
21
  type QdrantClientLike,
21
22
  type QdrantEmbedderLike,
22
23
  } from "../src/loader.js";
24
+ import { resetSanitizationRulesCache } from "../src/sanitize-rules.js";
23
25
  import type { Skill } from "../src/types.js";
24
26
 
27
+ /**
28
+ * The rules of the test.
29
+ *
30
+ * The rules of a client live in a local file, outside the repository. The
31
+ * test uses a fixture with made up names, so that the result of the test
32
+ * does not depend on the machine it runs on.
33
+ */
34
+ const FIXTURE = fileURLToPath(
35
+ new URL("./fixtures/sanitization.json", import.meta.url),
36
+ );
37
+ const originalEnv = process.env.PI_SKILL_SANITIZATION_FILE;
38
+
39
+ beforeEach(() => {
40
+ process.env.PI_SKILL_SANITIZATION_FILE = FIXTURE;
41
+ resetSanitizationRulesCache();
42
+ });
43
+
44
+ afterEach(() => {
45
+ resetSanitizationRulesCache();
46
+ if (originalEnv === undefined) delete process.env.PI_SKILL_SANITIZATION_FILE;
47
+ else process.env.PI_SKILL_SANITIZATION_FILE = originalEnv;
48
+ });
49
+
25
50
  /** A point of the vector store. */
26
51
  interface VectorPoint {
27
52
  id: number;
@@ -188,7 +213,7 @@ describe("a hit that carries the shared name of a local skill", () => {
188
213
  embedder: fakeEmbedder,
189
214
  collection: "pi-harness-skills-test",
190
215
  });
191
- registry.register(localSkill("inpac-pe-approval-workflow"));
216
+ registry.register(localSkill("acmecorp-pe-approval-workflow"));
192
217
 
193
218
  const results = await registry.findVector("workflow");
194
219
 
@@ -196,16 +221,18 @@ describe("a hit that carries the shared name of a local skill", () => {
196
221
  // The skill of this machine stays a local skill - with its own
197
222
  // name and its own body, not the sanitized copy.
198
223
  expect(results[0].source).toBe("local");
199
- expect(results[0].skill.id).toBe("inpac-pe-approval-workflow");
200
- expect(results[0].skill.body).toBe("the local body of inpac-pe-approval-workflow");
224
+ expect(results[0].skill.id).toBe("acmecorp-pe-approval-workflow");
225
+ expect(results[0].skill.body).toBe(
226
+ "the local body of acmecorp-pe-approval-workflow",
227
+ );
201
228
  });
202
229
 
203
230
  it("resolves the shared name of a local skill in a lookup", () => {
204
231
  const registry = createSkillRegistry({ indexer: null });
205
- registry.register(localSkill("inpac-pe-approval-workflow"));
232
+ registry.register(localSkill("acmecorp-pe-approval-workflow"));
206
233
 
207
234
  expect(registry.get("client-a-pe-approval-workflow")?.id).toBe(
208
- "inpac-pe-approval-workflow",
235
+ "acmecorp-pe-approval-workflow",
209
236
  );
210
237
  });
211
238
  });
@@ -5,29 +5,81 @@
5
5
  * into it. The rules used to live in `scripts/sync-to-qdrant.ts` - the
6
6
  * only writer that could reach them. The runtime registry writes into the
7
7
  * same collection, so it needs them too.
8
+ *
9
+ * The rules of the clients are **not** in this repository: it is public,
10
+ * and a list of client names in it is a list of client names in the world.
11
+ * They live in a local file, so the tests use a fixture with made up
12
+ * names and pass the rules in. A test must never read the rules of the
13
+ * machine it runs on - that would make the result of the test depend on
14
+ * the machine.
8
15
  */
9
16
 
10
- import { describe, expect, test } from "bun:test";
17
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
18
+ import { mkdtempSync, writeFileSync } from "node:fs";
19
+ import { tmpdir } from "node:os";
20
+ import { join } from "node:path";
21
+ import { fileURLToPath } from "node:url";
11
22
 
12
23
  import { sanitizeText, skillPointName } from "../src/sanitize.js";
24
+ import {
25
+ loadSanitizationRules,
26
+ resetSanitizationRulesCache,
27
+ sanitizationStatus,
28
+ } from "../src/sanitize-rules.js";
13
29
  import { skillPointPayload } from "../src/vector-schema.js";
14
30
 
31
+ const FIXTURE = fileURLToPath(new URL("./fixtures/sanitization.json", import.meta.url));
32
+ const MISSING = join(tmpdir(), "pi-harness-no-such-sanitization-file.json");
33
+
34
+ const originalEnv = process.env.PI_SKILL_SANITIZATION_FILE;
35
+
36
+ /** The rules of the fixture, as the mechanism reads them. */
37
+ function fixtureRules() {
38
+ resetSanitizationRulesCache();
39
+ process.env.PI_SKILL_SANITIZATION_FILE = FIXTURE;
40
+ const set = loadSanitizationRules();
41
+ resetSanitizationRulesCache();
42
+ return set;
43
+ }
44
+
45
+ beforeEach(() => {
46
+ resetSanitizationRulesCache();
47
+ process.env.PI_SKILL_SANITIZATION_FILE = FIXTURE;
48
+ });
49
+
50
+ afterEach(() => {
51
+ resetSanitizationRulesCache();
52
+ if (originalEnv === undefined) delete process.env.PI_SKILL_SANITIZATION_FILE;
53
+ else process.env.PI_SKILL_SANITIZATION_FILE = originalEnv;
54
+ });
55
+
15
56
  describe("the sanitization of a text", () => {
16
57
  test("the name of a client is replaced", () => {
17
- expect(sanitizeText("the inpac workflow")).toBe("the [Client A] workflow");
18
- expect(sanitizeText("for digisoft")).toBe("for [Client B]");
19
- expect(sanitizeText("gse-insurance step")).toBe("[Insurance Project] step");
58
+ const rules = fixtureRules();
59
+ expect(sanitizeText("the acmecorp workflow", rules)).toBe(
60
+ "the [Client A] workflow",
61
+ );
62
+ expect(sanitizeText("for globex", rules)).toBe("for [Client B]");
63
+ expect(sanitizeText("initech step", rules)).toBe("[Insurance Project] step");
20
64
  });
21
65
 
22
66
  test("the name of a client is replaced in any case", () => {
23
- expect(sanitizeText("INPAC and Inpac")).toBe("[Client A] and [Client A]");
67
+ const rules = fixtureRules();
68
+ expect(sanitizeText("ACMEcorp and Acmecorp", rules)).toBe(
69
+ "[Client A] and [Client A]",
70
+ );
24
71
  });
25
72
 
26
73
  test("a text without a client name stays the same", () => {
27
- expect(sanitizeText("a plain skill")).toBe("a plain skill");
74
+ const rules = fixtureRules();
75
+ expect(sanitizeText("a plain skill", rules)).toBe("a plain skill");
28
76
  });
29
77
 
30
78
  test("the generic phrases are replaced", () => {
79
+ // The generic rules hold for every installation, so they work
80
+ // without a local file.
81
+ process.env.PI_SKILL_SANITIZATION_FILE = MISSING;
82
+ resetSanitizationRulesCache();
31
83
  expect(sanitizeText("Company Name")).toBe("[Client Name]");
32
84
  expect(sanitizeText("your company")).toBe("[Client]");
33
85
  expect(sanitizeText("our client")).toBe("[Client]");
@@ -37,46 +89,53 @@ describe("the sanitization of a text", () => {
37
89
  // The name of a client rarely stands alone. A word boundary would
38
90
  // miss every one of these - and an underscore is a word character,
39
91
  // so a boundary never follows one.
40
- expect(sanitizeText("inpac_pharma")).toBe("[Client A]_pharma");
41
- expect(sanitizeText("th_digisoft_erp_chart.json")).toBe(
92
+ const rules = fixtureRules();
93
+ expect(sanitizeText("acmecorp_pharma", rules)).toBe("[Client A]_pharma");
94
+ expect(sanitizeText("th_globex_erp_chart.json", rules)).toBe(
42
95
  "th_[Client B]_erp_chart.json",
43
96
  );
44
- expect(sanitizeText("apps/inpac_pharma/setup")).toBe(
97
+ expect(sanitizeText("apps/acmecorp_pharma/setup", rules)).toBe(
45
98
  "apps/[Client A]_pharma/setup",
46
99
  );
47
100
  });
48
101
 
49
102
  test("a client name in a sentence is replaced", () => {
50
- expect(sanitizeText("integrated with Paperclip")).toBe(
51
- "integrated with [Client D]",
103
+ const rules = fixtureRules();
104
+ expect(sanitizeText("integrated with Globex", rules)).toBe(
105
+ "integrated with [Client B]",
106
+ );
107
+ expect(sanitizeText("Initech secrets", rules)).toBe(
108
+ "[Insurance Project] secrets",
52
109
  );
53
- expect(sanitizeText("OpenClaw secrets")).toBe("[Client F] secrets");
54
110
  });
55
111
  });
56
112
 
57
113
  describe("the name of a skill in the shared collection", () => {
58
114
  test("a name of the map is renamed", () => {
59
- expect(skillPointName("inpac-pe-approval-workflow")).toBe(
115
+ const rules = fixtureRules();
116
+ expect(skillPointName("acmecorp-pe-approval-workflow", rules)).toBe(
60
117
  "client-a-pe-approval-workflow",
61
118
  );
62
- expect(skillPointName("tbs-import-clearance-lcv")).toBe(
63
- "import-project-clearance",
119
+ expect(skillPointName("initech-step5-slot-filter", rules)).toBe(
120
+ "insurance-project-step-filter",
64
121
  );
65
- expect(skillPointName("whispertool")).toBe("whisper-tool");
66
122
  });
67
123
 
68
124
  test("a name without a client name stays the same", () => {
69
- expect(skillPointName("code-review")).toBe("code-review");
125
+ const rules = fixtureRules();
126
+ expect(skillPointName("code-review", rules)).toBe("code-review");
70
127
  });
71
128
 
72
129
  test("the name never carries a client name", () => {
130
+ const rules = fixtureRules();
73
131
  for (const name of [
74
- "inpac-pe-approval-workflow",
75
- "gse-insurance-step5-slot-filter",
76
- "tbs-import-clearance-lcv",
77
- "whispertool",
132
+ "acmecorp-pe-approval-workflow",
133
+ "initech-step5-slot-filter",
134
+ "globex-channel",
78
135
  ]) {
79
- expect(skillPointName(name)).not.toMatch(/inpac|gse-insurance|tbs-import/i);
136
+ expect(skillPointName(name, rules)).not.toMatch(
137
+ /acmecorp|initech|globex/i,
138
+ );
80
139
  }
81
140
  });
82
141
 
@@ -84,48 +143,108 @@ describe("the name of a skill in the shared collection", () => {
84
143
  // The name of a point is the id of the skill that a search returns,
85
144
  // so it has to be a name that a skill may carry.
86
145
  const NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
146
+ const rules = fixtureRules();
87
147
  for (const name of [
88
- "inpac-pe-approval-workflow",
89
- "openclaw-channel",
90
- "gse-insurance-step5-slot-filter",
91
- "tbs-import-clearance-lcv",
92
- "whispertool",
148
+ "acmecorp-pe-approval-workflow",
149
+ "globex-channel",
150
+ "initech-step5-slot-filter",
93
151
  "code-review",
94
152
  ]) {
95
- expect(skillPointName(name)).toMatch(NAME);
153
+ expect(skillPointName(name, rules)).toMatch(NAME);
96
154
  }
97
155
  });
98
156
 
99
157
  test("a name that needs no sanitization stays the same", () => {
100
- expect(skillPointName("code-review")).toBe("code-review");
101
- expect(skillPointName("frappe-doctype-field-order")).toBe(
158
+ const rules = fixtureRules();
159
+ expect(skillPointName("code-review", rules)).toBe("code-review");
160
+ expect(skillPointName("frappe-doctype-field-order", rules)).toBe(
102
161
  "frappe-doctype-field-order",
103
162
  );
104
163
  });
105
164
  });
106
165
 
166
+ describe("the rules of the installation", () => {
167
+ test("a missing file leaves the generic rules", () => {
168
+ process.env.PI_SKILL_SANITIZATION_FILE = MISSING;
169
+ resetSanitizationRulesCache();
170
+ const status = sanitizationStatus();
171
+ expect(status.configured).toBe(false);
172
+ expect(status.ruleCount).toBeGreaterThan(0);
173
+ });
174
+
175
+ test("a broken file is reported and does not take the rules down", () => {
176
+ const dir = mkdtempSync(join(tmpdir(), "pi-harness-sanitization-"));
177
+ const path = join(dir, "rules.json");
178
+ writeFileSync(path, "{ not json");
179
+ process.env.PI_SKILL_SANITIZATION_FILE = path;
180
+ resetSanitizationRulesCache();
181
+ const status = sanitizationStatus();
182
+ expect(status.configured).toBe(false);
183
+ expect(status.error).toContain("not valid JSON");
184
+ // The generic rules still hold.
185
+ expect(sanitizeText("Company Name")).toBe("[Client Name]");
186
+ });
187
+
188
+ test("a broken pattern does not take the other rules down", () => {
189
+ const dir = mkdtempSync(join(tmpdir(), "pi-harness-sanitization-"));
190
+ const path = join(dir, "rules.json");
191
+ writeFileSync(
192
+ path,
193
+ JSON.stringify({
194
+ rules: [
195
+ { pattern: "acmecorp", replacement: "[Client A]" },
196
+ { pattern: "([unclosed", replacement: "[Broken]" },
197
+ { pattern: "globex", replacement: "[Client B]" },
198
+ ],
199
+ }),
200
+ );
201
+ process.env.PI_SKILL_SANITIZATION_FILE = path;
202
+ resetSanitizationRulesCache();
203
+ const rules = loadSanitizationRules();
204
+ expect(sanitizeText("acmecorp and globex", rules)).toBe(
205
+ "[Client A] and [Client B]",
206
+ );
207
+ });
208
+
209
+ test("the local file is read once", () => {
210
+ process.env.PI_SKILL_SANITIZATION_FILE = FIXTURE;
211
+ resetSanitizationRulesCache();
212
+ const first = loadSanitizationRules();
213
+ const second = loadSanitizationRules();
214
+ expect(second).toBe(first);
215
+ });
216
+ });
217
+
107
218
  describe("the payload of a point is sanitized", () => {
108
219
  test("the client names of the body do not travel into the collection", () => {
109
- const payload = skillPointPayload({
110
- name: "inpac-pe-approval-workflow",
111
- description: "the workflow of inpac",
112
- body: "the approval of inpac for the client",
113
- });
220
+ const rules = fixtureRules();
221
+ const payload = skillPointPayload(
222
+ {
223
+ name: "acmecorp-pe-approval-workflow",
224
+ description: "the workflow of acmecorp",
225
+ body: "the approval of acmecorp for the client",
226
+ },
227
+ rules,
228
+ );
114
229
 
115
230
  expect(payload.name).toBe("client-a-pe-approval-workflow");
116
- expect(payload.description).not.toMatch(/inpac/i);
117
- expect(payload.body).not.toMatch(/inpac/i);
118
- expect(payload.text).not.toMatch(/inpac/i);
231
+ expect(payload.description).not.toMatch(/acmecorp/i);
232
+ expect(payload.body).not.toMatch(/acmecorp/i);
233
+ expect(payload.text).not.toMatch(/acmecorp/i);
119
234
  });
120
235
 
121
236
  test("the text is built from the sanitized parts", () => {
122
- const payload = skillPointPayload({
123
- name: "inpac-pe-approval-workflow",
124
- description: "the workflow",
125
- body: "the body",
126
- });
237
+ const rules = fixtureRules();
238
+ const payload = skillPointPayload(
239
+ {
240
+ name: "acmecorp-pe-approval-workflow",
241
+ description: "the workflow",
242
+ body: "the body",
243
+ },
244
+ rules,
245
+ );
127
246
 
128
247
  expect(payload.text).toContain("client-a-pe-approval-workflow");
129
- expect(payload.text).not.toContain("inpac-pe-approval-workflow");
248
+ expect(payload.text).not.toContain("acmecorp-pe-approval-workflow");
130
249
  });
131
250
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/sprint-planner",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "type": "module",
5
5
  "description": "Sprint planning engine for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/subscription-engine",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/task-compiler",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "Task Compiler for pi-harness-runtime — produces deterministic task DAG from CompiledRequirement",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/telegram",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "pi-harness-runtime — Telegram inbound/outbound",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/tencentdb-memory",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "TencentDB Agent Memory client - sync skills to centralized knowledge base",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tencentdb-sync",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "Sync SKILL.md skills to TencentDB-Agent-Memory server (RFC-0105/0106)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/test-data-generator",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "Test data generation for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/test-generator",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "Auto-Test Generator — generate unit and E2E tests from source code (RFC-0013)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/todo-bd-sync",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "Two-way sync between rpiv-todo overlay and bd issue tracker",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-estimation",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "pi-harness-runtime — token-estimation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-optimizer",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "Token Budget Optimizer — maximize prompt value within token limits (RFC-0011)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tooling-preference",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "pi-harness-runtime — JS/TS tooling preference (Biome vs ESLint/Prettier) proposal",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/trajectory",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "Trajectory capture for the write-review loop — persists cycle records for learning",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tui",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "pi-harness-runtime — tui",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/two-phase-review",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "pi-harness-runtime — two-phase implementation and review workflow (queue review signals, gate the phase, scope the diff)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/types",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "Shared TypeScript types for pi-harness-runtime packages",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/voice-api",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "type": "module",
5
5
  "description": "Voice-to-text API using Whisper for Telegram and LINE integration",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/webmcp-tester",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "WebMCP-powered E2E testing for pi-harness - natural language test scenarios",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/wiki",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "LLM Wiki integration for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workflow-events",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workspace-scanner",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/worktree",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "pi-harness-runtime — worktree",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/write-review",
3
- "version": "1.1.422",
3
+ "version": "1.1.423",
4
4
  "description": "Two-agent write-review loop with blackboard coordination",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -352,15 +352,11 @@ Sync Skills to Qdrant (Sanitized)
352
352
  Upload skills to Qdrant for team sharing.
353
353
  Client-specific names are REPLACED with placeholders.
354
354
 
355
- Sanitization Rules:
356
- inpac → [Client A]
357
- m-capital → [Client A]
358
- digisoft → [Client B]
359
- cloudshot → [Client C]
360
- paperclip → [Client D]
361
- teamw → [Client E]
362
- openclaw → [Client F]
363
- autoresearch → [Internal Tool]
355
+ The rules live in the local file of the installation:
356
+ ~/.pi-harness-runtime/skill-sanitization.json
357
+ They are not in this repository - it is public, and a list of client names
358
+ in it is a list of client names in the world. Set
359
+ PI_SKILL_SANITIZATION_FILE to use another file.
364
360
 
365
361
  Usage:
366
362
  bun scripts/sync-to-qdrant.ts --from <dir> Sync skills from directory