@davesheffer/hunch 1.8.1 → 1.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -336
- package/dist/cli/index.js +54 -37
- package/dist/cli/invocation.js +55 -2
- package/dist/constitution/experiment.js +90 -0
- package/dist/constitution/service.js +21 -4
- package/dist/store/embedder.js +1 -1
- package/dist/synthesis/provider.js +271 -51
- package/dist/synthesis/synthesize.js +4 -3
- package/dist/wiki/wiki.js +2 -2
- package/package.json +9 -2
- package/tooling/competitive-watch.mjs +108 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const repos = [
|
|
4
|
+
"davesheffer/hunch",
|
|
5
|
+
"gitmem-dev/gitmem",
|
|
6
|
+
"ismaelkedir/knowit",
|
|
7
|
+
"weigibbor/mnemo",
|
|
8
|
+
"oldskultxo/aictx",
|
|
9
|
+
"riponcm/projectmem",
|
|
10
|
+
"Cranot/roam-code",
|
|
11
|
+
"blackwell-systems/knowing",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const distinctivePhrases = [
|
|
15
|
+
"Causal Merge Verdict",
|
|
16
|
+
"corrections become enforced",
|
|
17
|
+
"content-matched constraints",
|
|
18
|
+
"deterministic Change Gate for AI-assisted codebases",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const token = process.env.GITHUB_TOKEN?.trim();
|
|
22
|
+
const headers = {
|
|
23
|
+
Accept: "application/vnd.github+json",
|
|
24
|
+
"User-Agent": "hunch-competitive-watch",
|
|
25
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
26
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
async function github(path) {
|
|
30
|
+
const response = await fetch(`https://api.github.com${path}`, { headers });
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
const detail = (await response.text()).slice(0, 300).replaceAll("\n", " ");
|
|
33
|
+
throw new Error(`GitHub ${response.status} for ${path}: ${detail}`);
|
|
34
|
+
}
|
|
35
|
+
return response.json();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function repoSnapshot(repo) {
|
|
39
|
+
const data = await github(`/repos/${repo}`);
|
|
40
|
+
return {
|
|
41
|
+
repo,
|
|
42
|
+
created: data.created_at,
|
|
43
|
+
pushed: data.pushed_at,
|
|
44
|
+
stars: data.stargazers_count,
|
|
45
|
+
forks: data.forks_count,
|
|
46
|
+
issues: data.open_issues_count,
|
|
47
|
+
url: data.html_url,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function phraseSnapshot(phrase) {
|
|
52
|
+
const query = encodeURIComponent(`\"${phrase}\"`);
|
|
53
|
+
const data = await github(`/search/code?q=${query}&per_page=100`);
|
|
54
|
+
const external = data.items
|
|
55
|
+
.filter((item) => !item.repository.full_name.startsWith("davesheffer/"))
|
|
56
|
+
.map((item) => ({
|
|
57
|
+
repo: item.repository.full_name,
|
|
58
|
+
path: item.path,
|
|
59
|
+
url: item.html_url,
|
|
60
|
+
}));
|
|
61
|
+
return { phrase, total: data.total_count, external };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function render(snapshot, phrases) {
|
|
65
|
+
const lines = [
|
|
66
|
+
`# Competitive watch — ${new Date().toISOString()}`,
|
|
67
|
+
"",
|
|
68
|
+
"## Public repository signals",
|
|
69
|
+
"",
|
|
70
|
+
"| Repository | Created | Last push | Stars | Forks | Open issues |",
|
|
71
|
+
"| --- | --- | --- | ---: | ---: | ---: |",
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (const item of snapshot) {
|
|
75
|
+
lines.push(
|
|
76
|
+
`| [${item.repo}](${item.url}) | ${item.created.slice(0, 10)} | ${item.pushed.slice(0, 10)} | ${item.stars} | ${item.forks} | ${item.issues} |`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
lines.push("", "## Distinctive phrase search", "");
|
|
81
|
+
if (!token) {
|
|
82
|
+
lines.push("Skipped: set `GITHUB_TOKEN` to enable authenticated GitHub code search.");
|
|
83
|
+
} else {
|
|
84
|
+
for (const result of phrases) {
|
|
85
|
+
lines.push(`- **${result.phrase}** — ${result.external.length} external indexed match(es)`);
|
|
86
|
+
for (const match of result.external) {
|
|
87
|
+
lines.push(` - [${match.repo} · ${match.path}](${match.url})`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
lines.push(
|
|
93
|
+
"",
|
|
94
|
+
"> Signals are leads, not copying findings. Re-check chronology and substantial similarity before drawing a conclusion.",
|
|
95
|
+
);
|
|
96
|
+
return `${lines.join("\n")}\n`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const snapshot = await Promise.all(repos.map(repoSnapshot));
|
|
101
|
+
const phrases = token
|
|
102
|
+
? await Promise.all(distinctivePhrases.map(phraseSnapshot))
|
|
103
|
+
: [];
|
|
104
|
+
process.stdout.write(render(snapshot, phrases));
|
|
105
|
+
} catch (error) {
|
|
106
|
+
process.stderr.write(`competitive-watch: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
}
|