@stainless-code/codemap 0.1.1 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @stainless-code/codemap
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#6](https://github.com/stainless-code/codemap/pull/6) [`ad29694`](https://github.com/stainless-code/codemap/commit/ad2969481d4bd4e60d4f29818e4f1e64986216f9) Thanks [@SutuSebastian](https://github.com/SutuSebastian)! - Align shipped agent templates with the published CLI (`codemap`, `npx @stainless-code/codemap`, …). Keep this repository’s `.agents/` rule and skill dev-oriented (`bun src/index.ts`). Remove the redundant `agents-first-convention` template. Document the dev vs `templates/agents/` split in `templates/agents/README.md` and `docs/agents.md`.
8
+
9
+ ## 0.1.2
10
+
11
+ ### Patch Changes
12
+
13
+ - [#4](https://github.com/stainless-code/codemap/pull/4) [`0a9d829`](https://github.com/stainless-code/codemap/commit/0a9d82935e775edfb942029c03b8a427f18f9e71) Thanks [@SutuSebastian](https://github.com/SutuSebastian)! - **`codemap agents init`:** For Git repos, ensure **`.codemap.*`** is in **`.gitignore`** (create the file or append the line once). **`--force`** removes only template file paths (same relpaths under **`.agents/rules/`** and **`.agents/skills/`** as **`templates/agents`**) before merging; other files under **`.agents/`**, **`rules/`**, or **`skills/`** are kept. **`--interactive` / `-i`** — pick IDE integrations (Cursor, GitHub Copilot, Windsurf, Continue, Cline, Amazon Q, **`CLAUDE.md`**, **`AGENTS.md`**, **`GEMINI.md`**) and symlink vs copy for rule mirrors; requires a TTY. Unknown positional arguments (e.g. `interactive` without `--interactive`) are rejected. Depends on **`@clack/prompts`**.
14
+
15
+ **Docs:** **[`docs/agents.md`](https://github.com/stainless-code/codemap/blob/main/docs/agents.md)**; **[`docs/README.md`](https://github.com/stainless-code/codemap/blob/main/docs/README.md)** index updated. Root **[`.gitignore`](https://github.com/stainless-code/codemap/blob/main/.gitignore)** uses a single **`.codemap.*`** line.
16
+
3
17
  ## 0.1.1
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  - **Not** full-text search or grep on arbitrary strings — use those when you need raw file-body search.
6
6
  - **Is** a fast, token-efficient way to navigate **structure**: definitions, imports, dependency direction, components, and other extracted facts.
7
7
 
8
- **Documentation:** [docs/README.md](docs/README.md) is the index for technical docs (architecture, packaging, roadmap, benchmarks). **AI / editor agents:** [`.agents/rules/`](.agents/rules/), [`.agents/skills/codemap/SKILL.md`](.agents/skills/codemap/SKILL.md); Cursor uses `.cursor/` symlinks — [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md).
8
+ **Documentation:** [docs/README.md](docs/README.md) is the hub (topic index + single-source rules). Topics: [architecture](docs/architecture.md), [agents](docs/agents.md) (`codemap agents init`), [benchmark](docs/benchmark.md), [packaging](docs/packaging.md), [roadmap](docs/roadmap.md), [why Codemap](docs/why-codemap.md). **Bundled rules/skills:** [`.agents/rules/`](.agents/rules/), [`.agents/skills/codemap/SKILL.md`](.agents/skills/codemap/SKILL.md). **Consumers:** [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md).
9
9
 
10
10
  ---
11
11
 
@@ -47,9 +47,10 @@ codemap --config /path/to/codemap.config.json --full
47
47
  # Re-index only given paths (relative to project root)
48
48
  codemap --files src/a.ts src/b.tsx
49
49
 
50
- # Scaffold .agents/ rules and skills from bundled templates (see CONTRIBUTING)
50
+ # Scaffold .agents/ from bundled templates full matrix: docs/agents.md
51
51
  codemap agents init
52
52
  codemap agents init --force
53
+ codemap agents init --interactive # -i; IDE wiring + symlink vs copy
53
54
  ```
54
55
 
55
56
  **Environment / flags:** `--root` overrides **`CODEMAP_ROOT`** / **`CODEMAP_TEST_BENCH`**, then **`process.cwd()`**. Indexing a project outside this clone: [docs/benchmark.md § Indexing another project](docs/benchmark.md#indexing-another-project).
@@ -0,0 +1,303 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, join, relative } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
6
+ //#region src/agents-init.ts
7
+ /**
8
+ * Directory containing `rules/` and `skills/` (next to `dist/` in published packages).
9
+ */
10
+ function resolveAgentsTemplateDir() {
11
+ return join(dirname(fileURLToPath(import.meta.url)), "..", "templates", "agents");
12
+ }
13
+ /**
14
+ * Every regular file path under `dir` relative to `dir` (POSIX-style `/`).
15
+ * Used for template paths (`--force` removal), template writes, and copy-mode IDE sync.
16
+ */
17
+ function listRegularFilesRecursive(dir, relPrefix = "") {
18
+ const out = [];
19
+ if (!existsSync(dir)) return out;
20
+ for (const ent of readdirSync(dir, { withFileTypes: true })) {
21
+ const name = ent.name;
22
+ const rel = relPrefix ? `${relPrefix}/${name}` : name;
23
+ const full = join(dir, name);
24
+ if (ent.isDirectory()) out.push(...listRegularFilesRecursive(full, rel));
25
+ else if (ent.isFile()) out.push(rel);
26
+ }
27
+ return out;
28
+ }
29
+ function relPathToAbsSegments(rel) {
30
+ return rel.split("/").filter(Boolean);
31
+ }
32
+ /** Copy only listed relative paths from `srcRoot` into `destRoot` (mkdir parents per file). */
33
+ function copyFilesGranular(srcRoot, destRoot, relPaths) {
34
+ for (const rel of relPaths) {
35
+ const from = join(srcRoot, ...relPathToAbsSegments(rel));
36
+ const to = join(destRoot, ...relPathToAbsSegments(rel));
37
+ mkdirSync(dirname(to), { recursive: true });
38
+ copyFileSync(from, to);
39
+ }
40
+ }
41
+ /** Symlink each file: `destRoot/<rel>` → relative path to `srcRoot/<rel>` (mkdir parents per file). */
42
+ function symlinkFilesGranular(srcRoot, destRoot, relPaths, labelForErrors) {
43
+ mkdirSync(destRoot, { recursive: true });
44
+ for (const rel of relPaths) {
45
+ const srcFile = join(srcRoot, ...relPathToAbsSegments(rel));
46
+ const destFile = join(destRoot, ...relPathToAbsSegments(rel));
47
+ mkdirSync(dirname(destFile), { recursive: true });
48
+ const target = relative(dirname(destFile), srcFile);
49
+ try {
50
+ symlinkSync(target, destFile, "file");
51
+ } catch (err) {
52
+ throw new Error(`Codemap: symlink failed for ${labelForErrors} (${destFile}): ${String(err)}. Try copy mode or check permissions on Windows.`, { cause: err });
53
+ }
54
+ }
55
+ }
56
+ function removeBundledPathsIfExist(destBase, relPaths) {
57
+ for (const rel of relPaths) {
58
+ const abs = join(destBase, ...relPathToAbsSegments(rel));
59
+ if (!existsSync(abs)) continue;
60
+ rmSync(abs, {
61
+ recursive: true,
62
+ force: true
63
+ });
64
+ }
65
+ }
66
+ /** Default DB basename `.codemap` plus SQLite sidecars (`.db`, `-wal`, `-shm`, …). */
67
+ const GITIGNORE_CODEMAP_PATTERN = ".codemap.*";
68
+ /** Targets that mirror `.agents/rules` (and Cursor also `.agents/skills`) via per-file symlink or copy. */
69
+ const AGENTS_INIT_SYMLINK_TARGETS = [
70
+ "cursor",
71
+ "windsurf",
72
+ "continue",
73
+ "cline",
74
+ "amazon-q"
75
+ ];
76
+ function targetsNeedLinkMode(targets) {
77
+ return targets.some((t) => AGENTS_INIT_SYMLINK_TARGETS.includes(t));
78
+ }
79
+ const POINTER_BODY = `This project uses [Codemap](https://github.com/stainless-code/codemap) — a structural SQLite index for AI agents.
80
+
81
+ - **Skill:** \`.agents/skills/codemap/SKILL.md\`
82
+ - **CLI:** \`codemap\` to index, \`codemap query "SELECT …"\` for SQL
83
+ - **Rules:** \`.agents/rules/\`
84
+
85
+ `;
86
+ const CLAUDE_MD_TEMPLATE = `# Codemap\n\n${POINTER_BODY}`;
87
+ const AGENTS_MD_TEMPLATE = `# Agent instructions (Codemap)
88
+
89
+ ${POINTER_BODY}
90
+ Also referenced by **Zed**, **JetBrains AI**-style tools, **Aider**, and other agents that read \`AGENTS.md\` at the repo root.
91
+
92
+ `;
93
+ const GEMINI_MD_TEMPLATE = `# Codemap (Gemini)
94
+
95
+ ${POINTER_BODY}
96
+ Use this file if your **Gemini** CLI or IDE loads \`GEMINI.md\` at the repo root.
97
+
98
+ `;
99
+ const COPILOT_TEMPLATE = `# Codemap — GitHub Copilot custom instructions
100
+
101
+ ${POINTER_BODY}
102
+ See [GitHub Docs: custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot).
103
+
104
+ `;
105
+ /** HTML comments — invisible in most Markdown renderers; used to upsert without duplicating on re-run. */
106
+ const CODMAP_POINTER_BEGIN = "<!-- codemap-pointer:begin -->";
107
+ const CODMAP_POINTER_END = "<!-- codemap-pointer:end -->";
108
+ function wrapCodemapPointerBlock(inner) {
109
+ return `${CODMAP_POINTER_BEGIN}\n${inner.trim()}\n${CODMAP_POINTER_END}\n`;
110
+ }
111
+ function escapeRegexChars(s) {
112
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
113
+ }
114
+ function codemapPointerBlockRegex() {
115
+ return new RegExp(`${escapeRegexChars(CODMAP_POINTER_BEGIN)}\\s*[\\s\\S]*?${escapeRegexChars(CODMAP_POINTER_END)}`, "m");
116
+ }
117
+ /** Heuristic: file looks like a prior Codemap pointer file before we added markers (upgrade → single managed block). */
118
+ function looksLikeLegacyCodemapPointer(content) {
119
+ const t = content.trim();
120
+ if (t.length < 80) return false;
121
+ return t.includes("stainless-code/codemap") && t.includes(".agents/skills/codemap") && t.includes("codemap query");
122
+ }
123
+ /**
124
+ * Create or merge a Codemap pointer file. Idempotent: managed section is between
125
+ * {@link CODMAP_POINTER_BEGIN} / {@link CODMAP_POINTER_END}; re-runs replace that section only.
126
+ * - **No file:** write managed block.
127
+ * - **Existing + markers:** replace inner section (updates stale template text).
128
+ * - **Existing, no markers, legacy Codemap content:** replace whole file with managed block.
129
+ * - **Existing, other content:** append managed block once.
130
+ * - **`force`:** replace entire file with the latest managed block (same as a fresh write).
131
+ */
132
+ function upsertCodemapPointerFile(path, innerTemplate, label, force) {
133
+ const wrapped = wrapCodemapPointerBlock(innerTemplate);
134
+ if (!existsSync(path)) {
135
+ writeFileSync(path, wrapped, "utf-8");
136
+ console.log(` Wrote ${label} with Codemap pointers`);
137
+ return;
138
+ }
139
+ if (force) {
140
+ writeFileSync(path, wrapped, "utf-8");
141
+ console.log(` Replaced ${label} (--force)`);
142
+ return;
143
+ }
144
+ const content = readFileSync(path, "utf-8");
145
+ const re = codemapPointerBlockRegex();
146
+ if (content.match(re)) {
147
+ const next = content.replace(re, wrapped);
148
+ if (next === content) {
149
+ console.log(` Codemap section in ${label} already up to date`);
150
+ return;
151
+ }
152
+ writeFileSync(path, next, "utf-8");
153
+ console.log(` Updated Codemap section in ${label}`);
154
+ return;
155
+ }
156
+ if (looksLikeLegacyCodemapPointer(content)) {
157
+ writeFileSync(path, wrapped, "utf-8");
158
+ console.log(` Migrated ${label} to managed Codemap section`);
159
+ return;
160
+ }
161
+ writeFileSync(path, content + (content.endsWith("\n") ? "\n" : "\n\n") + wrapped, "utf-8");
162
+ console.log(` Appended Codemap section to ${label}`);
163
+ }
164
+ /**
165
+ * Ensure `.codemap.*` is listed in `.gitignore` when the project uses Git:
166
+ * - If `<projectRoot>/.git` exists and there is no `.gitignore`, create one with `.codemap.*`.
167
+ * - If `.gitignore` exists, append `.codemap.*` once when missing.
168
+ * - If there is no `.git`, do nothing (not a Git working tree).
169
+ */
170
+ function ensureGitignoreCodemapPattern(projectRoot) {
171
+ const gitDir = join(projectRoot, ".git");
172
+ const gitignorePath = join(projectRoot, ".gitignore");
173
+ if (!existsSync(gitDir)) return;
174
+ if (!existsSync(gitignorePath)) {
175
+ writeFileSync(gitignorePath, `${GITIGNORE_CODEMAP_PATTERN}\n`, "utf-8");
176
+ console.log(` Created .gitignore with ${GITIGNORE_CODEMAP_PATTERN} (Git repo, no .gitignore yet)`);
177
+ return;
178
+ }
179
+ const content = readFileSync(gitignorePath, "utf-8");
180
+ if (content.split(/\r?\n/).some((line) => line.trim() === GITIGNORE_CODEMAP_PATTERN)) return;
181
+ appendFileSync(gitignorePath, `${content.length > 0 && !content.endsWith("\n") ? "\n" : ""}${GITIGNORE_CODEMAP_PATTERN}\n`, "utf-8");
182
+ console.log(` Appended ${GITIGNORE_CODEMAP_PATTERN} to .gitignore`);
183
+ }
184
+ function removePathForRewrite(path, force, label) {
185
+ if (!existsSync(path)) return;
186
+ if (!force) throw new Error(`Codemap: ${label} already exists — use --force to replace, or remove it manually.`);
187
+ rmSync(path, {
188
+ recursive: true,
189
+ force: true
190
+ });
191
+ }
192
+ /**
193
+ * Map `.agents/rules` into a destination directory (symlink or copy).
194
+ */
195
+ function wireAgentsRulesTo(projectRoot, destPath, label, linkMode, force) {
196
+ const agentsRules = join(projectRoot, ".agents", "rules");
197
+ mkdirSync(dirname(destPath), { recursive: true });
198
+ removePathForRewrite(destPath, force, label);
199
+ if (linkMode === "symlink") {
200
+ const ruleFiles = listRegularFilesRecursive(agentsRules);
201
+ symlinkFilesGranular(agentsRules, destPath, ruleFiles, label);
202
+ console.log(` Linked each file under ${label} → .agents/rules (${ruleFiles.length} files)`);
203
+ return;
204
+ }
205
+ copyFilesGranular(agentsRules, destPath, listRegularFilesRecursive(agentsRules));
206
+ console.log(` Copied .agents/rules → ${label}`);
207
+ }
208
+ /**
209
+ * Wire Cursor or other tools after `.agents/` exists.
210
+ */
211
+ function applyAgentsInitTargets(projectRoot, targets, linkMode, force) {
212
+ const agentsRules = join(projectRoot, ".agents", "rules");
213
+ const agentsSkills = join(projectRoot, ".agents", "skills");
214
+ if (!existsSync(agentsRules) || !existsSync(agentsSkills)) throw new Error("Codemap: .agents/rules and .agents/skills must exist before wiring integrations");
215
+ for (const t of targets) switch (t) {
216
+ case "cursor":
217
+ applyCursorIntegration(projectRoot, linkMode, force);
218
+ break;
219
+ case "windsurf":
220
+ wireAgentsRulesTo(projectRoot, join(projectRoot, ".windsurf", "rules"), ".windsurf/rules", linkMode, force);
221
+ break;
222
+ case "continue":
223
+ wireAgentsRulesTo(projectRoot, join(projectRoot, ".continue", "rules"), ".continue/rules", linkMode, force);
224
+ break;
225
+ case "cline":
226
+ wireAgentsRulesTo(projectRoot, join(projectRoot, ".clinerules"), ".clinerules", linkMode, force);
227
+ break;
228
+ case "amazon-q":
229
+ wireAgentsRulesTo(projectRoot, join(projectRoot, ".amazonq", "rules"), ".amazonq/rules", linkMode, force);
230
+ break;
231
+ case "claude-md":
232
+ upsertCodemapPointerFile(join(projectRoot, "CLAUDE.md"), CLAUDE_MD_TEMPLATE, "CLAUDE.md", force);
233
+ break;
234
+ case "copilot":
235
+ mkdirSync(join(projectRoot, ".github"), { recursive: true });
236
+ upsertCodemapPointerFile(join(projectRoot, ".github", "copilot-instructions.md"), COPILOT_TEMPLATE, ".github/copilot-instructions.md", force);
237
+ break;
238
+ case "agents-md":
239
+ upsertCodemapPointerFile(join(projectRoot, "AGENTS.md"), AGENTS_MD_TEMPLATE, "AGENTS.md", force);
240
+ break;
241
+ case "gemini-md":
242
+ upsertCodemapPointerFile(join(projectRoot, "GEMINI.md"), GEMINI_MD_TEMPLATE, "GEMINI.md", force);
243
+ break;
244
+ }
245
+ }
246
+ function applyCursorIntegration(projectRoot, linkMode, force) {
247
+ const agentsRules = join(projectRoot, ".agents", "rules");
248
+ const agentsSkills = join(projectRoot, ".agents", "skills");
249
+ const cursorRules = join(projectRoot, ".cursor", "rules");
250
+ const cursorSkills = join(projectRoot, ".cursor", "skills");
251
+ mkdirSync(join(projectRoot, ".cursor"), { recursive: true });
252
+ if (linkMode === "symlink") {
253
+ removePathForRewrite(cursorRules, force, ".cursor/rules");
254
+ removePathForRewrite(cursorSkills, force, ".cursor/skills");
255
+ const ruleFiles = listRegularFilesRecursive(agentsRules);
256
+ const skillFiles = listRegularFilesRecursive(agentsSkills);
257
+ symlinkFilesGranular(agentsRules, cursorRules, ruleFiles, ".cursor/rules");
258
+ symlinkFilesGranular(agentsSkills, cursorSkills, skillFiles, ".cursor/skills");
259
+ console.log(` Linked ${ruleFiles.length} rule file(s) and ${skillFiles.length} skill file(s) under .cursor/ → .agents/`);
260
+ return;
261
+ }
262
+ removePathForRewrite(cursorRules, force, ".cursor/rules");
263
+ removePathForRewrite(cursorSkills, force, ".cursor/skills");
264
+ copyFilesGranular(agentsRules, cursorRules, listRegularFilesRecursive(agentsRules));
265
+ copyFilesGranular(agentsSkills, cursorSkills, listRegularFilesRecursive(agentsSkills));
266
+ console.log(" Copied rules and skills into .cursor/rules and .cursor/skills");
267
+ }
268
+ /**
269
+ * Copy bundled `rules/` and `skills/` into `<projectRoot>/.agents/`, optional integrations, `.gitignore` hint.
270
+ * **`--force`** deletes only template-backed files, then writes those files again with per-file copies — your other files under **`.agents/`**, **`rules/`**, or **`skills/`** stay.
271
+ * @returns `false` when `.agents/` exists and `--force` was not used.
272
+ */
273
+ function runAgentsInit(options) {
274
+ const templateRoot = resolveAgentsTemplateDir();
275
+ if (!existsSync(templateRoot)) throw new Error(`Codemap: agent templates not found at ${templateRoot} (expected npm package layout: templates/agents next to dist/)`);
276
+ const templateRules = join(templateRoot, "rules");
277
+ const templateSkills = join(templateRoot, "skills");
278
+ const bundledRuleFiles = listRegularFilesRecursive(templateRules);
279
+ const bundledSkillFiles = listRegularFilesRecursive(templateSkills);
280
+ const destRoot = join(options.projectRoot, ".agents");
281
+ const destRules = join(destRoot, "rules");
282
+ const destSkills = join(destRoot, "skills");
283
+ if (existsSync(destRoot)) {
284
+ if (!statSync(destRoot).isDirectory()) throw new Error(`Codemap: ${destRoot} exists but is not a directory — remove or rename it, then retry.`);
285
+ if (!options.force) {
286
+ console.error(` .agents/ already exists at ${destRoot}. Re-run with --force to refresh bundled template files under rules/ and skills/, or remove the directory.`);
287
+ return false;
288
+ }
289
+ removeBundledPathsIfExist(destRules, bundledRuleFiles);
290
+ removeBundledPathsIfExist(destSkills, bundledSkillFiles);
291
+ } else mkdirSync(destRoot, { recursive: true });
292
+ copyFilesGranular(templateRules, destRules, bundledRuleFiles);
293
+ copyFilesGranular(templateSkills, destSkills, bundledSkillFiles);
294
+ console.log(` Wrote agent templates to ${destRoot}`);
295
+ const targets = options.targets ?? [];
296
+ const linkMode = options.linkMode ?? "symlink";
297
+ if (targets.length > 0) applyAgentsInitTargets(options.projectRoot, targets, linkMode, !!options.force);
298
+ else console.log(" Tip: run `codemap agents init --interactive` to wire editors (Cursor, Copilot, …) or add CLAUDE.md / AGENTS.md");
299
+ ensureGitignoreCodemapPattern(options.projectRoot);
300
+ return true;
301
+ }
302
+ //#endregion
303
+ export { targetsNeedLinkMode as n, runAgentsInit as t };
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { n as targetsNeedLinkMode, t as runAgentsInit } from "./agents-init-COkjrzc5.mjs";
4
+ import { cancel, confirm, intro, isCancel, multiselect, note, outro, select } from "@clack/prompts";
5
+ //#region src/agents-init-interactive.ts
6
+ const INTEGRATION_OPTIONS = [
7
+ {
8
+ value: "cursor",
9
+ label: "Cursor",
10
+ hint: ".cursor/rules + skills → .agents/"
11
+ },
12
+ {
13
+ value: "claude-md",
14
+ label: "Claude Code",
15
+ hint: "CLAUDE.md"
16
+ },
17
+ {
18
+ value: "copilot",
19
+ label: "GitHub Copilot",
20
+ hint: ".github/copilot-instructions.md"
21
+ },
22
+ {
23
+ value: "windsurf",
24
+ label: "Windsurf (Cascade)",
25
+ hint: ".windsurf/rules → .agents/rules"
26
+ },
27
+ {
28
+ value: "continue",
29
+ label: "Continue",
30
+ hint: ".continue/rules → .agents/rules"
31
+ },
32
+ {
33
+ value: "cline",
34
+ label: "Cline",
35
+ hint: ".clinerules → .agents/rules"
36
+ },
37
+ {
38
+ value: "amazon-q",
39
+ label: "Amazon Q Developer",
40
+ hint: ".amazonq/rules → .agents/rules"
41
+ },
42
+ {
43
+ value: "agents-md",
44
+ label: "AGENTS.md (Zed, JetBrains, Aider, …)",
45
+ hint: "Root AGENTS.md"
46
+ },
47
+ {
48
+ value: "gemini-md",
49
+ label: "Gemini",
50
+ hint: "GEMINI.md"
51
+ }
52
+ ];
53
+ function summarizeTargets(targets) {
54
+ const lines = [];
55
+ for (const t of targets) {
56
+ const opt = INTEGRATION_OPTIONS.find((o) => o.value === t);
57
+ lines.push(opt ? `${opt.label}: ${opt.hint}` : t);
58
+ }
59
+ return lines;
60
+ }
61
+ /**
62
+ * Interactive `codemap agents init`: choose integrations and symlink vs copy for rule mirrors.
63
+ */
64
+ async function runAgentsInitInteractive(opts) {
65
+ intro("codemap agents init");
66
+ note("Canonical templates always install to .agents/ (rules + skills).\nOptional steps wire other tools to the same content.", "Codemap");
67
+ const targetsRaw = await multiselect({
68
+ message: "Integrations (space to toggle, enter to confirm)",
69
+ options: INTEGRATION_OPTIONS,
70
+ required: false,
71
+ initialValues: []
72
+ });
73
+ if (isCancel(targetsRaw)) {
74
+ cancel("Cancelled.");
75
+ return false;
76
+ }
77
+ const targets = targetsRaw;
78
+ let linkMode = "symlink";
79
+ if (targetsNeedLinkMode(targets)) {
80
+ const mode = await select({
81
+ message: "How should tools that mirror .agents/rules (and Cursor skills) link?",
82
+ options: [{
83
+ value: "symlink",
84
+ label: "Symlink",
85
+ hint: "One source of truth; best on macOS / Linux"
86
+ }, {
87
+ value: "copy",
88
+ label: "Copy",
89
+ hint: "Duplicate files; safest on Windows / sandboxes"
90
+ }],
91
+ initialValue: "symlink"
92
+ });
93
+ if (isCancel(mode)) {
94
+ cancel("Cancelled.");
95
+ return false;
96
+ }
97
+ linkMode = mode;
98
+ }
99
+ note([
100
+ `Project: ${opts.projectRoot}`,
101
+ "Will write: .agents/rules, .agents/skills",
102
+ ...summarizeTargets(targets).map((l) => `• ${l}`)
103
+ ].join("\n"), "Summary");
104
+ const ok = await confirm({
105
+ message: "Proceed?",
106
+ initialValue: true
107
+ });
108
+ if (isCancel(ok) || !ok) {
109
+ cancel("Cancelled.");
110
+ return false;
111
+ }
112
+ const success = runAgentsInit({
113
+ projectRoot: opts.projectRoot,
114
+ force: opts.force,
115
+ targets,
116
+ linkMode
117
+ });
118
+ if (success) outro("Done. Edit .agents/ for your team; restart IDEs if rules did not reload.");
119
+ return success;
120
+ }
121
+ //#endregion
122
+ export { runAgentsInitInteractive };
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { t as runAgentsInit } from "./agents-init-COkjrzc5.mjs";
4
+ //#region src/cli/cmd-agents.ts
5
+ async function runAgentsInitCmd(opts) {
6
+ if (opts.interactive) {
7
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
8
+ console.error("codemap: --interactive requires an interactive terminal (TTY).");
9
+ process.exit(1);
10
+ }
11
+ const { runAgentsInitInteractive } = await import("./agents-init-interactive-HLqgP8gL.mjs");
12
+ return runAgentsInitInteractive(opts);
13
+ }
14
+ return runAgentsInit(opts);
15
+ }
16
+ //#endregion
17
+ export { runAgentsInitCmd };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { n as CodemapDatabase, t as ParsedFile } from "./parsed-types-udxNyD9e.mjs";
2
+ import { n as CodemapDatabase, t as ParsedFile } from "./parsed-types-CtqqGr-K.mjs";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/application/types.d.ts
package/dist/index.mjs CHANGED
@@ -8,7 +8,7 @@ import { fileURLToPath } from "node:url";
8
8
  //#endregion
9
9
  //#region src/version.ts
10
10
  /** Package version from `package.json` (inlined at build time). */
11
- const CODEMAP_VERSION = "0.1.1";
11
+ const CODEMAP_VERSION = "0.1.3";
12
12
  //#endregion
13
13
  //#region src/cli/bootstrap.ts
14
14
  /** Printed for `codemap --help` / `-h` (must run before config or DB access). */
@@ -23,7 +23,7 @@ Query:
23
23
  codemap query "<SQL>"
24
24
 
25
25
  Agents:
26
- codemap agents init [--force]
26
+ codemap agents init [--force] [--interactive|-i]
27
27
 
28
28
  Other:
29
29
  codemap version
@@ -48,7 +48,7 @@ function validateIndexModeArgs(rest) {
48
48
  if (rest[0] === "query") return;
49
49
  if (rest[0] === "agents") {
50
50
  if (rest[1] === "init") return;
51
- console.error(`codemap: unknown agents command "${rest[1] ?? "(missing)"}". Expected: codemap agents init [--force]`);
51
+ console.error(`codemap: unknown agents command "${rest[1] ?? "(missing)"}". Expected: codemap agents init [--force] [--interactive|-i]`);
52
52
  process.exit(1);
53
53
  }
54
54
  let i = 0;
@@ -116,17 +116,34 @@ async function main() {
116
116
  }
117
117
  if (rest[0] === "agents" && rest[1] === "init") {
118
118
  if (rest.includes("--help") || rest.includes("-h")) {
119
- console.log(`Usage: codemap agents init [--force]
119
+ console.log(`Usage: codemap agents init [--force] [--interactive|-i]
120
120
 
121
121
  Copies bundled agent templates into .agents/ under the project root.
122
- Use --force to overwrite an existing .agents/ directory.
122
+ --force Refresh only files that ship in templates/agents (merge into rules/ & skills/)
123
+ --interactive Pick IDEs (Cursor, Copilot, Windsurf, …) and symlink vs copy
123
124
  `);
124
125
  return;
125
126
  }
126
- const { runAgentsInitCmd } = await import("./cmd-agents-BJPx1vGG.mjs");
127
- if (!runAgentsInitCmd({
127
+ const initRest = rest.slice(2);
128
+ const knownInit = new Set([
129
+ "--force",
130
+ "--interactive",
131
+ "-i",
132
+ "--help",
133
+ "-h"
134
+ ]);
135
+ for (const a of initRest) {
136
+ if (knownInit.has(a)) continue;
137
+ if (a.startsWith("-")) console.error(`codemap: unknown option "${a}"`);
138
+ else console.error(`codemap: unexpected argument "${a}"`);
139
+ console.error("Run codemap agents init --help for usage.");
140
+ process.exit(1);
141
+ }
142
+ const { runAgentsInitCmd } = await import("./cmd-agents-Dph66vnf.mjs");
143
+ if (!await runAgentsInitCmd({
128
144
  projectRoot: root,
129
- force: rest.includes("--force")
145
+ force: rest.includes("--force"),
146
+ interactive: rest.includes("--interactive") || rest.includes("-i")
130
147
  })) process.exit(1);
131
148
  return;
132
149
  }
@@ -140,7 +157,7 @@ Example: codemap query "SELECT name, file_path FROM symbols LIMIT 10"
140
157
  `);
141
158
  return;
142
159
  }
143
- const { runQueryCmd } = await import("./cmd-query-D3zXZu7K.mjs");
160
+ const { runQueryCmd } = await import("./cmd-query-lSSRXMX8.mjs");
144
161
  await runQueryCmd({
145
162
  root,
146
163
  configFile,
@@ -148,7 +165,7 @@ Example: codemap query "SELECT name, file_path FROM symbols LIMIT 10"
148
165
  });
149
166
  return;
150
167
  }
151
- const { runIndexCmd } = await import("./cmd-index-oyoHr0c4.mjs");
168
+ const { runIndexCmd } = await import("./cmd-index-UeGdciH7.mjs");
152
169
  await runIndexCmd({
153
170
  root,
154
171
  configFile,
@@ -1,5 +1,5 @@
1
1
 
2
- import { t as ParsedFile } from "./parsed-types-udxNyD9e.mjs";
2
+ import { t as ParsedFile } from "./parsed-types-CtqqGr-K.mjs";
3
3
 
4
4
  //#region src/parse-worker-core.d.ts
5
5
  interface WorkerInput {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stainless-code/codemap",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Query your codebase — structural SQLite index for AI agents",
5
5
  "keywords": [
6
6
  "agents",
@@ -66,6 +66,7 @@
66
66
  "typecheck": "tsgo --noEmit"
67
67
  },
68
68
  "dependencies": {
69
+ "@clack/prompts": "^1.2.0",
69
70
  "better-sqlite3": "^12.8.0",
70
71
  "fast-glob": "^3.3.3",
71
72
  "lightningcss": "^1.32.0",
@@ -1,5 +1,9 @@
1
1
  # Bundled agent templates
2
2
 
3
- These files are **copies** of the upstream [`.agents/`](../../.agents/) rules and skills shipped with `@stainless-code/codemap` for `codemap agents init`.
3
+ These files ship with **`@stainless-code/codemap`** for **`codemap agents init`** written for **npm consumers** ( **`codemap`**, **`npx @stainless-code/codemap`**, etc.).
4
4
 
5
- After running the command, **edit** `.agents/` in your project (paths, SQL, team conventions). Treat updates here as a reference when refreshing your copy.
5
+ In **this** repository, **`.agents/`** (and **`.cursor/`** symlinks) are **maintainer / dev** copies: examples use **`bun src/index.ts`** where that matters. **`templates/agents/`** is the **published** agent surface and is **not** required to match **`.agents/`** byte-for-byte (the **codemap** rule and skill intentionally differ).
6
+
7
+ **Documentation:** [docs/agents.md](../../docs/agents.md) — interactive setup, **`.gitignore`**, and optional IDE wiring (Cursor, Copilot, …).
8
+
9
+ After running the command in **your** project, **edit** **`.agents/`** there (paths, SQL, team conventions). Treat updates here as a reference when refreshing your copy.
@@ -8,24 +8,33 @@ alwaysApply: true
8
8
 
9
9
  A local database (default **`.codemap.db`**) indexes structure: symbols, imports, exports, components, dependencies, markers, CSS variables, CSS classes, CSS keyframes.
10
10
 
11
- **Generic defaults:** This rule is **project-agnostic**. After you vendor or symlink it (or use a future `codemap` CLI that writes agent files), **edit your copy** to add app-specific triggers and SQL — upstream text is only a baseline.
11
+ **Generic defaults:** This rule is **project-agnostic**. After **`codemap agents init`** (or copying these files into **`.agents/`**), **edit your copy** to add app-specific triggers and SQL — upstream text is only a baseline.
12
12
 
13
- ## CLI (this repo vs installed package)
13
+ ## CLI (npm package **`@stainless-code/codemap`**)
14
14
 
15
- | Context | Incremental index | Query |
16
- | ------- | ----------------- | ----- |
17
- | Developing in this repo | `bun src/index.ts` | `bun src/index.ts query "<SQL>"` |
18
- | After `bun link` / global `codemap` | `codemap` | `codemap query "<SQL>"` |
19
- | Published package (when available) | `bunx @stainless-code/codemap` | `bunx @stainless-code/codemap query "<SQL>"` |
15
+ Install **[@stainless-code/codemap](https://www.npmjs.com/package/@stainless-code/codemap)** from npm. The executable name is **`codemap`**.
20
16
 
21
- Index another project: **`--root /path/to/repo`**, or set **`CODEMAP_ROOT`** or **`CODEMAP_TEST_BENCH`** (e.g. in **`.env`** in this reposee [docs/benchmark.md § Indexing another project](../../docs/benchmark.md#indexing-another-project)). Full rebuild: **`--full`**. Targeted re-index: **`--files path/to/a.ts path/to/b.tsx`**.
17
+ **Run without a global install:** **`npx @stainless-code/codemap`** (npm), **`pnpm dlx @stainless-code/codemap`** (pnpm), **`yarn dlx @stainless-code/codemap`** (Yarn 2+), or **`bunx @stainless-code/codemap`** (Bun)same flags everywhere. With a **local** devDependency, **`npx codemap`** / **`pnpm exec codemap`**. With a **global** install, **`codemap`** on your **`PATH`**.
18
+
19
+ **Examples below use `codemap`** — prefix with **`npx @stainless-code/codemap`** (or **`pnpm dlx`**, **`yarn dlx`**, **`bunx`**) when the CLI is not on your **`PATH`**.
20
+
21
+ | Action | Command |
22
+ | ------ | ------- |
23
+ | Incremental index | `codemap` |
24
+ | Query | `codemap query "<SQL>"` |
25
+
26
+ **Bundled rules/skills:** **`codemap agents init`** writes **`.agents/`** from the package (see [docs/agents.md](../../../docs/agents.md)).
27
+
28
+ Index another project: **`--root /path/to/repo`**, or set **`CODEMAP_ROOT`** or **`CODEMAP_TEST_BENCH`** (e.g. in **`.env`** — see [docs/benchmark.md § Indexing another project](../../../docs/benchmark.md#indexing-another-project)). Full rebuild: **`--full`**. Targeted re-index: **`--files path/to/a.ts path/to/b.tsx`**.
29
+
30
+ **Developing the Codemap repo itself:** from a clone, **`bun src/index.ts`** matches **`codemap`** (same flags); see the repository README.
22
31
 
23
32
  ## Session start (do this ONCE per conversation)
24
33
 
25
34
  Run incremental indexing to catch changes made outside this session:
26
35
 
27
36
  ```bash
28
- bun src/index.ts
37
+ codemap
29
38
  ```
30
39
 
31
40
  ## Pre-flight check (do this EVERY time before searching)
@@ -62,7 +71,7 @@ If the question looks like any of these → use the index:
62
71
  ## How to query
63
72
 
64
73
  ```bash
65
- bun src/index.ts query "<SQL>"
74
+ codemap query "<SQL>"
66
75
  ```
67
76
 
68
77
  ## Quick reference queries
@@ -94,13 +103,13 @@ For the full schema, advanced query patterns, and troubleshooting, read the skil
94
103
 
95
104
  ```bash
96
105
  # Targeted — re-index only the files you just touched
97
- bun src/index.ts --files path/to/file1.tsx path/to/file2.ts
106
+ codemap --files path/to/file1.tsx path/to/file2.ts
98
107
 
99
108
  # Incremental — auto-detects changed files via git
100
- bun src/index.ts
109
+ codemap
101
110
 
102
111
  # Full rebuild — after rebase, branch switch, or stale index
103
- bun src/index.ts --full
112
+ codemap --full
104
113
  ```
105
114
 
106
115
  ### When to re-index
@@ -6,15 +6,15 @@ Query codebase structure via SQLite instead of scanning files. Use when explorin
6
6
 
7
7
  Examples below use **placeholders** (`'...'`, `getConfig`, `~/lib/api`, etc.) — not a real product tree. **Shipped skill and rules stay generic** so they apply to any repo.
8
8
 
9
- **In your project:** copy or symlink these files into `.agents/` / `.cursor/` (see **`.github/CONTRIBUTING.md`**), or use a future **`codemap` CLI** that vendors agent files. Then **edit your copy** to add your team’s tsconfig aliases, directory conventions, and SQL snippets you reuse. Treat upstream updates as a reference; merge deliberately.
9
+ **In your project:** run **`codemap agents init`** (ships **`.agents/`** from **[@stainless-code/codemap](https://www.npmjs.com/package/@stainless-code/codemap)**), or copy/symlink rules and skills manually (see your repo’s contributor docs). Then **edit your copy** to add your team’s tsconfig aliases, directory conventions, and SQL snippets you reuse. Treat upstream updates as a reference; merge deliberately.
10
10
 
11
- **Run queries**
11
+ **Run queries** (same CLI everywhere — use **`npx @stainless-code/codemap`**, **`pnpm dlx @stainless-code/codemap`**, **`yarn dlx @stainless-code/codemap`**, or **`bunx @stainless-code/codemap`** if **`codemap`** is not on your **`PATH`**):
12
12
 
13
13
  ```bash
14
- bun src/index.ts query "<SQL>"
14
+ codemap query "<SQL>"
15
15
  ```
16
16
 
17
- When the package is installed globally or via `bunx`: `codemap query "<SQL>"` or `bunx @stainless-code/codemap query "<SQL>"`. Use **`--root`** to point at another project.
17
+ Use **`codemap --root /path/to/project`** (or **`CODEMAP_ROOT`**) to index another tree.
18
18
 
19
19
  ## Schema
20
20
 
@@ -282,31 +282,31 @@ SELECT kind, COUNT(*) as count FROM markers GROUP BY kind;
282
282
 
283
283
  ## Maintenance
284
284
 
285
- From this repository:
285
+ From the **[@stainless-code/codemap](https://www.npmjs.com/package/@stainless-code/codemap)** CLI (see the **codemap** rule for **`npx` / `pnpm dlx` / `yarn dlx` / `bunx`** invocations):
286
286
 
287
287
  ```bash
288
288
  # Targeted — re-index only specific files you just modified
289
- bun src/index.ts --files path/to/file.tsx path/to/other.ts
289
+ codemap --files path/to/file.tsx path/to/other.ts
290
290
 
291
291
  # Incremental — auto-detects changes via git
292
- bun src/index.ts
292
+ codemap
293
293
 
294
294
  # Full rebuild — after rebase, branch switch, or stale index
295
- bun src/index.ts --full
295
+ codemap --full
296
296
 
297
297
  # Check index freshness
298
- bun src/index.ts query "SELECT key, value FROM meta"
298
+ codemap query "SELECT key, value FROM meta"
299
299
  ```
300
300
 
301
301
  **Prefer `--files`** when you know which files you changed — it skips git diff and filesystem scanning for the rest of the tree. Deleted files passed to `--files` are auto-removed from the index.
302
302
 
303
- When Codemap is installed as a package: `codemap`, `codemap --root /path/to/project`, or `bunx @stainless-code/codemap …` (same flags).
303
+ Same flags as **`npx @stainless-code/codemap`**, **`pnpm dlx @stainless-code/codemap`**, etc. **`codemap --root /path/to/project`** indexes another working tree.
304
304
 
305
305
  ## Troubleshooting
306
306
 
307
- | Problem | Solution |
308
- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
309
- | Stale results after rebase | Run `bun src/index.ts --full` (or `codemap --full` when installed) |
310
- | Missing file in results | Check exclude / include globs in **`codemap.config.ts`**, **`codemap.config.json`**, or defaults in **`src/index.ts`** |
311
- | `resolved_path` is NULL | Import is an external package (not in project) |
312
- | Resolver errors | Verify `tsconfig.json` paths (or **`tsconfigPath`** in config) when resolving aliases |
307
+ | Problem | Solution |
308
+ | -------------------------- | --------------------------------------------------------------------------------------------------------------------- |
309
+ | Stale results after rebase | Run **`codemap --full`** (see **`npx @stainless-code/codemap`** / **`pnpm dlx`** / … above if needed) |
310
+ | Missing file in results | Check exclude / include globs in **`codemap.config.ts`**, **`codemap.config.json`**, or **`codemap --help`** defaults |
311
+ | `resolved_path` is NULL | Import is an external package (not in project) |
312
+ | Resolver errors | Verify `tsconfig.json` paths (or **`tsconfigPath`** in config) when resolving aliases |
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { cpSync, existsSync, mkdirSync } from "node:fs";
6
- //#region src/agents-init.ts
7
- /**
8
- * Directory containing `rules/` and `skills/` (next to `dist/` in published packages).
9
- */
10
- function resolveAgentsTemplateDir() {
11
- return join(dirname(fileURLToPath(import.meta.url)), "..", "templates", "agents");
12
- }
13
- /**
14
- * Copy bundled rules and skills into `<projectRoot>/.agents/`.
15
- * @returns `false` when `.agents/` exists and `--force` was not used.
16
- */
17
- function runAgentsInit(options) {
18
- const templateRoot = resolveAgentsTemplateDir();
19
- if (!existsSync(templateRoot)) throw new Error(`Codemap: agent templates not found at ${templateRoot} (expected npm package layout: templates/agents next to dist/)`);
20
- const destRoot = join(options.projectRoot, ".agents");
21
- if (existsSync(destRoot) && !options.force) {
22
- console.error(` .agents/ already exists at ${destRoot}. Re-run with --force to overwrite, or remove the directory.`);
23
- return false;
24
- }
25
- mkdirSync(destRoot, { recursive: true });
26
- cpSync(join(templateRoot, "rules"), join(destRoot, "rules"), { recursive: true });
27
- cpSync(join(templateRoot, "skills"), join(destRoot, "skills"), { recursive: true });
28
- console.log(` Wrote agent templates to ${destRoot}`);
29
- console.log(` Symlink into .cursor/ if needed — see https://github.com/stainless-code/codemap/blob/main/.github/CONTRIBUTING.md`);
30
- return true;
31
- }
32
- //#endregion
33
- //#region src/cli/cmd-agents.ts
34
- function runAgentsInitCmd(opts) {
35
- return runAgentsInit(opts);
36
- }
37
- //#endregion
38
- export { runAgentsInitCmd };
@@ -1,37 +0,0 @@
1
- ---
2
- description: When creating or moving rules/skills, always store the source file in .agents/ and symlink from .cursor/
3
- alwaysApply: true
4
- ---
5
-
6
- # Agents-First File Convention
7
-
8
- When creating **any** new rule or skill, follow this convention:
9
-
10
- ## Rules (`.mdc` files)
11
-
12
- 1. Create the file in `.agents/rules/<name>.mdc`
13
- 2. Create a symlink in `.cursor/rules/`:
14
-
15
- ```bash
16
- ln -s ../../.agents/rules/<name>.mdc .cursor/rules/<name>.mdc
17
- ```
18
-
19
- ## Skills (`SKILL.md` files)
20
-
21
- 1. Create the directory and file in `.agents/skills/<name>/SKILL.md`
22
- 2. Create a symlink in `.cursor/skills/`:
23
-
24
- ```bash
25
- ln -s ../../.agents/skills/<name> .cursor/skills/<name>
26
- ```
27
-
28
- ## Why
29
-
30
- - `.agents/` is the **source of truth** — it is IDE-agnostic and works across different AI coding tools.
31
- - `.cursor/` only contains **symlinks** pointing back to `.agents/`.
32
- - This keeps configuration portable and avoids duplication.
33
-
34
- ## Never
35
-
36
- - Never place original rule/skill content directly in `.cursor/rules/` or `.cursor/skills/`.
37
- - Never create a rule or skill without both the `.agents/` file and the `.cursor/` symlink.