@archznn/crewloop-skills 0.4.2 → 0.5.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.
Files changed (79) hide show
  1. package/package.json +1 -1
  2. package/packages/cli/dist/agents.d.ts +9 -0
  3. package/packages/cli/dist/agents.d.ts.map +1 -1
  4. package/packages/cli/dist/agents.js +62 -5
  5. package/packages/cli/dist/agents.js.map +1 -1
  6. package/packages/cli/dist/cli.d.ts +1 -0
  7. package/packages/cli/dist/cli.d.ts.map +1 -1
  8. package/packages/cli/dist/cli.js +34 -4
  9. package/packages/cli/dist/cli.js.map +1 -1
  10. package/packages/cli/dist/hooks.d.ts +37 -0
  11. package/packages/cli/dist/hooks.d.ts.map +1 -0
  12. package/packages/cli/dist/hooks.js +274 -0
  13. package/packages/cli/dist/hooks.js.map +1 -0
  14. package/packages/cli/dist/mcp.d.ts +8 -0
  15. package/packages/cli/dist/mcp.d.ts.map +1 -1
  16. package/packages/cli/dist/mcp.js +19 -1
  17. package/packages/cli/dist/mcp.js.map +1 -1
  18. package/packages/cli/dist/tests/hooks.test.d.ts +2 -0
  19. package/packages/cli/dist/tests/hooks.test.d.ts.map +1 -0
  20. package/packages/cli/dist/tests/hooks.test.js +165 -0
  21. package/packages/cli/dist/tests/hooks.test.js.map +1 -0
  22. package/packages/cli/dist/tests/mcp.test.js +79 -0
  23. package/packages/cli/dist/tests/mcp.test.js.map +1 -1
  24. package/servers/dashboard/dist/adapters/codex.d.ts +1 -0
  25. package/servers/dashboard/dist/adapters/codex.d.ts.map +1 -1
  26. package/servers/dashboard/dist/adapters/codex.js +1 -0
  27. package/servers/dashboard/dist/adapters/codex.js.map +1 -1
  28. package/servers/dashboard/dist/adapters/kimi.d.ts +1 -0
  29. package/servers/dashboard/dist/adapters/kimi.d.ts.map +1 -1
  30. package/servers/dashboard/dist/adapters/kimi.js +1 -0
  31. package/servers/dashboard/dist/adapters/kimi.js.map +1 -1
  32. package/servers/dashboard/dist/adapters/shim.d.ts +2 -1
  33. package/servers/dashboard/dist/adapters/shim.d.ts.map +1 -1
  34. package/servers/dashboard/dist/adapters/shim.js +15 -2
  35. package/servers/dashboard/dist/adapters/shim.js.map +1 -1
  36. package/servers/dashboard/dist/adapters/shim.test.js +43 -0
  37. package/servers/dashboard/dist/adapters/shim.test.js.map +1 -1
  38. package/servers/dashboard/dist/index.js +14 -4
  39. package/servers/dashboard/dist/index.js.map +1 -1
  40. package/servers/dashboard/dist/presenter.d.ts.map +1 -1
  41. package/servers/dashboard/dist/presenter.js +2 -0
  42. package/servers/dashboard/dist/presenter.js.map +1 -1
  43. package/servers/dashboard/dist/presenter.test.js +7 -0
  44. package/servers/dashboard/dist/presenter.test.js.map +1 -1
  45. package/servers/dashboard/dist/server.d.ts.map +1 -1
  46. package/servers/dashboard/dist/server.js +18 -1
  47. package/servers/dashboard/dist/server.js.map +1 -1
  48. package/servers/dashboard/dist/server.test.js +5 -0
  49. package/servers/dashboard/dist/server.test.js.map +1 -1
  50. package/servers/dashboard/dist/skills/infer.d.ts.map +1 -1
  51. package/servers/dashboard/dist/skills/infer.js +5 -0
  52. package/servers/dashboard/dist/skills/infer.js.map +1 -1
  53. package/servers/dashboard/dist/skills/infer.test.js +9 -0
  54. package/servers/dashboard/dist/skills/infer.test.js.map +1 -1
  55. package/servers/dashboard/dist/state.d.ts.map +1 -1
  56. package/servers/dashboard/dist/state.js +19 -1
  57. package/servers/dashboard/dist/state.js.map +1 -1
  58. package/servers/dashboard/dist/state.test.js +37 -0
  59. package/servers/dashboard/dist/state.test.js.map +1 -1
  60. package/servers/dashboard/dist/types.d.ts +4 -0
  61. package/servers/dashboard/dist/types.d.ts.map +1 -1
  62. package/servers/dashboard/package.json +1 -1
  63. package/servers/dashboard/public/app.js +81 -12
  64. package/servers/dashboard/public/styles.css +174 -19
  65. package/servers/dashboard/src/adapters/codex.ts +2 -0
  66. package/servers/dashboard/src/adapters/kimi.ts +2 -0
  67. package/servers/dashboard/src/adapters/shim.test.ts +64 -1
  68. package/servers/dashboard/src/adapters/shim.ts +18 -2
  69. package/servers/dashboard/src/index.ts +15 -4
  70. package/servers/dashboard/src/presenter.test.ts +8 -0
  71. package/servers/dashboard/src/presenter.ts +2 -0
  72. package/servers/dashboard/src/server.test.ts +6 -0
  73. package/servers/dashboard/src/server.ts +21 -1
  74. package/servers/dashboard/src/skills/infer.test.ts +10 -0
  75. package/servers/dashboard/src/skills/infer.ts +8 -0
  76. package/servers/dashboard/src/state.test.ts +43 -0
  77. package/servers/dashboard/src/state.ts +20 -1
  78. package/servers/dashboard/src/types.ts +4 -0
  79. package/skills/orchestrator/SKILL.md +6 -0
@@ -9,6 +9,14 @@ export class SkillInferenceEngine {
9
9
  }
10
10
 
11
11
  infer(event: DashboardEvent, session: Session): SkillInferenceResult {
12
+ const explicitSignal =
13
+ (event.event_type === 'skill_change' && event.skill) ||
14
+ (event.tool === 'Skill' && event.detail && this.normalizeSkillName(event.detail));
15
+
16
+ if (session.active_confidence === 'explicit' && !explicitSignal) {
17
+ return { skill: session.active_skill, confidence: 'explicit' };
18
+ }
19
+
12
20
  if (event.event_type === 'skill_change' && event.skill) {
13
21
  return { skill: event.skill, confidence: 'explicit' };
14
22
  }
@@ -15,6 +15,7 @@ function makeEvent(overrides: Partial<DashboardEvent> = {}): DashboardEvent {
15
15
  };
16
16
  }
17
17
 
18
+
18
19
  describe('StateStore', () => {
19
20
  it('creates session on first event', () => {
20
21
  const store = new StateStore({ maxEventsPerSession: 10, sessionMaxAgeMs: 60000 });
@@ -51,6 +52,14 @@ describe('StateStore', () => {
51
52
  assert.equal(session.active_confidence, 'explicit');
52
53
  });
53
54
 
55
+ it('sets explicit active skill from session_start event', () => {
56
+ const store = new StateStore({ maxEventsPerSession: 10, sessionMaxAgeMs: 60000 });
57
+ store.applyEvent(makeEvent({ skill: 'orchestrator', event_type: 'session_start' }));
58
+ const session = store.getSession('sess-1')!;
59
+ assert.equal(session.active_skill, 'orchestrator');
60
+ assert.equal(session.active_confidence, 'explicit');
61
+ });
62
+
54
63
  it('derives running status from tool_start', () => {
55
64
  const store = new StateStore({ maxEventsPerSession: 10, sessionMaxAgeMs: 60000 });
56
65
  store.applyEvent(makeEvent({ event_type: 'tool_start' }));
@@ -85,4 +94,38 @@ describe('StateStore', () => {
85
94
  assert.equal(sessions[0].id, 'b');
86
95
  assert.equal(sessions[1].id, 'a');
87
96
  });
97
+
98
+ it('starts with lifecycle starting on session_start', () => {
99
+ const store = new StateStore({ maxEventsPerSession: 10, sessionMaxAgeMs: 60000 });
100
+ store.applyEvent(makeEvent({ event_type: 'session_start' }));
101
+ const session = store.getSession('sess-1')!;
102
+ assert.equal(session.lifecycle, 'starting');
103
+ });
104
+
105
+ it('transitions to running on first tool event', () => {
106
+ const store = new StateStore({ maxEventsPerSession: 10, sessionMaxAgeMs: 60000 });
107
+ store.applyEvent(makeEvent({ event_type: 'session_start' }));
108
+ store.applyEvent(makeEvent({ event_type: 'tool_start', tool: 'Read' }));
109
+ const session = store.getSession('sess-1')!;
110
+ assert.equal(session.lifecycle, 'running');
111
+ });
112
+
113
+ it('sets lifecycle ended and ended_at on session_end', () => {
114
+ const store = new StateStore({ maxEventsPerSession: 10, sessionMaxAgeMs: 60000 });
115
+ store.applyEvent(makeEvent({ event_type: 'session_start' }));
116
+ const endTs = Date.now() + 1000;
117
+ store.applyEvent(makeEvent({ event_type: 'session_end', timestamp: endTs }));
118
+ const session = store.getSession('sess-1')!;
119
+ assert.equal(session.lifecycle, 'ended');
120
+ assert.equal(session.ended_at, endTs);
121
+ });
122
+
123
+ it('keeps session ended after subsequent tool events', () => {
124
+ const store = new StateStore({ maxEventsPerSession: 10, sessionMaxAgeMs: 60000 });
125
+ store.applyEvent(makeEvent({ event_type: 'session_start' }));
126
+ store.applyEvent(makeEvent({ event_type: 'session_end' }));
127
+ store.applyEvent(makeEvent({ event_type: 'tool_start', tool: 'Read' }));
128
+ const session = store.getSession('sess-1')!;
129
+ assert.equal(session.lifecycle, 'ended');
130
+ });
88
131
  });
@@ -32,11 +32,19 @@ export class StateStore {
32
32
  session.tool_counts[event.tool] = (session.tool_counts[event.tool] || 0) + 1;
33
33
  }
34
34
 
35
- if (event.skill) {
35
+ if (event.event_type === 'session_start' && event.skill) {
36
+ session.active_skill = event.skill;
37
+ session.active_confidence = 'explicit';
38
+ } else if (event.skill) {
36
39
  session.active_skill = event.skill;
37
40
  session.active_confidence = event.event_type === 'skill_change' ? 'explicit' : 'heuristic';
38
41
  }
39
42
 
43
+ if (event.event_type === 'session_end') {
44
+ session.ended_at = event.timestamp;
45
+ }
46
+ session.lifecycle = deriveLifecycle(event, session);
47
+
40
48
  session.status = deriveSessionStatus(event);
41
49
 
42
50
  this.sessions.set(event.session_id, session);
@@ -91,6 +99,7 @@ export class StateStore {
91
99
  source,
92
100
  events: [],
93
101
  tool_counts: {},
102
+ lifecycle: 'starting',
94
103
  started_at: now,
95
104
  last_event_at: now,
96
105
  };
@@ -99,6 +108,16 @@ export class StateStore {
99
108
  }
100
109
  }
101
110
 
111
+ function deriveLifecycle(event: DashboardEvent, session: Session): 'starting' | 'running' | 'ended' {
112
+ if (event.event_type === 'session_end' || session.ended_at) {
113
+ return 'ended';
114
+ }
115
+ if (event.event_type === 'session_start' && session.events.length <= 1) {
116
+ return 'starting';
117
+ }
118
+ return 'running';
119
+ }
120
+
102
121
  function deriveSessionStatus(event: DashboardEvent): EventStatus | undefined {
103
122
  switch (event.event_type) {
104
123
  case 'session_start':
@@ -28,10 +28,12 @@ export interface Session {
28
28
  active_skill?: string;
29
29
  active_confidence?: 'explicit' | 'heuristic' | 'unknown';
30
30
  status?: EventStatus;
31
+ lifecycle: 'starting' | 'running' | 'ended';
31
32
  events: DashboardEvent[];
32
33
  tool_counts: Record<string, number>;
33
34
  started_at: number;
34
35
  last_event_at: number;
36
+ ended_at?: number;
35
37
  }
36
38
 
37
39
  export interface DashboardState {
@@ -60,9 +62,11 @@ export interface ClientSession {
60
62
  skill?: string;
61
63
  activeSkill?: ClientActiveSkill;
62
64
  status?: EventStatus;
65
+ lifecycle: 'starting' | 'running' | 'ended';
63
66
  events: ClientEvent[];
64
67
  startTime: number;
65
68
  lastActivity: number;
69
+ endedAt?: number;
66
70
  toolCounts: Record<string, number>;
67
71
  }
68
72
 
@@ -11,6 +11,12 @@ You are a technical product manager and discovery specialist. Your job is to ext
11
11
 
12
12
  ---
13
13
 
14
+ ## DASHBOARD LIFECYCLE
15
+
16
+ When this skill is loaded at the start of a session, the CrewLoop dashboard should display an active session named `orchestrator`. If the agent supports lifecycle hooks, ensure the first event sent to the dashboard marks `orchestrator` as the active skill.
17
+
18
+ ---
19
+
14
20
  ### 🚨 MANDATORY: Read Reference & Template Files
15
21
  Before taking any action, you MUST read the global conventions in [conventions.md](../../references/conventions.md), the workflow in [workflow.md](../../references/workflow.md), and any local reference files or directories (such as `references/` or `assets/`) if present. Never skip this step or make assumptions about the guidelines.
16
22