@okf-harness/agent-pack 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +455 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# @okf-harness/agent-pack
|
|
2
|
+
|
|
3
|
+
Adapter renderer package for OKF Harness agent guidance. It renders shared Claude Code and Codex instructions from one source so workspace setup stays consistent across supported agents.
|
|
4
|
+
|
|
5
|
+
OKF Harness is an independent open-source project built on [Andrej Karpathy's LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) pattern and Google's [Open Knowledge Format](https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing) / [OKF specification](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md).
|
|
6
|
+
|
|
7
|
+
Most users should install the CLI instead:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @okf-harness/cli
|
|
11
|
+
okfh doctor --json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
For project overview, workflows, and security notes, see the [main repository README](https://github.com/pumblus/okf-harness#readme).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
declare const packageInfo: {
|
|
2
|
+
readonly name: "@okf-harness/agent-pack";
|
|
3
|
+
readonly role: "agent-pack";
|
|
4
|
+
};
|
|
5
|
+
type PackageInfo = typeof packageInfo;
|
|
6
|
+
type AgentAdapter = "claude" | "codex";
|
|
7
|
+
type AgentInstallTarget = AgentAdapter | "all";
|
|
8
|
+
type RenderedAgentFile = {
|
|
9
|
+
path: string;
|
|
10
|
+
contents: string;
|
|
11
|
+
};
|
|
12
|
+
type RenderAgentAdapterOptions = {
|
|
13
|
+
adapter: AgentAdapter;
|
|
14
|
+
version?: string;
|
|
15
|
+
};
|
|
16
|
+
type RenderedAgentAdapter = {
|
|
17
|
+
adapter: AgentAdapter;
|
|
18
|
+
files: RenderedAgentFile[];
|
|
19
|
+
};
|
|
20
|
+
type ManagedBlockAction = "created" | "inserted" | "replaced" | "unchanged";
|
|
21
|
+
type ManagedBlockResult = {
|
|
22
|
+
path: string;
|
|
23
|
+
action: ManagedBlockAction;
|
|
24
|
+
};
|
|
25
|
+
type AgentInstallConflict = {
|
|
26
|
+
path: string;
|
|
27
|
+
reason: string;
|
|
28
|
+
};
|
|
29
|
+
type InstallAgentAdaptersOptions = {
|
|
30
|
+
workspaceRoot: string;
|
|
31
|
+
adapter: AgentInstallTarget;
|
|
32
|
+
dryRun?: boolean;
|
|
33
|
+
force?: boolean;
|
|
34
|
+
};
|
|
35
|
+
type InstallAgentAdaptersResult = {
|
|
36
|
+
adapter: AgentInstallTarget;
|
|
37
|
+
dryRun: boolean;
|
|
38
|
+
writtenFiles: string[];
|
|
39
|
+
plannedFiles: string[];
|
|
40
|
+
replacedFiles: string[];
|
|
41
|
+
skippedFiles: string[];
|
|
42
|
+
conflicts: AgentInstallConflict[];
|
|
43
|
+
managedBlocks: ManagedBlockResult[];
|
|
44
|
+
};
|
|
45
|
+
declare function renderAgentAdapter(options: RenderAgentAdapterOptions): RenderedAgentAdapter;
|
|
46
|
+
declare function installAgentAdapters(options: InstallAgentAdaptersOptions): Promise<InstallAgentAdaptersResult>;
|
|
47
|
+
|
|
48
|
+
export { type AgentAdapter, type AgentInstallConflict, type AgentInstallTarget, type InstallAgentAdaptersOptions, type InstallAgentAdaptersResult, type ManagedBlockAction, type ManagedBlockResult, type PackageInfo, type RenderAgentAdapterOptions, type RenderedAgentAdapter, type RenderedAgentFile, installAgentAdapters, packageInfo, renderAgentAdapter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var packageInfo = {
|
|
5
|
+
name: "@okf-harness/agent-pack",
|
|
6
|
+
role: "agent-pack"
|
|
7
|
+
};
|
|
8
|
+
var defaultVersion = "0.1";
|
|
9
|
+
var managedBlockStart = "<!-- OKF Harness: start -->";
|
|
10
|
+
var managedBlockEnd = "<!-- OKF Harness: end -->";
|
|
11
|
+
var skillTemplates = [
|
|
12
|
+
{
|
|
13
|
+
name: "okf-harness-init",
|
|
14
|
+
title: "OKF Harness Init",
|
|
15
|
+
description: "Initialize and organize an OKF Harness workspace on macOS, including folders, git, OKF bundle files, and Claude/Codex adapters. Use when the user asks to set up, create, initialize, organize, or install OKF Harness support. Do not use for ingesting an already-added source.",
|
|
16
|
+
summary: "Use this skill to create a workspace or repair Claude/Codex adapter support.",
|
|
17
|
+
requiredBehavior: [
|
|
18
|
+
"Locate or choose the workspace path with the user.",
|
|
19
|
+
"Use the local shell to run `okfh init <workspace> --name <name> --agents all --json` for first-time setup.",
|
|
20
|
+
"Use `okfh agent install all --workspace <workspace> --json` to repair adapter files in an existing workspace.",
|
|
21
|
+
"If the CLI or local shell is unavailable, run `okfh doctor --json` when possible; otherwise stop and tell the user to install OKF Harness instead of hand-writing the workspace structure.",
|
|
22
|
+
"After initialization, run `okfh status --workspace <workspace> --json` and report the workspace path, lint status, warnings, and next step."
|
|
23
|
+
],
|
|
24
|
+
hardRules: [
|
|
25
|
+
"Do not create a parallel workspace skeleton by hand.",
|
|
26
|
+
"Do not overwrite a non-empty directory unless `okfh` returns an explicit safe plan.",
|
|
27
|
+
"Do not add plugin, hook, Pi, OpenCode, or Obsidian setup.",
|
|
28
|
+
"Use the same local shell `okfh --json` workflow in Desktop App and TUI sessions.",
|
|
29
|
+
"Run `git diff` before final response when file changes were made."
|
|
30
|
+
],
|
|
31
|
+
reference: {
|
|
32
|
+
path: "workflow.md",
|
|
33
|
+
title: "Init Workflow",
|
|
34
|
+
body: `# Init Workflow
|
|
35
|
+
|
|
36
|
+
## First-time setup
|
|
37
|
+
|
|
38
|
+
Run:
|
|
39
|
+
|
|
40
|
+
\`\`\`bash
|
|
41
|
+
okfh init <workspace> --name <name> --agents all --json
|
|
42
|
+
\`\`\`
|
|
43
|
+
|
|
44
|
+
Use \`--agents claude\`, \`--agents codex\`, or \`--agents none\` only when the user explicitly asks for that narrower target.
|
|
45
|
+
|
|
46
|
+
## Repair adapter support
|
|
47
|
+
|
|
48
|
+
Run:
|
|
49
|
+
|
|
50
|
+
\`\`\`bash
|
|
51
|
+
okfh agent install all --workspace <workspace> --json
|
|
52
|
+
\`\`\`
|
|
53
|
+
|
|
54
|
+
If the command returns conflicts, explain the conflicting paths and ask before using \`--force\`.
|
|
55
|
+
`
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "okf-harness-ingest",
|
|
60
|
+
title: "OKF Harness Ingest",
|
|
61
|
+
description: "Add source material and compile it into an OKF-compatible LLM Wiki by creating reference pages, updating topic/entity/project pages, citations, index, and log. Use when the user asks to add, ingest, absorb, summarize into the wiki, or organize a new source. Do not use for general question answering without new sources.",
|
|
62
|
+
summary: "Use this skill to register source material and compile it into the local OKF wiki.",
|
|
63
|
+
requiredBehavior: [
|
|
64
|
+
"Locate the workspace by finding `okfh.config.yaml`.",
|
|
65
|
+
"Use the local shell to run `okfh --json` commands.",
|
|
66
|
+
"If the source is not registered, run `okfh source add <path-or-url> --workspace <workspace> --json`.",
|
|
67
|
+
"Run `okfh ingest plan <source-id-or-path> --workspace <workspace> --json` before editing wiki files.",
|
|
68
|
+
"Treat candidate concepts as metadata hints only; read the full source before semantic analysis.",
|
|
69
|
+
"After ingest work changes wiki files, run `okfh lint --workspace <workspace> --json`.",
|
|
70
|
+
"Show the user changed files, lint status, and unresolved questions."
|
|
71
|
+
],
|
|
72
|
+
hardRules: [
|
|
73
|
+
"Never edit `raw/sources/`.",
|
|
74
|
+
"Never invent source IDs, citations, dates, or claims.",
|
|
75
|
+
"Do not hand-roll a source manifest, search index, graph, or raw source management.",
|
|
76
|
+
"If more than 20 wiki files seem affected, stop after an ingest plan and ask the user to narrow scope.",
|
|
77
|
+
"Run `git diff` before final response when file changes were made."
|
|
78
|
+
],
|
|
79
|
+
reference: {
|
|
80
|
+
path: "ingest-contract.md",
|
|
81
|
+
title: "Ingest Contract",
|
|
82
|
+
body: `# Ingest Contract
|
|
83
|
+
|
|
84
|
+
## Supported now
|
|
85
|
+
|
|
86
|
+
Run:
|
|
87
|
+
|
|
88
|
+
\`\`\`bash
|
|
89
|
+
okfh source add <path-or-url> --workspace <workspace> --json
|
|
90
|
+
okfh ingest plan <source-id-or-path> --workspace <workspace> --json
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
The ingest plan is metadata-level guidance. It returns a recommended reference path, candidate concepts, and an Agent checklist; it does not read source bodies, summarize content, extract claims, or synthesize wiki pages.
|
|
94
|
+
|
|
95
|
+
## Wiki update contract
|
|
96
|
+
|
|
97
|
+
- Create or update one \`wiki/references/<slug>.md\` page per source.
|
|
98
|
+
- Update only affected \`wiki/topics/\`, \`wiki/entities/\`, \`wiki/projects/\`, \`wiki/decisions/\`, or \`wiki/questions/\` pages.
|
|
99
|
+
- Preserve uncertainty and contradictions.
|
|
100
|
+
- Add or update \`# Citations\` sections.
|
|
101
|
+
- Update \`wiki/index.md\` and relevant subdirectory indexes.
|
|
102
|
+
- Append \`wiki/log.md\`.
|
|
103
|
+
`
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "okf-harness-query",
|
|
108
|
+
title: "OKF Harness Query",
|
|
109
|
+
description: "Answer questions using the local OKF Harness wiki by searching concepts, reading full pages, following citations, and citing concept paths. Use when the user asks what their knowledge base says, asks a research question, or requests synthesis from existing wiki knowledge. Do not use to ingest new source material.",
|
|
110
|
+
summary: "Use this skill to answer from existing OKF wiki knowledge.",
|
|
111
|
+
requiredBehavior: [
|
|
112
|
+
"Locate the workspace by finding `okfh.config.yaml`.",
|
|
113
|
+
"Run `okfh status --json` and confirm `data.capabilities.search`, `read`, and `graph` are available while `queryCommand` is not available.",
|
|
114
|
+
"Run `okfh read index --json` first to inspect the wiki map.",
|
|
115
|
+
"Run `okfh search <question> --json` to get candidate concept cards.",
|
|
116
|
+
"Run `okfh read <concept-id-or-path> --json` for relevant candidates before synthesizing.",
|
|
117
|
+
"Follow useful reference citations with `okfh read <reference-concept-id-or-path> --json` when factual precision matters.",
|
|
118
|
+
"If a read is truncated, continue with `--section`, `--section-id`, `--offset/--limit`, or `--full` before relying on omitted content.",
|
|
119
|
+
"Answer directly first, then list supporting concept paths and available source IDs.",
|
|
120
|
+
"If hits are weak, citations are missing, or only wiki synthesis was read, state the evidence limit plainly."
|
|
121
|
+
],
|
|
122
|
+
hardRules: [
|
|
123
|
+
"Do not ingest new source material from this skill.",
|
|
124
|
+
"Do not invent citations or claim the wiki says something without reading it.",
|
|
125
|
+
"Do not run or hallucinate an `okfh query` command.",
|
|
126
|
+
"Do not build an ad hoc search index or search `raw/sources/` for normal query answers.",
|
|
127
|
+
"Do not edit `raw/sources/`."
|
|
128
|
+
],
|
|
129
|
+
reference: {
|
|
130
|
+
path: "answer-contract.md",
|
|
131
|
+
title: "Answer Contract",
|
|
132
|
+
body: `# Answer Contract
|
|
133
|
+
|
|
134
|
+
## Query workflow
|
|
135
|
+
|
|
136
|
+
Use the CLI as the deterministic retrieval layer:
|
|
137
|
+
|
|
138
|
+
\`\`\`bash
|
|
139
|
+
okfh status --json
|
|
140
|
+
okfh read index --json
|
|
141
|
+
okfh search "<question>" --json
|
|
142
|
+
okfh read <concept-id-or-path> --json
|
|
143
|
+
\`\`\`
|
|
144
|
+
|
|
145
|
+
There is no \`okfh query\` command in the current CLI. Compose answers from search candidate cards plus bounded reads.
|
|
146
|
+
|
|
147
|
+
## Answer shape
|
|
148
|
+
|
|
149
|
+
Answer with:
|
|
150
|
+
|
|
151
|
+
- Direct answer.
|
|
152
|
+
- Evidence from concept paths and available source IDs.
|
|
153
|
+
- A note when evidence came only from wiki synthesis rather than raw source bodies.
|
|
154
|
+
- Open questions or contradictions.
|
|
155
|
+
- Insufficient-evidence statement when search hits are weak or citations are missing.
|
|
156
|
+
- Suggested follow-up only when it naturally follows from the wiki evidence.
|
|
157
|
+
`
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: "okf-harness-maintain",
|
|
162
|
+
title: "OKF Harness Maintain",
|
|
163
|
+
description: "Maintain an OKF Harness wiki by running lint, repairing broken links or missing metadata, updating index/log files, checking source hashes, and generating graph reports. Use when the user asks to check, clean up, repair, validate, lint, or visualize the knowledge base. Do not use for first-time initialization.",
|
|
164
|
+
summary: "Use this skill to lint and repair an existing OKF Harness workspace.",
|
|
165
|
+
requiredBehavior: [
|
|
166
|
+
"Locate the workspace by finding `okfh.config.yaml`.",
|
|
167
|
+
"Run `okfh lint --json` before deciding what to change.",
|
|
168
|
+
"Use small patches for wiki repairs.",
|
|
169
|
+
"Run `okfh lint --json` again after wiki edits.",
|
|
170
|
+
"Run `okfh graph --json` only when the user asks for a graph, visualization, or graph report.",
|
|
171
|
+
"Report lint status, changed files, graph report paths when generated, and any remaining manual fixes.",
|
|
172
|
+
"If the user did not ask for a graph, mention graph generation only as an optional follow-up."
|
|
173
|
+
],
|
|
174
|
+
hardRules: [
|
|
175
|
+
"Never edit `raw/sources/`.",
|
|
176
|
+
"Do not silently rewrite large wiki sections.",
|
|
177
|
+
"Do not hand-roll graph reports or source hash checks.",
|
|
178
|
+
"Run `git diff` before final response when file changes were made."
|
|
179
|
+
],
|
|
180
|
+
reference: {
|
|
181
|
+
path: "lint-contract.md",
|
|
182
|
+
title: "Lint Contract",
|
|
183
|
+
body: `# Lint Contract
|
|
184
|
+
|
|
185
|
+
## Supported now
|
|
186
|
+
|
|
187
|
+
Run:
|
|
188
|
+
|
|
189
|
+
\`\`\`bash
|
|
190
|
+
okfh lint --json
|
|
191
|
+
\`\`\`
|
|
192
|
+
|
|
193
|
+
Fix only issues that can be resolved from current wiki context without inventing missing source facts.
|
|
194
|
+
|
|
195
|
+
## Graph reports
|
|
196
|
+
|
|
197
|
+
Source hash checks are supported by \`okfh lint\`. Graph generation is supported by:
|
|
198
|
+
|
|
199
|
+
\`\`\`bash
|
|
200
|
+
okfh graph --json
|
|
201
|
+
\`\`\`
|
|
202
|
+
|
|
203
|
+
Run graph only when the user asks to visualize or generate a graph report. Maintain workflows should not generate graph reports automatically.
|
|
204
|
+
`
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
];
|
|
208
|
+
function renderAgentAdapter(options) {
|
|
209
|
+
const version = options.version ?? defaultVersion;
|
|
210
|
+
return {
|
|
211
|
+
adapter: options.adapter,
|
|
212
|
+
files: [renderRootGuidance(options.adapter), ...renderSkillFiles(options.adapter, version)]
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
async function installAgentAdapters(options) {
|
|
216
|
+
const workspaceRoot = path.resolve(options.workspaceRoot);
|
|
217
|
+
const dryRun = options.dryRun === true;
|
|
218
|
+
const force = options.force === true;
|
|
219
|
+
const plan = createInstallResult(options.adapter, dryRun);
|
|
220
|
+
await planAgentAdapterWrites(workspaceRoot, options.adapter, {
|
|
221
|
+
dryRun: true,
|
|
222
|
+
force,
|
|
223
|
+
result: plan
|
|
224
|
+
});
|
|
225
|
+
if (dryRun || plan.conflicts.length > 0) {
|
|
226
|
+
return plan;
|
|
227
|
+
}
|
|
228
|
+
const result = createInstallResult(options.adapter, false);
|
|
229
|
+
await planAgentAdapterWrites(workspaceRoot, options.adapter, { dryRun: false, force, result });
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
function createInstallResult(adapter, dryRun) {
|
|
233
|
+
return {
|
|
234
|
+
adapter,
|
|
235
|
+
dryRun,
|
|
236
|
+
writtenFiles: [],
|
|
237
|
+
plannedFiles: [],
|
|
238
|
+
replacedFiles: [],
|
|
239
|
+
skippedFiles: [],
|
|
240
|
+
conflicts: [],
|
|
241
|
+
managedBlocks: []
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
async function planAgentAdapterWrites(workspaceRoot, adapterTarget, context) {
|
|
245
|
+
for (const adapter of adaptersForTarget(adapterTarget)) {
|
|
246
|
+
for (const file of renderAgentAdapter({ adapter }).files) {
|
|
247
|
+
if (isRootGuidancePath(file.path)) {
|
|
248
|
+
await planRootGuidanceWrite(workspaceRoot, file, context);
|
|
249
|
+
} else {
|
|
250
|
+
await planManagedFileWrite(workspaceRoot, file, context);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function renderRootGuidance(adapter) {
|
|
256
|
+
const rootPath = adapter === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
257
|
+
const routePrefix = adapter === "claude" ? "/" : "$";
|
|
258
|
+
const routeLabel = adapter === "claude" ? "Use the project skills for user-facing workflows:" : "Use repo skills for workflows:";
|
|
259
|
+
return {
|
|
260
|
+
path: rootPath,
|
|
261
|
+
contents: `# OKF Harness workspace
|
|
262
|
+
|
|
263
|
+
${managedBlockStart}
|
|
264
|
+
This repository is an OKF Harness workspace.
|
|
265
|
+
|
|
266
|
+
${routeLabel}
|
|
267
|
+
|
|
268
|
+
- \`${routePrefix}okf-harness-init\` for first-time setup and adapter repair.
|
|
269
|
+
- \`${routePrefix}okf-harness-ingest\` for adding or compiling sources.
|
|
270
|
+
- \`${routePrefix}okf-harness-query\` for answering from the wiki.
|
|
271
|
+
- \`${routePrefix}okf-harness-maintain\` for lint, repair, and graph reports.
|
|
272
|
+
|
|
273
|
+
Rules:
|
|
274
|
+
|
|
275
|
+
- \`raw/sources/\` is immutable. Never edit source files.
|
|
276
|
+
- \`wiki/\` is the OKF bundle and may be edited by the agent.
|
|
277
|
+
- Use \`okfh --json\` through the local shell for deterministic harness operations.
|
|
278
|
+
- Desktop App and TUI sessions use the same local shell command workflow.
|
|
279
|
+
- If \`okfh\` or shell access fails, run \`okfh doctor --json\` when possible and report the failed checks.
|
|
280
|
+
- Run \`okfh lint --workspace <workspace> --json\` after modifying wiki files.
|
|
281
|
+
- Run \`git diff\` before final response after any file changes.
|
|
282
|
+
${managedBlockEnd}
|
|
283
|
+
`
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function adaptersForTarget(target) {
|
|
287
|
+
return target === "all" ? ["claude", "codex"] : [target];
|
|
288
|
+
}
|
|
289
|
+
function isRootGuidancePath(filePath) {
|
|
290
|
+
return filePath === "AGENTS.md" || filePath === "CLAUDE.md";
|
|
291
|
+
}
|
|
292
|
+
async function planRootGuidanceWrite(workspaceRoot, file, context) {
|
|
293
|
+
const absolutePath = path.join(workspaceRoot, file.path);
|
|
294
|
+
const existing = await readOptionalTextFile(absolutePath);
|
|
295
|
+
const nextContents = mergeRootGuidance(existing, file.contents);
|
|
296
|
+
if (nextContents.conflict !== void 0) {
|
|
297
|
+
context.result.conflicts.push({
|
|
298
|
+
path: file.path,
|
|
299
|
+
reason: nextContents.conflict
|
|
300
|
+
});
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
context.result.managedBlocks.push({ path: file.path, action: nextContents.action });
|
|
304
|
+
if (existing === nextContents.contents) {
|
|
305
|
+
context.result.skippedFiles.push(file.path);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
context.result.plannedFiles.push(file.path);
|
|
309
|
+
if (context.dryRun) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
await writeRenderedFile(absolutePath, nextContents.contents);
|
|
313
|
+
if (existing === void 0) {
|
|
314
|
+
context.result.writtenFiles.push(file.path);
|
|
315
|
+
} else {
|
|
316
|
+
context.result.replacedFiles.push(file.path);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
async function planManagedFileWrite(workspaceRoot, file, context) {
|
|
320
|
+
const absolutePath = path.join(workspaceRoot, file.path);
|
|
321
|
+
const existing = await readOptionalTextFile(absolutePath);
|
|
322
|
+
if (existing === file.contents) {
|
|
323
|
+
context.result.skippedFiles.push(file.path);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (existing !== void 0 && !isHarnessManagedSkill(existing) && !isBaseWorkspacePlaceholder(existing) && !context.force) {
|
|
327
|
+
context.result.conflicts.push({
|
|
328
|
+
path: file.path,
|
|
329
|
+
reason: "Existing file is not marked as OKF Harness managed. Re-run with --force to replace it."
|
|
330
|
+
});
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
context.result.plannedFiles.push(file.path);
|
|
334
|
+
if (context.dryRun) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
await writeRenderedFile(absolutePath, file.contents);
|
|
338
|
+
if (existing === void 0) {
|
|
339
|
+
context.result.writtenFiles.push(file.path);
|
|
340
|
+
} else {
|
|
341
|
+
context.result.replacedFiles.push(file.path);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function mergeRootGuidance(existing, renderedRoot) {
|
|
345
|
+
const renderedBlock = extractManagedBlock(renderedRoot);
|
|
346
|
+
if (existing === void 0 || isBaseWorkspacePlaceholder(existing)) {
|
|
347
|
+
return { contents: renderedRoot, action: existing === void 0 ? "created" : "replaced" };
|
|
348
|
+
}
|
|
349
|
+
const existingStart = existing.indexOf(managedBlockStart);
|
|
350
|
+
const existingEnd = existing.indexOf(managedBlockEnd);
|
|
351
|
+
if (existingStart !== -1 || existingEnd !== -1) {
|
|
352
|
+
if (existingStart === -1 || existingEnd === -1 || existingEnd <= existingStart) {
|
|
353
|
+
return {
|
|
354
|
+
contents: existing,
|
|
355
|
+
action: "unchanged",
|
|
356
|
+
conflict: "Existing root guidance contains a malformed OKF Harness managed block. Fix the markers before reinstalling."
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (existingStart !== -1 && existingEnd !== -1 && existingEnd > existingStart) {
|
|
361
|
+
const afterEnd = existingEnd + managedBlockEnd.length;
|
|
362
|
+
const contents = `${existing.slice(0, existingStart)}${renderedBlock}${existing.slice(afterEnd)}`;
|
|
363
|
+
return {
|
|
364
|
+
contents,
|
|
365
|
+
action: contents === existing ? "unchanged" : "replaced"
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const separator = existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
|
|
369
|
+
return { contents: `${existing}${separator}${renderedBlock}
|
|
370
|
+
`, action: "inserted" };
|
|
371
|
+
}
|
|
372
|
+
function extractManagedBlock(contents) {
|
|
373
|
+
const start = contents.indexOf(managedBlockStart);
|
|
374
|
+
const end = contents.indexOf(managedBlockEnd);
|
|
375
|
+
if (start === -1 || end === -1 || end <= start) {
|
|
376
|
+
throw new Error("Rendered root guidance is missing the OKF Harness managed block.");
|
|
377
|
+
}
|
|
378
|
+
return contents.slice(start, end + managedBlockEnd.length);
|
|
379
|
+
}
|
|
380
|
+
function isHarnessManagedSkill(contents) {
|
|
381
|
+
return contents.includes("okf-harness-managed: true");
|
|
382
|
+
}
|
|
383
|
+
function isBaseWorkspacePlaceholder(contents) {
|
|
384
|
+
return contents.trim() === "@AGENTS.md" || contents.includes("Placeholder. Install agent guidance with okfh init or okfh agent.");
|
|
385
|
+
}
|
|
386
|
+
async function readOptionalTextFile(filePath) {
|
|
387
|
+
try {
|
|
388
|
+
return await readFile(filePath, "utf8");
|
|
389
|
+
} catch (error) {
|
|
390
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
391
|
+
return void 0;
|
|
392
|
+
}
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function writeRenderedFile(filePath, contents) {
|
|
397
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
398
|
+
await writeFile(filePath, contents, "utf8");
|
|
399
|
+
}
|
|
400
|
+
function isNodeError(error) {
|
|
401
|
+
return typeof error === "object" && error !== null && "code" in error;
|
|
402
|
+
}
|
|
403
|
+
function renderSkillFiles(adapter, version) {
|
|
404
|
+
const skillRoot = adapter === "claude" ? ".claude/skills" : ".agents/skills";
|
|
405
|
+
return skillTemplates.flatMap((skill) => [
|
|
406
|
+
{
|
|
407
|
+
path: `${skillRoot}/${skill.name}/SKILL.md`,
|
|
408
|
+
contents: renderSkill(skill, version)
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
path: `${skillRoot}/${skill.name}/references/${skill.reference.path}`,
|
|
412
|
+
contents: skill.reference.body
|
|
413
|
+
}
|
|
414
|
+
]);
|
|
415
|
+
}
|
|
416
|
+
function renderSkill(skill, version) {
|
|
417
|
+
return `---
|
|
418
|
+
name: ${skill.name}
|
|
419
|
+
description: ${skill.description}
|
|
420
|
+
license: Apache-2.0
|
|
421
|
+
compatibility: Designed for Claude Code and Codex on macOS. Requires the okfh CLI and local shell command access.
|
|
422
|
+
metadata:
|
|
423
|
+
okf-harness-version: "${version}"
|
|
424
|
+
okf-harness-managed: true
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
# ${skill.title}
|
|
428
|
+
|
|
429
|
+
${skill.summary}
|
|
430
|
+
|
|
431
|
+
## Required Behavior
|
|
432
|
+
|
|
433
|
+
${numberedList(skill.requiredBehavior)}
|
|
434
|
+
|
|
435
|
+
## Hard Rules
|
|
436
|
+
|
|
437
|
+
${bulletList(skill.hardRules)}
|
|
438
|
+
|
|
439
|
+
See [the ${referenceLabel(skill.reference.title)}](references/${skill.reference.path}) for details.
|
|
440
|
+
`;
|
|
441
|
+
}
|
|
442
|
+
function numberedList(items) {
|
|
443
|
+
return items.map((item, index) => `${index + 1}. ${item}`).join("\n");
|
|
444
|
+
}
|
|
445
|
+
function bulletList(items) {
|
|
446
|
+
return items.map((item) => `- ${item}`).join("\n");
|
|
447
|
+
}
|
|
448
|
+
function referenceLabel(title) {
|
|
449
|
+
return title.toLowerCase();
|
|
450
|
+
}
|
|
451
|
+
export {
|
|
452
|
+
installAgentAdapters,
|
|
453
|
+
packageInfo,
|
|
454
|
+
renderAgentAdapter
|
|
455
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@okf-harness/agent-pack",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Claude and Codex adapter renderers and shared skill templates.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"author": "Eric Zhou",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.0.0"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/pumblus/okf-harness#readme",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/pumblus/okf-harness.git",
|
|
15
|
+
"directory": "packages/agent-pack"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/pumblus/okf-harness/issues"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"okf",
|
|
22
|
+
"llm-wiki",
|
|
23
|
+
"agent-skills",
|
|
24
|
+
"claude-code",
|
|
25
|
+
"codex",
|
|
26
|
+
"agent-guidance"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
44
|
+
"prepublishOnly": "pnpm run build"
|
|
45
|
+
}
|
|
46
|
+
}
|