@event4u/agent-config 9.16.0 → 9.18.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/.claude-plugin/marketplace.json +1 -1
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +80 -130
- package/README.md +1 -1
- package/dist/agent-src/commands/agent-handoff.md +100 -48
- package/dist/agent-src/commands/challenge-me/vision.md +5 -0
- package/dist/agent-src/commands/commit.md +1 -1
- package/dist/agent-src/commands/condense.md +19 -64
- package/dist/agent-src/commands/evals/agent-handoff.json +13 -1
- package/dist/agent-src/commands/evals/rule-compliance-audit.json +46 -0
- package/dist/agent-src/commands/feature/plan.md +20 -0
- package/dist/agent-src/commands/feature/roadmap.md +19 -0
- package/dist/agent-src/commands/memory/promote.md +14 -1
- package/dist/agent-src/commands/mode.md +9 -5
- package/dist/agent-src/commands/optimize/deep.md +72 -11
- package/dist/agent-src/commands/pr/create.md +29 -0
- package/dist/agent-src/commands/roadmap/create.md +29 -0
- package/dist/agent-src/commands/rule-compliance-audit.md +28 -5
- package/dist/agent-src/contexts/authority/commit-mechanics.md +5 -5
- package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
- package/dist/agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +6 -1
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +4 -1
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +27 -1
- package/dist/agent-src/contexts/execution/plan-confidence-gate.md +133 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +5 -2
- package/dist/agent-src/profiles/agency.yml +3 -3
- package/dist/agent-src/profiles/content_creator.yml +2 -2
- package/dist/agent-src/profiles/developer.yml +2 -2
- package/dist/agent-src/profiles/finance.yml +1 -1
- package/dist/agent-src/profiles/founder.yml +2 -2
- package/dist/agent-src/profiles/ops.yml +2 -2
- package/dist/agent-src/rules/active-remediation.md +2 -0
- package/dist/agent-src/rules/architecture.md +4 -2
- package/dist/agent-src/rules/augment-edit-discipline.md +2 -0
- package/dist/agent-src/rules/autonomous-execution.md +1 -1
- package/dist/agent-src/rules/brand-consistency.md +10 -62
- package/dist/agent-src/rules/brand-source-of-truth.md +62 -11
- package/dist/agent-src/rules/broken-access-control.md +4 -0
- package/dist/agent-src/rules/cli-output-handling.md +5 -0
- package/dist/agent-src/rules/code-comment-discipline.md +3 -0
- package/dist/agent-src/rules/commit-conventions.md +2 -0
- package/dist/agent-src/rules/context-hygiene.md +3 -2
- package/dist/agent-src/rules/cross-source-consistency.md +3 -0
- package/dist/agent-src/rules/design-fidelity.md +2 -0
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +2 -0
- package/dist/agent-src/rules/docker-commands.md +4 -0
- package/dist/agent-src/rules/domain-adoption-policy.md +2 -0
- package/dist/agent-src/rules/domain-safety-disclaimer.md +5 -2
- package/dist/agent-src/rules/domain-safety-pii.md +1 -1
- package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +9 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +4 -0
- package/dist/agent-src/rules/improve-before-implement.md +4 -0
- package/dist/agent-src/rules/laravel-routing.md +4 -0
- package/dist/agent-src/rules/legal-safety-floor.md +3 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +3 -0
- package/dist/agent-src/rules/media-governance-routing.md +5 -0
- package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
- package/dist/agent-src/rules/no-roadmap-references.md +2 -0
- package/dist/agent-src/rules/notes-first-reasoning.md +2 -0
- package/dist/agent-src/rules/output-discipline.md +9 -2
- package/dist/agent-src/rules/php-coding.md +2 -0
- package/dist/agent-src/rules/prefer-enums-over-literals.md +3 -0
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +5 -0
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +2 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +2 -0
- package/dist/agent-src/rules/rule-type-governance.md +2 -0
- package/dist/agent-src/rules/scale-discipline.md +3 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +9 -2
- package/dist/agent-src/rules/senior-engineering-discipline.md +6 -0
- package/dist/agent-src/rules/skill-quality.md +2 -0
- package/dist/agent-src/rules/source-confidentiality.md +4 -0
- package/dist/agent-src/rules/source-discovery-gate.md +3 -0
- package/dist/agent-src/rules/source-of-truth.md +2 -0
- package/dist/agent-src/rules/strategy-safety-floor.md +2 -0
- package/dist/agent-src/rules/think-before-action.md +5 -0
- package/dist/agent-src/rules/token-budget-discipline.md +2 -2
- package/dist/agent-src/rules/ui-audit-gate.md +2 -0
- package/dist/agent-src/rules/user-interaction.md +3 -4
- package/dist/agent-src/skills/build-buy-partner/SKILL.md +1 -1
- package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +4 -4
- package/dist/agent-src/skills/copilot-config/SKILL.md +1 -1
- package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +7 -4
- package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
- package/dist/agent-src/skills/design-intelligence/SKILL.md +6 -180
- package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +100 -0
- package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +91 -0
- package/dist/agent-src/skills/design-review/SKILL.md +4 -141
- package/dist/agent-src/skills/design-review/references/review-communication.md +92 -0
- package/dist/agent-src/skills/design-review/references/verification-automation.md +61 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +8 -27
- package/dist/agent-src/skills/existing-ui-audit/references/anti-slop-cross-reference.md +31 -0
- package/dist/agent-src/skills/fe-design/SKILL.md +4 -289
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +227 -0
- package/dist/agent-src/skills/fe-design/references/design-read-and-memory.md +75 -0
- package/dist/agent-src/skills/market-entry-analysis/SKILL.md +1 -1
- package/dist/agent-src/skills/persona-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/readme-reviewer/SKILL.md +2 -1
- package/dist/agent-src/skills/roadmap-management/SKILL.md +10 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +12 -1
- package/dist/agent-src/skills/rule-writing/SKILL.md +4 -2
- package/dist/agent-src/skills/skill-reviewer/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +3 -1
- package/dist/agent-src/skills/token-optimizer/SKILL.md +2 -2
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +6 -3
- package/dist/agent-src/templates/agent-settings.md +13 -15
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +6 -2
- package/dist/agent-src/templates/persona.md +2 -2
- package/dist/agent-src/templates/roadmaps.md +13 -1
- package/dist/agent-src/templates/rule.md +1 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
- package/dist/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/registry.js +3 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-AIIUU73N.js +2040 -0
- package/dist/cli-delegate/{chunk-ZXXJLL5K.js → chunk-BSLUHV2F.js} +127 -1487
- package/dist/cli-delegate/{chunk-H574KOUH.js → chunk-ELMFOQPW.js} +35 -1
- package/dist/cli-delegate/chunk-LF2KKJ4Q.js +183 -0
- package/dist/cli-delegate/chunk-VSYT7OHO.js +1382 -0
- package/dist/cli-delegate/chunk-XGA2NTU4.js +3727 -0
- package/dist/cli-delegate/cmd_conformance.js +4 -3
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +3 -2
- package/dist/cli-delegate/cmd_explain.js +36 -36
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_handoff.js +1169 -0
- package/dist/cli-delegate/cmd_memory_get.js +35 -3754
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_route_audit.js +249 -0
- package/dist/cli-delegate/cmd_route_explain.js +285 -0
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_update.js +3 -3
- package/dist/cli-delegate/cmd_upgrade.js +3 -3
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/cli-delegate/cmd_versions.js +3 -3
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +84 -84
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +602 -507
- package/dist/install/install.mjs +34 -0
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
- package/dist/mcp/registry-manifest.json +4 -4
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +25 -3
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_src.js +39 -6
- package/dist/scripts/_lib/agent_src.js.map +1 -1
- package/dist/server/schemas/settings.js +25 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/guidelines/abstraction-thresholds.md +53 -0
- package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +1 -1
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +7 -0
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +2 -2
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +5 -3
- package/docs/guidelines/agent-infra/size-and-scope.md +8 -2
- package/docs/guidelines/component-oriented-and-oop-development.md +11 -6
- package/docs/guidelines/docs/readme-size-and-splitting.md +5 -2
- package/llms.txt +1 -0
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
- package/src/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
- package/src/config/agent-settings.template.yml +68 -0
- package/src/config/evaluator-budgets.json +8 -11
- package/src/config/gate-coverage.yml +70 -0
- package/src/scripts/_cli/cmd_explain.ts +41 -36
- package/src/scripts/_cli/cmd_handoff.ts +405 -0
- package/src/scripts/_cli/cmd_route_audit.ts +290 -0
- package/src/scripts/_cli/cmd_route_explain.ts +269 -0
- package/src/scripts/_cli/handoff_generate.ts +453 -0
- package/src/scripts/_cli/handoff_sessions.ts +435 -0
- package/src/scripts/_dispatch.bash +32 -0
- package/src/scripts/_lib/agent_src.ts +36 -6
- package/src/scripts/_lib/git_env.ts +40 -0
- package/src/scripts/_lib/md_table.ts +27 -0
- package/src/scripts/_lib/planning_settings.ts +43 -0
- package/src/scripts/_lib/release_material.ts +156 -0
- package/src/scripts/_lib/router_match.ts +301 -0
- package/src/scripts/annotate_r1_outcomes.ts +258 -0
- package/src/scripts/check_completion_review.ts +1140 -0
- package/src/scripts/check_condensation.ts +2 -1
- package/src/scripts/check_finding_dispositions.ts +278 -0
- package/src/scripts/check_pack_size.ts +4 -3
- package/src/scripts/check_references.ts +1 -0
- package/src/scripts/check_release_highlights.ts +300 -0
- package/src/scripts/check_release_pr_shape.ts +4 -0
- package/src/scripts/check_release_surface_equality.ts +132 -0
- package/src/scripts/check_reply_consistency.ts +187 -138
- package/src/scripts/compile_router.ts +20 -3
- package/src/scripts/dispatch_r2_reviewer.ts +968 -0
- package/src/scripts/eval_ui_triviality.ts +156 -0
- package/src/scripts/handoff_context_hook.ts +148 -0
- package/src/scripts/hook_manifest.yaml +17 -7
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/lint_abstraction_thresholds.ts +327 -0
- package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
- package/src/scripts/lint_plan_risk_register.ts +931 -0
- package/src/scripts/lint_profile_personas.ts +153 -0
- package/src/scripts/lint_token_budget_discipline.ts +148 -0
- package/src/scripts/lint_trigger_collisions.ts +257 -0
- package/src/scripts/release.ts +156 -32
- package/src/scripts/release_drill.ts +491 -0
- package/src/scripts/router_telemetry.ts +16 -290
- package/src/scripts/schemas/command.schema.json +33 -0
- package/src/scripts/schemas/rule.schema.json +10 -0
- package/src/scripts/self_review_gate.ts +41 -3
- package/src/scripts/skill_linter.ts +3 -1
- package/src/scripts/smoke/router.sh +1 -1
- package/src/scripts/update_counts.ts +60 -12
- package/src/server/schemas/settings.ts +41 -0
- package/dist/cli-delegate/{chunk-XX6TOPD7.js → chunk-WR6GTRHJ.js} +3 -3
|
@@ -0,0 +1,2040 @@
|
|
|
1
|
+
import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
load_agent_settings
|
|
4
|
+
} from "./chunk-HJ6DAJHR.js";
|
|
5
|
+
|
|
6
|
+
// src/scripts/chat_history.ts
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import * as fs2 from "node:fs";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
11
|
+
|
|
12
|
+
// src/scripts/hooks/hook_stdin.ts
|
|
13
|
+
import * as fs from "node:fs";
|
|
14
|
+
var _override = null;
|
|
15
|
+
function readHookStdin() {
|
|
16
|
+
if (_override !== null) return _override;
|
|
17
|
+
try {
|
|
18
|
+
if (process.stdin.isTTY) return "";
|
|
19
|
+
return fs.readFileSync(0, "utf-8");
|
|
20
|
+
} catch {
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/scripts/chat_history.ts
|
|
26
|
+
var DEFAULT_FILE = "agents/runtime/.agent-chat-history";
|
|
27
|
+
var DEFAULT_SETTINGS_FILE = ".agent-settings.yml";
|
|
28
|
+
var SCHEMA_VERSION = 4;
|
|
29
|
+
var DEFAULT_MAX_SESSIONS = 5;
|
|
30
|
+
var VALID_FREQS = /* @__PURE__ */ new Set(["per_turn", "per_phase", "per_tool"]);
|
|
31
|
+
var VALID_OVERFLOW = /* @__PURE__ */ new Set(["rotate", "condense"]);
|
|
32
|
+
var REPLAY_ENV_VAR = "AGENT_CONFIG_REPLAY";
|
|
33
|
+
function _is_replay_mode() {
|
|
34
|
+
return (process.env[REPLAY_ENV_VAR] ?? "").trim() === "1";
|
|
35
|
+
}
|
|
36
|
+
var _WS_RE = /\s+/g;
|
|
37
|
+
var SESSION_ID_LEN = 16;
|
|
38
|
+
var SESSION_ID_UNKNOWN = "<unknown>";
|
|
39
|
+
var SESSION_ID_LEGACY = "<legacy>";
|
|
40
|
+
var AGENT_UNKNOWN = "<unknown>";
|
|
41
|
+
var DEFAULT_TEXT_LIMITS = {
|
|
42
|
+
user: 0,
|
|
43
|
+
agent: 5e3,
|
|
44
|
+
tool: 200,
|
|
45
|
+
phase: 200
|
|
46
|
+
};
|
|
47
|
+
var EXIT_OK = 0;
|
|
48
|
+
var EXIT_BAD_ARGS = 2;
|
|
49
|
+
var PyFloat = class {
|
|
50
|
+
constructor(value) {
|
|
51
|
+
this.value = value;
|
|
52
|
+
}
|
|
53
|
+
value;
|
|
54
|
+
};
|
|
55
|
+
function _isPlainObject(v) {
|
|
56
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyFloat);
|
|
57
|
+
}
|
|
58
|
+
function _pyJsonStr(s) {
|
|
59
|
+
let out = '"';
|
|
60
|
+
for (const ch of s) {
|
|
61
|
+
const code = ch.codePointAt(0);
|
|
62
|
+
switch (ch) {
|
|
63
|
+
case '"':
|
|
64
|
+
out += '\\"';
|
|
65
|
+
break;
|
|
66
|
+
case "\\":
|
|
67
|
+
out += "\\\\";
|
|
68
|
+
break;
|
|
69
|
+
case "\n":
|
|
70
|
+
out += "\\n";
|
|
71
|
+
break;
|
|
72
|
+
case "\r":
|
|
73
|
+
out += "\\r";
|
|
74
|
+
break;
|
|
75
|
+
case " ":
|
|
76
|
+
out += "\\t";
|
|
77
|
+
break;
|
|
78
|
+
case "\b":
|
|
79
|
+
out += "\\b";
|
|
80
|
+
break;
|
|
81
|
+
case "\f":
|
|
82
|
+
out += "\\f";
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
if (code < 32) {
|
|
86
|
+
out += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
87
|
+
} else {
|
|
88
|
+
out += ch;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return out + '"';
|
|
93
|
+
}
|
|
94
|
+
function _pyJsonNum(n) {
|
|
95
|
+
if (!Number.isFinite(n)) {
|
|
96
|
+
if (Number.isNaN(n)) {
|
|
97
|
+
return "NaN";
|
|
98
|
+
}
|
|
99
|
+
return n > 0 ? "Infinity" : "-Infinity";
|
|
100
|
+
}
|
|
101
|
+
return String(n);
|
|
102
|
+
}
|
|
103
|
+
function _pyJsonFloat(n) {
|
|
104
|
+
if (!Number.isFinite(n)) {
|
|
105
|
+
if (Number.isNaN(n)) {
|
|
106
|
+
return "NaN";
|
|
107
|
+
}
|
|
108
|
+
return n > 0 ? "Infinity" : "-Infinity";
|
|
109
|
+
}
|
|
110
|
+
return Number.isInteger(n) ? `${n}.0` : String(n);
|
|
111
|
+
}
|
|
112
|
+
function _pyJsonScalar(value) {
|
|
113
|
+
if (value === null || value === void 0) {
|
|
114
|
+
return "null";
|
|
115
|
+
}
|
|
116
|
+
if (typeof value === "boolean") {
|
|
117
|
+
return value ? "true" : "false";
|
|
118
|
+
}
|
|
119
|
+
if (value instanceof PyFloat) {
|
|
120
|
+
return _pyJsonFloat(value.value);
|
|
121
|
+
}
|
|
122
|
+
if (typeof value === "number") {
|
|
123
|
+
return _pyJsonNum(value);
|
|
124
|
+
}
|
|
125
|
+
if (typeof value === "string") {
|
|
126
|
+
return _pyJsonStr(value);
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
function _pyDumps(value) {
|
|
131
|
+
const scalar = _pyJsonScalar(value);
|
|
132
|
+
if (scalar !== null) {
|
|
133
|
+
return scalar;
|
|
134
|
+
}
|
|
135
|
+
if (Array.isArray(value)) {
|
|
136
|
+
return `[${value.map((v) => _pyDumps(v)).join(", ")}]`;
|
|
137
|
+
}
|
|
138
|
+
if (_isPlainObject(value)) {
|
|
139
|
+
const keys = Object.keys(value);
|
|
140
|
+
const parts = keys.map((k) => `${_pyJsonStr(k)}: ${_pyDumps(value[k])}`);
|
|
141
|
+
return `{${parts.join(", ")}}`;
|
|
142
|
+
}
|
|
143
|
+
return _pyJsonStr(String(value));
|
|
144
|
+
}
|
|
145
|
+
function _pyDumpsIndent2(value, depth = 0) {
|
|
146
|
+
const scalar = _pyJsonScalar(value);
|
|
147
|
+
if (scalar !== null) {
|
|
148
|
+
return scalar;
|
|
149
|
+
}
|
|
150
|
+
const pad = " ".repeat(2 * (depth + 1));
|
|
151
|
+
const closePad = " ".repeat(2 * depth);
|
|
152
|
+
if (Array.isArray(value)) {
|
|
153
|
+
if (value.length === 0) {
|
|
154
|
+
return "[]";
|
|
155
|
+
}
|
|
156
|
+
const items = value.map((v) => pad + _pyDumpsIndent2(v, depth + 1));
|
|
157
|
+
return `[
|
|
158
|
+
${items.join(",\n")}
|
|
159
|
+
${closePad}]`;
|
|
160
|
+
}
|
|
161
|
+
if (_isPlainObject(value)) {
|
|
162
|
+
const keys = Object.keys(value);
|
|
163
|
+
if (keys.length === 0) {
|
|
164
|
+
return "{}";
|
|
165
|
+
}
|
|
166
|
+
const items = keys.map(
|
|
167
|
+
(k) => `${pad}${_pyJsonStr(k)}: ${_pyDumpsIndent2(value[k], depth + 1)}`
|
|
168
|
+
);
|
|
169
|
+
return `{
|
|
170
|
+
${items.join(",\n")}
|
|
171
|
+
${closePad}}`;
|
|
172
|
+
}
|
|
173
|
+
return _pyJsonStr(String(value));
|
|
174
|
+
}
|
|
175
|
+
function _strip(s) {
|
|
176
|
+
return s.replace(/^\s+/u, "").replace(/\s+$/u, "");
|
|
177
|
+
}
|
|
178
|
+
function _pyLen(s) {
|
|
179
|
+
let n = 0;
|
|
180
|
+
for (const _ of s) {
|
|
181
|
+
n += 1;
|
|
182
|
+
}
|
|
183
|
+
return n;
|
|
184
|
+
}
|
|
185
|
+
function _pySliceTo(s, n) {
|
|
186
|
+
if (n <= 0) {
|
|
187
|
+
return "";
|
|
188
|
+
}
|
|
189
|
+
let out = "";
|
|
190
|
+
let i = 0;
|
|
191
|
+
for (const ch of s) {
|
|
192
|
+
if (i >= n) {
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
out += ch;
|
|
196
|
+
i += 1;
|
|
197
|
+
}
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
200
|
+
function _ljust(s, width) {
|
|
201
|
+
const pad = width - _pyLen(s);
|
|
202
|
+
return pad > 0 ? s + " ".repeat(pad) : s;
|
|
203
|
+
}
|
|
204
|
+
function _pyIntFromAny(value) {
|
|
205
|
+
if (typeof value === "number") {
|
|
206
|
+
return Math.trunc(value);
|
|
207
|
+
}
|
|
208
|
+
if (typeof value === "boolean") {
|
|
209
|
+
return value ? 1 : 0;
|
|
210
|
+
}
|
|
211
|
+
if (typeof value === "string") {
|
|
212
|
+
const parsed = Number.parseInt(value, 10);
|
|
213
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
214
|
+
}
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
function _pyRound(value, ndigits = 0) {
|
|
218
|
+
if (!Number.isFinite(value)) {
|
|
219
|
+
return value;
|
|
220
|
+
}
|
|
221
|
+
const factor = 10 ** ndigits;
|
|
222
|
+
const scaled = value * factor;
|
|
223
|
+
const floor = Math.floor(scaled);
|
|
224
|
+
const diff = scaled - floor;
|
|
225
|
+
let rounded;
|
|
226
|
+
const EPS = 1e-9;
|
|
227
|
+
if (Math.abs(diff - 0.5) < EPS) {
|
|
228
|
+
rounded = floor % 2 === 0 ? floor : floor + 1;
|
|
229
|
+
} else {
|
|
230
|
+
rounded = Math.round(scaled);
|
|
231
|
+
}
|
|
232
|
+
return rounded / factor;
|
|
233
|
+
}
|
|
234
|
+
function file_path() {
|
|
235
|
+
return process.env.AGENT_CHAT_HISTORY_FILE || DEFAULT_FILE;
|
|
236
|
+
}
|
|
237
|
+
function _now() {
|
|
238
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "+00:00");
|
|
239
|
+
}
|
|
240
|
+
function fingerprint(value) {
|
|
241
|
+
const normalized = _strip((value || "").replace(_WS_RE, " "));
|
|
242
|
+
return createHash("sha256").update(normalized, "utf-8").digest("hex");
|
|
243
|
+
}
|
|
244
|
+
function derive_session_tag(session_id) {
|
|
245
|
+
if (!session_id) {
|
|
246
|
+
return SESSION_ID_UNKNOWN;
|
|
247
|
+
}
|
|
248
|
+
return fingerprint(session_id).slice(0, SESSION_ID_LEN);
|
|
249
|
+
}
|
|
250
|
+
function _isFile(p) {
|
|
251
|
+
try {
|
|
252
|
+
return fs2.statSync(p).isFile();
|
|
253
|
+
} catch {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function _statSize(p) {
|
|
258
|
+
try {
|
|
259
|
+
return fs2.statSync(p).size;
|
|
260
|
+
} catch {
|
|
261
|
+
return -1;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function _preview(msg, n = 80) {
|
|
265
|
+
const flat = _strip((msg || "").replace(_WS_RE, " "));
|
|
266
|
+
return _pySliceTo(flat, n);
|
|
267
|
+
}
|
|
268
|
+
function _extract_text(obj) {
|
|
269
|
+
let text = obj.text;
|
|
270
|
+
if (typeof text !== "string" || !text) {
|
|
271
|
+
const payload = obj.payload;
|
|
272
|
+
if (_isPlainObject(payload)) {
|
|
273
|
+
text = payload.text;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return typeof text === "string" ? text : "";
|
|
277
|
+
}
|
|
278
|
+
function _summarize_session(head, tail, total, n = 60) {
|
|
279
|
+
const seen = /* @__PURE__ */ new Set();
|
|
280
|
+
const sample = [];
|
|
281
|
+
for (const e of [...head, ...tail]) {
|
|
282
|
+
if (seen.has(e)) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
seen.add(e);
|
|
286
|
+
sample.push(e);
|
|
287
|
+
}
|
|
288
|
+
const user_texts = sample.filter((e) => e.t === "user" && _extract_text(e)).map((e) => _extract_text(e));
|
|
289
|
+
if (user_texts.length > 0) {
|
|
290
|
+
const first = _preview(user_texts[0], n);
|
|
291
|
+
if (user_texts.length > 1 && user_texts[user_texts.length - 1] !== user_texts[0]) {
|
|
292
|
+
const last = _preview(user_texts[user_texts.length - 1], n);
|
|
293
|
+
return `${first} \u2192 ${last}`;
|
|
294
|
+
}
|
|
295
|
+
return first;
|
|
296
|
+
}
|
|
297
|
+
const counts = /* @__PURE__ */ new Map();
|
|
298
|
+
for (const e of sample) {
|
|
299
|
+
const k = typeof e.t === "string" ? e.t : "?";
|
|
300
|
+
counts.set(k, (counts.get(k) ?? 0) + 1);
|
|
301
|
+
}
|
|
302
|
+
const ordered = [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
303
|
+
const mix = ordered.map(([k, v]) => `${k}\xD7${v}`).join(" ");
|
|
304
|
+
return `(${total} entries \u2014 no user prompts; t-mix: ${mix})`;
|
|
305
|
+
}
|
|
306
|
+
function _session_tag_enabled() {
|
|
307
|
+
return (process.env.AGENT_CHAT_HISTORY_SESSION_TAG ?? "true").trim().toLowerCase() !== "false";
|
|
308
|
+
}
|
|
309
|
+
function _readLines(p) {
|
|
310
|
+
const raw = fs2.readFileSync(p, "utf-8");
|
|
311
|
+
if (raw === "") {
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
const parts = raw.split("\n");
|
|
315
|
+
return parts;
|
|
316
|
+
}
|
|
317
|
+
function _last_body_session_id(p) {
|
|
318
|
+
const target = p ?? file_path();
|
|
319
|
+
if (!_isFile(target) || _statSize(target) === 0) {
|
|
320
|
+
return SESSION_ID_UNKNOWN;
|
|
321
|
+
}
|
|
322
|
+
let lines;
|
|
323
|
+
try {
|
|
324
|
+
lines = _readLines(target);
|
|
325
|
+
} catch {
|
|
326
|
+
return SESSION_ID_UNKNOWN;
|
|
327
|
+
}
|
|
328
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
329
|
+
const line = _strip(lines[i] ?? "");
|
|
330
|
+
if (!line) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
let obj;
|
|
334
|
+
try {
|
|
335
|
+
obj = JSON.parse(line);
|
|
336
|
+
} catch {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (!_isPlainObject(obj) || obj.t === "header") {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const sid = obj.s;
|
|
343
|
+
if (typeof sid === "string" && sid) {
|
|
344
|
+
return sid;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return SESSION_ID_UNKNOWN;
|
|
348
|
+
}
|
|
349
|
+
function read_header(p) {
|
|
350
|
+
const target = p ?? file_path();
|
|
351
|
+
if (!_isFile(target) || _statSize(target) === 0) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
let first;
|
|
355
|
+
try {
|
|
356
|
+
const raw = fs2.readFileSync(target, "utf-8");
|
|
357
|
+
first = _strip(raw.split("\n", 1)[0] ?? "");
|
|
358
|
+
} catch {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
if (!first) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
const obj = JSON.parse(first);
|
|
366
|
+
if (!(_isPlainObject(obj) && obj.t === "header")) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
return obj;
|
|
370
|
+
} catch {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function _build_header(freq) {
|
|
375
|
+
return { t: "header", v: SCHEMA_VERSION, started: _now(), freq };
|
|
376
|
+
}
|
|
377
|
+
function init(freq = "per_phase", options = {}) {
|
|
378
|
+
if (!VALID_FREQS.has(freq)) {
|
|
379
|
+
throw new ValueError(`freq must be one of ${_pyReprSortedList(VALID_FREQS)}`);
|
|
380
|
+
}
|
|
381
|
+
const target = options.path ?? file_path();
|
|
382
|
+
const header = _build_header(freq);
|
|
383
|
+
if (_is_replay_mode()) {
|
|
384
|
+
return header;
|
|
385
|
+
}
|
|
386
|
+
fs2.mkdirSync(path.dirname(target), { recursive: true });
|
|
387
|
+
fs2.writeFileSync(target, _pyDumps(header) + "\n", { encoding: "utf-8" });
|
|
388
|
+
return header;
|
|
389
|
+
}
|
|
390
|
+
function migrate_header(p, options = {}) {
|
|
391
|
+
const target = p ?? file_path();
|
|
392
|
+
const existing = read_header(target);
|
|
393
|
+
if (existing === null) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
let current_v;
|
|
397
|
+
try {
|
|
398
|
+
current_v = _pyIntFromAny(existing.v ?? 0);
|
|
399
|
+
} catch {
|
|
400
|
+
current_v = 0;
|
|
401
|
+
}
|
|
402
|
+
if (current_v >= SCHEMA_VERSION) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
let chosen_freq = options.freq || (typeof existing.freq === "string" ? existing.freq : "") || "per_phase";
|
|
406
|
+
if (!VALID_FREQS.has(chosen_freq)) {
|
|
407
|
+
chosen_freq = "per_phase";
|
|
408
|
+
}
|
|
409
|
+
const new_header = _build_header(chosen_freq);
|
|
410
|
+
if (typeof existing.started === "string") {
|
|
411
|
+
new_header.started = existing.started;
|
|
412
|
+
}
|
|
413
|
+
const raw = fs2.readFileSync(target, "utf-8");
|
|
414
|
+
const lines = _pySplitlines(raw);
|
|
415
|
+
if (lines.length === 0) {
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
lines[0] = _pyDumps(new_header);
|
|
419
|
+
_atomic_write_text(target, lines.join("\n") + "\n");
|
|
420
|
+
return new_header;
|
|
421
|
+
}
|
|
422
|
+
function _pySplitlines(text) {
|
|
423
|
+
if (text === "") {
|
|
424
|
+
return [];
|
|
425
|
+
}
|
|
426
|
+
const out = [];
|
|
427
|
+
let cur = "";
|
|
428
|
+
for (let i = 0; i < text.length; i++) {
|
|
429
|
+
const ch = text[i];
|
|
430
|
+
if (ch === "\n") {
|
|
431
|
+
out.push(cur);
|
|
432
|
+
cur = "";
|
|
433
|
+
} else if (ch === "\r") {
|
|
434
|
+
out.push(cur);
|
|
435
|
+
cur = "";
|
|
436
|
+
if (text[i + 1] === "\n") {
|
|
437
|
+
i += 1;
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
cur += ch;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (cur !== "") {
|
|
444
|
+
out.push(cur);
|
|
445
|
+
}
|
|
446
|
+
return out;
|
|
447
|
+
}
|
|
448
|
+
function append(entry, options = {}) {
|
|
449
|
+
if (!_isPlainObject(entry) || !entry.t) {
|
|
450
|
+
throw new ValueError("entry must be a dict with non-empty 't' key");
|
|
451
|
+
}
|
|
452
|
+
if (entry.t === "header") {
|
|
453
|
+
throw new ValueError("use init() to write the header, not append()");
|
|
454
|
+
}
|
|
455
|
+
const target = options.path ?? file_path();
|
|
456
|
+
if (!("ts" in entry)) {
|
|
457
|
+
entry.ts = _now();
|
|
458
|
+
}
|
|
459
|
+
const session = options.session;
|
|
460
|
+
if (session !== void 0 && session !== null) {
|
|
461
|
+
entry.s = session;
|
|
462
|
+
} else if (!("s" in entry) && _session_tag_enabled()) {
|
|
463
|
+
entry.s = _last_body_session_id(target);
|
|
464
|
+
}
|
|
465
|
+
if (_is_replay_mode()) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
fs2.appendFileSync(target, _pyDumps(entry) + "\n", { encoding: "utf-8" });
|
|
469
|
+
}
|
|
470
|
+
function _atomic_write_text(target, text) {
|
|
471
|
+
if (_is_replay_mode()) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const suffix = path.extname(target);
|
|
475
|
+
const uniq = `${process.pid}.${createHash("sha256").update(`${process.pid}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 8)}`;
|
|
476
|
+
const tmp = target.slice(0, target.length - suffix.length) + `${suffix}.${uniq}.tmp`;
|
|
477
|
+
try {
|
|
478
|
+
fs2.writeFileSync(tmp, text, { encoding: "utf-8" });
|
|
479
|
+
fs2.renameSync(tmp, target);
|
|
480
|
+
} catch (exc) {
|
|
481
|
+
try {
|
|
482
|
+
fs2.unlinkSync(tmp);
|
|
483
|
+
} catch {
|
|
484
|
+
}
|
|
485
|
+
throw exc;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function _normalize_entries(entries) {
|
|
489
|
+
const out = [];
|
|
490
|
+
for (const raw of entries || []) {
|
|
491
|
+
if (!_isPlainObject(raw) || !raw.t) {
|
|
492
|
+
throw new ValueError("each entry must be a dict with non-empty 't' key");
|
|
493
|
+
}
|
|
494
|
+
if (raw.t === "header") {
|
|
495
|
+
throw new ValueError("entries must not contain headers");
|
|
496
|
+
}
|
|
497
|
+
const e = { ...raw };
|
|
498
|
+
if (!("ts" in e)) {
|
|
499
|
+
e.ts = _now();
|
|
500
|
+
}
|
|
501
|
+
out.push(e);
|
|
502
|
+
}
|
|
503
|
+
return out;
|
|
504
|
+
}
|
|
505
|
+
function reset_with_entries(entries, freq = "per_phase", options = {}) {
|
|
506
|
+
if (!VALID_FREQS.has(freq)) {
|
|
507
|
+
throw new ValueError(`freq must be one of ${_pyReprSortedList(VALID_FREQS)}`);
|
|
508
|
+
}
|
|
509
|
+
const target = options.path ?? file_path();
|
|
510
|
+
const header = _build_header(freq);
|
|
511
|
+
const body = _normalize_entries(entries);
|
|
512
|
+
fs2.mkdirSync(path.dirname(target), { recursive: true });
|
|
513
|
+
const lines = [_pyDumps(header), ...body.map((e) => _pyDumps(e))];
|
|
514
|
+
_atomic_write_text(target, lines.join("\n") + "\n");
|
|
515
|
+
return header;
|
|
516
|
+
}
|
|
517
|
+
function prepend_entries(entries, options = {}) {
|
|
518
|
+
const target = options.path ?? file_path();
|
|
519
|
+
if (!_isFile(target)) {
|
|
520
|
+
throw new FileNotFoundError(`no file at ${target}`);
|
|
521
|
+
}
|
|
522
|
+
const existing = _readLinesKeepNewline(target);
|
|
523
|
+
if (existing.length === 0) {
|
|
524
|
+
throw new ValueError(`empty file at ${target}`);
|
|
525
|
+
}
|
|
526
|
+
const header_line = existing[0];
|
|
527
|
+
const body = existing.slice(1);
|
|
528
|
+
const new_lines = _normalize_entries(entries).map((e) => _pyDumps(e) + "\n");
|
|
529
|
+
_atomic_write_text(target, header_line + new_lines.join("") + body.join(""));
|
|
530
|
+
return new_lines.length;
|
|
531
|
+
}
|
|
532
|
+
function _readLinesKeepNewline(p) {
|
|
533
|
+
const raw = fs2.readFileSync(p, "utf-8");
|
|
534
|
+
if (raw === "") {
|
|
535
|
+
return [];
|
|
536
|
+
}
|
|
537
|
+
const out = [];
|
|
538
|
+
let start = 0;
|
|
539
|
+
for (let i = 0; i < raw.length; i++) {
|
|
540
|
+
if (raw[i] === "\n") {
|
|
541
|
+
out.push(raw.slice(start, i + 1));
|
|
542
|
+
start = i + 1;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (start < raw.length) {
|
|
546
|
+
out.push(raw.slice(start));
|
|
547
|
+
}
|
|
548
|
+
return out;
|
|
549
|
+
}
|
|
550
|
+
function clear(options = {}) {
|
|
551
|
+
if (_is_replay_mode()) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const target = options.path ?? file_path();
|
|
555
|
+
if (fs2.existsSync(target)) {
|
|
556
|
+
fs2.unlinkSync(target);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function read_entries(options = {}) {
|
|
560
|
+
return read_entries_with_refs(options).map((p) => p.entry);
|
|
561
|
+
}
|
|
562
|
+
function read_entries_with_refs(options = {}) {
|
|
563
|
+
const target = options.path ?? file_path();
|
|
564
|
+
const last = options.last ?? null;
|
|
565
|
+
const session = options.session ?? null;
|
|
566
|
+
const agent = options.agent ?? null;
|
|
567
|
+
if (!_isFile(target)) {
|
|
568
|
+
return [];
|
|
569
|
+
}
|
|
570
|
+
let pairs = [];
|
|
571
|
+
const lines = _readLines(target);
|
|
572
|
+
let ref = 0;
|
|
573
|
+
for (let i = 0; i < lines.length; i++) {
|
|
574
|
+
const line = _strip(lines[i] ?? "");
|
|
575
|
+
if (!line) {
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
let obj;
|
|
579
|
+
try {
|
|
580
|
+
obj = JSON.parse(line);
|
|
581
|
+
} catch {
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
if (i === 0 && _isPlainObject(obj) && obj.t === "header") {
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
if (_isPlainObject(obj)) {
|
|
588
|
+
pairs.push({ entry: obj, ref });
|
|
589
|
+
ref += 1;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (session !== null) {
|
|
593
|
+
pairs = pairs.filter((p) => p.entry.s === session);
|
|
594
|
+
}
|
|
595
|
+
if (agent !== null) {
|
|
596
|
+
if (agent === AGENT_UNKNOWN) {
|
|
597
|
+
pairs = pairs.filter((p) => !p.entry.agent);
|
|
598
|
+
} else {
|
|
599
|
+
pairs = pairs.filter((p) => p.entry.agent === agent);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (last !== null && last >= 0) {
|
|
603
|
+
pairs = pairs.slice(pairs.length - last);
|
|
604
|
+
}
|
|
605
|
+
return pairs;
|
|
606
|
+
}
|
|
607
|
+
function read_entries_for_current(options = {}) {
|
|
608
|
+
const target = options.path ?? file_path();
|
|
609
|
+
const last = options.last ?? null;
|
|
610
|
+
const kill = (process.env.AGENT_CHAT_HISTORY_SESSION_FILTER ?? "true").trim().toLowerCase();
|
|
611
|
+
if (kill === "false") {
|
|
612
|
+
return read_entries({ last, path: target, session: null });
|
|
613
|
+
}
|
|
614
|
+
return read_entries({ last, path: target, session: _last_body_session_id(target) });
|
|
615
|
+
}
|
|
616
|
+
function list_sessions(options = {}) {
|
|
617
|
+
const target = options.path ?? file_path();
|
|
618
|
+
const summary = options.summary ?? false;
|
|
619
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
620
|
+
const _bucket = (sid) => {
|
|
621
|
+
let b = buckets.get(sid);
|
|
622
|
+
if (b === void 0) {
|
|
623
|
+
b = { id: sid, count: 0, first_ts: null, last_ts: null, preview: "", agents: [] };
|
|
624
|
+
if (summary) {
|
|
625
|
+
b._head = [];
|
|
626
|
+
b._tail = [];
|
|
627
|
+
}
|
|
628
|
+
buckets.set(sid, b);
|
|
629
|
+
}
|
|
630
|
+
return b;
|
|
631
|
+
};
|
|
632
|
+
if (_isFile(target)) {
|
|
633
|
+
const lines = _readLines(target);
|
|
634
|
+
for (let i = 0; i < lines.length; i++) {
|
|
635
|
+
const line = _strip(lines[i] ?? "");
|
|
636
|
+
if (!line) {
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
let obj;
|
|
640
|
+
try {
|
|
641
|
+
obj = JSON.parse(line);
|
|
642
|
+
} catch {
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
if (!_isPlainObject(obj)) {
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (i === 0 && obj.t === "header") {
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
const rawSid = obj.s;
|
|
652
|
+
const sid = typeof rawSid === "string" && rawSid ? rawSid : SESSION_ID_LEGACY;
|
|
653
|
+
const b = _bucket(sid);
|
|
654
|
+
b.count += 1;
|
|
655
|
+
const agent = typeof obj.agent === "string" ? obj.agent : null;
|
|
656
|
+
const tag = agent ? agent : AGENT_UNKNOWN;
|
|
657
|
+
if (!b.agents.includes(tag)) {
|
|
658
|
+
b.agents.push(tag);
|
|
659
|
+
}
|
|
660
|
+
const ts = obj.ts;
|
|
661
|
+
if (typeof ts === "string" && ts) {
|
|
662
|
+
if (b.first_ts === null || ts < b.first_ts) {
|
|
663
|
+
b.first_ts = ts;
|
|
664
|
+
}
|
|
665
|
+
if (b.last_ts === null || ts > b.last_ts) {
|
|
666
|
+
b.last_ts = ts;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (summary) {
|
|
670
|
+
if (b._head.length < 5) {
|
|
671
|
+
b._head.push(obj);
|
|
672
|
+
}
|
|
673
|
+
const tail = b._tail;
|
|
674
|
+
tail.push(obj);
|
|
675
|
+
if (tail.length > 5) {
|
|
676
|
+
tail.shift();
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
if (!b.preview || b._preview_from !== "user") {
|
|
680
|
+
if (obj.t === "user") {
|
|
681
|
+
const payload = _isPlainObject(obj.payload) ? obj.payload : {};
|
|
682
|
+
const text = obj.text || payload.text || "";
|
|
683
|
+
if (typeof text === "string" && text) {
|
|
684
|
+
b.preview = _preview(text);
|
|
685
|
+
b._preview_from = "user";
|
|
686
|
+
}
|
|
687
|
+
} else if (!b.preview) {
|
|
688
|
+
const text = obj.text || "";
|
|
689
|
+
if (typeof text === "string" && text) {
|
|
690
|
+
b.preview = _preview(text);
|
|
691
|
+
b._preview_from = "any";
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
const out = [];
|
|
698
|
+
for (const b of buckets.values()) {
|
|
699
|
+
delete b._preview_from;
|
|
700
|
+
b.agents = [...b.agents].sort(_pyStrCompare);
|
|
701
|
+
if (summary) {
|
|
702
|
+
const head = b._head ?? [];
|
|
703
|
+
const tail = b._tail ?? [];
|
|
704
|
+
delete b._head;
|
|
705
|
+
delete b._tail;
|
|
706
|
+
b.summary = _summarize_session(head, tail, b.count);
|
|
707
|
+
}
|
|
708
|
+
out.push(b);
|
|
709
|
+
}
|
|
710
|
+
out.sort((a, b) => _pyStrCompare(b.last_ts || "", a.last_ts || ""));
|
|
711
|
+
return out;
|
|
712
|
+
}
|
|
713
|
+
function status(options = {}) {
|
|
714
|
+
const target = options.path ?? file_path();
|
|
715
|
+
if (!_isFile(target)) {
|
|
716
|
+
return { exists: false, path: target };
|
|
717
|
+
}
|
|
718
|
+
const header = read_header(target);
|
|
719
|
+
let entry_count = 0;
|
|
720
|
+
const per_agent = /* @__PURE__ */ new Map();
|
|
721
|
+
const lines = _readLines(target);
|
|
722
|
+
for (let i = 0; i < lines.length; i++) {
|
|
723
|
+
const line = _strip(lines[i] ?? "");
|
|
724
|
+
if (!line) {
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
let obj;
|
|
728
|
+
try {
|
|
729
|
+
obj = JSON.parse(line);
|
|
730
|
+
} catch {
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
if (!_isPlainObject(obj)) {
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
if (i === 0 && obj.t === "header") {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
entry_count += 1;
|
|
740
|
+
const agent = typeof obj.agent === "string" ? obj.agent : null;
|
|
741
|
+
const tag = agent ? agent : AGENT_UNKNOWN;
|
|
742
|
+
per_agent.set(tag, (per_agent.get(tag) ?? 0) + 1);
|
|
743
|
+
}
|
|
744
|
+
const size = _statSize(target);
|
|
745
|
+
const sortedAgents = [...per_agent.entries()].sort((a, b) => _pyStrCompare(a[0], b[0]));
|
|
746
|
+
const per_agent_obj = {};
|
|
747
|
+
for (const [k, v] of sortedAgents) {
|
|
748
|
+
per_agent_obj[k] = v;
|
|
749
|
+
}
|
|
750
|
+
const agents = { total: per_agent.size, per_agent: per_agent_obj };
|
|
751
|
+
return {
|
|
752
|
+
exists: true,
|
|
753
|
+
path: target,
|
|
754
|
+
size_bytes: size,
|
|
755
|
+
size_kb: new PyFloat(_pyRound(size / 1024, 1)),
|
|
756
|
+
entries: entry_count,
|
|
757
|
+
header,
|
|
758
|
+
agents
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
function _pyStrCompare(a, b) {
|
|
762
|
+
if (a < b) {
|
|
763
|
+
return -1;
|
|
764
|
+
}
|
|
765
|
+
if (a > b) {
|
|
766
|
+
return 1;
|
|
767
|
+
}
|
|
768
|
+
return 0;
|
|
769
|
+
}
|
|
770
|
+
function _load_chat_history_section(settings_path) {
|
|
771
|
+
const data = load_agent_settings({ project_path: settings_path });
|
|
772
|
+
const section = data.chat_history;
|
|
773
|
+
return _isPlainObject(section) ? section : null;
|
|
774
|
+
}
|
|
775
|
+
function _read_chat_history_enabled(settings_path) {
|
|
776
|
+
const section = _load_chat_history_section(settings_path);
|
|
777
|
+
if (section === null) {
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
return _pyBool(section.enabled ?? false);
|
|
781
|
+
}
|
|
782
|
+
function _pyBool(v) {
|
|
783
|
+
if (v === null || v === void 0 || v === false) {
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
if (v === true) {
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
if (typeof v === "number") {
|
|
790
|
+
return v !== 0;
|
|
791
|
+
}
|
|
792
|
+
if (typeof v === "string") {
|
|
793
|
+
return v.length > 0;
|
|
794
|
+
}
|
|
795
|
+
if (Array.isArray(v)) {
|
|
796
|
+
return v.length > 0;
|
|
797
|
+
}
|
|
798
|
+
if (_isPlainObject(v)) {
|
|
799
|
+
return Object.keys(v).length > 0;
|
|
800
|
+
}
|
|
801
|
+
return true;
|
|
802
|
+
}
|
|
803
|
+
var VALID_HOOK_EVENTS = [
|
|
804
|
+
"session_start",
|
|
805
|
+
"session_end",
|
|
806
|
+
"user_prompt",
|
|
807
|
+
"agent_response",
|
|
808
|
+
"tool_use",
|
|
809
|
+
"phase",
|
|
810
|
+
"stop"
|
|
811
|
+
];
|
|
812
|
+
var VALID_HOOK_EVENTS_SET = new Set(VALID_HOOK_EVENTS);
|
|
813
|
+
var HOOK_EVENT_ENTRY_TYPE = {
|
|
814
|
+
user_prompt: "user",
|
|
815
|
+
agent_response: "agent",
|
|
816
|
+
tool_use: "tool",
|
|
817
|
+
phase: "phase",
|
|
818
|
+
stop: "agent",
|
|
819
|
+
session_end: "phase"
|
|
820
|
+
};
|
|
821
|
+
var CADENCE_EVENTS = {
|
|
822
|
+
per_turn: /* @__PURE__ */ new Set(["stop", "agent_response", "user_prompt"]),
|
|
823
|
+
per_phase: /* @__PURE__ */ new Set(["phase", "stop", "user_prompt"]),
|
|
824
|
+
per_tool: /* @__PURE__ */ new Set(["tool_use"])
|
|
825
|
+
};
|
|
826
|
+
var PLATFORM_EVENT_MAP = {
|
|
827
|
+
claude: {
|
|
828
|
+
SessionStart: "session_start",
|
|
829
|
+
UserPromptSubmit: "user_prompt",
|
|
830
|
+
PostToolUse: "tool_use",
|
|
831
|
+
Stop: "stop",
|
|
832
|
+
SessionEnd: "session_end",
|
|
833
|
+
PreCompact: "phase"
|
|
834
|
+
},
|
|
835
|
+
cowork: {
|
|
836
|
+
SessionStart: "session_start",
|
|
837
|
+
UserPromptSubmit: "user_prompt",
|
|
838
|
+
PostToolUse: "tool_use",
|
|
839
|
+
Stop: "stop",
|
|
840
|
+
SessionEnd: "session_end",
|
|
841
|
+
PreCompact: "phase"
|
|
842
|
+
},
|
|
843
|
+
augment: {
|
|
844
|
+
SessionStart: "session_start",
|
|
845
|
+
Stop: "stop",
|
|
846
|
+
PostToolUse: "tool_use",
|
|
847
|
+
SessionEnd: "session_end"
|
|
848
|
+
},
|
|
849
|
+
cursor: {
|
|
850
|
+
sessionStart: "session_start",
|
|
851
|
+
sessionEnd: "session_end",
|
|
852
|
+
afterAgentResponse: "agent_response",
|
|
853
|
+
stop: "stop",
|
|
854
|
+
postToolUse: "tool_use",
|
|
855
|
+
beforeSubmitPrompt: "user_prompt"
|
|
856
|
+
},
|
|
857
|
+
cline: {
|
|
858
|
+
TaskStart: "session_start",
|
|
859
|
+
TaskComplete: "session_end",
|
|
860
|
+
UserPromptSubmit: "user_prompt",
|
|
861
|
+
PostToolUse: "tool_use"
|
|
862
|
+
},
|
|
863
|
+
windsurf: {
|
|
864
|
+
pre_user_prompt: "user_prompt",
|
|
865
|
+
post_cascade_response: "agent_response",
|
|
866
|
+
post_cascade_response_with_transcript: "agent_response",
|
|
867
|
+
post_setup_worktree: "phase"
|
|
868
|
+
},
|
|
869
|
+
gemini: {
|
|
870
|
+
SessionStart: "session_start",
|
|
871
|
+
AfterAgent: "agent_response",
|
|
872
|
+
AfterTool: "tool_use",
|
|
873
|
+
SessionEnd: "session_end"
|
|
874
|
+
},
|
|
875
|
+
generic: Object.fromEntries(VALID_HOOK_EVENTS.map((ev) => [ev, ev]))
|
|
876
|
+
};
|
|
877
|
+
var VALID_PLATFORMS = Object.keys(PLATFORM_EVENT_MAP);
|
|
878
|
+
function _read_chat_history_frequency(settings_path) {
|
|
879
|
+
const section = _load_chat_history_section(settings_path);
|
|
880
|
+
if (section === null) {
|
|
881
|
+
return "per_phase";
|
|
882
|
+
}
|
|
883
|
+
const val = String(section.frequency ?? "per_phase").toLowerCase();
|
|
884
|
+
return VALID_FREQS.has(val) ? val : "per_phase";
|
|
885
|
+
}
|
|
886
|
+
function _read_chat_history_max_sessions(settings_path) {
|
|
887
|
+
const section = _load_chat_history_section(settings_path);
|
|
888
|
+
if (section === null) {
|
|
889
|
+
return DEFAULT_MAX_SESSIONS;
|
|
890
|
+
}
|
|
891
|
+
const rawN = section.max_sessions ?? DEFAULT_MAX_SESSIONS;
|
|
892
|
+
let n;
|
|
893
|
+
if (typeof rawN === "number" && Number.isFinite(rawN)) {
|
|
894
|
+
n = Math.trunc(rawN);
|
|
895
|
+
} else if (typeof rawN === "string") {
|
|
896
|
+
const parsed = Number.parseInt(rawN, 10);
|
|
897
|
+
if (Number.isNaN(parsed)) {
|
|
898
|
+
return DEFAULT_MAX_SESSIONS;
|
|
899
|
+
}
|
|
900
|
+
n = parsed;
|
|
901
|
+
} else {
|
|
902
|
+
return DEFAULT_MAX_SESSIONS;
|
|
903
|
+
}
|
|
904
|
+
return Math.max(1, n);
|
|
905
|
+
}
|
|
906
|
+
function _read_text_limits(settings_path) {
|
|
907
|
+
const out = { ...DEFAULT_TEXT_LIMITS };
|
|
908
|
+
const section = _load_chat_history_section(settings_path);
|
|
909
|
+
if (section === null) {
|
|
910
|
+
return out;
|
|
911
|
+
}
|
|
912
|
+
const overrides = section.text_limits;
|
|
913
|
+
if (!_isPlainObject(overrides)) {
|
|
914
|
+
return out;
|
|
915
|
+
}
|
|
916
|
+
for (const [kind, val] of Object.entries(overrides)) {
|
|
917
|
+
let n;
|
|
918
|
+
if (typeof val === "number" && Number.isFinite(val)) {
|
|
919
|
+
n = Math.trunc(val);
|
|
920
|
+
} else if (typeof val === "string") {
|
|
921
|
+
const parsed = Number.parseInt(val, 10);
|
|
922
|
+
if (Number.isNaN(parsed)) {
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
n = parsed;
|
|
926
|
+
} else {
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
out[kind] = Math.max(0, n);
|
|
930
|
+
}
|
|
931
|
+
return out;
|
|
932
|
+
}
|
|
933
|
+
function _apply_text_limit(text, kind, limits) {
|
|
934
|
+
if (!text) {
|
|
935
|
+
return "";
|
|
936
|
+
}
|
|
937
|
+
const n = limits[kind] ?? DEFAULT_TEXT_LIMITS[kind] ?? 0;
|
|
938
|
+
if (n <= 0) {
|
|
939
|
+
return text;
|
|
940
|
+
}
|
|
941
|
+
const flat = _strip(text.replace(_WS_RE, " "));
|
|
942
|
+
if (_pyLen(flat) <= n) {
|
|
943
|
+
return flat;
|
|
944
|
+
}
|
|
945
|
+
return `${_pySliceTo(flat, n)} \u2026 [+${_pyLen(flat) - n} chars]`;
|
|
946
|
+
}
|
|
947
|
+
function prune_sessions(max_sessions = DEFAULT_MAX_SESSIONS, options = {}) {
|
|
948
|
+
if (max_sessions < 1) {
|
|
949
|
+
max_sessions = 1;
|
|
950
|
+
}
|
|
951
|
+
const target = options.path ?? file_path();
|
|
952
|
+
if (!_isFile(target)) {
|
|
953
|
+
return { action: "noop", kept_sessions: 0, dropped_sessions: 0, dropped_entries: 0 };
|
|
954
|
+
}
|
|
955
|
+
const lines = _readLinesKeepNewline(target);
|
|
956
|
+
if (lines.length <= 1) {
|
|
957
|
+
return { action: "noop", kept_sessions: 0, dropped_sessions: 0, dropped_entries: 0 };
|
|
958
|
+
}
|
|
959
|
+
const header_line = lines[0];
|
|
960
|
+
const body = lines.slice(1);
|
|
961
|
+
const last_pos = /* @__PURE__ */ new Map();
|
|
962
|
+
const parsed = [];
|
|
963
|
+
for (let idx = 0; idx < body.length; idx++) {
|
|
964
|
+
const line = body[idx];
|
|
965
|
+
const stripped = _strip(line);
|
|
966
|
+
if (!stripped) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
let obj;
|
|
970
|
+
try {
|
|
971
|
+
obj = JSON.parse(stripped);
|
|
972
|
+
} catch {
|
|
973
|
+
parsed.push([SESSION_ID_LEGACY, line]);
|
|
974
|
+
last_pos.set(SESSION_ID_LEGACY, idx);
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
if (!_isPlainObject(obj)) {
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
const sid = typeof obj.s === "string" ? obj.s : SESSION_ID_LEGACY;
|
|
981
|
+
parsed.push([sid, line]);
|
|
982
|
+
last_pos.set(sid, idx);
|
|
983
|
+
}
|
|
984
|
+
if (last_pos.size <= max_sessions) {
|
|
985
|
+
return {
|
|
986
|
+
action: "noop",
|
|
987
|
+
kept_sessions: last_pos.size,
|
|
988
|
+
dropped_sessions: 0,
|
|
989
|
+
dropped_entries: 0
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
const ranked = [...last_pos.entries()].sort((a, b) => b[1] - a[1]);
|
|
993
|
+
const keep_set = new Set(ranked.slice(0, max_sessions).map(([sid]) => sid));
|
|
994
|
+
const drop_set = new Set(ranked.slice(max_sessions).map(([sid]) => sid));
|
|
995
|
+
const kept_lines = parsed.filter(([sid]) => keep_set.has(sid)).map(([, line]) => line);
|
|
996
|
+
const dropped_entries = parsed.length - kept_lines.length;
|
|
997
|
+
_atomic_write_text(target, header_line + kept_lines.join(""));
|
|
998
|
+
return {
|
|
999
|
+
action: "pruned",
|
|
1000
|
+
kept_sessions: keep_set.size,
|
|
1001
|
+
dropped_sessions: drop_set.size,
|
|
1002
|
+
dropped_entries
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
function overflow_handle(max_kb, mode = "rotate", options = {}) {
|
|
1006
|
+
if (!VALID_OVERFLOW.has(mode)) {
|
|
1007
|
+
throw new ValueError(`mode must be one of ${_pyReprSortedList(VALID_OVERFLOW)}`);
|
|
1008
|
+
}
|
|
1009
|
+
const target = options.path ?? file_path();
|
|
1010
|
+
if (!_isFile(target) || _statSize(target) <= max_kb * 1024) {
|
|
1011
|
+
return { action: "noop", kept: null, dropped: 0 };
|
|
1012
|
+
}
|
|
1013
|
+
const lines = _readLinesKeepNewline(target);
|
|
1014
|
+
if (lines.length === 0) {
|
|
1015
|
+
return { action: "noop", kept: 0, dropped: 0 };
|
|
1016
|
+
}
|
|
1017
|
+
const header_line = lines[0];
|
|
1018
|
+
const entries = lines.slice(1);
|
|
1019
|
+
if (mode === "rotate") {
|
|
1020
|
+
const budget = max_kb * 1024 - Buffer.byteLength(header_line, "utf-8");
|
|
1021
|
+
const kept = [];
|
|
1022
|
+
let total = 0;
|
|
1023
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
1024
|
+
const line = entries[i];
|
|
1025
|
+
const size = Buffer.byteLength(line, "utf-8");
|
|
1026
|
+
if (total + size > budget) {
|
|
1027
|
+
break;
|
|
1028
|
+
}
|
|
1029
|
+
kept.push(line);
|
|
1030
|
+
total += size;
|
|
1031
|
+
}
|
|
1032
|
+
kept.reverse();
|
|
1033
|
+
const dropped = entries.length - kept.length;
|
|
1034
|
+
_atomic_write_text(target, header_line + kept.join(""));
|
|
1035
|
+
return { action: "rotate", kept: kept.length, dropped };
|
|
1036
|
+
}
|
|
1037
|
+
const marker = {
|
|
1038
|
+
t: "needs_condense",
|
|
1039
|
+
ts: _now(),
|
|
1040
|
+
reason: `file exceeded ${max_kb} KB, condense-mode requested`
|
|
1041
|
+
};
|
|
1042
|
+
append(marker, { path: target });
|
|
1043
|
+
return { action: "condense_marked", kept: entries.length, dropped: 0 };
|
|
1044
|
+
}
|
|
1045
|
+
function hook_append(event, options = {}) {
|
|
1046
|
+
if (!VALID_HOOK_EVENTS_SET.has(event)) {
|
|
1047
|
+
throw new ValueError(`event must be one of ${_pyReprSortedListArr(VALID_HOOK_EVENTS)}`);
|
|
1048
|
+
}
|
|
1049
|
+
const dry_run = options.dry_run ?? false;
|
|
1050
|
+
const sp = options.settings_path ?? DEFAULT_SETTINGS_FILE;
|
|
1051
|
+
if (!_read_chat_history_enabled(sp)) {
|
|
1052
|
+
const out = { action: "disabled", event };
|
|
1053
|
+
if (dry_run) {
|
|
1054
|
+
out.dry_run = true;
|
|
1055
|
+
}
|
|
1056
|
+
return out;
|
|
1057
|
+
}
|
|
1058
|
+
const target = options.path ?? file_path();
|
|
1059
|
+
const payload = options.payload ?? {};
|
|
1060
|
+
const session_id = options.session_id ?? null;
|
|
1061
|
+
const s_tag = session_id ? derive_session_tag(session_id) : SESSION_ID_UNKNOWN;
|
|
1062
|
+
const prior_s = _isFile(target) ? _last_body_session_id(target) : SESSION_ID_UNKNOWN;
|
|
1063
|
+
const is_new_session = s_tag !== SESSION_ID_UNKNOWN && prior_s !== SESSION_ID_UNKNOWN && prior_s !== s_tag;
|
|
1064
|
+
if (is_new_session) {
|
|
1065
|
+
process.stderr.write(
|
|
1066
|
+
`chat-history session_rotation event=${event} prior_s=${prior_s} new_s=${s_tag}
|
|
1067
|
+
`
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
if (!dry_run) {
|
|
1071
|
+
if (!_isFile(target) || read_header(target) === null) {
|
|
1072
|
+
const freq2 = _read_chat_history_frequency(sp);
|
|
1073
|
+
init(freq2, { path: target });
|
|
1074
|
+
} else {
|
|
1075
|
+
migrate_header(target, { freq: _read_chat_history_frequency(sp) });
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
const _maybe_prune = () => {
|
|
1079
|
+
if (dry_run || !is_new_session) {
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
const max_n = _read_chat_history_max_sessions(sp);
|
|
1083
|
+
try {
|
|
1084
|
+
prune_sessions(max_n, { path: target });
|
|
1085
|
+
} catch (exc) {
|
|
1086
|
+
process.stderr.write(`chat-history prune_failed: ${_excStr(exc)}
|
|
1087
|
+
`);
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
if (event === "session_start") {
|
|
1091
|
+
_maybe_prune();
|
|
1092
|
+
const action = "session_start_noop";
|
|
1093
|
+
const out = { action: dry_run ? "dry_run" : action, event, s: s_tag };
|
|
1094
|
+
if (dry_run) {
|
|
1095
|
+
out.would_action = action;
|
|
1096
|
+
out.dry_run = true;
|
|
1097
|
+
}
|
|
1098
|
+
return out;
|
|
1099
|
+
}
|
|
1100
|
+
if (event === "session_end") {
|
|
1101
|
+
_maybe_prune();
|
|
1102
|
+
const action = "session_end_noop";
|
|
1103
|
+
const out = { action: dry_run ? "dry_run" : action, event, s: s_tag };
|
|
1104
|
+
if (dry_run) {
|
|
1105
|
+
out.would_action = action;
|
|
1106
|
+
out.dry_run = true;
|
|
1107
|
+
}
|
|
1108
|
+
return out;
|
|
1109
|
+
}
|
|
1110
|
+
const freq = _read_chat_history_frequency(sp);
|
|
1111
|
+
if (!(CADENCE_EVENTS[freq] ?? /* @__PURE__ */ new Set()).has(event)) {
|
|
1112
|
+
const out = { action: "skipped_cadence", event, frequency: freq };
|
|
1113
|
+
if (dry_run) {
|
|
1114
|
+
out.dry_run = true;
|
|
1115
|
+
}
|
|
1116
|
+
return out;
|
|
1117
|
+
}
|
|
1118
|
+
const entry_type = HOOK_EVENT_ENTRY_TYPE[event] ?? "agent";
|
|
1119
|
+
const limits = _read_text_limits(sp);
|
|
1120
|
+
const entry = { t: entry_type };
|
|
1121
|
+
const text = String(payload.text ?? "");
|
|
1122
|
+
if (text) {
|
|
1123
|
+
const sliced = _apply_text_limit(text, entry_type, limits);
|
|
1124
|
+
if (sliced) {
|
|
1125
|
+
entry.text = sliced;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
if (event === "tool_use") {
|
|
1129
|
+
const tool = payload.tool;
|
|
1130
|
+
if (tool) {
|
|
1131
|
+
entry.tool = String(tool);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
for (const k of ["agent", "source", "phase", "decision"]) {
|
|
1135
|
+
const v = payload[k];
|
|
1136
|
+
if (v) {
|
|
1137
|
+
entry[k] = String(v);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
if (dry_run) {
|
|
1141
|
+
const preview = { ...entry };
|
|
1142
|
+
preview.s = s_tag;
|
|
1143
|
+
return {
|
|
1144
|
+
action: "dry_run",
|
|
1145
|
+
would_action: "appended",
|
|
1146
|
+
event,
|
|
1147
|
+
type: entry_type,
|
|
1148
|
+
s: s_tag,
|
|
1149
|
+
entry_preview: preview,
|
|
1150
|
+
dry_run: true
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
append(entry, { path: target, session: s_tag });
|
|
1154
|
+
_maybe_prune();
|
|
1155
|
+
return { action: "appended", event, type: entry_type, s: s_tag };
|
|
1156
|
+
}
|
|
1157
|
+
function _extract_augment_conversation(payload) {
|
|
1158
|
+
const conv = payload.conversation;
|
|
1159
|
+
if (!_isPlainObject(conv)) {
|
|
1160
|
+
return ["", ""];
|
|
1161
|
+
}
|
|
1162
|
+
const user = conv.userPrompt;
|
|
1163
|
+
const agent = conv.agentTextResponse;
|
|
1164
|
+
const user_s = typeof user === "string" ? _strip(user) : "";
|
|
1165
|
+
const agent_s = typeof agent === "string" ? _strip(agent) : "";
|
|
1166
|
+
return [user_s, agent_s];
|
|
1167
|
+
}
|
|
1168
|
+
function _extract_claude_transcript_response(transcript_path) {
|
|
1169
|
+
if (!transcript_path) {
|
|
1170
|
+
return "";
|
|
1171
|
+
}
|
|
1172
|
+
if (!_isFile(transcript_path)) {
|
|
1173
|
+
return "";
|
|
1174
|
+
}
|
|
1175
|
+
let last_text = "";
|
|
1176
|
+
let lines;
|
|
1177
|
+
try {
|
|
1178
|
+
lines = _readLines(transcript_path);
|
|
1179
|
+
} catch {
|
|
1180
|
+
return "";
|
|
1181
|
+
}
|
|
1182
|
+
for (const rawLine of lines) {
|
|
1183
|
+
const line = _strip(rawLine);
|
|
1184
|
+
if (!line) {
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
let obj;
|
|
1188
|
+
try {
|
|
1189
|
+
obj = JSON.parse(line);
|
|
1190
|
+
} catch {
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
if (!_isPlainObject(obj)) {
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
if (obj.type !== "assistant") {
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
const msg = obj.message;
|
|
1200
|
+
if (!_isPlainObject(msg)) {
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
const content = msg.content;
|
|
1204
|
+
if (typeof content === "string") {
|
|
1205
|
+
last_text = content;
|
|
1206
|
+
} else if (Array.isArray(content)) {
|
|
1207
|
+
const parts = [];
|
|
1208
|
+
for (const blk of content) {
|
|
1209
|
+
if (_isPlainObject(blk) && blk.type === "text") {
|
|
1210
|
+
const t = blk.text ?? "";
|
|
1211
|
+
if (typeof t === "string") {
|
|
1212
|
+
parts.push(t);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
if (parts.length > 0) {
|
|
1217
|
+
last_text = parts.join("\n");
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
return _strip(last_text);
|
|
1222
|
+
}
|
|
1223
|
+
function _extract_cursor_text(payload, event) {
|
|
1224
|
+
if (event === "stop" || event === "agent_response") {
|
|
1225
|
+
const tp = payload.transcript_path ?? payload.transcriptPath;
|
|
1226
|
+
if (typeof tp === "string") {
|
|
1227
|
+
const txt = _extract_claude_transcript_response(tp);
|
|
1228
|
+
if (txt) {
|
|
1229
|
+
return txt;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return "";
|
|
1234
|
+
}
|
|
1235
|
+
function _extract_cline_text(payload, event) {
|
|
1236
|
+
if (event === "user_prompt") {
|
|
1237
|
+
const v = payload.prompt ?? payload.userPrompt;
|
|
1238
|
+
if (typeof v === "string" && _strip(v)) {
|
|
1239
|
+
return _strip(v);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
return "";
|
|
1243
|
+
}
|
|
1244
|
+
function _extract_gemini_text(payload, event) {
|
|
1245
|
+
if (event === "agent_response" || event === "stop") {
|
|
1246
|
+
const v = payload.prompt_response ?? payload.promptResponse;
|
|
1247
|
+
if (typeof v === "string" && _strip(v)) {
|
|
1248
|
+
return _strip(v);
|
|
1249
|
+
}
|
|
1250
|
+
const tp = payload.transcript_path ?? payload.transcriptPath;
|
|
1251
|
+
if (typeof tp === "string") {
|
|
1252
|
+
const txt = _extract_claude_transcript_response(tp);
|
|
1253
|
+
if (txt) {
|
|
1254
|
+
return txt;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
return "";
|
|
1259
|
+
}
|
|
1260
|
+
function _extract_windsurf_text(payload, event) {
|
|
1261
|
+
if (event === "agent_response" || event === "stop") {
|
|
1262
|
+
const info = payload.tool_info ?? payload.toolInfo;
|
|
1263
|
+
if (_isPlainObject(info)) {
|
|
1264
|
+
const v = info.response ?? info.text;
|
|
1265
|
+
if (typeof v === "string" && _strip(v)) {
|
|
1266
|
+
return _strip(v);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
const tp = payload.transcript_path ?? payload.transcriptPath;
|
|
1270
|
+
if (typeof tp === "string") {
|
|
1271
|
+
const txt = _extract_claude_transcript_response(tp);
|
|
1272
|
+
if (txt) {
|
|
1273
|
+
return txt;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
return "";
|
|
1278
|
+
}
|
|
1279
|
+
function _extract_hook_text(payload, options = {}) {
|
|
1280
|
+
const platform = options.platform ?? null;
|
|
1281
|
+
const event = options.event ?? null;
|
|
1282
|
+
if (platform === "augment") {
|
|
1283
|
+
const [user, agent] = _extract_augment_conversation(payload);
|
|
1284
|
+
if (event === "user_prompt" && user) {
|
|
1285
|
+
return user;
|
|
1286
|
+
}
|
|
1287
|
+
if ((event === "stop" || event === "agent_response") && agent) {
|
|
1288
|
+
return agent;
|
|
1289
|
+
}
|
|
1290
|
+
if (agent) {
|
|
1291
|
+
return agent;
|
|
1292
|
+
}
|
|
1293
|
+
if (user) {
|
|
1294
|
+
return user;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
if ((platform === "claude" || platform === "cowork") && (event === "stop" || event === "agent_response")) {
|
|
1298
|
+
const tp = payload.transcript_path ?? payload.transcriptPath;
|
|
1299
|
+
if (typeof tp === "string") {
|
|
1300
|
+
const txt = _extract_claude_transcript_response(tp);
|
|
1301
|
+
if (txt) {
|
|
1302
|
+
return txt;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
if (platform === "cursor") {
|
|
1307
|
+
const txt = _extract_cursor_text(payload, event);
|
|
1308
|
+
if (txt) {
|
|
1309
|
+
return txt;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
if (platform === "cline") {
|
|
1313
|
+
const txt = _extract_cline_text(payload, event);
|
|
1314
|
+
if (txt) {
|
|
1315
|
+
return txt;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
if (platform === "gemini") {
|
|
1319
|
+
const txt = _extract_gemini_text(payload, event);
|
|
1320
|
+
if (txt) {
|
|
1321
|
+
return txt;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
if (platform === "windsurf") {
|
|
1325
|
+
const txt = _extract_windsurf_text(payload, event);
|
|
1326
|
+
if (txt) {
|
|
1327
|
+
return txt;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
for (const key of [
|
|
1331
|
+
"prompt",
|
|
1332
|
+
"user_prompt",
|
|
1333
|
+
"first_user_msg",
|
|
1334
|
+
"firstUserMsg",
|
|
1335
|
+
"userMessage",
|
|
1336
|
+
"user_message",
|
|
1337
|
+
"text",
|
|
1338
|
+
"response",
|
|
1339
|
+
"message",
|
|
1340
|
+
"content"
|
|
1341
|
+
]) {
|
|
1342
|
+
const v = payload[key];
|
|
1343
|
+
if (typeof v === "string" && _strip(v)) {
|
|
1344
|
+
return _strip(v);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const tr = payload.tool_response ?? payload.toolResponse;
|
|
1348
|
+
if (_isPlainObject(tr)) {
|
|
1349
|
+
for (const key of ["output", "stdout", "result", "text"]) {
|
|
1350
|
+
const v = tr[key];
|
|
1351
|
+
if (typeof v === "string" && _strip(v)) {
|
|
1352
|
+
return _strip(v);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
return "";
|
|
1357
|
+
}
|
|
1358
|
+
function _extract_hook_tool(payload) {
|
|
1359
|
+
for (const key of ["tool_name", "toolName", "tool"]) {
|
|
1360
|
+
const v = payload[key];
|
|
1361
|
+
if (typeof v === "string" && _strip(v)) {
|
|
1362
|
+
return _strip(v);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
return "";
|
|
1366
|
+
}
|
|
1367
|
+
function _extract_hook_event(payload) {
|
|
1368
|
+
for (const key of ["hook_event_name", "event", "eventName", "event_name"]) {
|
|
1369
|
+
const v = payload[key];
|
|
1370
|
+
if (typeof v === "string" && _strip(v)) {
|
|
1371
|
+
return _strip(v);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return "";
|
|
1375
|
+
}
|
|
1376
|
+
function _extract_session_id(payload) {
|
|
1377
|
+
for (const key of ["session_id", "sessionId", "task_id", "taskId", "conversation_id", "conversationId"]) {
|
|
1378
|
+
const v = payload[key];
|
|
1379
|
+
if (typeof v === "string" && _strip(v)) {
|
|
1380
|
+
return _strip(v);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
return "";
|
|
1384
|
+
}
|
|
1385
|
+
function hook_dispatch(platform, raw_json, options = {}) {
|
|
1386
|
+
if (!(platform in PLATFORM_EVENT_MAP)) {
|
|
1387
|
+
throw new ValueError(
|
|
1388
|
+
`unknown platform: ${_pyRepr(platform)}; expected one of ${_pyReprSortedListArr(VALID_PLATFORMS)}`
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
const event_override = options.event_override ?? null;
|
|
1392
|
+
const dry_run = options.dry_run ?? false;
|
|
1393
|
+
const raw = _strip(raw_json || "");
|
|
1394
|
+
let payload;
|
|
1395
|
+
if (!raw) {
|
|
1396
|
+
payload = {};
|
|
1397
|
+
} else {
|
|
1398
|
+
let parsed;
|
|
1399
|
+
try {
|
|
1400
|
+
parsed = JSON.parse(raw);
|
|
1401
|
+
} catch (exc) {
|
|
1402
|
+
throw new ValueError(`invalid JSON on stdin: ${_excStr(exc)}`);
|
|
1403
|
+
}
|
|
1404
|
+
if (!_isPlainObject(parsed)) {
|
|
1405
|
+
throw new ValueError("stdin JSON must decode to an object");
|
|
1406
|
+
}
|
|
1407
|
+
payload = parsed;
|
|
1408
|
+
}
|
|
1409
|
+
let envelope_event = "";
|
|
1410
|
+
if (["schema_version", "platform", "event", "payload"].every((k) => k in payload)) {
|
|
1411
|
+
envelope_event = _strip(
|
|
1412
|
+
String(payload.native_event || payload.event || "")
|
|
1413
|
+
);
|
|
1414
|
+
const inner = payload.payload;
|
|
1415
|
+
payload = _isPlainObject(inner) ? inner : {};
|
|
1416
|
+
}
|
|
1417
|
+
const raw_event = _strip(
|
|
1418
|
+
String(event_override || envelope_event || _extract_hook_event(payload) || "")
|
|
1419
|
+
);
|
|
1420
|
+
const event = PLATFORM_EVENT_MAP[platform][raw_event];
|
|
1421
|
+
if (!event) {
|
|
1422
|
+
return { action: "skipped_unmapped_event", platform, raw_event };
|
|
1423
|
+
}
|
|
1424
|
+
const text = _extract_hook_text(payload, { platform, event });
|
|
1425
|
+
const tool = _extract_hook_tool(payload);
|
|
1426
|
+
const session_id = _extract_session_id(payload);
|
|
1427
|
+
let augment_user_prompt = "";
|
|
1428
|
+
if (platform === "augment" && event === "stop") {
|
|
1429
|
+
const [u] = _extract_augment_conversation(payload);
|
|
1430
|
+
augment_user_prompt = u;
|
|
1431
|
+
}
|
|
1432
|
+
const hook_payload = {
|
|
1433
|
+
source: `hook:${platform}:${raw_event}`,
|
|
1434
|
+
agent: platform
|
|
1435
|
+
};
|
|
1436
|
+
if (text && event !== "session_start") {
|
|
1437
|
+
hook_payload.text = text;
|
|
1438
|
+
}
|
|
1439
|
+
if (tool) {
|
|
1440
|
+
hook_payload.tool = tool;
|
|
1441
|
+
}
|
|
1442
|
+
if (augment_user_prompt) {
|
|
1443
|
+
hook_append("user_prompt", {
|
|
1444
|
+
session_id,
|
|
1445
|
+
payload: {
|
|
1446
|
+
text: augment_user_prompt,
|
|
1447
|
+
source: `hook:${platform}:${raw_event}:user`,
|
|
1448
|
+
agent: platform
|
|
1449
|
+
},
|
|
1450
|
+
path: options.path,
|
|
1451
|
+
settings_path: options.settings_path,
|
|
1452
|
+
dry_run
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
return hook_append(event, {
|
|
1456
|
+
session_id,
|
|
1457
|
+
payload: hook_payload,
|
|
1458
|
+
path: options.path,
|
|
1459
|
+
settings_path: options.settings_path,
|
|
1460
|
+
dry_run
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
var ValueError = class extends Error {
|
|
1464
|
+
};
|
|
1465
|
+
var FileNotFoundError = class extends Error {
|
|
1466
|
+
};
|
|
1467
|
+
function _excStr(exc) {
|
|
1468
|
+
if (exc instanceof Error) {
|
|
1469
|
+
return exc.message;
|
|
1470
|
+
}
|
|
1471
|
+
return String(exc);
|
|
1472
|
+
}
|
|
1473
|
+
function _pyRepr(s) {
|
|
1474
|
+
if (!s.includes("'") || s.includes('"')) {
|
|
1475
|
+
return `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
1476
|
+
}
|
|
1477
|
+
return `'${s}'`;
|
|
1478
|
+
}
|
|
1479
|
+
function _pyReprSortedList(set) {
|
|
1480
|
+
const items = [...set].sort(_pyStrCompare);
|
|
1481
|
+
return `[${items.map((i) => _pyRepr(i)).join(", ")}]`;
|
|
1482
|
+
}
|
|
1483
|
+
function _pyReprSortedListArr(arr) {
|
|
1484
|
+
const items = [...arr].sort(_pyStrCompare);
|
|
1485
|
+
return `[${items.map((i) => _pyRepr(i)).join(", ")}]`;
|
|
1486
|
+
}
|
|
1487
|
+
var PROG = "chat_history.py";
|
|
1488
|
+
var SUBCMDS = [
|
|
1489
|
+
"init",
|
|
1490
|
+
"append",
|
|
1491
|
+
"status",
|
|
1492
|
+
"reset",
|
|
1493
|
+
"prune-sessions",
|
|
1494
|
+
"prepend",
|
|
1495
|
+
"clear",
|
|
1496
|
+
"read",
|
|
1497
|
+
"sessions",
|
|
1498
|
+
"rotate",
|
|
1499
|
+
"hook-append",
|
|
1500
|
+
"hook-dispatch"
|
|
1501
|
+
];
|
|
1502
|
+
function _print(line) {
|
|
1503
|
+
process.stdout.write(line + "\n");
|
|
1504
|
+
}
|
|
1505
|
+
function _eprint(line) {
|
|
1506
|
+
process.stderr.write(line + "\n");
|
|
1507
|
+
}
|
|
1508
|
+
var _TOP_USAGE = `usage: ${PROG} [-h]
|
|
1509
|
+
{${SUBCMDS.join(",")}}
|
|
1510
|
+
...
|
|
1511
|
+
`;
|
|
1512
|
+
function _argError(usage, prog, message) {
|
|
1513
|
+
process.stderr.write(usage);
|
|
1514
|
+
process.stderr.write(`${prog}: error: ${message}
|
|
1515
|
+
`);
|
|
1516
|
+
return EXIT_BAD_ARGS;
|
|
1517
|
+
}
|
|
1518
|
+
function _cmd_init(argv) {
|
|
1519
|
+
let freq = "per_phase";
|
|
1520
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1521
|
+
const a = argv[i];
|
|
1522
|
+
if (a === "--freq") {
|
|
1523
|
+
freq = argv[i + 1] ?? freq;
|
|
1524
|
+
i += 1;
|
|
1525
|
+
} else if (a.startsWith("--freq=")) {
|
|
1526
|
+
freq = a.slice("--freq=".length);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
if (!VALID_FREQS.has(freq)) {
|
|
1530
|
+
return _argError(
|
|
1531
|
+
`usage: ${PROG} init [-h] [--freq {per_phase,per_tool,per_turn}]
|
|
1532
|
+
`,
|
|
1533
|
+
`${PROG} init`,
|
|
1534
|
+
`argument --freq: invalid choice: ${_pyRepr(freq)} (choose from 'per_phase', 'per_tool', 'per_turn')`
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
const h = init(freq);
|
|
1538
|
+
_print(_pyDumps(h));
|
|
1539
|
+
return 0;
|
|
1540
|
+
}
|
|
1541
|
+
function _cmd_hook_append(argv) {
|
|
1542
|
+
let event = null;
|
|
1543
|
+
let session_id = null;
|
|
1544
|
+
let payloadRaw = null;
|
|
1545
|
+
let settings = null;
|
|
1546
|
+
let dry_run = false;
|
|
1547
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1548
|
+
const a = argv[i];
|
|
1549
|
+
if (a === "--event") {
|
|
1550
|
+
event = argv[++i] ?? null;
|
|
1551
|
+
} else if (a.startsWith("--event=")) {
|
|
1552
|
+
event = a.slice("--event=".length);
|
|
1553
|
+
} else if (a === "--session-id") {
|
|
1554
|
+
session_id = argv[++i] ?? null;
|
|
1555
|
+
} else if (a.startsWith("--session-id=")) {
|
|
1556
|
+
session_id = a.slice("--session-id=".length);
|
|
1557
|
+
} else if (a === "--payload") {
|
|
1558
|
+
payloadRaw = argv[++i] ?? null;
|
|
1559
|
+
} else if (a.startsWith("--payload=")) {
|
|
1560
|
+
payloadRaw = a.slice("--payload=".length);
|
|
1561
|
+
} else if (a === "--settings") {
|
|
1562
|
+
settings = argv[++i] ?? null;
|
|
1563
|
+
} else if (a.startsWith("--settings=")) {
|
|
1564
|
+
settings = a.slice("--settings=".length);
|
|
1565
|
+
} else if (a === "--dry-run") {
|
|
1566
|
+
dry_run = true;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
const _hookAppendUsage = `usage: ${PROG} hook-append [-h] --event
|
|
1570
|
+
{agent_response,phase,session_end,session_start,stop,tool_use,user_prompt}
|
|
1571
|
+
[--session-id SESSION_ID]
|
|
1572
|
+
[--payload PAYLOAD] [--settings SETTINGS]
|
|
1573
|
+
[--dry-run]
|
|
1574
|
+
`;
|
|
1575
|
+
if (event === null) {
|
|
1576
|
+
return _argError(_hookAppendUsage, `${PROG} hook-append`, "the following arguments are required: --event");
|
|
1577
|
+
}
|
|
1578
|
+
if (!VALID_HOOK_EVENTS_SET.has(event)) {
|
|
1579
|
+
const choices = [...VALID_HOOK_EVENTS].sort(_pyStrCompare).map((c) => _pyRepr(c)).join(", ");
|
|
1580
|
+
return _argError(
|
|
1581
|
+
_hookAppendUsage,
|
|
1582
|
+
`${PROG} hook-append`,
|
|
1583
|
+
`argument --event: invalid choice: ${_pyRepr(event)} (choose from ${choices})`
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1586
|
+
let payload = {};
|
|
1587
|
+
if (payloadRaw) {
|
|
1588
|
+
let parsed;
|
|
1589
|
+
try {
|
|
1590
|
+
parsed = JSON.parse(payloadRaw);
|
|
1591
|
+
} catch (exc) {
|
|
1592
|
+
_eprint(`error: --payload must be valid JSON: ${_excStr(exc)}`);
|
|
1593
|
+
return EXIT_BAD_ARGS;
|
|
1594
|
+
}
|
|
1595
|
+
if (!_isPlainObject(parsed)) {
|
|
1596
|
+
_eprint("error: --payload must decode to a JSON object");
|
|
1597
|
+
return EXIT_BAD_ARGS;
|
|
1598
|
+
}
|
|
1599
|
+
payload = parsed;
|
|
1600
|
+
}
|
|
1601
|
+
try {
|
|
1602
|
+
const result = hook_append(event, {
|
|
1603
|
+
session_id,
|
|
1604
|
+
payload,
|
|
1605
|
+
settings_path: settings,
|
|
1606
|
+
dry_run
|
|
1607
|
+
});
|
|
1608
|
+
_print(_pyDumps(result));
|
|
1609
|
+
} catch (exc) {
|
|
1610
|
+
if (exc instanceof ValueError) {
|
|
1611
|
+
_eprint(`error: ${exc.message}`);
|
|
1612
|
+
return EXIT_BAD_ARGS;
|
|
1613
|
+
}
|
|
1614
|
+
throw exc;
|
|
1615
|
+
}
|
|
1616
|
+
return EXIT_OK;
|
|
1617
|
+
}
|
|
1618
|
+
function _cmd_hook_dispatch(argv) {
|
|
1619
|
+
let platform = null;
|
|
1620
|
+
let event = null;
|
|
1621
|
+
let settings = null;
|
|
1622
|
+
let dry_run = false;
|
|
1623
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1624
|
+
const a = argv[i];
|
|
1625
|
+
if (a === "--platform") {
|
|
1626
|
+
platform = argv[++i] ?? null;
|
|
1627
|
+
} else if (a.startsWith("--platform=")) {
|
|
1628
|
+
platform = a.slice("--platform=".length);
|
|
1629
|
+
} else if (a === "--event") {
|
|
1630
|
+
event = argv[++i] ?? null;
|
|
1631
|
+
} else if (a.startsWith("--event=")) {
|
|
1632
|
+
event = a.slice("--event=".length);
|
|
1633
|
+
} else if (a === "--settings") {
|
|
1634
|
+
settings = argv[++i] ?? null;
|
|
1635
|
+
} else if (a.startsWith("--settings=")) {
|
|
1636
|
+
settings = a.slice("--settings=".length);
|
|
1637
|
+
} else if (a === "--dry-run") {
|
|
1638
|
+
dry_run = true;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
const _hookDispatchUsage = `usage: ${PROG} hook-dispatch [-h] --platform
|
|
1642
|
+
{augment,claude,cline,cowork,cursor,gemini,generic,windsurf}
|
|
1643
|
+
[--event EVENT] [--settings SETTINGS]
|
|
1644
|
+
[--dry-run]
|
|
1645
|
+
`;
|
|
1646
|
+
if (platform === null) {
|
|
1647
|
+
return _argError(
|
|
1648
|
+
_hookDispatchUsage,
|
|
1649
|
+
`${PROG} hook-dispatch`,
|
|
1650
|
+
"the following arguments are required: --platform"
|
|
1651
|
+
);
|
|
1652
|
+
}
|
|
1653
|
+
if (!(platform in PLATFORM_EVENT_MAP)) {
|
|
1654
|
+
const choices = [...VALID_PLATFORMS].sort(_pyStrCompare).map((c) => _pyRepr(c)).join(", ");
|
|
1655
|
+
return _argError(
|
|
1656
|
+
_hookDispatchUsage,
|
|
1657
|
+
`${PROG} hook-dispatch`,
|
|
1658
|
+
`argument --platform: invalid choice: ${_pyRepr(platform)} (choose from ${choices})`
|
|
1659
|
+
);
|
|
1660
|
+
}
|
|
1661
|
+
const raw = _readStdin();
|
|
1662
|
+
try {
|
|
1663
|
+
const result = hook_dispatch(platform, raw, {
|
|
1664
|
+
event_override: event,
|
|
1665
|
+
settings_path: settings,
|
|
1666
|
+
dry_run
|
|
1667
|
+
});
|
|
1668
|
+
_print(_pyDumps(result));
|
|
1669
|
+
} catch (exc) {
|
|
1670
|
+
if (exc instanceof ValueError) {
|
|
1671
|
+
_eprint(`error: ${exc.message}`);
|
|
1672
|
+
return EXIT_BAD_ARGS;
|
|
1673
|
+
}
|
|
1674
|
+
throw exc;
|
|
1675
|
+
}
|
|
1676
|
+
return EXIT_OK;
|
|
1677
|
+
}
|
|
1678
|
+
function _cmd_append(argv) {
|
|
1679
|
+
let type_ = null;
|
|
1680
|
+
let jsonRaw = null;
|
|
1681
|
+
let session_id = null;
|
|
1682
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1683
|
+
const a = argv[i];
|
|
1684
|
+
if (a === "--type") {
|
|
1685
|
+
type_ = argv[++i] ?? null;
|
|
1686
|
+
} else if (a.startsWith("--type=")) {
|
|
1687
|
+
type_ = a.slice("--type=".length);
|
|
1688
|
+
} else if (a === "--json") {
|
|
1689
|
+
jsonRaw = argv[++i] ?? null;
|
|
1690
|
+
} else if (a.startsWith("--json=")) {
|
|
1691
|
+
jsonRaw = a.slice("--json=".length);
|
|
1692
|
+
} else if (a === "--session-id") {
|
|
1693
|
+
session_id = argv[++i] ?? null;
|
|
1694
|
+
} else if (a.startsWith("--session-id=")) {
|
|
1695
|
+
session_id = a.slice("--session-id=".length);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
const entry = jsonRaw ? JSON.parse(jsonRaw) : {};
|
|
1699
|
+
if (!("t" in entry)) {
|
|
1700
|
+
entry.t = type_;
|
|
1701
|
+
}
|
|
1702
|
+
if (!entry.t) {
|
|
1703
|
+
_eprint("error: --type or a 't' key in --json is required");
|
|
1704
|
+
return EXIT_BAD_ARGS;
|
|
1705
|
+
}
|
|
1706
|
+
const session = session_id ? derive_session_tag(session_id) : null;
|
|
1707
|
+
append(entry, { session });
|
|
1708
|
+
return EXIT_OK;
|
|
1709
|
+
}
|
|
1710
|
+
function _cmd_status() {
|
|
1711
|
+
_print(_pyDumpsIndent2(status()));
|
|
1712
|
+
return 0;
|
|
1713
|
+
}
|
|
1714
|
+
function _loadEntriesArg(entriesJson, entriesStdin) {
|
|
1715
|
+
const raw = entriesStdin ? _readStdin() : entriesJson || "[]";
|
|
1716
|
+
let data;
|
|
1717
|
+
try {
|
|
1718
|
+
data = JSON.parse(raw);
|
|
1719
|
+
} catch (exc) {
|
|
1720
|
+
throw new ValueError(`invalid JSON for entries: ${_excStr(exc)}`);
|
|
1721
|
+
}
|
|
1722
|
+
if (!Array.isArray(data)) {
|
|
1723
|
+
throw new ValueError("entries must be a JSON array");
|
|
1724
|
+
}
|
|
1725
|
+
return data;
|
|
1726
|
+
}
|
|
1727
|
+
function _cmd_reset(argv) {
|
|
1728
|
+
let freq = "per_phase";
|
|
1729
|
+
let entriesJson = null;
|
|
1730
|
+
let entriesStdin = false;
|
|
1731
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1732
|
+
const a = argv[i];
|
|
1733
|
+
if (a === "--freq") {
|
|
1734
|
+
freq = argv[++i] ?? freq;
|
|
1735
|
+
} else if (a.startsWith("--freq=")) {
|
|
1736
|
+
freq = a.slice("--freq=".length);
|
|
1737
|
+
} else if (a === "--entries-json") {
|
|
1738
|
+
entriesJson = argv[++i] ?? null;
|
|
1739
|
+
} else if (a.startsWith("--entries-json=")) {
|
|
1740
|
+
entriesJson = a.slice("--entries-json=".length);
|
|
1741
|
+
} else if (a === "--entries-stdin") {
|
|
1742
|
+
entriesStdin = true;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
if (!VALID_FREQS.has(freq)) {
|
|
1746
|
+
return _argError(
|
|
1747
|
+
`usage: ${PROG} reset [-h] [--freq {per_phase,per_tool,per_turn}]
|
|
1748
|
+
(--entries-json ENTRIES_JSON | --entries-stdin)
|
|
1749
|
+
`,
|
|
1750
|
+
`${PROG} reset`,
|
|
1751
|
+
`argument --freq: invalid choice: ${_pyRepr(freq)} (choose from 'per_phase', 'per_tool', 'per_turn')`
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
if (entriesJson === null && !entriesStdin) {
|
|
1755
|
+
return _argError(
|
|
1756
|
+
`usage: ${PROG} reset [-h] [--freq {per_phase,per_tool,per_turn}]
|
|
1757
|
+
(--entries-json ENTRIES_JSON | --entries-stdin)
|
|
1758
|
+
`,
|
|
1759
|
+
`${PROG} reset`,
|
|
1760
|
+
"one of the arguments --entries-json --entries-stdin is required"
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
const entries = _loadEntriesArg(entriesJson, entriesStdin);
|
|
1764
|
+
const h = reset_with_entries(entries, freq);
|
|
1765
|
+
_print(_pyDumps(h));
|
|
1766
|
+
return 0;
|
|
1767
|
+
}
|
|
1768
|
+
function _cmd_prune_sessions(argv) {
|
|
1769
|
+
let maxSessions = null;
|
|
1770
|
+
let settings = null;
|
|
1771
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1772
|
+
const a = argv[i];
|
|
1773
|
+
if (a === "--max-sessions") {
|
|
1774
|
+
maxSessions = _pyIntFromAny(argv[++i] ?? "");
|
|
1775
|
+
} else if (a.startsWith("--max-sessions=")) {
|
|
1776
|
+
maxSessions = _pyIntFromAny(a.slice("--max-sessions=".length));
|
|
1777
|
+
} else if (a === "--settings") {
|
|
1778
|
+
settings = argv[++i] ?? null;
|
|
1779
|
+
} else if (a.startsWith("--settings=")) {
|
|
1780
|
+
settings = a.slice("--settings=".length);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
let max_n;
|
|
1784
|
+
if (maxSessions !== null) {
|
|
1785
|
+
max_n = Math.max(1, maxSessions);
|
|
1786
|
+
} else {
|
|
1787
|
+
const sp = settings ?? DEFAULT_SETTINGS_FILE;
|
|
1788
|
+
max_n = _read_chat_history_max_sessions(sp);
|
|
1789
|
+
}
|
|
1790
|
+
const result = prune_sessions(max_n);
|
|
1791
|
+
_print(_pyDumps(result));
|
|
1792
|
+
return EXIT_OK;
|
|
1793
|
+
}
|
|
1794
|
+
function _cmd_prepend(argv) {
|
|
1795
|
+
let entriesJson = null;
|
|
1796
|
+
let entriesStdin = false;
|
|
1797
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1798
|
+
const a = argv[i];
|
|
1799
|
+
if (a === "--entries-json") {
|
|
1800
|
+
entriesJson = argv[++i] ?? null;
|
|
1801
|
+
} else if (a.startsWith("--entries-json=")) {
|
|
1802
|
+
entriesJson = a.slice("--entries-json=".length);
|
|
1803
|
+
} else if (a === "--entries-stdin") {
|
|
1804
|
+
entriesStdin = true;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
if (entriesJson === null && !entriesStdin) {
|
|
1808
|
+
return _argError(
|
|
1809
|
+
`usage: ${PROG} prepend [-h] (--entries-json ENTRIES_JSON | --entries-stdin)
|
|
1810
|
+
`,
|
|
1811
|
+
`${PROG} prepend`,
|
|
1812
|
+
"one of the arguments --entries-json --entries-stdin is required"
|
|
1813
|
+
);
|
|
1814
|
+
}
|
|
1815
|
+
const entries = _loadEntriesArg(entriesJson, entriesStdin);
|
|
1816
|
+
const n = prepend_entries(entries);
|
|
1817
|
+
_print(_pyDumps({ prepended: n }));
|
|
1818
|
+
return 0;
|
|
1819
|
+
}
|
|
1820
|
+
function _cmd_clear() {
|
|
1821
|
+
clear();
|
|
1822
|
+
return 0;
|
|
1823
|
+
}
|
|
1824
|
+
function _cmd_read(argv) {
|
|
1825
|
+
let last = 5;
|
|
1826
|
+
let lastSeen = false;
|
|
1827
|
+
let all = false;
|
|
1828
|
+
let session = null;
|
|
1829
|
+
let agent = null;
|
|
1830
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1831
|
+
const a = argv[i];
|
|
1832
|
+
if (a === "--last") {
|
|
1833
|
+
last = _pyIntFromAny(argv[++i] ?? "");
|
|
1834
|
+
lastSeen = true;
|
|
1835
|
+
} else if (a.startsWith("--last=")) {
|
|
1836
|
+
last = _pyIntFromAny(a.slice("--last=".length));
|
|
1837
|
+
lastSeen = true;
|
|
1838
|
+
} else if (a === "--all") {
|
|
1839
|
+
all = true;
|
|
1840
|
+
} else if (a === "--session") {
|
|
1841
|
+
session = argv[++i] ?? null;
|
|
1842
|
+
} else if (a.startsWith("--session=")) {
|
|
1843
|
+
session = a.slice("--session=".length);
|
|
1844
|
+
} else if (a === "--agent") {
|
|
1845
|
+
agent = argv[++i] ?? null;
|
|
1846
|
+
} else if (a.startsWith("--agent=")) {
|
|
1847
|
+
agent = a.slice("--agent=".length);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
if (lastSeen && all) {
|
|
1851
|
+
return _argError(
|
|
1852
|
+
`usage: ${PROG} read [-h] [--last LAST | --all] [--session SESSION]
|
|
1853
|
+
[--agent AGENT]
|
|
1854
|
+
`,
|
|
1855
|
+
`${PROG} read`,
|
|
1856
|
+
"argument --all: not allowed with argument --last"
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1859
|
+
const lastArg = all ? null : last;
|
|
1860
|
+
let entries;
|
|
1861
|
+
if (all) {
|
|
1862
|
+
entries = read_entries({ last: lastArg, session: null, agent });
|
|
1863
|
+
} else if (session !== null) {
|
|
1864
|
+
entries = read_entries({ last: lastArg, session, agent });
|
|
1865
|
+
} else if (agent !== null) {
|
|
1866
|
+
const sid = _last_body_session_id(file_path());
|
|
1867
|
+
entries = read_entries({ last: lastArg, session: sid, agent });
|
|
1868
|
+
} else {
|
|
1869
|
+
entries = read_entries_for_current({ last: lastArg });
|
|
1870
|
+
}
|
|
1871
|
+
_print(_pyDumpsIndent2(entries));
|
|
1872
|
+
return 0;
|
|
1873
|
+
}
|
|
1874
|
+
function _cmd_sessions(argv) {
|
|
1875
|
+
let limit = 20;
|
|
1876
|
+
let includeEmpty = false;
|
|
1877
|
+
let json = false;
|
|
1878
|
+
let summary = false;
|
|
1879
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1880
|
+
const a = argv[i];
|
|
1881
|
+
if (a === "--limit") {
|
|
1882
|
+
limit = _pyIntFromAny(argv[++i] ?? "");
|
|
1883
|
+
} else if (a.startsWith("--limit=")) {
|
|
1884
|
+
limit = _pyIntFromAny(a.slice("--limit=".length));
|
|
1885
|
+
} else if (a === "--include-empty") {
|
|
1886
|
+
includeEmpty = true;
|
|
1887
|
+
} else if (a === "--json") {
|
|
1888
|
+
json = true;
|
|
1889
|
+
} else if (a === "--summary") {
|
|
1890
|
+
summary = true;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
let sessions = list_sessions({ summary });
|
|
1894
|
+
if (!includeEmpty) {
|
|
1895
|
+
sessions = sessions.filter((s) => s.count > 0);
|
|
1896
|
+
}
|
|
1897
|
+
sessions = sessions.slice(0, limit);
|
|
1898
|
+
if (json) {
|
|
1899
|
+
_print(_pyDumpsIndent2(sessions));
|
|
1900
|
+
return 0;
|
|
1901
|
+
}
|
|
1902
|
+
if (sessions.length === 0) {
|
|
1903
|
+
_print("(no sessions)");
|
|
1904
|
+
return 0;
|
|
1905
|
+
}
|
|
1906
|
+
const lastCol = summary ? "SUMMARY" : "PREVIEW";
|
|
1907
|
+
const rows = [["ID", "COUNT", "AGENTS", "LAST_TS", lastCol]];
|
|
1908
|
+
for (const s of sessions) {
|
|
1909
|
+
const lastVal = summary ? s.summary : s.preview;
|
|
1910
|
+
const agentsVal = (s.agents || []).join(",") || "-";
|
|
1911
|
+
rows.push([s.id, String(s.count), agentsVal, s.last_ts || "-", lastVal || "-"]);
|
|
1912
|
+
}
|
|
1913
|
+
const widths = [];
|
|
1914
|
+
for (let i = 0; i < 5; i++) {
|
|
1915
|
+
widths.push(Math.max(...rows.map((r) => _pyLen(r[i]))));
|
|
1916
|
+
}
|
|
1917
|
+
for (let i = 0; i < rows.length; i++) {
|
|
1918
|
+
const r = rows[i];
|
|
1919
|
+
const line = r.map((c, j) => _ljust(c, widths[j])).join(" ");
|
|
1920
|
+
_print(line);
|
|
1921
|
+
if (i === 0) {
|
|
1922
|
+
_print(widths.map((w) => "-".repeat(w)).join(" "));
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
return 0;
|
|
1926
|
+
}
|
|
1927
|
+
function _cmd_rotate(argv) {
|
|
1928
|
+
let maxKb = 256;
|
|
1929
|
+
let mode = "rotate";
|
|
1930
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1931
|
+
const a = argv[i];
|
|
1932
|
+
if (a === "--max-kb") {
|
|
1933
|
+
maxKb = _pyIntFromAny(argv[++i] ?? "");
|
|
1934
|
+
} else if (a.startsWith("--max-kb=")) {
|
|
1935
|
+
maxKb = _pyIntFromAny(a.slice("--max-kb=".length));
|
|
1936
|
+
} else if (a === "--mode") {
|
|
1937
|
+
mode = argv[++i] ?? mode;
|
|
1938
|
+
} else if (a.startsWith("--mode=")) {
|
|
1939
|
+
mode = a.slice("--mode=".length);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
if (!VALID_OVERFLOW.has(mode)) {
|
|
1943
|
+
return _argError(
|
|
1944
|
+
`usage: ${PROG} rotate [-h] [--max-kb MAX_KB]
|
|
1945
|
+
[--mode {condense,rotate}]
|
|
1946
|
+
`,
|
|
1947
|
+
`${PROG} rotate`,
|
|
1948
|
+
`argument --mode: invalid choice: ${_pyRepr(mode)} (choose from 'condense', 'rotate')`
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
const result = overflow_handle(maxKb, mode);
|
|
1952
|
+
_print(_pyDumps(result));
|
|
1953
|
+
return 0;
|
|
1954
|
+
}
|
|
1955
|
+
function _readStdin() {
|
|
1956
|
+
return readHookStdin();
|
|
1957
|
+
}
|
|
1958
|
+
function main(argv = process.argv.slice(2)) {
|
|
1959
|
+
const cmd = argv[0];
|
|
1960
|
+
if (cmd === void 0) {
|
|
1961
|
+
return _argError(_TOP_USAGE, PROG, "the following arguments are required: cmd");
|
|
1962
|
+
}
|
|
1963
|
+
if (!SUBCMDS.includes(cmd)) {
|
|
1964
|
+
const choices = SUBCMDS.map((c) => _pyRepr(c)).join(", ");
|
|
1965
|
+
return _argError(
|
|
1966
|
+
_TOP_USAGE,
|
|
1967
|
+
PROG,
|
|
1968
|
+
`argument cmd: invalid choice: ${_pyRepr(cmd)} (choose from ${choices})`
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
const rest = argv.slice(1);
|
|
1972
|
+
switch (cmd) {
|
|
1973
|
+
case "init":
|
|
1974
|
+
return _cmd_init(rest);
|
|
1975
|
+
case "append":
|
|
1976
|
+
return _cmd_append(rest);
|
|
1977
|
+
case "status":
|
|
1978
|
+
return _cmd_status();
|
|
1979
|
+
case "reset":
|
|
1980
|
+
return _cmd_reset(rest);
|
|
1981
|
+
case "prune-sessions":
|
|
1982
|
+
return _cmd_prune_sessions(rest);
|
|
1983
|
+
case "prepend":
|
|
1984
|
+
return _cmd_prepend(rest);
|
|
1985
|
+
case "clear":
|
|
1986
|
+
return _cmd_clear();
|
|
1987
|
+
case "read":
|
|
1988
|
+
return _cmd_read(rest);
|
|
1989
|
+
case "sessions":
|
|
1990
|
+
return _cmd_sessions(rest);
|
|
1991
|
+
case "rotate":
|
|
1992
|
+
return _cmd_rotate(rest);
|
|
1993
|
+
case "hook-append":
|
|
1994
|
+
return _cmd_hook_append(rest);
|
|
1995
|
+
case "hook-dispatch":
|
|
1996
|
+
return _cmd_hook_dispatch(rest);
|
|
1997
|
+
default:
|
|
1998
|
+
return EXIT_BAD_ARGS;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
function _isCliEntry() {
|
|
2002
|
+
if (true) {
|
|
2003
|
+
return false;
|
|
2004
|
+
}
|
|
2005
|
+
if (process.argv[1] === void 0) {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
2009
|
+
if (import.meta.url === argvUrl) {
|
|
2010
|
+
return true;
|
|
2011
|
+
}
|
|
2012
|
+
try {
|
|
2013
|
+
const here = fs2.realpathSync(fileURLToPath(import.meta.url));
|
|
2014
|
+
const argv = fs2.realpathSync(path.resolve(process.argv[1]));
|
|
2015
|
+
return here === argv;
|
|
2016
|
+
} catch {
|
|
2017
|
+
return false;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
var _invokedDirectly = _isCliEntry();
|
|
2021
|
+
if (_invokedDirectly) {
|
|
2022
|
+
try {
|
|
2023
|
+
process.exitCode = main();
|
|
2024
|
+
} catch (exc) {
|
|
2025
|
+
if (exc instanceof ValueError || exc instanceof FileNotFoundError) {
|
|
2026
|
+
process.stderr.write(`${exc.constructor.name}: ${exc.message}
|
|
2027
|
+
`);
|
|
2028
|
+
process.exitCode = 1;
|
|
2029
|
+
} else {
|
|
2030
|
+
throw exc;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
export {
|
|
2036
|
+
readHookStdin,
|
|
2037
|
+
derive_session_tag,
|
|
2038
|
+
read_entries,
|
|
2039
|
+
list_sessions
|
|
2040
|
+
};
|