codex-arsenal 0.3.0 → 0.5.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 +5 -29
- package/README.md +58 -1
- package/bin/obsidian-bridge.js +120 -0
- package/configs/codex/session-config.json +30 -0
- package/lib/manifest.js +80 -0
- package/lib/obsidian-bridge.js +358 -0
- package/package.json +3 -2
- package/plugins/obsidian-codex-bridge/README.md +30 -0
- package/plugins/obsidian-codex-bridge/plugin.json +6 -0
- package/prompts/code-style.md +21 -0
- package/prompts/meaningful-work.md +30 -0
- package/prompts/session-bootstrap.md +22 -0
- package/skills/obsidian-session-loop/SKILL.md +50 -0
- package/skills/plan-counterargument/SKILL.md +17 -0
- package/skills/token-efficient-execution/SKILL.md +18 -0
package/CODEX.md
CHANGED
|
@@ -69,9 +69,9 @@ Transform tasks into verifiable goals:
|
|
|
69
69
|
For multi-step tasks, state a brief plan:
|
|
70
70
|
|
|
71
71
|
```
|
|
72
|
-
1. [Step]
|
|
73
|
-
2. [Step]
|
|
74
|
-
3. [Step]
|
|
72
|
+
1. [Step] -> verify: [check]
|
|
73
|
+
2. [Step] -> verify: [check]
|
|
74
|
+
3. [Step] -> verify: [check]
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
Strong success criteria let you loop independently.
|
|
@@ -91,31 +91,7 @@ Weak criteria ("make it work") require constant clarification.
|
|
|
91
91
|
|
|
92
92
|
---
|
|
93
93
|
|
|
94
|
-
## 6.
|
|
95
|
-
|
|
96
|
-
**Spend context only where it changes the decision.**
|
|
97
|
-
|
|
98
|
-
- Start with filenames, symbols, and short snippets before reading full files.
|
|
99
|
-
- Prefer `rg` and targeted search over broad recursive reads.
|
|
100
|
-
- Summarize long outputs immediately: key facts, file paths, line numbers, open questions.
|
|
101
|
-
- Do not paste large logs back to the user. Quote only the failing line or smallest useful excerpt.
|
|
102
|
-
- Reuse already-loaded context instead of re-reading the same files.
|
|
103
|
-
- When handing off, compress to: goal, changed files, commands run, verification status, next risk.
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
## 7. Context Awareness
|
|
108
|
-
|
|
109
|
-
**Codex has a limited context window — use it wisely.**
|
|
110
|
-
|
|
111
|
-
- Don't re-read files you've already loaded. Reference prior output instead.
|
|
112
|
-
- When summarizing large codebases, compress systematically: directory tree → key files → function signatures.
|
|
113
|
-
- If context is filling up, state it: *"I'm approaching context limits. Summarizing and continuing."*
|
|
114
|
-
- Prefer targeted grep/search over reading entire files when looking for a specific symbol.
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## 8. Tool Use Discipline
|
|
94
|
+
## 6. Tool Use Discipline
|
|
119
95
|
|
|
120
96
|
**Every tool call has a cost. Make them count.**
|
|
121
97
|
|
|
@@ -126,7 +102,7 @@ Weak criteria ("make it work") require constant clarification.
|
|
|
126
102
|
|
|
127
103
|
---
|
|
128
104
|
|
|
129
|
-
##
|
|
105
|
+
## These guidelines are working if:
|
|
130
106
|
|
|
131
107
|
- Diffs contain fewer unnecessary changes
|
|
132
108
|
- Fewer rewrites due to overcomplication
|
package/README.md
CHANGED
|
@@ -24,6 +24,14 @@ Install specific items:
|
|
|
24
24
|
npx codex-arsenal get codex-md skill-publishing-npm-packages
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
Install optional planning, token-efficiency, or Obsidian support:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx codex-arsenal get option-plan-counterargument
|
|
31
|
+
npx codex-arsenal get option-token-efficient-execution
|
|
32
|
+
npx codex-arsenal get option-obsidian-session-loop
|
|
33
|
+
```
|
|
34
|
+
|
|
27
35
|
Install into another directory:
|
|
28
36
|
|
|
29
37
|
```bash
|
|
@@ -42,12 +50,16 @@ npm exec --yes --package=codex-arsenal -- codex-arsenal list
|
|
|
42
50
|
codex-arsenal init [--yes] [--force] [--dir <path>]
|
|
43
51
|
codex-arsenal list
|
|
44
52
|
codex-arsenal get <id...> [--force] [--dir <path>]
|
|
53
|
+
codex-obsidian-bridge sync [--dir <path>] [--force]
|
|
54
|
+
codex-obsidian-bridge draft --title <text> --summary <text> [options]
|
|
45
55
|
```
|
|
46
56
|
|
|
47
57
|
- `init` opens a small selector. With `--yes`, it installs default items without prompting.
|
|
48
58
|
- `list` prints all installable manifest entries grouped by category.
|
|
49
59
|
- `get` installs one or more manifest entries by id.
|
|
50
60
|
- Existing files are skipped by default. Add `--force` to overwrite them intentionally.
|
|
61
|
+
- `codex-obsidian-bridge sync` mirrors the session bootstrap, code style, and meaningful-work notes into an Obsidian vault.
|
|
62
|
+
- `codex-obsidian-bridge draft` writes an article draft into the configured Obsidian inbox when the meaningful-work threshold is met.
|
|
51
63
|
|
|
52
64
|
The package is published on npm as [`codex-arsenal`](https://www.npmjs.com/package/codex-arsenal).
|
|
53
65
|
|
|
@@ -64,12 +76,26 @@ The package is published on npm as [`codex-arsenal`](https://www.npmjs.com/packa
|
|
|
64
76
|
| ID | Installs | Purpose |
|
|
65
77
|
| --- | --- | --- |
|
|
66
78
|
| `config-codex` | `.codex/config.json` | Starter Codex config. |
|
|
79
|
+
| `config-session-bootstrap` | `.codex/session-config.json` | Session bootstrap and Obsidian bridge settings. |
|
|
67
80
|
| `config-vscode` | `.vscode/settings.json` | VS Code settings for agent-assisted development. |
|
|
68
81
|
|
|
82
|
+
### Add-ons
|
|
83
|
+
|
|
84
|
+
These are opt-in: `init --yes` installs only the default `codex-md` guidance. Select an add-on in the interactive `init` prompt or pass its ID to `get`.
|
|
85
|
+
|
|
86
|
+
| ID | Installs | Purpose |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| `option-plan-counterargument` | `skills/plan-counterargument/SKILL.md` | Challenge a proposed plan and revise it when the evidence falsifies it. |
|
|
89
|
+
| `option-token-efficient-execution` | `skills/token-efficient-execution/SKILL.md` | Conserve context and tool-output budget without skipping verification. |
|
|
90
|
+
| `option-obsidian-session-loop` | Obsidian config, prompts, skill, and bridge sketch | Add session-note sync and meaningful-work article drafting in Obsidian. |
|
|
91
|
+
|
|
69
92
|
### Prompts
|
|
70
93
|
|
|
71
94
|
| ID | Installs | Purpose |
|
|
72
95
|
| --- | --- | --- |
|
|
96
|
+
| `prompt-session-bootstrap` | `prompts/session-bootstrap.md` | Session startup instructions for Codex. |
|
|
97
|
+
| `prompt-code-style` | `prompts/code-style.md` | Obsidian-backed code style note template. |
|
|
98
|
+
| `prompt-meaningful-work` | `prompts/meaningful-work.md` | Criteria and template for end-of-session articles. |
|
|
73
99
|
| `prompt-solo-dev` | `prompts/system-prompts/solo-dev.md` | Solo developer system prompt template. |
|
|
74
100
|
|
|
75
101
|
### Skills
|
|
@@ -79,12 +105,43 @@ The package is published on npm as [`codex-arsenal`](https://www.npmjs.com/packa
|
|
|
79
105
|
| `skill-debug-workflow` | `skills/debug-workflow/` | Reproduce, diagnose, test, and fix bugs systematically. |
|
|
80
106
|
| `skill-test-gen` | `skills/test-gen/` | Generate focused tests from behavior notes and function signatures. |
|
|
81
107
|
| `skill-publishing-npm-packages` | `skills/publishing-npm-packages/SKILL.md` | Prepare, troubleshoot, and automate npm releases with Trusted Publishing. |
|
|
108
|
+
| `skill-obsidian-session-loop` | `skills/obsidian-session-loop/SKILL.md` | Session startup, Obsidian sync, and end-of-session article drafting. |
|
|
82
109
|
|
|
83
110
|
### Plugins
|
|
84
111
|
|
|
85
112
|
| ID | Installs | Purpose |
|
|
86
113
|
| --- | --- | --- |
|
|
87
114
|
| `plugin-context-window-compressor` | `plugins/context-window-compressor/` | Plugin sketch for compressing long agent context into concise handoff notes. |
|
|
115
|
+
| `plugin-obsidian-codex-bridge` | `plugins/obsidian-codex-bridge/` | Plugin sketch for syncing Codex notes and article drafts with Obsidian. |
|
|
116
|
+
|
|
117
|
+
## Obsidian Bridge
|
|
118
|
+
|
|
119
|
+
The bridge is designed around two repeatable actions:
|
|
120
|
+
|
|
121
|
+
1. `sync` the session initializer and style notes into a vault so the notes stay close to where they are read.
|
|
122
|
+
2. `draft` a session article when the configured meaningful-work criteria are met.
|
|
123
|
+
|
|
124
|
+
The default config lives in `.codex/session-config.json` and can be customized per project. A minimal vault layout looks like this:
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
Codex/
|
|
128
|
+
Session-Initializer.md
|
|
129
|
+
Code-Style.md
|
|
130
|
+
Meaningful-Work.md
|
|
131
|
+
Inbox/
|
|
132
|
+
Codex/
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Example commands:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
codex-obsidian-bridge sync --dir .
|
|
139
|
+
codex-obsidian-bridge open --dir . --file "Codex/Session-Initializer.md"
|
|
140
|
+
codex-obsidian-bridge reveal --dir . --file "Inbox/Codex/2026-06-11-refined-session-bootstrap.md"
|
|
141
|
+
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"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
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
145
|
|
|
89
146
|
### Workflows
|
|
90
147
|
|
|
@@ -100,7 +157,7 @@ codex-arsenal/
|
|
|
100
157
|
lib/ manifest, installer, and fetcher
|
|
101
158
|
configs/ reusable editor and agent configs
|
|
102
159
|
plugins/ plugin sketches
|
|
103
|
-
prompts/ system prompt templates
|
|
160
|
+
prompts/ system prompt templates and session notes
|
|
104
161
|
skills/ reusable agent skills
|
|
105
162
|
workflows/ repeatable agentic workflows
|
|
106
163
|
references/ curated references and notes
|
|
@@ -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
|
+
}
|
package/lib/manifest.js
CHANGED
|
@@ -18,6 +18,13 @@ export const MANIFEST = [
|
|
|
18
18
|
description: "Starter Codex agent configuration.",
|
|
19
19
|
files: [{ src: "configs/codex/config.json", dest: ".codex/config.json" }]
|
|
20
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
|
+
},
|
|
21
28
|
{
|
|
22
29
|
id: "config-vscode",
|
|
23
30
|
category: "Configs",
|
|
@@ -25,6 +32,60 @@ export const MANIFEST = [
|
|
|
25
32
|
description: "VS Code settings for AI-assisted development.",
|
|
26
33
|
files: [{ src: "configs/vscode/settings.json", dest: ".vscode/settings.json" }]
|
|
27
34
|
},
|
|
35
|
+
{
|
|
36
|
+
id: "option-plan-counterargument",
|
|
37
|
+
category: "Add-ons",
|
|
38
|
+
label: "plan-counterargument",
|
|
39
|
+
description: "Challenge and falsify a proposed plan before implementation.",
|
|
40
|
+
files: [
|
|
41
|
+
{ src: "skills/plan-counterargument/SKILL.md", dest: "skills/plan-counterargument/SKILL.md" }
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "option-token-efficient-execution",
|
|
46
|
+
category: "Add-ons",
|
|
47
|
+
label: "token-efficient-execution",
|
|
48
|
+
description: "Use focused context and tool output while preserving verification quality.",
|
|
49
|
+
files: [
|
|
50
|
+
{ src: "skills/token-efficient-execution/SKILL.md", dest: "skills/token-efficient-execution/SKILL.md" }
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "option-obsidian-session-loop",
|
|
55
|
+
category: "Add-ons",
|
|
56
|
+
label: "obsidian-session-loop",
|
|
57
|
+
description: "Install the optional Obsidian session settings, notes, skill, and bridge sketch.",
|
|
58
|
+
files: [
|
|
59
|
+
{ src: "configs/codex/session-config.json", dest: ".codex/session-config.json" },
|
|
60
|
+
{ src: "prompts/session-bootstrap.md", dest: "prompts/session-bootstrap.md" },
|
|
61
|
+
{ src: "prompts/code-style.md", dest: "prompts/code-style.md" },
|
|
62
|
+
{ src: "prompts/meaningful-work.md", dest: "prompts/meaningful-work.md" },
|
|
63
|
+
{ src: "skills/obsidian-session-loop/SKILL.md", dest: "skills/obsidian-session-loop/SKILL.md" },
|
|
64
|
+
{ src: "plugins/obsidian-codex-bridge/README.md", dest: "plugins/obsidian-codex-bridge/README.md" },
|
|
65
|
+
{ src: "plugins/obsidian-codex-bridge/plugin.json", dest: "plugins/obsidian-codex-bridge/plugin.json" }
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: "prompt-session-bootstrap",
|
|
70
|
+
category: "Prompts",
|
|
71
|
+
label: "session-bootstrap",
|
|
72
|
+
description: "Session startup instructions for Codex.",
|
|
73
|
+
files: [{ src: "prompts/session-bootstrap.md", dest: "prompts/session-bootstrap.md" }]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "prompt-code-style",
|
|
77
|
+
category: "Prompts",
|
|
78
|
+
label: "code-style",
|
|
79
|
+
description: "Obsidian-backed code style note template.",
|
|
80
|
+
files: [{ src: "prompts/code-style.md", dest: "prompts/code-style.md" }]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "prompt-meaningful-work",
|
|
84
|
+
category: "Prompts",
|
|
85
|
+
label: "meaningful-work",
|
|
86
|
+
description: "Criteria and article template for meaningful Codex work.",
|
|
87
|
+
files: [{ src: "prompts/meaningful-work.md", dest: "prompts/meaningful-work.md" }]
|
|
88
|
+
},
|
|
28
89
|
{
|
|
29
90
|
id: "prompt-solo-dev",
|
|
30
91
|
category: "Prompts",
|
|
@@ -62,6 +123,15 @@ export const MANIFEST = [
|
|
|
62
123
|
{ src: "skills/publishing-npm-packages/SKILL.md", dest: "skills/publishing-npm-packages/SKILL.md" }
|
|
63
124
|
]
|
|
64
125
|
},
|
|
126
|
+
{
|
|
127
|
+
id: "skill-obsidian-session-loop",
|
|
128
|
+
category: "Skills",
|
|
129
|
+
label: "obsidian-session-loop",
|
|
130
|
+
description: "A Codex skill for session startup, Obsidian sync, and end-of-session article drafting.",
|
|
131
|
+
files: [
|
|
132
|
+
{ src: "skills/obsidian-session-loop/SKILL.md", dest: "skills/obsidian-session-loop/SKILL.md" }
|
|
133
|
+
]
|
|
134
|
+
},
|
|
65
135
|
{
|
|
66
136
|
id: "plugin-context-window-compressor",
|
|
67
137
|
category: "Plugins",
|
|
@@ -72,6 +142,16 @@ export const MANIFEST = [
|
|
|
72
142
|
{ src: "plugins/context-window-compressor/plugin.json", dest: "plugins/context-window-compressor/plugin.json" }
|
|
73
143
|
]
|
|
74
144
|
},
|
|
145
|
+
{
|
|
146
|
+
id: "plugin-obsidian-codex-bridge",
|
|
147
|
+
category: "Plugins",
|
|
148
|
+
label: "obsidian-codex-bridge",
|
|
149
|
+
description: "Plugin sketch for syncing Codex notes and article drafts with Obsidian.",
|
|
150
|
+
files: [
|
|
151
|
+
{ src: "plugins/obsidian-codex-bridge/README.md", dest: "plugins/obsidian-codex-bridge/README.md" },
|
|
152
|
+
{ src: "plugins/obsidian-codex-bridge/plugin.json", dest: "plugins/obsidian-codex-bridge/plugin.json" }
|
|
153
|
+
]
|
|
154
|
+
},
|
|
75
155
|
{
|
|
76
156
|
id: "workflow-solo-dev-loop",
|
|
77
157
|
category: "Workflows",
|
|
@@ -0,0 +1,358 @@
|
|
|
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
|
+
// Obsidian CLI arguments and obsidian:// URIs use vault-relative POSIX paths,
|
|
110
|
+
// even when this bridge runs on Windows.
|
|
111
|
+
const vaultRelativeFilePath = relativeFilePath.split(path.sep).join("/");
|
|
112
|
+
|
|
113
|
+
const templates = {
|
|
114
|
+
...defaultLaunchTemplates(),
|
|
115
|
+
...(obsidian.launch || {})
|
|
116
|
+
};
|
|
117
|
+
const template = templates[mode];
|
|
118
|
+
|
|
119
|
+
if (template) {
|
|
120
|
+
return {
|
|
121
|
+
type: "command",
|
|
122
|
+
command: template.command || obsidian.cliCommand || "obsidian",
|
|
123
|
+
args: normalizeTemplateValue(template.args || [], {
|
|
124
|
+
vaultName,
|
|
125
|
+
vaultRoot,
|
|
126
|
+
filePath: vaultRelativeFilePath,
|
|
127
|
+
mode
|
|
128
|
+
}),
|
|
129
|
+
cwd: vaultRoot
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
type: "uri",
|
|
135
|
+
uri: `obsidian://open?vault=${encodeURIComponent(vaultName)}&file=${encodeURIComponent(vaultRelativeFilePath)}`
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function launchObsidianTarget(spec, runner = execFileAsync) {
|
|
140
|
+
if (spec.type === "disabled") {
|
|
141
|
+
return { launched: false, mode: "disabled" };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (spec.type === "command") {
|
|
145
|
+
await runner(spec.command, spec.args, { cwd: spec.cwd });
|
|
146
|
+
return { launched: true, mode: "command" };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (spec.type === "uri") {
|
|
150
|
+
if (process.platform === "darwin") {
|
|
151
|
+
await runner("open", [spec.uri]);
|
|
152
|
+
} else if (process.platform === "linux") {
|
|
153
|
+
await runner("xdg-open", [spec.uri]);
|
|
154
|
+
} else if (process.platform === "win32") {
|
|
155
|
+
await runner("cmd", ["/c", "start", "", spec.uri]);
|
|
156
|
+
} else {
|
|
157
|
+
throw new Error(`Unsupported platform for launching Obsidian: ${process.platform}`);
|
|
158
|
+
}
|
|
159
|
+
return { launched: true, mode: "uri" };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
throw new Error(`Unknown Obsidian launch spec type: ${spec.type}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function loadSessionConfig(cwd = process.cwd(), readJson = readJsonFile) {
|
|
166
|
+
const projectConfigPath = resolveConfigPath(cwd);
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
return await readJson(projectConfigPath);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (error.code !== "ENOENT") {
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const packageConfigPath = resolvePackageAsset("configs", "codex", "session-config.json");
|
|
177
|
+
return readJson(packageConfigPath);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function shouldDraftArticle({ signals = [], config, integrationBoundary = false }) {
|
|
181
|
+
if (integrationBoundary) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const minimumSignals = config?.articleRules?.minimumSignalsForDraft ?? 2;
|
|
186
|
+
return signals.length >= minimumSignals;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function buildArticleMarkdown({
|
|
190
|
+
title,
|
|
191
|
+
summary,
|
|
192
|
+
changes = [],
|
|
193
|
+
decisions = [],
|
|
194
|
+
verification = [],
|
|
195
|
+
nextSteps = [],
|
|
196
|
+
signals = []
|
|
197
|
+
}) {
|
|
198
|
+
const sections = [
|
|
199
|
+
`# ${title}`,
|
|
200
|
+
"",
|
|
201
|
+
`- Date: ${isoDateInSeoul()}`,
|
|
202
|
+
signals.length ? `- Signals: ${signals.join(", ")}` : null,
|
|
203
|
+
"",
|
|
204
|
+
"## Why It Mattered",
|
|
205
|
+
summary || "TBD",
|
|
206
|
+
"",
|
|
207
|
+
"## What Changed",
|
|
208
|
+
...(changes.length ? changes.map((change) => `- ${change}`) : ["- TBD"]),
|
|
209
|
+
"",
|
|
210
|
+
"## Decisions",
|
|
211
|
+
...(decisions.length ? decisions.map((decision) => `- ${decision}`) : ["- TBD"]),
|
|
212
|
+
"",
|
|
213
|
+
"## Verification",
|
|
214
|
+
...(verification.length ? verification.map((item) => `- ${item}`) : ["- TBD"]),
|
|
215
|
+
"",
|
|
216
|
+
"## Next Steps",
|
|
217
|
+
...(nextSteps.length ? nextSteps.map((step) => `- ${step}`) : ["- TBD"])
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
return sections.filter((line) => line !== null).join("\n");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function readSourceMarkdown(relativePath, cwd = process.cwd()) {
|
|
224
|
+
if (path.isAbsolute(relativePath)) {
|
|
225
|
+
return readFile(relativePath, "utf8");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const localPath = path.resolve(cwd, relativePath);
|
|
229
|
+
try {
|
|
230
|
+
return await readFile(localPath, "utf8");
|
|
231
|
+
} catch (error) {
|
|
232
|
+
if (error.code !== "ENOENT") {
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return readFile(resolvePackageAsset(relativePath), "utf8");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function ensureDirectoryFor(filePath) {
|
|
241
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function writeIfNeeded(destination, content, force) {
|
|
245
|
+
if (!force) {
|
|
246
|
+
try {
|
|
247
|
+
await readFile(destination, "utf8");
|
|
248
|
+
return "skipped";
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (error.code !== "ENOENT") {
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await ensureDirectoryFor(destination);
|
|
257
|
+
await writeFile(destination, content, "utf8");
|
|
258
|
+
return "written";
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export async function syncObsidianNotes({ cwd = process.cwd(), config, force = false, readSource = readSourceMarkdown } = {}) {
|
|
262
|
+
const resolvedConfig = config || (await loadSessionConfig(cwd));
|
|
263
|
+
const obsidian = resolvedConfig.obsidian || {};
|
|
264
|
+
if (!obsidian.enabled) {
|
|
265
|
+
return { synced: [], skipped: [], targetVault: null };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const vaultRoot = normalizeVaultPath(obsidian.vaultPath, cwd);
|
|
269
|
+
const targets = {
|
|
270
|
+
...defaultNoteTargets(),
|
|
271
|
+
...(obsidian.noteTargets || {})
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const notes = [
|
|
275
|
+
{
|
|
276
|
+
src: resolvedConfig.sessionInitializer || "prompts/session-bootstrap.md",
|
|
277
|
+
dest: targets.sessionInitializer
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
src: resolvedConfig.codeStyleNote || "prompts/code-style.md",
|
|
281
|
+
dest: targets.codeStyle
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
src: resolvedConfig.meaningfulWorkNote || "prompts/meaningful-work.md",
|
|
285
|
+
dest: targets.meaningfulWork
|
|
286
|
+
}
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
const synced = [];
|
|
290
|
+
const skipped = [];
|
|
291
|
+
|
|
292
|
+
for (const note of notes) {
|
|
293
|
+
const sourceContent = await readSource(note.src, cwd);
|
|
294
|
+
const destination = path.join(vaultRoot, note.dest);
|
|
295
|
+
const status = await writeIfNeeded(destination, sourceContent, force);
|
|
296
|
+
if (status === "written") {
|
|
297
|
+
synced.push(note.dest);
|
|
298
|
+
} else {
|
|
299
|
+
skipped.push(note.dest);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return { synced, skipped, targetVault: vaultRoot };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export async function draftObsidianArticle({
|
|
307
|
+
cwd = process.cwd(),
|
|
308
|
+
config,
|
|
309
|
+
title,
|
|
310
|
+
summary,
|
|
311
|
+
changes = [],
|
|
312
|
+
decisions = [],
|
|
313
|
+
verification = [],
|
|
314
|
+
nextSteps = [],
|
|
315
|
+
signals = [],
|
|
316
|
+
integrationBoundary = false,
|
|
317
|
+
force = false
|
|
318
|
+
} = {}) {
|
|
319
|
+
const resolvedConfig = config || (await loadSessionConfig(cwd));
|
|
320
|
+
const obsidian = resolvedConfig.obsidian || {};
|
|
321
|
+
if (!obsidian.enabled) {
|
|
322
|
+
return { written: null, skipped: true, targetVault: null };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (!shouldDraftArticle({ signals, config: resolvedConfig, integrationBoundary })) {
|
|
326
|
+
return { written: null, skipped: true, targetVault: normalizeVaultPath(obsidian.vaultPath, cwd) };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const vaultRoot = normalizeVaultPath(obsidian.vaultPath, cwd);
|
|
330
|
+
const inboxPath = obsidian.inboxPath || "Inbox/Codex";
|
|
331
|
+
const articleTitle = title || "Codex Session Article";
|
|
332
|
+
const fileName = `${isoDateInSeoul()}-${slugifyTitle(articleTitle)}.md`;
|
|
333
|
+
const destination = path.join(vaultRoot, inboxPath, fileName);
|
|
334
|
+
const content = buildArticleMarkdown({
|
|
335
|
+
title: articleTitle,
|
|
336
|
+
summary,
|
|
337
|
+
changes,
|
|
338
|
+
decisions,
|
|
339
|
+
verification,
|
|
340
|
+
nextSteps,
|
|
341
|
+
signals
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const status = await writeIfNeeded(destination, content, force);
|
|
345
|
+
return { written: status === "written" ? path.relative(vaultRoot, destination) : null, skipped: status !== "written", targetVault: vaultRoot };
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export async function openObsidianNote({
|
|
349
|
+
cwd = process.cwd(),
|
|
350
|
+
config,
|
|
351
|
+
filePath,
|
|
352
|
+
mode = "open",
|
|
353
|
+
runner
|
|
354
|
+
} = {}) {
|
|
355
|
+
const spec = resolveObsidianLaunchSpec({ cwd, config, filePath, mode });
|
|
356
|
+
const result = await launchObsidianTarget(spec, runner);
|
|
357
|
+
return { ...result, spec };
|
|
358
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-arsenal",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
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"
|
|
@@ -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,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.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan-counterargument
|
|
3
|
+
description: Use when planning a Codex task and the project wants each proposed plan challenged before implementation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Plan Counterargument
|
|
7
|
+
|
|
8
|
+
Before presenting or executing a multi-step plan, add a concise counterargument pass.
|
|
9
|
+
|
|
10
|
+
1. State the intended outcome and the plan's key assumptions.
|
|
11
|
+
2. Identify the strongest plausible reason the plan could be wrong, incomplete, too broad, or unnecessarily complex.
|
|
12
|
+
3. Check the repository or task evidence that would falsify the plan.
|
|
13
|
+
4. Revise the plan when the counterargument holds; otherwise, record why it does not.
|
|
14
|
+
|
|
15
|
+
Keep this proportional: one focused challenge is enough for routine changes. For high-risk work, challenge scope, dependencies, failure modes, and verification separately.
|
|
16
|
+
|
|
17
|
+
Do not use counterarguments to stall. The purpose is to improve the next action, not to manufacture uncertainty.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: token-efficient-execution
|
|
3
|
+
description: Use when a Codex task should conserve context and tool-output budget without reducing verification quality.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Token-Efficient Execution
|
|
7
|
+
|
|
8
|
+
Spend context only where it changes the decision.
|
|
9
|
+
|
|
10
|
+
- Start with filenames, symbols, and short snippets before reading full files.
|
|
11
|
+
- Prefer `rg` and targeted searches over broad recursive reads.
|
|
12
|
+
- Summarize long output immediately: key facts, paths, line numbers, open questions.
|
|
13
|
+
- Quote only the smallest useful error excerpt; do not return large logs.
|
|
14
|
+
- Reuse already-loaded context instead of reading the same files again.
|
|
15
|
+
- Batch related read-only checks when their results are needed together.
|
|
16
|
+
- For handoffs, compress to goal, changed files, commands run, verification, and next risk.
|
|
17
|
+
|
|
18
|
+
Context conservation never replaces verification. Read the full source when a narrow excerpt cannot establish correctness.
|