Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/cli/src/cli.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { render } from "ink";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
import { setShellIfWindows, getProjectCode } from "@vegamo/deepcode-core";
import { ensureUserSettingsFile, getProjectCode, setShellIfWindows } from "@vegamo/deepcode-core";
import { checkForNpmUpdate, promptForPendingUpdate } from "./common/update-check";
import { AppContainer } from "./ui";
import { parseArguments } from "./cli-args";
Expand All @@ -28,6 +28,13 @@ async function main(): Promise<void> {
// If called before argument parsing, --help and --version would fail on those machines.
configureWindowsShell();

// First run: scaffold a ready-to-edit settings.json so the user only has to
// paste their API key instead of authoring the file by hand. (#202)
const scaffold = ensureUserSettingsFile();
if (scaffold.created) {
writeStderrLine(`Created ${scaffold.path} — edit it to add your API key.\n`);
}

let initialPrompt = parsed.prompt;
let resumeSessionId = parsed.resume;
let forkSessionId = parsed.fork;
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export {
getProjectSettingsPath,
getDefaultContextWindow,
getDefaultAutoCompactWindow,
ensureUserSettingsFile,
DEFAULT_MODEL,
DEFAULT_BASE_URL,
} from "./settings";
Expand Down
28 changes: 28 additions & 0 deletions packages/core/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,34 @@ export function readProjectSettings(projectRoot: string = process.cwd()): Deepco
return readSettingsFile(getProjectSettingsPath(projectRoot));
}

export const DEFAULT_SETTINGS_TEMPLATE: DeepcodingSettings = {
env: {
API_KEY: "",
BASE_URL: DEFAULT_BASE_URL,
MODEL: DEFAULT_MODEL,
},
};

/**
* Create the user settings file on first run with a ready-to-edit template so
* the user only has to paste their API key instead of authoring the file and
* guessing the schema by hand. Never overwrites an existing file. (#202)
*/
export function ensureUserSettingsFile(settingsPath: string = getUserSettingsPath()): {
created: boolean;
path: string;
} {
if (fs.existsSync(settingsPath)) {
return { created: false, path: settingsPath };
}
try {
writeSettingsFile(settingsPath, DEFAULT_SETTINGS_TEMPLATE);
return { created: true, path: settingsPath };
} catch {
return { created: false, path: settingsPath };
}
}

function writeSettingsFile(settingsPath: string, settings: DeepcodingSettings): void {
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
fs.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
Expand Down
23 changes: 23 additions & 0 deletions packages/core/src/tests/settings-and-notify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,32 @@ import {
type NotifySpawn,
} from "../common/notify";
import { applyModelConfigSelection, resolveSettings, resolveSettingsSources } from "../settings";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import { DEFAULT_SETTINGS_TEMPLATE, ensureUserSettingsFile, readSettingsFile } from "../settings";

const TEST_PROCESS_ENV = {};

test("ensureUserSettingsFile scaffolds a template and never overwrites an existing file", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "deepcode-settings-test-"));
try {
const settingsPath = path.join(dir, "settings.json");

const first = ensureUserSettingsFile(settingsPath);
assert.equal(first.created, true);
assert.equal(first.path, settingsPath);
assert.deepEqual(readSettingsFile(settingsPath), DEFAULT_SETTINGS_TEMPLATE);

fs.writeFileSync(settingsPath, JSON.stringify({ model: "custom-model" }), "utf8");
const second = ensureUserSettingsFile(settingsPath);
assert.equal(second.created, false);
assert.deepEqual(readSettingsFile(settingsPath), { model: "custom-model" });
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});

test("resolveSettings reads top-level thinkingEnabled, notify, and webSearchTool", () => {
const resolved = resolveSettings(
{
Expand Down