obsidian-lean-coder 0.1.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -2,7 +2,36 @@
2
2
 
3
3
  All notable changes to OLC (Obsidian Lean Coder) are recorded here.
4
4
  OLC is pre-release: versions are `0.x` until a controlled-testing release says otherwise.
5
- `package.json` keeps `"private": true` — npm publishing is a separate explicitly approved action, never automatic.
5
+ `package.json` shipped `"private": true` through 0.1.0; the flag was removed at publish time with explicit user approval, and 0.1.0 is live as [`obsidian-lean-coder`](https://www.npmjs.com/package/obsidian-lean-coder). Publishing a new version is still a separate explicitly approved action, never automatic.
6
+
7
+ ## [0.2.0] — 2026-10-13 — `olc-setup` resource installer
8
+
9
+ New `olc-setup setup [--project|--global]` command (run manually via `npx obsidian-lean-coder setup`
10
+ after install): copies the shipped agents/skills into OpenCode discovery paths —
11
+ `<cwd>/.opencode` (project, default) or `~/.config/opencode` (`$XDG_CONFIG_HOME/opencode` when set,
12
+ global; global use is allowed only for this plugin's own install). Missing directories are created;
13
+ existing OLC agent/skill files are overwritten with the package versions (a deliberate, narrow
14
+ exception to the create-only rule, scoped to the files this command owns — see README §2).
15
+ `--dry-run` previews without writing; `opencode.json` is never edited (manual snippet printed after
16
+ each run). Node builtins only, no runtime dependencies.
17
+
18
+ - **Verification:** 6 fixture tests (`tests/setup.test.ts`: create, skip/overwrite, dry-run,
19
+ global scope, usage errors, mkdir -p); full suite 146/146 pass; typecheck/build clean.
20
+ - **Upgrade:** `npm install obsidian-lean-coder@0.2.0`, then `npx obsidian-lean-coder setup`
21
+ (add `--global` for global scope).
22
+
23
+ ## [0.1.1] — 2026-10-13 — plugin entrypoint fix
24
+
25
+ Fixes npm-package activation (reported: `npm install` worked but nothing appeared in OpenCode).
26
+ The package-root entrypoint `dist/index.js` exported only `OLC_VERSION`, so npm loaders —
27
+ which resolve the package root — never saw the plugin default export and ignored the package.
28
+ `src/index.ts` now re-exports the plugin default alongside `OLC_VERSION`.
29
+ Agents/skills still appear only via the project-local `.opencode` copy (by design, OLC-012).
30
+
31
+ - **Verification:** typecheck clean, build OK, mock-ctx plugin load (`id=olc`, correct payload),
32
+ full suite 140/140 pass, real-loader test (`loading plugin`, zero `failed to load plugin`).
33
+ - **Upgrade:** `npm install obsidian-lean-coder@0.1.1`, then list the package in `opencode.json`
34
+ `plugins` (see README §2).
6
35
 
7
36
  ## [0.1.0] — 2026-10-13 — MVP pre-release
8
37
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OLC — Obsidian Lean Coder
2
2
 
3
- OpenCode extension for project-scoped planning, memory, and safe Git checkpoints. Work in progress — pre-release, not on npm.
3
+ OpenCode extension for project-scoped planning, memory, and safe Git checkpoints. On npm as `obsidian-lean-coder` (v0.1.1 fixes plugin activation — see CHANGELOG).
4
4
 
5
5
  ## Three separate steps
6
6
 
@@ -9,7 +9,7 @@ Do not confuse these. They are different operations with different commands:
9
9
  | # | Step | What it does | Command |
10
10
  |---|------|--------------|---------|
11
11
  | 1 | **Dev install** | Install npm deps to hack on OLC itself | `npm ci && npm run build` |
12
- | 2 | **Plugin activation** | Load this checkout as an OpenCode plugin (local dev path) | config snippet below |
12
+ | 2 | **Plugin activation** | Load OLC as an OpenCode plugin | `npm i obsidian-lean-coder` + `opencode.json` snippet below |
13
13
  | 3 | **Project init** | Register one app repo + vault with OLC (`initProject`, idempotent, create-only) | via the `olc-init` skill in OpenCode (the tested path) |
14
14
 
15
15
  ## 1. Dev install
@@ -26,11 +26,35 @@ npm run typecheck
26
26
 
27
27
  Expected: build clean, typecheck clean. `npm test` runs the full suite (currently 140 tests). `npm pack --dry-run` shows what would ship (`dist/`, `agents/`, `skills/`, `templates/`).
28
28
 
29
- > npm publish is blocked: `package.json` has `"private": true`. Removing that flag needs a separate explicit approval — never publish by accident.
29
+ > Published on npm as `obsidian-lean-coder` (MIT). Republishing needs a separate explicit approval — never publish by accident.
30
30
 
31
- ## 2. Plugin activation (local dev path)
31
+ ## 2. Plugin activation
32
32
 
33
- OLC is **not on npm**. Until a scoped name is reserved (DECISIONS.md #1), the only supported activation is the local-file path: an `index.js` under `.opencode/plugins/<name>/` auto-loads with no install step (proven in OLC-011, re-verified in OLC-071 against the packed tarball). The plugin entrypoint is `dist/plugin.js` (`src/plugin.ts` → `Plugin.define({ id: "olc" })`, one read-only `olc_status` tool, zero writes on load). Note `dist/index.js` only exports `OLC_VERSION` — it is not the plugin entrypoint.
33
+ ```sh
34
+ npm install obsidian-lean-coder
35
+ ```
36
+
37
+ Then list it in your project's `opencode.json` (npm loaders resolve the package root `dist/index.js`, which since v0.1.1 carries the plugin default export alongside `OLC_VERSION`):
38
+
39
+ ```json
40
+ {
41
+ "plugins": ["obsidian-lean-coder"]
42
+ }
43
+ ```
44
+
45
+ Verify it loads (from the consuming project directory; the model call itself will fail without account funds, but plugin loading happens first):
46
+
47
+ ```sh
48
+ OPENCODE_LOG_LEVEL=DEBUG opencode run --standalone "hi" --print-logs 2>&1 | grep -E "loading plugin.*obsidian-lean-coder|failed to load plugin"
49
+ ```
50
+
51
+ Expected: a `loading plugin ... obsidian-lean-coder` line and **zero** `failed to load plugin` lines. (Verified 2026-10-13 via the equivalent local-file shape against the real loader; npm-registry install itself verified in OLC-071.)
52
+
53
+ > `npm install` alone shows nothing in OpenCode — that is normal. The package must be listed under `plugins`, and agents/skills never auto-appear: they are project-local files you copy yourself (next step). No restart or rebuild needed after editing `opencode.json`.
54
+
55
+ ### Local dev path (this checkout, no npm)
56
+
57
+ An `index.js` under `.opencode/plugins/<name>/` auto-loads with no install step (proven in OLC-011, re-verified in OLC-071). The plugin entrypoint is `dist/plugin.js` (`src/plugin.ts` → `Plugin.define({ id: "olc" })`, one read-only `olc_status` tool, zero writes on load).
34
58
 
35
59
  In your consuming project (replace `/home/you/olc` with your OLC checkout):
36
60
 
@@ -41,17 +65,19 @@ printf '%s\n' 'export { default } from "file://'"$PKG"'/dist/plugin.js";' > .ope
41
65
  printf '%s\n' '{ "type": "module", "dependencies": { "@opencode/plugin": "2.0.27" } }' > .opencode/plugins/olc/package.json
42
66
  ```
43
67
 
44
- Verify it loads (from the consuming project directory; the model call itself will fail without account funds, but plugin loading happens first):
68
+ Verify with the same `grep` as above, expecting `.opencode/plugins/olc/index.js` in the loading line.
69
+
70
+ Then install the agent/skill resources — recommended via the setup helper:
45
71
 
46
72
  ```sh
47
- OPENCODE_LOG_LEVEL=DEBUG opencode run --standalone "hi" --print-logs 2>&1 | grep -E "loading plugin.*plugins/olc|failed to load plugin"
73
+ npx obsidian-lean-coder setup # project scope: ./.opencode
74
+ npx obsidian-lean-coder setup --global # global scope: ~/.config/opencode
75
+ npx obsidian-lean-coder setup --dry-run # preview, writes nothing
48
76
  ```
49
77
 
50
- Expected: a `loading plugin ... .opencode/plugins/olc/index.js` line and **zero** `failed to load plugin` lines.
51
-
52
- > Not supported (verified 2026-10-12, OLC-071): pointing `opencode.json` `plugins` (or `plugin`) at the package directory — bare path or `file://` URL — produced zero load attempts in this build, so there is nothing to verify there yet. npm-package activation is untestable until a name is reserved AND the entrypoint is wired (`dist/index.js` currently has no plugin default export; npm loaders resolve `dist/index.js`).
53
-
54
- Then place the agent/skill resources project-locally (DECISIONS.md #2 — programmatic registration was rejected for MVP):
78
+ Missing directories are created; existing OLC agent/skill files are overwritten with the package
79
+ versions (a deliberate exception to the create-only rule, scoped to these files only).
80
+ Manual equivalent (DECISIONS.md #2 — programmatic registration was rejected for MVP):
55
81
 
56
82
  ```sh
57
83
  PKG=/home/you/olc # your OLC checkout
@@ -74,7 +100,7 @@ Supported versions: OpenCode 2.x with the V2 plugin API. OLC pins `@opencode/plu
74
100
 
75
101
  `initProject` (`src/init/index.ts`) registers one app repo + vault. It is idempotent and **create-only**: existing files are reported `preserved`, never overwritten. Anything needing a human choice comes back as `status: "needs-decision"` — no prompting inside the module.
76
102
 
77
- Run it via the `olc-init` skill inside OpenCode (recommended — the only tested path for MVP). A programmatic import is not yet wired: the package entrypoint (`dist/index.js`) currently exports only `OLC_VERSION`, and `initProject` lives in `src/init/index.ts` for internal/agent use.
103
+ Run it via the `olc-init` skill inside OpenCode (recommended — the only tested path for MVP). A programmatic import is not yet wired: the package entrypoint (`dist/index.js`) exports `OLC_VERSION` plus the plugin default, and `initProject` lives in `src/init/index.ts` for internal/agent use.
78
104
 
79
105
  What it creates (only when missing):
80
106
 
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * OLC setup helper (OLC-075).
4
+ *
5
+ * Manually run after `npm install -g obsidian-lean-coder` (or npx):
6
+ * olc-setup setup [--project|--global] [--dry-run]
7
+ *
8
+ * Copies the shipped agent/skill resources into OpenCode's discovery paths:
9
+ * project scope (default): <cwd>/.opencode/{agents,skills}
10
+ * global scope: ~/.config/opencode/{agents,skills}
11
+ * ($XDG_CONFIG_HOME/opencode when set)
12
+ *
13
+ * Behavior (locked with the user):
14
+ * - Missing directories are created (mkdir -p).
15
+ * - Existing OLC agent/skill files are OVERWRITTEN with the package
16
+ * versions. This is a deliberate, narrow exception to OLC's usual
17
+ * create-only rule — and it applies ONLY to the files this command owns
18
+ * (listed in OWNED_FILES below). Nothing else is touched.
19
+ * - `--dry-run` prints what would happen and writes nothing.
20
+ * - `opencode.json` is NEVER edited by this command. After it runs, it
21
+ * prints the manual plugin-registration snippet for README §2.
22
+ *
23
+ * Node builtins only — no runtime dependencies.
24
+ */
25
+
26
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
27
+ import { dirname, join, resolve } from "node:path";
28
+ import { homedir } from "node:os";
29
+ import { fileURLToPath } from "node:url";
30
+
31
+ /** Files this command owns: [package-relative source, scope-relative dest]. */
32
+ const OWNED_FILES = [
33
+ ["agents/olc-plan.md", ".opencode/agents/olc-plan.md"],
34
+ ["agents/olc-build.md", ".opencode/agents/olc-build.md"],
35
+ ["skills/olc-explore/SKILL.md", ".opencode/skills/olc-explore/SKILL.md"],
36
+ ];
37
+
38
+ function usage() {
39
+ return [
40
+ "Usage: olc-setup setup [--project|--global] [--dry-run]",
41
+ "",
42
+ " setup copy shipped agents/skills into OpenCode discovery paths",
43
+ " --project install into <cwd>/.opencode (default)",
44
+ " --global install into ~/.config/opencode ($XDG_CONFIG_HOME/opencode when set)",
45
+ " --dry-run print what would happen, write nothing",
46
+ "",
47
+ "Existing OLC agent/skill files are overwritten with the package versions;",
48
+ "nothing else is touched, and opencode.json is never edited (see the",
49
+ "manual plugin-registration snippet printed after a successful run).",
50
+ ].join("\n");
51
+ }
52
+
53
+ function fail(message) {
54
+ process.stderr.write(`olc-setup: ${message}\n${usage()}\n`);
55
+ process.exit(2);
56
+ }
57
+
58
+ function parseArgs(argv) {
59
+ if (argv[0] !== "setup") fail(`expected subcommand \"setup\", got ${JSON.stringify(argv[0] ?? "(none)")}.`);
60
+ let scope = "project";
61
+ let dryRun = false;
62
+ for (const arg of argv.slice(1)) {
63
+ if (arg === "--project") scope = "project";
64
+ else if (arg === "--global") scope = "global";
65
+ else if (arg === "--dry-run") dryRun = true;
66
+ else fail(`unknown argument ${JSON.stringify(arg)}.`);
67
+ }
68
+ return { scope, dryRun };
69
+ }
70
+
71
+ function packageRoot() {
72
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..");
73
+ }
74
+
75
+ function scopeRoot(scope) {
76
+ if (scope === "project") return resolve(process.cwd());
77
+ const base = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
78
+ return resolve(base, "opencode");
79
+ }
80
+
81
+ function main() {
82
+ const { scope, dryRun } = parseArgs(process.argv.slice(2));
83
+ const pkg = packageRoot();
84
+ const root = scopeRoot(scope);
85
+
86
+ const missing = OWNED_FILES.map(([src]) => src).filter((src) => !existsSync(join(pkg, src)));
87
+ if (missing.length > 0) {
88
+ process.stderr.write(
89
+ `olc-setup: installed package is incomplete, missing: ${missing.join(", ")}. Reinstall obsidian-lean-coder.\n`,
90
+ );
91
+ process.exit(1);
92
+ }
93
+
94
+ const summary = [];
95
+ for (const [src, destRel] of OWNED_FILES) {
96
+ const dest = join(root, destRel);
97
+ const wanted = readFileSync(join(pkg, src), "utf8");
98
+ const prior = existsSync(dest) ? readFileSync(dest, "utf8") : null;
99
+ if (prior === wanted) {
100
+ summary.push({ file: destRel, action: "skipped" });
101
+ continue;
102
+ }
103
+ if (!dryRun) {
104
+ mkdirSync(dirname(dest), { recursive: true });
105
+ writeFileSync(dest, wanted, "utf8");
106
+ }
107
+ summary.push({ file: destRel, action: prior === null ? "created" : "overwritten" });
108
+ }
109
+
110
+ const counts = { created: 0, overwritten: 0, skipped: 0 };
111
+ for (const entry of summary) {
112
+ counts[entry.action] += 1;
113
+ process.stdout.write(`${entry.action} ${entry.file}\n`);
114
+ }
115
+ process.stdout.write(
116
+ `${dryRun ? "[dry-run] " : ""}done (${scope} scope, ${root}): ` +
117
+ `${counts.created} created, ${counts.overwritten} overwritten, ${counts.skipped} skipped.\n`,
118
+ );
119
+ if (!dryRun) {
120
+ process.stdout.write(
121
+ "\nNext (manual): register the plugin in opencode.json so OpenCode loads it:\n" +
122
+ ' { "plugins": ["obsidian-lean-coder"] }\n' +
123
+ "See README §2 for verification. opencode.json was left untouched.\n",
124
+ );
125
+ }
126
+ }
127
+
128
+ main();
package/dist/index.d.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  /**
2
2
  * OLC package entrypoint. Thin on purpose — see docs/PRD.md §8 for planned
3
3
  * module layout (config, core, git, logging, tools, validation).
4
+ *
5
+ * The plugin default export is re-exported here (not only from
6
+ * `./plugin.js`) because OpenCode's npm loader resolves the package root,
7
+ * i.e. `dist/index.js` — without a `default` export carrying `{ id, setup }`
8
+ * the loader ignores the package entirely (found by OLC-071 smoke test:
9
+ * zero load attempts for 0.1.0). `OLC_VERSION` is only consumed inside
10
+ * `plugin.ts` tool `execute` (deferred to call time), so this re-export
11
+ * cycle is safe under ESM live bindings.
4
12
  */
5
- export declare const OLC_VERSION = "0.1.0";
13
+ export declare const OLC_VERSION = "0.2.0";
14
+ export { default } from "./plugin.js";
package/dist/index.js CHANGED
@@ -1,5 +1,14 @@
1
1
  /**
2
2
  * OLC package entrypoint. Thin on purpose — see docs/PRD.md §8 for planned
3
3
  * module layout (config, core, git, logging, tools, validation).
4
+ *
5
+ * The plugin default export is re-exported here (not only from
6
+ * `./plugin.js`) because OpenCode's npm loader resolves the package root,
7
+ * i.e. `dist/index.js` — without a `default` export carrying `{ id, setup }`
8
+ * the loader ignores the package entirely (found by OLC-071 smoke test:
9
+ * zero load attempts for 0.1.0). `OLC_VERSION` is only consumed inside
10
+ * `plugin.ts` tool `execute` (deferred to call time), so this re-export
11
+ * cycle is safe under ESM live bindings.
4
12
  */
5
- export const OLC_VERSION = "0.1.0";
13
+ export const OLC_VERSION = "0.2.0";
14
+ export { default } from "./plugin.js";
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "obsidian-lean-coder",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Obsidian Lean Coder — OpenCode extension for project-scoped planning, memory, and safe Git checkpoints.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
+ "bin": {
8
+ "olc-setup": "./bin/olc-setup.js"
9
+ },
7
10
  "types": "./dist/index.d.ts",
8
11
  "exports": {
9
12
  ".": {
@@ -16,6 +19,7 @@
16
19
  "agents",
17
20
  "skills",
18
21
  "templates",
22
+ "bin",
19
23
  "README.md",
20
24
  "CHANGELOG.md"
21
25
  ],