aidimag 1.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/LICENSE +102 -0
- package/README.md +113 -0
- package/dist/capture/bootstrap.d.ts +25 -0
- package/dist/capture/bootstrap.js +188 -0
- package/dist/capture/commit-miner.d.ts +59 -0
- package/dist/capture/commit-miner.js +381 -0
- package/dist/capture/harvest.d.ts +50 -0
- package/dist/capture/harvest.js +207 -0
- package/dist/capture/pr-miner.d.ts +57 -0
- package/dist/capture/pr-miner.js +185 -0
- package/dist/capture/session-briefing.d.ts +36 -0
- package/dist/capture/session-briefing.js +150 -0
- package/dist/capture/session-extraction.d.ts +23 -0
- package/dist/capture/session-extraction.js +54 -0
- package/dist/capture/triage.d.ts +30 -0
- package/dist/capture/triage.js +108 -0
- package/dist/cli/commands/capture.d.ts +5 -0
- package/dist/cli/commands/capture.js +357 -0
- package/dist/cli/commands/hosts.d.ts +5 -0
- package/dist/cli/commands/hosts.js +98 -0
- package/dist/cli/commands/knowledge.d.ts +5 -0
- package/dist/cli/commands/knowledge.js +121 -0
- package/dist/cli/commands/memory.d.ts +6 -0
- package/dist/cli/commands/memory.js +392 -0
- package/dist/cli/commands/sync.d.ts +5 -0
- package/dist/cli/commands/sync.js +307 -0
- package/dist/cli/commands/tickets.d.ts +6 -0
- package/dist/cli/commands/tickets.js +328 -0
- package/dist/cli/commands/verify.d.ts +5 -0
- package/dist/cli/commands/verify.js +136 -0
- package/dist/cli/index.d.ts +19 -0
- package/dist/cli/index.js +46 -0
- package/dist/cli/shared.d.ts +37 -0
- package/dist/cli/shared.js +175 -0
- package/dist/config.d.ts +55 -0
- package/dist/config.js +51 -0
- package/dist/context/generate.d.ts +24 -0
- package/dist/context/generate.js +115 -0
- package/dist/critique/critique.d.ts +40 -0
- package/dist/critique/critique.js +110 -0
- package/dist/db/schema.d.ts +28 -0
- package/dist/db/schema.js +269 -0
- package/dist/db/store.d.ts +182 -0
- package/dist/db/store.js +906 -0
- package/dist/debug.d.ts +14 -0
- package/dist/debug.js +25 -0
- package/dist/embeddings/provider.d.ts +16 -0
- package/dist/embeddings/provider.js +100 -0
- package/dist/embeddings/search.d.ts +22 -0
- package/dist/embeddings/search.js +95 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +12 -0
- package/dist/knowledge/chunk.d.ts +9 -0
- package/dist/knowledge/chunk.js +78 -0
- package/dist/knowledge/extract.d.ts +34 -0
- package/dist/knowledge/extract.js +113 -0
- package/dist/knowledge/ingest.d.ts +79 -0
- package/dist/knowledge/ingest.js +305 -0
- package/dist/knowledge/llm.d.ts +21 -0
- package/dist/knowledge/llm.js +110 -0
- package/dist/mcp/server.d.ts +11 -0
- package/dist/mcp/server.js +532 -0
- package/dist/security/evidence.d.ts +11 -0
- package/dist/security/evidence.js +15 -0
- package/dist/security/seal.d.ts +3 -0
- package/dist/security/seal.js +28 -0
- package/dist/security/sync-push.d.ts +2 -0
- package/dist/security/sync-push.js +37 -0
- package/dist/security/url.d.ts +6 -0
- package/dist/security/url.js +67 -0
- package/dist/sync/client.d.ts +84 -0
- package/dist/sync/client.js +391 -0
- package/dist/sync/server.d.ts +75 -0
- package/dist/sync/server.js +659 -0
- package/dist/tickets/provider.d.ts +80 -0
- package/dist/tickets/provider.js +375 -0
- package/dist/types.d.ts +133 -0
- package/dist/types.js +5 -0
- package/dist/ui/page.d.ts +5 -0
- package/dist/ui/page.js +841 -0
- package/dist/ui/server.d.ts +10 -0
- package/dist/ui/server.js +437 -0
- package/dist/verify/check.d.ts +38 -0
- package/dist/verify/check.js +121 -0
- package/dist/verify/engine.d.ts +39 -0
- package/dist/verify/engine.js +178 -0
- package/dist/verify/hooks.d.ts +24 -0
- package/dist/verify/hooks.js +125 -0
- package/dist/verify/runners.d.ts +26 -0
- package/dist/verify/runners.js +193 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Elastic License 2.0
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Anup Khanal
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
8
|
+
|
|
9
|
+
## Copyright License
|
|
10
|
+
|
|
11
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable,
|
|
12
|
+
non-transferable license to use, copy, distribute, make available, and prepare
|
|
13
|
+
derivative works of the software, in each case subject to the limitations and
|
|
14
|
+
conditions below.
|
|
15
|
+
|
|
16
|
+
## Limitations
|
|
17
|
+
|
|
18
|
+
You may not provide the software to third parties as a hosted or managed service,
|
|
19
|
+
where the service provides users with access to any substantial set of the features
|
|
20
|
+
or functionality of the software.
|
|
21
|
+
|
|
22
|
+
You may not move, change, disable, or circumvent the license key functionality in
|
|
23
|
+
the software, and you may not remove or obscure any functionality in the software
|
|
24
|
+
that is protected by the license key.
|
|
25
|
+
|
|
26
|
+
You may not alter, remove, or obscure any licensing, copyright, or other notices
|
|
27
|
+
of the licensor in the software. Any use of the licensor's trademarks is subject
|
|
28
|
+
to applicable law.
|
|
29
|
+
|
|
30
|
+
## Additional Use Grant: Small Team License
|
|
31
|
+
|
|
32
|
+
**Free for organizations with 10 or fewer total employees/users.**
|
|
33
|
+
|
|
34
|
+
If your organization has more than 10 employees or users (including all affiliates
|
|
35
|
+
and entities under common control), you must obtain a commercial license from the
|
|
36
|
+
licensor. Contact: https://github.com/anupkhanal/aidimag/issues (commercial licensing inquiries)
|
|
37
|
+
|
|
38
|
+
For determining the user count, "users" includes anyone who:
|
|
39
|
+
- Runs commands using the software
|
|
40
|
+
- Uses AI agents connected to the software
|
|
41
|
+
- Accesses dashboards or IDE extensions provided by the software
|
|
42
|
+
|
|
43
|
+
## Patents
|
|
44
|
+
|
|
45
|
+
The licensor grants you a license, under any patent claims the licensor can license,
|
|
46
|
+
or becomes able to license, to make, have made, use, sell, offer for sale, import
|
|
47
|
+
and have imported the software, in each case subject to the limitations and conditions
|
|
48
|
+
in this license. This license does not cover any patent claims that you cause to be
|
|
49
|
+
infringed by modifications or additions to the software. If you or your company make
|
|
50
|
+
any written claim that the software infringes or contributes to infringement of any
|
|
51
|
+
patent, your patent license for the software granted under these terms ends immediately.
|
|
52
|
+
If your company makes such a claim, your patent license ends immediately for work on
|
|
53
|
+
behalf of your company.
|
|
54
|
+
|
|
55
|
+
## Notices
|
|
56
|
+
|
|
57
|
+
You must ensure that anyone who gets a copy of any part of the software from you also
|
|
58
|
+
gets a copy of these terms.
|
|
59
|
+
|
|
60
|
+
If you modify the software, you must include in any modified copies of the software
|
|
61
|
+
prominent notices stating that you have modified the software.
|
|
62
|
+
|
|
63
|
+
## No Other Rights
|
|
64
|
+
|
|
65
|
+
These terms do not imply any licenses other than those expressly granted in these terms.
|
|
66
|
+
|
|
67
|
+
## Termination
|
|
68
|
+
|
|
69
|
+
If you use the software in violation of these terms, such use is not licensed, and
|
|
70
|
+
your licenses will automatically terminate. If the licensor provides you with a notice
|
|
71
|
+
of your violation, and you cease all violation of this license no later than 30 days
|
|
72
|
+
after you receive that notice, your licenses will be reinstated retroactively. However,
|
|
73
|
+
if you violate these terms after such reinstatement, any additional violation of these
|
|
74
|
+
terms will cause your licenses to terminate automatically and permanently.
|
|
75
|
+
|
|
76
|
+
## No Liability
|
|
77
|
+
|
|
78
|
+
**As far as the law allows, the software comes as is, without any warranty or condition,
|
|
79
|
+
and the licensor will not be liable to you for any damages arising out of these terms
|
|
80
|
+
or the use or nature of the software, under any kind of legal claim.**
|
|
81
|
+
|
|
82
|
+
## Definitions
|
|
83
|
+
|
|
84
|
+
The **licensor** is Anup Khanal.
|
|
85
|
+
|
|
86
|
+
The **software** is the software the licensor makes available under these terms,
|
|
87
|
+
including any portion of it.
|
|
88
|
+
|
|
89
|
+
**You** refers to the individual or entity agreeing to these terms.
|
|
90
|
+
|
|
91
|
+
**Your company** is any legal entity, sole proprietorship, or other kind of organization
|
|
92
|
+
that you work for, plus all organizations that have control over, are under the control
|
|
93
|
+
of, or are under common control with that organization. **Control** means ownership of
|
|
94
|
+
substantially all the assets of an entity, or the power to direct its management and
|
|
95
|
+
policies by vote, contract, or otherwise. Control can be direct or indirect.
|
|
96
|
+
|
|
97
|
+
**Your licenses** are all the licenses granted to you for the software under these terms.
|
|
98
|
+
|
|
99
|
+
**Use** means anything you do with the software requiring one of your licenses.
|
|
100
|
+
|
|
101
|
+
**Trademark** means trademarks, service marks, and similar rights.
|
|
102
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# aiDimag
|
|
2
|
+
|
|
3
|
+
> Persistent, verified memory for AI coding agents. CLI: **`dim`**.
|
|
4
|
+
|
|
5
|
+
aiDimag gives any MCP-compatible agent (Claude Code, Cursor, Copilot, …) a memory of your
|
|
6
|
+
codebase that survives across sessions — decisions, conventions, gotchas, failed approaches,
|
|
7
|
+
**guardrails**, and reusable **skills** — stored as **falsifiable claims with grounding
|
|
8
|
+
evidence** in `.aidimag/` next to your code. It also generates the static context files
|
|
9
|
+
(`CLAUDE.md`, `.cursorrules`, …) that *non-MCP* tools read, so every AI tool benefits.
|
|
10
|
+
|
|
11
|
+
What makes it different: **memories are verified, not just stored.** Every memory carries
|
|
12
|
+
evidence (a shell check, an anchored commit, a test) that `dim verify` re-runs against the
|
|
13
|
+
current repo — beliefs that stop being true go **STALE** instead of silently misleading
|
|
14
|
+
your AI.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install -g aidimag
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires Node 18+. Ships two equivalent binaries: `dim` (short) and `aidimag`.
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
cd your-repo
|
|
28
|
+
dim init # creates .aidimag/, installs additive git hooks
|
|
29
|
+
dim bootstrap # optional: LLM-survey the repo into a starter memory set
|
|
30
|
+
dim review # approve what enters memory (nothing is stored unreviewed)
|
|
31
|
+
|
|
32
|
+
dim remember "All DB access goes through src/db/store.ts" -k INVARIANT -p src/db \
|
|
33
|
+
-e "STATIC_CHECK:grep -rL better-sqlite3 src --include=*.ts"
|
|
34
|
+
dim recall db access
|
|
35
|
+
dim verify # re-run all evidence; stale beliefs get flagged
|
|
36
|
+
dim brief # session-start briefing: in-scope memory, guardrails, gaps
|
|
37
|
+
|
|
38
|
+
# For non-MCP tools (Copilot, Cursor without MCP, etc.):
|
|
39
|
+
dim generate-context --format all --auto # creates .cursorrules, CLAUDE.md, copilot-instructions.md
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Hook it up to your AI agent (MCP)
|
|
43
|
+
|
|
44
|
+
Add to your agent config (e.g. `.mcp.json` for Claude Code):
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"aidimag": {
|
|
50
|
+
"command": "npx",
|
|
51
|
+
"args": ["-y", "aidimag", "mcp"],
|
|
52
|
+
"env": { "AIDIMAG_REPO": "/path/to/your/repo" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Agents get `memory_search`, `memory_propose`, `context_note` (live in-chat fact capture),
|
|
59
|
+
`memory_critique` (a second critic grounded in verified memory), session-start briefings,
|
|
60
|
+
session-end extraction, and more. For non-MCP tools, `dim generate-context -f all` renders
|
|
61
|
+
verified memory into `CLAUDE.md` / `.cursorrules` / `.github/copilot-instructions.md`
|
|
62
|
+
(`--auto` keeps them refreshed).
|
|
63
|
+
|
|
64
|
+
## Highlights
|
|
65
|
+
|
|
66
|
+
- **Human-gated capture** — commits, PRs, AI-chat transcripts, and pasted docs are mined
|
|
67
|
+
into *proposals*; nothing enters memory until you approve it in `dim review`
|
|
68
|
+
(auto-triaged best-first, `approve all --min-score 0.7` for batches).
|
|
69
|
+
- **Verification lifecycle** — `STATIC_CHECK` / `COMMIT_REF` / `TEST_RESULT` /
|
|
70
|
+
`EXEC_TRACE` / `HUMAN_ATTESTED` evidence; failing evidence flips memories to STALE and
|
|
71
|
+
auto-drafts a recovery proposal. Confidence decays without re-confirmation.
|
|
72
|
+
- **Evidence trust gate** — shell-command evidence that arrives via team sync is **never
|
|
73
|
+
executed** until you inspect and approve it (`dim verify --trust`).
|
|
74
|
+
- **Hybrid semantic recall** — FTS5 keyword + vector KNN (OpenAI or local Ollama,
|
|
75
|
+
auto-detected; works keyword-only with neither).
|
|
76
|
+
- **Guardrails & skills** — behavioral rules (`never` / `ask-first` / `always`) and
|
|
77
|
+
step-by-step procedures, enforced by `dim check` (pre-commit) and `memory_critique`.
|
|
78
|
+
- **Team mode, self-hosted** — `dim serve` + `dim sync`: local-first replicas, device-code
|
|
79
|
+
login, brain-scoped API keys, hashed credentials, cross-machine verification consensus.
|
|
80
|
+
- **Knowledgebase inbox** — drop design docs / ADRs / PDFs / DOCX into `knowledge/` and
|
|
81
|
+
they're summarized into reviewed, pinned memories.
|
|
82
|
+
- **Web dashboard** (`dim ui`) plus [VS Code](https://github.com/anupkhanal/aidimag/tree/main/vscode-extension) and
|
|
83
|
+
[IntelliJ](https://github.com/anupkhanal/aidimag/tree/main/intellij-plugin) extensions.
|
|
84
|
+
|
|
85
|
+
## Documentation
|
|
86
|
+
|
|
87
|
+
Full documentation available at: **[github.com/anupkhanal/aidimag](https://github.com/anupkhanal/aidimag)**
|
|
88
|
+
|
|
89
|
+
- [Getting Started](https://github.com/anupkhanal/aidimag/blob/main/docs/getting-started.md)
|
|
90
|
+
- [Quick Start](https://github.com/anupkhanal/aidimag/blob/main/docs/quickstart.md)
|
|
91
|
+
- [CLI Reference](https://github.com/anupkhanal/aidimag/blob/main/docs/cli-reference.md)
|
|
92
|
+
- [MCP Integration](https://github.com/anupkhanal/aidimag/blob/main/docs/mcp.md)
|
|
93
|
+
- [Team Sync Guide](https://github.com/anupkhanal/aidimag/blob/main/docs/guides/team-sync.md)
|
|
94
|
+
- [Configuration](https://github.com/anupkhanal/aidimag/blob/main/docs/configuration.md)
|
|
95
|
+
|
|
96
|
+
## Development
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
npm install
|
|
100
|
+
npm run build # tsc → dist/
|
|
101
|
+
npm test # node --test (builds first)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Self-hosted sync deployment (Docker / Fly.io): [deploy/](https://github.com/anupkhanal/aidimag/tree/main/deploy).
|
|
105
|
+
|
|
106
|
+
## Author
|
|
107
|
+
|
|
108
|
+
**Anup Khanal**
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
[Elastic License 2.0](./LICENSE) — free for teams of 10 or fewer users. Commercial license required for larger organizations. Cannot be offered as a managed service to third parties. See [pricing & licensing](docs/pricing.md) for details.
|
|
113
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dim bootstrap` — instant brain on day one (kills the cold-start problem).
|
|
3
|
+
*
|
|
4
|
+
* Surveys the repo (README/docs/ADRs, manifests, directory shape, git churn)
|
|
5
|
+
* and LLM-extracts an initial set of falsifiable memory candidates, each with
|
|
6
|
+
* a suggested STATIC_CHECK where one makes sense. Everything lands in the
|
|
7
|
+
* proposal queue (source `bootstrap`) — `dim review` remains the trust gate.
|
|
8
|
+
*/
|
|
9
|
+
import type { MemoryStore } from "../db/store.js";
|
|
10
|
+
export interface BootstrapResult {
|
|
11
|
+
proposed: number;
|
|
12
|
+
duplicates: number;
|
|
13
|
+
provider: string | null;
|
|
14
|
+
surveyedFiles: string[];
|
|
15
|
+
alreadyBootstrapped: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Top-level directory listing, two levels deep — enough shape without noise. */
|
|
18
|
+
export declare function surveyTree(repoRoot: string): string;
|
|
19
|
+
/** Most-changed files in recent history — where the action (and the gotchas) live. */
|
|
20
|
+
export declare function surveyChurn(repoRoot: string, maxCommits?: number, top?: number): string;
|
|
21
|
+
export declare const BOOTSTRAP_INSTRUCTIONS = "You are bootstrapping a \"repo brain\" for an AI coding assistant memory system. Below is a survey of a codebase: its docs, manifests, directory shape, and most-changed files.\n\nExtract the durable, project-specific knowledge as FALSIFIABLE claims. Rules:\n\n1. Only facts THIS survey supports \u2014 architecture, conventions, decisions, invariants, build/deploy procedures (SKILL), behavioral rules (GUARDRAIL with guardrail_level never|ask-first|always). Do NOT invent or pad; skip anything generic (\"uses TypeScript\" is useless unless there's a rule attached).\n2. Write each claim as a checkable statement scoped with paths when the survey names them.\n3. Where possible, include \"static_check\": a cheap shell command (grep/test/ls) that exits 0 iff the claim holds. Omit it when no honest check exists.\n4. kinds: DECISION, CONVENTION, GOTCHA, FAILED_APPROACH, ARCHITECTURE, INVARIANT, GUARDRAIL, SKILL, TODO_CONTEXT.\n5. Extract 5\u201330 claims depending on how much real signal exists. Quality over quantity.\n\nRespond with ONLY a JSON object:\n{\"claims\":[{\"kind\":\"ARCHITECTURE\",\"claim\":\"...\",\"paths\":[\"src/x\"],\"symbols\":[],\"guardrail_level\":null,\"rationale\":\"from README section ...\",\"static_check\":\"test -f src/x/index.ts\"}]}";
|
|
22
|
+
/** Turn a repo survey into an initial proposal set. */
|
|
23
|
+
export declare function bootstrapRepo(store: MemoryStore, repoRoot: string, opts?: {
|
|
24
|
+
force?: boolean;
|
|
25
|
+
}): Promise<BootstrapResult>;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dim bootstrap` — instant brain on day one (kills the cold-start problem).
|
|
3
|
+
*
|
|
4
|
+
* Surveys the repo (README/docs/ADRs, manifests, directory shape, git churn)
|
|
5
|
+
* and LLM-extracts an initial set of falsifiable memory candidates, each with
|
|
6
|
+
* a suggested STATIC_CHECK where one makes sense. Everything lands in the
|
|
7
|
+
* proposal queue (source `bootstrap`) — `dim review` remains the trust gate.
|
|
8
|
+
*/
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
10
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { getTextProvider } from "../knowledge/llm.js";
|
|
13
|
+
import { parseClaims } from "../knowledge/extract.js";
|
|
14
|
+
const BOOTSTRAP_DONE_KEY = "bootstrap_done_at";
|
|
15
|
+
const MAX_DOC_CHARS = 12_000;
|
|
16
|
+
const MAX_TOTAL_CHARS = 48_000;
|
|
17
|
+
/** Files worth reading whole (truncated) for the survey. */
|
|
18
|
+
const DOC_CANDIDATES = [
|
|
19
|
+
"README.md", "readme.md", "ARCHITECTURE.md", "DESIGN.md", "CONTRIBUTING.md",
|
|
20
|
+
"docs/architecture.md", "docs/adr", "adr", "docs/decisions",
|
|
21
|
+
];
|
|
22
|
+
const MANIFEST_CANDIDATES = [
|
|
23
|
+
"package.json", "pyproject.toml", "go.mod", "Cargo.toml", "pom.xml",
|
|
24
|
+
"build.gradle", "build.gradle.kts", "Gemfile", "composer.json", "Makefile",
|
|
25
|
+
"docker-compose.yml", "Dockerfile", ".github/workflows",
|
|
26
|
+
];
|
|
27
|
+
function git(repoRoot, args) {
|
|
28
|
+
try {
|
|
29
|
+
return execFileSync("git", args, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function readCapped(abs) {
|
|
36
|
+
try {
|
|
37
|
+
return readFileSync(abs, "utf8").slice(0, MAX_DOC_CHARS);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Top-level directory listing, two levels deep — enough shape without noise. */
|
|
44
|
+
export function surveyTree(repoRoot) {
|
|
45
|
+
const lines = [];
|
|
46
|
+
const skip = new Set(["node_modules", ".git", "dist", "build", ".aidimag", "coverage", "target", ".idea", ".vscode"]);
|
|
47
|
+
const walk = (dir, depth, prefix) => {
|
|
48
|
+
if (depth > 2)
|
|
49
|
+
return;
|
|
50
|
+
let entries;
|
|
51
|
+
try {
|
|
52
|
+
entries = readdirSync(dir).filter((e) => !skip.has(e) && !e.startsWith("."));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
for (const e of entries.slice(0, 30)) {
|
|
58
|
+
const abs = path.join(dir, e);
|
|
59
|
+
let isDir = false;
|
|
60
|
+
try {
|
|
61
|
+
isDir = statSync(abs).isDirectory();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
lines.push(`${prefix}${e}${isDir ? "/" : ""}`);
|
|
67
|
+
if (isDir)
|
|
68
|
+
walk(abs, depth + 1, prefix + " ");
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
walk(repoRoot, 1, "");
|
|
72
|
+
return lines.join("\n");
|
|
73
|
+
}
|
|
74
|
+
/** Most-changed files in recent history — where the action (and the gotchas) live. */
|
|
75
|
+
export function surveyChurn(repoRoot, maxCommits = 300, top = 15) {
|
|
76
|
+
const raw = git(repoRoot, ["log", `--max-count=${maxCommits}`, "--name-only", "--pretty=format:"]);
|
|
77
|
+
const counts = new Map();
|
|
78
|
+
for (const f of raw.split("\n").map((l) => l.trim()).filter(Boolean)) {
|
|
79
|
+
counts.set(f, (counts.get(f) ?? 0) + 1);
|
|
80
|
+
}
|
|
81
|
+
return [...counts.entries()]
|
|
82
|
+
.sort((a, b) => b[1] - a[1])
|
|
83
|
+
.slice(0, top)
|
|
84
|
+
.map(([f, n]) => `${n}× ${f}`)
|
|
85
|
+
.join("\n");
|
|
86
|
+
}
|
|
87
|
+
function collectDocs(repoRoot) {
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const cand of DOC_CANDIDATES) {
|
|
90
|
+
const abs = path.join(repoRoot, cand);
|
|
91
|
+
if (!existsSync(abs))
|
|
92
|
+
continue;
|
|
93
|
+
if (statSync(abs).isDirectory()) {
|
|
94
|
+
for (const f of readdirSync(abs).filter((f) => f.endsWith(".md")).slice(0, 5)) {
|
|
95
|
+
out.push({ file: path.join(cand, f), content: readCapped(path.join(abs, f)) });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
out.push({ file: cand, content: readCapped(abs) });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const cand of MANIFEST_CANDIDATES) {
|
|
103
|
+
const abs = path.join(repoRoot, cand);
|
|
104
|
+
if (!existsSync(abs) || statSync(abs).isDirectory())
|
|
105
|
+
continue;
|
|
106
|
+
out.push({ file: cand, content: readCapped(abs).slice(0, 4_000) });
|
|
107
|
+
}
|
|
108
|
+
// existing hand-written AI context files are pre-distilled knowledge — gold
|
|
109
|
+
for (const cand of ["CLAUDE.md", ".cursorrules", ".github/copilot-instructions.md"]) {
|
|
110
|
+
const abs = path.join(repoRoot, cand);
|
|
111
|
+
if (existsSync(abs) && !statSync(abs).isDirectory())
|
|
112
|
+
out.push({ file: cand, content: readCapped(abs) });
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
export const BOOTSTRAP_INSTRUCTIONS = `You are bootstrapping a "repo brain" for an AI coding assistant memory system. Below is a survey of a codebase: its docs, manifests, directory shape, and most-changed files.
|
|
117
|
+
|
|
118
|
+
Extract the durable, project-specific knowledge as FALSIFIABLE claims. Rules:
|
|
119
|
+
|
|
120
|
+
1. Only facts THIS survey supports — architecture, conventions, decisions, invariants, build/deploy procedures (SKILL), behavioral rules (GUARDRAIL with guardrail_level never|ask-first|always). Do NOT invent or pad; skip anything generic ("uses TypeScript" is useless unless there's a rule attached).
|
|
121
|
+
2. Write each claim as a checkable statement scoped with paths when the survey names them.
|
|
122
|
+
3. Where possible, include "static_check": a cheap shell command (grep/test/ls) that exits 0 iff the claim holds. Omit it when no honest check exists.
|
|
123
|
+
4. kinds: DECISION, CONVENTION, GOTCHA, FAILED_APPROACH, ARCHITECTURE, INVARIANT, GUARDRAIL, SKILL, TODO_CONTEXT.
|
|
124
|
+
5. Extract 5–30 claims depending on how much real signal exists. Quality over quantity.
|
|
125
|
+
|
|
126
|
+
Respond with ONLY a JSON object:
|
|
127
|
+
{"claims":[{"kind":"ARCHITECTURE","claim":"...","paths":["src/x"],"symbols":[],"guardrail_level":null,"rationale":"from README section ...","static_check":"test -f src/x/index.ts"}]}`;
|
|
128
|
+
/** Turn a repo survey into an initial proposal set. */
|
|
129
|
+
export async function bootstrapRepo(store, repoRoot, opts = {}) {
|
|
130
|
+
const result = {
|
|
131
|
+
proposed: 0,
|
|
132
|
+
duplicates: 0,
|
|
133
|
+
provider: null,
|
|
134
|
+
surveyedFiles: [],
|
|
135
|
+
alreadyBootstrapped: false,
|
|
136
|
+
};
|
|
137
|
+
if (!opts.force && store.getMeta(BOOTSTRAP_DONE_KEY)) {
|
|
138
|
+
result.alreadyBootstrapped = true;
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
const provider = await getTextProvider();
|
|
142
|
+
if (!provider)
|
|
143
|
+
return result;
|
|
144
|
+
result.provider = `${provider.name}/${provider.model}`;
|
|
145
|
+
const docs = collectDocs(repoRoot);
|
|
146
|
+
result.surveyedFiles = docs.map((d) => d.file);
|
|
147
|
+
const sections = [];
|
|
148
|
+
sections.push(`## Directory shape\n${surveyTree(repoRoot)}`);
|
|
149
|
+
const churn = surveyChurn(repoRoot);
|
|
150
|
+
if (churn)
|
|
151
|
+
sections.push(`## Most-changed files (recent history)\n${churn}`);
|
|
152
|
+
for (const d of docs) {
|
|
153
|
+
if (sections.join("\n").length > MAX_TOTAL_CHARS)
|
|
154
|
+
break;
|
|
155
|
+
sections.push(`## File: ${d.file}\n${d.content}`);
|
|
156
|
+
}
|
|
157
|
+
let claims = [];
|
|
158
|
+
try {
|
|
159
|
+
const raw = await provider.generate(BOOTSTRAP_INSTRUCTIONS, sections.join("\n\n").slice(0, MAX_TOTAL_CHARS));
|
|
160
|
+
claims = parseClaims(raw);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return result; // provider hiccup — bootstrap can simply be re-run
|
|
164
|
+
}
|
|
165
|
+
for (const c of claims) {
|
|
166
|
+
const evidence = [];
|
|
167
|
+
if (c.staticCheck)
|
|
168
|
+
evidence.push({ type: "STATIC_CHECK", payload: c.staticCheck });
|
|
169
|
+
const p = store.propose({
|
|
170
|
+
kind: c.kind,
|
|
171
|
+
claim: c.claim,
|
|
172
|
+
paths: c.paths,
|
|
173
|
+
symbols: c.symbols,
|
|
174
|
+
guardrailLevel: c.guardrailLevel,
|
|
175
|
+
evidence: evidence.length ? evidence : undefined,
|
|
176
|
+
rationale: c.rationale ?? "Extracted by repo bootstrap survey.",
|
|
177
|
+
source: "bootstrap",
|
|
178
|
+
sourceRef: "initial-survey",
|
|
179
|
+
});
|
|
180
|
+
if (p)
|
|
181
|
+
result.proposed++;
|
|
182
|
+
else
|
|
183
|
+
result.duplicates++;
|
|
184
|
+
}
|
|
185
|
+
store.setMeta(BOOTSTRAP_DONE_KEY, new Date().toISOString());
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commit miner (Phase 2 capture pipeline).
|
|
3
|
+
*
|
|
4
|
+
* Walks git history since the last mined commit and heuristically extracts
|
|
5
|
+
* memory-worthy candidates from commit messages + touched files. Candidates
|
|
6
|
+
* land in the proposal queue (human-in-the-loop) — never directly in memory.
|
|
7
|
+
*
|
|
8
|
+
* Each proposal is anchored with COMMIT_REF evidence so Phase 3 verification
|
|
9
|
+
* can re-check it against history.
|
|
10
|
+
*/
|
|
11
|
+
import type { MemoryKind, Proposal } from "../types.js";
|
|
12
|
+
import type { MemoryStore } from "../db/store.js";
|
|
13
|
+
export interface MinedCommit {
|
|
14
|
+
sha: string;
|
|
15
|
+
subject: string;
|
|
16
|
+
body: string;
|
|
17
|
+
files: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface MineResult {
|
|
20
|
+
scanned: number;
|
|
21
|
+
proposed: Proposal[];
|
|
22
|
+
skippedDuplicates: number;
|
|
23
|
+
lastSha: string | null;
|
|
24
|
+
/** True when the repo is a git repo but has no commits yet (HEAD does not exist). */
|
|
25
|
+
noCommits?: boolean;
|
|
26
|
+
/** True when incremental mining found no commits newer than the stored cursor. */
|
|
27
|
+
noNewCommits?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** False when the repo is initialized but has no commits yet (no HEAD). */
|
|
30
|
+
export declare function hasGitCommits(repoRoot: string): boolean;
|
|
31
|
+
export declare function readCommits(repoRoot: string, sinceSha: string | null, maxCommits?: number): MinedCommit[];
|
|
32
|
+
export declare function classifyCommit(c: MinedCommit): {
|
|
33
|
+
kind: MemoryKind;
|
|
34
|
+
matched: string;
|
|
35
|
+
} | null;
|
|
36
|
+
/** Reduce touched files to a few representative scope paths (common directories). */
|
|
37
|
+
export declare function scopeFromFiles(files: string[], maxPaths?: number): string[];
|
|
38
|
+
export declare function mineCommits(store: MemoryStore, repoRoot: string, opts?: {
|
|
39
|
+
maxCommits?: number;
|
|
40
|
+
full?: boolean;
|
|
41
|
+
}): MineResult;
|
|
42
|
+
export declare const COMMIT_EXTRACT_INSTRUCTIONS = "You are mining a git commit for durable, project-specific knowledge worth remembering across AI coding sessions: decisions (and rejected alternatives), conventions, gotchas, failed approaches, invariants, architecture facts.\n\nRules:\n1. Most commits contain NOTHING durable \u2014 routine features/fixes/refactors. Return zero claims for those. Do NOT invent.\n2. When there IS signal, SYNTHESIZE a falsifiable claim about the codebase \u2014 do not parrot the commit message. Bad: \"A decision was made: use Redis\". Good: \"Rate limiting uses Redis (src/limits); the in-memory limiter was abandoned because multi-instance deploys need shared counters\".\n3. Use the DIFF, not just the message \u2014 renamed modules, deleted approaches, and added config tell the real story.\n4. kinds: DECISION, CONVENTION, GOTCHA, FAILED_APPROACH, ARCHITECTURE, INVARIANT, GUARDRAIL (guardrail_level never|ask-first|always), SKILL, TODO_CONTEXT.\n5. Scope with the touched paths; add \"static_check\" (cheap shell command, exit 0 iff true) when an honest one exists.\n6. 0\u20132 claims per commit. Zero is the common case.\n\nRespond with ONLY: {\"claims\":[{\"kind\":\"DECISION\",\"claim\":\"...\",\"paths\":[\"src/x\"],\"symbols\":[],\"guardrail_level\":null,\"rationale\":\"...\",\"static_check\":null}]}";
|
|
43
|
+
/**
|
|
44
|
+
* LLM-powered deep mining: reads message + diff, synthesizes claims with
|
|
45
|
+
* suggested STATIC_CHECKs. Requires a text provider (OpenAI/Ollama); the
|
|
46
|
+
* caller falls back to regex mining when none is available. Same cursor as
|
|
47
|
+
* regex mining — the two modes are alternatives over the same history.
|
|
48
|
+
*/
|
|
49
|
+
export declare function mineCommitsLlm(store: MemoryStore, repoRoot: string, opts?: {
|
|
50
|
+
maxCommits?: number;
|
|
51
|
+
full?: boolean;
|
|
52
|
+
}): Promise<MineResult & {
|
|
53
|
+
provider: string | null;
|
|
54
|
+
}>;
|
|
55
|
+
/** Human-readable summary of a mine run (CLI + MCP). */
|
|
56
|
+
export declare function describeMineResult(r: MineResult, opts?: {
|
|
57
|
+
llmProvider?: string | null;
|
|
58
|
+
llmRequested?: boolean;
|
|
59
|
+
}): string;
|