@asterxsk/kiln 0.4.0 → 0.4.2
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 +155 -155
- package/agent/AGENTS.md +67 -67
- package/agent/README.md +5 -5
- package/agent/extensions/ask-user/index.ts +418 -418
- package/agent/extensions/ask-user/install.ps1 +27 -27
- package/agent/extensions/ask-user/install.sh +24 -24
- package/agent/extensions/ask-user/package-lock.json +769 -769
- package/agent/extensions/ask-user/package.json +19 -19
- package/agent/extensions/ask-user/prompt.ts +45 -45
- package/agent/extensions/ask-user/tsconfig.json +7 -7
- package/agent/extensions/background-terminals/docs/implementation-guide.md +942 -942
- package/agent/extensions/background-terminals/index.ts +627 -627
- package/agent/extensions/background-terminals/install.ps1 +27 -27
- package/agent/extensions/background-terminals/install.sh +24 -24
- package/agent/extensions/background-terminals/manager.test.ts +735 -735
- package/agent/extensions/background-terminals/output.test.ts +109 -109
- package/agent/extensions/background-terminals/package-lock.json +769 -769
- package/agent/extensions/background-terminals/package.json +17 -17
- package/agent/extensions/background-terminals/prompt.test.ts +125 -125
- package/agent/extensions/background-terminals/ps.test.ts +82 -82
- package/agent/extensions/background-terminals/result-delivery.test.ts +44 -44
- package/agent/extensions/background-terminals/src/domain.ts +87 -87
- package/agent/extensions/background-terminals/src/manager.ts +907 -907
- package/agent/extensions/background-terminals/src/output.ts +84 -84
- package/agent/extensions/background-terminals/src/prompt.ts +142 -142
- package/agent/extensions/background-terminals/src/result-delivery.ts +27 -27
- package/agent/extensions/background-terminals/src/runtime.ts +36 -36
- package/agent/extensions/background-terminals/src/ui/output-view.ts +79 -79
- package/agent/extensions/background-terminals/src/ui/ps.ts +621 -621
- package/agent/extensions/background-terminals/tsconfig.json +7 -7
- package/agent/extensions/destructive/README.md +31 -31
- package/agent/extensions/destructive/index.ts +88 -88
- package/agent/extensions/destructive/install.ps1 +27 -27
- package/agent/extensions/destructive/install.sh +24 -24
- package/agent/extensions/file-search/index.spec.ts +443 -443
- package/agent/extensions/file-search/index.ts +459 -459
- package/agent/extensions/file-search/install.ps1 +27 -27
- package/agent/extensions/file-search/install.sh +24 -24
- package/agent/extensions/file-search/package-lock.json +2253 -2253
- package/agent/extensions/file-search/package.json +23 -23
- package/agent/extensions/file-search/src/args.ts +122 -122
- package/agent/extensions/file-search/src/binaries.ts +422 -422
- package/agent/extensions/file-search/src/output.ts +126 -126
- package/agent/extensions/file-search/src/process.ts +146 -146
- package/agent/extensions/file-search/src/prompt.ts +52 -52
- package/agent/extensions/file-search/tsconfig.json +7 -7
- package/agent/extensions/goal/README.md +50 -50
- package/agent/extensions/goal/index.ts +155 -155
- package/agent/extensions/goal/install.ps1 +27 -27
- package/agent/extensions/goal/install.sh +24 -24
- package/agent/extensions/kiln-update/README.md +38 -25
- package/agent/extensions/kiln-update/index.ts +177 -99
- package/agent/extensions/modelconf/PLAN.md +915 -915
- package/agent/extensions/modelconf/README.md +66 -66
- package/agent/extensions/modelconf/index.ts +296 -296
- package/agent/extensions/modelconf/install.ps1 +27 -27
- package/agent/extensions/modelconf/install.sh +24 -24
- package/agent/extensions/modelconf/package-lock.json +1809 -1809
- package/agent/extensions/modelconf/package.json +13 -13
- package/agent/extensions/modelconf/src/fuzzy.ts +26 -26
- package/agent/extensions/modelconf/src/glob.ts +13 -13
- package/agent/extensions/modelconf/src/persistence.test.ts +46 -46
- package/agent/extensions/modelconf/src/persistence.ts +164 -164
- package/agent/extensions/modelconf/src/ui/ModelConfView.test.ts +75 -75
- package/agent/extensions/modelconf/src/ui/ModelConfView.ts +1101 -1101
- package/agent/extensions/modelconf/tsconfig.json +15 -15
- package/agent/extensions/pi-web-access/CHANGELOG.md +690 -690
- package/agent/extensions/pi-web-access/LICENSE +21 -21
- package/agent/extensions/pi-web-access/README.md +470 -470
- package/agent/extensions/pi-web-access/SECURITY.md +5 -5
- package/agent/extensions/pi-web-access/activity.ts +101 -101
- package/agent/extensions/pi-web-access/auth-fetch.ts +148 -148
- package/agent/extensions/pi-web-access/brightdata-unlocker.ts +272 -272
- package/agent/extensions/pi-web-access/chrome-cookies.ts +669 -669
- package/agent/extensions/pi-web-access/content-find.ts +139 -139
- package/agent/extensions/pi-web-access/credential-source.ts +191 -191
- package/agent/extensions/pi-web-access/data-uri-sanitize.ts +406 -406
- package/agent/extensions/pi-web-access/datalab-pdf-extract.ts +568 -568
- package/agent/extensions/pi-web-access/declared-web-links.ts +173 -173
- package/agent/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +496 -496
- package/agent/extensions/pi-web-access/evidence/contract-probe.mjs +140 -140
- package/agent/extensions/pi-web-access/exa.ts +526 -526
- package/agent/extensions/pi-web-access/extract.ts +1196 -1196
- package/agent/extensions/pi-web-access/feature-config.ts +29 -29
- package/agent/extensions/pi-web-access/fetch-params.ts +111 -111
- package/agent/extensions/pi-web-access/gemini-adc.ts +298 -298
- package/agent/extensions/pi-web-access/gemini-api.ts +353 -353
- package/agent/extensions/pi-web-access/gemini-pdf-extract.ts +108 -108
- package/agent/extensions/pi-web-access/gemini-search.ts +21 -21
- package/agent/extensions/pi-web-access/gemini-url-context.ts +128 -128
- package/agent/extensions/pi-web-access/gemini-web-config.ts +101 -101
- package/agent/extensions/pi-web-access/gemini-web.ts +487 -487
- package/agent/extensions/pi-web-access/github-api.ts +197 -197
- package/agent/extensions/pi-web-access/github-extract.ts +746 -746
- package/agent/extensions/pi-web-access/github-issue-pr.ts +700 -700
- package/agent/extensions/pi-web-access/index.ts +1737 -1737
- package/agent/extensions/pi-web-access/package-lock.json +5808 -5808
- package/agent/extensions/pi-web-access/package.json +64 -64
- package/agent/extensions/pi-web-access/page-query.ts +96 -96
- package/agent/extensions/pi-web-access/pdf-extract.ts +409 -409
- package/agent/extensions/pi-web-access/promise-try.d.ts +7 -7
- package/agent/extensions/pi-web-access/query-rewrite.ts +51 -51
- package/agent/extensions/pi-web-access/render-search-error.ts +170 -170
- package/agent/extensions/pi-web-access/rsc-extract.ts +338 -338
- package/agent/extensions/pi-web-access/search-types.ts +20 -20
- package/agent/extensions/pi-web-access/source-check.ts +282 -282
- package/agent/extensions/pi-web-access/ssrf-protection.ts +526 -526
- package/agent/extensions/pi-web-access/storage.ts +521 -521
- package/agent/extensions/pi-web-access/summary-model-scope.ts +125 -125
- package/agent/extensions/pi-web-access/test/auth-fetch.test.mjs +208 -208
- package/agent/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +840 -840
- package/agent/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +441 -441
- package/agent/extensions/pi-web-access/test/config-path.test.mjs +283 -283
- package/agent/extensions/pi-web-access/test/content-find.test.mjs +25 -25
- package/agent/extensions/pi-web-access/test/credential-source.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +210 -210
- package/agent/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +552 -552
- package/agent/extensions/pi-web-access/test/declared-web-links.test.mjs +212 -212
- package/agent/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +40 -40
- package/agent/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +334 -334
- package/agent/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +95 -95
- package/agent/extensions/pi-web-access/test/fetch-modes.test.mjs +53 -53
- package/agent/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +92 -92
- package/agent/extensions/pi-web-access/test/fetch-params.test.mjs +86 -86
- package/agent/extensions/pi-web-access/test/fetch-render-call.test.mjs +34 -34
- package/agent/extensions/pi-web-access/test/fetch-routing.test.mjs +173 -173
- package/agent/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +257 -257
- package/agent/extensions/pi-web-access/test/gemini-api-transport.test.mjs +170 -170
- package/agent/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +133 -133
- package/agent/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +178 -178
- package/agent/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +148 -148
- package/agent/extensions/pi-web-access/test/get-search-content.test.mjs +223 -223
- package/agent/extensions/pi-web-access/test/github-extract.test.mjs +378 -378
- package/agent/extensions/pi-web-access/test/github-issue-pr.test.mjs +565 -565
- package/agent/extensions/pi-web-access/test/inline-content-config.test.mjs +99 -99
- package/agent/extensions/pi-web-access/test/lazy-extract-load.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/local-video-oversize.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +50 -50
- package/agent/extensions/pi-web-access/test/page-query.test.mjs +51 -51
- package/agent/extensions/pi-web-access/test/pdf-config.test.mjs +140 -140
- package/agent/extensions/pi-web-access/test/pdf-extract.test.mjs +500 -500
- package/agent/extensions/pi-web-access/test/proxy-transport.test.mjs +286 -286
- package/agent/extensions/pi-web-access/test/query-rewrite.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/rsc-fallback.test.mjs +102 -102
- package/agent/extensions/pi-web-access/test/search-error-render.test.mjs +152 -152
- package/agent/extensions/pi-web-access/test/search-providers.test.mjs +274 -274
- package/agent/extensions/pi-web-access/test/source-check.test.mjs +179 -179
- package/agent/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +205 -205
- package/agent/extensions/pi-web-access/test/ssrf-protection.test.mjs +456 -456
- package/agent/extensions/pi-web-access/test/summary-model-scope.test.mjs +106 -106
- package/agent/extensions/pi-web-access/test/tool-registration-config.test.mjs +182 -182
- package/agent/extensions/pi-web-access/test/web-search-answer-render.test.mjs +66 -66
- package/agent/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +64 -64
- package/agent/extensions/pi-web-access/tsconfig.json +11 -11
- package/agent/extensions/pi-web-access/utils.ts +451 -451
- package/agent/extensions/pi-web-access/video-extract.ts +392 -392
- package/agent/extensions/pi-web-access/youtube-extract.ts +328 -328
- package/agent/extensions/shared/activity-status.ts +31 -31
- package/agent/extensions/shared/child-session.test.ts +270 -270
- package/agent/extensions/shared/child-session.ts +148 -148
- package/agent/extensions/shared/context-utilization.test.ts +48 -48
- package/agent/extensions/shared/context-utilization.ts +47 -47
- package/agent/extensions/shared/dashboard-state.ts +99 -99
- package/agent/extensions/shared/install.ps1 +27 -27
- package/agent/extensions/shared/install.sh +24 -24
- package/agent/extensions/shared/tool-call-timeout.test.ts +117 -117
- package/agent/extensions/shared/tool-call-timeout.ts +104 -104
- package/agent/extensions/skillsconf/README.md +74 -74
- package/agent/extensions/skillsconf/index.ts +92 -92
- package/agent/extensions/skillsconf/install.ps1 +27 -27
- package/agent/extensions/skillsconf/install.sh +24 -24
- package/agent/extensions/skillsconf/package-lock.json +1809 -1809
- package/agent/extensions/skillsconf/package.json +18 -18
- package/agent/extensions/skillsconf/src/delete-skill.test.ts +64 -64
- package/agent/extensions/skillsconf/src/delete-skill.ts +45 -45
- package/agent/extensions/skillsconf/src/filter.test.ts +80 -80
- package/agent/extensions/skillsconf/src/filter.ts +74 -74
- package/agent/extensions/skillsconf/src/fuzzy.ts +26 -26
- package/agent/extensions/skillsconf/src/persistence.test.ts +72 -72
- package/agent/extensions/skillsconf/src/persistence.ts +169 -169
- package/agent/extensions/skillsconf/src/ui/SkillConfView.test.ts +337 -337
- package/agent/extensions/skillsconf/src/ui/SkillConfView.ts +758 -758
- package/agent/extensions/skillsconf/src/ui/text-input.ts +91 -91
- package/agent/extensions/skillsconf/src/ui/tui-helpers.ts +144 -144
- package/agent/extensions/skillsconf/tsconfig.json +15 -15
- package/agent/extensions/statusline/index.ts +273 -282
- package/agent/extensions/statusline/install.ps1 +27 -27
- package/agent/extensions/statusline/install.sh +24 -24
- package/agent/extensions/subagents/by-the-way.test.ts +29 -29
- package/agent/extensions/subagents/claude.test.ts +119 -119
- package/agent/extensions/subagents/codex.test.ts +102 -102
- package/agent/extensions/subagents/context-usage.test.ts +107 -107
- package/agent/extensions/subagents/docs/design-plan.md +568 -568
- package/agent/extensions/subagents/docs/effect-v4-extension-guide.md +354 -354
- package/agent/extensions/subagents/docs/effect-v4-notes.md +571 -571
- package/agent/extensions/subagents/index.ts +779 -779
- package/agent/extensions/subagents/install.ps1 +27 -27
- package/agent/extensions/subagents/install.sh +24 -24
- package/agent/extensions/subagents/manager.test.ts +276 -276
- package/agent/extensions/subagents/package-lock.json +2244 -2244
- package/agent/extensions/subagents/package.json +19 -19
- package/agent/extensions/subagents/result-delivery.test.ts +27 -27
- package/agent/extensions/subagents/src/backend.ts +73 -73
- package/agent/extensions/subagents/src/backends/claude.ts +701 -701
- package/agent/extensions/subagents/src/backends/codex.ts +1060 -1060
- package/agent/extensions/subagents/src/backends/pi.ts +575 -575
- package/agent/extensions/subagents/src/backends/stub.ts +300 -300
- package/agent/extensions/subagents/src/by-the-way.ts +21 -21
- package/agent/extensions/subagents/src/domain.ts +253 -253
- package/agent/extensions/subagents/src/format.ts +74 -74
- package/agent/extensions/subagents/src/manager.ts +736 -736
- package/agent/extensions/subagents/src/prompt.ts +92 -92
- package/agent/extensions/subagents/src/result-delivery.ts +20 -20
- package/agent/extensions/subagents/src/runtime.ts +53 -53
- package/agent/extensions/subagents/src/ui/takeover.ts +583 -583
- package/agent/extensions/subagents/src/ui/transcript.ts +201 -201
- package/agent/extensions/subagents/takeover.test.ts +29 -29
- package/agent/extensions/subagents/tsconfig.json +7 -7
- package/agent/extensions/todo/AGENTS.md +38 -38
- package/agent/extensions/todo/LICENSE +21 -21
- package/agent/extensions/todo/config.ts +55 -55
- package/agent/extensions/todo/index.ts +151 -151
- package/agent/extensions/todo/install.ps1 +27 -27
- package/agent/extensions/todo/install.sh +24 -24
- package/agent/extensions/todo/locales/de.json +17 -17
- package/agent/extensions/todo/locales/en.json +15 -15
- package/agent/extensions/todo/locales/es.json +17 -17
- package/agent/extensions/todo/locales/fr.json +17 -17
- package/agent/extensions/todo/locales/pt-BR.json +17 -17
- package/agent/extensions/todo/locales/pt.json +17 -17
- package/agent/extensions/todo/locales/ru.json +17 -17
- package/agent/extensions/todo/locales/uk.json +17 -17
- package/agent/extensions/todo/locales/zh.json +17 -17
- package/agent/extensions/todo/package-lock.json +3358 -3358
- package/agent/extensions/todo/package.json +67 -67
- package/agent/extensions/todo/state/i18n-bridge.ts +64 -64
- package/agent/extensions/todo/state/invariants.ts +20 -20
- package/agent/extensions/todo/state/replay.ts +38 -38
- package/agent/extensions/todo/state/selectors.ts +107 -107
- package/agent/extensions/todo/state/state-reducer.ts +326 -326
- package/agent/extensions/todo/state/state.ts +18 -18
- package/agent/extensions/todo/state/store.ts +82 -82
- package/agent/extensions/todo/state/task-graph.ts +57 -57
- package/agent/extensions/todo/todo-overlay.ts +200 -200
- package/agent/extensions/todo/todo.ts +155 -155
- package/agent/extensions/todo/tool/response-envelope.ts +109 -109
- package/agent/extensions/todo/tool/types.ts +206 -206
- package/agent/extensions/todo/view/format.ts +177 -177
- package/agent/extensions/trim-context/README.md +54 -54
- package/agent/extensions/trim-context/index.ts +487 -487
- package/agent/extensions/trim-context/install.ps1 +27 -27
- package/agent/extensions/trim-context/install.sh +24 -24
- package/agent/keybindings.json +7 -7
- package/agent/version.txt +1 -1
- package/bin/kiln.js +20 -13
- package/package.json +1 -1
- package/agent/extensions/taste/index.ts +0 -443
- package/agent/extensions/taste/install.ps1 +0 -27
- package/agent/extensions/taste/install.sh +0 -24
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import {
|
|
3
|
-
cloneState,
|
|
4
|
-
isDisabled,
|
|
5
|
-
normalizeMembers,
|
|
6
|
-
statesEqual,
|
|
7
|
-
toggleMembership,
|
|
8
|
-
togglePackage,
|
|
9
|
-
toggleSkill,
|
|
10
|
-
} from "./persistence.js";
|
|
11
|
-
import type { SkillState } from "./persistence.js";
|
|
12
|
-
|
|
13
|
-
// isDisabled
|
|
14
|
-
assert.equal(isDisabled("a", undefined), false);
|
|
15
|
-
assert.equal(isDisabled("a", []), false);
|
|
16
|
-
assert.equal(isDisabled("a", ["a"]), true);
|
|
17
|
-
assert.equal(isDisabled("b", ["a"]), false);
|
|
18
|
-
|
|
19
|
-
// toggleSkill
|
|
20
|
-
assert.deepEqual(toggleSkill("a", undefined), ["a"]);
|
|
21
|
-
assert.deepEqual(toggleSkill("a", ["a"]), undefined);
|
|
22
|
-
assert.deepEqual(toggleSkill("b", ["a"]), ["a", "b"]);
|
|
23
|
-
assert.deepEqual(toggleSkill("a", ["a", "b"]), ["b"]);
|
|
24
|
-
|
|
25
|
-
// togglePackage: partial -> disable all; full -> enable all
|
|
26
|
-
assert.deepEqual(togglePackage(["a", "b"], undefined), ["a", "b"]);
|
|
27
|
-
assert.deepEqual(togglePackage(["a", "b"], ["a"]), ["a", "b"]);
|
|
28
|
-
assert.deepEqual(togglePackage(["a", "b"], ["a", "b"]), undefined);
|
|
29
|
-
// preserves unrelated disabled entries
|
|
30
|
-
assert.deepEqual(togglePackage(["a"], ["a", "z"]), ["z"]);
|
|
31
|
-
// empty package -> no change
|
|
32
|
-
assert.deepEqual(togglePackage([], ["a"]), ["a"]);
|
|
33
|
-
assert.equal(togglePackage([], undefined), undefined);
|
|
34
|
-
|
|
35
|
-
// toggleMembership
|
|
36
|
-
assert.deepEqual(toggleMembership(["a"], "b"), ["a", "b"]);
|
|
37
|
-
assert.deepEqual(toggleMembership(["a", "b"], "a"), ["b"]);
|
|
38
|
-
assert.deepEqual(toggleMembership([], "a"), ["a"]);
|
|
39
|
-
|
|
40
|
-
// normalizeMembers dedupes + sorts
|
|
41
|
-
assert.deepEqual(normalizeMembers(["b", "a", "b"]), ["a", "b"]);
|
|
42
|
-
|
|
43
|
-
// statesEqual
|
|
44
|
-
const s = (disabled: string[] | undefined, packages: SkillState["packages"]): SkillState => ({
|
|
45
|
-
disabled,
|
|
46
|
-
packages,
|
|
47
|
-
});
|
|
48
|
-
assert.equal(statesEqual(s(undefined, {}), s(undefined, {})), true);
|
|
49
|
-
assert.equal(statesEqual(s(["a"], {}), s(undefined, {})), false);
|
|
50
|
-
assert.equal(statesEqual(s(["b", "a"], {}), s(["a", "b"], {})), true);
|
|
51
|
-
assert.equal(
|
|
52
|
-
statesEqual(s(undefined, { p: ["a"] }), s(undefined, { p: ["a", "a"] })),
|
|
53
|
-
true,
|
|
54
|
-
);
|
|
55
|
-
assert.equal(
|
|
56
|
-
statesEqual(s(undefined, { p: ["a"] }), s(undefined, { p: ["b"] })),
|
|
57
|
-
false,
|
|
58
|
-
);
|
|
59
|
-
assert.equal(
|
|
60
|
-
statesEqual(s(undefined, { p: ["a"] }), s(undefined, { q: ["a"] })),
|
|
61
|
-
false,
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
// cloneState is a deep copy
|
|
65
|
-
const orig = s(["a"], { p: ["x"] });
|
|
66
|
-
const copy = cloneState(orig);
|
|
67
|
-
assert.equal(statesEqual(orig, copy), true);
|
|
68
|
-
copy.packages["p"].push("y");
|
|
69
|
-
(copy.disabled as string[]).push("z");
|
|
70
|
-
assert.equal(statesEqual(orig, copy), false);
|
|
71
|
-
|
|
72
|
-
console.log("persistence.test.ts: all assertions passed");
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import {
|
|
3
|
+
cloneState,
|
|
4
|
+
isDisabled,
|
|
5
|
+
normalizeMembers,
|
|
6
|
+
statesEqual,
|
|
7
|
+
toggleMembership,
|
|
8
|
+
togglePackage,
|
|
9
|
+
toggleSkill,
|
|
10
|
+
} from "./persistence.js";
|
|
11
|
+
import type { SkillState } from "./persistence.js";
|
|
12
|
+
|
|
13
|
+
// isDisabled
|
|
14
|
+
assert.equal(isDisabled("a", undefined), false);
|
|
15
|
+
assert.equal(isDisabled("a", []), false);
|
|
16
|
+
assert.equal(isDisabled("a", ["a"]), true);
|
|
17
|
+
assert.equal(isDisabled("b", ["a"]), false);
|
|
18
|
+
|
|
19
|
+
// toggleSkill
|
|
20
|
+
assert.deepEqual(toggleSkill("a", undefined), ["a"]);
|
|
21
|
+
assert.deepEqual(toggleSkill("a", ["a"]), undefined);
|
|
22
|
+
assert.deepEqual(toggleSkill("b", ["a"]), ["a", "b"]);
|
|
23
|
+
assert.deepEqual(toggleSkill("a", ["a", "b"]), ["b"]);
|
|
24
|
+
|
|
25
|
+
// togglePackage: partial -> disable all; full -> enable all
|
|
26
|
+
assert.deepEqual(togglePackage(["a", "b"], undefined), ["a", "b"]);
|
|
27
|
+
assert.deepEqual(togglePackage(["a", "b"], ["a"]), ["a", "b"]);
|
|
28
|
+
assert.deepEqual(togglePackage(["a", "b"], ["a", "b"]), undefined);
|
|
29
|
+
// preserves unrelated disabled entries
|
|
30
|
+
assert.deepEqual(togglePackage(["a"], ["a", "z"]), ["z"]);
|
|
31
|
+
// empty package -> no change
|
|
32
|
+
assert.deepEqual(togglePackage([], ["a"]), ["a"]);
|
|
33
|
+
assert.equal(togglePackage([], undefined), undefined);
|
|
34
|
+
|
|
35
|
+
// toggleMembership
|
|
36
|
+
assert.deepEqual(toggleMembership(["a"], "b"), ["a", "b"]);
|
|
37
|
+
assert.deepEqual(toggleMembership(["a", "b"], "a"), ["b"]);
|
|
38
|
+
assert.deepEqual(toggleMembership([], "a"), ["a"]);
|
|
39
|
+
|
|
40
|
+
// normalizeMembers dedupes + sorts
|
|
41
|
+
assert.deepEqual(normalizeMembers(["b", "a", "b"]), ["a", "b"]);
|
|
42
|
+
|
|
43
|
+
// statesEqual
|
|
44
|
+
const s = (disabled: string[] | undefined, packages: SkillState["packages"]): SkillState => ({
|
|
45
|
+
disabled,
|
|
46
|
+
packages,
|
|
47
|
+
});
|
|
48
|
+
assert.equal(statesEqual(s(undefined, {}), s(undefined, {})), true);
|
|
49
|
+
assert.equal(statesEqual(s(["a"], {}), s(undefined, {})), false);
|
|
50
|
+
assert.equal(statesEqual(s(["b", "a"], {}), s(["a", "b"], {})), true);
|
|
51
|
+
assert.equal(
|
|
52
|
+
statesEqual(s(undefined, { p: ["a"] }), s(undefined, { p: ["a", "a"] })),
|
|
53
|
+
true,
|
|
54
|
+
);
|
|
55
|
+
assert.equal(
|
|
56
|
+
statesEqual(s(undefined, { p: ["a"] }), s(undefined, { p: ["b"] })),
|
|
57
|
+
false,
|
|
58
|
+
);
|
|
59
|
+
assert.equal(
|
|
60
|
+
statesEqual(s(undefined, { p: ["a"] }), s(undefined, { q: ["a"] })),
|
|
61
|
+
false,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// cloneState is a deep copy
|
|
65
|
+
const orig = s(["a"], { p: ["x"] });
|
|
66
|
+
const copy = cloneState(orig);
|
|
67
|
+
assert.equal(statesEqual(orig, copy), true);
|
|
68
|
+
copy.packages["p"].push("y");
|
|
69
|
+
(copy.disabled as string[]).push("z");
|
|
70
|
+
assert.equal(statesEqual(orig, copy), false);
|
|
71
|
+
|
|
72
|
+
console.log("persistence.test.ts: all assertions passed");
|
|
@@ -1,169 +1,169 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import fsSync from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
|
|
6
|
-
export type SkillPackages = Record<string, string[]>;
|
|
7
|
-
|
|
8
|
-
export interface SkillState {
|
|
9
|
-
/** Names of disabled skills. undefined/empty = all enabled. */
|
|
10
|
-
disabled: string[] | undefined;
|
|
11
|
-
/** Package name -> member skill names. Never undefined (may be empty). */
|
|
12
|
-
packages: SkillPackages;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function settingsPath(): string {
|
|
16
|
-
return path.join(os.homedir(), ".pi", "agent", "settings.json");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function parseState(json: unknown): SkillState {
|
|
20
|
-
const j = (json ?? {}) as Record<string, unknown>;
|
|
21
|
-
const rawDisabled = Array.isArray(j.disabledSkills)
|
|
22
|
-
? (j.disabledSkills as unknown[]).filter((x): x is string => typeof x === "string")
|
|
23
|
-
: undefined;
|
|
24
|
-
const rawPkgs = (j.skillPackages ?? {}) as Record<string, unknown>;
|
|
25
|
-
const packages: SkillPackages = {};
|
|
26
|
-
if (rawPkgs && typeof rawPkgs === "object") {
|
|
27
|
-
for (const [k, v] of Object.entries(rawPkgs)) {
|
|
28
|
-
if (typeof k !== "string" || !Array.isArray(v)) continue;
|
|
29
|
-
packages[k] = normalizeMembers(v.filter((x): x is string => typeof x === "string"));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
disabled: rawDisabled && rawDisabled.length > 0 ? [...new Set(rawDisabled)].sort() : undefined,
|
|
34
|
-
packages,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function loadSkillState(): Promise<SkillState> {
|
|
39
|
-
try {
|
|
40
|
-
const raw = await fs.readFile(settingsPath(), "utf8");
|
|
41
|
-
return parseState(JSON.parse(raw));
|
|
42
|
-
} catch {
|
|
43
|
-
return { disabled: undefined, packages: {} };
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function loadSkillStateSync(): SkillState {
|
|
48
|
-
try {
|
|
49
|
-
const raw = fsSync.readFileSync(settingsPath(), "utf8");
|
|
50
|
-
return parseState(JSON.parse(raw));
|
|
51
|
-
} catch {
|
|
52
|
-
return { disabled: undefined, packages: {} };
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Persist state. `knownSkills` (when provided) is used to drop stale names
|
|
58
|
-
* from the disabled list and from package memberships. Packages are kept
|
|
59
|
-
* even when empty so freshly created groups survive until filled.
|
|
60
|
-
*/
|
|
61
|
-
export async function saveSkillState(
|
|
62
|
-
disabled: string[] | undefined,
|
|
63
|
-
packages: SkillPackages,
|
|
64
|
-
knownSkills?: string[],
|
|
65
|
-
): Promise<void> {
|
|
66
|
-
const p = settingsPath();
|
|
67
|
-
let j: Record<string, unknown> = {};
|
|
68
|
-
try {
|
|
69
|
-
const raw = await fs.readFile(p, "utf8");
|
|
70
|
-
j = JSON.parse(raw) as Record<string, unknown>;
|
|
71
|
-
} catch (e: unknown) {
|
|
72
|
-
if ((e as { code?: string })?.code !== "ENOENT") {
|
|
73
|
-
try {
|
|
74
|
-
const raw = await fs.readFile(p, "utf8");
|
|
75
|
-
JSON.parse(raw);
|
|
76
|
-
} catch {}
|
|
77
|
-
}
|
|
78
|
-
j = {};
|
|
79
|
-
}
|
|
80
|
-
const known = knownSkills ? new Set(knownSkills) : null;
|
|
81
|
-
const cleanDisabled = (disabled ?? []).filter((n) => (known ? known.has(n) : true));
|
|
82
|
-
if (cleanDisabled.length === 0) delete j.disabledSkills;
|
|
83
|
-
else j.disabledSkills = [...new Set(cleanDisabled)].sort();
|
|
84
|
-
|
|
85
|
-
const cleanPkgs: SkillPackages = {};
|
|
86
|
-
for (const name of Object.keys(packages).sort()) {
|
|
87
|
-
const members = normalizeMembers(packages[name] ?? []).filter((n) =>
|
|
88
|
-
known ? known.has(n) : true,
|
|
89
|
-
);
|
|
90
|
-
cleanPkgs[name] = members;
|
|
91
|
-
}
|
|
92
|
-
if (Object.keys(cleanPkgs).length === 0) delete j.skillPackages;
|
|
93
|
-
else j.skillPackages = cleanPkgs;
|
|
94
|
-
|
|
95
|
-
await fs.writeFile(p, JSON.stringify(j, null, 2) + "\n", "utf8");
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ---------- pure helpers (unit-tested) ----------
|
|
99
|
-
|
|
100
|
-
export function normalizeMembers(members: string[]): string[] {
|
|
101
|
-
return [...new Set(members)].sort();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function isDisabled(name: string, disabled: string[] | undefined): boolean {
|
|
105
|
-
if (!disabled || disabled.length === 0) return false;
|
|
106
|
-
return disabled.includes(name);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** Toggle one skill. Returns undefined when nothing ends up disabled. */
|
|
110
|
-
export function toggleSkill(name: string, disabled: string[] | undefined): string[] | undefined {
|
|
111
|
-
const set = new Set(disabled ?? []);
|
|
112
|
-
if (set.has(name)) set.delete(name);
|
|
113
|
-
else set.add(name);
|
|
114
|
-
if (set.size === 0) return undefined;
|
|
115
|
-
return [...set].sort();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Toggle a whole package: if every (known) member is disabled, enable them
|
|
120
|
-
* all; otherwise disable all of them. Members unknown to the caller are
|
|
121
|
-
* passed through untouched. Returns undefined when nothing ends up disabled.
|
|
122
|
-
*/
|
|
123
|
-
export function togglePackage(
|
|
124
|
-
members: string[],
|
|
125
|
-
disabled: string[] | undefined,
|
|
126
|
-
): string[] | undefined {
|
|
127
|
-
if (members.length === 0) return disabled && disabled.length > 0 ? [...disabled] : undefined;
|
|
128
|
-
const set = new Set(disabled ?? []);
|
|
129
|
-
const allDisabled = members.every((m) => set.has(m));
|
|
130
|
-
if (allDisabled) {
|
|
131
|
-
for (const m of members) set.delete(m);
|
|
132
|
-
} else {
|
|
133
|
-
for (const m of members) set.add(m);
|
|
134
|
-
}
|
|
135
|
-
if (set.size === 0) return undefined;
|
|
136
|
-
return [...set].sort();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function toggleMembership(members: string[], name: string): string[] {
|
|
140
|
-
const set = new Set(members);
|
|
141
|
-
if (set.has(name)) set.delete(name);
|
|
142
|
-
else set.add(name);
|
|
143
|
-
return [...set].sort();
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** Normalized deep-equal for dirty checking. */
|
|
147
|
-
export function statesEqual(a: SkillState, b: SkillState): boolean {
|
|
148
|
-
const da = a.disabled ?? [];
|
|
149
|
-
const db = b.disabled ?? [];
|
|
150
|
-
if (da.length !== db.length) return false;
|
|
151
|
-
const sa = [...da].sort();
|
|
152
|
-
const sb = [...db].sort();
|
|
153
|
-
if (JSON.stringify(sa) !== JSON.stringify(sb)) return false;
|
|
154
|
-
const ka = Object.keys(a.packages).sort();
|
|
155
|
-
const kb = Object.keys(b.packages).sort();
|
|
156
|
-
if (JSON.stringify(ka) !== JSON.stringify(kb)) return false;
|
|
157
|
-
for (const k of ka) {
|
|
158
|
-
const ma = normalizeMembers(a.packages[k] ?? []);
|
|
159
|
-
const mb = normalizeMembers(b.packages[k] ?? []);
|
|
160
|
-
if (JSON.stringify(ma) !== JSON.stringify(mb)) return false;
|
|
161
|
-
}
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function cloneState(s: SkillState): SkillState {
|
|
166
|
-
const packages: SkillPackages = {};
|
|
167
|
-
for (const [k, v] of Object.entries(s.packages)) packages[k] = [...v];
|
|
168
|
-
return { disabled: s.disabled === undefined ? undefined : [...s.disabled], packages };
|
|
169
|
-
}
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import fsSync from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
export type SkillPackages = Record<string, string[]>;
|
|
7
|
+
|
|
8
|
+
export interface SkillState {
|
|
9
|
+
/** Names of disabled skills. undefined/empty = all enabled. */
|
|
10
|
+
disabled: string[] | undefined;
|
|
11
|
+
/** Package name -> member skill names. Never undefined (may be empty). */
|
|
12
|
+
packages: SkillPackages;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function settingsPath(): string {
|
|
16
|
+
return path.join(os.homedir(), ".pi", "agent", "settings.json");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseState(json: unknown): SkillState {
|
|
20
|
+
const j = (json ?? {}) as Record<string, unknown>;
|
|
21
|
+
const rawDisabled = Array.isArray(j.disabledSkills)
|
|
22
|
+
? (j.disabledSkills as unknown[]).filter((x): x is string => typeof x === "string")
|
|
23
|
+
: undefined;
|
|
24
|
+
const rawPkgs = (j.skillPackages ?? {}) as Record<string, unknown>;
|
|
25
|
+
const packages: SkillPackages = {};
|
|
26
|
+
if (rawPkgs && typeof rawPkgs === "object") {
|
|
27
|
+
for (const [k, v] of Object.entries(rawPkgs)) {
|
|
28
|
+
if (typeof k !== "string" || !Array.isArray(v)) continue;
|
|
29
|
+
packages[k] = normalizeMembers(v.filter((x): x is string => typeof x === "string"));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
disabled: rawDisabled && rawDisabled.length > 0 ? [...new Set(rawDisabled)].sort() : undefined,
|
|
34
|
+
packages,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function loadSkillState(): Promise<SkillState> {
|
|
39
|
+
try {
|
|
40
|
+
const raw = await fs.readFile(settingsPath(), "utf8");
|
|
41
|
+
return parseState(JSON.parse(raw));
|
|
42
|
+
} catch {
|
|
43
|
+
return { disabled: undefined, packages: {} };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function loadSkillStateSync(): SkillState {
|
|
48
|
+
try {
|
|
49
|
+
const raw = fsSync.readFileSync(settingsPath(), "utf8");
|
|
50
|
+
return parseState(JSON.parse(raw));
|
|
51
|
+
} catch {
|
|
52
|
+
return { disabled: undefined, packages: {} };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Persist state. `knownSkills` (when provided) is used to drop stale names
|
|
58
|
+
* from the disabled list and from package memberships. Packages are kept
|
|
59
|
+
* even when empty so freshly created groups survive until filled.
|
|
60
|
+
*/
|
|
61
|
+
export async function saveSkillState(
|
|
62
|
+
disabled: string[] | undefined,
|
|
63
|
+
packages: SkillPackages,
|
|
64
|
+
knownSkills?: string[],
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
const p = settingsPath();
|
|
67
|
+
let j: Record<string, unknown> = {};
|
|
68
|
+
try {
|
|
69
|
+
const raw = await fs.readFile(p, "utf8");
|
|
70
|
+
j = JSON.parse(raw) as Record<string, unknown>;
|
|
71
|
+
} catch (e: unknown) {
|
|
72
|
+
if ((e as { code?: string })?.code !== "ENOENT") {
|
|
73
|
+
try {
|
|
74
|
+
const raw = await fs.readFile(p, "utf8");
|
|
75
|
+
JSON.parse(raw);
|
|
76
|
+
} catch {}
|
|
77
|
+
}
|
|
78
|
+
j = {};
|
|
79
|
+
}
|
|
80
|
+
const known = knownSkills ? new Set(knownSkills) : null;
|
|
81
|
+
const cleanDisabled = (disabled ?? []).filter((n) => (known ? known.has(n) : true));
|
|
82
|
+
if (cleanDisabled.length === 0) delete j.disabledSkills;
|
|
83
|
+
else j.disabledSkills = [...new Set(cleanDisabled)].sort();
|
|
84
|
+
|
|
85
|
+
const cleanPkgs: SkillPackages = {};
|
|
86
|
+
for (const name of Object.keys(packages).sort()) {
|
|
87
|
+
const members = normalizeMembers(packages[name] ?? []).filter((n) =>
|
|
88
|
+
known ? known.has(n) : true,
|
|
89
|
+
);
|
|
90
|
+
cleanPkgs[name] = members;
|
|
91
|
+
}
|
|
92
|
+
if (Object.keys(cleanPkgs).length === 0) delete j.skillPackages;
|
|
93
|
+
else j.skillPackages = cleanPkgs;
|
|
94
|
+
|
|
95
|
+
await fs.writeFile(p, JSON.stringify(j, null, 2) + "\n", "utf8");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------- pure helpers (unit-tested) ----------
|
|
99
|
+
|
|
100
|
+
export function normalizeMembers(members: string[]): string[] {
|
|
101
|
+
return [...new Set(members)].sort();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function isDisabled(name: string, disabled: string[] | undefined): boolean {
|
|
105
|
+
if (!disabled || disabled.length === 0) return false;
|
|
106
|
+
return disabled.includes(name);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Toggle one skill. Returns undefined when nothing ends up disabled. */
|
|
110
|
+
export function toggleSkill(name: string, disabled: string[] | undefined): string[] | undefined {
|
|
111
|
+
const set = new Set(disabled ?? []);
|
|
112
|
+
if (set.has(name)) set.delete(name);
|
|
113
|
+
else set.add(name);
|
|
114
|
+
if (set.size === 0) return undefined;
|
|
115
|
+
return [...set].sort();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Toggle a whole package: if every (known) member is disabled, enable them
|
|
120
|
+
* all; otherwise disable all of them. Members unknown to the caller are
|
|
121
|
+
* passed through untouched. Returns undefined when nothing ends up disabled.
|
|
122
|
+
*/
|
|
123
|
+
export function togglePackage(
|
|
124
|
+
members: string[],
|
|
125
|
+
disabled: string[] | undefined,
|
|
126
|
+
): string[] | undefined {
|
|
127
|
+
if (members.length === 0) return disabled && disabled.length > 0 ? [...disabled] : undefined;
|
|
128
|
+
const set = new Set(disabled ?? []);
|
|
129
|
+
const allDisabled = members.every((m) => set.has(m));
|
|
130
|
+
if (allDisabled) {
|
|
131
|
+
for (const m of members) set.delete(m);
|
|
132
|
+
} else {
|
|
133
|
+
for (const m of members) set.add(m);
|
|
134
|
+
}
|
|
135
|
+
if (set.size === 0) return undefined;
|
|
136
|
+
return [...set].sort();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function toggleMembership(members: string[], name: string): string[] {
|
|
140
|
+
const set = new Set(members);
|
|
141
|
+
if (set.has(name)) set.delete(name);
|
|
142
|
+
else set.add(name);
|
|
143
|
+
return [...set].sort();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Normalized deep-equal for dirty checking. */
|
|
147
|
+
export function statesEqual(a: SkillState, b: SkillState): boolean {
|
|
148
|
+
const da = a.disabled ?? [];
|
|
149
|
+
const db = b.disabled ?? [];
|
|
150
|
+
if (da.length !== db.length) return false;
|
|
151
|
+
const sa = [...da].sort();
|
|
152
|
+
const sb = [...db].sort();
|
|
153
|
+
if (JSON.stringify(sa) !== JSON.stringify(sb)) return false;
|
|
154
|
+
const ka = Object.keys(a.packages).sort();
|
|
155
|
+
const kb = Object.keys(b.packages).sort();
|
|
156
|
+
if (JSON.stringify(ka) !== JSON.stringify(kb)) return false;
|
|
157
|
+
for (const k of ka) {
|
|
158
|
+
const ma = normalizeMembers(a.packages[k] ?? []);
|
|
159
|
+
const mb = normalizeMembers(b.packages[k] ?? []);
|
|
160
|
+
if (JSON.stringify(ma) !== JSON.stringify(mb)) return false;
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function cloneState(s: SkillState): SkillState {
|
|
166
|
+
const packages: SkillPackages = {};
|
|
167
|
+
for (const [k, v] of Object.entries(s.packages)) packages[k] = [...v];
|
|
168
|
+
return { disabled: s.disabled === undefined ? undefined : [...s.disabled], packages };
|
|
169
|
+
}
|