@voybio/ace-swarm 0.1.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/CHANGELOG.md +109 -0
- package/LICENSE +186 -0
- package/README.md +229 -0
- package/assets/.agents/ACE/ACE-Init/AGENTS.md +210 -0
- package/assets/.agents/ACE/ACE-Init/instructions.md +118 -0
- package/assets/.agents/ACE/ACE_coders/AGENTS.md +154 -0
- package/assets/.agents/ACE/ACE_coders/INSTRUCTIONS.md +216 -0
- package/assets/.agents/ACE/AGENT_REGISTRY.md +70 -0
- package/assets/.agents/ACE/AGENT_REGISTRY_7.md +9 -0
- package/assets/.agents/ACE/DIRECTIVE_KERNEL.md +234 -0
- package/assets/.agents/ACE/UI/AGENTS.md +115 -0
- package/assets/.agents/ACE/UI/instructions.md +178 -0
- package/assets/.agents/ACE/VOS/ACE_VOS_MISSING_INFO_MATRIX.md +42 -0
- package/assets/.agents/ACE/VOS/AGENTS.md +72 -0
- package/assets/.agents/ACE/VOS/instructions.md +211 -0
- package/assets/.agents/ACE/agent-astgrep/AGENTS.md +123 -0
- package/assets/.agents/ACE/agent-astgrep/instructions.md +91 -0
- package/assets/.agents/ACE/agent-builder/AGENTS.md +172 -0
- package/assets/.agents/ACE/agent-builder/instructions.md +137 -0
- package/assets/.agents/ACE/agent-docs/AGENTS.md +159 -0
- package/assets/.agents/ACE/agent-docs/instructions.md +133 -0
- package/assets/.agents/ACE/agent-eval/AGENTS.md +46 -0
- package/assets/.agents/ACE/agent-eval/instructions.md +56 -0
- package/assets/.agents/ACE/agent-memory/AGENTS.md +49 -0
- package/assets/.agents/ACE/agent-memory/instructions.md +50 -0
- package/assets/.agents/ACE/agent-observability/AGENTS.md +46 -0
- package/assets/.agents/ACE/agent-observability/instructions.md +50 -0
- package/assets/.agents/ACE/agent-ops/AGENTS.md +201 -0
- package/assets/.agents/ACE/agent-ops/instructions.md +136 -0
- package/assets/.agents/ACE/agent-qa/AGENTS.md +189 -0
- package/assets/.agents/ACE/agent-qa/instructions.md +121 -0
- package/assets/.agents/ACE/agent-release/AGENTS.md +48 -0
- package/assets/.agents/ACE/agent-release/instructions.md +49 -0
- package/assets/.agents/ACE/agent-research/AGENTS.md +160 -0
- package/assets/.agents/ACE/agent-research/instructions.md +118 -0
- package/assets/.agents/ACE/agent-security/AGENTS.md +48 -0
- package/assets/.agents/ACE/agent-security/instructions.md +50 -0
- package/assets/.agents/ACE/agent-skeptic/AGENTS.md +178 -0
- package/assets/.agents/ACE/agent-skeptic/instructions.md +196 -0
- package/assets/.agents/ACE/agent-spec/AGENTS.md +169 -0
- package/assets/.agents/ACE/agent-spec/instructions.md +116 -0
- package/assets/.agents/ACE/orchestrator/AGENTS.md +365 -0
- package/assets/.agents/ACE/orchestrator/instructions.md +231 -0
- package/assets/.agents/skills/ace-orchestrator/SKILL.md +63 -0
- package/assets/.agents/skills/ace-orchestrator/references/engineering-bootstrap-playbook.md +360 -0
- package/assets/.agents/skills/astgrep-index/SKILL.md +58 -0
- package/assets/.agents/skills/codemunch/SKILL.md +65 -0
- package/assets/.agents/skills/codemunch/references/ast-driven-protocol.md +543 -0
- package/assets/.agents/skills/codesnipe/SKILL.md +64 -0
- package/assets/.agents/skills/codesnipe/references/dual-codebase-playbook.md +671 -0
- package/assets/.agents/skills/eval-harness/SKILL.md +203 -0
- package/assets/.agents/skills/handoff-lint/SKILL.md +164 -0
- package/assets/.agents/skills/incident-commander/SKILL.md +174 -0
- package/assets/.agents/skills/landing-review-watcher/SKILL.md +68 -0
- package/assets/.agents/skills/memory-curator/SKILL.md +179 -0
- package/assets/.agents/skills/problem-triage/SKILL.md +57 -0
- package/assets/.agents/skills/problem-triage/agents/openai.yaml +3 -0
- package/assets/.agents/skills/release-sentry/SKILL.md +189 -0
- package/assets/.agents/skills/risk-quant/SKILL.md +190 -0
- package/assets/.agents/skills/schema-forge/SKILL.md +174 -0
- package/assets/.agents/skills/skill-auditor/SKILL.md +52 -0
- package/assets/.agents/skills/state-auditor/SKILL.md +182 -0
- package/assets/.github/hooks/ace-copilot.json +68 -0
- package/assets/agent-state/ACE_WORKFLOW.md +131 -0
- package/assets/agent-state/ARTIFACT_MANIFEST.json +5 -0
- package/assets/agent-state/AST_GREP_COMMANDS.md +121 -0
- package/assets/agent-state/AST_GREP_INDEX.json +13 -0
- package/assets/agent-state/AST_GREP_INDEX.md +15 -0
- package/assets/agent-state/DECISIONS.md +7 -0
- package/assets/agent-state/EVIDENCE_LOG.md +7 -0
- package/assets/agent-state/HANDOFF.json +24 -0
- package/assets/agent-state/INTERFACE_REGISTRY.md +75 -0
- package/assets/agent-state/MODULES/gates/gate-autonomy.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-completeness.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-correctness.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-evaluation.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-operability.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-security.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-typescript-public-surface.json +7 -0
- package/assets/agent-state/MODULES/registry.json +41 -0
- package/assets/agent-state/MODULES/roles/capability-astgrep.json +49 -0
- package/assets/agent-state/MODULES/roles/capability-build.json +39 -0
- package/assets/agent-state/MODULES/roles/capability-docs.json +38 -0
- package/assets/agent-state/MODULES/roles/capability-eval.json +20 -0
- package/assets/agent-state/MODULES/roles/capability-memory.json +20 -0
- package/assets/agent-state/MODULES/roles/capability-observability.json +20 -0
- package/assets/agent-state/MODULES/roles/capability-ops.json +45 -0
- package/assets/agent-state/MODULES/roles/capability-qa.json +40 -0
- package/assets/agent-state/MODULES/roles/capability-release.json +21 -0
- package/assets/agent-state/MODULES/roles/capability-research.json +44 -0
- package/assets/agent-state/MODULES/roles/capability-security.json +21 -0
- package/assets/agent-state/MODULES/roles/capability-skeptic.json +48 -0
- package/assets/agent-state/MODULES/roles/capability-spec.json +42 -0
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +289 -0
- package/assets/agent-state/MODULES/schemas/ARTIFACT_MANIFEST.schema.json +185 -0
- package/assets/agent-state/MODULES/schemas/HANDOFF.agent-state.schema.json +124 -0
- package/assets/agent-state/MODULES/schemas/HANDOFF.schema.json +55 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +290 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +144 -0
- package/assets/agent-state/MODULES/schemas/STATUS_EVENT.schema.json +84 -0
- package/assets/agent-state/MODULES/schemas/SWARM_HANDOFF.schema.json +138 -0
- package/assets/agent-state/MODULES/schemas/TRACKER_SNAPSHOT.schema.json +134 -0
- package/assets/agent-state/MODULES/schemas/VERICIFY_BRIDGE_SNAPSHOT.schema.json +157 -0
- package/assets/agent-state/MODULES/schemas/VERICIFY_PROCESS_POST_LOG.schema.json +93 -0
- package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +133 -0
- package/assets/agent-state/PROVENANCE_LOG.md +28 -0
- package/assets/agent-state/QUALITY_GATES.md +15 -0
- package/assets/agent-state/RISKS.md +8 -0
- package/assets/agent-state/SCOPE.md +20 -0
- package/assets/agent-state/SKILL_CATALOG.md +48 -0
- package/assets/agent-state/STATUS.md +8 -0
- package/assets/agent-state/STATUS_EVENTS.ndjson +1 -0
- package/assets/agent-state/TASK.md +18 -0
- package/assets/agent-state/TEAL_CONFIG.md +117 -0
- package/assets/agent-state/handoff-registry.json +5 -0
- package/assets/agent-state/index-fingerprints.json +7 -0
- package/assets/agent-state/index.json +32 -0
- package/assets/agent-state/run-ledger.json +5 -0
- package/assets/agent-state/runtime-executor-sessions.json +5 -0
- package/assets/agent-state/runtime-tool-specs.json +5 -0
- package/assets/agent-state/runtime-workspaces.json +5 -0
- package/assets/agent-state/todo-state.json +7 -0
- package/assets/agent-state/tracker-snapshot.json +7 -0
- package/assets/agent-state/vericify/ace-bridge.json +60 -0
- package/assets/agent-state/vericify/process-posts.json +5 -0
- package/assets/instructions/ACE.instructions.md +187 -0
- package/assets/instructions/ACE_Coder.instructions.md +146 -0
- package/assets/instructions/ACE_UI.instructions.md +178 -0
- package/assets/instructions/ACE_VOS.instructions.md +211 -0
- package/assets/scripts/ace-hook-dispatch.mjs +538 -0
- package/assets/scripts/bootstrap-workspace.sh +27 -0
- package/assets/scripts/copilot-hook-dispatch.mjs +3 -0
- package/assets/scripts/eval-harness.sh +68 -0
- package/assets/scripts/render-mcp-configs.sh +396 -0
- package/assets/tasks/README.md +48 -0
- package/assets/tasks/SWARM_HANDOFF.example.json +53 -0
- package/assets/tasks/SWARM_HANDOFF.example_ui_to_coders.json +55 -0
- package/assets/tasks/SWARM_HANDOFF.example_vos_to_ui.json +55 -0
- package/assets/tasks/SWARM_HANDOFF.template.json +52 -0
- package/assets/tasks/cli_work_split.md +22 -0
- package/assets/tasks/lessons.md +17 -0
- package/assets/tasks/role_tasks.md +206 -0
- package/assets/tasks/todo.md +23 -0
- package/dist/ace-autonomy.d.ts +137 -0
- package/dist/ace-autonomy.js +472 -0
- package/dist/ace-context.d.ts +29 -0
- package/dist/ace-context.js +240 -0
- package/dist/ace-internal-tools.d.ts +8 -0
- package/dist/ace-internal-tools.js +76 -0
- package/dist/ace-server-instructions.d.ts +12 -0
- package/dist/ace-server-instructions.js +324 -0
- package/dist/agent-runtime/role-adapters.d.ts +29 -0
- package/dist/agent-runtime/role-adapters.js +573 -0
- package/dist/astgrep-index.d.ts +24 -0
- package/dist/astgrep-index.js +476 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +591 -0
- package/dist/git-ops.d.ts +53 -0
- package/dist/git-ops.js +238 -0
- package/dist/handoff-registry.d.ts +71 -0
- package/dist/handoff-registry.js +422 -0
- package/dist/helpers.d.ts +126 -0
- package/dist/helpers.js +1687 -0
- package/dist/index-store.d.ts +51 -0
- package/dist/index-store.js +328 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/internal-tool-runtime.d.ts +21 -0
- package/dist/internal-tool-runtime.js +136 -0
- package/dist/job-scheduler.d.ts +175 -0
- package/dist/job-scheduler.js +1217 -0
- package/dist/kanban.d.ts +27 -0
- package/dist/kanban.js +339 -0
- package/dist/local-model-runtime.d.ts +40 -0
- package/dist/local-model-runtime.js +174 -0
- package/dist/model-bridge.d.ts +54 -0
- package/dist/model-bridge.js +587 -0
- package/dist/orchestrator-supervisor.d.ts +100 -0
- package/dist/orchestrator-supervisor.js +399 -0
- package/dist/problem-triage.d.ts +23 -0
- package/dist/problem-triage.js +448 -0
- package/dist/prompts.d.ts +7 -0
- package/dist/prompts.js +628 -0
- package/dist/public-surface.d.ts +30 -0
- package/dist/public-surface.js +316 -0
- package/dist/resources.d.ts +7 -0
- package/dist/resources.js +545 -0
- package/dist/run-ledger.d.ts +36 -0
- package/dist/run-ledger.js +257 -0
- package/dist/runtime-command.d.ts +18 -0
- package/dist/runtime-command.js +76 -0
- package/dist/runtime-executor.d.ts +104 -0
- package/dist/runtime-executor.js +985 -0
- package/dist/runtime-profile.d.ts +116 -0
- package/dist/runtime-profile.js +532 -0
- package/dist/runtime-tool-specs.d.ts +68 -0
- package/dist/runtime-tool-specs.js +527 -0
- package/dist/safe-edit.d.ts +52 -0
- package/dist/safe-edit.js +255 -0
- package/dist/schemas.d.ts +44 -0
- package/dist/schemas.js +830 -0
- package/dist/semantic-cache.d.ts +147 -0
- package/dist/semantic-cache.js +552 -0
- package/dist/semantic-hash.d.ts +83 -0
- package/dist/semantic-hash.js +346 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +46 -0
- package/dist/shared.d.ts +136 -0
- package/dist/shared.js +269 -0
- package/dist/skill-auditor.d.ts +26 -0
- package/dist/skill-auditor.js +184 -0
- package/dist/skill-catalog.d.ts +60 -0
- package/dist/skill-catalog.js +305 -0
- package/dist/status-events.d.ts +40 -0
- package/dist/status-events.js +269 -0
- package/dist/store/ace-packed-store.d.ts +69 -0
- package/dist/store/ace-packed-store.js +434 -0
- package/dist/store/bootstrap-store.d.ts +46 -0
- package/dist/store/bootstrap-store.js +242 -0
- package/dist/store/catalog-builder.d.ts +21 -0
- package/dist/store/catalog-builder.js +68 -0
- package/dist/store/importer.d.ts +19 -0
- package/dist/store/importer.js +157 -0
- package/dist/store/knowledge-bake.d.ts +59 -0
- package/dist/store/knowledge-bake.js +339 -0
- package/dist/store/materializers/hook-context-materializer.d.ts +25 -0
- package/dist/store/materializers/hook-context-materializer.js +100 -0
- package/dist/store/materializers/host-file-materializer.d.ts +37 -0
- package/dist/store/materializers/host-file-materializer.js +271 -0
- package/dist/store/materializers/todo-syncer.d.ts +30 -0
- package/dist/store/materializers/todo-syncer.js +140 -0
- package/dist/store/materializers/vericify-projector.d.ts +38 -0
- package/dist/store/materializers/vericify-projector.js +239 -0
- package/dist/store/repositories/discovery-repository.d.ts +24 -0
- package/dist/store/repositories/discovery-repository.js +58 -0
- package/dist/store/repositories/handoff-repository.d.ts +31 -0
- package/dist/store/repositories/handoff-repository.js +67 -0
- package/dist/store/repositories/ledger-repository.d.ts +26 -0
- package/dist/store/repositories/ledger-repository.js +49 -0
- package/dist/store/repositories/runtime-kv-repository.d.ts +16 -0
- package/dist/store/repositories/runtime-kv-repository.js +36 -0
- package/dist/store/repositories/scheduler-repository.d.ts +50 -0
- package/dist/store/repositories/scheduler-repository.js +123 -0
- package/dist/store/repositories/session-repository.d.ts +33 -0
- package/dist/store/repositories/session-repository.js +82 -0
- package/dist/store/repositories/todo-repository.d.ts +31 -0
- package/dist/store/repositories/todo-repository.js +77 -0
- package/dist/store/repositories/tracker-repository.d.ts +25 -0
- package/dist/store/repositories/tracker-repository.js +43 -0
- package/dist/store/repositories/vericify-repository.d.ts +32 -0
- package/dist/store/repositories/vericify-repository.js +58 -0
- package/dist/store/skills-install.d.ts +28 -0
- package/dist/store/skills-install.js +86 -0
- package/dist/store/state-reader.d.ts +49 -0
- package/dist/store/state-reader.js +111 -0
- package/dist/store/store-artifacts.d.ts +12 -0
- package/dist/store/store-artifacts.js +138 -0
- package/dist/store/store-snapshot.d.ts +19 -0
- package/dist/store/store-snapshot.js +140 -0
- package/dist/store/topology-bake.d.ts +15 -0
- package/dist/store/topology-bake.js +215 -0
- package/dist/store/types.d.ts +155 -0
- package/dist/store/types.js +35 -0
- package/dist/store/workspace-snapshot.d.ts +26 -0
- package/dist/store/workspace-snapshot.js +107 -0
- package/dist/store/write-queue.d.ts +7 -0
- package/dist/store/write-queue.js +26 -0
- package/dist/todo-state.d.ts +41 -0
- package/dist/todo-state.js +399 -0
- package/dist/tools-agent.d.ts +7 -0
- package/dist/tools-agent.js +1542 -0
- package/dist/tools-discovery.d.ts +6 -0
- package/dist/tools-discovery.js +178 -0
- package/dist/tools-drift.d.ts +13 -0
- package/dist/tools-drift.js +357 -0
- package/dist/tools-files.d.ts +6 -0
- package/dist/tools-files.js +679 -0
- package/dist/tools-framework.d.ts +7 -0
- package/dist/tools-framework.js +1414 -0
- package/dist/tools-git.d.ts +6 -0
- package/dist/tools-git.js +183 -0
- package/dist/tools-handoff.d.ts +32 -0
- package/dist/tools-handoff.js +489 -0
- package/dist/tools-lifecycle.d.ts +6 -0
- package/dist/tools-lifecycle.js +205 -0
- package/dist/tools-memory.d.ts +6 -0
- package/dist/tools-memory.js +260 -0
- package/dist/tools-scheduler.d.ts +6 -0
- package/dist/tools-scheduler.js +228 -0
- package/dist/tools-skills.d.ts +3 -0
- package/dist/tools-skills.js +104 -0
- package/dist/tools-todo.d.ts +6 -0
- package/dist/tools-todo.js +154 -0
- package/dist/tools.d.ts +9 -0
- package/dist/tools.js +33 -0
- package/dist/tracker-adapters.d.ts +74 -0
- package/dist/tracker-adapters.js +776 -0
- package/dist/tracker-sync.d.ts +10 -0
- package/dist/tracker-sync.js +84 -0
- package/dist/tui/agent-runner.d.ts +137 -0
- package/dist/tui/agent-runner.js +466 -0
- package/dist/tui/agent-worker.d.ts +10 -0
- package/dist/tui/agent-worker.js +347 -0
- package/dist/tui/chat.d.ts +84 -0
- package/dist/tui/chat.js +368 -0
- package/dist/tui/commands.d.ts +57 -0
- package/dist/tui/commands.js +432 -0
- package/dist/tui/dashboard.d.ts +24 -0
- package/dist/tui/dashboard.js +110 -0
- package/dist/tui/index.d.ts +114 -0
- package/dist/tui/index.js +1059 -0
- package/dist/tui/input.d.ts +49 -0
- package/dist/tui/input.js +336 -0
- package/dist/tui/layout.d.ts +116 -0
- package/dist/tui/layout.js +367 -0
- package/dist/tui/ollama.d.ts +116 -0
- package/dist/tui/ollama.js +192 -0
- package/dist/tui/openai-compatible.d.ts +63 -0
- package/dist/tui/openai-compatible.js +370 -0
- package/dist/tui/provider-discovery.d.ts +59 -0
- package/dist/tui/provider-discovery.js +530 -0
- package/dist/tui/renderer.d.ts +166 -0
- package/dist/tui/renderer.js +304 -0
- package/dist/tui/tabs.d.ts +70 -0
- package/dist/tui/tabs.js +208 -0
- package/dist/tui/telemetry.d.ts +56 -0
- package/dist/tui/telemetry.js +106 -0
- package/dist/vericify-bridge.d.ts +146 -0
- package/dist/vericify-bridge.js +571 -0
- package/dist/vericify-context.d.ts +10 -0
- package/dist/vericify-context.js +72 -0
- package/dist/workspace-manager.d.ts +107 -0
- package/dist/workspace-manager.js +636 -0
- package/package.json +83 -0
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { DEFAULTS_ROOT, resolveWorkspaceArtifactPath as resolveWorkspaceArtifactPathHelper, resolveWorkspaceRoot, } from "./helpers.js";
|
|
6
|
+
import { validateTrackerSnapshotPayload } from "./schemas.js";
|
|
7
|
+
import { appendStatusEvent } from "./status-events.js";
|
|
8
|
+
import { readRuntimeProfile } from "./runtime-profile.js";
|
|
9
|
+
export const TRACKER_SNAPSHOT_REL_PATH = "agent-state/tracker-snapshot.json";
|
|
10
|
+
export const TRACKER_SNAPSHOT_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/TRACKER_SNAPSHOT.schema.json";
|
|
11
|
+
export const TRACKER_SNAPSHOT_SCHEMA_NAME = "tracker-snapshot@1.0.0";
|
|
12
|
+
const DEFAULT_TRACKER_SNAPSHOT_PATH = resolve(DEFAULTS_ROOT, TRACKER_SNAPSHOT_REL_PATH);
|
|
13
|
+
function defaultTrackerSnapshot(kind = "none") {
|
|
14
|
+
return {
|
|
15
|
+
version: 1,
|
|
16
|
+
adapter_kind: kind,
|
|
17
|
+
updated_at: "1970-01-01T00:00:00.000Z",
|
|
18
|
+
items: [],
|
|
19
|
+
comments: [],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function normalizeSource(path) {
|
|
23
|
+
return path === DEFAULT_TRACKER_SNAPSHOT_PATH || path.startsWith(DEFAULTS_ROOT)
|
|
24
|
+
? "package-defaults"
|
|
25
|
+
: "workspace";
|
|
26
|
+
}
|
|
27
|
+
function resolveTrackerSnapshotTarget(explicitPath) {
|
|
28
|
+
if (explicitPath) {
|
|
29
|
+
const resolved = explicitPath.startsWith("/")
|
|
30
|
+
? resolve(explicitPath)
|
|
31
|
+
: resolveWorkspaceArtifactPathHelper(explicitPath, "read");
|
|
32
|
+
return { path: resolved, source: normalizeSource(resolved) };
|
|
33
|
+
}
|
|
34
|
+
const workspacePath = resolveWorkspaceArtifactPathHelper(TRACKER_SNAPSHOT_REL_PATH, "read");
|
|
35
|
+
if (existsSync(workspacePath)) {
|
|
36
|
+
return { path: workspacePath, source: "workspace" };
|
|
37
|
+
}
|
|
38
|
+
return { path: DEFAULT_TRACKER_SNAPSHOT_PATH, source: "package-defaults" };
|
|
39
|
+
}
|
|
40
|
+
function resolveWorkspaceArtifactPath(filePath) {
|
|
41
|
+
return resolveWorkspaceArtifactPathHelper(filePath, "write");
|
|
42
|
+
}
|
|
43
|
+
function safeWriteWorkspaceFile(filePath, content) {
|
|
44
|
+
const abs = resolveWorkspaceArtifactPath(filePath);
|
|
45
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
46
|
+
const tmpPath = `${abs}.${process.pid}.${Date.now()}.tmp`;
|
|
47
|
+
writeFileSync(tmpPath, content, "utf-8");
|
|
48
|
+
renameSync(tmpPath, abs);
|
|
49
|
+
return abs;
|
|
50
|
+
}
|
|
51
|
+
function normalizeTrackerItem(item) {
|
|
52
|
+
return {
|
|
53
|
+
...item,
|
|
54
|
+
labels: [...new Set(item.labels)].sort((a, b) => a.localeCompare(b)),
|
|
55
|
+
metadata: { ...(item.metadata ?? {}) },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function normalizeTrackerComment(comment) {
|
|
59
|
+
return {
|
|
60
|
+
...comment,
|
|
61
|
+
metadata: { ...(comment.metadata ?? {}) },
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function normalizeTrackerSnapshot(snapshot) {
|
|
65
|
+
return {
|
|
66
|
+
...snapshot,
|
|
67
|
+
items: [...snapshot.items]
|
|
68
|
+
.map(normalizeTrackerItem)
|
|
69
|
+
.sort((a, b) => a.item_id.localeCompare(b.item_id)),
|
|
70
|
+
comments: [...snapshot.comments]
|
|
71
|
+
.map(normalizeTrackerComment)
|
|
72
|
+
.sort((a, b) => a.comment_id.localeCompare(b.comment_id)),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function parseTrackerSnapshotContent(raw) {
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = JSON.parse(raw);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
throw new Error(`Tracker snapshot contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
+
}
|
|
83
|
+
const validation = validateTrackerSnapshotPayload(parsed);
|
|
84
|
+
if (!validation.ok) {
|
|
85
|
+
throw new Error(`Tracker snapshot failed validation (${validation.schema}): ${validation.errors.join("; ")}`);
|
|
86
|
+
}
|
|
87
|
+
return normalizeTrackerSnapshot(parsed);
|
|
88
|
+
}
|
|
89
|
+
function readTrackerConfig() {
|
|
90
|
+
const config = readRuntimeProfile().tracker.config;
|
|
91
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
return { ...config };
|
|
95
|
+
}
|
|
96
|
+
function readStringList(input, key) {
|
|
97
|
+
const value = input[key];
|
|
98
|
+
let strings = [];
|
|
99
|
+
if (Array.isArray(value)) {
|
|
100
|
+
strings = value
|
|
101
|
+
.filter((entry) => typeof entry === "string")
|
|
102
|
+
.map((entry) => entry.trim())
|
|
103
|
+
.filter(Boolean);
|
|
104
|
+
}
|
|
105
|
+
else if (typeof value === "string") {
|
|
106
|
+
strings = value
|
|
107
|
+
.split(",")
|
|
108
|
+
.map((entry) => entry.trim())
|
|
109
|
+
.filter(Boolean);
|
|
110
|
+
}
|
|
111
|
+
return strings.length > 0 ? strings : undefined;
|
|
112
|
+
}
|
|
113
|
+
function filterTrackerItems(items, filter) {
|
|
114
|
+
const config = readTrackerConfig();
|
|
115
|
+
const ids = Array.isArray(filter?.ids)
|
|
116
|
+
? filter.ids.filter((entry) => typeof entry === "string")
|
|
117
|
+
: undefined;
|
|
118
|
+
const states = Array.isArray(filter?.states)
|
|
119
|
+
? filter.states.filter((entry) => typeof entry === "string")
|
|
120
|
+
: readStringList(config, "active_states");
|
|
121
|
+
const labels = Array.isArray(filter?.labels)
|
|
122
|
+
? filter.labels.filter((entry) => typeof entry === "string")
|
|
123
|
+
: undefined;
|
|
124
|
+
const assignee = typeof filter?.assignee === "string" ? filter.assignee.trim() : undefined;
|
|
125
|
+
const query = typeof filter?.query === "string" ? filter.query.trim().toLowerCase() : "";
|
|
126
|
+
return items.filter((item) => {
|
|
127
|
+
if (ids && ids.length > 0) {
|
|
128
|
+
const match = ids.includes(item.item_id) || ids.includes(item.external_id ?? "");
|
|
129
|
+
if (!match)
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
if (states && states.length > 0 && !states.includes(item.state)) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (labels &&
|
|
136
|
+
labels.length > 0 &&
|
|
137
|
+
!labels.every((label) => item.labels.includes(label))) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
if (assignee && item.assignee !== assignee) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
if (query) {
|
|
144
|
+
const haystack = `${item.title}\n${item.body ?? ""}`.toLowerCase();
|
|
145
|
+
if (!haystack.includes(query))
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function emitTrackerEvent(eventType, summary, payload) {
|
|
152
|
+
try {
|
|
153
|
+
appendStatusEvent({
|
|
154
|
+
source_module: "capability-framework",
|
|
155
|
+
event_type: eventType,
|
|
156
|
+
status: eventType === "TRACKER_SNAPSHOT_REFRESH_FAILED" ? "fail" : "done",
|
|
157
|
+
summary,
|
|
158
|
+
payload,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Tracker operations must not fail because event logging failed.
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function resolveTrackerAdapterKind(kind) {
|
|
166
|
+
const candidate = (kind?.trim() || readRuntimeProfile().tracker.kind || "none");
|
|
167
|
+
if (candidate === "none" || candidate === "memory" || candidate === "external") {
|
|
168
|
+
return candidate;
|
|
169
|
+
}
|
|
170
|
+
throw new Error(`Unknown tracker adapter kind: ${candidate}`);
|
|
171
|
+
}
|
|
172
|
+
function findItemIndex(snapshot, itemId) {
|
|
173
|
+
return snapshot.items.findIndex((item) => item.item_id === itemId || item.external_id === itemId);
|
|
174
|
+
}
|
|
175
|
+
function getMemoryTrackerAuthor() {
|
|
176
|
+
const config = readTrackerConfig();
|
|
177
|
+
if (typeof config.author === "string" && config.author.trim().length > 0) {
|
|
178
|
+
return config.author.trim();
|
|
179
|
+
}
|
|
180
|
+
return "ace-memory";
|
|
181
|
+
}
|
|
182
|
+
function getExternalTrackerAuthor() {
|
|
183
|
+
const config = readTrackerConfig();
|
|
184
|
+
if (typeof config.author === "string" && config.author.trim().length > 0) {
|
|
185
|
+
return config.author.trim();
|
|
186
|
+
}
|
|
187
|
+
return "ace-external";
|
|
188
|
+
}
|
|
189
|
+
function readConfigString(config, key) {
|
|
190
|
+
const value = config[key];
|
|
191
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
192
|
+
}
|
|
193
|
+
function readConfigPositiveInt(config, key) {
|
|
194
|
+
const value = config[key];
|
|
195
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
196
|
+
}
|
|
197
|
+
function resolveExternalTrackerProvider() {
|
|
198
|
+
const config = readTrackerConfig();
|
|
199
|
+
const provider = (readConfigString(config, "provider") ?? "github").toLowerCase();
|
|
200
|
+
if (provider === "github")
|
|
201
|
+
return "github";
|
|
202
|
+
throw new Error(`Unsupported external tracker provider: ${provider}`);
|
|
203
|
+
}
|
|
204
|
+
function resolveExternalTrackerCommand() {
|
|
205
|
+
const config = readTrackerConfig();
|
|
206
|
+
return readConfigString(config, "command") ?? "gh";
|
|
207
|
+
}
|
|
208
|
+
function resolveExternalTrackerRepo() {
|
|
209
|
+
const config = readTrackerConfig();
|
|
210
|
+
const repo = readConfigString(config, "repo");
|
|
211
|
+
if (!repo)
|
|
212
|
+
throw new Error("External tracker config requires tracker.config.repo.");
|
|
213
|
+
return repo;
|
|
214
|
+
}
|
|
215
|
+
function resolveExternalTrackerCwd() {
|
|
216
|
+
const config = readTrackerConfig();
|
|
217
|
+
const cwd = readConfigString(config, "cwd");
|
|
218
|
+
return cwd
|
|
219
|
+
? cwd.startsWith("/")
|
|
220
|
+
? resolve(cwd)
|
|
221
|
+
: resolve(resolveWorkspaceRoot(), cwd)
|
|
222
|
+
: resolveWorkspaceRoot();
|
|
223
|
+
}
|
|
224
|
+
function resolveExternalTrackerLimit() {
|
|
225
|
+
const config = readTrackerConfig();
|
|
226
|
+
return readConfigPositiveInt(config, "limit") ?? 100;
|
|
227
|
+
}
|
|
228
|
+
async function runExternalTrackerCommand(args) {
|
|
229
|
+
const command = resolveExternalTrackerCommand();
|
|
230
|
+
const cwd = resolveExternalTrackerCwd();
|
|
231
|
+
return new Promise((resolvePromise) => {
|
|
232
|
+
const child = spawn(command, args, {
|
|
233
|
+
cwd,
|
|
234
|
+
env: process.env,
|
|
235
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
236
|
+
});
|
|
237
|
+
let stdout = "";
|
|
238
|
+
let stderr = "";
|
|
239
|
+
child.stdout.on("data", (chunk) => {
|
|
240
|
+
stdout += chunk.toString();
|
|
241
|
+
});
|
|
242
|
+
child.stderr.on("data", (chunk) => {
|
|
243
|
+
stderr += chunk.toString();
|
|
244
|
+
});
|
|
245
|
+
child.on("error", (error) => {
|
|
246
|
+
resolvePromise({
|
|
247
|
+
ok: false,
|
|
248
|
+
stdout,
|
|
249
|
+
stderr: `${stderr}${error.message}`,
|
|
250
|
+
exit_code: null,
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
child.on("close", (code) => {
|
|
254
|
+
resolvePromise({
|
|
255
|
+
ok: code === 0,
|
|
256
|
+
stdout,
|
|
257
|
+
stderr,
|
|
258
|
+
exit_code: code,
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function parseGithubIssueNumber(itemId) {
|
|
264
|
+
const trimmed = itemId.trim();
|
|
265
|
+
const match = trimmed.match(/#?(\d+)$/);
|
|
266
|
+
if (!match) {
|
|
267
|
+
throw new Error(`GitHub tracker item id is invalid: ${itemId}`);
|
|
268
|
+
}
|
|
269
|
+
return match[1];
|
|
270
|
+
}
|
|
271
|
+
function readGithubJson(stdout, label) {
|
|
272
|
+
try {
|
|
273
|
+
return JSON.parse(stdout);
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
throw new Error(`${label} returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function normalizeGithubTrackerItem(issue, repo) {
|
|
280
|
+
const numberValue = issue.number;
|
|
281
|
+
const issueNumber = typeof numberValue === "number" || typeof numberValue === "string"
|
|
282
|
+
? String(numberValue)
|
|
283
|
+
: "";
|
|
284
|
+
if (!issueNumber) {
|
|
285
|
+
throw new Error("GitHub issue payload is missing number.");
|
|
286
|
+
}
|
|
287
|
+
const labels = Array.isArray(issue.labels)
|
|
288
|
+
? issue.labels
|
|
289
|
+
.map((label) => {
|
|
290
|
+
if (typeof label === "string")
|
|
291
|
+
return label;
|
|
292
|
+
if (label && typeof label === "object" && typeof label.name === "string") {
|
|
293
|
+
return label.name;
|
|
294
|
+
}
|
|
295
|
+
return "";
|
|
296
|
+
})
|
|
297
|
+
.map((entry) => entry.trim())
|
|
298
|
+
.filter(Boolean)
|
|
299
|
+
: [];
|
|
300
|
+
const assignee = Array.isArray(issue.assignees)
|
|
301
|
+
? issue.assignees
|
|
302
|
+
.map((entry) => {
|
|
303
|
+
if (typeof entry === "string")
|
|
304
|
+
return entry;
|
|
305
|
+
if (entry && typeof entry === "object") {
|
|
306
|
+
const record = entry;
|
|
307
|
+
if (typeof record.login === "string")
|
|
308
|
+
return record.login;
|
|
309
|
+
if (typeof record.name === "string")
|
|
310
|
+
return record.name;
|
|
311
|
+
}
|
|
312
|
+
return "";
|
|
313
|
+
})
|
|
314
|
+
.map((entry) => entry.trim())
|
|
315
|
+
.find(Boolean)
|
|
316
|
+
: undefined;
|
|
317
|
+
const state = typeof issue.state === "string" && issue.state.trim().length > 0
|
|
318
|
+
? issue.state.trim().toLowerCase()
|
|
319
|
+
: "open";
|
|
320
|
+
return normalizeTrackerItem({
|
|
321
|
+
item_id: `github:${repo}#${issueNumber}`,
|
|
322
|
+
external_id: issueNumber,
|
|
323
|
+
title: typeof issue.title === "string" ? issue.title.trim() : `GitHub issue ${issueNumber}`,
|
|
324
|
+
body: typeof issue.body === "string" ? issue.body : undefined,
|
|
325
|
+
state,
|
|
326
|
+
labels,
|
|
327
|
+
assignee,
|
|
328
|
+
url: typeof issue.url === "string" ? issue.url : undefined,
|
|
329
|
+
updated_at: typeof issue.updatedAt === "string" && issue.updatedAt.trim().length > 0
|
|
330
|
+
? issue.updatedAt
|
|
331
|
+
: new Date().toISOString(),
|
|
332
|
+
metadata: {
|
|
333
|
+
provider: "github",
|
|
334
|
+
repo,
|
|
335
|
+
issue_number: issueNumber,
|
|
336
|
+
issue_id: typeof issue.id === "string" ? issue.id : undefined,
|
|
337
|
+
state_reason: typeof issue.stateReason === "string" ? issue.stateReason : undefined,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
async function githubListIssues(filter) {
|
|
342
|
+
const repo = resolveExternalTrackerRepo();
|
|
343
|
+
const limit = Math.max(1, resolveExternalTrackerLimit());
|
|
344
|
+
const result = await runExternalTrackerCommand([
|
|
345
|
+
"issue",
|
|
346
|
+
"list",
|
|
347
|
+
"--repo",
|
|
348
|
+
repo,
|
|
349
|
+
"--state",
|
|
350
|
+
"all",
|
|
351
|
+
"--limit",
|
|
352
|
+
String(limit),
|
|
353
|
+
"--json",
|
|
354
|
+
"id,number,title,body,state,stateReason,labels,assignees,updatedAt,url",
|
|
355
|
+
]);
|
|
356
|
+
if (!result.ok) {
|
|
357
|
+
throw new Error(result.stderr.trim() || `GitHub issue list failed (exit ${result.exit_code ?? "?"}).`);
|
|
358
|
+
}
|
|
359
|
+
const issues = readGithubJson(result.stdout, "GitHub issue list");
|
|
360
|
+
return filterTrackerItems(issues.map((issue) => normalizeGithubTrackerItem(issue, repo)), filter);
|
|
361
|
+
}
|
|
362
|
+
async function githubGetIssuesById(ids) {
|
|
363
|
+
const repo = resolveExternalTrackerRepo();
|
|
364
|
+
const items = [];
|
|
365
|
+
for (const id of ids) {
|
|
366
|
+
const issueNumber = parseGithubIssueNumber(id);
|
|
367
|
+
const result = await runExternalTrackerCommand([
|
|
368
|
+
"issue",
|
|
369
|
+
"view",
|
|
370
|
+
issueNumber,
|
|
371
|
+
"--repo",
|
|
372
|
+
repo,
|
|
373
|
+
"--json",
|
|
374
|
+
"id,number,title,body,state,stateReason,labels,assignees,updatedAt,url",
|
|
375
|
+
]);
|
|
376
|
+
if (!result.ok) {
|
|
377
|
+
throw new Error(result.stderr.trim() || `GitHub issue view failed for ${id}.`);
|
|
378
|
+
}
|
|
379
|
+
items.push(normalizeGithubTrackerItem(readGithubJson(result.stdout, "GitHub issue view"), repo));
|
|
380
|
+
}
|
|
381
|
+
return items;
|
|
382
|
+
}
|
|
383
|
+
function readOrBuildExternalSnapshot() {
|
|
384
|
+
const loaded = loadTrackerSnapshot();
|
|
385
|
+
if (loaded.ok && loaded.snapshot)
|
|
386
|
+
return loaded.snapshot;
|
|
387
|
+
return buildDefaultTrackerSnapshot("external");
|
|
388
|
+
}
|
|
389
|
+
function upsertTrackerItem(snapshot, item) {
|
|
390
|
+
const items = [...snapshot.items];
|
|
391
|
+
const index = findItemIndex(snapshot, item.item_id);
|
|
392
|
+
if (index === -1) {
|
|
393
|
+
items.push(item);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
items[index] = item;
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
...snapshot,
|
|
400
|
+
adapter_kind: "external",
|
|
401
|
+
updated_at: item.updated_at,
|
|
402
|
+
items,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
async function githubCreateComment(itemId, body) {
|
|
406
|
+
const issueNumber = parseGithubIssueNumber(itemId);
|
|
407
|
+
const repo = resolveExternalTrackerRepo();
|
|
408
|
+
const trimmedBody = body.trim();
|
|
409
|
+
if (!trimmedBody) {
|
|
410
|
+
return { ok: false, error: "Comment body must not be empty." };
|
|
411
|
+
}
|
|
412
|
+
const result = await runExternalTrackerCommand([
|
|
413
|
+
"issue",
|
|
414
|
+
"comment",
|
|
415
|
+
issueNumber,
|
|
416
|
+
"--repo",
|
|
417
|
+
repo,
|
|
418
|
+
"--body",
|
|
419
|
+
trimmedBody,
|
|
420
|
+
]);
|
|
421
|
+
if (!result.ok) {
|
|
422
|
+
return {
|
|
423
|
+
ok: false,
|
|
424
|
+
error: result.stderr.trim() || `GitHub issue comment failed for ${itemId}.`,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
const snapshot = readOrBuildExternalSnapshot();
|
|
429
|
+
const issue = (await githubGetIssuesById([itemId]))[0] ??
|
|
430
|
+
normalizeTrackerItem({
|
|
431
|
+
item_id: `github:${repo}#${issueNumber}`,
|
|
432
|
+
external_id: issueNumber,
|
|
433
|
+
title: `GitHub issue ${issueNumber}`,
|
|
434
|
+
state: "open",
|
|
435
|
+
labels: [],
|
|
436
|
+
updated_at: new Date().toISOString(),
|
|
437
|
+
metadata: { provider: "github", repo, issue_number: issueNumber },
|
|
438
|
+
});
|
|
439
|
+
const now = new Date().toISOString();
|
|
440
|
+
const commentId = randomUUID();
|
|
441
|
+
const nextSnapshot = upsertTrackerItem(snapshot, issue);
|
|
442
|
+
nextSnapshot.comments = [
|
|
443
|
+
...nextSnapshot.comments,
|
|
444
|
+
normalizeTrackerComment({
|
|
445
|
+
comment_id: commentId,
|
|
446
|
+
item_id: issue.item_id,
|
|
447
|
+
body: trimmedBody,
|
|
448
|
+
author: getExternalTrackerAuthor(),
|
|
449
|
+
created_at: now,
|
|
450
|
+
metadata: {
|
|
451
|
+
provider: "github",
|
|
452
|
+
repo,
|
|
453
|
+
issue_number: issueNumber,
|
|
454
|
+
},
|
|
455
|
+
}),
|
|
456
|
+
];
|
|
457
|
+
nextSnapshot.updated_at = now;
|
|
458
|
+
writeTrackerSnapshot(nextSnapshot);
|
|
459
|
+
emitTrackerEvent("TRACKER_COMMENT_CREATED", `Tracker comment created for ${issue.item_id}.`, {
|
|
460
|
+
adapter_kind: "external",
|
|
461
|
+
provider: "github",
|
|
462
|
+
item_id: issue.item_id,
|
|
463
|
+
comment_id: commentId,
|
|
464
|
+
});
|
|
465
|
+
return { ok: true, comment_id: commentId };
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function githubUpdateItemState(itemId, state) {
|
|
472
|
+
const issueNumber = parseGithubIssueNumber(itemId);
|
|
473
|
+
const repo = resolveExternalTrackerRepo();
|
|
474
|
+
const nextState = state.trim().toLowerCase();
|
|
475
|
+
if (!nextState) {
|
|
476
|
+
return { ok: false, error: "Tracker state must not be empty." };
|
|
477
|
+
}
|
|
478
|
+
const action = ["closed", "done", "completed", "resolved"].includes(nextState)
|
|
479
|
+
? "close"
|
|
480
|
+
: ["open", "opened", "reopened", "todo", "backlog", "in_progress"].includes(nextState)
|
|
481
|
+
? "reopen"
|
|
482
|
+
: undefined;
|
|
483
|
+
if (!action) {
|
|
484
|
+
return {
|
|
485
|
+
ok: false,
|
|
486
|
+
error: `GitHub tracker only supports open/closed transitions, received "${state}".`,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
const result = await runExternalTrackerCommand([
|
|
490
|
+
"issue",
|
|
491
|
+
action,
|
|
492
|
+
issueNumber,
|
|
493
|
+
"--repo",
|
|
494
|
+
repo,
|
|
495
|
+
]);
|
|
496
|
+
if (!result.ok) {
|
|
497
|
+
return {
|
|
498
|
+
ok: false,
|
|
499
|
+
error: result.stderr.trim() || `GitHub issue ${action} failed for ${itemId}.`,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
try {
|
|
503
|
+
const issue = (await githubGetIssuesById([itemId]))[0];
|
|
504
|
+
const snapshot = upsertTrackerItem(readOrBuildExternalSnapshot(), issue);
|
|
505
|
+
writeTrackerSnapshot(snapshot);
|
|
506
|
+
emitTrackerEvent("TRACKER_ITEM_STATE_UPDATED", `Tracker item ${issue.item_id} moved to ${issue.state}.`, {
|
|
507
|
+
adapter_kind: "external",
|
|
508
|
+
provider: "github",
|
|
509
|
+
item_id: issue.item_id,
|
|
510
|
+
state: issue.state,
|
|
511
|
+
});
|
|
512
|
+
return { ok: true };
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
export function validateTrackerSnapshotContent(raw) {
|
|
519
|
+
try {
|
|
520
|
+
parseTrackerSnapshotContent(raw);
|
|
521
|
+
return { ok: true, schema: TRACKER_SNAPSHOT_SCHEMA_NAME };
|
|
522
|
+
}
|
|
523
|
+
catch (error) {
|
|
524
|
+
return {
|
|
525
|
+
ok: false,
|
|
526
|
+
schema: TRACKER_SNAPSHOT_SCHEMA_NAME,
|
|
527
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
export function loadTrackerSnapshot(explicitPath) {
|
|
532
|
+
const target = resolveTrackerSnapshotTarget(explicitPath);
|
|
533
|
+
if (!existsSync(target.path)) {
|
|
534
|
+
return {
|
|
535
|
+
ok: false,
|
|
536
|
+
schema: TRACKER_SNAPSHOT_SCHEMA_NAME,
|
|
537
|
+
path: target.path,
|
|
538
|
+
source: target.source,
|
|
539
|
+
errors: [`Tracker snapshot file not found: ${target.path}`],
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
try {
|
|
543
|
+
const raw = readFileSync(target.path, "utf-8");
|
|
544
|
+
const snapshot = parseTrackerSnapshotContent(raw);
|
|
545
|
+
return {
|
|
546
|
+
ok: true,
|
|
547
|
+
schema: TRACKER_SNAPSHOT_SCHEMA_NAME,
|
|
548
|
+
path: target.path,
|
|
549
|
+
source: target.source,
|
|
550
|
+
snapshot,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
catch (error) {
|
|
554
|
+
return {
|
|
555
|
+
ok: false,
|
|
556
|
+
schema: TRACKER_SNAPSHOT_SCHEMA_NAME,
|
|
557
|
+
path: target.path,
|
|
558
|
+
source: target.source,
|
|
559
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
export function readTrackerSnapshot() {
|
|
564
|
+
const result = loadTrackerSnapshot();
|
|
565
|
+
if (result.ok && result.snapshot)
|
|
566
|
+
return result.snapshot;
|
|
567
|
+
throw new Error((result.errors ?? ["Tracker snapshot unavailable"]).join("; "));
|
|
568
|
+
}
|
|
569
|
+
export function getTrackerSnapshotPath() {
|
|
570
|
+
return resolveTrackerSnapshotTarget().path;
|
|
571
|
+
}
|
|
572
|
+
export function writeTrackerSnapshot(snapshot) {
|
|
573
|
+
const normalized = normalizeTrackerSnapshot(snapshot);
|
|
574
|
+
const validation = validateTrackerSnapshotPayload(normalized);
|
|
575
|
+
if (!validation.ok) {
|
|
576
|
+
throw new Error(`Tracker snapshot failed validation (${validation.schema}): ${validation.errors.join("; ")}`);
|
|
577
|
+
}
|
|
578
|
+
return safeWriteWorkspaceFile(TRACKER_SNAPSHOT_REL_PATH, JSON.stringify(normalized, null, 2));
|
|
579
|
+
}
|
|
580
|
+
const noneTrackerAdapter = {
|
|
581
|
+
kind: "none",
|
|
582
|
+
async healthCheck() {
|
|
583
|
+
return { ok: true, detail: "Tracker disabled via runtime profile." };
|
|
584
|
+
},
|
|
585
|
+
async listActiveItems() {
|
|
586
|
+
return [];
|
|
587
|
+
},
|
|
588
|
+
async getItemsById() {
|
|
589
|
+
return [];
|
|
590
|
+
},
|
|
591
|
+
async createComment() {
|
|
592
|
+
return {
|
|
593
|
+
ok: false,
|
|
594
|
+
error: "Tracker adapter 'none' does not support comment creation.",
|
|
595
|
+
};
|
|
596
|
+
},
|
|
597
|
+
async updateItemState() {
|
|
598
|
+
return {
|
|
599
|
+
ok: false,
|
|
600
|
+
error: "Tracker adapter 'none' does not support state updates.",
|
|
601
|
+
};
|
|
602
|
+
},
|
|
603
|
+
};
|
|
604
|
+
const externalTrackerAdapter = {
|
|
605
|
+
kind: "external",
|
|
606
|
+
async healthCheck() {
|
|
607
|
+
try {
|
|
608
|
+
const provider = resolveExternalTrackerProvider();
|
|
609
|
+
if (provider !== "github") {
|
|
610
|
+
return { ok: false, detail: `Unsupported external tracker provider: ${provider}` };
|
|
611
|
+
}
|
|
612
|
+
const repo = resolveExternalTrackerRepo();
|
|
613
|
+
const result = await runExternalTrackerCommand([
|
|
614
|
+
"issue",
|
|
615
|
+
"list",
|
|
616
|
+
"--repo",
|
|
617
|
+
repo,
|
|
618
|
+
"--state",
|
|
619
|
+
"all",
|
|
620
|
+
"--limit",
|
|
621
|
+
"1",
|
|
622
|
+
"--json",
|
|
623
|
+
"number",
|
|
624
|
+
]);
|
|
625
|
+
return result.ok
|
|
626
|
+
? { ok: true, detail: `GitHub tracker healthy for ${repo}.` }
|
|
627
|
+
: {
|
|
628
|
+
ok: false,
|
|
629
|
+
detail: result.stderr.trim() ||
|
|
630
|
+
`GitHub tracker health check failed for ${repo} (exit ${result.exit_code ?? "?"}).`,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
catch (error) {
|
|
634
|
+
return { ok: false, detail: error instanceof Error ? error.message : String(error) };
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
async listActiveItems(filter) {
|
|
638
|
+
return githubListIssues(filter);
|
|
639
|
+
},
|
|
640
|
+
async getItemsById(ids) {
|
|
641
|
+
return githubGetIssuesById(ids);
|
|
642
|
+
},
|
|
643
|
+
async createComment(itemId, body) {
|
|
644
|
+
return githubCreateComment(itemId, body);
|
|
645
|
+
},
|
|
646
|
+
async updateItemState(itemId, state) {
|
|
647
|
+
return githubUpdateItemState(itemId, state);
|
|
648
|
+
},
|
|
649
|
+
};
|
|
650
|
+
const memoryTrackerAdapter = {
|
|
651
|
+
kind: "memory",
|
|
652
|
+
async healthCheck() {
|
|
653
|
+
try {
|
|
654
|
+
const snapshot = readTrackerSnapshot();
|
|
655
|
+
return {
|
|
656
|
+
ok: true,
|
|
657
|
+
detail: `Memory tracker snapshot loaded with ${snapshot.items.length} items.`,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
return {
|
|
662
|
+
ok: false,
|
|
663
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
},
|
|
667
|
+
async listActiveItems(filter) {
|
|
668
|
+
const snapshot = readTrackerSnapshot();
|
|
669
|
+
return filterTrackerItems(snapshot.items, filter);
|
|
670
|
+
},
|
|
671
|
+
async getItemsById(ids) {
|
|
672
|
+
const snapshot = readTrackerSnapshot();
|
|
673
|
+
return filterTrackerItems(snapshot.items, { ids });
|
|
674
|
+
},
|
|
675
|
+
async createComment(itemId, body) {
|
|
676
|
+
try {
|
|
677
|
+
const trimmedBody = body.trim();
|
|
678
|
+
if (!trimmedBody) {
|
|
679
|
+
return { ok: false, error: "Comment body must not be empty." };
|
|
680
|
+
}
|
|
681
|
+
const snapshot = readTrackerSnapshot();
|
|
682
|
+
const itemIndex = findItemIndex(snapshot, itemId);
|
|
683
|
+
if (itemIndex === -1) {
|
|
684
|
+
return { ok: false, error: `Tracker item not found: ${itemId}` };
|
|
685
|
+
}
|
|
686
|
+
const now = new Date().toISOString();
|
|
687
|
+
const comment = {
|
|
688
|
+
comment_id: randomUUID(),
|
|
689
|
+
item_id: snapshot.items[itemIndex].item_id,
|
|
690
|
+
body: trimmedBody,
|
|
691
|
+
author: getMemoryTrackerAuthor(),
|
|
692
|
+
created_at: now,
|
|
693
|
+
metadata: {
|
|
694
|
+
adapter_kind: "memory",
|
|
695
|
+
},
|
|
696
|
+
};
|
|
697
|
+
const nextSnapshot = {
|
|
698
|
+
...snapshot,
|
|
699
|
+
adapter_kind: "memory",
|
|
700
|
+
updated_at: now,
|
|
701
|
+
comments: [...snapshot.comments, comment],
|
|
702
|
+
};
|
|
703
|
+
writeTrackerSnapshot(nextSnapshot);
|
|
704
|
+
emitTrackerEvent("TRACKER_COMMENT_CREATED", `Tracker comment created for ${comment.item_id}.`, {
|
|
705
|
+
adapter_kind: "memory",
|
|
706
|
+
item_id: comment.item_id,
|
|
707
|
+
comment_id: comment.comment_id,
|
|
708
|
+
});
|
|
709
|
+
return { ok: true, comment_id: comment.comment_id };
|
|
710
|
+
}
|
|
711
|
+
catch (error) {
|
|
712
|
+
return {
|
|
713
|
+
ok: false,
|
|
714
|
+
error: error instanceof Error ? error.message : String(error),
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
},
|
|
718
|
+
async updateItemState(itemId, state) {
|
|
719
|
+
try {
|
|
720
|
+
const nextState = state.trim();
|
|
721
|
+
if (!nextState) {
|
|
722
|
+
return { ok: false, error: "Tracker state must not be empty." };
|
|
723
|
+
}
|
|
724
|
+
const snapshot = readTrackerSnapshot();
|
|
725
|
+
const itemIndex = findItemIndex(snapshot, itemId);
|
|
726
|
+
if (itemIndex === -1) {
|
|
727
|
+
return { ok: false, error: `Tracker item not found: ${itemId}` };
|
|
728
|
+
}
|
|
729
|
+
const now = new Date().toISOString();
|
|
730
|
+
const items = [...snapshot.items];
|
|
731
|
+
items[itemIndex] = {
|
|
732
|
+
...items[itemIndex],
|
|
733
|
+
state: nextState,
|
|
734
|
+
updated_at: now,
|
|
735
|
+
metadata: {
|
|
736
|
+
...items[itemIndex].metadata,
|
|
737
|
+
adapter_kind: "memory",
|
|
738
|
+
},
|
|
739
|
+
};
|
|
740
|
+
const nextSnapshot = {
|
|
741
|
+
...snapshot,
|
|
742
|
+
adapter_kind: "memory",
|
|
743
|
+
updated_at: now,
|
|
744
|
+
items,
|
|
745
|
+
};
|
|
746
|
+
writeTrackerSnapshot(nextSnapshot);
|
|
747
|
+
emitTrackerEvent("TRACKER_ITEM_STATE_UPDATED", `Tracker item ${items[itemIndex].item_id} moved to ${nextState}.`, {
|
|
748
|
+
adapter_kind: "memory",
|
|
749
|
+
item_id: items[itemIndex].item_id,
|
|
750
|
+
state: nextState,
|
|
751
|
+
});
|
|
752
|
+
return { ok: true };
|
|
753
|
+
}
|
|
754
|
+
catch (error) {
|
|
755
|
+
return {
|
|
756
|
+
ok: false,
|
|
757
|
+
error: error instanceof Error ? error.message : String(error),
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
};
|
|
762
|
+
const TRACKER_ADAPTERS = {
|
|
763
|
+
none: noneTrackerAdapter,
|
|
764
|
+
memory: memoryTrackerAdapter,
|
|
765
|
+
external: externalTrackerAdapter,
|
|
766
|
+
};
|
|
767
|
+
export function listTrackerAdapterKinds() {
|
|
768
|
+
return Object.keys(TRACKER_ADAPTERS).sort((a, b) => a.localeCompare(b));
|
|
769
|
+
}
|
|
770
|
+
export function getTrackerAdapter(kind) {
|
|
771
|
+
return TRACKER_ADAPTERS[resolveTrackerAdapterKind(kind)];
|
|
772
|
+
}
|
|
773
|
+
export function buildDefaultTrackerSnapshot(kind = "none") {
|
|
774
|
+
return defaultTrackerSnapshot(kind);
|
|
775
|
+
}
|
|
776
|
+
//# sourceMappingURL=tracker-adapters.js.map
|