@aidemd-mcp/server 0.2.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.
Files changed (114) hide show
  1. package/.aide/docs/.aide +128 -0
  2. package/.aide/docs/agent-readable-code.md +74 -0
  3. package/.aide/docs/aide-spec.md +201 -0
  4. package/.aide/docs/aide-template.md +110 -0
  5. package/.aide/docs/automated-qa.md +111 -0
  6. package/.aide/docs/cascading-alignment.md +107 -0
  7. package/.aide/docs/index.md +38 -0
  8. package/.aide/docs/plan-aide.md +77 -0
  9. package/.aide/docs/plan.aide +60 -0
  10. package/.aide/docs/progressive-disclosure.md +72 -0
  11. package/.aide/docs/todo-aide.md +77 -0
  12. package/.aide/intent.aide +256 -0
  13. package/.aide/plan.aide +169 -0
  14. package/.aide/todo.aide +47 -0
  15. package/.claude/.aide +246 -0
  16. package/.claude/commands/aide/align.md +15 -0
  17. package/.claude/commands/aide/build.md +17 -0
  18. package/.claude/commands/aide/fix.md +20 -0
  19. package/.claude/commands/aide/init.md +171 -0
  20. package/.claude/commands/aide/plan.md +25 -0
  21. package/.claude/commands/aide/qa.md +25 -0
  22. package/.claude/commands/aide/refactor.md +29 -0
  23. package/.claude/commands/aide/research.md +21 -0
  24. package/.claude/commands/aide/spec.md +24 -0
  25. package/.claude/commands/aide/synthesize.md +20 -0
  26. package/.claude/commands/aide/update-playbook.md +18 -0
  27. package/.claude/commands/aide/upgrade.md +91 -0
  28. package/LICENSE +21 -0
  29. package/README.md +88 -0
  30. package/dist/cli/App/index.d.ts +14 -0
  31. package/dist/cli/App/index.js +282 -0
  32. package/dist/cli/DetailPanel/index.d.ts +24 -0
  33. package/dist/cli/DetailPanel/index.js +57 -0
  34. package/dist/cli/TreePanel/index.d.ts +24 -0
  35. package/dist/cli/TreePanel/index.js +65 -0
  36. package/dist/cli/buildTreeData/index.d.ts +7 -0
  37. package/dist/cli/buildTreeData/index.js +51 -0
  38. package/dist/cli/findPrimaryIntent/index.d.ts +12 -0
  39. package/dist/cli/findPrimaryIntent/index.js +20 -0
  40. package/dist/cli/flattenTree/index.d.ts +16 -0
  41. package/dist/cli/flattenTree/index.js +20 -0
  42. package/dist/cli/index.d.ts +2 -0
  43. package/dist/cli/index.js +15 -0
  44. package/dist/cli/init/index.d.ts +2 -0
  45. package/dist/cli/init/index.js +33 -0
  46. package/dist/cli/init/writeInitCommand/index.d.ts +13 -0
  47. package/dist/cli/init/writeInitCommand/index.js +25 -0
  48. package/dist/cli/init/writeMcpEntry/index.d.ts +12 -0
  49. package/dist/cli/init/writeMcpEntry/index.js +51 -0
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.js +228 -0
  52. package/dist/tools/discover/buildAncestorChain/index.d.ts +17 -0
  53. package/dist/tools/discover/buildAncestorChain/index.js +98 -0
  54. package/dist/tools/discover/buildTree/index.d.ts +9 -0
  55. package/dist/tools/discover/buildTree/index.js +57 -0
  56. package/dist/tools/discover/index.d.ts +20 -0
  57. package/dist/tools/discover/index.js +49 -0
  58. package/dist/tools/init/applySteps/index.d.ts +30 -0
  59. package/dist/tools/init/applySteps/index.js +76 -0
  60. package/dist/tools/init/configureIde/index.d.ts +21 -0
  61. package/dist/tools/init/configureIde/index.js +135 -0
  62. package/dist/tools/init/detectFramework/index.d.ts +11 -0
  63. package/dist/tools/init/detectFramework/index.js +53 -0
  64. package/dist/tools/init/index.d.ts +46 -0
  65. package/dist/tools/init/index.js +99 -0
  66. package/dist/tools/init/initContent/index.d.ts +99 -0
  67. package/dist/tools/init/initContent/index.js +162 -0
  68. package/dist/tools/init/installAgents/index.d.ts +12 -0
  69. package/dist/tools/init/installAgents/index.js +60 -0
  70. package/dist/tools/init/installMethodologyDocs/index.d.ts +14 -0
  71. package/dist/tools/init/installMethodologyDocs/index.js +62 -0
  72. package/dist/tools/init/installSkills/index.d.ts +12 -0
  73. package/dist/tools/init/installSkills/index.js +60 -0
  74. package/dist/tools/init/provisionBrain/index.d.ts +23 -0
  75. package/dist/tools/init/provisionBrain/index.js +239 -0
  76. package/dist/tools/init/resolveBrainHints/index.d.ts +17 -0
  77. package/dist/tools/init/resolveBrainHints/index.js +44 -0
  78. package/dist/tools/init/scaffoldCommands/index.d.ts +38 -0
  79. package/dist/tools/init/scaffoldCommands/index.js +94 -0
  80. package/dist/tools/init/wireMcp/index.d.ts +16 -0
  81. package/dist/tools/init/wireMcp/index.js +72 -0
  82. package/dist/tools/init/writeMethodology/index.d.ts +20 -0
  83. package/dist/tools/init/writeMethodology/index.js +94 -0
  84. package/dist/tools/read/index.d.ts +15 -0
  85. package/dist/tools/read/index.js +79 -0
  86. package/dist/tools/scaffold/index.d.ts +22 -0
  87. package/dist/tools/scaffold/index.js +128 -0
  88. package/dist/tools/upgrade/applyFiles/index.d.ts +33 -0
  89. package/dist/tools/upgrade/applyFiles/index.js +65 -0
  90. package/dist/tools/upgrade/buildVersionsMeta/index.d.ts +20 -0
  91. package/dist/tools/upgrade/buildVersionsMeta/index.js +51 -0
  92. package/dist/tools/upgrade/checkIdeConfig/index.d.ts +24 -0
  93. package/dist/tools/upgrade/checkIdeConfig/index.js +134 -0
  94. package/dist/tools/upgrade/checkMcpConfig/index.d.ts +17 -0
  95. package/dist/tools/upgrade/checkMcpConfig/index.js +81 -0
  96. package/dist/tools/upgrade/compareFile/index.d.ts +12 -0
  97. package/dist/tools/upgrade/compareFile/index.js +24 -0
  98. package/dist/tools/upgrade/index.d.ts +24 -0
  99. package/dist/tools/upgrade/index.js +139 -0
  100. package/dist/tools/upgrade/spliceStub/index.d.ts +13 -0
  101. package/dist/tools/upgrade/spliceStub/index.js +91 -0
  102. package/dist/tools/validate/index.d.ts +18 -0
  103. package/dist/tools/validate/index.js +65 -0
  104. package/dist/types/index.d.ts +277 -0
  105. package/dist/types/index.js +10 -0
  106. package/dist/util/classify/index.d.ts +17 -0
  107. package/dist/util/classify/index.js +134 -0
  108. package/dist/util/parseBody/index.d.ts +7 -0
  109. package/dist/util/parseBody/index.js +43 -0
  110. package/dist/util/parseFrontmatter/index.d.ts +12 -0
  111. package/dist/util/parseFrontmatter/index.js +64 -0
  112. package/dist/util/scan/index.d.ts +7 -0
  113. package/dist/util/scan/index.js +82 -0
  114. package/package.json +59 -0
@@ -0,0 +1,98 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join, relative, dirname } from "node:path";
3
+ import parseFrontmatter from "../../../util/parseFrontmatter/index.js";
4
+ /**
5
+ * Walk from `targetPath` up to `root`, collecting .aide specs at each directory
6
+ * level along the way. Renders them top-down (root first, target-parent last)
7
+ * so agents read broadest context first before drilling into the subtree.
8
+ *
9
+ * The target directory's own specs are excluded — those appear in the subtree
10
+ * section rendered by buildTree. This function is concerned only with the
11
+ * inherited lineage above the target.
12
+ *
13
+ * Root-level spec detection uses the canonical `.aide/intent.aide` path
14
+ * (per aide-spec.md placement rules). At all other directory levels, checks
15
+ * `.aide` first, then falls back to `intent.aide`.
16
+ *
17
+ * Returns `"Ancestor chain:\n" + lines` when at least one ancestor spec is
18
+ * found, or an empty string if the target is the root (no ancestors above it).
19
+ */
20
+ export default async function buildAncestorChain(root, targetPath) {
21
+ // Normalize to absolute paths
22
+ const absRoot = root.replace(/\\/g, "/");
23
+ const absTarget = targetPath.replace(/\\/g, "/");
24
+ // No ancestors when the target IS the root
25
+ if (absTarget === absRoot)
26
+ return "";
27
+ /** Walk upward from target's parent to root, collecting directory levels. */
28
+ const levels = [];
29
+ let current = dirname(absTarget);
30
+ while (true) {
31
+ levels.push(current);
32
+ if (current === absRoot)
33
+ break;
34
+ const parent = dirname(current);
35
+ // Guard against hitting the filesystem root (should not happen in practice)
36
+ if (parent === current)
37
+ break;
38
+ current = parent;
39
+ }
40
+ // levels is ordered target-parent → root; reverse for root-first rendering
41
+ levels.reverse();
42
+ /** Try to read a file, returning null if it does not exist. */
43
+ async function tryRead(filePath) {
44
+ try {
45
+ return await readFile(filePath, "utf-8");
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ /** Resolve which spec file to use at a given directory level, if any. */
52
+ async function resolveSpec(dir) {
53
+ const isRoot = dir === absRoot;
54
+ if (isRoot) {
55
+ // Root-level spec lives at .aide/intent.aide per AIDE placement rules
56
+ const rootSpec = join(dir, ".aide", "intent.aide");
57
+ const content = await tryRead(rootSpec);
58
+ if (content !== null)
59
+ return { specPath: rootSpec, content };
60
+ return null;
61
+ }
62
+ // Non-root: check .aide first, then intent.aide
63
+ const dotAide = join(dir, ".aide");
64
+ const intentAide = join(dir, "intent.aide");
65
+ const dotAideContent = await tryRead(dotAide);
66
+ if (dotAideContent !== null)
67
+ return { specPath: dotAide, content: dotAideContent };
68
+ const intentAideContent = await tryRead(intentAide);
69
+ if (intentAideContent !== null)
70
+ return { specPath: intentAide, content: intentAideContent };
71
+ return null;
72
+ }
73
+ const lines = [];
74
+ for (const dir of levels) {
75
+ const spec = await resolveSpec(dir);
76
+ if (!spec)
77
+ continue;
78
+ const { frontmatter } = parseFrontmatter(spec.content);
79
+ const description = frontmatter?.description;
80
+ const status = frontmatter?.status;
81
+ // Compute a display path relative to the project root
82
+ const rel = relative(absRoot, spec.specPath).replace(/\\/g, "/");
83
+ const displayPath = rel || spec.specPath;
84
+ let line = ` ${displayPath}`;
85
+ if (description) {
86
+ line += ` — ${description}`;
87
+ }
88
+ // Status badge renders whenever set, regardless of whether description is present
89
+ if (status === "aligned" || status === "misaligned") {
90
+ line += ` [${status}]`;
91
+ }
92
+ // If no description and no status, show path alone (no em-dash, no fabrication)
93
+ lines.push(line);
94
+ }
95
+ if (lines.length === 0)
96
+ return "";
97
+ return `Ancestor chain:\n${lines.join("\n")}`;
98
+ }
@@ -0,0 +1,9 @@
1
+ import type { AideFile } from "../../../types/index.js";
2
+ /**
3
+ * Build a progressive disclosure tree string from discovered .aide files.
4
+ * Collapses directories with no .aide files. Each entry shows:
5
+ * - Relative path with tree-drawing characters
6
+ * - File type tag in brackets: [intent], [research], [plan], [todo]
7
+ * - Truncated summary (~80 chars) from first paragraph
8
+ */
9
+ export default function buildTree(files: AideFile[], root: string): string;
@@ -0,0 +1,57 @@
1
+ import { basename } from "node:path";
2
+ /** Sort priority for file types: intent first, then research, plan, then todo. */
3
+ const TYPE_ORDER = { intent: 0, research: 1, plan: 2, todo: 3 };
4
+ /** Group files by their parent directory path. */
5
+ function groupByDir(files) {
6
+ const groups = new Map();
7
+ for (const file of files) {
8
+ const parts = file.relativePath.split("/");
9
+ const dir = parts.slice(0, -1).join("/") || ".";
10
+ const group = groups.get(dir) ?? [];
11
+ group.push(file);
12
+ groups.set(dir, group);
13
+ }
14
+ return groups;
15
+ }
16
+ /** Sort files within a directory by type priority, then by name. */
17
+ function sortFiles(files) {
18
+ return [...files].sort((a, b) => {
19
+ const typeA = TYPE_ORDER[a.type];
20
+ const typeB = TYPE_ORDER[b.type];
21
+ if (typeA !== typeB)
22
+ return typeA - typeB;
23
+ return basename(a.relativePath).localeCompare(basename(b.relativePath));
24
+ });
25
+ }
26
+ /**
27
+ * Build a progressive disclosure tree string from discovered .aide files.
28
+ * Collapses directories with no .aide files. Each entry shows:
29
+ * - Relative path with tree-drawing characters
30
+ * - File type tag in brackets: [intent], [research], [plan], [todo]
31
+ * - Truncated summary (~80 chars) from first paragraph
32
+ */
33
+ export default function buildTree(files, root) {
34
+ if (files.length === 0)
35
+ return "";
36
+ const groups = groupByDir(files);
37
+ const sortedDirs = [...groups.keys()].sort();
38
+ const lines = [];
39
+ for (let d = 0; d < sortedDirs.length; d++) {
40
+ const dir = sortedDirs[d];
41
+ const dirFiles = sortFiles(groups.get(dir));
42
+ // Directory header
43
+ lines.push(`${dir}/`);
44
+ for (let f = 0; f < dirFiles.length; f++) {
45
+ const file = dirFiles[f];
46
+ const isLastFile = f === dirFiles.length - 1;
47
+ const connector = isLastFile ? "└──" : "├──";
48
+ const name = basename(file.relativePath);
49
+ const tag = `[${file.type}]`;
50
+ const summary = file.summary ? ` — ${file.summary}` : "";
51
+ lines.push(` ${connector} ${name} ${tag}${summary}`);
52
+ }
53
+ if (d < sortedDirs.length - 1)
54
+ lines.push("");
55
+ }
56
+ return lines.join("\n");
57
+ }
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ export declare const DiscoverInput: z.ZodObject<{
3
+ path: z.ZodOptional<z.ZodString>;
4
+ }, "strip", z.ZodTypeAny, {
5
+ path?: string | undefined;
6
+ }, {
7
+ path?: string | undefined;
8
+ }>;
9
+ /**
10
+ * Scan for .aide files and return a progressive disclosure tree map.
11
+ *
12
+ * Without a path: shallow scan of the entire project — returns file locations
13
+ * and types only (no summaries, no content reading). This is the project map
14
+ * the agent uses to understand where specs live.
15
+ *
16
+ * With a path: deep scan of that subtree — includes summaries extracted from
17
+ * file content, plus anomaly warnings. This is how the agent drills into the
18
+ * area it's working on.
19
+ */
20
+ export default function discover(root: string, path?: string): Promise<string>;
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ import { basename, join } from "node:path";
3
+ import scan from "../../util/scan/index.js";
4
+ import buildTree from "../../tools/discover/buildTree/index.js";
5
+ import buildAncestorChain from "./buildAncestorChain/index.js";
6
+ import { detectAnomalies } from "../../util/classify/index.js";
7
+ export const DiscoverInput = z.object({
8
+ path: z.string().optional().describe("Subdirectory to scan (defaults to entire project)"),
9
+ });
10
+ /**
11
+ * Scan for .aide files and return a progressive disclosure tree map.
12
+ *
13
+ * Without a path: shallow scan of the entire project — returns file locations
14
+ * and types only (no summaries, no content reading). This is the project map
15
+ * the agent uses to understand where specs live.
16
+ *
17
+ * With a path: deep scan of that subtree — includes summaries extracted from
18
+ * file content, plus anomaly warnings. This is how the agent drills into the
19
+ * area it's working on.
20
+ */
21
+ export default async function discover(root, path) {
22
+ const shallow = !path;
23
+ const result = await scan(root, path, shallow);
24
+ const { files } = result;
25
+ if (files.length === 0) {
26
+ return "No .aide files found." + (path ? ` (searched in ${path})` : "");
27
+ }
28
+ const projectName = basename(root);
29
+ const header = `${projectName} project — ${files.length} spec${files.length === 1 ? "" : "s"} found`;
30
+ const tree = buildTree(files, root);
31
+ // Only run anomaly detection and ancestor chain on deep (scoped) scans
32
+ let ancestorChain = "";
33
+ let warningBlock = "";
34
+ if (!shallow) {
35
+ ancestorChain = await buildAncestorChain(root, join(root, path));
36
+ const anomalies = await detectAnomalies(files, root);
37
+ if (anomalies.length > 0) {
38
+ const lines = anomalies.map((w) => ` ${w.path} — ${w.message}`);
39
+ warningBlock = `\n\n⚠ Warnings:\n${lines.join("\n")}`;
40
+ }
41
+ }
42
+ // Build output: header + optional ancestor chain + tree + optional warnings
43
+ // Each block separated by a blank line when present
44
+ const parts = [header];
45
+ if (ancestorChain)
46
+ parts.push(ancestorChain);
47
+ parts.push(tree);
48
+ return parts.join("\n\n") + warningBlock;
49
+ }
@@ -0,0 +1,30 @@
1
+ import type { InitStep } from "../../../types/index.js";
2
+ /**
3
+ * Apply mode writer — turns a plan into disk state and returns the manifest
4
+ * the agent reports to the user.
5
+ *
6
+ * For each step:
7
+ * - `"would-create"` file steps (no `prescription` field, not a brain vault step):
8
+ * creates parent directories and writes `content` to `filePath`. Returns the
9
+ * step with `status` changed to `"created"` and `content` removed.
10
+ * - `"would-create"` brain vault steps (category `"brain"`) — two sub-types:
11
+ * - **Directory step** (`filePath` has no extension): parses `content` as a
12
+ * JSON array of directory names, creates each under `filePath` with
13
+ * `recursive: true`. Returns with `status: "created"` and `content` removed.
14
+ * - **File step** (`filePath` has a file extension, e.g. `.md`): creates the
15
+ * parent directory and writes `content` to `filePath`. Used for content
16
+ * templates such as the playbook hub and vault CLAUDE.md. Returns with
17
+ * `status: "created"` and `content` removed.
18
+ * - `"would-create"` steps with a `prescription` field (MCP steps): passed
19
+ * through unchanged — the agent merges prescriptions itself. Never written
20
+ * by this helper.
21
+ * - `"would-create"` IDE VS Code steps (name contains "VS Code"): passed
22
+ * through unchanged — requires the external `code` CLI. The agent executes
23
+ * the extension install command.
24
+ * - `"exists"`, `"would-skip"` steps: passed through unchanged.
25
+ * - `"created"` steps (already applied): passed through unchanged (idempotent).
26
+ *
27
+ * The `prescription` field is never stripped — MCP steps keep their
28
+ * prescription so the agent can perform the merge.
29
+ */
30
+ export default function applySteps(steps: InitStep[]): Promise<InitStep[]>;
@@ -0,0 +1,76 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname, extname } from "node:path";
3
+ /**
4
+ * Apply mode writer — turns a plan into disk state and returns the manifest
5
+ * the agent reports to the user.
6
+ *
7
+ * For each step:
8
+ * - `"would-create"` file steps (no `prescription` field, not a brain vault step):
9
+ * creates parent directories and writes `content` to `filePath`. Returns the
10
+ * step with `status` changed to `"created"` and `content` removed.
11
+ * - `"would-create"` brain vault steps (category `"brain"`) — two sub-types:
12
+ * - **Directory step** (`filePath` has no extension): parses `content` as a
13
+ * JSON array of directory names, creates each under `filePath` with
14
+ * `recursive: true`. Returns with `status: "created"` and `content` removed.
15
+ * - **File step** (`filePath` has a file extension, e.g. `.md`): creates the
16
+ * parent directory and writes `content` to `filePath`. Used for content
17
+ * templates such as the playbook hub and vault CLAUDE.md. Returns with
18
+ * `status: "created"` and `content` removed.
19
+ * - `"would-create"` steps with a `prescription` field (MCP steps): passed
20
+ * through unchanged — the agent merges prescriptions itself. Never written
21
+ * by this helper.
22
+ * - `"would-create"` IDE VS Code steps (name contains "VS Code"): passed
23
+ * through unchanged — requires the external `code` CLI. The agent executes
24
+ * the extension install command.
25
+ * - `"exists"`, `"would-skip"` steps: passed through unchanged.
26
+ * - `"created"` steps (already applied): passed through unchanged (idempotent).
27
+ *
28
+ * The `prescription` field is never stripped — MCP steps keep their
29
+ * prescription so the agent can perform the merge.
30
+ */
31
+ export default async function applySteps(steps) {
32
+ return Promise.all(steps.map(applyStep));
33
+ }
34
+ async function applyStep(step) {
35
+ // Only act on would-create steps that haven't been applied yet
36
+ if (step.status !== "would-create") {
37
+ return step;
38
+ }
39
+ // MCP steps carry a prescription — the agent merges them; never written here
40
+ if (step.prescription !== undefined) {
41
+ return step;
42
+ }
43
+ // IDE VS Code steps require the external `code` CLI — pass through with
44
+ // instructions so the agent knows what command to run
45
+ if (step.category === "ide" && step.name.includes("VS Code")) {
46
+ return { ...step, instructions: `code --install-extension ${step.filePath}` };
47
+ }
48
+ // Brain step — two sub-types distinguished by whether filePath has an extension
49
+ if (step.category === "brain") {
50
+ if (!step.content) {
51
+ // No content means placeholder (filePath is empty string) — pass through
52
+ return step;
53
+ }
54
+ if (extname(step.filePath)) {
55
+ // File step — content is a template destined for a specific file path
56
+ await mkdir(dirname(step.filePath), { recursive: true });
57
+ await writeFile(step.filePath, step.content, "utf-8");
58
+ }
59
+ else {
60
+ // Directory step — content is a JSON array of subdirectory names to create
61
+ const dirs = JSON.parse(step.content);
62
+ await Promise.all(dirs.map((dir) => mkdir(`${step.filePath}/${dir}`, { recursive: true })));
63
+ }
64
+ const { content: _content, ...rest } = step;
65
+ return { ...rest, status: "created" };
66
+ }
67
+ // Regular file step — write content to filePath
68
+ if (step.content !== undefined) {
69
+ await mkdir(dirname(step.filePath), { recursive: true });
70
+ await writeFile(step.filePath, step.content, "utf-8");
71
+ const { content: _content, ...rest } = step;
72
+ return { ...rest, status: "created" };
73
+ }
74
+ // No content and not a special case — pass through unchanged
75
+ return step;
76
+ }
@@ -0,0 +1,21 @@
1
+ import type { InitStep } from "../../../types/index.js";
2
+ /**
3
+ * Inspect Zed settings and return a planning step for .aide file association.
4
+ *
5
+ * Returns `exists` when the `*.aide` entry is already in `.zed/settings.json`.
6
+ * Returns `would-create` with the full patched settings JSON as `content`
7
+ * when the entry is absent.
8
+ *
9
+ * This function never writes to disk — it is a planner only.
10
+ */
11
+ export declare function configureZed(projectRoot: string): Promise<InitStep>;
12
+ /**
13
+ * Check VS Code extension installation status and return a planning step.
14
+ *
15
+ * Returns `exists` when the aide-markdown extension is already installed.
16
+ * Returns `would-skip` when `code` CLI is unavailable or the .vsix is missing
17
+ * (the agent will install when confirmed).
18
+ *
19
+ * This function never writes to disk — it is a planner only.
20
+ */
21
+ export declare function configureVscode(extensionsDir: string): Promise<InitStep>;
@@ -0,0 +1,135 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { execFile } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ const execFileAsync = promisify(execFile);
6
+ /** Read a file, returning empty string if it doesn't exist. */
7
+ async function safeReadFile(path) {
8
+ try {
9
+ return await readFile(path, "utf-8");
10
+ }
11
+ catch {
12
+ return "";
13
+ }
14
+ }
15
+ /**
16
+ * Inspect Zed settings and return a planning step for .aide file association.
17
+ *
18
+ * Returns `exists` when the `*.aide` entry is already in `.zed/settings.json`.
19
+ * Returns `would-create` with the full patched settings JSON as `content`
20
+ * when the entry is absent.
21
+ *
22
+ * This function never writes to disk — it is a planner only.
23
+ */
24
+ export async function configureZed(projectRoot) {
25
+ const settingsPath = join(projectRoot, ".zed", "settings.json");
26
+ const filePath = settingsPath;
27
+ const existing = await safeReadFile(settingsPath);
28
+ if (existing) {
29
+ try {
30
+ const settings = JSON.parse(existing);
31
+ const fileTypes = settings.file_types || {};
32
+ const mdTypes = fileTypes.Markdown || [];
33
+ if (mdTypes.includes("*.aide")) {
34
+ return {
35
+ name: "Zed config",
36
+ status: "exists",
37
+ category: "ide",
38
+ filePath,
39
+ };
40
+ }
41
+ mdTypes.push("*.aide");
42
+ fileTypes.Markdown = mdTypes;
43
+ settings.file_types = fileTypes;
44
+ return {
45
+ name: "Zed config",
46
+ status: "would-create",
47
+ category: "ide",
48
+ filePath,
49
+ content: JSON.stringify(settings, null, 2) + "\n",
50
+ };
51
+ }
52
+ catch {
53
+ return {
54
+ name: "Zed config",
55
+ status: "would-skip",
56
+ category: "ide",
57
+ filePath,
58
+ };
59
+ }
60
+ }
61
+ const settings = { file_types: { Markdown: ["*.aide"] } };
62
+ return {
63
+ name: "Zed config",
64
+ status: "would-create",
65
+ category: "ide",
66
+ filePath,
67
+ content: JSON.stringify(settings, null, 2) + "\n",
68
+ };
69
+ }
70
+ /**
71
+ * Check VS Code extension installation status and return a planning step.
72
+ *
73
+ * Returns `exists` when the aide-markdown extension is already installed.
74
+ * Returns `would-skip` when `code` CLI is unavailable or the .vsix is missing
75
+ * (the agent will install when confirmed).
76
+ *
77
+ * This function never writes to disk — it is a planner only.
78
+ */
79
+ export async function configureVscode(extensionsDir) {
80
+ const vsixPath = resolve(extensionsDir, "aide-markdown-0.0.1.vsix");
81
+ // filePath points to the vsix — not a file the agent writes, but a meaningful
82
+ // path for the agent to display when reporting extension installation.
83
+ const filePath = vsixPath;
84
+ // Check if `code` CLI is on PATH
85
+ try {
86
+ await execFileAsync("code", ["--version"]);
87
+ }
88
+ catch {
89
+ return {
90
+ name: "VS Code extension",
91
+ status: "would-skip",
92
+ category: "ide",
93
+ filePath,
94
+ };
95
+ }
96
+ // Check if already installed
97
+ try {
98
+ const { stdout } = await execFileAsync("code", ["--list-extensions"]);
99
+ if (stdout.toLowerCase().includes("aide-markdown")) {
100
+ return {
101
+ name: "VS Code extension",
102
+ status: "exists",
103
+ category: "ide",
104
+ filePath,
105
+ };
106
+ }
107
+ }
108
+ catch {
109
+ return {
110
+ name: "VS Code extension",
111
+ status: "would-skip",
112
+ category: "ide",
113
+ filePath,
114
+ };
115
+ }
116
+ // Check if the .vsix is present
117
+ try {
118
+ await readFile(vsixPath);
119
+ }
120
+ catch {
121
+ return {
122
+ name: "VS Code extension",
123
+ status: "would-skip",
124
+ category: "ide",
125
+ filePath,
126
+ };
127
+ }
128
+ // The agent will install when the user confirms — report as would-create
129
+ return {
130
+ name: "VS Code extension",
131
+ status: "would-create",
132
+ category: "ide",
133
+ filePath,
134
+ };
135
+ }
@@ -0,0 +1,11 @@
1
+ import type { FrameworkType, FrameworkConfig } from "../../../types/index.js";
2
+ /**
3
+ * Detect the agent framework in use, or return config for a specified framework.
4
+ * Checks for framework-specific marker files/directories. Defaults to Claude Code
5
+ * if nothing is detected.
6
+ *
7
+ * Brain vault resolution is NOT performed here — it is a separate concern handled
8
+ * by `resolveBrainHints`, which the init orchestrator calls independently. This
9
+ * keeps framework detection focused on framework-related config only.
10
+ */
11
+ export default function detectFramework(root: string, framework?: FrameworkType): Promise<FrameworkConfig>;
@@ -0,0 +1,53 @@
1
+ import { access } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ /**
4
+ * Per-framework path resolution for aide_init. The `docHubDir` field is
5
+ * uniform across frameworks on purpose: the host-side doc hub is a
6
+ * framework-agnostic surface (the agent crawls it via relative links, not
7
+ * via framework-specific command wiring), so there is no reason to
8
+ * diverge. Keeping the value uniform also makes it trivial to change the
9
+ * hub location across every framework in a single edit.
10
+ */
11
+ const FRAMEWORK_CONFIGS = {
12
+ claude: { configPath: "CLAUDE.md", commandDir: ".claude/commands", mcpConfigPath: ".mcp.json", docHubDir: ".aide/docs", agentDir: ".claude/agents", skillDir: ".claude/skills" },
13
+ cursor: { configPath: ".cursorrules", commandDir: ".cursor/commands", mcpConfigPath: ".cursor/mcp.json", docHubDir: ".aide/docs", agentDir: ".cursor/agents", skillDir: ".cursor/skills" },
14
+ windsurf: { configPath: ".windsurfrules", commandDir: ".windsurf/commands", mcpConfigPath: ".windsurf/mcp.json", docHubDir: ".aide/docs", agentDir: ".windsurf/agents", skillDir: ".windsurf/skills" },
15
+ copilot: { configPath: ".github/copilot-instructions.md", commandDir: ".github/commands", mcpConfigPath: ".mcp.json", docHubDir: ".aide/docs", agentDir: ".github/agents", skillDir: ".github/skills" },
16
+ };
17
+ const DETECTION_SIGNALS = [
18
+ { framework: "claude", paths: [".claude", "CLAUDE.md"] },
19
+ { framework: "cursor", paths: [".cursor", ".cursorrules"] },
20
+ { framework: "windsurf", paths: [".windsurf", ".windsurfrules"] },
21
+ { framework: "copilot", paths: [".github/copilot-instructions.md"] },
22
+ ];
23
+ /** Check if a path exists. */
24
+ async function exists(path) {
25
+ try {
26
+ await access(path);
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /**
34
+ * Detect the agent framework in use, or return config for a specified framework.
35
+ * Checks for framework-specific marker files/directories. Defaults to Claude Code
36
+ * if nothing is detected.
37
+ *
38
+ * Brain vault resolution is NOT performed here — it is a separate concern handled
39
+ * by `resolveBrainHints`, which the init orchestrator calls independently. This
40
+ * keeps framework detection focused on framework-related config only.
41
+ */
42
+ export default async function detectFramework(root, framework) {
43
+ if (framework)
44
+ return { framework, ...FRAMEWORK_CONFIGS[framework] };
45
+ for (const signal of DETECTION_SIGNALS) {
46
+ for (const path of signal.paths) {
47
+ if (await exists(join(root, path))) {
48
+ return { framework: signal.framework, ...FRAMEWORK_CONFIGS[signal.framework] };
49
+ }
50
+ }
51
+ }
52
+ return { framework: "claude", ...FRAMEWORK_CONFIGS.claude };
53
+ }
@@ -0,0 +1,46 @@
1
+ import { z } from "zod";
2
+ import type { FrameworkType, InitResult } from "../../types/index.js";
3
+ /**
4
+ * Input schema for aide_init.
5
+ *
6
+ * `brainPath` and `skipIde` are removed: the brain path is always resolved
7
+ * through agent-user conversation (not a silent tool parameter), and IDE
8
+ * configuration is presented per-category during the agent interview.
9
+ * `framework` remains so the agent can re-call after user confirms or
10
+ * overrides framework detection.
11
+ */
12
+ export declare const InitInput: z.ZodObject<{
13
+ framework: z.ZodOptional<z.ZodEnum<["claude", "cursor", "windsurf", "copilot"]>>;
14
+ path: z.ZodOptional<z.ZodString>;
15
+ category: z.ZodOptional<z.ZodEnum<["framework", "methodology", "commands", "agents", "skills", "mcp", "brain", "ide"]>>;
16
+ brainPath: z.ZodOptional<z.ZodString>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ framework?: "claude" | "cursor" | "windsurf" | "copilot" | undefined;
19
+ path?: string | undefined;
20
+ category?: "framework" | "methodology" | "commands" | "agents" | "skills" | "mcp" | "brain" | "ide" | undefined;
21
+ brainPath?: string | undefined;
22
+ }, {
23
+ framework?: "claude" | "cursor" | "windsurf" | "copilot" | undefined;
24
+ path?: string | undefined;
25
+ category?: "framework" | "methodology" | "commands" | "agents" | "skills" | "mcp" | "brain" | "ide" | undefined;
26
+ brainPath?: string | undefined;
27
+ }>;
28
+ /**
29
+ * Bootstrap the AIDE development environment into a project.
30
+ *
31
+ * Returns structured JSON (`InitResult`) — no prose, no formatting. The
32
+ * calling agent interprets the result, walks the user through each category,
33
+ * and applies the steps the user confirms.
34
+ *
35
+ * Each step is idempotent: re-running on a fully initialized project returns
36
+ * all steps as `exists`. Brain hints are discovered from env var, sibling
37
+ * path, and conventional path — returned as candidates the agent presents
38
+ * to the user; the agent confirms the path before any vault work is done.
39
+ *
40
+ * @param root - Server working directory (from --root CLI arg or cwd).
41
+ * @param framework - Optional framework override.
42
+ * @param path - Optional project root override (absolute or relative to root).
43
+ * @param brainPath - User-confirmed vault path. When provided, forwarded to
44
+ * provisionBrain instead of using the first discovered hint.
45
+ */
46
+ export default function init(root: string, framework?: FrameworkType, path?: string, brainPath?: string): Promise<InitResult>;