@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.
- package/.aide/docs/.aide +128 -0
- package/.aide/docs/agent-readable-code.md +74 -0
- package/.aide/docs/aide-spec.md +201 -0
- package/.aide/docs/aide-template.md +110 -0
- package/.aide/docs/automated-qa.md +111 -0
- package/.aide/docs/cascading-alignment.md +107 -0
- package/.aide/docs/index.md +38 -0
- package/.aide/docs/plan-aide.md +77 -0
- package/.aide/docs/plan.aide +60 -0
- package/.aide/docs/progressive-disclosure.md +72 -0
- package/.aide/docs/todo-aide.md +77 -0
- package/.aide/intent.aide +256 -0
- package/.aide/plan.aide +169 -0
- package/.aide/todo.aide +47 -0
- package/.claude/.aide +246 -0
- package/.claude/commands/aide/align.md +15 -0
- package/.claude/commands/aide/build.md +17 -0
- package/.claude/commands/aide/fix.md +20 -0
- package/.claude/commands/aide/init.md +171 -0
- package/.claude/commands/aide/plan.md +25 -0
- package/.claude/commands/aide/qa.md +25 -0
- package/.claude/commands/aide/refactor.md +29 -0
- package/.claude/commands/aide/research.md +21 -0
- package/.claude/commands/aide/spec.md +24 -0
- package/.claude/commands/aide/synthesize.md +20 -0
- package/.claude/commands/aide/update-playbook.md +18 -0
- package/.claude/commands/aide/upgrade.md +91 -0
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/cli/App/index.d.ts +14 -0
- package/dist/cli/App/index.js +282 -0
- package/dist/cli/DetailPanel/index.d.ts +24 -0
- package/dist/cli/DetailPanel/index.js +57 -0
- package/dist/cli/TreePanel/index.d.ts +24 -0
- package/dist/cli/TreePanel/index.js +65 -0
- package/dist/cli/buildTreeData/index.d.ts +7 -0
- package/dist/cli/buildTreeData/index.js +51 -0
- package/dist/cli/findPrimaryIntent/index.d.ts +12 -0
- package/dist/cli/findPrimaryIntent/index.js +20 -0
- package/dist/cli/flattenTree/index.d.ts +16 -0
- package/dist/cli/flattenTree/index.js +20 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/init/index.d.ts +2 -0
- package/dist/cli/init/index.js +33 -0
- package/dist/cli/init/writeInitCommand/index.d.ts +13 -0
- package/dist/cli/init/writeInitCommand/index.js +25 -0
- package/dist/cli/init/writeMcpEntry/index.d.ts +12 -0
- package/dist/cli/init/writeMcpEntry/index.js +51 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +228 -0
- package/dist/tools/discover/buildAncestorChain/index.d.ts +17 -0
- package/dist/tools/discover/buildAncestorChain/index.js +98 -0
- package/dist/tools/discover/buildTree/index.d.ts +9 -0
- package/dist/tools/discover/buildTree/index.js +57 -0
- package/dist/tools/discover/index.d.ts +20 -0
- package/dist/tools/discover/index.js +49 -0
- package/dist/tools/init/applySteps/index.d.ts +30 -0
- package/dist/tools/init/applySteps/index.js +76 -0
- package/dist/tools/init/configureIde/index.d.ts +21 -0
- package/dist/tools/init/configureIde/index.js +135 -0
- package/dist/tools/init/detectFramework/index.d.ts +11 -0
- package/dist/tools/init/detectFramework/index.js +53 -0
- package/dist/tools/init/index.d.ts +46 -0
- package/dist/tools/init/index.js +99 -0
- package/dist/tools/init/initContent/index.d.ts +99 -0
- package/dist/tools/init/initContent/index.js +162 -0
- package/dist/tools/init/installAgents/index.d.ts +12 -0
- package/dist/tools/init/installAgents/index.js +60 -0
- package/dist/tools/init/installMethodologyDocs/index.d.ts +14 -0
- package/dist/tools/init/installMethodologyDocs/index.js +62 -0
- package/dist/tools/init/installSkills/index.d.ts +12 -0
- package/dist/tools/init/installSkills/index.js +60 -0
- package/dist/tools/init/provisionBrain/index.d.ts +23 -0
- package/dist/tools/init/provisionBrain/index.js +239 -0
- package/dist/tools/init/resolveBrainHints/index.d.ts +17 -0
- package/dist/tools/init/resolveBrainHints/index.js +44 -0
- package/dist/tools/init/scaffoldCommands/index.d.ts +38 -0
- package/dist/tools/init/scaffoldCommands/index.js +94 -0
- package/dist/tools/init/wireMcp/index.d.ts +16 -0
- package/dist/tools/init/wireMcp/index.js +72 -0
- package/dist/tools/init/writeMethodology/index.d.ts +20 -0
- package/dist/tools/init/writeMethodology/index.js +94 -0
- package/dist/tools/read/index.d.ts +15 -0
- package/dist/tools/read/index.js +79 -0
- package/dist/tools/scaffold/index.d.ts +22 -0
- package/dist/tools/scaffold/index.js +128 -0
- package/dist/tools/upgrade/applyFiles/index.d.ts +33 -0
- package/dist/tools/upgrade/applyFiles/index.js +65 -0
- package/dist/tools/upgrade/buildVersionsMeta/index.d.ts +20 -0
- package/dist/tools/upgrade/buildVersionsMeta/index.js +51 -0
- package/dist/tools/upgrade/checkIdeConfig/index.d.ts +24 -0
- package/dist/tools/upgrade/checkIdeConfig/index.js +134 -0
- package/dist/tools/upgrade/checkMcpConfig/index.d.ts +17 -0
- package/dist/tools/upgrade/checkMcpConfig/index.js +81 -0
- package/dist/tools/upgrade/compareFile/index.d.ts +12 -0
- package/dist/tools/upgrade/compareFile/index.js +24 -0
- package/dist/tools/upgrade/index.d.ts +24 -0
- package/dist/tools/upgrade/index.js +139 -0
- package/dist/tools/upgrade/spliceStub/index.d.ts +13 -0
- package/dist/tools/upgrade/spliceStub/index.js +91 -0
- package/dist/tools/validate/index.d.ts +18 -0
- package/dist/tools/validate/index.js +65 -0
- package/dist/types/index.d.ts +277 -0
- package/dist/types/index.js +10 -0
- package/dist/util/classify/index.d.ts +17 -0
- package/dist/util/classify/index.js +134 -0
- package/dist/util/parseBody/index.d.ts +7 -0
- package/dist/util/parseBody/index.js +43 -0
- package/dist/util/parseFrontmatter/index.d.ts +12 -0
- package/dist/util/parseFrontmatter/index.js +64 -0
- package/dist/util/scan/index.d.ts +7 -0
- package/dist/util/scan/index.js +82 -0
- 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>;
|