hippo-memory 1.61.0 → 1.62.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/README.md +28 -53
- package/dist/agent-memories/apply.d.ts +1 -1
- package/dist/agent-memories/claude-code.d.ts +3 -1
- package/dist/agent-memories/claude-code.js +53 -9
- package/dist/agent-memories/sync.d.ts +3 -3
- package/dist/agent-memories/sync.js +14 -6
- package/dist/agent-memories/types.d.ts +0 -2
- package/dist/api/assemble.js +55 -54
- package/dist/api/context-select.d.ts +49 -0
- package/dist/api/context-select.js +344 -0
- package/dist/api/context.d.ts +2 -2
- package/dist/api/context.js +195 -522
- package/dist/api/drill-down.js +36 -33
- package/dist/api/promote.js +55 -66
- package/dist/api/recall.js +303 -438
- package/dist/api/sleep.js +203 -218
- package/dist/capture/compact.d.ts +1 -1
- package/dist/capture/compact.js +2 -2
- package/dist/cli/briefs.js +324 -306
- package/dist/cli/context.js +44 -34
- package/dist/cli/continuity.js +283 -271
- package/dist/cli/curate.js +35 -34
- package/dist/cli/decisions.js +333 -333
- package/dist/cli/explain.js +66 -60
- package/dist/cli/maintenance.js +62 -51
- package/dist/cli/playbooks.js +387 -370
- package/dist/cli/projects.js +8 -5
- package/dist/cli/recall.js +28 -43
- package/dist/cli/remember.js +113 -70
- package/dist/cli/session-hooks.js +100 -90
- package/dist/cli/setup.js +267 -246
- package/dist/cli/status.js +73 -64
- package/dist/cli/transfer.js +85 -99
- package/dist/compaction-record.d.ts +0 -2
- package/dist/compaction-record.js +1 -1
- package/dist/customer-notes.js +77 -68
- package/dist/dag.js +222 -186
- package/dist/decisions.js +93 -76
- package/dist/doctor.js +11 -7
- package/dist/goals.js +99 -86
- package/dist/incidents.js +45 -38
- package/dist/policies.js +85 -68
- package/dist/processes.js +87 -71
- package/dist/project-briefs.js +135 -108
- package/dist/project-merge.d.ts +12 -4
- package/dist/project-merge.js +130 -45
- package/dist/shared.d.ts +9 -0
- package/dist/shared.js +10 -8
- package/dist/skills.js +81 -65
- package/dist/store/search-rows.d.ts +2 -2
- package/dist/store/search-rows.js +15 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli/playbooks.js
CHANGED
|
@@ -33,135 +33,144 @@ function collectProcessSteps(stepRaw) {
|
|
|
33
33
|
}
|
|
34
34
|
return [];
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
|
|
43
|
-
const limitRaw = flags['limit'];
|
|
44
|
-
const limit = limitRaw !== undefined ? parseInt(String(limitRaw), 10) : 100;
|
|
45
|
-
if (!Number.isFinite(limit) || limit <= 0) {
|
|
46
|
-
printError(`Invalid --limit: "${limitRaw}". Must be a positive integer.`);
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
let results;
|
|
50
|
-
if (status === 'all') {
|
|
51
|
-
results = processesModule.loadProcesses(hippoRoot, tenantId, { limit });
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
if (!processesModule.VALID_PROCESS_STATES.has(status)) {
|
|
55
|
-
printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
results = processesModule.loadProcesses(hippoRoot, tenantId, {
|
|
59
|
-
status: status,
|
|
60
|
-
limit,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
if (results.length === 0) {
|
|
64
|
-
console.log('No processes.');
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
console.log(`Found ${results.length} processes:\n`);
|
|
68
|
-
for (const proc of results) {
|
|
69
|
-
console.log(`#${proc.id} [${proc.status}] v${proc.version} steps=${proc.steps.length} memory=${proc.memoryId ?? '-'}`);
|
|
70
|
-
console.log(` ${proc.processName}`);
|
|
71
|
-
if (proc.changeSummary)
|
|
72
|
-
console.log(` change: ${proc.changeSummary}`);
|
|
73
|
-
}
|
|
74
|
-
return;
|
|
36
|
+
function parseListLimit(flags) {
|
|
37
|
+
const limitRaw = flags['limit'];
|
|
38
|
+
const limit = limitRaw !== undefined ? parseInt(String(limitRaw), 10) : 100;
|
|
39
|
+
if (!Number.isFinite(limit) || limit <= 0) {
|
|
40
|
+
printError(`Invalid --limit: "${limitRaw}". Must be a positive integer.`);
|
|
41
|
+
process.exit(1);
|
|
75
42
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
printError(`Process ${id} not found.`);
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
console.log(`Process #${proc.id}`);
|
|
89
|
-
console.log(` name: ${proc.processName}`);
|
|
90
|
-
console.log(` status: ${proc.status}`);
|
|
91
|
-
console.log(` version: ${proc.version}`);
|
|
92
|
-
if (proc.description)
|
|
93
|
-
console.log(` description: ${proc.description}`);
|
|
94
|
-
if (proc.steps.length > 0) {
|
|
95
|
-
console.log(` steps:`);
|
|
96
|
-
proc.steps.forEach((s, i) => console.log(` ${i + 1}. ${s}`));
|
|
97
|
-
}
|
|
98
|
-
if (proc.changeSummary)
|
|
99
|
-
console.log(` change_summary: ${proc.changeSummary}`);
|
|
100
|
-
if (proc.supersededBy !== null)
|
|
101
|
-
console.log(` superseded_by: #${proc.supersededBy}`);
|
|
102
|
-
if (proc.supersededAt)
|
|
103
|
-
console.log(` superseded_at: ${proc.supersededAt}`);
|
|
104
|
-
if (proc.closedAt)
|
|
105
|
-
console.log(` closed_at: ${proc.closedAt}`);
|
|
106
|
-
if (proc.memoryId)
|
|
107
|
-
console.log(` memory: ${proc.memoryId}`);
|
|
108
|
-
console.log(` created: ${proc.createdAt}`);
|
|
109
|
-
return;
|
|
43
|
+
return limit;
|
|
44
|
+
}
|
|
45
|
+
function processList(hippoRoot, tenantId, flags) {
|
|
46
|
+
const statusRaw = flags['status'];
|
|
47
|
+
const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
|
|
48
|
+
const limit = parseListLimit(flags);
|
|
49
|
+
let results;
|
|
50
|
+
if (status === 'all') {
|
|
51
|
+
results = processesModule.loadProcesses(hippoRoot, tenantId, { limit });
|
|
110
52
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
printError('Usage: hippo process supersede <id> --step "<text>" [--step ...] [--change "<summary>"] [--description "<text>"]');
|
|
53
|
+
else {
|
|
54
|
+
if (!processesModule.VALID_PROCESS_STATES.has(status)) {
|
|
55
|
+
printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
|
|
115
56
|
process.exit(1);
|
|
116
57
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
printError('hippo process supersede requires at least one --step "<text>" for the new version.');
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
// A supersession is a new version of the SAME process, so the new row reuses
|
|
124
|
-
// the predecessor's name (stable identity across versions). loadProcessById
|
|
125
|
-
// gives an early not-found before the write; saveProcess's in-SAVEPOINT
|
|
126
|
-
// preflight is the authoritative active-state check.
|
|
127
|
-
const existing = processesModule.loadProcessById(hippoRoot, tenantId, id);
|
|
128
|
-
if (!existing) {
|
|
129
|
-
printError(`Process ${id} not found.`);
|
|
130
|
-
process.exit(1);
|
|
131
|
-
}
|
|
132
|
-
const changeRaw = flags['change'];
|
|
133
|
-
const changeSummary = typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined;
|
|
134
|
-
const descRaw = flags['description'];
|
|
135
|
-
const description = typeof descRaw === 'string' && descRaw ? descRaw : undefined;
|
|
136
|
-
const procPathTags = extractPathTags(process.cwd());
|
|
137
|
-
const created = processesModule.saveProcess(hippoRoot, tenantId, {
|
|
138
|
-
processName: existing.processName,
|
|
139
|
-
steps,
|
|
140
|
-
description,
|
|
141
|
-
changeSummary,
|
|
142
|
-
supersedesProcessId: id,
|
|
143
|
-
extraTags: procPathTags,
|
|
58
|
+
results = processesModule.loadProcesses(hippoRoot, tenantId, {
|
|
59
|
+
status: status,
|
|
60
|
+
limit,
|
|
144
61
|
});
|
|
145
|
-
console.log(`Process #${created.id} recorded (v${created.version}), superseding #${id}.`);
|
|
146
|
-
if (created.memoryId)
|
|
147
|
-
console.log(` memory: ${created.memoryId}`);
|
|
148
|
-
return;
|
|
149
62
|
}
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
if (!idRaw) {
|
|
153
|
-
printError('Usage: hippo process close <id>');
|
|
154
|
-
process.exit(1);
|
|
155
|
-
}
|
|
156
|
-
const id = parsePositiveProcessId(idRaw);
|
|
157
|
-
const closed = processesModule.closeProcess(hippoRoot, tenantId, id);
|
|
158
|
-
console.log(`Process #${closed.id} closed.`);
|
|
63
|
+
if (results.length === 0) {
|
|
64
|
+
console.log('No processes.');
|
|
159
65
|
return;
|
|
160
66
|
}
|
|
67
|
+
console.log(`Found ${results.length} processes:\n`);
|
|
68
|
+
for (const proc of results) {
|
|
69
|
+
console.log(`#${proc.id} [${proc.status}] v${proc.version} steps=${proc.steps.length} memory=${proc.memoryId ?? '-'}`);
|
|
70
|
+
console.log(` ${proc.processName}`);
|
|
71
|
+
if (proc.changeSummary)
|
|
72
|
+
console.log(` change: ${proc.changeSummary}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function processGet(hippoRoot, tenantId, args) {
|
|
76
|
+
const idRaw = args[1];
|
|
77
|
+
if (!idRaw) {
|
|
78
|
+
printError('Usage: hippo process get <id>');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const id = parsePositiveProcessId(idRaw);
|
|
82
|
+
const proc = processesModule.loadProcessById(hippoRoot, tenantId, id);
|
|
83
|
+
if (!proc) {
|
|
84
|
+
printError(`Process ${id} not found.`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
console.log(`Process #${proc.id}`);
|
|
88
|
+
console.log(` name: ${proc.processName}`);
|
|
89
|
+
console.log(` status: ${proc.status}`);
|
|
90
|
+
console.log(` version: ${proc.version}`);
|
|
91
|
+
if (proc.description)
|
|
92
|
+
console.log(` description: ${proc.description}`);
|
|
93
|
+
if (proc.steps.length > 0) {
|
|
94
|
+
console.log(` steps:`);
|
|
95
|
+
proc.steps.forEach((s, i) => console.log(` ${i + 1}. ${s}`));
|
|
96
|
+
}
|
|
97
|
+
if (proc.changeSummary)
|
|
98
|
+
console.log(` change_summary: ${proc.changeSummary}`);
|
|
99
|
+
if (proc.supersededBy !== null)
|
|
100
|
+
console.log(` superseded_by: #${proc.supersededBy}`);
|
|
101
|
+
if (proc.supersededAt)
|
|
102
|
+
console.log(` superseded_at: ${proc.supersededAt}`);
|
|
103
|
+
if (proc.closedAt)
|
|
104
|
+
console.log(` closed_at: ${proc.closedAt}`);
|
|
105
|
+
if (proc.memoryId)
|
|
106
|
+
console.log(` memory: ${proc.memoryId}`);
|
|
107
|
+
console.log(` created: ${proc.createdAt}`);
|
|
108
|
+
}
|
|
109
|
+
function processSupersede(hippoRoot, tenantId, args, flags) {
|
|
110
|
+
const idRaw = args[1];
|
|
111
|
+
if (!idRaw) {
|
|
112
|
+
printError('Usage: hippo process supersede <id> --step "<text>" [--step ...] [--change "<summary>"] [--description "<text>"]');
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
const id = parsePositiveProcessId(idRaw);
|
|
116
|
+
const steps = collectProcessSteps(flags['step']);
|
|
117
|
+
if (steps.length === 0) {
|
|
118
|
+
printError('hippo process supersede requires at least one --step "<text>" for the new version.');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
// A supersession is a new version of the SAME process, so the new row reuses the predecessor's name (stable identity
|
|
122
|
+
// across versions). loadProcessById gives an early not-found before the write; saveProcess's in-SAVEPOINT preflight
|
|
123
|
+
// is the authoritative active-state check.
|
|
124
|
+
const existing = processesModule.loadProcessById(hippoRoot, tenantId, id);
|
|
125
|
+
if (!existing) {
|
|
126
|
+
printError(`Process ${id} not found.`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
const changeRaw = flags['change'];
|
|
130
|
+
const changeSummary = typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined;
|
|
131
|
+
const descRaw = flags['description'];
|
|
132
|
+
const description = typeof descRaw === 'string' && descRaw ? descRaw : undefined;
|
|
133
|
+
const procPathTags = extractPathTags(process.cwd());
|
|
134
|
+
const created = processesModule.saveProcess(hippoRoot, tenantId, {
|
|
135
|
+
processName: existing.processName,
|
|
136
|
+
steps,
|
|
137
|
+
description,
|
|
138
|
+
changeSummary,
|
|
139
|
+
supersedesProcessId: id,
|
|
140
|
+
extraTags: procPathTags,
|
|
141
|
+
});
|
|
142
|
+
console.log(`Process #${created.id} recorded (v${created.version}), superseding #${id}.`);
|
|
143
|
+
if (created.memoryId)
|
|
144
|
+
console.log(` memory: ${created.memoryId}`);
|
|
145
|
+
}
|
|
146
|
+
function processClose(hippoRoot, tenantId, args) {
|
|
147
|
+
const idRaw = args[1];
|
|
148
|
+
if (!idRaw) {
|
|
149
|
+
printError('Usage: hippo process close <id>');
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
const id = parsePositiveProcessId(idRaw);
|
|
153
|
+
const closed = processesModule.closeProcess(hippoRoot, tenantId, id);
|
|
154
|
+
console.log(`Process #${closed.id} closed.`);
|
|
155
|
+
}
|
|
156
|
+
export function cmdProcess(hippoRoot, args, flags) {
|
|
157
|
+
requireInit(hippoRoot);
|
|
158
|
+
const tenantId = resolveTenantId({});
|
|
159
|
+
const subcommand = args[0] ?? '';
|
|
160
|
+
if (subcommand === 'list')
|
|
161
|
+
return processList(hippoRoot, tenantId, flags);
|
|
162
|
+
if (subcommand === 'get')
|
|
163
|
+
return processGet(hippoRoot, tenantId, args);
|
|
164
|
+
if (subcommand === 'supersede')
|
|
165
|
+
return processSupersede(hippoRoot, tenantId, args, flags);
|
|
166
|
+
if (subcommand === 'close')
|
|
167
|
+
return processClose(hippoRoot, tenantId, args);
|
|
161
168
|
// Default subcommand: new (create). Accept both the documented
|
|
162
169
|
// `process new "<name>"` form and the bare `process "<name>"` form: for the
|
|
163
170
|
// `new` keyword the name is args[1], otherwise args[0] IS the name.
|
|
164
|
-
|
|
171
|
+
processCreate(hippoRoot, tenantId, subcommand === 'new' ? (args[1] ?? '') : subcommand, flags);
|
|
172
|
+
}
|
|
173
|
+
function processCreate(hippoRoot, tenantId, processName, flags) {
|
|
165
174
|
if (!processName) {
|
|
166
175
|
printError('Usage: hippo process new "<name>" --step "<text>" [--step ...] [--description "<text>"]');
|
|
167
176
|
printError(' hippo process list [--status active|superseded|closed|all] [--limit N]');
|
|
@@ -202,150 +211,154 @@ function printPolicyRow(p) {
|
|
|
202
211
|
if (p.changeSummary)
|
|
203
212
|
console.log(` change: ${p.changeSummary}`);
|
|
204
213
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const limitRaw = flags['limit'];
|
|
213
|
-
const limit = limitRaw !== undefined ? parseInt(String(limitRaw), 10) : 100;
|
|
214
|
-
if (!Number.isFinite(limit) || limit <= 0) {
|
|
215
|
-
printError(`Invalid --limit: "${limitRaw}". Must be a positive integer.`);
|
|
216
|
-
process.exit(1);
|
|
217
|
-
}
|
|
218
|
-
let results;
|
|
219
|
-
if (status === 'all') {
|
|
220
|
-
results = policiesModule.loadPolicies(hippoRoot, tenantId, { limit });
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
if (!policiesModule.VALID_POLICY_STATES.has(status)) {
|
|
224
|
-
printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
|
|
225
|
-
process.exit(1);
|
|
226
|
-
}
|
|
227
|
-
results = policiesModule.loadPolicies(hippoRoot, tenantId, {
|
|
228
|
-
status: status,
|
|
229
|
-
limit,
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
if (results.length === 0) {
|
|
233
|
-
console.log('No policies.');
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
console.log(`Found ${results.length} policies:\n`);
|
|
237
|
-
for (const p of results)
|
|
238
|
-
printPolicyRow(p);
|
|
239
|
-
return;
|
|
214
|
+
function policyList(hippoRoot, tenantId, flags) {
|
|
215
|
+
const statusRaw = flags['status'];
|
|
216
|
+
const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
|
|
217
|
+
const limit = parseListLimit(flags);
|
|
218
|
+
let results;
|
|
219
|
+
if (status === 'all') {
|
|
220
|
+
results = policiesModule.loadPolicies(hippoRoot, tenantId, { limit });
|
|
240
221
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
printError('Usage: hippo policy asof <iso-date> [--name "<policy>"]');
|
|
245
|
-
process.exit(1);
|
|
246
|
-
}
|
|
247
|
-
const nameRaw = flags['name'];
|
|
248
|
-
const name = typeof nameRaw === 'string' && nameRaw ? nameRaw : undefined;
|
|
249
|
-
let results;
|
|
250
|
-
try {
|
|
251
|
-
results = policiesModule.loadPoliciesAsOf(hippoRoot, tenantId, dateRaw, { name });
|
|
252
|
-
}
|
|
253
|
-
catch (e) {
|
|
254
|
-
printError(e.message);
|
|
222
|
+
else {
|
|
223
|
+
if (!policiesModule.VALID_POLICY_STATES.has(status)) {
|
|
224
|
+
printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
|
|
255
225
|
process.exit(1);
|
|
256
226
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
console.log(`Policies in force at ${dateRaw}:\n`);
|
|
262
|
-
for (const p of results)
|
|
263
|
-
printPolicyRow(p);
|
|
264
|
-
return;
|
|
227
|
+
results = policiesModule.loadPolicies(hippoRoot, tenantId, {
|
|
228
|
+
status: status,
|
|
229
|
+
limit,
|
|
230
|
+
});
|
|
265
231
|
}
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
if (!idRaw) {
|
|
269
|
-
printError('Usage: hippo policy get <id>');
|
|
270
|
-
process.exit(1);
|
|
271
|
-
}
|
|
272
|
-
const id = parsePositivePolicyId(idRaw);
|
|
273
|
-
const p = policiesModule.loadPolicyById(hippoRoot, tenantId, id);
|
|
274
|
-
if (!p) {
|
|
275
|
-
printError(`Policy ${id} not found.`);
|
|
276
|
-
process.exit(1);
|
|
277
|
-
}
|
|
278
|
-
console.log(`Policy #${p.id}`);
|
|
279
|
-
console.log(` name: ${p.policyName}`);
|
|
280
|
-
console.log(` text: ${p.policyText}`);
|
|
281
|
-
console.log(` status: ${p.status}`);
|
|
282
|
-
console.log(` version: ${p.version}`);
|
|
283
|
-
console.log(` valid_from: ${p.validFrom}`);
|
|
284
|
-
console.log(` valid_to: ${p.validTo ?? '(open-ended)'}`);
|
|
285
|
-
if (p.changeSummary)
|
|
286
|
-
console.log(` change_summary: ${p.changeSummary}`);
|
|
287
|
-
if (p.supersededBy !== null)
|
|
288
|
-
console.log(` superseded_by: #${p.supersededBy}`);
|
|
289
|
-
if (p.supersededAt)
|
|
290
|
-
console.log(` superseded_at: ${p.supersededAt}`);
|
|
291
|
-
if (p.closedAt)
|
|
292
|
-
console.log(` closed_at: ${p.closedAt}`);
|
|
293
|
-
if (p.memoryId)
|
|
294
|
-
console.log(` memory: ${p.memoryId}`);
|
|
295
|
-
console.log(` created: ${p.createdAt}`);
|
|
232
|
+
if (results.length === 0) {
|
|
233
|
+
console.log('No policies.');
|
|
296
234
|
return;
|
|
297
235
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
printError('hippo policy supersede requires --text "<rule>" for the new version.');
|
|
308
|
-
process.exit(1);
|
|
309
|
-
}
|
|
310
|
-
const existing = policiesModule.loadPolicyById(hippoRoot, tenantId, id);
|
|
311
|
-
if (!existing) {
|
|
312
|
-
printError(`Policy ${id} not found.`);
|
|
313
|
-
process.exit(1);
|
|
314
|
-
}
|
|
315
|
-
const fromRaw = flags['from'];
|
|
316
|
-
const toRaw = flags['to'];
|
|
317
|
-
const changeRaw = flags['change'];
|
|
318
|
-
try {
|
|
319
|
-
const created = policiesModule.savePolicy(hippoRoot, tenantId, {
|
|
320
|
-
policyName: existing.policyName,
|
|
321
|
-
policyText: textRaw,
|
|
322
|
-
validFrom: typeof fromRaw === 'string' && fromRaw ? fromRaw : undefined,
|
|
323
|
-
validTo: typeof toRaw === 'string' && toRaw ? toRaw : undefined,
|
|
324
|
-
changeSummary: typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined,
|
|
325
|
-
supersedesPolicyId: id,
|
|
326
|
-
extraTags: extractPathTags(process.cwd()),
|
|
327
|
-
});
|
|
328
|
-
console.log(`Policy #${created.id} recorded (v${created.version}), superseding #${id}.`);
|
|
329
|
-
if (created.memoryId)
|
|
330
|
-
console.log(` memory: ${created.memoryId}`);
|
|
331
|
-
}
|
|
332
|
-
catch (e) {
|
|
333
|
-
printError(e.message);
|
|
334
|
-
process.exit(1);
|
|
335
|
-
}
|
|
336
|
-
return;
|
|
236
|
+
console.log(`Found ${results.length} policies:\n`);
|
|
237
|
+
for (const p of results)
|
|
238
|
+
printPolicyRow(p);
|
|
239
|
+
}
|
|
240
|
+
function policyAsOf(hippoRoot, tenantId, args, flags) {
|
|
241
|
+
const dateRaw = args[1];
|
|
242
|
+
if (!dateRaw) {
|
|
243
|
+
printError('Usage: hippo policy asof <iso-date> [--name "<policy>"]');
|
|
244
|
+
process.exit(1);
|
|
337
245
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
246
|
+
const nameRaw = flags['name'];
|
|
247
|
+
const name = typeof nameRaw === 'string' && nameRaw ? nameRaw : undefined;
|
|
248
|
+
let results;
|
|
249
|
+
try {
|
|
250
|
+
results = policiesModule.loadPoliciesAsOf(hippoRoot, tenantId, dateRaw, { name });
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
printError(e.message);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
if (results.length === 0) {
|
|
257
|
+
console.log(`No active policies in force at ${dateRaw}${name ? ` for "${name}"` : ''}.`);
|
|
347
258
|
return;
|
|
348
259
|
}
|
|
260
|
+
console.log(`Policies in force at ${dateRaw}:\n`);
|
|
261
|
+
for (const p of results)
|
|
262
|
+
printPolicyRow(p);
|
|
263
|
+
}
|
|
264
|
+
function policyGet(hippoRoot, tenantId, args) {
|
|
265
|
+
const idRaw = args[1];
|
|
266
|
+
if (!idRaw) {
|
|
267
|
+
printError('Usage: hippo policy get <id>');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
const id = parsePositivePolicyId(idRaw);
|
|
271
|
+
const p = policiesModule.loadPolicyById(hippoRoot, tenantId, id);
|
|
272
|
+
if (!p) {
|
|
273
|
+
printError(`Policy ${id} not found.`);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
console.log(`Policy #${p.id}`);
|
|
277
|
+
console.log(` name: ${p.policyName}`);
|
|
278
|
+
console.log(` text: ${p.policyText}`);
|
|
279
|
+
console.log(` status: ${p.status}`);
|
|
280
|
+
console.log(` version: ${p.version}`);
|
|
281
|
+
console.log(` valid_from: ${p.validFrom}`);
|
|
282
|
+
console.log(` valid_to: ${p.validTo ?? '(open-ended)'}`);
|
|
283
|
+
if (p.changeSummary)
|
|
284
|
+
console.log(` change_summary: ${p.changeSummary}`);
|
|
285
|
+
if (p.supersededBy !== null)
|
|
286
|
+
console.log(` superseded_by: #${p.supersededBy}`);
|
|
287
|
+
if (p.supersededAt)
|
|
288
|
+
console.log(` superseded_at: ${p.supersededAt}`);
|
|
289
|
+
if (p.closedAt)
|
|
290
|
+
console.log(` closed_at: ${p.closedAt}`);
|
|
291
|
+
if (p.memoryId)
|
|
292
|
+
console.log(` memory: ${p.memoryId}`);
|
|
293
|
+
console.log(` created: ${p.createdAt}`);
|
|
294
|
+
}
|
|
295
|
+
function policySupersede(hippoRoot, tenantId, args, flags) {
|
|
296
|
+
const idRaw = args[1];
|
|
297
|
+
if (!idRaw) {
|
|
298
|
+
printError('Usage: hippo policy supersede <id> --text "<rule>" [--from <iso>] [--to <iso>] [--change "<summary>"]');
|
|
299
|
+
process.exit(1);
|
|
300
|
+
}
|
|
301
|
+
const id = parsePositivePolicyId(idRaw);
|
|
302
|
+
const textRaw = flags['text'];
|
|
303
|
+
if (typeof textRaw !== 'string' || !textRaw.trim()) {
|
|
304
|
+
printError('hippo policy supersede requires --text "<rule>" for the new version.');
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
const existing = policiesModule.loadPolicyById(hippoRoot, tenantId, id);
|
|
308
|
+
if (!existing) {
|
|
309
|
+
printError(`Policy ${id} not found.`);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
const fromRaw = flags['from'];
|
|
313
|
+
const toRaw = flags['to'];
|
|
314
|
+
const changeRaw = flags['change'];
|
|
315
|
+
try {
|
|
316
|
+
const created = policiesModule.savePolicy(hippoRoot, tenantId, {
|
|
317
|
+
policyName: existing.policyName,
|
|
318
|
+
policyText: textRaw,
|
|
319
|
+
validFrom: typeof fromRaw === 'string' && fromRaw ? fromRaw : undefined,
|
|
320
|
+
validTo: typeof toRaw === 'string' && toRaw ? toRaw : undefined,
|
|
321
|
+
changeSummary: typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined,
|
|
322
|
+
supersedesPolicyId: id,
|
|
323
|
+
extraTags: extractPathTags(process.cwd()),
|
|
324
|
+
});
|
|
325
|
+
console.log(`Policy #${created.id} recorded (v${created.version}), superseding #${id}.`);
|
|
326
|
+
if (created.memoryId)
|
|
327
|
+
console.log(` memory: ${created.memoryId}`);
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
printError(e.message);
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function policyClose(hippoRoot, tenantId, args) {
|
|
335
|
+
const idRaw = args[1];
|
|
336
|
+
if (!idRaw) {
|
|
337
|
+
printError('Usage: hippo policy close <id>');
|
|
338
|
+
process.exit(1);
|
|
339
|
+
}
|
|
340
|
+
const id = parsePositivePolicyId(idRaw);
|
|
341
|
+
const closed = policiesModule.closePolicy(hippoRoot, tenantId, id);
|
|
342
|
+
console.log(`Policy #${closed.id} closed.`);
|
|
343
|
+
}
|
|
344
|
+
export function cmdPolicy(hippoRoot, args, flags) {
|
|
345
|
+
requireInit(hippoRoot);
|
|
346
|
+
const tenantId = resolveTenantId({});
|
|
347
|
+
const subcommand = args[0] ?? '';
|
|
348
|
+
if (subcommand === 'list')
|
|
349
|
+
return policyList(hippoRoot, tenantId, flags);
|
|
350
|
+
if (subcommand === 'asof')
|
|
351
|
+
return policyAsOf(hippoRoot, tenantId, args, flags);
|
|
352
|
+
if (subcommand === 'get')
|
|
353
|
+
return policyGet(hippoRoot, tenantId, args);
|
|
354
|
+
if (subcommand === 'supersede')
|
|
355
|
+
return policySupersede(hippoRoot, tenantId, args, flags);
|
|
356
|
+
if (subcommand === 'close')
|
|
357
|
+
return policyClose(hippoRoot, tenantId, args);
|
|
358
|
+
policyCreate(hippoRoot, tenantId, args, flags);
|
|
359
|
+
}
|
|
360
|
+
function policyCreate(hippoRoot, tenantId, args, flags) {
|
|
361
|
+
const subcommand = args[0] ?? '';
|
|
349
362
|
// Default subcommand: new (create). Accept both `policy new "<name>"` and the
|
|
350
363
|
// bare `policy "<name>"` form: for the `new` keyword the name is args[1].
|
|
351
364
|
const policyName = subcommand === 'new' ? (args[1] ?? '') : subcommand;
|
|
@@ -397,132 +410,136 @@ function printSkillRow(s) {
|
|
|
397
410
|
if (s.changeSummary)
|
|
398
411
|
console.log(` change: ${s.changeSummary}`);
|
|
399
412
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (!
|
|
410
|
-
printError(`Invalid --
|
|
413
|
+
function skillList(hippoRoot, tenantId, flags) {
|
|
414
|
+
const statusRaw = flags['status'];
|
|
415
|
+
const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
|
|
416
|
+
const limit = parseListLimit(flags);
|
|
417
|
+
let results;
|
|
418
|
+
if (status === 'all') {
|
|
419
|
+
results = skillsModule.loadSkills(hippoRoot, tenantId, { limit });
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
if (!skillsModule.VALID_SKILL_STATES.has(status)) {
|
|
423
|
+
printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
|
|
411
424
|
process.exit(1);
|
|
412
425
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
if (!skillsModule.VALID_SKILL_STATES.has(status)) {
|
|
419
|
-
printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
|
|
420
|
-
process.exit(1);
|
|
421
|
-
}
|
|
422
|
-
results = skillsModule.loadSkills(hippoRoot, tenantId, {
|
|
423
|
-
status: status,
|
|
424
|
-
limit,
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
if (results.length === 0) {
|
|
428
|
-
console.log('No skills.');
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
console.log(`Found ${results.length} skills:\n`);
|
|
432
|
-
for (const s of results)
|
|
433
|
-
printSkillRow(s);
|
|
434
|
-
return;
|
|
426
|
+
results = skillsModule.loadSkills(hippoRoot, tenantId, {
|
|
427
|
+
status: status,
|
|
428
|
+
limit,
|
|
429
|
+
});
|
|
435
430
|
}
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
if (!md) {
|
|
439
|
-
console.log('No active skills.');
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
console.log(md);
|
|
431
|
+
if (results.length === 0) {
|
|
432
|
+
console.log('No skills.');
|
|
443
433
|
return;
|
|
444
434
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
if (!s) {
|
|
454
|
-
printError(`Skill ${id} not found.`);
|
|
455
|
-
process.exit(1);
|
|
456
|
-
}
|
|
457
|
-
console.log(`Skill #${s.id}`);
|
|
458
|
-
console.log(` name: ${s.skillName}`);
|
|
459
|
-
console.log(` status: ${s.status}`);
|
|
460
|
-
console.log(` version: ${s.version}`);
|
|
461
|
-
if (s.trigger)
|
|
462
|
-
console.log(` when: ${s.trigger}`);
|
|
463
|
-
console.log(` instructions: ${s.instructions}`);
|
|
464
|
-
if (s.changeSummary)
|
|
465
|
-
console.log(` change_summary: ${s.changeSummary}`);
|
|
466
|
-
if (s.supersededBy !== null)
|
|
467
|
-
console.log(` superseded_by: #${s.supersededBy}`);
|
|
468
|
-
if (s.supersededAt)
|
|
469
|
-
console.log(` superseded_at: ${s.supersededAt}`);
|
|
470
|
-
if (s.closedAt)
|
|
471
|
-
console.log(` closed_at: ${s.closedAt}`);
|
|
472
|
-
if (s.memoryId)
|
|
473
|
-
console.log(` memory: ${s.memoryId}`);
|
|
474
|
-
console.log(` created: ${s.createdAt}`);
|
|
435
|
+
console.log(`Found ${results.length} skills:\n`);
|
|
436
|
+
for (const s of results)
|
|
437
|
+
printSkillRow(s);
|
|
438
|
+
}
|
|
439
|
+
function skillExport(hippoRoot, tenantId) {
|
|
440
|
+
const md = skillsModule.exportSkills(hippoRoot, tenantId);
|
|
441
|
+
if (!md) {
|
|
442
|
+
console.log('No active skills.');
|
|
475
443
|
return;
|
|
476
444
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const instrRaw = flags['instructions'];
|
|
485
|
-
if (typeof instrRaw !== 'string' || !instrRaw.trim()) {
|
|
486
|
-
printError('hippo skill supersede requires --instructions "<text>" for the new version.');
|
|
487
|
-
process.exit(1);
|
|
488
|
-
}
|
|
489
|
-
const existing = skillsModule.loadSkillById(hippoRoot, tenantId, id);
|
|
490
|
-
if (!existing) {
|
|
491
|
-
printError(`Skill ${id} not found.`);
|
|
492
|
-
process.exit(1);
|
|
493
|
-
}
|
|
494
|
-
const trigRaw = flags['trigger'];
|
|
495
|
-
const changeRaw = flags['change'];
|
|
496
|
-
try {
|
|
497
|
-
const created = skillsModule.saveSkill(hippoRoot, tenantId, {
|
|
498
|
-
skillName: existing.skillName,
|
|
499
|
-
instructions: instrRaw,
|
|
500
|
-
trigger: typeof trigRaw === 'string' && trigRaw ? trigRaw : undefined,
|
|
501
|
-
changeSummary: typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined,
|
|
502
|
-
supersedesSkillId: id,
|
|
503
|
-
extraTags: extractPathTags(process.cwd()),
|
|
504
|
-
});
|
|
505
|
-
console.log(`Skill #${created.id} recorded (v${created.version}), superseding #${id}.`);
|
|
506
|
-
if (created.memoryId)
|
|
507
|
-
console.log(` memory: ${created.memoryId}`);
|
|
508
|
-
}
|
|
509
|
-
catch (e) {
|
|
510
|
-
printError(e.message);
|
|
511
|
-
process.exit(1);
|
|
512
|
-
}
|
|
513
|
-
return;
|
|
445
|
+
console.log(md);
|
|
446
|
+
}
|
|
447
|
+
function skillGet(hippoRoot, tenantId, args) {
|
|
448
|
+
const idRaw = args[1];
|
|
449
|
+
if (!idRaw) {
|
|
450
|
+
printError('Usage: hippo skill get <id>');
|
|
451
|
+
process.exit(1);
|
|
514
452
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
453
|
+
const id = parsePositiveSkillId(idRaw);
|
|
454
|
+
const s = skillsModule.loadSkillById(hippoRoot, tenantId, id);
|
|
455
|
+
if (!s) {
|
|
456
|
+
printError(`Skill ${id} not found.`);
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
console.log(`Skill #${s.id}`);
|
|
460
|
+
console.log(` name: ${s.skillName}`);
|
|
461
|
+
console.log(` status: ${s.status}`);
|
|
462
|
+
console.log(` version: ${s.version}`);
|
|
463
|
+
if (s.trigger)
|
|
464
|
+
console.log(` when: ${s.trigger}`);
|
|
465
|
+
console.log(` instructions: ${s.instructions}`);
|
|
466
|
+
if (s.changeSummary)
|
|
467
|
+
console.log(` change_summary: ${s.changeSummary}`);
|
|
468
|
+
if (s.supersededBy !== null)
|
|
469
|
+
console.log(` superseded_by: #${s.supersededBy}`);
|
|
470
|
+
if (s.supersededAt)
|
|
471
|
+
console.log(` superseded_at: ${s.supersededAt}`);
|
|
472
|
+
if (s.closedAt)
|
|
473
|
+
console.log(` closed_at: ${s.closedAt}`);
|
|
474
|
+
if (s.memoryId)
|
|
475
|
+
console.log(` memory: ${s.memoryId}`);
|
|
476
|
+
console.log(` created: ${s.createdAt}`);
|
|
477
|
+
}
|
|
478
|
+
function skillSupersede(hippoRoot, tenantId, args, flags) {
|
|
479
|
+
const idRaw = args[1];
|
|
480
|
+
if (!idRaw) {
|
|
481
|
+
printError('Usage: hippo skill supersede <id> --instructions "<text>" [--trigger "<when>"] [--change "<summary>"]');
|
|
482
|
+
process.exit(1);
|
|
483
|
+
}
|
|
484
|
+
const id = parsePositiveSkillId(idRaw);
|
|
485
|
+
const instrRaw = flags['instructions'];
|
|
486
|
+
if (typeof instrRaw !== 'string' || !instrRaw.trim()) {
|
|
487
|
+
printError('hippo skill supersede requires --instructions "<text>" for the new version.');
|
|
488
|
+
process.exit(1);
|
|
525
489
|
}
|
|
490
|
+
const existing = skillsModule.loadSkillById(hippoRoot, tenantId, id);
|
|
491
|
+
if (!existing) {
|
|
492
|
+
printError(`Skill ${id} not found.`);
|
|
493
|
+
process.exit(1);
|
|
494
|
+
}
|
|
495
|
+
const trigRaw = flags['trigger'];
|
|
496
|
+
const changeRaw = flags['change'];
|
|
497
|
+
try {
|
|
498
|
+
const created = skillsModule.saveSkill(hippoRoot, tenantId, {
|
|
499
|
+
skillName: existing.skillName,
|
|
500
|
+
instructions: instrRaw,
|
|
501
|
+
trigger: typeof trigRaw === 'string' && trigRaw ? trigRaw : undefined,
|
|
502
|
+
changeSummary: typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined,
|
|
503
|
+
supersedesSkillId: id,
|
|
504
|
+
extraTags: extractPathTags(process.cwd()),
|
|
505
|
+
});
|
|
506
|
+
console.log(`Skill #${created.id} recorded (v${created.version}), superseding #${id}.`);
|
|
507
|
+
if (created.memoryId)
|
|
508
|
+
console.log(` memory: ${created.memoryId}`);
|
|
509
|
+
}
|
|
510
|
+
catch (e) {
|
|
511
|
+
printError(e.message);
|
|
512
|
+
process.exit(1);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
function skillClose(hippoRoot, tenantId, args) {
|
|
516
|
+
const idRaw = args[1];
|
|
517
|
+
if (!idRaw) {
|
|
518
|
+
printError('Usage: hippo skill close <id>');
|
|
519
|
+
process.exit(1);
|
|
520
|
+
}
|
|
521
|
+
const id = parsePositiveSkillId(idRaw);
|
|
522
|
+
const closed = skillsModule.closeSkill(hippoRoot, tenantId, id);
|
|
523
|
+
console.log(`Skill #${closed.id} closed.`);
|
|
524
|
+
}
|
|
525
|
+
export function cmdSkill(hippoRoot, args, flags) {
|
|
526
|
+
requireInit(hippoRoot);
|
|
527
|
+
const tenantId = resolveTenantId({});
|
|
528
|
+
const subcommand = args[0] ?? '';
|
|
529
|
+
if (subcommand === 'list')
|
|
530
|
+
return skillList(hippoRoot, tenantId, flags);
|
|
531
|
+
if (subcommand === 'export')
|
|
532
|
+
return skillExport(hippoRoot, tenantId);
|
|
533
|
+
if (subcommand === 'get')
|
|
534
|
+
return skillGet(hippoRoot, tenantId, args);
|
|
535
|
+
if (subcommand === 'supersede')
|
|
536
|
+
return skillSupersede(hippoRoot, tenantId, args, flags);
|
|
537
|
+
if (subcommand === 'close')
|
|
538
|
+
return skillClose(hippoRoot, tenantId, args);
|
|
539
|
+
skillCreate(hippoRoot, tenantId, args, flags);
|
|
540
|
+
}
|
|
541
|
+
function skillCreate(hippoRoot, tenantId, args, flags) {
|
|
542
|
+
const subcommand = args[0] ?? '';
|
|
526
543
|
// Default subcommand: new (create). Accept both `skill new "<name>"` and the
|
|
527
544
|
// bare `skill "<name>"` form: for the `new` keyword the name is args[1].
|
|
528
545
|
const skillName = subcommand === 'new' ? (args[1] ?? '') : subcommand;
|