@litfamily/litopencode 1.0.34 → 1.0.35
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/CHANGELOG.md +8 -0
- package/README-Ko-KR.md +6 -6
- package/README.md +6 -6
- package/dist/adhd-next.d.ts +1 -1
- package/dist/adhd-next.js +11 -0
- package/dist/cli/agent-file-command.d.ts +21 -0
- package/dist/cli/agent-file-command.js +99 -0
- package/dist/cli/agent-file.d.ts +46 -0
- package/dist/cli/agent-file.js +197 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.js +25 -1
- package/dist/cli/doctor.js +10 -0
- package/dist/cli/install.d.ts +4 -1
- package/dist/cli/install.js +8 -3
- package/dist/cli/managed-block.d.ts +26 -0
- package/dist/cli/managed-block.js +80 -0
- package/dist/cli/model-routing.d.ts +5 -0
- package/dist/cli/model-routing.js +1 -1
- package/dist/cli/types.d.ts +10 -0
- package/dist/cli.js +14 -1
- package/docs/assets/readme/badge-version.svg +1 -1
- package/docs/reference-Ko-KR.md +27 -0
- package/docs/reference.md +28 -1
- package/package.json +1 -1
- package/skills/lit-adhd/SKILL.md +3 -3
- package/skills/lit-adhd/references/brief-format.md +1 -1
- package/skills/lit-adhd/references/status-taxonomy.md +1 -1
- package/skills/lit-adhd/scripts/rewind.mjs +73 -14
- package/skills/lit-init/SKILL.md +4 -0
- package/skills/managed-skill-manifest.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ are tracked separately from this product history.
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 1.0.35 - 2026-10-31
|
|
9
|
+
|
|
10
|
+
- `lit adhd` counts the other lanes before it offers to resume this session's own work. A session that had started herdr lanes (or sat beside other running sessions) was still told to continue its paused discipline with `go` while they worked; now the box says to wait for them, arms nothing and ends with "Do not type `go` until they finish." A pending approval is still offered, and without lanes the paused discipline is offered as before.
|
|
11
|
+
|
|
12
|
+
- OpenCode reads one global instruction file at the start of every session, `AGENTS.md` in its config folder, and until now LitOpenCode never put anything there. The installer can now add a short block to it that says what `lit` means, where plans, the handoff and run state live, and that nothing is pushed, published, tagged or version-bumped without your yes. An interactive install asks once, `--yes` adds it, `--no-agent-file` skips it for a run, and an install with no terminal and no `--yes` never writes it. Your own text stays byte for byte, the first change keeps one backup copy, and `litopencode agent-file remove` takes the block out and returns the file to what it was. `litopencode agent-file off` removes it and keeps the choice in `litopencode.json`; `on` brings it back; `doctor` prints the block's state. OpenCode falls back to `~/.claude/CLAUDE.md` only while its own `AGENTS.md` is missing, so the installer never creates that file over an existing `CLAUDE.md`. `litopencode agent-file project` writes the matching project block into a repo's `AGENTS.md`, and `lit-init` points to it.
|
|
13
|
+
|
|
14
|
+
- `lit adhd` no longer offers `go` while other lanes work: other running sessions and, under `HERDR_ENV=1`, herdr agents in this workspace or repo make the box say to wait for them. Its HANDOFF step skips items already marked done and reads the newest handoff file first, another session's running start-work is never a `go` step here, and `go` re-checks that the recorded run is still this session's before it says keep working.
|
|
15
|
+
|
|
8
16
|
## 1.0.34 - 2026-10-29
|
|
9
17
|
|
|
10
18
|
- Releases are now built and published from GitHub Actions with npm provenance. The npm page shows a green check that links each version to this repository and the commit it was built from.
|
package/README-Ko-KR.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
1
|
+
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 일곱 로봇 패널이 차례로 켜지고, LitOpenCode 로봇의 눈과 테두리가 빛난 뒤 LITFAMILY와 KEEP THE WORK LIT. 문구가 밝아지는 영상" /></picture></p>
|
|
2
2
|
|
|
3
3
|
# LitOpenCode
|
|
4
4
|
|
|
5
5
|
**LitOpenCode는 OpenCode용 워크플로 플러그인입니다. 프롬프트에 `lit`만 붙이면 계획하고, 만들고, 확인하고, 기록까지 남겨서 다음 세션이 어디서 멈췄는지 바로 압니다.**
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
8
|
+
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/readme/badge-version.svg" alt="1.0.35" /></a>
|
|
9
9
|
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="월간 npm 다운로드 수" /></a>
|
|
10
|
-
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
10
|
+
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/readme/badge-license.svg" alt="MIT 라이선스" /></a>
|
|
11
11
|
<a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub 스타" /></a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
@@ -57,9 +57,9 @@ npm uninstall -g @litfamily/litopencode
|
|
|
57
57
|
## 더 보기
|
|
58
58
|
|
|
59
59
|
- [GitHub의 사용 안내](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide-Ko-KR.md): 설치 옵션, 자동 인수인계, 문제 해결, 제거
|
|
60
|
-
- [워크플로 상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
61
|
-
- [마이그레이션 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
62
|
-
- [LitFamily 허브](https://github.com/wjgoarxiv/litfamily) · [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
60
|
+
- [워크플로 상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/reference-Ko-KR.md) · [English reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/reference.md)
|
|
61
|
+
- [마이그레이션 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/CHANGELOG.md) · [개인정보와 네트워크 동작](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/privacy.md)
|
|
62
|
+
- [LitFamily 허브](https://github.com/wjgoarxiv/litfamily) · [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/LICENSE) · [상표 정책](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/TRADEMARK.md) · [ASCII 글꼴 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/readme/JetBrainsMono-OFL.txt)
|
|
63
63
|
|
|
64
64
|
<details>
|
|
65
65
|
<summary>ASCII 로고 복사</summary>
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
1
|
+
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: seven armored robots power on one by one, the LitOpenCode robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
|
|
2
2
|
|
|
3
3
|
# LitOpenCode
|
|
4
4
|
|
|
5
5
|
**LitOpenCode is a workflow plugin for OpenCode. Add `lit` to a prompt and the work gets planned, built, checked and written down, so the next session knows where you stopped.**
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
8
|
+
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/readme/badge-version.svg" alt="1.0.35" /></a>
|
|
9
9
|
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="npm downloads per month" /></a>
|
|
10
|
-
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
10
|
+
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
|
|
11
11
|
<a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub stars" /></a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
@@ -57,9 +57,9 @@ npm uninstall -g @litfamily/litopencode
|
|
|
57
57
|
## More
|
|
58
58
|
|
|
59
59
|
- [Guide on GitHub](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide.md): install options, automatic handoff, troubleshooting and removal.
|
|
60
|
-
- [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
61
|
-
- [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
62
|
-
- [LitFamily hub](https://github.com/wjgoarxiv/litfamily) · [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
60
|
+
- [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/reference.md)
|
|
61
|
+
- [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/CHANGELOG.md) · [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/privacy.md)
|
|
62
|
+
- [LitFamily hub](https://github.com/wjgoarxiv/litfamily) · [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/LICENSE) · [Trademark policy](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/TRADEMARK.md) · [ASCII font license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.35/docs/assets/readme/JetBrainsMono-OFL.txt)
|
|
63
63
|
|
|
64
64
|
<details>
|
|
65
65
|
<summary>Copy ASCII logo</summary>
|
package/dist/adhd-next.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ActivationMemory } from "./activation-reentry.ts";
|
|
2
2
|
import type { ChatActivationMode } from "./activation-routing.ts";
|
|
3
|
-
import type
|
|
3
|
+
import { type BoundedAuthorityOptions } from "./bounded-authority.ts";
|
|
4
4
|
export declare const adhdNextSchema = "litopencode.adhd-next/v1";
|
|
5
5
|
export declare const adhdNextTtlMs: number;
|
|
6
6
|
export type NextKind = "continue" | "work" | "plan" | "elsewhere";
|
package/dist/adhd-next.js
CHANGED
|
@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { createActivationMemory, decideReentry } from "./activation-reentry.js";
|
|
5
5
|
import { startWorkPromptInjection } from "./activation-workflow-prompts.js";
|
|
6
|
+
import { createBoundedAuthorityLifecycle } from "./bounded-authority.js";
|
|
6
7
|
import { answerBoundary, boundaryVerdict, startTypedWork } from "./start-work-typed.js";
|
|
7
8
|
import { createRuntimePaths } from "./state.js";
|
|
8
9
|
// The brief from lit-adhd ends with one recommended next step and the word `go`. The collector writes that
|
|
@@ -115,6 +116,16 @@ export async function routeGo(input) {
|
|
|
115
116
|
};
|
|
116
117
|
}
|
|
117
118
|
if (record.kind === "work") {
|
|
119
|
+
// The brief saw this session's run active; go re-checks that before it says keep working.
|
|
120
|
+
const state = await createBoundedAuthorityLifecycle(input.projectRoot, input.boundedAuthority).read().catch(() => undefined);
|
|
121
|
+
if (state?.status !== "active" || state.sessionID !== input.sessionID) {
|
|
122
|
+
const why = state === undefined ? "there is no start-work run any more" : state.sessionID !== input.sessionID ? "the start-work run now belongs to another session" : `the start-work run is ${state.status}`;
|
|
123
|
+
return {
|
|
124
|
+
text: `LitOpenCode: the user answered go to the lit-adhd brief, but its step no longer holds: ${why}. Say so in one line and start nothing; \`lit adhd\` gives a fresh step.`,
|
|
125
|
+
mode: "lit-adhd",
|
|
126
|
+
kind: "elsewhere"
|
|
127
|
+
};
|
|
128
|
+
}
|
|
118
129
|
return {
|
|
119
130
|
text: "LitOpenCode: the user answered go to the brief's next step, which is the running start-work run. Keep working from the checked plan; do not restart it, replan or widen what it may touch. A pending boundary still needs `lit approve` from the user.",
|
|
120
131
|
mode: "start-work",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AgentFileMode } from "./agent-file.ts";
|
|
2
|
+
import type { CliResult, ParsedArgs } from "./types.ts";
|
|
3
|
+
type InstallInput = {
|
|
4
|
+
readonly root: string;
|
|
5
|
+
readonly version: string;
|
|
6
|
+
readonly assumeYes: boolean;
|
|
7
|
+
readonly skip: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type AgentFilePlan = {
|
|
10
|
+
readonly path: string;
|
|
11
|
+
readonly state: string;
|
|
12
|
+
readonly mode: AgentFileMode | "unset";
|
|
13
|
+
readonly action: "add" | "refresh" | "ask" | "none" | "off" | "skipped-claude-fallback";
|
|
14
|
+
};
|
|
15
|
+
/** What an install would do to the global file, without asking or writing. */
|
|
16
|
+
export declare function planAgentFile(input: InstallInput, env?: NodeJS.ProcessEnv): Promise<AgentFilePlan>;
|
|
17
|
+
/** The install step: adds on a yes, refreshes a block already there, leaves everything else as it is. */
|
|
18
|
+
export declare function installAgentFile(input: InstallInput, env?: NodeJS.ProcessEnv, ask?: () => Promise<boolean>): Promise<string>;
|
|
19
|
+
/** `litopencode agent-file on|off|remove|project`. */
|
|
20
|
+
export declare function runAgentFileCommand(parsed: ParsedArgs, version: string, env?: NodeJS.ProcessEnv): Promise<CliResult>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// The install-time step and the `litopencode agent-file` command around the LitFamily block: one place
|
|
2
|
+
// that decides whether OpenCode's global AGENTS.md gets the block, and says so in one `AGENT_FILE:` line.
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { agentFileBody, agentFileLine, agentFileState, applyAgentFile, claudeFallbackFile, globalAgentFilePath, readAgentFileMode, writeAgentFileMode } from "./agent-file.js";
|
|
6
|
+
import { createPromptReader } from "./model-routing.js";
|
|
7
|
+
const QUESTION = "Add the short LitFamily block to OpenCode's global AGENTS.md? Your own text stays as it is. [Y/n] ";
|
|
8
|
+
const SHADOW_HINT = "OpenCode reads ~/.claude/CLAUDE.md while this file is missing, and a new AGENTS.md would hide it; create the file yourself first, or set OPENCODE_DISABLE_CLAUDE_CODE_PROMPT=1";
|
|
9
|
+
function shouldAsk(env) {
|
|
10
|
+
if (env.LITOPENCODE_AGENT_FILE_PROMPT === "1")
|
|
11
|
+
return true;
|
|
12
|
+
if (env.LITOPENCODE_AGENT_FILE_PROMPT === "0")
|
|
13
|
+
return false;
|
|
14
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true && env.CI === undefined;
|
|
15
|
+
}
|
|
16
|
+
async function askYes() {
|
|
17
|
+
const reader = createPromptReader();
|
|
18
|
+
try {
|
|
19
|
+
return !/^\s*n/iu.test(await reader.question(QUESTION));
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
reader.close();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const note = (status, filePath, hint) => `AGENT_FILE: ${status} ${filePath} (${hint})`;
|
|
26
|
+
/** What an install would do to the global file, without asking or writing. */
|
|
27
|
+
export async function planAgentFile(input, env = process.env) {
|
|
28
|
+
const filePath = globalAgentFilePath(input.root);
|
|
29
|
+
const mode = await readAgentFileMode(input.root, env);
|
|
30
|
+
const state = agentFileState({ path: filePath, version: input.version });
|
|
31
|
+
const base = { path: filePath, state, mode };
|
|
32
|
+
if (input.skip || mode === "off")
|
|
33
|
+
return { ...base, action: "off" };
|
|
34
|
+
if (state.startsWith("present"))
|
|
35
|
+
return { ...base, action: "none" };
|
|
36
|
+
if (state.startsWith("stale"))
|
|
37
|
+
return { ...base, action: "refresh" };
|
|
38
|
+
// A malformed, linked or non-UTF-8 file goes through the writer, which refuses it and says why.
|
|
39
|
+
if (state !== "absent")
|
|
40
|
+
return { ...base, action: "refresh" };
|
|
41
|
+
if (!fs.existsSync(filePath) && claudeFallbackFile(env) !== null)
|
|
42
|
+
return { ...base, action: "skipped-claude-fallback" };
|
|
43
|
+
return { ...base, action: input.assumeYes ? "add" : "ask" };
|
|
44
|
+
}
|
|
45
|
+
/** The install step: adds on a yes, refreshes a block already there, leaves everything else as it is. */
|
|
46
|
+
export async function installAgentFile(input, env = process.env, ask = askYes) {
|
|
47
|
+
const plan = await planAgentFile(input, env);
|
|
48
|
+
const write = () => agentFileLine(applyAgentFile({ path: plan.path, version: input.version, body: agentFileBody("global"), action: "install" }));
|
|
49
|
+
switch (plan.action) {
|
|
50
|
+
case "off":
|
|
51
|
+
return note("off", plan.path, "left as it is; turn on: litopencode agent-file on");
|
|
52
|
+
case "none":
|
|
53
|
+
return agentFileLine({ status: "unchanged", path: plan.path });
|
|
54
|
+
case "refresh":
|
|
55
|
+
case "add":
|
|
56
|
+
return write();
|
|
57
|
+
case "skipped-claude-fallback":
|
|
58
|
+
return note("skipped-claude-fallback", plan.path, SHADOW_HINT);
|
|
59
|
+
case "ask":
|
|
60
|
+
if (!shouldAsk(env))
|
|
61
|
+
return note("skipped", plan.path, "add the LitFamily block: litopencode agent-file on");
|
|
62
|
+
if (await ask())
|
|
63
|
+
return write();
|
|
64
|
+
await writeAgentFileMode(input.root, "off");
|
|
65
|
+
return note("off", plan.path, "turn on later: litopencode agent-file on");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** `litopencode agent-file on|off|remove|project`. */
|
|
69
|
+
export async function runAgentFileCommand(parsed, version, env = process.env) {
|
|
70
|
+
const action = parsed.agentFileAction;
|
|
71
|
+
const failed = (result) => (result.status.startsWith("refused-") ? 1 : 0);
|
|
72
|
+
if (action === "project") {
|
|
73
|
+
const dir = path.resolve(parsed.agentFileDir ?? process.cwd());
|
|
74
|
+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory())
|
|
75
|
+
return { exitCode: 2, stderr: `agent-file project: ${dir} is not a folder` };
|
|
76
|
+
const result = applyAgentFile({ path: path.join(dir, "AGENTS.md"), version, body: agentFileBody("project"), action: "install", dryRun: parsed.dryRun, backup: false });
|
|
77
|
+
return { exitCode: failed(result), stdout: agentFileLine(result) };
|
|
78
|
+
}
|
|
79
|
+
const filePath = globalAgentFilePath(parsed.root);
|
|
80
|
+
const lines = [];
|
|
81
|
+
if (action === "on" || action === "off") {
|
|
82
|
+
if (!parsed.dryRun)
|
|
83
|
+
await writeAgentFileMode(parsed.root, action);
|
|
84
|
+
lines.push(`AGENT_FILE: ${action}`);
|
|
85
|
+
}
|
|
86
|
+
if (action === "on" && !fs.existsSync(filePath) && claudeFallbackFile(env) !== null) {
|
|
87
|
+
lines.push(note("skipped-claude-fallback", filePath, SHADOW_HINT));
|
|
88
|
+
return { exitCode: 0, stdout: lines.join("\n") };
|
|
89
|
+
}
|
|
90
|
+
const result = applyAgentFile({
|
|
91
|
+
path: filePath,
|
|
92
|
+
version,
|
|
93
|
+
body: agentFileBody("global"),
|
|
94
|
+
action: action === "on" ? "install" : "uninstall",
|
|
95
|
+
dryRun: parsed.dryRun
|
|
96
|
+
});
|
|
97
|
+
lines.push(agentFileLine(result));
|
|
98
|
+
return { exitCode: failed(result), stdout: lines.join("\n") };
|
|
99
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const AGENT_FILE_ID = "litopencode";
|
|
2
|
+
export declare const AGENT_FILE_MODES: readonly ["on", "off"];
|
|
3
|
+
export type AgentFileMode = (typeof AGENT_FILE_MODES)[number];
|
|
4
|
+
export declare const AGENT_FILE_MAX_BYTES = 1200;
|
|
5
|
+
/** The shipped block text: `global` for OpenCode's AGENTS.md, `project` for a repo's AGENTS.md. */
|
|
6
|
+
export declare function agentFileBody(kind?: "global" | "project"): string;
|
|
7
|
+
export declare function globalAgentFilePath(root: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* OpenCode reads the first of `<config root>/AGENTS.md` and `~/.claude/CLAUDE.md` that exists. A new
|
|
10
|
+
* AGENTS.md would therefore hide the user's CLAUDE.md from OpenCode, unless the host's own
|
|
11
|
+
* OPENCODE_DISABLE_CLAUDE_CODE_PROMPT switch has already turned that fallback off.
|
|
12
|
+
*/
|
|
13
|
+
export declare function claudeFallbackFile(env?: NodeJS.ProcessEnv): string | null;
|
|
14
|
+
export type AgentFileStatus = "created" | "updated" | "unchanged" | "removed" | "absent" | "dry-run" | "refused-symlink" | "refused-not-file" | "refused-not-utf8" | "refused-malformed" | "refused-io";
|
|
15
|
+
export type AgentFileResult = {
|
|
16
|
+
readonly status: AgentFileStatus;
|
|
17
|
+
readonly path: string;
|
|
18
|
+
readonly backup?: string;
|
|
19
|
+
readonly preview?: string;
|
|
20
|
+
readonly error?: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Installs or removes block `id` in `path`. `backup: false` skips the first-insert copy (a repo's
|
|
24
|
+
* AGENTS.md, where a copy would sit beside tracked files).
|
|
25
|
+
*/
|
|
26
|
+
export declare function applyAgentFile(input: {
|
|
27
|
+
readonly path: string;
|
|
28
|
+
readonly id?: string;
|
|
29
|
+
readonly version: string;
|
|
30
|
+
readonly body: string;
|
|
31
|
+
readonly action: "install" | "uninstall";
|
|
32
|
+
readonly dryRun?: boolean;
|
|
33
|
+
readonly now?: Date;
|
|
34
|
+
readonly backup?: boolean;
|
|
35
|
+
}): AgentFileResult;
|
|
36
|
+
/** `present v=<x>`, `stale v=<x>` (an older install's block), `absent`, `malformed`, or `symlink` / `not-file` / `not-utf8`. */
|
|
37
|
+
export declare function agentFileState(input: {
|
|
38
|
+
readonly path: string;
|
|
39
|
+
readonly id?: string;
|
|
40
|
+
readonly version: string;
|
|
41
|
+
}): string;
|
|
42
|
+
export declare function agentFileLine(result: AgentFileResult): string;
|
|
43
|
+
/** The stored setting in litopencode.json; LITOPENCODE_AGENT_FILE=on|off overrides it. A missing or unreadable value is `unset`. */
|
|
44
|
+
export declare function readAgentFileMode(root: string, env?: NodeJS.ProcessEnv): Promise<AgentFileMode | "unset">;
|
|
45
|
+
/** Stores the setting, keeping every other key; a missing litopencode.json starts from the install defaults. */
|
|
46
|
+
export declare function writeAgentFileMode(root: string, mode: AgentFileMode): Promise<void>;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// The LitFamily block on disk: OpenCode's global AGENTS.md at `<config root>/AGENTS.md` (the folder
|
|
2
|
+
// OpenCode itself reads, `$XDG_CONFIG_HOME/opencode` or `~/.config/opencode`), and a repo's own
|
|
3
|
+
// AGENTS.md. Writes go through a temporary file and a rename and keep the file's mode. A symlink, a
|
|
4
|
+
// non-regular file or a non-UTF-8 file is refused rather than rewritten, and a file system error comes
|
|
5
|
+
// back as `refused-io` instead of stopping the installer. The user's original global file is copied
|
|
6
|
+
// once, the first time a block goes in. The on/off setting is `agentFile` in litopencode.json.
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
|
+
import fsSync from "node:fs";
|
|
9
|
+
import os from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { defaultGlobalConfigJson } from "../config.js";
|
|
12
|
+
import { litOpenCodeConfigFileForOpenCodeRoot } from "../state.js";
|
|
13
|
+
import { isRecord, readJsonObjectIfPresent } from "./json.js";
|
|
14
|
+
import { inspectBlock, ManagedBlockError, removeBlock, upsertBlock } from "./managed-block.js";
|
|
15
|
+
export const AGENT_FILE_ID = "litopencode";
|
|
16
|
+
export const AGENT_FILE_MODES = ["on", "off"];
|
|
17
|
+
// OpenCode reads the global file on every turn of every session, so the block stays small.
|
|
18
|
+
export const AGENT_FILE_MAX_BYTES = 1200;
|
|
19
|
+
const globalBody = `## LitFamily · LitOpenCode
|
|
20
|
+
|
|
21
|
+
LitOpenCode adds the lit workflows to OpenCode (lit-plan, start-work, lit-loop, review-work, lit-adhd,
|
|
22
|
+
litresearch). Its plugin hooks load the full rules each turn, so this block only sets the ground rules.
|
|
23
|
+
|
|
24
|
+
- \`lit\` at the start of a prompt is LitOpenCode's activation word, not the Lit web library.
|
|
25
|
+
- Plans live in \`plans/\`, the session handoff in \`HANDOFF.md\`, run state in \`.litopencode/\`. Read them
|
|
26
|
+
before resuming work in a repo.
|
|
27
|
+
- Never push, publish, tag or bump a version without the user's explicit yes for that action.
|
|
28
|
+
- Call work done only after its tests pass and the changed surface was checked for real.
|
|
29
|
+
- In a new repo, \`lit-init\` writes a short project \`AGENTS.md\` with these conventions.
|
|
30
|
+
- Turn this block off with \`litopencode agent-file off\`.
|
|
31
|
+
`;
|
|
32
|
+
const projectBody = `## LitFamily · project conventions
|
|
33
|
+
|
|
34
|
+
This repo is worked with LitOpenCode's lit workflows (lit-plan, start-work, lit-loop, review-work).
|
|
35
|
+
|
|
36
|
+
- Plans live in \`plans/\`, the session handoff in \`HANDOFF.md\`, run state in \`.litopencode/\` (keep it out
|
|
37
|
+
of commits). Read the handoff and the active plan before resuming.
|
|
38
|
+
- Never push, publish, tag or bump a version without the user's explicit yes for that action.
|
|
39
|
+
- Pair every behaviour change with a test that failed first, and check the changed surface for real
|
|
40
|
+
before calling it done.
|
|
41
|
+
`;
|
|
42
|
+
/** The shipped block text: `global` for OpenCode's AGENTS.md, `project` for a repo's AGENTS.md. */
|
|
43
|
+
export function agentFileBody(kind = "global") {
|
|
44
|
+
return kind === "global" ? globalBody : projectBody;
|
|
45
|
+
}
|
|
46
|
+
export function globalAgentFilePath(root) {
|
|
47
|
+
return path.join(path.resolve(root), "AGENTS.md");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* OpenCode reads the first of `<config root>/AGENTS.md` and `~/.claude/CLAUDE.md` that exists. A new
|
|
51
|
+
* AGENTS.md would therefore hide the user's CLAUDE.md from OpenCode, unless the host's own
|
|
52
|
+
* OPENCODE_DISABLE_CLAUDE_CODE_PROMPT switch has already turned that fallback off.
|
|
53
|
+
*/
|
|
54
|
+
export function claudeFallbackFile(env = process.env) {
|
|
55
|
+
const disabled = env.OPENCODE_DISABLE_CLAUDE_CODE_PROMPT;
|
|
56
|
+
if (disabled !== undefined && disabled !== "" && disabled !== "0" && disabled.toLowerCase() !== "false")
|
|
57
|
+
return null;
|
|
58
|
+
const home = env.OPENCODE_TEST_HOME ?? (process.platform === "win32" ? env.USERPROFILE : env.HOME) ?? os.homedir();
|
|
59
|
+
const candidate = path.join(home, ".claude", "CLAUDE.md");
|
|
60
|
+
return fsSync.existsSync(candidate) ? candidate : null;
|
|
61
|
+
}
|
|
62
|
+
function entryKind(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
const stats = fsSync.lstatSync(filePath);
|
|
65
|
+
return stats.isSymbolicLink() ? "symlink" : stats.isFile() ? "file" : "other";
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error.code === "ENOENT")
|
|
69
|
+
return "missing";
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// The text, or null for a file that is not UTF-8 (UTF-16 from a Windows editor, Latin-1): rewriting
|
|
74
|
+
// one would corrupt the user's bytes outside the markers.
|
|
75
|
+
function readUtf8(filePath) {
|
|
76
|
+
const bytes = fsSync.readFileSync(filePath);
|
|
77
|
+
const utf16 = (bytes[0] === 0xff && bytes[1] === 0xfe) || (bytes[0] === 0xfe && bytes[1] === 0xff);
|
|
78
|
+
if (bytes.includes(0) || utf16)
|
|
79
|
+
return null;
|
|
80
|
+
try {
|
|
81
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// A unique staging name, so a leftover from a crashed run never blocks the next one.
|
|
88
|
+
function writeAtomic(filePath, text) {
|
|
89
|
+
fsSync.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
90
|
+
const staging = `${filePath}.litfamily-${randomUUID()}.tmp`;
|
|
91
|
+
let mode = null;
|
|
92
|
+
try {
|
|
93
|
+
mode = fsSync.statSync(filePath).mode & 0o7777;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// A new file takes the default mode.
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
fsSync.writeFileSync(staging, text, { flag: "wx" });
|
|
100
|
+
if (mode !== null)
|
|
101
|
+
fsSync.chmodSync(staging, mode);
|
|
102
|
+
fsSync.renameSync(staging, filePath);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
fsSync.rmSync(staging, { force: true });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Installs or removes block `id` in `path`. `backup: false` skips the first-insert copy (a repo's
|
|
110
|
+
* AGENTS.md, where a copy would sit beside tracked files).
|
|
111
|
+
*/
|
|
112
|
+
export function applyAgentFile(input) {
|
|
113
|
+
const { path: filePath, id = AGENT_FILE_ID, version, body, action, dryRun = false, now = new Date(), backup = true } = input;
|
|
114
|
+
try {
|
|
115
|
+
const kind = entryKind(filePath);
|
|
116
|
+
if (kind === "symlink")
|
|
117
|
+
return { status: "refused-symlink", path: filePath };
|
|
118
|
+
if (kind === "other")
|
|
119
|
+
return { status: "refused-not-file", path: filePath };
|
|
120
|
+
const text = kind === "file" ? readUtf8(filePath) : "";
|
|
121
|
+
if (text === null)
|
|
122
|
+
return { status: "refused-not-utf8", path: filePath };
|
|
123
|
+
let next;
|
|
124
|
+
try {
|
|
125
|
+
next = action === "uninstall" ? removeBlock(text, id) : upsertBlock(text, { id, version, body }, { created: kind === "missing" });
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (error instanceof ManagedBlockError)
|
|
129
|
+
return { status: "refused-malformed", path: filePath };
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
if (!next.changed)
|
|
133
|
+
return { status: action === "uninstall" ? "absent" : "unchanged", path: filePath };
|
|
134
|
+
if (dryRun)
|
|
135
|
+
return { status: "dry-run", path: filePath, preview: next.text };
|
|
136
|
+
if (action === "uninstall") {
|
|
137
|
+
if (next.text === "" && next.created === true)
|
|
138
|
+
fsSync.unlinkSync(filePath);
|
|
139
|
+
else
|
|
140
|
+
writeAtomic(filePath, next.text);
|
|
141
|
+
return { status: "removed", path: filePath };
|
|
142
|
+
}
|
|
143
|
+
let copy;
|
|
144
|
+
if (backup && kind === "file" && inspectBlock(text, id).state === "absent") {
|
|
145
|
+
copy = `${filePath}.litfamily-backup-${now.toISOString().replace(/[:.]/gu, "-")}`;
|
|
146
|
+
fsSync.copyFileSync(filePath, copy);
|
|
147
|
+
}
|
|
148
|
+
writeAtomic(filePath, next.text);
|
|
149
|
+
return { status: kind === "file" ? "updated" : "created", path: filePath, ...(copy === undefined ? {} : { backup: copy }) };
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
const code = error?.code;
|
|
153
|
+
return { status: "refused-io", path: filePath, error: code ?? (error instanceof Error ? error.message : String(error)) };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** `present v=<x>`, `stale v=<x>` (an older install's block), `absent`, `malformed`, or `symlink` / `not-file` / `not-utf8`. */
|
|
157
|
+
export function agentFileState(input) {
|
|
158
|
+
const kind = entryKind(input.path);
|
|
159
|
+
if (kind === "missing")
|
|
160
|
+
return "absent";
|
|
161
|
+
if (kind === "symlink")
|
|
162
|
+
return "symlink";
|
|
163
|
+
if (kind === "other")
|
|
164
|
+
return "not-file";
|
|
165
|
+
const text = readUtf8(input.path);
|
|
166
|
+
if (text === null)
|
|
167
|
+
return "not-utf8";
|
|
168
|
+
const found = inspectBlock(text, input.id ?? AGENT_FILE_ID);
|
|
169
|
+
if (found.state !== "present")
|
|
170
|
+
return found.state;
|
|
171
|
+
return `${found.version === input.version ? "present" : "stale"} v=${found.version}`;
|
|
172
|
+
}
|
|
173
|
+
export function agentFileLine(result) {
|
|
174
|
+
return `AGENT_FILE: ${result.status} ${result.path}${result.backup === undefined ? "" : ` (backup: ${result.backup})`}`;
|
|
175
|
+
}
|
|
176
|
+
/** The stored setting in litopencode.json; LITOPENCODE_AGENT_FILE=on|off overrides it. A missing or unreadable value is `unset`. */
|
|
177
|
+
export async function readAgentFileMode(root, env = process.env) {
|
|
178
|
+
const override = (env.LITOPENCODE_AGENT_FILE ?? "").trim().toLowerCase();
|
|
179
|
+
if (override === "on" || override === "off")
|
|
180
|
+
return override;
|
|
181
|
+
try {
|
|
182
|
+
const stored = (await readJsonObjectIfPresent(litOpenCodeConfigFileForOpenCodeRoot(root)))?.agentFile;
|
|
183
|
+
return stored === "on" || stored === "off" ? stored : "unset";
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return "unset";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Stores the setting, keeping every other key; a missing litopencode.json starts from the install defaults. */
|
|
190
|
+
export async function writeAgentFileMode(root, mode) {
|
|
191
|
+
const filePath = litOpenCodeConfigFileForOpenCodeRoot(root);
|
|
192
|
+
const existing = await readJsonObjectIfPresent(filePath);
|
|
193
|
+
const base = isRecord(existing) ? existing : defaultGlobalConfigJson();
|
|
194
|
+
if (base.agentFile === mode)
|
|
195
|
+
return;
|
|
196
|
+
writeAtomic(filePath, JSON.stringify({ ...base, agentFile: mode }, null, 2) + "\n");
|
|
197
|
+
}
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ParsedArgs } from "./types.ts";
|
|
2
2
|
export declare const loopCommands: readonly ["create-goals", "status", "record-evidence", "checkpoint", "steer", "complete-goals", "record-review-blockers"];
|
|
3
|
+
export declare const agentFileActions: readonly ["on", "off", "remove", "project"];
|
|
3
4
|
export type LoopCommand = (typeof loopCommands)[number];
|
|
4
5
|
export declare function isLoopCommand(command: string | undefined): command is LoopCommand;
|
|
5
6
|
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
package/dist/cli/args.js
CHANGED
|
@@ -11,6 +11,7 @@ export const loopCommands = [
|
|
|
11
11
|
"complete-goals",
|
|
12
12
|
"record-review-blockers"
|
|
13
13
|
];
|
|
14
|
+
export const agentFileActions = ["on", "off", "remove", "project"];
|
|
14
15
|
export function isLoopCommand(command) {
|
|
15
16
|
return loopCommands.some((candidate) => candidate === command);
|
|
16
17
|
}
|
|
@@ -56,6 +57,8 @@ export function parseArgs(argv) {
|
|
|
56
57
|
let directive;
|
|
57
58
|
let resolve;
|
|
58
59
|
let host = "auto";
|
|
60
|
+
let agentFile = "auto";
|
|
61
|
+
let agentFileDir;
|
|
59
62
|
if (command === "--help" || command === "-h") {
|
|
60
63
|
command = "help";
|
|
61
64
|
}
|
|
@@ -71,7 +74,11 @@ export function parseArgs(argv) {
|
|
|
71
74
|
const tuiAction = command === "tui" ? argv[1] : undefined;
|
|
72
75
|
if (command === "tui" && tuiAction !== "remove")
|
|
73
76
|
throw new Error("tui requires an action: remove");
|
|
74
|
-
const
|
|
77
|
+
const agentFileAction = command === "agent-file" ? argv[1] : undefined;
|
|
78
|
+
if (command === "agent-file" && !agentFileActions.some((action) => action === agentFileAction)) {
|
|
79
|
+
throw new Error(`agent-file requires an action: ${agentFileActions.join(", ")}`);
|
|
80
|
+
}
|
|
81
|
+
const firstOptionIndex = command === "fetch-public" || command === "tui" || command === "agent-file" ? 2 : 1;
|
|
75
82
|
if (command === "fetch-public") {
|
|
76
83
|
fetchPublicUrl = cleanOption(argv[1], "fetch-public URL");
|
|
77
84
|
if (!fetchPublicUrl)
|
|
@@ -91,6 +98,19 @@ export function parseArgs(argv) {
|
|
|
91
98
|
dryRun = true;
|
|
92
99
|
continue;
|
|
93
100
|
}
|
|
101
|
+
if (arg === "--no-agent-file") {
|
|
102
|
+
if (command !== "install")
|
|
103
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
104
|
+
agentFile = "skip";
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (arg === "--dir") {
|
|
108
|
+
if (agentFileAction !== "project")
|
|
109
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
110
|
+
agentFileDir = cleanOption(argv[index + 1], arg);
|
|
111
|
+
index += 1;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
94
114
|
if (arg === "--no-auto-update") {
|
|
95
115
|
continue;
|
|
96
116
|
}
|
|
@@ -315,6 +335,10 @@ export function parseArgs(argv) {
|
|
|
315
335
|
modelPrompt: yes ? "never" : modelPrompt,
|
|
316
336
|
outputStylePrompt: yes ? "never" : "auto",
|
|
317
337
|
...(tuiAction === undefined ? {} : { tuiAction }),
|
|
338
|
+
...(agentFileAction === undefined ? {} : { agentFileAction }),
|
|
339
|
+
...(agentFileDir === undefined ? {} : { agentFileDir }),
|
|
340
|
+
agentFile,
|
|
341
|
+
assumeYes: yes,
|
|
318
342
|
host,
|
|
319
343
|
loop: {
|
|
320
344
|
projectRoot,
|
package/dist/cli/doctor.js
CHANGED
|
@@ -14,6 +14,7 @@ import { inspectHostCompactionPoint, inspectHostLimits } from "./host-limits.js"
|
|
|
14
14
|
import { autoHandoffEnvEnabled, autoHandoffEnvPercent, createAutoHandoffSettings } from "../auto-handoff.js";
|
|
15
15
|
import { inspectLspCapability } from "./lsp-capability.js";
|
|
16
16
|
import { probeMotionRuntime } from "./motion-runtime.js";
|
|
17
|
+
import { agentFileState, globalAgentFilePath, readAgentFileMode } from "./agent-file.js";
|
|
17
18
|
import { isLitOpenCodeEntry } from "./plugin-mutation.js";
|
|
18
19
|
import { inspectTuiPlugin, planCliJsonCleanup } from "./tui-config.js";
|
|
19
20
|
import { detectOpenCodeHosts, targetsV1, targetsV2 } from "./host-detect.js";
|
|
@@ -256,10 +257,19 @@ export async function doctor(root, hostChoice = "auto") {
|
|
|
256
257
|
if (autoHandoff.enabled && autoHandoff.percent !== null && hostCompaction.percent !== null && autoHandoff.percent >= hostCompaction.percent) {
|
|
257
258
|
autoHandoffWarnings.push(`OpenCode compacts on its own at about ${hostCompaction.percent}% of the context window. Your ${autoHandoff.percent}% is at or above that, so the host compacts first and the handoff has no time to run. Choose a percent below ${hostCompaction.percent}.`);
|
|
258
259
|
}
|
|
260
|
+
const agentFilePath = globalAgentFilePath(root);
|
|
261
|
+
const agentFileMode = await readAgentFileMode(root);
|
|
262
|
+
const agentFileBlock = agentFileMode === "off" ? "off" : agentFileState({ path: agentFilePath, version: metadata.version });
|
|
259
263
|
return {
|
|
260
264
|
exitCode: 0,
|
|
261
265
|
stdout: JSON.stringify({
|
|
262
266
|
package: metadata,
|
|
267
|
+
agentFile: {
|
|
268
|
+
path: agentFilePath,
|
|
269
|
+
state: agentFileBlock,
|
|
270
|
+
mode: agentFileMode,
|
|
271
|
+
line: `AGENT_FILE: ${agentFileBlock} (${agentFilePath}; change: litopencode agent-file on|off)`
|
|
272
|
+
},
|
|
263
273
|
capabilities: openCodeHostCapabilities(),
|
|
264
274
|
hostLimits: { path: hostConfig.path, ...inspectHostLimits(hostConfig.config) },
|
|
265
275
|
host: {
|
package/dist/cli/install.d.ts
CHANGED
|
@@ -7,4 +7,7 @@ import { type HostChoice } from "./host-detect.ts";
|
|
|
7
7
|
*/
|
|
8
8
|
export declare function withoutV2PluginEntries(config: Record<string, unknown> | null): Record<string, unknown> | null;
|
|
9
9
|
export declare function assertNoHostOnlyKeysAdded(before: Record<string, unknown> | null, next: Record<string, unknown> | null): void;
|
|
10
|
-
export declare function install(root: string, dryRun: boolean, modelSelection?: InstallModelSelection | undefined, modelPrompt?: ModelPromptMode, permissionMode?: InstallPermissionMode, permissionModeExplicit?: boolean, permissionPrompt?: PermissionPromptMode, progress?: InstallProgress, outputStylePrompt?: PermissionPromptMode, host?: HostChoice
|
|
10
|
+
export declare function install(root: string, dryRun: boolean, modelSelection?: InstallModelSelection | undefined, modelPrompt?: ModelPromptMode, permissionMode?: InstallPermissionMode, permissionModeExplicit?: boolean, permissionPrompt?: PermissionPromptMode, progress?: InstallProgress, outputStylePrompt?: PermissionPromptMode, host?: HostChoice, agentFileChoice?: {
|
|
11
|
+
readonly assumeYes: boolean;
|
|
12
|
+
readonly skip: boolean;
|
|
13
|
+
}): Promise<CliResult>;
|