okforge 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -1,15 +1,88 @@
1
- # okforge
1
+ # okforge — Open Knowledge Format bundle skill
2
2
 
3
- Deterministic mechanics and a Stop-hook nudge for the **okf** Claude Code skill,
4
- which maintains an [Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)
5
- (OKF) knowledge bundle under `okf/` in any repository.
3
+ A Claude Code skill for maintaining a repository's [Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)
4
+ (OKF) knowledge bundle under `okf/`, usable in any repository.
6
5
 
7
- The skill prose lives in [`.claude/skills/okf/`](.claude/skills/okf); its
8
- deterministic mechanics live in [`src/`](src) as a small TypeScript CLI. The model
9
- writes the prose; the CLI answers "what is each folder derived from?" and "is the
10
- bundle still well-formed?".
6
+ OKF is an open, human- and agent-friendly format for **knowledge** — the
7
+ metadata, context, and curated insight that surrounds a system. A bundle is a
8
+ directory of plain markdown files: each concept document carries YAML frontmatter
9
+ with a required `type`, and reserved `index.md` / `log.md` files provide listings
10
+ and history. If you can `cat` a file you can read OKF; if you can `git clone` a
11
+ repo you can ship it.
11
12
 
12
- ## Usable in any repository
13
+ The skill prose lives in [`dotclaude_folder/skills/okf/`](dotclaude_folder/skills/okf)
14
+ (`SKILL.md` is the instruction file Claude loads, shipped as data and copied into a
15
+ target's `.claude/` by `okforge install`); its deterministic mechanics live in
16
+ [`src/`](src) as a small TypeScript CLI. The model writes the prose; the CLI
17
+ answers "what is each folder derived from?" and "is the bundle still well-formed?".
18
+
19
+ ## Blog series
20
+
21
+ A four-part series on the ideas behind okforge — keeping knowledge in sync with code:
22
+
23
+ 1. [The Faster AI Writes Code, the Faster Your Docs Rot](docs/blog_posts/post-1-the-faster-ai-writes-code-the-faster-your-docs-rot.md) — AI-accelerated coding compounds knowledge debt; treat documentation as derived state, not hand-maintained prose.
24
+ 2. [Give the Model Less](docs/blog_posts/post-2-give-the-model-less.md) — reliability comes from drawing the model/deterministic boundary deliberately, and drawing it small.
25
+ 3. [Don't Make It Remember, Make It Read](docs/blog_posts/post-3-dont-make-it-remember-make-it-read.md) — grounding output in real source; hallucination is a design failure, not a model failure.
26
+ 4. [Bet on Boring Formats](docs/blog_posts/post-4-bet-on-boring-formats.md) — why okforge ships plain markdown, an open spec, and an installable skill instead of a clever app.
27
+
28
+ ## How to install
29
+
30
+ Run `install` from the root of the repository you want to add OKF to (Node >=
31
+ 20.12, no install step — `npx` fetches it):
32
+
33
+ ```bash
34
+ npx okforge install .claude
35
+ ```
36
+
37
+ When the destination folder is named `.claude`, this drops the skill prose into
38
+ `.claude/skills/okf/` **and** registers the `npx okforge nudge` Stop hook in
39
+ `.claude/settings.json` (idempotent and non-destructive — existing settings and
40
+ hooks are preserved). For any other destination it copies the skill only and
41
+ leaves `settings.json` untouched.
42
+
43
+ Then write an `.okforge.config.json` at the project root describing that repo's
44
+ folder-to-source mapping (see [`.okforge.config.json`](#usable-in-any-repository-okforgeconfigjson)
45
+ below), and ask Claude to "set up okf" — or run `/okf` — to scaffold the bundle.
46
+
47
+ ## Why a skill
48
+
49
+ The `okf/` bundle is **derived** from source — each folder is generated from
50
+ specific files, so when those files change the docs drift. The hard parts to keep
51
+ consistent are the OKF format, the folder-to-source mapping, and link integrity.
52
+ The skill keeps those uniform so you can focus on accurate prose.
53
+
54
+ ## What it does
55
+
56
+ Three modes, chosen from how you ask:
57
+
58
+ | You say | Mode | What happens |
59
+ |---|---|---|
60
+ | "set up okf", "create an OKF bundle", bundle missing | **scaffold** | Creates `.okforge.config.json`, `okf/index.md` (root, with `okf_version`), `okf/log.md`, the folders from the mapping, and refreshes each. |
61
+ | "the API changed, update okf", "refresh okf for X", "update the OKF docs" | **refresh** | Reads the current source for the affected folder(s) and regenerates only the docs whose source actually changed, grounded in what it read. |
62
+ | "check okf", "is the bundle conformant", "any dead links" | **check** | Runs the conformance and dead-link lint. |
63
+
64
+ Invoke it by asking Claude in plain language, or with `/okf`. Regeneration is
65
+ model-driven, so refresh is a **draft-then-review** loop: the skill rewrites the
66
+ affected docs, you review them, then commit. It will not silently rewrite docs
67
+ whose source did not change.
68
+
69
+ ## Automatic capture going forward
70
+
71
+ A companion `Stop` hook, `npx okforge nudge` (registered in
72
+ `.claude/settings.json`), reminds you when a session changed source that an OKF
73
+ folder documents but left `okf/` untouched. It is deliberately gentle:
74
+ non-blocking, at most once per session, and silent if you already touched `okf/`
75
+ that session. It reads the same mapping the skill uses (via `npx okforge stale`),
76
+ so the skill and the nudge never diverge.
77
+
78
+ ## Where the bundle lives
79
+
80
+ The bundle is the `okf/` directory at the repository root — a valid OKF bundle is
81
+ just a subdirectory of a larger repo, so there is no build step or manifest beyond
82
+ `okf_version` in the root `okf/index.md`. The folder-to-source mapping lives next
83
+ to it in `.okforge.config.json` at the project root.
84
+
85
+ ## Usable in any repository: `.okforge.config.json`
13
86
 
14
87
  okforge ships with **no** repository-specific paths. Each repo declares its own
15
88
  folder-to-source mapping in `.okforge.config.json` at the project root:
@@ -23,12 +96,17 @@ folder-to-source mapping in `.okforge.config.json` at the project root:
23
96
  }
24
97
  ```
25
98
 
26
- With no config present, `map`/`folders` are empty and `stale` is a no-op; `check`
27
- still works, since it lints the bundle's markdown alone.
99
+ Each key is an OKF concept folder; each value is the list of source path prefixes
100
+ that folder is derived from. With no config present, `map`/`folders` are empty and
101
+ `stale` is a no-op; `check` still works, since it lints the bundle's markdown
102
+ alone. This is the only project-specific part — both the skill and the nudge read
103
+ it from here.
28
104
 
29
105
  ## The `okforge` CLI
30
106
 
31
- Run with `npx` (Node >= 20.12). `<dir>` defaults to the current directory.
107
+ The bundled [`okforge`](https://www.npmjs.com/package/okforge) CLI owns the
108
+ deterministic mechanics, run with `npx` (Node >= 20.12). `<dir>` defaults to the
109
+ current directory (the repository root).
32
110
 
33
111
  | Command | Purpose |
34
112
  |---|---|
@@ -38,6 +116,11 @@ Run with `npx` (Node >= 20.12). `<dir>` defaults to the current directory.
38
116
  | `okforge stale [<dir>]` | List folders whose source changed since HEAD while the folder was not edited. |
39
117
  | `okforge check [<dir>]` | Conformance + dead-link lint; exits non-zero on problems. |
40
118
  | `okforge nudge` | Stop-hook entry: read the hook payload on stdin and maybe remind. |
119
+ | `okforge install [<agent_folder>]` | Copy the bundled okf skill into an agent folder (default `.`); when that folder is named `.claude`, also register the `nudge` Stop hook in its `settings.json`. |
120
+
121
+ `check` verifies: snake_case names only, every non-index `.md` has a non-empty
122
+ frontmatter `type`, sub-folder `index.md` files carry no frontmatter, and every
123
+ bundle-relative `.md` link resolves.
41
124
 
42
125
  ## Development
43
126
 
@@ -48,10 +131,12 @@ npm run typecheck # tsc --noEmit
48
131
  npm run build # compile to dist/
49
132
  ```
50
133
 
134
+ You can also run the source directly with `npx tsx src/cli.ts <command>`.
135
+
51
136
  ## Layout
52
137
 
53
138
  ```
54
- src/
139
+ src/ the okforge CLI (mechanics)
55
140
  ├── cli.ts Commander entry; wires the subcommands below
56
141
  ├── misc/
57
142
  │ └── okf_store.ts mapping load, stale detection, conformance lint
@@ -61,12 +146,27 @@ src/
61
146
  ├── sources_command.ts print a folder's source paths
62
147
  ├── stale_command.ts folders whose source changed since HEAD
63
148
  ├── check_command.ts conformance + dead-link lint
64
- └── nudge_command.ts the Stop-hook nudge
65
- .claude/skills/okf/
66
- ├── SKILL.md instructions Claude loads
67
- └── README.md human-facing skill overview
149
+ ├── nudge_command.ts the Stop-hook nudge
150
+ └── install_command.ts copy the skill into a target agent folder
151
+ dotclaude_folder/ data shipped to a target's .claude/ by `okforge install`
152
+ └── skills/okf/
153
+ └── SKILL.md instructions Claude loads
68
154
  ```
69
155
 
156
+ ## Conventions
157
+
158
+ - File and folder names are snake_case. No kebab-case, no spaces.
159
+ - Every non-index `.md` is a concept document: YAML frontmatter with a non-empty
160
+ `type`, then structural markdown (headings, lists, tables, code) using the
161
+ conventional `# Schema` / `# Examples` / `# Citations` sections where they apply.
162
+ - `index.md` is reserved and carries no frontmatter — except the root
163
+ `okf/index.md`, which declares `okf_version: "0.1"` and `type: Bundle Index`.
164
+ - Cross-link concepts with bundle-relative absolute paths
165
+ (`/runtime_concepts/job_store.md`); cite real source files with repo-relative
166
+ paths (`../../packages/...`).
167
+ - Ground every claim in real source. Do not invent fields, routes, flags, or
168
+ states; if uncertain, omit.
169
+
70
170
  ## License
71
171
 
72
172
  MIT © Jerome Etienne
package/dist/cli.js CHANGED
@@ -96,8 +96,16 @@ async function main() {
96
96
  for (const file of result.files) {
97
97
  console.log(`${Chalk.green(file.action)} ${file.destination}`);
98
98
  }
99
+ if (result.hook.status === 'added') {
100
+ console.log(`${Chalk.green('hook')} registered \`npx okforge nudge\` in ${result.hook.settingsPath}`);
101
+ }
102
+ else if (result.hook.status === 'present') {
103
+ console.log(`${Chalk.dim('hook')} \`npx okforge nudge\` already registered in ${result.hook.settingsPath}`);
104
+ }
105
+ else {
106
+ console.log(Chalk.dim('hook skipped (target is not a .claude folder; pass a .claude path to register the Stop hook)'));
107
+ }
99
108
  console.log(Chalk.bold(`\n${result.files.length} file(s) → ${result.destinationDir}`));
100
- console.log('Next: register `npx okforge nudge` as a Stop hook in .claude/settings.json.');
101
109
  });
102
110
  await program.parseAsync(process.argv);
103
111
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -4,21 +4,41 @@ export type InstalledFile = {
4
4
  action: 'created' | 'updated';
5
5
  destination: string;
6
6
  };
7
+ /** Outcome of registering the Stop hook in the target's settings.json. */
8
+ export type HookOutcome = {
9
+ status: 'added' | 'present' | 'skipped';
10
+ settingsPath?: string;
11
+ };
7
12
  /** Summary returned by {@link InstallCommand.install}. */
8
13
  export type InstallResult = {
9
14
  destinationDir: string;
10
15
  files: InstalledFile[];
16
+ hook: HookOutcome;
11
17
  };
12
18
  /** `install` — copy the bundled okf skill into an AI agent folder (e.g. `.claude`). */
13
19
  export declare class InstallCommand {
14
20
  /**
15
21
  * Copy the bundled `skills/okf/` tree into `agentFolder` (the agent directory,
16
- * e.g. `.claude`), preserving the `skills/okf/...` layout, to set the skill up
17
- * in a target project. Machine-local settings are never copied.
22
+ * e.g. `.claude`), preserving the `skills/okf/...` layout. When the destination
23
+ * is itself named `.claude`, also register the `npx okforge nudge` Stop hook in
24
+ * its `settings.json` (idempotent, non-destructive); otherwise the hook step is
25
+ * skipped, since `settings.json` is a `.claude` concept.
18
26
  * @param agentFolder Destination agent folder; the caller defaults it to `.`.
19
- * @returns The destination directory and the per-file outcome.
27
+ * @returns The destination directory, the per-file outcome, and the hook outcome.
20
28
  */
21
29
  static install(agentFolder: string): InstallResult;
30
+ /**
31
+ * Merge the `npx okforge nudge` Stop hook into `claudeDir/settings.json`,
32
+ * creating the file when absent and preserving any existing settings and hooks.
33
+ * No-op when the hook is already registered.
34
+ */
35
+ static registerHook(claudeDir: string): HookOutcome;
36
+ /** Whether any entry in a `Stop` hook array already runs `okforge nudge`. */
37
+ static hasNudgeHook(stop: unknown[]): boolean;
38
+ /** `value` as a plain object, or `{}` when it is not one. */
39
+ static asRecord(value: unknown): Record<string, unknown>;
40
+ /** `value` as an array, or `[]` when it is not one. */
41
+ static asArray(value: unknown): unknown[];
22
42
  /** Paths of every file under `dir`, relative to `dir`, recursively. */
23
43
  static listFiles(dir: string): string[];
24
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"install_command.d.ts","sourceRoot":"","sources":["../../src/commands/install_command.ts"],"names":[],"mappings":"AAGA,0EAA0E;AAC1E,MAAM,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,aAAa,EAAE,CAAC;CACvB,CAAC;AAEF,uFAAuF;AACvF,qBAAa,cAAc;IAC1B;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa;IAkBlD,uEAAuE;IACvE,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE;CAavC"}
1
+ {"version":3,"file":"install_command.d.ts","sourceRoot":"","sources":["../../src/commands/install_command.ts"],"names":[],"mappings":"AAMA,0EAA0E;AAC1E,MAAM,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG;IACzB,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,IAAI,EAAE,WAAW,CAAC;CAClB,CAAC;AAEF,uFAAuF;AACvF,qBAAa,cAAc;IAC1B;;;;;;;;OAQG;IACH,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa;IAsBlD;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW;IAyBnD,6EAA6E;IAC7E,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO;IAY7C,6DAA6D;IAC7D,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAOxD,uDAAuD;IACvD,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE;IAIzC,uEAAuE;IACvE,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE;CAavC"}
@@ -1,16 +1,20 @@
1
1
  import Fs from 'node:fs';
2
2
  import Path from 'node:path';
3
+ /** The Stop-hook command registered in the target's settings.json. */
4
+ const NUDGE_COMMAND = 'npx okforge nudge';
3
5
  /** `install` — copy the bundled okf skill into an AI agent folder (e.g. `.claude`). */
4
6
  export class InstallCommand {
5
7
  /**
6
8
  * Copy the bundled `skills/okf/` tree into `agentFolder` (the agent directory,
7
- * e.g. `.claude`), preserving the `skills/okf/...` layout, to set the skill up
8
- * in a target project. Machine-local settings are never copied.
9
+ * e.g. `.claude`), preserving the `skills/okf/...` layout. When the destination
10
+ * is itself named `.claude`, also register the `npx okforge nudge` Stop hook in
11
+ * its `settings.json` (idempotent, non-destructive); otherwise the hook step is
12
+ * skipped, since `settings.json` is a `.claude` concept.
9
13
  * @param agentFolder Destination agent folder; the caller defaults it to `.`.
10
- * @returns The destination directory and the per-file outcome.
14
+ * @returns The destination directory, the per-file outcome, and the hook outcome.
11
15
  */
12
16
  static install(agentFolder) {
13
- const skillsDir = Path.join(import.meta.dirname, '..', '..', '.claude', 'skills');
17
+ const skillsDir = Path.join(import.meta.dirname, '..', '..', 'dotclaude_folder', 'skills');
14
18
  if (Fs.existsSync(skillsDir) === false) {
15
19
  throw new Error(`bundled skill files not found at ${skillsDir}`);
16
20
  }
@@ -24,7 +28,63 @@ export class InstallCommand {
24
28
  Fs.copyFileSync(Path.join(skillsDir, relative), destination);
25
29
  files.push({ name, action: exists === true ? 'updated' : 'created', destination });
26
30
  }
27
- return { destinationDir, files };
31
+ const hook = Path.basename(destinationDir) === '.claude'
32
+ ? InstallCommand.registerHook(destinationDir)
33
+ : { status: 'skipped' };
34
+ return { destinationDir, files, hook };
35
+ }
36
+ /**
37
+ * Merge the `npx okforge nudge` Stop hook into `claudeDir/settings.json`,
38
+ * creating the file when absent and preserving any existing settings and hooks.
39
+ * No-op when the hook is already registered.
40
+ */
41
+ static registerHook(claudeDir) {
42
+ const settingsPath = Path.join(claudeDir, 'settings.json');
43
+ let settings = {};
44
+ if (Fs.existsSync(settingsPath) === true) {
45
+ try {
46
+ const parsed = JSON.parse(Fs.readFileSync(settingsPath, 'utf-8'));
47
+ settings = InstallCommand.asRecord(parsed);
48
+ }
49
+ catch (error) {
50
+ const message = error instanceof Error ? error.message : String(error);
51
+ throw new Error(`invalid JSON in ${settingsPath}: ${message}`);
52
+ }
53
+ }
54
+ const hooks = InstallCommand.asRecord(settings.hooks);
55
+ const stop = InstallCommand.asArray(hooks.Stop);
56
+ if (InstallCommand.hasNudgeHook(stop) === true) {
57
+ return { status: 'present', settingsPath };
58
+ }
59
+ stop.push({ matcher: '*', hooks: [{ type: 'command', command: NUDGE_COMMAND }] });
60
+ hooks.Stop = stop;
61
+ settings.hooks = hooks;
62
+ Fs.mkdirSync(claudeDir, { recursive: true });
63
+ Fs.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
64
+ return { status: 'added', settingsPath };
65
+ }
66
+ /** Whether any entry in a `Stop` hook array already runs `okforge nudge`. */
67
+ static hasNudgeHook(stop) {
68
+ for (const entry of stop) {
69
+ for (const hook of InstallCommand.asArray(InstallCommand.asRecord(entry).hooks)) {
70
+ const command = InstallCommand.asRecord(hook).command;
71
+ if (typeof command === 'string' && command.includes('okforge nudge') === true) {
72
+ return true;
73
+ }
74
+ }
75
+ }
76
+ return false;
77
+ }
78
+ /** `value` as a plain object, or `{}` when it is not one. */
79
+ static asRecord(value) {
80
+ if (typeof value === 'object' && value !== null && Array.isArray(value) === false) {
81
+ return value;
82
+ }
83
+ return {};
84
+ }
85
+ /** `value` as an array, or `[]` when it is not one. */
86
+ static asArray(value) {
87
+ return Array.isArray(value) === true ? value : [];
28
88
  }
29
89
  /** Paths of every file under `dir`, relative to `dir`, recursively. */
30
90
  static listFiles(dir) {
@@ -1 +1 @@
1
- {"version":3,"file":"install_command.js","sourceRoot":"","sources":["../../src/commands/install_command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAe7B,uFAAuF;AACvF,MAAM,OAAO,cAAc;IAC1B;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,WAAmB;QACjC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAClF,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,KAAK,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,oCAAoC,SAAS,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACjD,MAAM,KAAK,GAAoB,EAAE,CAAC;QAClC,KAAK,MAAM,QAAQ,IAAI,cAAc,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;YAC1C,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7D,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,WAAW,CAAC,CAAC;YAC7D,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QACpF,CAAC;QACD,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;IAClC,CAAC;IAED,uEAAuE;IACvE,MAAM,CAAC,SAAS,CAAC,GAAW;QAC3B,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAClE,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC;gBAClC,KAAK,MAAM,MAAM,IAAI,cAAc,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;oBAC3E,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC5C,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzB,CAAC;QACF,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;CACD"}
1
+ {"version":3,"file":"install_command.js","sourceRoot":"","sources":["../../src/commands/install_command.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okforge",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Open Knowledge Format (OKF) skill for Claude Code — deterministic okf bundle mechanics and a Stop-hook nudge.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "types": "dist/cli.d.ts",
11
11
  "files": [
12
12
  "dist",
13
- ".claude/skills",
13
+ "dotclaude_folder",
14
14
  "LICENSE"
15
15
  ],
16
16
  "engines": {
@@ -1,141 +0,0 @@
1
- # okf — Open Knowledge Format bundle skill
2
-
3
- A Claude Code skill for maintaining the repository's Open Knowledge Format (OKF)
4
- knowledge bundle under `okf/`. Spec:
5
- https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md
6
-
7
- OKF is an open, human- and agent-friendly format for **knowledge** — the
8
- metadata, context, and curated insight that surrounds a system. A bundle is a
9
- directory of plain markdown files: each concept document carries YAML
10
- frontmatter with a required `type`, and reserved `index.md` / `log.md` files
11
- provide listings and history. If you can `cat` a file you can read OKF; if you
12
- can `git clone` a repo you can ship it.
13
-
14
- This directory holds the skill. `SKILL.md` is the instruction file Claude loads;
15
- this README is for humans browsing the repository.
16
-
17
- ## Why a skill
18
-
19
- The `okf/` bundle is **derived** from source — each folder is generated from
20
- specific files, so when those files change the docs drift. The hard parts to
21
- keep consistent are the OKF format, the folder-to-source mapping, and link
22
- integrity. The skill keeps those uniform so you can focus on accurate prose.
23
-
24
- ## What it does
25
-
26
- Three modes, chosen from how you ask:
27
-
28
- | You say | Mode | What happens |
29
- |---|---|---|
30
- | "set up okf", "create an OKF bundle", bundle missing | **scaffold** | Creates `.okforge.config.json`, `okf/index.md` (root, with `okf_version`), `okf/log.md`, the folders from the mapping, and refreshes each. |
31
- | "the API changed, update okf", "refresh okf for X", "update the OKF docs" | **refresh** | Reads the current source for the affected folder(s) and regenerates only the docs whose source actually changed, grounded in what it read. |
32
- | "check okf", "is the bundle conformant", "any dead links" | **check** | Runs the conformance and dead-link lint. |
33
-
34
- Invoke it by asking Claude in plain language, or with `/okf`.
35
-
36
- Regeneration is model-driven, so refresh is a **draft-then-review** loop: the
37
- skill rewrites the affected docs, you review them, then commit. It will not
38
- silently rewrite docs whose source did not change.
39
-
40
- ## Automatic capture going forward
41
-
42
- A companion `Stop` hook, `npx okforge nudge` (registered in
43
- `.claude/settings.json`), reminds you when a session changed source that an OKF
44
- folder documents but left `okf/` untouched. It is deliberately gentle:
45
- non-blocking, at most once per session, and silent if you already touched
46
- `okf/` that session. It reads the same mapping the skill uses (via
47
- `npx okforge stale`), so the skill and the nudge never diverge.
48
-
49
- ## Where the bundle lives
50
-
51
- The bundle is the `okf/` directory at the repository root — a valid OKF bundle
52
- is just a subdirectory of a larger repo, so there is no build step or manifest
53
- beyond `okf_version` in the root `okf/index.md`. The folder-to-source mapping
54
- lives next to it in `.okforge.config.json` at the project root.
55
-
56
- ## The `okforge` CLI
57
-
58
- The bundled [`okforge`](https://www.npmjs.com/package/okforge) CLI owns the
59
- deterministic mechanics, run with `npx` (Node >= 20.12). The model writes the
60
- prose; the CLI answers "what is each folder derived from?" and "is the bundle
61
- still well-formed?".
62
-
63
- | Command | Purpose |
64
- |---|---|
65
- | `npx okforge map [<dir>]` | Print the full folder-to-source mapping. |
66
- | `npx okforge folders [<dir>]` | List the OKF concept folders. |
67
- | `npx okforge sources <folder> [<dir>]` | Print the source paths a folder is derived from. |
68
- | `npx okforge stale [<dir>]` | List folders whose source changed since HEAD while the folder itself was not edited. |
69
- | `npx okforge check [<dir>]` | Conformance + dead-link lint; exits non-zero on problems. |
70
- | `npx okforge nudge` | Stop-hook entry: read the hook payload on stdin and maybe remind. |
71
-
72
- `<dir>` defaults to the current directory (the repository root). `check` verifies:
73
- snake_case names only, every non-index `.md` has a non-empty frontmatter `type`,
74
- sub-folder `index.md` files carry no frontmatter, and every bundle-relative `.md`
75
- link resolves.
76
-
77
- When developing in this repo you can run the source directly with
78
- `npx tsx src/cli.ts <command>`, or via the npm script `npm run okforge -- <command>`.
79
-
80
- ## The mapping: `.okforge.config.json`
81
-
82
- okforge ships with **no** repository-specific paths. Each repo declares its own
83
- folder-to-source mapping in `.okforge.config.json` at the project root:
84
-
85
- ```json
86
- {
87
- "folders": {
88
- "runtime_concepts": ["packages/foo/src/model/", "packages/foo/src/event/"],
89
- "config_formats": ["packages/foo/data/schemas/thing.schema.json"]
90
- }
91
- }
92
- ```
93
-
94
- Each key is an OKF concept folder; each value is the list of source path prefixes
95
- that folder is derived from. With no config present, `map`/`folders` are empty and
96
- `stale` is a no-op; `check` still works, since it lints the bundle's markdown
97
- alone. This is the only project-specific part — both the skill and the nudge read
98
- it from here.
99
-
100
- ## Layout
101
-
102
- ```
103
- .claude/skills/okf/
104
- ├── SKILL.md instructions Claude loads
105
- └── README.md this file
106
-
107
- src/ the okforge CLI (mechanics)
108
- ├── cli.ts Commander entry; wires the subcommands below
109
- ├── misc/
110
- │ └── okf_store.ts mapping load, stale detection, conformance lint
111
- └── commands/
112
- ├── map_command.ts print the folder-to-source mapping
113
- ├── folders_command.ts list the concept folders
114
- ├── sources_command.ts print a folder's source paths
115
- ├── stale_command.ts folders whose source changed since HEAD
116
- ├── check_command.ts conformance + dead-link lint
117
- └── nudge_command.ts the Stop-hook nudge
118
- ```
119
-
120
- ## Conventions
121
-
122
- - File and folder names are snake_case. No kebab-case, no spaces.
123
- - Every non-index `.md` is a concept document: YAML frontmatter with a non-empty
124
- `type`, then structural markdown (headings, lists, tables, code) using the
125
- conventional `# Schema` / `# Examples` / `# Citations` sections where they
126
- apply.
127
- - `index.md` is reserved and carries no frontmatter — except the root
128
- `okf/index.md`, which declares `okf_version: "0.1"` and `type: Bundle Index`.
129
- - Cross-link concepts with bundle-relative absolute paths
130
- (`/runtime_concepts/job_store.md`); cite real source files with repo-relative
131
- paths (`../../packages/...`).
132
- - Ground every claim in real source. Do not invent fields, routes, flags, or
133
- states; if uncertain, omit.
134
-
135
- ## Reusing this in another project
136
-
137
- The skill is self-contained. Copy `.claude/skills/okf/`, register `npx okforge
138
- nudge` as a `Stop` hook in `.claude/settings.json`, and write an
139
- `.okforge.config.json` describing the target project's folder-to-source mapping.
140
- The mapping is the only project-specific part — both the skill and the nudge read
141
- it from there.