Skip to content
Closed
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ Show help:
openwiki --help
```

Reconfigure your saved provider, API key, model, and optional LangSmith key
without starting a documentation run:

```sh
openwiki --config
```

`openwiki` creates initial documentation in `openwiki/` when no wiki exists. If `openwiki/` already exists, it refreshes that documentation from repository changes. By default, the CLI stays open after each run so you can send follow-up messages. Use `-p` or `--print` for a one-shot non-interactive run that prints the final assistant output.

`openwiki` will automatically append prompting to your `AGENTS.md` and/or `CLAUDE.md` files to instruct your coding agent to reference it when searching for context. If the file does not already exist in your repository, OpenWiki will create it for you.
Expand Down
1 change: 1 addition & 0 deletions openwiki/cli/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ From `src/commands.ts` and `README.md`, the supported entry patterns are:
- `openwiki "message"` — send a chat message immediately, then stay open.
- `openwiki --init [message]` — generate initial OpenWiki documentation.
- `openwiki --update [message]` — refresh existing OpenWiki documentation.
- `openwiki --config` — re-run provider, API key, model, and optional LangSmith setup without starting an agent run.
- `openwiki -p, --print` — run once and print the final assistant output (non-interactive).
- `openwiki --modelId <id>` / `--model-id <id>` — choose a model ID for the run.
- `openwiki --help` / `-h` — print usage, options, and examples.
Expand Down
58 changes: 51 additions & 7 deletions src/cli.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ function App({ command }: AppProps) {
process.stdin.isTTY &&
runState.status === "idle" &&
needsCredentialSetup(sessionModelId);
const shouldRunConfigSetup =
command.kind === "config" && runState.status === "idle";
const displayModelId = sessionModelId ?? startupModelId;

function submitChatMessage(message: string) {
Expand Down Expand Up @@ -225,13 +227,13 @@ function App({ command }: AppProps) {
return;
}

if (command.dryRun) {
process.exitCode = 0;
app.exit();
if (command.kind !== "run") {
return;
}

if (command.kind !== "run") {
if (command.dryRun) {
process.exitCode = 0;
app.exit();
return;
}

Expand Down Expand Up @@ -383,11 +385,17 @@ function App({ command }: AppProps) {
return;
}

if (runState.status === "init-setup-saved" && command.kind === "config") {
process.exitCode = 0;
app.exit();
return;
}

if (runState.status === "success" && autoExitOnSuccess) {
process.exitCode = 0;
app.exit();
}
}, [app, autoExitOnSuccess, runState.status]);
}, [app, autoExitOnSuccess, command.kind, runState.status]);

if (command.kind === "help") {
return <HelpView />;
Expand All @@ -403,6 +411,27 @@ function App({ command }: AppProps) {
);
}

if (shouldRunConfigSetup) {
return (
<InitSetup
forceReconfigure
onComplete={(result) => {
if (result.modelId) {
setSessionModelId(result.modelId);
}
if (result.provider) {
setSessionProvider(result.provider);
}

setRunState({ status: "init-setup-saved", result });
}}
onError={(message) => {
setRunState({ status: "error", message });
}}
/>
);
}

if (command.kind === "run" && command.dryRun) {
return (
<DryRunView
Expand Down Expand Up @@ -463,7 +492,11 @@ function App({ command }: AppProps) {
value={runState.result.modelId}
/>
) : null}
<StatusLine tone="active" label="Next" value="starting openwiki" />
<StatusLine
tone="active"
label="Next"
value={command.kind === "config" ? "done" : "starting openwiki"}
/>
</Box>
);
}
Expand Down Expand Up @@ -3023,7 +3056,10 @@ function Rows({ rows }: RowsProps) {
const argv = process.argv.slice(2);
const parsedCommand = parseCommand(argv);

if (parsedCommand.kind === "run" && !parsedCommand.dryRun) {
if (
(parsedCommand.kind === "run" && !parsedCommand.dryRun) ||
parsedCommand.kind === "config"
) {
await loadOpenWikiEnv();
}

Expand Down Expand Up @@ -3113,6 +3149,14 @@ function writePrintErrorDiagnostics(error: unknown): void {
}

function resolveStartupCommand(command: CliCommand): CliCommand {
if (command.kind === "config" && !process.stdin.isTTY) {
return {
kind: "error",
exitCode: 1,
message: "openwiki --config requires an interactive terminal.",
};
}

if (
command.kind === "run" &&
!command.dryRun &&
Expand Down
19 changes: 19 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type HelpContent = {
};

export type CliCommand =
| { kind: "config"; exitCode: 0 }
| { kind: "help"; exitCode: 0 }
| {
kind: "run";
Expand All @@ -40,6 +41,18 @@ export function parseCommand(argv: string[]): CliCommand {
return { kind: "help", exitCode: 0 };
}

if (argv.includes("--config")) {
if (argv.length > 1) {
return {
kind: "error",
exitCode: 1,
message: "--config cannot be combined with other options or messages.",
};
}

return { kind: "config", exitCode: 0 };
}

let dryRun = false;
let modelId: string | null = null;
let print = false;
Expand Down Expand Up @@ -176,6 +189,7 @@ export const helpContent: HelpContent = {
usage: [
"openwiki [--modelId <model>]",
"openwiki [--modelId <model>] [message]",
"openwiki --config",
"openwiki --init [message]",
"openwiki --update [message]",
],
Expand All @@ -186,6 +200,10 @@ export const helpContent: HelpContent = {
},
],
options: [
{
label: "--config",
description: "Configure or update saved provider credentials.",
},
{
label: "--init",
description: "Generate initial OpenWiki documentation.",
Expand All @@ -211,6 +229,7 @@ export const helpContent: HelpContent = {
],
examples: [
"openwiki",
"openwiki --config",
"openwiki --init",
"openwiki --update",
'openwiki "What can you do?"',
Expand Down
79 changes: 55 additions & 24 deletions src/credentials.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export type InitSetupResult = {
};

type InitSetupProps = {
forceReconfigure?: boolean;
modelIdOverride?: string | null;
onComplete: (result: InitSetupResult) => void;
onError: (message: string) => void;
Expand Down Expand Up @@ -68,6 +69,7 @@ function isBaseUrlConfigured(provider: OpenWikiProvider): boolean {
}

export function InitSetup({
forceReconfigure = false,
modelIdOverride = null,
onComplete,
onError,
Expand Down Expand Up @@ -96,7 +98,11 @@ export function InitSetup({
const [isSaving, setIsSaving] = useState(false);

useEffect(() => {
const initialStep = getInitialStep(modelIdOverride, initialProvider);
const initialStep = getInitialStep(
modelIdOverride,
initialProvider,
forceReconfigure,
);

if (initialStep === null) {
onComplete({
Expand Down Expand Up @@ -127,7 +133,7 @@ export function InitSetup({
shouldStartWithCustomModelInput(initialProvider),
);
setStep(initialStep);
}, [initialProvider, modelIdOverride, onComplete]);
}, [forceReconfigure, initialProvider, modelIdOverride, onComplete]);

useInput((inputValue, key) => {
if (isSaving || step === null) {
Expand Down Expand Up @@ -211,6 +217,7 @@ export function InitSetup({
const nextStep = getNextStepAfterProvider(
selectedProvider,
modelIdOverride,
forceReconfigure,
);

if (nextStep) {
Expand Down Expand Up @@ -242,7 +249,11 @@ export function InitSetup({

setApiKey(trimmedInput);
setInput("");
const nextStep = getNextStepAfterApiKey(provider, modelIdOverride);
const nextStep = getNextStepAfterApiKey(
provider,
modelIdOverride,
forceReconfigure,
);

if (nextStep) {
setIsCustomModelInput(
Expand Down Expand Up @@ -279,7 +290,11 @@ export function InitSetup({

setBaseUrl(trimmedInput);
setInput("");
const nextStep = getNextStepAfterBaseUrl(provider, modelIdOverride);
const nextStep = getNextStepAfterBaseUrl(
provider,
modelIdOverride,
forceReconfigure,
);

if (nextStep) {
setIsCustomModelInput(
Expand Down Expand Up @@ -322,7 +337,7 @@ export function InitSetup({
setInput("");
setIsCustomModelInput(false);

if (process.env.LANGSMITH_API_KEY === undefined) {
if (forceReconfigure || process.env.LANGSMITH_API_KEY === undefined) {
setStep("langsmith");
return;
}
Expand Down Expand Up @@ -446,21 +461,21 @@ export function InitSetup({
<SetupStep
label="Provider"
state={
process.env[OPENWIKI_PROVIDER_ENV_KEY]
? "done"
: step === "provider"
? "current"
step === "provider"
? "current"
: process.env[OPENWIKI_PROVIDER_ENV_KEY]
? "done"
: "pending"
}
detail={getProviderSetupDetail(provider)}
/>
<SetupStep
label="Provider key"
state={
process.env[getProviderApiKeyEnvKey(provider)]
? "done"
: step === "api-key"
? "current"
step === "api-key"
? "current"
: process.env[getProviderApiKeyEnvKey(provider)]
? "done"
: "pending"
}
detail={
Expand Down Expand Up @@ -489,21 +504,21 @@ export function InitSetup({
<SetupStep
label="Model"
state={
modelIdOverride || process.env[OPENWIKI_MODEL_ID_ENV_KEY]
? "done"
: step === "model"
? "current"
step === "model"
? "current"
: modelIdOverride || process.env[OPENWIKI_MODEL_ID_ENV_KEY]
? "done"
: "pending"
}
detail={getModelSetupDetail(modelIdOverride, provider)}
/>
<SetupStep
label="LangSmith"
state={
process.env.LANGSMITH_API_KEY !== undefined
? "done"
: step === "langsmith"
? "current"
step === "langsmith"
? "current"
: process.env.LANGSMITH_API_KEY !== undefined
? "done"
: "optional"
}
detail={
Expand All @@ -530,7 +545,7 @@ export function InitSetup({
)}
</SetupPanel>

{needsCredentialPrompt ? (
{forceReconfigure || needsCredentialPrompt ? (
<Text color="gray">Secrets are masked and saved only after setup.</Text>
) : null}

Expand Down Expand Up @@ -745,7 +760,12 @@ function SelectionMarker({ isSelected }: { isSelected: boolean }) {
function getInitialStep(
modelIdOverride: string | null,
provider: OpenWikiProvider,
forceReconfigure = false,
): PromptStep | null {
if (forceReconfigure) {
return "provider";
}

if (process.env[OPENWIKI_PROVIDER_ENV_KEY] === undefined) {
return "provider";
}
Expand Down Expand Up @@ -775,29 +795,40 @@ function getInitialStep(
function getNextStepAfterProvider(
provider: OpenWikiProvider,
modelIdOverride: string | null,
forceReconfigure = false,
): PromptStep | null {
if (forceReconfigure) {
return "api-key";
}

if (!process.env[getProviderApiKeyEnvKey(provider)]) {
return "api-key";
}

return getNextStepAfterApiKey(provider, modelIdOverride);
return getNextStepAfterApiKey(provider, modelIdOverride, forceReconfigure);
}

function getNextStepAfterApiKey(
provider: OpenWikiProvider,
modelIdOverride: string | null,
forceReconfigure = false,
): PromptStep | null {
if (needsBaseUrlStep(provider)) {
return "base-url";
}

return getNextStepAfterBaseUrl(provider, modelIdOverride);
return getNextStepAfterBaseUrl(provider, modelIdOverride, forceReconfigure);
}

function getNextStepAfterBaseUrl(
provider: OpenWikiProvider,
modelIdOverride: string | null,
forceReconfigure = false,
): PromptStep | null {
if (forceReconfigure) {
return "model";
}

if (
modelIdOverride === null &&
process.env[OPENWIKI_MODEL_ID_ENV_KEY] === undefined
Expand Down