codex-arsenal 0.2.1 → 0.4.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/CODEX.md CHANGED
@@ -6,6 +6,23 @@
6
6
 
7
7
  ---
8
8
 
9
+ ## Session Bootstrap
10
+
11
+ When this repository is installed into a project, read `prompts/session-bootstrap.md` immediately after `CODEX.md` if it exists.
12
+
13
+ If `configs/codex/session-config.json` exists, use it as the project-local source of truth for:
14
+
15
+ - the session initializer path;
16
+ - the code-style note path;
17
+ - the meaningful-work criteria;
18
+ - the Obsidian vault and inbox paths.
19
+
20
+ If the configured code-style note exists, merge it into your working style before editing files.
21
+
22
+ If the task satisfies the meaningful-work criteria, draft an Obsidian article at the end of the session.
23
+
24
+ ---
25
+
9
26
  ## 1. Think Before Coding
10
27
 
11
28
  **Don't assume. Don't hide confusion. Surface tradeoffs.**
@@ -91,7 +108,20 @@ Weak criteria ("make it work") require constant clarification.
91
108
 
92
109
  ---
93
110
 
94
- ## 6. Context Awareness
111
+ ## 6. Token Budgeting
112
+
113
+ **Spend context only where it changes the decision.**
114
+
115
+ - Start with filenames, symbols, and short snippets before reading full files.
116
+ - Prefer `rg` and targeted search over broad recursive reads.
117
+ - Summarize long outputs immediately: key facts, file paths, line numbers, open questions.
118
+ - Do not paste large logs back to the user. Quote only the failing line or smallest useful excerpt.
119
+ - Reuse already-loaded context instead of re-reading the same files.
120
+ - When handing off, compress to: goal, changed files, commands run, verification status, next risk.
121
+
122
+ ---
123
+
124
+ ## 7. Context Awareness
95
125
 
96
126
  **Codex has a limited context window — use it wisely.**
97
127
 
@@ -102,7 +132,7 @@ Weak criteria ("make it work") require constant clarification.
102
132
 
103
133
  ---
104
134
 
105
- ## 7. Tool Use Discipline
135
+ ## 8. Tool Use Discipline
106
136
 
107
137
  **Every tool call has a cost. Make them count.**
108
138
 
@@ -118,4 +148,4 @@ Weak criteria ("make it work") require constant clarification.
118
148
  - Diffs contain fewer unnecessary changes
119
149
  - Fewer rewrites due to overcomplication
120
150
  - Clarifying questions come **before** implementation, not after mistakes
121
- - Agentic runs complete tasks end-to-end without silent failures or scope creep
151
+ - Agentic runs complete tasks end-to-end without silent failures or scope creep
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Codex-Arsenal
2
2
 
3
- Practical building blocks for working with OpenAI Codex, Claude Code, and other coding agents.
3
+ Practical building blocks for working with OpenAI Codex.
4
4
 
5
- Codex-Arsenal is not an awesome list. It is a small, installable collection of agent guidelines, skills, prompts, workflows, configs, and plugin sketches that can be copied into real projects.
5
+ Codex-Arsenal is not an awesome list. It is a small, installable collection of Codex guidelines, skills, prompts, workflows, configs, and plugin sketches that can be copied into real projects.
6
6
 
7
7
  ## Install
8
8
 
@@ -39,14 +39,19 @@ npm exec --yes --package=codex-arsenal -- codex-arsenal list
39
39
  ## CLI
40
40
 
41
41
  ```bash
42
- codex-arsenal init [--yes] [--dir <path>]
42
+ codex-arsenal init [--yes] [--force] [--dir <path>]
43
43
  codex-arsenal list
44
- codex-arsenal get <id...> [--dir <path>]
44
+ codex-arsenal get <id...> [--force] [--dir <path>]
45
+ codex-obsidian-bridge sync [--dir <path>] [--force]
46
+ codex-obsidian-bridge draft --title <text> --summary <text> [options]
45
47
  ```
46
48
 
47
49
  - `init` opens a small selector. With `--yes`, it installs default items without prompting.
48
50
  - `list` prints all installable manifest entries grouped by category.
49
51
  - `get` installs one or more manifest entries by id.
52
+ - Existing files are skipped by default. Add `--force` to overwrite them intentionally.
53
+ - `codex-obsidian-bridge sync` mirrors the session bootstrap, code style, and meaningful-work notes into an Obsidian vault.
54
+ - `codex-obsidian-bridge draft` writes an article draft into the configured Obsidian inbox when the meaningful-work threshold is met.
50
55
 
51
56
  The package is published on npm as [`codex-arsenal`](https://www.npmjs.com/package/codex-arsenal).
52
57
 
@@ -57,19 +62,22 @@ The package is published on npm as [`codex-arsenal`](https://www.npmjs.com/packa
57
62
  | ID | Installs | Purpose |
58
63
  | --- | --- | --- |
59
64
  | `codex-md` | `CODEX.md` | Project-local behavioral guardrails for Codex-style agents. |
60
- | `claude-md` | `CLAUDE.md` | Project-local behavioral guardrails for Claude Code. |
61
65
 
62
66
  ### Configs
63
67
 
64
68
  | ID | Installs | Purpose |
65
69
  | --- | --- | --- |
66
70
  | `config-codex` | `.codex/config.json` | Starter Codex config. |
71
+ | `config-session-bootstrap` | `.codex/session-config.json` | Session bootstrap and Obsidian bridge settings. |
67
72
  | `config-vscode` | `.vscode/settings.json` | VS Code settings for agent-assisted development. |
68
73
 
69
74
  ### Prompts
70
75
 
71
76
  | ID | Installs | Purpose |
72
77
  | --- | --- | --- |
78
+ | `prompt-session-bootstrap` | `prompts/session-bootstrap.md` | Session startup instructions for Codex. |
79
+ | `prompt-code-style` | `prompts/code-style.md` | Obsidian-backed code style note template. |
80
+ | `prompt-meaningful-work` | `prompts/meaningful-work.md` | Criteria and template for end-of-session articles. |
73
81
  | `prompt-solo-dev` | `prompts/system-prompts/solo-dev.md` | Solo developer system prompt template. |
74
82
 
75
83
  ### Skills
@@ -79,12 +87,43 @@ The package is published on npm as [`codex-arsenal`](https://www.npmjs.com/packa
79
87
  | `skill-debug-workflow` | `skills/debug-workflow/` | Reproduce, diagnose, test, and fix bugs systematically. |
80
88
  | `skill-test-gen` | `skills/test-gen/` | Generate focused tests from behavior notes and function signatures. |
81
89
  | `skill-publishing-npm-packages` | `skills/publishing-npm-packages/SKILL.md` | Prepare, troubleshoot, and automate npm releases with Trusted Publishing. |
90
+ | `skill-obsidian-session-loop` | `skills/obsidian-session-loop/SKILL.md` | Session startup, Obsidian sync, and end-of-session article drafting. |
82
91
 
83
92
  ### Plugins
84
93
 
85
94
  | ID | Installs | Purpose |
86
95
  | --- | --- | --- |
87
96
  | `plugin-context-window-compressor` | `plugins/context-window-compressor/` | Plugin sketch for compressing long agent context into concise handoff notes. |
97
+ | `plugin-obsidian-codex-bridge` | `plugins/obsidian-codex-bridge/` | Plugin sketch for syncing Codex notes and article drafts with Obsidian. |
98
+
99
+ ## Obsidian Bridge
100
+
101
+ The bridge is designed around two repeatable actions:
102
+
103
+ 1. `sync` the session initializer and style notes into a vault so the notes stay close to where they are read.
104
+ 2. `draft` a session article when the configured meaningful-work criteria are met.
105
+
106
+ The default config lives in `.codex/session-config.json` and can be customized per project. A minimal vault layout looks like this:
107
+
108
+ ```text
109
+ Codex/
110
+ Session-Initializer.md
111
+ Code-Style.md
112
+ Meaningful-Work.md
113
+ Inbox/
114
+ Codex/
115
+ ```
116
+
117
+ Example commands:
118
+
119
+ ```bash
120
+ codex-obsidian-bridge sync --dir .
121
+ codex-obsidian-bridge open --dir . --file "Codex/Session-Initializer.md"
122
+ codex-obsidian-bridge reveal --dir . --file "Inbox/Codex/2026-06-11-refined-session-bootstrap.md"
123
+ codex-obsidian-bridge draft --dir . --title "Refined session bootstrap" --summary "Added an Obsidian bridge and session-level startup guidance." --changes "added bridge CLI, added note templates" --decisions "kept config file-based" --verification "npm test" --signals "new integration boundary, README update"
124
+ ```
125
+
126
+ If you have an Obsidian CLI or local launcher, add `obsidian.launch.open` and `obsidian.launch.reveal` templates to `.codex/session-config.json`. The bridge will use those templates first and fall back to an `obsidian://` launch URI if no CLI template is configured.
88
127
 
89
128
  ### Workflows
90
129
 
@@ -100,7 +139,7 @@ codex-arsenal/
100
139
  lib/ manifest, installer, and fetcher
101
140
  configs/ reusable editor and agent configs
102
141
  plugins/ plugin sketches
103
- prompts/ system prompt templates
142
+ prompts/ system prompt templates and session notes
104
143
  skills/ reusable agent skills
105
144
  workflows/ repeatable agentic workflows
106
145
  references/ curated references and notes
@@ -153,6 +192,19 @@ git push --follow-tags
153
192
 
154
193
  The publish workflow runs on `v*` tags and publishes with OIDC, so it does not require a long-lived `NPM_TOKEN`.
155
194
 
195
+ ## Codex Plugin Distribution
196
+
197
+ The npm package is the current public distribution channel. Codex plugin library publishing is separate from npm and currently does not expose a general public submission flow in the public docs.
198
+
199
+ For now, use one of these paths:
200
+
201
+ - publish reusable files through `codex-arsenal` on npm;
202
+ - keep plugin sketches under `plugins/`;
203
+ - create a local or team marketplace file at `.agents/plugins/marketplace.json` when you want Codex app users to install a repo plugin from a local marketplace;
204
+ - track OpenAI's Codex plugin documentation for an official public marketplace submission path.
205
+
206
+ See `docs/codex-plugin-distribution.md` for the current assessment.
207
+
156
208
  ## Contribution Guidelines
157
209
 
158
210
  Good additions should be:
package/bin/cli.js CHANGED
@@ -6,9 +6,11 @@ function printHelp() {
6
6
  console.log(`codex-arsenal
7
7
 
8
8
  Usage:
9
- codex-arsenal init [--yes] [--dir <path>]
9
+ codex-arsenal init [--yes] [--force] [--dir <path>]
10
10
  codex-arsenal list
11
- codex-arsenal get <id...> [--dir <path>]
11
+ codex-arsenal get <id...> [--force] [--dir <path>]
12
+
13
+ By default, existing files are skipped. Use --force to overwrite them.
12
14
  `);
13
15
  }
14
16
 
@@ -28,7 +30,7 @@ function nonOptionArgs(args) {
28
30
  index += 1;
29
31
  continue;
30
32
  }
31
- if (arg === "--yes" || arg === "-y") {
33
+ if (arg === "--yes" || arg === "-y" || arg === "--force") {
32
34
  continue;
33
35
  }
34
36
  result.push(arg);
@@ -51,6 +53,7 @@ function printList() {
51
53
  async function main(argv) {
52
54
  const [command = "init", ...args] = argv;
53
55
  const dir = readOption(args, "--dir", readOption(args, "-d", process.cwd()));
56
+ const force = args.includes("--force");
54
57
 
55
58
  if (command === "help" || command === "--help" || command === "-h") {
56
59
  printHelp();
@@ -67,12 +70,12 @@ async function main(argv) {
67
70
  if (!ids.length) {
68
71
  throw new Error("get requires at least one item id");
69
72
  }
70
- await runInit({ preSelected: findManifestItems(ids), skipPrompt: true, dir });
73
+ await runInit({ preSelected: findManifestItems(ids), skipPrompt: true, dir, force });
71
74
  return;
72
75
  }
73
76
 
74
77
  if (command === "init") {
75
- await runInit({ yes: args.includes("--yes") || args.includes("-y"), dir });
78
+ await runInit({ yes: args.includes("--yes") || args.includes("-y"), dir, force });
76
79
  return;
77
80
  }
78
81
 
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ openObsidianNote,
4
+ draftObsidianArticle,
5
+ loadSessionConfig,
6
+ syncObsidianNotes
7
+ } from "../lib/obsidian-bridge.js";
8
+
9
+ function printHelp() {
10
+ console.log(`codex-obsidian-bridge
11
+
12
+ Usage:
13
+ codex-obsidian-bridge sync [--dir <path>] [--force]
14
+ codex-obsidian-bridge open [--dir <path>] [--file <path>]
15
+ codex-obsidian-bridge reveal [--dir <path>] [--file <path>]
16
+ codex-obsidian-bridge draft --title <text> --summary <text> [options]
17
+
18
+ Options:
19
+ --dir <path> Project root that contains .codex/session-config.json
20
+ --force Overwrite existing Obsidian notes
21
+ --file <path> Vault-relative note path to open or reveal
22
+ --title <text> Article title
23
+ --summary <text> Short summary of why the work mattered
24
+ --changes <items> Comma-separated list of change bullets
25
+ --decisions <items> Comma-separated list of decision bullets
26
+ --verification <items> Comma-separated list of verification bullets
27
+ --next-steps <items> Comma-separated list of follow-up bullets
28
+ --signals <items> Comma-separated list of meaningful-work signals
29
+ --integration-boundary Force an article when a new integration is introduced
30
+ `);
31
+ }
32
+
33
+ function readOption(args, name, fallback = undefined) {
34
+ const index = args.indexOf(name);
35
+ if (index === -1) {
36
+ return fallback;
37
+ }
38
+ return args[index + 1] || fallback;
39
+ }
40
+
41
+ function readListOption(args, name) {
42
+ const value = readOption(args, name, "");
43
+ if (!value) {
44
+ return [];
45
+ }
46
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
47
+ }
48
+
49
+ async function main(argv) {
50
+ const [command = "help", ...args] = argv;
51
+ const cwd = readOption(args, "--dir", process.cwd());
52
+ const force = args.includes("--force");
53
+
54
+ if (command === "help" || command === "--help" || command === "-h") {
55
+ printHelp();
56
+ return;
57
+ }
58
+
59
+ const config = await loadSessionConfig(cwd);
60
+
61
+ if (command === "sync") {
62
+ const result = await syncObsidianNotes({ cwd, config, force });
63
+ for (const file of result.synced) {
64
+ console.log(`synced ${file}`);
65
+ }
66
+ for (const file of result.skipped) {
67
+ console.log(`skipped ${file} (already exists; use --force to overwrite)`);
68
+ }
69
+ console.log(`target vault: ${result.targetVault}`);
70
+ return;
71
+ }
72
+
73
+ if (command === "open" || command === "reveal") {
74
+ const result = await openObsidianNote({
75
+ cwd,
76
+ config,
77
+ filePath: readOption(args, "--file"),
78
+ mode: command
79
+ });
80
+
81
+ if (!result.launched) {
82
+ console.log("Obsidian is disabled for this project.");
83
+ return;
84
+ }
85
+
86
+ console.log(result.mode === "command" ? "launched configured Obsidian command" : `opened ${result.spec.uri}`);
87
+ return;
88
+ }
89
+
90
+ if (command === "draft") {
91
+ const result = await draftObsidianArticle({
92
+ cwd,
93
+ config,
94
+ title: readOption(args, "--title"),
95
+ summary: readOption(args, "--summary"),
96
+ changes: readListOption(args, "--changes"),
97
+ decisions: readListOption(args, "--decisions"),
98
+ verification: readListOption(args, "--verification"),
99
+ nextSteps: readListOption(args, "--next-steps"),
100
+ signals: readListOption(args, "--signals"),
101
+ integrationBoundary: args.includes("--integration-boundary"),
102
+ force
103
+ });
104
+
105
+ if (result.skipped) {
106
+ console.log("no article drafted");
107
+ return;
108
+ }
109
+
110
+ console.log(`drafted ${result.written}`);
111
+ return;
112
+ }
113
+
114
+ throw new Error(`Unknown command: ${command}`);
115
+ }
116
+
117
+ main(process.argv.slice(2)).catch((error) => {
118
+ console.error(error.message);
119
+ process.exitCode = 1;
120
+ });
@@ -0,0 +1,30 @@
1
+ {
2
+ "sessionInitializer": "prompts/session-bootstrap.md",
3
+ "codeStyleNote": "prompts/code-style.md",
4
+ "meaningfulWorkNote": "prompts/meaningful-work.md",
5
+ "obsidian": {
6
+ "enabled": true,
7
+ "cliCommand": "obsidian",
8
+ "vaultPath": "~/Obsidian/Codex",
9
+ "inboxPath": "Inbox/Codex",
10
+ "launch": {
11
+ "open": null,
12
+ "reveal": null
13
+ },
14
+ "noteTargets": {
15
+ "sessionInitializer": "Codex/Session-Initializer.md",
16
+ "codeStyle": "Codex/Code-Style.md",
17
+ "meaningfulWork": "Codex/Meaningful-Work.md"
18
+ }
19
+ },
20
+ "articleRules": {
21
+ "highSignalTriggers": [
22
+ "new or changed installable content",
23
+ "behavioral change",
24
+ "external integration",
25
+ "substantive README or workflow update"
26
+ ],
27
+ "minimumSignalsForDraft": 2,
28
+ "requiresVerification": true
29
+ }
30
+ }
@@ -0,0 +1,54 @@
1
+ # Codex Plugin Distribution
2
+
3
+ ## Current Assessment
4
+
5
+ As of May 23, 2026, the practical public distribution path for Codex-Arsenal is npm. OpenAI's public Codex material says Codex users can browse the plugins library and create plugins in the Codex app, but it does not document a general public submission process for third-party plugins.
6
+
7
+ Relevant OpenAI pages:
8
+
9
+ - [Plugins and skills](https://openai.com/academy/codex-plugins-and-skills/)
10
+ - [Codex](https://openai.com/codex/)
11
+ - [Using Codex with your ChatGPT plan](https://help.openai.com/en/articles/11369540/)
12
+
13
+ ## Recommended Path
14
+
15
+ 1. Keep publishing installable skills, prompts, configs, and workflow files through `codex-arsenal` on npm.
16
+ 2. Keep plugin ideas in `plugins/<name>/` until they become real Codex plugins.
17
+ 3. When a plugin is ready for Codex app testing, convert it to a Codex plugin structure with `.codex-plugin/plugin.json`.
18
+ 4. Add a repo-local marketplace entry in `.agents/plugins/marketplace.json` for team/local installation.
19
+ 5. If OpenAI opens a formal public marketplace submission path, package the plugin manifest, documentation, permission model, and test notes for review.
20
+
21
+ ## Repo Marketplace Shape
22
+
23
+ Use this shape for local or team discovery:
24
+
25
+ ```json
26
+ {
27
+ "name": "codex-arsenal",
28
+ "interface": {
29
+ "displayName": "Codex-Arsenal"
30
+ },
31
+ "plugins": [
32
+ {
33
+ "name": "context-window-compressor",
34
+ "source": {
35
+ "source": "local",
36
+ "path": "./plugins/context-window-compressor"
37
+ },
38
+ "policy": {
39
+ "installation": "AVAILABLE",
40
+ "authentication": "ON_INSTALL"
41
+ },
42
+ "category": "Productivity"
43
+ }
44
+ ]
45
+ }
46
+ ```
47
+
48
+ ## Before Submitting Anywhere
49
+
50
+ - Define exactly what external systems the plugin touches.
51
+ - Keep permissions narrow and explain authentication.
52
+ - Provide a small demo workflow and screenshots if the plugin has UI.
53
+ - Include test notes for install, auth, failure states, and uninstall.
54
+ - Avoid bundling broad arsenals into one plugin. Publish focused plugins that solve one job well.
@@ -0,0 +1,35 @@
1
+ # npm Publishing
2
+
3
+ This package is set up for npm Trusted Publishing from GitHub Actions.
4
+
5
+ ## One-time npm setup
6
+
7
+ 1. Create or claim the `codex-arsenal` package on npm.
8
+ 2. On npmjs.com, open the package settings and add a Trusted Publisher:
9
+ - Provider: GitHub Actions
10
+ - Owner: `citron03`
11
+ - Repository: `Codex-Arsenal`
12
+ - Workflow filename: `publish.yml`
13
+ - Environment: `npm`
14
+ 3. After the first trusted publish works, set publishing access to require 2FA and disallow tokens.
15
+
16
+ ## Release process
17
+
18
+ 1. Ensure `main` is green.
19
+ 2. Update the version:
20
+
21
+ ```bash
22
+ npm version patch
23
+ ```
24
+
25
+ 3. Push the commit and tag:
26
+
27
+ ```bash
28
+ git push --follow-tags
29
+ ```
30
+
31
+ GitHub Actions will run tests, perform a package dry run, and publish to npm from the `v*` tag.
32
+
33
+ ## Why Trusted Publishing
34
+
35
+ Trusted Publishing uses short-lived OIDC credentials from GitHub Actions instead of a long-lived `NPM_TOKEN`. For public packages published from public repositories, npm also creates provenance attestations automatically.
package/lib/installer.js CHANGED
@@ -2,12 +2,15 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { createInterface } from "node:readline/promises";
3
3
  import { stdin as input, stdout as output } from "node:process";
4
4
  import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
5
6
  import { BASE_URL, MANIFEST } from "./manifest.js";
6
7
  import { fetchFile } from "./fetcher.js";
7
8
 
9
+ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
10
+
8
11
  async function readSourceFile(src) {
9
12
  try {
10
- return await readFile(src, "utf8");
13
+ return await readFile(path.join(PACKAGE_ROOT, src), "utf8");
11
14
  } catch (error) {
12
15
  if (error.code !== "ENOENT") {
13
16
  throw error;
@@ -21,13 +24,27 @@ export async function installItems(items, targetDir, opts = {}) {
21
24
  let installed = 0;
22
25
  let failed = 0;
23
26
  const successes = [];
27
+ const skipped = [];
24
28
  const failures = [];
25
29
  const readSource = opts.readSourceFile || readSourceFile;
30
+ const force = !!opts.force;
26
31
 
27
32
  for (const item of items) {
28
33
  for (const file of item.files) {
29
34
  const destination = path.join(resolvedTarget, file.dest);
30
35
  try {
36
+ if (!force) {
37
+ try {
38
+ await readFile(destination, "utf8");
39
+ skipped.push(file.dest);
40
+ continue;
41
+ } catch (error) {
42
+ if (error.code !== "ENOENT") {
43
+ throw error;
44
+ }
45
+ }
46
+ }
47
+
31
48
  const content = await readSource(file.src);
32
49
  await mkdir(path.dirname(destination), { recursive: true });
33
50
  await writeFile(destination, content, "utf8");
@@ -40,7 +57,7 @@ export async function installItems(items, targetDir, opts = {}) {
40
57
  }
41
58
  }
42
59
 
43
- return { installed, failed, successes, failures, targetDir: resolvedTarget };
60
+ return { installed, failed, successes, skipped, failures, targetDir: resolvedTarget };
44
61
  }
45
62
 
46
63
  function defaultItems() {
@@ -78,18 +95,24 @@ export async function runInit(opts = {}) {
78
95
 
79
96
  if (!selected.length) {
80
97
  console.log("No items selected.");
81
- return { installed: 0, failed: 0, successes: [], failures: [], targetDir: path.resolve(opts.dir || ".") };
98
+ return { installed: 0, failed: 0, successes: [], skipped: [], failures: [], targetDir: path.resolve(opts.dir || ".") };
82
99
  }
83
100
 
84
101
  const result = await installItems(selected, opts.dir || process.cwd(), {
85
- readSourceFile: opts.readSourceFile
102
+ readSourceFile: opts.readSourceFile,
103
+ force: opts.force
86
104
  });
87
105
  for (const file of result.successes) {
88
106
  console.log(`installed ${file}`);
89
107
  }
108
+ for (const file of result.skipped) {
109
+ console.log(`skipped ${file} (already exists; use --force to overwrite)`);
110
+ }
90
111
  for (const failure of result.failures) {
91
112
  console.log(`failed ${failure.dest} (${failure.error})`);
92
113
  }
93
- console.log(`\nDone: ${result.installed} installed${result.failed ? `, ${result.failed} failed` : ""}.`);
114
+ console.log(
115
+ `\nDone: ${result.installed} installed${result.skipped.length ? `, ${result.skipped.length} skipped` : ""}${result.failed ? `, ${result.failed} failed` : ""}.`
116
+ );
94
117
  return result;
95
118
  }
package/lib/manifest.js CHANGED
@@ -11,13 +11,6 @@ export const MANIFEST = [
11
11
  files: [{ src: "CODEX.md", dest: "CODEX.md" }],
12
12
  default: true
13
13
  },
14
- {
15
- id: "claude-md",
16
- category: "Behavior Guidelines",
17
- label: "CLAUDE.md",
18
- description: "Behavior guidelines adapted for Claude Code agents.",
19
- files: [{ src: "CLAUDE.md", dest: "CLAUDE.md" }]
20
- },
21
14
  {
22
15
  id: "config-codex",
23
16
  category: "Configs",
@@ -25,6 +18,13 @@ export const MANIFEST = [
25
18
  description: "Starter Codex agent configuration.",
26
19
  files: [{ src: "configs/codex/config.json", dest: ".codex/config.json" }]
27
20
  },
21
+ {
22
+ id: "config-session-bootstrap",
23
+ category: "Configs",
24
+ label: ".codex/session-config.json",
25
+ description: "Session bootstrap and Obsidian bridge settings.",
26
+ files: [{ src: "configs/codex/session-config.json", dest: ".codex/session-config.json" }]
27
+ },
28
28
  {
29
29
  id: "config-vscode",
30
30
  category: "Configs",
@@ -32,6 +32,27 @@ export const MANIFEST = [
32
32
  description: "VS Code settings for AI-assisted development.",
33
33
  files: [{ src: "configs/vscode/settings.json", dest: ".vscode/settings.json" }]
34
34
  },
35
+ {
36
+ id: "prompt-session-bootstrap",
37
+ category: "Prompts",
38
+ label: "session-bootstrap",
39
+ description: "Session startup instructions for Codex.",
40
+ files: [{ src: "prompts/session-bootstrap.md", dest: "prompts/session-bootstrap.md" }]
41
+ },
42
+ {
43
+ id: "prompt-code-style",
44
+ category: "Prompts",
45
+ label: "code-style",
46
+ description: "Obsidian-backed code style note template.",
47
+ files: [{ src: "prompts/code-style.md", dest: "prompts/code-style.md" }]
48
+ },
49
+ {
50
+ id: "prompt-meaningful-work",
51
+ category: "Prompts",
52
+ label: "meaningful-work",
53
+ description: "Criteria and article template for meaningful Codex work.",
54
+ files: [{ src: "prompts/meaningful-work.md", dest: "prompts/meaningful-work.md" }]
55
+ },
35
56
  {
36
57
  id: "prompt-solo-dev",
37
58
  category: "Prompts",
@@ -69,6 +90,15 @@ export const MANIFEST = [
69
90
  { src: "skills/publishing-npm-packages/SKILL.md", dest: "skills/publishing-npm-packages/SKILL.md" }
70
91
  ]
71
92
  },
93
+ {
94
+ id: "skill-obsidian-session-loop",
95
+ category: "Skills",
96
+ label: "obsidian-session-loop",
97
+ description: "A Codex skill for session startup, Obsidian sync, and end-of-session article drafting.",
98
+ files: [
99
+ { src: "skills/obsidian-session-loop/SKILL.md", dest: "skills/obsidian-session-loop/SKILL.md" }
100
+ ]
101
+ },
72
102
  {
73
103
  id: "plugin-context-window-compressor",
74
104
  category: "Plugins",
@@ -79,6 +109,16 @@ export const MANIFEST = [
79
109
  { src: "plugins/context-window-compressor/plugin.json", dest: "plugins/context-window-compressor/plugin.json" }
80
110
  ]
81
111
  },
112
+ {
113
+ id: "plugin-obsidian-codex-bridge",
114
+ category: "Plugins",
115
+ label: "obsidian-codex-bridge",
116
+ description: "Plugin sketch for syncing Codex notes and article drafts with Obsidian.",
117
+ files: [
118
+ { src: "plugins/obsidian-codex-bridge/README.md", dest: "plugins/obsidian-codex-bridge/README.md" },
119
+ { src: "plugins/obsidian-codex-bridge/plugin.json", dest: "plugins/obsidian-codex-bridge/plugin.json" }
120
+ ]
121
+ },
82
122
  {
83
123
  id: "workflow-solo-dev-loop",
84
124
  category: "Workflows",
@@ -0,0 +1,354 @@
1
+ import { execFile } from "node:child_process";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { promisify } from "node:util";
6
+
7
+ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
+ const execFileAsync = promisify(execFile);
9
+
10
+ export function resolveConfigPath(cwd = process.cwd()) {
11
+ return path.join(cwd, ".codex", "session-config.json");
12
+ }
13
+
14
+ export function resolvePackageAsset(...segments) {
15
+ return path.join(PACKAGE_ROOT, ...segments);
16
+ }
17
+
18
+ export function defaultNoteTargets() {
19
+ return {
20
+ sessionInitializer: "Codex/Session-Initializer.md",
21
+ codeStyle: "Codex/Code-Style.md",
22
+ meaningfulWork: "Codex/Meaningful-Work.md"
23
+ };
24
+ }
25
+
26
+ export function defaultLaunchTemplates() {
27
+ return {
28
+ open: null,
29
+ reveal: null
30
+ };
31
+ }
32
+
33
+ export function normalizeVaultPath(vaultPath, cwd = process.cwd()) {
34
+ if (!vaultPath) {
35
+ return null;
36
+ }
37
+ if (vaultPath.startsWith("~")) {
38
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
39
+ return path.resolve(homeDir, vaultPath.slice(1).replace(/^\//, ""));
40
+ }
41
+ return path.resolve(cwd, vaultPath);
42
+ }
43
+
44
+ export function getVaultName(vaultPath) {
45
+ return path.basename(vaultPath);
46
+ }
47
+
48
+ export function slugifyTitle(title) {
49
+ return title
50
+ .toLowerCase()
51
+ .trim()
52
+ .replace(/[^a-z0-9]+/g, "-")
53
+ .replace(/^-+|-+$/g, "")
54
+ .slice(0, 72) || "session-note";
55
+ }
56
+
57
+ export function isoDateInSeoul(date = new Date()) {
58
+ return new Intl.DateTimeFormat("en-CA", {
59
+ timeZone: "Asia/Seoul",
60
+ year: "numeric",
61
+ month: "2-digit",
62
+ day: "2-digit"
63
+ }).format(date);
64
+ }
65
+
66
+ export async function readJsonFile(filePath) {
67
+ const raw = await readFile(filePath, "utf8");
68
+ return JSON.parse(raw);
69
+ }
70
+
71
+ function normalizeTemplateValue(value, context) {
72
+ if (typeof value === "string") {
73
+ return value
74
+ .replaceAll("{vault}", context.vaultName)
75
+ .replaceAll("{vaultPath}", context.vaultRoot)
76
+ .replaceAll("{file}", context.filePath)
77
+ .replaceAll("{mode}", context.mode);
78
+ }
79
+
80
+ if (Array.isArray(value)) {
81
+ return value.map((item) => normalizeTemplateValue(item, context));
82
+ }
83
+
84
+ return value;
85
+ }
86
+
87
+ export function resolveObsidianLaunchSpec({
88
+ cwd = process.cwd(),
89
+ config,
90
+ filePath,
91
+ mode = "open"
92
+ } = {}) {
93
+ const resolvedConfig = config || {};
94
+ const obsidian = resolvedConfig.obsidian || {};
95
+ if (!obsidian.enabled) {
96
+ return { type: "disabled" };
97
+ }
98
+
99
+ const vaultRoot = normalizeVaultPath(obsidian.vaultPath, cwd);
100
+ const vaultName = getVaultName(vaultRoot);
101
+ const notePath = filePath || obsidian.noteTargets?.sessionInitializer || defaultNoteTargets().sessionInitializer;
102
+ const absoluteNotePath = path.isAbsolute(notePath) ? notePath : path.join(vaultRoot, notePath);
103
+ const relativeFilePath = path.relative(vaultRoot, absoluteNotePath);
104
+
105
+ if (relativeFilePath.startsWith("..")) {
106
+ throw new Error(`Obsidian note must be inside the vault: ${notePath}`);
107
+ }
108
+
109
+ const templates = {
110
+ ...defaultLaunchTemplates(),
111
+ ...(obsidian.launch || {})
112
+ };
113
+ const template = templates[mode];
114
+
115
+ if (template) {
116
+ return {
117
+ type: "command",
118
+ command: template.command || obsidian.cliCommand || "obsidian",
119
+ args: normalizeTemplateValue(template.args || [], {
120
+ vaultName,
121
+ vaultRoot,
122
+ filePath: relativeFilePath,
123
+ mode
124
+ }),
125
+ cwd: vaultRoot
126
+ };
127
+ }
128
+
129
+ return {
130
+ type: "uri",
131
+ uri: `obsidian://open?vault=${encodeURIComponent(vaultName)}&file=${encodeURIComponent(relativeFilePath)}`
132
+ };
133
+ }
134
+
135
+ export async function launchObsidianTarget(spec, runner = execFileAsync) {
136
+ if (spec.type === "disabled") {
137
+ return { launched: false, mode: "disabled" };
138
+ }
139
+
140
+ if (spec.type === "command") {
141
+ await runner(spec.command, spec.args, { cwd: spec.cwd });
142
+ return { launched: true, mode: "command" };
143
+ }
144
+
145
+ if (spec.type === "uri") {
146
+ if (process.platform === "darwin") {
147
+ await runner("open", [spec.uri]);
148
+ } else if (process.platform === "linux") {
149
+ await runner("xdg-open", [spec.uri]);
150
+ } else if (process.platform === "win32") {
151
+ await runner("cmd", ["/c", "start", "", spec.uri]);
152
+ } else {
153
+ throw new Error(`Unsupported platform for launching Obsidian: ${process.platform}`);
154
+ }
155
+ return { launched: true, mode: "uri" };
156
+ }
157
+
158
+ throw new Error(`Unknown Obsidian launch spec type: ${spec.type}`);
159
+ }
160
+
161
+ export async function loadSessionConfig(cwd = process.cwd(), readJson = readJsonFile) {
162
+ const projectConfigPath = resolveConfigPath(cwd);
163
+
164
+ try {
165
+ return await readJson(projectConfigPath);
166
+ } catch (error) {
167
+ if (error.code !== "ENOENT") {
168
+ throw error;
169
+ }
170
+ }
171
+
172
+ const packageConfigPath = resolvePackageAsset("configs", "codex", "session-config.json");
173
+ return readJson(packageConfigPath);
174
+ }
175
+
176
+ export function shouldDraftArticle({ signals = [], config, integrationBoundary = false }) {
177
+ if (integrationBoundary) {
178
+ return true;
179
+ }
180
+
181
+ const minimumSignals = config?.articleRules?.minimumSignalsForDraft ?? 2;
182
+ return signals.length >= minimumSignals;
183
+ }
184
+
185
+ export function buildArticleMarkdown({
186
+ title,
187
+ summary,
188
+ changes = [],
189
+ decisions = [],
190
+ verification = [],
191
+ nextSteps = [],
192
+ signals = []
193
+ }) {
194
+ const sections = [
195
+ `# ${title}`,
196
+ "",
197
+ `- Date: ${isoDateInSeoul()}`,
198
+ signals.length ? `- Signals: ${signals.join(", ")}` : null,
199
+ "",
200
+ "## Why It Mattered",
201
+ summary || "TBD",
202
+ "",
203
+ "## What Changed",
204
+ ...(changes.length ? changes.map((change) => `- ${change}`) : ["- TBD"]),
205
+ "",
206
+ "## Decisions",
207
+ ...(decisions.length ? decisions.map((decision) => `- ${decision}`) : ["- TBD"]),
208
+ "",
209
+ "## Verification",
210
+ ...(verification.length ? verification.map((item) => `- ${item}`) : ["- TBD"]),
211
+ "",
212
+ "## Next Steps",
213
+ ...(nextSteps.length ? nextSteps.map((step) => `- ${step}`) : ["- TBD"])
214
+ ];
215
+
216
+ return sections.filter((line) => line !== null).join("\n");
217
+ }
218
+
219
+ async function readSourceMarkdown(relativePath, cwd = process.cwd()) {
220
+ if (path.isAbsolute(relativePath)) {
221
+ return readFile(relativePath, "utf8");
222
+ }
223
+
224
+ const localPath = path.resolve(cwd, relativePath);
225
+ try {
226
+ return await readFile(localPath, "utf8");
227
+ } catch (error) {
228
+ if (error.code !== "ENOENT") {
229
+ throw error;
230
+ }
231
+ }
232
+
233
+ return readFile(resolvePackageAsset(relativePath), "utf8");
234
+ }
235
+
236
+ async function ensureDirectoryFor(filePath) {
237
+ await mkdir(path.dirname(filePath), { recursive: true });
238
+ }
239
+
240
+ async function writeIfNeeded(destination, content, force) {
241
+ if (!force) {
242
+ try {
243
+ await readFile(destination, "utf8");
244
+ return "skipped";
245
+ } catch (error) {
246
+ if (error.code !== "ENOENT") {
247
+ throw error;
248
+ }
249
+ }
250
+ }
251
+
252
+ await ensureDirectoryFor(destination);
253
+ await writeFile(destination, content, "utf8");
254
+ return "written";
255
+ }
256
+
257
+ export async function syncObsidianNotes({ cwd = process.cwd(), config, force = false, readSource = readSourceMarkdown } = {}) {
258
+ const resolvedConfig = config || (await loadSessionConfig(cwd));
259
+ const obsidian = resolvedConfig.obsidian || {};
260
+ if (!obsidian.enabled) {
261
+ return { synced: [], skipped: [], targetVault: null };
262
+ }
263
+
264
+ const vaultRoot = normalizeVaultPath(obsidian.vaultPath, cwd);
265
+ const targets = {
266
+ ...defaultNoteTargets(),
267
+ ...(obsidian.noteTargets || {})
268
+ };
269
+
270
+ const notes = [
271
+ {
272
+ src: resolvedConfig.sessionInitializer || "prompts/session-bootstrap.md",
273
+ dest: targets.sessionInitializer
274
+ },
275
+ {
276
+ src: resolvedConfig.codeStyleNote || "prompts/code-style.md",
277
+ dest: targets.codeStyle
278
+ },
279
+ {
280
+ src: resolvedConfig.meaningfulWorkNote || "prompts/meaningful-work.md",
281
+ dest: targets.meaningfulWork
282
+ }
283
+ ];
284
+
285
+ const synced = [];
286
+ const skipped = [];
287
+
288
+ for (const note of notes) {
289
+ const sourceContent = await readSource(note.src, cwd);
290
+ const destination = path.join(vaultRoot, note.dest);
291
+ const status = await writeIfNeeded(destination, sourceContent, force);
292
+ if (status === "written") {
293
+ synced.push(note.dest);
294
+ } else {
295
+ skipped.push(note.dest);
296
+ }
297
+ }
298
+
299
+ return { synced, skipped, targetVault: vaultRoot };
300
+ }
301
+
302
+ export async function draftObsidianArticle({
303
+ cwd = process.cwd(),
304
+ config,
305
+ title,
306
+ summary,
307
+ changes = [],
308
+ decisions = [],
309
+ verification = [],
310
+ nextSteps = [],
311
+ signals = [],
312
+ integrationBoundary = false,
313
+ force = false
314
+ } = {}) {
315
+ const resolvedConfig = config || (await loadSessionConfig(cwd));
316
+ const obsidian = resolvedConfig.obsidian || {};
317
+ if (!obsidian.enabled) {
318
+ return { written: null, skipped: true, targetVault: null };
319
+ }
320
+
321
+ if (!shouldDraftArticle({ signals, config: resolvedConfig, integrationBoundary })) {
322
+ return { written: null, skipped: true, targetVault: normalizeVaultPath(obsidian.vaultPath, cwd) };
323
+ }
324
+
325
+ const vaultRoot = normalizeVaultPath(obsidian.vaultPath, cwd);
326
+ const inboxPath = obsidian.inboxPath || "Inbox/Codex";
327
+ const articleTitle = title || "Codex Session Article";
328
+ const fileName = `${isoDateInSeoul()}-${slugifyTitle(articleTitle)}.md`;
329
+ const destination = path.join(vaultRoot, inboxPath, fileName);
330
+ const content = buildArticleMarkdown({
331
+ title: articleTitle,
332
+ summary,
333
+ changes,
334
+ decisions,
335
+ verification,
336
+ nextSteps,
337
+ signals
338
+ });
339
+
340
+ const status = await writeIfNeeded(destination, content, force);
341
+ return { written: status === "written" ? path.relative(vaultRoot, destination) : null, skipped: status !== "written", targetVault: vaultRoot };
342
+ }
343
+
344
+ export async function openObsidianNote({
345
+ cwd = process.cwd(),
346
+ config,
347
+ filePath,
348
+ mode = "open",
349
+ runner
350
+ } = {}) {
351
+ const spec = resolveObsidianLaunchSpec({ cwd, config, filePath, mode });
352
+ const result = await launchObsidianTarget(spec, runner);
353
+ return { ...result, spec };
354
+ }
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "codex-arsenal",
3
- "version": "0.2.1",
4
- "description": "A practical arsenal of Codex and Claude Code plugins, skills, prompts, workflows, and configs.",
3
+ "version": "0.4.0",
4
+ "description": "A practical arsenal of Codex plugins, skills, prompts, workflows, and configs.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "codex-arsenal": "bin/cli.js"
7
+ "codex-arsenal": "bin/cli.js",
8
+ "codex-obsidian-bridge": "bin/obsidian-bridge.js"
8
9
  },
9
10
  "scripts": {
10
11
  "test": "node --test"
@@ -22,9 +23,10 @@
22
23
  },
23
24
  "files": [
24
25
  "bin/",
26
+ "docs/codex-plugin-distribution.md",
27
+ "docs/npm-publishing.md",
25
28
  "lib/",
26
29
  "CODEX.md",
27
- "CLAUDE.md",
28
30
  "configs/",
29
31
  "plugins/",
30
32
  "prompts/",
@@ -36,7 +38,6 @@
36
38
  },
37
39
  "keywords": [
38
40
  "codex",
39
- "claude-code",
40
41
  "ai-agent",
41
42
  "developer-tools",
42
43
  "prompt-engineering"
@@ -0,0 +1,30 @@
1
+ # obsidian-codex-bridge
2
+
3
+ Plugin sketch for connecting Codex sessions to an Obsidian vault.
4
+
5
+ ## Goal
6
+
7
+ Make the session bootstrap, code-style note, and end-of-session article draft part of one loop instead of separate manual steps.
8
+
9
+ ## Proposed Modes
10
+
11
+ - `bootstrap`: read the configured session note and merge project settings.
12
+ - `sync`: copy or mirror the latest code-style and session notes into the vault.
13
+ - `draft-article`: write a session article into the Obsidian inbox when the meaningful-work criteria are met.
14
+
15
+ ## Suggested Settings
16
+
17
+ - `configs/codex/session-config.json`
18
+ - `prompts/session-bootstrap.md`
19
+ - `prompts/code-style.md`
20
+ - `prompts/meaningful-work.md`
21
+
22
+ ## Obsidian CLI
23
+
24
+ If an `obsidian` command is available, the bridge can use it to open the vault, reveal the generated note, or hand off to a vault-local workflow. If the CLI is not available, keep the markdown files as the source of truth and mirror them manually.
25
+
26
+ ## Practical CLI
27
+
28
+ The repository now ships a small `codex-obsidian-bridge` command that can sync the notes, open or reveal a note, and draft an article into the configured inbox.
29
+
30
+ If a project has an Obsidian CLI, configure `obsidian.launch.open` and `obsidian.launch.reveal` in `.codex/session-config.json`. Otherwise the bridge falls back to an `obsidian://` URI launch.
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "obsidian-codex-bridge",
3
+ "version": "0.1.0",
4
+ "description": "Sketch for connecting Codex session notes and article drafts to Obsidian.",
5
+ "entry": "README.md"
6
+ }
@@ -0,0 +1,21 @@
1
+ # Code Style Note
2
+
3
+ This note is meant to live in Obsidian and flow back into session startup.
4
+
5
+ ## Baseline Style
6
+
7
+ - Prefer small, direct modules with one responsibility.
8
+ - Keep names explicit and stable.
9
+ - Favor readable control flow over clever abstractions.
10
+ - Match the repository's existing style before introducing new patterns.
11
+ - Keep comments rare and only use them when the intent would otherwise be hard to follow.
12
+
13
+ ## Example Source of Inspiration
14
+
15
+ Use widely adopted linting conventions such as Airbnb-style JavaScript guidance as a reference point, but adapt the final rules to the current repository.
16
+
17
+ ## Project-Specific Overrides
18
+
19
+ - Document any exceptions here before asking the agent to apply them.
20
+ - If a project wants a different style for a specific directory, note that directory here.
21
+ - Keep the note short enough that it can be read at the start of every session.
@@ -0,0 +1,30 @@
1
+ # Meaningful Work Criteria
2
+
3
+ Use these criteria to decide when a Codex session should generate an Obsidian article at the end of the task.
4
+
5
+ ## Trigger Rules
6
+
7
+ Draft an article when the session meets at least two of these signals:
8
+
9
+ - the change touches two or more repository files in one coherent slice;
10
+ - the task introduces or changes installable content, configs, or workflows;
11
+ - the work changes behavior, not just copy or formatting;
12
+ - the change requires verification beyond a quick smoke check;
13
+ - the task includes a substantive README or documentation update.
14
+
15
+ Always draft an article when the session introduces a new integration boundary, such as a bridge to Obsidian or another external tool.
16
+
17
+ ## Article Template
18
+
19
+ Use this outline for the draft:
20
+
21
+ 1. Title
22
+ 2. Why the change mattered
23
+ 3. What changed
24
+ 4. Decisions and tradeoffs
25
+ 5. Verification performed
26
+ 6. What to revisit later
27
+
28
+ ## Writing Rule
29
+
30
+ Keep the article concrete. Write about the decision, the structure, and the follow-up work, not just the file list.
@@ -0,0 +1,22 @@
1
+ # Session Bootstrap
2
+
3
+ Use this file as the first project-local note after `CODEX.md`.
4
+
5
+ ## Startup Order
6
+
7
+ 1. Read `configs/codex/session-config.json` if it exists.
8
+ 2. Load the configured session initializer, code style note, and meaningful-work note.
9
+ 3. Merge the current task with the project-specific style guidance.
10
+ 4. Check whether Obsidian integration is available before drafting session notes.
11
+ 5. If the bridge is available, sync the session notes before starting the task.
12
+
13
+ ## What This Session Needs
14
+
15
+ - Keep a short, explicit summary of the current goal.
16
+ - Keep the active assumptions visible.
17
+ - Prefer the simplest change that still satisfies the task.
18
+ - Treat the configured code-style note as the source of truth for formatting and tone.
19
+
20
+ ## End-of-Session Rule
21
+
22
+ If the current work satisfies the meaningful-work criteria, create a draft article in the configured Obsidian inbox and summarize the result there.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: obsidian-session-loop
3
+ description: Use when starting or ending a Codex session that should read project-local session notes, sync Obsidian-backed style guidance, and draft an article when the work is meaningful.
4
+ ---
5
+
6
+ # Obsidian Session Loop
7
+
8
+ ## Overview
9
+
10
+ Use this skill when a project keeps session guidance in `CODEX.md`, stores session settings in `.codex/session-config.json`, and mirrors the working notes into an Obsidian vault.
11
+
12
+ ## When To Use
13
+
14
+ - At the start of a Codex session for this repository.
15
+ - When the project wants a code-style note to influence the current session.
16
+ - When the work should produce a session article in Obsidian.
17
+ - When the task touches the session bootstrap, Obsidian bridge, or the meaningful-work criteria.
18
+
19
+ ## Session Startup
20
+
21
+ 1. Read `CODEX.md` first.
22
+ 2. Load `.codex/session-config.json` if it exists.
23
+ 3. Read the configured session initializer, code-style note, and meaningful-work note.
24
+ 4. Sync the notes into Obsidian if `codex-obsidian-bridge` is available.
25
+ 5. Merge the code-style note into the current working style before editing files.
26
+
27
+ ## Meaningful Work Check
28
+
29
+ Treat the work as meaningful when at least two of these are true:
30
+
31
+ - the change spans multiple files in one coherent slice;
32
+ - the task adds or changes installable content, configs, or workflows;
33
+ - the work changes behavior rather than only copy;
34
+ - verification is required beyond a trivial smoke test;
35
+ - the README or user-facing guidance needs a substantive update.
36
+
37
+ Always treat new integration boundaries as meaningful, including the Obsidian bridge itself.
38
+
39
+ ## End Of Session
40
+
41
+ 1. Summarize the actual change, not the file list.
42
+ 2. If the meaningful-work check passes, draft an Obsidian article with the bridge CLI.
43
+ 3. Include what changed, why it mattered, the tradeoffs, verification, and follow-up work.
44
+
45
+ ## Guardrails
46
+
47
+ - Keep the session bootstrap short and readable.
48
+ - Prefer the configured note paths over hardcoded vault locations.
49
+ - Do not draft an article for tiny formatting-only changes.
50
+ - Do not overwrite a user’s Obsidian note unless `--force` was intentional.
package/CLAUDE.md DELETED
@@ -1,15 +0,0 @@
1
- # CLAUDE.md
2
-
3
- Use this file as a project-local behavior guide for Claude Code agents.
4
-
5
- ## Operating Principles
6
-
7
- - Read the repository instructions before editing.
8
- - Keep changes small and directly tied to the task.
9
- - Prefer tests before behavior changes.
10
- - Explain uncertainty instead of guessing silently.
11
- - Do not overwrite user work unless explicitly asked.
12
-
13
- ## Completion Criteria
14
-
15
- Before reporting completion, run the smallest command that proves the change works and summarize the result.