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.
- package/README.md +38 -12
- package/dist/cli/index.d.ts +25 -0
- package/dist/cli/index.js +97 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/{config.d.ts → config/index.d.ts} +1 -1
- package/dist/config/index.js +62 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.js +52 -9
- package/dist/index.js.map +1 -1
- package/dist/init/constants.d.ts +6 -0
- package/dist/init/constants.js +17 -0
- package/dist/init/constants.js.map +1 -0
- package/dist/init/display.d.ts +9 -0
- package/dist/init/display.js +28 -0
- package/dist/init/display.js.map +1 -0
- package/dist/init/files.d.ts +12 -0
- package/dist/init/files.js +39 -0
- package/dist/init/files.js.map +1 -0
- package/dist/init/index.d.ts +6 -0
- package/dist/init/index.js +157 -0
- package/dist/init/index.js.map +1 -0
- package/dist/init/inspect.d.ts +21 -0
- package/dist/init/inspect.js +57 -0
- package/dist/init/inspect.js.map +1 -0
- package/dist/init/integrations/claude.d.ts +17 -0
- package/dist/init/integrations/claude.js +94 -0
- package/dist/init/integrations/claude.js.map +1 -0
- package/dist/init/integrations/opencode.d.ts +20 -0
- package/dist/init/integrations/opencode.js +108 -0
- package/dist/init/integrations/opencode.js.map +1 -0
- package/dist/init/integrations/workflow-files.d.ts +23 -0
- package/dist/init/integrations/workflow-files.js +102 -0
- package/dist/init/integrations/workflow-files.js.map +1 -0
- package/dist/init/prompts.d.ts +9 -0
- package/dist/init/prompts.js +17 -0
- package/dist/init/prompts.js.map +1 -0
- package/dist/server/http.d.ts +21 -0
- package/dist/server/http.js +63 -0
- package/dist/server/http.js.map +1 -0
- package/dist/{server.d.ts → server/index.d.ts} +3 -1
- package/dist/{server.js → server/index.js} +7 -4
- package/dist/server/index.js.map +1 -0
- package/dist/shared/constants.d.ts +27 -0
- package/dist/shared/constants.js +26 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/errors.d.ts +17 -0
- package/dist/shared/errors.js +28 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/types.d.ts +29 -0
- package/dist/shared/types.js +17 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/{context-store.d.ts → store/context-store.d.ts} +2 -16
- package/dist/store/context-store.js +147 -0
- package/dist/store/context-store.js.map +1 -0
- package/dist/store/index-builder.d.ts +32 -0
- package/dist/store/index-builder.js +162 -0
- package/dist/store/index-builder.js.map +1 -0
- package/dist/validation/index.d.ts +16 -0
- package/dist/validation/index.js +39 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/{validation.d.ts → validation/write-guard.d.ts} +0 -16
- package/dist/{validation.js → validation/write-guard.js} +2 -38
- package/dist/validation/write-guard.js.map +1 -0
- package/package.json +2 -2
- package/dist/config.js +0 -145
- package/dist/config.js.map +0 -1
- package/dist/context-store.js +0 -296
- package/dist/context-store.js.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/types.d.ts +0 -56
- package/dist/types.js +0 -63
- package/dist/types.js.map +0 -1
- 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>;
|