@nichollasf/ai-kit 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/.agents/archetypes/implementer-senior.md +46 -0
- package/.agents/archetypes/implementer.md +46 -0
- package/.agents/archetypes/orchestrator.md +54 -0
- package/.agents/archetypes/planner.md +54 -0
- package/.agents/archetypes/quick.md +46 -0
- package/.agents/archetypes/researcher.md +46 -0
- package/.agents/archetypes/reviewer.md +46 -0
- package/.agents/catalog/catalog-v2.schema.json +1641 -0
- package/.agents/catalog/personal.json +370 -0
- package/.agents/skills/ai-kit-model-calibration/SKILL.md +57 -0
- package/.agents/skills/ai-kit-model-calibration/assets/scorecard.md +61 -0
- package/.agents/skills/ai-kit-model-calibration/references/rubric.md +41 -0
- package/.agents/skills/ai-kit-task-routing/SKILL.md +44 -0
- package/.agents/skills/ai-kit-task-routing/references/handoff.md +29 -0
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/catalog.d.ts +99 -0
- package/dist/catalog.js +590 -0
- package/dist/catalog.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +201 -0
- package/dist/cli.js.map +1 -0
- package/dist/diagnostics.d.ts +2 -0
- package/dist/diagnostics.js +301 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/installer.d.ts +18 -0
- package/dist/installer.js +303 -0
- package/dist/installer.js.map +1 -0
- package/dist/legacy.d.ts +7 -0
- package/dist/legacy.js +69 -0
- package/dist/legacy.js.map +1 -0
- package/dist/manifest.d.ts +50 -0
- package/dist/manifest.js +223 -0
- package/dist/manifest.js.map +1 -0
- package/dist/transaction.d.ts +10 -0
- package/dist/transaction.js +187 -0
- package/dist/transaction.js.map +1 -0
- package/docs/architecture.md +61 -0
- package/package.json +62 -0
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { normalizeSelection } from "./catalog.js";
|
|
6
|
+
import { MANAGED_PATHS as V2_PATHS, LEGACY_V1_REQUIRED_PATHS, LEGACY_V1_CODEX_PATHS, } from "./legacy.js";
|
|
7
|
+
export const MANIFEST_PATH = ".agents/.ai-kit-manifest.json";
|
|
8
|
+
export const MANIFEST_SCHEMA_VERSION = 3;
|
|
9
|
+
export const PROFILE = "personal";
|
|
10
|
+
export const PACKAGE_ROOT = fileURLToPath(new URL("../", import.meta.url));
|
|
11
|
+
export function sha256(content) {
|
|
12
|
+
return createHash("sha256").update(content).digest("hex");
|
|
13
|
+
}
|
|
14
|
+
export function resolveOwnedPath(target, relativePath) {
|
|
15
|
+
return path.join(target, ...relativePath.split("/"));
|
|
16
|
+
}
|
|
17
|
+
function object(value) {
|
|
18
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
19
|
+
throw new Error("Manifest must contain objects.");
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
function keys(record, allowed) {
|
|
23
|
+
if (Object.keys(record).some((k) => !allowed.includes(k)))
|
|
24
|
+
throw new Error("Manifest has unknown fields.");
|
|
25
|
+
}
|
|
26
|
+
function checksum(value) {
|
|
27
|
+
if (typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value))
|
|
28
|
+
throw new Error("Manifest checksum is invalid.");
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
export function validateOwnedPath(value, allowedPaths) {
|
|
32
|
+
if (typeof value !== "string" ||
|
|
33
|
+
value.includes("\\") ||
|
|
34
|
+
path.posix.isAbsolute(value) ||
|
|
35
|
+
path.posix.normalize(value) !== value ||
|
|
36
|
+
value.split("/").includes("..") ||
|
|
37
|
+
!allowedPaths.has(value))
|
|
38
|
+
throw new Error(`Manifest contains an unsafe owned path: ${String(value)}`);
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
function parseFiles(value, allowed, legacy) {
|
|
42
|
+
if (!Array.isArray(value) || !value.length)
|
|
43
|
+
throw new Error("Manifest files must be non-empty.");
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
return value.map((entry) => {
|
|
46
|
+
const record = object(entry);
|
|
47
|
+
keys(record, legacy ? ["path", "sha256"] : ["path", "sha256", "kind", "linkTarget"]);
|
|
48
|
+
const ownedPath = validateOwnedPath(record.path, allowed);
|
|
49
|
+
if (seen.has(ownedPath))
|
|
50
|
+
throw new Error(`Duplicate manifest path: ${ownedPath}`);
|
|
51
|
+
seen.add(ownedPath);
|
|
52
|
+
const kind = legacy ? "file" : record.kind;
|
|
53
|
+
if (kind !== "file" && kind !== "symlink")
|
|
54
|
+
throw new Error("Invalid artifact kind.");
|
|
55
|
+
const file = {
|
|
56
|
+
path: ownedPath,
|
|
57
|
+
sha256: checksum(record.sha256),
|
|
58
|
+
kind,
|
|
59
|
+
};
|
|
60
|
+
if (kind === "symlink") {
|
|
61
|
+
const link = record.linkTarget;
|
|
62
|
+
if (typeof link !== "string" ||
|
|
63
|
+
!link ||
|
|
64
|
+
link.includes(path.sep === "/" ? "\\" : "/") ||
|
|
65
|
+
path.parse(link).root !== "" ||
|
|
66
|
+
path.normalize(link) !== link ||
|
|
67
|
+
sha256(link) !== file.sha256)
|
|
68
|
+
throw new Error(`Invalid link target: ${ownedPath}`);
|
|
69
|
+
file.linkTarget = link;
|
|
70
|
+
}
|
|
71
|
+
else if (record.linkTarget !== undefined)
|
|
72
|
+
throw new Error("File must not contain linkTarget.");
|
|
73
|
+
return file;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
export function parseManifest(value, allowedPaths = []) {
|
|
77
|
+
const record = object(value);
|
|
78
|
+
const version = record.schemaVersion;
|
|
79
|
+
if (![1, 2, 3].includes(version))
|
|
80
|
+
throw new Error(`Unsupported manifest schema: ${String(version)}`);
|
|
81
|
+
const legacy = version !== 3;
|
|
82
|
+
keys(record, legacy
|
|
83
|
+
? [
|
|
84
|
+
"schemaVersion",
|
|
85
|
+
"packageVersion",
|
|
86
|
+
"profile",
|
|
87
|
+
"installedAt",
|
|
88
|
+
"files",
|
|
89
|
+
...(version === 2 ? ["catalog", "renderedFiles"] : []),
|
|
90
|
+
]
|
|
91
|
+
: [
|
|
92
|
+
"schemaVersion",
|
|
93
|
+
"packageVersion",
|
|
94
|
+
"profile",
|
|
95
|
+
"installedAt",
|
|
96
|
+
"files",
|
|
97
|
+
"selection",
|
|
98
|
+
"presets",
|
|
99
|
+
"catalog",
|
|
100
|
+
]);
|
|
101
|
+
if (typeof record.packageVersion !== "string" ||
|
|
102
|
+
!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(record.packageVersion))
|
|
103
|
+
throw new Error("Invalid package version.");
|
|
104
|
+
if (typeof record.installedAt !== "string" ||
|
|
105
|
+
Number.isNaN(Date.parse(record.installedAt)))
|
|
106
|
+
throw new Error("Invalid installation timestamp.");
|
|
107
|
+
if (record.profile !== (legacy ? "openai-pro" : PROFILE))
|
|
108
|
+
throw new Error("Unsupported manifest profile.");
|
|
109
|
+
const allowed = new Set(legacy ? [...V2_PATHS, ...LEGACY_V1_REQUIRED_PATHS] : allowedPaths);
|
|
110
|
+
const files = parseFiles(record.files, allowed, legacy);
|
|
111
|
+
const common = {
|
|
112
|
+
packageVersion: record.packageVersion,
|
|
113
|
+
installedAt: record.installedAt,
|
|
114
|
+
files,
|
|
115
|
+
};
|
|
116
|
+
if (legacy) {
|
|
117
|
+
const actual = new Set(files.map((f) => f.path));
|
|
118
|
+
const candidates = version === 1
|
|
119
|
+
? [
|
|
120
|
+
LEGACY_V1_REQUIRED_PATHS,
|
|
121
|
+
[...LEGACY_V1_REQUIRED_PATHS, ...LEGACY_V1_CODEX_PATHS],
|
|
122
|
+
]
|
|
123
|
+
: [V2_PATHS];
|
|
124
|
+
if (!candidates.some((candidate) => candidate.length === actual.size &&
|
|
125
|
+
candidate.every((p) => actual.has(p))))
|
|
126
|
+
throw new Error(`Manifest v${version} does not contain a complete historical file set; refusing migration.`);
|
|
127
|
+
const result = {
|
|
128
|
+
schemaVersion: version,
|
|
129
|
+
profile: "openai-pro",
|
|
130
|
+
...common,
|
|
131
|
+
};
|
|
132
|
+
if (version === 2) {
|
|
133
|
+
const catalog = object(record.catalog);
|
|
134
|
+
keys(catalog, ["baseSha256", "overlaySha256"]);
|
|
135
|
+
result.catalog = {
|
|
136
|
+
baseSha256: checksum(catalog.baseSha256),
|
|
137
|
+
overlaySha256: checksum(catalog.overlaySha256),
|
|
138
|
+
};
|
|
139
|
+
const rendered = parseFiles(record.renderedFiles, allowed, true);
|
|
140
|
+
const expected = V2_PATHS.filter((p) => p.startsWith(".codex/") || p.startsWith(".kilo/"));
|
|
141
|
+
if (rendered.length !== expected.length ||
|
|
142
|
+
rendered.some((f) => !expected.includes(f.path) ||
|
|
143
|
+
!files.some((all) => all.path === f.path && all.sha256 === f.sha256)))
|
|
144
|
+
throw new Error("Legacy rendered inventory is inconsistent.");
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
const selectionRecord = object(record.selection);
|
|
149
|
+
keys(selectionRecord, ["harnesses", "archetypes", "method", "primary"]);
|
|
150
|
+
const selection = normalizeSelection(selectionRecord);
|
|
151
|
+
const presets = object(record.presets);
|
|
152
|
+
if (Object.keys(presets).length !== selection.harnesses.length ||
|
|
153
|
+
selection.harnesses.some((h) => presets[h] !==
|
|
154
|
+
(h === "opencode"
|
|
155
|
+
? "gpt-default+claude"
|
|
156
|
+
: h === "codex"
|
|
157
|
+
? "gpt"
|
|
158
|
+
: "claude")))
|
|
159
|
+
throw new Error("Manifest presets do not match selection.");
|
|
160
|
+
const catalog = object(record.catalog);
|
|
161
|
+
keys(catalog, ["baseSha256"]);
|
|
162
|
+
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
163
|
+
for (const file of files) {
|
|
164
|
+
if (file.kind !== "symlink")
|
|
165
|
+
continue;
|
|
166
|
+
const destination = path.posix.join(path.posix.dirname(file.path), file.linkTarget.split(path.sep).join("/"));
|
|
167
|
+
const target = byPath.get(destination);
|
|
168
|
+
if (!destination.startsWith(".agents/") ||
|
|
169
|
+
!target ||
|
|
170
|
+
target.kind !== "file")
|
|
171
|
+
throw new Error(`Link must point directly to a known canonical file: ${file.path}`);
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
schemaVersion: 3,
|
|
175
|
+
profile: PROFILE,
|
|
176
|
+
...common,
|
|
177
|
+
selection,
|
|
178
|
+
presets: presets,
|
|
179
|
+
catalog: { baseSha256: checksum(catalog.baseSha256) },
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
export function manifestFile(asset) {
|
|
183
|
+
const file = {
|
|
184
|
+
path: asset.path,
|
|
185
|
+
sha256: asset.sha256,
|
|
186
|
+
kind: asset.kind ?? "file",
|
|
187
|
+
};
|
|
188
|
+
if (asset.linkTarget !== undefined)
|
|
189
|
+
file.linkTarget = asset.linkTarget;
|
|
190
|
+
return file;
|
|
191
|
+
}
|
|
192
|
+
export function createManifest(packageVersion, assets, selection, baseSha256) {
|
|
193
|
+
return {
|
|
194
|
+
schemaVersion: 3,
|
|
195
|
+
packageVersion,
|
|
196
|
+
profile: PROFILE,
|
|
197
|
+
installedAt: new Date().toISOString(),
|
|
198
|
+
selection,
|
|
199
|
+
presets: Object.fromEntries(selection.harnesses.map((h) => [
|
|
200
|
+
h,
|
|
201
|
+
h === "opencode"
|
|
202
|
+
? "gpt-default+claude"
|
|
203
|
+
: h === "codex"
|
|
204
|
+
? "gpt"
|
|
205
|
+
: "claude",
|
|
206
|
+
])),
|
|
207
|
+
catalog: { baseSha256 },
|
|
208
|
+
files: assets
|
|
209
|
+
.map(manifestFile)
|
|
210
|
+
.sort((a, b) => a.path.localeCompare(b.path)),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
export function serializeManifest(manifest) {
|
|
214
|
+
return Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
215
|
+
}
|
|
216
|
+
export async function readPackageVersion(packageRoot = PACKAGE_ROOT) {
|
|
217
|
+
const value = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"));
|
|
218
|
+
if (typeof value.version !== "string" ||
|
|
219
|
+
!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(value.version))
|
|
220
|
+
throw new Error("Package version missing or invalid.");
|
|
221
|
+
return value.version;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Asset, type ManifestFile } from "./manifest.js";
|
|
2
|
+
export interface FileChange {
|
|
3
|
+
path: string;
|
|
4
|
+
asset?: Asset;
|
|
5
|
+
expected?: ManifestFile;
|
|
6
|
+
}
|
|
7
|
+
export declare function exists(filePath: string): Promise<boolean>;
|
|
8
|
+
export declare function assertAncestors(target: string, relativePath: string): Promise<void>;
|
|
9
|
+
export declare function verifyState(target: string, relativePath: string, expected?: ManifestFile): Promise<void>;
|
|
10
|
+
export declare function applyTransaction(target: string, changes: readonly FileChange[], beforeStep?: (index: number) => Promise<void>): Promise<string[]>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { lstat, mkdir, mkdtemp, readFile, readlink, rename, rm, rmdir, symlink, writeFile, } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { MANIFEST_PATH, resolveOwnedPath, sha256, } from "./manifest.js";
|
|
4
|
+
export async function exists(filePath) {
|
|
5
|
+
try {
|
|
6
|
+
await lstat(filePath);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
if (error.code === "ENOENT")
|
|
11
|
+
return false;
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function assertAncestors(target, relativePath) {
|
|
16
|
+
let current = target;
|
|
17
|
+
for (const segment of relativePath.split("/").slice(0, -1)) {
|
|
18
|
+
current = path.join(current, segment);
|
|
19
|
+
try {
|
|
20
|
+
const info = await lstat(current);
|
|
21
|
+
if (info.isSymbolicLink() || !info.isDirectory())
|
|
22
|
+
throw new Error(`Managed path traverses a symbolic link or non-directory: ${path.relative(target, current)}`);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error.code === "ENOENT")
|
|
26
|
+
return;
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export async function verifyState(target, relativePath, expected) {
|
|
32
|
+
await assertAncestors(target, relativePath);
|
|
33
|
+
const absolute = resolveOwnedPath(target, relativePath);
|
|
34
|
+
if (!(await exists(absolute))) {
|
|
35
|
+
if (expected)
|
|
36
|
+
throw new Error(`Owned path is missing: ${relativePath}`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!expected)
|
|
40
|
+
throw new Error(`Unowned collision; no changes made: ${relativePath}`);
|
|
41
|
+
const info = await lstat(absolute);
|
|
42
|
+
if (expected.kind === "symlink") {
|
|
43
|
+
if (!info.isSymbolicLink() ||
|
|
44
|
+
(await readlink(absolute)) !== expected.linkTarget)
|
|
45
|
+
throw new Error(`Owned link was modified: ${relativePath}`);
|
|
46
|
+
}
|
|
47
|
+
else if (!info.isFile() ||
|
|
48
|
+
info.isSymbolicLink() ||
|
|
49
|
+
sha256(await readFile(absolute)) !== expected.sha256)
|
|
50
|
+
throw new Error(`Owned file was modified: ${relativePath}`);
|
|
51
|
+
}
|
|
52
|
+
async function makeDirectories(target, relative, created) {
|
|
53
|
+
let current = target;
|
|
54
|
+
for (const segment of relative
|
|
55
|
+
.split("/")
|
|
56
|
+
.filter((x) => x !== "." && x !== "")) {
|
|
57
|
+
current = path.join(current, segment);
|
|
58
|
+
try {
|
|
59
|
+
await mkdir(current);
|
|
60
|
+
created.push(current);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error.code !== "EEXIST")
|
|
64
|
+
throw error;
|
|
65
|
+
const info = await lstat(current);
|
|
66
|
+
if (info.isSymbolicLink() || !info.isDirectory())
|
|
67
|
+
throw new Error(`Unsafe directory: ${current}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export async function applyTransaction(target, changes, beforeStep) {
|
|
72
|
+
if (changes.at(-1)?.path !== MANIFEST_PATH)
|
|
73
|
+
throw new Error("Manifest must be applied last.");
|
|
74
|
+
for (const change of changes)
|
|
75
|
+
await verifyState(target, change.path, change.expected);
|
|
76
|
+
const created = [];
|
|
77
|
+
let staging;
|
|
78
|
+
const applied = [];
|
|
79
|
+
let lock;
|
|
80
|
+
try {
|
|
81
|
+
await makeDirectories(target, ".agents", created);
|
|
82
|
+
const lockPath = resolveOwnedPath(target, ".agents/.ai-kit-lock");
|
|
83
|
+
try {
|
|
84
|
+
await mkdir(lockPath);
|
|
85
|
+
lock = lockPath;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (error.code === "EEXIST")
|
|
89
|
+
throw new Error("Another ai-kit transaction or stale .agents/.ai-kit-lock exists; inspect it before retrying.");
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
staging = await mkdtemp(resolveOwnedPath(target, ".agents/.ai-kit-tx-"));
|
|
93
|
+
await mkdir(path.join(staging, "staged"));
|
|
94
|
+
await mkdir(path.join(staging, "backup"));
|
|
95
|
+
for (const [index, change] of changes.entries()) {
|
|
96
|
+
if (!change.asset)
|
|
97
|
+
continue;
|
|
98
|
+
const staged = path.join(staging, "staged", String(index));
|
|
99
|
+
if (change.asset.kind === "symlink") {
|
|
100
|
+
try {
|
|
101
|
+
await symlink(change.asset.linkTarget, staged, "file");
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
throw new Error("Unable to create symlinks; retry with --method copy. No automatic fallback.");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
await writeFile(staged, change.asset.content, {
|
|
109
|
+
mode: 0o644,
|
|
110
|
+
flag: "wx",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Validate every expectation again after staging, before touching owned data.
|
|
114
|
+
for (const change of changes)
|
|
115
|
+
await verifyState(target, change.path, change.expected);
|
|
116
|
+
for (const [index, change] of changes.entries()) {
|
|
117
|
+
await beforeStep?.(index);
|
|
118
|
+
await verifyState(target, change.path, change.expected);
|
|
119
|
+
const destination = resolveOwnedPath(target, change.path);
|
|
120
|
+
await makeDirectories(target, path.posix.dirname(change.path), created);
|
|
121
|
+
const record = { change };
|
|
122
|
+
if (change.expected) {
|
|
123
|
+
const backup = path.join(staging, "backup", String(index));
|
|
124
|
+
await rename(destination, backup);
|
|
125
|
+
record.backup = backup;
|
|
126
|
+
}
|
|
127
|
+
applied.push(record);
|
|
128
|
+
if (change.asset)
|
|
129
|
+
await rename(path.join(staging, "staged", String(index)), destination);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const failures = [];
|
|
134
|
+
for (const record of applied.reverse()) {
|
|
135
|
+
try {
|
|
136
|
+
await assertAncestors(target, record.change.path);
|
|
137
|
+
const destination = resolveOwnedPath(target, record.change.path);
|
|
138
|
+
if (await exists(destination)) {
|
|
139
|
+
const asset = record.change.asset;
|
|
140
|
+
if (!asset)
|
|
141
|
+
throw new Error(`Path appeared during rollback: ${record.change.path}`);
|
|
142
|
+
await verifyState(target, record.change.path, {
|
|
143
|
+
path: asset.path,
|
|
144
|
+
sha256: asset.sha256,
|
|
145
|
+
kind: asset.kind ?? "file",
|
|
146
|
+
...(asset.linkTarget === undefined
|
|
147
|
+
? {}
|
|
148
|
+
: { linkTarget: asset.linkTarget }),
|
|
149
|
+
});
|
|
150
|
+
await rm(destination);
|
|
151
|
+
}
|
|
152
|
+
if (record.backup)
|
|
153
|
+
await rename(record.backup, destination);
|
|
154
|
+
}
|
|
155
|
+
catch (rollbackError) {
|
|
156
|
+
failures.push(rollbackError.message);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (failures.length)
|
|
160
|
+
throw new Error(`Transaction failed; rollback incomplete. Backups and lock preserved at ${staging}: ${failures.join("; ")}`);
|
|
161
|
+
if (staging)
|
|
162
|
+
await rm(staging, { recursive: true, force: true });
|
|
163
|
+
if (lock)
|
|
164
|
+
await rmdir(lock);
|
|
165
|
+
for (const dir of created.reverse()) {
|
|
166
|
+
try {
|
|
167
|
+
await rmdir(dir);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
/* Preserve concurrent/unrelated entries. */
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
const warnings = [];
|
|
176
|
+
try {
|
|
177
|
+
if (staging)
|
|
178
|
+
await rm(staging, { recursive: true, force: true });
|
|
179
|
+
if (lock)
|
|
180
|
+
await rmdir(lock);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
warnings.push(`Committed, but transaction cleanup failed: ${staging}`);
|
|
184
|
+
}
|
|
185
|
+
return warnings;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=transaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Architecture decision: portable archetype catalog
|
|
2
|
+
|
|
3
|
+
Status: accepted for version 1.0.0 after implementation and independent review. Date: 2026-09-12. This supersedes the initial Kilo/Codex `openai-pro` architecture. The `ai-kit` executable remains unchanged; manifest v3, removal of Kilo support and the new role/layout contracts justify the major-version transition from 0.1.0. The user explicitly authorized commit, push and publication, subject to checking that no personal credentials are exposed.
|
|
4
|
+
|
|
5
|
+
Version 1.0.1 corrects the npm package name to `@nichollasf/ai-kit`, the authenticated npm account explicitly selected by the user. The GitHub repository remains `nychollas09/ai-kit`. The v1.0.0 GitHub release used the earlier scope, which was never published to npm. This metadata correction does not change installer ownership or role contracts.
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
Keep three distinct concepts: seven model-free archetypes, a versioned JSON model/effort binding catalog, and harness-specific materialization. `.agents` is ai-kit's canonical convention; discovery uses each harness's documented paths. Support only project installs for Claude Code, Codex and OpenCode. Global installs, LSP, arbitrary wizard-selected models and silent fallback are outside scope.
|
|
10
|
+
|
|
11
|
+
The selection/install interaction takes inspiration from [Matt Pocock's Skills collection](https://github.com/mattpocock/skills). Responsibility separation combines the orchestrator-worker and evaluator-optimizer patterns described by [Anthropic](https://www.anthropic.com/engineering/building-effective-agents) and [Cursor's planner/worker report](https://cursor.com/blog/scaling-agents). Planning is not recursive.
|
|
12
|
+
|
|
13
|
+
Planner chooses architecture and defines tasks but cannot delegate or implement. Orchestrator dispatches an approved plan without implementing or replanning. `implementer-senior` remains the existing identifier and becomes a complex implementation worker. Implementer and Quick have bounded execution scopes. Researcher gathers evidence; Reviewer independently reviews without editing the work. Only Orchestrator delegates; every worker returns to it. Cap concurrent workers at four and require independent review for non-trivial changes.
|
|
14
|
+
|
|
15
|
+
Contracts record mission, activation, inputs, deliverables, allowed actions, prohibitions, write scope, escalation and the fixed handoff. Approval digests detect changed plan bytes; they do not establish authorization. Evidence freshness, dependency checks, scope separation and approval provenance remain behavioral obligations.
|
|
16
|
+
|
|
17
|
+
## Catalog and materialization
|
|
18
|
+
|
|
19
|
+
`personal.json` has schemaVersion 2 and profile `personal`. `catalog-v2.schema.json` documents its exact structure. The zero-dependency runtime validator rejects unknown fields, relaxed authority, unsafe model IDs, `latest`, unsupported efforts and invalid routes. Archetype frontmatter supports only two scalar fields: JSON-quoted `description` and an explicit `mode`, with LF newlines. No generic YAML parser is shipped.
|
|
20
|
+
|
|
21
|
+
Each role has GPT and Claude bindings; harness presets select one binding family. OpenCode installs both families with GPT default. Stable aliases name archetype, explicit model ID and effort; convenience aliases retain the archetype and expose `-claude` separately in OpenCode. Haiku's unsupported effort is represented in the catalog, with no emitted parameter. The GPT effort matrix is preserved, adding Orchestrator at xhigh. The user-required presets are recorded in the README and catalog.
|
|
22
|
+
|
|
23
|
+
The source catalog distributes intentional model changes through package updates. Installed overlays never override the presets. Current source legacy `openai-pro.json`, schema and prompts were preserved when incorporating the initial worktree; the package excludes them. User plans/evaluations remain outside the installer inventory.
|
|
24
|
+
|
|
25
|
+
Materialization emits canonical regular files under `.agents/generated/<harness>`, plus per-file discovery links or equivalent copies. Every link points directly to a known regular canonical file; there are no symlink chains, directory links or targets outside the installation. `.agents/generated/selection.json` records the chosen roles/harnesses and whether the full-team workflow is enabled. Any incomplete team disables orchestration across adapters and routing skills. Optional `selection.primary` chooses a selected standalone role; `update --primary` preserves the installed inventory while switching the main role transactionally. Full-team primary remains Orchestrator.
|
|
26
|
+
|
|
27
|
+
## Adapters and enforcement
|
|
28
|
+
|
|
29
|
+
| Adapter | Discovery and configuration | Relevant official contract |
|
|
30
|
+
| ----------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| OpenCode | `.opencode/agents/*.md`; root `opencode.json`; primary Orchestrator, explicit task allowlist; native `.agents/skills` | [Agents](https://opencode.ai/docs/agents/), [configuration](https://opencode.ai/docs/config/), [skills](https://opencode.ai/docs/skills/) |
|
|
32
|
+
| Claude Code | Separate Markdown frontmatter in `.claude/agents`; `.claude/settings.json` selects main Orchestrator; `.claude/skills` file links | [Subagents](https://code.claude.com/docs/en/sub-agents), [settings](https://code.claude.com/docs/en/settings) |
|
|
33
|
+
| Codex | TOML role layers in `.codex/agents`; `.codex/config.toml` registers roles and main Orchestrator; native `.agents/skills` | [Configuration reference](https://learn.chatgpt.com/docs/config-file/config-reference), [subagents](https://learn.chatgpt.com/docs/agent-configuration/subagents) |
|
|
34
|
+
|
|
35
|
+
Claude's `Agent(...)` allowlist only applies when the profile is the main agent. Workers explicitly deny `Agent` and `Task`; read-only roles omit shell/write tools. Planner retains Write/Edit with a behavioral `.agents/plans` scope. The main settings set depth one and concurrency four; documented harness exceptions mean the concurrency contract also appears in instructions. [Claude model and effort documentation](https://platform.claude.com/docs/en/models/overview) establishes the requested IDs and Haiku's lack of effort control.
|
|
36
|
+
|
|
37
|
+
OpenCode uses native permissions to prevent worker Task delegation and restrict Orchestrator dispatch. Planner editing is limited to planning paths; read-only roles deny edit and shell. Its four-worker cap and task-specific write scope are behavioral. The primary's configured GPT route remains available if an unselected Claude model is absent.
|
|
38
|
+
|
|
39
|
+
Codex workers set `agents.enabled=false`; planner/researcher/reviewer use read-only sandboxes. The main Orchestrator uses workspace-write, because broader permissions in a worker must not be assumed to escape a read-only parent. No-product-edit and exact registered-profile dispatch remain behavioral constraints in the main context. Codex built-in roles and explicit CLI overrides are not represented as a hard allowlist. The four-worker setting uses `max_concurrent_threads_per_session`, excluding the main context. The read-only Planner returns plan text in its handoff for the user to save. Claude/OpenCode roles without hash tools require fresh trusted external hash verification plus the verified plan snapshot; they must not fabricate a cryptographic result.
|
|
40
|
+
|
|
41
|
+
Runtime Codex diagnostics deliberately avoid `config/read`: an isolated synthetic-sentinel test proved that 0.154.0 returns unredacted MCP environment values. The safe `doctor --json` exposes redacted parse/model/auth status but cannot establish effective project trust or all role settings, so `check` reports that layer unverified and returns 4. Integration may use the richer API only against isolated empty/synthetic stores.
|
|
42
|
+
|
|
43
|
+
Native integration verifies configuration parsing/discovery and exposed capabilities without making model calls. This is evidence about configuration, not proof that a model obeys a prompt. Account entitlement and personal configuration can change readiness independently of structural correctness. Local validated versions: Codex 0.154.0, Claude Code 2.1.265, OpenCode 1.18.30.
|
|
44
|
+
|
|
45
|
+
## Ownership, transaction and migration
|
|
46
|
+
|
|
47
|
+
Manifest v3 records package version, selection, presets, catalog checksum and an exact artifact list. Regular file entries record SHA-256 of bytes. Symlinks record their exact relative target with native platform separators and SHA-256 of that target string; the canonical target has a separate file checksum. Manifest artifact paths retain portable forward slashes. Alternate separator spellings and non-normalized link targets are rejected rather than accepted through equivalence checks.
|
|
48
|
+
|
|
49
|
+
The parser accepts only an enumerated renderer inventory, with complete selection/method consistency and direct canonical link targets. It never infers ownership from a directory. During v3 updates, the installer first validates the checksum of the fixed regular catalog path, validates that catalog, and derives its exact old aliases. This lets reviewed source-model updates retire earlier aliases without granting ownership to arbitrary files.
|
|
50
|
+
|
|
51
|
+
Legacy v1/v2 manifests require a complete known historical inventory. Every owned file must be intact. Migration removes only recorded retired artifacts, including Kilo profiles, and preserves personal Kilo configuration, unowned files, plans and evaluations. It does not enumerate or open personal Kilo settings. Existing overlays remain unmanaged: incompatible overrides stop migration before writes; empty legacy metadata may remain.
|
|
52
|
+
|
|
53
|
+
Preflight validates the real project target, managed ancestors, final artifact types, checksums, link targets and all unowned collisions. Whole configuration files are owned; no automatic merge is attempted. Alternate OpenCode JSON/JSONC project configs and Claude settings.local.json are rejected by existence checks without reading their contents. Staging and backups use the target filesystem. A cooperating installer lock prevents concurrent transactions. All expectations are rechecked after staging and before each change. Backups preserve file/link types, rollback unwinds applied changes, and the manifest commits last. Only directories created during a failed transaction are candidates for rollback cleanup; existing empty directories are preserved.
|
|
54
|
+
|
|
55
|
+
`--dry-run` performs preflight with no files, directories, locks or harness processes created. If symlink creation is unsupported, staging aborts and recommends explicit copy mode. Incomplete rollback preserves backups and the lock with a diagnostic path. The transaction is exception-atomic; power loss/process termination recovery is manual, and this does not claim an OS-level isolation boundary against concurrent hostile filesystem mutation.
|
|
56
|
+
|
|
57
|
+
## Validation and release gate
|
|
58
|
+
|
|
59
|
+
`npm run check` includes format, typecheck, catalog/CLI/installer tests and package checks. `npm run test:integration` exercises native harnesses with temporary isolated configuration stores, no credential copying and no model calls. Tests inspect effective discovery and permissions, relative links/copies, space-containing paths, collisions, tampering, rollback, v1/v2 migration, v3 model-alias updates and zero-write dry-runs. A tarball test installs through offline `npx` into a temporary project.
|
|
60
|
+
|
|
61
|
+
`npm pack --dry-run --json` must match an exact allowlist. Runtime dependencies remain zero; package content excludes local overlays, credentials, plans, evaluations and third-party skills. Review contracts, adapters, ownership and migrations independently before completion. Major/layout breaks, permission broadening and a future force option require architecture and major-version review; no force flag is introduced here.
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nichollasf/ai-kit",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "A portable, project-scoped archetype catalog for Claude Code, Codex and OpenCode.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-kit": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/",
|
|
11
|
+
".agents/catalog/catalog-v2.schema.json",
|
|
12
|
+
".agents/catalog/personal.json",
|
|
13
|
+
".agents/archetypes/*.md",
|
|
14
|
+
".agents/skills/ai-kit-model-calibration/",
|
|
15
|
+
".agents/skills/ai-kit-task-routing/",
|
|
16
|
+
"README.md",
|
|
17
|
+
"docs/architecture.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json && node --input-type=module -e \"import { chmodSync } from 'node:fs'; chmodSync('dist/cli.js', 0o755)\"",
|
|
22
|
+
"prepack": "npm run build",
|
|
23
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
24
|
+
"format": "prettier --write .",
|
|
25
|
+
"format:check": "prettier --check .",
|
|
26
|
+
"test": "npm run build && node --import tsx --test test/installer.test.ts test/catalog.test.ts test/cli.test.ts",
|
|
27
|
+
"test:integration": "npm run build && node --import tsx --test test/harness.integration.test.ts test/codex.integration.test.ts",
|
|
28
|
+
"package:check": "npm run build && node --import tsx --test test/package.test.ts",
|
|
29
|
+
"check": "npm run format:check && npm run typecheck && npm test && npm run package:check"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+ssh://git@github.com/nychollas09/ai-kit.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/nychollas09/ai-kit/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/nychollas09/ai-kit#readme",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"author": "Nichollas",
|
|
47
|
+
"keywords": [
|
|
48
|
+
"claude-code",
|
|
49
|
+
"codex",
|
|
50
|
+
"opencode",
|
|
51
|
+
"agents",
|
|
52
|
+
"ai",
|
|
53
|
+
"cli"
|
|
54
|
+
],
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@openai/codex": "0.154.0",
|
|
57
|
+
"@types/node": "24.3.1",
|
|
58
|
+
"prettier": "3.6.2",
|
|
59
|
+
"tsx": "4.20.5",
|
|
60
|
+
"typescript": "5.9.2"
|
|
61
|
+
}
|
|
62
|
+
}
|