@elnora-ai/linear 1.0.1 → 2.0.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/.claude-plugin/marketplace.json +7 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +25 -1
- package/README.md +275 -25
- package/agents/linear-issue-creator.md +135 -17
- package/agents/linear-issue-reviewer.md +122 -23
- package/agents/linear-issue-updater.md +137 -25
- package/agents/linear-state-curator.md +173 -0
- package/agents/linear-url-to-issues.md +190 -26
- package/commands/linear-cleanup.md +64 -29
- package/dist/cli.js +69 -1
- package/dist/cli.js.map +1 -1
- package/dist/client/auth.d.ts +10 -0
- package/dist/client/auth.d.ts.map +1 -1
- package/dist/client/auth.js +50 -3
- package/dist/client/auth.js.map +1 -1
- package/dist/client/linear-client.d.ts +7 -0
- package/dist/client/linear-client.d.ts.map +1 -1
- package/dist/client/linear-client.js +13 -1
- package/dist/client/linear-client.js.map +1 -1
- package/dist/commands/agent-activities.d.ts +3 -0
- package/dist/commands/agent-activities.d.ts.map +1 -0
- package/dist/commands/agent-activities.js +144 -0
- package/dist/commands/agent-activities.js.map +1 -0
- package/dist/commands/agent-sessions.d.ts +3 -0
- package/dist/commands/agent-sessions.d.ts.map +1 -0
- package/dist/commands/agent-sessions.js +132 -0
- package/dist/commands/agent-sessions.js.map +1 -0
- package/dist/commands/attachments.d.ts +3 -0
- package/dist/commands/attachments.d.ts.map +1 -0
- package/dist/commands/attachments.js +265 -0
- package/dist/commands/attachments.js.map +1 -0
- package/dist/commands/audit.d.ts +3 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +73 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/comments.d.ts +3 -0
- package/dist/commands/comments.d.ts.map +1 -0
- package/dist/commands/comments.js +107 -0
- package/dist/commands/comments.js.map +1 -0
- package/dist/commands/completion.d.ts +3 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +62 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/context.d.ts +3 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +94 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/curator.d.ts +14 -0
- package/dist/commands/curator.d.ts.map +1 -1
- package/dist/commands/curator.js +97 -19
- package/dist/commands/curator.js.map +1 -1
- package/dist/commands/customer-needs.d.ts +3 -0
- package/dist/commands/customer-needs.d.ts.map +1 -0
- package/dist/commands/customer-needs.js +198 -0
- package/dist/commands/customer-needs.js.map +1 -0
- package/dist/commands/customers.d.ts +5 -0
- package/dist/commands/customers.d.ts.map +1 -0
- package/dist/commands/customers.js +201 -0
- package/dist/commands/customers.js.map +1 -0
- package/dist/commands/cycles.d.ts +3 -0
- package/dist/commands/cycles.d.ts.map +1 -0
- package/dist/commands/cycles.js +67 -0
- package/dist/commands/cycles.js.map +1 -0
- package/dist/commands/documents.d.ts +3 -0
- package/dist/commands/documents.d.ts.map +1 -0
- package/dist/commands/documents.js +105 -0
- package/dist/commands/documents.js.map +1 -0
- package/dist/commands/favorites.d.ts +3 -0
- package/dist/commands/favorites.d.ts.map +1 -0
- package/dist/commands/favorites.js +101 -0
- package/dist/commands/favorites.js.map +1 -0
- package/dist/commands/index.d.ts +30 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +30 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/initiatives.d.ts +3 -0
- package/dist/commands/initiatives.d.ts.map +1 -0
- package/dist/commands/initiatives.js +106 -0
- package/dist/commands/initiatives.js.map +1 -0
- package/dist/commands/issues.d.ts +21 -0
- package/dist/commands/issues.d.ts.map +1 -0
- package/dist/commands/issues.js +1083 -0
- package/dist/commands/issues.js.map +1 -0
- package/dist/commands/labels.d.ts +3 -0
- package/dist/commands/labels.d.ts.map +1 -0
- package/dist/commands/labels.js +111 -0
- package/dist/commands/labels.js.map +1 -0
- package/dist/commands/milestones.d.ts +3 -0
- package/dist/commands/milestones.d.ts.map +1 -0
- package/dist/commands/milestones.js +94 -0
- package/dist/commands/milestones.js.map +1 -0
- package/dist/commands/notifications.d.ts +3 -0
- package/dist/commands/notifications.d.ts.map +1 -0
- package/dist/commands/notifications.js +130 -0
- package/dist/commands/notifications.js.map +1 -0
- package/dist/commands/project-labels.d.ts +3 -0
- package/dist/commands/project-labels.d.ts.map +1 -0
- package/dist/commands/project-labels.js +80 -0
- package/dist/commands/project-labels.js.map +1 -0
- package/dist/commands/project-relations.d.ts +3 -0
- package/dist/commands/project-relations.d.ts.map +1 -0
- package/dist/commands/project-relations.js +96 -0
- package/dist/commands/project-relations.js.map +1 -0
- package/dist/commands/projects.d.ts +3 -0
- package/dist/commands/projects.d.ts.map +1 -0
- package/dist/commands/projects.js +263 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/quota.d.ts +3 -0
- package/dist/commands/quota.d.ts.map +1 -0
- package/dist/commands/quota.js +28 -0
- package/dist/commands/quota.js.map +1 -0
- package/dist/commands/reactions.d.ts +7 -0
- package/dist/commands/reactions.d.ts.map +1 -0
- package/dist/commands/reactions.js +53 -0
- package/dist/commands/reactions.js.map +1 -0
- package/dist/commands/relations.d.ts +3 -0
- package/dist/commands/relations.d.ts.map +1 -0
- package/dist/commands/relations.js +73 -0
- package/dist/commands/relations.js.map +1 -0
- package/dist/commands/states.d.ts +3 -0
- package/dist/commands/states.d.ts.map +1 -0
- package/dist/commands/states.js +52 -0
- package/dist/commands/states.js.map +1 -0
- package/dist/commands/status-updates.d.ts +3 -0
- package/dist/commands/status-updates.d.ts.map +1 -0
- package/dist/commands/status-updates.js +117 -0
- package/dist/commands/status-updates.js.map +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +58 -18
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/teams.d.ts +3 -0
- package/dist/commands/teams.d.ts.map +1 -0
- package/dist/commands/teams.js +135 -0
- package/dist/commands/teams.js.map +1 -0
- package/dist/commands/templates.d.ts +3 -0
- package/dist/commands/templates.d.ts.map +1 -0
- package/dist/commands/templates.js +76 -0
- package/dist/commands/templates.js.map +1 -0
- package/dist/commands/users.d.ts +3 -0
- package/dist/commands/users.d.ts.map +1 -0
- package/dist/commands/users.js +40 -0
- package/dist/commands/users.js.map +1 -0
- package/dist/commands/views.d.ts +3 -0
- package/dist/commands/views.d.ts.map +1 -0
- package/dist/commands/views.js +177 -0
- package/dist/commands/views.js.map +1 -0
- package/dist/commands/webhooks.d.ts +3 -0
- package/dist/commands/webhooks.d.ts.map +1 -0
- package/dist/commands/webhooks.js +234 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +3 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/types.d.ts +15 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/curator/dispatch.d.ts +52 -0
- package/dist/curator/dispatch.d.ts.map +1 -0
- package/dist/curator/dispatch.js +144 -0
- package/dist/curator/dispatch.js.map +1 -0
- package/dist/curator/index.d.ts +5 -0
- package/dist/curator/index.d.ts.map +1 -0
- package/dist/curator/index.js +5 -0
- package/dist/curator/index.js.map +1 -0
- package/dist/curator/llm.d.ts +70 -0
- package/dist/curator/llm.d.ts.map +1 -0
- package/dist/curator/llm.js +107 -0
- package/dist/curator/llm.js.map +1 -0
- package/dist/curator/snapshot.d.ts +34 -0
- package/dist/curator/snapshot.d.ts.map +1 -0
- package/dist/curator/snapshot.js +127 -0
- package/dist/curator/snapshot.js.map +1 -0
- package/dist/curator/state.d.ts +50 -0
- package/dist/curator/state.d.ts.map +1 -0
- package/dist/curator/state.js +125 -0
- package/dist/curator/state.js.map +1 -0
- package/dist/lib/bulk-graphql.d.ts +144 -0
- package/dist/lib/bulk-graphql.d.ts.map +1 -0
- package/dist/lib/bulk-graphql.js +380 -0
- package/dist/lib/bulk-graphql.js.map +1 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/output/cli.d.ts +17 -0
- package/dist/output/cli.d.ts.map +1 -0
- package/dist/output/cli.js +252 -0
- package/dist/output/cli.js.map +1 -0
- package/dist/output/formatter.d.ts +6 -0
- package/dist/output/formatter.d.ts.map +1 -1
- package/dist/output/formatter.js +10 -0
- package/dist/output/formatter.js.map +1 -1
- package/dist/output/index.d.ts +1 -0
- package/dist/output/index.d.ts.map +1 -1
- package/dist/output/index.js +1 -0
- package/dist/output/index.js.map +1 -1
- package/dist/scripts/sync-linear-templates.d.ts +26 -0
- package/dist/scripts/sync-linear-templates.d.ts.map +1 -0
- package/dist/scripts/sync-linear-templates.js +115 -0
- package/dist/scripts/sync-linear-templates.js.map +1 -0
- package/dist/signals/github-commits.d.ts +31 -0
- package/dist/signals/github-commits.d.ts.map +1 -0
- package/dist/signals/github-commits.js +127 -0
- package/dist/signals/github-commits.js.map +1 -0
- package/dist/signals/github-pr.d.ts +16 -0
- package/dist/signals/github-pr.d.ts.map +1 -0
- package/dist/signals/github-pr.js +98 -0
- package/dist/signals/github-pr.js.map +1 -0
- package/dist/signals/index.d.ts +4 -0
- package/dist/signals/index.d.ts.map +1 -1
- package/dist/signals/index.js +4 -0
- package/dist/signals/index.js.map +1 -1
- package/dist/signals/linear-issues.d.ts +20 -0
- package/dist/signals/linear-issues.d.ts.map +1 -0
- package/dist/signals/linear-issues.js +115 -0
- package/dist/signals/linear-issues.js.map +1 -0
- package/dist/signals/registry.d.ts +4 -3
- package/dist/signals/registry.d.ts.map +1 -1
- package/dist/signals/registry.js +33 -11
- package/dist/signals/registry.js.map +1 -1
- package/dist/signals/slack-messages.d.ts +20 -0
- package/dist/signals/slack-messages.d.ts.map +1 -0
- package/dist/signals/slack-messages.js +129 -0
- package/dist/signals/slack-messages.js.map +1 -0
- package/dist/utils/errors.d.ts +81 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +110 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/label-policy.d.ts +60 -0
- package/dist/utils/label-policy.d.ts.map +1 -0
- package/dist/utils/label-policy.js +103 -0
- package/dist/utils/label-policy.js.map +1 -0
- package/dist/utils/parse.d.ts +48 -0
- package/dist/utils/parse.d.ts.map +1 -0
- package/dist/utils/parse.js +133 -0
- package/dist/utils/parse.js.map +1 -0
- package/dist/utils/project-status.d.ts +6 -0
- package/dist/utils/project-status.d.ts.map +1 -0
- package/dist/utils/project-status.js +33 -0
- package/dist/utils/project-status.js.map +1 -0
- package/dist/utils/rate-limit.d.ts +24 -0
- package/dist/utils/rate-limit.d.ts.map +1 -0
- package/dist/utils/rate-limit.js +89 -0
- package/dist/utils/rate-limit.js.map +1 -0
- package/dist/utils/resolve.d.ts +84 -0
- package/dist/utils/resolve.d.ts.map +1 -0
- package/dist/utils/resolve.js +172 -0
- package/dist/utils/resolve.js.map +1 -0
- package/dist/utils/sleep.d.ts +2 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +4 -0
- package/dist/utils/sleep.js.map +1 -0
- package/dist/utils/webhook-verify.d.ts +42 -0
- package/dist/utils/webhook-verify.d.ts.map +1 -0
- package/dist/utils/webhook-verify.js +65 -0
- package/dist/utils/webhook-verify.js.map +1 -0
- package/package.json +7 -2
- package/references/agent-description-template.md +31 -0
- package/references/cli-reference.md +227 -0
- package/references/curator-tiering-rules.md +78 -0
- package/references/label-policy.example.json +37 -0
- package/references/label-policy.placeholder.json +6 -0
- package/references/settings-template.md +30 -0
- package/references/signal-sources.example.json +0 -8
- package/references/sla-reference.md +70 -0
- package/references/template-index.md +34 -0
- package/references/workspace-labels.md +124 -0
- package/references/workspace-projects.md +56 -0
- package/references/workspace-routing.md +58 -0
- package/schemas/label-policy.json +72 -0
- package/scripts/postinstall.mjs +195 -0
- package/skills/linear-workspace/SKILL.md +65 -4
- package/templates/ACC-PRO-provision.md +74 -0
- package/templates/ACC-PRV-privileged.md +66 -0
- package/templates/ACC-QTR-review.md +77 -0
- package/templates/ACC-REV-revoke.md +67 -0
- package/templates/AI-USE-capability.md +111 -0
- package/templates/AUD-CAP-corrective.md +89 -0
- package/templates/AUD-INT-internal.md +92 -0
- package/templates/AUD-MGT-management.md +110 -0
- package/templates/CHG-MAJ-major.md +110 -0
- package/templates/CHG-SIG-significant.md +83 -0
- package/templates/CHG-STD-standard.md +47 -0
- package/templates/LRN-DOC-lessons.md +75 -0
- package/templates/OPS-BCK-backup.md +99 -0
- package/templates/OPS-DAT-data-mod.md +98 -0
- package/templates/RCA-DOC-root-cause.md +105 -0
- package/templates/RSK-ASS-assessment.md +87 -0
- package/templates/RSK-VND-vendor.md +113 -0
- package/templates/SEC-INC-incident.md +76 -0
- package/templates/SEC-PEN-pentest.md +58 -0
- package/templates/SEC-VLN-vulnerability.md +69 -0
- package/templates/SLA-AVL-availability.md +86 -0
- package/templates/SLA-OPS-operational.md +70 -0
- package/templates/agent-server-template/README.md +88 -0
- package/templates/agent-server-template/server.example.ts +185 -0
package/dist/config/types.js
CHANGED
package/dist/config/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,yEAAyE;AACzE,gEAAgE;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,yEAAyE;AACzE,gEAAgE;AAYhE,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC/C,OAAO;IACP,UAAU;IACV,OAAO;IACP,OAAO;IACP,OAAO;IACP,gBAAgB;IAChB,WAAW;IACX,cAAc;CACd,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { LinearClient } from "@linear/sdk";
|
|
2
|
+
import type { CuratorAction, CuratorHighAction } from "./llm.js";
|
|
3
|
+
import { type CuratorState } from "./state.js";
|
|
4
|
+
export declare const MAX_MUTATIONS = 20;
|
|
5
|
+
export declare const MAX_MEDIUM_QUEUED = 10;
|
|
6
|
+
export interface DispatchOptions {
|
|
7
|
+
dryRun?: boolean;
|
|
8
|
+
stateDir?: string;
|
|
9
|
+
maxMutations?: number;
|
|
10
|
+
maxMedium?: number;
|
|
11
|
+
now?: Date;
|
|
12
|
+
/** Test hook: override the actual Linear mutation path. */
|
|
13
|
+
applyHigh?: (client: LinearClient, action: CuratorHighAction) => Promise<{
|
|
14
|
+
ok: true;
|
|
15
|
+
} | {
|
|
16
|
+
ok: false;
|
|
17
|
+
error: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
export interface DispatchResult {
|
|
21
|
+
applied: {
|
|
22
|
+
issue_id: string;
|
|
23
|
+
rule: string;
|
|
24
|
+
from: string;
|
|
25
|
+
to: string;
|
|
26
|
+
}[];
|
|
27
|
+
queued: {
|
|
28
|
+
issue_id: string;
|
|
29
|
+
rule: string;
|
|
30
|
+
thread_key: string;
|
|
31
|
+
}[];
|
|
32
|
+
reported: {
|
|
33
|
+
issue_id: string;
|
|
34
|
+
rule: string;
|
|
35
|
+
rationale: string;
|
|
36
|
+
}[];
|
|
37
|
+
skipped: {
|
|
38
|
+
issue_id: string;
|
|
39
|
+
rule: string;
|
|
40
|
+
reason: string;
|
|
41
|
+
}[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Has this action been queued or applied within the debounce window?
|
|
45
|
+
*/
|
|
46
|
+
declare function isDebounced(state: CuratorState, action: CuratorAction, _now: Date): boolean;
|
|
47
|
+
export declare function dispatchActions(client: LinearClient, actions: CuratorAction[], state: CuratorState, opts?: DispatchOptions): Promise<DispatchResult>;
|
|
48
|
+
export declare const _internal: {
|
|
49
|
+
isDebounced: typeof isDebounced;
|
|
50
|
+
};
|
|
51
|
+
export {};
|
|
52
|
+
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/curator/dispatch.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAuB,MAAM,UAAU,CAAC;AACtF,OAAO,EAAoB,KAAK,YAAY,EAAe,MAAM,YAAY,CAAC;AAE9E,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,MAAM,WAAW,eAAe;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,2DAA2D;IAC3D,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,iBAAiB,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtH;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxE,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjE,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClE,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC9D;AAED;;GAEG;AACH,iBAAS,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAmBpF;AA6BD,wBAAsB,eAAe,CACpC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,aAAa,EAAE,EACxB,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,eAAoB,GACxB,OAAO,CAAC,cAAc,CAAC,CAkFzB;AAED,eAAO,MAAM,SAAS;;CAAkB,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Curator action dispatcher.
|
|
2
|
+
//
|
|
3
|
+
// HIGH actions auto-apply (state change + rationale comment) up to MAX_MUTATIONS.
|
|
4
|
+
// MEDIUM actions queue as pending questions in the state file — the Slack
|
|
5
|
+
// integration (or a bot) is responsible for actually posting them; the
|
|
6
|
+
// dispatcher just stages the question + sets the debounce key.
|
|
7
|
+
// LOW actions go straight to the report (no side effects).
|
|
8
|
+
//
|
|
9
|
+
// All applied actions are recorded in `curator-report.jsonl` for audit.
|
|
10
|
+
import { resolveStateId } from "../lib/bulk-graphql.js";
|
|
11
|
+
import { withRateLimit } from "../utils/rate-limit.js";
|
|
12
|
+
import { appendReportLine, debounceKey } from "./state.js";
|
|
13
|
+
export const MAX_MUTATIONS = 20;
|
|
14
|
+
export const MAX_MEDIUM_QUEUED = 10;
|
|
15
|
+
/**
|
|
16
|
+
* Has this action been queued or applied within the debounce window?
|
|
17
|
+
*/
|
|
18
|
+
function isDebounced(state, action, _now) {
|
|
19
|
+
if (action.tier === "HIGH") {
|
|
20
|
+
const key = debounceKey(action.issue_id, {
|
|
21
|
+
from: action.from_state,
|
|
22
|
+
to: action.to_state,
|
|
23
|
+
});
|
|
24
|
+
return state.pending_questions.some((q) => q.thread_key === key) || state.processed_thread_keys.includes(key);
|
|
25
|
+
}
|
|
26
|
+
if (action.tier === "MEDIUM") {
|
|
27
|
+
const proposed = action.proposed_action;
|
|
28
|
+
const key = debounceKey(action.issue_id, { type: proposed.type, from: proposed.from, to: proposed.to });
|
|
29
|
+
const recentlyAsked = state.pending_questions.some((q) => q.thread_key === key);
|
|
30
|
+
if (recentlyAsked)
|
|
31
|
+
return true;
|
|
32
|
+
if (!state.processed_thread_keys.includes(key))
|
|
33
|
+
return false;
|
|
34
|
+
// Even processed keys age out — but we don't have per-key timestamps,
|
|
35
|
+
// so we conservatively skip if the key is in the processed set.
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
async function applyHighAction(client, action) {
|
|
41
|
+
// Look up the state ID for the team prefix the issue belongs to.
|
|
42
|
+
const match = action.issue_id.match(/^([A-Z]+)-\d+$/);
|
|
43
|
+
if (!match) {
|
|
44
|
+
return { ok: false, error: `Cannot parse team prefix from issue_id ${action.issue_id}` };
|
|
45
|
+
}
|
|
46
|
+
const teamKey = match[1];
|
|
47
|
+
const stateId = await withRateLimit(() => resolveStateId(teamKey, action.to_state));
|
|
48
|
+
if (!stateId) {
|
|
49
|
+
return { ok: false, error: `State "${action.to_state}" not found on team ${teamKey}` };
|
|
50
|
+
}
|
|
51
|
+
// Resolve the issue UUID via the public CLI's findIssueByIdentifier — but we
|
|
52
|
+
// only need the UUID for the SDK update call. Cheapest path: client.issue.
|
|
53
|
+
const issue = await withRateLimit(() => client.issue(action.issue_id));
|
|
54
|
+
const update = await withRateLimit(() => client.updateIssue(issue.id, { stateId }));
|
|
55
|
+
if (!update.success) {
|
|
56
|
+
return { ok: false, error: "Linear API rejected the state update" };
|
|
57
|
+
}
|
|
58
|
+
// Comment with rationale.
|
|
59
|
+
const body = `${action.rationale}\n\n_Auto-applied by elnora-linear curator (rule ${action.rule})._`;
|
|
60
|
+
await withRateLimit(() => client.createComment({ issueId: issue.id, body }));
|
|
61
|
+
return { ok: true };
|
|
62
|
+
}
|
|
63
|
+
export async function dispatchActions(client, actions, state, opts = {}) {
|
|
64
|
+
const now = opts.now ?? new Date();
|
|
65
|
+
const maxMutations = opts.maxMutations ?? MAX_MUTATIONS;
|
|
66
|
+
const maxMedium = opts.maxMedium ?? MAX_MEDIUM_QUEUED;
|
|
67
|
+
const result = { applied: [], queued: [], reported: [], skipped: [] };
|
|
68
|
+
let highCount = 0;
|
|
69
|
+
let mediumCount = 0;
|
|
70
|
+
for (const action of actions) {
|
|
71
|
+
if (isDebounced(state, action, now)) {
|
|
72
|
+
result.skipped.push({ issue_id: action.issue_id, rule: action.rule, reason: "debounced" });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (action.tier === "LOW") {
|
|
76
|
+
result.reported.push({ issue_id: action.issue_id, rule: action.rule, rationale: action.rationale });
|
|
77
|
+
appendReportLine({ tier: "LOW", action }, { stateDir: opts.stateDir });
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (action.tier === "HIGH") {
|
|
81
|
+
if (highCount >= maxMutations) {
|
|
82
|
+
result.skipped.push({ issue_id: action.issue_id, rule: action.rule, reason: "cap_high" });
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (opts.dryRun) {
|
|
86
|
+
highCount++;
|
|
87
|
+
result.applied.push({
|
|
88
|
+
issue_id: action.issue_id,
|
|
89
|
+
rule: action.rule,
|
|
90
|
+
from: action.from_state,
|
|
91
|
+
to: action.to_state,
|
|
92
|
+
});
|
|
93
|
+
appendReportLine({ tier: "HIGH", action, dryRun: true }, { stateDir: opts.stateDir });
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const apply = opts.applyHigh ?? applyHighAction;
|
|
98
|
+
const res = await apply(client, action);
|
|
99
|
+
if (res.ok) {
|
|
100
|
+
highCount++;
|
|
101
|
+
result.applied.push({
|
|
102
|
+
issue_id: action.issue_id,
|
|
103
|
+
rule: action.rule,
|
|
104
|
+
from: action.from_state,
|
|
105
|
+
to: action.to_state,
|
|
106
|
+
});
|
|
107
|
+
const key = debounceKey(action.issue_id, { from: action.from_state, to: action.to_state });
|
|
108
|
+
state.processed_thread_keys.push(key);
|
|
109
|
+
appendReportLine({ tier: "HIGH", action, applied: true }, { stateDir: opts.stateDir });
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
result.skipped.push({ issue_id: action.issue_id, rule: action.rule, reason: res.error });
|
|
113
|
+
appendReportLine({ tier: "HIGH", action, error: res.error }, { stateDir: opts.stateDir });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
118
|
+
result.skipped.push({ issue_id: action.issue_id, rule: action.rule, reason: msg });
|
|
119
|
+
appendReportLine({ tier: "HIGH", action, error: msg }, { stateDir: opts.stateDir });
|
|
120
|
+
}
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
// MEDIUM
|
|
124
|
+
if (mediumCount >= maxMedium) {
|
|
125
|
+
result.skipped.push({ issue_id: action.issue_id, rule: action.rule, reason: "cap_medium" });
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
mediumCount++;
|
|
129
|
+
const m = action;
|
|
130
|
+
const proposed = m.proposed_action;
|
|
131
|
+
const key = debounceKey(m.issue_id, { type: proposed.type, from: proposed.from, to: proposed.to });
|
|
132
|
+
state.pending_questions.push({
|
|
133
|
+
issue_id: m.issue_id,
|
|
134
|
+
thread_key: key,
|
|
135
|
+
posted_at: now.toISOString(),
|
|
136
|
+
question_text: m.question_text,
|
|
137
|
+
});
|
|
138
|
+
result.queued.push({ issue_id: m.issue_id, rule: m.rule, thread_key: key });
|
|
139
|
+
appendReportLine({ tier: "MEDIUM", action, queued: true }, { stateDir: opts.stateDir });
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
export const _internal = { isDebounced };
|
|
144
|
+
//# sourceMappingURL=dispatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/curator/dispatch.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,EAAE;AACF,kFAAkF;AAClF,0EAA0E;AAC1E,uEAAuE;AACvE,+DAA+D;AAC/D,2DAA2D;AAC3D,EAAE;AACF,wEAAwE;AAGxE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAqB,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9E,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC;AAChC,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAmBpC;;GAEG;AACH,SAAS,WAAW,CAAC,KAAmB,EAAE,MAAqB,EAAE,IAAU;IAC1E,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE;YACxC,IAAI,EAAE,MAAM,CAAC,UAAU;YACvB,EAAE,EAAE,MAAM,CAAC,QAAQ;SACnB,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,qBAAqB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC/G,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,CAAC;QACxC,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;QACxG,MAAM,aAAa,GAAG,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC;QAChF,IAAI,aAAa;YAAE,OAAO,IAAI,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7D,sEAAsE;QACtE,gEAAgE;QAChE,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,KAAK,UAAU,eAAe,CAC7B,MAAoB,EACpB,MAAyB;IAEzB,iEAAiE;IACjE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,0CAA0C,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC1F,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpF,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,MAAM,CAAC,QAAQ,uBAAuB,OAAO,EAAE,EAAE,CAAC;IACxF,CAAC;IACD,6EAA6E;IAC7E,2EAA2E;IAC3E,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACpF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAC;IACrE,CAAC;IACD,0BAA0B;IAC1B,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,SAAS,oDAAoD,MAAM,CAAC,IAAI,KAAK,CAAC;IACrG,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,MAAoB,EACpB,OAAwB,EACxB,KAAmB,EACnB,OAAwB,EAAE;IAE1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACnC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,aAAa,CAAC;IACxD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,iBAAiB,CAAC;IACtD,MAAM,MAAM,GAAmB,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAEtF,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YAC3F,SAAS;QACV,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAC3B,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;YACpG,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACvE,SAAS;QACV,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC5B,IAAI,SAAS,IAAI,YAAY,EAAE,CAAC;gBAC/B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;gBAC1F,SAAS;YACV,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,SAAS,EAAE,CAAC;gBACZ,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;oBACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,IAAI,EAAE,MAAM,CAAC,UAAU;oBACvB,EAAE,EAAE,MAAM,CAAC,QAAQ;iBACnB,CAAC,CAAC;gBACH,gBAAgB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACtF,SAAS;YACV,CAAC;YACD,IAAI,CAAC;gBACJ,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,IAAI,eAAe,CAAC;gBAChD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBACxC,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,SAAS,EAAE,CAAC;oBACZ,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;wBACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;wBACzB,IAAI,EAAE,MAAM,CAAC,IAAI;wBACjB,IAAI,EAAE,MAAM,CAAC,UAAU;wBACvB,EAAE,EAAE,MAAM,CAAC,QAAQ;qBACnB,CAAC,CAAC;oBACH,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAC3F,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACtC,gBAAgB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACxF,CAAC;qBAAM,CAAC;oBACP,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;oBACzF,gBAAgB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC3F,CAAC;YACF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC7D,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;gBACnF,gBAAgB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACrF,CAAC;YACD,SAAS;QACV,CAAC;QAED,SAAS;QACT,IAAI,WAAW,IAAI,SAAS,EAAE,CAAC;YAC9B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;YAC5F,SAAS;QACV,CAAC;QACD,WAAW,EAAE,CAAC;QACd,MAAM,CAAC,GAAG,MAA6B,CAAC;QACxC,MAAM,QAAQ,GAAG,CAAC,CAAC,eAAe,CAAC;QACnC,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;QACnG,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC;YAC5B,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,UAAU,EAAE,GAAG;YACf,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE;YAC5B,aAAa,EAAE,CAAC,CAAC,aAAa;SAC9B,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5E,gBAAgB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { type DispatchOptions, type DispatchResult, dispatchActions, MAX_MEDIUM_QUEUED, MAX_MUTATIONS, } from "./dispatch.js";
|
|
2
|
+
export { type CuratorAction, type CuratorHighAction, type CuratorLlmOptions, type CuratorLowAction, type CuratorMediumAction, type CuratorResponse, callCuratorLlm, loadCuratorSystemPrompt, parseActionsJson, } from "./llm.js";
|
|
3
|
+
export { buildSnapshot, type PendingQuestion, type SnapshotInput } from "./snapshot.js";
|
|
4
|
+
export { appendReportLine, type CuratorRunStats, type CuratorState, debounceKey, loadState, reportPath, resolveStateDir, type StateDirOptions, saveState, statePath, } from "./state.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/curator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,eAAe,EACf,iBAAiB,EACjB,aAAa,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,cAAc,EACd,uBAAuB,EACvB,gBAAgB,GAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACxF,OAAO,EACN,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,WAAW,EACX,SAAS,EACT,UAAU,EACV,eAAe,EACf,KAAK,eAAe,EACpB,SAAS,EACT,SAAS,GACT,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { dispatchActions, MAX_MEDIUM_QUEUED, MAX_MUTATIONS, } from "./dispatch.js";
|
|
2
|
+
export { callCuratorLlm, loadCuratorSystemPrompt, parseActionsJson, } from "./llm.js";
|
|
3
|
+
export { buildSnapshot } from "./snapshot.js";
|
|
4
|
+
export { appendReportLine, debounceKey, loadState, reportPath, resolveStateDir, saveState, statePath, } from "./state.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/curator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,eAAe,EACf,iBAAiB,EACjB,aAAa,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EAON,cAAc,EACd,uBAAuB,EACvB,gBAAgB,GAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,aAAa,EAA4C,MAAM,eAAe,CAAC;AACxF,OAAO,EACN,gBAAgB,EAGhB,WAAW,EACX,SAAS,EACT,UAAU,EACV,eAAe,EAEf,SAAS,EACT,SAAS,GACT,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export interface CuratorActionBase {
|
|
2
|
+
issue_id: string;
|
|
3
|
+
tier: "HIGH" | "MEDIUM" | "LOW";
|
|
4
|
+
rule: string;
|
|
5
|
+
rationale: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CuratorHighAction extends CuratorActionBase {
|
|
8
|
+
tier: "HIGH";
|
|
9
|
+
decision: "set_state";
|
|
10
|
+
from_state: string;
|
|
11
|
+
to_state: string;
|
|
12
|
+
signals_cited: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface CuratorMediumAction extends CuratorActionBase {
|
|
15
|
+
tier: "MEDIUM";
|
|
16
|
+
decision: "ask_in_slack";
|
|
17
|
+
proposed_action: {
|
|
18
|
+
type: "set_state";
|
|
19
|
+
from: string;
|
|
20
|
+
to: string;
|
|
21
|
+
};
|
|
22
|
+
alternative_action?: {
|
|
23
|
+
type: "set_state";
|
|
24
|
+
from: string;
|
|
25
|
+
to: string;
|
|
26
|
+
};
|
|
27
|
+
question_text: string;
|
|
28
|
+
signals_cited: string[];
|
|
29
|
+
}
|
|
30
|
+
export interface CuratorLowAction extends CuratorActionBase {
|
|
31
|
+
tier: "LOW";
|
|
32
|
+
decision: "report_only";
|
|
33
|
+
}
|
|
34
|
+
export type CuratorAction = CuratorHighAction | CuratorMediumAction | CuratorLowAction;
|
|
35
|
+
export interface CuratorResponse {
|
|
36
|
+
actions: CuratorAction[];
|
|
37
|
+
summary: {
|
|
38
|
+
total_issues_reviewed?: number;
|
|
39
|
+
high_count?: number;
|
|
40
|
+
medium_count?: number;
|
|
41
|
+
low_count?: number;
|
|
42
|
+
skipped_no_signal?: number;
|
|
43
|
+
notes?: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export declare function loadCuratorSystemPrompt(opts?: {
|
|
47
|
+
agentPath?: string;
|
|
48
|
+
}): string;
|
|
49
|
+
/**
|
|
50
|
+
* Strip ```json fences (the LLM sometimes adds them despite the contract).
|
|
51
|
+
*/
|
|
52
|
+
declare function stripFences(text: string): string;
|
|
53
|
+
export declare function parseActionsJson(raw: string): CuratorResponse;
|
|
54
|
+
export interface CuratorLlmOptions {
|
|
55
|
+
model?: string;
|
|
56
|
+
apiKey?: string;
|
|
57
|
+
maxTokens?: number;
|
|
58
|
+
agentPath?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Run the curator LLM call. Returns the parsed response. Throws on missing
|
|
62
|
+
* ANTHROPIC_API_KEY (caller catches and surfaces in the report).
|
|
63
|
+
*/
|
|
64
|
+
export declare function callCuratorLlm(snapshot: string, opts?: CuratorLlmOptions): Promise<CuratorResponse>;
|
|
65
|
+
export declare const _internal: {
|
|
66
|
+
stripFences: typeof stripFences;
|
|
67
|
+
BUNDLED_AGENT_PATH: string;
|
|
68
|
+
};
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=llm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/curator/llm.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC7D,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,cAAc,CAAC;IACzB,eAAe,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,kBAAkB,CAAC,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IAC1D,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,gBAAgB,CAAC;AAEvF,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,OAAO,EAAE;QACR,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,CAmBjF;AAED;;GAEG;AACH,iBAAS,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzC;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CA0B7D;AAED,MAAM,WAAW,iBAAiB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,CA6B7G;AAED,eAAO,MAAM,SAAS;;;CAAsC,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Anthropic-API client for the curator.
|
|
2
|
+
//
|
|
3
|
+
// One Messages.create() call per curator run. The system prompt is the body
|
|
4
|
+
// of `agents/linear-state-curator.md` (loaded at runtime); the user content is
|
|
5
|
+
// the markdown snapshot from `snapshot.ts`. Response shape is parsed by
|
|
6
|
+
// `parseActionsJson`.
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
// dist/curator/llm.js → package root is two up; same for src/curator/llm.ts.
|
|
12
|
+
const PACKAGE_ROOT = resolve(HERE, "..", "..");
|
|
13
|
+
const BUNDLED_AGENT_PATH = join(PACKAGE_ROOT, "agents", "linear-state-curator.md");
|
|
14
|
+
export function loadCuratorSystemPrompt(opts = {}) {
|
|
15
|
+
const candidates = [];
|
|
16
|
+
if (opts.agentPath)
|
|
17
|
+
candidates.push(opts.agentPath);
|
|
18
|
+
candidates.push(BUNDLED_AGENT_PATH);
|
|
19
|
+
for (const path of candidates) {
|
|
20
|
+
try {
|
|
21
|
+
if (existsSync(path)) {
|
|
22
|
+
const raw = readFileSync(path, "utf-8");
|
|
23
|
+
// Strip the YAML frontmatter; the body is the prompt.
|
|
24
|
+
const match = raw.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
|
|
25
|
+
return (match ? match[1] : raw).trim();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Try next candidate.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
throw new Error("Could not locate agents/linear-state-curator.md for curator system prompt. Pass agentPath explicitly.");
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Strip ```json fences (the LLM sometimes adds them despite the contract).
|
|
36
|
+
*/
|
|
37
|
+
function stripFences(text) {
|
|
38
|
+
const fenced = text.match(/^```(?:json)?\s*([\s\S]*?)\s*```\s*$/);
|
|
39
|
+
return fenced ? fenced[1].trim() : text.trim();
|
|
40
|
+
}
|
|
41
|
+
export function parseActionsJson(raw) {
|
|
42
|
+
const trimmed = stripFences(raw);
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(trimmed);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
throw new Error(`Curator LLM output is not valid JSON: ${err.message}`);
|
|
49
|
+
}
|
|
50
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
51
|
+
throw new Error("Curator LLM output must be a JSON object.");
|
|
52
|
+
}
|
|
53
|
+
const obj = parsed;
|
|
54
|
+
if (!Array.isArray(obj.actions)) {
|
|
55
|
+
throw new Error("Curator LLM output is missing `actions` array.");
|
|
56
|
+
}
|
|
57
|
+
const validActions = [];
|
|
58
|
+
for (const raw of obj.actions) {
|
|
59
|
+
if (typeof raw !== "object" || raw === null)
|
|
60
|
+
continue;
|
|
61
|
+
const a = raw;
|
|
62
|
+
if (!a.issue_id || !a.tier || !a.rule)
|
|
63
|
+
continue;
|
|
64
|
+
if (a.tier !== "HIGH" && a.tier !== "MEDIUM" && a.tier !== "LOW")
|
|
65
|
+
continue;
|
|
66
|
+
validActions.push(a);
|
|
67
|
+
}
|
|
68
|
+
const summary = typeof obj.summary === "object" && obj.summary !== null ? obj.summary : {};
|
|
69
|
+
return { actions: validActions, summary };
|
|
70
|
+
}
|
|
71
|
+
const DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
72
|
+
const DEFAULT_MAX_TOKENS = 4096;
|
|
73
|
+
/**
|
|
74
|
+
* Run the curator LLM call. Returns the parsed response. Throws on missing
|
|
75
|
+
* ANTHROPIC_API_KEY (caller catches and surfaces in the report).
|
|
76
|
+
*/
|
|
77
|
+
export async function callCuratorLlm(snapshot, opts = {}) {
|
|
78
|
+
const apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
79
|
+
if (!apiKey) {
|
|
80
|
+
throw new Error("ANTHROPIC_API_KEY not set; cannot run the LLM phase of the curator.");
|
|
81
|
+
}
|
|
82
|
+
const model = opts.model ?? process.env.LINEAR_CURATOR_MODEL ?? DEFAULT_MODEL;
|
|
83
|
+
const maxTokens = opts.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
84
|
+
const system = loadCuratorSystemPrompt({ agentPath: opts.agentPath });
|
|
85
|
+
const Anthropic = (await import("@anthropic-ai/sdk")).default;
|
|
86
|
+
const client = new Anthropic({ apiKey });
|
|
87
|
+
const res = await client.messages.create({
|
|
88
|
+
model,
|
|
89
|
+
max_tokens: maxTokens,
|
|
90
|
+
system,
|
|
91
|
+
messages: [{ role: "user", content: snapshot }],
|
|
92
|
+
});
|
|
93
|
+
const textParts = [];
|
|
94
|
+
for (const block of res.content ?? []) {
|
|
95
|
+
if (block.type === "text") {
|
|
96
|
+
const t = block.text;
|
|
97
|
+
if (typeof t === "string")
|
|
98
|
+
textParts.push(t);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (textParts.length === 0) {
|
|
102
|
+
throw new Error("Curator LLM returned no text content.");
|
|
103
|
+
}
|
|
104
|
+
return parseActionsJson(textParts.join(""));
|
|
105
|
+
}
|
|
106
|
+
export const _internal = { stripFences, BUNDLED_AGENT_PATH };
|
|
107
|
+
//# sourceMappingURL=llm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/curator/llm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BulkIssueNode } from "../lib/bulk-graphql.js";
|
|
2
|
+
import type { Signal } from "../signals/types.js";
|
|
3
|
+
export interface PendingQuestion {
|
|
4
|
+
issue_id: string;
|
|
5
|
+
thread_key: string;
|
|
6
|
+
posted_at: string;
|
|
7
|
+
question_text: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SnapshotInput {
|
|
10
|
+
issues: BulkIssueNode[];
|
|
11
|
+
signals: Signal[];
|
|
12
|
+
pendingQuestions: PendingQuestion[];
|
|
13
|
+
tieringRulesPath?: string;
|
|
14
|
+
referencesDir?: string;
|
|
15
|
+
}
|
|
16
|
+
declare function loadTieringRules(opts: SnapshotInput): string;
|
|
17
|
+
/**
|
|
18
|
+
* Wrap untrusted text in `<untrusted>` tags so the model treats embedded
|
|
19
|
+
* directives as data, not instructions. The system prompt tells the curator
|
|
20
|
+
* never to act on directives inside these tags. We also strip any literal
|
|
21
|
+
* closing tags from the input so an attacker can't break out of the wrapper.
|
|
22
|
+
*/
|
|
23
|
+
declare function untrusted(text: string): string;
|
|
24
|
+
declare function formatPendingQuestions(qs: PendingQuestion[]): string;
|
|
25
|
+
declare function groupSignals(signals: Signal[]): Map<string, Signal[]>;
|
|
26
|
+
export declare function buildSnapshot(input: SnapshotInput): string;
|
|
27
|
+
export declare const _internal: {
|
|
28
|
+
loadTieringRules: typeof loadTieringRules;
|
|
29
|
+
formatPendingQuestions: typeof formatPendingQuestions;
|
|
30
|
+
groupSignals: typeof groupSignals;
|
|
31
|
+
untrusted: typeof untrusted;
|
|
32
|
+
};
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/curator/snapshot.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAMlD,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,iBAAS,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAcrD;AAED;;;;;GAKG;AACH,iBAAS,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvC;AAED,iBAAS,sBAAsB,CAAC,EAAE,EAAE,eAAe,EAAE,GAAG,MAAM,CAG7D;AAED,iBAAS,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAY9D;AAyCD,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAuB1D;AAED,eAAO,MAAM,SAAS;;;;;CAAwE,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Builds the markdown snapshot the curator LLM consumes.
|
|
2
|
+
//
|
|
3
|
+
// One snapshot per `elnora-linear curator-run` invocation. Embeds:
|
|
4
|
+
// - the tiering-rules markdown (from references/curator-tiering-rules.md)
|
|
5
|
+
// - any pending Slack questions (from the state file)
|
|
6
|
+
// - per-issue blocks listing relevant signals indexed by issueIdentifier
|
|
7
|
+
//
|
|
8
|
+
// Signals come from the curator's runCollect() phase. We collate by
|
|
9
|
+
// issueIdentifier so the LLM sees each issue's evidence together.
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import { dirname, join, resolve } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
// dist/curator/snapshot.js → package root is two up. Same for src/ during tests.
|
|
14
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const BUNDLED_TIERING_RULES_PATH = resolve(HERE, "..", "..", "references", "curator-tiering-rules.md");
|
|
16
|
+
function loadTieringRules(opts) {
|
|
17
|
+
const candidates = [];
|
|
18
|
+
if (opts.tieringRulesPath)
|
|
19
|
+
candidates.push(opts.tieringRulesPath);
|
|
20
|
+
if (opts.referencesDir)
|
|
21
|
+
candidates.push(join(opts.referencesDir, "curator-tiering-rules.md"));
|
|
22
|
+
// Final fallback: the copy bundled in the installed npm package.
|
|
23
|
+
candidates.push(BUNDLED_TIERING_RULES_PATH);
|
|
24
|
+
for (const path of candidates) {
|
|
25
|
+
try {
|
|
26
|
+
if (existsSync(path))
|
|
27
|
+
return readFileSync(path, "utf-8").trim();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Try next candidate.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return "(curator-tiering-rules.md not found — using defaults baked into the agent prompt)";
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Wrap untrusted text in `<untrusted>` tags so the model treats embedded
|
|
37
|
+
* directives as data, not instructions. The system prompt tells the curator
|
|
38
|
+
* never to act on directives inside these tags. We also strip any literal
|
|
39
|
+
* closing tags from the input so an attacker can't break out of the wrapper.
|
|
40
|
+
*/
|
|
41
|
+
function untrusted(text) {
|
|
42
|
+
const sanitized = text.replace(/<\/?untrusted>/gi, "");
|
|
43
|
+
return `<untrusted>${sanitized}</untrusted>`;
|
|
44
|
+
}
|
|
45
|
+
function formatPendingQuestions(qs) {
|
|
46
|
+
if (qs.length === 0)
|
|
47
|
+
return "(none)";
|
|
48
|
+
return qs.map((q) => `- ${q.issue_id} [${q.thread_key}] posted ${q.posted_at}: ${q.question_text}`).join("\n");
|
|
49
|
+
}
|
|
50
|
+
function groupSignals(signals) {
|
|
51
|
+
const out = new Map();
|
|
52
|
+
for (const sig of signals) {
|
|
53
|
+
const key = sig.issueIdentifier ?? "_unattributed";
|
|
54
|
+
const list = out.get(key);
|
|
55
|
+
if (list) {
|
|
56
|
+
list.push(sig);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
out.set(key, [sig]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function formatIssueBlock(issue, signals) {
|
|
65
|
+
const lines = [];
|
|
66
|
+
lines.push(`### ${issue.identifier} — ${issue.title}`);
|
|
67
|
+
lines.push(`- state: ${issue.state?.name ?? "(none)"} (${issue.state?.type ?? "?"})`);
|
|
68
|
+
lines.push(`- assignee: ${issue.assignee?.name ?? "(unassigned)"}`);
|
|
69
|
+
lines.push(`- project: ${issue.project?.name ?? "(none)"}`);
|
|
70
|
+
lines.push(`- team: ${issue.team?.name ?? "(none)"} (${issue.team?.key ?? "?"})`);
|
|
71
|
+
lines.push(`- labels: [${issue.labels.nodes.map((l) => l.name).join(", ")}]`);
|
|
72
|
+
lines.push(`- updatedAt: ${issue.updatedAt}`);
|
|
73
|
+
if (issue.description) {
|
|
74
|
+
const truncated = issue.description.slice(0, 600);
|
|
75
|
+
const suffix = issue.description.length > 600 ? "..." : "";
|
|
76
|
+
lines.push(`- description: ${untrusted(`${truncated}${suffix}`)}`);
|
|
77
|
+
}
|
|
78
|
+
if (signals.length > 0) {
|
|
79
|
+
lines.push("- signals:");
|
|
80
|
+
for (const sig of signals) {
|
|
81
|
+
const payload = JSON.stringify(sig.payload).slice(0, 200);
|
|
82
|
+
lines.push(` - [${sig.source}/${sig.type}] ${untrusted(payload)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
lines.push("- signals: (none)");
|
|
87
|
+
}
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
90
|
+
function formatUnattributedSignals(signals) {
|
|
91
|
+
if (signals.length === 0)
|
|
92
|
+
return "";
|
|
93
|
+
const lines = ["", "## Unattributed signals (no issueIdentifier)"];
|
|
94
|
+
for (const sig of signals.slice(0, 50)) {
|
|
95
|
+
const payload = JSON.stringify(sig.payload).slice(0, 200);
|
|
96
|
+
lines.push(`- [${sig.source}/${sig.type}] ${untrusted(payload)}`);
|
|
97
|
+
}
|
|
98
|
+
if (signals.length > 50) {
|
|
99
|
+
lines.push(`- (+${signals.length - 50} more elided)`);
|
|
100
|
+
}
|
|
101
|
+
return lines.join("\n");
|
|
102
|
+
}
|
|
103
|
+
export function buildSnapshot(input) {
|
|
104
|
+
const rules = loadTieringRules(input);
|
|
105
|
+
const grouped = groupSignals(input.signals);
|
|
106
|
+
const issueBlocks = [];
|
|
107
|
+
for (const issue of input.issues) {
|
|
108
|
+
const signals = grouped.get(issue.identifier) ?? [];
|
|
109
|
+
issueBlocks.push(formatIssueBlock(issue, signals));
|
|
110
|
+
}
|
|
111
|
+
const unattributed = formatUnattributedSignals(grouped.get("_unattributed") ?? []);
|
|
112
|
+
return [
|
|
113
|
+
"## Tiering rules",
|
|
114
|
+
rules,
|
|
115
|
+
"",
|
|
116
|
+
"## Pending Slack questions (awareness only — do NOT emit actions for these)",
|
|
117
|
+
formatPendingQuestions(input.pendingQuestions),
|
|
118
|
+
"",
|
|
119
|
+
"## Open issues snapshot",
|
|
120
|
+
issueBlocks.join("\n\n"),
|
|
121
|
+
unattributed,
|
|
122
|
+
]
|
|
123
|
+
.join("\n")
|
|
124
|
+
.trim();
|
|
125
|
+
}
|
|
126
|
+
export const _internal = { loadTieringRules, formatPendingQuestions, groupSignals, untrusted };
|
|
127
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/curator/snapshot.ts"],"names":[],"mappings":"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"}
|