@kevin5251984/guild 0.2.12
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/LICENSE +21 -0
- package/bin/guildd.mjs +20 -0
- package/cordis.yml +24 -0
- package/package.json +52 -0
- package/src/agent-file.ts +125 -0
- package/src/browser.ts +668 -0
- package/src/catalog/default-bots.ts +263 -0
- package/src/catalog/skills.ts +128 -0
- package/src/catalog/subagents.ts +70 -0
- package/src/chat-parts.ts +71 -0
- package/src/cli-args.ts +75 -0
- package/src/cli.ts +60 -0
- package/src/compact.ts +355 -0
- package/src/cordis.d.ts +40 -0
- package/src/db.ts +653 -0
- package/src/generate.ts +673 -0
- package/src/handlers.ts +1623 -0
- package/src/harness.ts +326 -0
- package/src/host-agents.ts +137 -0
- package/src/host-browse.ts +199 -0
- package/src/host-skills.ts +150 -0
- package/src/image-gen.ts +270 -0
- package/src/index.ts +12 -0
- package/src/llm.ts +993 -0
- package/src/mcp.ts +563 -0
- package/src/memory.ts +159 -0
- package/src/mention.ts +176 -0
- package/src/oauth.ts +1474 -0
- package/src/plugins/api.ts +8 -0
- package/src/plugins/chat.ts +31 -0
- package/src/plugins/harness.ts +77 -0
- package/src/plugins/llm.ts +50 -0
- package/src/plugins/mcp.ts +58 -0
- package/src/plugins/memory.ts +42 -0
- package/src/plugins/oauth.ts +47 -0
- package/src/plugins/server.ts +126 -0
- package/src/plugins/store.ts +29 -0
- package/src/plugins/tools.ts +79 -0
- package/src/public/buddy.js +432 -0
- package/src/public/chat.css +3045 -0
- package/src/public/chat.html +5834 -0
- package/src/public/favicon-16.png +0 -0
- package/src/public/favicon-16.svg +10 -0
- package/src/public/favicon-32.png +0 -0
- package/src/public/favicon.ico +0 -0
- package/src/public/favicon.svg +13 -0
- package/src/public/i18n.js +663 -0
- package/src/public/index.html +143 -0
- package/src/public/library.html +678 -0
- package/src/public/mcp-add.html +126 -0
- package/src/public/md.js +332 -0
- package/src/public/rpg/inn-street.jpg +0 -0
- package/src/public/settings.html +795 -0
- package/src/public/skills-add.html +212 -0
- package/src/public/studio.html +1181 -0
- package/src/public/style.css +1678 -0
- package/src/public/subagents-add.html +152 -0
- package/src/router.ts +978 -0
- package/src/send-budget.ts +52 -0
- package/src/server.ts +1 -0
- package/src/skill-import.ts +250 -0
- package/src/slash.ts +15 -0
- package/src/start.ts +103 -0
- package/src/store.ts +1208 -0
- package/src/subagent.ts +355 -0
- package/src/tools.ts +818 -0
- package/src/trajectory.ts +339 -0
- package/src/usage.ts +111 -0
- package/vendor/protocol/package.json +19 -0
- package/vendor/protocol/src/index.ts +159 -0
package/src/mention.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/** @channel / @quest / @here / @all still mean the whole quest. */
|
|
2
|
+
const BROADCAST = /(^|\s)@(channel|quest|here|all)\b/i;
|
|
3
|
+
const HANDLE = /@([A-Za-z0-9_-]+)/g;
|
|
4
|
+
/** Consecutive @handles at the start of a line (optional backticks). */
|
|
5
|
+
const LINE_LEAD = /^[ \t]*((?:`?@[A-Za-z0-9_-]+`?[\s,、]*)+)/;
|
|
6
|
+
/** `1. @design` / `- @infra`. */
|
|
7
|
+
const LIST_MARK = /^[ \t]*(?:\d+[.)、]\s*|[-*•]\s+)/;
|
|
8
|
+
/** Assignee right after the marker, or after 通過後/最後/再叫… — not the first @ anywhere on the line. */
|
|
9
|
+
const LIST_ASSIGN =
|
|
10
|
+
/^(?:`?@([A-Za-z0-9_-]+)`?|(?:通過後|最後|再叫|交給|交棒(?:給)?|指派|(?:call|ask)\s+)\s*`?@([A-Za-z0-9_-]+)`?)/i;
|
|
11
|
+
|
|
12
|
+
/** Drop fenced / inline code so `@pm` in a snippet does not dispatch. */
|
|
13
|
+
export function stripMentionNoise(text: string): string {
|
|
14
|
+
return String(text ?? "")
|
|
15
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
16
|
+
.replace(/`[^`]*`/g, " ");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isBroadcastMention(text: string): boolean {
|
|
20
|
+
return BROADCAST.test(stripMentionNoise(text));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Every unique known @handle in the text, in first-seen order. */
|
|
24
|
+
export function mentionedHandles(text: string, handles: string[]): string[] {
|
|
25
|
+
const scan = stripMentionNoise(text);
|
|
26
|
+
const known = new Set(handles.map((handle) => handle.toLowerCase()));
|
|
27
|
+
const out: string[] = [];
|
|
28
|
+
for (const row of scan.matchAll(/(?:^|\s)@([A-Za-z0-9_-]+)\b/g)) {
|
|
29
|
+
const key = row[1].toLowerCase();
|
|
30
|
+
if (!known.has(key) || out.includes(key)) continue;
|
|
31
|
+
out.push(key);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type MentionBlock = {
|
|
37
|
+
start: number;
|
|
38
|
+
end: number;
|
|
39
|
+
handles: string[];
|
|
40
|
+
kind: "lead" | "list";
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function knownHandlesIn(chunk: string, known: Set<string>): string[] {
|
|
44
|
+
const names: string[] = [];
|
|
45
|
+
for (const row of chunk.matchAll(HANDLE)) {
|
|
46
|
+
const key = row[1].toLowerCase();
|
|
47
|
+
if (!known.has(key) || names.includes(key)) continue;
|
|
48
|
+
names.push(key);
|
|
49
|
+
}
|
|
50
|
+
return names;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Line-start @handle groups, plus markdown list items that name a seat. */
|
|
54
|
+
export function lineStartMentions(text: string, handles: string[]): MentionBlock[] {
|
|
55
|
+
const raw = String(text ?? "");
|
|
56
|
+
const known = new Set(handles.map((handle) => handle.toLowerCase()));
|
|
57
|
+
const starts: { start: number; handles: string[]; kind: "lead" | "list" }[] = [];
|
|
58
|
+
let fence = false;
|
|
59
|
+
let offset = 0;
|
|
60
|
+
for (const part of raw.split(/(\r?\n)/)) {
|
|
61
|
+
if (part === "\n" || part === "\r\n") {
|
|
62
|
+
offset += part.length;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const trimmed = part.trim();
|
|
66
|
+
if (trimmed.startsWith("```")) {
|
|
67
|
+
fence = !fence;
|
|
68
|
+
offset += part.length;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (!fence) {
|
|
72
|
+
const listed = LIST_MARK.test(part);
|
|
73
|
+
const lead = part.match(LINE_LEAD);
|
|
74
|
+
let names: string[] = [];
|
|
75
|
+
if (lead) names = knownHandlesIn(lead[1], known);
|
|
76
|
+
if (!names.length && listed) {
|
|
77
|
+
const rest = part.replace(LIST_MARK, "");
|
|
78
|
+
const hit = rest.match(LIST_ASSIGN);
|
|
79
|
+
const key = (hit?.[1] || hit?.[2] || "").toLowerCase();
|
|
80
|
+
if (key && known.has(key)) names = [key];
|
|
81
|
+
}
|
|
82
|
+
if (names.length) {
|
|
83
|
+
starts.push({
|
|
84
|
+
start: offset,
|
|
85
|
+
handles: names,
|
|
86
|
+
kind: listed ? "list" : "lead",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
offset += part.length;
|
|
91
|
+
}
|
|
92
|
+
return starts.map((row, i) => ({
|
|
93
|
+
start: row.start,
|
|
94
|
+
end: i + 1 < starts.length ? starts[i + 1].start : raw.length,
|
|
95
|
+
handles: row.handles,
|
|
96
|
+
kind: row.kind,
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Fallback when the client did not pick an assignee.
|
|
102
|
+
* Every line-start @handle group, otherwise the first @handle in prose.
|
|
103
|
+
*/
|
|
104
|
+
export function summonedHandles(text: string, handles: string[]): string[] {
|
|
105
|
+
const known = new Set(handles.map((handle) => handle.toLowerCase()));
|
|
106
|
+
const take = (names: string[]): string[] => {
|
|
107
|
+
const out: string[] = [];
|
|
108
|
+
for (const name of names) {
|
|
109
|
+
const key = name.toLowerCase();
|
|
110
|
+
if (!known.has(key) || out.includes(key)) continue;
|
|
111
|
+
out.push(key);
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
};
|
|
115
|
+
const blocks = lineStartMentions(text, handles);
|
|
116
|
+
if (blocks.length) {
|
|
117
|
+
const out: string[] = [];
|
|
118
|
+
for (const block of blocks) {
|
|
119
|
+
for (const handle of block.handles) {
|
|
120
|
+
if (!out.includes(handle)) out.push(handle);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
const scan = stripMentionNoise(text);
|
|
126
|
+
const first = /(?:^|\s)@([A-Za-z0-9_-]+)\b/.exec(scan);
|
|
127
|
+
if (first) return take([first[1]]);
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Fenced samples only. Keep inline `code` so 「再叫 `@infra`」 still parses. */
|
|
132
|
+
function stripFences(text: string): string {
|
|
133
|
+
return String(text ?? "").replace(/```[\s\S]*?```/g, " ");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Bot replies: line-start specs, else first prose @handle, plus assignment verbs
|
|
138
|
+
* even when the handle is wrapped in backticks (models quote @infra a lot).
|
|
139
|
+
* Fixture chatter like `(@pm / @rd)` has no verb, so it does not hop.
|
|
140
|
+
*/
|
|
141
|
+
const HANDOFF_ASK =
|
|
142
|
+
/(?:(?:再叫|交給|交棒(?:給)?|指派)\s*|(?:call|ask|ping|notify|handoff(?:\s+to)?)\s+)`?@([A-Za-z0-9_-]+)`?/gi;
|
|
143
|
+
|
|
144
|
+
export function handoffHandles(text: string, handles: string[]): string[] {
|
|
145
|
+
const known = new Set(handles.map((handle) => handle.toLowerCase()));
|
|
146
|
+
const out = summonedHandles(text, handles);
|
|
147
|
+
const ask = new RegExp(HANDOFF_ASK.source, "gi");
|
|
148
|
+
for (const row of stripFences(text).matchAll(ask)) {
|
|
149
|
+
const key = row[1].toLowerCase();
|
|
150
|
+
if (!known.has(key) || out.includes(key)) continue;
|
|
151
|
+
out.push(key);
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Spec for one seat: shared preamble plus that @handle's line-start block.
|
|
158
|
+
* Full text when the handle was only summoned in prose.
|
|
159
|
+
*/
|
|
160
|
+
export function assignmentFor(
|
|
161
|
+
text: string,
|
|
162
|
+
handle: string,
|
|
163
|
+
handles: string[],
|
|
164
|
+
): string {
|
|
165
|
+
const raw = String(text ?? "");
|
|
166
|
+
const key = String(handle || "").toLowerCase();
|
|
167
|
+
const blocks = lineStartMentions(raw, handles);
|
|
168
|
+
if (!key || !blocks.length) return raw;
|
|
169
|
+
const mine = blocks.filter((block) => block.handles.includes(key));
|
|
170
|
+
if (!mine.length) return raw;
|
|
171
|
+
// Numbered plans share constraints ("don't ship until PM signs off"). Slice only classic specs.
|
|
172
|
+
if (!blocks.some((block) => block.kind === "lead")) return raw;
|
|
173
|
+
const preamble = raw.slice(0, blocks[0].start).trim();
|
|
174
|
+
const chunks = mine.map((block) => raw.slice(block.start, block.end).trim());
|
|
175
|
+
return [preamble, ...chunks].filter(Boolean).join("\n\n");
|
|
176
|
+
}
|