hippo-memory 1.39.0 → 1.40.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 +8 -0
- package/dist/card.d.ts +45 -0
- package/dist/card.d.ts.map +1 -0
- package/dist/card.js +16 -0
- package/dist/card.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +308 -3
- package/dist/cli.js.map +1 -1
- package/dist/connectors/slack/backfill.js +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +123 -1
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/src/card.js +16 -0
- package/dist/src/card.js.map +1 -0
- package/dist/src/cli.js +308 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/connectors/slack/backfill.js +1 -1
- package/dist/src/db.js +123 -1
- package/dist/src/db.js.map +1 -1
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/store.js +421 -1
- package/dist/src/store.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/store.d.ts +44 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +421 -1
- package/dist/store.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-ui/index.html +12 -12
- 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/benchmarks/e1.3/scenarios.json +0 -2587
package/README.md
CHANGED
|
@@ -619,6 +619,14 @@ hippo watch "npm run build"
|
|
|
619
619
|
| `hippo session latest` | Show latest task snapshot + events |
|
|
620
620
|
| `hippo session resume` | Re-inject latest handoff as context |
|
|
621
621
|
| `hippo current show` | Compact current state (task + session events) |
|
|
622
|
+
| `hippo card create --title "..."` | Create a work-queue card (`--repo`, `--contract`, `--budget`, repeatable `--depends-on <id>`) |
|
|
623
|
+
| `hippo card show <id>` | Show a card, its deps, runs, comments and latest handoff |
|
|
624
|
+
| `hippo card list [--status <status>]` | List cards, newest-updated first |
|
|
625
|
+
| `hippo card claim <id> --runtime <name>` | Claim a ready or blocked card |
|
|
626
|
+
| `hippo card block <id> --reason "<why>"` | Block a running card; the reason is recorded as a comment |
|
|
627
|
+
| `hippo card review <id>` | Move a running card to review |
|
|
628
|
+
| `hippo card complete <id> --outcome <success\|failure\|partial>` | Complete a card in review: `success` marks it done and promotes children whose parents are all done; `failure` or `partial` shelves it |
|
|
629
|
+
| `hippo card comment <id> --body "..."` | Add a comment to a card |
|
|
622
630
|
| `hippo wm push --scope <s> --content "..."` | Push to working memory |
|
|
623
631
|
| `hippo wm read --scope <s>` | Read working memory entries |
|
|
624
632
|
| `hippo wm clear --scope <s>` | Clear working memory |
|
package/dist/card.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** A work-queue card's lifecycle state; CARD_TRANSITIONS lists the legal moves between them. */
|
|
2
|
+
export type CardStatus = 'backlog' | 'ready' | 'running' | 'blocked' | 'review' | 'done' | 'shelved';
|
|
3
|
+
/** Narrows a raw string, such as a CLI flag value, to a CardStatus. */
|
|
4
|
+
export declare function isCardStatus(v: string): v is CardStatus;
|
|
5
|
+
/** A card row; leaseUntil and heartbeatAt are reserved for W2b's lease and heartbeat, W2a never writes them and they stay null. */
|
|
6
|
+
export interface Card {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
status: CardStatus;
|
|
10
|
+
assigneeRuntime: string | null;
|
|
11
|
+
repo: string | null;
|
|
12
|
+
contract: string | null;
|
|
13
|
+
budget: number | null;
|
|
14
|
+
leaseUntil: string | null;
|
|
15
|
+
heartbeatAt: string | null;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
updatedAt: string;
|
|
18
|
+
tenantId: string;
|
|
19
|
+
scope: string | null;
|
|
20
|
+
}
|
|
21
|
+
/** One claim-to-close attempt at a card by a runtime. */
|
|
22
|
+
export interface CardRun {
|
|
23
|
+
id: number;
|
|
24
|
+
card: string;
|
|
25
|
+
runtime: string;
|
|
26
|
+
sessionId: string | null;
|
|
27
|
+
started: string;
|
|
28
|
+
ended: string | null;
|
|
29
|
+
outcome: string | null;
|
|
30
|
+
}
|
|
31
|
+
/** A comment left on a card. */
|
|
32
|
+
export interface CardComment {
|
|
33
|
+
id: number;
|
|
34
|
+
cardId: string;
|
|
35
|
+
author: string;
|
|
36
|
+
body: string;
|
|
37
|
+
createdAt: string;
|
|
38
|
+
}
|
|
39
|
+
/** The legal next-statuses for each CardStatus. */
|
|
40
|
+
export type CardTransitions = {
|
|
41
|
+
readonly [S in CardStatus]: readonly CardStatus[];
|
|
42
|
+
};
|
|
43
|
+
/** transitionCard checks every status write against this map. */
|
|
44
|
+
export declare const CARD_TRANSITIONS: CardTransitions;
|
|
45
|
+
//# sourceMappingURL=card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../src/card.ts"],"names":[],"mappings":"AACA,gGAAgG;AAChG,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAIrG,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,UAAU,CAEvD;AAED,mIAAmI;AACnI,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,yDAAyD;AACzD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,gCAAgC;AAChC,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mDAAmD;AACnD,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,CAAC,IAAI,UAAU,GAAG,SAAS,UAAU,EAAE;CAAE,CAAC;AAEpF,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,EAAE,eAQ9B,CAAC"}
|
package/dist/card.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const CARD_STATUSES = ['backlog', 'ready', 'running', 'blocked', 'review', 'done', 'shelved'];
|
|
2
|
+
/** Narrows a raw string, such as a CLI flag value, to a CardStatus. */
|
|
3
|
+
export function isCardStatus(v) {
|
|
4
|
+
return CARD_STATUSES.some((s) => s === v);
|
|
5
|
+
}
|
|
6
|
+
/** transitionCard checks every status write against this map. */
|
|
7
|
+
export const CARD_TRANSITIONS = {
|
|
8
|
+
backlog: ['ready'],
|
|
9
|
+
ready: ['running'],
|
|
10
|
+
running: ['blocked', 'review'],
|
|
11
|
+
blocked: ['running'],
|
|
12
|
+
review: ['done', 'shelved'],
|
|
13
|
+
done: [],
|
|
14
|
+
shelved: [],
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=card.js.map
|
package/dist/card.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card.js","sourceRoot":"","sources":["../src/card.ts"],"names":[],"mappings":"AAIA,MAAM,aAAa,GAA0B,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAErH,uEAAuE;AACvE,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5C,CAAC;AA0CD,iEAAiE;AACjE,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,OAAO,EAAE,CAAC,OAAO,CAAC;IAClB,KAAK,EAAE,CAAC,SAAS,CAAC;IAClB,OAAO,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC;IAC9B,OAAO,EAAE,CAAC,SAAS,CAAC;IACpB,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,EAAE;CACZ,CAAC"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* hippo snapshot <save|show|clear>
|
|
14
14
|
* hippo session <log|show|latest|resume|complete>
|
|
15
15
|
* hippo handoff <create|latest|show>
|
|
16
|
+
* hippo card <create|show|list|claim|block|review|complete|comment>
|
|
16
17
|
* hippo current <show>
|
|
17
18
|
* hippo forget <id> [--archive --reason "<why>"]
|
|
18
19
|
* hippo reject <id>|--value "<text>" --reason "<why>"
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA2BH,OAAO,EAWL,WAAW,EAEZ,MAAM,aAAa,CAAC;AA2HrB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AA4ChC,kFAAkF;AAClF,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAsJD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAA;CAAE,CAwCjI;AAqxED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,MAAqB,GAAG,MAAM,CAyF3F;AAyGD;;;GAGG;AACH,oHAAoH;AACpH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAuD/D;AA87GD,oHAAoH;AACpH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,EACtF,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,MAAkB,GAC1B,IAAI,CA8BN"}
|
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* hippo snapshot <save|show|clear>
|
|
14
14
|
* hippo session <log|show|latest|resume|complete>
|
|
15
15
|
* hippo handoff <create|latest|show>
|
|
16
|
+
* hippo card <create|show|list|claim|block|review|complete|comment>
|
|
16
17
|
* hippo current <show>
|
|
17
18
|
* hippo forget <id> [--archive --reason "<why>"]
|
|
18
19
|
* hippo reject <id>|--value "<text>" --reason "<why>"
|
|
@@ -37,10 +38,11 @@ import { execFileSync, execSync, spawn } from 'child_process';
|
|
|
37
38
|
import { installJsonHooks, uninstallJsonHooks, resolveJsonHookPaths, detectInstalledTools, defaultSleepLogPath, ensureCodexWrapperInstalled, installCodexWrapper, isCodexWrapperInstalled, repairCodexWrapperIfInstalled, uninstallCodexWrapper, resolveCodexSessionTranscript, resolveCodexWrapperPaths, installOpencodePlugin, uninstallOpencodePlugin, resolveOpencodePluginPath, } from './hooks.js';
|
|
38
39
|
import { createMemory, calculateStrength, calculateRewardFactor, deriveHalfLife, resolveConfidence, confidenceFacets, confidenceLabel, computeSchemaFit, Layer, } from './memory.js';
|
|
39
40
|
import { detectSecret } from './secret-detect.js';
|
|
40
|
-
import { getHippoRoot, isInitialized, initStore, writeEntry, readEntry, deleteEntry, loadAllEntries, loadSearchEntries, loadRecallSearchEntries, loadIndex, saveIndex, loadStats, updateStats, saveActiveTaskSnapshot, loadActiveTaskSnapshot, closeTaskSnapshotsForSession, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, listMemoryConflicts, resolveConflict, saveSessionHandoff, loadLatestHandoff, loadHandoffById, stampHandoffOutcome, writeSessionEndHandoff, } from './store.js';
|
|
41
|
+
import { getHippoRoot, isInitialized, initStore, writeEntry, readEntry, deleteEntry, loadAllEntries, loadSearchEntries, loadRecallSearchEntries, loadIndex, saveIndex, loadStats, updateStats, saveActiveTaskSnapshot, loadActiveTaskSnapshot, closeTaskSnapshotsForSession, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, listMemoryConflicts, resolveConflict, saveSessionHandoff, loadLatestHandoff, loadHandoffById, stampHandoffOutcome, writeSessionEndHandoff, createCard, loadCard, listCards, loadCardDeps, loadCardRuns, loadCardComments, claimCard, blockCard, reviewCard, completeCard, addCardComment, loadLatestHandoffForCard, } from './store.js';
|
|
41
42
|
import { rejectValue, unrejectValue, listRejectionsForTenant } from './reject-flow.js';
|
|
42
43
|
import { RejectedValueError } from './rejection.js';
|
|
43
44
|
import { isHandoffOutcome, formatHandoffEvidenceLine } from './handoff.js';
|
|
45
|
+
import { isCardStatus } from './card.js';
|
|
44
46
|
import { passesScopeFilterForRecall } from './recall-scope.js';
|
|
45
47
|
import { markRetrieved, hybridSearch, physicsSearch, explainMatch, textOverlap, tokenize as tokenizeQuery } from './search.js';
|
|
46
48
|
import { compareEntryIdentity } from './compare.js';
|
|
@@ -253,8 +255,8 @@ export function parseArgs(argv) {
|
|
|
253
255
|
i++;
|
|
254
256
|
}
|
|
255
257
|
else {
|
|
256
|
-
// Check if it's a repeatable flag (tag, artifact, link, step, constraint)
|
|
257
|
-
if (key === 'tag' || key === 'artifact' || key === 'link' || key === 'step' || key === 'constraint') {
|
|
258
|
+
// Check if it's a repeatable flag (tag, artifact, link, step, constraint, depends-on)
|
|
259
|
+
if (key === 'tag' || key === 'artifact' || key === 'link' || key === 'step' || key === 'constraint' || key === 'depends-on') {
|
|
258
260
|
if (Array.isArray(flags[key])) {
|
|
259
261
|
flags[key].push(next);
|
|
260
262
|
}
|
|
@@ -4024,6 +4026,281 @@ function cmdHandoff(hippoRoot, args, flags) {
|
|
|
4024
4026
|
console.error('Usage: hippo handoff <create|latest|show>');
|
|
4025
4027
|
process.exit(1);
|
|
4026
4028
|
}
|
|
4029
|
+
// Mirrors ARCHIVE_REASON_REQUIRED so the block message can't drift from its usage line.
|
|
4030
|
+
const CARD_BLOCK_REASON_REQUIRED = 'hippo card block <id> requires --reason "<why>" (recorded as a comment).';
|
|
4031
|
+
function printCard(hippoRoot, tenantId, card) {
|
|
4032
|
+
console.log(`## Card ${card.id}\n`);
|
|
4033
|
+
console.log(`- Title: ${card.title}`);
|
|
4034
|
+
console.log(`- Status: ${card.status}`);
|
|
4035
|
+
if (card.assigneeRuntime)
|
|
4036
|
+
console.log(`- Assignee: ${card.assigneeRuntime}`);
|
|
4037
|
+
if (card.repo)
|
|
4038
|
+
console.log(`- Repo: ${card.repo}`);
|
|
4039
|
+
if (card.contract)
|
|
4040
|
+
console.log(`- Contract: ${card.contract}`);
|
|
4041
|
+
if (card.budget !== null)
|
|
4042
|
+
console.log(`- Budget: ${card.budget}`);
|
|
4043
|
+
console.log(`- Updated: ${card.updatedAt}`);
|
|
4044
|
+
const deps = loadCardDeps(hippoRoot, tenantId, card.id);
|
|
4045
|
+
if (deps.parents.length > 0)
|
|
4046
|
+
console.log(`- Parents: ${deps.parents.join(', ')}`);
|
|
4047
|
+
if (deps.children.length > 0)
|
|
4048
|
+
console.log(`- Children: ${deps.children.join(', ')}`);
|
|
4049
|
+
const runs = loadCardRuns(hippoRoot, tenantId, card.id);
|
|
4050
|
+
if (runs.length > 0) {
|
|
4051
|
+
console.log('\n### Runs');
|
|
4052
|
+
for (const run of runs) {
|
|
4053
|
+
console.log(`- ${run.runtime} started ${run.started}${run.ended ? ` ended ${run.ended} (${run.outcome})` : ' (open)'}`);
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
const comments = loadCardComments(hippoRoot, tenantId, card.id);
|
|
4057
|
+
if (comments.length > 0) {
|
|
4058
|
+
console.log('\n### Comments');
|
|
4059
|
+
for (const comment of comments) {
|
|
4060
|
+
console.log(`- [${comment.createdAt}] ${comment.author}: ${comment.body}`);
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
4063
|
+
const handoff = loadLatestHandoffForCard(hippoRoot, tenantId, card.id);
|
|
4064
|
+
if (handoff) {
|
|
4065
|
+
console.log('\n### Latest handoff');
|
|
4066
|
+
console.log(`- Session: ${handoff.sessionId}, updated ${handoff.updatedAt}`);
|
|
4067
|
+
console.log(handoff.summary);
|
|
4068
|
+
}
|
|
4069
|
+
console.log('');
|
|
4070
|
+
}
|
|
4071
|
+
// parseArgs turns a value-less flag into `true`; refuse rather than silently
|
|
4072
|
+
// stringifying it (String(true) === 'true'), mirroring cmdHandoff's guard.
|
|
4073
|
+
function cardStringFlag(flags, key) {
|
|
4074
|
+
const v = flags[key];
|
|
4075
|
+
if (v === undefined)
|
|
4076
|
+
return undefined;
|
|
4077
|
+
if (v === true || v === false || Array.isArray(v)) {
|
|
4078
|
+
console.error(`--${key} requires a value`);
|
|
4079
|
+
process.exit(1);
|
|
4080
|
+
}
|
|
4081
|
+
return v.trim();
|
|
4082
|
+
}
|
|
4083
|
+
const CARD_SUBCOMMAND_FLAGS = {
|
|
4084
|
+
create: ['title', 'repo', 'contract', 'budget', 'depends-on'],
|
|
4085
|
+
show: ['json'],
|
|
4086
|
+
list: ['status', 'json'],
|
|
4087
|
+
claim: ['runtime', 'session'],
|
|
4088
|
+
block: ['reason'],
|
|
4089
|
+
review: new Array(),
|
|
4090
|
+
complete: ['outcome'],
|
|
4091
|
+
comment: ['body', 'author'],
|
|
4092
|
+
};
|
|
4093
|
+
function cmdCard(hippoRoot, args, flags) {
|
|
4094
|
+
requireInit(hippoRoot);
|
|
4095
|
+
const tenantId = resolveTenantId({});
|
|
4096
|
+
const subcommand = args[0] ?? '';
|
|
4097
|
+
if (Object.hasOwn(CARD_SUBCOMMAND_FLAGS, subcommand)) {
|
|
4098
|
+
// SAFETY: hasOwn, unlike `in`, skips inherited keys such as constructor, so subcommand is a real key.
|
|
4099
|
+
const allowedFlags = CARD_SUBCOMMAND_FLAGS[subcommand];
|
|
4100
|
+
for (const key of Object.keys(flags)) {
|
|
4101
|
+
if (!allowedFlags.includes(key)) {
|
|
4102
|
+
// --flag=value never splits on '=' (see parseArgs), so it lands here as one long key.
|
|
4103
|
+
const hint = key.includes('=') ? ` Use --${key.slice(0, key.indexOf('='))} <value>, not --${key}.` : '';
|
|
4104
|
+
const valid = allowedFlags.length > 0 ? allowedFlags.map((f) => `--${f}`).join(', ') : '(none)';
|
|
4105
|
+
console.error(`Unknown flag --${key} for hippo card ${subcommand}.${hint} Valid flags: ${valid}`);
|
|
4106
|
+
process.exit(1);
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
if (subcommand === 'create') {
|
|
4111
|
+
const title = cardStringFlag(flags, 'title') ?? '';
|
|
4112
|
+
if (!title) {
|
|
4113
|
+
console.error('Usage: hippo card create --title "..." [--repo <name>] [--contract <text>] [--budget <n>] [--depends-on <id>...]');
|
|
4114
|
+
process.exit(1);
|
|
4115
|
+
}
|
|
4116
|
+
const repo = cardStringFlag(flags, 'repo') || undefined;
|
|
4117
|
+
const contract = cardStringFlag(flags, 'contract') || undefined;
|
|
4118
|
+
const budgetRaw = cardStringFlag(flags, 'budget');
|
|
4119
|
+
let budget;
|
|
4120
|
+
if (budgetRaw !== undefined) {
|
|
4121
|
+
if (!/^\d+$/.test(budgetRaw)) {
|
|
4122
|
+
console.error(`Invalid budget: "${budgetRaw}" (expected a positive integer)`);
|
|
4123
|
+
process.exit(1);
|
|
4124
|
+
}
|
|
4125
|
+
budget = Number(budgetRaw);
|
|
4126
|
+
}
|
|
4127
|
+
const dependsOnFlag = flags['depends-on'];
|
|
4128
|
+
if (dependsOnFlag === true) {
|
|
4129
|
+
console.error('--depends-on requires a value');
|
|
4130
|
+
process.exit(1);
|
|
4131
|
+
}
|
|
4132
|
+
const dependsOn = Array.isArray(dependsOnFlag) ? dependsOnFlag : [];
|
|
4133
|
+
let card;
|
|
4134
|
+
try {
|
|
4135
|
+
card = createCard(hippoRoot, tenantId, { title, repo, contract, budget, dependsOn });
|
|
4136
|
+
}
|
|
4137
|
+
catch (error) {
|
|
4138
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
4139
|
+
process.exit(1);
|
|
4140
|
+
}
|
|
4141
|
+
console.log(`Created card ${card.id} (status: ${card.status})`);
|
|
4142
|
+
return;
|
|
4143
|
+
}
|
|
4144
|
+
if (subcommand === 'show') {
|
|
4145
|
+
const id = args[1];
|
|
4146
|
+
if (!id) {
|
|
4147
|
+
console.error('Usage: hippo card show <id> [--json]');
|
|
4148
|
+
process.exit(1);
|
|
4149
|
+
}
|
|
4150
|
+
const card = loadCard(hippoRoot, tenantId, id);
|
|
4151
|
+
if (!card) {
|
|
4152
|
+
console.error(`No card found with id ${id}.`);
|
|
4153
|
+
process.exit(1);
|
|
4154
|
+
}
|
|
4155
|
+
if (flags['json']) {
|
|
4156
|
+
console.log(JSON.stringify({
|
|
4157
|
+
card,
|
|
4158
|
+
deps: loadCardDeps(hippoRoot, tenantId, id),
|
|
4159
|
+
runs: loadCardRuns(hippoRoot, tenantId, id),
|
|
4160
|
+
comments: loadCardComments(hippoRoot, tenantId, id),
|
|
4161
|
+
handoff: loadLatestHandoffForCard(hippoRoot, tenantId, id),
|
|
4162
|
+
}, null, 2));
|
|
4163
|
+
return;
|
|
4164
|
+
}
|
|
4165
|
+
printCard(hippoRoot, tenantId, card);
|
|
4166
|
+
return;
|
|
4167
|
+
}
|
|
4168
|
+
if (subcommand === 'list') {
|
|
4169
|
+
const status = cardStringFlag(flags, 'status');
|
|
4170
|
+
if (status !== undefined && !isCardStatus(status)) {
|
|
4171
|
+
console.error(`Invalid status: "${status}".`);
|
|
4172
|
+
process.exit(1);
|
|
4173
|
+
}
|
|
4174
|
+
const cards = listCards(hippoRoot, tenantId, { status });
|
|
4175
|
+
if (flags['json']) {
|
|
4176
|
+
console.log(JSON.stringify({ cards }, null, 2));
|
|
4177
|
+
return;
|
|
4178
|
+
}
|
|
4179
|
+
if (cards.length === 0) {
|
|
4180
|
+
console.log('No cards found.');
|
|
4181
|
+
return;
|
|
4182
|
+
}
|
|
4183
|
+
for (const card of cards) {
|
|
4184
|
+
console.log(`${card.id}\t${card.status}\t${card.title}${card.assigneeRuntime ? `\t(${card.assigneeRuntime})` : ''}`);
|
|
4185
|
+
}
|
|
4186
|
+
return;
|
|
4187
|
+
}
|
|
4188
|
+
if (subcommand === 'claim') {
|
|
4189
|
+
const id = args[1];
|
|
4190
|
+
const runtime = cardStringFlag(flags, 'runtime') ?? '';
|
|
4191
|
+
if (!id || !runtime) {
|
|
4192
|
+
console.error('Usage: hippo card claim <id> --runtime <name> [--session <id>]');
|
|
4193
|
+
process.exit(1);
|
|
4194
|
+
}
|
|
4195
|
+
const sessionId = cardStringFlag(flags, 'session') || undefined;
|
|
4196
|
+
let card;
|
|
4197
|
+
try {
|
|
4198
|
+
card = claimCard(hippoRoot, tenantId, id, runtime, sessionId);
|
|
4199
|
+
}
|
|
4200
|
+
catch (error) {
|
|
4201
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
4202
|
+
process.exit(1);
|
|
4203
|
+
}
|
|
4204
|
+
if (!card) {
|
|
4205
|
+
console.error(`Could not claim card ${id} (not ready/blocked, or already claimed).`);
|
|
4206
|
+
process.exit(1);
|
|
4207
|
+
}
|
|
4208
|
+
console.log(`Claimed card ${card.id} for ${runtime}`);
|
|
4209
|
+
return;
|
|
4210
|
+
}
|
|
4211
|
+
if (subcommand === 'block') {
|
|
4212
|
+
const id = args[1];
|
|
4213
|
+
const reason = cardStringFlag(flags, 'reason') ?? '';
|
|
4214
|
+
if (!id || !reason) {
|
|
4215
|
+
console.error(CARD_BLOCK_REASON_REQUIRED);
|
|
4216
|
+
process.exit(1);
|
|
4217
|
+
}
|
|
4218
|
+
let card;
|
|
4219
|
+
try {
|
|
4220
|
+
card = blockCard(hippoRoot, tenantId, id, reason);
|
|
4221
|
+
}
|
|
4222
|
+
catch (error) {
|
|
4223
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
4224
|
+
process.exit(1);
|
|
4225
|
+
}
|
|
4226
|
+
if (!card) {
|
|
4227
|
+
console.error(`Could not block card ${id} (not running).`);
|
|
4228
|
+
process.exit(1);
|
|
4229
|
+
}
|
|
4230
|
+
console.log(`Blocked card ${card.id}`);
|
|
4231
|
+
return;
|
|
4232
|
+
}
|
|
4233
|
+
if (subcommand === 'review') {
|
|
4234
|
+
const id = args[1];
|
|
4235
|
+
if (!id) {
|
|
4236
|
+
console.error('Usage: hippo card review <id>');
|
|
4237
|
+
process.exit(1);
|
|
4238
|
+
}
|
|
4239
|
+
let card;
|
|
4240
|
+
try {
|
|
4241
|
+
card = reviewCard(hippoRoot, tenantId, id);
|
|
4242
|
+
}
|
|
4243
|
+
catch (error) {
|
|
4244
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
4245
|
+
process.exit(1);
|
|
4246
|
+
}
|
|
4247
|
+
if (!card) {
|
|
4248
|
+
console.error(`Could not move card ${id} to review (not running).`);
|
|
4249
|
+
process.exit(1);
|
|
4250
|
+
}
|
|
4251
|
+
console.log(`Card ${card.id} moved to review`);
|
|
4252
|
+
return;
|
|
4253
|
+
}
|
|
4254
|
+
if (subcommand === 'complete') {
|
|
4255
|
+
const id = args[1];
|
|
4256
|
+
const outcomeRaw = flags['outcome'];
|
|
4257
|
+
if (!id || !isHandoffOutcome(outcomeRaw)) {
|
|
4258
|
+
console.error('Usage: hippo card complete <id> --outcome <success|failure|partial>');
|
|
4259
|
+
process.exit(1);
|
|
4260
|
+
}
|
|
4261
|
+
let result;
|
|
4262
|
+
try {
|
|
4263
|
+
result = completeCard(hippoRoot, tenantId, id, outcomeRaw);
|
|
4264
|
+
}
|
|
4265
|
+
catch (error) {
|
|
4266
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
4267
|
+
process.exit(1);
|
|
4268
|
+
}
|
|
4269
|
+
if (!result) {
|
|
4270
|
+
console.error(`Could not complete card ${id} (not in review).`);
|
|
4271
|
+
process.exit(1);
|
|
4272
|
+
}
|
|
4273
|
+
console.log(`Completed card ${result.card.id} (status: ${result.card.status})`);
|
|
4274
|
+
if (result.promotedChildren.length > 0) {
|
|
4275
|
+
console.log(`Promoted to ready: ${result.promotedChildren.join(', ')}`);
|
|
4276
|
+
}
|
|
4277
|
+
return;
|
|
4278
|
+
}
|
|
4279
|
+
if (subcommand === 'comment') {
|
|
4280
|
+
const id = args[1];
|
|
4281
|
+
if (!id) {
|
|
4282
|
+
console.error('Usage: hippo card comment <id> --body "..." [--author <name>]');
|
|
4283
|
+
process.exit(1);
|
|
4284
|
+
}
|
|
4285
|
+
// Only show and comment look the card up directly; claim/block/review/complete throw from the store instead.
|
|
4286
|
+
const card = loadCard(hippoRoot, tenantId, id);
|
|
4287
|
+
if (!card) {
|
|
4288
|
+
console.error(`No card found with id ${id}.`);
|
|
4289
|
+
process.exit(1);
|
|
4290
|
+
}
|
|
4291
|
+
const body = cardStringFlag(flags, 'body') ?? '';
|
|
4292
|
+
if (!body) {
|
|
4293
|
+
console.error('Usage: hippo card comment <id> --body "..." [--author <name>]');
|
|
4294
|
+
process.exit(1);
|
|
4295
|
+
}
|
|
4296
|
+
const author = cardStringFlag(flags, 'author') || 'cli';
|
|
4297
|
+
const comment = addCardComment(hippoRoot, tenantId, id, author, body);
|
|
4298
|
+
console.log(`Added comment ${comment.id} to card ${id}`);
|
|
4299
|
+
return;
|
|
4300
|
+
}
|
|
4301
|
+
console.error('Usage: hippo card <create|show|list|claim|block|review|complete|comment>');
|
|
4302
|
+
process.exit(1);
|
|
4303
|
+
}
|
|
4027
4304
|
// ---------------------------------------------------------------------------
|
|
4028
4305
|
// E2 prediction first-class object (v0.31)
|
|
4029
4306
|
// docs/plans/2026-05-26-e2-prediction-object.md
|
|
@@ -7936,6 +8213,28 @@ Commands:
|
|
|
7936
8213
|
--session <id> Filter by session
|
|
7937
8214
|
--json Output as JSON
|
|
7938
8215
|
handoff show <id> Show a specific handoff by ID
|
|
8216
|
+
card <sub> Manage claimable work-queue cards
|
|
8217
|
+
card create Create a new card
|
|
8218
|
+
--title <text> Card title (required)
|
|
8219
|
+
--repo <name> Associated repo
|
|
8220
|
+
--contract <text> Associated contract
|
|
8221
|
+
--budget <n> Token/step budget
|
|
8222
|
+
--depends-on <id> Parent card id (repeatable)
|
|
8223
|
+
card show <id> Show a card, its deps, runs, comments and latest handoff
|
|
8224
|
+
--json Output as JSON
|
|
8225
|
+
card list List cards, newest-updated first
|
|
8226
|
+
--status <status> Filter by status
|
|
8227
|
+
card claim <id> Claim a ready or blocked card
|
|
8228
|
+
--runtime <name> Claiming runtime (required)
|
|
8229
|
+
--session <id> Session ID
|
|
8230
|
+
card block <id> Block a running card
|
|
8231
|
+
--reason "<why>" Reason recorded as a card comment (required)
|
|
8232
|
+
card review <id> Move a running card to review
|
|
8233
|
+
card complete <id> Complete a card in review
|
|
8234
|
+
--outcome <o> success | failure | partial (required)
|
|
8235
|
+
card comment <id> Add a comment to a card
|
|
8236
|
+
--body <text> Comment body (required)
|
|
8237
|
+
--author <name> Comment author (default: cli)
|
|
7939
8238
|
current <sub> Show compact current state for agent injection
|
|
7940
8239
|
current show Active task + recent session events (default)
|
|
7941
8240
|
--json Output as JSON
|
|
@@ -8161,6 +8460,9 @@ Examples:
|
|
|
8161
8460
|
hippo snapshot save --task "Ship feature" --summary "Tests are green" --next-step "Open the PR" --session sess_123
|
|
8162
8461
|
hippo handoff create --summary "PR is open, tests green" --next "Merge after review" --session sess_123 --artifact src/foo.ts
|
|
8163
8462
|
hippo handoff create --summary s --constraint a --constraint b --outcome partial --target-runtime codex --card-id c1 --tests pass
|
|
8463
|
+
hippo card create --title "Add cards table" --repo hippo --depends-on card_abc
|
|
8464
|
+
hippo card claim card_abc --runtime codex
|
|
8465
|
+
hippo card complete card_abc --outcome success
|
|
8164
8466
|
hippo embed --status
|
|
8165
8467
|
hippo watch "npm run build"
|
|
8166
8468
|
hippo learn --git --days 30
|
|
@@ -8548,6 +8850,9 @@ async function main() {
|
|
|
8548
8850
|
case 'handoff':
|
|
8549
8851
|
cmdHandoff(hippoRoot, args, flags);
|
|
8550
8852
|
break;
|
|
8853
|
+
case 'card':
|
|
8854
|
+
cmdCard(hippoRoot, args, flags);
|
|
8855
|
+
break;
|
|
8551
8856
|
case 'predict':
|
|
8552
8857
|
cmdPredict(hippoRoot, args, flags);
|
|
8553
8858
|
break;
|