opencode-teammate 0.1.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/.bunli/commands.gen.ts +87 -0
- package/.github/workflows/ci.yml +31 -0
- package/.github/workflows/release.yml +140 -0
- package/.oxfmtrc.json +3 -0
- package/.oxlintrc.json +4 -0
- package/.zed/settings.json +76 -0
- package/README.md +15 -0
- package/bunli.config.ts +11 -0
- package/bunup.config.ts +31 -0
- package/package.json +36 -0
- package/src/adapters/assets/index.ts +1 -0
- package/src/adapters/assets/specifications.ts +70 -0
- package/src/adapters/beads/agents.ts +105 -0
- package/src/adapters/beads/config.ts +17 -0
- package/src/adapters/beads/index.ts +4 -0
- package/src/adapters/beads/issues.ts +156 -0
- package/src/adapters/beads/specifications.ts +55 -0
- package/src/adapters/environments/index.ts +43 -0
- package/src/adapters/environments/worktrees.ts +78 -0
- package/src/adapters/teammates/index.ts +15 -0
- package/src/assets/agent/planner.md +196 -0
- package/src/assets/command/brainstorm.md +60 -0
- package/src/assets/command/specify.md +135 -0
- package/src/assets/command/work.md +247 -0
- package/src/assets/index.ts +37 -0
- package/src/cli/commands/manifest.ts +6 -0
- package/src/cli/commands/spec/sync.ts +47 -0
- package/src/cli/commands/work.ts +110 -0
- package/src/cli/index.ts +11 -0
- package/src/plugin.ts +45 -0
- package/src/tools/i-am-done.ts +44 -0
- package/src/tools/i-am-stuck.ts +49 -0
- package/src/tools/index.ts +2 -0
- package/src/use-cases/index.ts +5 -0
- package/src/use-cases/inject-beads-issue.ts +97 -0
- package/src/use-cases/sync-specifications.ts +48 -0
- package/src/use-cases/sync-teammates.ts +35 -0
- package/src/use-cases/track-specs.ts +91 -0
- package/src/use-cases/work-on-issue.ts +110 -0
- package/src/utils/chain.ts +60 -0
- package/src/utils/frontmatter.spec.ts +491 -0
- package/src/utils/frontmatter.ts +317 -0
- package/src/utils/opencode.ts +102 -0
- package/src/utils/polling.ts +41 -0
- package/src/utils/projects.ts +35 -0
- package/src/utils/shell/client.spec.ts +106 -0
- package/src/utils/shell/client.ts +117 -0
- package/src/utils/shell/error.ts +29 -0
- package/src/utils/shell/index.ts +2 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { type ShellExpression } from "bun";
|
|
2
|
+
import { isArray, isBoolean, isEmpty, isObject, listify, shake, sift, tryit } from "radashi";
|
|
3
|
+
import { InvalidExitCodeError, ShellError } from "./error";
|
|
4
|
+
|
|
5
|
+
export interface ExecOptions {
|
|
6
|
+
quiet?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Client extends ExecuteTemplateTag {
|
|
10
|
+
[SHELL_CLIENT]: true;
|
|
11
|
+
|
|
12
|
+
// Bun $ extension
|
|
13
|
+
cwd(path: string): Client;
|
|
14
|
+
env(vars: Record<string, string>): Client;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ClientInput = Client | Bun.$;
|
|
18
|
+
export function use(input: ClientInput): Client {
|
|
19
|
+
if (input instanceof Bun.$.Shell) return create(input);
|
|
20
|
+
if (SHELL_CLIENT in input) return input;
|
|
21
|
+
|
|
22
|
+
throw new Error("Invalid client input");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function create(_$: Bun.$): Client {
|
|
26
|
+
let shell = _$;
|
|
27
|
+
|
|
28
|
+
const makeExec: ExecuteTemplateTag = <T>(
|
|
29
|
+
strings: TemplateStringsArray,
|
|
30
|
+
...expressions: (CLIPositionalArgument | CLIOptions)[]
|
|
31
|
+
): ShellPromise<T> => {
|
|
32
|
+
let injected = 0;
|
|
33
|
+
|
|
34
|
+
const parts = [...strings];
|
|
35
|
+
const extended = expressions.flatMap((expr, i): ShellExpression[] => {
|
|
36
|
+
if (!isObject(expr)) return [expr];
|
|
37
|
+
|
|
38
|
+
const options = listify(shake(expr), (key, value) => {
|
|
39
|
+
if (isBoolean(value)) return `--${key}`;
|
|
40
|
+
if (isArray(value)) return `--${key}=${sift(value).join(",")}`;
|
|
41
|
+
|
|
42
|
+
return `--${key}='${String(value)}'`;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (isEmpty(options)) return [""];
|
|
46
|
+
|
|
47
|
+
const separators = options.length - 1;
|
|
48
|
+
|
|
49
|
+
parts.splice(i + 1 + injected, 0, ...Array(separators).fill(" "));
|
|
50
|
+
injected += separators;
|
|
51
|
+
|
|
52
|
+
return options;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const template = Object.assign(parts, {
|
|
56
|
+
raw: [...parts],
|
|
57
|
+
}) as TemplateStringsArray;
|
|
58
|
+
|
|
59
|
+
const promise = shell(template, ...extended);
|
|
60
|
+
return createPromise(promise);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const client: Client = Object.assign(makeExec, {
|
|
64
|
+
[SHELL_CLIENT]: <const>true,
|
|
65
|
+
cwd(path: string): Client {
|
|
66
|
+
shell = shell.cwd(path);
|
|
67
|
+
return client;
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
env(vars: Record<string, string>): Client {
|
|
71
|
+
shell = shell.env(vars);
|
|
72
|
+
return client;
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return client;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const SHELL_CLIENT = Symbol("shell_client");
|
|
80
|
+
|
|
81
|
+
type PrimaryType = string | number | boolean | undefined;
|
|
82
|
+
type CLIPositionalArgument = string | number | boolean;
|
|
83
|
+
type CLIOptions = Record<string, PrimaryType | PrimaryType[] | undefined>;
|
|
84
|
+
type CLIExpression = CLIPositionalArgument | CLIOptions | Record<string, any>;
|
|
85
|
+
|
|
86
|
+
interface ShellPromise<U = unknown> {
|
|
87
|
+
json: <T = U>(options?: ExecOptions) => Promise<T>;
|
|
88
|
+
text: (
|
|
89
|
+
options?: ExecOptions,
|
|
90
|
+
) => Promise<[ShellError, string | undefined] | [undefined, string | undefined]>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function createPromise(shell: Bun.$.ShellPromise): ShellPromise {
|
|
94
|
+
return {
|
|
95
|
+
json: async <T = unknown>(options?: ExecOptions) => {
|
|
96
|
+
const output = await shell.quiet(options?.quiet).nothrow();
|
|
97
|
+
const [notJson, result] = await tryit(() => output.json())();
|
|
98
|
+
if (result) return result as T;
|
|
99
|
+
|
|
100
|
+
throw new ShellError(output, { cause: notJson });
|
|
101
|
+
},
|
|
102
|
+
text: async (options?: ExecOptions) => {
|
|
103
|
+
const output = await shell.quiet(options?.quiet).nothrow();
|
|
104
|
+
const out = output.stdout.toString().trim() || undefined;
|
|
105
|
+
|
|
106
|
+
if (output.exitCode !== 0)
|
|
107
|
+
return <const>[new ShellError(output, { cause: new InvalidExitCodeError(output) }), out];
|
|
108
|
+
|
|
109
|
+
return <const>[undefined, out];
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
type ExecuteTemplateTag = <T>(
|
|
115
|
+
strings: TemplateStringsArray,
|
|
116
|
+
...expressions: CLIExpression[]
|
|
117
|
+
) => ShellPromise<T>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class ShellError extends Error {
|
|
2
|
+
private readonly output: Bun.$.ShellOutput;
|
|
3
|
+
|
|
4
|
+
constructor(
|
|
5
|
+
output: Bun.$.ShellOutput,
|
|
6
|
+
options: {
|
|
7
|
+
cause?: Error;
|
|
8
|
+
},
|
|
9
|
+
) {
|
|
10
|
+
const stdout = output.stdout.toString().trim() || undefined;
|
|
11
|
+
const stderr = output.stderr.toString().trim() || undefined;
|
|
12
|
+
const message = stderr ?? stdout ?? "Unknown shell error";
|
|
13
|
+
|
|
14
|
+
super(message, { cause: options.cause });
|
|
15
|
+
|
|
16
|
+
this.output = output;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class InvalidExitCodeError extends Error {
|
|
21
|
+
public readonly exitCode: number;
|
|
22
|
+
|
|
23
|
+
constructor(output: Bun.$.ShellOutput) {
|
|
24
|
+
const exitCode = output.exitCode;
|
|
25
|
+
super(`Exited with code '${exitCode}'`);
|
|
26
|
+
|
|
27
|
+
this.exitCode = exitCode;
|
|
28
|
+
}
|
|
29
|
+
}
|