okforge 1.0.1
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/.claude/skills/okf/README.md +141 -0
- package/.claude/skills/okf/SKILL.md +124 -0
- package/LICENSE +21 -0
- package/README.md +72 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +109 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/check_command.d.ts +15 -0
- package/dist/commands/check_command.d.ts.map +1 -0
- package/dist/commands/check_command.js +17 -0
- package/dist/commands/check_command.js.map +1 -0
- package/dist/commands/folders_command.d.ts +6 -0
- package/dist/commands/folders_command.d.ts.map +1 -0
- package/dist/commands/folders_command.js +9 -0
- package/dist/commands/folders_command.js.map +1 -0
- package/dist/commands/install_command.d.ts +25 -0
- package/dist/commands/install_command.d.ts.map +1 -0
- package/dist/commands/install_command.js +45 -0
- package/dist/commands/install_command.js.map +1 -0
- package/dist/commands/map_command.d.ts +6 -0
- package/dist/commands/map_command.d.ts.map +1 -0
- package/dist/commands/map_command.js +16 -0
- package/dist/commands/map_command.js.map +1 -0
- package/dist/commands/nudge_command.d.ts +32 -0
- package/dist/commands/nudge_command.d.ts.map +1 -0
- package/dist/commands/nudge_command.js +72 -0
- package/dist/commands/nudge_command.js.map +1 -0
- package/dist/commands/sources_command.d.ts +6 -0
- package/dist/commands/sources_command.d.ts.map +1 -0
- package/dist/commands/sources_command.js +13 -0
- package/dist/commands/sources_command.js.map +1 -0
- package/dist/commands/stale_command.d.ts +6 -0
- package/dist/commands/stale_command.d.ts.map +1 -0
- package/dist/commands/stale_command.js +9 -0
- package/dist/commands/stale_command.js.map +1 -0
- package/dist/misc/okf_store.d.ts +78 -0
- package/dist/misc/okf_store.d.ts.map +1 -0
- package/dist/misc/okf_store.js +261 -0
- package/dist/misc/okf_store.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,141 @@
|
|
|
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.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: okf
|
|
3
|
+
description: >-
|
|
4
|
+
Maintain the Open Knowledge Format (OKF) knowledge bundle under okf/. Use
|
|
5
|
+
whenever the user wants to update, refresh, or regenerate OKF docs, set up an
|
|
6
|
+
OKF bundle, check OKF conformance, or when source code that the bundle
|
|
7
|
+
documents (config schemas, web API routes, CLI commands, runtime subsystems,
|
|
8
|
+
example crews, ADRs) has changed and the docs need to catch up. Three modes:
|
|
9
|
+
scaffold (create the bundle), refresh (regenerate a folder's docs from current
|
|
10
|
+
source), and check (conformance and dead-link lint). Prefer this skill over
|
|
11
|
+
hand-editing okf/ so the format, the folder<->source mapping, and link
|
|
12
|
+
integrity stay consistent.
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Open Knowledge Format (OKF) maintenance
|
|
16
|
+
|
|
17
|
+
OKF is an open, human- and agent-friendly format for knowledge — the metadata
|
|
18
|
+
and curated insight that surrounds a system. This repo's bundle lives at `okf/`.
|
|
19
|
+
Spec: https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md
|
|
20
|
+
|
|
21
|
+
The bundle is **derived** from source. Each folder is generated from specific
|
|
22
|
+
files; when those files change, the folder's docs drift. This skill keeps three
|
|
23
|
+
things consistent so you can focus on accurate prose: the OKF format, the
|
|
24
|
+
folder<->source mapping, and link integrity.
|
|
25
|
+
|
|
26
|
+
A bundled CLI owns the mechanics; you own the prose. Run it with `npx` from the
|
|
27
|
+
project root (Node >= 20.12):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx okforge <command> # map | folders | sources <folder> | stale | check | nudge
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Pick the mode from what the user asked
|
|
34
|
+
|
|
35
|
+
- **"refresh okf", "update the OKF docs for X", "the API changed, update okf"**
|
|
36
|
+
→ Refresh mode (the common case).
|
|
37
|
+
- **"set up okf", "create an OKF bundle", bundle missing** → Scaffold mode.
|
|
38
|
+
- **"check okf", "is the bundle conformant", "any dead links"** → Check mode.
|
|
39
|
+
|
|
40
|
+
## The folder <-> source mapping
|
|
41
|
+
|
|
42
|
+
The mapping is **per-repository data**, declared in `.okforge.config.json` at the
|
|
43
|
+
project root. okforge ships with none of it. The file shape is:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"folders": {
|
|
48
|
+
"runtime_concepts": ["packages/foo/src/model/", "packages/foo/src/event/"],
|
|
49
|
+
"config_formats": ["packages/foo/data/schemas/thing.schema.json"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Each key is an OKF concept folder; each value is the list of source path prefixes
|
|
55
|
+
that folder is derived from. `npx okforge map` prints the resolved mapping;
|
|
56
|
+
`npx okforge sources <folder>` prints one folder's source paths;
|
|
57
|
+
`npx okforge folders` lists the declared folders. Never hardcode the mapping in
|
|
58
|
+
docs — read it from the config so there is one source of truth (the hook reads it
|
|
59
|
+
too). When the codebase moves, edit `.okforge.config.json`.
|
|
60
|
+
|
|
61
|
+
## Refresh mode
|
|
62
|
+
|
|
63
|
+
1. Determine which folder(s) to refresh. If the user named one, use it. If they
|
|
64
|
+
said "whatever changed", run `npx okforge stale` — it lists folders whose
|
|
65
|
+
source changed since HEAD without the folder being edited.
|
|
66
|
+
2. For each folder, run `npx okforge sources <folder>` and **READ those real
|
|
67
|
+
files**.
|
|
68
|
+
3. Regenerate the affected concept docs from what you read, following the OKF
|
|
69
|
+
authoring rules below. Only rewrite docs whose underlying source actually
|
|
70
|
+
changed; leave correct docs untouched. Preserve existing filenames unless the
|
|
71
|
+
source they document was renamed or removed.
|
|
72
|
+
4. If a concept's source was deleted, delete its doc and remove it from the
|
|
73
|
+
folder `index.md`. If a new source appeared, add a new concept doc and link
|
|
74
|
+
it from `index.md`.
|
|
75
|
+
5. Update the folder `index.md`, and if folders were added/removed update the
|
|
76
|
+
root `okf/index.md`, the `.okforge.config.json` mapping, and prepend a dated
|
|
77
|
+
entry to `okf/log.md`.
|
|
78
|
+
6. Run `npx okforge check` and resolve every problem it reports before finishing.
|
|
79
|
+
|
|
80
|
+
## Scaffold mode
|
|
81
|
+
|
|
82
|
+
Create `.okforge.config.json` at the project root with the folder<->source
|
|
83
|
+
mapping for this repository (ask the user or infer it from the layout). Then
|
|
84
|
+
create `okf/index.md` (the only file with frontmatter: `okf_version: "0.1"` and
|
|
85
|
+
`type: Bundle Index`) and `okf/log.md`. Create the folders from the mapping and
|
|
86
|
+
refresh each. Finish with `npx okforge check`.
|
|
87
|
+
|
|
88
|
+
## Check mode
|
|
89
|
+
|
|
90
|
+
Run `npx okforge check`. It verifies snake_case names, that every non-index `.md`
|
|
91
|
+
has a non-empty frontmatter `type`, that sub-folder `index.md` files carry no
|
|
92
|
+
frontmatter, and that bundle-relative `.md` links resolve. Report results; fix
|
|
93
|
+
problems if the user asked you to.
|
|
94
|
+
|
|
95
|
+
## OKF authoring rules (follow exactly when writing docs)
|
|
96
|
+
|
|
97
|
+
- **File names**: snake_case only. No kebab-case, no spaces.
|
|
98
|
+
- **Concept docs**: every non-index `.md` starts with a YAML frontmatter block
|
|
99
|
+
delimited by `---` lines. Required: `type` (non-empty, e.g. `Config Format`,
|
|
100
|
+
`Concept`, `Data Model`, `API Endpoint`, `CLI Command`, `Crew`, `Package`).
|
|
101
|
+
Recommended: `title`, `description` (one sentence), `resource` (path/URI of
|
|
102
|
+
the real underlying asset), `tags`, `timestamp` (ISO 8601 — set to today's
|
|
103
|
+
date on refresh).
|
|
104
|
+
- **Body**: favor structural markdown — headings, lists, tables, fenced code —
|
|
105
|
+
over prose. Use conventional sections where they apply: `# Schema` (field/
|
|
106
|
+
shape), `# Examples` (code), `# Citations` (sources).
|
|
107
|
+
- **Index files** (reserved): `index.md` has NO frontmatter (except the root
|
|
108
|
+
bundle index). It lists the folder's concepts as bulleted markdown links with
|
|
109
|
+
one-line descriptions, optionally grouped under `#` headings.
|
|
110
|
+
- **Links**: cross-link concepts with bundle-relative absolute paths, e.g.
|
|
111
|
+
`[job store](/runtime_concepts/job_store.md)`. Link to real repo source files
|
|
112
|
+
(in `# Citations`) with repo-relative paths from the doc, e.g.
|
|
113
|
+
`../../packages/foo/src/model/job_store.ts`.
|
|
114
|
+
- **Grounding (critical)**: every claim must come from the real source you read.
|
|
115
|
+
Do not invent field names, routes, flags, states, or behavior. If uncertain,
|
|
116
|
+
omit. Quote real schema fields and real route paths.
|
|
117
|
+
- **Style**: plain English, no slang, no abbreviations.
|
|
118
|
+
|
|
119
|
+
## Companion hook
|
|
120
|
+
|
|
121
|
+
A `Stop` hook (`npx okforge nudge`, registered in `.claude/settings.json`) nudges
|
|
122
|
+
at session end when mapped source changed but `okf/` was not updated. It reads the
|
|
123
|
+
same `.okforge.config.json` mapping via `npx okforge stale`, so editing the
|
|
124
|
+
mapping updates both the skill and the nudge.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jerome Etienne
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# okforge
|
|
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.
|
|
6
|
+
|
|
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?".
|
|
11
|
+
|
|
12
|
+
## Usable in any repository
|
|
13
|
+
|
|
14
|
+
okforge ships with **no** repository-specific paths. Each repo declares its own
|
|
15
|
+
folder-to-source mapping in `.okforge.config.json` at the project root:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"folders": {
|
|
20
|
+
"runtime_concepts": ["packages/foo/src/model/", "packages/foo/src/event/"],
|
|
21
|
+
"config_formats": ["packages/foo/data/schemas/thing.schema.json"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
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.
|
|
28
|
+
|
|
29
|
+
## The `okforge` CLI
|
|
30
|
+
|
|
31
|
+
Run with `npx` (Node >= 20.12). `<dir>` defaults to the current directory.
|
|
32
|
+
|
|
33
|
+
| Command | Purpose |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `okforge map [<dir>]` | Print the full folder-to-source mapping. |
|
|
36
|
+
| `okforge folders [<dir>]` | List the OKF concept folders. |
|
|
37
|
+
| `okforge sources <folder> [<dir>]` | Print the source paths a folder is derived from. |
|
|
38
|
+
| `okforge stale [<dir>]` | List folders whose source changed since HEAD while the folder was not edited. |
|
|
39
|
+
| `okforge check [<dir>]` | Conformance + dead-link lint; exits non-zero on problems. |
|
|
40
|
+
| `okforge nudge` | Stop-hook entry: read the hook payload on stdin and maybe remind. |
|
|
41
|
+
|
|
42
|
+
## Development
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install
|
|
46
|
+
npm run okforge -- <command> # run the CLI from source via tsx
|
|
47
|
+
npm run typecheck # tsc --noEmit
|
|
48
|
+
npm run build # compile to dist/
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Layout
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
src/
|
|
55
|
+
├── cli.ts Commander entry; wires the subcommands below
|
|
56
|
+
├── misc/
|
|
57
|
+
│ └── okf_store.ts mapping load, stale detection, conformance lint
|
|
58
|
+
└── commands/
|
|
59
|
+
├── map_command.ts print the folder-to-source mapping
|
|
60
|
+
├── folders_command.ts list the concept folders
|
|
61
|
+
├── sources_command.ts print a folder's source paths
|
|
62
|
+
├── stale_command.ts folders whose source changed since HEAD
|
|
63
|
+
├── 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
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT © Jerome Etienne
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import Fs from 'node:fs';
|
|
3
|
+
import Path from 'node:path';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import Chalk from 'chalk';
|
|
6
|
+
import { MapCommand } from './commands/map_command.js';
|
|
7
|
+
import { FoldersCommand } from './commands/folders_command.js';
|
|
8
|
+
import { SourcesCommand } from './commands/sources_command.js';
|
|
9
|
+
import { StaleCommand } from './commands/stale_command.js';
|
|
10
|
+
import { CheckCommand } from './commands/check_command.js';
|
|
11
|
+
import { NudgeCommand } from './commands/nudge_command.js';
|
|
12
|
+
import { InstallCommand } from './commands/install_command.js';
|
|
13
|
+
/** Wire up the subcommands and run them. */
|
|
14
|
+
async function main() {
|
|
15
|
+
const packageJsonPath = Path.join(import.meta.dirname, '..', 'package.json');
|
|
16
|
+
const { version } = JSON.parse(Fs.readFileSync(packageJsonPath, 'utf8'));
|
|
17
|
+
const program = new Command();
|
|
18
|
+
program
|
|
19
|
+
.name('okforge')
|
|
20
|
+
.description('Deterministic mechanics and Stop-hook nudge for the okf (Open Knowledge Format) skill')
|
|
21
|
+
.version(version, '-V, --version', 'output the version number');
|
|
22
|
+
program
|
|
23
|
+
.command('folders')
|
|
24
|
+
.description('List the OKF concept folders declared in .okforge.config.json')
|
|
25
|
+
.argument('[dir]', 'Repository root', '.')
|
|
26
|
+
.action((dir) => {
|
|
27
|
+
for (const folder of FoldersCommand.folders(Path.resolve(dir))) {
|
|
28
|
+
console.log(folder);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
program
|
|
32
|
+
.command('map')
|
|
33
|
+
.description('Print the full folder -> sources mapping')
|
|
34
|
+
.argument('[dir]', 'Repository root', '.')
|
|
35
|
+
.action((dir) => {
|
|
36
|
+
for (const line of MapCommand.map(Path.resolve(dir))) {
|
|
37
|
+
console.log(line);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
program
|
|
41
|
+
.command('sources')
|
|
42
|
+
.description('Print the source paths a folder is derived from')
|
|
43
|
+
.argument('<folder>', 'OKF concept folder')
|
|
44
|
+
.argument('[dir]', 'Repository root', '.')
|
|
45
|
+
.action((folder, dir) => {
|
|
46
|
+
for (const source of SourcesCommand.sources(Path.resolve(dir), folder)) {
|
|
47
|
+
console.log(source);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
program
|
|
51
|
+
.command('stale')
|
|
52
|
+
.description('List folders whose source changed since HEAD while the folder was not edited')
|
|
53
|
+
.argument('[dir]', 'Repository root', '.')
|
|
54
|
+
.action((dir) => {
|
|
55
|
+
for (const line of StaleCommand.stale(Path.resolve(dir))) {
|
|
56
|
+
console.log(line);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
program
|
|
60
|
+
.command('check')
|
|
61
|
+
.description('Conformance + dead-link lint; exits non-zero on problems')
|
|
62
|
+
.argument('[dir]', 'Repository root', '.')
|
|
63
|
+
.action((dir) => {
|
|
64
|
+
const cwd = Path.resolve(dir);
|
|
65
|
+
let result;
|
|
66
|
+
try {
|
|
67
|
+
result = CheckCommand.check(cwd);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
console.error(message);
|
|
72
|
+
process.exit(2);
|
|
73
|
+
}
|
|
74
|
+
for (const problem of result.problems) {
|
|
75
|
+
console.log(problem);
|
|
76
|
+
}
|
|
77
|
+
if (result.problems.length === 0) {
|
|
78
|
+
console.log(result.summary);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
console.error(result.summary);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
84
|
+
program
|
|
85
|
+
.command('nudge')
|
|
86
|
+
.description('Stop-hook companion: read the hook payload on stdin and maybe remind')
|
|
87
|
+
.action(async () => {
|
|
88
|
+
await NudgeCommand.nudge();
|
|
89
|
+
});
|
|
90
|
+
program
|
|
91
|
+
.command('install')
|
|
92
|
+
.description('Copy the bundled okf skill into an AI agent folder (e.g. .claude)')
|
|
93
|
+
.argument('[agent_folder]', 'Destination agent folder', '.')
|
|
94
|
+
.action((agentFolder) => {
|
|
95
|
+
const result = InstallCommand.install(agentFolder);
|
|
96
|
+
for (const file of result.files) {
|
|
97
|
+
console.log(`${Chalk.green(file.action)} ${file.destination}`);
|
|
98
|
+
}
|
|
99
|
+
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
|
+
});
|
|
102
|
+
await program.parseAsync(process.argv);
|
|
103
|
+
}
|
|
104
|
+
main().catch((error) => {
|
|
105
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
106
|
+
console.error(Chalk.red(`error: ${message}`));
|
|
107
|
+
process.exit(1);
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Outcome of a conformance run: the problems found and a summary line. */
|
|
2
|
+
export type CheckResult = {
|
|
3
|
+
problems: string[];
|
|
4
|
+
summary: string;
|
|
5
|
+
};
|
|
6
|
+
/** `check` — conformance + dead-link lint of the `okf/` bundle. */
|
|
7
|
+
export declare class CheckCommand {
|
|
8
|
+
/**
|
|
9
|
+
* Lint the bundle under `cwd`. Returns the problems and a summary line:
|
|
10
|
+
* `OK: N concept docs conformant` when clean, `FAILED: N problem(s)` otherwise.
|
|
11
|
+
* Throws when there is no `okf/` bundle.
|
|
12
|
+
*/
|
|
13
|
+
static check(cwd: string): CheckResult;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=check_command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check_command.d.ts","sourceRoot":"","sources":["../../src/commands/check_command.ts"],"names":[],"mappings":"AAEA,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,mEAAmE;AACnE,qBAAa,YAAY;IACxB;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW;CAOtC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { OkfStore } from '../misc/okf_store.js';
|
|
2
|
+
/** `check` — conformance + dead-link lint of the `okf/` bundle. */
|
|
3
|
+
export class CheckCommand {
|
|
4
|
+
/**
|
|
5
|
+
* Lint the bundle under `cwd`. Returns the problems and a summary line:
|
|
6
|
+
* `OK: N concept docs conformant` when clean, `FAILED: N problem(s)` otherwise.
|
|
7
|
+
* Throws when there is no `okf/` bundle.
|
|
8
|
+
*/
|
|
9
|
+
static check(cwd) {
|
|
10
|
+
const problems = OkfStore.check(cwd);
|
|
11
|
+
if (problems.length === 0) {
|
|
12
|
+
return { problems, summary: `OK: ${OkfStore.conceptCount(cwd)} concept docs conformant` };
|
|
13
|
+
}
|
|
14
|
+
return { problems, summary: `FAILED: ${problems.length} problem(s)` };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=check_command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check_command.js","sourceRoot":"","sources":["../../src/commands/check_command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQhD,mEAAmE;AACnE,MAAM,OAAO,YAAY;IACxB;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,GAAW;QACvB,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,0BAA0B,EAAE,CAAC;QAC3F,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,QAAQ,CAAC,MAAM,aAAa,EAAE,CAAC;IACvE,CAAC;CACD"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** `folders` — list the OKF concept folders, one per line. */
|
|
2
|
+
export declare class FoldersCommand {
|
|
3
|
+
/** The OKF concept folder names declared in `cwd`'s config, in declared order. */
|
|
4
|
+
static folders(cwd: string): string[];
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=folders_command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders_command.d.ts","sourceRoot":"","sources":["../../src/commands/folders_command.ts"],"names":[],"mappings":"AAEA,8DAA8D;AAC9D,qBAAa,cAAc;IAC1B,kFAAkF;IAClF,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE;CAGrC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { OkfStore } from '../misc/okf_store.js';
|
|
2
|
+
/** `folders` — list the OKF concept folders, one per line. */
|
|
3
|
+
export class FoldersCommand {
|
|
4
|
+
/** The OKF concept folder names declared in `cwd`'s config, in declared order. */
|
|
5
|
+
static folders(cwd) {
|
|
6
|
+
return OkfStore.folders(cwd);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=folders_command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders_command.js","sourceRoot":"","sources":["../../src/commands/folders_command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,8DAA8D;AAC9D,MAAM,OAAO,cAAc;IAC1B,kFAAkF;IAClF,MAAM,CAAC,OAAO,CAAC,GAAW;QACzB,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;CACD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Outcome of copying a single skill file into the destination folder. */
|
|
2
|
+
export type InstalledFile = {
|
|
3
|
+
name: string;
|
|
4
|
+
action: 'created' | 'updated';
|
|
5
|
+
destination: string;
|
|
6
|
+
};
|
|
7
|
+
/** Summary returned by {@link InstallCommand.install}. */
|
|
8
|
+
export type InstallResult = {
|
|
9
|
+
destinationDir: string;
|
|
10
|
+
files: InstalledFile[];
|
|
11
|
+
};
|
|
12
|
+
/** `install` — copy the bundled okf skill into an AI agent folder (e.g. `.claude`). */
|
|
13
|
+
export declare class InstallCommand {
|
|
14
|
+
/**
|
|
15
|
+
* 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.
|
|
18
|
+
* @param agentFolder Destination agent folder; the caller defaults it to `.`.
|
|
19
|
+
* @returns The destination directory and the per-file outcome.
|
|
20
|
+
*/
|
|
21
|
+
static install(agentFolder: string): InstallResult;
|
|
22
|
+
/** Paths of every file under `dir`, relative to `dir`, recursively. */
|
|
23
|
+
static listFiles(dir: string): string[];
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=install_command.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Fs from 'node:fs';
|
|
2
|
+
import Path from 'node:path';
|
|
3
|
+
/** `install` — copy the bundled okf skill into an AI agent folder (e.g. `.claude`). */
|
|
4
|
+
export class InstallCommand {
|
|
5
|
+
/**
|
|
6
|
+
* 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
|
+
* @param agentFolder Destination agent folder; the caller defaults it to `.`.
|
|
10
|
+
* @returns The destination directory and the per-file outcome.
|
|
11
|
+
*/
|
|
12
|
+
static install(agentFolder) {
|
|
13
|
+
const skillsDir = Path.join(import.meta.dirname, '..', '..', '.claude', 'skills');
|
|
14
|
+
if (Fs.existsSync(skillsDir) === false) {
|
|
15
|
+
throw new Error(`bundled skill files not found at ${skillsDir}`);
|
|
16
|
+
}
|
|
17
|
+
const destinationDir = Path.resolve(agentFolder);
|
|
18
|
+
const files = [];
|
|
19
|
+
for (const relative of InstallCommand.listFiles(skillsDir)) {
|
|
20
|
+
const name = Path.join('skills', relative);
|
|
21
|
+
const destination = Path.join(destinationDir, name);
|
|
22
|
+
const exists = Fs.existsSync(destination);
|
|
23
|
+
Fs.mkdirSync(Path.dirname(destination), { recursive: true });
|
|
24
|
+
Fs.copyFileSync(Path.join(skillsDir, relative), destination);
|
|
25
|
+
files.push({ name, action: exists === true ? 'updated' : 'created', destination });
|
|
26
|
+
}
|
|
27
|
+
return { destinationDir, files };
|
|
28
|
+
}
|
|
29
|
+
/** Paths of every file under `dir`, relative to `dir`, recursively. */
|
|
30
|
+
static listFiles(dir) {
|
|
31
|
+
const result = [];
|
|
32
|
+
for (const entry of Fs.readdirSync(dir, { withFileTypes: true })) {
|
|
33
|
+
if (entry.isDirectory() === true) {
|
|
34
|
+
for (const nested of InstallCommand.listFiles(Path.join(dir, entry.name))) {
|
|
35
|
+
result.push(Path.join(entry.name, nested));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
result.push(entry.name);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=install_command.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map_command.d.ts","sourceRoot":"","sources":["../../src/commands/map_command.ts"],"names":[],"mappings":"AAEA,wDAAwD;AACxD,qBAAa,UAAU;IACtB,sFAAsF;IACtF,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE;CAUjC"}
|