@litfamily/litcodex 1.0.1 → 1.0.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 +18 -21
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +2 -38
- package/dist/install/generated-skill-payload-hashes.d.ts +1 -6
- package/dist/install/generated-skill-payload-hashes.js +1 -6
- package/dist/install/skill-catalog.d.ts +1 -1
- package/dist/install/skill-catalog.js +1 -6
- package/dist/install/skill-resource-hashes.d.ts +1 -1
- package/dist/install/skill-resource-hashes.js +1 -1
- package/marketplace/plugins/litcodex/.codex-plugin/plugin.json +1 -1
- package/marketplace/plugins/litcodex/components/auto-update/package.json +1 -1
- package/marketplace/plugins/litcodex/components/comment-checker/package.json +1 -1
- package/marketplace/plugins/litcodex/components/git-bash/package.json +1 -1
- package/marketplace/plugins/litcodex/components/lit-loop/dist/cli.js +3 -20
- package/marketplace/plugins/litcodex/components/lit-loop/dist/codex-hook.js +1 -8
- package/marketplace/plugins/litcodex/components/lit-loop/dist/hook-cli.d.ts +2 -1
- package/marketplace/plugins/litcodex/components/lit-loop/dist/hook-cli.js +4 -27
- package/marketplace/plugins/litcodex/components/lit-loop/dist/markers.d.ts +0 -2
- package/marketplace/plugins/litcodex/components/lit-loop/dist/markers.js +0 -2
- package/marketplace/plugins/litcodex/components/lit-loop/dist/modes.d.ts +1 -3
- package/marketplace/plugins/litcodex/components/lit-loop/dist/modes.js +1 -10
- package/marketplace/plugins/litcodex/components/lit-loop/package.json +1 -1
- package/marketplace/plugins/litcodex/components/lsp/package.json +1 -1
- package/marketplace/plugins/litcodex/components/rules/dist/static-injection.js +2 -2
- package/marketplace/plugins/litcodex/components/rules/package.json +1 -1
- package/marketplace/plugins/litcodex/components/start-work-continuation/package.json +1 -1
- package/marketplace/plugins/litcodex/components/telemetry/package.json +1 -1
- package/marketplace/plugins/litcodex/components/wikify-knowledge/package.json +1 -1
- package/marketplace/plugins/litcodex/skills/browser-drive/scripts/capability-probe.mjs +13 -5
- package/node_modules/@litcodex/lit-loop/README.md +1 -7
- package/node_modules/@litcodex/lit-loop/dist/cli.js +3 -20
- package/node_modules/@litcodex/lit-loop/dist/codex-hook.js +1 -8
- package/node_modules/@litcodex/lit-loop/dist/hook-cli.d.ts +2 -1
- package/node_modules/@litcodex/lit-loop/dist/hook-cli.js +4 -27
- package/node_modules/@litcodex/lit-loop/dist/markers.d.ts +0 -2
- package/node_modules/@litcodex/lit-loop/dist/markers.js +0 -2
- package/node_modules/@litcodex/lit-loop/dist/modes.d.ts +1 -3
- package/node_modules/@litcodex/lit-loop/dist/modes.js +1 -10
- package/node_modules/@litcodex/lit-loop/package.json +1 -1
- package/node_modules/@litcodex/lit-loop/skills/browser-drive/scripts/capability-probe.mjs +13 -5
- package/package.json +2 -2
- package/readme-assets/badge-version.svg +1 -1
- package/readme-assets/cover-motion.webp +0 -0
- package/marketplace/plugins/litcodex/components/lit-loop/directives/skill-observer.md +0 -105
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/apply.d.ts +0 -16
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/apply.js +0 -227
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/config.d.ts +0 -13
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/config.js +0 -12
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/curator.d.ts +0 -14
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/curator.js +0 -112
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/hook.d.ts +0 -19
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/hook.js +0 -435
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/ownership.d.ts +0 -6
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/ownership.js +0 -145
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/paths.d.ts +0 -18
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/paths.js +0 -43
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/review.d.ts +0 -28
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/review.js +0 -702
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/store.d.ts +0 -32
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/store.js +0 -667
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/transaction.d.ts +0 -26
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/transaction.js +0 -524
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/types.d.ts +0 -81
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/types.js +0 -246
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/usage.d.ts +0 -6
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop/usage.js +0 -119
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop-cli.d.ts +0 -8
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-loop-cli.js +0 -111
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-observer-cli.d.ts +0 -3
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-observer-cli.js +0 -116
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-observer-route.d.ts +0 -6
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-observer-route.js +0 -12
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-observer.d.ts +0 -18
- package/marketplace/plugins/litcodex/components/lit-loop/dist/skill-observer.js +0 -946
- package/marketplace/plugins/litcodex/skills/skill-observer/SKILL.md +0 -215
- package/marketplace/plugins/litcodex/skills/skill-observer/agents/openai.yaml +0 -6
- package/marketplace/plugins/litcodex/skills/skill-observer/references/review-contract.md +0 -77
- package/node_modules/@litcodex/lit-loop/directives/skill-observer.md +0 -105
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/apply.d.ts +0 -16
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/apply.js +0 -227
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/config.d.ts +0 -13
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/config.js +0 -12
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/curator.d.ts +0 -14
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/curator.js +0 -112
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/hook.d.ts +0 -19
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/hook.js +0 -435
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/ownership.d.ts +0 -6
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/ownership.js +0 -145
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/paths.d.ts +0 -18
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/paths.js +0 -43
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/review.d.ts +0 -28
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/review.js +0 -702
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/store.d.ts +0 -32
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/store.js +0 -667
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/transaction.d.ts +0 -26
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/transaction.js +0 -524
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/types.d.ts +0 -81
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/types.js +0 -246
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/usage.d.ts +0 -6
- package/node_modules/@litcodex/lit-loop/dist/skill-loop/usage.js +0 -119
- package/node_modules/@litcodex/lit-loop/dist/skill-loop-cli.d.ts +0 -8
- package/node_modules/@litcodex/lit-loop/dist/skill-loop-cli.js +0 -111
- package/node_modules/@litcodex/lit-loop/dist/skill-observer-cli.d.ts +0 -3
- package/node_modules/@litcodex/lit-loop/dist/skill-observer-cli.js +0 -116
- package/node_modules/@litcodex/lit-loop/dist/skill-observer-route.d.ts +0 -6
- package/node_modules/@litcodex/lit-loop/dist/skill-observer-route.js +0 -12
- package/node_modules/@litcodex/lit-loop/dist/skill-observer.d.ts +0 -18
- package/node_modules/@litcodex/lit-loop/dist/skill-observer.js +0 -946
- package/node_modules/@litcodex/lit-loop/skills/skill-observer/SKILL.md +0 -215
- package/node_modules/@litcodex/lit-loop/skills/skill-observer/references/review-contract.md +0 -77
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import { redactSecretsInValue } from "../redaction.js";
|
|
3
|
-
import { SKILL_RENAMES } from "../skill-renames.js";
|
|
4
|
-
export class SkillLoopError extends Error {
|
|
5
|
-
constructor(code, message) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.name = "SkillLoopError";
|
|
8
|
-
this.code = code;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
export const SHIPPED_SKILL_IDS = Object.freeze(new Set([
|
|
12
|
-
"autoconference",
|
|
13
|
-
"autoresearch",
|
|
14
|
-
"browser-drive",
|
|
15
|
-
"coding-session-audit",
|
|
16
|
-
"comment-checker",
|
|
17
|
-
"debugging",
|
|
18
|
-
"deep-interview",
|
|
19
|
-
"frontend-ui-ux",
|
|
20
|
-
"lit-commit",
|
|
21
|
-
"lit-crucible",
|
|
22
|
-
"lit-init",
|
|
23
|
-
"lit-korean",
|
|
24
|
-
"lit-comprehend",
|
|
25
|
-
"lit-handoff",
|
|
26
|
-
"lit-loop",
|
|
27
|
-
"lit-plan",
|
|
28
|
-
"lit-recap",
|
|
29
|
-
"lit-scientific-visualization",
|
|
30
|
-
"litcodex-contribute-bug-fix",
|
|
31
|
-
"litcodex-doctor",
|
|
32
|
-
"litcodex-report-bug",
|
|
33
|
-
"litgoal",
|
|
34
|
-
"litresearch",
|
|
35
|
-
"litwork",
|
|
36
|
-
"lsp",
|
|
37
|
-
"lsp-setup",
|
|
38
|
-
"lit-code",
|
|
39
|
-
"lit-fetch",
|
|
40
|
-
"refactor",
|
|
41
|
-
"lit-burnoff",
|
|
42
|
-
"lit-burnoff-file",
|
|
43
|
-
...Object.keys(SKILL_RENAMES),
|
|
44
|
-
"review-work",
|
|
45
|
-
"rules",
|
|
46
|
-
"skill-observer",
|
|
47
|
-
"start-work",
|
|
48
|
-
"structural-search",
|
|
49
|
-
"lit-team",
|
|
50
|
-
"visual-qa",
|
|
51
|
-
"wikify",
|
|
52
|
-
]));
|
|
53
|
-
const SHIPPED_SKILL_IDENTITIES = new Set([...SHIPPED_SKILL_IDS].map((skillId) => canonicalPathIdentity(skillId)));
|
|
54
|
-
const PROPOSAL_KEYS = new Set([
|
|
55
|
-
"schema",
|
|
56
|
-
"id",
|
|
57
|
-
"createdAt",
|
|
58
|
-
"host",
|
|
59
|
-
"sessionRef",
|
|
60
|
-
"signal",
|
|
61
|
-
"targetSkill",
|
|
62
|
-
"targetRoot",
|
|
63
|
-
"action",
|
|
64
|
-
"patch",
|
|
65
|
-
"createSpec",
|
|
66
|
-
"rationale",
|
|
67
|
-
"evidenceRefs",
|
|
68
|
-
"status",
|
|
69
|
-
"ledgerEntryId",
|
|
70
|
-
]);
|
|
71
|
-
const PATCH_KEYS = new Set(["file", "oldString", "newString"]);
|
|
72
|
-
const CREATE_SPEC_KEYS = new Set(["files"]);
|
|
73
|
-
const CREATE_FILE_KEYS = new Set(["file", "content"]);
|
|
74
|
-
const ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
75
|
-
const SKILL_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
76
|
-
const RFC3339 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/u;
|
|
77
|
-
const CONTROL = /[\u0000-\u001f\u007f-\u009f]/u;
|
|
78
|
-
const PORTABLE_FORBIDDEN = /[:*?"<>|\\]/u;
|
|
79
|
-
const DEVICE = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu;
|
|
80
|
-
function isRecord(value) {
|
|
81
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
82
|
-
}
|
|
83
|
-
function assertOnlyKeys(record, keys) {
|
|
84
|
-
if (Object.keys(record).some((key) => !keys.has(key))) {
|
|
85
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the proposal contains an unknown field");
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function text(record, key, min, max) {
|
|
89
|
-
const value = record[key];
|
|
90
|
-
if (typeof value !== "string" || value.length < min || value.length > max) {
|
|
91
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", `the proposal ${key} field is invalid`);
|
|
92
|
-
}
|
|
93
|
-
return value;
|
|
94
|
-
}
|
|
95
|
-
export function canonicalPathIdentity(path) {
|
|
96
|
-
return path
|
|
97
|
-
.split("/")
|
|
98
|
-
.map((segment) => segment
|
|
99
|
-
.normalize("NFKC")
|
|
100
|
-
.toLocaleLowerCase("en-US")
|
|
101
|
-
.replace(/[. ]+$/u, ""))
|
|
102
|
-
.join("/");
|
|
103
|
-
}
|
|
104
|
-
export function validateTargetSkillIdentity(skillId) {
|
|
105
|
-
const identity = canonicalPathIdentity(skillId);
|
|
106
|
-
if (!SKILL_ID.test(skillId) ||
|
|
107
|
-
identity !== skillId ||
|
|
108
|
-
DEVICE.test(skillId) ||
|
|
109
|
-
SHIPPED_SKILL_IDENTITIES.has(identity)) {
|
|
110
|
-
throw new SkillLoopError("TARGET_NOT_AGENT_OWNED", "the proposal target is not a portable agent-owned id");
|
|
111
|
-
}
|
|
112
|
-
return skillId;
|
|
113
|
-
}
|
|
114
|
-
export function validateRelativeFile(file, targetSkill) {
|
|
115
|
-
if (file.length === 0 ||
|
|
116
|
-
file.length > 1024 ||
|
|
117
|
-
file.startsWith("/") ||
|
|
118
|
-
/^[A-Za-z]:/u.test(file) ||
|
|
119
|
-
file.includes("//") ||
|
|
120
|
-
CONTROL.test(file) ||
|
|
121
|
-
PORTABLE_FORBIDDEN.test(file)) {
|
|
122
|
-
throw new SkillLoopError("PROPOSAL_PATH_INVALID", "the proposal file path is not portable");
|
|
123
|
-
}
|
|
124
|
-
const segments = file.split("/");
|
|
125
|
-
if (segments.some((segment) => segment === "" || segment === "." || segment === ".." || /[. ]$/u.test(segment) || DEVICE.test(segment))) {
|
|
126
|
-
throw new SkillLoopError("PROPOSAL_PATH_INVALID", "the proposal file path is not portable");
|
|
127
|
-
}
|
|
128
|
-
const identity = canonicalPathIdentity(file);
|
|
129
|
-
const targetIdentity = canonicalPathIdentity(targetSkill);
|
|
130
|
-
if (identity === targetIdentity || identity.startsWith(`${targetIdentity}/`)) {
|
|
131
|
-
throw new SkillLoopError("PROPOSAL_PATH_INVALID", "proposal files are relative to the target skill");
|
|
132
|
-
}
|
|
133
|
-
return file;
|
|
134
|
-
}
|
|
135
|
-
function validatePatch(value, targetSkill) {
|
|
136
|
-
if (!isRecord(value))
|
|
137
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the patch is invalid");
|
|
138
|
-
assertOnlyKeys(value, PATCH_KEYS);
|
|
139
|
-
return {
|
|
140
|
-
file: validateRelativeFile(text(value, "file", 1, 1024), targetSkill),
|
|
141
|
-
oldString: text(value, "oldString", 1, 65_536),
|
|
142
|
-
newString: text(value, "newString", 0, 65_536),
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
function validateCreateSpec(value, targetSkill) {
|
|
146
|
-
if (!isRecord(value))
|
|
147
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the createSpec is invalid");
|
|
148
|
-
assertOnlyKeys(value, CREATE_SPEC_KEYS);
|
|
149
|
-
if (!Array.isArray(value["files"]) || value["files"].length < 1 || value["files"].length > 64) {
|
|
150
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the createSpec files are invalid");
|
|
151
|
-
}
|
|
152
|
-
const files = value["files"].map((item) => {
|
|
153
|
-
if (!isRecord(item))
|
|
154
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "a createSpec file is invalid");
|
|
155
|
-
assertOnlyKeys(item, CREATE_FILE_KEYS);
|
|
156
|
-
return {
|
|
157
|
-
file: validateRelativeFile(text(item, "file", 1, 1024), targetSkill),
|
|
158
|
-
content: text(item, "content", 0, 65_536),
|
|
159
|
-
};
|
|
160
|
-
});
|
|
161
|
-
const identities = files.map((file) => canonicalPathIdentity(file.file));
|
|
162
|
-
if (new Set(identities).size !== files.length) {
|
|
163
|
-
throw new SkillLoopError("PROPOSAL_PATH_INVALID", "createSpec files collide by canonical path identity");
|
|
164
|
-
}
|
|
165
|
-
return { files };
|
|
166
|
-
}
|
|
167
|
-
function assertNoSecret(value) {
|
|
168
|
-
const serialized = JSON.stringify(value);
|
|
169
|
-
const redacted = JSON.stringify(redactSecretsInValue(value));
|
|
170
|
-
if (serialized !== redacted) {
|
|
171
|
-
throw new SkillLoopError("PROPOSAL_SECRET_REFUSED", "the proposal contained secret-shaped data");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
export function validateProposal(value, expectedRoot) {
|
|
175
|
-
if (!isRecord(value) || Buffer.byteLength(JSON.stringify(value)) > 512_000) {
|
|
176
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the proposal is invalid or oversized");
|
|
177
|
-
}
|
|
178
|
-
assertOnlyKeys(value, PROPOSAL_KEYS);
|
|
179
|
-
assertNoSecret(value);
|
|
180
|
-
const id = text(value, "id", 1, 128);
|
|
181
|
-
const createdAt = text(value, "createdAt", 1, 64);
|
|
182
|
-
const targetSkill = text(value, "targetSkill", 1, 128);
|
|
183
|
-
const targetRoot = text(value, "targetRoot", 1, 2048);
|
|
184
|
-
const status = value["status"];
|
|
185
|
-
const action = value["action"];
|
|
186
|
-
if (!ID.test(id) || !RFC3339.test(createdAt) || Number.isNaN(Date.parse(createdAt))) {
|
|
187
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the proposal identity or timestamp is invalid");
|
|
188
|
-
}
|
|
189
|
-
validateTargetSkillIdentity(targetSkill);
|
|
190
|
-
if (value["schema"] !== "litfamily.skill-proposal/v1" || value["host"] !== "litcodex") {
|
|
191
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the proposal schema or host is invalid");
|
|
192
|
-
}
|
|
193
|
-
if (resolve(targetRoot) !== resolve(expectedRoot)) {
|
|
194
|
-
throw new SkillLoopError("TARGET_NOT_AGENT_OWNED", "the proposal target is not agent-owned");
|
|
195
|
-
}
|
|
196
|
-
if (!["patch", "create", "add-reference", "archive"].includes(action)) {
|
|
197
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the proposal action is invalid");
|
|
198
|
-
}
|
|
199
|
-
if (!["pending", "approved", "applied", "rejected", "rolled-back"].includes(status)) {
|
|
200
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the proposal status is invalid");
|
|
201
|
-
}
|
|
202
|
-
const evidenceRefs = value["evidenceRefs"];
|
|
203
|
-
if (!Array.isArray(evidenceRefs) ||
|
|
204
|
-
evidenceRefs.length > 32 ||
|
|
205
|
-
evidenceRefs.some((item) => typeof item !== "string" || item.length < 1 || item.length > 2048) ||
|
|
206
|
-
new Set(evidenceRefs).size !== evidenceRefs.length) {
|
|
207
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the proposal evidenceRefs are invalid");
|
|
208
|
-
}
|
|
209
|
-
const ledgerEntryId = value["ledgerEntryId"];
|
|
210
|
-
const terminal = status === "applied" || status === "rejected" || status === "rolled-back";
|
|
211
|
-
if (terminal !== (typeof ledgerEntryId === "string" && ledgerEntryId.length > 0)) {
|
|
212
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "the proposal ledger status invariant is invalid");
|
|
213
|
-
}
|
|
214
|
-
let patch;
|
|
215
|
-
let createSpec;
|
|
216
|
-
if (action === "patch") {
|
|
217
|
-
patch = validatePatch(value["patch"], targetSkill);
|
|
218
|
-
if (value["createSpec"] !== undefined)
|
|
219
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "patch forbids createSpec");
|
|
220
|
-
}
|
|
221
|
-
else if (action === "create" || action === "add-reference") {
|
|
222
|
-
createSpec = validateCreateSpec(value["createSpec"], targetSkill);
|
|
223
|
-
if (value["patch"] !== undefined)
|
|
224
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "create forbids patch");
|
|
225
|
-
}
|
|
226
|
-
else if (value["patch"] !== undefined || value["createSpec"] !== undefined) {
|
|
227
|
-
throw new SkillLoopError("PROPOSAL_SCHEMA_INVALID", "archive forbids content payloads");
|
|
228
|
-
}
|
|
229
|
-
return {
|
|
230
|
-
schema: "litfamily.skill-proposal/v1",
|
|
231
|
-
id,
|
|
232
|
-
createdAt,
|
|
233
|
-
host: "litcodex",
|
|
234
|
-
sessionRef: text(value, "sessionRef", 1, 512),
|
|
235
|
-
signal: text(value, "signal", 1, 128),
|
|
236
|
-
targetSkill,
|
|
237
|
-
targetRoot: resolve(targetRoot),
|
|
238
|
-
action: action,
|
|
239
|
-
...(patch === undefined ? {} : { patch }),
|
|
240
|
-
...(createSpec === undefined ? {} : { createSpec }),
|
|
241
|
-
rationale: text(value, "rationale", 1, 8192),
|
|
242
|
-
evidenceRefs: evidenceRefs,
|
|
243
|
-
status: status,
|
|
244
|
-
...(typeof ledgerEntryId === "string" ? { ledgerEntryId } : {}),
|
|
245
|
-
};
|
|
246
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { SkillLoopPaths } from "./paths.js";
|
|
2
|
-
import { type SkillUsageSidecar } from "./types.js";
|
|
3
|
-
export declare function validateUsageSidecar(value: unknown): SkillUsageSidecar;
|
|
4
|
-
export declare function nextAppliedUsage(paths: SkillLoopPaths, skillId: string, timestamp: string): SkillUsageSidecar;
|
|
5
|
-
export declare function nextAppliedUsageFrom(current: SkillUsageSidecar | null, skillId: string, timestamp: string): SkillUsageSidecar;
|
|
6
|
-
export declare function recordAppliedUsage(paths: SkillLoopPaths, skillId: string, timestamp: string): void;
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { readUsage, writeUsage } from "./store.js";
|
|
2
|
-
import { SkillLoopError } from "./types.js";
|
|
3
|
-
function validDate(value) {
|
|
4
|
-
return typeof value === "string" && value.length > 0 && !Number.isNaN(Date.parse(value));
|
|
5
|
-
}
|
|
6
|
-
function validateEntry(value, updatedAt) {
|
|
7
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
8
|
-
return false;
|
|
9
|
-
const entry = value;
|
|
10
|
-
const required = [
|
|
11
|
-
"state",
|
|
12
|
-
"use_count",
|
|
13
|
-
"view_count",
|
|
14
|
-
"patch_count",
|
|
15
|
-
"created_at",
|
|
16
|
-
"created_by",
|
|
17
|
-
"last_used_at",
|
|
18
|
-
"last_viewed_at",
|
|
19
|
-
"last_patched_at",
|
|
20
|
-
"archived_at",
|
|
21
|
-
"pinned",
|
|
22
|
-
];
|
|
23
|
-
if (Object.keys(entry).length !== required.length || required.some((key) => !Object.hasOwn(entry, key)))
|
|
24
|
-
return false;
|
|
25
|
-
if (!["active", "stale", "archived"].includes(entry["state"]))
|
|
26
|
-
return false;
|
|
27
|
-
for (const field of ["use_count", "view_count", "patch_count"]) {
|
|
28
|
-
const count = entry[field];
|
|
29
|
-
if (typeof count !== "number" || !Number.isInteger(count) || count < 0)
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
const createdAtValue = entry["created_at"];
|
|
33
|
-
if (!validDate(createdAtValue) || Date.parse(createdAtValue) > updatedAt)
|
|
34
|
-
return false;
|
|
35
|
-
const createdAt = Date.parse(createdAtValue);
|
|
36
|
-
const useCount = entry["use_count"];
|
|
37
|
-
const viewCount = entry["view_count"];
|
|
38
|
-
const patchCount = entry["patch_count"];
|
|
39
|
-
if (!["agent", "user", "installed", "curator", null].includes(entry["created_by"]))
|
|
40
|
-
return false;
|
|
41
|
-
for (const field of ["last_used_at", "last_viewed_at", "last_patched_at", "archived_at"]) {
|
|
42
|
-
const date = entry[field];
|
|
43
|
-
if (date !== null) {
|
|
44
|
-
if (!validDate(date) || Date.parse(date) < createdAt || Date.parse(date) > updatedAt)
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if ((entry["state"] === "archived") !== (entry["archived_at"] !== null))
|
|
49
|
-
return false;
|
|
50
|
-
if (useCount > 0 && entry["last_used_at"] === null)
|
|
51
|
-
return false;
|
|
52
|
-
if (viewCount > 0 && entry["last_viewed_at"] === null)
|
|
53
|
-
return false;
|
|
54
|
-
if (patchCount > 0 && entry["last_patched_at"] === null)
|
|
55
|
-
return false;
|
|
56
|
-
return typeof entry["pinned"] === "boolean";
|
|
57
|
-
}
|
|
58
|
-
export function validateUsageSidecar(value) {
|
|
59
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
60
|
-
throw new SkillLoopError("USAGE_INVALID", "the usage sidecar is invalid");
|
|
61
|
-
}
|
|
62
|
-
const sidecar = value;
|
|
63
|
-
if (Object.keys(sidecar).some((key) => !["schema", "updatedAt", "skills"].includes(key)) ||
|
|
64
|
-
sidecar["schema"] !== "litfamily.skill-usage/v1" ||
|
|
65
|
-
!validDate(sidecar["updatedAt"]) ||
|
|
66
|
-
typeof sidecar["skills"] !== "object" ||
|
|
67
|
-
sidecar["skills"] === null ||
|
|
68
|
-
Array.isArray(sidecar["skills"])) {
|
|
69
|
-
throw new SkillLoopError("USAGE_INVALID", "the usage sidecar is invalid");
|
|
70
|
-
}
|
|
71
|
-
const updatedAt = Date.parse(sidecar["updatedAt"]);
|
|
72
|
-
for (const [id, entry] of Object.entries(sidecar["skills"])) {
|
|
73
|
-
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(id) || !validateEntry(entry, updatedAt)) {
|
|
74
|
-
throw new SkillLoopError("USAGE_INVALID", "the usage sidecar contains an invalid entry");
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return sidecar;
|
|
78
|
-
}
|
|
79
|
-
export function nextAppliedUsage(paths, skillId, timestamp) {
|
|
80
|
-
const current = readUsage(paths);
|
|
81
|
-
return nextAppliedUsageFrom(current, skillId, timestamp);
|
|
82
|
-
}
|
|
83
|
-
export function nextAppliedUsageFrom(current, skillId, timestamp) {
|
|
84
|
-
const sidecar = current === null
|
|
85
|
-
? { schema: "litfamily.skill-usage/v1", updatedAt: timestamp, skills: {} }
|
|
86
|
-
: validateUsageSidecar(current);
|
|
87
|
-
const previous = sidecar.skills[skillId];
|
|
88
|
-
if (previous?.last_patched_at === timestamp && sidecar.updatedAt === timestamp)
|
|
89
|
-
return sidecar;
|
|
90
|
-
const entry = previous === undefined
|
|
91
|
-
? {
|
|
92
|
-
state: "active",
|
|
93
|
-
use_count: 0,
|
|
94
|
-
view_count: 0,
|
|
95
|
-
patch_count: 1,
|
|
96
|
-
created_at: timestamp,
|
|
97
|
-
created_by: "agent",
|
|
98
|
-
last_used_at: null,
|
|
99
|
-
last_viewed_at: null,
|
|
100
|
-
last_patched_at: timestamp,
|
|
101
|
-
archived_at: null,
|
|
102
|
-
pinned: false,
|
|
103
|
-
}
|
|
104
|
-
: {
|
|
105
|
-
...previous,
|
|
106
|
-
state: "active",
|
|
107
|
-
patch_count: previous.patch_count + 1,
|
|
108
|
-
last_patched_at: timestamp,
|
|
109
|
-
archived_at: null,
|
|
110
|
-
};
|
|
111
|
-
return {
|
|
112
|
-
schema: "litfamily.skill-usage/v1",
|
|
113
|
-
updatedAt: timestamp,
|
|
114
|
-
skills: { ...sidecar.skills, [skillId]: entry },
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
export function recordAppliedUsage(paths, skillId, timestamp) {
|
|
118
|
-
writeUsage(paths, nextAppliedUsage(paths, skillId, timestamp));
|
|
119
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export interface SkillLoopCliContext {
|
|
2
|
-
readonly cwd: string;
|
|
3
|
-
readonly env: NodeJS.ProcessEnv;
|
|
4
|
-
readonly stdout: NodeJS.WritableStream;
|
|
5
|
-
readonly stderr: NodeJS.WritableStream;
|
|
6
|
-
}
|
|
7
|
-
export declare const SKILL_LOOP_SUBCOMMANDS: readonly ["propose", "list", "apply", "reject", "rollback", "review", "curator"];
|
|
8
|
-
export declare function skillLoopCommand(args: readonly string[], context: SkillLoopCliContext): number;
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { existsSync, lstatSync, readFileSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import { applyProposal, rejectProposal, rollbackMutation } from "./skill-loop/apply.js";
|
|
4
|
-
import { runCurator } from "./skill-loop/curator.js";
|
|
5
|
-
import { agentSkillRoot } from "./skill-loop/paths.js";
|
|
6
|
-
import { runCodexReview } from "./skill-loop/review.js";
|
|
7
|
-
import { listProposals, storeProposal } from "./skill-loop/store.js";
|
|
8
|
-
import { SkillLoopError } from "./skill-loop/types.js";
|
|
9
|
-
const MAX_PROPOSAL_FILE_BYTES = 512_000;
|
|
10
|
-
export const SKILL_LOOP_SUBCOMMANDS = Object.freeze([
|
|
11
|
-
"propose",
|
|
12
|
-
"list",
|
|
13
|
-
"apply",
|
|
14
|
-
"reject",
|
|
15
|
-
"rollback",
|
|
16
|
-
"review",
|
|
17
|
-
"curator",
|
|
18
|
-
]);
|
|
19
|
-
function writeJson(stream, value) {
|
|
20
|
-
stream.write(`${JSON.stringify(value)}\n`);
|
|
21
|
-
}
|
|
22
|
-
function readProposalFile(cwd, input) {
|
|
23
|
-
if (!input)
|
|
24
|
-
throw new SkillLoopError("PROPOSAL_FILE_REQUIRED", "propose requires one JSON file");
|
|
25
|
-
const path = resolve(cwd, input);
|
|
26
|
-
if (!existsSync(path))
|
|
27
|
-
throw new SkillLoopError("PROPOSAL_FILE_MISSING", "the proposal file was not found");
|
|
28
|
-
const stat = lstatSync(path);
|
|
29
|
-
if (stat.isSymbolicLink() || !stat.isFile() || stat.size > MAX_PROPOSAL_FILE_BYTES) {
|
|
30
|
-
throw new SkillLoopError("PROPOSAL_FILE_UNSAFE", "the proposal file is unsafe or oversized");
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
return JSON.parse(readFileSync(path, "utf8"));
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
throw new SkillLoopError("PROPOSAL_FILE_INVALID", "the proposal file is not valid JSON");
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function requireSingle(args, command) {
|
|
40
|
-
if (args.length !== 2 || !args[1])
|
|
41
|
-
throw new SkillLoopError("SKILL_LOOP_USAGE", `${command} requires one id`);
|
|
42
|
-
return args[1];
|
|
43
|
-
}
|
|
44
|
-
export function skillLoopCommand(args, context) {
|
|
45
|
-
try {
|
|
46
|
-
const command = args[0];
|
|
47
|
-
if (command === "propose") {
|
|
48
|
-
if (args.length !== 2)
|
|
49
|
-
throw new SkillLoopError("SKILL_LOOP_USAGE", "propose requires one JSON file");
|
|
50
|
-
const proposal = storeProposal(context.cwd, readProposalFile(context.cwd, args[1]), { env: context.env });
|
|
51
|
-
writeJson(context.stdout, { ok: true, id: proposal.id, status: proposal.status });
|
|
52
|
-
return 0;
|
|
53
|
-
}
|
|
54
|
-
if (command === "list") {
|
|
55
|
-
if (args.length !== 1)
|
|
56
|
-
throw new SkillLoopError("SKILL_LOOP_USAGE", "list accepts no arguments");
|
|
57
|
-
writeJson(context.stdout, { ok: true, proposals: listProposals(context.cwd, { env: context.env }) });
|
|
58
|
-
return 0;
|
|
59
|
-
}
|
|
60
|
-
if (command === "apply") {
|
|
61
|
-
const result = applyProposal(context.cwd, requireSingle(args, "apply"), { env: context.env });
|
|
62
|
-
writeJson(context.stdout, {
|
|
63
|
-
ok: true,
|
|
64
|
-
id: result.proposal.id,
|
|
65
|
-
status: result.proposal.status,
|
|
66
|
-
ledgerId: result.ledger.id,
|
|
67
|
-
target: resolve(agentSkillRoot(context.env), result.proposal.targetSkill),
|
|
68
|
-
});
|
|
69
|
-
return 0;
|
|
70
|
-
}
|
|
71
|
-
if (command === "reject") {
|
|
72
|
-
const result = rejectProposal(context.cwd, requireSingle(args, "reject"), { env: context.env });
|
|
73
|
-
writeJson(context.stdout, {
|
|
74
|
-
ok: true,
|
|
75
|
-
id: result.proposal.id,
|
|
76
|
-
status: result.proposal.status,
|
|
77
|
-
ledgerId: result.ledger.id,
|
|
78
|
-
});
|
|
79
|
-
return 0;
|
|
80
|
-
}
|
|
81
|
-
if (command === "rollback") {
|
|
82
|
-
const result = rollbackMutation(context.cwd, requireSingle(args, "rollback"), { env: context.env });
|
|
83
|
-
writeJson(context.stdout, {
|
|
84
|
-
ok: true,
|
|
85
|
-
status: result.proposal?.status ?? "rolled-back",
|
|
86
|
-
ledgerId: result.ledger.id,
|
|
87
|
-
restoresLedgerEntryId: result.ledger.restoresLedgerEntryId,
|
|
88
|
-
});
|
|
89
|
-
return 0;
|
|
90
|
-
}
|
|
91
|
-
if (command === "review") {
|
|
92
|
-
if (args.length !== 1)
|
|
93
|
-
throw new SkillLoopError("SKILL_LOOP_USAGE", "review accepts no arguments");
|
|
94
|
-
writeJson(context.stdout, runCodexReview(context.cwd, { env: context.env }));
|
|
95
|
-
return 0;
|
|
96
|
-
}
|
|
97
|
-
if (command === "curator") {
|
|
98
|
-
if (args.length !== 1)
|
|
99
|
-
throw new SkillLoopError("SKILL_LOOP_USAGE", "curator accepts no arguments");
|
|
100
|
-
writeJson(context.stdout, { ok: true, ...runCurator(context.cwd, { env: context.env }) });
|
|
101
|
-
return 0;
|
|
102
|
-
}
|
|
103
|
-
throw new SkillLoopError("SKILL_LOOP_USAGE", "usage: skill-loop <propose|list|apply|reject|rollback|review|curator>");
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
const code = error instanceof SkillLoopError ? error.code : "SKILL_LOOP_FAILED";
|
|
107
|
-
const message = error instanceof SkillLoopError ? error.message : "the skill-loop command failed";
|
|
108
|
-
writeJson(context.stderr, { ok: false, error: { code, message } });
|
|
109
|
-
return code === "REVIEW_EXEC_BLOCKED" ? 3 : 2;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { TextDecoder } from "node:util";
|
|
2
|
-
import { recordObservation, SkillObserverError } from "./skill-observer.js";
|
|
3
|
-
export const MAX_OBSERVER_STDIN_BYTES = 16_384;
|
|
4
|
-
export const OBSERVER_STDIN_TIMEOUT_MS = 5_000;
|
|
5
|
-
const OBSERVATION_LOG_RELATIVE_PATH = ".litcodex/skill-observer/observations.jsonl";
|
|
6
|
-
const UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
7
|
-
function writeJsonLine(stream, value) {
|
|
8
|
-
stream.write(`${JSON.stringify(value)}\n`);
|
|
9
|
-
}
|
|
10
|
-
function writeInputError(stream, code, message) {
|
|
11
|
-
writeJsonLine(stream, { ok: false, error: { code, message } });
|
|
12
|
-
}
|
|
13
|
-
function readBoundedStdin(stdin) {
|
|
14
|
-
return new Promise((resolve) => {
|
|
15
|
-
const chunks = [];
|
|
16
|
-
let total = 0;
|
|
17
|
-
let settled = false;
|
|
18
|
-
let timeout;
|
|
19
|
-
const onData = (chunk) => {
|
|
20
|
-
const bytes = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
21
|
-
total += bytes.byteLength;
|
|
22
|
-
if (total > MAX_OBSERVER_STDIN_BYTES) {
|
|
23
|
-
finish({ bytes: null, errorCode: "OBSERVER_STDIN_TOO_LARGE" });
|
|
24
|
-
stdin.pause();
|
|
25
|
-
const destroy = stdin.destroy;
|
|
26
|
-
if (typeof destroy === "function")
|
|
27
|
-
destroy.call(stdin);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
chunks.push(bytes);
|
|
31
|
-
};
|
|
32
|
-
const onEnd = () => finish({ bytes: Buffer.concat(chunks), errorCode: null });
|
|
33
|
-
const onError = () => finish({ bytes: null, errorCode: "OBSERVER_STDIN_UNAVAILABLE" });
|
|
34
|
-
const onClose = () => finish({ bytes: null, errorCode: "OBSERVER_STDIN_UNAVAILABLE" });
|
|
35
|
-
const cleanup = () => {
|
|
36
|
-
if (timeout !== undefined)
|
|
37
|
-
clearTimeout(timeout);
|
|
38
|
-
stdin.removeListener("data", onData);
|
|
39
|
-
stdin.removeListener("end", onEnd);
|
|
40
|
-
stdin.removeListener("error", onError);
|
|
41
|
-
stdin.removeListener("close", onClose);
|
|
42
|
-
};
|
|
43
|
-
const finish = (value) => {
|
|
44
|
-
if (settled)
|
|
45
|
-
return;
|
|
46
|
-
settled = true;
|
|
47
|
-
cleanup();
|
|
48
|
-
resolve(value);
|
|
49
|
-
};
|
|
50
|
-
stdin.on("data", onData);
|
|
51
|
-
stdin.on("end", onEnd);
|
|
52
|
-
stdin.on("error", onError);
|
|
53
|
-
stdin.on("close", onClose);
|
|
54
|
-
timeout = setTimeout(() => {
|
|
55
|
-
finish({ bytes: null, errorCode: "OBSERVER_STDIN_UNAVAILABLE" });
|
|
56
|
-
stdin.pause();
|
|
57
|
-
const destroy = stdin.destroy;
|
|
58
|
-
if (typeof destroy === "function")
|
|
59
|
-
destroy.call(stdin);
|
|
60
|
-
}, OBSERVER_STDIN_TIMEOUT_MS);
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
function storageError(error) {
|
|
64
|
-
if (error instanceof SkillObserverError) {
|
|
65
|
-
const message = error.code === "OBSERVER_RECORD_COMMITTED_CLEANUP_FAILED"
|
|
66
|
-
? "the record was committed, but cleanup failed; read the log before retrying"
|
|
67
|
-
: "the observer input was refused or could not be stored";
|
|
68
|
-
return { ok: false, error: { code: error.code, message } };
|
|
69
|
-
}
|
|
70
|
-
return { ok: false, error: { code: "OBSERVER_COMMAND_FAILED", message: "the observer command failed" } };
|
|
71
|
-
}
|
|
72
|
-
export async function runSkillObserverCli(stdin, stdout, stderr) {
|
|
73
|
-
const input = await readBoundedStdin(stdin);
|
|
74
|
-
if (input.errorCode !== null) {
|
|
75
|
-
const message = input.errorCode === "OBSERVER_STDIN_TOO_LARGE"
|
|
76
|
-
? "observer input exceeded the byte limit"
|
|
77
|
-
: "observer input stream was unavailable";
|
|
78
|
-
writeInputError(stderr, input.errorCode, message);
|
|
79
|
-
return 2;
|
|
80
|
-
}
|
|
81
|
-
const bytes = input.bytes;
|
|
82
|
-
if (bytes === null) {
|
|
83
|
-
writeInputError(stderr, "OBSERVER_STDIN_UNAVAILABLE", "observer input stream was unavailable");
|
|
84
|
-
return 2;
|
|
85
|
-
}
|
|
86
|
-
let raw;
|
|
87
|
-
try {
|
|
88
|
-
raw = UTF8_DECODER.decode(bytes);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
writeInputError(stderr, "OBSERVER_STDIN_INVALID_UTF8", "observer input was not valid UTF-8");
|
|
92
|
-
return 2;
|
|
93
|
-
}
|
|
94
|
-
if (raw.trim().length === 0) {
|
|
95
|
-
writeInputError(stderr, "OBSERVER_INPUT_EMPTY", "observer input was empty");
|
|
96
|
-
return 2;
|
|
97
|
-
}
|
|
98
|
-
let parsed;
|
|
99
|
-
try {
|
|
100
|
-
parsed = JSON.parse(raw);
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
writeInputError(stderr, "OBSERVER_STDIN_INVALID_JSON", "observer input was not valid JSON");
|
|
104
|
-
return 2;
|
|
105
|
-
}
|
|
106
|
-
try {
|
|
107
|
-
const root = process.cwd();
|
|
108
|
-
const record = recordObservation(root, parsed);
|
|
109
|
-
writeJsonLine(stdout, { ok: true, record, logPath: OBSERVATION_LOG_RELATIVE_PATH });
|
|
110
|
-
return 0;
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
writeJsonLine(stderr, storageError(error));
|
|
114
|
-
return 2;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type GuardTranscriptInput } from "./guards.js";
|
|
2
|
-
/** Match only the complete observer route or its exact Codex scoped shorthand. */
|
|
3
|
-
export declare function isExactSkillObserverPrompt(prompt: string): boolean;
|
|
4
|
-
export declare const isExactBareSkillObserverPrompt: typeof isExactSkillObserverPrompt;
|
|
5
|
-
/** Apply the existing per-mode idempotency and context-pressure guards. */
|
|
6
|
-
export declare function shouldSuppressSkillObserverInjection(transcriptPath: GuardTranscriptInput): boolean;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { transcriptHasContextPressureMarker, transcriptHasDirectiveMarker, } from "./guards.js";
|
|
2
|
-
import { SKILL_OBSERVER_DIRECTIVE_MARKER } from "./markers.js";
|
|
3
|
-
/** Match only the complete observer route or its exact Codex scoped shorthand. */
|
|
4
|
-
export function isExactSkillObserverPrompt(prompt) {
|
|
5
|
-
return /^\s*(?:skill-observer|\$litcodex:skill-observer)\s*$/iu.test(prompt);
|
|
6
|
-
}
|
|
7
|
-
export const isExactBareSkillObserverPrompt = isExactSkillObserverPrompt;
|
|
8
|
-
/** Apply the existing per-mode idempotency and context-pressure guards. */
|
|
9
|
-
export function shouldSuppressSkillObserverInjection(transcriptPath) {
|
|
10
|
-
return (transcriptHasDirectiveMarker(transcriptPath, SKILL_OBSERVER_DIRECTIVE_MARKER) ||
|
|
11
|
-
transcriptHasContextPressureMarker(transcriptPath));
|
|
12
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export declare const OBSERVER_SIGNALS: readonly ["correction", "repetition", "coverage-gap"];
|
|
2
|
-
export declare const OBSERVER_RECORD_STATE_UNKNOWN: "OBSERVER_RECORD_STATE_UNKNOWN";
|
|
3
|
-
export declare const OBSERVER_RECORD_COMMITTED_CLEANUP_FAILED: "OBSERVER_RECORD_COMMITTED_CLEANUP_FAILED";
|
|
4
|
-
export type ObserverSignal = (typeof OBSERVER_SIGNALS)[number];
|
|
5
|
-
export type SkillObservation = {
|
|
6
|
-
readonly signal: ObserverSignal;
|
|
7
|
-
readonly skillId: string;
|
|
8
|
-
readonly observed: string;
|
|
9
|
-
readonly proposal: string;
|
|
10
|
-
readonly applied: false;
|
|
11
|
-
};
|
|
12
|
-
export declare class SkillObserverError extends Error {
|
|
13
|
-
readonly code: string;
|
|
14
|
-
constructor(code: string, message: string);
|
|
15
|
-
}
|
|
16
|
-
export declare function observationLogPath(root?: string): string;
|
|
17
|
-
export declare function recordObservation(root: string, observation: unknown): SkillObservation;
|
|
18
|
-
export declare function readObservations(root?: string): readonly SkillObservation[];
|