@mclawnet/swarm 0.1.13 → 0.1.15
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/__tests__/always-on-activity-reader.test.d.ts +2 -0
- package/dist/__tests__/always-on-activity-reader.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-activity-reader.test.js +193 -0
- package/dist/__tests__/always-on-activity-reader.test.js.map +1 -0
- package/dist/__tests__/always-on-config.test.d.ts +2 -0
- package/dist/__tests__/always-on-config.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-config.test.js +285 -0
- package/dist/__tests__/always-on-config.test.js.map +1 -0
- package/dist/__tests__/always-on-manager.test.d.ts +2 -0
- package/dist/__tests__/always-on-manager.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-manager.test.js +797 -0
- package/dist/__tests__/always-on-manager.test.js.map +1 -0
- package/dist/__tests__/always-on-parity.test.d.ts +2 -0
- package/dist/__tests__/always-on-parity.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-parity.test.js +20 -0
- package/dist/__tests__/always-on-parity.test.js.map +1 -0
- package/dist/__tests__/cascade-picker.test.d.ts +2 -0
- package/dist/__tests__/cascade-picker.test.d.ts.map +1 -0
- package/dist/__tests__/cascade-picker.test.js +122 -0
- package/dist/__tests__/cascade-picker.test.js.map +1 -0
- package/dist/__tests__/coordinator-shipment.test.d.ts +2 -0
- package/dist/__tests__/coordinator-shipment.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator-shipment.test.js +280 -0
- package/dist/__tests__/coordinator-shipment.test.js.map +1 -0
- package/dist/__tests__/coordinator-workspace-recover.test.d.ts +2 -0
- package/dist/__tests__/coordinator-workspace-recover.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator-workspace-recover.test.js +140 -0
- package/dist/__tests__/coordinator-workspace-recover.test.js.map +1 -0
- package/dist/__tests__/coordinator-workspace.test.d.ts +2 -0
- package/dist/__tests__/coordinator-workspace.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator-workspace.test.js +135 -0
- package/dist/__tests__/coordinator-workspace.test.js.map +1 -0
- package/dist/__tests__/default-runner-epipe.test.d.ts +2 -0
- package/dist/__tests__/default-runner-epipe.test.d.ts.map +1 -0
- package/dist/__tests__/default-runner-epipe.test.js +43 -0
- package/dist/__tests__/default-runner-epipe.test.js.map +1 -0
- package/dist/__tests__/discovery-scheduler.test.d.ts +2 -0
- package/dist/__tests__/discovery-scheduler.test.d.ts.map +1 -0
- package/dist/__tests__/discovery-scheduler.test.js +367 -0
- package/dist/__tests__/discovery-scheduler.test.js.map +1 -0
- package/dist/__tests__/env-forward-e2e.test.d.ts +2 -0
- package/dist/__tests__/env-forward-e2e.test.d.ts.map +1 -0
- package/dist/__tests__/env-forward-e2e.test.js +57 -0
- package/dist/__tests__/env-forward-e2e.test.js.map +1 -0
- package/dist/__tests__/gh-pr-creator.test.d.ts +2 -0
- package/dist/__tests__/gh-pr-creator.test.d.ts.map +1 -0
- package/dist/__tests__/gh-pr-creator.test.js +107 -0
- package/dist/__tests__/gh-pr-creator.test.js.map +1 -0
- package/dist/__tests__/git-worktree-provider.test.d.ts +2 -0
- package/dist/__tests__/git-worktree-provider.test.d.ts.map +1 -0
- package/dist/__tests__/git-worktree-provider.test.js +98 -0
- package/dist/__tests__/git-worktree-provider.test.js.map +1 -0
- package/dist/__tests__/gitignore-check.test.d.ts +2 -0
- package/dist/__tests__/gitignore-check.test.d.ts.map +1 -0
- package/dist/__tests__/gitignore-check.test.js +39 -0
- package/dist/__tests__/gitignore-check.test.js.map +1 -0
- package/dist/__tests__/idea-research-source.test.d.ts +2 -0
- package/dist/__tests__/idea-research-source.test.d.ts.map +1 -0
- package/dist/__tests__/idea-research-source.test.js +425 -0
- package/dist/__tests__/idea-research-source.test.js.map +1 -0
- package/dist/__tests__/idea-todo-source.test.d.ts +2 -0
- package/dist/__tests__/idea-todo-source.test.d.ts.map +1 -0
- package/dist/__tests__/idea-todo-source.test.js +258 -0
- package/dist/__tests__/idea-todo-source.test.js.map +1 -0
- package/dist/__tests__/introspection-dedupe.test.d.ts +2 -0
- package/dist/__tests__/introspection-dedupe.test.d.ts.map +1 -0
- package/dist/__tests__/introspection-dedupe.test.js +484 -0
- package/dist/__tests__/introspection-dedupe.test.js.map +1 -0
- package/dist/__tests__/introspection-source.test.d.ts +2 -0
- package/dist/__tests__/introspection-source.test.d.ts.map +1 -0
- package/dist/__tests__/introspection-source.test.js +1051 -0
- package/dist/__tests__/introspection-source.test.js.map +1 -0
- package/dist/__tests__/migration-roles.test.js +1 -22
- package/dist/__tests__/migration-roles.test.js.map +1 -1
- package/dist/__tests__/reconcile-researching.test.d.ts +2 -0
- package/dist/__tests__/reconcile-researching.test.d.ts.map +1 -0
- package/dist/__tests__/reconcile-researching.test.js +224 -0
- package/dist/__tests__/reconcile-researching.test.js.map +1 -0
- package/dist/__tests__/role-loader-preamble-all.test.js +3 -1
- package/dist/__tests__/role-loader-preamble-all.test.js.map +1 -1
- package/dist/__tests__/role-loader.test.js +95 -0
- package/dist/__tests__/role-loader.test.js.map +1 -1
- package/dist/__tests__/role-prompt-no-legacy-protocol.test.js +3 -1
- package/dist/__tests__/role-prompt-no-legacy-protocol.test.js.map +1 -1
- package/dist/__tests__/secret-scrub.test.d.ts +2 -0
- package/dist/__tests__/secret-scrub.test.d.ts.map +1 -0
- package/dist/__tests__/secret-scrub.test.js +55 -0
- package/dist/__tests__/secret-scrub.test.js.map +1 -0
- package/dist/__tests__/shipment-actions.test.d.ts +2 -0
- package/dist/__tests__/shipment-actions.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-actions.test.js +378 -0
- package/dist/__tests__/shipment-actions.test.js.map +1 -0
- package/dist/__tests__/shipment-persistence.test.d.ts +2 -0
- package/dist/__tests__/shipment-persistence.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-persistence.test.js +120 -0
- package/dist/__tests__/shipment-persistence.test.js.map +1 -0
- package/dist/__tests__/shipment-pipeline.test.d.ts +2 -0
- package/dist/__tests__/shipment-pipeline.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-pipeline.test.js +392 -0
- package/dist/__tests__/shipment-pipeline.test.js.map +1 -0
- package/dist/__tests__/shipment-report.test.d.ts +2 -0
- package/dist/__tests__/shipment-report.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-report.test.js +78 -0
- package/dist/__tests__/shipment-report.test.js.map +1 -0
- package/dist/__tests__/shipment-stdin-integration.test.d.ts +2 -0
- package/dist/__tests__/shipment-stdin-integration.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-stdin-integration.test.js +49 -0
- package/dist/__tests__/shipment-stdin-integration.test.js.map +1 -0
- package/dist/__tests__/shipment-type-parity.test.d.ts +2 -0
- package/dist/__tests__/shipment-type-parity.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-type-parity.test.js +10 -0
- package/dist/__tests__/shipment-type-parity.test.js.map +1 -0
- package/dist/__tests__/snapshot-copy-provider.test.d.ts +2 -0
- package/dist/__tests__/snapshot-copy-provider.test.d.ts.map +1 -0
- package/dist/__tests__/snapshot-copy-provider.test.js +88 -0
- package/dist/__tests__/snapshot-copy-provider.test.js.map +1 -0
- package/dist/__tests__/swarm-coordinator-backend.test.js +153 -0
- package/dist/__tests__/swarm-coordinator-backend.test.js.map +1 -1
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts +2 -0
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts.map +1 -0
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.js +111 -0
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.js.map +1 -0
- package/dist/__tests__/task-store-source.test.d.ts +2 -0
- package/dist/__tests__/task-store-source.test.d.ts.map +1 -0
- package/dist/__tests__/task-store-source.test.js +56 -0
- package/dist/__tests__/task-store-source.test.js.map +1 -0
- package/dist/__tests__/transport-detect.test.d.ts +2 -0
- package/dist/__tests__/transport-detect.test.d.ts.map +1 -0
- package/dist/__tests__/transport-detect.test.js +92 -0
- package/dist/__tests__/transport-detect.test.js.map +1 -0
- package/dist/__tests__/workcycle-runner-cascade.test.d.ts +2 -0
- package/dist/__tests__/workcycle-runner-cascade.test.d.ts.map +1 -0
- package/dist/__tests__/workcycle-runner-cascade.test.js +203 -0
- package/dist/__tests__/workcycle-runner-cascade.test.js.map +1 -0
- package/dist/__tests__/workcycle-runner.test.d.ts +2 -0
- package/dist/__tests__/workcycle-runner.test.d.ts.map +1 -0
- package/dist/__tests__/workcycle-runner.test.js +369 -0
- package/dist/__tests__/workcycle-runner.test.js.map +1 -0
- package/dist/__tests__/workspace-diff.test.d.ts +2 -0
- package/dist/__tests__/workspace-diff.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-diff.test.js +62 -0
- package/dist/__tests__/workspace-diff.test.js.map +1 -0
- package/dist/__tests__/workspace-manager.test.d.ts +2 -0
- package/dist/__tests__/workspace-manager.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-manager.test.js +120 -0
- package/dist/__tests__/workspace-manager.test.js.map +1 -0
- package/dist/__tests__/workspace-types.test.d.ts +2 -0
- package/dist/__tests__/workspace-types.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-types.test.js +37 -0
- package/dist/__tests__/workspace-types.test.js.map +1 -0
- package/dist/__tests__/worktree-gc.test.d.ts +2 -0
- package/dist/__tests__/worktree-gc.test.d.ts.map +1 -0
- package/dist/__tests__/worktree-gc.test.js +183 -0
- package/dist/__tests__/worktree-gc.test.js.map +1 -0
- package/dist/always-on/activity-reader.d.ts +27 -0
- package/dist/always-on/activity-reader.d.ts.map +1 -0
- package/dist/always-on/activity-reader.js +95 -0
- package/dist/always-on/activity-reader.js.map +1 -0
- package/dist/always-on/always-on-manager.d.ts +170 -0
- package/dist/always-on/always-on-manager.d.ts.map +1 -0
- package/dist/always-on/always-on-manager.js +538 -0
- package/dist/always-on/always-on-manager.js.map +1 -0
- package/dist/always-on/config.d.ts +141 -0
- package/dist/always-on/config.d.ts.map +1 -0
- package/dist/always-on/config.js +324 -0
- package/dist/always-on/config.js.map +1 -0
- package/dist/always-on/discovery-scheduler.d.ts +60 -0
- package/dist/always-on/discovery-scheduler.d.ts.map +1 -0
- package/dist/always-on/discovery-scheduler.js +287 -0
- package/dist/always-on/discovery-scheduler.js.map +1 -0
- package/dist/always-on/ideas-client.d.ts +23 -0
- package/dist/always-on/ideas-client.d.ts.map +1 -0
- package/dist/always-on/ideas-client.js +13 -0
- package/dist/always-on/ideas-client.js.map +1 -0
- package/dist/always-on/reconcile-researching.d.ts +42 -0
- package/dist/always-on/reconcile-researching.d.ts.map +1 -0
- package/dist/always-on/reconcile-researching.js +133 -0
- package/dist/always-on/reconcile-researching.js.map +1 -0
- package/dist/always-on/task-sources/cascade-picker.d.ts +42 -0
- package/dist/always-on/task-sources/cascade-picker.d.ts.map +1 -0
- package/dist/always-on/task-sources/cascade-picker.js +65 -0
- package/dist/always-on/task-sources/cascade-picker.js.map +1 -0
- package/dist/always-on/task-sources/idea-dedupe.d.ts +62 -0
- package/dist/always-on/task-sources/idea-dedupe.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-dedupe.js +130 -0
- package/dist/always-on/task-sources/idea-dedupe.js.map +1 -0
- package/dist/always-on/task-sources/idea-research-source.d.ts +46 -0
- package/dist/always-on/task-sources/idea-research-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-research-source.js +308 -0
- package/dist/always-on/task-sources/idea-research-source.js.map +1 -0
- package/dist/always-on/task-sources/idea-sort.d.ts +3 -0
- package/dist/always-on/task-sources/idea-sort.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-sort.js +25 -0
- package/dist/always-on/task-sources/idea-sort.js.map +1 -0
- package/dist/always-on/task-sources/idea-todo-source.d.ts +48 -0
- package/dist/always-on/task-sources/idea-todo-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-todo-source.js +226 -0
- package/dist/always-on/task-sources/idea-todo-source.js.map +1 -0
- package/dist/always-on/task-sources/introspection-source.d.ts +101 -0
- package/dist/always-on/task-sources/introspection-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/introspection-source.js +695 -0
- package/dist/always-on/task-sources/introspection-source.js.map +1 -0
- package/dist/always-on/task-sources/task-store-source.d.ts +15 -0
- package/dist/always-on/task-sources/task-store-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/task-store-source.js +59 -0
- package/dist/always-on/task-sources/task-store-source.js.map +1 -0
- package/dist/always-on/task-sources/types.d.ts +108 -0
- package/dist/always-on/task-sources/types.d.ts.map +1 -0
- package/dist/always-on/task-sources/types.js +13 -0
- package/dist/always-on/task-sources/types.js.map +1 -0
- package/dist/always-on/types.d.ts +76 -0
- package/dist/always-on/types.d.ts.map +1 -0
- package/dist/always-on/types.js +17 -0
- package/dist/always-on/types.js.map +1 -0
- package/dist/always-on/workcycle-runner.d.ts +115 -0
- package/dist/always-on/workcycle-runner.d.ts.map +1 -0
- package/dist/always-on/workcycle-runner.js +285 -0
- package/dist/always-on/workcycle-runner.js.map +1 -0
- package/dist/always-on/worktree-gc.d.ts +41 -0
- package/dist/always-on/worktree-gc.d.ts.map +1 -0
- package/dist/always-on/worktree-gc.js +167 -0
- package/dist/always-on/worktree-gc.js.map +1 -0
- package/dist/index.d.ts +26 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/persistence.d.ts +37 -1
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +48 -0
- package/dist/persistence.js.map +1 -1
- package/dist/retrospective.d.ts.map +1 -1
- package/dist/retrospective.js +6 -0
- package/dist/retrospective.js.map +1 -1
- package/dist/roles/role-loader.d.ts +1 -1
- package/dist/roles/role-loader.d.ts.map +1 -1
- package/dist/roles/role-loader.js +18 -0
- package/dist/roles/role-loader.js.map +1 -1
- package/dist/roles/types.d.ts +12 -0
- package/dist/roles/types.d.ts.map +1 -1
- package/dist/shipment/gh-pr-creator.d.ts +28 -0
- package/dist/shipment/gh-pr-creator.d.ts.map +1 -0
- package/dist/shipment/gh-pr-creator.js +80 -0
- package/dist/shipment/gh-pr-creator.js.map +1 -0
- package/dist/shipment/report.d.ts +27 -0
- package/dist/shipment/report.d.ts.map +1 -0
- package/dist/shipment/report.js +41 -0
- package/dist/shipment/report.js.map +1 -0
- package/dist/shipment/secret-scrub.d.ts +12 -0
- package/dist/shipment/secret-scrub.d.ts.map +1 -0
- package/dist/shipment/secret-scrub.js +30 -0
- package/dist/shipment/secret-scrub.js.map +1 -0
- package/dist/shipment/shipment-actions.d.ts +85 -0
- package/dist/shipment/shipment-actions.d.ts.map +1 -0
- package/dist/shipment/shipment-actions.js +190 -0
- package/dist/shipment/shipment-actions.js.map +1 -0
- package/dist/shipment/shipment-pipeline.d.ts +48 -0
- package/dist/shipment/shipment-pipeline.d.ts.map +1 -0
- package/dist/shipment/shipment-pipeline.js +256 -0
- package/dist/shipment/shipment-pipeline.js.map +1 -0
- package/dist/shipment/transport-detect.d.ts +16 -0
- package/dist/shipment/transport-detect.d.ts.map +1 -0
- package/dist/shipment/transport-detect.js +54 -0
- package/dist/shipment/transport-detect.js.map +1 -0
- package/dist/shipment/workspace-diff.d.ts +39 -0
- package/dist/shipment/workspace-diff.d.ts.map +1 -0
- package/dist/shipment/workspace-diff.js +64 -0
- package/dist/shipment/workspace-diff.js.map +1 -0
- package/dist/swarm-coordinator.d.ts +20 -1
- package/dist/swarm-coordinator.d.ts.map +1 -1
- package/dist/swarm-coordinator.js +193 -10
- package/dist/swarm-coordinator.js.map +1 -1
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/workspace/git-worktree-provider.d.ts +11 -0
- package/dist/workspace/git-worktree-provider.d.ts.map +1 -0
- package/dist/workspace/git-worktree-provider.js +123 -0
- package/dist/workspace/git-worktree-provider.js.map +1 -0
- package/dist/workspace/gitignore-check.d.ts +10 -0
- package/dist/workspace/gitignore-check.d.ts.map +1 -0
- package/dist/workspace/gitignore-check.js +25 -0
- package/dist/workspace/gitignore-check.js.map +1 -0
- package/dist/workspace/index.d.ts +5 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +5 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/snapshot-copy-provider.d.ts +11 -0
- package/dist/workspace/snapshot-copy-provider.d.ts.map +1 -0
- package/dist/workspace/snapshot-copy-provider.js +66 -0
- package/dist/workspace/snapshot-copy-provider.js.map +1 -0
- package/dist/workspace/types.d.ts +36 -0
- package/dist/workspace/types.d.ts.map +1 -0
- package/dist/workspace/types.js +2 -0
- package/dist/workspace/types.js.map +1 -0
- package/dist/workspace/workspace-manager.d.ts +30 -0
- package/dist/workspace/workspace-manager.d.ts.map +1 -0
- package/dist/workspace/workspace-manager.js +104 -0
- package/dist/workspace/workspace-manager.js.map +1 -0
- package/package.json +4 -4
- package/roles/queen.md +1 -0
- package/templates/introspection.md +64 -0
- package/templates/research-only.md +58 -0
- package/roles/preset-analyst-simons.md +0 -39
- package/roles/preset-architect-knuth.md +0 -39
- package/roles/preset-designer-norman.md +0 -39
- package/roles/preset-designer.md +0 -39
- package/roles/preset-dev-carmack.md +0 -39
- package/roles/preset-dev-gosling.md +0 -39
- package/roles/preset-developer.md +0 -52
- package/roles/preset-manager-grove.md +0 -39
- package/roles/preset-manager-musk.md +0 -39
- package/roles/preset-pm.md +0 -78
- package/roles/preset-researcher-feynman.md +0 -39
- package/roles/preset-reviewer.md +0 -46
- package/roles/preset-strategist-buffett.md +0 -39
- package/roles/preset-strategist-munger.md +0 -39
- package/roles/preset-strategist-sunzi.md +0 -39
- package/roles/preset-tester-beck.md +0 -40
- package/roles/preset-tester.md +0 -47
- package/roles/preset-writer-orwell.md +0 -39
- package/roles/preset-writer.md +0 -39
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export declare const DEDUPE_BODY_PREFIX_CHARS = 500;
|
|
2
|
+
export declare const DEDUPE_SHINGLE_K = 4;
|
|
3
|
+
export declare const DEDUPE_JACCARD_THRESHOLD = 0.7;
|
|
4
|
+
/**
|
|
5
|
+
* Lowercase, strip punctuation/symbols (Unicode-aware), collapse runs of
|
|
6
|
+
* whitespace, trim. Two titles that normalize to the same string are treated
|
|
7
|
+
* as identical for dedupe purposes.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeTitle(title: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Character k-shingle set over a lowercased + whitespace-collapsed copy of
|
|
12
|
+
* `text`. Returns an empty set for input shorter than `k` *after*
|
|
13
|
+
* normalization (so {whitespace-only, empty, "ab"} all return empty sets at
|
|
14
|
+
* k=4), since a single shingle would otherwise produce spurious Jaccard=1
|
|
15
|
+
* matches against any other tiny input.
|
|
16
|
+
*/
|
|
17
|
+
export declare function shingles(text: string, k?: number): Set<string>;
|
|
18
|
+
export declare function jaccard(a: Set<string>, b: Set<string>): number;
|
|
19
|
+
export interface DedupeIndexEntry {
|
|
20
|
+
id: string;
|
|
21
|
+
normalizedTitle: string;
|
|
22
|
+
bodyShingles: Set<string>;
|
|
23
|
+
}
|
|
24
|
+
export interface DedupeCandidate {
|
|
25
|
+
title: string;
|
|
26
|
+
body: string;
|
|
27
|
+
}
|
|
28
|
+
export interface DedupeMatch {
|
|
29
|
+
matchedIdeaId: string;
|
|
30
|
+
reason: "title-exact" | "shingle-jaccard";
|
|
31
|
+
/** Jaccard score when reason === "shingle-jaccard"; omitted otherwise. */
|
|
32
|
+
score?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Pre-compute an index over the existing ideas pulled from the hub so each
|
|
36
|
+
* incoming candidate is checked in O(N) without re-tokenizing on every
|
|
37
|
+
* comparison. The index is built once per ingest cycle and discarded.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildDedupeIndex(existing: Array<{
|
|
40
|
+
id: string;
|
|
41
|
+
title: string;
|
|
42
|
+
body: string;
|
|
43
|
+
}>): DedupeIndexEntry[];
|
|
44
|
+
/**
|
|
45
|
+
* Linear scan: return the first index entry that matches the candidate, or
|
|
46
|
+
* null if none cross either threshold. Title-exact is checked before
|
|
47
|
+
* shingle-jaccard so the cheaper signal wins ties and the returned reason is
|
|
48
|
+
* the more interpretable one.
|
|
49
|
+
*/
|
|
50
|
+
export declare function findDuplicate(candidate: DedupeCandidate, index: DedupeIndexEntry[], threshold?: number): DedupeMatch | null;
|
|
51
|
+
/**
|
|
52
|
+
* Convenience: extend an existing index with a freshly-inserted idea so the
|
|
53
|
+
* next candidate in the same cycle is checked against it too (prevents the
|
|
54
|
+
* queen from accidentally producing two near-identical ideas in one cohort
|
|
55
|
+
* and slipping both past the hub).
|
|
56
|
+
*/
|
|
57
|
+
export declare function appendToIndex(index: DedupeIndexEntry[], inserted: {
|
|
58
|
+
id: string;
|
|
59
|
+
title: string;
|
|
60
|
+
body: string;
|
|
61
|
+
}): void;
|
|
62
|
+
//# sourceMappingURL=idea-dedupe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idea-dedupe.d.ts","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-dedupe.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,GAAE,MAAyB,GAAG,GAAG,CAAC,MAAM,CAAC,CAQhF;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAQ9D;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,aAAa,GAAG,iBAAiB,CAAC;IAC1C,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAC3D,gBAAgB,EAAE,CAMpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,eAAe,EAC1B,KAAK,EAAE,gBAAgB,EAAE,EACzB,SAAS,GAAE,MAAiC,GAC3C,WAAW,GAAG,IAAI,CA2BpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,gBAAgB,EAAE,EACzB,QAAQ,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpD,IAAI,CAMN"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// M8.2 — cheap text-similarity dedupe for IntrospectionSource.
|
|
2
|
+
//
|
|
3
|
+
// Used by `introspection-source.ts` to avoid re-posting the same candidate
|
|
4
|
+
// idea every introspection cycle (always-on can fire many ticks/day, and the
|
|
5
|
+
// queen's gap analysis tends to converge on the same handful of obvious
|
|
6
|
+
// "missing tests / missing docs / dead code" findings until the user actually
|
|
7
|
+
// acts on them).
|
|
8
|
+
//
|
|
9
|
+
// Two cheap checks, OR'd together:
|
|
10
|
+
// 1. Normalized-title exact match (lowercase + collapsed whitespace +
|
|
11
|
+
// stripped punctuation). Catches the dominant duplicate case where the
|
|
12
|
+
// queen produces an identical title across cycles.
|
|
13
|
+
// 2. Body-prefix k-shingle Jaccard similarity over the first
|
|
14
|
+
// `DEDUPE_BODY_PREFIX_CHARS` characters. Catches re-phrasings where
|
|
15
|
+
// title drifts slightly ("Add input validation" vs "Validate inputs")
|
|
16
|
+
// but body remains effectively the same.
|
|
17
|
+
//
|
|
18
|
+
// No embeddings, no network, no external deps — pure string ops. The
|
|
19
|
+
// reasoning is that the introspection corpus is tiny (≤ a few hundred ideas
|
|
20
|
+
// per project even after months) and false positives are bounded by the
|
|
21
|
+
// `INTROSPECTION_MAX_IDEAS` cap (3/cycle); even if dedupe occasionally
|
|
22
|
+
// suppresses a borderline-novel idea, the user can still discover it by
|
|
23
|
+
// re-running introspection after acting on prior suggestions.
|
|
24
|
+
export const DEDUPE_BODY_PREFIX_CHARS = 500;
|
|
25
|
+
export const DEDUPE_SHINGLE_K = 4;
|
|
26
|
+
export const DEDUPE_JACCARD_THRESHOLD = 0.7;
|
|
27
|
+
/**
|
|
28
|
+
* Lowercase, strip punctuation/symbols (Unicode-aware), collapse runs of
|
|
29
|
+
* whitespace, trim. Two titles that normalize to the same string are treated
|
|
30
|
+
* as identical for dedupe purposes.
|
|
31
|
+
*/
|
|
32
|
+
export function normalizeTitle(title) {
|
|
33
|
+
return title
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.replace(/[\p{P}\p{S}]+/gu, " ")
|
|
36
|
+
.replace(/\s+/g, " ")
|
|
37
|
+
.trim();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Character k-shingle set over a lowercased + whitespace-collapsed copy of
|
|
41
|
+
* `text`. Returns an empty set for input shorter than `k` *after*
|
|
42
|
+
* normalization (so {whitespace-only, empty, "ab"} all return empty sets at
|
|
43
|
+
* k=4), since a single shingle would otherwise produce spurious Jaccard=1
|
|
44
|
+
* matches against any other tiny input.
|
|
45
|
+
*/
|
|
46
|
+
export function shingles(text, k = DEDUPE_SHINGLE_K) {
|
|
47
|
+
const norm = text.toLowerCase().replace(/\s+/g, " ").trim();
|
|
48
|
+
const out = new Set();
|
|
49
|
+
if (norm.length < k)
|
|
50
|
+
return out;
|
|
51
|
+
for (let i = 0; i <= norm.length - k; i++) {
|
|
52
|
+
out.add(norm.slice(i, i + k));
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
export function jaccard(a, b) {
|
|
57
|
+
if (a.size === 0 || b.size === 0)
|
|
58
|
+
return 0;
|
|
59
|
+
const [small, large] = a.size < b.size ? [a, b] : [b, a];
|
|
60
|
+
let intersect = 0;
|
|
61
|
+
for (const s of small)
|
|
62
|
+
if (large.has(s))
|
|
63
|
+
intersect++;
|
|
64
|
+
const union = a.size + b.size - intersect;
|
|
65
|
+
if (union === 0)
|
|
66
|
+
return 0;
|
|
67
|
+
return intersect / union;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Pre-compute an index over the existing ideas pulled from the hub so each
|
|
71
|
+
* incoming candidate is checked in O(N) without re-tokenizing on every
|
|
72
|
+
* comparison. The index is built once per ingest cycle and discarded.
|
|
73
|
+
*/
|
|
74
|
+
export function buildDedupeIndex(existing) {
|
|
75
|
+
return existing.map((e) => ({
|
|
76
|
+
id: e.id,
|
|
77
|
+
normalizedTitle: normalizeTitle(e.title),
|
|
78
|
+
bodyShingles: shingles((e.body ?? "").slice(0, DEDUPE_BODY_PREFIX_CHARS)),
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Linear scan: return the first index entry that matches the candidate, or
|
|
83
|
+
* null if none cross either threshold. Title-exact is checked before
|
|
84
|
+
* shingle-jaccard so the cheaper signal wins ties and the returned reason is
|
|
85
|
+
* the more interpretable one.
|
|
86
|
+
*/
|
|
87
|
+
export function findDuplicate(candidate, index, threshold = DEDUPE_JACCARD_THRESHOLD) {
|
|
88
|
+
const normTitle = normalizeTitle(candidate.title);
|
|
89
|
+
if (normTitle.length > 0) {
|
|
90
|
+
for (const e of index) {
|
|
91
|
+
if (e.normalizedTitle.length > 0 && e.normalizedTitle === normTitle) {
|
|
92
|
+
return { matchedIdeaId: e.id, reason: "title-exact" };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const candShingles = shingles((candidate.body ?? "").slice(0, DEDUPE_BODY_PREFIX_CHARS));
|
|
97
|
+
if (candShingles.size === 0)
|
|
98
|
+
return null;
|
|
99
|
+
let best = null;
|
|
100
|
+
for (const e of index) {
|
|
101
|
+
if (e.bodyShingles.size === 0)
|
|
102
|
+
continue;
|
|
103
|
+
const score = jaccard(candShingles, e.bodyShingles);
|
|
104
|
+
if (score >= threshold && (!best || score > best.score)) {
|
|
105
|
+
best = { score, entry: e };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (best) {
|
|
109
|
+
return {
|
|
110
|
+
matchedIdeaId: best.entry.id,
|
|
111
|
+
reason: "shingle-jaccard",
|
|
112
|
+
score: best.score,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Convenience: extend an existing index with a freshly-inserted idea so the
|
|
119
|
+
* next candidate in the same cycle is checked against it too (prevents the
|
|
120
|
+
* queen from accidentally producing two near-identical ideas in one cohort
|
|
121
|
+
* and slipping both past the hub).
|
|
122
|
+
*/
|
|
123
|
+
export function appendToIndex(index, inserted) {
|
|
124
|
+
index.push({
|
|
125
|
+
id: inserted.id,
|
|
126
|
+
normalizedTitle: normalizeTitle(inserted.title),
|
|
127
|
+
bodyShingles: shingles((inserted.body ?? "").slice(0, DEDUPE_BODY_PREFIX_CHARS)),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=idea-dedupe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idea-dedupe.js","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-dedupe.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,wEAAwE;AACxE,8EAA8E;AAC9E,iBAAiB;AACjB,EAAE;AACF,mCAAmC;AACnC,wEAAwE;AACxE,4EAA4E;AAC5E,wDAAwD;AACxD,+DAA+D;AAC/D,yEAAyE;AACzE,2EAA2E;AAC3E,8CAA8C;AAC9C,EAAE;AACF,qEAAqE;AACrE,4EAA4E;AAC5E,wEAAwE;AACxE,uEAAuE;AACvE,wEAAwE;AACxE,8DAA8D;AAE9D,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAC5C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAClC,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,KAAK;SACT,WAAW,EAAE;SACb,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC;SAC/B,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,IAAY,gBAAgB;IACjE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5D,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,CAAc,EAAE,CAAc;IACpD,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzD,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,SAAS,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,SAAS,CAAC;IAC1C,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1B,OAAO,SAAS,GAAG,KAAK,CAAC;AAC3B,CAAC;AAoBD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAA4D;IAE5D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,eAAe,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC;QACxC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAC;KAC1E,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,SAA0B,EAC1B,KAAyB,EACzB,YAAoB,wBAAwB;IAE5C,MAAM,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBACpE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAC,CAAC;IACzF,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,IAAI,GAAsD,IAAI,CAAC;IACnE,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC;YAAE,SAAS;QACxC,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;QACpD,IAAI,KAAK,IAAI,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,IAAI,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,IAAI,IAAI,EAAE,CAAC;QACT,OAAO;YACL,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE;YAC5B,MAAM,EAAE,iBAAiB;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAyB,EACzB,QAAqD;IAErD,KAAK,CAAC,IAAI,CAAC;QACT,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,eAAe,EAAE,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC;QAC/C,YAAY,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAC;KACjF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { IdeasClient, ResearchConclusion } from "../ideas-client.js";
|
|
2
|
+
import { sortIdeasByPriorityThenAge } from "./idea-sort.js";
|
|
3
|
+
import type { TaskSource } from "./types.js";
|
|
4
|
+
export interface IdeaResearchSourceOptions {
|
|
5
|
+
ideasClient: IdeasClient;
|
|
6
|
+
/**
|
|
7
|
+
* Per-cycle list cap forwarded to the ideas API. Default 50 — only the
|
|
8
|
+
* head after sorting is consumed each cycle.
|
|
9
|
+
*/
|
|
10
|
+
listLimit?: number;
|
|
11
|
+
/** Optional warn logger; defaults to silent. */
|
|
12
|
+
warn?: (obj: Record<string, unknown>, msg?: string) => void;
|
|
13
|
+
/** Optional info logger; defaults to silent. */
|
|
14
|
+
info?: (obj: Record<string, unknown>, msg?: string) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Test seam: override report directory resolution. Defaults to
|
|
17
|
+
* `<projectRoot>/.clawnet/ideas/<ideaId>`.
|
|
18
|
+
*/
|
|
19
|
+
resolveReportDir?: (projectRoot: string, ideaId: string) => string;
|
|
20
|
+
/** Test seam: clock. */
|
|
21
|
+
now?: () => Date;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Re-export under the original name for back-compat (S5 hotfix moved the
|
|
25
|
+
* implementation into `./idea-sort` to deduplicate the two source files).
|
|
26
|
+
*/
|
|
27
|
+
export declare const sortIdeasForResearch: typeof sortIdeasByPriorityThenAge;
|
|
28
|
+
/**
|
|
29
|
+
* Minimal YAML-frontmatter parser. We only need the `conclusion` field for
|
|
30
|
+
* M8.1, so a 20-line scanner is plenty — pulling in `gray-matter` for one
|
|
31
|
+
* key would be overkill and adds a new transitive dep (per M8.1 constraints).
|
|
32
|
+
*
|
|
33
|
+
* Recognises:
|
|
34
|
+
* ---
|
|
35
|
+
* conclusion: promote ← keys are flat; values are unquoted single tokens
|
|
36
|
+
* key: value
|
|
37
|
+
* ---
|
|
38
|
+
* body…
|
|
39
|
+
*
|
|
40
|
+
* Returns null if no frontmatter block is present. Unknown keys are ignored.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseReportFrontmatter(content: string): Record<string, string> | null;
|
|
43
|
+
/** Tolerant casing + dash/space variants. Unknown → null (caller falls back). */
|
|
44
|
+
export declare function normaliseConclusion(raw: string | undefined): ResearchConclusion | null;
|
|
45
|
+
export declare function createIdeaResearchSource(opts: IdeaResearchSourceOptions): TaskSource;
|
|
46
|
+
//# sourceMappingURL=idea-research-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idea-research-source.d.ts","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-research-source.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAEV,WAAW,EACX,kBAAkB,EAEnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,WAAW,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,gDAAgD;IAChD,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACnE,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,mCAA6B,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAqBrF;AAUD,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,kBAAkB,GAAG,IAAI,CAItF;AAwCD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,yBAAyB,GAAG,UAAU,CAkPpF"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// M8.1 — IdeaResearchSource: consumes ideas with status="idea" and drives
|
|
2
|
+
// them through researching → todo/archived/idea via a research-only swarm
|
|
3
|
+
// cycle (no code shipped, only a markdown report).
|
|
4
|
+
//
|
|
5
|
+
// Flow (D3 in design doc):
|
|
6
|
+
// idea.status="idea"
|
|
7
|
+
// → pick (highest priority + projectId match)
|
|
8
|
+
// → onClaim: PATCH status=researching, researchKind=investigate,
|
|
9
|
+
// linkedSwarmTaskId
|
|
10
|
+
// → swarm runs with cycleKind=research → research-only template
|
|
11
|
+
// on success: parse <projectRoot>/.clawnet/ideas/<id>/research-*.md
|
|
12
|
+
// frontmatter, append ResearchRef, transition per conclusion:
|
|
13
|
+
// promote → status=todo
|
|
14
|
+
// archive → status=archived
|
|
15
|
+
// needs-info → status=idea (await user input)
|
|
16
|
+
// on failure: revert status=idea, clear researchKind +
|
|
17
|
+
// linkedSwarmTaskId
|
|
18
|
+
//
|
|
19
|
+
// Missing/malformed report = treated as needs-info (best-effort: never throws).
|
|
20
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
21
|
+
import { join, relative } from "node:path";
|
|
22
|
+
import { encodeCwd } from "@mclawnet/shared";
|
|
23
|
+
import { sortIdeasByPriorityThenAge } from "./idea-sort.js";
|
|
24
|
+
/**
|
|
25
|
+
* Re-export under the original name for back-compat (S5 hotfix moved the
|
|
26
|
+
* implementation into `./idea-sort` to deduplicate the two source files).
|
|
27
|
+
*/
|
|
28
|
+
export const sortIdeasForResearch = sortIdeasByPriorityThenAge;
|
|
29
|
+
/**
|
|
30
|
+
* Minimal YAML-frontmatter parser. We only need the `conclusion` field for
|
|
31
|
+
* M8.1, so a 20-line scanner is plenty — pulling in `gray-matter` for one
|
|
32
|
+
* key would be overkill and adds a new transitive dep (per M8.1 constraints).
|
|
33
|
+
*
|
|
34
|
+
* Recognises:
|
|
35
|
+
* ---
|
|
36
|
+
* conclusion: promote ← keys are flat; values are unquoted single tokens
|
|
37
|
+
* key: value
|
|
38
|
+
* ---
|
|
39
|
+
* body…
|
|
40
|
+
*
|
|
41
|
+
* Returns null if no frontmatter block is present. Unknown keys are ignored.
|
|
42
|
+
*/
|
|
43
|
+
export function parseReportFrontmatter(content) {
|
|
44
|
+
// Normalise CRLF. N3 hotfix: the regex below requires `---` at column 0
|
|
45
|
+
// (no leading whitespace tolerated) — the previous comment claimed
|
|
46
|
+
// optional leading whitespace which was misleading. We deliberately keep
|
|
47
|
+
// the strict shape to match the standard YAML frontmatter convention.
|
|
48
|
+
const normalised = content.replace(/\r\n/g, "\n");
|
|
49
|
+
const m = normalised.match(/^---\n([\s\S]*?)\n---/);
|
|
50
|
+
if (!m)
|
|
51
|
+
return null;
|
|
52
|
+
const out = {};
|
|
53
|
+
for (const line of m[1].split("\n")) {
|
|
54
|
+
const kv = line.match(/^(\w+):\s*(.*)$/);
|
|
55
|
+
if (!kv)
|
|
56
|
+
continue;
|
|
57
|
+
const key = kv[1].trim();
|
|
58
|
+
let val = kv[2].trim();
|
|
59
|
+
// Strip wrapping quotes if any.
|
|
60
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
61
|
+
val = val.slice(1, -1);
|
|
62
|
+
}
|
|
63
|
+
out[key] = val;
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
const CONCLUSION_ALIASES = {
|
|
68
|
+
promote: "promote",
|
|
69
|
+
"needs-info": "needs-info",
|
|
70
|
+
needsinfo: "needs-info",
|
|
71
|
+
needinfo: "needs-info",
|
|
72
|
+
archive: "archive",
|
|
73
|
+
};
|
|
74
|
+
/** Tolerant casing + dash/space variants. Unknown → null (caller falls back). */
|
|
75
|
+
export function normaliseConclusion(raw) {
|
|
76
|
+
if (!raw)
|
|
77
|
+
return null;
|
|
78
|
+
const key = raw.trim().toLowerCase().replace(/\s+/g, "-");
|
|
79
|
+
return CONCLUSION_ALIASES[key] ?? null;
|
|
80
|
+
}
|
|
81
|
+
function conclusionToStatus(c) {
|
|
82
|
+
switch (c) {
|
|
83
|
+
case "promote":
|
|
84
|
+
return "todo";
|
|
85
|
+
case "archive":
|
|
86
|
+
return "archived";
|
|
87
|
+
case "needs-info":
|
|
88
|
+
return "idea";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Scan a report directory for `research-*.md` files and return the path of
|
|
93
|
+
* the newest by mtime. Returns null when the dir is missing/empty.
|
|
94
|
+
*/
|
|
95
|
+
function findNewestReport(reportDir) {
|
|
96
|
+
if (!existsSync(reportDir))
|
|
97
|
+
return null;
|
|
98
|
+
let best = null;
|
|
99
|
+
try {
|
|
100
|
+
for (const name of readdirSync(reportDir)) {
|
|
101
|
+
if (!name.startsWith("research-") || !name.endsWith(".md"))
|
|
102
|
+
continue;
|
|
103
|
+
const full = join(reportDir, name);
|
|
104
|
+
try {
|
|
105
|
+
const s = statSync(full);
|
|
106
|
+
if (!s.isFile())
|
|
107
|
+
continue;
|
|
108
|
+
if (!best || s.mtimeMs > best.mtimeMs) {
|
|
109
|
+
best = { path: full, mtimeMs: s.mtimeMs };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// ignore individual stat errors
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return best?.path ?? null;
|
|
121
|
+
}
|
|
122
|
+
export function createIdeaResearchSource(opts) {
|
|
123
|
+
const { ideasClient, listLimit = 50, warn, info, resolveReportDir = (projectRoot, ideaId) => join(projectRoot, ".clawnet", "ideas", ideaId), now = () => new Date(), } = opts;
|
|
124
|
+
return {
|
|
125
|
+
name: "idea-research",
|
|
126
|
+
kind: "idea-research",
|
|
127
|
+
async pick(projectRoot) {
|
|
128
|
+
let projectId;
|
|
129
|
+
try {
|
|
130
|
+
projectId = encodeCwd(projectRoot);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
warn?.({ err: err instanceof Error ? err.message : String(err), projectRoot }, "idea-research-source: encodeCwd failed");
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
let ideas;
|
|
137
|
+
try {
|
|
138
|
+
ideas = await ideasClient.list({
|
|
139
|
+
status: "idea",
|
|
140
|
+
projectId,
|
|
141
|
+
limit: listLimit,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
warn?.({ err: err instanceof Error ? err.message : String(err), projectId }, "idea-research-source: list failed");
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const matching = ideas.filter((i) => i.status === "idea" && i.projectId === projectId);
|
|
149
|
+
if (matching.length === 0)
|
|
150
|
+
return null;
|
|
151
|
+
const sorted = sortIdeasByPriorityThenAge(matching);
|
|
152
|
+
const head = sorted[0];
|
|
153
|
+
const description = head.body && head.body.trim().length > 0 ? head.body : undefined;
|
|
154
|
+
const taskRef = {
|
|
155
|
+
id: head.id,
|
|
156
|
+
title: head.title,
|
|
157
|
+
...(description ? { description } : {}),
|
|
158
|
+
sourceKind: "idea-research",
|
|
159
|
+
sourceId: head.id,
|
|
160
|
+
// Stamp projectRoot + cycleKind so the runner picks the right template
|
|
161
|
+
// and onSuccess can locate the report file post-cycle.
|
|
162
|
+
projectRoot,
|
|
163
|
+
cycleKind: "research",
|
|
164
|
+
};
|
|
165
|
+
info?.({ ideaId: head.id, priority: head.priority, projectId }, "idea-research-source: picked");
|
|
166
|
+
return taskRef;
|
|
167
|
+
},
|
|
168
|
+
async onClaim(taskRef, swarmId) {
|
|
169
|
+
const ideaId = taskRef.sourceId ?? taskRef.id;
|
|
170
|
+
try {
|
|
171
|
+
// S3 hotfix note (M8 holistic review): `linkedSwarmTaskId` is a
|
|
172
|
+
// legacy column name — it stores a swarmId, not a taskId. The DB
|
|
173
|
+
// column shipped in migration 0014; renaming requires a destructive
|
|
174
|
+
// migration so we keep the name and document the mismatch here.
|
|
175
|
+
// S9 note: races between two cascade cycles (e.g. simultaneous
|
|
176
|
+
// research + execute claim attempts) can both flip an idea — M8
|
|
177
|
+
// assumes single-agent installs; M9+ may add optimistic locking.
|
|
178
|
+
await ideasClient.patch(ideaId, {
|
|
179
|
+
status: "researching",
|
|
180
|
+
researchKind: "investigate",
|
|
181
|
+
linkedSwarmTaskId: swarmId,
|
|
182
|
+
});
|
|
183
|
+
info?.({ ideaId, swarmId }, "idea-research-source: claimed → researching (investigate)");
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId }, "idea-research-source: onClaim patch failed");
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
async onSuccess(taskRef, swarmId) {
|
|
190
|
+
const ideaId = taskRef.sourceId ?? taskRef.id;
|
|
191
|
+
const projectRoot = taskRef.projectRoot;
|
|
192
|
+
// Default conclusion if anything goes wrong below — "needs-info" keeps
|
|
193
|
+
// the idea in front of the user without false-positive promote/archive.
|
|
194
|
+
let conclusion = "needs-info";
|
|
195
|
+
let reportPath = null;
|
|
196
|
+
if (!projectRoot) {
|
|
197
|
+
warn?.({ ideaId, swarmId }, "idea-research-source: onSuccess missing projectRoot — treating as needs-info");
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
try {
|
|
201
|
+
const reportDir = resolveReportDir(projectRoot, ideaId);
|
|
202
|
+
const found = findNewestReport(reportDir);
|
|
203
|
+
if (!found) {
|
|
204
|
+
warn?.({ ideaId, swarmId, reportDir }, "idea-research-source: no research report found — treating as needs-info");
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
reportPath = found;
|
|
208
|
+
try {
|
|
209
|
+
const body = readFileSync(found, "utf-8");
|
|
210
|
+
const fm = parseReportFrontmatter(body);
|
|
211
|
+
if (!fm) {
|
|
212
|
+
warn?.({ ideaId, swarmId, found }, "idea-research-source: report missing frontmatter — needs-info");
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
const parsed = normaliseConclusion(fm.conclusion);
|
|
216
|
+
if (!parsed) {
|
|
217
|
+
warn?.({ ideaId, swarmId, raw: fm.conclusion }, "idea-research-source: unknown conclusion — needs-info");
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
conclusion = parsed;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId, found }, "idea-research-source: failed to read report — needs-info");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId }, "idea-research-source: resolveReportDir threw — needs-info");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Build the ResearchRef, normalising path to projectRoot-relative when
|
|
234
|
+
// possible (matches the schema convention in
|
|
235
|
+
// packages/@clawnet/shared/src/schemas.ts).
|
|
236
|
+
const ranAt = now().toISOString();
|
|
237
|
+
let storedReportPath;
|
|
238
|
+
if (reportPath && projectRoot) {
|
|
239
|
+
// Hotfix I5 — use path.relative so we get a proper separator
|
|
240
|
+
// boundary. The prior `startsWith(projectRoot)` check would happily
|
|
241
|
+
// strip the wrong prefix for sibling directories sharing a name
|
|
242
|
+
// prefix (`/x/proj` vs `/x/proj-other` → `-other/...`). When the
|
|
243
|
+
// report sits outside the project tree we fall back to the absolute
|
|
244
|
+
// path so the breadcrumb still points somewhere meaningful.
|
|
245
|
+
const rel = relative(projectRoot, reportPath);
|
|
246
|
+
storedReportPath = rel.startsWith("..") ? reportPath : rel;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
storedReportPath = reportPath ?? `.clawnet/ideas/${ideaId}/research-missing.md`;
|
|
250
|
+
}
|
|
251
|
+
const ref = {
|
|
252
|
+
reportPath: storedReportPath,
|
|
253
|
+
swarmId,
|
|
254
|
+
conclusion,
|
|
255
|
+
ranAt,
|
|
256
|
+
};
|
|
257
|
+
// Read-modify-write the researchReports array so we don't clobber
|
|
258
|
+
// earlier reports. Best-effort: a hub read error degrades to a
|
|
259
|
+
// single-element array (caller can still see this run).
|
|
260
|
+
let nextReports = [ref];
|
|
261
|
+
try {
|
|
262
|
+
const current = await ideasClient.get(ideaId);
|
|
263
|
+
const existing = current?.researchReports ?? [];
|
|
264
|
+
// Newest-first ordering per design doc D2.
|
|
265
|
+
nextReports = [ref, ...existing];
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId }, "idea-research-source: failed to read existing researchReports — overwriting with single entry");
|
|
269
|
+
}
|
|
270
|
+
const nextStatus = conclusionToStatus(conclusion);
|
|
271
|
+
try {
|
|
272
|
+
await ideasClient.patch(ideaId, {
|
|
273
|
+
status: nextStatus,
|
|
274
|
+
researchKind: null,
|
|
275
|
+
researchReports: nextReports,
|
|
276
|
+
});
|
|
277
|
+
info?.({ ideaId, swarmId, conclusion, nextStatus }, "idea-research-source: research cycle complete");
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
warn?.({ err: err instanceof Error ? err.message : String(err), ideaId, swarmId }, "idea-research-source: onSuccess patch failed");
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
async onFailure(taskRef, swarmId, error) {
|
|
284
|
+
const ideaId = taskRef.sourceId ?? taskRef.id;
|
|
285
|
+
try {
|
|
286
|
+
// Revert to status=idea so the user (or a future cycle) can re-pick.
|
|
287
|
+
// Clear both researchKind and linkedSwarmTaskId — the failed cycle is
|
|
288
|
+
// no longer relevant breadcrumbs (vs IdeaTodoSource which keeps the
|
|
289
|
+
// linkedSwarmTaskId for debugging).
|
|
290
|
+
await ideasClient.patch(ideaId, {
|
|
291
|
+
status: "idea",
|
|
292
|
+
researchKind: null,
|
|
293
|
+
linkedSwarmTaskId: null,
|
|
294
|
+
});
|
|
295
|
+
info?.({ ideaId, swarmId, error: error ?? null }, "idea-research-source: reverted to idea after failure");
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
warn?.({
|
|
299
|
+
err: err instanceof Error ? err.message : String(err),
|
|
300
|
+
ideaId,
|
|
301
|
+
swarmId,
|
|
302
|
+
originalError: error,
|
|
303
|
+
}, "idea-research-source: onFailure revert failed");
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=idea-research-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idea-research-source.js","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-research-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idea-sort.d.ts","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-sort.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,IAAI,EAAgB,MAAM,oBAAoB,CAAC;AAQ7D,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAMhE"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// M8 hotfix S5 — shared sort helper used by both IdeaTodoSource (status=todo
|
|
2
|
+
// cycle) and IdeaResearchSource (status=idea research cycle). Originally
|
|
3
|
+
// duplicated as `sortIdeasForPicking` / `sortIdeasForResearch`; both used
|
|
4
|
+
// the same priority + updatedAt ordering, only the type was identical.
|
|
5
|
+
// Extracting here keeps the two sources in lockstep and removes a future
|
|
6
|
+
// drift risk.
|
|
7
|
+
//
|
|
8
|
+
// Sort order:
|
|
9
|
+
// 1. priority DESC (high > medium > low)
|
|
10
|
+
// 2. updatedAt ASC (oldest first — avoids starving early-written ideas
|
|
11
|
+
// when fresh edits keep landing on newer ones).
|
|
12
|
+
const PRIORITY_RANK = {
|
|
13
|
+
high: 3,
|
|
14
|
+
medium: 2,
|
|
15
|
+
low: 1,
|
|
16
|
+
};
|
|
17
|
+
export function sortIdeasByPriorityThenAge(ideas) {
|
|
18
|
+
return [...ideas].sort((a, b) => {
|
|
19
|
+
const pdiff = (PRIORITY_RANK[b.priority] ?? 0) - (PRIORITY_RANK[a.priority] ?? 0);
|
|
20
|
+
if (pdiff !== 0)
|
|
21
|
+
return pdiff;
|
|
22
|
+
return a.updatedAt.localeCompare(b.updatedAt);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=idea-sort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idea-sort.js","sourceRoot":"","sources":["../../../src/always-on/task-sources/idea-sort.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,yEAAyE;AACzE,0EAA0E;AAC1E,uEAAuE;AACvE,yEAAyE;AACzE,cAAc;AACd,EAAE;AACF,cAAc;AACd,2CAA2C;AAC3C,yEAAyE;AACzE,qDAAqD;AAIrD,MAAM,aAAa,GAAiC;IAClD,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;CACP,CAAC;AAEF,MAAM,UAAU,0BAA0B,CAAC,KAAa;IACtD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,KAAK,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAClF,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC9B,OAAO,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC"}
|