@forwardimpact/map 0.11.1 → 0.13.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 +1 -1
- package/bin/fit-map.js +91 -34
- package/package.json +14 -4
- package/schema/json/capability.schema.json +33 -0
- package/schema/json/discipline.schema.json +2 -6
- package/schema/rdf/capability.ttl +48 -0
- package/schema/rdf/discipline.ttl +6 -19
- package/src/index-generator.js +67 -38
- package/src/index.js +10 -25
- package/src/loader.js +407 -559
- package/src/schema-validation.js +327 -307
- package/src/validation.js +54 -0
- package/examples/behaviours/_index.yaml +0 -8
- package/examples/behaviours/outcome_ownership.yaml +0 -43
- package/examples/behaviours/polymathic_knowledge.yaml +0 -41
- package/examples/behaviours/precise_communication.yaml +0 -39
- package/examples/behaviours/relentless_curiosity.yaml +0 -37
- package/examples/behaviours/systems_thinking.yaml +0 -40
- package/examples/capabilities/_index.yaml +0 -8
- package/examples/capabilities/business.yaml +0 -205
- package/examples/capabilities/delivery.yaml +0 -1001
- package/examples/capabilities/people.yaml +0 -68
- package/examples/capabilities/reliability.yaml +0 -349
- package/examples/capabilities/scale.yaml +0 -1672
- package/examples/copilot-setup-steps.yaml +0 -25
- package/examples/devcontainer.yaml +0 -21
- package/examples/disciplines/_index.yaml +0 -6
- package/examples/disciplines/data_engineering.yaml +0 -68
- package/examples/disciplines/engineering_management.yaml +0 -61
- package/examples/disciplines/software_engineering.yaml +0 -68
- package/examples/drivers.yaml +0 -202
- package/examples/framework.yaml +0 -73
- package/examples/levels.yaml +0 -115
- package/examples/questions/behaviours/outcome_ownership.yaml +0 -228
- package/examples/questions/behaviours/polymathic_knowledge.yaml +0 -275
- package/examples/questions/behaviours/precise_communication.yaml +0 -248
- package/examples/questions/behaviours/relentless_curiosity.yaml +0 -248
- package/examples/questions/behaviours/systems_thinking.yaml +0 -238
- package/examples/questions/capabilities/business.yaml +0 -107
- package/examples/questions/capabilities/delivery.yaml +0 -101
- package/examples/questions/capabilities/people.yaml +0 -106
- package/examples/questions/capabilities/reliability.yaml +0 -105
- package/examples/questions/capabilities/scale.yaml +0 -104
- package/examples/questions/skills/architecture_design.yaml +0 -115
- package/examples/questions/skills/cloud_platforms.yaml +0 -105
- package/examples/questions/skills/code_quality.yaml +0 -162
- package/examples/questions/skills/data_modeling.yaml +0 -107
- package/examples/questions/skills/devops.yaml +0 -111
- package/examples/questions/skills/full_stack_development.yaml +0 -118
- package/examples/questions/skills/sre_practices.yaml +0 -113
- package/examples/questions/skills/stakeholder_management.yaml +0 -116
- package/examples/questions/skills/team_collaboration.yaml +0 -106
- package/examples/questions/skills/technical_writing.yaml +0 -110
- package/examples/self-assessments.yaml +0 -64
- package/examples/stages.yaml +0 -191
- package/examples/tracks/_index.yaml +0 -5
- package/examples/tracks/platform.yaml +0 -47
- package/examples/tracks/sre.yaml +0 -46
- package/examples/vscode-settings.yaml +0 -21
package/README.md
CHANGED
package/bin/fit-map.js
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* fit-map CLI
|
|
5
5
|
*
|
|
6
|
-
* Map validation
|
|
6
|
+
* Map validation, index generation, and activity management for Engineering Pathway data.
|
|
7
7
|
*
|
|
8
8
|
* Commands:
|
|
9
9
|
* validate [--json|--shacl] Run validation (default: --json)
|
|
10
10
|
* generate-index [--data=PATH] Generate _index.yaml files
|
|
11
|
+
* people import <file> Import people from CSV/YAML
|
|
11
12
|
* --help Show help
|
|
12
13
|
*/
|
|
13
14
|
|
|
@@ -22,22 +23,29 @@ const __dirname = dirname(__filename);
|
|
|
22
23
|
* Parse CLI arguments
|
|
23
24
|
*/
|
|
24
25
|
function parseArgs(args) {
|
|
25
|
-
const command = args[0];
|
|
26
26
|
const options = {};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (arg.startsWith("--
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const positional = [];
|
|
28
|
+
|
|
29
|
+
for (const arg of args) {
|
|
30
|
+
if (arg.startsWith("--")) {
|
|
31
|
+
if (arg === "--help") {
|
|
32
|
+
options.help = true;
|
|
33
|
+
} else {
|
|
34
|
+
const [key, value] = arg.slice(2).split("=");
|
|
35
|
+
options[key] = value ?? true;
|
|
36
|
+
}
|
|
37
|
+
} else if (arg === "-h") {
|
|
33
38
|
options.help = true;
|
|
34
|
-
} else
|
|
35
|
-
|
|
36
|
-
options[key] = value ?? true;
|
|
39
|
+
} else {
|
|
40
|
+
positional.push(arg);
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
const command = positional[0] || null;
|
|
45
|
+
const subcommand = positional[1] || null;
|
|
46
|
+
const rest = positional.slice(2);
|
|
47
|
+
|
|
48
|
+
return { command, subcommand, options, positional: rest };
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
/**
|
|
@@ -64,11 +72,11 @@ async function findDataDir(providedPath) {
|
|
|
64
72
|
throw new Error(`Data directory not found: ${providedPath}`);
|
|
65
73
|
}
|
|
66
74
|
|
|
67
|
-
// Check common locations
|
|
68
75
|
const candidates = [
|
|
76
|
+
join(process.cwd(), "data/pathway"),
|
|
77
|
+
join(process.cwd(), "examples/pathway"),
|
|
69
78
|
join(process.cwd(), "data"),
|
|
70
79
|
join(process.cwd(), "examples"),
|
|
71
|
-
join(__dirname, "../examples"),
|
|
72
80
|
];
|
|
73
81
|
|
|
74
82
|
for (const candidate of candidates) {
|
|
@@ -116,17 +124,17 @@ function formatValidationResults(result) {
|
|
|
116
124
|
async function runValidate(dataDir) {
|
|
117
125
|
console.log(`🔍 Validating data in: ${dataDir}\n`);
|
|
118
126
|
|
|
119
|
-
const {
|
|
127
|
+
const { createDataLoader, createSchemaValidator } =
|
|
128
|
+
await import("../src/index.js");
|
|
120
129
|
|
|
121
|
-
|
|
122
|
-
const
|
|
130
|
+
const loader = createDataLoader();
|
|
131
|
+
const validator = createSchemaValidator();
|
|
123
132
|
|
|
124
|
-
|
|
125
|
-
const result = await
|
|
133
|
+
const data = await loader.loadAllData(dataDir);
|
|
134
|
+
const result = await validator.runFullValidation(dataDir, data);
|
|
126
135
|
|
|
127
136
|
console.log(formatValidationResults(result));
|
|
128
137
|
|
|
129
|
-
// Print summary
|
|
130
138
|
console.log("\n📊 Data Summary:");
|
|
131
139
|
console.log(` Skills: ${data.skills?.length || 0}`);
|
|
132
140
|
console.log(` Behaviours: ${data.behaviours?.length || 0}`);
|
|
@@ -144,9 +152,10 @@ async function runValidate(dataDir) {
|
|
|
144
152
|
async function runGenerateIndex(dataDir) {
|
|
145
153
|
console.log(`📁 Generating index files in: ${dataDir}\n`);
|
|
146
154
|
|
|
147
|
-
const {
|
|
155
|
+
const { createIndexGenerator } = await import("../src/index.js");
|
|
148
156
|
|
|
149
|
-
const
|
|
157
|
+
const generator = createIndexGenerator();
|
|
158
|
+
const results = await generator.generateAllIndexes(dataDir);
|
|
150
159
|
|
|
151
160
|
for (const [dir, files] of Object.entries(results)) {
|
|
152
161
|
if (files.error) {
|
|
@@ -171,7 +180,6 @@ async function runValidateShacl() {
|
|
|
171
180
|
const { default: N3 } = await import("n3");
|
|
172
181
|
const { readFile, readdir } = await import("fs/promises");
|
|
173
182
|
|
|
174
|
-
// Find all .ttl files in the RDF directory
|
|
175
183
|
const files = await readdir(rdfDir);
|
|
176
184
|
const ttlFiles = files.filter((f) => f.endsWith(".ttl")).sort();
|
|
177
185
|
|
|
@@ -214,31 +222,62 @@ async function runValidateShacl() {
|
|
|
214
222
|
}
|
|
215
223
|
}
|
|
216
224
|
|
|
225
|
+
/**
|
|
226
|
+
* People import command
|
|
227
|
+
*/
|
|
228
|
+
async function runPeopleImport(filePath, dataDir) {
|
|
229
|
+
console.log(`👤 Importing people from: ${filePath}\n`);
|
|
230
|
+
|
|
231
|
+
const { loadPeopleFile, validatePeople } =
|
|
232
|
+
await import("../activity/transform/people.js");
|
|
233
|
+
|
|
234
|
+
const people = await loadPeopleFile(filePath);
|
|
235
|
+
console.log(` Loaded ${people.length} people from file`);
|
|
236
|
+
|
|
237
|
+
const { valid, errors } = await validatePeople(people, dataDir);
|
|
238
|
+
|
|
239
|
+
if (errors.length > 0) {
|
|
240
|
+
console.log(`\n❌ Validation errors:`);
|
|
241
|
+
for (const err of errors) {
|
|
242
|
+
console.log(` • Row ${err.row}: ${err.message}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
console.log(`\n✅ ${valid.length} people validated`);
|
|
247
|
+
if (errors.length > 0) {
|
|
248
|
+
console.log(`❌ ${errors.length} rows with errors\n`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return errors.length > 0 ? 1 : 0;
|
|
252
|
+
}
|
|
253
|
+
|
|
217
254
|
/**
|
|
218
255
|
* Show help
|
|
219
256
|
*/
|
|
220
257
|
function showHelp() {
|
|
221
258
|
console.log(`
|
|
222
|
-
fit-map - Data validation for Engineering Pathway
|
|
259
|
+
fit-map - Data validation and management for Engineering Pathway
|
|
223
260
|
|
|
224
261
|
Usage:
|
|
225
262
|
fit-map <command> [options]
|
|
226
263
|
|
|
227
264
|
Commands:
|
|
228
|
-
validate
|
|
229
|
-
generate-index
|
|
265
|
+
validate Run validation (default: JSON schema validation)
|
|
266
|
+
generate-index Generate _index.yaml files for directories
|
|
267
|
+
people import <file> Import people from CSV/YAML (validates against framework)
|
|
230
268
|
|
|
231
269
|
Options:
|
|
232
|
-
--json
|
|
233
|
-
--shacl
|
|
234
|
-
--data=PATH
|
|
235
|
-
--help, -h
|
|
270
|
+
--json JSON schema + referential validation (default)
|
|
271
|
+
--shacl SHACL schema syntax validation
|
|
272
|
+
--data=PATH Path to data directory (default: ./data or ./examples)
|
|
273
|
+
--help, -h Show this help message
|
|
236
274
|
|
|
237
275
|
Examples:
|
|
238
276
|
fit-map validate
|
|
239
277
|
fit-map validate --shacl
|
|
240
278
|
fit-map validate --data=./my-data
|
|
241
279
|
fit-map generate-index
|
|
280
|
+
fit-map people import ./org/people.yaml
|
|
242
281
|
`);
|
|
243
282
|
}
|
|
244
283
|
|
|
@@ -246,11 +285,13 @@ Examples:
|
|
|
246
285
|
* Main entry point
|
|
247
286
|
*/
|
|
248
287
|
async function main() {
|
|
249
|
-
const { command, options } = parseArgs(
|
|
288
|
+
const { command, subcommand, options, positional } = parseArgs(
|
|
289
|
+
process.argv.slice(2),
|
|
290
|
+
);
|
|
250
291
|
|
|
251
292
|
if (options.help || !command) {
|
|
252
293
|
showHelp();
|
|
253
|
-
process.exit(
|
|
294
|
+
process.exit(options.help ? 0 : 1);
|
|
254
295
|
}
|
|
255
296
|
|
|
256
297
|
try {
|
|
@@ -261,16 +302,32 @@ async function main() {
|
|
|
261
302
|
if (options.shacl) {
|
|
262
303
|
exitCode = await runValidateShacl();
|
|
263
304
|
} else {
|
|
264
|
-
const dataDir = await findDataDir(options.
|
|
305
|
+
const dataDir = await findDataDir(options.data);
|
|
265
306
|
exitCode = await runValidate(dataDir);
|
|
266
307
|
}
|
|
267
308
|
break;
|
|
268
309
|
}
|
|
269
310
|
case "generate-index": {
|
|
270
|
-
const dataDir = await findDataDir(options.
|
|
311
|
+
const dataDir = await findDataDir(options.data);
|
|
271
312
|
exitCode = await runGenerateIndex(dataDir);
|
|
272
313
|
break;
|
|
273
314
|
}
|
|
315
|
+
case "people": {
|
|
316
|
+
if (subcommand === "import") {
|
|
317
|
+
const filePath = positional[0];
|
|
318
|
+
if (!filePath) {
|
|
319
|
+
console.error("Error: people import requires a file path");
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
const dataDir = await findDataDir(options.data);
|
|
323
|
+
exitCode = await runPeopleImport(filePath, dataDir);
|
|
324
|
+
} else {
|
|
325
|
+
console.error(`Unknown people subcommand: ${subcommand || "(none)"}`);
|
|
326
|
+
showHelp();
|
|
327
|
+
exitCode = 1;
|
|
328
|
+
}
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
274
331
|
default:
|
|
275
332
|
console.error(`Unknown command: ${command}`);
|
|
276
333
|
showHelp();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/map",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Public data model for career frameworks, consumed by AI agents and engineers",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"bin/",
|
|
23
|
-
"examples/",
|
|
24
23
|
"src/",
|
|
25
24
|
"schema/"
|
|
26
25
|
],
|
|
@@ -31,9 +30,20 @@
|
|
|
31
30
|
"./schema-validation": "./src/schema-validation.js",
|
|
32
31
|
"./index-generator": "./src/index-generator.js",
|
|
33
32
|
"./levels": "./src/levels.js",
|
|
34
|
-
"./examples/*": "./examples/*",
|
|
35
33
|
"./schema/json/*": "./schema/json/*",
|
|
36
|
-
"./schema/rdf/*": "./schema/rdf/*"
|
|
34
|
+
"./schema/rdf/*": "./schema/rdf/*",
|
|
35
|
+
"./activity/queries/org": "./activity/queries/org.js",
|
|
36
|
+
"./activity/queries/snapshots": "./activity/queries/snapshots.js",
|
|
37
|
+
"./activity/queries/evidence": "./activity/queries/evidence.js",
|
|
38
|
+
"./activity/queries/artifacts": "./activity/queries/artifacts.js",
|
|
39
|
+
"./activity/storage": "./activity/storage.js",
|
|
40
|
+
"./activity/extract/github": "./activity/extract/github.js",
|
|
41
|
+
"./activity/extract/getdx": "./activity/extract/getdx.js",
|
|
42
|
+
"./activity/extract/people": "./activity/extract/people.js",
|
|
43
|
+
"./activity/transform/github": "./activity/transform/github.js",
|
|
44
|
+
"./activity/transform/getdx": "./activity/transform/getdx.js",
|
|
45
|
+
"./activity/transform/people": "./activity/transform/people.js",
|
|
46
|
+
"./activity/transform": "./activity/transform/index.js"
|
|
37
47
|
},
|
|
38
48
|
"dependencies": {
|
|
39
49
|
"ajv": "^8.17.1",
|
|
@@ -105,10 +105,43 @@
|
|
|
105
105
|
"implementationReference": {
|
|
106
106
|
"type": "string",
|
|
107
107
|
"description": "Code examples and detailed reference material, exported to references/REFERENCE.md"
|
|
108
|
+
},
|
|
109
|
+
"markers": {
|
|
110
|
+
"$ref": "#/$defs/skillMarkers",
|
|
111
|
+
"description": "Observable indicators of skill proficiency, keyed by level"
|
|
108
112
|
}
|
|
109
113
|
},
|
|
110
114
|
"additionalProperties": false
|
|
111
115
|
},
|
|
116
|
+
"skillMarkers": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"description": "Observable indicators of skill proficiency, keyed by level",
|
|
119
|
+
"propertyNames": {
|
|
120
|
+
"enum": [
|
|
121
|
+
"awareness",
|
|
122
|
+
"foundational",
|
|
123
|
+
"working",
|
|
124
|
+
"practitioner",
|
|
125
|
+
"expert"
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"additionalProperties": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"properties": {
|
|
131
|
+
"human": {
|
|
132
|
+
"type": "array",
|
|
133
|
+
"items": { "type": "string" },
|
|
134
|
+
"description": "Observable markers for human engineers"
|
|
135
|
+
},
|
|
136
|
+
"agent": {
|
|
137
|
+
"type": "array",
|
|
138
|
+
"items": { "type": "string" },
|
|
139
|
+
"description": "Observable markers for AI agents"
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"additionalProperties": false
|
|
143
|
+
}
|
|
144
|
+
},
|
|
112
145
|
"toolReference": {
|
|
113
146
|
"type": "object",
|
|
114
147
|
"required": ["name", "description", "useWhen"],
|
|
@@ -88,13 +88,9 @@
|
|
|
88
88
|
"disciplineHumanSection": {
|
|
89
89
|
"type": "object",
|
|
90
90
|
"properties": {
|
|
91
|
-
"
|
|
91
|
+
"roleSummary": {
|
|
92
92
|
"type": "string",
|
|
93
|
-
"description": "Role summary for
|
|
94
|
-
},
|
|
95
|
-
"managementRoleSummary": {
|
|
96
|
-
"type": "string",
|
|
97
|
-
"description": "Role summary for management track. May use {specialization} placeholder."
|
|
93
|
+
"description": "Role summary for this discipline. May use {roleTitle} or {specialization} placeholder."
|
|
98
94
|
}
|
|
99
95
|
},
|
|
100
96
|
"additionalProperties": false
|
|
@@ -38,6 +38,10 @@ fit:ToolReference a rdfs:Class ;
|
|
|
38
38
|
rdfs:label "Tool Reference"@en ;
|
|
39
39
|
rdfs:comment "Reference to an external tool with usage guidance"@en .
|
|
40
40
|
|
|
41
|
+
fit:SkillMarkers a rdfs:Class ;
|
|
42
|
+
rdfs:label "Skill Markers"@en ;
|
|
43
|
+
rdfs:comment "Observable indicators of skill proficiency at a level"@en .
|
|
44
|
+
|
|
41
45
|
# -----------------------------------------------------------------------------
|
|
42
46
|
# Properties
|
|
43
47
|
# -----------------------------------------------------------------------------
|
|
@@ -95,6 +99,24 @@ fit:implementationReference a rdf:Property ;
|
|
|
95
99
|
rdfs:domain fit:Skill ;
|
|
96
100
|
rdfs:range xsd:string .
|
|
97
101
|
|
|
102
|
+
fit:markers a rdf:Property ;
|
|
103
|
+
rdfs:label "markers"@en ;
|
|
104
|
+
rdfs:comment "Observable indicators keyed by proficiency level"@en ;
|
|
105
|
+
rdfs:domain fit:Skill ;
|
|
106
|
+
rdfs:range fit:SkillMarkers .
|
|
107
|
+
|
|
108
|
+
fit:humanMarkers a rdf:Property ;
|
|
109
|
+
rdfs:label "humanMarkers"@en ;
|
|
110
|
+
rdfs:comment "Observable markers for human engineers"@en ;
|
|
111
|
+
rdfs:domain fit:SkillMarkers ;
|
|
112
|
+
rdfs:range xsd:string .
|
|
113
|
+
|
|
114
|
+
fit:agentMarkers a rdf:Property ;
|
|
115
|
+
rdfs:label "agentMarkers"@en ;
|
|
116
|
+
rdfs:comment "Observable markers for AI agents"@en ;
|
|
117
|
+
rdfs:domain fit:SkillMarkers ;
|
|
118
|
+
rdfs:range xsd:string .
|
|
119
|
+
|
|
98
120
|
fit:toolReferences a rdf:Property ;
|
|
99
121
|
rdfs:label "toolReferences"@en ;
|
|
100
122
|
rdfs:comment "Required tools for this skill"@en ;
|
|
@@ -315,6 +337,32 @@ fit:SkillShape a sh:NodeShape ;
|
|
|
315
337
|
sh:maxCount 1 ;
|
|
316
338
|
sh:name "implementationReference" ;
|
|
317
339
|
sh:description "Code examples and detailed reference material, exported to references/REFERENCE.md" ;
|
|
340
|
+
] ;
|
|
341
|
+
sh:property [
|
|
342
|
+
sh:path fit:markers ;
|
|
343
|
+
sh:node fit:SkillMarkersShape ;
|
|
344
|
+
sh:maxCount 1 ;
|
|
345
|
+
sh:name "markers" ;
|
|
346
|
+
sh:description "Observable indicators of skill proficiency, keyed by level" ;
|
|
347
|
+
] .
|
|
348
|
+
|
|
349
|
+
# -----------------------------------------------------------------------------
|
|
350
|
+
# Skill Markers Shape
|
|
351
|
+
# -----------------------------------------------------------------------------
|
|
352
|
+
|
|
353
|
+
fit:SkillMarkersShape a sh:NodeShape ;
|
|
354
|
+
sh:targetClass fit:SkillMarkers ;
|
|
355
|
+
sh:property [
|
|
356
|
+
sh:path fit:humanMarkers ;
|
|
357
|
+
sh:datatype xsd:string ;
|
|
358
|
+
sh:name "human" ;
|
|
359
|
+
sh:description "Observable markers for human engineers" ;
|
|
360
|
+
] ;
|
|
361
|
+
sh:property [
|
|
362
|
+
sh:path fit:agentMarkers ;
|
|
363
|
+
sh:datatype xsd:string ;
|
|
364
|
+
sh:name "agent" ;
|
|
365
|
+
sh:description "Observable markers for AI agents" ;
|
|
318
366
|
] .
|
|
319
367
|
|
|
320
368
|
# -----------------------------------------------------------------------------
|
|
@@ -86,15 +86,9 @@ fit:behaviourModifiers a rdf:Property ;
|
|
|
86
86
|
rdfs:label "behaviourModifiers"@en ;
|
|
87
87
|
rdfs:comment "Modifiers to behaviour expectations"@en .
|
|
88
88
|
|
|
89
|
-
fit:
|
|
90
|
-
rdfs:label "
|
|
91
|
-
rdfs:comment "Role summary for
|
|
92
|
-
rdfs:domain fit:DisciplineHumanSection ;
|
|
93
|
-
rdfs:range xsd:string .
|
|
94
|
-
|
|
95
|
-
fit:managementRoleSummary a rdf:Property ;
|
|
96
|
-
rdfs:label "managementRoleSummary"@en ;
|
|
97
|
-
rdfs:comment "Role summary for management track. May use {specialization} placeholder."@en ;
|
|
89
|
+
fit:roleSummary a rdf:Property ;
|
|
90
|
+
rdfs:label "roleSummary"@en ;
|
|
91
|
+
rdfs:comment "Role summary for this discipline. May use {roleTitle} or {specialization} placeholder."@en ;
|
|
98
92
|
rdfs:domain fit:DisciplineHumanSection ;
|
|
99
93
|
rdfs:range xsd:string .
|
|
100
94
|
|
|
@@ -244,18 +238,11 @@ fit:DisciplineShape a sh:NodeShape ;
|
|
|
244
238
|
fit:DisciplineHumanSectionShape a sh:NodeShape ;
|
|
245
239
|
sh:targetClass fit:DisciplineHumanSection ;
|
|
246
240
|
sh:property [
|
|
247
|
-
sh:path fit:
|
|
248
|
-
sh:datatype xsd:string ;
|
|
249
|
-
sh:maxCount 1 ;
|
|
250
|
-
sh:name "professionalRoleSummary" ;
|
|
251
|
-
sh:description "Role summary for professional/IC track" ;
|
|
252
|
-
] ;
|
|
253
|
-
sh:property [
|
|
254
|
-
sh:path fit:managementRoleSummary ;
|
|
241
|
+
sh:path fit:roleSummary ;
|
|
255
242
|
sh:datatype xsd:string ;
|
|
256
243
|
sh:maxCount 1 ;
|
|
257
|
-
sh:name "
|
|
258
|
-
sh:description "Role summary for
|
|
244
|
+
sh:name "roleSummary" ;
|
|
245
|
+
sh:description "Role summary for this discipline" ;
|
|
259
246
|
] .
|
|
260
247
|
|
|
261
248
|
# -----------------------------------------------------------------------------
|
package/src/index-generator.js
CHANGED
|
@@ -10,56 +10,85 @@ import { join, basename } from "path";
|
|
|
10
10
|
import { stringify as stringifyYaml } from "yaml";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @param {string} dir - Directory path
|
|
15
|
-
* @returns {Promise<string[]>} List of file IDs included
|
|
13
|
+
* Index generator class with injectable filesystem and serializer dependencies.
|
|
16
14
|
*/
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
export class IndexGenerator {
|
|
16
|
+
#fs;
|
|
17
|
+
#yaml;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {{ readdir: Function, writeFile: Function }} fs
|
|
21
|
+
* @param {{ stringify: Function }} yamlSerializer
|
|
22
|
+
*/
|
|
23
|
+
constructor(fs, yamlSerializer) {
|
|
24
|
+
if (!fs) throw new Error("fs is required");
|
|
25
|
+
if (!yamlSerializer) throw new Error("yamlSerializer is required");
|
|
26
|
+
this.#fs = fs;
|
|
27
|
+
this.#yaml = yamlSerializer;
|
|
28
|
+
}
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Generate _index.yaml for a directory
|
|
32
|
+
* @param {string} dir - Directory path
|
|
33
|
+
* @returns {Promise<string[]>} List of file IDs included
|
|
34
|
+
*/
|
|
35
|
+
async generateDirIndex(dir) {
|
|
36
|
+
const files = await this.#fs.readdir(dir);
|
|
37
|
+
const yamlFiles = files.filter(
|
|
38
|
+
(f) => f.endsWith(".yaml") && !f.startsWith("_"),
|
|
39
|
+
);
|
|
24
40
|
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
// Auto-generated index for browser loading
|
|
28
|
-
// Do not edit manually - regenerate with: npx pathway --generate-index
|
|
29
|
-
files: fileIds,
|
|
30
|
-
},
|
|
31
|
-
{ lineWidth: 0 },
|
|
32
|
-
);
|
|
41
|
+
const fileIds = yamlFiles.map((f) => basename(f, ".yaml")).sort();
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
const content = this.#yaml.stringify(
|
|
44
|
+
{
|
|
45
|
+
// Auto-generated index for browser loading
|
|
46
|
+
// Do not edit manually - regenerate with: npx pathway --generate-index
|
|
47
|
+
files: fileIds,
|
|
48
|
+
},
|
|
49
|
+
{ lineWidth: 0 },
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const output = `# Auto-generated index for browser loading
|
|
36
53
|
# Do not edit manually - regenerate with: npx pathway --generate-index
|
|
37
54
|
${content}`;
|
|
38
55
|
|
|
39
|
-
|
|
56
|
+
await this.#fs.writeFile(join(dir, "_index.yaml"), output, "utf-8");
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
}
|
|
58
|
+
return fileIds;
|
|
59
|
+
}
|
|
43
60
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Generate all index files for the data directory
|
|
63
|
+
* @param {string} dataDir - Path to the data directory
|
|
64
|
+
* @returns {Promise<Object>} Summary of generated indexes
|
|
65
|
+
*/
|
|
66
|
+
async generateAllIndexes(dataDir) {
|
|
67
|
+
const directories = ["behaviours", "disciplines", "tracks", "capabilities"];
|
|
51
68
|
|
|
52
|
-
|
|
69
|
+
const results = {};
|
|
53
70
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
for (const dir of directories) {
|
|
72
|
+
const fullPath = join(dataDir, dir);
|
|
73
|
+
try {
|
|
74
|
+
const files = await this.generateDirIndex(fullPath);
|
|
75
|
+
results[dir] = files;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
results[dir] = { error: err.message };
|
|
78
|
+
}
|
|
61
79
|
}
|
|
80
|
+
|
|
81
|
+
return results;
|
|
62
82
|
}
|
|
83
|
+
}
|
|
63
84
|
|
|
64
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Create an IndexGenerator with real filesystem and serializer dependencies
|
|
87
|
+
* @returns {IndexGenerator}
|
|
88
|
+
*/
|
|
89
|
+
export function createIndexGenerator() {
|
|
90
|
+
return new IndexGenerator(
|
|
91
|
+
{ readdir, writeFile },
|
|
92
|
+
{ stringify: stringifyYaml },
|
|
93
|
+
);
|
|
65
94
|
}
|
package/src/index.js
CHANGED
|
@@ -4,22 +4,17 @@
|
|
|
4
4
|
* Data model definitions, validation, and loading for Engineering Pathway.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
export {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
loadExampleData,
|
|
17
|
-
loadAndValidate,
|
|
18
|
-
loadAgentData,
|
|
19
|
-
loadSkillsWithAgentData,
|
|
20
|
-
} from "./loader.js";
|
|
7
|
+
// Classes
|
|
8
|
+
export { DataLoader } from "./loader.js";
|
|
9
|
+
export { SchemaValidator } from "./schema-validation.js";
|
|
10
|
+
export { IndexGenerator } from "./index-generator.js";
|
|
11
|
+
|
|
12
|
+
// Factory functions
|
|
13
|
+
export { createDataLoader } from "./loader.js";
|
|
14
|
+
export { createSchemaValidator } from "./schema-validation.js";
|
|
15
|
+
export { createIndexGenerator } from "./index-generator.js";
|
|
21
16
|
|
|
22
|
-
//
|
|
17
|
+
// Pure validation functions (unchanged)
|
|
23
18
|
export {
|
|
24
19
|
validateAllData,
|
|
25
20
|
validateQuestionBank,
|
|
@@ -27,16 +22,6 @@ export {
|
|
|
27
22
|
validateAgentData,
|
|
28
23
|
} from "./validation.js";
|
|
29
24
|
|
|
30
|
-
// Schema-based validation
|
|
31
|
-
export {
|
|
32
|
-
validateDataDirectory,
|
|
33
|
-
validateReferentialIntegrity,
|
|
34
|
-
runSchemaValidation,
|
|
35
|
-
} from "./schema-validation.js";
|
|
36
|
-
|
|
37
|
-
// Index generation
|
|
38
|
-
export { generateAllIndexes, generateDirIndex } from "./index-generator.js";
|
|
39
|
-
|
|
40
25
|
// Type constants and helpers
|
|
41
26
|
export * from "./levels.js";
|
|
42
27
|
|