openhermes 1.2.2
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 +281 -0
- package/autorecall.mjs +167 -0
- package/bootstrap.mjs +255 -0
- package/curator.mjs +470 -0
- package/harness/commands/build-fix.md +60 -0
- package/harness/commands/code-review.md +71 -0
- package/harness/commands/doctor.md +42 -0
- package/harness/commands/learn.md +37 -0
- package/harness/commands/memory-search.md +37 -0
- package/harness/commands/plan.md +53 -0
- package/harness/commands/security.md +93 -0
- package/harness/constitution/soul.md +76 -0
- package/harness/instructions/RUNTIME.md +21 -0
- package/harness/prompts/architect.txt +175 -0
- package/harness/prompts/build-error-resolver.md +37 -0
- package/harness/prompts/code-reviewer.md +33 -0
- package/harness/prompts/e2e-runner.txt +305 -0
- package/harness/prompts/explore.md +29 -0
- package/harness/prompts/planner.md +30 -0
- package/harness/prompts/security-reviewer.md +35 -0
- package/harness/rules/audit.md +84 -0
- package/harness/rules/checkpointing.md +75 -0
- package/harness/rules/context-loading.md +33 -0
- package/harness/rules/credential-exposure.md +0 -0
- package/harness/rules/delegation.md +76 -0
- package/harness/rules/memory-management.md +28 -0
- package/harness/rules/precedence.md +52 -0
- package/harness/rules/promotion.md +46 -0
- package/harness/rules/ranking.md +64 -0
- package/harness/rules/retrieval.md +94 -0
- package/harness/rules/runtime-guards.md +196 -0
- package/harness/rules/self-heal.md +79 -0
- package/harness/rules/session-start.md +34 -0
- package/harness/rules/skills-management.md +165 -0
- package/harness/rules/state-drift.md +192 -0
- package/harness/rules/verification.md +88 -0
- package/harness/skills/.bundled_manifest +17 -0
- package/harness/skills/.usage.json +6 -0
- package/harness/skills/api-design/SKILL.md +523 -0
- package/harness/skills/backend-patterns/SKILL.md +598 -0
- package/harness/skills/coding-standards/SKILL.md +549 -0
- package/harness/skills/e2e-testing/SKILL.md +326 -0
- package/harness/skills/frontend-patterns/SKILL.md +642 -0
- package/harness/skills/frontend-slides/SKILL.md +184 -0
- package/harness/skills/security-review/SKILL.md +495 -0
- package/harness/skills/strategic-compact/SKILL.md +131 -0
- package/harness/skills/tdd-workflow/SKILL.md +463 -0
- package/harness/skills/verification-loop/SKILL.md +126 -0
- package/index.mjs +5 -0
- package/lib/hardening.mjs +113 -0
- package/lib/memory-tools-plugin.mjs +265 -0
- package/lib/schema-validator.mjs +77 -0
- package/lib/tools/_memory.mjs +230 -0
- package/lib/tools/hm_get.mjs +13 -0
- package/lib/tools/hm_latest.mjs +12 -0
- package/lib/tools/hm_list.mjs +13 -0
- package/lib/tools/hm_put.mjs +14 -0
- package/lib/tools/hm_search.mjs +16 -0
- package/package.json +49 -0
- package/schemas/audit.schema.json +61 -0
- package/schemas/backlog.schema.json +42 -0
- package/schemas/checkpoint.schema.json +44 -0
- package/schemas/constraint.schema.json +41 -0
- package/schemas/decision.schema.json +42 -0
- package/schemas/instinct.schema.json +42 -0
- package/schemas/loop-state.schema.json +33 -0
- package/schemas/mistake.schema.json +43 -0
- package/schemas/verification_receipt.schema.json +67 -0
- package/skill-builder.mjs +113 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Instinct",
|
|
4
|
+
"description": "Reusable proven patterns or heuristics with success/failure tracking.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["id", "class", "summary", "trigger", "action", "success_count", "failure_count", "promotion_state", "provenance", "created_at", "status"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"id": { "type": "string", "description": "Unique identifier, recommended pattern: instinct-YYYYMMDD-short-slug" },
|
|
9
|
+
"class": { "type": "string", "const": "instinct" },
|
|
10
|
+
"project": { "type": ["string", "null"], "description": "Project identifier; null for global/openhermes/session scope" },
|
|
11
|
+
"scope": { "type": "string", "enum": ["project", "global", "session", "harness"], "description": "Scope of applicability" },
|
|
12
|
+
"summary": { "type": "string", "description": "One-line description of the instinct" },
|
|
13
|
+
"tags": { "type": "array", "items": { "type": "string" }, "description": "Searchable tags" },
|
|
14
|
+
"source": { "type": "string", "enum": ["user", "agent", "audit", "migration", "repair"], "description": "Origin of this record" },
|
|
15
|
+
"provenance": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["session_id"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"session_id": { "type": "string" },
|
|
20
|
+
"task_id": { "type": "string" },
|
|
21
|
+
"db_refs": { "type": "array", "items": { "type": "string" } },
|
|
22
|
+
"file_refs": { "type": "array", "items": { "type": "string" } },
|
|
23
|
+
"log_refs": { "type": "array", "items": { "type": "string" } }
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"created_at": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp" },
|
|
27
|
+
"updated_at": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp" },
|
|
28
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.5 },
|
|
29
|
+
"signal": { "type": "string", "enum": ["low", "medium", "high", "critical"] },
|
|
30
|
+
"visibility": { "type": "string", "enum": ["implicit", "selective", "explicit"] },
|
|
31
|
+
"status": { "type": "string", "enum": ["active", "superseded", "archived", "closed", "open"] },
|
|
32
|
+
"refs": { "type": "array", "items": { "type": "string" }, "description": "Related object IDs or file paths" },
|
|
33
|
+
"review_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 or null" },
|
|
34
|
+
"decay_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 or null" },
|
|
35
|
+
"archived_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 or null" },
|
|
36
|
+
"trigger": { "type": "string", "description": "Condition or pattern that triggers this instinct" },
|
|
37
|
+
"action": { "type": "string", "description": "Recommended action when triggered" },
|
|
38
|
+
"success_count": { "type": "integer", "minimum": 0, "default": 0, "description": "Number of successful applications" },
|
|
39
|
+
"failure_count": { "type": "integer", "minimum": 0, "default": 0, "description": "Number of failed applications" },
|
|
40
|
+
"promotion_state": { "type": "string", "enum": ["project", "candidate_global", "global"], "description": "Current promotion level" }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Loop State",
|
|
4
|
+
"description": "Runtime loop state for curator session tracking and heartbeat. Written by curator.js plugin.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["status", "updated_at", "phase", "heartbeat_at"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"status": { "type": "string", "enum": ["idle", "active", "error", "compacted"], "description": "Current session status" },
|
|
9
|
+
"last_gate_result": { "type": ["string", "null"], "description": "Result of last gate check, if any" },
|
|
10
|
+
"last_error": { "type": ["string", "null"], "description": "Last error message" },
|
|
11
|
+
"updated_at": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp of last update" },
|
|
12
|
+
"last_checkpoint_id": { "type": "string", "description": "ID of the most recent checkpoint" },
|
|
13
|
+
"phase": { "type": "string", "description": "Current lifecycle phase (session.idle, session.compacted, compress, etc.)" },
|
|
14
|
+
"heartbeat_at": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp of last heartbeat" },
|
|
15
|
+
"budget": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"description": "Token or execution budget tracking",
|
|
18
|
+
"properties": {
|
|
19
|
+
"remaining": { "type": "number" },
|
|
20
|
+
"total": { "type": "number" },
|
|
21
|
+
"reset_at": { "type": "string", "format": "date-time" }
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"rollback": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"description": "Rollback tracking",
|
|
27
|
+
"properties": {
|
|
28
|
+
"level": { "type": "integer" },
|
|
29
|
+
"last_rollback_at": { "type": "string", "format": "date-time" }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Mistake",
|
|
4
|
+
"description": "Failure memory with root cause, fix, and prevention rule. Stored as JSONL (one object per line).",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["id", "class", "summary", "type", "failure", "root_cause", "fix", "prevention", "strike", "provenance", "created_at", "status"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"id": { "type": "string", "description": "Unique identifier, recommended pattern: mistake-YYYYMMDD-short-slug" },
|
|
9
|
+
"class": { "type": "string", "const": "mistake" },
|
|
10
|
+
"project": { "type": ["string", "null"], "description": "Project identifier; null for global/openhermes scope" },
|
|
11
|
+
"scope": { "type": "string", "enum": ["project", "global", "harness"], "description": "Scope of applicability" },
|
|
12
|
+
"summary": { "type": "string", "description": "One-line description of the mistake" },
|
|
13
|
+
"tags": { "type": "array", "items": { "type": "string" }, "description": "Searchable tags" },
|
|
14
|
+
"source": { "type": "string", "enum": ["user", "agent", "audit", "migration", "repair"], "description": "Origin of this record" },
|
|
15
|
+
"type": { "type": "string", "enum": ["build", "logic", "config", "security", "tool", "scope", "other"], "description": "Category of mistake" },
|
|
16
|
+
"failure": { "type": "string", "description": "What happened — the observable failure" },
|
|
17
|
+
"root_cause": { "type": "string", "description": "Why it happened — the underlying cause" },
|
|
18
|
+
"fix": { "type": "string", "description": "How it was resolved" },
|
|
19
|
+
"prevention": { "type": "string", "description": "Rule or guard to prevent recurrence" },
|
|
20
|
+
"strike": { "type": "integer", "minimum": 1, "description": "Strike count for this mistake type" },
|
|
21
|
+
"provenance": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"required": ["session_id"],
|
|
24
|
+
"properties": {
|
|
25
|
+
"session_id": { "type": "string" },
|
|
26
|
+
"task_id": { "type": "string" },
|
|
27
|
+
"db_refs": { "type": "array", "items": { "type": "string" } },
|
|
28
|
+
"file_refs": { "type": "array", "items": { "type": "string" } },
|
|
29
|
+
"log_refs": { "type": "array", "items": { "type": "string" } }
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1, "default": 1.0 },
|
|
33
|
+
"signal": { "type": "string", "enum": ["low", "medium", "high", "critical"] },
|
|
34
|
+
"visibility": { "type": "string", "enum": ["implicit", "selective", "explicit"] },
|
|
35
|
+
"status": { "type": "string", "enum": ["active", "superseded", "archived", "closed", "open"] },
|
|
36
|
+
"refs": { "type": "array", "items": { "type": "string" }, "description": "Related object IDs or file paths" },
|
|
37
|
+
"created_at": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp" },
|
|
38
|
+
"updated_at": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp" },
|
|
39
|
+
"review_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 or null" },
|
|
40
|
+
"decay_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 or null" },
|
|
41
|
+
"archived_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 or null" }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Verification Receipt",
|
|
4
|
+
"description": "Cached verification result keyed by artifact identity + fingerprint. Stored in memory\\verification_receipts\\.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["id", "class", "artifact", "artifact_type", "fingerprint", "method", "result", "provenance", "created_at", "status"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"id": { "type": "string", "description": "Unique identifier, recommended pattern: v:<artifact-path-or-logical-id>" },
|
|
9
|
+
"class": { "type": "string", "const": "verification_receipt" },
|
|
10
|
+
"project": { "type": ["string", "null"], "description": "Project identifier; null for global/openhermes scope" },
|
|
11
|
+
"scope": { "type": "string", "enum": ["project", "global", "session", "harness"], "description": "Scope of applicability" },
|
|
12
|
+
"summary": { "type": "string", "description": "One-line description of what was verified" },
|
|
13
|
+
"tags": { "type": "array", "items": { "type": "string" }, "description": "Searchable tags" },
|
|
14
|
+
"source": { "type": "string", "enum": ["user", "agent", "audit", "migration", "repair"], "description": "Origin of this record" },
|
|
15
|
+
"provenance": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["session_id"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"session_id": { "type": "string" },
|
|
20
|
+
"task_id": { "type": "string" },
|
|
21
|
+
"db_refs": { "type": "array", "items": { "type": "string" } },
|
|
22
|
+
"file_refs": { "type": "array", "items": { "type": "string" } },
|
|
23
|
+
"log_refs": { "type": "array", "items": { "type": "string" } }
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"created_at": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp" },
|
|
27
|
+
"updated_at": { "type": "string", "format": "date-time", "description": "ISO-8601 timestamp" },
|
|
28
|
+
"expires_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 — receipt invalid after this time regardless of fingerprint" },
|
|
29
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1, "default": 1.0 },
|
|
30
|
+
"signal": { "type": "string", "enum": ["low", "medium", "high", "critical"] },
|
|
31
|
+
"visibility": { "type": "string", "enum": ["implicit", "selective", "explicit"] },
|
|
32
|
+
"status": { "type": "string", "enum": ["active", "superseded", "archived", "closed", "open", "expired"] },
|
|
33
|
+
"refs": { "type": "array", "items": { "type": "string" }, "description": "Related object IDs or file paths" },
|
|
34
|
+
"review_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 or null" },
|
|
35
|
+
"decay_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 or null" },
|
|
36
|
+
"archived_at": { "type": ["string", "null"], "format": "date-time", "description": "ISO-8601 or null" },
|
|
37
|
+
"artifact": { "type": "string", "description": "Full path or logical identity of the verified artifact" },
|
|
38
|
+
"artifact_type": { "type": "string", "enum": ["file", "command", "config", "dependency", "memory", "state", "other"], "description": "Type of artifact verified" },
|
|
39
|
+
"fingerprint": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"required": ["path"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"path": { "type": "string", "description": "Normalized file path or logical ID" },
|
|
44
|
+
"mtime": { "type": "string", "description": "File modification time (ISO-8601)" },
|
|
45
|
+
"size": { "type": "integer", "description": "File size in bytes" },
|
|
46
|
+
"sha256": { "type": "string", "description": "SHA-256 hash (optional but preferred)" }
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"environment": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"properties": {
|
|
52
|
+
"cwd": { "type": "string" },
|
|
53
|
+
"os": { "type": "string" },
|
|
54
|
+
"shell": { "type": "string", "default": "cmd.exe" },
|
|
55
|
+
"provider": { "type": "string" },
|
|
56
|
+
"model": { "type": "string" }
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"method": { "type": "string", "enum": ["command", "read", "test", "schema-validate", "manual-inspection", "bash", "grep", "live-hook-run", "permission-regression", "end-to-end-run", "external-test", "runtime-proof"], "description": "Verification method used. runtime-proof = linked to runtime_proof record for executable evidence." },
|
|
60
|
+
"command": { "type": "string", "description": "The command or action executed to verify" },
|
|
61
|
+
"result": { "type": "string", "enum": ["pass", "fail", "unknown"], "description": "Verification outcome" },
|
|
62
|
+
"result_detail": { "type": "string", "description": "Detailed result description or output summary" },
|
|
63
|
+
"supersedes": { "type": "array", "items": { "type": "string" }, "description": "Receipt IDs this receipt supersedes" },
|
|
64
|
+
"superseded_by": { "type": "array", "items": { "type": "string" }, "description": "Receipt IDs that supersede this one" },
|
|
65
|
+
"invalidation_reason": { "type": "string", "description": "If status is expired/superseded, why the receipt was invalidated" }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import path from "node:path"
|
|
2
|
+
import fs from "node:fs"
|
|
3
|
+
import os from "node:os"
|
|
4
|
+
import { atomicWriteJson, fingerprintEnvironment, isTruthy, sanitizeRecord } from "./lib/hardening.mjs"
|
|
5
|
+
|
|
6
|
+
function getHarnessRoot(directory) {
|
|
7
|
+
const home = process.env.USERPROFILE || os.homedir()
|
|
8
|
+
const configRoot = path.join(home, ".config", "opencode")
|
|
9
|
+
const projectHarness = path.join(directory, ".opencode", "openhermes")
|
|
10
|
+
const projectMemory = path.join(projectHarness, "memory")
|
|
11
|
+
if (isTruthy(process.env.OPENCODE_ALLOW_PROJECT_HARNESS)) {
|
|
12
|
+
try { fs.accessSync(projectMemory); return projectHarness } catch {}
|
|
13
|
+
}
|
|
14
|
+
return path.join(configRoot, "openhermes")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function readJson(fp, fallback) {
|
|
18
|
+
try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return fallback }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildEnvironmentFingerprint(root, directory, project) {
|
|
22
|
+
return fingerprintEnvironment({
|
|
23
|
+
cwd: directory,
|
|
24
|
+
harnessRoot: root,
|
|
25
|
+
projectRoot: directory,
|
|
26
|
+
project: project?.name || path.basename(directory),
|
|
27
|
+
sessionId: project?.session_id || null,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const COMPLEXITY_THRESHOLD = { toolCalls: 8, subagents: 2 }
|
|
32
|
+
let sessionStats = { toolCalls: 0, subagents: 0, startTime: Date.now() }
|
|
33
|
+
|
|
34
|
+
export const SkillBuilderPlugin = async ({ project, directory }) => {
|
|
35
|
+
return {
|
|
36
|
+
"tool.execute.after": async (input, output) => {
|
|
37
|
+
sessionStats.toolCalls++
|
|
38
|
+
if (input.tool === "task") sessionStats.subagents++
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
event: async ({ event }) => {
|
|
42
|
+
if (event.type === "session.created") {
|
|
43
|
+
sessionStats = { toolCalls: 0, subagents: 0, startTime: Date.now() }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (event.type === "session.idle") {
|
|
47
|
+
const durationMin = Math.round((Date.now() - sessionStats.startTime) / 60000)
|
|
48
|
+
const isComplex = sessionStats.toolCalls >= COMPLEXITY_THRESHOLD.toolCalls
|
|
49
|
+
|| sessionStats.subagents >= COMPLEXITY_THRESHOLD.subagents
|
|
50
|
+
|
|
51
|
+
if (isComplex) {
|
|
52
|
+
try {
|
|
53
|
+
const root = getHarnessRoot(directory)
|
|
54
|
+
const ts = new Date().toISOString()
|
|
55
|
+
const id = `bl_skill_candidate_${ts.replace(/[:.]/g, "-")}`
|
|
56
|
+
const backlogIndexPath = path.join(root, "memory", "backlog", "index.json")
|
|
57
|
+
const backlogIndex = readJson(backlogIndexPath, [])
|
|
58
|
+
const hasOpenCandidate = Array.isArray(backlogIndex)
|
|
59
|
+
? backlogIndex.some(e => e.status === "open" && String(e.summary || "").includes("[skill-candidate]"))
|
|
60
|
+
: false
|
|
61
|
+
if (hasOpenCandidate) {
|
|
62
|
+
sessionStats = { toolCalls: 0, subagents: 0, startTime: Date.now() }
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
const environmentFingerprint = buildEnvironmentFingerprint(root, directory, project)
|
|
66
|
+
const record = {
|
|
67
|
+
id,
|
|
68
|
+
class: "backlog",
|
|
69
|
+
scope: "global",
|
|
70
|
+
summary: `[skill-candidate] Complex session: ${sessionStats.toolCalls} tool calls, ${sessionStats.subagents} subagents, ${durationMin}min`,
|
|
71
|
+
description: `Session exceeded complexity thresholds (toolCalls>=${COMPLEXITY_THRESHOLD.toolCalls} or subagents>=${COMPLEXITY_THRESHOLD.subagents}). Session had ${sessionStats.toolCalls} tool calls and ${sessionStats.subagents} subagent spawns over ${durationMin} minutes.`,
|
|
72
|
+
title: `Skill candidate: Complex session (${sessionStats.toolCalls} tool calls${sessionStats.subagents ? `, ${sessionStats.subagents} subagents`:""})`,
|
|
73
|
+
priority: "medium",
|
|
74
|
+
trigger: "drift",
|
|
75
|
+
status: "open",
|
|
76
|
+
provenance: {
|
|
77
|
+
session_id: project?.session_id || `session-${Date.now()}`,
|
|
78
|
+
harness_root: root,
|
|
79
|
+
project_root: directory,
|
|
80
|
+
},
|
|
81
|
+
tags: ["skill-candidate", "auto-detected"],
|
|
82
|
+
source: "agent",
|
|
83
|
+
created_at: ts,
|
|
84
|
+
updated_at: ts,
|
|
85
|
+
project: project?.name || path.basename(directory),
|
|
86
|
+
environment_fingerprint: environmentFingerprint,
|
|
87
|
+
}
|
|
88
|
+
const dir = path.join(root, "memory", "backlog")
|
|
89
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
90
|
+
const safeRecord = sanitizeRecord(record, { maxStringLength: 4000 })
|
|
91
|
+
atomicWriteJson(path.join(dir, `${id}.json`), safeRecord)
|
|
92
|
+
|
|
93
|
+
const index = Array.isArray(backlogIndex) ? backlogIndex : []
|
|
94
|
+
index.push({
|
|
95
|
+
id,
|
|
96
|
+
summary: safeRecord.summary,
|
|
97
|
+
title: safeRecord.title,
|
|
98
|
+
status: "open",
|
|
99
|
+
updated_at: ts,
|
|
100
|
+
path: `openhermes/memory/backlog/${id}.json`,
|
|
101
|
+
priority: "medium",
|
|
102
|
+
trigger: "drift",
|
|
103
|
+
environment_fingerprint: environmentFingerprint,
|
|
104
|
+
})
|
|
105
|
+
atomicWriteJson(path.join(dir, "index.json"), index)
|
|
106
|
+
|
|
107
|
+
} catch (err) {}
|
|
108
|
+
}
|
|
109
|
+
sessionStats = { toolCalls: 0, subagents: 0, startTime: Date.now() }
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
}
|