@nex-ai/nex 0.1.27 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -8
- package/dist/agent/message-router.d.ts +26 -0
- package/dist/agent/message-router.js +114 -0
- package/dist/agent/message-router.js.map +1 -0
- package/dist/agent/orchestrate.d.ts +26 -0
- package/dist/agent/orchestrate.js +87 -0
- package/dist/agent/orchestrate.js.map +1 -0
- package/dist/agent/providers/types.d.ts +5 -0
- package/dist/agent/providers/types.js +2 -0
- package/dist/agent/providers/types.js.map +1 -0
- package/dist/agent/templates.js +7 -0
- package/dist/agent/templates.js.map +1 -1
- package/dist/agent/tick-manager.d.ts +20 -0
- package/dist/agent/tick-manager.js +50 -0
- package/dist/agent/tick-manager.js.map +1 -0
- package/dist/commands/dispatch.d.ts +5 -0
- package/dist/commands/dispatch.js +108 -0
- package/dist/commands/dispatch.js.map +1 -1
- package/dist/index.js +90 -22
- package/dist/index.js.map +1 -1
- package/dist/lib/claude-code-installer.d.ts +8 -0
- package/dist/lib/claude-code-installer.js +76 -0
- package/dist/lib/claude-code-installer.js.map +1 -0
- package/dist/plugin/context-format.d.ts +4 -0
- package/dist/plugin/context-format.js +4 -0
- package/dist/plugin/context-format.js.map +1 -1
- package/dist/plugin/recall-filter.d.ts +14 -5
- package/dist/plugin/recall-filter.js +29 -41
- package/dist/plugin/recall-filter.js.map +1 -1
- package/dist/tui/app.js +9 -104
- package/dist/tui/app.js.map +1 -1
- package/dist/tui/register-views.js +7 -1
- package/dist/tui/register-views.js.map +1 -1
- package/dist/tui/views/stream.d.ts +16 -0
- package/dist/tui/views/stream.js +210 -0
- package/dist/tui/views/stream.js.map +1 -0
- package/package.json +7 -5
- package/platform-rules/aider-conventions.md +20 -12
- package/platform-rules/cline-rules.md +20 -8
- package/platform-rules/continue-rules.md +20 -8
- package/platform-rules/cursor-rules.md +20 -8
- package/platform-rules/kilocode-rules.md +20 -8
- package/platform-rules/opencode-agents.md +20 -12
- package/platform-rules/vscode-instructions.md +20 -12
- package/platform-rules/windsurf-rules.md +20 -8
- package/platform-rules/zed-rules.md +20 -12
package/README.md
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Nex: Compounding Intelligence for AI agents
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@nex-ai/nex)
|
|
4
4
|
[](https://github.com/nex-crm/nex-as-a-skill)
|
|
5
|
+
[](https://discord.gg/gjSySC3PzV)
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
Turn all your AI agent conversations into a unified knowledge graph. Supports Claude Code, Codex, OpenClaw, Cursor, OpenCode, etc. Adds additional context from Email, Meetings, Slack, HubSpot, Salesforce.
|
|
8
|
+
|
|
9
|
+
<a href="https://discord.gg/gjSySC3PzV"><img src="https://img.shields.io/badge/Join%20our%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Join our Discord" /></a>
|
|
10
|
+
|
|
11
|
+
Talk to the team, share feedback, and connect with other developers building AI agents with Nex.
|
|
7
12
|
|
|
8
13
|
**GitHub**: [github.com/nex-crm/nex-as-a-skill](https://github.com/nex-crm/nex-as-a-skill)
|
|
9
14
|
|
|
@@ -11,7 +16,7 @@ Nex CLI provides organizational context & memory to AI agents across 12 platform
|
|
|
11
16
|
|
|
12
17
|
```bash
|
|
13
18
|
# Install globally
|
|
14
|
-
|
|
19
|
+
bun install -g @nex-ai/nex
|
|
15
20
|
|
|
16
21
|
# Or run directly (no install)
|
|
17
22
|
npx @nex-ai/nex ask "who is Maria?"
|
|
@@ -99,7 +104,7 @@ nex graph # Open the workspace graph in your browser
|
|
|
99
104
|
- Creates `.nex.toml` project config with commented defaults
|
|
100
105
|
- Syncs API key to `~/.nex-mcp.json` (shared config)
|
|
101
106
|
|
|
102
|
-
**Single install**: `
|
|
107
|
+
**Single install**: `bun install -g @nex-ai/nex` bundles everything — hooks, adapters, platform plugins, slash commands, rules, and MCP server. No separate packages needed.
|
|
103
108
|
|
|
104
109
|
**Integration hierarchy** (per platform): Hooks > Custom plugins > Custom agents/modes > Workflows > Rules > MCP. Each platform gets every layer it supports.
|
|
105
110
|
|
|
@@ -252,9 +257,9 @@ git diff | nex capture
|
|
|
252
257
|
## Development
|
|
253
258
|
|
|
254
259
|
```bash
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
260
|
+
bun install
|
|
261
|
+
bun run build # TypeScript → dist/
|
|
262
|
+
bun run dev # Run TS directly (no build)
|
|
263
|
+
bun test # Unit + integration tests
|
|
259
264
|
NEX_DEV_URL=http://localhost:30000 nex ask "test" # Local API
|
|
260
265
|
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MessageRouter: routes incoming messages to the best-fit agent
|
|
3
|
+
* using TaskRouter for skill scoring and thread-detection heuristics
|
|
4
|
+
* for follow-up messages.
|
|
5
|
+
*/
|
|
6
|
+
export interface RoutingResult {
|
|
7
|
+
primary: string;
|
|
8
|
+
collaborators: string[];
|
|
9
|
+
isFollowUp: boolean;
|
|
10
|
+
teamLeadAware: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare class MessageRouter {
|
|
13
|
+
private router;
|
|
14
|
+
private recentThreads;
|
|
15
|
+
private FOLLOWUP_WINDOW_MS;
|
|
16
|
+
registerAgent(slug: string, expertise: string[]): void;
|
|
17
|
+
unregisterAgent(slug: string): void;
|
|
18
|
+
recordAgentActivity(agentSlug: string): void;
|
|
19
|
+
route(message: string, availableAgents: Array<{
|
|
20
|
+
slug: string;
|
|
21
|
+
expertise: string[];
|
|
22
|
+
}>): RoutingResult;
|
|
23
|
+
private detectFollowUp;
|
|
24
|
+
/** Extract skill keywords from a message using pattern matching. */
|
|
25
|
+
extractSkills(message: string): string[];
|
|
26
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MessageRouter: routes incoming messages to the best-fit agent
|
|
3
|
+
* using TaskRouter for skill scoring and thread-detection heuristics
|
|
4
|
+
* for follow-up messages.
|
|
5
|
+
*/
|
|
6
|
+
import { TaskRouter } from '../orchestration/router.js';
|
|
7
|
+
export class MessageRouter {
|
|
8
|
+
router = new TaskRouter();
|
|
9
|
+
recentThreads = new Map();
|
|
10
|
+
FOLLOWUP_WINDOW_MS = 30_000;
|
|
11
|
+
registerAgent(slug, expertise) {
|
|
12
|
+
this.router.registerAgent(slug, expertise.map((e) => ({ name: e, description: e, proficiency: 0.8 })));
|
|
13
|
+
}
|
|
14
|
+
unregisterAgent(slug) {
|
|
15
|
+
this.router.unregisterAgent(slug);
|
|
16
|
+
}
|
|
17
|
+
recordAgentActivity(agentSlug) {
|
|
18
|
+
this.recentThreads.set(agentSlug, { agentSlug, lastActivity: Date.now() });
|
|
19
|
+
}
|
|
20
|
+
route(message, availableAgents) {
|
|
21
|
+
// Step 1: Thread detection
|
|
22
|
+
const followUp = this.detectFollowUp(message);
|
|
23
|
+
if (followUp) {
|
|
24
|
+
return { primary: followUp, collaborators: [], isFollowUp: true, teamLeadAware: false };
|
|
25
|
+
}
|
|
26
|
+
// Step 2: Extract skills from message using keyword patterns
|
|
27
|
+
const requiredSkills = this.extractSkills(message);
|
|
28
|
+
// No recognized skills → route to team-lead for triage
|
|
29
|
+
if (requiredSkills.length === 0) {
|
|
30
|
+
return { primary: 'team-lead', collaborators: [], isFollowUp: false, teamLeadAware: false };
|
|
31
|
+
}
|
|
32
|
+
// Step 3: Register agents (ensure current state) and score
|
|
33
|
+
for (const agent of availableAgents) {
|
|
34
|
+
this.registerAgent(agent.slug, agent.expertise);
|
|
35
|
+
}
|
|
36
|
+
const task = {
|
|
37
|
+
id: `route-${Date.now()}`,
|
|
38
|
+
title: message,
|
|
39
|
+
description: message,
|
|
40
|
+
requiredSkills,
|
|
41
|
+
priority: 'medium',
|
|
42
|
+
status: 'pending',
|
|
43
|
+
createdAt: Date.now(),
|
|
44
|
+
};
|
|
45
|
+
const capable = this.router.findCapableAgents(task);
|
|
46
|
+
if (capable.length === 0) {
|
|
47
|
+
return { primary: 'team-lead', collaborators: [], isFollowUp: false, teamLeadAware: false };
|
|
48
|
+
}
|
|
49
|
+
const primary = capable[0].agentSlug;
|
|
50
|
+
const collaborators = capable
|
|
51
|
+
.slice(1)
|
|
52
|
+
.filter((c) => c.score > 0.5)
|
|
53
|
+
.map((c) => c.agentSlug);
|
|
54
|
+
return {
|
|
55
|
+
primary,
|
|
56
|
+
collaborators,
|
|
57
|
+
isFollowUp: false,
|
|
58
|
+
teamLeadAware: primary !== 'team-lead',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
detectFollowUp(message) {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const followUpPatterns = /^(also|and |too |that |it |the results|those |these |this |what about|how about|can you also)/i;
|
|
64
|
+
if (!followUpPatterns.test(message.trim()))
|
|
65
|
+
return null;
|
|
66
|
+
// Find most recent active thread within window
|
|
67
|
+
let mostRecent = null;
|
|
68
|
+
for (const [, ctx] of this.recentThreads) {
|
|
69
|
+
if (now - ctx.lastActivity <= this.FOLLOWUP_WINDOW_MS) {
|
|
70
|
+
if (!mostRecent || ctx.lastActivity > mostRecent.lastActivity) {
|
|
71
|
+
mostRecent = ctx;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return mostRecent?.agentSlug ?? null;
|
|
76
|
+
}
|
|
77
|
+
/** Extract skill keywords from a message using pattern matching. */
|
|
78
|
+
extractSkills(message) {
|
|
79
|
+
const skillPatterns = [
|
|
80
|
+
{
|
|
81
|
+
pattern: /\b(research|investigate|analyze|analysis|competitor)\b/i,
|
|
82
|
+
skills: ['market-research', 'competitive-analysis'],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
pattern: /\b(leads?|prospect|outreach|prospecting|pipeline)\b/i,
|
|
86
|
+
skills: ['prospecting', 'outreach'],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
pattern: /\b(enrich|validate|data quality|fill in|complete)\b/i,
|
|
90
|
+
skills: ['data-enrichment', 'validation'],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
pattern: /\b(seo|keyword|search visibility|ranking|content)\b/i,
|
|
94
|
+
skills: ['seo', 'content-analysis'],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
pattern: /\b(customer|success|health|renewal|churn|retention)\b/i,
|
|
98
|
+
skills: ['relationship-management', 'health-scoring'],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
pattern: /\b(code|bug|fix|implement|build|deploy|test)\b/i,
|
|
102
|
+
skills: ['general', 'planning'],
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
const skills = [];
|
|
106
|
+
for (const { pattern, skills: matchedSkills } of skillPatterns) {
|
|
107
|
+
if (pattern.test(message)) {
|
|
108
|
+
skills.push(...matchedSkills);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [...new Set(skills)];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=message-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-router.js","sourceRoot":"","sources":["../../src/agent/message-router.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAexD,MAAM,OAAO,aAAa;IAChB,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;IAC1B,aAAa,GAAG,IAAI,GAAG,EAAyB,CAAC;IACjD,kBAAkB,GAAG,MAAM,CAAC;IAEpC,aAAa,CAAC,IAAY,EAAE,SAAmB;QAC7C,IAAI,CAAC,MAAM,CAAC,aAAa,CACvB,IAAI,EACJ,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,CACtE,CAAC;IACJ,CAAC;IAED,eAAe,CAAC,IAAY;QAC1B,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,mBAAmB,CAAC,SAAiB;QACnC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,KAAK,CACH,OAAe,EACf,eAA6D;QAE7D,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;QAC1F,CAAC;QAED,6DAA6D;QAC7D,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAEnD,uDAAuD;QACvD,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;QAC9F,CAAC;QAED,2DAA2D;QAC3D,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YACpC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,IAAI,GAAmB;YAC3B,EAAE,EAAE,SAAS,IAAI,CAAC,GAAG,EAAE,EAAE;YACzB,KAAK,EAAE,OAAO;YACd,WAAW,EAAE,OAAO;YACpB,cAAc;YACd,QAAQ,EAAE,QAAQ;YAClB,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAEpD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;QAC9F,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACrC,MAAM,aAAa,GAAG,OAAO;aAC1B,KAAK,CAAC,CAAC,CAAC;aACR,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC;aAC5B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAE3B,OAAO;YACL,OAAO;YACP,aAAa;YACb,UAAU,EAAE,KAAK;YACjB,aAAa,EAAE,OAAO,KAAK,WAAW;SACvC,CAAC;IACJ,CAAC;IAEO,cAAc,CAAC,OAAe;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,gBAAgB,GACpB,gGAAgG,CAAC;QAEnG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;QAExD,+CAA+C;QAC/C,IAAI,UAAU,GAAyB,IAAI,CAAC;QAC5C,KAAK,MAAM,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACzC,IAAI,GAAG,GAAG,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACtD,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC,YAAY,GAAG,UAAU,CAAC,YAAY,EAAE,CAAC;oBAC9D,UAAU,GAAG,GAAG,CAAC;gBACnB,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,UAAU,EAAE,SAAS,IAAI,IAAI,CAAC;IACvC,CAAC;IAED,oEAAoE;IACpE,aAAa,CAAC,OAAe;QAC3B,MAAM,aAAa,GAAiD;YAClE;gBACE,OAAO,EAAE,yDAAyD;gBAClE,MAAM,EAAE,CAAC,iBAAiB,EAAE,sBAAsB,CAAC;aACpD;YACD;gBACE,OAAO,EAAE,sDAAsD;gBAC/D,MAAM,EAAE,CAAC,aAAa,EAAE,UAAU,CAAC;aACpC;YACD;gBACE,OAAO,EAAE,sDAAsD;gBAC/D,MAAM,EAAE,CAAC,iBAAiB,EAAE,YAAY,CAAC;aAC1C;YACD;gBACE,OAAO,EAAE,sDAAsD;gBAC/D,MAAM,EAAE,CAAC,KAAK,EAAE,kBAAkB,CAAC;aACpC;YACD;gBACE,OAAO,EAAE,wDAAwD;gBACjE,MAAM,EAAE,CAAC,yBAAyB,EAAE,gBAAgB,CAAC;aACtD;YACD;gBACE,OAAO,EAAE,iDAAiD;gBAC1D,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;aAChC;SACF,CAAC;QAEF,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,aAAa,EAAE,CAAC;YAC/D,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9B,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Objective-first delegation: Team-Lead decomposes objectives
|
|
3
|
+
* and routes sub-tasks to specialist agents via steer messages.
|
|
4
|
+
*
|
|
5
|
+
* Uses the orchestration TaskRouter for skill matching and
|
|
6
|
+
* MessageQueues for non-blocking delegation.
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentConfig } from './types.js';
|
|
9
|
+
import type { MessageQueues } from './queues.js';
|
|
10
|
+
/** Extract actionable sub-tasks from the Team-Lead's response text. */
|
|
11
|
+
export declare function extractSubTasks(response: string): Array<{
|
|
12
|
+
action: string;
|
|
13
|
+
skills: string[];
|
|
14
|
+
}>;
|
|
15
|
+
export interface Specialist {
|
|
16
|
+
config: AgentConfig;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Delegate sub-tasks to specialist agents.
|
|
20
|
+
* Non-blocking: sends steer messages to matching specialists
|
|
21
|
+
* so they pick up work on their next tick.
|
|
22
|
+
*/
|
|
23
|
+
export declare function delegateToSpecialists(response: string, specialists: Specialist[], queues: MessageQueues): Array<{
|
|
24
|
+
agentSlug: string;
|
|
25
|
+
task: string;
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Objective-first delegation: Team-Lead decomposes objectives
|
|
3
|
+
* and routes sub-tasks to specialist agents via steer messages.
|
|
4
|
+
*
|
|
5
|
+
* Uses the orchestration TaskRouter for skill matching and
|
|
6
|
+
* MessageQueues for non-blocking delegation.
|
|
7
|
+
*/
|
|
8
|
+
import { TaskRouter } from '../orchestration/router.js';
|
|
9
|
+
import { getAgentService } from '../tui/services/agent-service.js';
|
|
10
|
+
/** Map agent expertise strings to SkillDeclarations for the router. */
|
|
11
|
+
function expertiseToSkills(expertise) {
|
|
12
|
+
return expertise.map(e => ({
|
|
13
|
+
name: e,
|
|
14
|
+
description: e,
|
|
15
|
+
proficiency: 0.8,
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
/** Extract actionable sub-tasks from the Team-Lead's response text. */
|
|
19
|
+
export function extractSubTasks(response) {
|
|
20
|
+
const subTasks = [];
|
|
21
|
+
// Skill keyword mapping — matches action words to required expertise
|
|
22
|
+
const skillPatterns = [
|
|
23
|
+
{ pattern: /\b(research|investigate|analyze|analysis)\b/i, skills: ['research', 'market-research', 'competitive-analysis'] },
|
|
24
|
+
{ pattern: /\b(find leads|generate leads|prospect|outreach|prospecting)\b/i, skills: ['prospecting', 'enrichment', 'outreach'] },
|
|
25
|
+
{ pattern: /\b(enrich|validate|fill in|complete data|data quality)\b/i, skills: ['data-enrichment', 'research', 'validation'] },
|
|
26
|
+
{ pattern: /\b(seo|keyword|search visibility|content.?analysis|ranking)\b/i, skills: ['seo', 'content-analysis', 'keyword-research'] },
|
|
27
|
+
{ pattern: /\b(customer success|health score|renewal|churn|retention)\b/i, skills: ['relationship-management', 'health-scoring', 'renewal-tracking'] },
|
|
28
|
+
];
|
|
29
|
+
// Split response into sentences and look for action-oriented ones
|
|
30
|
+
const sentences = response.split(/[.!?\n]+/).map(s => s.trim()).filter(Boolean);
|
|
31
|
+
for (const sentence of sentences) {
|
|
32
|
+
for (const { pattern, skills } of skillPatterns) {
|
|
33
|
+
if (pattern.test(sentence)) {
|
|
34
|
+
// Avoid duplicates
|
|
35
|
+
if (!subTasks.some(t => t.action === sentence)) {
|
|
36
|
+
subTasks.push({ action: sentence, skills });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return subTasks;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Delegate sub-tasks to specialist agents.
|
|
45
|
+
* Non-blocking: sends steer messages to matching specialists
|
|
46
|
+
* so they pick up work on their next tick.
|
|
47
|
+
*/
|
|
48
|
+
export function delegateToSpecialists(response, specialists, queues) {
|
|
49
|
+
if (specialists.length === 0)
|
|
50
|
+
return [];
|
|
51
|
+
const subTasks = extractSubTasks(response);
|
|
52
|
+
if (subTasks.length === 0)
|
|
53
|
+
return [];
|
|
54
|
+
// Build a router with registered specialists
|
|
55
|
+
const router = new TaskRouter();
|
|
56
|
+
for (const spec of specialists) {
|
|
57
|
+
router.registerAgent(spec.config.slug, expertiseToSkills(spec.config.expertise));
|
|
58
|
+
}
|
|
59
|
+
const delegated = [];
|
|
60
|
+
for (const subTask of subTasks) {
|
|
61
|
+
const taskDef = {
|
|
62
|
+
id: `delegate-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
63
|
+
title: subTask.action,
|
|
64
|
+
description: subTask.action,
|
|
65
|
+
requiredSkills: subTask.skills,
|
|
66
|
+
priority: 'medium',
|
|
67
|
+
status: 'pending',
|
|
68
|
+
createdAt: Date.now(),
|
|
69
|
+
};
|
|
70
|
+
const best = router.findBestAgent(taskDef);
|
|
71
|
+
if (best && best.score > 0.1) {
|
|
72
|
+
// Steer the specialist with the sub-task
|
|
73
|
+
queues.steer(best.agentSlug, `[TEAM-LEAD DELEGATION] ${subTask.action}`);
|
|
74
|
+
delegated.push({ agentSlug: best.agentSlug, task: subTask.action });
|
|
75
|
+
// Ensure specialist agent tick loop is running
|
|
76
|
+
try {
|
|
77
|
+
const agentService = getAgentService();
|
|
78
|
+
agentService.ensureRunning(best.agentSlug);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// AgentService may not be initialized yet — delegation still succeeds via queue
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return delegated;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=orchestrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../../src/agent/orchestrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/agent/providers/types.ts"],"names":[],"mappings":""}
|
package/dist/agent/templates.js
CHANGED
|
@@ -37,6 +37,13 @@ export const templates = {
|
|
|
37
37
|
heartbeatCron: '0 */8 * * *',
|
|
38
38
|
tools: ['nex_search', 'nex_ask', 'nex_record_list', 'nex_record_get', 'nex_remember'],
|
|
39
39
|
},
|
|
40
|
+
'team-lead': {
|
|
41
|
+
name: 'Team Lead',
|
|
42
|
+
expertise: ['general', 'research', 'analysis', 'communication', 'planning', 'orchestration'],
|
|
43
|
+
personality: 'You are the Team Lead — the primary interface between the human and the specialist agents. You understand objectives, break them into tasks, delegate to the right specialists, and synthesize results. When the user says what they want, you figure out the path to get there using the context graph and available agents. You always respond first, then coordinate behind the scenes.',
|
|
44
|
+
heartbeatCron: 'manual',
|
|
45
|
+
tools: ['nex_search', 'nex_ask', 'nex_remember', 'nex_record_list', 'nex_record_get', 'nex_record_create', 'nex_record_update'],
|
|
46
|
+
},
|
|
40
47
|
'founding-agent': {
|
|
41
48
|
name: 'Founding Agent',
|
|
42
49
|
expertise: ['general', 'research', 'analysis', 'communication', 'planning'],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/agent/templates.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,CAAC,MAAM,SAAS,GAA8C;IAClE,WAAW,EAAE;QACX,IAAI,EAAE,aAAa;QACnB,SAAS,EAAE,CAAC,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,CAAC;QAC1D,WAAW,EAAE,2DAA2D;QACxE,aAAa,EAAE,OAAO;QACtB,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,CAAC;KACpE;IACD,UAAU,EAAE;QACV,IAAI,EAAE,gBAAgB;QACtB,SAAS,EAAE,CAAC,aAAa,EAAE,YAAY,EAAE,UAAU,CAAC;QACpD,WAAW,EAAE,mDAAmD;QAChE,aAAa,EAAE,aAAa;QAC5B,KAAK,EAAE,CAAC,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,cAAc,CAAC;KAC9E;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,eAAe;QACrB,SAAS,EAAE,CAAC,iBAAiB,EAAE,UAAU,EAAE,YAAY,CAAC;QACxD,WAAW,EAAE,6CAA6C;QAC1D,aAAa,EAAE,aAAa;QAC5B,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,cAAc,CAAC;KACxF;IACD,UAAU,EAAE;QACV,IAAI,EAAE,kBAAkB;QACxB,SAAS,EAAE,CAAC,iBAAiB,EAAE,sBAAsB,EAAE,gBAAgB,CAAC;QACxE,WAAW,EAAE,oDAAoD;QACjE,aAAa,EAAE,OAAO;QACtB,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,cAAc,CAAC;KACjD;IACD,kBAAkB,EAAE;QAClB,IAAI,EAAE,kBAAkB;QACxB,SAAS,EAAE,CAAC,yBAAyB,EAAE,gBAAgB,EAAE,kBAAkB,CAAC;QAC5E,WAAW,EAAE,uDAAuD;QACpE,aAAa,EAAE,aAAa;QAC5B,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,CAAC;KACtF;IACD,gBAAgB,EAAE;QAChB,IAAI,EAAE,gBAAgB;QACtB,SAAS,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,CAAC;QAC3E,WAAW,EAAE,uIAAuI;QACpJ,aAAa,EAAE,OAAO;QACtB,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,CAAC;KAChI;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/agent/templates.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,CAAC,MAAM,SAAS,GAA8C;IAClE,WAAW,EAAE;QACX,IAAI,EAAE,aAAa;QACnB,SAAS,EAAE,CAAC,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,CAAC;QAC1D,WAAW,EAAE,2DAA2D;QACxE,aAAa,EAAE,OAAO;QACtB,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,CAAC;KACpE;IACD,UAAU,EAAE;QACV,IAAI,EAAE,gBAAgB;QACtB,SAAS,EAAE,CAAC,aAAa,EAAE,YAAY,EAAE,UAAU,CAAC;QACpD,WAAW,EAAE,mDAAmD;QAChE,aAAa,EAAE,aAAa;QAC5B,KAAK,EAAE,CAAC,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,cAAc,CAAC;KAC9E;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,eAAe;QACrB,SAAS,EAAE,CAAC,iBAAiB,EAAE,UAAU,EAAE,YAAY,CAAC;QACxD,WAAW,EAAE,6CAA6C;QAC1D,aAAa,EAAE,aAAa;QAC5B,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,cAAc,CAAC;KACxF;IACD,UAAU,EAAE;QACV,IAAI,EAAE,kBAAkB;QACxB,SAAS,EAAE,CAAC,iBAAiB,EAAE,sBAAsB,EAAE,gBAAgB,CAAC;QACxE,WAAW,EAAE,oDAAoD;QACjE,aAAa,EAAE,OAAO;QACtB,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,cAAc,CAAC;KACjD;IACD,kBAAkB,EAAE;QAClB,IAAI,EAAE,kBAAkB;QACxB,SAAS,EAAE,CAAC,yBAAyB,EAAE,gBAAgB,EAAE,kBAAkB,CAAC;QAC5E,WAAW,EAAE,uDAAuD;QACpE,aAAa,EAAE,aAAa;QAC5B,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,CAAC;KACtF;IACD,WAAW,EAAE;QACX,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,CAAC;QAC5F,WAAW,EAAE,4XAA4X;QACzY,aAAa,EAAE,QAAQ;QACvB,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,CAAC;KAChI;IACD,gBAAgB,EAAE;QAChB,IAAI,EAAE,gBAAgB;QACtB,SAAS,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,CAAC;QAC3E,WAAW,EAAE,uIAAuI;QACpJ,aAAa,EAAE,OAAO;QACtB,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,CAAC;KAChI;CACF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TickManager: drives agent loops on a fixed interval.
|
|
3
|
+
* Each agent gets its own setInterval; ticks are skipped
|
|
4
|
+
* when the agent is idle with no queued work.
|
|
5
|
+
*/
|
|
6
|
+
export declare class TickManager {
|
|
7
|
+
private intervals;
|
|
8
|
+
private tickRateMs;
|
|
9
|
+
constructor(tickRateMs?: number);
|
|
10
|
+
startLoop(slug: string, loop: {
|
|
11
|
+
getState(): {
|
|
12
|
+
phase: string;
|
|
13
|
+
};
|
|
14
|
+
start(): void;
|
|
15
|
+
tick(): Promise<void>;
|
|
16
|
+
}, hasWork: () => boolean): void;
|
|
17
|
+
stopLoop(slug: string): void;
|
|
18
|
+
isRunning(slug: string): boolean;
|
|
19
|
+
stopAll(): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TickManager: drives agent loops on a fixed interval.
|
|
3
|
+
* Each agent gets its own setInterval; ticks are skipped
|
|
4
|
+
* when the agent is idle with no queued work.
|
|
5
|
+
*/
|
|
6
|
+
export class TickManager {
|
|
7
|
+
intervals = new Map();
|
|
8
|
+
tickRateMs;
|
|
9
|
+
constructor(tickRateMs = 500) {
|
|
10
|
+
this.tickRateMs = tickRateMs;
|
|
11
|
+
}
|
|
12
|
+
startLoop(slug, loop, hasWork) {
|
|
13
|
+
if (this.intervals.has(slug))
|
|
14
|
+
return; // idempotent
|
|
15
|
+
const interval = setInterval(async () => {
|
|
16
|
+
const state = loop.getState();
|
|
17
|
+
// Only tick if there's pending work
|
|
18
|
+
if ((state.phase === 'done' || state.phase === 'error' || state.phase === 'idle') &&
|
|
19
|
+
!hasWork()) {
|
|
20
|
+
return; // idle skip
|
|
21
|
+
}
|
|
22
|
+
if (state.phase === 'done' || state.phase === 'error') {
|
|
23
|
+
loop.start(); // reset to idle
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
await loop.tick();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
/* swallow tick errors */
|
|
30
|
+
}
|
|
31
|
+
}, this.tickRateMs);
|
|
32
|
+
this.intervals.set(slug, interval);
|
|
33
|
+
}
|
|
34
|
+
stopLoop(slug) {
|
|
35
|
+
const interval = this.intervals.get(slug);
|
|
36
|
+
if (interval) {
|
|
37
|
+
clearInterval(interval);
|
|
38
|
+
this.intervals.delete(slug);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
isRunning(slug) {
|
|
42
|
+
return this.intervals.has(slug);
|
|
43
|
+
}
|
|
44
|
+
stopAll() {
|
|
45
|
+
for (const [slug] of this.intervals) {
|
|
46
|
+
this.stopLoop(slug);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=tick-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tick-manager.js","sourceRoot":"","sources":["../../src/agent/tick-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,OAAO,WAAW;IACd,SAAS,GAAG,IAAI,GAAG,EAA0C,CAAC;IAC9D,UAAU,CAAS;IAE3B,YAAY,UAAU,GAAG,GAAG;QAC1B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,SAAS,CACP,IAAY,EACZ,IAA6E,EAC7E,OAAsB;QAEtB,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,CAAC,aAAa;QAEnD,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;YACtC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,oCAAoC;YACpC,IACE,CAAC,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC;gBAC7E,CAAC,OAAO,EAAE,EACV,CAAC;gBACD,OAAO,CAAC,YAAY;YACtB,CAAC;YACD,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;gBACtD,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,gBAAgB;YAChC,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,yBAAyB;YAC3B,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAEpB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,QAAQ,EAAE,CAAC;YACb,aAAa,CAAC,QAAQ,CAAC,CAAC;YACxB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,SAAS,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,OAAO;QACL,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -28,6 +28,11 @@ export interface CommandContext {
|
|
|
28
28
|
* The TUI and CLI can both call this to execute commands without stdout side effects.
|
|
29
29
|
*/
|
|
30
30
|
export declare function dispatch(input: string, ctx?: CommandContext): Promise<CommandResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Dispatch from pre-tokenized args (e.g. process.argv).
|
|
33
|
+
* Use this when the shell has already handled quoting/splitting.
|
|
34
|
+
*/
|
|
35
|
+
export declare function dispatchTokens(tokens: string[], ctx?: CommandContext): Promise<CommandResult>;
|
|
31
36
|
/** All registered command names, for autocomplete. */
|
|
32
37
|
export declare const commandNames: string[];
|
|
33
38
|
/** Help entries for each command, with category and description. */
|
|
@@ -1217,6 +1217,106 @@ register("agent stop", { execute: executeAgentStop, description: "Stop an agent
|
|
|
1217
1217
|
register("agent steer", { execute: executeAgentSteer, description: "Send a steer message to an agent", category: "agent", usage: "agent steer <slug> <message>" });
|
|
1218
1218
|
register("agent inspect", { execute: executeAgentInspect, description: "Inspect agent config and state", category: "agent", usage: "agent inspect <slug>" });
|
|
1219
1219
|
register("agent templates", { execute: executeAgentTemplates, description: "List available agent templates", category: "agent", usage: "agent templates" });
|
|
1220
|
+
// -- Scan --
|
|
1221
|
+
async function executeScan(args, ctx) {
|
|
1222
|
+
const { positional, opts } = extractOpts(args);
|
|
1223
|
+
const dir = positional[0] || ".";
|
|
1224
|
+
const maxFiles = typeof opts["max-files"] === "string" ? parseInt(opts["max-files"], 10) : undefined;
|
|
1225
|
+
if (maxFiles !== undefined && (!Number.isInteger(maxFiles) || maxFiles <= 0)) {
|
|
1226
|
+
return fail("Invalid --max-files. Expected a positive integer.");
|
|
1227
|
+
}
|
|
1228
|
+
const depth = typeof opts.depth === "string" ? parseInt(opts.depth, 10) : undefined;
|
|
1229
|
+
if (depth !== undefined && (!Number.isInteger(depth) || depth < 0)) {
|
|
1230
|
+
return fail("Invalid --depth. Expected a non-negative integer.");
|
|
1231
|
+
}
|
|
1232
|
+
const extensions = typeof opts.extensions === "string" ? opts.extensions : undefined;
|
|
1233
|
+
const force = opts.force === true;
|
|
1234
|
+
const dryRun = opts["dry-run"] === true;
|
|
1235
|
+
try {
|
|
1236
|
+
const { scanFiles, loadScanConfig } = await import("../lib/file-scanner.js");
|
|
1237
|
+
const scanOpts = loadScanConfig({
|
|
1238
|
+
extensions: extensions?.split(",").map((e) => e.trim()),
|
|
1239
|
+
maxFiles,
|
|
1240
|
+
depth,
|
|
1241
|
+
force,
|
|
1242
|
+
dryRun,
|
|
1243
|
+
});
|
|
1244
|
+
const client = makeClient(ctx);
|
|
1245
|
+
const result = await scanFiles(dir, scanOpts, async (content, context) => {
|
|
1246
|
+
return client.post("/v1/context/text", { content, context });
|
|
1247
|
+
});
|
|
1248
|
+
return ok({ scanned: result.scanned, skipped: result.skipped, errors: result.errors }, ctx);
|
|
1249
|
+
}
|
|
1250
|
+
catch (err) {
|
|
1251
|
+
return wrapError(err);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
register("scan", { execute: executeScan, description: "Scan directory and ingest files", category: "config", usage: "scan [dir] [--max-files <n>] [--force] [--dry-run]" });
|
|
1255
|
+
// -- Register --
|
|
1256
|
+
async function executeRegister(args, ctx) {
|
|
1257
|
+
const { opts } = extractOpts(args);
|
|
1258
|
+
const email = typeof opts.email === "string" ? opts.email : undefined;
|
|
1259
|
+
if (!email)
|
|
1260
|
+
return fail("Usage: register --email <email>");
|
|
1261
|
+
try {
|
|
1262
|
+
const { persistRegistration } = await import("../lib/config.js");
|
|
1263
|
+
const client = new NexClient(undefined, ctx.timeout ?? resolveTimeout());
|
|
1264
|
+
const data = await client.register(email, typeof opts.name === "string" ? opts.name : undefined, typeof opts.company === "string" ? opts.company : undefined);
|
|
1265
|
+
persistRegistration(data);
|
|
1266
|
+
return ok(data, ctx);
|
|
1267
|
+
}
|
|
1268
|
+
catch (err) {
|
|
1269
|
+
return wrapError(err);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
register("register", { execute: executeRegister, description: "Register a new Nex workspace", category: "config", usage: "register --email <email> [--name <name>] [--company <company>]" });
|
|
1273
|
+
// -- Session --
|
|
1274
|
+
async function executeSessionList(_args, ctx) {
|
|
1275
|
+
try {
|
|
1276
|
+
const { SessionStore } = await import("../lib/session-store.js");
|
|
1277
|
+
const store = new SessionStore();
|
|
1278
|
+
return ok(store.list(), ctx);
|
|
1279
|
+
}
|
|
1280
|
+
catch (err) {
|
|
1281
|
+
return wrapError(err);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
async function executeSessionClear(_args, ctx) {
|
|
1285
|
+
try {
|
|
1286
|
+
const { SessionStore } = await import("../lib/session-store.js");
|
|
1287
|
+
const store = new SessionStore();
|
|
1288
|
+
store.clear();
|
|
1289
|
+
return ok({ message: "All sessions cleared." }, ctx);
|
|
1290
|
+
}
|
|
1291
|
+
catch (err) {
|
|
1292
|
+
return wrapError(err);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
register("session list", { execute: executeSessionList, description: "List stored session mappings", category: "config" });
|
|
1296
|
+
register("session clear", { execute: executeSessionClear, description: "Clear all session mappings", category: "config" });
|
|
1297
|
+
// -- List member operations --
|
|
1298
|
+
function makeListRecordCommand(method, usage, pathFn, bodyFn) {
|
|
1299
|
+
return async (args, ctx) => {
|
|
1300
|
+
const { positional, opts } = extractOpts(args);
|
|
1301
|
+
const [listId, recordId] = positional;
|
|
1302
|
+
if (!listId || !recordId)
|
|
1303
|
+
return fail(`Usage: ${usage}`);
|
|
1304
|
+
try {
|
|
1305
|
+
const client = makeClient(ctx);
|
|
1306
|
+
const path = pathFn(encodeURIComponent(listId), encodeURIComponent(recordId));
|
|
1307
|
+
const body = bodyFn?.(recordId, opts);
|
|
1308
|
+
const data = method === "delete" ? await client.delete(path) : await client[method](path, body);
|
|
1309
|
+
return ok(data, ctx);
|
|
1310
|
+
}
|
|
1311
|
+
catch (err) {
|
|
1312
|
+
return wrapError(err);
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
register("list add-member", { execute: makeListRecordCommand("post", "list add-member <list-id> <record-id>", (lid) => `/v1/lists/${lid}/records`, (rid) => ({ record_id: rid })), description: "Add a record to a list", category: "write", usage: "list add-member <list-id> <record-id>" });
|
|
1317
|
+
register("list upsert-member", { execute: makeListRecordCommand("put", "list upsert-member <list-id> <record-id>", (lid, rid) => `/v1/lists/${lid}/records/${rid}`, (_rid, opts) => opts), description: "Upsert a record in a list", category: "write", usage: "list upsert-member <list-id> <record-id>" });
|
|
1318
|
+
register("list update-record", { execute: makeListRecordCommand("patch", "list update-record <list-id> <record-id>", (lid, rid) => `/v1/lists/${lid}/records/${rid}`, (_rid, opts) => opts), description: "Update a list record", category: "write", usage: "list update-record <list-id> <record-id>" });
|
|
1319
|
+
register("list remove-record", { execute: makeListRecordCommand("delete", "list remove-record <list-id> <record-id>", (lid, rid) => `/v1/lists/${lid}/records/${rid}`), description: "Remove a record from a list", category: "write", usage: "list remove-record <list-id> <record-id>" });
|
|
1220
1320
|
// -- TUI view hints (so aliases resolve and dispatch does not return "unknown command") --
|
|
1221
1321
|
register("chat", {
|
|
1222
1322
|
execute: async () => ({ output: "Use the 'c' keybinding to open the chat view.", exitCode: 0 }),
|
|
@@ -1241,6 +1341,7 @@ const COMMAND_ALIASES = {
|
|
|
1241
1341
|
objects: "object list",
|
|
1242
1342
|
orch: "orchestration",
|
|
1243
1343
|
setup: "init",
|
|
1344
|
+
sessions: "session list",
|
|
1244
1345
|
};
|
|
1245
1346
|
/**
|
|
1246
1347
|
* Resolve alias tokens into their canonical form.
|
|
@@ -1285,6 +1386,13 @@ function resolveCommand(tokens) {
|
|
|
1285
1386
|
*/
|
|
1286
1387
|
export async function dispatch(input, ctx) {
|
|
1287
1388
|
const tokens = parseInput(input);
|
|
1389
|
+
return dispatchTokens(tokens, ctx);
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* Dispatch from pre-tokenized args (e.g. process.argv).
|
|
1393
|
+
* Use this when the shell has already handled quoting/splitting.
|
|
1394
|
+
*/
|
|
1395
|
+
export async function dispatchTokens(tokens, ctx) {
|
|
1288
1396
|
if (tokens.length === 0) {
|
|
1289
1397
|
return fail("No command provided.");
|
|
1290
1398
|
}
|