@rolexjs/local-platform 0.8.0 → 0.9.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/dist/index.d.ts +24 -12
- package/dist/index.js +170 -59
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Platform,
|
|
1
|
+
import { Platform, Feature, OrganizationInfo, Duty, Assignment, PositionInfo, Goal, Plan, Task } from '@rolexjs/core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* LocalPlatform — Local filesystem implementation of Platform.
|
|
@@ -6,29 +6,40 @@ import { Platform, Organization, Feature, Goal, Plan, Task } from '@rolexjs/core
|
|
|
6
6
|
* Everything lives under a single .rolex/ directory.
|
|
7
7
|
* rolex.json is the single source of truth (CAS):
|
|
8
8
|
* - roles: all born role names
|
|
9
|
-
* -
|
|
9
|
+
* - organizations: org configs with positions
|
|
10
|
+
* - assignments: role → org/position mappings
|
|
10
11
|
*
|
|
11
12
|
* Directory convention:
|
|
12
|
-
* <rootDir>/rolex.json
|
|
13
|
-
* <rootDir
|
|
14
|
-
* <rootDir
|
|
15
|
-
* <rootDir
|
|
16
|
-
* <rootDir
|
|
13
|
+
* <rootDir>/rolex.json
|
|
14
|
+
* <rootDir>/roles/<role>/identity/*.identity.feature
|
|
15
|
+
* <rootDir>/roles/<role>/goals/<name>/<name>.goal.feature
|
|
16
|
+
* <rootDir>/roles/<role>/goals/<name>/<name>.plan.feature
|
|
17
|
+
* <rootDir>/roles/<role>/goals/<name>/tasks/<name>.task.feature
|
|
18
|
+
* <rootDir>/orgs/<org>/org.feature (optional)
|
|
19
|
+
* <rootDir>/orgs/<org>/positions/<name>/<name>.position.feature
|
|
20
|
+
* <rootDir>/orgs/<org>/positions/<name>/*.duty.feature
|
|
17
21
|
*/
|
|
18
22
|
|
|
19
23
|
declare class LocalPlatform implements Platform {
|
|
20
24
|
private readonly rootDir;
|
|
21
25
|
private config;
|
|
22
26
|
constructor(rootDir: string);
|
|
23
|
-
found(name: string): void;
|
|
24
|
-
organization(): Organization | null;
|
|
25
27
|
allBornRoles(): string[];
|
|
26
28
|
born(name: string, source: string): Feature;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
found(name: string, source?: string, parent?: string): void;
|
|
30
|
+
getOrganization(name: string): OrganizationInfo | null;
|
|
31
|
+
allOrganizations(): OrganizationInfo[];
|
|
32
|
+
hire(roleId: string, orgName: string): void;
|
|
33
|
+
fire(roleId: string, orgName: string): void;
|
|
34
|
+
establish(positionName: string, source: string, orgName: string): void;
|
|
35
|
+
appoint(roleId: string, positionName: string, orgName: string): void;
|
|
36
|
+
dismiss(roleId: string): void;
|
|
37
|
+
positionDuties(positionName: string, orgName: string): Duty[];
|
|
38
|
+
getAssignment(roleId: string): Assignment | null;
|
|
39
|
+
getPosition(positionName: string, orgName: string): PositionInfo | null;
|
|
40
|
+
identity(roleId: string): Feature[];
|
|
29
41
|
growup(roleId: string, type: "knowledge" | "experience" | "voice", name: string, source: string): Feature;
|
|
30
42
|
reflect(roleId: string, experienceNames: string[], knowledgeName: string, knowledgeSource: string): Feature;
|
|
31
|
-
identity(roleId: string): Feature[];
|
|
32
43
|
activeGoal(roleId: string): (Goal & {
|
|
33
44
|
plan: Plan | null;
|
|
34
45
|
tasks: Task[];
|
|
@@ -49,6 +60,7 @@ declare class LocalPlatform implements Platform {
|
|
|
49
60
|
private loadConfig;
|
|
50
61
|
private saveConfig;
|
|
51
62
|
private resolveRoleDir;
|
|
63
|
+
private findPositionHolder;
|
|
52
64
|
private loadGoalIfActive;
|
|
53
65
|
private getActiveGoalDir;
|
|
54
66
|
private toFeature;
|
package/dist/index.js
CHANGED
|
@@ -2,43 +2,25 @@
|
|
|
2
2
|
import { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from "fs";
|
|
3
3
|
import { join, basename } from "path";
|
|
4
4
|
import { parse } from "@rolexjs/parser";
|
|
5
|
+
import {
|
|
6
|
+
getRoleState,
|
|
7
|
+
getPositionState,
|
|
8
|
+
transition,
|
|
9
|
+
ROLE_MACHINE,
|
|
10
|
+
POSITION_MACHINE
|
|
11
|
+
} from "@rolexjs/core";
|
|
5
12
|
var LocalPlatform = class {
|
|
6
13
|
rootDir;
|
|
7
14
|
config = null;
|
|
8
15
|
constructor(rootDir) {
|
|
9
16
|
this.rootDir = rootDir;
|
|
10
17
|
}
|
|
11
|
-
// ========== Found ==========
|
|
12
|
-
found(name) {
|
|
13
|
-
const config = this.loadConfig();
|
|
14
|
-
config.organization = {
|
|
15
|
-
name,
|
|
16
|
-
teams: { default: [] }
|
|
17
|
-
};
|
|
18
|
-
this.saveConfig(config);
|
|
19
|
-
}
|
|
20
|
-
// ========== Organization ==========
|
|
21
|
-
organization() {
|
|
22
|
-
const config = this.loadConfig();
|
|
23
|
-
if (!config.organization) return null;
|
|
24
|
-
const roles = [];
|
|
25
|
-
for (const [teamName, roleNames] of Object.entries(config.organization.teams)) {
|
|
26
|
-
for (const roleName of roleNames) {
|
|
27
|
-
roles.push({
|
|
28
|
-
name: roleName,
|
|
29
|
-
team: teamName
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return { name: config.organization.name, roles };
|
|
34
|
-
}
|
|
35
18
|
// ========== Society ==========
|
|
36
19
|
allBornRoles() {
|
|
37
20
|
return this.loadConfig().roles;
|
|
38
21
|
}
|
|
39
|
-
// ========== Born ==========
|
|
40
22
|
born(name, source) {
|
|
41
|
-
const roleDir = join(this.rootDir, name);
|
|
23
|
+
const roleDir = join(this.rootDir, "roles", name);
|
|
42
24
|
mkdirSync(join(roleDir, "identity"), { recursive: true });
|
|
43
25
|
mkdirSync(join(roleDir, "goals"), { recursive: true });
|
|
44
26
|
const filePath = join(roleDir, "identity", "persona.identity.feature");
|
|
@@ -51,33 +33,159 @@ var LocalPlatform = class {
|
|
|
51
33
|
const doc = parse(source);
|
|
52
34
|
return this.toFeature(doc.feature, "persona");
|
|
53
35
|
}
|
|
54
|
-
|
|
36
|
+
found(name, source, parent) {
|
|
55
37
|
const config = this.loadConfig();
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
throw new Error(`Role not found: ${name}. Call born() first.`);
|
|
38
|
+
if (config.organizations[name]) {
|
|
39
|
+
throw new Error(`Organization already exists: ${name}`);
|
|
59
40
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
41
|
+
if (parent && !config.organizations[parent]) {
|
|
42
|
+
throw new Error(`Parent organization not found: ${parent}`);
|
|
43
|
+
}
|
|
44
|
+
const orgDir = join(this.rootDir, "orgs", name);
|
|
45
|
+
mkdirSync(orgDir, { recursive: true });
|
|
46
|
+
if (source) {
|
|
47
|
+
writeFileSync(join(orgDir, "org.feature"), source, "utf-8");
|
|
64
48
|
}
|
|
49
|
+
config.organizations[name] = {
|
|
50
|
+
parent,
|
|
51
|
+
positions: []
|
|
52
|
+
};
|
|
53
|
+
this.saveConfig(config);
|
|
65
54
|
}
|
|
66
|
-
|
|
55
|
+
getOrganization(name) {
|
|
67
56
|
const config = this.loadConfig();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
const orgConfig = config.organizations[name];
|
|
58
|
+
if (!orgConfig) return null;
|
|
59
|
+
const members = Object.entries(config.assignments).filter(([, a]) => a.org === name).map(([role]) => role);
|
|
60
|
+
return {
|
|
61
|
+
name,
|
|
62
|
+
parent: orgConfig.parent,
|
|
63
|
+
positions: orgConfig.positions,
|
|
64
|
+
members
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
allOrganizations() {
|
|
68
|
+
const config = this.loadConfig();
|
|
69
|
+
return Object.keys(config.organizations).map((name) => this.getOrganization(name));
|
|
70
|
+
}
|
|
71
|
+
// ========== Organization ==========
|
|
72
|
+
hire(roleId, orgName) {
|
|
73
|
+
const config = this.loadConfig();
|
|
74
|
+
if (!config.roles.includes(roleId)) {
|
|
75
|
+
throw new Error(`Role not found: ${roleId}. Call born() first.`);
|
|
76
|
+
}
|
|
77
|
+
if (!config.organizations[orgName]) {
|
|
78
|
+
throw new Error(`Organization not found: ${orgName}. Call found() first.`);
|
|
79
|
+
}
|
|
80
|
+
const assignment = config.assignments[roleId] ?? null;
|
|
81
|
+
const currentState = getRoleState(assignment);
|
|
82
|
+
transition(ROLE_MACHINE, currentState, "hire");
|
|
83
|
+
if (assignment && assignment.org !== orgName) {
|
|
84
|
+
throw new Error(`Role "${roleId}" is already hired in organization "${assignment.org}"`);
|
|
85
|
+
}
|
|
86
|
+
config.assignments[roleId] = { org: orgName };
|
|
87
|
+
this.saveConfig(config);
|
|
88
|
+
}
|
|
89
|
+
fire(roleId, orgName) {
|
|
90
|
+
const config = this.loadConfig();
|
|
91
|
+
const assignment = config.assignments[roleId];
|
|
92
|
+
if (!assignment || assignment.org !== orgName) {
|
|
93
|
+
throw new Error(`Role "${roleId}" is not hired in organization "${orgName}"`);
|
|
94
|
+
}
|
|
95
|
+
const currentState = getRoleState(assignment);
|
|
96
|
+
transition(ROLE_MACHINE, currentState, "fire");
|
|
97
|
+
if (assignment.position) {
|
|
98
|
+
}
|
|
99
|
+
delete config.assignments[roleId];
|
|
100
|
+
this.saveConfig(config);
|
|
101
|
+
}
|
|
102
|
+
establish(positionName, source, orgName) {
|
|
103
|
+
const config = this.loadConfig();
|
|
104
|
+
if (!config.organizations[orgName]) {
|
|
105
|
+
throw new Error(`Organization not found: ${orgName}. Call found() first.`);
|
|
106
|
+
}
|
|
107
|
+
if (config.organizations[orgName].positions.includes(positionName)) {
|
|
108
|
+
throw new Error(`Position "${positionName}" already exists in organization "${orgName}"`);
|
|
109
|
+
}
|
|
110
|
+
const posDir = join(this.rootDir, "orgs", orgName, "positions", positionName);
|
|
111
|
+
mkdirSync(posDir, { recursive: true });
|
|
112
|
+
writeFileSync(join(posDir, `${positionName}.position.feature`), source, "utf-8");
|
|
113
|
+
config.organizations[orgName].positions.push(positionName);
|
|
114
|
+
this.saveConfig(config);
|
|
115
|
+
}
|
|
116
|
+
appoint(roleId, positionName, orgName) {
|
|
117
|
+
const config = this.loadConfig();
|
|
118
|
+
const assignment = config.assignments[roleId];
|
|
119
|
+
if (!assignment || assignment.org !== orgName) {
|
|
120
|
+
throw new Error(`Role "${roleId}" is not a member of organization "${orgName}". Hire first.`);
|
|
121
|
+
}
|
|
122
|
+
if (!config.organizations[orgName]?.positions.includes(positionName)) {
|
|
123
|
+
throw new Error(`Position "${positionName}" not found in organization "${orgName}"`);
|
|
124
|
+
}
|
|
125
|
+
const roleState = getRoleState(assignment);
|
|
126
|
+
transition(ROLE_MACHINE, roleState, "appoint");
|
|
127
|
+
const currentHolder = this.findPositionHolder(config, orgName, positionName);
|
|
128
|
+
const posState = getPositionState(currentHolder);
|
|
129
|
+
transition(POSITION_MACHINE, posState, "appoint");
|
|
130
|
+
config.assignments[roleId] = { org: orgName, position: positionName };
|
|
131
|
+
this.saveConfig(config);
|
|
132
|
+
}
|
|
133
|
+
dismiss(roleId) {
|
|
134
|
+
const config = this.loadConfig();
|
|
135
|
+
const assignment = config.assignments[roleId];
|
|
136
|
+
if (!assignment || !assignment.position) {
|
|
137
|
+
throw new Error(`Role "${roleId}" is not appointed to any position`);
|
|
77
138
|
}
|
|
78
|
-
|
|
139
|
+
const roleState = getRoleState(assignment);
|
|
140
|
+
transition(ROLE_MACHINE, roleState, "dismiss");
|
|
141
|
+
config.assignments[roleId] = { org: assignment.org };
|
|
79
142
|
this.saveConfig(config);
|
|
80
143
|
}
|
|
144
|
+
positionDuties(positionName, orgName) {
|
|
145
|
+
const posDir = join(this.rootDir, "orgs", orgName, "positions", positionName);
|
|
146
|
+
if (!existsSync(posDir)) return [];
|
|
147
|
+
return readdirSync(posDir).filter((f) => f.endsWith(".duty.feature")).sort().map((f) => {
|
|
148
|
+
const source = readFileSync(join(posDir, f), "utf-8");
|
|
149
|
+
const doc = parse(source);
|
|
150
|
+
return this.toFeature(doc.feature, "duty");
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
getAssignment(roleId) {
|
|
154
|
+
const config = this.loadConfig();
|
|
155
|
+
return config.assignments[roleId] ?? null;
|
|
156
|
+
}
|
|
157
|
+
getPosition(positionName, orgName) {
|
|
158
|
+
const config = this.loadConfig();
|
|
159
|
+
if (!config.organizations[orgName]?.positions.includes(positionName)) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
const holder = this.findPositionHolder(config, orgName, positionName);
|
|
163
|
+
const duties = this.positionDuties(positionName, orgName);
|
|
164
|
+
return {
|
|
165
|
+
name: positionName,
|
|
166
|
+
org: orgName,
|
|
167
|
+
state: getPositionState(holder),
|
|
168
|
+
assignedRole: holder,
|
|
169
|
+
duties
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
// ========== Role Identity ==========
|
|
173
|
+
identity(roleId) {
|
|
174
|
+
const roleDir = this.resolveRoleDir(roleId);
|
|
175
|
+
const dir = join(roleDir, "identity");
|
|
176
|
+
if (!existsSync(dir)) return [];
|
|
177
|
+
const features = readdirSync(dir).filter((f) => f.endsWith(".identity.feature")).sort().map((f) => {
|
|
178
|
+
const source = readFileSync(join(dir, f), "utf-8");
|
|
179
|
+
const doc = parse(source);
|
|
180
|
+
return this.toFeature(doc.feature, this.detectIdentityType(f));
|
|
181
|
+
});
|
|
182
|
+
const assignment = this.getAssignment(roleId);
|
|
183
|
+
if (assignment?.position) {
|
|
184
|
+
const duties = this.positionDuties(assignment.position, assignment.org);
|
|
185
|
+
features.push(...duties);
|
|
186
|
+
}
|
|
187
|
+
return features;
|
|
188
|
+
}
|
|
81
189
|
// ========== Growup ==========
|
|
82
190
|
growup(roleId, type, name, source) {
|
|
83
191
|
const roleDir = this.resolveRoleDir(roleId);
|
|
@@ -101,17 +209,7 @@ var LocalPlatform = class {
|
|
|
101
209
|
}
|
|
102
210
|
return this.growup(roleId, "knowledge", knowledgeName, knowledgeSource);
|
|
103
211
|
}
|
|
104
|
-
// ==========
|
|
105
|
-
identity(roleId) {
|
|
106
|
-
const roleDir = this.resolveRoleDir(roleId);
|
|
107
|
-
const dir = join(roleDir, "identity");
|
|
108
|
-
if (!existsSync(dir)) return [];
|
|
109
|
-
return readdirSync(dir).filter((f) => f.endsWith(".identity.feature")).sort().map((f) => {
|
|
110
|
-
const source = readFileSync(join(dir, f), "utf-8");
|
|
111
|
-
const doc = parse(source);
|
|
112
|
-
return this.toFeature(doc.feature, this.detectIdentityType(f));
|
|
113
|
-
});
|
|
114
|
-
}
|
|
212
|
+
// ========== Goals ==========
|
|
115
213
|
activeGoal(roleId) {
|
|
116
214
|
const roleDir = this.resolveRoleDir(roleId);
|
|
117
215
|
const goalsDir = join(roleDir, "goals");
|
|
@@ -239,11 +337,16 @@ ${source}`;
|
|
|
239
337
|
if (this.config) return this.config;
|
|
240
338
|
const configPath = join(this.rootDir, "rolex.json");
|
|
241
339
|
if (existsSync(configPath)) {
|
|
242
|
-
|
|
340
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
341
|
+
this.config = {
|
|
342
|
+
roles: raw.roles ?? [],
|
|
343
|
+
organizations: raw.organizations ?? {},
|
|
344
|
+
assignments: raw.assignments ?? {}
|
|
345
|
+
};
|
|
243
346
|
return this.config;
|
|
244
347
|
}
|
|
245
348
|
mkdirSync(this.rootDir, { recursive: true });
|
|
246
|
-
const config = { roles: [],
|
|
349
|
+
const config = { roles: [], organizations: {}, assignments: {} };
|
|
247
350
|
this.saveConfig(config);
|
|
248
351
|
return config;
|
|
249
352
|
}
|
|
@@ -253,12 +356,20 @@ ${source}`;
|
|
|
253
356
|
this.config = config;
|
|
254
357
|
}
|
|
255
358
|
resolveRoleDir(roleId) {
|
|
256
|
-
const roleDir = join(this.rootDir, roleId);
|
|
359
|
+
const roleDir = join(this.rootDir, "roles", roleId);
|
|
257
360
|
if (!existsSync(roleDir)) {
|
|
258
361
|
throw new Error(`Role directory not found: ${roleDir}`);
|
|
259
362
|
}
|
|
260
363
|
return roleDir;
|
|
261
364
|
}
|
|
365
|
+
findPositionHolder(config, orgName, positionName) {
|
|
366
|
+
for (const [role, assignment] of Object.entries(config.assignments)) {
|
|
367
|
+
if (assignment.org === orgName && assignment.position === positionName) {
|
|
368
|
+
return role;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
262
373
|
loadGoalIfActive(goalDir) {
|
|
263
374
|
const goalFile = this.findFeatureFile(goalDir, ".goal.feature");
|
|
264
375
|
if (!goalFile) return null;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/LocalPlatform.ts"],"sourcesContent":["/**\n * LocalPlatform — Local filesystem implementation of Platform.\n *\n * Everything lives under a single .rolex/ directory.\n * rolex.json is the single source of truth (CAS):\n * - roles: all born role names\n * - organization: optional org with teams\n *\n * Directory convention:\n * <rootDir>/rolex.json <- Society config (always exists)\n * <rootDir>/<role>/identity/*.identity.feature <- Identity features\n * <rootDir>/<role>/goals/<name>/<name>.goal.feature\n * <rootDir>/<role>/goals/<name>/<name>.plan.feature\n * <rootDir>/<role>/goals/<name>/tasks/<name>.task.feature\n */\n\nimport { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { parse } from \"@rolexjs/parser\";\nimport type { Feature as GherkinFeature } from \"@rolexjs/parser\";\nimport type {\n Platform,\n RolexConfig,\n Organization,\n RoleEntry,\n Feature,\n Scenario,\n Goal,\n Plan,\n Task,\n} from \"@rolexjs/core\";\n\nexport class LocalPlatform implements Platform {\n private readonly rootDir: string;\n private config: RolexConfig | null = null;\n\n constructor(rootDir: string) {\n this.rootDir = rootDir;\n }\n\n // ========== Found ==========\n\n found(name: string): void {\n const config = this.loadConfig();\n config.organization = {\n name,\n teams: { default: [] },\n };\n this.saveConfig(config);\n }\n\n // ========== Organization ==========\n\n organization(): Organization | null {\n const config = this.loadConfig();\n if (!config.organization) return null;\n\n const roles: RoleEntry[] = [];\n\n for (const [teamName, roleNames] of Object.entries(config.organization.teams)) {\n for (const roleName of roleNames) {\n roles.push({\n name: roleName,\n team: teamName,\n });\n }\n }\n\n return { name: config.organization.name, roles };\n }\n\n // ========== Society ==========\n\n allBornRoles(): string[] {\n return this.loadConfig().roles;\n }\n\n // ========== Born ==========\n\n born(name: string, source: string): Feature {\n const roleDir = join(this.rootDir, name);\n mkdirSync(join(roleDir, \"identity\"), { recursive: true });\n mkdirSync(join(roleDir, \"goals\"), { recursive: true });\n\n const filePath = join(roleDir, \"identity\", \"persona.identity.feature\");\n writeFileSync(filePath, source, \"utf-8\");\n\n // Register in config (CAS)\n const config = this.loadConfig();\n if (!config.roles.includes(name)) {\n config.roles.push(name);\n this.saveConfig(config);\n }\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"persona\");\n }\n\n hire(name: string): void {\n const config = this.loadConfig();\n if (!config.organization) throw new Error(\"No organization found. Call found() first.\");\n\n if (!config.roles.includes(name)) {\n throw new Error(`Role not found: ${name}. Call born() first.`);\n }\n\n // Pure config update — goals dir already exists from born()\n const [firstTeam] = Object.keys(config.organization.teams);\n if (!config.organization.teams[firstTeam].includes(name)) {\n config.organization.teams[firstTeam].push(name);\n this.saveConfig(config);\n }\n }\n\n fire(name: string): void {\n const config = this.loadConfig();\n if (!config.organization) throw new Error(\"No organization found. Call found() first.\");\n\n // Check role is in org\n let found = false;\n for (const teamName of Object.keys(config.organization.teams)) {\n const idx = config.organization.teams[teamName].indexOf(name);\n if (idx !== -1) {\n config.organization.teams[teamName].splice(idx, 1);\n found = true;\n break;\n }\n }\n if (!found) throw new Error(`Role not hired: ${name}`);\n\n // Pure config update — goals are the role's own, not deleted\n this.saveConfig(config);\n }\n\n // ========== Growup ==========\n\n growup(\n roleId: string,\n type: \"knowledge\" | \"experience\" | \"voice\",\n name: string,\n source: string\n ): Feature {\n const roleDir = this.resolveRoleDir(roleId);\n const dir = join(roleDir, \"identity\");\n mkdirSync(dir, { recursive: true });\n\n const filePath = join(dir, `${name}.${type}.identity.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, type);\n }\n\n // ========== Reflect ==========\n\n reflect(\n roleId: string,\n experienceNames: string[],\n knowledgeName: string,\n knowledgeSource: string\n ): Feature {\n const roleDir = this.resolveRoleDir(roleId);\n const identityDir = join(roleDir, \"identity\");\n\n // Delete experience files\n for (const expName of experienceNames) {\n const expFile = join(identityDir, `${expName}.experience.identity.feature`);\n if (!existsSync(expFile)) {\n throw new Error(`Experience not found: ${expName}`);\n }\n rmSync(expFile);\n }\n\n // Create knowledge\n return this.growup(roleId, \"knowledge\", knowledgeName, knowledgeSource);\n }\n\n // ========== Query ==========\n\n identity(roleId: string): Feature[] {\n const roleDir = this.resolveRoleDir(roleId);\n const dir = join(roleDir, \"identity\");\n if (!existsSync(dir)) return [];\n\n return readdirSync(dir)\n .filter((f) => f.endsWith(\".identity.feature\"))\n .sort()\n .map((f) => {\n const source = readFileSync(join(dir, f), \"utf-8\");\n const doc = parse(source);\n return this.toFeature(doc.feature!, this.detectIdentityType(f));\n });\n }\n\n activeGoal(roleId: string): (Goal & { plan: Plan | null; tasks: Task[] }) | null {\n const roleDir = this.resolveRoleDir(roleId);\n const goalsDir = join(roleDir, \"goals\");\n if (!existsSync(goalsDir)) return null;\n\n // If a focused goal is set, try to load it first\n const focusedName = this.getFocusedGoal(roleId);\n if (focusedName) {\n const goalDir = join(goalsDir, focusedName);\n const result = this.loadGoalIfActive(goalDir);\n if (result) return result;\n }\n\n // Fallback: first uncompleted goal alphabetically\n const goalDirs = readdirSync(goalsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n\n for (const goalName of goalDirs) {\n const goalDir = join(goalsDir, goalName);\n const result = this.loadGoalIfActive(goalDir);\n if (result) return result;\n }\n\n return null;\n }\n\n allActiveGoals(roleId: string): Goal[] {\n const roleDir = this.resolveRoleDir(roleId);\n const goalsDir = join(roleDir, \"goals\");\n if (!existsSync(goalsDir)) return [];\n\n const goalDirs = readdirSync(goalsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n\n const goals: Goal[] = [];\n for (const goalName of goalDirs) {\n const goalDir = join(goalsDir, goalName);\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\");\n if (!goalFile) continue;\n\n const source = readFileSync(goalFile, \"utf-8\");\n const doc = parse(source);\n if (!doc.feature) continue;\n\n if (doc.feature.tags.some((t) => t.name === \"@done\" || t.name === \"@abandoned\")) continue;\n\n goals.push(this.toFeature(doc.feature, \"goal\") as Goal);\n }\n\n return goals;\n }\n\n getFocusedGoal(roleId: string): string | null {\n const roleDir = this.resolveRoleDir(roleId);\n const focusFile = join(roleDir, \"goals\", \".focus\");\n if (!existsSync(focusFile)) return null;\n return readFileSync(focusFile, \"utf-8\").trim() || null;\n }\n\n setFocusedGoal(roleId: string, name: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalsDir = join(roleDir, \"goals\");\n const goalDir = join(goalsDir, name);\n\n if (!existsSync(goalDir)) {\n throw new Error(`Goal not found: ${name}`);\n }\n\n writeFileSync(join(goalsDir, \".focus\"), name, \"utf-8\");\n }\n\n // ========== Write ==========\n\n createGoal(roleId: string, name: string, source: string, testable?: boolean): Goal {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = join(roleDir, \"goals\", name);\n mkdirSync(goalDir, { recursive: true });\n\n if (testable) source = `@testable\\n${source}`;\n const filePath = join(goalDir, `${name}.goal.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"goal\") as Goal;\n }\n\n createPlan(roleId: string, source: string): Plan {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const goalName = basename(goalDir);\n const filePath = join(goalDir, `${goalName}.plan.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"plan\") as Plan;\n }\n\n createTask(roleId: string, name: string, source: string, testable?: boolean): Task {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const tasksDir = join(goalDir, \"tasks\");\n mkdirSync(tasksDir, { recursive: true });\n\n if (testable) source = `@testable\\n${source}`;\n const filePath = join(tasksDir, `${name}.task.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"task\") as Task;\n }\n\n // ========== Close ==========\n\n completeGoal(roleId: string, experience?: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\")!;\n this.addDoneTag(goalFile);\n\n if (experience) {\n const goalName = basename(goalDir);\n this.growup(roleId, \"experience\", goalName, experience);\n }\n }\n\n abandonGoal(roleId: string, experience?: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\")!;\n this.addTag(goalFile, \"@abandoned\");\n\n if (experience) {\n const goalName = basename(goalDir);\n this.growup(roleId, \"experience\", goalName, experience);\n }\n }\n\n completeTask(roleId: string, name: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const tasksDir = join(goalDir, \"tasks\");\n const taskFile = join(tasksDir, `${name}.task.feature`);\n if (!existsSync(taskFile)) throw new Error(`Task not found: ${name}`);\n\n this.addDoneTag(taskFile);\n }\n\n // ========== Internal ==========\n\n /**\n * Load config — always returns a valid RolexConfig.\n * Creates default config if rolex.json doesn't exist yet.\n */\n private loadConfig(): RolexConfig {\n if (this.config) return this.config;\n\n const configPath = join(this.rootDir, \"rolex.json\");\n if (existsSync(configPath)) {\n this.config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n return this.config!;\n }\n\n // Create default config\n mkdirSync(this.rootDir, { recursive: true });\n const config: RolexConfig = { roles: [], organization: null };\n this.saveConfig(config);\n return config;\n }\n\n private saveConfig(config: RolexConfig): void {\n mkdirSync(this.rootDir, { recursive: true });\n writeFileSync(join(this.rootDir, \"rolex.json\"), JSON.stringify(config, null, 2), \"utf-8\");\n this.config = config;\n }\n\n private resolveRoleDir(roleId: string): string {\n const roleDir = join(this.rootDir, roleId);\n if (!existsSync(roleDir)) {\n throw new Error(`Role directory not found: ${roleDir}`);\n }\n return roleDir;\n }\n\n private loadGoalIfActive(goalDir: string): (Goal & { plan: Plan | null; tasks: Task[] }) | null {\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\");\n if (!goalFile) return null;\n\n const source = readFileSync(goalFile, \"utf-8\");\n const doc = parse(source);\n if (!doc.feature) return null;\n\n if (doc.feature.tags.some((t) => t.name === \"@done\" || t.name === \"@abandoned\")) return null;\n\n const goal = this.toFeature(doc.feature, \"goal\") as Goal;\n return {\n ...goal,\n plan: this.loadPlan(goalDir),\n tasks: this.loadTasks(goalDir),\n };\n }\n\n private getActiveGoalDir(roleDir: string): string | null {\n const goalsDir = join(roleDir, \"goals\");\n if (!existsSync(goalsDir)) return null;\n\n // Respect focused goal\n const focusFile = join(goalsDir, \".focus\");\n if (existsSync(focusFile)) {\n const focusedName = readFileSync(focusFile, \"utf-8\").trim();\n if (focusedName) {\n const focusedDir = join(goalsDir, focusedName);\n if (existsSync(focusedDir) && this.loadGoalIfActive(focusedDir)) {\n return focusedDir;\n }\n }\n }\n\n // Fallback: first uncompleted goal\n const goalDirs = readdirSync(goalsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n\n for (const goalName of goalDirs) {\n const goalDir = join(goalsDir, goalName);\n if (this.loadGoalIfActive(goalDir)) {\n return goalDir;\n }\n }\n\n return null;\n }\n\n private toFeature(gherkin: GherkinFeature, type: Feature[\"type\"]): Feature {\n return {\n ...gherkin,\n type,\n scenarios: this.extractScenarios(gherkin),\n };\n }\n\n private extractScenarios(feature: GherkinFeature): Scenario[] {\n const featureTestable = feature.tags.some((t) => t.name === \"@testable\");\n return (feature.children || [])\n .filter((c) => c.scenario)\n .map((c) => ({\n ...c.scenario!,\n verifiable: featureTestable || c.scenario!.tags.some((t) => t.name === \"@testable\"),\n }));\n }\n\n private loadPlan(goalDir: string): Plan | null {\n const planFile = this.findFeatureFile(goalDir, \".plan.feature\");\n if (!planFile) return null;\n\n const source = readFileSync(planFile, \"utf-8\");\n const doc = parse(source);\n if (!doc.feature) return null;\n\n return this.toFeature(doc.feature, \"plan\") as Plan;\n }\n\n private loadTasks(goalDir: string): Task[] {\n const tasksDir = join(goalDir, \"tasks\");\n if (!existsSync(tasksDir)) return [];\n\n return readdirSync(tasksDir)\n .filter((f) => f.endsWith(\".task.feature\"))\n .sort()\n .map((f) => {\n const source = readFileSync(join(tasksDir, f), \"utf-8\");\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"task\") as Task;\n });\n }\n\n private detectIdentityType(filename: string): Feature[\"type\"] {\n if (filename === \"persona.identity.feature\") return \"persona\";\n if (filename.endsWith(\".knowledge.identity.feature\")) return \"knowledge\";\n if (filename.endsWith(\".experience.identity.feature\")) return \"experience\";\n if (filename.endsWith(\".voice.identity.feature\")) return \"voice\";\n return \"knowledge\";\n }\n\n private findFeatureFile(dir: string, suffix: string): string | null {\n if (!existsSync(dir)) return null;\n const file = readdirSync(dir).find((f) => f.endsWith(suffix));\n return file ? join(dir, file) : null;\n }\n\n private addTag(filePath: string, tag: string): void {\n const content = readFileSync(filePath, \"utf-8\");\n const updated = content.replace(/^(Feature:)/m, `${tag}\\n$1`);\n writeFileSync(filePath, updated, \"utf-8\");\n }\n\n private addDoneTag(filePath: string): void {\n this.addTag(filePath, \"@done\");\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/LocalPlatform.ts"],"sourcesContent":["/**\n * LocalPlatform — Local filesystem implementation of Platform.\n *\n * Everything lives under a single .rolex/ directory.\n * rolex.json is the single source of truth (CAS):\n * - roles: all born role names\n * - organizations: org configs with positions\n * - assignments: role → org/position mappings\n *\n * Directory convention:\n * <rootDir>/rolex.json\n * <rootDir>/roles/<role>/identity/*.identity.feature\n * <rootDir>/roles/<role>/goals/<name>/<name>.goal.feature\n * <rootDir>/roles/<role>/goals/<name>/<name>.plan.feature\n * <rootDir>/roles/<role>/goals/<name>/tasks/<name>.task.feature\n * <rootDir>/orgs/<org>/org.feature (optional)\n * <rootDir>/orgs/<org>/positions/<name>/<name>.position.feature\n * <rootDir>/orgs/<org>/positions/<name>/*.duty.feature\n */\n\nimport { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { parse } from \"@rolexjs/parser\";\nimport type { Feature as GherkinFeature } from \"@rolexjs/parser\";\nimport type {\n Platform,\n RolexConfig,\n OrganizationInfo,\n PositionInfo,\n Assignment,\n Feature,\n Scenario,\n Goal,\n Plan,\n Task,\n Duty,\n} from \"@rolexjs/core\";\nimport {\n getRoleState,\n getPositionState,\n transition,\n ROLE_MACHINE,\n POSITION_MACHINE,\n} from \"@rolexjs/core\";\n\nexport class LocalPlatform implements Platform {\n private readonly rootDir: string;\n private config: RolexConfig | null = null;\n\n constructor(rootDir: string) {\n this.rootDir = rootDir;\n }\n\n // ========== Society ==========\n\n allBornRoles(): string[] {\n return this.loadConfig().roles;\n }\n\n born(name: string, source: string): Feature {\n const roleDir = join(this.rootDir, \"roles\", name);\n mkdirSync(join(roleDir, \"identity\"), { recursive: true });\n mkdirSync(join(roleDir, \"goals\"), { recursive: true });\n\n const filePath = join(roleDir, \"identity\", \"persona.identity.feature\");\n writeFileSync(filePath, source, \"utf-8\");\n\n // Register in config (CAS)\n const config = this.loadConfig();\n if (!config.roles.includes(name)) {\n config.roles.push(name);\n this.saveConfig(config);\n }\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"persona\");\n }\n\n found(name: string, source?: string, parent?: string): void {\n const config = this.loadConfig();\n\n if (config.organizations[name]) {\n throw new Error(`Organization already exists: ${name}`);\n }\n\n if (parent && !config.organizations[parent]) {\n throw new Error(`Parent organization not found: ${parent}`);\n }\n\n // Create org directory\n const orgDir = join(this.rootDir, \"orgs\", name);\n mkdirSync(orgDir, { recursive: true });\n\n // Write optional org feature\n if (source) {\n writeFileSync(join(orgDir, \"org.feature\"), source, \"utf-8\");\n }\n\n // Register in config\n config.organizations[name] = {\n parent,\n positions: [],\n };\n this.saveConfig(config);\n }\n\n getOrganization(name: string): OrganizationInfo | null {\n const config = this.loadConfig();\n const orgConfig = config.organizations[name];\n if (!orgConfig) return null;\n\n const members = Object.entries(config.assignments)\n .filter(([, a]) => a.org === name)\n .map(([role]) => role);\n\n return {\n name,\n parent: orgConfig.parent,\n positions: orgConfig.positions,\n members,\n };\n }\n\n allOrganizations(): OrganizationInfo[] {\n const config = this.loadConfig();\n return Object.keys(config.organizations).map((name) => this.getOrganization(name)!);\n }\n\n // ========== Organization ==========\n\n hire(roleId: string, orgName: string): void {\n const config = this.loadConfig();\n\n if (!config.roles.includes(roleId)) {\n throw new Error(`Role not found: ${roleId}. Call born() first.`);\n }\n if (!config.organizations[orgName]) {\n throw new Error(`Organization not found: ${orgName}. Call found() first.`);\n }\n\n const assignment = config.assignments[roleId] ?? null;\n const currentState = getRoleState(assignment);\n\n // Validate state transition\n transition(ROLE_MACHINE, currentState, \"hire\");\n\n // One-to-one: role can only belong to one org\n if (assignment && assignment.org !== orgName) {\n throw new Error(`Role \"${roleId}\" is already hired in organization \"${assignment.org}\"`);\n }\n\n config.assignments[roleId] = { org: orgName };\n this.saveConfig(config);\n }\n\n fire(roleId: string, orgName: string): void {\n const config = this.loadConfig();\n\n const assignment = config.assignments[roleId];\n if (!assignment || assignment.org !== orgName) {\n throw new Error(`Role \"${roleId}\" is not hired in organization \"${orgName}\"`);\n }\n\n const currentState = getRoleState(assignment);\n\n // Validate state transition (fire works from member or on_duty)\n transition(ROLE_MACHINE, currentState, \"fire\");\n\n // Auto-dismiss: if on_duty, clear position state first\n if (assignment.position) {\n // No need to explicitly transition position — just removing assignment\n }\n\n delete config.assignments[roleId];\n this.saveConfig(config);\n }\n\n establish(positionName: string, source: string, orgName: string): void {\n const config = this.loadConfig();\n\n if (!config.organizations[orgName]) {\n throw new Error(`Organization not found: ${orgName}. Call found() first.`);\n }\n\n if (config.organizations[orgName].positions.includes(positionName)) {\n throw new Error(`Position \"${positionName}\" already exists in organization \"${orgName}\"`);\n }\n\n // Create position directory\n const posDir = join(this.rootDir, \"orgs\", orgName, \"positions\", positionName);\n mkdirSync(posDir, { recursive: true });\n\n // Write position feature\n writeFileSync(join(posDir, `${positionName}.position.feature`), source, \"utf-8\");\n\n // Register in config\n config.organizations[orgName].positions.push(positionName);\n this.saveConfig(config);\n }\n\n appoint(roleId: string, positionName: string, orgName: string): void {\n const config = this.loadConfig();\n\n // Validate role is member of this org\n const assignment = config.assignments[roleId];\n if (!assignment || assignment.org !== orgName) {\n throw new Error(`Role \"${roleId}\" is not a member of organization \"${orgName}\". Hire first.`);\n }\n\n // Validate position exists in org\n if (!config.organizations[orgName]?.positions.includes(positionName)) {\n throw new Error(`Position \"${positionName}\" not found in organization \"${orgName}\"`);\n }\n\n // Validate role state transition (member → on_duty)\n const roleState = getRoleState(assignment);\n transition(ROLE_MACHINE, roleState, \"appoint\");\n\n // Validate position state transition (vacant → filled)\n const currentHolder = this.findPositionHolder(config, orgName, positionName);\n const posState = getPositionState(currentHolder);\n transition(POSITION_MACHINE, posState, \"appoint\");\n\n // Update assignment\n config.assignments[roleId] = { org: orgName, position: positionName };\n this.saveConfig(config);\n }\n\n dismiss(roleId: string): void {\n const config = this.loadConfig();\n\n const assignment = config.assignments[roleId];\n if (!assignment || !assignment.position) {\n throw new Error(`Role \"${roleId}\" is not appointed to any position`);\n }\n\n // Validate role state transition (on_duty → member)\n const roleState = getRoleState(assignment);\n transition(ROLE_MACHINE, roleState, \"dismiss\");\n\n // Update assignment — keep org, remove position\n config.assignments[roleId] = { org: assignment.org };\n this.saveConfig(config);\n }\n\n positionDuties(positionName: string, orgName: string): Duty[] {\n const posDir = join(this.rootDir, \"orgs\", orgName, \"positions\", positionName);\n if (!existsSync(posDir)) return [];\n\n return readdirSync(posDir)\n .filter((f) => f.endsWith(\".duty.feature\"))\n .sort()\n .map((f) => {\n const source = readFileSync(join(posDir, f), \"utf-8\");\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"duty\") as Duty;\n });\n }\n\n getAssignment(roleId: string): Assignment | null {\n const config = this.loadConfig();\n return config.assignments[roleId] ?? null;\n }\n\n getPosition(positionName: string, orgName: string): PositionInfo | null {\n const config = this.loadConfig();\n\n if (!config.organizations[orgName]?.positions.includes(positionName)) {\n return null;\n }\n\n const holder = this.findPositionHolder(config, orgName, positionName);\n const duties = this.positionDuties(positionName, orgName);\n\n return {\n name: positionName,\n org: orgName,\n state: getPositionState(holder),\n assignedRole: holder,\n duties,\n };\n }\n\n // ========== Role Identity ==========\n\n identity(roleId: string): Feature[] {\n const roleDir = this.resolveRoleDir(roleId);\n const dir = join(roleDir, \"identity\");\n if (!existsSync(dir)) return [];\n\n // Personal identity features\n const features = readdirSync(dir)\n .filter((f) => f.endsWith(\".identity.feature\"))\n .sort()\n .map((f) => {\n const source = readFileSync(join(dir, f), \"utf-8\");\n const doc = parse(source);\n return this.toFeature(doc.feature!, this.detectIdentityType(f));\n });\n\n // Inject duty features if on_duty\n const assignment = this.getAssignment(roleId);\n if (assignment?.position) {\n const duties = this.positionDuties(assignment.position, assignment.org);\n features.push(...duties);\n }\n\n return features;\n }\n\n // ========== Growup ==========\n\n growup(\n roleId: string,\n type: \"knowledge\" | \"experience\" | \"voice\",\n name: string,\n source: string\n ): Feature {\n const roleDir = this.resolveRoleDir(roleId);\n const dir = join(roleDir, \"identity\");\n mkdirSync(dir, { recursive: true });\n\n const filePath = join(dir, `${name}.${type}.identity.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, type);\n }\n\n // ========== Reflect ==========\n\n reflect(\n roleId: string,\n experienceNames: string[],\n knowledgeName: string,\n knowledgeSource: string\n ): Feature {\n const roleDir = this.resolveRoleDir(roleId);\n const identityDir = join(roleDir, \"identity\");\n\n // Delete experience files\n for (const expName of experienceNames) {\n const expFile = join(identityDir, `${expName}.experience.identity.feature`);\n if (!existsSync(expFile)) {\n throw new Error(`Experience not found: ${expName}`);\n }\n rmSync(expFile);\n }\n\n // Create knowledge\n return this.growup(roleId, \"knowledge\", knowledgeName, knowledgeSource);\n }\n\n // ========== Goals ==========\n\n activeGoal(roleId: string): (Goal & { plan: Plan | null; tasks: Task[] }) | null {\n const roleDir = this.resolveRoleDir(roleId);\n const goalsDir = join(roleDir, \"goals\");\n if (!existsSync(goalsDir)) return null;\n\n // If a focused goal is set, try to load it first\n const focusedName = this.getFocusedGoal(roleId);\n if (focusedName) {\n const goalDir = join(goalsDir, focusedName);\n const result = this.loadGoalIfActive(goalDir);\n if (result) return result;\n }\n\n // Fallback: first uncompleted goal alphabetically\n const goalDirs = readdirSync(goalsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n\n for (const goalName of goalDirs) {\n const goalDir = join(goalsDir, goalName);\n const result = this.loadGoalIfActive(goalDir);\n if (result) return result;\n }\n\n return null;\n }\n\n allActiveGoals(roleId: string): Goal[] {\n const roleDir = this.resolveRoleDir(roleId);\n const goalsDir = join(roleDir, \"goals\");\n if (!existsSync(goalsDir)) return [];\n\n const goalDirs = readdirSync(goalsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n\n const goals: Goal[] = [];\n for (const goalName of goalDirs) {\n const goalDir = join(goalsDir, goalName);\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\");\n if (!goalFile) continue;\n\n const source = readFileSync(goalFile, \"utf-8\");\n const doc = parse(source);\n if (!doc.feature) continue;\n\n if (doc.feature.tags.some((t) => t.name === \"@done\" || t.name === \"@abandoned\")) continue;\n\n goals.push(this.toFeature(doc.feature, \"goal\") as Goal);\n }\n\n return goals;\n }\n\n getFocusedGoal(roleId: string): string | null {\n const roleDir = this.resolveRoleDir(roleId);\n const focusFile = join(roleDir, \"goals\", \".focus\");\n if (!existsSync(focusFile)) return null;\n return readFileSync(focusFile, \"utf-8\").trim() || null;\n }\n\n setFocusedGoal(roleId: string, name: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalsDir = join(roleDir, \"goals\");\n const goalDir = join(goalsDir, name);\n\n if (!existsSync(goalDir)) {\n throw new Error(`Goal not found: ${name}`);\n }\n\n writeFileSync(join(goalsDir, \".focus\"), name, \"utf-8\");\n }\n\n // ========== Write ==========\n\n createGoal(roleId: string, name: string, source: string, testable?: boolean): Goal {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = join(roleDir, \"goals\", name);\n mkdirSync(goalDir, { recursive: true });\n\n if (testable) source = `@testable\\n${source}`;\n const filePath = join(goalDir, `${name}.goal.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"goal\") as Goal;\n }\n\n createPlan(roleId: string, source: string): Plan {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const goalName = basename(goalDir);\n const filePath = join(goalDir, `${goalName}.plan.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"plan\") as Plan;\n }\n\n createTask(roleId: string, name: string, source: string, testable?: boolean): Task {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const tasksDir = join(goalDir, \"tasks\");\n mkdirSync(tasksDir, { recursive: true });\n\n if (testable) source = `@testable\\n${source}`;\n const filePath = join(tasksDir, `${name}.task.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"task\") as Task;\n }\n\n // ========== Close ==========\n\n completeGoal(roleId: string, experience?: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\")!;\n this.addDoneTag(goalFile);\n\n if (experience) {\n const goalName = basename(goalDir);\n this.growup(roleId, \"experience\", goalName, experience);\n }\n }\n\n abandonGoal(roleId: string, experience?: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\")!;\n this.addTag(goalFile, \"@abandoned\");\n\n if (experience) {\n const goalName = basename(goalDir);\n this.growup(roleId, \"experience\", goalName, experience);\n }\n }\n\n completeTask(roleId: string, name: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const tasksDir = join(goalDir, \"tasks\");\n const taskFile = join(tasksDir, `${name}.task.feature`);\n if (!existsSync(taskFile)) throw new Error(`Task not found: ${name}`);\n\n this.addDoneTag(taskFile);\n }\n\n // ========== Internal ==========\n\n /**\n * Load config — always returns a valid RolexConfig.\n * Creates default config if rolex.json doesn't exist yet.\n */\n private loadConfig(): RolexConfig {\n if (this.config) return this.config;\n\n const configPath = join(this.rootDir, \"rolex.json\");\n if (existsSync(configPath)) {\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n // Migrate old format: ensure required fields exist\n this.config = {\n roles: raw.roles ?? [],\n organizations: raw.organizations ?? {},\n assignments: raw.assignments ?? {},\n };\n return this.config;\n }\n\n // Create default config\n mkdirSync(this.rootDir, { recursive: true });\n const config: RolexConfig = { roles: [], organizations: {}, assignments: {} };\n this.saveConfig(config);\n return config;\n }\n\n private saveConfig(config: RolexConfig): void {\n mkdirSync(this.rootDir, { recursive: true });\n writeFileSync(join(this.rootDir, \"rolex.json\"), JSON.stringify(config, null, 2), \"utf-8\");\n this.config = config;\n }\n\n private resolveRoleDir(roleId: string): string {\n const roleDir = join(this.rootDir, \"roles\", roleId);\n if (!existsSync(roleDir)) {\n throw new Error(`Role directory not found: ${roleDir}`);\n }\n return roleDir;\n }\n\n private findPositionHolder(\n config: RolexConfig,\n orgName: string,\n positionName: string\n ): string | null {\n for (const [role, assignment] of Object.entries(config.assignments)) {\n if (assignment.org === orgName && assignment.position === positionName) {\n return role;\n }\n }\n return null;\n }\n\n private loadGoalIfActive(goalDir: string): (Goal & { plan: Plan | null; tasks: Task[] }) | null {\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\");\n if (!goalFile) return null;\n\n const source = readFileSync(goalFile, \"utf-8\");\n const doc = parse(source);\n if (!doc.feature) return null;\n\n if (doc.feature.tags.some((t) => t.name === \"@done\" || t.name === \"@abandoned\")) return null;\n\n const goal = this.toFeature(doc.feature, \"goal\") as Goal;\n return {\n ...goal,\n plan: this.loadPlan(goalDir),\n tasks: this.loadTasks(goalDir),\n };\n }\n\n private getActiveGoalDir(roleDir: string): string | null {\n const goalsDir = join(roleDir, \"goals\");\n if (!existsSync(goalsDir)) return null;\n\n // Respect focused goal\n const focusFile = join(goalsDir, \".focus\");\n if (existsSync(focusFile)) {\n const focusedName = readFileSync(focusFile, \"utf-8\").trim();\n if (focusedName) {\n const focusedDir = join(goalsDir, focusedName);\n if (existsSync(focusedDir) && this.loadGoalIfActive(focusedDir)) {\n return focusedDir;\n }\n }\n }\n\n // Fallback: first uncompleted goal\n const goalDirs = readdirSync(goalsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n\n for (const goalName of goalDirs) {\n const goalDir = join(goalsDir, goalName);\n if (this.loadGoalIfActive(goalDir)) {\n return goalDir;\n }\n }\n\n return null;\n }\n\n private toFeature(gherkin: GherkinFeature, type: Feature[\"type\"]): Feature {\n return {\n ...gherkin,\n type,\n scenarios: this.extractScenarios(gherkin),\n };\n }\n\n private extractScenarios(feature: GherkinFeature): Scenario[] {\n const featureTestable = feature.tags.some((t) => t.name === \"@testable\");\n return (feature.children || [])\n .filter((c) => c.scenario)\n .map((c) => ({\n ...c.scenario!,\n verifiable: featureTestable || c.scenario!.tags.some((t) => t.name === \"@testable\"),\n }));\n }\n\n private loadPlan(goalDir: string): Plan | null {\n const planFile = this.findFeatureFile(goalDir, \".plan.feature\");\n if (!planFile) return null;\n\n const source = readFileSync(planFile, \"utf-8\");\n const doc = parse(source);\n if (!doc.feature) return null;\n\n return this.toFeature(doc.feature, \"plan\") as Plan;\n }\n\n private loadTasks(goalDir: string): Task[] {\n const tasksDir = join(goalDir, \"tasks\");\n if (!existsSync(tasksDir)) return [];\n\n return readdirSync(tasksDir)\n .filter((f) => f.endsWith(\".task.feature\"))\n .sort()\n .map((f) => {\n const source = readFileSync(join(tasksDir, f), \"utf-8\");\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"task\") as Task;\n });\n }\n\n private detectIdentityType(filename: string): Feature[\"type\"] {\n if (filename === \"persona.identity.feature\") return \"persona\";\n if (filename.endsWith(\".knowledge.identity.feature\")) return \"knowledge\";\n if (filename.endsWith(\".experience.identity.feature\")) return \"experience\";\n if (filename.endsWith(\".voice.identity.feature\")) return \"voice\";\n return \"knowledge\";\n }\n\n private findFeatureFile(dir: string, suffix: string): string | null {\n if (!existsSync(dir)) return null;\n const file = readdirSync(dir).find((f) => f.endsWith(suffix));\n return file ? join(dir, file) : null;\n }\n\n private addTag(filePath: string, tag: string): void {\n const content = readFileSync(filePath, \"utf-8\");\n const updated = content.replace(/^(Feature:)/m, `${tag}\\n$1`);\n writeFileSync(filePath, updated, \"utf-8\");\n }\n\n private addDoneTag(filePath: string): void {\n this.addTag(filePath, \"@done\");\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rolexjs/local-platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Local filesystem Platform for RoleX — stores roles in .rolex/ directories",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
"clean": "rm -rf dist"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@rolexjs/core": "^0.
|
|
23
|
-
"@rolexjs/parser": "^0.
|
|
22
|
+
"@rolexjs/core": "^0.9.0",
|
|
23
|
+
"@rolexjs/parser": "^0.9.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"rolexjs": "^0.
|
|
26
|
+
"rolexjs": "^0.9.0"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|