@cyber-dash-tech/revela 0.17.5 → 0.17.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -46
- package/README.zh-CN.md +26 -46
- package/bin/revela.ts +98 -0
- package/lib/commands/review.ts +8 -5
- package/lib/deck-html/foundation.ts +190 -0
- package/lib/edit/prompt.ts +6 -2
- package/lib/edit/server.ts +2 -2
- package/lib/inspect/prompt.ts +5 -1
- package/lib/qa/index.ts +12 -0
- package/lib/refine/comment-requests.ts +77 -0
- package/lib/refine/open.ts +5 -2
- package/lib/refine/prompt-bridge.ts +219 -0
- package/lib/refine/qa-suppression.ts +41 -0
- package/lib/refine/server.ts +122 -34
- package/lib/runtime/index.ts +225 -0
- package/lib/runtime/research.ts +175 -0
- package/lib/runtime/review.ts +270 -0
- package/lib/runtime/story.ts +53 -0
- package/package.json +6 -1
- package/plugin.ts +6 -2
- package/plugins/revela/.codex-plugin/plugin.json +37 -0
- package/plugins/revela/.mcp.json +11 -0
- package/plugins/revela/assets/README.md +2 -0
- package/plugins/revela/hooks/hooks.json +28 -0
- package/plugins/revela/hooks/revela_guard.ts +10 -0
- package/plugins/revela/hooks/revela_post_write_notice.ts +18 -0
- package/plugins/revela/mcp/revela-server.ts +504 -0
- package/plugins/revela/mcp/runtime-resolver.ts +109 -0
- package/plugins/revela/skills/revela-design/SKILL.md +20 -0
- package/plugins/revela/skills/revela-domain/SKILL.md +18 -0
- package/plugins/revela/skills/revela-export/SKILL.md +21 -0
- package/plugins/revela/skills/revela-init/SKILL.md +36 -0
- package/plugins/revela/skills/revela-make-deck/SKILL.md +37 -0
- package/plugins/revela/skills/revela-research/SKILL.md +38 -0
- package/plugins/revela/skills/revela-review-deck/SKILL.md +33 -0
- package/plugins/revela/skills/revela-story/SKILL.md +24 -0
- package/skill/SKILL.md +17 -8
- package/tools/deck-foundation.ts +48 -0
- package/tools/decks.ts +10 -78
- package/tools/research-save.ts +8 -72
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: revela-init
|
|
3
|
+
description: Initialize or refresh a Revela narrative workspace in Codex from local source materials, preserving source traceability and file-native state.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Revela Init
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks to start Revela, initialize the workspace, ingest local materials, or prepare a trusted narrative graph.
|
|
9
|
+
|
|
10
|
+
## Product Contract
|
|
11
|
+
|
|
12
|
+
- `revela-narrative/` is the editable source of truth for communication meaning when present.
|
|
13
|
+
- `NarrativeStateV1` is the compiled internal interface.
|
|
14
|
+
- `deck-plan/` is render planning, not canonical meaning.
|
|
15
|
+
- `decks/*.html` are artifacts.
|
|
16
|
+
- `DECKS.json` is compatibility/cache state, not workflow authority.
|
|
17
|
+
- Do not invent quotes, source paths, URLs, page references, caveats, claim ids, evidence ids, or artifact coverage.
|
|
18
|
+
|
|
19
|
+
## Workflow
|
|
20
|
+
|
|
21
|
+
1. Inspect the workspace with normal Codex file tools. Stay inside the current workspace root.
|
|
22
|
+
2. Prefer local source materials first: Markdown, text, CSV, PDFs, Office files, existing `researches/`, existing `revela-narrative/`, `deck-plan/`, and `decks/`.
|
|
23
|
+
3. Call `revela_domain_list` and `revela_domain_read` for active domain guidance before authoring narrative meaning. Treat domain guidance as framing guidance, never as evidence.
|
|
24
|
+
4. If `revela-narrative/` exists, call `revela_markdown_qa` and `revela_compile_narrative`.
|
|
25
|
+
5. If the narrative vault is missing, create the initial `revela-narrative/` Markdown nodes directly with valid frontmatter and plain wikilink relations.
|
|
26
|
+
6. Evidence nodes must preserve source, quote/snippet, support scope, unsupported scope, caveat, and strength before being treated as support.
|
|
27
|
+
7. After writing narrative Markdown, call `revela_markdown_qa` and `revela_compile_narrative` again.
|
|
28
|
+
8. End with a concise init report: local materials found, active domain, narrative graph status, open gaps, Markdown QA status, and next command/action.
|
|
29
|
+
|
|
30
|
+
## Markdown Rules
|
|
31
|
+
|
|
32
|
+
- Use node types: `index`, `audience`, `decision`, `thesis`, `claim`, `evidence`, `objection`, `risk`, `research-gap`.
|
|
33
|
+
- Use one leading frontmatter block per file.
|
|
34
|
+
- Use `## Relations` with plain node-id wikilinks, such as `- supports: [[claim-recommendation]]`.
|
|
35
|
+
- Do not use typed wikilinks such as `[[claim:claim-recommendation]]`.
|
|
36
|
+
- Do not duplicate stable headings like `## Evidence`, `## Caveats`, `## Relations`, `## Response`, or `## Mitigation`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: revela-make-deck
|
|
3
|
+
description: Plan and generate Revela deck artifacts in Codex from canonical narrative state and deck-plan files.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Revela Make Deck
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks to make, generate, or update a Revela deck.
|
|
9
|
+
|
|
10
|
+
## Source Authority
|
|
11
|
+
|
|
12
|
+
- Canonical meaning comes from `revela-narrative/`.
|
|
13
|
+
- Deck execution planning comes from `deck-plan/`.
|
|
14
|
+
- Generated artifacts live under `decks/*.html`.
|
|
15
|
+
- `DECKS.json.slides[]` is not the slide-count contract.
|
|
16
|
+
|
|
17
|
+
## Workflow
|
|
18
|
+
|
|
19
|
+
1. Call `revela_compile_narrative` and `revela_markdown_qa`.
|
|
20
|
+
2. Report narrative and Markdown diagnostics, but treat only malformed/unsafe files and technical artifact validity as hard blockers.
|
|
21
|
+
3. Call `revela_read_deck_plan`. If missing, author `deck-plan/index.md` and `deck-plan/slides/*.md` before HTML generation.
|
|
22
|
+
4. For new HTML files, call `revela_create_deck_foundation`.
|
|
23
|
+
5. Read active design guidance with `revela_design_list` and `revela_design_read` when choosing layouts/components. If the user asks to switch designs persistently, call `revela_design_activate`; if they ask for a one-off design, read that design by name and pass `designName` to `revela_create_deck_foundation`.
|
|
24
|
+
6. Patch slides into the foundation between Revela slide markers. Preserve positive 1-based `data-slide-index` values.
|
|
25
|
+
7. Generate chapter by chapter. Keep the HTML valid after each write.
|
|
26
|
+
8. After every HTML write, call `revela_run_deck_qa` and repair hard errors before review or export.
|
|
27
|
+
|
|
28
|
+
## QA Repair Loop
|
|
29
|
+
|
|
30
|
+
- `revela_run_deck_qa` launches a browser. In sandboxed Codex sessions, this may require user-approved command escalation.
|
|
31
|
+
- If QA reports `text_overflow` or `text_clipped`, reduce font size, line length, padding, or line-height before changing narrative meaning.
|
|
32
|
+
- Prefer conservative cover and section-title sizing in smoke or diagnostic decks.
|
|
33
|
+
- If QA reports that a standalone smoke artifact is not the active legacy deck target, treat it as a non-blocking warning when slide identity and canvas checks pass.
|
|
34
|
+
|
|
35
|
+
## Deck Plan Requirements
|
|
36
|
+
|
|
37
|
+
Every deck plan should include Cover, Table of Contents, and Closing. Use 3-5 chapter headings, explicit slide ranges, low-fidelity layout sketches, narrative links, visual intent, evidence trace, and caveats.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: revela-research
|
|
3
|
+
description: Research Revela story gaps and bind evidence in Codex while preserving explicit source boundaries.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Revela Research
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks to research, close evidence gaps, evaluate saved findings, or bind support to current Revela claims.
|
|
9
|
+
|
|
10
|
+
## Contract
|
|
11
|
+
|
|
12
|
+
- Saved findings in `researches/**/*.md` are not canonical evidence until specific evidence nodes or bindings preserve source trace, quote/snippet, support scope, unsupported scope, caveat, and strength.
|
|
13
|
+
- Missing evidence must stay visible as a gap.
|
|
14
|
+
- Do not broaden claims to fit a source.
|
|
15
|
+
- Do not write deck artifacts during research.
|
|
16
|
+
|
|
17
|
+
## Workflow
|
|
18
|
+
|
|
19
|
+
1. Call `revela_research_targets` to derive target order, selected target, saved findings diagnostics, and evidence gaps.
|
|
20
|
+
2. For existing saved findings, call `revela_evaluate_research_findings` before deciding whether they can support a claim.
|
|
21
|
+
3. Use external research only when the user allowed or requested it and the gap is publicly researchable.
|
|
22
|
+
4. After external research, call `revela_research_save` with structured Markdown findings and explicit source list.
|
|
23
|
+
5. Bind only when `bindingEval.status === "bindable"` by calling `revela_bind_research_findings`; do not hand-author evidence Markdown for bindable saved findings.
|
|
24
|
+
6. If a finding is incomplete, report missing fields instead of inventing them.
|
|
25
|
+
7. After binding or any narrative edit, call `revela_markdown_qa` and `revela_compile_narrative`.
|
|
26
|
+
8. Report evidence bound, unbound findings, remaining caveats, and the next smallest story action.
|
|
27
|
+
|
|
28
|
+
## Binding Criteria
|
|
29
|
+
|
|
30
|
+
Bind only when the supported claim exists and the evidence includes:
|
|
31
|
+
|
|
32
|
+
- source URL/path/findings file
|
|
33
|
+
- quote or traceable snippet
|
|
34
|
+
- support scope
|
|
35
|
+
- unsupported scope
|
|
36
|
+
- caveat
|
|
37
|
+
- strength
|
|
38
|
+
- explicit supported claim context
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: revela-review-deck
|
|
3
|
+
description: Review Revela deck artifacts in Codex for technical validity, evidence trace, and narrative alignment.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Revela Review Deck
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks to review, inspect, diagnose, or refine a generated Revela deck.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
1. Resolve the target `decks/*.html` file from the user request or unambiguous workspace state.
|
|
13
|
+
2. For a plain request like `review decks/foo.html`, call `revela_review_deck_open` and let the tool open the browser by default.
|
|
14
|
+
3. Use `revela_review_deck_read`, normally with `format: "markdown"`, only when the user explicitly asks to diagnose, QA, read, check, inspect source alignment, inspect evidence trace, or avoid opening a GUI.
|
|
15
|
+
4. Use the read output as the deterministic diagnostics packet for artifact QA, deck-plan diagnostics, narrative/vault diagnostics, artifact coverage, and evidence trace.
|
|
16
|
+
5. Pass `openBrowser: false` only for tests, no-GUI environments, or when the user explicitly asks for a link instead of opening the page.
|
|
17
|
+
6. Do not call `revela_run_deck_qa`, `revela_compile_narrative`, or `revela_read_deck_plan` separately for a normal Review UI open.
|
|
18
|
+
7. Call `revela_run_deck_qa` separately only for focused low-level artifact QA, after a repair, or when the user explicitly asks for QA detail.
|
|
19
|
+
8. Separate technical blockers from narrative/evidence diagnostics.
|
|
20
|
+
9. Pure visual/layout/export fixes may patch artifacts directly when the user asks for a change. Meaning changes must update `revela-narrative/` first.
|
|
21
|
+
|
|
22
|
+
## QA Notes
|
|
23
|
+
|
|
24
|
+
- `revela_review_deck_read` is read-only: it must not mutate deck HTML, `revela-narrative/`, `deck-plan/`, assets, or compatibility state.
|
|
25
|
+
- `revela_review_deck_open` opens the local Review server from the MCP process and uses the Codex `codex-exec` bridge for Insight and Comment/Apply Fix. It returns URL/token/open state and basic file metadata, not aggregate diagnostics.
|
|
26
|
+
- `revela_run_deck_qa` may need browser-launch permission in Codex sandboxed sessions.
|
|
27
|
+
- Repair hard QA errors before treating a deck as review-ready.
|
|
28
|
+
- Text clipping should usually be fixed with typography and spacing changes, not by deleting evidence or changing claim meaning.
|
|
29
|
+
- A warning that a smoke/development artifact is not the active legacy deck target is non-blocking when the requested file passes hard artifact checks.
|
|
30
|
+
|
|
31
|
+
## Technical Blockers
|
|
32
|
+
|
|
33
|
+
Hard blockers are limited to missing or ambiguous files, invalid HTML contract, invalid slide identity, canvas/export failure, malformed Markdown/frontmatter, or unsafe writes.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: revela-story
|
|
3
|
+
description: Inspect a Revela narrative graph in Codex without mutating artifacts or canonical meaning.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Revela Story
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks to view, inspect, understand, or audit the current Revela story.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
1. Call `revela_story_read` first, normally with `format: "markdown"`.
|
|
13
|
+
2. Use the returned deterministic map, diagnostics, narrative hash, and Markdown reading view as the authoritative Story surface.
|
|
14
|
+
3. Call `revela_markdown_qa` or `revela_compile_narrative` only when you need deeper structural diagnostics than `revela_story_read` returned.
|
|
15
|
+
4. If `revela_story_read.ok` is false because `revela-narrative/` is missing, report the init guidance. Do not create files from Story mode.
|
|
16
|
+
5. Present audience, belief shift, decision/action, thesis, central claims, evidence, objections, risks, research gaps, artifact coverage, and diagnostics.
|
|
17
|
+
6. Keep claim ids, evidence ids, source facts, quotes, URLs, numbers, and caveats exact.
|
|
18
|
+
7. Do not write claims, evidence, research gaps, deck HTML, deck-plan files, assets, or artifacts from Story mode.
|
|
19
|
+
|
|
20
|
+
## Output
|
|
21
|
+
|
|
22
|
+
Lead with the narrative status, narrative hash, and key diagnostics. Then show the claim flow and evidence boundaries.
|
|
23
|
+
|
|
24
|
+
Keep the reading evidence-first: for each claim, show source trace, support scope, unsupported scope, caveat, support strength, linked objections/risks, and remaining research gaps. Separate structural Markdown QA from evidence trust. Story is read-only; do not turn it into a mutation workflow.
|
package/skill/SKILL.md
CHANGED
|
@@ -64,8 +64,13 @@ handoff exactly:
|
|
|
64
64
|
5. Use `readDeckPlan` to inspect the current `deck-plan/` projection before
|
|
65
65
|
artifact review or HTML generation. Diagnostics are advisory unless they are
|
|
66
66
|
artifact validity errors handled by QA.
|
|
67
|
-
6.
|
|
68
|
-
|
|
67
|
+
6. For a new deck HTML file, call `revela-deck-foundation` to create the
|
|
68
|
+
active-design foundation shell. The helper is file-native and must not create
|
|
69
|
+
narrative slide content, choose layouts/components, or read/write `DECKS.json`.
|
|
70
|
+
7. Fetch the required design rules, layouts, and components with
|
|
71
|
+
`revela-designs read`.
|
|
72
|
+
8. Patch slides between the foundation shell's `revela-slides` markers when the
|
|
73
|
+
user proceeds and the deck contract can be satisfied.
|
|
69
74
|
|
|
70
75
|
Before any HTML generation, call `revela-decks` action `readDeckPlan` and follow
|
|
71
76
|
the current `deck-plan/`: Source Authority, deck parameters, Chapter Writing
|
|
@@ -164,7 +169,8 @@ one broad `write`, `edit`, or `apply_patch` call.
|
|
|
164
169
|
|
|
165
170
|
For decks with 5 or more slides:
|
|
166
171
|
|
|
167
|
-
- First
|
|
172
|
+
- First call `revela-deck-foundation` for new files, then patch structural
|
|
173
|
+
slides and the first chapter between the `revela-slides` markers.
|
|
168
174
|
- Then fill or revise exactly one chapter range at a time.
|
|
169
175
|
- Do not mix multiple central-claim chapters in the same write.
|
|
170
176
|
- Chapter divider or chapter TOC slides are allowed as structural wayfinding and
|
|
@@ -193,14 +199,17 @@ If a write produces QA hard errors, fix them before continuing.
|
|
|
193
199
|
Before writing or materially changing HTML:
|
|
194
200
|
|
|
195
201
|
1. Read the deck-plan projection's layout and component names.
|
|
196
|
-
2.
|
|
202
|
+
2. For a new deck HTML file, call `revela-deck-foundation` before adding slide
|
|
203
|
+
content. Use `mode: "repair"` only for explicit foundation repair or QA
|
|
204
|
+
foundation contract fixes, not normal Review Comment edits.
|
|
205
|
+
3. Call `revela-designs` with `action: "read"` and `section: "rules"` to fetch
|
|
197
206
|
the active design's current composition and usage rules.
|
|
198
|
-
|
|
207
|
+
4. Call `revela-designs` with `action: "read"` and `layout` set to all required
|
|
199
208
|
layout names, comma-separated.
|
|
200
|
-
|
|
209
|
+
5. Call `revela-designs` with `action: "read"` and `component` set to all
|
|
201
210
|
required component names, comma-separated.
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
6. Fetch `section: "chart-rules"` before using ECharts.
|
|
212
|
+
7. Do not update legacy `requiredInputs`; design fetching is an execution step,
|
|
204
213
|
not a workflow permission gate.
|
|
205
214
|
|
|
206
215
|
Never generate HTML from memory or prior knowledge of a design. Copy the fetched
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin"
|
|
2
|
+
import { createDeckFoundation } from "../lib/deck-html/foundation"
|
|
3
|
+
|
|
4
|
+
export default tool({
|
|
5
|
+
description:
|
|
6
|
+
"Create or repair a file-native Revela deck HTML foundation shell from the active design. " +
|
|
7
|
+
"Writes a deterministic empty deck shell with doctype, html/head/body, active design foundation CSS, complete SlidePresentation JavaScript, and stable slide insertion markers. " +
|
|
8
|
+
"It does not create narrative slide content, choose layouts/components, or read/write DECKS.json.",
|
|
9
|
+
args: {
|
|
10
|
+
outputPath: tool.schema
|
|
11
|
+
.string()
|
|
12
|
+
.describe("Workspace-relative HTML output path, usually decks/{name}.html."),
|
|
13
|
+
title: tool.schema
|
|
14
|
+
.string()
|
|
15
|
+
.describe("Presentation title for the HTML <title> tag only; this does not create a cover slide."),
|
|
16
|
+
language: tool.schema
|
|
17
|
+
.string()
|
|
18
|
+
.describe("HTML language tag, e.g. en or zh-CN."),
|
|
19
|
+
designName: tool.schema
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Optional design name. Defaults to the active design."),
|
|
23
|
+
mode: tool.schema
|
|
24
|
+
.enum(["create", "repair"])
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("create protects existing files unless overwrite=true; repair may replace an existing foundation shell."),
|
|
27
|
+
overwrite: tool.schema
|
|
28
|
+
.boolean()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Whether create mode may overwrite an existing HTML file. Defaults to false."),
|
|
31
|
+
},
|
|
32
|
+
async execute(args, { directory }) {
|
|
33
|
+
try {
|
|
34
|
+
const result = createDeckFoundation({
|
|
35
|
+
workspaceRoot: directory || process.cwd(),
|
|
36
|
+
outputPath: args.outputPath,
|
|
37
|
+
title: args.title,
|
|
38
|
+
language: args.language,
|
|
39
|
+
designName: args.designName,
|
|
40
|
+
mode: args.mode,
|
|
41
|
+
overwrite: args.overwrite ?? false,
|
|
42
|
+
})
|
|
43
|
+
return JSON.stringify(result, null, 2)
|
|
44
|
+
} catch (e: any) {
|
|
45
|
+
return JSON.stringify({ error: e?.message || String(e) })
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
})
|
package/tools/decks.ts
CHANGED
|
@@ -34,30 +34,12 @@ import {
|
|
|
34
34
|
import { compileDeckPlanFromNarrative } from "../lib/narrative-state/render-plan"
|
|
35
35
|
import { DECK_PLAN_ARTIFACT_PATH, readDeckPlanArtifact } from "../lib/narrative-state/deck-plan-artifact"
|
|
36
36
|
import { backfillSlideClaimRefsFromCoverage } from "../lib/narrative-state/coverage"
|
|
37
|
-
import { closeResearchGapInState, deriveResearchGapsFromReadiness,
|
|
38
|
-
import {
|
|
39
|
-
import { computeNarrativeHash, stableEvidenceId } from "../lib/narrative-state/hash"
|
|
37
|
+
import { closeResearchGapInState, deriveResearchGapsFromReadiness, updateResearchGapInState, upsertResearchGapsInState } from "../lib/narrative-state/research-gaps"
|
|
38
|
+
import { computeNarrativeHash } from "../lib/narrative-state/hash"
|
|
40
39
|
import { normalizeNarrativeState } from "../lib/narrative-state/normalize"
|
|
41
40
|
import { buildNarrativeVaultInventory, compileNarrativeVault, exportNarrativeStateToVault, formatVaultDiagnosticReport, getNarrativeVaultMigrationHint, hasNarrativeVault, initNarrativeVault, narrativeVaultAuthoringContract, narrativeVaultTimestampMs, removeVaultRelation, runNarrativeMarkdownQa, updateVaultCoreNodes, updateVaultResearchGapNode, upsertVaultClaimNode, upsertVaultEvidenceNode, upsertVaultObjectionNode, upsertVaultRelation, upsertVaultRiskNode, VAULT_MIGRATION_PRESERVED_IN_DECKS_JSON } from "../lib/narrative-vault"
|
|
42
41
|
import { compileCacheMirrorNarrativeVault } from "../lib/narrative-vault/compile-mirror"
|
|
43
|
-
|
|
44
|
-
function missingBindableEvidenceFields(input: Record<string, unknown>): string[] {
|
|
45
|
-
const missing: string[] = []
|
|
46
|
-
for (const key of ["id", "claimId", "source", "quote", "supportScope", "unsupportedScope", "caveat", "strength"] as const) {
|
|
47
|
-
if (!String(input[key] ?? "").trim()) missing.push(key)
|
|
48
|
-
}
|
|
49
|
-
if (!String(input.sourcePath ?? "").trim() && !String(input.url ?? "").trim() && !String(input.findingsFile ?? "").trim()) missing.push("sourcePath|url|findingsFile")
|
|
50
|
-
return missing
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function exactResearchGapForBinding(state: DecksState, findingsFile: string, claimId: string) {
|
|
54
|
-
const gaps = state.narrative?.researchGaps ?? []
|
|
55
|
-
const exact = gaps.filter((gap) => gap.targetType === "claim" && gap.targetId === claimId && gap.findingsFile === findingsFile)
|
|
56
|
-
if (exact.length === 1) return exact[0]
|
|
57
|
-
if (exact.length > 1) return undefined
|
|
58
|
-
const byClaim = gaps.filter((gap) => gap.targetType === "claim" && gap.targetId === claimId && !gap.findingsFile)
|
|
59
|
-
return byClaim.length === 1 ? byClaim[0] : undefined
|
|
60
|
-
}
|
|
42
|
+
import { bindResearchFindings as bindResearchFindingsRuntime, evaluateResearchFindings as evaluateResearchFindingsRuntime, researchTargets as researchTargetsRuntime } from "../lib/runtime/research"
|
|
61
43
|
|
|
62
44
|
function forbiddenVaultCompatibilityAction(action: string, replacement: string): string {
|
|
63
45
|
return `${action} is a JSON-era compatibility action and is blocked in vault workspaces. Use ${replacement}, or patch the existing Markdown node only for a small read-before-edit repair.`
|
|
@@ -469,54 +451,11 @@ export default tool({
|
|
|
469
451
|
}
|
|
470
452
|
|
|
471
453
|
if (args.action === "bindResearchFindings") {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
const draft = bindingEval.recommendedEvidenceDraft
|
|
479
|
-
const evidence = {
|
|
480
|
-
id: args.evidence?.id?.trim() || stableEvidenceId(bindingEval.claimId, `${bindingEval.findingsFile}:${draft.quote ?? ""}`),
|
|
481
|
-
claimId: bindingEval.claimId,
|
|
482
|
-
source: draft.source,
|
|
483
|
-
sourcePath: draft.sourcePath,
|
|
484
|
-
findingsFile: draft.findingsFile ?? bindingEval.findingsFile,
|
|
485
|
-
quote: draft.quote,
|
|
486
|
-
location: draft.location,
|
|
487
|
-
url: draft.url,
|
|
488
|
-
caveat: draft.caveat,
|
|
489
|
-
supportScope: draft.supportScope,
|
|
490
|
-
unsupportedScope: draft.unsupportedScope,
|
|
491
|
-
strength: draft.strength,
|
|
492
|
-
}
|
|
493
|
-
const missing = missingBindableEvidenceFields(evidence)
|
|
494
|
-
if (missing.length > 0) return JSON.stringify({ ok: false, skipped: true, reason: "recommended evidence draft is incomplete", missingFields: missing, bindingEval }, null, 2)
|
|
495
|
-
|
|
496
|
-
const mutation = upsertVaultEvidenceNode(workspaceRoot, evidence as any)
|
|
497
|
-
if (!mutation.ok) return JSON.stringify({ ok: false, mutation, bindingEval }, null, 2)
|
|
498
|
-
const gap = exactResearchGapForBinding(state, bindingEval.findingsFile, bindingEval.claimId)
|
|
499
|
-
const gapMutation = gap
|
|
500
|
-
? updateVaultResearchGapNode(workspaceRoot, {
|
|
501
|
-
id: gap.id,
|
|
502
|
-
status: "evidence_bound",
|
|
503
|
-
findingsFile: bindingEval.findingsFile,
|
|
504
|
-
evidenceBindingIds: [...new Set([...(gap.evidenceBindingIds ?? []), evidence.id])],
|
|
505
|
-
notes: gap.notes,
|
|
506
|
-
})
|
|
507
|
-
: undefined
|
|
508
|
-
const compiled = compileCacheMirrorNarrativeVault(workspaceRoot, mirrorOptions())
|
|
509
|
-
return JSON.stringify({
|
|
510
|
-
ok: compiled.result.ok,
|
|
511
|
-
path: mutation.file,
|
|
512
|
-
bindingEval,
|
|
513
|
-
mutation,
|
|
514
|
-
gapMutation: gapMutation ?? { ok: true, skipped: true, reason: "no exact single research gap matched this findings file and claim" },
|
|
515
|
-
evidence,
|
|
516
|
-
diagnostics: compiled.result.diagnostics,
|
|
517
|
-
diagnosticReport: compiled.diagnosticReport,
|
|
518
|
-
narrative: compiled.result.narrative,
|
|
519
|
-
}, null, 2)
|
|
454
|
+
return JSON.stringify(bindResearchFindingsRuntime({
|
|
455
|
+
workspaceRoot,
|
|
456
|
+
findingsFile: args.findingsFile ?? "",
|
|
457
|
+
evidenceId: args.evidence?.id,
|
|
458
|
+
}), null, 2)
|
|
520
459
|
}
|
|
521
460
|
|
|
522
461
|
if (args.action === "updateVaultResearchGap") {
|
|
@@ -799,18 +738,11 @@ export default tool({
|
|
|
799
738
|
}
|
|
800
739
|
|
|
801
740
|
if (args.action === "deriveResearchTargets") {
|
|
802
|
-
|
|
803
|
-
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result }, null, 2)
|
|
741
|
+
return JSON.stringify(researchTargetsRuntime({ workspaceRoot }), null, 2)
|
|
804
742
|
}
|
|
805
743
|
|
|
806
744
|
if (args.action === "evaluateResearchFindings") {
|
|
807
|
-
|
|
808
|
-
const bindingEval = evaluateResearchFindingsBinding(state, workspaceRoot, args.findingsFile)
|
|
809
|
-
const targets = deriveResearchTargets(state, { workspaceRoot })
|
|
810
|
-
const vaultDiagnostics = hasNarrativeVault(workspaceRoot)
|
|
811
|
-
? formatVaultDiagnosticReport(compileNarrativeVault(workspaceRoot, { fallbackApprovals: state.narrative?.approvals ?? [] }).diagnostics)
|
|
812
|
-
: undefined
|
|
813
|
-
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result: { bindingEval, selected: targets.selected, vaultDiagnostics } }, null, 2)
|
|
745
|
+
return JSON.stringify(evaluateResearchFindingsRuntime({ workspaceRoot, findingsFile: args.findingsFile ?? "" }), null, 2)
|
|
814
746
|
}
|
|
815
747
|
|
|
816
748
|
if (args.action === "upsertResearchGaps") {
|
package/tools/research-save.ts
CHANGED
|
@@ -1,46 +1,5 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin"
|
|
2
|
-
import {
|
|
3
|
-
import { join } from "path"
|
|
4
|
-
import { hasDecksState, readDecksState, writeDecksState } from "../lib/decks-state"
|
|
5
|
-
import { evaluateResearchFindingsBinding } from "../lib/narrative-state/research-binding-eval"
|
|
6
|
-
import { recordWorkspaceAction } from "../lib/workspace-state/actions"
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Format today's date as YYYY-MM-DD
|
|
10
|
-
*/
|
|
11
|
-
function today(): string {
|
|
12
|
-
return new Date().toISOString().slice(0, 10)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Sanitize a key: lowercase, alphanumeric + hyphens only.
|
|
17
|
-
*/
|
|
18
|
-
function keyify(s: string): string {
|
|
19
|
-
return s
|
|
20
|
-
.toLowerCase()
|
|
21
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
22
|
-
.replace(/^-+|-+$/g, "")
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Build YAML frontmatter string.
|
|
27
|
-
*/
|
|
28
|
-
function buildFrontmatter(topic: string, axis: string, sources: string[]): string {
|
|
29
|
-
const lines = [
|
|
30
|
-
"---",
|
|
31
|
-
`topic: ${topic}`,
|
|
32
|
-
`axis: ${axis}`,
|
|
33
|
-
`date: ${today()}`,
|
|
34
|
-
]
|
|
35
|
-
if (sources.length > 0) {
|
|
36
|
-
lines.push("sources:")
|
|
37
|
-
for (const s of sources) {
|
|
38
|
-
lines.push(` - "${s.replace(/"/g, '\\"')}"`)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
lines.push("---")
|
|
42
|
-
return lines.join("\n")
|
|
43
|
-
}
|
|
2
|
+
import { researchSave } from "../lib/runtime/research"
|
|
44
3
|
|
|
45
4
|
export default tool({
|
|
46
5
|
description:
|
|
@@ -77,36 +36,13 @@ export default tool({
|
|
|
77
36
|
},
|
|
78
37
|
async execute(args, context) {
|
|
79
38
|
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const frontmatter = buildFrontmatter(args.topic, fileKey, args.sources ?? [])
|
|
88
|
-
const fileContent = `${frontmatter}\n\n${args.content ?? ""}\n`
|
|
89
|
-
const filePath = join(topicDir, `${fileKey}.md`)
|
|
90
|
-
const relPath = `researches/${topicKey}/${fileKey}.md`
|
|
91
|
-
|
|
92
|
-
writeFileSync(filePath, fileContent, "utf-8")
|
|
93
|
-
|
|
94
|
-
if (hasDecksState(workspaceDir)) {
|
|
95
|
-
const state = readDecksState(workspaceDir)
|
|
96
|
-
recordWorkspaceAction(state, {
|
|
97
|
-
type: "research.findings_saved",
|
|
98
|
-
actor: "revela-research-save",
|
|
99
|
-
inputs: { topic: topicKey, axis: fileKey, sourceCount: args.sources?.length ?? 0 },
|
|
100
|
-
outputs: { path: relPath, sources: args.sources ?? [] },
|
|
101
|
-
summary: `Saved research findings for ${topicKey}/${fileKey}.`,
|
|
102
|
-
nodeIds: [`finding:${relPath}`],
|
|
103
|
-
})
|
|
104
|
-
const bindingEval = evaluateResearchFindingsBinding(state, workspaceDir, relPath)
|
|
105
|
-
writeDecksState(workspaceDir, state)
|
|
106
|
-
return JSON.stringify({ ok: true, path: relPath, bindingEval })
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return JSON.stringify({ ok: true, path: relPath })
|
|
39
|
+
return JSON.stringify(researchSave({
|
|
40
|
+
topic: args.topic,
|
|
41
|
+
filename: args.filename,
|
|
42
|
+
content: args.content,
|
|
43
|
+
sources: args.sources,
|
|
44
|
+
workspaceRoot: context.directory ?? process.cwd(),
|
|
45
|
+
}))
|
|
110
46
|
} catch (e: any) {
|
|
111
47
|
return JSON.stringify({ error: e.message || String(e) })
|
|
112
48
|
}
|