@soleri/core 9.5.0 → 9.7.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/adapters/claude-code-adapter.d.ts +27 -0
- package/dist/adapters/claude-code-adapter.d.ts.map +1 -0
- package/dist/adapters/claude-code-adapter.js +111 -0
- package/dist/adapters/claude-code-adapter.js.map +1 -0
- package/dist/adapters/index.d.ts +9 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +10 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/registry.d.ts +21 -0
- package/dist/adapters/registry.d.ts.map +1 -0
- package/dist/adapters/registry.js +44 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/types.d.ts +93 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +10 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/brain/brain.d.ts +12 -1
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +106 -44
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +36 -30
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/chat/agent-loop.js +1 -1
- package/dist/chat/agent-loop.js.map +1 -1
- package/dist/chat/notifications.d.ts.map +1 -1
- package/dist/chat/notifications.js +4 -0
- package/dist/chat/notifications.js.map +1 -1
- package/dist/control/intent-router.d.ts +1 -0
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +11 -5
- package/dist/control/intent-router.js.map +1 -1
- package/dist/curator/curator.d.ts +4 -0
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +141 -27
- package/dist/curator/curator.js.map +1 -1
- package/dist/index.d.ts +22 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/llm-client.d.ts.map +1 -1
- package/dist/llm/llm-client.js +1 -0
- package/dist/llm/llm-client.js.map +1 -1
- package/dist/packs/index.d.ts +3 -2
- package/dist/packs/index.d.ts.map +1 -1
- package/dist/packs/index.js +3 -2
- package/dist/packs/index.js.map +1 -1
- package/dist/packs/lockfile.d.ts +23 -1
- package/dist/packs/lockfile.d.ts.map +1 -1
- package/dist/packs/lockfile.js +50 -4
- package/dist/packs/lockfile.js.map +1 -1
- package/dist/packs/pack-installer.d.ts +10 -0
- package/dist/packs/pack-installer.d.ts.map +1 -1
- package/dist/packs/pack-installer.js +69 -2
- package/dist/packs/pack-installer.js.map +1 -1
- package/dist/packs/pack-lifecycle.d.ts +50 -0
- package/dist/packs/pack-lifecycle.d.ts.map +1 -0
- package/dist/packs/pack-lifecycle.js +91 -0
- package/dist/packs/pack-lifecycle.js.map +1 -0
- package/dist/packs/types.d.ts +76 -29
- package/dist/packs/types.d.ts.map +1 -1
- package/dist/packs/types.js +9 -0
- package/dist/packs/types.js.map +1 -1
- package/dist/persistence/sqlite-provider.d.ts +5 -1
- package/dist/persistence/sqlite-provider.d.ts.map +1 -1
- package/dist/persistence/sqlite-provider.js +22 -2
- package/dist/persistence/sqlite-provider.js.map +1 -1
- package/dist/planning/github-projection.d.ts +11 -9
- package/dist/planning/github-projection.d.ts.map +1 -1
- package/dist/planning/github-projection.js +47 -43
- package/dist/planning/github-projection.js.map +1 -1
- package/dist/planning/goal-ancestry.d.ts +72 -0
- package/dist/planning/goal-ancestry.d.ts.map +1 -0
- package/dist/planning/goal-ancestry.js +137 -0
- package/dist/planning/goal-ancestry.js.map +1 -0
- package/dist/planning/plan-lifecycle.d.ts +2 -0
- package/dist/planning/plan-lifecycle.d.ts.map +1 -1
- package/dist/planning/plan-lifecycle.js +1 -0
- package/dist/planning/plan-lifecycle.js.map +1 -1
- package/dist/planning/planner-types.d.ts +2 -0
- package/dist/planning/planner-types.d.ts.map +1 -1
- package/dist/plugins/types.d.ts +21 -21
- package/dist/queue/pipeline-runner.d.ts.map +1 -1
- package/dist/queue/pipeline-runner.js +4 -0
- package/dist/queue/pipeline-runner.js.map +1 -1
- package/dist/runtime/context-health.d.ts +14 -1
- package/dist/runtime/context-health.d.ts.map +1 -1
- package/dist/runtime/context-health.js +30 -2
- package/dist/runtime/context-health.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
- package/dist/runtime/curator-extra-ops.js +9 -1
- package/dist/runtime/curator-extra-ops.js.map +1 -1
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
- package/dist/runtime/facades/memory-facade.js +169 -0
- package/dist/runtime/facades/memory-facade.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +133 -4
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +128 -90
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/session-briefing.d.ts.map +1 -1
- package/dist/runtime/session-briefing.js +44 -11
- package/dist/runtime/session-briefing.js.map +1 -1
- package/dist/runtime/shutdown-registry.d.ts +36 -0
- package/dist/runtime/shutdown-registry.d.ts.map +1 -0
- package/dist/runtime/shutdown-registry.js +74 -0
- package/dist/runtime/shutdown-registry.js.map +1 -0
- package/dist/runtime/types.d.ts +10 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/session/compaction-evaluator.d.ts +20 -0
- package/dist/session/compaction-evaluator.d.ts.map +1 -0
- package/dist/session/compaction-evaluator.js +73 -0
- package/dist/session/compaction-evaluator.js.map +1 -0
- package/dist/session/compaction-policy.d.ts +50 -0
- package/dist/session/compaction-policy.d.ts.map +1 -0
- package/dist/session/compaction-policy.js +17 -0
- package/dist/session/compaction-policy.js.map +1 -0
- package/dist/session/handoff-renderer.d.ts +22 -0
- package/dist/session/handoff-renderer.d.ts.map +1 -0
- package/dist/session/handoff-renderer.js +49 -0
- package/dist/session/handoff-renderer.js.map +1 -0
- package/dist/session/index.d.ts +6 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +5 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/policy-resolver.d.ts +20 -0
- package/dist/session/policy-resolver.d.ts.map +1 -0
- package/dist/session/policy-resolver.js +28 -0
- package/dist/session/policy-resolver.js.map +1 -0
- package/dist/skills/sync-skills.d.ts +27 -0
- package/dist/skills/sync-skills.d.ts.map +1 -1
- package/dist/skills/sync-skills.js +92 -1
- package/dist/skills/sync-skills.js.map +1 -1
- package/dist/skills/trust-classifier.d.ts +32 -0
- package/dist/skills/trust-classifier.d.ts.map +1 -0
- package/dist/skills/trust-classifier.js +109 -0
- package/dist/skills/trust-classifier.js.map +1 -0
- package/dist/subagent/concurrency-manager.d.ts +29 -0
- package/dist/subagent/concurrency-manager.d.ts.map +1 -0
- package/dist/subagent/concurrency-manager.js +73 -0
- package/dist/subagent/concurrency-manager.js.map +1 -0
- package/dist/subagent/dispatcher.d.ts +45 -0
- package/dist/subagent/dispatcher.d.ts.map +1 -0
- package/dist/subagent/dispatcher.js +271 -0
- package/dist/subagent/dispatcher.js.map +1 -0
- package/dist/subagent/index.d.ts +14 -0
- package/dist/subagent/index.d.ts.map +1 -0
- package/dist/subagent/index.js +15 -0
- package/dist/subagent/index.js.map +1 -0
- package/dist/subagent/orphan-reaper.d.ts +37 -0
- package/dist/subagent/orphan-reaper.d.ts.map +1 -0
- package/dist/subagent/orphan-reaper.js +71 -0
- package/dist/subagent/orphan-reaper.js.map +1 -0
- package/dist/subagent/result-aggregator.d.ts +7 -0
- package/dist/subagent/result-aggregator.d.ts.map +1 -0
- package/dist/subagent/result-aggregator.js +57 -0
- package/dist/subagent/result-aggregator.js.map +1 -0
- package/dist/subagent/task-checkout.d.ts +36 -0
- package/dist/subagent/task-checkout.d.ts.map +1 -0
- package/dist/subagent/task-checkout.js +52 -0
- package/dist/subagent/task-checkout.js.map +1 -0
- package/dist/subagent/types.d.ts +114 -0
- package/dist/subagent/types.d.ts.map +1 -0
- package/dist/subagent/types.js +9 -0
- package/dist/subagent/types.js.map +1 -0
- package/dist/subagent/workspace-resolver.d.ts +35 -0
- package/dist/subagent/workspace-resolver.d.ts.map +1 -0
- package/dist/subagent/workspace-resolver.js +99 -0
- package/dist/subagent/workspace-resolver.js.map +1 -0
- package/dist/transport/http-server.d.ts.map +1 -1
- package/dist/transport/http-server.js +49 -3
- package/dist/transport/http-server.js.map +1 -1
- package/dist/transport/ws-server.d.ts.map +1 -1
- package/dist/transport/ws-server.js +7 -0
- package/dist/transport/ws-server.js.map +1 -1
- package/dist/vault/linking.d.ts +3 -4
- package/dist/vault/linking.d.ts.map +1 -1
- package/dist/vault/linking.js +79 -32
- package/dist/vault/linking.js.map +1 -1
- package/dist/vault/vault-maintenance.d.ts.map +1 -1
- package/dist/vault/vault-maintenance.js +7 -14
- package/dist/vault/vault-maintenance.js.map +1 -1
- package/dist/vault/vault-memories.d.ts.map +1 -1
- package/dist/vault/vault-memories.js +19 -9
- package/dist/vault/vault-memories.js.map +1 -1
- package/dist/vault/vault-schema.d.ts +1 -0
- package/dist/vault/vault-schema.d.ts.map +1 -1
- package/dist/vault/vault-schema.js +20 -0
- package/dist/vault/vault-schema.js.map +1 -1
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +7 -3
- package/dist/vault/vault.js.map +1 -1
- package/package.json +5 -2
- package/src/__tests__/adapters/claude-code-adapter.test.ts +167 -0
- package/src/__tests__/adapters/registry.test.ts +100 -0
- package/src/__tests__/packs/pack-lifecycle.test.ts +379 -0
- package/src/__tests__/subagent/concurrency-manager.test.ts +132 -0
- package/src/__tests__/subagent/dispatcher.test.ts +195 -0
- package/src/__tests__/subagent/orphan-reaper.test.ts +141 -0
- package/src/__tests__/subagent/result-aggregator.test.ts +141 -0
- package/src/__tests__/subagent/task-checkout.test.ts +86 -0
- package/src/__tests__/subagent/workspace-resolver.test.ts +138 -0
- package/src/adapters/claude-code-adapter.ts +163 -0
- package/src/adapters/index.ts +22 -0
- package/src/adapters/registry.ts +53 -0
- package/src/adapters/types.ts +114 -0
- package/src/curator/curator.ts +1 -0
- package/src/index.ts +78 -1
- package/src/packs/index.ts +9 -1
- package/src/packs/lockfile.ts +70 -5
- package/src/packs/pack-installer.ts +78 -2
- package/src/packs/pack-lifecycle.ts +115 -0
- package/src/packs/pack-lockfile.test.ts +1 -1
- package/src/packs/pack-system.test.ts +1 -1
- package/src/packs/types.ts +72 -2
- package/src/persistence/sqlite-provider.ts +26 -2
- package/src/planning/github-projection.ts +6 -0
- package/src/planning/goal-ancestry.test.ts +427 -0
- package/src/planning/goal-ancestry.ts +187 -0
- package/src/planning/plan-lifecycle.ts +3 -0
- package/src/planning/planner-types.ts +2 -0
- package/src/runtime/admin-setup-ops.test.ts +9 -4
- package/src/runtime/context-health.ts +42 -2
- package/src/runtime/orchestrate-ops.ts +153 -1
- package/src/runtime/runtime.ts +15 -0
- package/src/runtime/session-briefing.test.ts +94 -2
- package/src/runtime/session-briefing.ts +48 -12
- package/src/runtime/types.ts +6 -0
- package/src/session/compaction-evaluator.ts +87 -0
- package/src/session/compaction-policy.ts +66 -0
- package/src/session/compaction.test.ts +259 -0
- package/src/session/handoff-renderer.ts +56 -0
- package/src/session/index.ts +12 -0
- package/src/session/policy-resolver.ts +34 -0
- package/src/skills/sync-skills.ts +114 -1
- package/src/skills/trust-classifier.test.ts +252 -0
- package/src/skills/trust-classifier.ts +127 -0
- package/src/subagent/concurrency-manager.ts +89 -0
- package/src/subagent/dispatcher.ts +342 -0
- package/src/subagent/index.ts +28 -0
- package/src/subagent/orphan-reaper.ts +82 -0
- package/src/subagent/result-aggregator.ts +66 -0
- package/src/subagent/task-checkout.ts +60 -0
- package/src/subagent/types.ts +138 -0
- package/src/subagent/workspace-resolver.ts +117 -0
- package/src/vault/vault-scaling.test.ts +3 -2
- package/vitest.config.ts +2 -0
- package/src/hooks/index.ts +0 -6
package/src/packs/lockfile.ts
CHANGED
|
@@ -41,15 +41,25 @@ export interface LockEntry {
|
|
|
41
41
|
soleriRange?: string;
|
|
42
42
|
/** Pack tier: default (ships with engine), community (free), premium (unlocked today) */
|
|
43
43
|
tier?: PackTier;
|
|
44
|
+
/** Current pack lifecycle state */
|
|
45
|
+
state?: string;
|
|
46
|
+
/** Most recent lifecycle transition */
|
|
47
|
+
lastTransition?: { from: string; to: string; timestamp: string; reason?: string };
|
|
48
|
+
/** ISO timestamp when the pack was disabled */
|
|
49
|
+
disabledAt?: string;
|
|
50
|
+
/** Error details if the pack is in an error state */
|
|
51
|
+
errorMessage?: string;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
export type PackType = 'hooks' | 'skills' | 'knowledge' | 'domain' | 'bundle';
|
|
47
55
|
export type PackSource = 'built-in' | 'local' | 'npm';
|
|
48
56
|
export type PackTier = 'default' | 'community' | 'premium';
|
|
49
57
|
|
|
58
|
+
export const LOCKFILE_VERSION = 2;
|
|
59
|
+
|
|
50
60
|
export interface LockfileData {
|
|
51
61
|
/** Lockfile format version */
|
|
52
|
-
version:
|
|
62
|
+
version: number;
|
|
53
63
|
/** Map of packId → lock entry */
|
|
54
64
|
packs: Record<string, LockEntry>;
|
|
55
65
|
}
|
|
@@ -141,18 +151,73 @@ export class PackLockfile {
|
|
|
141
151
|
return 'sha256-' + createHash('sha256').update(content).digest('hex');
|
|
142
152
|
}
|
|
143
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Update lifecycle fields for a pack entry and save.
|
|
156
|
+
*/
|
|
157
|
+
updateLifecycle(
|
|
158
|
+
packId: string,
|
|
159
|
+
state: string,
|
|
160
|
+
transition?: { from: string; to: string; reason?: string },
|
|
161
|
+
): void {
|
|
162
|
+
const entry = this.data.packs[packId];
|
|
163
|
+
if (!entry) return;
|
|
164
|
+
|
|
165
|
+
entry.state = state;
|
|
166
|
+
|
|
167
|
+
if (transition) {
|
|
168
|
+
entry.lastTransition = {
|
|
169
|
+
from: transition.from,
|
|
170
|
+
to: transition.to,
|
|
171
|
+
timestamp: new Date().toISOString(),
|
|
172
|
+
reason: transition.reason,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (state === 'disabled') {
|
|
177
|
+
entry.disabledAt = new Date().toISOString();
|
|
178
|
+
} else {
|
|
179
|
+
delete entry.disabledAt;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (state === 'error') {
|
|
183
|
+
// errorMessage is set externally if needed; keep existing value
|
|
184
|
+
} else {
|
|
185
|
+
delete entry.errorMessage;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.dirty = true;
|
|
189
|
+
this.save();
|
|
190
|
+
}
|
|
191
|
+
|
|
144
192
|
private load(): LockfileData {
|
|
145
193
|
if (!existsSync(this.filePath)) {
|
|
146
|
-
return { version:
|
|
194
|
+
return { version: LOCKFILE_VERSION, packs: {} };
|
|
147
195
|
}
|
|
148
196
|
try {
|
|
149
197
|
const raw = JSON.parse(readFileSync(this.filePath, 'utf-8'));
|
|
150
|
-
if (
|
|
198
|
+
if (typeof raw.packs !== 'object') {
|
|
199
|
+
return { version: LOCKFILE_VERSION, packs: {} };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Migrate v1 → v2: add lifecycle fields
|
|
203
|
+
if (raw.version === 1) {
|
|
204
|
+
for (const entry of Object.values(raw.packs) as LockEntry[]) {
|
|
205
|
+
if (!entry.state) {
|
|
206
|
+
entry.state = 'ready';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
raw.version = LOCKFILE_VERSION;
|
|
210
|
+
// Mark dirty so the migrated data gets persisted on next save
|
|
211
|
+
this.dirty = true;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (raw.version === LOCKFILE_VERSION) {
|
|
151
215
|
return raw as LockfileData;
|
|
152
216
|
}
|
|
153
|
-
|
|
217
|
+
|
|
218
|
+
return { version: LOCKFILE_VERSION, packs: {} };
|
|
154
219
|
} catch {
|
|
155
|
-
return { version:
|
|
220
|
+
return { version: LOCKFILE_VERSION, packs: {} };
|
|
156
221
|
}
|
|
157
222
|
}
|
|
158
223
|
}
|
|
@@ -22,6 +22,7 @@ import type { Vault } from '../vault/vault.js';
|
|
|
22
22
|
import type { PluginRegistry } from '../plugins/plugin-registry.js';
|
|
23
23
|
import type { PluginContext } from '../plugins/types.js';
|
|
24
24
|
import type { PackRuntime } from '../domain-packs/pack-runtime.js';
|
|
25
|
+
import { PackLifecycleManager } from './pack-lifecycle.js';
|
|
25
26
|
|
|
26
27
|
const MANIFEST_FILENAME = 'soleri-pack.json';
|
|
27
28
|
|
|
@@ -31,6 +32,7 @@ const MANIFEST_FILENAME = 'soleri-pack.json';
|
|
|
31
32
|
|
|
32
33
|
export class PackInstaller {
|
|
33
34
|
private packs = new Map<string, InstalledPack>();
|
|
35
|
+
readonly lifecycle = new PackLifecycleManager();
|
|
34
36
|
|
|
35
37
|
constructor(
|
|
36
38
|
private vault: Vault,
|
|
@@ -206,17 +208,21 @@ export class PackInstaller {
|
|
|
206
208
|
const hooksDir = join(packDir, manifest.hooks?.dir ?? 'hooks');
|
|
207
209
|
const hooks = existsSync(hooksDir) ? listMarkdownFiles(hooksDir) : [];
|
|
208
210
|
|
|
209
|
-
// Track installed pack
|
|
211
|
+
// Track installed pack with lifecycle
|
|
212
|
+
this.lifecycle.initState(manifest.id, 'installed');
|
|
213
|
+
this.lifecycle.transition(manifest.id, 'ready', 'Initial install');
|
|
214
|
+
|
|
210
215
|
const installed: InstalledPack = {
|
|
211
216
|
id: manifest.id,
|
|
212
217
|
manifest,
|
|
213
218
|
directory: packDir,
|
|
214
|
-
status: '
|
|
219
|
+
status: 'ready',
|
|
215
220
|
vaultEntries,
|
|
216
221
|
skills,
|
|
217
222
|
hooks,
|
|
218
223
|
facadesRegistered,
|
|
219
224
|
installedAt: Date.now(),
|
|
225
|
+
transitions: this.lifecycle.getTransitions(manifest.id),
|
|
220
226
|
};
|
|
221
227
|
this.packs.set(manifest.id, installed);
|
|
222
228
|
|
|
@@ -231,17 +237,23 @@ export class PackInstaller {
|
|
|
231
237
|
} catch (e) {
|
|
232
238
|
const error = e instanceof Error ? e.message : String(e);
|
|
233
239
|
|
|
240
|
+
// Transition to error state
|
|
241
|
+
this.lifecycle.initState(manifest.id, 'installed');
|
|
242
|
+
this.lifecycle.transition(manifest.id, 'error', error);
|
|
243
|
+
|
|
234
244
|
this.packs.set(manifest.id, {
|
|
235
245
|
id: manifest.id,
|
|
236
246
|
manifest,
|
|
237
247
|
directory: packDir,
|
|
238
248
|
status: 'error',
|
|
239
249
|
error,
|
|
250
|
+
errorMessage: error,
|
|
240
251
|
vaultEntries: 0,
|
|
241
252
|
skills: [],
|
|
242
253
|
hooks: [],
|
|
243
254
|
facadesRegistered: false,
|
|
244
255
|
installedAt: Date.now(),
|
|
256
|
+
transitions: this.lifecycle.getTransitions(manifest.id),
|
|
245
257
|
});
|
|
246
258
|
|
|
247
259
|
return {
|
|
@@ -269,11 +281,75 @@ export class PackInstaller {
|
|
|
269
281
|
this.pluginRegistry.deactivate(packId);
|
|
270
282
|
}
|
|
271
283
|
|
|
284
|
+
// Transition to uninstalled
|
|
285
|
+
try {
|
|
286
|
+
this.lifecycle.transition(packId, 'uninstalled', 'User uninstall');
|
|
287
|
+
} catch {
|
|
288
|
+
// May not be tracked in lifecycle — continue anyway
|
|
289
|
+
}
|
|
290
|
+
this.lifecycle.remove(packId);
|
|
291
|
+
|
|
272
292
|
pack.status = 'uninstalled';
|
|
273
293
|
this.packs.delete(packId);
|
|
274
294
|
return true;
|
|
275
295
|
}
|
|
276
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Disable a pack — deactivates capabilities but preserves vault entries.
|
|
299
|
+
*/
|
|
300
|
+
disable(packId: string): boolean {
|
|
301
|
+
const pack = this.packs.get(packId);
|
|
302
|
+
if (!pack) return false;
|
|
303
|
+
|
|
304
|
+
this.lifecycle.transition(packId, 'disabled', 'User disabled');
|
|
305
|
+
|
|
306
|
+
// Deactivate facades
|
|
307
|
+
if (pack.facadesRegistered) {
|
|
308
|
+
this.pluginRegistry.deactivate(packId);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
pack.status = 'disabled';
|
|
312
|
+
pack.disabledAt = Date.now();
|
|
313
|
+
pack.transitions = this.lifecycle.getTransitions(packId);
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Enable a previously disabled pack — reactivates capabilities.
|
|
319
|
+
*/
|
|
320
|
+
async enable(packId: string, runtimeCtx?: unknown, packRuntime?: PackRuntime): Promise<boolean> {
|
|
321
|
+
const pack = this.packs.get(packId);
|
|
322
|
+
if (!pack) return false;
|
|
323
|
+
|
|
324
|
+
this.lifecycle.transition(packId, 'ready', 'User enabled');
|
|
325
|
+
|
|
326
|
+
// Reactivate facades
|
|
327
|
+
if (pack.manifest.facades.length > 0 && this.pluginRegistry.get(packId)) {
|
|
328
|
+
const ctx: PluginContext = {
|
|
329
|
+
packRuntime:
|
|
330
|
+
packRuntime ??
|
|
331
|
+
({
|
|
332
|
+
vault: {},
|
|
333
|
+
getProject: () => undefined,
|
|
334
|
+
listProjects: () => [],
|
|
335
|
+
createCheck: () => '',
|
|
336
|
+
validateCheck: () => null,
|
|
337
|
+
validateAndConsume: () => null,
|
|
338
|
+
} as unknown as PackRuntime),
|
|
339
|
+
runtime: runtimeCtx ?? {},
|
|
340
|
+
manifest: this.pluginRegistry.get(packId)!.manifest,
|
|
341
|
+
directory: pack.directory,
|
|
342
|
+
};
|
|
343
|
+
await this.pluginRegistry.activate(packId, ctx);
|
|
344
|
+
pack.facadesRegistered = true;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
pack.status = 'ready';
|
|
348
|
+
pack.disabledAt = undefined;
|
|
349
|
+
pack.transitions = this.lifecycle.getTransitions(packId);
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
277
353
|
/**
|
|
278
354
|
* Get an installed pack by ID.
|
|
279
355
|
*/
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pack Lifecycle Manager — Central state machine for pack lifecycle management.
|
|
3
|
+
*
|
|
4
|
+
* Manages in-memory pack states, validates transitions against VALID_TRANSITIONS,
|
|
5
|
+
* records transition history, and notifies listeners on state changes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { PackState, PackTransition } from './types.js';
|
|
9
|
+
import { VALID_TRANSITIONS } from './types.js';
|
|
10
|
+
|
|
11
|
+
type TransitionListener = (packId: string, from: PackState, to: PackState, reason?: string) => void;
|
|
12
|
+
|
|
13
|
+
interface PackEntry {
|
|
14
|
+
state: PackState;
|
|
15
|
+
transitions: PackTransition[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class PackLifecycleManager {
|
|
19
|
+
private packs: Map<string, PackEntry> = new Map();
|
|
20
|
+
private listeners: Array<TransitionListener> = [];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Transition a pack to a new state. Validates against VALID_TRANSITIONS.
|
|
24
|
+
* Throws a descriptive error if the transition is not allowed.
|
|
25
|
+
*/
|
|
26
|
+
transition(packId: string, to: PackState, reason?: string): void {
|
|
27
|
+
const entry = this.packs.get(packId);
|
|
28
|
+
if (!entry) {
|
|
29
|
+
throw new Error(`Pack '${packId}' is not being tracked`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const currentState = entry.state;
|
|
33
|
+
const validTargets = VALID_TRANSITIONS[currentState];
|
|
34
|
+
|
|
35
|
+
if (!validTargets.includes(to)) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Invalid pack lifecycle transition for '${packId}': '${currentState}' \u2192 '${to}'. Valid targets from '${currentState}': ${validTargets.join(', ')}`,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const transition: PackTransition = {
|
|
42
|
+
from: currentState,
|
|
43
|
+
to,
|
|
44
|
+
timestamp: Date.now(),
|
|
45
|
+
reason,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
entry.state = to;
|
|
49
|
+
entry.transitions.push(transition);
|
|
50
|
+
|
|
51
|
+
for (const listener of this.listeners) {
|
|
52
|
+
listener(packId, currentState, to, reason);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Set initial state without transition validation (for loading from lockfile).
|
|
58
|
+
*/
|
|
59
|
+
initState(packId: string, state: PackState): void {
|
|
60
|
+
this.packs.set(packId, { state, transitions: [] });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Returns current state of a pack, or undefined if not tracked.
|
|
65
|
+
*/
|
|
66
|
+
getState(packId: string): PackState | undefined {
|
|
67
|
+
return this.packs.get(packId)?.state;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Returns the full transition history for a pack.
|
|
72
|
+
*/
|
|
73
|
+
getTransitions(packId: string): PackTransition[] {
|
|
74
|
+
return this.packs.get(packId)?.transitions ?? [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Register a listener for state transitions. Returns an unsubscribe function.
|
|
79
|
+
*/
|
|
80
|
+
onTransition(callback: TransitionListener): () => void {
|
|
81
|
+
this.listeners.push(callback);
|
|
82
|
+
return () => {
|
|
83
|
+
const idx = this.listeners.indexOf(callback);
|
|
84
|
+
if (idx !== -1) {
|
|
85
|
+
this.listeners.splice(idx, 1);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Remove a pack from tracking entirely.
|
|
92
|
+
*/
|
|
93
|
+
remove(packId: string): void {
|
|
94
|
+
this.packs.delete(packId);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* List all tracked packs with their current state.
|
|
99
|
+
*/
|
|
100
|
+
listAll(): Array<{ packId: string; state: PackState }> {
|
|
101
|
+
const result: Array<{ packId: string; state: PackState }> = [];
|
|
102
|
+
for (const [packId, entry] of this.packs) {
|
|
103
|
+
result.push({ packId, state: entry.state });
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Clear all tracked state and listeners.
|
|
110
|
+
*/
|
|
111
|
+
reset(): void {
|
|
112
|
+
this.packs.clear();
|
|
113
|
+
this.listeners.length = 0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -81,7 +81,7 @@ describe('PackLockfile', () => {
|
|
|
81
81
|
expect(existsSync(lockPath)).toBe(true);
|
|
82
82
|
|
|
83
83
|
const data = JSON.parse(readFileSync(lockPath, 'utf-8'));
|
|
84
|
-
expect(data.version).toBe(
|
|
84
|
+
expect(data.version).toBe(2);
|
|
85
85
|
expect(data.packs['test-pack'].version).toBe('1.0.0');
|
|
86
86
|
});
|
|
87
87
|
|
|
@@ -417,7 +417,7 @@ describe('PackInstaller', () => {
|
|
|
417
417
|
const pack = installer.get('specific');
|
|
418
418
|
expect(pack).toBeDefined();
|
|
419
419
|
expect(pack?.manifest.version).toBe('3.0.0');
|
|
420
|
-
expect(pack?.status).toBe('
|
|
420
|
+
expect(pack?.status).toBe('ready');
|
|
421
421
|
});
|
|
422
422
|
});
|
|
423
423
|
});
|
package/src/packs/types.ts
CHANGED
|
@@ -19,6 +19,38 @@
|
|
|
19
19
|
|
|
20
20
|
import { z } from 'zod';
|
|
21
21
|
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// SKILL TRUST & SOURCE TRACKING
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
/** How much a skill can do — escalates from pure docs to executable code */
|
|
27
|
+
export type TrustLevel = 'markdown_only' | 'assets' | 'scripts';
|
|
28
|
+
|
|
29
|
+
/** Where a skill came from */
|
|
30
|
+
export type SourceType = 'builtin' | 'pack' | 'local' | 'github' | 'npm';
|
|
31
|
+
|
|
32
|
+
/** A single file in a skill directory, classified by kind */
|
|
33
|
+
export interface SkillInventoryItem {
|
|
34
|
+
/** Relative path within the skill directory */
|
|
35
|
+
path: string;
|
|
36
|
+
/** What kind of file this is */
|
|
37
|
+
kind: 'skill' | 'reference' | 'asset' | 'script';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Extended metadata for a discovered skill */
|
|
41
|
+
export interface SkillMetadata {
|
|
42
|
+
/** Computed trust level based on directory contents */
|
|
43
|
+
trust: TrustLevel;
|
|
44
|
+
/** Where this skill was sourced from */
|
|
45
|
+
source: { type: SourceType; uri: string };
|
|
46
|
+
/** Engine version compatibility check result */
|
|
47
|
+
compatibility: 'compatible' | 'unknown' | 'invalid';
|
|
48
|
+
/** Engine version declared by the skill (if any) */
|
|
49
|
+
engineVersion?: string;
|
|
50
|
+
/** Classified inventory of all files in the skill directory */
|
|
51
|
+
inventory: SkillInventoryItem[];
|
|
52
|
+
}
|
|
53
|
+
|
|
22
54
|
// =============================================================================
|
|
23
55
|
// MANIFEST SCHEMA
|
|
24
56
|
// =============================================================================
|
|
@@ -108,13 +140,43 @@ export type PackManifest = z.infer<typeof packManifestSchema>;
|
|
|
108
140
|
// INSTALL TYPES
|
|
109
141
|
// =============================================================================
|
|
110
142
|
|
|
111
|
-
|
|
143
|
+
// ─── Lifecycle States ─────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
/** Full lifecycle state for a pack */
|
|
146
|
+
export type PackState =
|
|
147
|
+
| 'installed' // Just installed, not yet activated
|
|
148
|
+
| 'ready' // Active — capabilities, skills, hooks all live
|
|
149
|
+
| 'disabled' // Temporarily deactivated — vault entries kept, capabilities off
|
|
150
|
+
| 'error' // Failed to activate — error message in errorMessage field
|
|
151
|
+
| 'upgrade_pending' // New version available, old version still active
|
|
152
|
+
| 'uninstalled'; // Removed — vault entries remain (permanent knowledge)
|
|
153
|
+
|
|
154
|
+
/** @deprecated Use PackState instead */
|
|
155
|
+
export type PackStatus = PackState;
|
|
156
|
+
|
|
157
|
+
/** Valid state transitions — key is "from" state, values are allowed "to" states */
|
|
158
|
+
export const VALID_TRANSITIONS: Record<PackState, PackState[]> = {
|
|
159
|
+
installed: ['ready', 'error', 'uninstalled'],
|
|
160
|
+
ready: ['ready', 'disabled', 'error', 'upgrade_pending', 'uninstalled'],
|
|
161
|
+
disabled: ['ready', 'uninstalled'],
|
|
162
|
+
error: ['ready', 'uninstalled'],
|
|
163
|
+
upgrade_pending: ['ready', 'error', 'uninstalled'],
|
|
164
|
+
uninstalled: ['installed'],
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/** A recorded state transition */
|
|
168
|
+
export interface PackTransition {
|
|
169
|
+
from: PackState;
|
|
170
|
+
to: PackState;
|
|
171
|
+
timestamp: number;
|
|
172
|
+
reason?: string;
|
|
173
|
+
}
|
|
112
174
|
|
|
113
175
|
export interface InstalledPack {
|
|
114
176
|
id: string;
|
|
115
177
|
manifest: PackManifest;
|
|
116
178
|
directory: string;
|
|
117
|
-
status:
|
|
179
|
+
status: PackState;
|
|
118
180
|
error?: string;
|
|
119
181
|
/** Number of vault entries seeded */
|
|
120
182
|
vaultEntries: number;
|
|
@@ -125,6 +187,14 @@ export interface InstalledPack {
|
|
|
125
187
|
/** Whether facades were registered via plugin system */
|
|
126
188
|
facadesRegistered: boolean;
|
|
127
189
|
installedAt: number;
|
|
190
|
+
/** Lifecycle transition history (most recent last) */
|
|
191
|
+
transitions?: PackTransition[];
|
|
192
|
+
/** When the pack was disabled (if in disabled state) */
|
|
193
|
+
disabledAt?: number;
|
|
194
|
+
/** Error message (if in error state) */
|
|
195
|
+
errorMessage?: string;
|
|
196
|
+
/** Version available for upgrade (if in upgrade_pending state) */
|
|
197
|
+
upgradeVersion?: string;
|
|
128
198
|
}
|
|
129
199
|
|
|
130
200
|
export interface InstallResult {
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Supports both positional (array) and named (object) parameters.
|
|
5
5
|
* Exposes getDatabase() for backward-compat consumers that need the raw db.
|
|
6
|
+
*
|
|
7
|
+
* better-sqlite3 is loaded lazily at construction time (not at module import)
|
|
8
|
+
* so that code paths that never instantiate a provider don't require the
|
|
9
|
+
* native module to be installed.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
|
-
import Database from 'better-sqlite3';
|
|
12
|
+
import type Database from 'better-sqlite3';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
9
14
|
import { mkdirSync } from 'node:fs';
|
|
10
15
|
import { dirname } from 'node:path';
|
|
11
16
|
import type {
|
|
@@ -15,6 +20,24 @@ import type {
|
|
|
15
20
|
FtsSearchOptions,
|
|
16
21
|
} from './types.js';
|
|
17
22
|
|
|
23
|
+
type DatabaseConstructor = typeof Database;
|
|
24
|
+
let _DatabaseCtor: DatabaseConstructor | undefined;
|
|
25
|
+
|
|
26
|
+
function loadDriver(): DatabaseConstructor {
|
|
27
|
+
if (!_DatabaseCtor) {
|
|
28
|
+
const req = createRequire(import.meta.url);
|
|
29
|
+
try {
|
|
30
|
+
_DatabaseCtor = req('better-sqlite3') as DatabaseConstructor;
|
|
31
|
+
} catch {
|
|
32
|
+
throw new Error(
|
|
33
|
+
'better-sqlite3 is required for persistence but is not installed.\n' +
|
|
34
|
+
'Run: npm install better-sqlite3',
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return _DatabaseCtor;
|
|
39
|
+
}
|
|
40
|
+
|
|
18
41
|
/** Apply performance-tuning PRAGMAs for file-backed SQLite databases. */
|
|
19
42
|
export function applyPerformancePragmas(db: Database.Database): void {
|
|
20
43
|
db.pragma('cache_size = -64000'); // 64MB
|
|
@@ -28,8 +51,9 @@ export class SQLitePersistenceProvider implements PersistenceProvider {
|
|
|
28
51
|
private db: Database.Database;
|
|
29
52
|
|
|
30
53
|
constructor(path: string = ':memory:') {
|
|
54
|
+
const Driver = loadDriver();
|
|
31
55
|
if (path !== ':memory:') mkdirSync(dirname(path), { recursive: true });
|
|
32
|
-
this.db = new
|
|
56
|
+
this.db = new Driver(path);
|
|
33
57
|
if (path !== ':memory:') applyPerformancePragmas(this.db);
|
|
34
58
|
}
|
|
35
59
|
|
|
@@ -333,6 +333,7 @@ export function formatIssueBody(
|
|
|
333
333
|
plan: PlanMetadataForIssue,
|
|
334
334
|
taskTitle: string,
|
|
335
335
|
taskDescription: string,
|
|
336
|
+
options?: { goalContext?: string },
|
|
336
337
|
): string {
|
|
337
338
|
const lines: string[] = [];
|
|
338
339
|
|
|
@@ -341,6 +342,11 @@ export function formatIssueBody(
|
|
|
341
342
|
lines.push(`**Objective:** ${plan.objective}`);
|
|
342
343
|
lines.push('');
|
|
343
344
|
|
|
345
|
+
if (options?.goalContext) {
|
|
346
|
+
lines.push(options.goalContext);
|
|
347
|
+
lines.push('');
|
|
348
|
+
}
|
|
349
|
+
|
|
344
350
|
if (plan.decisions.length > 0) {
|
|
345
351
|
lines.push('## Decisions');
|
|
346
352
|
for (const d of plan.decisions) {
|