@intentic/sandbox-contract 1.169.0 → 1.171.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-catalog.d.ts +1 -0
- package/dist/agent-catalog.d.ts.map +1 -1
- package/dist/agent-catalog.js +2 -1
- package/dist/agent-catalog.js.map +1 -1
- package/dist/chores/chores.d.ts +45 -0
- package/dist/chores/chores.d.ts.map +1 -0
- package/dist/chores/chores.js +487 -0
- package/dist/chores/chores.js.map +1 -0
- package/dist/chores/digest.d.ts +3 -0
- package/dist/chores/digest.d.ts.map +1 -0
- package/dist/chores/digest.js +0 -0
- package/dist/chores/digest.js.map +1 -0
- package/dist/chores/index.d.ts +10 -0
- package/dist/chores/index.d.ts.map +1 -0
- package/dist/chores/index.js +6 -0
- package/dist/chores/index.js.map +1 -0
- package/dist/chores/probes.d.ts +15 -0
- package/dist/chores/probes.d.ts.map +1 -0
- package/dist/chores/probes.js +177 -0
- package/dist/chores/probes.js.map +1 -0
- package/dist/chores/prompt.d.ts +14 -0
- package/dist/chores/prompt.d.ts.map +1 -0
- package/dist/chores/prompt.js +12 -0
- package/dist/chores/prompt.js.map +1 -0
- package/dist/chores/verdict.d.ts +20 -0
- package/dist/chores/verdict.d.ts.map +1 -0
- package/dist/chores/verdict.js +59 -0
- package/dist/chores/verdict.js.map +1 -0
- package/dist/contracts/agent.contract.d.ts +22 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +2 -1
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +2 -0
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/automations.contract.d.ts +54 -0
- package/dist/contracts/automations.contract.d.ts.map +1 -1
- package/dist/contracts/chores.contract.d.ts +151 -0
- package/dist/contracts/chores.contract.d.ts.map +1 -0
- package/dist/contracts/chores.contract.js +8 -0
- package/dist/contracts/chores.contract.js.map +1 -0
- package/dist/contracts/extensions.contract.d.ts +7 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/extensions.contract.js +2 -1
- package/dist/contracts/extensions.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +14 -14
- package/dist/contracts/workspace.contract.d.ts +11 -24
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/events.d.ts +10 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +7 -1
- package/dist/events.js.map +1 -1
- package/dist/hostnames.d.ts +0 -1
- package/dist/hostnames.d.ts.map +1 -1
- package/dist/hostnames.js +0 -1
- package/dist/hostnames.js.map +1 -1
- package/dist/index.d.ts +261 -38
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +618 -40
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +145 -2
- package/dist/schemas.js.map +1 -1
- package/dist/tunnel-ids.d.ts +2 -0
- package/dist/tunnel-ids.d.ts.map +1 -1
- package/dist/tunnel-ids.js +2 -0
- package/dist/tunnel-ids.js.map +1 -1
- package/dist/workspace-state.d.ts.map +1 -1
- package/dist/workspace-state.js +9 -0
- package/dist/workspace-state.js.map +1 -1
- package/package.json +14 -2
- package/src/agent-catalog.test.ts +32 -1
- package/src/agent-catalog.ts +25 -7
- package/src/chores/chores.ts +837 -0
- package/src/chores/digest.test.ts +30 -0
- package/src/chores/digest.ts +0 -0
- package/src/chores/index.ts +9 -0
- package/src/chores/probes.test.ts +166 -0
- package/src/chores/probes.ts +273 -0
- package/src/chores/prompt.ts +64 -0
- package/src/chores/verdict.test.ts +394 -0
- package/src/chores/verdict.ts +167 -0
- package/src/contracts/agent.contract.ts +14 -1
- package/src/contracts/chores.contract.ts +23 -0
- package/src/contracts/extensions.contract.ts +5 -0
- package/src/events.ts +15 -2
- package/src/hostnames.ts +4 -6
- package/src/index.ts +3 -0
- package/src/schemas.ts +427 -19
- package/src/tunnel-ids.test.ts +49 -0
- package/src/tunnel-ids.ts +24 -0
- package/src/workspace-state.ts +13 -0
package/dist/agent-catalog.d.ts
CHANGED
|
@@ -43,4 +43,5 @@ export declare const limitationsOf: (capabilities: AgentCapabilities) => string[
|
|
|
43
43
|
export declare const CLAUDE_SEED_MODELS: readonly Model[];
|
|
44
44
|
export declare const modelsFor: (provider: AgentProvider) => CatalogOption[];
|
|
45
45
|
export declare const effortAllowed: (effort: string, provider: AgentProvider, thinking: boolean) => boolean;
|
|
46
|
+
export declare const sendableEffort: (effort: string | undefined, thinking: boolean | undefined) => string | undefined;
|
|
46
47
|
//# sourceMappingURL=agent-catalog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-catalog.d.ts","sourceRoot":"","sources":["../src/agent-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAoB,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAO7I,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAKD,eAAO,MAAM,SAAS,EAAE,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,EASxE,CAAC;AAMF,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,KAAK,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,cAAc,CAMlE,CAAC;AAWF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAM1D,CAAC;AAMF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAwC,CAAC;AAI5F,eAAO,MAAM,SAAS,aAAc,aAAa,KAAG,cAAc,GAAG,SAAwD,CAAC;AAI9H,eAAO,MAAM,aAAa,aAAc,aAAa,KAAG,MAAwE,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-catalog.d.ts","sourceRoot":"","sources":["../src/agent-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAoB,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAO7I,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAKD,eAAO,MAAM,SAAS,EAAE,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,EASxE,CAAC;AAMF,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,KAAK,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,cAAc,CAMlE,CAAC;AAWF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAM1D,CAAC;AAMF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAwC,CAAC;AAI5F,eAAO,MAAM,SAAS,aAAc,aAAa,KAAG,cAAc,GAAG,SAAwD,CAAC;AAI9H,eAAO,MAAM,aAAa,aAAc,aAAa,KAAG,MAAwE,CAAC;AAiBjI,eAAO,MAAM,oBAAoB,EAAE,SAAS,cAAc,EAA0C,CAAC;AACrG,eAAO,MAAM,iBAAiB,aAAc,aAAa,KAAG,OAAoE,CAAC;AAMjI,eAAO,MAAM,SAAS,EAAE,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,EAGtE,CAAC;AAgBF,MAAM,WAAW,iBAAiB;IAM9B,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG,KAAK,CAAC;IAE/D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAI3B,QAAQ,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,CAAC;IAEvC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAI5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAEvC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAKzB,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,KAAK,CAAC;IAExC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAG5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC9B;AAkED,eAAO,MAAM,cAAc,aAAc,aAAa,WAAW,YAAY,KAAG,iBAQ/E,CAAC;AAIF,eAAO,MAAM,QAAQ,iBAAkB,iBAAiB,KAAG,SAAS,cAAc,EACgD,CAAC;AAInI,eAAO,MAAM,SAAS,SAAU,cAAc,gBAAgB,iBAAiB,KAAG,cACZ,CAAC;AAIvE,eAAO,MAAM,aAAa,iBAAkB,iBAAiB,KAAG,MAAM,EAUrE,CAAC;AASF,eAAO,MAAM,kBAAkB,EAAE,SAAS,KAAK,EAI9C,CAAC;AAOF,eAAO,MAAM,SAAS,aAAc,aAAa,KAAG,aAAa,EAMhE,CAAC;AASF,eAAO,MAAM,aAAa,WAAY,MAAM,YAAY,aAAa,YAAY,OAAO,KAAG,OAChC,CAAC;AAc5D,eAAO,MAAM,cAAc,WAAY,MAAM,GAAG,SAAS,YAAY,OAAO,GAAG,SAAS,KAAG,MAAM,GAAG,SACzC,CAAC"}
|
package/dist/agent-catalog.js
CHANGED
|
@@ -23,7 +23,7 @@ export const PROVIDER_VENDOR = {
|
|
|
23
23
|
export const ACCESS_COST = { free: 0, subscription: 1, key: 2 };
|
|
24
24
|
export const accessFor = (provider) => PROVIDER_ACCESS[provider];
|
|
25
25
|
export const providerLabel = (provider) => PROVIDERS.find((p) => p.value === provider)?.label ?? provider;
|
|
26
|
-
export const PLAN_LIMIT_PROVIDERS = ["claude", "codex", "gemini"];
|
|
26
|
+
export const PLAN_LIMIT_PROVIDERS = ["claude", "codex", "gemini", "kimi"];
|
|
27
27
|
export const reportsPlanLimits = (provider) => PLAN_LIMIT_PROVIDERS.includes(provider);
|
|
28
28
|
export const HARNESSES = [
|
|
29
29
|
{ label: "Native", value: "native" },
|
|
@@ -111,4 +111,5 @@ export const modelsFor = (provider) => {
|
|
|
111
111
|
return [];
|
|
112
112
|
};
|
|
113
113
|
export const effortAllowed = (effort, provider, thinking) => effort !== "max" || (provider === "claude" && thinking);
|
|
114
|
+
export const sendableEffort = (effort, thinking) => effort === "max" && thinking !== true ? "high" : effort;
|
|
114
115
|
//# sourceMappingURL=agent-catalog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-catalog.js","sourceRoot":"","sources":["../src/agent-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,gBAAgB,EAA4C,MAAM,cAAc,CAAC;AAe7I,MAAM,CAAC,MAAM,SAAS,GAAwD;IAC1E,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE;IACzC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;IAClC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;IAChC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE;IAIrC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;CACvC,CAAC;AAgBF,MAAM,CAAC,MAAM,eAAe,GAA2C;IACnE,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,qBAAqB,EAAE,IAAI,EAAE,aAAa,EAAE;IACzF,KAAK,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,sBAAsB,EAAE,IAAI,EAAE,OAAO,EAAE;IACnF,IAAI,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,wBAAwB,EAAE,IAAI,EAAE,MAAM,EAAE;IACnF,IAAI,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,wBAAwB,EAAE,IAAI,EAAE,WAAW,EAAE;IACxF,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,IAAI,EAAE,8CAA8C,EAAE;CAChH,CAAC;AAWF,MAAM,CAAC,MAAM,eAAe,GAAmC;IAC3D,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,QAAQ;CACnB,CAAC;AAMF,MAAM,CAAC,MAAM,WAAW,GAA+B,EAAE,IAAI,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AAI5F,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,QAAuB,EAA8B,EAAE,CAAC,eAAe,CAAC,QAA0B,CAAC,CAAC;AAI9H,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAuB,EAAU,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,EAAE,KAAK,IAAI,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-catalog.js","sourceRoot":"","sources":["../src/agent-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ChoreSignals, ProbeId, ProbeResult } from "../schemas.js";
|
|
2
|
+
export type ChoreStance = "act" | "report";
|
|
3
|
+
export interface ChoreContext {
|
|
4
|
+
readonly repo: string;
|
|
5
|
+
readonly probes: ReadonlyMap<ProbeId, ProbeResult>;
|
|
6
|
+
readonly signals: ChoreSignals;
|
|
7
|
+
readonly node: string;
|
|
8
|
+
readonly nowMs: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ChoreFinding {
|
|
11
|
+
readonly headline: string;
|
|
12
|
+
readonly detail: readonly string[];
|
|
13
|
+
readonly digest: string;
|
|
14
|
+
readonly severity: "info" | "warning";
|
|
15
|
+
readonly why: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Chore {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly title: string;
|
|
20
|
+
readonly icon: string;
|
|
21
|
+
readonly description: string;
|
|
22
|
+
readonly criterion: string;
|
|
23
|
+
readonly applies?: (signals: ChoreSignals) => string | undefined;
|
|
24
|
+
readonly stance: ChoreStance;
|
|
25
|
+
readonly needs: readonly ProbeId[];
|
|
26
|
+
readonly cadenceMs: number;
|
|
27
|
+
readonly survey?: true;
|
|
28
|
+
readonly automation?: {
|
|
29
|
+
readonly cron: string;
|
|
30
|
+
readonly guard: string;
|
|
31
|
+
readonly note: string;
|
|
32
|
+
readonly report: string;
|
|
33
|
+
readonly woke: string;
|
|
34
|
+
};
|
|
35
|
+
readonly assess: (context: ChoreContext) => ChoreFinding | undefined;
|
|
36
|
+
readonly diagnosis: string;
|
|
37
|
+
readonly goal: string;
|
|
38
|
+
readonly done: string;
|
|
39
|
+
}
|
|
40
|
+
export declare const repoLabel: (repo: string) => string;
|
|
41
|
+
export declare const CHORES: readonly Chore[];
|
|
42
|
+
export declare const choreById: (id: string) => Chore | undefined;
|
|
43
|
+
export declare const choreAutomationPrompt: (chore: Chore) => string | undefined;
|
|
44
|
+
export declare const chorePrompt: (chore: Chore, finding: ChoreFinding, repo: string) => string;
|
|
45
|
+
//# sourceMappingURL=chores.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chores.d.ts","sourceRoot":"","sources":["../../src/chores/chores.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,YAAY,EAAmB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAoCnG,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE3C,MAAM,WAAW,YAAY;IAEzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACnD,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAE/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAGD,MAAM,WAAW,YAAY;IAEzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAG1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAEnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAGxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAEtC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,KAAK;IAClB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAGvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAY7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAa3B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,MAAM,GAAG,SAAS,CAAC;IAGjE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAG7B,QAAQ,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;IAInC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAG3B,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC;IAcvB,QAAQ,CAAC,UAAU,CAAC,EAAE;QAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAExB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,YAAY,GAAG,SAAS,CAAC;IAGrE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAeD,eAAO,MAAM,SAAS,SAAU,MAAM,KAAG,MAAmF,CAAC;AA2nB7H,eAAO,MAAM,MAAM,EAAE,SAAS,KAAK,EAclC,CAAC;AAEF,eAAO,MAAM,SAAS,OAAQ,MAAM,KAAG,KAAK,GAAG,SAAoD,CAAC;AAUpG,eAAO,MAAM,qBAAqB,UAAW,KAAK,KAAG,MAAM,GAAG,SAUlD,CAAC;AAEb,eAAO,MAAM,WAAW,UAAW,KAAK,WAAW,YAAY,QAAQ,MAAM,KAAG,MAW1E,CAAC"}
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { bucketOf, digestOf } from "./digest.js";
|
|
2
|
+
import { CHORE_INVARIANTS, composeAsk, REPORT_INVARIANTS, TRIAGE_NOTE } from "./prompt.js";
|
|
3
|
+
const DAY_MS = 86_400_000;
|
|
4
|
+
const AUDIT_REPORT = `/tmp/intentic-chore-audit.json`;
|
|
5
|
+
const KNIP_REPORT = `/tmp/intentic-chore-knip.json`;
|
|
6
|
+
const JSCPD_DIR = `/tmp/intentic-chore-jscpd`;
|
|
7
|
+
const JSCPD_REPORT = `${JSCPD_DIR}/jscpd-report.json`;
|
|
8
|
+
export const repoLabel = (repo) => (repo === `root` || repo === `` ? `the workspace root repository` : repo);
|
|
9
|
+
const plural = (count, one, many = `${one}s`) => `${count} ${count === 1 ? one : many}`;
|
|
10
|
+
const outdatedLine = (entry) => `${entry.kind} · ${entry.name} ${entry.current} → ${entry.latest}`;
|
|
11
|
+
const factsOf = (context, id) => {
|
|
12
|
+
const probe = context.probes.get(id);
|
|
13
|
+
if (probe?.state !== `ok` || probe.facts === undefined || probe.facts.id !== id) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
return probe.facts;
|
|
17
|
+
};
|
|
18
|
+
const BLOCKING = new Set([`critical`, `high`]);
|
|
19
|
+
const security = {
|
|
20
|
+
id: `security-advisories`,
|
|
21
|
+
title: `Patch security advisories`,
|
|
22
|
+
icon: `shield`,
|
|
23
|
+
description: `Published advisories against this dependency tree, and the ones whose fix is a version bump.`,
|
|
24
|
+
criterion: `pnpm audit reports an advisory of high or critical severity against the resolved tree.`,
|
|
25
|
+
applies: (signals) => (signals.shape.lockfile ? undefined : `there is no lockfile here, so nothing resolves to a tree that could be audited`),
|
|
26
|
+
stance: `act`,
|
|
27
|
+
needs: [`audit`],
|
|
28
|
+
cadenceMs: 0,
|
|
29
|
+
automation: {
|
|
30
|
+
cron: `0 4 * * *`,
|
|
31
|
+
guard: `pnpm audit --json > ${AUDIT_REPORT} 2>/dev/null; ` +
|
|
32
|
+
`[ "$(jq '(.metadata.vulnerabilities.high // 0) + (.metadata.vulnerabilities.critical // 0)' ${AUDIT_REPORT} 2>/dev/null || echo 0)" -gt 0 ]`,
|
|
33
|
+
note: `nightly · high + critical only`,
|
|
34
|
+
report: AUDIT_REPORT,
|
|
35
|
+
woke: `pnpm audit's report for this workspace is in ${AUDIT_REPORT} (JSON), and it woke you because it carries a high or critical advisory.`,
|
|
36
|
+
},
|
|
37
|
+
assess: (context) => {
|
|
38
|
+
const facts = factsOf(context, `audit`);
|
|
39
|
+
if (facts === undefined) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const blocking = facts.advisories.filter((advisory) => BLOCKING.has(advisory.severity));
|
|
43
|
+
if (blocking.length === 0) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const production = blocking.filter((advisory) => !advisory.dev);
|
|
47
|
+
const patchable = blocking.filter((advisory) => advisory.patched !== undefined);
|
|
48
|
+
return {
|
|
49
|
+
headline: `${plural(blocking.length, `advisory`, `advisories`)}, ${patchable.length} with a published fix`,
|
|
50
|
+
detail: blocking
|
|
51
|
+
.toSorted((left, right) => left.name.localeCompare(right.name))
|
|
52
|
+
.map((advisory) => `${advisory.severity} · ${advisory.name} — ${advisory.title}${advisory.patched === undefined ? ` (no patch yet)` : ``}`),
|
|
53
|
+
digest: digestOf(...blocking.map((advisory) => `${advisory.name}@${advisory.severity}`).toSorted()),
|
|
54
|
+
severity: production.length > 0 ? `warning` : `info`,
|
|
55
|
+
why: `pnpm audit reports ${plural(blocking.length, `high or critical advisory`, `high or critical advisories`)} against ` +
|
|
56
|
+
`${repoLabel(context.repo)} — ${production.length} reaching a production dependency path, ${patchable.length} with a published patched range: ` +
|
|
57
|
+
`${blocking.map((advisory) => `${advisory.name} (${advisory.severity}${advisory.dev ? `, dev-only` : ``}${advisory.patched === undefined ? `, no patch` : `, fixed in ${advisory.patched}`})`).join(`; `)}.`,
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
diagnosis: `An advisory with a published fix is a version bump someone has to actually make; one without is a risk to decide about.`,
|
|
61
|
+
goal: `For each advisory, establish whether this workspace reaches the vulnerable code path at all — a transitive dependency of a ` +
|
|
62
|
+
`build-time tool is a different problem from one in a running service. Where the fix is a version bump the lockfile can absorb, ` +
|
|
63
|
+
`make it. Where it needs a real upgrade or has no patch published, leave it and say what it would take. Never rewrite ` +
|
|
64
|
+
`application code to route around a CVE.`,
|
|
65
|
+
done: `Done when \`pnpm audit\` reports fewer high/critical advisories than it did, and the repository's type-check and tests pass.`,
|
|
66
|
+
};
|
|
67
|
+
const OUTDATED_NOISE_FLOOR = 20;
|
|
68
|
+
const dependencies = {
|
|
69
|
+
id: `dependencies-outdated`,
|
|
70
|
+
title: `Update dependencies`,
|
|
71
|
+
icon: `arrow-circle-up`,
|
|
72
|
+
description: `How far behind the registry this tree has drifted, and which majors are waiting.`,
|
|
73
|
+
criterion: `A dependency is a major version behind, or more than 20 are behind by any amount.`,
|
|
74
|
+
applies: (signals) => (signals.shape.packageManifest ? undefined : `this repository has no package.json, so there is no npm dependency tree to be behind`),
|
|
75
|
+
stance: `act`,
|
|
76
|
+
needs: [`outdated`],
|
|
77
|
+
cadenceMs: 30 * DAY_MS,
|
|
78
|
+
assess: (context) => {
|
|
79
|
+
const facts = factsOf(context, `outdated`);
|
|
80
|
+
if (facts === undefined) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const majors = facts.packages.filter((entry) => entry.kind === `major`);
|
|
84
|
+
if (majors.length === 0 && facts.packages.length < OUTDATED_NOISE_FLOOR) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
headline: majors.length === 0
|
|
89
|
+
? `${plural(facts.packages.length, `package`)} behind`
|
|
90
|
+
: `${plural(majors.length, `major`)} waiting, ${facts.packages.length} behind in total`,
|
|
91
|
+
detail: majors.toSorted((left, right) => left.name.localeCompare(right.name)).map(outdatedLine),
|
|
92
|
+
digest: digestOf(...majors.map((entry) => `${entry.name}@${entry.latest}`).toSorted(), `total:${bucketOf(facts.packages.length)}`),
|
|
93
|
+
severity: `info`,
|
|
94
|
+
why: `pnpm outdated reports ${plural(facts.packages.length, `dependency`, `dependencies`)} behind the registry in ` +
|
|
95
|
+
`${repoLabel(context.repo)}, ${majors.length} of them by a major version` +
|
|
96
|
+
`${majors.length === 0 ? `` : `: ${majors.map((entry) => `${entry.name} ${entry.current} → ${entry.latest}`).join(`; `)}`}.`,
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
diagnosis: `Version drift is cheap to fix continuously and expensive to fix in one go, because the majors start depending on each other.`,
|
|
100
|
+
goal: `Take the patch and minor upgrades in one pass — those are what the lockfile can absorb without argument. Then take the majors ` +
|
|
101
|
+
`ONE AT A TIME, reading each one's changelog for breaking changes before you touch anything, and stop at the first one that ` +
|
|
102
|
+
`needs more than a mechanical fix: leave it, and say what it would take. Do not batch majors; a failing test after eight of them ` +
|
|
103
|
+
`is a bisect nobody wanted.`,
|
|
104
|
+
done: `Done when the repository's type-check and tests pass, and your summary names every major you took and every one you left, with the reason.`,
|
|
105
|
+
};
|
|
106
|
+
const deadCode = {
|
|
107
|
+
id: `dead-code`,
|
|
108
|
+
title: `Clear out dead code`,
|
|
109
|
+
icon: `trash`,
|
|
110
|
+
description: `Files, exports and dependencies nothing in this repository references any more.`,
|
|
111
|
+
criterion: `knip reports at least one unreferenced file, export or dependency.`,
|
|
112
|
+
applies: (signals) => (signals.shape.packageManifest ? undefined : `this repository is not a Node project, and knip only reads those`),
|
|
113
|
+
stance: `act`,
|
|
114
|
+
needs: [`knip`],
|
|
115
|
+
cadenceMs: 14 * DAY_MS,
|
|
116
|
+
automation: {
|
|
117
|
+
cron: `0 3 * * *`,
|
|
118
|
+
guard: `pnpm exec knip --version >/dev/null 2>&1 || { echo "knip is not a devDependency of this repo"; exit 1; }; ` +
|
|
119
|
+
`pnpm exec knip --reporter json > ${KNIP_REPORT} && { echo "no dead code"; exit 1; }`,
|
|
120
|
+
note: `nightly · wakes only on findings`,
|
|
121
|
+
report: KNIP_REPORT,
|
|
122
|
+
woke: `knip's findings for this workspace are in ${KNIP_REPORT} (JSON), and it woke you because there are some.`,
|
|
123
|
+
},
|
|
124
|
+
assess: (context) => {
|
|
125
|
+
const facts = factsOf(context, `knip`);
|
|
126
|
+
if (facts === undefined) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const { files, exports, types, dependencies: unusedDeps, devDependencies, sample } = facts.deadCode;
|
|
130
|
+
const total = files + exports + types + unusedDeps + devDependencies;
|
|
131
|
+
if (total === 0) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
headline: `${plural(files, `unreferenced file`)}, ${exports + types} unused exports, ${unusedDeps + devDependencies} unused dependencies`,
|
|
136
|
+
detail: sample.map((path) => `unreferenced · ${path}`),
|
|
137
|
+
digest: digestOf(...sample.toSorted(), `exports:${bucketOf(exports + types)}`, `deps:${bucketOf(unusedDeps + devDependencies)}`),
|
|
138
|
+
severity: `info`,
|
|
139
|
+
why: `knip reports ${plural(files, `unreferenced file`)}, ${exports + types} unused exports and ` +
|
|
140
|
+
`${unusedDeps + devDependencies} unused dependencies in ${repoLabel(context.repo)}` +
|
|
141
|
+
`${sample.length === 0 ? `` : `, among them ${sample.join(`, `)}`}.`,
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
diagnosis: `Code nothing reaches still has to be read, type-checked and kept compiling by everyone who works nearby.`,
|
|
145
|
+
goal: `Re-run knip yourself first — this measurement is hours old and the tree has moved. Then check each finding against how the ` +
|
|
146
|
+
`file is actually used: knip is confidently wrong about anything reachable from OUTSIDE the repository, which means a package's ` +
|
|
147
|
+
`public entry points, files a bundler or framework loads by convention, and types consumed only by a downstream package. Delete ` +
|
|
148
|
+
`what is genuinely unreachable. Leave the false positives and list them in one line each, so the next run's reader knows they ` +
|
|
149
|
+
`were considered rather than missed.`,
|
|
150
|
+
done: `Done when knip reports fewer findings, the repository's type-check and tests pass, and nothing you deleted is reachable from another package.`,
|
|
151
|
+
};
|
|
152
|
+
const DUPLICATION_FLOOR = 5;
|
|
153
|
+
const duplication = {
|
|
154
|
+
id: `duplication`,
|
|
155
|
+
title: `Find duplication worth collapsing`,
|
|
156
|
+
icon: `clone`,
|
|
157
|
+
description: `Copy-paste that has grown past a fifth of a percent of the tree. Reports only — extracting is a design call.`,
|
|
158
|
+
criterion: `jscpd reports more than 5% of the scanned tree duplicated.`,
|
|
159
|
+
stance: `report`,
|
|
160
|
+
needs: [`jscpd`],
|
|
161
|
+
cadenceMs: 30 * DAY_MS,
|
|
162
|
+
automation: {
|
|
163
|
+
cron: `0 3 * * 1`,
|
|
164
|
+
guard: `pnpm dlx jscpd --reporters json --output ${JSCPD_DIR} --min-lines 12 --threshold 100 . >/dev/null 2>&1; ` +
|
|
165
|
+
`[ "$(jq '.statistics.total.percentage // 0 | floor' ${JSCPD_REPORT} 2>/dev/null || echo 0)" -ge ${DUPLICATION_FLOOR} ]`,
|
|
166
|
+
note: `weekly · wakes above ${DUPLICATION_FLOOR}% duplication`,
|
|
167
|
+
report: JSCPD_REPORT,
|
|
168
|
+
woke: `jscpd's clone report for this workspace is in ${JSCPD_REPORT}, and it woke you because duplication is above ${DUPLICATION_FLOOR}%.`,
|
|
169
|
+
},
|
|
170
|
+
assess: (context) => {
|
|
171
|
+
const facts = factsOf(context, `jscpd`);
|
|
172
|
+
if (facts === undefined || facts.duplication.percentage < DUPLICATION_FLOOR) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
const { percentage, clones, top } = facts.duplication;
|
|
176
|
+
return {
|
|
177
|
+
headline: `${percentage.toFixed(1)}% of the tree is duplicated, across ${plural(clones, `clone`)}`,
|
|
178
|
+
detail: top.map((clone) => `${clone.lines} lines · ${clone.first} ↔ ${clone.second}`),
|
|
179
|
+
digest: digestOf(`pct:${Math.round(percentage)}`, ...top.map((clone) => `${clone.first}|${clone.second}`).toSorted()),
|
|
180
|
+
severity: `info`,
|
|
181
|
+
why: `jscpd reports ${percentage.toFixed(1)}% duplication across ${plural(clones, `clone`)} in ${repoLabel(context.repo)}; ` +
|
|
182
|
+
`the largest are ${top.map((clone) => `${clone.first} ↔ ${clone.second} (${clone.lines} lines)`).join(`; `)}.`,
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
diagnosis: `Duplication only costs anything when the copies have to change together — and only some of it does.`,
|
|
186
|
+
goal: `Report the clones where the copies genuinely have to change together. For each: cite both file:line ranges, say what the shared ` +
|
|
187
|
+
`concept actually is, and name where the extraction would live. Then say explicitly which of the reported clones you are NOT ` +
|
|
188
|
+
`recommending against — generated files, deliberately repetitive tests, and lookalikes owned by different subsystems — so the ` +
|
|
189
|
+
`next reader knows the list was triaged rather than truncated.`,
|
|
190
|
+
done: `Done when every clone in the report has either a named extraction or a one-line reason it should stay.`,
|
|
191
|
+
};
|
|
192
|
+
const documentation = {
|
|
193
|
+
id: `documentation-refresh`,
|
|
194
|
+
title: `Document what nothing explains`,
|
|
195
|
+
icon: `file-edit`,
|
|
196
|
+
description: `Packages in this repository with no architecture document — new ones first.`,
|
|
197
|
+
criterion: `A workspace package has no docs/architecture document.`,
|
|
198
|
+
applies: (signals) => (signals.packages.length > 0 ? undefined : `this repository is not a workspace, so it has no packages to document one by one`),
|
|
199
|
+
stance: `act`,
|
|
200
|
+
needs: [],
|
|
201
|
+
cadenceMs: 90 * DAY_MS,
|
|
202
|
+
assess: (context) => {
|
|
203
|
+
const undocumented = context.signals.packages.filter((entry) => !entry.documented);
|
|
204
|
+
if (undocumented.length === 0) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
headline: `${plural(undocumented.length, `package`)} of ${context.signals.packages.length} have no document`,
|
|
209
|
+
detail: undocumented.map((entry) => `${entry.name} · ${entry.dir}`),
|
|
210
|
+
digest: digestOf(...undocumented.map((entry) => entry.dir).toSorted()),
|
|
211
|
+
severity: `info`,
|
|
212
|
+
why: `${plural(undocumented.length, `package`)} of ${context.signals.packages.length} in ${repoLabel(context.repo)} have no ` +
|
|
213
|
+
`docs/architecture document: ${undocumented.map((entry) => entry.dir).join(`, `)}.`,
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
diagnosis: `A package nobody can read the shape of gets worked in by guesswork, and the guesses accumulate.`,
|
|
217
|
+
goal: `Follow this workspace's own documentation conventions — read them first, they are not optional and they are not generic. For ` +
|
|
218
|
+
`each undocumented package, read the package before you write a word about it, and produce the document its conventions call ` +
|
|
219
|
+
`for: what the package is FOR, how it fits the system, and which files matter. Explain at the module level. Never describe code ` +
|
|
220
|
+
`line by line, and never document a package you did not read.`,
|
|
221
|
+
done: `Done when every package you named has a document that a newcomer could use to find the file they need, and no other file changed.`,
|
|
222
|
+
};
|
|
223
|
+
const COMPLEXITY_MULTIPLE = 3;
|
|
224
|
+
const median = (values) => {
|
|
225
|
+
if (values.length === 0) {
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
const sorted = values.toSorted((left, right) => left - right);
|
|
229
|
+
const middle = Math.floor(sorted.length / 2);
|
|
230
|
+
return sorted.length % 2 === 0 ? ((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2 : (sorted[middle] ?? 0);
|
|
231
|
+
};
|
|
232
|
+
const complexity = {
|
|
233
|
+
id: `complexity`,
|
|
234
|
+
title: `Simplify what everything waits on`,
|
|
235
|
+
icon: `wave-pulse`,
|
|
236
|
+
description: `Files that both churn and carry the repository — where edits are slow and ripple outward.`,
|
|
237
|
+
criterion: `A file in the hotspot ranking is also a key module, or its branching is three times the median of that ranking.`,
|
|
238
|
+
stance: `act`,
|
|
239
|
+
needs: [],
|
|
240
|
+
cadenceMs: 30 * DAY_MS,
|
|
241
|
+
assess: (context) => {
|
|
242
|
+
if (!context.signals.indexed || context.signals.hotspots.length === 0) {
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
const keyModules = new Set(context.signals.keyModules.map((module) => module.path));
|
|
246
|
+
const middle = median(context.signals.hotspots.map((hotspot) => hotspot.complexity));
|
|
247
|
+
const found = context.signals.hotspots.filter((hotspot) => keyModules.has(hotspot.path) || hotspot.complexity >= middle * COMPLEXITY_MULTIPLE);
|
|
248
|
+
if (found.length === 0) {
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
const reason = (path, branches) => keyModules.has(path) ? `churns and the rest of the repository imports it` : `${branches} branch points against a median of ${middle}`;
|
|
252
|
+
return {
|
|
253
|
+
headline: `${plural(found.length, `file`)} where every edit is slow and ripples outward`,
|
|
254
|
+
detail: found.map((hotspot) => `${hotspot.path} — ${hotspot.commits} commits, ${reason(hotspot.path, hotspot.complexity)}`),
|
|
255
|
+
digest: digestOf(...found.map((hotspot) => hotspot.path).toSorted()),
|
|
256
|
+
severity: `info`,
|
|
257
|
+
why: `${plural(found.length, `file`)} in ${repoLabel(context.repo)} are both change magnets and structurally tangled: ` +
|
|
258
|
+
`${found.map((hotspot) => `${hotspot.path} (${hotspot.commits} commits, ${hotspot.complexity} branch points)`).join(`; `)}.`,
|
|
259
|
+
};
|
|
260
|
+
},
|
|
261
|
+
diagnosis: `A file that changes constantly and branches heavily makes every edit near it slow and easy to get wrong.`,
|
|
262
|
+
goal: `Take ONE file — the worst of them — and no more. Read it first. If the rest of the repository imports it, separate the stable ` +
|
|
263
|
+
`contract from the churn: a narrow surface for importers, the volatile implementation private behind it. If it is simply ` +
|
|
264
|
+
`tangled, flatten it where it stands — edge cases as early returns, compound conditions behind named predicates, long chains as ` +
|
|
265
|
+
`lookups — and extract a unit only if a cohesive one falls out. Behaviour stays identical, and no re-export shims are left behind.`,
|
|
266
|
+
done: `Done when \`iq hotspots\` reports materially fewer branch points for that file, the repository's checks pass, and no importer changed meaning.`,
|
|
267
|
+
};
|
|
268
|
+
const NODE_EOL = {
|
|
269
|
+
16: `2023-09-11`,
|
|
270
|
+
18: `2025-04-30`,
|
|
271
|
+
20: `2026-04-30`,
|
|
272
|
+
22: `2027-04-30`,
|
|
273
|
+
24: `2028-04-30`,
|
|
274
|
+
};
|
|
275
|
+
const EOL_HORIZON_MS = 90 * DAY_MS;
|
|
276
|
+
const runtime = {
|
|
277
|
+
id: `runtime-eol`,
|
|
278
|
+
title: `Move off an end-of-life runtime`,
|
|
279
|
+
icon: `bolt`,
|
|
280
|
+
description: `Whether the Node this sandbox runs still receives security patches.`,
|
|
281
|
+
criterion: `The Node release this sandbox runs is past its end-of-life date, or within 90 days of it.`,
|
|
282
|
+
applies: (signals) => (signals.shape.packageManifest ? undefined : `this repository is not a Node project, so the sandbox's runtime is not its concern`),
|
|
283
|
+
stance: `act`,
|
|
284
|
+
needs: [],
|
|
285
|
+
cadenceMs: 0,
|
|
286
|
+
assess: (context) => {
|
|
287
|
+
const major = Number.parseInt(context.node.replace(/^v/, ``), 10);
|
|
288
|
+
const eol = NODE_EOL[major];
|
|
289
|
+
if (Number.isNaN(major) || eol === undefined) {
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
const eolMs = Date.parse(`${eol}T00:00:00Z`);
|
|
293
|
+
if (context.nowMs < eolMs - EOL_HORIZON_MS) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
const past = context.nowMs >= eolMs;
|
|
297
|
+
const days = Math.round(Math.abs(eolMs - context.nowMs) / DAY_MS);
|
|
298
|
+
const pinned = context.signals.packages.filter((entry) => entry.engines?.[`node`] !== undefined);
|
|
299
|
+
return {
|
|
300
|
+
headline: past ? `Node ${major} stopped receiving security patches ${days} days ago` : `Node ${major} reaches end of life in ${days} days`,
|
|
301
|
+
detail: [
|
|
302
|
+
`running · ${context.node}`,
|
|
303
|
+
`end of life · ${eol}`,
|
|
304
|
+
...pinned.map((entry) => `pinned · ${entry.name} requires node ${entry.engines?.[`node`] ?? ``}`),
|
|
305
|
+
],
|
|
306
|
+
digest: digestOf(`node:${major}`, past ? `eol` : `approaching`),
|
|
307
|
+
severity: past ? `warning` : `info`,
|
|
308
|
+
why: `This sandbox runs ${context.node}, and Node ${major} ${past ? `reached end of life on ${eol}` : `reaches end of life on ${eol}`} — ` +
|
|
309
|
+
`${plural(pinned.length, `package`)} in ${repoLabel(context.repo)} pin a node engine range.`,
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
diagnosis: `An unsupported runtime stops receiving security patches, so every advisory against it stays open permanently.`,
|
|
313
|
+
goal: `Establish what actually pins this runtime: the image's own base, the workspace's useNodeVersion, and each package's engines ` +
|
|
314
|
+
`range. Propose the smallest move to a supported LTS — which of those pins have to change, in what order, and what is likely to ` +
|
|
315
|
+
`break at that boundary. Make the pin changes that are mechanical; do NOT attempt the image rebuild itself.`,
|
|
316
|
+
done: `Done when the pins name a supported release, the repository's type-check and tests pass on it, and anything needing a rebuild is named as such.`,
|
|
317
|
+
};
|
|
318
|
+
const CATEGORIES = [
|
|
319
|
+
{ category: `date handling`, members: [`moment`, `dayjs`, `date-fns`, `luxon`, `js-joda`] },
|
|
320
|
+
{ category: `HTTP clients`, members: [`axios`, `got`, `node-fetch`, `superagent`, `undici`, `request`] },
|
|
321
|
+
{ category: `schema validation`, members: [`zod`, `yup`, `joi`, `ajv`, `superstruct`, `valibot`] },
|
|
322
|
+
{ category: `utility belts`, members: [`lodash`, `underscore`, `ramda`, `remeda`] },
|
|
323
|
+
{ category: `state stores`, members: [`redux`, `mobx`, `zustand`, `jotai`, `recoil`, `pinia`, `valtio`] },
|
|
324
|
+
{ category: `UUID generation`, members: [`uuid`, `nanoid`, `cuid`, `shortid`, `ulid`] },
|
|
325
|
+
{ category: `test runners`, members: [`jest`, `mocha`, `ava`, `tap`] },
|
|
326
|
+
];
|
|
327
|
+
const libraries = {
|
|
328
|
+
id: `library-overlap`,
|
|
329
|
+
title: `Settle on one library per job`,
|
|
330
|
+
icon: `box`,
|
|
331
|
+
description: `Two dependencies solving the same problem — both shipped, both maintained, one picked at random.`,
|
|
332
|
+
criterion: `Two or more installed dependencies do the same job.`,
|
|
333
|
+
applies: (signals) => (signals.packages.length > 0 ? undefined : `this repository is not a workspace, so there are no package manifests to compare`),
|
|
334
|
+
stance: `report`,
|
|
335
|
+
needs: [],
|
|
336
|
+
cadenceMs: 90 * DAY_MS,
|
|
337
|
+
assess: (context) => {
|
|
338
|
+
const installed = new Set(context.signals.packages.flatMap((entry) => [...entry.dependencies, ...entry.devDependencies]));
|
|
339
|
+
const collisions = CATEGORIES.map(({ category, members }) => ({ category, found: members.filter((member) => installed.has(member)) })).filter(({ found }) => found.length > 1);
|
|
340
|
+
if (collisions.length === 0) {
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
headline: `${plural(collisions.length, `job`)} done by more than one library`,
|
|
345
|
+
detail: collisions.map(({ category, found }) => `${category} · ${found.join(`, `)}`),
|
|
346
|
+
digest: digestOf(...collisions.map(({ category, found }) => `${category}:${found.toSorted().join(`+`)}`).toSorted()),
|
|
347
|
+
severity: `info`,
|
|
348
|
+
why: `${repoLabel(context.repo)} depends on more than one library for the same job: ` +
|
|
349
|
+
`${collisions.map(({ category, found }) => `${category} (${found.join(`, `)})`).join(`; `)}.`,
|
|
350
|
+
};
|
|
351
|
+
},
|
|
352
|
+
diagnosis: `Two libraries for one job means both ship, both need upgrading, and new code picks whichever the neighbouring file used.`,
|
|
353
|
+
goal: `For each overlapping pair, find out which one is actually load-bearing — how many call sites each has, whether one is a ` +
|
|
354
|
+
`transitive dependency nobody chose, and whether either is unmaintained. Recommend the one to keep and estimate the migration ` +
|
|
355
|
+
`honestly, including the call sites where the two libraries genuinely differ in behaviour. Where the overlap is deliberate or ` +
|
|
356
|
+
`the second is only transitive, say so and close the question.`,
|
|
357
|
+
done: `Done when every overlapping pair has a recommendation with a call-site count behind it, or a reason the overlap is fine.`,
|
|
358
|
+
};
|
|
359
|
+
const survey = ({ id, title, icon, description, diagnosis, goal, done, cadenceDays, applies }) => ({
|
|
360
|
+
id,
|
|
361
|
+
title,
|
|
362
|
+
icon,
|
|
363
|
+
description,
|
|
364
|
+
criterion: `${cadenceDays} days have passed since this review was last run.`,
|
|
365
|
+
applies,
|
|
366
|
+
stance: `report`,
|
|
367
|
+
needs: [],
|
|
368
|
+
cadenceMs: cadenceDays * DAY_MS,
|
|
369
|
+
survey: true,
|
|
370
|
+
assess: (context) => ({
|
|
371
|
+
headline: `Not surveyed in ${cadenceDays} days`,
|
|
372
|
+
detail: [`Cadence · every ${cadenceDays} days`],
|
|
373
|
+
digest: digestOf(id, `period:${Math.floor(context.nowMs / (cadenceDays * DAY_MS))}`),
|
|
374
|
+
severity: `info`,
|
|
375
|
+
why: `This is a periodic review of ${repoLabel(context.repo)}, run every ${cadenceDays} days; nothing measured it — it is due because it has been that long.`,
|
|
376
|
+
}),
|
|
377
|
+
diagnosis,
|
|
378
|
+
goal,
|
|
379
|
+
done,
|
|
380
|
+
});
|
|
381
|
+
const PATTERNS_FLOOR = 25;
|
|
382
|
+
const patterns = survey({
|
|
383
|
+
id: `standardize-patterns`,
|
|
384
|
+
title: `Standardize the cross-cutting patterns`,
|
|
385
|
+
icon: `sitemap`,
|
|
386
|
+
description: `Error handling, validation, logging, configuration, retries, pagination — the things every file does slightly differently.`,
|
|
387
|
+
diagnosis: `Cross-cutting concerns drift one file at a time, and the cost only shows up when someone has to work across several of them.`,
|
|
388
|
+
goal: `Pick the cross-cutting concerns this repository actually has — error handling, input validation, logging, configuration, retries, ` +
|
|
389
|
+
`pagination, serialization — and for each, survey how it is done. Name the dominant pattern, the outliers, and which of the ` +
|
|
390
|
+
`outliers are deliberate. Recommend ONE convention per concern with a file to point at as the reference implementation, and ` +
|
|
391
|
+
`estimate the size of the conversion. Do not convert anything.`,
|
|
392
|
+
done: `Done when each concern has a named convention, a reference file, and a count of the sites that diverge from it.`,
|
|
393
|
+
cadenceDays: 90,
|
|
394
|
+
applies: (signals) => signals.totals.files >= PATTERNS_FLOOR
|
|
395
|
+
? undefined
|
|
396
|
+
: `this repository has ${signals.totals.files} indexed files — too few for cross-cutting patterns to have diverged`,
|
|
397
|
+
});
|
|
398
|
+
const deprecated = survey({
|
|
399
|
+
id: `deprecated-apis`,
|
|
400
|
+
title: `Audit deprecated APIs`,
|
|
401
|
+
icon: `exclamation-triangle`,
|
|
402
|
+
description: `Language, runtime and framework APIs this code still uses that their own maintainers have moved on from.`,
|
|
403
|
+
diagnosis: `A deprecated API works right up until the upgrade that removes it, and then it is an emergency during someone else's migration.`,
|
|
404
|
+
goal: `Survey what this repository uses that its own dependencies have deprecated: read the framework and runtime versions in use, check ` +
|
|
405
|
+
`their deprecation notices, and search for the call sites. Include the repository's OWN deprecations — anything its code marks ` +
|
|
406
|
+
`as deprecated and still calls. Rank by when each one actually breaks, not by how many call sites it has, and name the ` +
|
|
407
|
+
`replacement for each. Change nothing.`,
|
|
408
|
+
done: `Done when every deprecation has call sites cited, a replacement named, and the release it is expected to break in.`,
|
|
409
|
+
cadenceDays: 90,
|
|
410
|
+
applies: (signals) => (signals.shape.packageManifest ? undefined : `this repository declares no dependencies whose deprecations could be read`),
|
|
411
|
+
});
|
|
412
|
+
const documentationDrift = survey({
|
|
413
|
+
id: `documentation-drift`,
|
|
414
|
+
title: `Re-read the documentation against the code`,
|
|
415
|
+
icon: `file`,
|
|
416
|
+
description: `Whether what the documents claim is still what the code does — the drift no tool can measure.`,
|
|
417
|
+
diagnosis: `Documentation is trusted in proportion to how recently it was true, and a document that is quietly wrong is worse than a missing one.`,
|
|
418
|
+
goal: `Read this repository's architecture documents against the code they describe. Report every claim that is no longer true, citing the ` +
|
|
419
|
+
`document line and the file that contradicts it. Prioritise the claims someone would ACT on — where a subsystem lives, what owns ` +
|
|
420
|
+
`what, which file to change — over prose that has merely aged. Do not rewrite the documents; produce the list of what is wrong.`,
|
|
421
|
+
done: `Done when every architecture document has been read and every false claim is listed with both sides cited.`,
|
|
422
|
+
cadenceDays: 90,
|
|
423
|
+
applies: (signals) => (signals.shape.docs.length > 0 ? undefined : `this repository has no architecture documents to re-read`),
|
|
424
|
+
});
|
|
425
|
+
const pipelines = survey({
|
|
426
|
+
id: `ci-hygiene`,
|
|
427
|
+
title: `Tighten the CI pipeline`,
|
|
428
|
+
icon: `bolt`,
|
|
429
|
+
description: `What the pipeline re-does every run: uncached installs, rebuilt layers, jobs that could run in parallel.`,
|
|
430
|
+
diagnosis: `A slow pipeline is paid on every push by everyone, and it degrades one uncached step at a time without anyone deciding to.`,
|
|
431
|
+
goal: `Read this repository's pipeline definitions and report what it pays for repeatedly: dependency installs with no cache key, ` +
|
|
432
|
+
`build outputs recomputed between jobs, steps that are serial for no reason, and matrix legs that duplicate each other's work. ` +
|
|
433
|
+
`For each, name the file and step, say roughly what it costs per run, and give the change that would fix it. Where a step is slow ` +
|
|
434
|
+
`because it genuinely has to be, say so — a pipeline that is honestly expensive is not a finding.`,
|
|
435
|
+
done: `Done when every finding names a file, a step, and a concrete change, and anything deliberately slow is called out as such.`,
|
|
436
|
+
cadenceDays: 90,
|
|
437
|
+
applies: (signals) => (signals.shape.ci.length > 0 ? undefined : `this repository defines no CI pipeline`),
|
|
438
|
+
});
|
|
439
|
+
const images = survey({
|
|
440
|
+
id: `docker-image`,
|
|
441
|
+
title: `Slim the container image`,
|
|
442
|
+
icon: `box`,
|
|
443
|
+
description: `Layer order, build context and final size — what ships in the image that did not need to.`,
|
|
444
|
+
diagnosis: `Image size is paid on every pull and every cold start, and layer order decides how much of a build is cache hits.`,
|
|
445
|
+
goal: `Read this repository's Dockerfiles and report what makes the image larger or the build slower than it needs to be: layers ordered ` +
|
|
446
|
+
`so that a source edit invalidates the dependency install, build-time toolchains left in the final stage, a build context that ships ` +
|
|
447
|
+
`the whole repository, and package caches never cleaned. For each, cite the file and line, and name the change. Do not rewrite the ` +
|
|
448
|
+
`Dockerfiles — an image that fails to build is a much worse problem than one that is larger than ideal.`,
|
|
449
|
+
done: `Done when every finding cites a Dockerfile line and names the change, with the ones that would need a base-image swap called out separately.`,
|
|
450
|
+
cadenceDays: 90,
|
|
451
|
+
applies: (signals) => (signals.shape.dockerfiles.length > 0 ? undefined : `this repository ships no Dockerfile`),
|
|
452
|
+
});
|
|
453
|
+
export const CHORES = [
|
|
454
|
+
security,
|
|
455
|
+
runtime,
|
|
456
|
+
dependencies,
|
|
457
|
+
deadCode,
|
|
458
|
+
complexity,
|
|
459
|
+
documentation,
|
|
460
|
+
duplication,
|
|
461
|
+
libraries,
|
|
462
|
+
patterns,
|
|
463
|
+
deprecated,
|
|
464
|
+
documentationDrift,
|
|
465
|
+
pipelines,
|
|
466
|
+
images,
|
|
467
|
+
];
|
|
468
|
+
export const choreById = (id) => CHORES.find((chore) => chore.id === id);
|
|
469
|
+
export const choreAutomationPrompt = (chore) => chore.automation === undefined
|
|
470
|
+
? undefined
|
|
471
|
+
: composeAsk({
|
|
472
|
+
subject: `${chore.title} across this workspace.`,
|
|
473
|
+
why: `${chore.automation.woke} ${TRIAGE_NOTE}`,
|
|
474
|
+
diagnosis: chore.diagnosis,
|
|
475
|
+
goal: chore.goal,
|
|
476
|
+
invariants: chore.stance === `act` ? CHORE_INVARIANTS : REPORT_INVARIANTS,
|
|
477
|
+
done: chore.done,
|
|
478
|
+
});
|
|
479
|
+
export const chorePrompt = (chore, finding, repo) => composeAsk({
|
|
480
|
+
subject: `${chore.title} in ${repoLabel(repo)}.`,
|
|
481
|
+
why: `${finding.why} You were woken because: ${chore.criterion} ${TRIAGE_NOTE}`,
|
|
482
|
+
diagnosis: chore.diagnosis,
|
|
483
|
+
goal: chore.goal,
|
|
484
|
+
invariants: chore.stance === `act` ? CHORE_INVARIANTS : REPORT_INVARIANTS,
|
|
485
|
+
done: chore.done,
|
|
486
|
+
});
|
|
487
|
+
//# sourceMappingURL=chores.js.map
|