foliko 2.0.4 → 2.0.5
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/.editorconfig +56 -56
- package/.lintstagedrc +7 -7
- package/.prettierignore +29 -29
- package/.prettierrc +11 -11
- package/Dockerfile +63 -63
- package/docs/public-api.md +924 -6
- package/install.ps1 +129 -129
- package/install.sh +121 -121
- package/package.json +1 -1
- package/plugins/core/default/bootstrap.js +21 -0
- package/plugins/core/skill-manager/index.js +158 -0
- package/plugins/core/workflow/index.js +24 -0
- package/skills/find-skills/SKILL.md +133 -133
- package/src/agent/prompt-registry.js +95 -89
- package/src/framework/framework.js +616 -2
- package/src/plugin/base.js +5 -5
- package/tests/core/plugin-prompts.test.js +13 -13
- package/tests/core/prompt-registry.test.js +59 -51
|
@@ -17,7 +17,7 @@ describe('Plugin declarative prompts', () => {
|
|
|
17
17
|
prompts = [
|
|
18
18
|
{
|
|
19
19
|
name: 'demo',
|
|
20
|
-
|
|
20
|
+
priority: 50,
|
|
21
21
|
description: 'demo part',
|
|
22
22
|
provider: function () {
|
|
23
23
|
return 'demo-content';
|
|
@@ -43,7 +43,7 @@ describe('Plugin declarative prompts', () => {
|
|
|
43
43
|
prompts = [
|
|
44
44
|
{
|
|
45
45
|
name: 'foo',
|
|
46
|
-
|
|
46
|
+
scope: 'global',
|
|
47
47
|
provider: function () {
|
|
48
48
|
return 'foo';
|
|
49
49
|
},
|
|
@@ -70,7 +70,7 @@ describe('Plugin declarative prompts', () => {
|
|
|
70
70
|
{ provider: () => 'no-name' },
|
|
71
71
|
{
|
|
72
72
|
name: 'valid',
|
|
73
|
-
|
|
73
|
+
priority: 50,
|
|
74
74
|
provider: function () {
|
|
75
75
|
return 'valid-content';
|
|
76
76
|
},
|
|
@@ -99,7 +99,7 @@ describe('Plugin declarative prompts', () => {
|
|
|
99
99
|
prompts = [
|
|
100
100
|
{
|
|
101
101
|
name: 'capture',
|
|
102
|
-
|
|
102
|
+
scope: 'global',
|
|
103
103
|
provider: function () {
|
|
104
104
|
capturedThis = this;
|
|
105
105
|
return 'x';
|
|
@@ -122,8 +122,8 @@ describe('Plugin declarative prompts', () => {
|
|
|
122
122
|
return this;
|
|
123
123
|
}
|
|
124
124
|
prompts = [
|
|
125
|
-
{ name: 'a',
|
|
126
|
-
{ name: 'b',
|
|
125
|
+
{ name: 'a', priority: 50, provider: () => 'a' },
|
|
126
|
+
{ name: 'b', priority: 60, provider: () => 'b' },
|
|
127
127
|
];
|
|
128
128
|
}
|
|
129
129
|
const plugin = new CleanupPlugin();
|
|
@@ -146,7 +146,7 @@ describe('Plugin declarative prompts', () => {
|
|
|
146
146
|
prompts = [
|
|
147
147
|
{
|
|
148
148
|
name: 'reloadable',
|
|
149
|
-
|
|
149
|
+
priority: 50,
|
|
150
150
|
provider: function () {
|
|
151
151
|
return 'v1';
|
|
152
152
|
},
|
|
@@ -177,7 +177,7 @@ describe('Plugin declarative prompts', () => {
|
|
|
177
177
|
prompts = [
|
|
178
178
|
{
|
|
179
179
|
name: 'demo',
|
|
180
|
-
|
|
180
|
+
priority: 50,
|
|
181
181
|
provider: function () {
|
|
182
182
|
return 'demo-content';
|
|
183
183
|
},
|
|
@@ -192,7 +192,7 @@ describe('Plugin declarative prompts', () => {
|
|
|
192
192
|
expect(fw.prompts.getPart('test-owner', 'demo').get()).toBe('demo-content');
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
-
it('renders in
|
|
195
|
+
it('renders in priority order', () => {
|
|
196
196
|
class MultiPlugin extends Plugin {
|
|
197
197
|
name = 'multi';
|
|
198
198
|
install(f) {
|
|
@@ -200,9 +200,9 @@ describe('Plugin declarative prompts', () => {
|
|
|
200
200
|
return this;
|
|
201
201
|
}
|
|
202
202
|
prompts = [
|
|
203
|
-
{ name: 'behavior',
|
|
204
|
-
{ name: 'identity',
|
|
205
|
-
{ name: 'capability',
|
|
203
|
+
{ name: 'behavior', priority: 50, provider: () => 'BEHAVIOR_CONTENT' },
|
|
204
|
+
{ name: 'identity', priority: 10, provider: () => 'IDENTITY_CONTENT' },
|
|
205
|
+
{ name: 'capability', priority: 30, provider: () => 'CAPABILITY_CONTENT' },
|
|
206
206
|
];
|
|
207
207
|
}
|
|
208
208
|
const plugin = new MultiPlugin();
|
|
@@ -216,4 +216,4 @@ describe('Plugin declarative prompts', () => {
|
|
|
216
216
|
expect(identityIdx).toBeLessThan(capabilityIdx);
|
|
217
217
|
expect(capabilityIdx).toBeLessThan(behaviorIdx);
|
|
218
218
|
});
|
|
219
|
-
});
|
|
219
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { PromptRegistry
|
|
2
|
+
import { PromptRegistry } from '../../src/index';
|
|
3
3
|
|
|
4
4
|
describe('PromptRegistry', () => {
|
|
5
5
|
let reg;
|
|
@@ -10,13 +10,20 @@ describe('PromptRegistry', () => {
|
|
|
10
10
|
|
|
11
11
|
describe('basic API', () => {
|
|
12
12
|
it('registers and retrieves a part', () => {
|
|
13
|
-
reg.register('plugin-a', 'foo', () => 'hello'
|
|
13
|
+
reg.register('plugin-a', 'foo', () => 'hello');
|
|
14
14
|
const part = reg.getPart('plugin-a', 'foo');
|
|
15
15
|
expect(part).toBeTruthy();
|
|
16
16
|
expect(part.get()).toBe('hello');
|
|
17
17
|
expect(reg.count()).toBe(1);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
it('registers with scope and priority', () => {
|
|
21
|
+
reg.register('plugin-a', 'foo', () => 'hello', { scope: 'global', priority: 50 });
|
|
22
|
+
const part = reg.getPart('plugin-a', 'foo');
|
|
23
|
+
expect(part.scope).toBe('global');
|
|
24
|
+
expect(part.priority).toBe(50);
|
|
25
|
+
});
|
|
26
|
+
|
|
20
27
|
it('requires owner/name/provider', () => {
|
|
21
28
|
expect(() => reg.register('', 'foo', () => 'x')).toThrow(/owner/);
|
|
22
29
|
expect(() => reg.register('p', '', () => 'x')).toThrow(/name/);
|
|
@@ -47,46 +54,53 @@ describe('PromptRegistry', () => {
|
|
|
47
54
|
});
|
|
48
55
|
});
|
|
49
56
|
|
|
50
|
-
describe('
|
|
51
|
-
it('orders by
|
|
52
|
-
reg.register('p', 'a', () => '
|
|
53
|
-
reg.register('p', 'b', () => '
|
|
54
|
-
reg.register('p', 'c', () => '
|
|
55
|
-
const order = reg.list().map((p) => p.
|
|
56
|
-
expect(order).toEqual(['
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('uses explicit order within same slot', () => {
|
|
60
|
-
reg.register('p', 'late', () => 'late', { slot: 'IDENTITY', order: 5 });
|
|
61
|
-
reg.register('p', 'early', () => 'early', { slot: 'IDENTITY', order: 1 });
|
|
62
|
-
const names = reg.list().map((p) => p.name);
|
|
63
|
-
expect(names).toEqual(['early', 'late']);
|
|
57
|
+
describe('priority ordering', () => {
|
|
58
|
+
it('orders by priority (smaller first)', () => {
|
|
59
|
+
reg.register('p', 'a', () => 'a', { priority: 100 });
|
|
60
|
+
reg.register('p', 'b', () => 'b', { priority: 10 });
|
|
61
|
+
reg.register('p', 'c', () => 'c', { priority: 50 });
|
|
62
|
+
const order = reg.list().map((p) => p.name);
|
|
63
|
+
expect(order).toEqual(['b', 'c', 'a']);
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
it('falls back to alphabetical when
|
|
67
|
-
reg.register('zeta', 'z', () => 'z', {
|
|
68
|
-
reg.register('alpha', 'a', () => 'a', {
|
|
66
|
+
it('falls back to owner alphabetical when priority is equal', () => {
|
|
67
|
+
reg.register('zeta', 'z', () => 'z', { priority: 50 });
|
|
68
|
+
reg.register('alpha', 'a', () => 'a', { priority: 50 });
|
|
69
69
|
const owners = reg.list().map((p) => p.owner);
|
|
70
70
|
expect(owners).toEqual(['alpha', 'zeta']);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
it('
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
expect(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
it('defaults to priority 100', () => {
|
|
74
|
+
reg.register('p', 'a', () => 'a');
|
|
75
|
+
reg.register('p', 'b', () => 'b', { priority: 50 });
|
|
76
|
+
const order = reg.list().map((p) => p.name);
|
|
77
|
+
expect(order).toEqual(['b', 'a']);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('scope filtering', () => {
|
|
82
|
+
it('listByScope filters correctly', () => {
|
|
83
|
+
reg.register('p', 'a', () => 'a', { scope: 'global' });
|
|
84
|
+
reg.register('p', 'b', () => 'b', { scope: 'main' });
|
|
85
|
+
reg.register('p', 'c', () => 'c', { scope: 'sub-1' });
|
|
86
|
+
expect(reg.listByScope('global').map((p) => p.name)).toEqual(['a']);
|
|
87
|
+
expect(reg.listByScope('main').map((p) => p.name)).toEqual(['b']);
|
|
88
|
+
expect(reg.listByScope('sub-1').map((p) => p.name)).toEqual(['c']);
|
|
82
89
|
});
|
|
83
90
|
|
|
84
|
-
it('
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
it('listForAgent includes global and matching scope', () => {
|
|
92
|
+
reg.register('p', 'global', () => 'g', { scope: 'global' });
|
|
93
|
+
reg.register('p', 'main', () => 'm', { scope: 'main' });
|
|
94
|
+
reg.register('p', 'sub1', () => 's1', { scope: 'sub-1' });
|
|
95
|
+
reg.register('p', 'sub2', () => 's2', { scope: 'sub-2' });
|
|
96
|
+
|
|
97
|
+
// Main agent sees global + main
|
|
98
|
+
const mainParts = reg.listForAgent(null).map((p) => p.name);
|
|
99
|
+
expect(mainParts).toEqual(['global', 'main']);
|
|
100
|
+
|
|
101
|
+
// sub-1 agent sees global + sub1
|
|
102
|
+
const sub1Parts = reg.listForAgent('sub-1').map((p) => p.name);
|
|
103
|
+
expect(sub1Parts).toEqual(['global', 'sub1']);
|
|
90
104
|
});
|
|
91
105
|
});
|
|
92
106
|
|
|
@@ -97,6 +111,13 @@ describe('PromptRegistry', () => {
|
|
|
97
111
|
expect(reg.build()).toBe('AAA\n\nBBB');
|
|
98
112
|
});
|
|
99
113
|
|
|
114
|
+
it('builds only parts for specific agent', () => {
|
|
115
|
+
reg.register('p', 'global', () => 'G', { scope: 'global' });
|
|
116
|
+
reg.register('p', 'main', () => 'M', { scope: 'main' });
|
|
117
|
+
reg.register('p', 'sub1', () => 'S1', { scope: 'sub-1' });
|
|
118
|
+
expect(reg.buildForAgent('sub-1')).toBe('G\n\nS1');
|
|
119
|
+
});
|
|
120
|
+
|
|
100
121
|
it('skips null/empty content', () => {
|
|
101
122
|
reg.register('p', 'a', () => 'AAA');
|
|
102
123
|
reg.register('p', 'b', () => null);
|
|
@@ -116,7 +137,6 @@ describe('PromptRegistry', () => {
|
|
|
116
137
|
console.warn = (msg) => warnings.push(msg);
|
|
117
138
|
try {
|
|
118
139
|
const result = reg.build();
|
|
119
|
-
// 排序:after → bad(fail) → good → "BBB\n\nAAA"
|
|
120
140
|
expect(result).toBe('BBB\n\nAAA');
|
|
121
141
|
expect(warnings.length).toBe(1);
|
|
122
142
|
} finally {
|
|
@@ -170,14 +190,14 @@ describe('PromptRegistry', () => {
|
|
|
170
190
|
|
|
171
191
|
describe('inspect', () => {
|
|
172
192
|
it('returns structured per-part metadata', () => {
|
|
173
|
-
reg.register('plugin-a', 'foo', () => 'hello world', {
|
|
193
|
+
reg.register('plugin-a', 'foo', () => 'hello world', { scope: 'global', priority: 50 });
|
|
174
194
|
const out = reg.inspect();
|
|
175
195
|
expect(out).toHaveLength(1);
|
|
176
196
|
expect(out[0]).toMatchObject({
|
|
177
197
|
owner: 'plugin-a',
|
|
178
198
|
name: 'foo',
|
|
179
|
-
|
|
180
|
-
|
|
199
|
+
scope: 'global',
|
|
200
|
+
priority: 50,
|
|
181
201
|
hasContent: true,
|
|
182
202
|
length: 11,
|
|
183
203
|
});
|
|
@@ -190,20 +210,8 @@ describe('PromptRegistry', () => {
|
|
|
190
210
|
const { Framework } = require('../../src/index');
|
|
191
211
|
const fw = new Framework({ silent: true });
|
|
192
212
|
expect(fw.prompts).toBeDefined();
|
|
193
|
-
expect(fw.prompts).toBe(fw.promptRegistry);
|
|
194
213
|
expect(typeof fw.prompts.register).toBe('function');
|
|
195
214
|
expect(typeof fw.prompts.preview).toBe('function');
|
|
196
215
|
});
|
|
197
|
-
|
|
198
|
-
it('each agent has its own promptRegistry via BaseAgent', () => {
|
|
199
|
-
const { BaseAgent } = require('../../src/agent/base');
|
|
200
|
-
const { Framework } = require('../../src/index');
|
|
201
|
-
const fw = new Framework({ silent: true });
|
|
202
|
-
const agent = new BaseAgent(fw, { name: 'Test' });
|
|
203
|
-
expect(agent.promptRegistry).toBeDefined();
|
|
204
|
-
expect(agent.promptRegistry).toBe(agent._promptRegistry);
|
|
205
|
-
agent.promptRegistry.register('test', 'foo', () => 'x', { slot: 'CUSTOM' });
|
|
206
|
-
expect(agent.promptRegistry.count()).toBe(1);
|
|
207
|
-
});
|
|
208
216
|
});
|
|
209
|
-
});
|
|
217
|
+
});
|