@snarkipus/opencode-beads 0.9.1 → 0.9.3
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 +41 -78
- package/package.json +3 -3
- package/src/opencode-runtime.ts +156 -0
- package/src/plugin-core.ts +92 -35
- package/src/plugin-hooks.ts +56 -0
- package/src/plugin.ts +5 -143
- package/src/prime.ts +71 -7
- package/vendor/commands/audit.md +1 -2
- package/vendor/commands/blocked.md +1 -1
- package/vendor/commands/close.md +1 -1
- package/vendor/commands/comments.md +1 -1
- package/vendor/commands/compact.md +1 -1
- package/vendor/commands/create.md +1 -1
- package/vendor/commands/decision.md +2 -2
- package/vendor/commands/delete.md +1 -1
- package/vendor/commands/dep.md +1 -1
- package/vendor/commands/epic.md +4 -1
- package/vendor/commands/export.md +2 -2
- package/vendor/commands/import.md +75 -9
- package/vendor/commands/init.md +1 -1
- package/vendor/commands/label.md +1 -1
- package/vendor/commands/list.md +1 -1
- package/vendor/commands/quickstart.md +2 -2
- package/vendor/commands/rename-prefix.md +1 -2
- package/vendor/commands/reopen.md +1 -1
- package/vendor/commands/restore.md +1 -1
- package/vendor/commands/search.md +1 -1
- package/vendor/commands/show.md +1 -1
- package/vendor/commands/sync.md +101 -9
- package/vendor/commands/update.md +1 -1
- package/vendor/manifest.json +47 -47
package/src/plugin.ts
CHANGED
|
@@ -1,150 +1,12 @@
|
|
|
1
1
|
/** OpenCode adapter for the Beads issue tracker. */
|
|
2
2
|
|
|
3
|
-
import type { Plugin
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
resolveProjectDirectory,
|
|
8
|
-
type PluginRuntime,
|
|
9
|
-
} from "./plugin-core";
|
|
10
|
-
import { runBdPrime } from "./prime";
|
|
11
|
-
|
|
12
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
13
|
-
return typeof value === "object" && value !== null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function isSessionMessage(value: unknown): boolean {
|
|
17
|
-
if (
|
|
18
|
-
!isRecord(value) ||
|
|
19
|
-
!isRecord(value.info) ||
|
|
20
|
-
(value.info.role !== "user" && value.info.role !== "assistant") ||
|
|
21
|
-
(value.info.agent !== undefined && typeof value.info.agent !== "string") ||
|
|
22
|
-
(value.info.system !== undefined && typeof value.info.system !== "string") ||
|
|
23
|
-
(value.info.model !== undefined &&
|
|
24
|
-
(!isRecord(value.info.model) ||
|
|
25
|
-
typeof value.info.model.providerID !== "string" ||
|
|
26
|
-
typeof value.info.model.modelID !== "string"))
|
|
27
|
-
) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
if (!Array.isArray(value.parts)) return false;
|
|
31
|
-
|
|
32
|
-
return value.parts.every(
|
|
33
|
-
(part) =>
|
|
34
|
-
isRecord(part) &&
|
|
35
|
-
typeof part.type === "string" &&
|
|
36
|
-
(part.text === undefined || typeof part.text === "string")
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function isAgent(value: unknown): boolean {
|
|
41
|
-
return (
|
|
42
|
-
isRecord(value) &&
|
|
43
|
-
typeof value.name === "string" &&
|
|
44
|
-
(value.mode === "subagent" || value.mode === "primary" || value.mode === "all")
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Prevent the synthetic no-reply prompt from recursively triggering its own injection. */
|
|
49
|
-
function isBeadsContextInjection(parts: ReadonlyArray<Part>): boolean {
|
|
50
|
-
return parts.some(
|
|
51
|
-
(part) =>
|
|
52
|
-
part.type === "text" &&
|
|
53
|
-
part.synthetic === true &&
|
|
54
|
-
part.text.includes("<beads-context>")
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Adapt the official OpenCode client to the controller's small deterministic boundary. */
|
|
59
|
-
export function createOpenCodeRuntime(client: PluginInput["client"]): PluginRuntime {
|
|
60
|
-
return {
|
|
61
|
-
async getMessages(projectDirectory, sessionID, limit) {
|
|
62
|
-
const response = await client.session.messages({
|
|
63
|
-
path: { id: sessionID },
|
|
64
|
-
query: { directory: projectDirectory, limit },
|
|
65
|
-
});
|
|
66
|
-
if (response.error !== undefined) throw response.error;
|
|
67
|
-
if (!Array.isArray(response.data) || !response.data.every(isSessionMessage)) {
|
|
68
|
-
throw new Error("OpenCode returned malformed session messages");
|
|
69
|
-
}
|
|
70
|
-
return response.data satisfies SessionMessagesResponse;
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
async getAgents(projectDirectory) {
|
|
74
|
-
const response = await client.app.agents({ query: { directory: projectDirectory } });
|
|
75
|
-
if (response.error !== undefined) throw response.error;
|
|
76
|
-
if (!Array.isArray(response.data) || !response.data.every(isAgent)) {
|
|
77
|
-
throw new Error("OpenCode returned malformed agents");
|
|
78
|
-
}
|
|
79
|
-
return response.data satisfies Agent[];
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
async prompt(projectDirectory, sessionID, body) {
|
|
83
|
-
const response = await client.session.prompt({
|
|
84
|
-
path: { id: sessionID },
|
|
85
|
-
query: { directory: projectDirectory },
|
|
86
|
-
body,
|
|
87
|
-
});
|
|
88
|
-
if (response.error !== undefined) throw response.error;
|
|
89
|
-
if (response.data === undefined) throw new Error("OpenCode returned no prompt result");
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
async prime(projectDirectory) {
|
|
93
|
-
return runBdPrime(projectDirectory);
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
async diagnose(diagnostic) {
|
|
97
|
-
const extra =
|
|
98
|
-
diagnostic.code === "config_collision"
|
|
99
|
-
? { surface: diagnostic.surface, names: diagnostic.names }
|
|
100
|
-
: { sessionID: diagnostic.sessionID };
|
|
101
|
-
const response = await client.app.log({
|
|
102
|
-
query: { directory: diagnostic.directory },
|
|
103
|
-
body: {
|
|
104
|
-
service: "opencode-beads",
|
|
105
|
-
level: "warn",
|
|
106
|
-
message: diagnostic.code,
|
|
107
|
-
extra,
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
if (response.error !== undefined) throw response.error;
|
|
111
|
-
if (response.data === undefined) throw new Error("OpenCode returned no log result");
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
}
|
|
3
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
4
|
+
import { resolveProjectDirectory } from "./plugin-core";
|
|
5
|
+
import { createBeadsHooks } from "./plugin-hooks";
|
|
6
|
+
import { createOpenCodeRuntime } from "./opencode-runtime";
|
|
115
7
|
|
|
116
8
|
export const BeadsPlugin: Plugin = async ({ client, directory, worktree }) => {
|
|
117
9
|
const projectDirectory = resolveProjectDirectory(directory, worktree);
|
|
118
10
|
const runtime = createOpenCodeRuntime(client);
|
|
119
|
-
|
|
120
|
-
const controller = await createBeadsController(runtime, projectDirectory);
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
"chat.message": async (input, output) => {
|
|
124
|
-
if (isBeadsContextInjection(output.parts)) return;
|
|
125
|
-
|
|
126
|
-
await controller.onMessage(
|
|
127
|
-
{
|
|
128
|
-
sessionID: input.sessionID,
|
|
129
|
-
model: input.model,
|
|
130
|
-
agent: input.agent,
|
|
131
|
-
},
|
|
132
|
-
(context) => {
|
|
133
|
-
output.message.system = output.message.system
|
|
134
|
-
? `${output.message.system}\n\n${context}`
|
|
135
|
-
: context;
|
|
136
|
-
}
|
|
137
|
-
);
|
|
138
|
-
},
|
|
139
|
-
|
|
140
|
-
event: async ({ event }) => {
|
|
141
|
-
if (event.type === "session.compacted") {
|
|
142
|
-
await controller.onCompacted(event.properties.sessionID);
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
config: async (config) => {
|
|
147
|
-
await controller.configure(config);
|
|
148
|
-
},
|
|
149
|
-
};
|
|
11
|
+
return createBeadsHooks(runtime, projectDirectory);
|
|
150
12
|
};
|
package/src/prime.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { kill as killProcess } from "node:process";
|
|
2
|
+
|
|
1
3
|
export const DEFAULT_PRIME_TIMEOUT_MS = 10_000;
|
|
4
|
+
export const DEFAULT_PRIME_DRAIN_TIMEOUT_MS = 1_000;
|
|
2
5
|
|
|
3
6
|
export class PrimeTimeoutError extends Error {
|
|
4
7
|
constructor(timeoutMs: number) {
|
|
@@ -15,25 +18,55 @@ export class PrimeProcessError extends Error {
|
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
export interface PrimeProcess {
|
|
21
|
+
pid: number;
|
|
18
22
|
stdout: ReadableStream<Uint8Array>;
|
|
19
23
|
stderr: ReadableStream<Uint8Array>;
|
|
20
24
|
exited: Promise<number>;
|
|
21
25
|
kill(signal?: NodeJS.Signals): void;
|
|
26
|
+
killTree?(signal?: NodeJS.Signals): void;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
export interface PrimeExecutionOptions {
|
|
25
30
|
timeoutMs?: number;
|
|
31
|
+
drainTimeoutMs?: number;
|
|
26
32
|
spawn?: (directory: string, args: readonly string[]) => PrimeProcess;
|
|
27
33
|
scheduleTimeout?: (callback: () => void, delayMs: number) => () => void;
|
|
34
|
+
scheduleDrainTimeout?: (callback: () => void, delayMs: number) => () => void;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
function spawnPrime(directory: string, args: readonly string[]): PrimeProcess {
|
|
31
|
-
|
|
38
|
+
const child = Bun.spawn(["bd", "prime", ...args], {
|
|
32
39
|
cwd: directory,
|
|
33
40
|
stdin: "ignore",
|
|
34
41
|
stdout: "pipe",
|
|
35
42
|
stderr: "pipe",
|
|
43
|
+
detached: true,
|
|
36
44
|
});
|
|
45
|
+
let processGroupID: number | undefined;
|
|
46
|
+
if (process.platform !== "win32") {
|
|
47
|
+
const group = Bun.spawnSync(["ps", "-o", "pgid=", "-p", String(child.pid)], {
|
|
48
|
+
stdout: "pipe",
|
|
49
|
+
stderr: "ignore",
|
|
50
|
+
timeout: 100,
|
|
51
|
+
});
|
|
52
|
+
const parsed = Number.parseInt(new TextDecoder().decode(group.stdout).trim(), 10);
|
|
53
|
+
if (group.exitCode === 0 && parsed === child.pid && parsed > 1) processGroupID = parsed;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
pid: child.pid,
|
|
58
|
+
stdout: child.stdout,
|
|
59
|
+
stderr: child.stderr,
|
|
60
|
+
exited: child.exited,
|
|
61
|
+
kill: (signal) => child.kill(signal),
|
|
62
|
+
killTree: (signal) => {
|
|
63
|
+
if (processGroupID !== undefined) {
|
|
64
|
+
killProcess(-processGroupID, signal);
|
|
65
|
+
} else {
|
|
66
|
+
child.kill(signal);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
};
|
|
37
70
|
}
|
|
38
71
|
|
|
39
72
|
function scheduleTimeout(callback: () => void, delayMs: number): () => void {
|
|
@@ -41,15 +74,42 @@ function scheduleTimeout(callback: () => void, delayMs: number): () => void {
|
|
|
41
74
|
return () => clearTimeout(timer);
|
|
42
75
|
}
|
|
43
76
|
|
|
77
|
+
function terminateProcessTree(child: PrimeProcess): void {
|
|
78
|
+
try {
|
|
79
|
+
(child.killTree ?? child.kill)("SIGKILL");
|
|
80
|
+
} catch {
|
|
81
|
+
try {
|
|
82
|
+
child.kill("SIGKILL");
|
|
83
|
+
} catch {
|
|
84
|
+
// Timeout remains authoritative if the process exited concurrently.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function drainAfterKill(
|
|
90
|
+
completion: Promise<unknown>,
|
|
91
|
+
timeoutMs: number,
|
|
92
|
+
scheduler: (callback: () => void, delayMs: number) => () => void
|
|
93
|
+
): Promise<void> {
|
|
94
|
+
await new Promise<void>((resolve) => {
|
|
95
|
+
const cancelTimeout = scheduler(resolve, timeoutMs);
|
|
96
|
+
void completion.finally(() => {
|
|
97
|
+
cancelTimeout();
|
|
98
|
+
resolve();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
44
103
|
async function runPrimeAttempt(
|
|
45
104
|
directory: string,
|
|
46
105
|
args: readonly string[],
|
|
47
106
|
options: PrimeExecutionOptions = {}
|
|
48
107
|
): Promise<string> {
|
|
49
108
|
const timeoutMs = options.timeoutMs ?? DEFAULT_PRIME_TIMEOUT_MS;
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
109
|
+
const drainTimeoutMs = options.drainTimeoutMs ?? DEFAULT_PRIME_DRAIN_TIMEOUT_MS;
|
|
110
|
+
const child = (options.spawn ?? spawnPrime)(directory, args);
|
|
111
|
+
const stdout = new Response(child.stdout).text();
|
|
112
|
+
const stderr = new Response(child.stderr).text();
|
|
53
113
|
|
|
54
114
|
let timedOut = false;
|
|
55
115
|
let rejectTimeout: (error: PrimeTimeoutError) => void = () => {};
|
|
@@ -58,11 +118,11 @@ async function runPrimeAttempt(
|
|
|
58
118
|
});
|
|
59
119
|
const cancelTimeout = (options.scheduleTimeout ?? scheduleTimeout)(() => {
|
|
60
120
|
timedOut = true;
|
|
61
|
-
|
|
121
|
+
terminateProcessTree(child);
|
|
62
122
|
rejectTimeout(new PrimeTimeoutError(timeoutMs));
|
|
63
123
|
}, timeoutMs);
|
|
64
124
|
|
|
65
|
-
const completion = Promise.all([
|
|
125
|
+
const completion = Promise.all([child.exited, stdout, stderr]).then(
|
|
66
126
|
([exitCode, output, errorOutput]) => {
|
|
67
127
|
if (exitCode !== 0) throw new PrimeProcessError(exitCode, errorOutput);
|
|
68
128
|
return output;
|
|
@@ -74,7 +134,11 @@ async function runPrimeAttempt(
|
|
|
74
134
|
} finally {
|
|
75
135
|
cancelTimeout();
|
|
76
136
|
if (timedOut) {
|
|
77
|
-
await
|
|
137
|
+
await drainAfterKill(
|
|
138
|
+
completion.catch(() => undefined),
|
|
139
|
+
drainTimeoutMs,
|
|
140
|
+
options.scheduleDrainTimeout ?? scheduleTimeout
|
|
141
|
+
);
|
|
78
142
|
}
|
|
79
143
|
}
|
|
80
144
|
}
|
package/vendor/commands/audit.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Log and label agent interactions (append-only JSONL)
|
|
3
|
-
argument-hint: record|label
|
|
3
|
+
argument-hint: "record|label"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Append-only audit logging for agent interactions (prompts, responses, tool calls) in `.beads/interactions.jsonl`.
|
|
@@ -25,4 +25,3 @@ Each line is one event. Labeling is done by appending a new `"label"` event refe
|
|
|
25
25
|
- Audit entries are **append-only** (no in-place edits).
|
|
26
26
|
- `bd dolt push` includes `.beads/interactions.jsonl` in the commit allowlist.
|
|
27
27
|
|
|
28
|
-
|
package/vendor/commands/close.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Compact old closed issues using semantic summarization
|
|
3
|
-
argument-hint: [--all] [--id issue-id] [--dry-run]
|
|
3
|
+
argument-hint: "[--all] [--id issue-id] [--dry-run]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Reduce database size by summarizing closed issues no longer actively referenced.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Record, list, and manage project decisions with rationale tracking
|
|
3
|
-
argument-hint: record|list|show|supersede
|
|
3
|
+
argument-hint: "record|list|show|supersede"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Record and track project decisions as beads issues with structured rationale, alternatives considered, and links to affected work.
|
|
@@ -9,7 +9,7 @@ Decisions use `--type decision`. The description field holds the structured deci
|
|
|
9
9
|
|
|
10
10
|
## Record a Decision
|
|
11
11
|
|
|
12
|
-
When the user wants to record a decision (
|
|
12
|
+
When the user wants to record a decision (i.e. the `record` action of this command):
|
|
13
13
|
|
|
14
14
|
1. Gather the following (ask if not provided):
|
|
15
15
|
- **Title**: Short summary of what was decided (required)
|
package/vendor/commands/dep.md
CHANGED
package/vendor/commands/epic.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Epic management commands
|
|
3
|
-
argument-hint: [command]
|
|
3
|
+
argument-hint: "[command]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Manage epics (large features composed of multiple issues).
|
|
@@ -21,6 +21,9 @@ Manage epics (large features composed of multiple issues).
|
|
|
21
21
|
1. Create epic: `bd create "Large Feature" -t epic -p 1`
|
|
22
22
|
2. Link subtasks: `bd dep add bd-20 bd-10 --type parent-child` (task bd-20 is child of epic bd-10)
|
|
23
23
|
- Or at creation: `bd create "Subtask title" -t task --parent bd-10`
|
|
24
|
+
- Children inherit parent labels by default. If the epic uses size/effort
|
|
25
|
+
labels and children should have their own, add `--no-inherit-labels`
|
|
26
|
+
(GH#4562).
|
|
24
27
|
3. Track progress: `bd epic status`
|
|
25
28
|
4. Auto-close when done: `bd epic close-eligible`
|
|
26
29
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Export issues to JSONL format
|
|
3
|
-
argument-hint: [-o output-file]
|
|
3
|
+
argument-hint: "[-o output-file]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Export all issues to JSON Lines format (one JSON object per line).
|
|
@@ -9,7 +9,7 @@ Export all issues to JSON Lines format (one JSON object per line).
|
|
|
9
9
|
|
|
10
10
|
- **To stdout**: `bd export`
|
|
11
11
|
- **To file**: `bd export -o issues.jsonl`
|
|
12
|
-
- **
|
|
12
|
+
- **Include all records**: `bd export --all -o full.jsonl`
|
|
13
13
|
|
|
14
14
|
Issues are sorted by ID for consistent diffs, making git diffs readable.
|
|
15
15
|
|
|
@@ -1,18 +1,84 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Import issues from JSONL
|
|
3
|
-
argument-hint:
|
|
2
|
+
description: Import issues and memories from JSONL
|
|
3
|
+
argument-hint: "[file|-] [--input file] [--dry-run] [--dedup] [--allow-stale]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
`bd import`
|
|
6
|
+
`bd import` imports newline-delimited JSON into an existing Beads database.
|
|
7
|
+
Issues with new IDs are created and existing IDs use guarded upsert semantics.
|
|
8
|
+
The command is not supported in proxied-server mode.
|
|
7
9
|
|
|
8
|
-
##
|
|
9
|
-
|
|
10
|
-
If you need to import issues from a JSONL file, use `bd init` with the `--from-jsonl` flag:
|
|
10
|
+
## Usage
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
bd
|
|
13
|
+
bd import # Read .beads/<import.path>
|
|
14
|
+
bd import backup.jsonl # Read a specific file
|
|
15
|
+
bd import --input backup.jsonl # Same as -i backup.jsonl
|
|
16
|
+
bd import - # Read JSONL from stdin
|
|
17
|
+
bd export --include-memories | bd import - # Round-trip issues and memories
|
|
18
|
+
bd import --dry-run backup.jsonl # Parse and report without writing
|
|
19
|
+
bd import --json backup.jsonl # Return a structured result
|
|
14
20
|
```
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
With no file argument, the source is the `import.path` file under `.beads/`;
|
|
23
|
+
`import.path` defaults to `issues.jsonl`. Use either a positional file or
|
|
24
|
+
`--input`/`-i`, not both. The special path `-` reads stdin. With `--global` and
|
|
25
|
+
no file, the source is `global-issues.jsonl` in the active Beads directory.
|
|
26
|
+
|
|
27
|
+
Each non-empty line must be a JSON object. Issue records accept the schema
|
|
28
|
+
emitted by `bd export`; `title` is the only required issue field. Records with
|
|
29
|
+
`"_type":"memory"` and non-empty `key` and `value` fields are imported as
|
|
30
|
+
persistent memories. Optional export header records are ignored, and tombstone
|
|
31
|
+
rows are skipped. `bd export` omits memories by default; add
|
|
32
|
+
`--include-memories` or `--all` when they should be carried into an import.
|
|
33
|
+
|
|
34
|
+
## Merge and Safety Semantics
|
|
35
|
+
|
|
36
|
+
For records that supply `updated_at`, an incoming row rewrites an existing
|
|
37
|
+
issue only when its timestamp is strictly newer. Older records are skipped
|
|
38
|
+
wholesale, including their labels, comments, and dependencies. When timestamps
|
|
39
|
+
are equal, the local issue row wins, while those auxiliary records still merge.
|
|
40
|
+
The stale check is repeated inside the upsert so a concurrent newer local update
|
|
41
|
+
is not overwritten.
|
|
42
|
+
|
|
43
|
+
If `updated_at` is omitted, the importer fills it with the current time during
|
|
44
|
+
the write. A record naming an existing ID can therefore overwrite local state;
|
|
45
|
+
include the exported timestamp when stale protection matters.
|
|
46
|
+
|
|
47
|
+
Use `--allow-stale` only when deliberately restoring an older snapshot: it
|
|
48
|
+
disables that guard and can overwrite newer local issue state. Imports are
|
|
49
|
+
upserts, and labels, comments, and dependencies are deduplicated. Re-running
|
|
50
|
+
canonical export data or other input with stable issue IDs and preserved
|
|
51
|
+
`updated_at` values is safe and converges. Without a preserved timestamp, each
|
|
52
|
+
run can appear newer and rewrite the row. An issue record without an ID is
|
|
53
|
+
assigned one; re-importing it can create another issue instead of targeting the
|
|
54
|
+
first.
|
|
55
|
+
|
|
56
|
+
Large issue imports use bounded SQL transaction chunks. If one fails, the
|
|
57
|
+
command exits nonzero and keeps the already committed prefix in the Dolt working
|
|
58
|
+
set; no final import-summary Dolt commit is created. Re-run stable-ID input to
|
|
59
|
+
resume only when its `updated_at` values are also preserved.
|
|
60
|
+
|
|
61
|
+
After a successful non-dry import with accepted issue or memory records, the
|
|
62
|
+
command calls `store.Commit` with an import summary regardless of the
|
|
63
|
+
`dolt.auto-commit` policy. That commit can include unrelated pending working-set
|
|
64
|
+
changes; in direct server mode, config (including memories) is excluded from
|
|
65
|
+
this commit. When commit isolation matters, run `bd doctor` before importing and
|
|
66
|
+
proceed only when its Dolt Status check reports a clean working set.
|
|
67
|
+
|
|
68
|
+
The database must already exist; initialize or bootstrap it before importing.
|
|
69
|
+
Use `--dry-run` to parse the input, apply optional title deduplication, and
|
|
70
|
+
report issue and memory counts without writing. It does not run write-time field
|
|
71
|
+
validation or predict stale skips and updates.
|
|
72
|
+
|
|
73
|
+
## Flags
|
|
17
74
|
|
|
18
|
-
|
|
75
|
+
- `--input`, `-i <file>`: read a specific file instead of a positional path.
|
|
76
|
+
- `--dry-run`: report parsed record counts without writing.
|
|
77
|
+
- `--dedup`: skip an input issue whose title exactly matches, ignoring case, an
|
|
78
|
+
existing non-closed issue. Input rows are not compared with one another; if
|
|
79
|
+
the existing-issue search fails, title filtering is skipped.
|
|
80
|
+
- `--allow-stale`: import older rows too, allowing newer local state to be
|
|
81
|
+
overwritten.
|
|
82
|
+
- `--json`: use global JSON-output mode for structured counts and IDs. The
|
|
83
|
+
`created` count is accepted issue rows, including existing rows, not only new
|
|
84
|
+
inserts.
|
package/vendor/commands/init.md
CHANGED
package/vendor/commands/label.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Manage issue labels
|
|
3
|
-
argument-hint: [command] [issue-id] [label]
|
|
3
|
+
argument-hint: "[command] [issue-id] [label]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Manage labels on beads issues. Labels provide flexible cross-cutting metadata beyond structured fields (status, priority, type).
|
package/vendor/commands/list.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Quick start guide for bd workflows (deprecated)
|
|
3
|
-
argument-hint:
|
|
3
|
+
argument-hint: ""
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
**Note:** The `bd quickstart` command is deprecated. See the [Quick Start](https://
|
|
6
|
+
**Note:** The `bd quickstart` command is deprecated. See the [Quick Start](https://beads.gascity.com/getting-started/quickstart) on the documentation site, or the in-repo source [docs/getting-started/quickstart.md](https://github.com/gastownhall/beads/blob/main/docs/getting-started/quickstart.md).
|
|
7
7
|
|
|
8
8
|
The quickstart documentation covers:
|
|
9
9
|
- Getting started with bd
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Rename the issue prefix for all issues
|
|
3
|
-
argument-hint: <new-prefix> [--dry-run]
|
|
3
|
+
argument-hint: "<new-prefix> [--dry-run]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Rename the issue prefix for all issues in the database.
|
|
@@ -9,7 +9,6 @@ Updates all issue IDs and all text references across all fields.
|
|
|
9
9
|
|
|
10
10
|
## Prefix Rules
|
|
11
11
|
|
|
12
|
-
- Max length: 8 characters
|
|
13
12
|
- Allowed: lowercase letters, numbers, hyphens
|
|
14
13
|
- Must start with a letter
|
|
15
14
|
- Must end with a hyphen (e.g., 'kw-', 'work-')
|