okforge 1.0.1 → 1.0.2
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 +105 -17
- package/dist/cli.js +9 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/install_command.d.ts +23 -3
- package/dist/commands/install_command.d.ts.map +1 -1
- package/dist/commands/install_command.js +65 -5
- package/dist/commands/install_command.js.map +1 -1
- package/package.json +2 -2
- package/.claude/skills/okf/README.md +0 -141
- /package/{.claude → dotclaude_folder}/skills/okf/SKILL.md +0 -0
package/README.md
CHANGED
|
@@ -1,15 +1,60 @@
|
|
|
1
|
-
# okforge
|
|
1
|
+
# okforge — Open Knowledge Format bundle skill
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
+
## Why a skill
|
|
20
|
+
|
|
21
|
+
The `okf/` bundle is **derived** from source — each folder is generated from
|
|
22
|
+
specific files, so when those files change the docs drift. The hard parts to keep
|
|
23
|
+
consistent are the OKF format, the folder-to-source mapping, and link integrity.
|
|
24
|
+
The skill keeps those uniform so you can focus on accurate prose.
|
|
25
|
+
|
|
26
|
+
## What it does
|
|
27
|
+
|
|
28
|
+
Three modes, chosen from how you ask:
|
|
29
|
+
|
|
30
|
+
| You say | Mode | What happens |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| "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. |
|
|
33
|
+
| "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. |
|
|
34
|
+
| "check okf", "is the bundle conformant", "any dead links" | **check** | Runs the conformance and dead-link lint. |
|
|
35
|
+
|
|
36
|
+
Invoke it by asking Claude in plain language, or with `/okf`. Regeneration is
|
|
37
|
+
model-driven, so refresh is a **draft-then-review** loop: the skill rewrites the
|
|
38
|
+
affected docs, you review them, then commit. It will not silently rewrite docs
|
|
39
|
+
whose source did not change.
|
|
40
|
+
|
|
41
|
+
## Automatic capture going forward
|
|
42
|
+
|
|
43
|
+
A companion `Stop` hook, `npx okforge nudge` (registered in
|
|
44
|
+
`.claude/settings.json`), reminds you when a session changed source that an OKF
|
|
45
|
+
folder documents but left `okf/` untouched. It is deliberately gentle:
|
|
46
|
+
non-blocking, at most once per session, and silent if you already touched `okf/`
|
|
47
|
+
that session. It reads the same mapping the skill uses (via `npx okforge stale`),
|
|
48
|
+
so the skill and the nudge never diverge.
|
|
49
|
+
|
|
50
|
+
## Where the bundle lives
|
|
51
|
+
|
|
52
|
+
The bundle is the `okf/` directory at the repository root — a valid OKF bundle is
|
|
53
|
+
just a subdirectory of a larger repo, so there is no build step or manifest beyond
|
|
54
|
+
`okf_version` in the root `okf/index.md`. The folder-to-source mapping lives next
|
|
55
|
+
to it in `.okforge.config.json` at the project root.
|
|
56
|
+
|
|
57
|
+
## Usable in any repository: `.okforge.config.json`
|
|
13
58
|
|
|
14
59
|
okforge ships with **no** repository-specific paths. Each repo declares its own
|
|
15
60
|
folder-to-source mapping in `.okforge.config.json` at the project root:
|
|
@@ -23,12 +68,17 @@ folder-to-source mapping in `.okforge.config.json` at the project root:
|
|
|
23
68
|
}
|
|
24
69
|
```
|
|
25
70
|
|
|
26
|
-
|
|
27
|
-
|
|
71
|
+
Each key is an OKF concept folder; each value is the list of source path prefixes
|
|
72
|
+
that folder is derived from. With no config present, `map`/`folders` are empty and
|
|
73
|
+
`stale` is a no-op; `check` still works, since it lints the bundle's markdown
|
|
74
|
+
alone. This is the only project-specific part — both the skill and the nudge read
|
|
75
|
+
it from here.
|
|
28
76
|
|
|
29
77
|
## The `okforge` CLI
|
|
30
78
|
|
|
31
|
-
|
|
79
|
+
The bundled [`okforge`](https://www.npmjs.com/package/okforge) CLI owns the
|
|
80
|
+
deterministic mechanics, run with `npx` (Node >= 20.12). `<dir>` defaults to the
|
|
81
|
+
current directory (the repository root).
|
|
32
82
|
|
|
33
83
|
| Command | Purpose |
|
|
34
84
|
|---|---|
|
|
@@ -38,6 +88,11 @@ Run with `npx` (Node >= 20.12). `<dir>` defaults to the current directory.
|
|
|
38
88
|
| `okforge stale [<dir>]` | List folders whose source changed since HEAD while the folder was not edited. |
|
|
39
89
|
| `okforge check [<dir>]` | Conformance + dead-link lint; exits non-zero on problems. |
|
|
40
90
|
| `okforge nudge` | Stop-hook entry: read the hook payload on stdin and maybe remind. |
|
|
91
|
+
| `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`. |
|
|
92
|
+
|
|
93
|
+
`check` verifies: snake_case names only, every non-index `.md` has a non-empty
|
|
94
|
+
frontmatter `type`, sub-folder `index.md` files carry no frontmatter, and every
|
|
95
|
+
bundle-relative `.md` link resolves.
|
|
41
96
|
|
|
42
97
|
## Development
|
|
43
98
|
|
|
@@ -48,10 +103,12 @@ npm run typecheck # tsc --noEmit
|
|
|
48
103
|
npm run build # compile to dist/
|
|
49
104
|
```
|
|
50
105
|
|
|
106
|
+
You can also run the source directly with `npx tsx src/cli.ts <command>`.
|
|
107
|
+
|
|
51
108
|
## Layout
|
|
52
109
|
|
|
53
110
|
```
|
|
54
|
-
src/
|
|
111
|
+
src/ the okforge CLI (mechanics)
|
|
55
112
|
├── cli.ts Commander entry; wires the subcommands below
|
|
56
113
|
├── misc/
|
|
57
114
|
│ └── okf_store.ts mapping load, stale detection, conformance lint
|
|
@@ -61,12 +118,43 @@ src/
|
|
|
61
118
|
├── sources_command.ts print a folder's source paths
|
|
62
119
|
├── stale_command.ts folders whose source changed since HEAD
|
|
63
120
|
├── check_command.ts conformance + dead-link lint
|
|
64
|
-
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
└──
|
|
121
|
+
├── nudge_command.ts the Stop-hook nudge
|
|
122
|
+
└── install_command.ts copy the skill into a target agent folder
|
|
123
|
+
dotclaude_folder/ data shipped to a target's .claude/ by `okforge install`
|
|
124
|
+
└── skills/okf/
|
|
125
|
+
└── SKILL.md instructions Claude loads
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Conventions
|
|
129
|
+
|
|
130
|
+
- File and folder names are snake_case. No kebab-case, no spaces.
|
|
131
|
+
- Every non-index `.md` is a concept document: YAML frontmatter with a non-empty
|
|
132
|
+
`type`, then structural markdown (headings, lists, tables, code) using the
|
|
133
|
+
conventional `# Schema` / `# Examples` / `# Citations` sections where they apply.
|
|
134
|
+
- `index.md` is reserved and carries no frontmatter — except the root
|
|
135
|
+
`okf/index.md`, which declares `okf_version: "0.1"` and `type: Bundle Index`.
|
|
136
|
+
- Cross-link concepts with bundle-relative absolute paths
|
|
137
|
+
(`/runtime_concepts/job_store.md`); cite real source files with repo-relative
|
|
138
|
+
paths (`../../packages/...`).
|
|
139
|
+
- Ground every claim in real source. Do not invent fields, routes, flags, or
|
|
140
|
+
states; if uncertain, omit.
|
|
141
|
+
|
|
142
|
+
## Reusing this in another project
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx okforge install .claude
|
|
68
146
|
```
|
|
69
147
|
|
|
148
|
+
When the destination folder is named `.claude`, `install` both drops the skill
|
|
149
|
+
prose into `.claude/skills/okf/` **and** registers the `npx okforge nudge` Stop
|
|
150
|
+
hook in `.claude/settings.json` (idempotent and non-destructive — existing
|
|
151
|
+
settings and hooks are preserved). For any other destination it copies the skill
|
|
152
|
+
only and leaves `settings.json` untouched.
|
|
153
|
+
|
|
154
|
+
Then write an `.okforge.config.json` at the project root describing that repo's
|
|
155
|
+
folder-to-source mapping. The mapping is the only project-specific part — both the
|
|
156
|
+
skill and the nudge read it from there.
|
|
157
|
+
|
|
70
158
|
## License
|
|
71
159
|
|
|
72
160
|
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
|
|
17
|
-
*
|
|
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
|
|
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":"
|
|
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
|
|
8
|
-
*
|
|
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
|
|
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, '..', '..', '
|
|
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
|
-
|
|
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;
|
|
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.
|
|
3
|
+
"version": "1.0.2",
|
|
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
|
-
"
|
|
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.
|
|
File without changes
|