rolexjs 1.5.0 → 1.6.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.js CHANGED
@@ -1,8 +1,332 @@
1
1
  // src/index.ts
2
2
  export * from "@rolexjs/core";
3
3
 
4
+ // src/context.ts
5
+ var RoleContext = class {
6
+ roleId;
7
+ focusedGoalId = null;
8
+ focusedPlanId = null;
9
+ encounterIds = /* @__PURE__ */ new Set();
10
+ experienceIds = /* @__PURE__ */ new Set();
11
+ constructor(roleId) {
12
+ this.roleId = roleId;
13
+ }
14
+ // ================================================================
15
+ // Requirements — throw if missing
16
+ // ================================================================
17
+ requireGoalId() {
18
+ if (!this.focusedGoalId) throw new Error("No focused goal. Call want first.");
19
+ return this.focusedGoalId;
20
+ }
21
+ requirePlanId() {
22
+ if (!this.focusedPlanId) throw new Error("No focused plan. Call plan first.");
23
+ return this.focusedPlanId;
24
+ }
25
+ // ================================================================
26
+ // Cognition registries
27
+ // ================================================================
28
+ addEncounter(id) {
29
+ this.encounterIds.add(id);
30
+ }
31
+ requireEncounterIds(ids) {
32
+ for (const id of ids) {
33
+ if (!this.encounterIds.has(id)) throw new Error(`Encounter not found: "${id}"`);
34
+ }
35
+ }
36
+ consumeEncounters(ids) {
37
+ for (const id of ids) {
38
+ this.encounterIds.delete(id);
39
+ }
40
+ }
41
+ addExperience(id) {
42
+ this.experienceIds.add(id);
43
+ }
44
+ requireExperienceIds(ids) {
45
+ for (const id of ids) {
46
+ if (!this.experienceIds.has(id)) throw new Error(`Experience not found: "${id}"`);
47
+ }
48
+ }
49
+ consumeExperiences(ids) {
50
+ for (const id of ids) {
51
+ this.experienceIds.delete(id);
52
+ }
53
+ }
54
+ // ================================================================
55
+ // Rehydration — rebuild from activation state
56
+ // ================================================================
57
+ /** Walk the state tree and populate registries. */
58
+ rehydrate(state) {
59
+ this.walk(state);
60
+ }
61
+ walk(node) {
62
+ if (node.id) {
63
+ switch (node.name) {
64
+ case "goal":
65
+ if (!this.focusedGoalId) this.focusedGoalId = node.id;
66
+ break;
67
+ case "encounter":
68
+ this.encounterIds.add(node.id);
69
+ break;
70
+ case "experience":
71
+ this.experienceIds.add(node.id);
72
+ break;
73
+ }
74
+ }
75
+ for (const child of node.children ?? []) {
76
+ this.walk(child);
77
+ }
78
+ }
79
+ // ================================================================
80
+ // Cognitive hints — state-aware AI self-direction cues
81
+ // ================================================================
82
+ /** First-person, state-aware hint for the AI after an operation. */
83
+ cognitiveHint(process) {
84
+ switch (process) {
85
+ case "activate":
86
+ if (!this.focusedGoalId)
87
+ return "I have no goal yet. I should call `want` to declare one, or `focus` to review existing goals.";
88
+ return "I have an active goal. I should call `focus` to review progress, or `want` to declare a new goal.";
89
+ case "focus":
90
+ if (!this.focusedPlanId)
91
+ return "I have a goal but no focused plan. I should call `plan` to create or focus on one.";
92
+ return "I have a plan. I should call `todo` to create tasks, or continue working.";
93
+ case "want":
94
+ return "Goal declared. I should call `plan` to design how to achieve it.";
95
+ case "plan":
96
+ return "Plan created. I should call `todo` to create concrete tasks.";
97
+ case "todo":
98
+ return "Task created. I can add more with `todo`, or start working and call `finish` when done.";
99
+ case "finish": {
100
+ const encCount = this.encounterIds.size;
101
+ if (encCount > 0 && !this.focusedGoalId)
102
+ return `Task finished. No more goals \u2014 I have ${encCount} encounter(s) to choose from for \`reflect\`, or \`want\` a new goal.`;
103
+ return "Task finished. I should continue with remaining tasks, or call `complete` when the plan is done.";
104
+ }
105
+ case "complete":
106
+ case "abandon": {
107
+ const encCount = this.encounterIds.size;
108
+ const goalNote = this.focusedGoalId ? ` I should check if goal "${this.focusedGoalId}" needs a new \`plan\`, or \`forget\` it if the direction is fulfilled.` : "";
109
+ if (encCount > 0)
110
+ return `Plan closed.${goalNote} I have ${encCount} encounter(s) to choose from for \`reflect\`, or I can continue with other plans.`;
111
+ return `Plan closed.${goalNote} I can create a new \`plan\`, or \`focus\` on another goal.`;
112
+ }
113
+ case "reflect": {
114
+ const expCount = this.experienceIds.size;
115
+ if (expCount > 0)
116
+ return `Experience gained. I can \`realize\` principles or \`master\` procedures \u2014 ${expCount} experience(s) available.`;
117
+ return "Experience gained. I can `realize` a principle, `master` a procedure, or continue working.";
118
+ }
119
+ case "realize":
120
+ return "Principle added. I should continue working.";
121
+ case "master":
122
+ return "Procedure added. I should continue working.";
123
+ default:
124
+ return null;
125
+ }
126
+ }
127
+ };
128
+
129
+ // src/feature.ts
130
+ import { parse as parseGherkin } from "@rolexjs/parser";
131
+ function parse(source) {
132
+ const doc = parseGherkin(source);
133
+ const f = doc.feature;
134
+ if (!f) throw new Error("No Feature found in source");
135
+ return {
136
+ name: f.name,
137
+ ...f.description?.trim() ? { description: f.description.trim() } : {},
138
+ ...f.tags?.length ? { tags: f.tags.map((t) => t.name) } : {},
139
+ scenarios: (f.children ?? []).filter((c) => c.scenario).map((c) => {
140
+ const s = c.scenario;
141
+ return {
142
+ name: s.name,
143
+ ...s.description?.trim() ? { description: s.description.trim() } : {},
144
+ ...s.tags?.length ? { tags: s.tags.map((t) => t.name) } : {},
145
+ steps: (s.steps ?? []).map((st) => ({
146
+ keyword: st.keyword,
147
+ text: st.text,
148
+ ...st.dataTable ? {
149
+ dataTable: st.dataTable.rows.map((r) => ({
150
+ cells: r.cells.map((c2) => c2.value)
151
+ }))
152
+ } : {}
153
+ }))
154
+ };
155
+ })
156
+ };
157
+ }
158
+ function serialize(feature) {
159
+ const lines = [];
160
+ if (feature.tags?.length) {
161
+ lines.push(feature.tags.join(" "));
162
+ }
163
+ lines.push(`Feature: ${feature.name}`);
164
+ if (feature.description) {
165
+ for (const line of feature.description.split("\n")) {
166
+ lines.push(` ${line}`);
167
+ }
168
+ }
169
+ for (const scenario of feature.scenarios) {
170
+ lines.push("");
171
+ if (scenario.tags?.length) {
172
+ lines.push(` ${scenario.tags.join(" ")}`);
173
+ }
174
+ lines.push(` Scenario: ${scenario.name}`);
175
+ if (scenario.description) {
176
+ for (const line of scenario.description.split("\n")) {
177
+ lines.push(` ${line}`);
178
+ }
179
+ }
180
+ for (const step of scenario.steps) {
181
+ lines.push(` ${step.keyword}${step.text}`);
182
+ if (step.dataTable) {
183
+ for (const row of step.dataTable) {
184
+ lines.push(` | ${row.cells.join(" | ")} |`);
185
+ }
186
+ }
187
+ }
188
+ }
189
+ return `${lines.join("\n")}
190
+ `;
191
+ }
192
+
193
+ // src/find.ts
194
+ var PRIORITY = {
195
+ individual: 0,
196
+ organization: 0,
197
+ position: 0,
198
+ goal: 1,
199
+ plan: 2,
200
+ task: 2,
201
+ procedure: 3,
202
+ principle: 3,
203
+ encounter: 4,
204
+ experience: 4,
205
+ identity: 5,
206
+ charter: 5,
207
+ duty: 6,
208
+ requirement: 6,
209
+ background: 6,
210
+ tone: 6,
211
+ mindset: 6
212
+ };
213
+ function priorityOf(name) {
214
+ return PRIORITY[name] ?? 7;
215
+ }
216
+ function matches(node, target) {
217
+ if (node.id?.toLowerCase() === target) return true;
218
+ if (node.alias) {
219
+ for (const a of node.alias) {
220
+ if (a.toLowerCase() === target) return true;
221
+ }
222
+ }
223
+ return false;
224
+ }
225
+ function findInState(state, target) {
226
+ const lowered = target.toLowerCase();
227
+ let best = null;
228
+ let bestPriority = Infinity;
229
+ function walk(node) {
230
+ if (matches(node, lowered)) {
231
+ const p = priorityOf(node.name);
232
+ if (p < bestPriority) {
233
+ best = node;
234
+ bestPriority = p;
235
+ if (p === 0) return;
236
+ }
237
+ }
238
+ for (const child of node.children ?? []) {
239
+ walk(child);
240
+ if (bestPriority === 0) return;
241
+ }
242
+ }
243
+ walk(state);
244
+ return best;
245
+ }
246
+
247
+ // src/issue-render.ts
248
+ function renderIssue(issue, labelNames) {
249
+ const lines = [];
250
+ lines.push(`#${issue.number} ${issue.title} [${issue.status}]`);
251
+ const meta = [`Author: ${issue.author}`];
252
+ if (issue.assignee) meta.push(`Assignee: ${issue.assignee}`);
253
+ if (labelNames && labelNames.length > 0) {
254
+ meta.push(`Labels: ${labelNames.join(", ")}`);
255
+ }
256
+ lines.push(meta.join(" | "));
257
+ if (issue.body) {
258
+ lines.push("\u2500\u2500\u2500");
259
+ lines.push(issue.body);
260
+ }
261
+ return lines.join("\n");
262
+ }
263
+ function renderIssueList(issues) {
264
+ if (issues.length === 0) return "No issues found.";
265
+ const lines = [];
266
+ for (const issue of issues) {
267
+ const assignee = issue.assignee ? ` \u2192 ${issue.assignee}` : "";
268
+ lines.push(`#${issue.number} [${issue.status}] ${issue.title} (${issue.author}${assignee})`);
269
+ }
270
+ return lines.join("\n");
271
+ }
272
+ function renderComment(comment) {
273
+ const time = formatTime(comment.createdAt);
274
+ return `${comment.author} (${time}):
275
+ ${comment.body}`;
276
+ }
277
+ function renderCommentList(comments) {
278
+ if (comments.length === 0) return "No comments.";
279
+ return comments.map(renderComment).join("\n\u2500\u2500\u2500\n");
280
+ }
281
+ var statusTemplates = {
282
+ publish: (i) => `Issue #${i.number} created.`,
283
+ close: (i) => `Issue #${i.number} closed.`,
284
+ reopen: (i) => `Issue #${i.number} reopened.`,
285
+ update: (i) => `Issue #${i.number} updated.`,
286
+ assign: (i) => `Issue #${i.number} assigned to ${i.assignee ?? "nobody"}.`,
287
+ comment: (i) => `Comment added to #${i.number}.`,
288
+ label: (i) => `Label added to #${i.number}.`,
289
+ unlabel: (i) => `Label removed from #${i.number}.`
290
+ };
291
+ async function renderIssueResult(action, result, resolveLabels) {
292
+ switch (action) {
293
+ case "list":
294
+ return renderIssueList(result);
295
+ case "comments":
296
+ return renderCommentList(result);
297
+ case "comment": {
298
+ const comment = result;
299
+ return `Comment added to issue.
300
+
301
+ ${renderComment(comment)}`;
302
+ }
303
+ case "get": {
304
+ if (!result) return "Issue not found.";
305
+ const issue = result;
306
+ const labelNames = await resolveLabelNames(issue, resolveLabels);
307
+ return renderIssue(issue, labelNames);
308
+ }
309
+ default: {
310
+ const issue = result;
311
+ const labelNames = await resolveLabelNames(issue, resolveLabels);
312
+ const status = statusTemplates[action]?.(issue) ?? `Issue #${issue.number} ${action}.`;
313
+ return `${status}
314
+
315
+ ${renderIssue(issue, labelNames)}`;
316
+ }
317
+ }
318
+ }
319
+ function formatTime(iso) {
320
+ return iso.replace("T", " ").replace(/\.\d+Z$/, "");
321
+ }
322
+ async function resolveLabelNames(issue, resolveLabels) {
323
+ if (!issue.labels || issue.labels.length === 0) return void 0;
324
+ if (!resolveLabels) return void 0;
325
+ return resolveLabels(issue.labels);
326
+ }
327
+
4
328
  // src/render.ts
5
- import { directives, processes, world } from "@rolexjs/core";
329
+ import { directives, processes, world } from "@rolexjs/prototype";
6
330
  var descriptions = {
7
331
  // Lifecycle
8
332
  born: (n) => `Individual "${n}" is born.`,
@@ -30,20 +354,6 @@ var descriptions = {
30
354
  deliver: (n) => `Deliverable "${n}" added.`,
31
355
  wiki: (n) => `Wiki entry "${n}" added.`,
32
356
  archive: (n) => `Project "${n}" archived.`,
33
- produce: (n) => `Product "${n}" produced.`,
34
- // Product
35
- strategy: (n) => `Strategy defined for "${n}".`,
36
- spec: (n) => `Spec "${n}" added.`,
37
- release: (n) => `Release "${n}" published.`,
38
- channel: (n) => `Channel "${n}" added.`,
39
- own: (n) => `Owner assigned to "${n}".`,
40
- disown: (n) => `Owner removed from "${n}".`,
41
- deprecate: (n) => `Product "${n}" deprecated.`,
42
- // Society
43
- crown: (n) => `"${n}" crowned \u2014 sovereign permissions granted.`,
44
- uncrown: (n) => `"${n}" uncrowned \u2014 sovereign permissions revoked.`,
45
- // Top-level perception
46
- inspect: (n) => `Inspecting "${n}".`,
47
357
  // Role
48
358
  activate: (n) => `Role "${n}" activated.`,
49
359
  focus: (n) => `Focused on goal "${n}".`,
@@ -92,20 +402,6 @@ var hints = {
92
402
  deliver: "deliverable recorded.",
93
403
  wiki: "knowledge entry recorded.",
94
404
  archive: "the project is archived.",
95
- produce: "define strategy, add specs, or assign an owner.",
96
- // Product
97
- strategy: "add behavior specs (BDD contracts) for the product.",
98
- spec: "add more specs, or publish a release.",
99
- release: "add distribution channels, or continue adding specs.",
100
- channel: "distribution channel recorded.",
101
- own: "the individual is now the product owner.",
102
- disown: "the individual is no longer the product owner.",
103
- deprecate: "the product is deprecated.",
104
- // Society
105
- crown: "the individual now has sovereign permissions over society.",
106
- uncrown: "the individual no longer has sovereign permissions.",
107
- // Top-level perception
108
- inspect: "use inspect on child nodes for deeper detail, or activate to work as a role.",
109
405
  // Role
110
406
  activate: "want a goal, or check the current state.",
111
407
  focus: "plan how to work toward it, or add tasks.",
@@ -127,13 +423,19 @@ function hint(process) {
127
423
  const h = hints[process];
128
424
  return h ? `Next: ${h}` : "What would you like to do next?";
129
425
  }
426
+ function detail(process) {
427
+ return processes[process] ?? "";
428
+ }
429
+ function directive(topic, scenario) {
430
+ return directives[topic]?.[scenario] ?? "";
431
+ }
130
432
  function renderState(state, depth = 1, options) {
131
433
  const lines = [];
132
434
  const level = Math.min(depth, 6);
133
435
  const heading = "#".repeat(level);
134
436
  const idPart = state.id ? ` (${state.id})` : "";
135
437
  const originPart = state.origin ? ` {${state.origin}}` : "";
136
- const tagPart = state.tags?.length ? ` ${state.tags.map((t) => `#${t}`).join(" ")}` : "";
438
+ const tagPart = state.tag ? ` #${state.tag}` : "";
137
439
  const progressPart = state.name === "goal" ? goalProgress(state) : "";
138
440
  lines.push(`${heading} [${state.name}]${idPart}${originPart}${tagPart}${progressPart}`);
139
441
  if (options?.fold?.(state)) {
@@ -145,12 +447,11 @@ function renderState(state, depth = 1, options) {
145
447
  }
146
448
  if (state.links && state.links.length > 0) {
147
449
  const compactRelations = /* @__PURE__ */ new Set(["after", "before", "fallback", "fallback-for"]);
148
- const allCompact = options?.compactLinks;
149
- const compact = allCompact ? state.links : state.links.filter((l) => compactRelations.has(l.relation));
150
- const expanded = allCompact ? [] : state.links.filter((l) => !compactRelations.has(l.relation));
450
+ const compact = state.links.filter((l) => compactRelations.has(l.relation));
451
+ const expanded = state.links.filter((l) => !compactRelations.has(l.relation));
151
452
  for (const link of compact) {
152
453
  const targetId = link.target.id ? ` (${link.target.id})` : "";
153
- const targetTag = link.target.tags?.length ? ` ${link.target.tags.map((t) => `#${t}`).join(" ")}` : "";
454
+ const targetTag = link.target.tag ? ` #${link.target.tag}` : "";
154
455
  lines.push(`> ${link.relation}: [${link.target.name}]${targetId}${targetTag}`);
155
456
  }
156
457
  if (expanded.length > 0) {
@@ -170,41 +471,6 @@ function renderState(state, depth = 1, options) {
170
471
  }
171
472
  return lines.join("\n");
172
473
  }
173
- function collectPermissions(state) {
174
- const seen = /* @__PURE__ */ new Set();
175
- const result = [];
176
- const walk = (s) => {
177
- if (s.links) {
178
- for (const link of s.links) {
179
- if (link.permissions) {
180
- for (const p of link.permissions) {
181
- if (!seen.has(p.command)) {
182
- seen.add(p.command);
183
- result.push(p);
184
- }
185
- }
186
- }
187
- }
188
- }
189
- if (s.children) {
190
- for (const child of s.children) {
191
- walk(child);
192
- }
193
- }
194
- };
195
- walk(state);
196
- return result;
197
- }
198
- function renderPermissions(permissions) {
199
- if (permissions.length === 0) return "";
200
- const lines = [];
201
- lines.push("# Permissions\n");
202
- for (const p of permissions) {
203
- lines.push(p.content);
204
- lines.push("");
205
- }
206
- return lines.join("\n");
207
- }
208
474
  var CONCEPT_ORDER = [
209
475
  // Individual — Identity
210
476
  "identity",
@@ -230,12 +496,7 @@ var CONCEPT_ORDER = [
230
496
  "scope",
231
497
  "milestone",
232
498
  "deliverable",
233
- "wiki",
234
- // Product
235
- "strategy",
236
- "spec",
237
- "release",
238
- "channel"
499
+ "wiki"
239
500
  ];
240
501
  function goalProgress(goal) {
241
502
  let plans = 0;
@@ -245,10 +506,10 @@ function goalProgress(goal) {
245
506
  function walk(node) {
246
507
  if (node.name === "plan") {
247
508
  plans++;
248
- if (node.tags?.includes("done") || node.tags?.includes("abandoned")) plansDone++;
509
+ if (node.tag === "done" || node.tag === "abandoned") plansDone++;
249
510
  } else if (node.name === "task") {
250
511
  tasks++;
251
- if (node.tags?.includes("done")) tasksDone++;
512
+ if (node.tag === "done") tasksDone++;
252
513
  }
253
514
  for (const child of node.children ?? []) walk(child);
254
515
  }
@@ -271,92 +532,25 @@ function sortByConceptOrder(children) {
271
532
  return aOrder - bOrder;
272
533
  });
273
534
  }
274
-
275
- // src/product-render.ts
276
- function renderProduct(state) {
277
- const lines = [];
278
- const id = state.id ?? "(no id)";
279
- const tagPart = state.tags?.length ? ` ${state.tags.map((t) => `#${t}`).join(" ")}` : "";
280
- lines.push(`# ${id}${tagPart}`);
281
- if (state.information) {
282
- lines.push("");
283
- lines.push(state.information);
284
- }
285
- const owners = state.links?.filter((l) => l.relation === "ownership") ?? [];
286
- if (owners.length > 0) {
287
- lines.push("");
288
- lines.push("## Owner");
289
- for (const o of owners) {
290
- const alias = Array.isArray(o.target.alias) && o.target.alias.length ? ` (${o.target.alias.join(", ")})` : "";
291
- lines.push(`- ${o.target.id ?? "(no id)"}${alias}`);
292
- }
293
- }
294
- const children = state.children ?? [];
295
- const strategies = children.filter((c) => c.name === "strategy");
296
- const specs = children.filter((c) => c.name === "spec");
297
- const releases = children.filter((c) => c.name === "release");
298
- const channels = children.filter((c) => c.name === "channel");
299
- if (strategies.length > 0) {
300
- lines.push("");
301
- lines.push("## Strategy");
302
- for (const s of strategies) {
303
- if (s.information) lines.push(s.information);
304
- }
305
- }
306
- if (specs.length > 0) {
307
- lines.push("");
308
- lines.push("## Specs");
309
- for (const s of specs) {
310
- const title = s.id ?? extractFeatureTitle(s.information);
311
- lines.push(`- ${title}`);
312
- }
313
- }
314
- if (releases.length > 0) {
315
- lines.push("");
316
- lines.push("## Releases");
317
- for (const r of releases) {
318
- const rTag = r.tags?.length ? ` ${r.tags.map((t) => `#${t}`).join(" ")}` : "";
319
- const title = r.id ?? extractFeatureTitle(r.information);
320
- lines.push(`- ${title}${rTag}`);
321
- }
322
- }
323
- if (channels.length > 0) {
324
- lines.push("");
325
- lines.push("## Channels");
326
- for (const c of channels) {
327
- const title = c.id ?? extractFeatureTitle(c.information);
328
- lines.push(`- ${title}`);
329
- }
330
- }
331
- return lines.join("\n");
332
- }
333
- function renderProductResult(action, state) {
334
- const name = state.id ?? state.name;
535
+ function render(opts) {
536
+ const { process, name, state, cognitiveHint, fold } = opts;
335
537
  const lines = [];
336
- lines.push(describe(action, name, state));
337
- lines.push(hint(action));
338
- const productState = isProductNode(state) ? state : null;
339
- if (productState) {
340
- lines.push("");
341
- lines.push(renderProduct(productState));
538
+ lines.push(describe(process, name, state));
539
+ lines.push(hint(process));
540
+ if (cognitiveHint) {
541
+ lines.push(`I \u2192 ${cognitiveHint}`);
342
542
  }
543
+ lines.push("");
544
+ lines.push(renderState(state, 1, fold ? { fold } : void 0));
343
545
  return lines.join("\n");
344
546
  }
345
- function isProductNode(state) {
346
- return state.name === "product";
347
- }
348
- function extractFeatureTitle(information) {
349
- if (!information) return "(untitled)";
350
- const match = information.match(/^Feature:\s*(.+)$/m);
351
- return match?.[1]?.trim() ?? information.split("\n")[0].trim();
352
- }
353
547
 
354
548
  // src/project-render.ts
355
549
  function renderProject(state) {
356
550
  const lines = [];
357
551
  const id = state.id ?? "(no id)";
358
- const tagPart = state.tags?.length ? ` ${state.tags.map((t) => `#${t}`).join(" ")}` : "";
359
- lines.push(`# ${id}${tagPart}`);
552
+ const tag = state.tag ? ` #${state.tag}` : "";
553
+ lines.push(`# ${id}${tag}`);
360
554
  if (state.information) {
361
555
  lines.push("");
362
556
  lines.push(state.information);
@@ -366,7 +560,7 @@ function renderProject(state) {
366
560
  lines.push("");
367
561
  lines.push("## Members");
368
562
  for (const m of members) {
369
- const alias = Array.isArray(m.target.alias) && m.target.alias.length ? ` (${m.target.alias.join(", ")})` : "";
563
+ const alias = m.target.alias?.length ? ` (${m.target.alias.join(", ")})` : "";
370
564
  lines.push(`- ${m.target.id ?? "(no id)"}${alias}`);
371
565
  }
372
566
  }
@@ -386,12 +580,12 @@ function renderProject(state) {
386
580
  lines.push("");
387
581
  lines.push("## Milestones");
388
582
  for (const m of milestones) {
389
- const mTag = m.tags?.length ? ` ${m.tags.map((t) => `#${t}`).join(" ")}` : "";
390
- const marker = m.tags?.includes("done") ? "[x]" : "[ ]";
391
- const title = extractFeatureTitle2(m.information);
392
- lines.push(`- ${marker} ${m.id ?? title}${mTag}`);
583
+ const tag2 = m.tag ? ` #${m.tag}` : "";
584
+ const marker = m.tag === "done" ? "[x]" : "[ ]";
585
+ const title = extractFeatureTitle(m.information);
586
+ lines.push(`- ${marker} ${m.id ?? title}${tag2}`);
393
587
  if (m.information && m.id) {
394
- const desc = extractFeatureTitle2(m.information);
588
+ const desc = extractFeatureTitle(m.information);
395
589
  if (desc && desc !== m.id) lines.push(` ${desc}`);
396
590
  }
397
591
  }
@@ -400,7 +594,7 @@ function renderProject(state) {
400
594
  lines.push("");
401
595
  lines.push("## Deliverables");
402
596
  for (const d of deliverables) {
403
- const title = d.id ?? extractFeatureTitle2(d.information);
597
+ const title = d.id ?? extractFeatureTitle(d.information);
404
598
  lines.push(`- ${title}`);
405
599
  }
406
600
  }
@@ -408,7 +602,7 @@ function renderProject(state) {
408
602
  lines.push("");
409
603
  lines.push("## Wiki");
410
604
  for (const w of wikis) {
411
- const title = w.id ?? extractFeatureTitle2(w.information);
605
+ const title = w.id ?? extractFeatureTitle(w.information);
412
606
  lines.push(`- ${title}`);
413
607
  }
414
608
  }
@@ -429,207 +623,361 @@ function renderProjectResult(action, state) {
429
623
  function isProjectNode(state) {
430
624
  return state.name === "project";
431
625
  }
432
- function extractFeatureTitle2(information) {
626
+ function extractFeatureTitle(information) {
433
627
  if (!information) return "(untitled)";
434
628
  const match = information.match(/^Feature:\s*(.+)$/m);
435
629
  return match?.[1]?.trim() ?? information.split("\n")[0].trim();
436
630
  }
437
631
 
438
- // src/rolex.ts
439
- import { createBuilder } from "@rolexjs/core";
440
- import { genesis } from "@rolexjs/genesis";
441
-
442
- // src/renderers/index.ts
443
- import { RendererRouter } from "@rolexjs/core";
444
-
445
- // src/renderers/inspect.ts
446
- var InspectRenderer = class {
447
- render(_command, result) {
448
- const name = result.state.id ?? result.state.name;
449
- const lines = [];
450
- lines.push(describe(result.process, name, result.state));
451
- lines.push(hint(result.process));
452
- lines.push("");
453
- lines.push(renderState(result.state, 1, { compactLinks: true }));
454
- return lines.join("\n");
632
+ // src/role.ts
633
+ var Role = class {
634
+ roleId;
635
+ ctx;
636
+ api;
637
+ constructor(roleId, ctx, api) {
638
+ this.roleId = roleId;
639
+ this.ctx = ctx;
640
+ this.api = api;
455
641
  }
456
- };
457
-
458
- // src/renderers/org.ts
459
- var OrgRenderer = class {
460
- render(command, result) {
461
- const process = command.startsWith("org.") ? command.slice("org.".length) : command;
462
- const name = result.state.id ?? result.state.name;
463
- const lines = [];
464
- lines.push(describe(process, name, result.state));
465
- lines.push(hint(process));
466
- lines.push("");
467
- lines.push(renderState(result.state));
468
- return lines.join("\n");
642
+ /** Project the individual's full state tree (used after activate). */
643
+ async project() {
644
+ const result = await this.api.ops["role.focus"](this.roleId);
645
+ const focusedGoalId = this.ctx.focusedGoalId;
646
+ return this.fmt("activate", this.roleId, result, {
647
+ fold: (node) => node.name === "goal" && node.id !== focusedGoalId || node.name === "requirement"
648
+ });
469
649
  }
470
- };
471
-
472
- // src/renderers/position.ts
473
- var PositionRenderer = class {
474
- render(command, result) {
475
- const process = command.startsWith("position.") ? command.slice("position.".length) : command;
476
- const name = result.state.id ?? result.state.name;
477
- const lines = [];
478
- lines.push(describe(process, name, result.state));
479
- lines.push(hint(process));
480
- lines.push("");
481
- lines.push(renderState(result.state));
482
- return lines.join("\n");
650
+ /** Render an OpResult into a 3-layer output string. */
651
+ fmt(process, name, result, extra) {
652
+ return render({
653
+ process,
654
+ name,
655
+ state: result.state,
656
+ cognitiveHint: this.ctx.cognitiveHint(process) ?? null,
657
+ fold: extra?.fold
658
+ });
483
659
  }
484
- };
485
-
486
- // src/renderers/product.ts
487
- var ProductRenderer = class {
488
- render(command, result) {
489
- const action = command.startsWith("product.") ? command.slice("product.".length) : command;
490
- return renderProductResult(action, result.state);
660
+ async save() {
661
+ await this.api.saveCtx(this.ctx);
491
662
  }
492
- };
493
-
494
- // src/renderers/project.ts
495
- var ProjectRenderer = class {
496
- render(command, result) {
497
- const action = command.startsWith("project.") ? command.slice("project.".length) : command;
498
- if (action === "produce") {
499
- return renderProductResult(action, result.state);
663
+ // ---- Execution ----
664
+ /** Focus: view or switch focused goal. Only accepts goal ids. */
665
+ async focus(goal) {
666
+ const goalId = goal ?? this.ctx.requireGoalId();
667
+ const result = await this.api.ops["role.focus"](goalId);
668
+ if (result.state.name !== "goal") {
669
+ throw new Error(
670
+ `"${goalId}" is a ${result.state.name}, not a goal. focus only accepts goal ids.`
671
+ );
500
672
  }
501
- return renderProjectResult(action, result.state);
673
+ const switched = goalId !== this.ctx.focusedGoalId;
674
+ this.ctx.focusedGoalId = goalId;
675
+ if (switched) this.ctx.focusedPlanId = null;
676
+ await this.save();
677
+ return this.fmt("focus", goalId, result);
502
678
  }
503
- };
504
-
505
- // src/renderers/role.ts
506
- var RoleRenderer = class {
507
- render(command, result) {
508
- const process = command.startsWith("role.") ? command.slice("role.".length) : command;
509
- const name = result.state.id ?? result.state.name;
510
- const lines = [];
511
- lines.push(describe(process, name, result.state));
512
- lines.push(hint(process));
513
- lines.push("");
514
- lines.push(renderState(result.state));
515
- if (process === "activate") {
516
- const permissions = collectPermissions(result.state);
517
- if (permissions.length > 0) {
518
- lines.push("");
519
- lines.push(renderPermissions(permissions));
679
+ /** Want: declare a goal. */
680
+ async want(goal, id, alias) {
681
+ const result = await this.api.ops["role.want"](this.roleId, goal, id, alias);
682
+ if (id) this.ctx.focusedGoalId = id;
683
+ this.ctx.focusedPlanId = null;
684
+ await this.save();
685
+ return this.fmt("want", id ?? this.roleId, result);
686
+ }
687
+ /** Plan: create a plan for the focused goal. */
688
+ async plan(plan, id, after, fallback) {
689
+ const result = await this.api.ops["role.plan"](
690
+ this.ctx.requireGoalId(),
691
+ plan,
692
+ id,
693
+ after,
694
+ fallback
695
+ );
696
+ if (id) this.ctx.focusedPlanId = id;
697
+ await this.save();
698
+ return this.fmt("plan", id ?? "plan", result);
699
+ }
700
+ /** Todo: add a task to the focused plan. */
701
+ async todo(task, id, alias) {
702
+ const result = await this.api.ops["role.todo"](this.ctx.requirePlanId(), task, id, alias);
703
+ return this.fmt("todo", id ?? "task", result);
704
+ }
705
+ /** Finish: complete a task, optionally record an encounter. */
706
+ async finish(task, encounter) {
707
+ const result = await this.api.ops["role.finish"](task, this.roleId, encounter);
708
+ if (encounter && result.state.id) {
709
+ this.ctx.addEncounter(result.state.id);
710
+ }
711
+ return this.fmt("finish", task, result);
712
+ }
713
+ /** Complete: close a plan as done, record encounter. */
714
+ async complete(plan, encounter) {
715
+ const planId = plan ?? this.ctx.requirePlanId();
716
+ const result = await this.api.ops["role.complete"](planId, this.roleId, encounter);
717
+ this.ctx.addEncounter(result.state.id ?? planId);
718
+ if (this.ctx.focusedPlanId === planId) this.ctx.focusedPlanId = null;
719
+ await this.save();
720
+ return this.fmt("complete", planId, result);
721
+ }
722
+ /** Abandon: drop a plan, record encounter. */
723
+ async abandon(plan, encounter) {
724
+ const planId = plan ?? this.ctx.requirePlanId();
725
+ const result = await this.api.ops["role.abandon"](planId, this.roleId, encounter);
726
+ this.ctx.addEncounter(result.state.id ?? planId);
727
+ if (this.ctx.focusedPlanId === planId) this.ctx.focusedPlanId = null;
728
+ await this.save();
729
+ return this.fmt("abandon", planId, result);
730
+ }
731
+ // ---- Cognition ----
732
+ /** Reflect: consume encounters → experience. Empty encounters = direct creation. */
733
+ async reflect(encounters, experience, id) {
734
+ if (encounters.length > 0) {
735
+ this.ctx.requireEncounterIds(encounters);
736
+ }
737
+ const first = encounters[0];
738
+ const result = await this.api.ops["role.reflect"](first, this.roleId, experience, id);
739
+ for (let i = 1; i < encounters.length; i++) {
740
+ await this.api.ops["role.forget"](encounters[i]);
741
+ }
742
+ if (encounters.length > 0) {
743
+ this.ctx.consumeEncounters(encounters);
744
+ }
745
+ if (id) this.ctx.addExperience(id);
746
+ return this.fmt("reflect", id ?? "experience", result);
747
+ }
748
+ /** Realize: consume experiences → principle. Empty experiences = direct creation. */
749
+ async realize(experiences, principle, id) {
750
+ if (experiences.length > 0) {
751
+ this.ctx.requireExperienceIds(experiences);
752
+ }
753
+ const first = experiences[0];
754
+ const result = await this.api.ops["role.realize"](first, this.roleId, principle, id);
755
+ for (let i = 1; i < experiences.length; i++) {
756
+ await this.api.ops["role.forget"](experiences[i]);
757
+ }
758
+ if (experiences.length > 0) {
759
+ this.ctx.consumeExperiences(experiences);
760
+ }
761
+ return this.fmt("realize", id ?? "principle", result);
762
+ }
763
+ /** Master: create procedure, optionally consuming experiences. */
764
+ async master(procedure, id, experiences) {
765
+ if (experiences && experiences.length > 0) {
766
+ this.ctx.requireExperienceIds(experiences);
767
+ }
768
+ const first = experiences?.[0];
769
+ const result = await this.api.ops["role.master"](this.roleId, procedure, id, first);
770
+ if (experiences) {
771
+ for (let i = 1; i < experiences.length; i++) {
772
+ await this.api.ops["role.forget"](experiences[i]);
520
773
  }
774
+ this.ctx.consumeExperiences(experiences);
521
775
  }
522
- return lines.join("\n");
776
+ return this.fmt("master", id ?? "procedure", result);
523
777
  }
524
- };
525
-
526
- // src/renderers/society.ts
527
- var SocietyRenderer = class {
528
- render(command, result) {
529
- const process = command.startsWith("society.") ? command.slice("society.".length) : command;
530
- const name = result.state.id ?? result.state.name;
531
- const lines = [];
532
- lines.push(describe(process, name, result.state));
533
- lines.push(hint(process));
534
- lines.push("");
535
- lines.push(renderState(result.state));
536
- return lines.join("\n");
778
+ // ---- Knowledge management ----
779
+ /** Forget: remove any node under the individual by id. */
780
+ async forget(nodeId) {
781
+ const result = await this.api.ops["role.forget"](nodeId);
782
+ if (this.ctx.focusedGoalId === nodeId) this.ctx.focusedGoalId = null;
783
+ if (this.ctx.focusedPlanId === nodeId) this.ctx.focusedPlanId = null;
784
+ await this.save();
785
+ return this.fmt("forget", nodeId, result);
786
+ }
787
+ // ---- Skills + unified entry ----
788
+ /** Skill: load full skill content by locator. */
789
+ async skill(locator) {
790
+ return await this.api.ops["role.skill"](locator);
791
+ }
792
+ /** Use: subjective execution — `!ns.method` or ResourceX locator. */
793
+ async use(locator, args) {
794
+ const result = await this.api.direct(locator, args);
795
+ if (locator.startsWith("!issue.")) {
796
+ const action = locator.slice("!issue.".length);
797
+ return await renderIssueResult(action, result, this.api.resolveLabels);
798
+ }
799
+ if (locator.startsWith("!project.")) {
800
+ const action = locator.slice("!project.".length);
801
+ const opResult = result;
802
+ return renderProjectResult(action, opResult.state);
803
+ }
804
+ return result;
537
805
  }
538
806
  };
539
807
 
540
- // src/renderers/survey.ts
541
- var SurveyRenderer = class {
542
- render(_command, result) {
543
- const children = result.state.children ?? [];
544
- if (children.length === 0) {
545
- return "Society is empty.";
546
- }
547
- const types = new Set(children.map((c) => c.name));
548
- if (types.size === 1 && !types.has("organization")) {
549
- return this.renderFlat(children);
550
- }
551
- return this.renderTree(children);
552
- }
553
- renderFlat(items) {
554
- const lines = [];
555
- for (const item of items) {
556
- const tag = item.tags?.length ? ` ${item.tags.map((t) => `#${t}`).join(" ")}` : "";
557
- const alias = Array.isArray(item.alias) && item.alias.length ? ` (${item.alias.join(", ")})` : "";
558
- lines.push(`${item.id ?? "(no id)"}${alias}${tag}`);
808
+ // src/rolex.ts
809
+ import * as C from "@rolexjs/core";
810
+ import { createOps, directives as directives2, toArgs } from "@rolexjs/prototype";
811
+ import { createIssueX } from "issuexjs";
812
+ import { createResourceX, setProvider } from "resourcexjs";
813
+ var Rolex = class _Rolex {
814
+ rt;
815
+ ops;
816
+ resourcex;
817
+ issuex;
818
+ repo;
819
+ initializer;
820
+ bootstrap;
821
+ society;
822
+ past;
823
+ constructor(platform) {
824
+ this.repo = platform.repository;
825
+ this.rt = this.repo.runtime;
826
+ this.initializer = platform.initializer;
827
+ this.bootstrap = platform.bootstrap ?? [];
828
+ if (platform.resourcexProvider) {
829
+ setProvider(platform.resourcexProvider);
830
+ this.resourcex = createResourceX(
831
+ platform.resourcexExecutor ? { isolator: "custom", executor: platform.resourcexExecutor } : void 0
832
+ );
833
+ }
834
+ if (platform.issuexProvider) {
835
+ this.issuex = createIssueX({ provider: platform.issuexProvider });
559
836
  }
560
- return lines.join("\n");
561
837
  }
562
- renderTree(children) {
563
- const orgs = children.filter((c) => c.name === "organization");
564
- const individuals = children.filter((c) => c.name === "individual");
565
- const individualPositions = /* @__PURE__ */ new Map();
566
- for (const ind of individuals) {
567
- const serves = ind.links?.filter((l) => l.relation === "serve") ?? [];
568
- if (serves.length > 0) {
569
- individualPositions.set(
570
- ind.id ?? "",
571
- serves.map((l) => l.target.id ?? "(no id)")
572
- );
573
- }
838
+ /** Create a Rolex instance from a Platform (async due to Runtime initialization). */
839
+ static async create(platform) {
840
+ const rolex = new _Rolex(platform);
841
+ await rolex.init();
842
+ return rolex;
843
+ }
844
+ /** Async initialization — called by Rolex.create(). */
845
+ async init() {
846
+ const roots = await this.rt.roots();
847
+ this.society = roots.find((r) => r.name === "society") ?? await this.rt.create(null, C.society);
848
+ const societyState = await this.rt.project(this.society);
849
+ const existingPast = societyState.children?.find((c) => c.name === "past");
850
+ this.past = existingPast ?? await this.rt.create(this.society, C.past);
851
+ this.ops = createOps({
852
+ rt: this.rt,
853
+ society: this.society,
854
+ past: this.past,
855
+ resolve: async (id) => {
856
+ const node = await this.find(id);
857
+ if (!node) throw new Error(`"${id}" not found.`);
858
+ return node;
859
+ },
860
+ find: (id) => this.find(id),
861
+ resourcex: this.resourcex,
862
+ issuex: this.issuex,
863
+ prototype: this.repo.prototype,
864
+ direct: (locator, args) => this.direct(locator, args)
865
+ });
866
+ }
867
+ /** Genesis — create the world on first run. Settles built-in prototypes. */
868
+ async genesis() {
869
+ await this.initializer?.bootstrap();
870
+ for (const source of this.bootstrap) {
871
+ await this.direct("!prototype.settle", { source });
574
872
  }
575
- const affiliatedIndividuals = /* @__PURE__ */ new Set();
576
- const lines = [];
577
- for (const org of orgs) {
578
- const alias = Array.isArray(org.alias) && org.alias.length ? ` (${org.alias.join(", ")})` : "";
579
- const tag = org.tags?.length ? ` ${org.tags.map((t) => `#${t}`).join(" ")}` : "";
580
- lines.push(`${org.id}${alias}${tag}`);
581
- const projects = org.links?.filter((l) => l.relation === "project") ?? [];
582
- for (const p of projects) {
583
- const pAlias = Array.isArray(p.target.alias) && p.target.alias.length ? ` (${p.target.alias.join(", ")})` : "";
584
- const pTag = p.target.tags?.length ? ` ${p.target.tags.map((t) => `#${t}`).join(" ")}` : "";
585
- lines.push(` \u{1F4E6} ${p.target.id ?? "(no id)"}${pAlias}${pTag}`);
873
+ }
874
+ /**
875
+ * Activate a role returns a stateful Role handle.
876
+ *
877
+ * If the individual does not exist in runtime but a prototype is registered,
878
+ * auto-born the individual first.
879
+ */
880
+ async activate(individual) {
881
+ let node = await this.find(individual);
882
+ if (!node) {
883
+ const hasProto = Object.hasOwn(this.repo.prototype.list(), individual);
884
+ if (hasProto) {
885
+ await this.ops["individual.born"](void 0, individual);
886
+ node = await this.find(individual);
887
+ } else {
888
+ throw new Error(`"${individual}" not found.`);
586
889
  }
587
- const members = org.links?.filter((l) => l.relation === "membership") ?? [];
588
- if (members.length === 0 && projects.length === 0) {
589
- lines.push(" (empty)");
890
+ }
891
+ const state = await this.rt.project(node);
892
+ const ctx = new RoleContext(individual);
893
+ ctx.rehydrate(state);
894
+ const persisted = await this.repo.loadContext(individual);
895
+ if (persisted) {
896
+ if (persisted.focusedGoalId && await this.find(persisted.focusedGoalId)) {
897
+ ctx.focusedGoalId = persisted.focusedGoalId;
590
898
  }
591
- for (const m of members) {
592
- affiliatedIndividuals.add(m.target.id ?? "");
593
- const mAlias = Array.isArray(m.target.alias) && m.target.alias.length ? ` (${m.target.alias.join(", ")})` : "";
594
- const mTag = m.target.tags?.length ? ` ${m.target.tags.map((t) => `#${t}`).join(" ")}` : "";
595
- const posLabels = individualPositions.get(m.target.id ?? "");
596
- const posStr = posLabels?.length ? ` \u2014 ${posLabels.join(", ")}` : "";
597
- lines.push(` ${m.target.id}${mAlias}${mTag}${posStr}`);
899
+ if (persisted.focusedPlanId && await this.find(persisted.focusedPlanId)) {
900
+ ctx.focusedPlanId = persisted.focusedPlanId;
598
901
  }
599
- lines.push("");
600
902
  }
601
- const unaffiliated = individuals.filter((ind) => !affiliatedIndividuals.has(ind.id ?? ""));
602
- if (unaffiliated.length > 0) {
603
- lines.push("\u2500\u2500\u2500 unaffiliated \u2500\u2500\u2500");
604
- for (const ind of unaffiliated) {
605
- const alias = Array.isArray(ind.alias) && ind.alias.length ? ` (${ind.alias.join(", ")})` : "";
606
- const tag = ind.tags?.length ? ` ${ind.tags.map((t) => `#${t}`).join(" ")}` : "";
607
- const posLabels = individualPositions.get(ind.id ?? "");
608
- const posStr = posLabels?.length ? ` \u2014 ${posLabels.join(", ")}` : "";
609
- lines.push(` ${ind.id}${alias}${tag}${posStr}`);
903
+ const ops = this.ops;
904
+ const repo = this.repo;
905
+ const saveCtx = async (c) => {
906
+ await repo.saveContext(c.roleId, {
907
+ focusedGoalId: c.focusedGoalId,
908
+ focusedPlanId: c.focusedPlanId
909
+ });
910
+ };
911
+ const api = {
912
+ ops,
913
+ saveCtx,
914
+ direct: this.direct.bind(this),
915
+ resolveLabels: this.issuex ? async (ids) => {
916
+ const names = [];
917
+ for (const id of ids) {
918
+ const label = await this.issuex.getLabel(id);
919
+ if (label) names.push(label.name);
920
+ }
921
+ return names;
922
+ } : void 0
923
+ };
924
+ return new Role(individual, ctx, api);
925
+ }
926
+ /** Find a node by id or alias across the entire society tree. Internal use only. */
927
+ async find(id) {
928
+ const state = await this.rt.project(this.society);
929
+ return findInState(state, id);
930
+ }
931
+ /**
932
+ * Direct the world to execute an instruction.
933
+ *
934
+ * - `!namespace.method` — dispatch to ops
935
+ * - anything else — delegate to ResourceX `ingest`
936
+ */
937
+ async direct(locator, args) {
938
+ if (locator.startsWith("!")) {
939
+ const command = locator.slice(1);
940
+ const fn = this.ops[command];
941
+ if (!fn) {
942
+ const hint2 = directives2["identity-ethics"]?.["on-unknown-command"] ?? "";
943
+ throw new Error(
944
+ `Unknown command "${locator}".
945
+
946
+ You may be guessing the command name. Load the relevant skill first with skill(locator) to learn the correct syntax.
947
+
948
+ ` + hint2
949
+ );
610
950
  }
951
+ return await fn(...toArgs(command, args ?? {}));
611
952
  }
612
- return lines.join("\n");
953
+ if (!this.resourcex) throw new Error("ResourceX is not available.");
954
+ return this.resourcex.ingest(locator, args);
613
955
  }
614
956
  };
615
-
616
- // src/renderers/index.ts
617
- function createRendererRouter() {
618
- return new RendererRouter().register("role", new RoleRenderer()).register("org", new OrgRenderer()).register("position", new PositionRenderer()).register("product", new ProductRenderer()).register("project", new ProjectRenderer()).register("society", new SocietyRenderer()).register("survey", new SurveyRenderer()).register("inspect", new InspectRenderer());
619
- }
620
-
621
- // src/rolex.ts
622
- function createRoleX(config) {
623
- return createBuilder({
624
- platform: config.platform,
625
- renderer: config.renderer ?? createRendererRouter(),
626
- prototypes: [genesis]
627
- });
957
+ async function createRoleX(platform) {
958
+ return Rolex.create(platform);
628
959
  }
629
960
  export {
961
+ Role,
962
+ RoleContext,
963
+ Rolex,
630
964
  createRoleX,
631
- renderProduct,
632
- renderProductResult,
633
- renderProjectResult
965
+ describe,
966
+ detail,
967
+ directive,
968
+ findInState,
969
+ hint,
970
+ parse,
971
+ render,
972
+ renderComment,
973
+ renderCommentList,
974
+ renderIssue,
975
+ renderIssueList,
976
+ renderIssueResult,
977
+ renderProject,
978
+ renderProjectResult,
979
+ renderState,
980
+ serialize,
981
+ world
634
982
  };
635
983
  //# sourceMappingURL=index.js.map