repo-context-center 0.1.2 → 0.1.4
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 +61 -5
- package/dist/cli/commands/estimate.d.ts +2 -0
- package/dist/cli/commands/estimate.js +99 -0
- package/dist/cli/commands/init.js +6 -3
- package/dist/cli/commands/scan.d.ts +2 -0
- package/dist/cli/commands/scan.js +56 -0
- package/dist/cli/commands/suggest.d.ts +2 -0
- package/dist/cli/commands/suggest.js +65 -0
- package/dist/cli/index.js +14 -2
- package/dist/core/contextFiles.d.ts +5 -0
- package/dist/core/contextFiles.js +12 -1
- package/dist/core/contextReader.d.ts +7 -0
- package/dist/core/contextReader.js +30 -0
- package/dist/core/fileSystem.d.ts +3 -0
- package/dist/core/fileSystem.js +37 -0
- package/dist/core/scanner.d.ts +34 -0
- package/dist/core/scanner.js +214 -0
- package/dist/core/suggester.d.ts +20 -0
- package/dist/core/suggester.js +272 -0
- package/dist/core/templateInstaller.d.ts +2 -0
- package/dist/core/templateInstaller.js +18 -0
- package/dist/core/tokenEstimator.d.ts +45 -0
- package/dist/core/tokenEstimator.js +382 -0
- package/dist/core/validator.js +60 -1
- package/dist/templates/generic/AGENTS.md +47 -12
- package/dist/templates/generic/docs/ai-context/CHANGE_LOG.md +6 -9
- package/dist/templates/generic/docs/ai-context/COMMUNICATION_MODE.md +6 -10
- package/dist/templates/generic/docs/ai-context/DEPENDENCY_MAP.md +6 -10
- package/dist/templates/generic/docs/ai-context/DO_NOT_READ.md +3 -4
- package/dist/templates/generic/docs/ai-context/HOTSPOTS.md +7 -12
- package/dist/templates/generic/docs/ai-context/LESSONS_LEARNED.md +6 -13
- package/dist/templates/generic/docs/ai-context/MODULE_INDEX.md +6 -10
- package/dist/templates/generic/docs/ai-context/PROJECT_MAP.md +10 -12
- package/dist/templates/generic/docs/ai-context/RISK_REGISTER.md +7 -11
- package/dist/templates/generic/docs/ai-context/SYMBOL_MAP.md +9 -11
- package/dist/templates/generic/docs/ai-context/TASK_ROUTING.md +6 -15
- package/dist/templates/generic/docs/ai-context/TOKEN_BUDGET.md +16 -7
- package/dist/templates/github/context-check.yml +20 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,13 @@ A context layer for AI coding agents.
|
|
|
4
4
|
|
|
5
5
|
`repo-context-center` installs a small set of repository instructions and context maps into a target repo so AI coding tools can find the right context faster and avoid rereading noisy files.
|
|
6
6
|
|
|
7
|
+
Measure estimated context token savings before using the repo.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
repo-context-center estimate --compare-naive
|
|
11
|
+
repo-context-center estimate --task "fix Cypress test" --mode compact
|
|
12
|
+
```
|
|
13
|
+
|
|
7
14
|
It is not an AI agent. It is not a code analyzer. It does not understand your project automatically yet. It gives agents a durable place to store routing notes, module maps, risk notes, token guidance, and lessons learned.
|
|
8
15
|
|
|
9
16
|
It is designed to work with Codex, Claude Code, Cursor, Copilot-style agents, and other tools that read repository instructions.
|
|
@@ -22,6 +29,12 @@ This project adds a generic Repository Context Center to answer those questions
|
|
|
22
29
|
|
|
23
30
|
## Quick Start
|
|
24
31
|
|
|
32
|
+
Inside a target repository:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npx repo-context-center init
|
|
36
|
+
```
|
|
37
|
+
|
|
25
38
|
From a clone of this project:
|
|
26
39
|
|
|
27
40
|
```sh
|
|
@@ -35,6 +48,7 @@ Then, inside a target repository:
|
|
|
35
48
|
```sh
|
|
36
49
|
repo-context-center init
|
|
37
50
|
repo-context-center validate
|
|
51
|
+
repo-context-center suggest "fix auth bug"
|
|
38
52
|
```
|
|
39
53
|
|
|
40
54
|
Preview installation without writing files:
|
|
@@ -43,6 +57,12 @@ Preview installation without writing files:
|
|
|
43
57
|
repo-context-center init --dry-run
|
|
44
58
|
```
|
|
45
59
|
|
|
60
|
+
Install a PR validation workflow:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
repo-context-center init --github-action
|
|
64
|
+
```
|
|
65
|
+
|
|
46
66
|
Overwrite existing context templates:
|
|
47
67
|
|
|
48
68
|
```sh
|
|
@@ -70,13 +90,20 @@ repo-context-center init --force
|
|
|
70
90
|
|
|
71
91
|
It also creates `.repo-context-center/config.json`.
|
|
72
92
|
|
|
93
|
+
With `--github-action`, it also creates:
|
|
94
|
+
|
|
95
|
+
- `.github/workflows/repo-context-check.yml`
|
|
96
|
+
|
|
73
97
|
## Commands
|
|
74
98
|
|
|
75
99
|
```sh
|
|
76
100
|
repo-context-center --help
|
|
77
|
-
repo-context-center init [--dry-run] [--force]
|
|
101
|
+
repo-context-center init [--dry-run] [--force] [--github-action]
|
|
78
102
|
repo-context-center validate [--strict]
|
|
79
103
|
repo-context-center archive [--keep <number>] [--dry-run]
|
|
104
|
+
repo-context-center estimate [--mode compact|investigation|detailed] [--compare-naive] [--json]
|
|
105
|
+
repo-context-center scan [--json]
|
|
106
|
+
repo-context-center suggest "<task>" [--json]
|
|
80
107
|
```
|
|
81
108
|
|
|
82
109
|
- `init`: install the generic context templates.
|
|
@@ -84,6 +111,31 @@ repo-context-center archive [--keep <number>] [--dry-run]
|
|
|
84
111
|
Missing `.repo-context-center/config.json` is a warning by default and a
|
|
85
112
|
failure with `--strict`.
|
|
86
113
|
- `archive`: archive older entries from long-running context files; defaults to keeping 50 entries.
|
|
114
|
+
- `estimate`: estimate context token overhead and compare it with a naive repo scan.
|
|
115
|
+
- `scan`: inspect only the repository layout and suggest lightweight entries for context maps.
|
|
116
|
+
- `suggest`: recommend low-token context files, likely modules, likely tests, mode, and risk level for a task.
|
|
117
|
+
|
|
118
|
+
See [docs/github-action.md](docs/github-action.md) for PR validation setup.
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
repo-context-center suggest "fix payment consent bug" --json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Estimate context overhead:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
repo-context-center estimate --compare-naive
|
|
130
|
+
repo-context-center estimate --task "fix Cypress test" --mode compact
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Token estimates use `ceil(characters / 4)`. They are rough planning numbers,
|
|
134
|
+
not exact tokenizer output and not model billing estimates. The command is meant
|
|
135
|
+
to help evaluate whether the context center is reducing broad repo reads enough
|
|
136
|
+
to justify its own startup cost. In an empty repo, startup context can be zero
|
|
137
|
+
until templates are installed; after `repo-context-center init`, default context
|
|
138
|
+
files should produce a realistic non-zero startup estimate.
|
|
87
139
|
|
|
88
140
|
## Recommended AI Agent Workflow
|
|
89
141
|
|
|
@@ -91,9 +143,10 @@ Ask the agent to:
|
|
|
91
143
|
|
|
92
144
|
1. Read `AGENTS.md`.
|
|
93
145
|
2. Follow `docs/ai-context/TASK_ROUTING.md`.
|
|
94
|
-
3.
|
|
95
|
-
4.
|
|
96
|
-
5.
|
|
146
|
+
3. Check `docs/ai-context/TOKEN_BUDGET.md` and `docs/ai-context/DO_NOT_READ.md`.
|
|
147
|
+
4. Read only the on-demand context files relevant to the task.
|
|
148
|
+
5. Verify source code before changing behavior.
|
|
149
|
+
6. Update context files only when durable repo knowledge changes.
|
|
97
150
|
|
|
98
151
|
See [docs/agent-usage.md](docs/agent-usage.md).
|
|
99
152
|
|
|
@@ -126,17 +179,20 @@ More examples are in [docs/examples.md](docs/examples.md).
|
|
|
126
179
|
- [Agent usage](docs/agent-usage.md)
|
|
127
180
|
- [Token strategy](docs/token-strategy.md)
|
|
128
181
|
- [Examples](docs/examples.md)
|
|
182
|
+
- [GitHub Action](docs/github-action.md)
|
|
129
183
|
|
|
130
184
|
## Supported Project Types
|
|
131
185
|
|
|
132
186
|
The generic templates are language-agnostic. They can be installed in JavaScript, TypeScript, Python, Go, Rust, Ruby, Java, monorepos, docs repos, and mixed stacks.
|
|
133
187
|
|
|
134
|
-
Project-specific templates
|
|
188
|
+
Project-specific templates are not implemented yet. The current scanner is intentionally lightweight and does not read full source files.
|
|
135
189
|
|
|
136
190
|
## Roadmap
|
|
137
191
|
|
|
138
192
|
- Generic template installation and validation.
|
|
139
193
|
- Context file archiving.
|
|
194
|
+
- Lightweight repo layout scanning.
|
|
195
|
+
- Task-specific context suggestions.
|
|
140
196
|
- Project-specific template packs.
|
|
141
197
|
- Optional repository scanning to prefill maps.
|
|
142
198
|
- Safer update workflows for existing context centers.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.estimateCommand = estimateCommand;
|
|
4
|
+
const tokenEstimator_1 = require("../../core/tokenEstimator");
|
|
5
|
+
function parseEstimateOptions(args) {
|
|
6
|
+
let json = false;
|
|
7
|
+
let mode = "compact";
|
|
8
|
+
let task;
|
|
9
|
+
let compareNaive = false;
|
|
10
|
+
let maxFiles = 500;
|
|
11
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
12
|
+
const arg = args[index];
|
|
13
|
+
if (arg === "--json") {
|
|
14
|
+
json = true;
|
|
15
|
+
}
|
|
16
|
+
else if (arg === "--compare-naive") {
|
|
17
|
+
compareNaive = true;
|
|
18
|
+
}
|
|
19
|
+
else if (arg === "--mode") {
|
|
20
|
+
const value = args[index + 1];
|
|
21
|
+
if (value !== "compact" && value !== "investigation" && value !== "detailed") {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
mode = value;
|
|
25
|
+
index += 1;
|
|
26
|
+
}
|
|
27
|
+
else if (arg === "--task") {
|
|
28
|
+
const value = args[index + 1];
|
|
29
|
+
if (!value) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
task = value;
|
|
33
|
+
index += 1;
|
|
34
|
+
}
|
|
35
|
+
else if (arg === "--max-files") {
|
|
36
|
+
const value = Number.parseInt(args[index + 1] ?? "", 10);
|
|
37
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
maxFiles = value;
|
|
41
|
+
index += 1;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { json, mode, task, compareNaive, maxFiles };
|
|
48
|
+
}
|
|
49
|
+
function formatNumber(value) {
|
|
50
|
+
return value === undefined ? "not run" : value.toLocaleString("en-US");
|
|
51
|
+
}
|
|
52
|
+
function formatReport(report) {
|
|
53
|
+
const lines = [
|
|
54
|
+
"repo-context-center token estimate",
|
|
55
|
+
"",
|
|
56
|
+
"Method:",
|
|
57
|
+
`- Approximation: ${report.method}`,
|
|
58
|
+
"",
|
|
59
|
+
"Mode:",
|
|
60
|
+
`- ${report.mode}`,
|
|
61
|
+
"",
|
|
62
|
+
"Context cost:",
|
|
63
|
+
`- Startup context: ${formatNumber(report.startupTokens)} tokens`,
|
|
64
|
+
`- On-demand context available: ${formatNumber(report.onDemandTokens)} tokens`,
|
|
65
|
+
`- History/archive excluded: ${formatNumber(report.historyTokens)} tokens`,
|
|
66
|
+
`- Included for mode: ${formatNumber(report.includedContextTokens)} tokens`
|
|
67
|
+
];
|
|
68
|
+
if (report.naiveScanTokens !== undefined) {
|
|
69
|
+
lines.push("", "Naive comparison:", `- Estimated naive scan: ${formatNumber(report.naiveScanTokens)} tokens`, `- Estimated compact startup: ${formatNumber(report.startupTokens)} tokens`, `- Estimated saving: ${formatNumber(report.estimatedSavedTokens)} tokens`, `- Estimated saving: ${formatNumber(report.estimatedSavingPercent)}%`);
|
|
70
|
+
if (report.naiveScanCapped) {
|
|
71
|
+
lines.push(`- Naive scan capped at ${report.maxFiles} files`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (report.taskEstimate) {
|
|
75
|
+
lines.push("", "Task recommendation:", `- Task: ${report.taskEstimate.task}`, `- Recommended context: ${formatNumber(report.taskEstimate.recommendedContextTokens)} tokens`, `- Likely source: ${formatNumber(report.taskEstimate.likelySourceTokens)} tokens`, `- Likely tests: ${formatNumber(report.taskEstimate.likelyTestTokens)} tokens`, `- Likely context docs: ${formatNumber(report.taskEstimate.likelyContextTokens)} tokens`);
|
|
76
|
+
}
|
|
77
|
+
lines.push("", "Warnings:", ...report.warnings.map((warning) => `- ${warning}`));
|
|
78
|
+
return `${lines.join("\n")}\n`;
|
|
79
|
+
}
|
|
80
|
+
async function estimateCommand(io, args = []) {
|
|
81
|
+
const options = parseEstimateOptions(args);
|
|
82
|
+
if (!options) {
|
|
83
|
+
io.stderr('Usage: repo-context-center estimate [--json] [--mode compact|investigation|detailed] [--task "<task>"] [--compare-naive] [--max-files <number>]\n');
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
const report = await (0, tokenEstimator_1.estimateTokenCost)({
|
|
87
|
+
cwd: io.cwd,
|
|
88
|
+
mode: options.mode,
|
|
89
|
+
task: options.task,
|
|
90
|
+
compareNaive: options.compareNaive,
|
|
91
|
+
maxFiles: options.maxFiles
|
|
92
|
+
});
|
|
93
|
+
if (options.json) {
|
|
94
|
+
io.stdout(`${JSON.stringify(report, null, 2)}\n`);
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
io.stdout(formatReport(report));
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
@@ -6,7 +6,8 @@ const templateInstaller_1 = require("../../core/templateInstaller");
|
|
|
6
6
|
function parseInitOptions(args) {
|
|
7
7
|
return {
|
|
8
8
|
force: args.includes("--force"),
|
|
9
|
-
dryRun: args.includes("--dry-run")
|
|
9
|
+
dryRun: args.includes("--dry-run"),
|
|
10
|
+
githubAction: args.includes("--github-action")
|
|
10
11
|
};
|
|
11
12
|
}
|
|
12
13
|
function formatInstallMessage(result, dryRun) {
|
|
@@ -24,7 +25,8 @@ function formatInstallMessage(result, dryRun) {
|
|
|
24
25
|
}
|
|
25
26
|
async function initCommand(io, args = []) {
|
|
26
27
|
const options = parseInitOptions(args);
|
|
27
|
-
const
|
|
28
|
+
const knownFlags = new Set(["--force", "--dry-run", "--github-action"]);
|
|
29
|
+
const unknownFlag = args.find((arg) => arg.startsWith("--") && !knownFlags.has(arg));
|
|
28
30
|
if (unknownFlag) {
|
|
29
31
|
io.stderr(`Unknown init option: ${unknownFlag}\n`);
|
|
30
32
|
return 1;
|
|
@@ -32,7 +34,8 @@ async function initCommand(io, args = []) {
|
|
|
32
34
|
const results = await (0, templateInstaller_1.installGenericTemplates)({
|
|
33
35
|
cwd: io.cwd,
|
|
34
36
|
force: options.force,
|
|
35
|
-
dryRun: options.dryRun
|
|
37
|
+
dryRun: options.dryRun,
|
|
38
|
+
githubAction: options.githubAction
|
|
36
39
|
});
|
|
37
40
|
for (const result of results) {
|
|
38
41
|
io.stdout(formatInstallMessage(result, options.dryRun));
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scanCommand = scanCommand;
|
|
4
|
+
const scanner_1 = require("../../core/scanner");
|
|
5
|
+
function parseScanOptions(args) {
|
|
6
|
+
const unknownFlag = args.find((arg) => arg !== "--json");
|
|
7
|
+
if (unknownFlag) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
json: args.includes("--json")
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function formatList(values) {
|
|
15
|
+
return values.length > 0 ? values.join(", ") : "none";
|
|
16
|
+
}
|
|
17
|
+
function formatScanReport(report) {
|
|
18
|
+
const lines = [
|
|
19
|
+
"repo-context-center scan report",
|
|
20
|
+
"",
|
|
21
|
+
`Source folders: ${formatList(report.detected.sourceFolders)}`,
|
|
22
|
+
`Test folders: ${formatList(report.detected.testFolders)}`,
|
|
23
|
+
`Generated folders: ${formatList(report.detected.generatedFolders)}`,
|
|
24
|
+
""
|
|
25
|
+
];
|
|
26
|
+
if (report.detected.modules.length > 0) {
|
|
27
|
+
lines.push("Detected modules:");
|
|
28
|
+
for (const module of report.detected.modules) {
|
|
29
|
+
lines.push(` - ${module.path}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
lines.push("Detected modules: none");
|
|
34
|
+
}
|
|
35
|
+
lines.push("");
|
|
36
|
+
for (const [file, suggestions] of Object.entries(report.suggestions)) {
|
|
37
|
+
lines.push(file);
|
|
38
|
+
lines.push(...suggestions.map((line) => (line ? ` ${line}` : "")));
|
|
39
|
+
lines.push("");
|
|
40
|
+
}
|
|
41
|
+
return `${lines.join("\n").trimEnd()}\n`;
|
|
42
|
+
}
|
|
43
|
+
async function scanCommand(io, args = []) {
|
|
44
|
+
const options = parseScanOptions(args);
|
|
45
|
+
if (!options) {
|
|
46
|
+
io.stderr("Unknown scan option. Supported options: --json\n");
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
const report = await (0, scanner_1.scanRepository)(io.cwd);
|
|
50
|
+
if (options.json) {
|
|
51
|
+
io.stdout(`${JSON.stringify(report, null, 2)}\n`);
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
io.stdout(formatScanReport(report));
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.suggestCommand = suggestCommand;
|
|
4
|
+
const suggester_1 = require("../../core/suggester");
|
|
5
|
+
function parseSuggestOptions(args) {
|
|
6
|
+
const json = args.includes("--json");
|
|
7
|
+
const symbols = args.includes("--symbols");
|
|
8
|
+
const taskParts = args.filter((arg) => arg !== "--json" && arg !== "--symbols");
|
|
9
|
+
if (taskParts.some((arg) => arg.startsWith("--"))) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const task = taskParts.join(" ").trim();
|
|
13
|
+
if (!task) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
return { json, symbols, task };
|
|
17
|
+
}
|
|
18
|
+
function formatList(values) {
|
|
19
|
+
return values.length > 0 ? values.map((value) => ` - ${value}`).join("\n") : " - none";
|
|
20
|
+
}
|
|
21
|
+
function formatSymbols(suggestion) {
|
|
22
|
+
if (suggestion.relevantSymbols.length === 0) {
|
|
23
|
+
return " - none";
|
|
24
|
+
}
|
|
25
|
+
return suggestion.relevantSymbols
|
|
26
|
+
.map((entry) => [
|
|
27
|
+
` - ${entry.file}`,
|
|
28
|
+
...entry.symbols.map((symbol) => ` - ${symbol}`)
|
|
29
|
+
].join("\n"))
|
|
30
|
+
.join("\n");
|
|
31
|
+
}
|
|
32
|
+
function formatSuggestion(suggestion, includeSymbols) {
|
|
33
|
+
const lines = [
|
|
34
|
+
"repo-context-center suggestion",
|
|
35
|
+
"",
|
|
36
|
+
`Task: ${suggestion.task}`,
|
|
37
|
+
`Mode: ${suggestion.mode}`,
|
|
38
|
+
`Risk: ${suggestion.riskLevel}`,
|
|
39
|
+
"",
|
|
40
|
+
"Context files:",
|
|
41
|
+
formatList(suggestion.contextFiles),
|
|
42
|
+
"",
|
|
43
|
+
"Likely source files:",
|
|
44
|
+
formatList(suggestion.likelySourceFiles),
|
|
45
|
+
];
|
|
46
|
+
if (includeSymbols) {
|
|
47
|
+
lines.push("", "Relevant symbols:", formatSymbols(suggestion));
|
|
48
|
+
}
|
|
49
|
+
lines.push("", "Likely tests:", formatList(suggestion.likelyTests), "", "Reasons:", formatList(suggestion.reasons));
|
|
50
|
+
return `${lines.join("\n")}\n`;
|
|
51
|
+
}
|
|
52
|
+
async function suggestCommand(io, args = []) {
|
|
53
|
+
const options = parseSuggestOptions(args);
|
|
54
|
+
if (!options) {
|
|
55
|
+
io.stderr('Usage: repo-context-center suggest "<task>" [--json] [--symbols]\n');
|
|
56
|
+
return 1;
|
|
57
|
+
}
|
|
58
|
+
const suggestion = await (0, suggester_1.suggestContext)(io.cwd, options.task);
|
|
59
|
+
if (options.json) {
|
|
60
|
+
io.stdout(`${JSON.stringify(suggestion, null, 2)}\n`);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
io.stdout(formatSuggestion(suggestion, options.symbols));
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -4,12 +4,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.getHelpText = getHelpText;
|
|
5
5
|
exports.run = run;
|
|
6
6
|
const archive_1 = require("./commands/archive");
|
|
7
|
+
const estimate_1 = require("./commands/estimate");
|
|
7
8
|
const init_1 = require("./commands/init");
|
|
9
|
+
const scan_1 = require("./commands/scan");
|
|
10
|
+
const suggest_1 = require("./commands/suggest");
|
|
8
11
|
const validate_1 = require("./commands/validate");
|
|
9
12
|
const commands = {
|
|
10
13
|
init: init_1.initCommand,
|
|
11
14
|
validate: validate_1.validateCommand,
|
|
12
|
-
archive: archive_1.archiveCommand
|
|
15
|
+
archive: archive_1.archiveCommand,
|
|
16
|
+
estimate: estimate_1.estimateCommand,
|
|
17
|
+
scan: scan_1.scanCommand,
|
|
18
|
+
suggest: suggest_1.suggestCommand
|
|
13
19
|
};
|
|
14
20
|
const helpText = `repo-context-center
|
|
15
21
|
|
|
@@ -18,11 +24,17 @@ Usage:
|
|
|
18
24
|
|
|
19
25
|
Commands:
|
|
20
26
|
init Install generic context templates and config
|
|
21
|
-
Options: --dry-run, --force
|
|
27
|
+
Options: --dry-run, --force, --github-action
|
|
22
28
|
validate Validate required context files and warnings
|
|
23
29
|
Options: --strict
|
|
24
30
|
archive Archive older CHANGE_LOG and LESSONS_LEARNED entries
|
|
25
31
|
Options: --keep <number>, --dry-run
|
|
32
|
+
estimate Estimate context token costs and rough savings
|
|
33
|
+
Options: --json, --mode <mode>, --task <text>, --compare-naive, --max-files <number>
|
|
34
|
+
scan Suggest lightweight context entries from repo layout
|
|
35
|
+
Options: --json
|
|
36
|
+
suggest Recommend context files for a task
|
|
37
|
+
Usage: suggest "<task>" [--json] [--symbols]
|
|
26
38
|
|
|
27
39
|
Options:
|
|
28
40
|
-h, --help Show this help
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export declare const requiredContextFiles: readonly ["AGENTS.md", "docs/ai-context/COMMUNICATION_MODE.md", "docs/ai-context/TASK_ROUTING.md", "docs/ai-context/MODULE_INDEX.md", "docs/ai-context/PROJECT_MAP.md", "docs/ai-context/RISK_REGISTER.md", "docs/ai-context/DEPENDENCY_MAP.md", "docs/ai-context/SYMBOL_MAP.md", "docs/ai-context/TOKEN_BUDGET.md", "docs/ai-context/DO_NOT_READ.md", "docs/ai-context/HOTSPOTS.md", "docs/ai-context/LESSONS_LEARNED.md", "docs/ai-context/CHANGE_LOG.md"];
|
|
2
2
|
export type RequiredContextFile = (typeof requiredContextFiles)[number];
|
|
3
3
|
export declare const contextArchiveDir = "docs/ai-context/archive";
|
|
4
|
+
export declare const agentsFile = "AGENTS.md";
|
|
5
|
+
export declare const tokenBudgetFile = "docs/ai-context/TOKEN_BUDGET.md";
|
|
6
|
+
export declare const doNotReadFile = "docs/ai-context/DO_NOT_READ.md";
|
|
7
|
+
export declare const requiredTokenBudgetModes: readonly ["Compact Mode", "Investigation Mode"];
|
|
8
|
+
export declare const generatedFolderExclusions: readonly ["node_modules", "dist", "build", "coverage", "archive"];
|
|
4
9
|
export declare const contextSizeLimits: Partial<Record<RequiredContextFile, number>>;
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.contextSizeLimits = exports.contextArchiveDir = exports.requiredContextFiles = void 0;
|
|
3
|
+
exports.contextSizeLimits = exports.generatedFolderExclusions = exports.requiredTokenBudgetModes = exports.doNotReadFile = exports.tokenBudgetFile = exports.agentsFile = exports.contextArchiveDir = exports.requiredContextFiles = void 0;
|
|
4
4
|
const generic_1 = require("../templates/generic");
|
|
5
5
|
exports.requiredContextFiles = generic_1.genericTemplateFiles;
|
|
6
6
|
exports.contextArchiveDir = "docs/ai-context/archive";
|
|
7
|
+
exports.agentsFile = "AGENTS.md";
|
|
8
|
+
exports.tokenBudgetFile = "docs/ai-context/TOKEN_BUDGET.md";
|
|
9
|
+
exports.doNotReadFile = "docs/ai-context/DO_NOT_READ.md";
|
|
10
|
+
exports.requiredTokenBudgetModes = ["Compact Mode", "Investigation Mode"];
|
|
11
|
+
exports.generatedFolderExclusions = [
|
|
12
|
+
"node_modules",
|
|
13
|
+
"dist",
|
|
14
|
+
"build",
|
|
15
|
+
"coverage",
|
|
16
|
+
"archive"
|
|
17
|
+
];
|
|
7
18
|
exports.contextSizeLimits = {
|
|
8
19
|
"docs/ai-context/CHANGE_LOG.md": 32 * 1024,
|
|
9
20
|
"docs/ai-context/TASK_ROUTING.md": 16 * 1024
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const suggestContextFiles: readonly ["docs/ai-context/TASK_ROUTING.md", "docs/ai-context/MODULE_INDEX.md", "docs/ai-context/DEPENDENCY_MAP.md", "docs/ai-context/SYMBOL_MAP.md", "docs/ai-context/RISK_REGISTER.md", "docs/ai-context/HOTSPOTS.md"];
|
|
2
|
+
export type SuggestContextFile = (typeof suggestContextFiles)[number];
|
|
3
|
+
export interface ContextDocument {
|
|
4
|
+
path: SuggestContextFile;
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function readSuggestContext(cwd: string): Promise<ContextDocument[]>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.suggestContextFiles = void 0;
|
|
7
|
+
exports.readSuggestContext = readSuggestContext;
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const fileSystem_1 = require("./fileSystem");
|
|
10
|
+
exports.suggestContextFiles = [
|
|
11
|
+
"docs/ai-context/TASK_ROUTING.md",
|
|
12
|
+
"docs/ai-context/MODULE_INDEX.md",
|
|
13
|
+
"docs/ai-context/DEPENDENCY_MAP.md",
|
|
14
|
+
"docs/ai-context/SYMBOL_MAP.md",
|
|
15
|
+
"docs/ai-context/RISK_REGISTER.md",
|
|
16
|
+
"docs/ai-context/HOTSPOTS.md"
|
|
17
|
+
];
|
|
18
|
+
async function readSuggestContext(cwd) {
|
|
19
|
+
const documents = [];
|
|
20
|
+
for (const file of exports.suggestContextFiles) {
|
|
21
|
+
const fullPath = node_path_1.default.join(cwd, file);
|
|
22
|
+
if (await (0, fileSystem_1.pathExists)(fullPath)) {
|
|
23
|
+
documents.push({
|
|
24
|
+
path: file,
|
|
25
|
+
content: await (0, fileSystem_1.readTextFile)(fullPath)
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return documents;
|
|
30
|
+
}
|
|
@@ -2,4 +2,7 @@ export declare function pathExists(filePath: string): Promise<boolean>;
|
|
|
2
2
|
export declare function ensureDir(dirPath: string): Promise<void>;
|
|
3
3
|
export declare function writeJsonFile(filePath: string, value: unknown): Promise<void>;
|
|
4
4
|
export declare function writeTextFile(filePath: string, content: string): Promise<void>;
|
|
5
|
+
export declare function readTextFile(filePath: string): Promise<string>;
|
|
5
6
|
export declare function readJsonFile<T>(filePath: string): Promise<T>;
|
|
7
|
+
export declare function listDirectoryNames(dirPath: string): Promise<string[]>;
|
|
8
|
+
export declare function listFilesRecursive(dirPath: string, rootPath?: string): Promise<string[]>;
|
package/dist/core/fileSystem.js
CHANGED
|
@@ -7,7 +7,10 @@ exports.pathExists = pathExists;
|
|
|
7
7
|
exports.ensureDir = ensureDir;
|
|
8
8
|
exports.writeJsonFile = writeJsonFile;
|
|
9
9
|
exports.writeTextFile = writeTextFile;
|
|
10
|
+
exports.readTextFile = readTextFile;
|
|
10
11
|
exports.readJsonFile = readJsonFile;
|
|
12
|
+
exports.listDirectoryNames = listDirectoryNames;
|
|
13
|
+
exports.listFilesRecursive = listFilesRecursive;
|
|
11
14
|
const promises_1 = require("node:fs/promises");
|
|
12
15
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
16
|
async function pathExists(filePath) {
|
|
@@ -30,6 +33,40 @@ async function writeTextFile(filePath, content) {
|
|
|
30
33
|
await ensureDir(node_path_1.default.dirname(filePath));
|
|
31
34
|
await (0, promises_1.writeFile)(filePath, content, "utf8");
|
|
32
35
|
}
|
|
36
|
+
async function readTextFile(filePath) {
|
|
37
|
+
return (0, promises_1.readFile)(filePath, "utf8");
|
|
38
|
+
}
|
|
33
39
|
async function readJsonFile(filePath) {
|
|
34
40
|
return JSON.parse(await (0, promises_1.readFile)(filePath, "utf8"));
|
|
35
41
|
}
|
|
42
|
+
async function listDirectoryNames(dirPath) {
|
|
43
|
+
try {
|
|
44
|
+
const entries = await (0, promises_1.readdir)(dirPath, { withFileTypes: true });
|
|
45
|
+
return entries
|
|
46
|
+
.filter((entry) => entry.isDirectory())
|
|
47
|
+
.map((entry) => entry.name)
|
|
48
|
+
.sort((left, right) => left.localeCompare(right));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function listFilesRecursive(dirPath, rootPath = dirPath) {
|
|
55
|
+
try {
|
|
56
|
+
const entries = await (0, promises_1.readdir)(dirPath, { withFileTypes: true });
|
|
57
|
+
const files = [];
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
const fullPath = node_path_1.default.join(dirPath, entry.name);
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
files.push(...await listFilesRecursive(fullPath, rootPath));
|
|
62
|
+
}
|
|
63
|
+
else if (entry.isFile()) {
|
|
64
|
+
files.push(node_path_1.default.relative(rootPath, fullPath).split(node_path_1.default.sep).join("/"));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return files.sort((left, right) => left.localeCompare(right));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface ScannedModule {
|
|
2
|
+
name: string;
|
|
3
|
+
path: string;
|
|
4
|
+
sourceRoot: string;
|
|
5
|
+
}
|
|
6
|
+
export type ScannedSymbolKind = "function" | "class" | "interface" | "type" | "const" | "export";
|
|
7
|
+
export interface ScannedSymbol {
|
|
8
|
+
name: string;
|
|
9
|
+
kind: ScannedSymbolKind;
|
|
10
|
+
path: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ScannedSourceFile {
|
|
13
|
+
path: string;
|
|
14
|
+
symbols: ScannedSymbol[];
|
|
15
|
+
tests: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface ScanReport {
|
|
18
|
+
root: string;
|
|
19
|
+
detected: {
|
|
20
|
+
sourceFolders: string[];
|
|
21
|
+
testFolders: string[];
|
|
22
|
+
generatedFolders: string[];
|
|
23
|
+
modules: ScannedModule[];
|
|
24
|
+
sourceFiles: ScannedSourceFile[];
|
|
25
|
+
};
|
|
26
|
+
suggestions: {
|
|
27
|
+
"MODULE_INDEX.md": string[];
|
|
28
|
+
"DO_NOT_READ.md": string[];
|
|
29
|
+
"TASK_ROUTING.md": string[];
|
|
30
|
+
"SYMBOL_MAP.md": string[];
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export declare function extractExportedSymbols(sourcePath: string, content: string): ScannedSymbol[];
|
|
34
|
+
export declare function scanRepository(cwd: string): Promise<ScanReport>;
|