@leji-org/leji 1.0.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 +36 -0
- package/assets-manifest.json +25 -0
- package/cli.json +82 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/conformance.d.ts +24 -0
- package/dist/commands/conformance.js +111 -0
- package/dist/commands/conformance.js.map +1 -0
- package/dist/commands/docs.d.ts +32 -0
- package/dist/commands/docs.js +196 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/freshness.d.ts +21 -0
- package/dist/commands/freshness.js +41 -0
- package/dist/commands/freshness.js.map +1 -0
- package/dist/commands/indexgen.d.ts +55 -0
- package/dist/commands/indexgen.js +256 -0
- package/dist/commands/indexgen.js.map +1 -0
- package/dist/commands/init.d.ts +28 -0
- package/dist/commands/init.js +378 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/validate.d.ts +25 -0
- package/dist/commands/validate.js +359 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +324 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/findings.d.ts +17 -0
- package/dist/lib/findings.js +29 -0
- package/dist/lib/findings.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +14 -0
- package/dist/lib/frontmatter.js +28 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/fsx.d.ts +21 -0
- package/dist/lib/fsx.js +100 -0
- package/dist/lib/fsx.js.map +1 -0
- package/dist/lib/git.d.ts +10 -0
- package/dist/lib/git.js +55 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/layer.d.ts +32 -0
- package/dist/lib/layer.js +138 -0
- package/dist/lib/layer.js.map +1 -0
- package/dist/lib/manifest.d.ts +62 -0
- package/dist/lib/manifest.js +54 -0
- package/dist/lib/manifest.js.map +1 -0
- package/dist/lib/schemas.d.ts +38 -0
- package/dist/lib/schemas.js +57 -0
- package/dist/lib/schemas.js.map +1 -0
- package/package.json +61 -0
- package/schemas/README.md +3 -0
- package/schemas/agent-profile.schema.json +129 -0
- package/schemas/context-changelog.schema.json +150 -0
- package/schemas/context-index.schema.json +137 -0
- package/schemas/context-manifest.schema.json +253 -0
- package/schemas/decision-record.schema.json +84 -0
- package/templates/README.md +5 -0
- package/templates/agent-profile.md +25 -0
- package/templates/agents/core.md +27 -0
- package/templates/boot-profile.md +39 -0
- package/templates/decision-record.md +28 -0
- package/templates/docs-viewer-assets/PROVENANCE.txt +18 -0
- package/templates/docs-viewer-assets/docsify-sidebar-collapse.min.css +24 -0
- package/templates/docs-viewer-assets/docsify-sidebar-collapse.min.js +149 -0
- package/templates/docs-viewer-assets/docsify.min.js +1 -0
- package/templates/docs-viewer-assets/search.min.js +314 -0
- package/templates/docs-viewer-assets/vue.css +1063 -0
- package/templates/docs-viewer.html +63 -0
- package/templates/leji.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,SAAS,GAAG,CAAC,IAAY,EAAE,IAAc;IACtC,IAAI,CAAC;QACF,OAAO,YAAY,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE;YAC/C,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;YACnC,OAAO,EAAE,MAAM;SACjB,CAAC,CAAC;IACN,CAAC;IAAC,MAAM,CAAC;QACN,OAAO,IAAI,CAAC;IACf,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,WAAW,CAAC,IAAY;IACrC,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACxD,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe;IAC1D,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACnE,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC;IACzB,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7B,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,OAAe;IACtD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,sEAAsE;IACtE,sEAAsE;IACtE,IAAI,WAAmB,CAAC;IACxB,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACF,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACnC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACN,qEAAqE;QACrE,OAAO,IAAI,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;IAClE,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,CAAC,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Finding } from './findings.js';
|
|
2
|
+
import { type CategoryId, type Manifest } from './manifest.js';
|
|
3
|
+
export interface ScannedDoc {
|
|
4
|
+
relPath: string;
|
|
5
|
+
category: CategoryId;
|
|
6
|
+
frontmatter: Record<string, unknown> | null;
|
|
7
|
+
body: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ScannedProfile {
|
|
10
|
+
relPath: string;
|
|
11
|
+
frontmatter: Record<string, unknown> | null;
|
|
12
|
+
findings: Finding[];
|
|
13
|
+
}
|
|
14
|
+
/** Files validate/index must not treat as category content. */
|
|
15
|
+
export declare function excludedFromCategories(manifest: Manifest): (relPath: string) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Collect category documents. A file matched by several category paths is
|
|
18
|
+
* assigned once: longest declared path wins, manifest order breaks ties.
|
|
19
|
+
*/
|
|
20
|
+
export declare function scanCategories(root: string, manifest: Manifest): ScannedDoc[];
|
|
21
|
+
export declare function scanAgentProfiles(root: string, manifest: Manifest): ScannedProfile[];
|
|
22
|
+
export declare function scanDecisionRecords(root: string, manifest: Manifest): ScannedProfile[];
|
|
23
|
+
/** Duplicate-id findings across a set of artifacts that carry an `id`. */
|
|
24
|
+
export declare function duplicateIdFindings(items: {
|
|
25
|
+
id: unknown;
|
|
26
|
+
relPath: string;
|
|
27
|
+
}[], scope: string): Finding[];
|
|
28
|
+
/** Read a declared JSON artifact; returns parsed value or a finding. */
|
|
29
|
+
export declare function readJsonArtifact(root: string, relPath: string): {
|
|
30
|
+
data: unknown;
|
|
31
|
+
finding?: Finding;
|
|
32
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { finding } from './findings.js';
|
|
3
|
+
import { isFile, readText, realpathWithin, underPath, walkMd } from './fsx.js';
|
|
4
|
+
import { parseFrontmatter } from './frontmatter.js';
|
|
5
|
+
import { CATEGORY_IDS } from './manifest.js';
|
|
6
|
+
import { schemaErrors } from './schemas.js';
|
|
7
|
+
/** Files validate/index must not treat as category content. */
|
|
8
|
+
export function excludedFromCategories(manifest) {
|
|
9
|
+
const profilesDir = manifest.machine?.agentProfilesPath;
|
|
10
|
+
return (relPath) => {
|
|
11
|
+
if (relPath === manifest.bootProfilePath)
|
|
12
|
+
return true;
|
|
13
|
+
if (profilesDir && underPath(relPath, profilesDir))
|
|
14
|
+
return true;
|
|
15
|
+
if (path.posix.basename(relPath).toLowerCase() === 'readme.md')
|
|
16
|
+
return true;
|
|
17
|
+
return false;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Collect category documents. A file matched by several category paths is
|
|
22
|
+
* assigned once: longest declared path wins, manifest order breaks ties.
|
|
23
|
+
*/
|
|
24
|
+
export function scanCategories(root, manifest) {
|
|
25
|
+
const excluded = excludedFromCategories(manifest);
|
|
26
|
+
const byFile = new Map();
|
|
27
|
+
for (const category of CATEGORY_IDS) {
|
|
28
|
+
const mapping = manifest.categories[category];
|
|
29
|
+
if (!mapping)
|
|
30
|
+
continue;
|
|
31
|
+
for (const declared of mapping.paths) {
|
|
32
|
+
for (const relPath of walkMd(root, declared)) {
|
|
33
|
+
if (excluded(relPath))
|
|
34
|
+
continue;
|
|
35
|
+
const prev = byFile.get(relPath);
|
|
36
|
+
if (!prev || declared.length > prev.declaredLen) {
|
|
37
|
+
byFile.set(relPath, { category, declaredLen: declared.length });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const docs = [];
|
|
43
|
+
for (const [relPath, { category }] of [...byFile.entries()].sort(([a], [b]) => (a < b ? -1 : 1))) {
|
|
44
|
+
const text = readText(path.join(root, relPath));
|
|
45
|
+
const fm = parseFrontmatter(text);
|
|
46
|
+
docs.push({ relPath, category, frontmatter: fm.data, body: fm.body });
|
|
47
|
+
}
|
|
48
|
+
return docs;
|
|
49
|
+
}
|
|
50
|
+
function scanFrontmatterArtifacts(root, dir, schemaName, rule) {
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const relPath of walkMd(root, dir)) {
|
|
53
|
+
if (path.posix.basename(relPath).toLowerCase() === 'readme.md')
|
|
54
|
+
continue;
|
|
55
|
+
const text = readText(path.join(root, relPath));
|
|
56
|
+
const fm = parseFrontmatter(text);
|
|
57
|
+
const findings = [];
|
|
58
|
+
if (fm.error) {
|
|
59
|
+
findings.push(finding(rule, 'error', fm.error, relPath));
|
|
60
|
+
}
|
|
61
|
+
else if (!fm.data) {
|
|
62
|
+
findings.push(finding(rule, 'error', 'missing YAML frontmatter', relPath));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
for (const err of schemaErrors(schemaName, fm.data)) {
|
|
66
|
+
findings.push(finding(rule, 'error', err, relPath));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
out.push({ relPath, frontmatter: fm.data, findings });
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
export function scanAgentProfiles(root, manifest) {
|
|
74
|
+
const dir = manifest.machine?.agentProfilesPath;
|
|
75
|
+
if (!dir)
|
|
76
|
+
return [];
|
|
77
|
+
return scanFrontmatterArtifacts(root, dir, 'agent-profile', 'profile-frontmatter');
|
|
78
|
+
}
|
|
79
|
+
export function scanDecisionRecords(root, manifest) {
|
|
80
|
+
// Scan the declared records path and every mapped decisions path; a layer
|
|
81
|
+
// may map several and valid records can live in any of them.
|
|
82
|
+
const dirs = new Set();
|
|
83
|
+
if (manifest.machine?.decisionRecordsPath)
|
|
84
|
+
dirs.add(manifest.machine.decisionRecordsPath);
|
|
85
|
+
for (const p of manifest.categories.decisions?.paths ?? [])
|
|
86
|
+
dirs.add(p);
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const dir of dirs) {
|
|
90
|
+
for (const scanned of scanFrontmatterArtifacts(root, dir, 'decision-record', 'decision-frontmatter')) {
|
|
91
|
+
if (seen.has(scanned.relPath))
|
|
92
|
+
continue;
|
|
93
|
+
seen.add(scanned.relPath);
|
|
94
|
+
out.push(scanned);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
/** Duplicate-id findings across a set of artifacts that carry an `id`. */
|
|
100
|
+
export function duplicateIdFindings(items, scope) {
|
|
101
|
+
const seen = new Map();
|
|
102
|
+
const findings = [];
|
|
103
|
+
for (const { id, relPath } of items) {
|
|
104
|
+
if (typeof id !== 'string' || id === '')
|
|
105
|
+
continue;
|
|
106
|
+
const first = seen.get(id);
|
|
107
|
+
if (first !== undefined && first !== relPath) {
|
|
108
|
+
findings.push(finding('id-duplicate', 'error', `${scope} id "${id}" already used by ${first}`, relPath));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
seen.set(id, relPath);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return findings;
|
|
115
|
+
}
|
|
116
|
+
/** Read a declared JSON artifact; returns parsed value or a finding. */
|
|
117
|
+
export function readJsonArtifact(root, relPath) {
|
|
118
|
+
const abs = path.join(root, relPath);
|
|
119
|
+
if (!isFile(abs)) {
|
|
120
|
+
return { data: null };
|
|
121
|
+
}
|
|
122
|
+
if (!realpathWithin(path.resolve(root), abs)) {
|
|
123
|
+
return {
|
|
124
|
+
data: null,
|
|
125
|
+
finding: finding('artifact-parse', 'error', `artifact ${relPath} resolves outside the layer root`, relPath),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
return { data: JSON.parse(readText(abs)) };
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
return {
|
|
133
|
+
data: null,
|
|
134
|
+
finding: finding('artifact-parse', 'error', `invalid JSON: ${e.message}`, relPath),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=layer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer.js","sourceRoot":"","sources":["../../src/lib/layer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type Finding } from './findings.js';
|
|
2
|
+
export declare const CATEGORY_IDS: readonly ["domain", "system", "practice", "governance", "decisions"];
|
|
3
|
+
export type CategoryId = (typeof CATEGORY_IDS)[number];
|
|
4
|
+
export declare const CONFORMANCE_LEVELS: readonly ["core", "indexed", "governed", "federated"];
|
|
5
|
+
export type ConformanceLevel = (typeof CONFORMANCE_LEVELS)[number];
|
|
6
|
+
export interface Owner {
|
|
7
|
+
name: string;
|
|
8
|
+
contact?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface Manifest {
|
|
11
|
+
$schema?: string;
|
|
12
|
+
leji: string;
|
|
13
|
+
name: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
rootPath: string;
|
|
16
|
+
bootProfilePath: string;
|
|
17
|
+
categories: Partial<Record<CategoryId, {
|
|
18
|
+
paths: string[];
|
|
19
|
+
}>>;
|
|
20
|
+
machine?: {
|
|
21
|
+
indexPath?: string;
|
|
22
|
+
changelogPath?: string;
|
|
23
|
+
agentProfilesPath?: string;
|
|
24
|
+
decisionRecordsPath?: string;
|
|
25
|
+
};
|
|
26
|
+
agents?: Record<string, string>;
|
|
27
|
+
docs?: {
|
|
28
|
+
port?: number;
|
|
29
|
+
};
|
|
30
|
+
owners: {
|
|
31
|
+
primary: Owner;
|
|
32
|
+
continuity?: Owner;
|
|
33
|
+
};
|
|
34
|
+
conformance?: {
|
|
35
|
+
claimedLevel?: ConformanceLevel;
|
|
36
|
+
claimedAt?: string;
|
|
37
|
+
};
|
|
38
|
+
federation?: {
|
|
39
|
+
mounts?: {
|
|
40
|
+
path: string;
|
|
41
|
+
name: string;
|
|
42
|
+
owner: Owner;
|
|
43
|
+
role?: string;
|
|
44
|
+
source?: string;
|
|
45
|
+
}[];
|
|
46
|
+
};
|
|
47
|
+
vendorAdapters?: string[];
|
|
48
|
+
}
|
|
49
|
+
export interface ManifestLoad {
|
|
50
|
+
manifest: Manifest | null;
|
|
51
|
+
findings: Finding[];
|
|
52
|
+
}
|
|
53
|
+
export declare const MANIFEST_FILENAME = "leji.json";
|
|
54
|
+
/**
|
|
55
|
+
* Load and structurally validate leji.json at the repository root: existence,
|
|
56
|
+
* JSON parse, declared spec line, manifest schema. Content-level checks
|
|
57
|
+
* (paths existing, categories populated) live in the validate command.
|
|
58
|
+
*/
|
|
59
|
+
export declare function loadManifest(root: string): ManifestLoad;
|
|
60
|
+
/** Effective conformance claim: absent claim is treated as core. */
|
|
61
|
+
export declare function claimedLevel(manifest: Manifest): ConformanceLevel;
|
|
62
|
+
export declare function levelAtLeast(level: ConformanceLevel, threshold: ConformanceLevel): boolean;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { finding } from './findings.js';
|
|
3
|
+
import { exists, isFile, readText } from './fsx.js';
|
|
4
|
+
import { SUPPORTED_LINES, schemaErrors } from './schemas.js';
|
|
5
|
+
export const CATEGORY_IDS = ['domain', 'system', 'practice', 'governance', 'decisions'];
|
|
6
|
+
export const CONFORMANCE_LEVELS = ['core', 'indexed', 'governed', 'federated'];
|
|
7
|
+
export const MANIFEST_FILENAME = 'leji.json';
|
|
8
|
+
/**
|
|
9
|
+
* Load and structurally validate leji.json at the repository root: existence,
|
|
10
|
+
* JSON parse, declared spec line, manifest schema. Content-level checks
|
|
11
|
+
* (paths existing, categories populated) live in the validate command.
|
|
12
|
+
*/
|
|
13
|
+
export function loadManifest(root) {
|
|
14
|
+
const abs = path.join(root, MANIFEST_FILENAME);
|
|
15
|
+
if (!exists(abs) || !isFile(abs)) {
|
|
16
|
+
return {
|
|
17
|
+
manifest: null,
|
|
18
|
+
findings: [
|
|
19
|
+
finding('manifest-missing', 'error', `no ${MANIFEST_FILENAME} at the repository root`, MANIFEST_FILENAME),
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
let data;
|
|
24
|
+
try {
|
|
25
|
+
data = JSON.parse(readText(abs));
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
return {
|
|
29
|
+
manifest: null,
|
|
30
|
+
findings: [finding('manifest-parse', 'error', `invalid JSON: ${e.message}`, MANIFEST_FILENAME)],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const findings = [];
|
|
34
|
+
const line = data?.leji;
|
|
35
|
+
if (typeof line === 'string' && /^\d+\.\d+$/.test(line) && !SUPPORTED_LINES.includes(line)) {
|
|
36
|
+
findings.push(finding('manifest-line', 'error', `declared spec line "${line}" is not supported by this SDK (supported: ${SUPPORTED_LINES.join(', ')})`, MANIFEST_FILENAME));
|
|
37
|
+
return { manifest: null, findings };
|
|
38
|
+
}
|
|
39
|
+
for (const err of schemaErrors('context-manifest', data)) {
|
|
40
|
+
findings.push(finding('manifest-schema', 'error', err, MANIFEST_FILENAME));
|
|
41
|
+
}
|
|
42
|
+
if (findings.some((f) => f.rule === 'manifest-schema')) {
|
|
43
|
+
return { manifest: null, findings };
|
|
44
|
+
}
|
|
45
|
+
return { manifest: data, findings };
|
|
46
|
+
}
|
|
47
|
+
/** Effective conformance claim: absent claim is treated as core. */
|
|
48
|
+
export function claimedLevel(manifest) {
|
|
49
|
+
return manifest.conformance?.claimedLevel ?? 'core';
|
|
50
|
+
}
|
|
51
|
+
export function levelAtLeast(level, threshold) {
|
|
52
|
+
return CONFORMANCE_LEVELS.indexOf(level) >= CONFORMANCE_LEVELS.indexOf(threshold);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/lib/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type ValidateFunction } from 'ajv/dist/2020.js';
|
|
2
|
+
/** Spec lines this SDK supports (versioning.md: validate against the declared line). */
|
|
3
|
+
export declare const SUPPORTED_LINES: string[];
|
|
4
|
+
export type SchemaName = 'context-manifest' | 'context-index' | 'context-changelog' | 'agent-profile' | 'decision-record';
|
|
5
|
+
export declare const SDK_VERSION: string;
|
|
6
|
+
/** Directory holding the vendored schema files for a spec line. */
|
|
7
|
+
export declare function schemasDir(): string;
|
|
8
|
+
/** Directory holding the vendored templates. */
|
|
9
|
+
export declare function templatesDir(): string;
|
|
10
|
+
export interface CliOption {
|
|
11
|
+
flags: string;
|
|
12
|
+
summary: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CliCommand {
|
|
15
|
+
name: string;
|
|
16
|
+
summary: string;
|
|
17
|
+
usage: string;
|
|
18
|
+
description: string;
|
|
19
|
+
options: CliOption[];
|
|
20
|
+
examples: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface CliSpec {
|
|
23
|
+
name: string;
|
|
24
|
+
summary: string;
|
|
25
|
+
usage: string;
|
|
26
|
+
globalOptions: CliOption[];
|
|
27
|
+
exitCodes: {
|
|
28
|
+
code: number;
|
|
29
|
+
meaning: string;
|
|
30
|
+
}[];
|
|
31
|
+
commands: CliCommand[];
|
|
32
|
+
}
|
|
33
|
+
/** The canonical CLI description, single-sourced from cli.json. The terminal
|
|
34
|
+
* help and the docs site both render from this. */
|
|
35
|
+
export declare function loadCliSpec(): CliSpec;
|
|
36
|
+
export declare function getValidator(name: SchemaName): ValidateFunction;
|
|
37
|
+
/** Validate data against a vendored schema; returns human-readable error strings. */
|
|
38
|
+
export declare function schemaErrors(name: SchemaName, data: unknown): string[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
5
|
+
/** Spec lines this SDK supports (versioning.md: validate against the declared line). */
|
|
6
|
+
export const SUPPORTED_LINES = ['1.0'];
|
|
7
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
8
|
+
function readSdkVersion() {
|
|
9
|
+
try {
|
|
10
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
11
|
+
return typeof pkg.version === 'string' && pkg.version ? pkg.version : '0.0.0';
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return '0.0.0';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export const SDK_VERSION = readSdkVersion();
|
|
18
|
+
/** Directory holding the vendored schema files for a spec line. */
|
|
19
|
+
export function schemasDir() {
|
|
20
|
+
return path.join(packageRoot, 'schemas');
|
|
21
|
+
}
|
|
22
|
+
/** Directory holding the vendored templates. */
|
|
23
|
+
export function templatesDir() {
|
|
24
|
+
return path.join(packageRoot, 'templates');
|
|
25
|
+
}
|
|
26
|
+
/** The canonical CLI description, single-sourced from cli.json. The terminal
|
|
27
|
+
* help and the docs site both render from this. */
|
|
28
|
+
export function loadCliSpec() {
|
|
29
|
+
return JSON.parse(fs.readFileSync(path.join(packageRoot, 'cli.json'), 'utf8'));
|
|
30
|
+
}
|
|
31
|
+
const ajv = new Ajv2020({ allErrors: true, strict: false, validateFormats: false });
|
|
32
|
+
const cache = new Map();
|
|
33
|
+
export function getValidator(name) {
|
|
34
|
+
let v = cache.get(name);
|
|
35
|
+
if (!v) {
|
|
36
|
+
const schema = JSON.parse(fs.readFileSync(path.join(schemasDir(), `${name}.schema.json`), 'utf8'));
|
|
37
|
+
v = ajv.compile(schema);
|
|
38
|
+
cache.set(name, v);
|
|
39
|
+
}
|
|
40
|
+
return v;
|
|
41
|
+
}
|
|
42
|
+
/** Validate data against a vendored schema; returns human-readable error strings. */
|
|
43
|
+
export function schemaErrors(name, data) {
|
|
44
|
+
const v = getValidator(name);
|
|
45
|
+
if (v(data))
|
|
46
|
+
return [];
|
|
47
|
+
return ((v.errors ?? [])
|
|
48
|
+
// Ajv reports conditional (if/then) failures twice: the inner error plus
|
|
49
|
+
// a "must match then schema" wrapper. Drop the wrapper for finding-count
|
|
50
|
+
// parity with the Python SDK's jsonschema.
|
|
51
|
+
.filter((e) => e.keyword !== 'if')
|
|
52
|
+
.map((e) => {
|
|
53
|
+
const where = e.instancePath === '' ? '(root)' : e.instancePath;
|
|
54
|
+
return `${where} ${e.message ?? 'invalid'}`;
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/lib/schemas.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@leji-org/leji",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Reference SDK and CLI for Leji, the open specification for the shared context layer of AI-native teams: validate, index, changelog, freshness, conformance, and init.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"leji",
|
|
7
|
+
"ai-native",
|
|
8
|
+
"ai-native-teams",
|
|
9
|
+
"context-layer",
|
|
10
|
+
"shared-context",
|
|
11
|
+
"agent-context",
|
|
12
|
+
"ai-agents",
|
|
13
|
+
"governance",
|
|
14
|
+
"specification",
|
|
15
|
+
"validation",
|
|
16
|
+
"cli"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://leji.org",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/leji-org/leji.git",
|
|
22
|
+
"directory": "packages/sdk"
|
|
23
|
+
},
|
|
24
|
+
"author": "Vuong Nguyen (https://vuongnguyen.com)",
|
|
25
|
+
"license": "Apache-2.0",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"leji": "dist/cli.js"
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"schemas",
|
|
41
|
+
"templates",
|
|
42
|
+
"assets-manifest.json",
|
|
43
|
+
"cli.json"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc",
|
|
47
|
+
"pretest": "npm run build",
|
|
48
|
+
"test": "node --test",
|
|
49
|
+
"coverage": "npm run build && c8 --reporter=text --include='dist/**/*.js' --all node --test",
|
|
50
|
+
"validate": "node ./dist/cli.js validate"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"ajv": "8.20.0",
|
|
54
|
+
"yaml": "2.9.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "25.9.3",
|
|
58
|
+
"c8": "11.0.0",
|
|
59
|
+
"typescript": "6.0.3"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://leji.org/schemas/v1.0/agent-profile.schema.json",
|
|
4
|
+
"title": "Leji agent profile frontmatter",
|
|
5
|
+
"description": "Validates the YAML frontmatter of an agent profile document. The markdown body stays free-form.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"id",
|
|
9
|
+
"name",
|
|
10
|
+
"role",
|
|
11
|
+
"requiredRead",
|
|
12
|
+
"mustAskWhen"
|
|
13
|
+
],
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"properties": {
|
|
16
|
+
"id": {
|
|
17
|
+
"description": "Stable identifier for this profile, e.g. \"core\" or \"reviewer\".",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
|
|
20
|
+
},
|
|
21
|
+
"name": {
|
|
22
|
+
"description": "Human-readable name of the role.",
|
|
23
|
+
"type": "string",
|
|
24
|
+
"minLength": 1
|
|
25
|
+
},
|
|
26
|
+
"role": {
|
|
27
|
+
"description": "The role identifier this profile fills, bound in the manifest's agents map.",
|
|
28
|
+
"type": "string",
|
|
29
|
+
"minLength": 1
|
|
30
|
+
},
|
|
31
|
+
"purpose": {
|
|
32
|
+
"description": "One line on what this role is for.",
|
|
33
|
+
"type": "string"
|
|
34
|
+
},
|
|
35
|
+
"version": {
|
|
36
|
+
"description": "Optional version of this profile.",
|
|
37
|
+
"type": "string"
|
|
38
|
+
},
|
|
39
|
+
"inherits": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "id of the profile this one extends, typically the core profile."
|
|
42
|
+
},
|
|
43
|
+
"requiredRead": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"minItems": 1,
|
|
46
|
+
"items": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"pattern": "^(?!/)(?!\\./)(?!.*(^|/)\\.\\.(/|$))(?!.*\\\\).+$"
|
|
49
|
+
},
|
|
50
|
+
"description": "Paths this role loads before any task."
|
|
51
|
+
},
|
|
52
|
+
"defaultContext": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"items": {
|
|
55
|
+
"enum": [
|
|
56
|
+
"domain",
|
|
57
|
+
"system",
|
|
58
|
+
"practice",
|
|
59
|
+
"governance",
|
|
60
|
+
"decisions"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"description": "Categories this role loads by default."
|
|
64
|
+
},
|
|
65
|
+
"mustAskWhen": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"minItems": 1,
|
|
68
|
+
"items": {
|
|
69
|
+
"type": "string"
|
|
70
|
+
},
|
|
71
|
+
"description": "Conditions under which the role must stop and ask a human."
|
|
72
|
+
},
|
|
73
|
+
"mustRefuseWhen": {
|
|
74
|
+
"description": "Conditions under which the role must refuse outright rather than ask.",
|
|
75
|
+
"type": "array",
|
|
76
|
+
"items": {
|
|
77
|
+
"type": "string"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"escalation": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "Who or what to escalate to."
|
|
83
|
+
},
|
|
84
|
+
"owners": {
|
|
85
|
+
"description": "Optional owners of this profile.",
|
|
86
|
+
"type": "array",
|
|
87
|
+
"items": {
|
|
88
|
+
"type": "string"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"freshness": {
|
|
92
|
+
"description": "Review horizon for this profile.",
|
|
93
|
+
"type": "object",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"properties": {
|
|
96
|
+
"reviewAfter": {
|
|
97
|
+
"description": "ISO 8601 date after which the profile should be reviewed for staleness.",
|
|
98
|
+
"type": "string",
|
|
99
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"host": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"description": "The agent host that runs this agent, e.g. \"claude-code\", \"codex\", \"cursor\". Omit for host-agnostic profiles."
|
|
106
|
+
},
|
|
107
|
+
"invocation": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"additionalProperties": false,
|
|
110
|
+
"required": [
|
|
111
|
+
"command"
|
|
112
|
+
],
|
|
113
|
+
"properties": {
|
|
114
|
+
"command": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"description": "Command template to engage this agent from a shell, with <prompt> as the placeholder."
|
|
117
|
+
},
|
|
118
|
+
"constraints": {
|
|
119
|
+
"type": "array",
|
|
120
|
+
"items": {
|
|
121
|
+
"type": "string"
|
|
122
|
+
},
|
|
123
|
+
"description": "Operational constraints worth machine-knowing, e.g. prompt length limits, timeouts."
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"description": "How to engage this agent as an external CLI. Omit for the host's own resident agent."
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|