opencontext-mcp 1.2.1 → 1.3.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 (73) hide show
  1. package/README.md +38 -12
  2. package/dist/cli/index.d.ts +25 -0
  3. package/dist/cli/index.js +97 -0
  4. package/dist/cli/index.js.map +1 -0
  5. package/dist/{config.d.ts → config/index.d.ts} +1 -1
  6. package/dist/config/index.js +62 -0
  7. package/dist/config/index.js.map +1 -0
  8. package/dist/index.js +52 -9
  9. package/dist/index.js.map +1 -1
  10. package/dist/init/constants.d.ts +6 -0
  11. package/dist/init/constants.js +17 -0
  12. package/dist/init/constants.js.map +1 -0
  13. package/dist/init/display.d.ts +9 -0
  14. package/dist/init/display.js +28 -0
  15. package/dist/init/display.js.map +1 -0
  16. package/dist/init/files.d.ts +12 -0
  17. package/dist/init/files.js +39 -0
  18. package/dist/init/files.js.map +1 -0
  19. package/dist/init/index.d.ts +6 -0
  20. package/dist/init/index.js +157 -0
  21. package/dist/init/index.js.map +1 -0
  22. package/dist/init/inspect.d.ts +21 -0
  23. package/dist/init/inspect.js +57 -0
  24. package/dist/init/inspect.js.map +1 -0
  25. package/dist/init/integrations/claude.d.ts +17 -0
  26. package/dist/init/integrations/claude.js +94 -0
  27. package/dist/init/integrations/claude.js.map +1 -0
  28. package/dist/init/integrations/opencode.d.ts +20 -0
  29. package/dist/init/integrations/opencode.js +108 -0
  30. package/dist/init/integrations/opencode.js.map +1 -0
  31. package/dist/init/integrations/workflow-files.d.ts +23 -0
  32. package/dist/init/integrations/workflow-files.js +102 -0
  33. package/dist/init/integrations/workflow-files.js.map +1 -0
  34. package/dist/init/prompts.d.ts +9 -0
  35. package/dist/init/prompts.js +17 -0
  36. package/dist/init/prompts.js.map +1 -0
  37. package/dist/server/http.d.ts +21 -0
  38. package/dist/server/http.js +63 -0
  39. package/dist/server/http.js.map +1 -0
  40. package/dist/{server.d.ts → server/index.d.ts} +3 -1
  41. package/dist/{server.js → server/index.js} +7 -4
  42. package/dist/server/index.js.map +1 -0
  43. package/dist/shared/constants.d.ts +27 -0
  44. package/dist/shared/constants.js +26 -0
  45. package/dist/shared/constants.js.map +1 -0
  46. package/dist/shared/errors.d.ts +17 -0
  47. package/dist/shared/errors.js +28 -0
  48. package/dist/shared/errors.js.map +1 -0
  49. package/dist/shared/types.d.ts +29 -0
  50. package/dist/shared/types.js +17 -0
  51. package/dist/shared/types.js.map +1 -0
  52. package/dist/{context-store.d.ts → store/context-store.d.ts} +2 -16
  53. package/dist/store/context-store.js +147 -0
  54. package/dist/store/context-store.js.map +1 -0
  55. package/dist/store/index-builder.d.ts +32 -0
  56. package/dist/store/index-builder.js +162 -0
  57. package/dist/store/index-builder.js.map +1 -0
  58. package/dist/validation/index.d.ts +16 -0
  59. package/dist/validation/index.js +39 -0
  60. package/dist/validation/index.js.map +1 -0
  61. package/dist/{validation.d.ts → validation/write-guard.d.ts} +0 -16
  62. package/dist/{validation.js → validation/write-guard.js} +2 -38
  63. package/dist/validation/write-guard.js.map +1 -0
  64. package/package.json +2 -2
  65. package/dist/config.js +0 -145
  66. package/dist/config.js.map +0 -1
  67. package/dist/context-store.js +0 -296
  68. package/dist/context-store.js.map +0 -1
  69. package/dist/server.js.map +0 -1
  70. package/dist/types.d.ts +0 -56
  71. package/dist/types.js +0 -63
  72. package/dist/types.js.map +0 -1
  73. package/dist/validation.js.map +0 -1
@@ -0,0 +1,157 @@
1
+ import { mkdir, readdir, unlink } from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import * as readline from "node:readline/promises";
4
+ import { stdin, stdout } from "node:process";
5
+ import { inspectTargetDir } from "./inspect.js";
6
+ import { promptForConfirm } from "./prompts.js";
7
+ import { atomicWrite, validateConfigFile } from "./files.js";
8
+ import { printStep, printResult } from "./display.js";
9
+ import { CONFIG_FILENAME, CONTEXT_DIR_NAME, CONFIG_CONTENT } from "./constants.js";
10
+ import { upsertOpenCodeConfig, OPENCODE_CONFIG_FILENAME, } from "./integrations/opencode.js";
11
+ import { upsertClaudeConfig, CLAUDE_MCP_FILENAME, } from "./integrations/claude.js";
12
+ import { upsertWorkflowFile, AGENTS_FILENAME, CLAUDE_FILENAME, } from "./integrations/workflow-files.js";
13
+ /**
14
+ * Runs the init command.
15
+ * Inspects the current directory, prompts for configuration, and generates files.
16
+ * Existing configs are extended, never overwritten.
17
+ */
18
+ export async function runInit() {
19
+ const targetDir = path.resolve(".");
20
+ let opencode = true;
21
+ let claude = true;
22
+ // Step 1: Inspect
23
+ printStep("Inspecting directory", false);
24
+ const inspection = await inspectTargetDir(targetDir);
25
+ printStep("Inspecting directory");
26
+ // Show environment info
27
+ process.stderr.write(`\n Node.js ${inspection.nodeVersion}`);
28
+ if (inspection.packageManager) {
29
+ process.stderr.write(` | ${inspection.packageManager}`);
30
+ }
31
+ process.stderr.write("\n\n");
32
+ // Step 2: Interactive selection
33
+ const rl = readline.createInterface({ input: stdin, output: stdout });
34
+ try {
35
+ if (inspection.isNonEmpty) {
36
+ const proceed = await promptForConfirm(rl, " Directory is not empty. Continue anyway?");
37
+ if (!proceed) {
38
+ process.stderr.write(" Aborted.\n");
39
+ return;
40
+ }
41
+ }
42
+ opencode = await promptForConfirm(rl, " Enable OpenCode integration?", true);
43
+ if (!opencode) {
44
+ printResult("skipped", "OpenCode integration skipped");
45
+ }
46
+ claude = await promptForConfirm(rl, " Enable Claude Code integration?", true);
47
+ if (!claude) {
48
+ printResult("skipped", "Claude Code integration skipped");
49
+ }
50
+ }
51
+ finally {
52
+ rl.close();
53
+ }
54
+ // Step 3: Create directories + files
55
+ const createdFiles = [];
56
+ const createdDirs = [];
57
+ try {
58
+ // Ensure target directory exists
59
+ await mkdir(targetDir, { recursive: true });
60
+ // Create .opencontext/ directory
61
+ const contextDirPath = path.join(targetDir, CONTEXT_DIR_NAME);
62
+ await mkdir(contextDirPath, { recursive: true });
63
+ createdDirs.push(contextDirPath);
64
+ // Step 4: OpenContext config — create when missing, preserve otherwise
65
+ const configPath = path.join(targetDir, CONFIG_FILENAME);
66
+ if (!inspection.hasConfigFile) {
67
+ await atomicWrite(configPath, CONFIG_CONTENT);
68
+ createdFiles.push(CONFIG_FILENAME);
69
+ printResult("created", `${CONFIG_FILENAME} created.`);
70
+ }
71
+ else {
72
+ const valid = await validateConfigFile(configPath);
73
+ if (valid) {
74
+ printResult("unchanged", `${CONFIG_FILENAME} already exists and is valid — preserved.`);
75
+ }
76
+ else {
77
+ printResult("invalid", `${CONFIG_FILENAME} already exists but is malformed — left untouched.`);
78
+ }
79
+ }
80
+ // Step 5: AI client configs — always extended, never overwritten
81
+ if (opencode) {
82
+ const result = await upsertOpenCodeConfig(targetDir);
83
+ printResult(result.status, result.message);
84
+ if (result.status === "created") {
85
+ createdFiles.push(OPENCODE_CONFIG_FILENAME);
86
+ }
87
+ }
88
+ else {
89
+ printResult("skipped", "OpenCode config skipped.");
90
+ }
91
+ if (claude) {
92
+ const result = await upsertClaudeConfig(targetDir);
93
+ printResult(result.status, result.message);
94
+ if (result.status === "created") {
95
+ createdFiles.push(CLAUDE_MCP_FILENAME);
96
+ }
97
+ }
98
+ else {
99
+ printResult("skipped", "Claude Code config skipped.");
100
+ }
101
+ // Step 6: Workflow files — AGENTS.md for OpenCode, CLAUDE.md for Claude Code
102
+ if (opencode) {
103
+ const result = await upsertWorkflowFile(targetDir, AGENTS_FILENAME);
104
+ printResult(result.status, result.message);
105
+ if (result.status === "created") {
106
+ createdFiles.push(AGENTS_FILENAME);
107
+ }
108
+ }
109
+ if (claude) {
110
+ const result = await upsertWorkflowFile(targetDir, CLAUDE_FILENAME);
111
+ printResult(result.status, result.message);
112
+ if (result.status === "created") {
113
+ createdFiles.push(CLAUDE_FILENAME);
114
+ }
115
+ }
116
+ // Done
117
+ process.stderr.write("\n");
118
+ // Summary
119
+ process.stderr.write(" Created:\n");
120
+ for (const file of createdFiles) {
121
+ process.stderr.write(` ${file}\n`);
122
+ }
123
+ for (const dir of createdDirs) {
124
+ process.stderr.write(` ${CONTEXT_DIR_NAME}/\n`);
125
+ }
126
+ process.stderr.write("\n Next steps:\n");
127
+ process.stderr.write(` cd ${path.relative(process.cwd(), targetDir) || "."}\n`);
128
+ process.stderr.write(" Restart OpenCode / Claude Code to load the OpenContext MCP server.\n");
129
+ }
130
+ catch (error) {
131
+ // Rollback: remove created files and directories
132
+ process.stderr.write("\n Generation failed. Cleaning up...\n");
133
+ for (const file of createdFiles) {
134
+ try {
135
+ await unlink(path.join(targetDir, file));
136
+ }
137
+ catch {
138
+ // Ignore cleanup errors
139
+ }
140
+ }
141
+ // Only remove context dir if we created it and it's empty
142
+ for (const dir of createdDirs) {
143
+ try {
144
+ const entries = await readdir(dir);
145
+ if (entries.length === 0) {
146
+ const { rmdir } = await import("node:fs/promises");
147
+ await rmdir(dir);
148
+ }
149
+ }
150
+ catch {
151
+ // Ignore cleanup errors
152
+ }
153
+ }
154
+ throw error;
155
+ }
156
+ }
157
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/init/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /** Result of inspecting the target directory. */
2
+ export interface InspectionResult {
3
+ /** Absolute path to the target directory. */
4
+ targetDir: string;
5
+ /** Whether .opencontext.json already exists. */
6
+ hasConfigFile: boolean;
7
+ /** Whether .opencontext/ directory already exists. */
8
+ hasContextDir: boolean;
9
+ /** Whether the directory is non-empty (excluding dotfiles). */
10
+ isNonEmpty: boolean;
11
+ /** Detected package manager, if any. */
12
+ packageManager: string | null;
13
+ /** Node.js version string. */
14
+ nodeVersion: string;
15
+ }
16
+ /**
17
+ * Inspects the target directory for existing configuration and environment.
18
+ * @param targetDir - Absolute path to inspect
19
+ * @returns InspectionResult with findings
20
+ */
21
+ export declare function inspectTargetDir(targetDir: string): Promise<InspectionResult>;
@@ -0,0 +1,57 @@
1
+ import { readdir } from "node:fs/promises";
2
+ import { CONFIG_FILENAME, CONTEXT_DIR_NAME } from "./constants.js";
3
+ /**
4
+ * Inspects the target directory for existing configuration and environment.
5
+ * @param targetDir - Absolute path to inspect
6
+ * @returns InspectionResult with findings
7
+ */
8
+ export async function inspectTargetDir(targetDir) {
9
+ let entries = [];
10
+ try {
11
+ entries = await readdir(targetDir);
12
+ }
13
+ catch (error) {
14
+ if (error.code === "ENOENT") {
15
+ // Directory doesn't exist — that's fine, we'll create it
16
+ return {
17
+ targetDir,
18
+ hasConfigFile: false,
19
+ hasContextDir: false,
20
+ isNonEmpty: false,
21
+ packageManager: null,
22
+ nodeVersion: process.version,
23
+ };
24
+ }
25
+ throw error;
26
+ }
27
+ const hasConfigFile = entries.includes(CONFIG_FILENAME);
28
+ const hasContextDir = entries.includes(CONTEXT_DIR_NAME);
29
+ // Filter out dotfiles and common non-user files for emptiness check
30
+ const userFiles = entries.filter((e) => !e.startsWith(".") && e !== "node_modules");
31
+ const isNonEmpty = userFiles.length > 0;
32
+ const packageManager = detectPackageManager(entries);
33
+ return {
34
+ targetDir,
35
+ hasConfigFile,
36
+ hasContextDir,
37
+ isNonEmpty,
38
+ packageManager,
39
+ nodeVersion: process.version,
40
+ };
41
+ }
42
+ function detectPackageManager(entries) {
43
+ if (entries.includes("pnpm-lock.yaml"))
44
+ return "pnpm";
45
+ if (entries.includes("yarn.lock"))
46
+ return "yarn";
47
+ if (entries.includes("package-lock.json"))
48
+ return "npm";
49
+ if (entries.includes("bun.lockb"))
50
+ return "bun";
51
+ if (entries.includes("Cargo.toml"))
52
+ return "cargo";
53
+ if (entries.includes("go.mod"))
54
+ return "go";
55
+ return null;
56
+ }
57
+ //# sourceMappingURL=inspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect.js","sourceRoot":"","sources":["../../src/init/inspect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAkBnE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB;IACtD,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,yDAAyD;YACzD,OAAO;gBACL,SAAS;gBACT,aAAa,EAAE,KAAK;gBACpB,aAAa,EAAE,KAAK;gBACpB,UAAU,EAAE,KAAK;gBACjB,cAAc,EAAE,IAAI;gBACpB,WAAW,EAAE,OAAO,CAAC,OAAO;aAC7B,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IACxD,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAEzD,oEAAoE;IACpE,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,cAAc,CAClD,CAAC;IACF,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IAExC,MAAM,cAAc,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAErD,OAAO;QACL,SAAS;QACT,aAAa;QACb,aAAa;QACb,UAAU;QACV,cAAc;QACd,WAAW,EAAE,OAAO,CAAC,OAAO;KAC7B,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAiB;IAC7C,IAAI,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAAE,OAAO,MAAM,CAAC;IACtD,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,MAAM,CAAC;IACjD,IAAI,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAAE,OAAO,KAAK,CAAC;IACxD,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,OAAO,CAAC;IACnD,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { ConfigWriteResult } from "../../shared/types.js";
2
+ /** Filename for the Claude Code project MCP config. */
3
+ export declare const CLAUDE_MCP_FILENAME = ".mcp.json";
4
+ /** Shape of the Claude Code MCP server entry generated by init. */
5
+ export interface ClaudeMcpEntry {
6
+ command: string;
7
+ args: string[];
8
+ }
9
+ /** Current Claude Code MCP server entry. */
10
+ export declare function claudeMcpEntry(): ClaudeMcpEntry;
11
+ /**
12
+ * Ensures the Claude Code config has a current OpenContext MCP server entry.
13
+ * Existing configs are extended, never overwritten: all other keys and MCP
14
+ * servers are preserved. An already-current entry is left untouched.
15
+ * @param targetDir - Project root directory
16
+ */
17
+ export declare function upsertClaudeConfig(targetDir: string): Promise<ConfigWriteResult>;
@@ -0,0 +1,94 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { OPENCONTEXT_MCP_PACKAGE } from "../../shared/constants.js";
4
+ import { isNodeError } from "../../shared/errors.js";
5
+ /** Filename for the Claude Code project MCP config. */
6
+ export const CLAUDE_MCP_FILENAME = ".mcp.json";
7
+ /** Current Claude Code MCP server entry. */
8
+ export function claudeMcpEntry() {
9
+ return {
10
+ command: "npx",
11
+ args: ["-y", OPENCONTEXT_MCP_PACKAGE],
12
+ };
13
+ }
14
+ function isPlainObject(value) {
15
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16
+ }
17
+ /**
18
+ * Reads an existing JSON config file, distinguishing "missing" from
19
+ * "present but not parseable".
20
+ */
21
+ async function readExistingJson(filePath) {
22
+ let raw;
23
+ try {
24
+ raw = await readFile(filePath, "utf8");
25
+ }
26
+ catch (error) {
27
+ if (isNodeError(error) && error.code === "ENOENT") {
28
+ return { exists: false, malformed: false };
29
+ }
30
+ throw error;
31
+ }
32
+ try {
33
+ return {
34
+ exists: true,
35
+ malformed: false,
36
+ parsed: JSON.parse(raw),
37
+ };
38
+ }
39
+ catch {
40
+ return { exists: true, malformed: true };
41
+ }
42
+ }
43
+ /** Whether an existing Claude MCP entry matches the current shape. */
44
+ function isClaudeMcpCurrent(entry) {
45
+ if (!isPlainObject(entry)) {
46
+ return false;
47
+ }
48
+ const desired = claudeMcpEntry();
49
+ const current = entry;
50
+ const args = current.args;
51
+ return (current.command === desired.command &&
52
+ Array.isArray(args) &&
53
+ args.length === desired.args.length &&
54
+ args.every((part, i) => part === desired.args[i]));
55
+ }
56
+ /**
57
+ * Ensures the Claude Code config has a current OpenContext MCP server entry.
58
+ * Existing configs are extended, never overwritten: all other keys and MCP
59
+ * servers are preserved. An already-current entry is left untouched.
60
+ * @param targetDir - Project root directory
61
+ */
62
+ export async function upsertClaudeConfig(targetDir) {
63
+ const filePath = path.join(targetDir, CLAUDE_MCP_FILENAME);
64
+ const existing = await readExistingJson(filePath);
65
+ if (existing.exists && existing.malformed) {
66
+ return {
67
+ status: "skipped",
68
+ message: `${CLAUDE_MCP_FILENAME} exists but is not valid JSON — left untouched.`,
69
+ };
70
+ }
71
+ const existingConfig = existing.parsed ?? {};
72
+ const existingServers = isPlainObject(existingConfig.mcpServers)
73
+ ? existingConfig.mcpServers
74
+ : {};
75
+ const current = existingServers.opencontext;
76
+ if (current !== undefined && isClaudeMcpCurrent(current)) {
77
+ return {
78
+ status: "unchanged",
79
+ message: `${CLAUDE_MCP_FILENAME} — OpenContext MCP server is up to date.`,
80
+ };
81
+ }
82
+ const merged = { ...existingConfig };
83
+ const servers = { ...existingServers };
84
+ servers.opencontext = claudeMcpEntry();
85
+ merged.mcpServers = servers;
86
+ await writeFile(filePath, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
87
+ const status = existing.exists ? "updated" : "created";
88
+ const verb = existing.exists ? "updated" : "created";
89
+ return {
90
+ status,
91
+ message: `${CLAUDE_MCP_FILENAME} ${verb} — OpenContext MCP server configured.`,
92
+ };
93
+ }
94
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../../src/init/integrations/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ConfigWriteResult } from "../../shared/types.js";
2
+ /** Filename for the OpenCode project config. */
3
+ export declare const OPENCODE_CONFIG_FILENAME = "opencode.json";
4
+ /** JSON schema used for generated OpenCode configs. */
5
+ export declare const OPENCODE_CONFIG_SCHEMA = "https://opencode.ai/config.json";
6
+ /** Shape of the OpenCode MCP server entry generated by init. */
7
+ export interface OpenCodeMcpEntry {
8
+ type: "local";
9
+ command: string[];
10
+ enabled: boolean;
11
+ }
12
+ /** Current OpenCode MCP server entry. */
13
+ export declare function openCodeMcpEntry(): OpenCodeMcpEntry;
14
+ /**
15
+ * Ensures the OpenCode config has a current OpenContext MCP server entry.
16
+ * Existing configs are extended, never overwritten: all other keys and MCP
17
+ * servers are preserved. An already-current entry is left untouched.
18
+ * @param targetDir - Project root directory
19
+ */
20
+ export declare function upsertOpenCodeConfig(targetDir: string): Promise<ConfigWriteResult>;
@@ -0,0 +1,108 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { OPENCONTEXT_MCP_PACKAGE } from "../../shared/constants.js";
4
+ import { isNodeError } from "../../shared/errors.js";
5
+ /** Filename for the OpenCode project config. */
6
+ export const OPENCODE_CONFIG_FILENAME = "opencode.json";
7
+ /** JSON schema used for generated OpenCode configs. */
8
+ export const OPENCODE_CONFIG_SCHEMA = "https://opencode.ai/config.json";
9
+ /** Current OpenCode MCP server entry. */
10
+ export function openCodeMcpEntry() {
11
+ return {
12
+ type: "local",
13
+ command: ["npx", "-y", OPENCONTEXT_MCP_PACKAGE],
14
+ enabled: true,
15
+ };
16
+ }
17
+ function isPlainObject(value) {
18
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19
+ }
20
+ /**
21
+ * Reads an existing JSON config file, distinguishing "missing" from
22
+ * "present but not parseable".
23
+ */
24
+ async function readExistingJson(filePath) {
25
+ let raw;
26
+ try {
27
+ raw = await readFile(filePath, "utf8");
28
+ }
29
+ catch (error) {
30
+ if (isNodeError(error) && error.code === "ENOENT") {
31
+ return { exists: false, malformed: false };
32
+ }
33
+ throw error;
34
+ }
35
+ try {
36
+ return {
37
+ exists: true,
38
+ malformed: false,
39
+ parsed: JSON.parse(raw),
40
+ };
41
+ }
42
+ catch {
43
+ return { exists: true, malformed: true };
44
+ }
45
+ }
46
+ /** Whether an existing OpenCode MCP entry matches the current shape. */
47
+ function isOpenCodeMcpCurrent(entry) {
48
+ if (!isPlainObject(entry)) {
49
+ return false;
50
+ }
51
+ const desired = openCodeMcpEntry();
52
+ const current = entry;
53
+ const command = current.command;
54
+ return (current.type === desired.type &&
55
+ Array.isArray(command) &&
56
+ command.length === desired.command.length &&
57
+ command.every((part, i) => part === desired.command[i]) &&
58
+ current.enabled !== false);
59
+ }
60
+ /**
61
+ * Ensures the OpenCode config has a current OpenContext MCP server entry.
62
+ * Existing configs are extended, never overwritten: all other keys and MCP
63
+ * servers are preserved. An already-current entry is left untouched.
64
+ * @param targetDir - Project root directory
65
+ */
66
+ export async function upsertOpenCodeConfig(targetDir) {
67
+ const filePath = path.join(targetDir, OPENCODE_CONFIG_FILENAME);
68
+ const existing = await readExistingJson(filePath);
69
+ if (existing.exists && existing.malformed) {
70
+ return {
71
+ status: "skipped",
72
+ message: `${OPENCODE_CONFIG_FILENAME} exists but is not valid JSON — left untouched.`,
73
+ };
74
+ }
75
+ const existingConfig = existing.parsed ?? {};
76
+ const existingMcp = isPlainObject(existingConfig.mcp) ? existingConfig.mcp : {};
77
+ const current = existingMcp.opencontext;
78
+ if (current !== undefined && isOpenCodeMcpCurrent(current)) {
79
+ return {
80
+ status: "unchanged",
81
+ message: `${OPENCODE_CONFIG_FILENAME} — OpenContext MCP server is up to date.`,
82
+ };
83
+ }
84
+ // Preserve existing $schema, otherwise add the current one.
85
+ const merged = {};
86
+ if (typeof existingConfig.$schema === "string") {
87
+ merged.$schema = existingConfig.$schema;
88
+ }
89
+ else {
90
+ merged.$schema = OPENCODE_CONFIG_SCHEMA;
91
+ }
92
+ for (const [key, value] of Object.entries(existingConfig)) {
93
+ if (key !== "$schema" && key !== "mcp") {
94
+ merged[key] = value;
95
+ }
96
+ }
97
+ const mcp = { ...existingMcp };
98
+ mcp.opencontext = openCodeMcpEntry();
99
+ merged.mcp = mcp;
100
+ await writeFile(filePath, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
101
+ const status = existing.exists ? "updated" : "created";
102
+ const verb = existing.exists ? "updated" : "created";
103
+ return {
104
+ status,
105
+ message: `${OPENCODE_CONFIG_FILENAME} ${verb} — OpenContext MCP server configured.`,
106
+ };
107
+ }
108
+ //# sourceMappingURL=opencode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../../src/init/integrations/opencode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { ConfigStatus } from "../../shared/types.js";
2
+ /** AGENTS.md — auto-read by OpenCode (and most agent tools). */
3
+ export declare const AGENTS_FILENAME = "AGENTS.md";
4
+ /** CLAUDE.md — auto-read by Claude Code. */
5
+ export declare const CLAUDE_FILENAME = "CLAUDE.md";
6
+ /** Marker delimiting the managed OpenContext section inside workflow files. */
7
+ export declare const SECTION_START = "<!-- OPENCONTEXT:START -->";
8
+ export declare const SECTION_END = "<!-- OPENCONTEXT:END -->";
9
+ export interface WorkflowFileResult {
10
+ status: ConfigStatus;
11
+ message: string;
12
+ }
13
+ /** Returns the current OpenContext workflow section for AGENTS.md / CLAUDE.md. */
14
+ export declare function workflowSection(): string;
15
+ /**
16
+ * Ensures a workflow file (AGENTS.md / CLAUDE.md) carries the current
17
+ * OpenContext section. Existing files are extended, never overwritten: the
18
+ * section is inserted between the OPENCONTEXT markers when present, or
19
+ * appended otherwise. All other content is preserved.
20
+ * @param targetDir - Project root directory
21
+ * @param filename - Workflow file name (AGENTS.md or CLAUDE.md)
22
+ */
23
+ export declare function upsertWorkflowFile(targetDir: string, filename: string): Promise<WorkflowFileResult>;
@@ -0,0 +1,102 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { isNodeError } from "../../shared/errors.js";
4
+ /** AGENTS.md — auto-read by OpenCode (and most agent tools). */
5
+ export const AGENTS_FILENAME = "AGENTS.md";
6
+ /** CLAUDE.md — auto-read by Claude Code. */
7
+ export const CLAUDE_FILENAME = "CLAUDE.md";
8
+ /** Marker delimiting the managed OpenContext section inside workflow files. */
9
+ export const SECTION_START = "<!-- OPENCONTEXT:START -->";
10
+ export const SECTION_END = "<!-- OPENCONTEXT:END -->";
11
+ /** Returns the current OpenContext workflow section for AGENTS.md / CLAUDE.md. */
12
+ export function workflowSection() {
13
+ return `
14
+ ${SECTION_START}
15
+ ## CRITICAL MEMORY SYSTEM: OpenContext Protocol
16
+
17
+ You have access to persistent project context via OpenContext MCP tools (\`.opencontext/\`). Adherence to this protocol is mandatory for consistency across sessions.
18
+
19
+ ### 1. Mandatory Pre-Flight: Inspect Context
20
+ Before editing, refactoring, or generating code that affects:
21
+ - Architecture, module boundaries, or dependency graph
22
+ - Database schemas, migrations, or models
23
+ - API contracts, route handlers, or DTOs
24
+ - Authentication, authorization, or security rules
25
+
26
+ **Action Required:** Execute \`read_context\` FIRST to check existing conventions, past design decisions, and project rules. Do NOT assume architectural patterns without reading relevant context topics.
27
+
28
+ ### 2. Post-Implementation: Persist Changes
29
+ Immediately after introducing, altering, or deprecating conventions, run the corresponding OpenContext tool:
30
+
31
+ * **Save/Update (\`save_context\`):**
32
+ - **When:** You add or change an architectural pattern, schema, shared utility rule, or tech stack decision.
33
+ - **Format:** Concise, topic-scoped Markdown summary.
34
+ - **Lifecycle Frontmatter:** When updating an existing convention, you MUST include YAML frontmatter:
35
+ \`\`\`yaml
36
+ ---
37
+ status: active | superseded | deprecated
38
+ supersedes: previous_topic_slug
39
+ ---
40
+ \`\`\`
41
+ * **Deprecate vs. Delete (\`delete_context\`):**
42
+ - Use \`status: deprecated\` or \`status: superseded\` in YAML frontmatter if historical context is valuable.
43
+ - Call \`delete_context\` ONLY when a topic is fully obsolete or entirely removed from the codebase.
44
+
45
+ ### 3. Execution Rules
46
+ - **Never skip:** Do not bypass \`read_context\` on multi-file or cross-cutting tasks to save tokens.
47
+ - **Fail-safe:** If unsure whether an architectural decision exists, invoke \`read_context\` before taking action.
48
+ ${SECTION_END}
49
+ `;
50
+ }
51
+ /**
52
+ * Ensures a workflow file (AGENTS.md / CLAUDE.md) carries the current
53
+ * OpenContext section. Existing files are extended, never overwritten: the
54
+ * section is inserted between the OPENCONTEXT markers when present, or
55
+ * appended otherwise. All other content is preserved.
56
+ * @param targetDir - Project root directory
57
+ * @param filename - Workflow file name (AGENTS.md or CLAUDE.md)
58
+ */
59
+ export async function upsertWorkflowFile(targetDir, filename) {
60
+ const filePath = path.join(targetDir, filename);
61
+ const section = workflowSection();
62
+ let existing;
63
+ try {
64
+ existing = await readFile(filePath, "utf8");
65
+ }
66
+ catch (error) {
67
+ if (isNodeError(error) && error.code === "ENOENT") {
68
+ await writeFile(filePath, `${section}\n`, "utf8");
69
+ return {
70
+ status: "created",
71
+ message: `${filename} created with the OpenContext workflow.`,
72
+ };
73
+ }
74
+ throw error;
75
+ }
76
+ const startIdx = existing.indexOf(SECTION_START);
77
+ const endIdx = existing.indexOf(SECTION_END);
78
+ if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
79
+ // Replace the managed section with the current content.
80
+ const updated = `${existing.slice(0, startIdx)}${section}${existing.slice(endIdx + SECTION_END.length)}`;
81
+ if (updated === existing) {
82
+ return {
83
+ status: "unchanged",
84
+ message: `${filename} — OpenContext workflow is up to date.`,
85
+ };
86
+ }
87
+ await writeFile(filePath, updated, "utf8");
88
+ return {
89
+ status: "updated",
90
+ message: `${filename} — OpenContext workflow updated.`,
91
+ };
92
+ }
93
+ // No managed section yet — append it, preserving the existing content.
94
+ const trimmed = existing.replace(/\s*$/, "");
95
+ const updated = `${trimmed}\n\n${section}\n`;
96
+ await writeFile(filePath, updated, "utf8");
97
+ return {
98
+ status: "updated",
99
+ message: `${filename} — OpenContext workflow added.`,
100
+ };
101
+ }
102
+ //# sourceMappingURL=workflow-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-files.js","sourceRoot":"","sources":["../../../src/init/integrations/workflow-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGrD,gEAAgE;AAChE,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C,4CAA4C;AAC5C,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,aAAa,GAAG,4BAA4B,CAAC;AAC1D,MAAM,CAAC,MAAM,WAAW,GAAG,0BAA0B,CAAC;AAOtD,kFAAkF;AAClF,MAAM,UAAU,eAAe;IAC7B,OAAO;EACP,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCb,WAAW;GACV,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB,EACjB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;IAElC,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAClD,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,OAAO,IAAI,EAAE,MAAM,CAAC,CAAC;YAClD,OAAO;gBACL,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,GAAG,QAAQ,yCAAyC;aAC9D,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAE7C,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC,IAAI,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC1D,wDAAwD;QACxD,MAAM,OAAO,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACzG,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YACzB,OAAO;gBACL,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE,GAAG,QAAQ,wCAAwC;aAC7D,CAAC;QACJ,CAAC;QACD,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3C,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,GAAG,QAAQ,kCAAkC;SACvD,CAAC;IACJ,CAAC;IAED,uEAAuE;IACvE,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,GAAG,OAAO,OAAO,OAAO,IAAI,CAAC;IAC7C,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3C,OAAO;QACL,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,GAAG,QAAQ,gCAAgC;KACrD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import * as readline from "node:readline/promises";
2
+ /**
3
+ * Prompts the user for a yes/no answer.
4
+ * @param rl - Readline interface
5
+ * @param message - Prompt message
6
+ * @param defaultYes - Default answer when the user presses Enter
7
+ * @returns true if user confirms
8
+ */
9
+ export declare function promptForConfirm(rl: readline.Interface, message: string, defaultYes?: boolean): Promise<boolean>;