@yourtechtribe-labs/koncept-core 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +49 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +114 -0
- package/dist/indexer.js.map +1 -0
- package/dist/parser.d.ts +38 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +95 -0
- package/dist/parser.js.map +1 -0
- package/dist/paths.d.ts +22 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +34 -0
- package/dist/paths.js.map +1 -0
- package/dist/schema.d.ts +137 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +74 -0
- package/dist/schema.js.map +1 -0
- package/dist/search.d.ts +21 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +76 -0
- package/dist/search.js.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Albert Gil López — yourtechtribe-labs
|
|
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,40 @@
|
|
|
1
|
+
# @yourtechtribe-labs/koncept-core
|
|
2
|
+
|
|
3
|
+
> Core schema, parser, and indexer for [koncepto](https://github.com/yourtechtribe-labs/koncept-mcp) — semantic concept graphs for codebases.
|
|
4
|
+
|
|
5
|
+
**Status**: pre-alpha (`v0.1.0-alpha.1`).
|
|
6
|
+
|
|
7
|
+
## What this package contains
|
|
8
|
+
|
|
9
|
+
- **Zod schema** for the YAML concept format (`ConceptSchema`, `KEBAB_ID_REGEX`, enums)
|
|
10
|
+
- **Parser** that loads `.koncept/concepts/*.yaml` and validates against the schema
|
|
11
|
+
- **Indexer** that scans the registry, detects duplicate ids, unresolved cross-refs, and missing participant files
|
|
12
|
+
- **Search API** for in-memory queries over the index
|
|
13
|
+
- **Path helpers** for cross-platform path normalization
|
|
14
|
+
|
|
15
|
+
This is the runtime foundation. End users normally interact with [`@yourtechtribe-labs/koncept-cli`](https://www.npmjs.com/package/@yourtechtribe-labs/koncept-cli) or [`@yourtechtribe-labs/koncept-mcp-server`](https://www.npmjs.com/package/@yourtechtribe-labs/koncept-mcp-server) and don't import this directly.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @yourtechtribe-labs/koncept-core@alpha
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { indexConcepts, isIndexClean, ConceptSchema } from '@yourtechtribe-labs/koncept-core'
|
|
27
|
+
|
|
28
|
+
const result = await indexConcepts(process.cwd())
|
|
29
|
+
if (!isIndexClean(result)) {
|
|
30
|
+
console.error('registry has issues:', result)
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT — see [LICENSE](./LICENSE).
|
|
37
|
+
|
|
38
|
+
## Repository
|
|
39
|
+
|
|
40
|
+
Source, issues, and full docs at [github.com/yourtechtribe-labs/koncept-mcp](https://github.com/yourtechtribe-labs/koncept-mcp).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const VERSION = "0.1.0-alpha.0";
|
|
2
|
+
export { ConceptSchema, IndexEntrySchema, RoleEnum, ConceptTypeEnum, SeverityEnum, StatusEnum, } from './schema.js';
|
|
3
|
+
export type { Concept, IndexEntry, Participant, Invariant, SourceOfTruth, Role, ConceptType, Severity, Status, } from './schema.js';
|
|
4
|
+
export { parseConceptString, parseConceptFile } from './parser.js';
|
|
5
|
+
export type { ParseResult, ParseError, ParseErrorType } from './parser.js';
|
|
6
|
+
export { indexConcepts, writeIndex, isIndexClean } from './indexer.js';
|
|
7
|
+
export type { IndexResult, IndexErrorEntry, DuplicateId, UnresolvedRelated, MissingFile, } from './indexer.js';
|
|
8
|
+
export { searchEntries } from './search.js';
|
|
9
|
+
export type { SearchHit, MatchField } from './search.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO,kBAAkB,CAAA;AAItC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,OAAO,EACP,UAAU,EACV,WAAW,EACX,SAAS,EACT,aAAa,EACb,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,MAAM,GACP,MAAM,aAAa,CAAA;AAGpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAClE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAG1E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACtE,YAAY,EACV,WAAW,EACX,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,WAAW,GACZ,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @yourtechtribe-labs/koncept-core — public API.
|
|
2
|
+
// Greenfield: keep the surface intentional. Internal helpers (regexes,
|
|
3
|
+
// constants, sub-schemas) stay un-exported.
|
|
4
|
+
export const VERSION = '0.1.0-alpha.0';
|
|
5
|
+
// Schema — top-level types + main schemas. Sub-schemas (Participant, Invariant,
|
|
6
|
+
// SourceOfTruth) and the kebab-id regex stay internal.
|
|
7
|
+
export { ConceptSchema, IndexEntrySchema, RoleEnum, ConceptTypeEnum, SeverityEnum, StatusEnum, } from './schema.js';
|
|
8
|
+
// Parser
|
|
9
|
+
export { parseConceptString, parseConceptFile } from './parser.js';
|
|
10
|
+
// Indexer
|
|
11
|
+
export { indexConcepts, writeIndex, isIndexClean } from './indexer.js';
|
|
12
|
+
// Search
|
|
13
|
+
export { searchEntries } from './search.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,uEAAuE;AACvE,4CAA4C;AAE5C,MAAM,CAAC,MAAM,OAAO,GAAG,eAAe,CAAA;AAEtC,gFAAgF;AAChF,uDAAuD;AACvD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAA;AAapB,SAAS;AACT,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAGlE,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAStE,SAAS;AACT,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indexer — scan `.koncept/concepts/*.yaml`, parse them, validate cross-refs,
|
|
3
|
+
* and produce a denormalized IndexEntry list.
|
|
4
|
+
*
|
|
5
|
+
* Validations performed:
|
|
6
|
+
* - YAML/Zod parse errors (collected per-file).
|
|
7
|
+
* - Duplicate concept ids across files.
|
|
8
|
+
* - related_concepts pointing to ids that don't exist.
|
|
9
|
+
* - participants[].file paths that don't resolve on disk.
|
|
10
|
+
*/
|
|
11
|
+
import { type ParseError } from './parser.js';
|
|
12
|
+
import type { IndexEntry } from './schema.js';
|
|
13
|
+
export interface DuplicateId {
|
|
14
|
+
id: string;
|
|
15
|
+
files: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface UnresolvedRelated {
|
|
18
|
+
conceptId: string;
|
|
19
|
+
missingRelatedId: string;
|
|
20
|
+
}
|
|
21
|
+
export interface MissingFile {
|
|
22
|
+
conceptId: string;
|
|
23
|
+
missingFile: string;
|
|
24
|
+
}
|
|
25
|
+
export interface IndexErrorEntry {
|
|
26
|
+
filePath: string;
|
|
27
|
+
errors: ParseError[];
|
|
28
|
+
}
|
|
29
|
+
export interface IndexResult {
|
|
30
|
+
entries: IndexEntry[];
|
|
31
|
+
errors: IndexErrorEntry[];
|
|
32
|
+
duplicateIds: DuplicateId[];
|
|
33
|
+
unresolvedRelated: UnresolvedRelated[];
|
|
34
|
+
missingFiles: MissingFile[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Scan a project root and produce the full index report.
|
|
38
|
+
* @param rootDir absolute or relative path to the project root containing `.koncept/`
|
|
39
|
+
*/
|
|
40
|
+
export declare function indexConcepts(rootDir: string): Promise<IndexResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Persist the index to `.koncept/index.json` (gitignored cache).
|
|
43
|
+
*/
|
|
44
|
+
export declare function writeIndex(rootDir: string, entries: IndexEntry[]): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* True if the IndexResult is fully clean (no errors of any kind).
|
|
47
|
+
*/
|
|
48
|
+
export declare function isIndexClean(result: IndexResult): boolean;
|
|
49
|
+
//# sourceMappingURL=indexer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../src/indexer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAE/D,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,aAAa,CAAA;AAEtD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,UAAU,EAAE,CAAA;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,YAAY,EAAE,WAAW,EAAE,CAAA;IAC3B,iBAAiB,EAAE,iBAAiB,EAAE,CAAA;IACtC,YAAY,EAAE,WAAW,EAAE,CAAA;CAC5B;AAKD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAoFzE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAOzD"}
|
package/dist/indexer.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indexer — scan `.koncept/concepts/*.yaml`, parse them, validate cross-refs,
|
|
3
|
+
* and produce a denormalized IndexEntry list.
|
|
4
|
+
*
|
|
5
|
+
* Validations performed:
|
|
6
|
+
* - YAML/Zod parse errors (collected per-file).
|
|
7
|
+
* - Duplicate concept ids across files.
|
|
8
|
+
* - related_concepts pointing to ids that don't exist.
|
|
9
|
+
* - participants[].file paths that don't resolve on disk.
|
|
10
|
+
*/
|
|
11
|
+
import { glob } from 'glob';
|
|
12
|
+
import { stat, writeFile } from 'node:fs/promises';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { parseConceptFile } from './parser.js';
|
|
15
|
+
import { resolveRelative, normalizeForward } from './paths.js';
|
|
16
|
+
const CONCEPTS_GLOB = '.koncept/concepts/*.yaml';
|
|
17
|
+
const INDEX_FILENAME = '.koncept/index.json';
|
|
18
|
+
/**
|
|
19
|
+
* Scan a project root and produce the full index report.
|
|
20
|
+
* @param rootDir absolute or relative path to the project root containing `.koncept/`
|
|
21
|
+
*/
|
|
22
|
+
export async function indexConcepts(rootDir) {
|
|
23
|
+
// Pass cwd explicitly + posix:true so glob handles forward slashes natively
|
|
24
|
+
// and avoids meta-character escaping issues if rootDir contains `[`, `(`, `*`.
|
|
25
|
+
const files = await glob(CONCEPTS_GLOB, {
|
|
26
|
+
cwd: rootDir,
|
|
27
|
+
absolute: true,
|
|
28
|
+
nodir: true,
|
|
29
|
+
posix: true,
|
|
30
|
+
});
|
|
31
|
+
const result = {
|
|
32
|
+
entries: [],
|
|
33
|
+
errors: [],
|
|
34
|
+
duplicateIds: [],
|
|
35
|
+
unresolvedRelated: [],
|
|
36
|
+
missingFiles: [],
|
|
37
|
+
};
|
|
38
|
+
// Per-file parse + collect concepts (parallel; preserves ordering by source array).
|
|
39
|
+
const parsedAll = await Promise.all(files.map((filePath) => parseConceptFile(filePath)));
|
|
40
|
+
const concepts = [];
|
|
41
|
+
for (const parsed of parsedAll) {
|
|
42
|
+
if (parsed.concept === null) {
|
|
43
|
+
result.errors.push({ filePath: parsed.filePath, errors: parsed.errors });
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
concepts.push({ concept: parsed.concept, filePath: parsed.filePath });
|
|
47
|
+
}
|
|
48
|
+
const idToFiles = new Map();
|
|
49
|
+
for (const { concept, filePath } of concepts) {
|
|
50
|
+
const list = idToFiles.get(concept.id) ?? [];
|
|
51
|
+
list.push(filePath);
|
|
52
|
+
idToFiles.set(concept.id, list);
|
|
53
|
+
}
|
|
54
|
+
for (const [id, fileList] of idToFiles) {
|
|
55
|
+
if (fileList.length > 1) {
|
|
56
|
+
result.duplicateIds.push({ id, files: fileList });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Duplicate ids stay in `knownIds` so related_concepts pointing at them still
|
|
60
|
+
// resolve (the duplicate is reported separately via `duplicateIds`).
|
|
61
|
+
const knownIds = new Set(idToFiles.keys());
|
|
62
|
+
await Promise.all(concepts.map(async ({ concept, filePath }) => {
|
|
63
|
+
for (const relatedId of concept.related_concepts) {
|
|
64
|
+
if (!knownIds.has(relatedId)) {
|
|
65
|
+
result.unresolvedRelated.push({
|
|
66
|
+
conceptId: concept.id,
|
|
67
|
+
missingRelatedId: relatedId,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const participantChecks = await Promise.all(concept.participants.map(async (participant) => {
|
|
72
|
+
const abs = resolveRelative(rootDir, participant.file);
|
|
73
|
+
try {
|
|
74
|
+
await stat(abs);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return participant.file;
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
for (const missing of participantChecks) {
|
|
82
|
+
if (missing !== null) {
|
|
83
|
+
result.missingFiles.push({ conceptId: concept.id, missingFile: missing });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
result.entries.push({
|
|
87
|
+
id: concept.id,
|
|
88
|
+
name: concept.name,
|
|
89
|
+
type: concept.type,
|
|
90
|
+
status: concept.status,
|
|
91
|
+
participants_paths: concept.participants.map((p) => normalizeForward(p.file)),
|
|
92
|
+
tags: concept.tags,
|
|
93
|
+
file: normalizeForward(filePath),
|
|
94
|
+
});
|
|
95
|
+
}));
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Persist the index to `.koncept/index.json` (gitignored cache).
|
|
100
|
+
*/
|
|
101
|
+
export async function writeIndex(rootDir, entries) {
|
|
102
|
+
const path = join(rootDir, INDEX_FILENAME);
|
|
103
|
+
await writeFile(path, JSON.stringify(entries, null, 2) + '\n', 'utf-8');
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* True if the IndexResult is fully clean (no errors of any kind).
|
|
107
|
+
*/
|
|
108
|
+
export function isIndexClean(result) {
|
|
109
|
+
return (result.errors.length === 0 &&
|
|
110
|
+
result.duplicateIds.length === 0 &&
|
|
111
|
+
result.unresolvedRelated.length === 0 &&
|
|
112
|
+
result.missingFiles.length === 0);
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=indexer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../src/indexer.ts"],"names":[],"mappings":"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"}
|
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML parser for koncepto concept files.
|
|
3
|
+
*
|
|
4
|
+
* Two-step pipeline:
|
|
5
|
+
* 1. YAML parse via `yaml` v2 (with size + alias-count guards) → captures syntax errors
|
|
6
|
+
* 2. Zod validate → captures schema mismatches
|
|
7
|
+
*
|
|
8
|
+
* Always returns a structured ParseResult; never throws.
|
|
9
|
+
*
|
|
10
|
+
* Security (OWASP A04 Insecure Design): explicit DoS guards on untrusted YAML.
|
|
11
|
+
* - MAX_YAML_BYTES caps payload size (rejects >100 KB before parse).
|
|
12
|
+
* - MAX_ALIAS_COUNT caps alias expansion (billion-laughs mitigation).
|
|
13
|
+
* yaml >= 2.8.3 fixes CVE-2026-33532 stack-overflow; these are defense-in-depth
|
|
14
|
+
* (concept YAML may originate from third-party PRs in OSS context).
|
|
15
|
+
*/
|
|
16
|
+
import { type Concept } from './schema.js';
|
|
17
|
+
export type ParseErrorType = 'yaml_syntax' | 'schema_validation' | 'io_error' | 'unknown';
|
|
18
|
+
export interface ParseError {
|
|
19
|
+
type: ParseErrorType;
|
|
20
|
+
message: string;
|
|
21
|
+
field?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ParseResult {
|
|
24
|
+
concept: Concept | null;
|
|
25
|
+
errors: ParseError[];
|
|
26
|
+
filePath: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse a YAML string in memory.
|
|
30
|
+
* @param yamlText raw YAML content
|
|
31
|
+
* @param filePath identifier (no fs read; just metadata for error context)
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseConceptString(yamlText: string, filePath: string): ParseResult;
|
|
34
|
+
/**
|
|
35
|
+
* Read + parse a YAML concept file from disk.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseConceptFile(filePath: string): Promise<ParseResult>;
|
|
38
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,aAAa,CAAA;AAKzD,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,mBAAmB,GAAG,UAAU,GAAG,SAAS,CAAA;AAEzF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,cAAc,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAgDlF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAiB7E"}
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML parser for koncepto concept files.
|
|
3
|
+
*
|
|
4
|
+
* Two-step pipeline:
|
|
5
|
+
* 1. YAML parse via `yaml` v2 (with size + alias-count guards) → captures syntax errors
|
|
6
|
+
* 2. Zod validate → captures schema mismatches
|
|
7
|
+
*
|
|
8
|
+
* Always returns a structured ParseResult; never throws.
|
|
9
|
+
*
|
|
10
|
+
* Security (OWASP A04 Insecure Design): explicit DoS guards on untrusted YAML.
|
|
11
|
+
* - MAX_YAML_BYTES caps payload size (rejects >100 KB before parse).
|
|
12
|
+
* - MAX_ALIAS_COUNT caps alias expansion (billion-laughs mitigation).
|
|
13
|
+
* yaml >= 2.8.3 fixes CVE-2026-33532 stack-overflow; these are defense-in-depth
|
|
14
|
+
* (concept YAML may originate from third-party PRs in OSS context).
|
|
15
|
+
*/
|
|
16
|
+
import { readFile } from 'node:fs/promises';
|
|
17
|
+
import YAML from 'yaml';
|
|
18
|
+
import { ConceptSchema } from './schema.js';
|
|
19
|
+
const MAX_YAML_BYTES = 100 * 1024; // 100 KB — concepts are small structured docs
|
|
20
|
+
const MAX_ALIAS_COUNT = 50; // explicit billion-laughs / alias-bomb guard
|
|
21
|
+
/**
|
|
22
|
+
* Parse a YAML string in memory.
|
|
23
|
+
* @param yamlText raw YAML content
|
|
24
|
+
* @param filePath identifier (no fs read; just metadata for error context)
|
|
25
|
+
*/
|
|
26
|
+
export function parseConceptString(yamlText, filePath) {
|
|
27
|
+
// DoS guard 1: byte-size limit (UTF-8) before any parsing work. `string.length`
|
|
28
|
+
// returns UTF-16 code units, which under-counts multi-byte content.
|
|
29
|
+
const byteLength = Buffer.byteLength(yamlText, 'utf8');
|
|
30
|
+
if (byteLength > MAX_YAML_BYTES) {
|
|
31
|
+
return {
|
|
32
|
+
concept: null,
|
|
33
|
+
errors: [
|
|
34
|
+
{
|
|
35
|
+
type: 'yaml_syntax',
|
|
36
|
+
message: `YAML exceeds size limit (${byteLength} > ${MAX_YAML_BYTES} bytes)`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
filePath,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
let raw;
|
|
43
|
+
try {
|
|
44
|
+
// DoS guard 2: cap alias expansion (billion-laughs mitigation).
|
|
45
|
+
raw = YAML.parse(yamlText, { maxAliasCount: MAX_ALIAS_COUNT });
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
return {
|
|
49
|
+
concept: null,
|
|
50
|
+
errors: [
|
|
51
|
+
{
|
|
52
|
+
type: 'yaml_syntax',
|
|
53
|
+
message: err instanceof Error ? err.message : String(err),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
filePath,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const result = ConceptSchema.safeParse(raw);
|
|
60
|
+
if (!result.success) {
|
|
61
|
+
return {
|
|
62
|
+
concept: null,
|
|
63
|
+
errors: result.error.issues.map((issue) => ({
|
|
64
|
+
type: 'schema_validation',
|
|
65
|
+
message: issue.message,
|
|
66
|
+
field: issue.path.length > 0 ? issue.path.join('.') : undefined,
|
|
67
|
+
})),
|
|
68
|
+
filePath,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return { concept: result.data, errors: [], filePath };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Read + parse a YAML concept file from disk.
|
|
75
|
+
*/
|
|
76
|
+
export async function parseConceptFile(filePath) {
|
|
77
|
+
let text;
|
|
78
|
+
try {
|
|
79
|
+
text = await readFile(filePath, 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
return {
|
|
83
|
+
concept: null,
|
|
84
|
+
errors: [
|
|
85
|
+
{
|
|
86
|
+
type: 'io_error',
|
|
87
|
+
message: err instanceof Error ? err.message : String(err),
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
filePath,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return parseConceptString(text, filePath);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAA;AAEzD,MAAM,cAAc,GAAG,GAAG,GAAG,IAAI,CAAA,CAAC,8CAA8C;AAChF,MAAM,eAAe,GAAG,EAAE,CAAA,CAAC,6CAA6C;AAgBxE;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,QAAgB;IACnE,gFAAgF;IAChF,oEAAoE;IACpE,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IACtD,IAAI,UAAU,GAAG,cAAc,EAAE,CAAC;QAChC,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE;gBACN;oBACE,IAAI,EAAE,aAAa;oBACnB,OAAO,EAAE,4BAA4B,UAAU,MAAM,cAAc,SAAS;iBAC7E;aACF;YACD,QAAQ;SACT,CAAA;IACH,CAAC;IAED,IAAI,GAAY,CAAA;IAChB,IAAI,CAAC;QACH,gEAAgE;QAChE,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,eAAe,EAAE,CAAC,CAAA;IAChE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE;gBACN;oBACE,IAAI,EAAE,aAAa;oBACnB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBAC1D;aACF;YACD,QAAQ;SACT,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC1C,IAAI,EAAE,mBAA4B;gBAClC,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;aAChE,CAAC,CAAC;YACH,QAAQ;SACT,CAAA;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAA;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAAgB;IACrD,IAAI,IAAY,CAAA;IAChB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE;gBACN;oBACE,IAAI,EAAE,UAAU;oBAChB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBAC1D;aACF;YACD,QAAQ;SACT,CAAA;IACH,CAAC;IACD,OAAO,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;AAC3C,CAAC"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform path normalization for koncepto.
|
|
3
|
+
*
|
|
4
|
+
* YAML files store participant paths as forward-slash relative paths
|
|
5
|
+
* (Unix-style). On Windows the filesystem returns backslashes; we normalize
|
|
6
|
+
* everywhere we read/write paths so equality + globs are consistent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Convert all backslashes to forward slashes.
|
|
10
|
+
* Works on both Windows (`C:\foo\bar` → `C:/foo/bar`) and Unix (no-op).
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeForward(p: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a (possibly relative) path against a root, then return a forward-slash
|
|
15
|
+
* normalized absolute path.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveRelative(rootDir: string, relative: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Cross-platform basename. Stripped of extension if `stripExt` is true.
|
|
20
|
+
*/
|
|
21
|
+
export declare function basename(p: string, stripExt?: boolean): string;
|
|
22
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,MAAM,CAK5D"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform path normalization for koncepto.
|
|
3
|
+
*
|
|
4
|
+
* YAML files store participant paths as forward-slash relative paths
|
|
5
|
+
* (Unix-style). On Windows the filesystem returns backslashes; we normalize
|
|
6
|
+
* everywhere we read/write paths so equality + globs are consistent.
|
|
7
|
+
*/
|
|
8
|
+
import { resolve, basename as nodeBasename, isAbsolute } from 'node:path';
|
|
9
|
+
/**
|
|
10
|
+
* Convert all backslashes to forward slashes.
|
|
11
|
+
* Works on both Windows (`C:\foo\bar` → `C:/foo/bar`) and Unix (no-op).
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeForward(p) {
|
|
14
|
+
return p.replace(/\\/g, '/');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a (possibly relative) path against a root, then return a forward-slash
|
|
18
|
+
* normalized absolute path.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveRelative(rootDir, relative) {
|
|
21
|
+
const absolute = isAbsolute(relative) ? relative : resolve(rootDir, relative);
|
|
22
|
+
return normalizeForward(absolute);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Cross-platform basename. Stripped of extension if `stripExt` is true.
|
|
26
|
+
*/
|
|
27
|
+
export function basename(p, stripExt = false) {
|
|
28
|
+
const name = nodeBasename(normalizeForward(p));
|
|
29
|
+
if (!stripExt)
|
|
30
|
+
return name;
|
|
31
|
+
const dot = name.lastIndexOf('.');
|
|
32
|
+
return dot > 0 ? name.slice(0, dot) : name;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEzE;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAS;IACxC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,QAAgB;IAC/D,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IAC7E,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAA;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,CAAS,EAAE,QAAQ,GAAG,KAAK;IAClD,MAAM,IAAI,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9C,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IACjC,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AAC5C,CAAC"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* koncepto schema — Zod definitions for the YAML concept format.
|
|
3
|
+
*
|
|
4
|
+
* One schema = (1) runtime validator, (2) TypeScript types via z.infer.
|
|
5
|
+
* See spec § "Schema v0.1" for semantic intent.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
/**
|
|
9
|
+
* Kebab-case strict regex for ids (concept id, invariant id, related_concepts).
|
|
10
|
+
* Lowercase first char, then [a-z0-9-]. No leading/trailing hyphen, no consecutive
|
|
11
|
+
* separators enforced softly via ^[a-z] start.
|
|
12
|
+
*/
|
|
13
|
+
export declare const KEBAB_ID_REGEX: RegExp;
|
|
14
|
+
export declare const RoleEnum: z.ZodEnum<{
|
|
15
|
+
writer: "writer";
|
|
16
|
+
reader: "reader";
|
|
17
|
+
tester: "tester";
|
|
18
|
+
docs: "docs";
|
|
19
|
+
}>;
|
|
20
|
+
export type Role = z.infer<typeof RoleEnum>;
|
|
21
|
+
export declare const ConceptTypeEnum: z.ZodEnum<{
|
|
22
|
+
"behavioral-invariant": "behavioral-invariant";
|
|
23
|
+
"architectural-decision": "architectural-decision";
|
|
24
|
+
"data-flow": "data-flow";
|
|
25
|
+
"ui-pattern": "ui-pattern";
|
|
26
|
+
"naming-convention": "naming-convention";
|
|
27
|
+
}>;
|
|
28
|
+
export type ConceptType = z.infer<typeof ConceptTypeEnum>;
|
|
29
|
+
export declare const SeverityEnum: z.ZodEnum<{
|
|
30
|
+
high: "high";
|
|
31
|
+
medium: "medium";
|
|
32
|
+
low: "low";
|
|
33
|
+
}>;
|
|
34
|
+
export type Severity = z.infer<typeof SeverityEnum>;
|
|
35
|
+
export declare const StatusEnum: z.ZodEnum<{
|
|
36
|
+
active: "active";
|
|
37
|
+
deprecated: "deprecated";
|
|
38
|
+
superseded: "superseded";
|
|
39
|
+
}>;
|
|
40
|
+
export type Status = z.infer<typeof StatusEnum>;
|
|
41
|
+
export declare const SourceOfTruthSchema: z.ZodObject<{
|
|
42
|
+
symbol: z.ZodOptional<z.ZodString>;
|
|
43
|
+
file: z.ZodString;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
export type SourceOfTruth = z.infer<typeof SourceOfTruthSchema>;
|
|
46
|
+
export declare const ParticipantSchema: z.ZodObject<{
|
|
47
|
+
file: z.ZodString;
|
|
48
|
+
role: z.ZodEnum<{
|
|
49
|
+
writer: "writer";
|
|
50
|
+
reader: "reader";
|
|
51
|
+
tester: "tester";
|
|
52
|
+
docs: "docs";
|
|
53
|
+
}>;
|
|
54
|
+
purpose: z.ZodString;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
export type Participant = z.infer<typeof ParticipantSchema>;
|
|
57
|
+
export declare const InvariantSchema: z.ZodObject<{
|
|
58
|
+
id: z.ZodString;
|
|
59
|
+
description: z.ZodString;
|
|
60
|
+
severity: z.ZodEnum<{
|
|
61
|
+
high: "high";
|
|
62
|
+
medium: "medium";
|
|
63
|
+
low: "low";
|
|
64
|
+
}>;
|
|
65
|
+
automated_check: z.ZodDefault<z.ZodBoolean>;
|
|
66
|
+
}, z.core.$strip>;
|
|
67
|
+
export type Invariant = z.infer<typeof InvariantSchema>;
|
|
68
|
+
export declare const ConceptSchema: z.ZodObject<{
|
|
69
|
+
id: z.ZodString;
|
|
70
|
+
name: z.ZodString;
|
|
71
|
+
type: z.ZodEnum<{
|
|
72
|
+
"behavioral-invariant": "behavioral-invariant";
|
|
73
|
+
"architectural-decision": "architectural-decision";
|
|
74
|
+
"data-flow": "data-flow";
|
|
75
|
+
"ui-pattern": "ui-pattern";
|
|
76
|
+
"naming-convention": "naming-convention";
|
|
77
|
+
}>;
|
|
78
|
+
status: z.ZodDefault<z.ZodEnum<{
|
|
79
|
+
active: "active";
|
|
80
|
+
deprecated: "deprecated";
|
|
81
|
+
superseded: "superseded";
|
|
82
|
+
}>>;
|
|
83
|
+
description: z.ZodString;
|
|
84
|
+
source_of_truth: z.ZodObject<{
|
|
85
|
+
symbol: z.ZodOptional<z.ZodString>;
|
|
86
|
+
file: z.ZodString;
|
|
87
|
+
}, z.core.$strip>;
|
|
88
|
+
participants: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
89
|
+
file: z.ZodString;
|
|
90
|
+
role: z.ZodEnum<{
|
|
91
|
+
writer: "writer";
|
|
92
|
+
reader: "reader";
|
|
93
|
+
tester: "tester";
|
|
94
|
+
docs: "docs";
|
|
95
|
+
}>;
|
|
96
|
+
purpose: z.ZodString;
|
|
97
|
+
}, z.core.$strip>>>;
|
|
98
|
+
invariants: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
99
|
+
id: z.ZodString;
|
|
100
|
+
description: z.ZodString;
|
|
101
|
+
severity: z.ZodEnum<{
|
|
102
|
+
high: "high";
|
|
103
|
+
medium: "medium";
|
|
104
|
+
low: "low";
|
|
105
|
+
}>;
|
|
106
|
+
automated_check: z.ZodDefault<z.ZodBoolean>;
|
|
107
|
+
}, z.core.$strip>>>;
|
|
108
|
+
risks_if_broken: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
109
|
+
related_concepts: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
110
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
111
|
+
created: z.ZodString;
|
|
112
|
+
last_updated: z.ZodString;
|
|
113
|
+
captured_by: z.ZodOptional<z.ZodString>;
|
|
114
|
+
references: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
115
|
+
}, z.core.$strip>;
|
|
116
|
+
export type Concept = z.infer<typeof ConceptSchema>;
|
|
117
|
+
export declare const IndexEntrySchema: z.ZodObject<{
|
|
118
|
+
id: z.ZodString;
|
|
119
|
+
name: z.ZodString;
|
|
120
|
+
type: z.ZodEnum<{
|
|
121
|
+
"behavioral-invariant": "behavioral-invariant";
|
|
122
|
+
"architectural-decision": "architectural-decision";
|
|
123
|
+
"data-flow": "data-flow";
|
|
124
|
+
"ui-pattern": "ui-pattern";
|
|
125
|
+
"naming-convention": "naming-convention";
|
|
126
|
+
}>;
|
|
127
|
+
status: z.ZodEnum<{
|
|
128
|
+
active: "active";
|
|
129
|
+
deprecated: "deprecated";
|
|
130
|
+
superseded: "superseded";
|
|
131
|
+
}>;
|
|
132
|
+
participants_paths: z.ZodArray<z.ZodString>;
|
|
133
|
+
tags: z.ZodArray<z.ZodString>;
|
|
134
|
+
file: z.ZodString;
|
|
135
|
+
}, z.core.$strip>;
|
|
136
|
+
export type IndexEntry = z.infer<typeof IndexEntrySchema>;
|
|
137
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAsB,CAAA;AAQjD,eAAO,MAAM,QAAQ;;;;;EAAiD,CAAA;AACtE,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAA;AAE3C,eAAO,MAAM,eAAe;;;;;;EAM1B,CAAA;AACF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAEzD,eAAO,MAAM,YAAY;;;;EAAoC,CAAA;AAC7D,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAEnD,eAAO,MAAM,UAAU;;;;EAAiD,CAAA;AACxE,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAA;AAI/C,eAAO,MAAM,mBAAmB;;;iBAG9B,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE/D,eAAO,MAAM,iBAAiB;;;;;;;;;iBAI5B,CAAA;AACF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAE3D,eAAO,MAAM,eAAe;;;;;;;;;iBAK1B,CAAA;AACF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAIvD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoBxB,CAAA;AACF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAA;AAInD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;iBAQ3B,CAAA;AACF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* koncepto schema — Zod definitions for the YAML concept format.
|
|
3
|
+
*
|
|
4
|
+
* One schema = (1) runtime validator, (2) TypeScript types via z.infer.
|
|
5
|
+
* See spec § "Schema v0.1" for semantic intent.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
// ─── Identifiers ─────────────────────────────────────────────────────────────
|
|
9
|
+
/**
|
|
10
|
+
* Kebab-case strict regex for ids (concept id, invariant id, related_concepts).
|
|
11
|
+
* Lowercase first char, then [a-z0-9-]. No leading/trailing hyphen, no consecutive
|
|
12
|
+
* separators enforced softly via ^[a-z] start.
|
|
13
|
+
*/
|
|
14
|
+
export const KEBAB_ID_REGEX = /^[a-z][a-z0-9-]+$/;
|
|
15
|
+
const KebabId = z.string().regex(KEBAB_ID_REGEX, {
|
|
16
|
+
message: 'must be kebab-case (lowercase, [a-z0-9-]+, starts with letter)',
|
|
17
|
+
});
|
|
18
|
+
// ─── Enums ───────────────────────────────────────────────────────────────────
|
|
19
|
+
export const RoleEnum = z.enum(['writer', 'reader', 'tester', 'docs']);
|
|
20
|
+
export const ConceptTypeEnum = z.enum([
|
|
21
|
+
'behavioral-invariant',
|
|
22
|
+
'architectural-decision',
|
|
23
|
+
'data-flow',
|
|
24
|
+
'ui-pattern',
|
|
25
|
+
'naming-convention',
|
|
26
|
+
]);
|
|
27
|
+
export const SeverityEnum = z.enum(['high', 'medium', 'low']);
|
|
28
|
+
export const StatusEnum = z.enum(['active', 'deprecated', 'superseded']);
|
|
29
|
+
// ─── Sub-objects ─────────────────────────────────────────────────────────────
|
|
30
|
+
export const SourceOfTruthSchema = z.object({
|
|
31
|
+
symbol: z.string().min(1).optional(),
|
|
32
|
+
file: z.string().min(1),
|
|
33
|
+
});
|
|
34
|
+
export const ParticipantSchema = z.object({
|
|
35
|
+
file: z.string().min(1),
|
|
36
|
+
role: RoleEnum,
|
|
37
|
+
purpose: z.string().min(1),
|
|
38
|
+
});
|
|
39
|
+
export const InvariantSchema = z.object({
|
|
40
|
+
id: KebabId,
|
|
41
|
+
description: z.string().min(1),
|
|
42
|
+
severity: SeverityEnum,
|
|
43
|
+
automated_check: z.boolean().default(false),
|
|
44
|
+
});
|
|
45
|
+
// ─── Concept (top-level) ─────────────────────────────────────────────────────
|
|
46
|
+
export const ConceptSchema = z.object({
|
|
47
|
+
id: KebabId,
|
|
48
|
+
name: z.string().min(1),
|
|
49
|
+
type: ConceptTypeEnum,
|
|
50
|
+
status: StatusEnum.default('active'),
|
|
51
|
+
description: z.string().min(1),
|
|
52
|
+
source_of_truth: SourceOfTruthSchema,
|
|
53
|
+
participants: z.array(ParticipantSchema).default([]),
|
|
54
|
+
invariants: z.array(InvariantSchema).default([]),
|
|
55
|
+
risks_if_broken: z.array(z.string().min(1)).default([]),
|
|
56
|
+
related_concepts: z.array(KebabId).default([]),
|
|
57
|
+
tags: z.array(z.string().min(1)).default([]),
|
|
58
|
+
// Provenance
|
|
59
|
+
created: z.string().min(1),
|
|
60
|
+
last_updated: z.string().min(1),
|
|
61
|
+
captured_by: z.string().optional(),
|
|
62
|
+
references: z.array(z.string().min(1)).default([]),
|
|
63
|
+
});
|
|
64
|
+
// ─── Index entry (denormalized for fast lookup) ──────────────────────────────
|
|
65
|
+
export const IndexEntrySchema = z.object({
|
|
66
|
+
id: KebabId,
|
|
67
|
+
name: z.string().min(1),
|
|
68
|
+
type: ConceptTypeEnum,
|
|
69
|
+
status: StatusEnum,
|
|
70
|
+
participants_paths: z.array(z.string().min(1)),
|
|
71
|
+
tags: z.array(z.string().min(1)),
|
|
72
|
+
file: z.string().min(1),
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/search.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy search over IndexEntry list.
|
|
3
|
+
*
|
|
4
|
+
* Score model (0-1, higher = better match):
|
|
5
|
+
* 1.0 exact id match
|
|
6
|
+
* 0.9 exact tag match
|
|
7
|
+
* 0.7 case-insensitive substring in id
|
|
8
|
+
* 0.6 case-insensitive substring in name
|
|
9
|
+
* 0.5 case-insensitive substring in tag
|
|
10
|
+
*
|
|
11
|
+
* Exact matches return early; fuzzy substring scores accumulate.
|
|
12
|
+
*/
|
|
13
|
+
import type { IndexEntry } from './schema.js';
|
|
14
|
+
export type MatchField = 'id' | 'name' | 'tags';
|
|
15
|
+
export interface SearchHit {
|
|
16
|
+
entry: IndexEntry;
|
|
17
|
+
score: number;
|
|
18
|
+
matchedOn: MatchField[];
|
|
19
|
+
}
|
|
20
|
+
export declare function searchEntries(entries: IndexEntry[], query: string, limit?: number): SearchHit[];
|
|
21
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAE/C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,UAAU,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,UAAU,EAAE,CAAA;CACxB;AAiED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,UAAU,EAAE,EACrB,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAsB,GAC5B,SAAS,EAAE,CAYb"}
|
package/dist/search.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy search over IndexEntry list.
|
|
3
|
+
*
|
|
4
|
+
* Score model (0-1, higher = better match):
|
|
5
|
+
* 1.0 exact id match
|
|
6
|
+
* 0.9 exact tag match
|
|
7
|
+
* 0.7 case-insensitive substring in id
|
|
8
|
+
* 0.6 case-insensitive substring in name
|
|
9
|
+
* 0.5 case-insensitive substring in tag
|
|
10
|
+
*
|
|
11
|
+
* Exact matches return early; fuzzy substring scores accumulate.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_LIMIT = 10;
|
|
14
|
+
// Memoize lowercased projections per entries-array reference. Avoids re-allocating
|
|
15
|
+
// O(N×tags) strings on every search call when the same index is queried repeatedly.
|
|
16
|
+
const loweredCache = new WeakMap();
|
|
17
|
+
function getLowered(entries) {
|
|
18
|
+
const cached = loweredCache.get(entries);
|
|
19
|
+
if (cached)
|
|
20
|
+
return cached;
|
|
21
|
+
const projected = entries.map((entry) => ({
|
|
22
|
+
entry,
|
|
23
|
+
idLower: entry.id.toLowerCase(),
|
|
24
|
+
nameLower: entry.name.toLowerCase(),
|
|
25
|
+
tagsLower: entry.tags.map((t) => t.toLowerCase()),
|
|
26
|
+
}));
|
|
27
|
+
loweredCache.set(entries, projected);
|
|
28
|
+
return projected;
|
|
29
|
+
}
|
|
30
|
+
function scoreEntry(lowered, q) {
|
|
31
|
+
const { entry, idLower, nameLower, tagsLower } = lowered;
|
|
32
|
+
if (idLower === q) {
|
|
33
|
+
return { entry, score: 1.0, matchedOn: ['id'] };
|
|
34
|
+
}
|
|
35
|
+
if (tagsLower.includes(q)) {
|
|
36
|
+
return { entry, score: 0.9, matchedOn: ['tags'] };
|
|
37
|
+
}
|
|
38
|
+
let score = 0;
|
|
39
|
+
const matchedOn = [];
|
|
40
|
+
if (idLower.includes(q)) {
|
|
41
|
+
score += 0.7;
|
|
42
|
+
matchedOn.push('id');
|
|
43
|
+
}
|
|
44
|
+
if (nameLower.includes(q)) {
|
|
45
|
+
score += 0.6;
|
|
46
|
+
matchedOn.push('name');
|
|
47
|
+
}
|
|
48
|
+
// Single pass over tags: partial match contributes once.
|
|
49
|
+
let tagPartial = false;
|
|
50
|
+
for (const t of tagsLower) {
|
|
51
|
+
if (t.includes(q)) {
|
|
52
|
+
tagPartial = true;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (tagPartial) {
|
|
57
|
+
score += 0.5;
|
|
58
|
+
matchedOn.push('tags');
|
|
59
|
+
}
|
|
60
|
+
return score > 0 ? { entry, score, matchedOn } : null;
|
|
61
|
+
}
|
|
62
|
+
export function searchEntries(entries, query, limit = DEFAULT_LIMIT) {
|
|
63
|
+
const q = query.trim().toLowerCase();
|
|
64
|
+
if (q.length === 0)
|
|
65
|
+
return [];
|
|
66
|
+
const lowered = getLowered(entries);
|
|
67
|
+
const hits = [];
|
|
68
|
+
for (const item of lowered) {
|
|
69
|
+
const hit = scoreEntry(item, q);
|
|
70
|
+
if (hit)
|
|
71
|
+
hits.push(hit);
|
|
72
|
+
}
|
|
73
|
+
hits.sort((a, b) => b.score - a.score);
|
|
74
|
+
return hits.slice(0, limit);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yourtechtribe-labs/koncept-core",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "Core schema, parser, and indexer for koncepto semantic concept graphs.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"glob": "13.0.6",
|
|
20
|
+
"yaml": "2.8.4",
|
|
21
|
+
"zod": "4.4.2"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public",
|
|
25
|
+
"tag": "alpha"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"homepage": "https://github.com/yourtechtribe-labs/koncept-mcp",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/yourtechtribe-labs/koncept-mcp",
|
|
32
|
+
"directory": "packages/koncept-core"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"lint": "echo 'lint stub'",
|
|
38
|
+
"clean": "rm -rf dist"
|
|
39
|
+
}
|
|
40
|
+
}
|