opencode-agent-skills-md 1.0.1 → 1.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/dist/cli.mjs +770 -0
- package/dist/plugin.mjs +1138 -0
- package/dist/src/cli/config.d.ts +144 -0
- package/dist/src/cli/install.d.ts +33 -0
- package/dist/src/cli/main.d.ts +11 -0
- package/dist/src/cli/real-fs.d.ts +6 -0
- package/dist/src/cli/status.d.ts +34 -0
- package/dist/src/cli/uninstall.d.ts +22 -0
- package/dist/src/host.d.ts +51 -0
- package/dist/src/index.d.ts +17 -0
- package/dist/src/plugin.d.ts +35 -0
- package/dist/src/sdk.d.ts +51 -0
- package/dist/src/tools.d.ts +86 -0
- package/package.json +48 -18
- package/.beads/.local_version +0 -1
- package/.beads/README.md +0 -81
- package/.beads/config.yaml +0 -61
- package/.beads/deletions.jsonl +0 -1
- package/.beads/issues.jsonl +0 -64
- package/.beads/metadata.json +0 -4
- package/.gitattributes +0 -3
- package/.github/CODEOWNERS +0 -1
- package/.github/copilot-instructions.md +0 -78
- package/.github/dependabot.yml +0 -13
- package/.github/workflows/release.yml +0 -51
- package/.opencode/command/test-compaction.md +0 -9
- package/.opencode/command/test-find-skills.md +0 -7
- package/.opencode/command/test-read-skill-file.md +0 -14
- package/.opencode/command/test-run-skill-script.md +0 -13
- package/.opencode/command/test-skills.md +0 -14
- package/.opencode/command/test-use-skill.md +0 -10
- package/.opencode/skills/git-helper/SKILL.md +0 -65
- package/.opencode/skills/test-skill/SKILL.md +0 -43
- package/.opencode/skills/test-skill/example-config.json +0 -16
- package/.opencode/skills/test-skill/helper-docs.md +0 -29
- package/.opencode/skills/test-skill/scripts/echo-args +0 -14
- package/.opencode/skills/test-skill/scripts/greet +0 -6
- package/AGENTS.md +0 -43
- package/CHANGELOG.md +0 -178
- package/Justfile +0 -39
- package/README.md +0 -220
- package/openspec/changes/archive/2026-06-14-skills-core-decouple/specs/core-decoupling/spec.md +0 -74
- package/openspec/changes/archive/2026-06-14-skills-core-decouple/tasks.md +0 -64
- package/openspec/changes/archive/2026-06-14-skills-core-decouple/verify-report.md +0 -75
- package/openspec/changes/archive/2026-06-17-fix-skill-loading-regression/apply-progress.md +0 -136
- package/openspec/changes/archive/2026-06-17-fix-skill-loading-regression/archive-report.md +0 -77
- package/openspec/changes/archive/2026-06-17-fix-skill-loading-regression/design.md +0 -89
- package/openspec/changes/archive/2026-06-17-fix-skill-loading-regression/proposal.md +0 -65
- package/openspec/changes/archive/2026-06-17-fix-skill-loading-regression/specs/core-decoupling/spec.md +0 -77
- package/openspec/changes/archive/2026-06-17-fix-skill-loading-regression/tasks.md +0 -65
- package/openspec/changes/archive/2026-06-17-fix-skill-loading-regression/verify-report.md +0 -165
- package/openspec/specs/core-decoupling/spec.md +0 -110
- package/packages/core/package.json +0 -30
- package/packages/core/src/content.d.ts +0 -16
- package/packages/core/src/content.ts +0 -30
- package/packages/core/src/debug.ts +0 -16
- package/packages/core/src/discovery.d.ts +0 -86
- package/packages/core/src/discovery.ts +0 -257
- package/packages/core/src/index.d.ts +0 -20
- package/packages/core/src/index.ts +0 -55
- package/packages/core/src/match.d.ts +0 -19
- package/packages/core/src/match.ts +0 -75
- package/packages/core/src/parse.d.ts +0 -26
- package/packages/core/src/parse.ts +0 -141
- package/packages/core/src/scripts.d.ts +0 -17
- package/packages/core/src/scripts.ts +0 -79
- package/packages/core/src/search.d.ts +0 -83
- package/packages/core/src/search.ts +0 -188
- package/packages/core/src/types.d.ts +0 -82
- package/packages/core/src/types.ts +0 -131
- package/packages/core/src/walk.ts +0 -109
- package/packages/core/tests/agnostic.test.ts +0 -346
- package/packages/core/tests/content.test.ts +0 -65
- package/packages/core/tests/discovery.test.ts +0 -370
- package/packages/core/tests/package-boundary.test.ts +0 -310
- package/packages/core/tests/parse-trigger.test.ts +0 -282
- package/packages/core/tests/search.test.ts +0 -374
- package/packages/core/tests/subpath.test.ts +0 -87
- package/packages/core/tsconfig.json +0 -10
- package/packages/opencode-agent-skills-md/package.json +0 -66
- package/packages/opencode-agent-skills-md/rolldown.config.js +0 -47
- package/packages/opencode-agent-skills-md/tests/cli-commands.test.ts +0 -1423
- package/packages/opencode-agent-skills-md/tests/e2e/startup-smoke.test.ts +0 -66
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/home/.claude/skills/claude-user-only-skill/SKILL.md +0 -8
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/home/.config/opencode/skills/shared-skill/SKILL.md +0 -8
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/home/.config/opencode/skills/user-only-skill/SKILL.md +0 -8
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/project/.claude/skills/claude-project-only-skill/SKILL.md +0 -8
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/project/.opencode/skills/go-tester/SKILL.md +0 -12
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/project/.opencode/skills/nested/team/nested-skill/SKILL.md +0 -8
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/project/.opencode/skills/rust-tester/SKILL.md +0 -11
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/project/.opencode/skills/scripted-skill/SKILL.md +0 -8
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/project/.opencode/skills/scripted-skill/bin/echo.sh +0 -2
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/project/.opencode/skills/scripted-skill/docs/reference.md +0 -1
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/project/.opencode/skills/shared-skill/SKILL.md +0 -8
- package/packages/opencode-agent-skills-md/tests/fixtures/skills/project/.opencode/skills/using-superpowers/SKILL.md +0 -8
- package/packages/opencode-agent-skills-md/tests/integration/helpers/mock-opencode.ts +0 -114
- package/packages/opencode-agent-skills-md/tests/integration/plugin.test.ts +0 -316
- package/packages/opencode-agent-skills-md/tests/integration/skill-discovery.test.ts +0 -315
- package/packages/opencode-agent-skills-md/tests/opencode/host.test.ts +0 -179
- package/packages/opencode-agent-skills-md/tests/opencode/plugin.test.ts +0 -551
- package/packages/opencode-agent-skills-md/tests/opencode/subpath.test.ts +0 -66
- package/packages/opencode-agent-skills-md/tests/opencode/tools.test.ts +0 -213
- package/packages/opencode-agent-skills-md/tests/package-boundary.test.ts +0 -345
- package/packages/opencode-agent-skills-md/tests/tools-security.test.ts +0 -72
- package/packages/opencode-agent-skills-md/tsconfig.build.json +0 -11
- package/packages/opencode-agent-skills-md/tsconfig.json +0 -10
- package/plans/001-ci-gate.md +0 -177
- package/plans/002-is-path-safe.md +0 -243
- package/plans/003-escape-prompts.md +0 -310
- package/plans/004-test-security-paths.md +0 -228
- package/plans/005-stop-swallowing-errors.md +0 -246
- package/plans/006-preserve-jsonc-commas.md +0 -144
- package/plans/007-write-before-purge.md +0 -144
- package/plans/008-reuse-walkdir-for-list-skill-files.md +0 -164
- package/plans/README.md +0 -43
- package/pnpm-workspace.yaml +0 -6
- package/tests/workspace.test.ts +0 -367
- package/tsconfig.json +0 -15
- /package/{packages/opencode-agent-skills-md/src → src}/cli/config.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/cli/install.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/cli/main.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/cli/real-fs.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/cli/status.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/cli/uninstall.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/host.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/index.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/plugin.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/sdk.ts +0 -0
- /package/{packages/opencode-agent-skills-md/src → src}/tools.ts +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/** npm package name for this plugin. */
|
|
2
|
+
export declare const PLUGIN_NAME = "opencode-agent-skills-md";
|
|
3
|
+
/** Maximum number of CLI-created backups retained in the config directory. */
|
|
4
|
+
export declare const BACKUP_LIMIT = 3;
|
|
5
|
+
/** Filename used for the OpenCode global config (preferred). */
|
|
6
|
+
export declare const CONFIG_FILE_BASENAME = "opencode";
|
|
7
|
+
/** Subdirectory under the user config root that holds `opencode.json`. */
|
|
8
|
+
export declare const OPENCODE_CONFIG_SUBDIR = "opencode";
|
|
9
|
+
/**
|
|
10
|
+
* Minimal synchronous filesystem surface that the `oas` CLI commands rely
|
|
11
|
+
* on. The CLI never imports `node:fs` directly — every disk operation
|
|
12
|
+
* goes through this boundary so tests can run fully in memory.
|
|
13
|
+
*
|
|
14
|
+
* The methods intentionally mirror only the operations the CLI needs:
|
|
15
|
+
* helpers that demand more (e.g. `rmSync` for recursive directory
|
|
16
|
+
* removal during uninstall purge) live outside this interface and call
|
|
17
|
+
* `node:fs` directly at the call site, where they belong.
|
|
18
|
+
*/
|
|
19
|
+
export interface CliFs {
|
|
20
|
+
readFileSync(path: string): string;
|
|
21
|
+
writeFileSync(path: string, content: string): void;
|
|
22
|
+
renameSync(from: string, to: string): void;
|
|
23
|
+
copyFileSync(from: string, to: string): void;
|
|
24
|
+
unlinkSync(path: string): void;
|
|
25
|
+
mkdirSync(path: string, opts?: {
|
|
26
|
+
recursive?: boolean;
|
|
27
|
+
}): void;
|
|
28
|
+
readdirSync(path: string): string[];
|
|
29
|
+
existsSync(path: string): boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface ResolvedConfigPath {
|
|
32
|
+
/** Absolute path to use for reads/writes. `.json` by default. */
|
|
33
|
+
path: string;
|
|
34
|
+
/** "json" when the resolved file ends in `.json`, "jsonc" otherwise. */
|
|
35
|
+
format: "json" | "jsonc";
|
|
36
|
+
/** True when `path` already existed on disk before resolution. */
|
|
37
|
+
existed: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the parent directory that holds the global OpenCode config.
|
|
41
|
+
* `$OPENCODE_CONFIG_DIR` wins; otherwise we fall back to
|
|
42
|
+
* `$HOME/.config/opencode` (or `os.homedir()` as a last-resort).
|
|
43
|
+
*
|
|
44
|
+
* Exposed separately so tests and the rotation helper can reuse it
|
|
45
|
+
* without re-deriving the precedence rules.
|
|
46
|
+
*/
|
|
47
|
+
export declare const resolveConfigDir: (env?: NodeJS.ProcessEnv) => string;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the global OpenCode config file path.
|
|
50
|
+
*
|
|
51
|
+
* Precedence:
|
|
52
|
+
* 1. If `$OPENCODE_CONFIG_DIR/opencode.json` exists, use it.
|
|
53
|
+
* 2. Else if `$OPENCODE_CONFIG_DIR/opencode.jsonc` exists, use it.
|
|
54
|
+
* 3. Else fall back to `$HOME/.config/opencode/opencode.json`, then `.jsonc`.
|
|
55
|
+
* 4. If nothing exists, return the preferred target `.json` in the
|
|
56
|
+
* resolved directory so `install` knows where to create the file.
|
|
57
|
+
*
|
|
58
|
+
* `.json` always wins over `.jsonc` when both exist.
|
|
59
|
+
*/
|
|
60
|
+
export declare const resolveGlobalConfigPath: (fs: CliFs, env?: NodeJS.ProcessEnv) => ResolvedConfigPath;
|
|
61
|
+
/**
|
|
62
|
+
* Strip JSONC-style comments and trailing commas, then parse with `JSON.parse`.
|
|
63
|
+
*
|
|
64
|
+
* Returns `{}` for empty input or whitespace-only input.
|
|
65
|
+
* **Throws** on malformed JSON — callers must handle the error to avoid
|
|
66
|
+
* silently overwriting a corrupt config with an empty one.
|
|
67
|
+
*/
|
|
68
|
+
export declare const parseJsonc: (text: string) => Record<string, unknown>;
|
|
69
|
+
/**
|
|
70
|
+
* True when `entry` is a string that resolves to this plugin by base name.
|
|
71
|
+
* Matches `opencode-agent-skills-md` and any `opencode-agent-skills-md@<spec>`
|
|
72
|
+
* variant. Non-string entries (legacy object-form leftover) return false.
|
|
73
|
+
*/
|
|
74
|
+
export declare const matchesPlugin: (entry: unknown) => boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Coerce the raw value of `config.plugin` into a clean string array.
|
|
77
|
+
*
|
|
78
|
+
* Handles:
|
|
79
|
+
* - `undefined` / `null` → `[]`
|
|
80
|
+
* - array of strings (or mixed) → only the string entries
|
|
81
|
+
* - the broken object form `{ "<name>": ... }` → the keys (in declaration order)
|
|
82
|
+
* - any other non-object, non-array shape → `[]` (doctor surfaces it)
|
|
83
|
+
*/
|
|
84
|
+
export declare const normalizePlugin: (raw: unknown) => string[];
|
|
85
|
+
/**
|
|
86
|
+
* Dedupe the plugin list by base name (the part before the first `@`),
|
|
87
|
+
* keeping the LAST occurrence of each base. Any `opencode-agent-skills-md`
|
|
88
|
+
* entries are removed entirely so the install flow can append one fresh
|
|
89
|
+
* entry at the end without leaving stale versions behind.
|
|
90
|
+
*
|
|
91
|
+
* Order is preserved for the surviving entries (last-wins per base).
|
|
92
|
+
*/
|
|
93
|
+
export declare const dedupePlugins: (entries: readonly string[]) => string[];
|
|
94
|
+
/**
|
|
95
|
+
* Build the npm specifier we will write into `plugin[]`:
|
|
96
|
+
* `"opencode-agent-skills-md"` when no version is supplied, otherwise
|
|
97
|
+
* `"opencode-agent-skills-md@<version>"`. Empty / whitespace-only versions
|
|
98
|
+
* are treated as "no version".
|
|
99
|
+
*/
|
|
100
|
+
export declare const buildSpecifier: (version?: string) => string;
|
|
101
|
+
/**
|
|
102
|
+
* If `configPath` already exists, copy it next to itself as a timestamped
|
|
103
|
+
* sibling and prune older CLI-created backups so at most `BACKUP_LIMIT`
|
|
104
|
+
* survive (newest first). Returns the backup path, or `null` when no
|
|
105
|
+
* backup was needed (file missing or not writable).
|
|
106
|
+
*/
|
|
107
|
+
export declare const backupIfWritable: (configPath: string, fs: CliFs) => string | null;
|
|
108
|
+
/**
|
|
109
|
+
* Prune CLI-created backups of `configPath`, keeping only the newest
|
|
110
|
+
* `limit` siblings (lexical order on the timestamp suffix is fine because
|
|
111
|
+
* the stamp is fixed-width and ISO-8601-derived).
|
|
112
|
+
*/
|
|
113
|
+
export declare const rotateBackups: (configPath: string, limit: number, fs: CliFs) => void;
|
|
114
|
+
/**
|
|
115
|
+
* Write `content` to `targetPath` via a temp sibling + rename. The
|
|
116
|
+
* rename is atomic on POSIX (and best-effort on Windows), so a crashed
|
|
117
|
+
* CLI never leaves a half-written config behind. Any parent directories
|
|
118
|
+
* are created with `{ recursive: true }` so first-run installs Just Work.
|
|
119
|
+
*/
|
|
120
|
+
export declare const writeAtomically: (targetPath: string, content: string, fs: CliFs) => void;
|
|
121
|
+
export interface LoadedConfig {
|
|
122
|
+
/** Absolute path the loader used (existing or newly-targeted). */
|
|
123
|
+
path: string;
|
|
124
|
+
/** Parsed config object — `{}` if the file was absent or unreadable. */
|
|
125
|
+
config: Record<string, unknown>;
|
|
126
|
+
/** Whether the file existed on disk before loading. */
|
|
127
|
+
existed: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* If the existing config file is malformed JSON, this contains the
|
|
130
|
+
* error message. Commands must check this and abort rather than
|
|
131
|
+
* silently overwriting the corrupt file with an empty config.
|
|
132
|
+
*/
|
|
133
|
+
parseError?: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Resolve the global config path, read it (if it exists), and parse it.
|
|
137
|
+
* Missing files yield `config = {}` and `existed = false` so the install
|
|
138
|
+
* flow can treat "fresh install" and "already installed" the same way.
|
|
139
|
+
*
|
|
140
|
+
* Malformed JSONC is surfaced via `LoadedConfig.parseError`. Commands MUST
|
|
141
|
+
* check this field and abort instead of silently overwriting the user's
|
|
142
|
+
* corrupt config with an empty one.
|
|
143
|
+
*/
|
|
144
|
+
export declare const loadGlobalConfig: (fs: CliFs, env?: NodeJS.ProcessEnv) => LoadedConfig;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type CliFs } from "./config";
|
|
2
|
+
export interface InstallOptions {
|
|
3
|
+
/** Optional version pin (e.g. `"1.2.3"`, `"latest"`). Omit for bare specifier. */
|
|
4
|
+
version?: string;
|
|
5
|
+
/** Plan the change and print it without writing. */
|
|
6
|
+
dryRun?: boolean;
|
|
7
|
+
/** Reserved for future confirmation prompts; accepted but unused for now. */
|
|
8
|
+
yes?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface InstallResult {
|
|
11
|
+
/** Outcome of the command. */
|
|
12
|
+
status: "wrote" | "planned" | "noop";
|
|
13
|
+
/** Resolved config path (existing or newly-targeted). */
|
|
14
|
+
path: string;
|
|
15
|
+
/** Specifier that was added (or would be added under `--dry-run`). */
|
|
16
|
+
specifier: string;
|
|
17
|
+
/** Backup path created before the write, or `null` when no backup was needed. */
|
|
18
|
+
backup: string | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Run `oas install` against the global OpenCode config.
|
|
22
|
+
*
|
|
23
|
+
* Steps: load → normalize → drop existing oas variants → dedupe surviving
|
|
24
|
+
* entries → append one requested specifier → backup → atomic write. The
|
|
25
|
+
* backup is a timestamped sibling of the config file; rotation to
|
|
26
|
+
* `BACKUP_LIMIT` is handled inside `backupIfWritable`.
|
|
27
|
+
*
|
|
28
|
+
* Idempotency: re-running with the same specifier resolves to a `noop`
|
|
29
|
+
* result without touching disk. A malformed config triggers an error so
|
|
30
|
+
* the user can fix the JSONC instead of silently losing it to an empty
|
|
31
|
+
* overwrite.
|
|
32
|
+
*/
|
|
33
|
+
export declare const runInstall: (opts?: InstallOptions, fs?: CliFs) => InstallResult;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export interface MainResult {
|
|
3
|
+
command: string | null;
|
|
4
|
+
exitCode: 0 | 1 | 2;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Pure(ish) dispatcher: takes argv, runs the matching command, sets
|
|
8
|
+
* `process.exitCode`, and returns a structured result so tests can assert
|
|
9
|
+
* without reading the exit code.
|
|
10
|
+
*/
|
|
11
|
+
export declare const runMain: (argv?: readonly string[]) => MainResult;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type CliFs } from "./config";
|
|
2
|
+
export interface StatusResult {
|
|
3
|
+
/** Whether an `opencode-agent-skills-md` entry is present in `plugin`. */
|
|
4
|
+
installed: boolean;
|
|
5
|
+
/** Resolved config path the loader used. */
|
|
6
|
+
path: string;
|
|
7
|
+
/** Detected on-disk format. */
|
|
8
|
+
format: "json" | "jsonc";
|
|
9
|
+
/** The active specifier, or `null` when not installed. */
|
|
10
|
+
specifier: string | null;
|
|
11
|
+
/** Other plugin entries preserved alongside the oas one. */
|
|
12
|
+
extras: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface DoctorResult {
|
|
15
|
+
/** True when there are zero blocking issues. */
|
|
16
|
+
ok: boolean;
|
|
17
|
+
/** Blocking problems — the install flow will not work until they are fixed. */
|
|
18
|
+
issues: string[];
|
|
19
|
+
/** Non-blocking advisories — install may still work. */
|
|
20
|
+
warnings: string[];
|
|
21
|
+
/** Informational notes about what was checked. */
|
|
22
|
+
info: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read-only status probe. Prints a human-readable report to stdout and
|
|
26
|
+
* returns the same data as a structured result so callers (including
|
|
27
|
+
* `main.ts` and tests) can consume it without parsing the message.
|
|
28
|
+
*/
|
|
29
|
+
export declare const runStatus: (fs?: CliFs) => StatusResult;
|
|
30
|
+
/**
|
|
31
|
+
* Health checks. The function does not exit on its own — it returns a
|
|
32
|
+
* `DoctorResult` and `main.ts` maps `ok === false` to exit code 1.
|
|
33
|
+
*/
|
|
34
|
+
export declare const runDoctor: (fs?: CliFs, env?: NodeJS.ProcessEnv) => DoctorResult;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type CliFs } from "./config";
|
|
2
|
+
export interface UninstallOptions {
|
|
3
|
+
/** Also remove the runtime cache and the plugin's own config dir. */
|
|
4
|
+
purge?: boolean;
|
|
5
|
+
/** Plan the change and print it without writing. */
|
|
6
|
+
dryRun?: boolean;
|
|
7
|
+
/** Reserved for future confirmation prompts; accepted but unused for now. */
|
|
8
|
+
yes?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface UninstallResult {
|
|
11
|
+
status: "wrote" | "planned" | "noop";
|
|
12
|
+
path: string;
|
|
13
|
+
/** Plugin entries that were (or would be) removed from the config. */
|
|
14
|
+
removed: string[];
|
|
15
|
+
/** Cache / config dirs removed under `--purge`. Empty when `--purge` was not set. */
|
|
16
|
+
purged: string[];
|
|
17
|
+
}
|
|
18
|
+
/** Bun/npm-style cache path where the plugin gets installed at runtime. */
|
|
19
|
+
export declare const cachePath: (env?: NodeJS.ProcessEnv) => string;
|
|
20
|
+
/** Plugin's own XDG config dir (separate from the OpenCode config it edits). */
|
|
21
|
+
export declare const pluginConfigPath: (env?: NodeJS.ProcessEnv) => string;
|
|
22
|
+
export declare const runUninstall: (opts?: UninstallOptions, fs?: CliFs) => UninstallResult;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode host adapter.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the OpenCode SDK client (`PluginInput["client"]`) and provides
|
|
5
|
+
* a bounded surface for content injection, session context, and filesystem
|
|
6
|
+
* access consumed by the plugin and skill tools.
|
|
7
|
+
*
|
|
8
|
+
* The boundary contracts (`SkillHostClient`, `SkillHostSession`,
|
|
9
|
+
* `SkillHostContext`) are declared in the `opencode-agent-skills-md-core`
|
|
10
|
+
* package per spec R2; this module IMPLEMENTS them over the OpenCode SDK
|
|
11
|
+
* client plus `node:fs/promises`. No other package may declare a concrete
|
|
12
|
+
* implementation — the plugin package owns exactly one.
|
|
13
|
+
*/
|
|
14
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
15
|
+
import type { SkillHostClient, SkillHostSession } from "opencode-agent-skills-md-core";
|
|
16
|
+
/** Concrete OpenCode client (the SDK's generated client type). */
|
|
17
|
+
export type OpencodeClient = PluginInput["client"];
|
|
18
|
+
/**
|
|
19
|
+
* File access surface exposed alongside the host client. Tools
|
|
20
|
+
* consume these via the host instead of importing `node:fs/promises` so the
|
|
21
|
+
* boundary stays explicit and easy to stub in tests.
|
|
22
|
+
*/
|
|
23
|
+
export interface OpencodeHostFileAccess {
|
|
24
|
+
readFile(path: string): Promise<string>;
|
|
25
|
+
readdir(path: string): Promise<string[]>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Concrete OpenCode client surface.
|
|
29
|
+
*
|
|
30
|
+
* Structurally identical to the core boundary contract `SkillHostClient`
|
|
31
|
+
* (it implements all four methods). The alias is preserved for backward
|
|
32
|
+
* compatibility with prior plugin-package consumers and to make the
|
|
33
|
+
* OpenCode-specific implementation obvious at use sites.
|
|
34
|
+
*/
|
|
35
|
+
export type OpencodeSkillHostClient = SkillHostClient;
|
|
36
|
+
/**
|
|
37
|
+
* The full host surface: a bounded client plus a session factory. Each call
|
|
38
|
+
* to `session(id)` returns a `SkillHostSession` carrying only the id the core
|
|
39
|
+
* needs to thread through host calls.
|
|
40
|
+
*/
|
|
41
|
+
export interface OpencodeSkillHost {
|
|
42
|
+
client: OpencodeSkillHostClient;
|
|
43
|
+
session: (id: string) => SkillHostSession;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build an `OpencodeSkillHost` over the supplied OpenCode SDK client.
|
|
47
|
+
*
|
|
48
|
+
* The host is the only place in the codebase that touches the SDK's
|
|
49
|
+
* `client.session.prompt` and `client.session.messages` methods.
|
|
50
|
+
*/
|
|
51
|
+
export declare const createOpencodeSkillHost: (client: OpencodeClient) => OpencodeSkillHost;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode host adapter — root entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the plugin factory as the package's default export so the
|
|
5
|
+
* `rolldown` build can target this file directly. The root `src/plugin.ts`
|
|
6
|
+
* shim forwards to this module to preserve the legacy import path while
|
|
7
|
+
* `package.json` still resolves `dist/plugin.mjs` to the package main.
|
|
8
|
+
*
|
|
9
|
+
* Public surface:
|
|
10
|
+
* - default export: SkillsPlugin (the @opencode-ai/plugin Plugin factory)
|
|
11
|
+
* - named exports: SkillsPlugin, createOpencodeSkillHost
|
|
12
|
+
*/
|
|
13
|
+
import { SkillsPlugin } from "./plugin";
|
|
14
|
+
export { SkillsPlugin };
|
|
15
|
+
export { createOpencodeSkillHost } from "./host";
|
|
16
|
+
export type { OpencodeClient, OpencodeSkillHost, OpencodeSkillHostClient, OpencodeHostFileAccess, } from "./host";
|
|
17
|
+
export default SkillsPlugin;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Agent Skills Plugin (host adapter).
|
|
3
|
+
*
|
|
4
|
+
* The plugin factory builds the host over the OpenCode SDK client, composes
|
|
5
|
+
* the four skill tools, and wires the chat.message and event hooks. The
|
|
6
|
+
* keyword matcher and session/loaded-skill bookkeeping are the only
|
|
7
|
+
* adapter-specific logic; everything else delegates to the portable core
|
|
8
|
+
* or the host.
|
|
9
|
+
*/
|
|
10
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
11
|
+
import { type SkillSummary } from "opencode-agent-skills-md-core";
|
|
12
|
+
/**
|
|
13
|
+
* Render the matched-skill synthetic injection that asks the model to
|
|
14
|
+
* evaluate which of the matched skills (if any) it should activate.
|
|
15
|
+
*
|
|
16
|
+
* Each skill line carries a sub-line `trigger: <text>` whenever the
|
|
17
|
+
* skill has a non-empty `trigger`, so the model knows which user
|
|
18
|
+
* phrases should activate it. Skills with no trigger render exactly as
|
|
19
|
+
* before (`- name: description`).
|
|
20
|
+
*/
|
|
21
|
+
export declare const formatMatchedSkillsInjection: (matchedSkills: SkillSummary[]) => string;
|
|
22
|
+
/**
|
|
23
|
+
* Lightweight keyword matching to replace ML embeddings.
|
|
24
|
+
*
|
|
25
|
+
* Per-token contribution:
|
|
26
|
+
* - name hit = 2x
|
|
27
|
+
* - trigger hit = 1.5x
|
|
28
|
+
* - desc hit = 1x
|
|
29
|
+
*
|
|
30
|
+
* The trigger tier (1.5x) sits between name (2x) and description (1x)
|
|
31
|
+
* so a trigger-matched skill outranks a description-matched skill at
|
|
32
|
+
* the same query, but a name-matched skill still wins overall.
|
|
33
|
+
*/
|
|
34
|
+
export declare const matchSkillsByKeyword: (userMessage: string, availableSkills: SkillSummary[]) => SkillSummary[];
|
|
35
|
+
export declare const SkillsPlugin: Plugin;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local interfaces for the OpenCode hook payload shapes this plugin
|
|
3
|
+
* actually consumes.
|
|
4
|
+
*
|
|
5
|
+
* These intentionally mirror only the narrow slice of the SDK types
|
|
6
|
+
* the plugin reads — defining them locally (rather than importing the
|
|
7
|
+
* SDK's broad `UserMessage` / `Part` / `Event` types) keeps the
|
|
8
|
+
* adapter resilient to upstream shape changes and lets us narrow
|
|
9
|
+
* untyped runtime payloads safely.
|
|
10
|
+
*
|
|
11
|
+
* Internal only: this module is not re-exported from `src/index.ts`.
|
|
12
|
+
*/
|
|
13
|
+
/** A text-bearing chat part. `text` is optional because some parts carry metadata only. */
|
|
14
|
+
export interface ChatTextPart {
|
|
15
|
+
type: "text";
|
|
16
|
+
text?: string;
|
|
17
|
+
synthetic?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Minimal shape of the `chat.message` output payload the plugin reads. */
|
|
20
|
+
export interface ChatMessageOutput {
|
|
21
|
+
message: {
|
|
22
|
+
sessionID: string;
|
|
23
|
+
model?: string;
|
|
24
|
+
agent?: string;
|
|
25
|
+
};
|
|
26
|
+
parts: unknown[];
|
|
27
|
+
}
|
|
28
|
+
/** `session.compacted` event payload. */
|
|
29
|
+
export interface SessionCompactedEvent {
|
|
30
|
+
type: "session.compacted";
|
|
31
|
+
properties: {
|
|
32
|
+
sessionID: string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** `session.deleted` event payload. */
|
|
36
|
+
export interface SessionDeletedEvent {
|
|
37
|
+
type: "session.deleted";
|
|
38
|
+
properties: {
|
|
39
|
+
info: {
|
|
40
|
+
id: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Discriminated union of the session lifecycle events this plugin handles. */
|
|
45
|
+
export type SessionEvent = SessionCompactedEvent | SessionDeletedEvent;
|
|
46
|
+
/** Type guard: narrows `unknown` to `ChatTextPart` when `part.type === "text"`. */
|
|
47
|
+
export declare const isChatTextPart: (part: unknown) => part is ChatTextPart;
|
|
48
|
+
/** Type guard: narrows `unknown` to `SessionCompactedEvent`. */
|
|
49
|
+
export declare const isSessionCompactedEvent: (event: unknown) => event is SessionCompactedEvent;
|
|
50
|
+
/** Type guard: narrows `unknown` to `SessionDeletedEvent`. */
|
|
51
|
+
export declare const isSessionDeletedEvent: (event: unknown) => event is SessionDeletedEvent;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode tool factories.
|
|
3
|
+
*
|
|
4
|
+
* The four skill tools (get_available_skills, read_skill_file, run_skill_script,
|
|
5
|
+
* use_skill) compose the portable core engine with the OpenCode host. Tools
|
|
6
|
+
* consume the host's bounded client surface; they never reference the
|
|
7
|
+
* OpenCode SDK client or the `node:fs` module directly.
|
|
8
|
+
*
|
|
9
|
+
* `createSkillTools(host, $, directory)` returns the four tool factories
|
|
10
|
+
* pre-bound to the host, the shell runner, and the project directory. The
|
|
11
|
+
* plugin instantiates them at registration time.
|
|
12
|
+
*/
|
|
13
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
14
|
+
import { tool } from "@opencode-ai/plugin";
|
|
15
|
+
import type { OpencodeSkillHost } from "./host";
|
|
16
|
+
/** @internal - exported for testing */
|
|
17
|
+
export declare const _escapeXml: (s: string) => string;
|
|
18
|
+
/** @internal - exported for testing */
|
|
19
|
+
export declare const _escapeShellArg: (arg: string) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Portable return type for tool factory consts.
|
|
22
|
+
*
|
|
23
|
+
* The `tool()` helper captures the Zod shape of its `args` parameter in the
|
|
24
|
+
* generic parameter, so the inferred return type of each factory leaks Zod
|
|
25
|
+
* types. When TypeScript emits `.d.ts` files for the package, that generic
|
|
26
|
+
* instantiation cannot be named portably across zod versions. Annotating
|
|
27
|
+
* the return type as `ReturnType<typeof tool>` erases the per-call Zod
|
|
28
|
+
* shape and leaves a stable, portable declaration for downstream consumers.
|
|
29
|
+
*/
|
|
30
|
+
type SkillTool = ReturnType<typeof tool>;
|
|
31
|
+
/**
|
|
32
|
+
* Tool translation guide for skills written for Claude Code.
|
|
33
|
+
* Injected into skill content to help the AI use OpenCode equivalents.
|
|
34
|
+
*/
|
|
35
|
+
export declare const toolTranslation = "<tool-translation>\nThis skill may reference Claude Code tools. Use OpenCode equivalents:\n- TodoWrite/TodoRead -> todowrite/todoread\n- Task (subagents) -> task tool with subagent_type parameter\n- Skill tool -> use_skill tool\n- Read/Write/Edit/Bash/Glob/Grep/WebFetch -> lowercase (read/write/edit/bash/glob/grep/webfetch)\n</tool-translation>";
|
|
36
|
+
export interface SkillTools {
|
|
37
|
+
GetAvailableSkills: ReturnType<typeof GetAvailableSkills>;
|
|
38
|
+
ReadSkillFile: ReturnType<typeof ReadSkillFile>;
|
|
39
|
+
RunSkillScript: ReturnType<typeof RunSkillScript>;
|
|
40
|
+
UseSkill: ReturnType<typeof UseSkill>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Callback fired by `UseSkill` after a successful load so the host can
|
|
44
|
+
* update its session-level bookkeeping (loaded-skill set, TUI icon, etc.).
|
|
45
|
+
* The core never assumes a callback is registered; missing it must not
|
|
46
|
+
* break the load.
|
|
47
|
+
*/
|
|
48
|
+
export type OnSkillLoaded = (sessionID: string, skillName: string) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Build the four skill tool factories bound to the host, shell, and
|
|
51
|
+
* project directory. The returned object is what the plugin registers
|
|
52
|
+
* under its `tool` hook.
|
|
53
|
+
*
|
|
54
|
+
* The optional `onSkillLoaded` callback is threaded through to `UseSkill`
|
|
55
|
+
* so a successful load can update host session state (e.g., the loaded-
|
|
56
|
+
* skill set used to suppress duplicate match injection in `chat.message`).
|
|
57
|
+
*/
|
|
58
|
+
export declare const createSkillTools: (host: OpencodeSkillHost, $: PluginInput["$"], directory: string, onSkillLoaded?: OnSkillLoaded) => SkillTools;
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a skill by name, or return a "not found" message with a
|
|
61
|
+
* close-match suggestion.
|
|
62
|
+
*
|
|
63
|
+
* Centralizes the duplicated resolve-then-suggest pattern that
|
|
64
|
+
* `use_skill`, `read_skill_file`, and `run_skill_script` all need.
|
|
65
|
+
* Returning a single `string` keeps the call site trivial:
|
|
66
|
+
*
|
|
67
|
+
* - skill found → returns `skill.name`
|
|
68
|
+
* - skill missing, suggestion → `Skill "<name>" not found. Did you mean "<suggestion>"?`
|
|
69
|
+
* - skill missing, no hint → `Skill "<name>" not found. Use get_available_skills to list available skills.`
|
|
70
|
+
*
|
|
71
|
+
* Not-found messages always start with the literal `Skill "` so callers
|
|
72
|
+
* can detect them with `result.startsWith('Skill "')`. The skill-name
|
|
73
|
+
* regex (`/^[\p{Ll}\p{N}-]+$/u`) forbids uppercase initial characters,
|
|
74
|
+
* so a legitimate skill name can never collide with that prefix.
|
|
75
|
+
*
|
|
76
|
+
* The helper does its own discovery; callers that need the `Skill` object
|
|
77
|
+
* (rather than just its name) re-resolve via a second `discoverAllSkills`
|
|
78
|
+
* call. Discovery is cheap (file-listing only) and the OS-level metadata
|
|
79
|
+
* cache absorbs most of the cost.
|
|
80
|
+
*/
|
|
81
|
+
export declare const resolveSkillOrSuggest: (directory: string, skillName: string) => Promise<string>;
|
|
82
|
+
declare const GetAvailableSkills: (directory: string) => SkillTool;
|
|
83
|
+
declare const ReadSkillFile: (directory: string, host: OpencodeSkillHost) => SkillTool;
|
|
84
|
+
declare const RunSkillScript: (directory: string, $: PluginInput["$"]) => SkillTool;
|
|
85
|
+
declare const UseSkill: (directory: string, host: OpencodeSkillHost, onSkillLoaded?: (sessionID: string, skillName: string) => void) => SkillTool;
|
|
86
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,14 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-agent-skills-md",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "OpenCode adapter for the opencode-agent-skills-md workspace: plugin factory, host, and the four skill tools.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./src/index.ts",
|
|
10
|
+
"import": "./src/index.ts",
|
|
11
|
+
"default": "./src/index.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"oas": "./dist/cli.mjs"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"yaml": "^2.9.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@opencode-ai/plugin": ">=1.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^25.9.1",
|
|
31
|
+
"rolldown": "^1.0.3",
|
|
32
|
+
"tsx": "^4.22.3",
|
|
33
|
+
"typescript": "^6.0.3",
|
|
34
|
+
"opencode-agent-skills-md-core": "0.0.0"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
7
39
|
"license": "MIT",
|
|
8
40
|
"repository": {
|
|
9
41
|
"type": "git",
|
|
10
42
|
"url": "https://github.com/MetalbolicX/opencode-agent-skills-md.git"
|
|
11
43
|
},
|
|
44
|
+
"homepage": "https://github.com/MetalbolicX/opencode-agent-skills-md",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/MetalbolicX/opencode-agent-skills-md/issues"
|
|
47
|
+
},
|
|
48
|
+
"author": {
|
|
49
|
+
"name": "Josh Thomas",
|
|
50
|
+
"url": "https://github.com/joshjoshthomas"
|
|
51
|
+
},
|
|
12
52
|
"keywords": [
|
|
13
53
|
"opencode",
|
|
14
54
|
"plugin",
|
|
@@ -16,20 +56,10 @@
|
|
|
16
56
|
"agent",
|
|
17
57
|
"ai"
|
|
18
58
|
],
|
|
19
|
-
"engines": {
|
|
20
|
-
"node": ">=18.0.0"
|
|
21
|
-
},
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"@types/node": "^25.9.1",
|
|
24
|
-
"tsx": "^4.22.3",
|
|
25
|
-
"typescript": "^6.0.3",
|
|
26
|
-
"opencode-agent-skills-md": "1.0.1",
|
|
27
|
-
"opencode-agent-skills-md-core": "0.0.0"
|
|
28
|
-
},
|
|
29
59
|
"scripts": {
|
|
30
|
-
"build": "
|
|
31
|
-
"
|
|
32
|
-
"test
|
|
33
|
-
"typecheck": "
|
|
60
|
+
"build": "rm -rf dist && rolldown -c && tsc -p tsconfig.build.json",
|
|
61
|
+
"pretest": "npm run build",
|
|
62
|
+
"test": "node --import tsx --test \"tests/**/*.test.ts\"",
|
|
63
|
+
"typecheck": "tsc --noEmit"
|
|
34
64
|
}
|
|
35
65
|
}
|
package/.beads/.local_version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.44.0
|
package/.beads/README.md
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# Beads - AI-Native Issue Tracking
|
|
2
|
-
|
|
3
|
-
Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
|
|
4
|
-
|
|
5
|
-
## What is Beads?
|
|
6
|
-
|
|
7
|
-
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
|
|
8
|
-
|
|
9
|
-
**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)
|
|
10
|
-
|
|
11
|
-
## Quick Start
|
|
12
|
-
|
|
13
|
-
### Essential Commands
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
# Create new issues
|
|
17
|
-
bd create "Add user authentication"
|
|
18
|
-
|
|
19
|
-
# View all issues
|
|
20
|
-
bd list
|
|
21
|
-
|
|
22
|
-
# View issue details
|
|
23
|
-
bd show <issue-id>
|
|
24
|
-
|
|
25
|
-
# Update issue status
|
|
26
|
-
bd update <issue-id> --status in_progress
|
|
27
|
-
bd update <issue-id> --status done
|
|
28
|
-
|
|
29
|
-
# Sync with git remote
|
|
30
|
-
bd sync
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Working with Issues
|
|
34
|
-
|
|
35
|
-
Issues in Beads are:
|
|
36
|
-
- **Git-native**: Stored in `.beads/issues.jsonl` and synced like code
|
|
37
|
-
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
|
|
38
|
-
- **Branch-aware**: Issues can follow your branch workflow
|
|
39
|
-
- **Always in sync**: Auto-syncs with your commits
|
|
40
|
-
|
|
41
|
-
## Why Beads?
|
|
42
|
-
|
|
43
|
-
✨ **AI-Native Design**
|
|
44
|
-
- Built specifically for AI-assisted development workflows
|
|
45
|
-
- CLI-first interface works seamlessly with AI coding agents
|
|
46
|
-
- No context switching to web UIs
|
|
47
|
-
|
|
48
|
-
🚀 **Developer Focused**
|
|
49
|
-
- Issues live in your repo, right next to your code
|
|
50
|
-
- Works offline, syncs when you push
|
|
51
|
-
- Fast, lightweight, and stays out of your way
|
|
52
|
-
|
|
53
|
-
🔧 **Git Integration**
|
|
54
|
-
- Automatic sync with git commits
|
|
55
|
-
- Branch-aware issue tracking
|
|
56
|
-
- Intelligent JSONL merge resolution
|
|
57
|
-
|
|
58
|
-
## Get Started with Beads
|
|
59
|
-
|
|
60
|
-
Try Beads in your own projects:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
# Install Beads
|
|
64
|
-
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
|
|
65
|
-
|
|
66
|
-
# Initialize in your repo
|
|
67
|
-
bd init
|
|
68
|
-
|
|
69
|
-
# Create your first issue
|
|
70
|
-
bd create "Try out Beads"
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Learn More
|
|
74
|
-
|
|
75
|
-
- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
|
|
76
|
-
- **Quick Start Guide**: Run `bd quickstart`
|
|
77
|
-
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
*Beads: Issue tracking that moves at the speed of thought* ⚡
|