fdeops 5.1.12 → 5.1.13
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/bin/fde.js +13 -5
- package/bin/lib/provenance.js +3 -2
- package/mcp/fdeops-ingest/package.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/fde/SKILL.md +8 -2
package/bin/fde.js
CHANGED
|
@@ -2611,17 +2611,24 @@ function cmdReceipts(args) {
|
|
|
2611
2611
|
const scaffold = new Set((templatesDir() ? readClean(templatesDir(), file) : '').split('\n').map(line => line.trim()))
|
|
2612
2612
|
const decisionSources = new Map()
|
|
2613
2613
|
if (file === 'decisions.md') for (const entry of datedDecisions(document)) {
|
|
2614
|
-
for (let line = entry.line; line < entry.line + entry.text.split('\n').length; line++) decisionSources.set(line, entry
|
|
2614
|
+
for (let line = entry.line; line < entry.line + entry.text.split('\n').length; line++) decisionSources.set(line, entry)
|
|
2615
2615
|
}
|
|
2616
|
+
const seenEntries = new Set()
|
|
2616
2617
|
document.split('\n').forEach((line, i) => {
|
|
2617
2618
|
if (!line.toLowerCase().includes(term.toLowerCase())) return
|
|
2618
|
-
const
|
|
2619
|
+
const entry = decisionSources.get(i + 1)
|
|
2620
|
+
if (entry && seenEntries.has(entry.line)) return
|
|
2621
|
+
if (entry) seenEntries.add(entry.line)
|
|
2622
|
+
const sourceText = entry ? entry.text : line
|
|
2619
2623
|
const source = sourceReference(sourceText)
|
|
2620
2624
|
if (!source && scaffold.has(line.trim())) return
|
|
2621
2625
|
const sources = sourceReferences(sourceText)
|
|
2622
2626
|
const attribution = masking.mask(sources.join('; '))
|
|
2623
2627
|
const displayed = Buffer.byteLength(attribution) <= 320 ? attribution : context.clipMaskedUtf8(attribution, 240) + '… [sources truncated; use fde recall]'
|
|
2624
|
-
const
|
|
2628
|
+
const excerpt = masking.mask(entry ? entry.text : line.trim())
|
|
2629
|
+
const excerptLimit = entry ? 1400 : 160
|
|
2630
|
+
const body = context.clipMaskedUtf8(excerpt, excerptLimit) + (Buffer.byteLength(excerpt) > excerptLimit ? ' [excerpt truncated; use fde recall]' : '')
|
|
2631
|
+
const hit = ` ${file}:${entry ? entry.line : i + 1} ${body}${source ? ` [${sources.length > 1 ? 'sources' : 'source'}: ${displayed}]` : ' [source missing]'}${dirty.has(file) ? ' dirty file - review manual edits' : ''}`
|
|
2625
2632
|
;(recordFiles.includes(file) && source ? records : claims).push({ file, hit })
|
|
2626
2633
|
})
|
|
2627
2634
|
}
|
|
@@ -2643,8 +2650,9 @@ function cmdReceipts(args) {
|
|
|
2643
2650
|
}
|
|
2644
2651
|
return `Selected ${selected.length} of ${hits.length} matching lines; omitted matches require a narrower search.\n` + selected.join('\n')
|
|
2645
2652
|
}
|
|
2646
|
-
const sections = ['RECEIPTS: a cited record is not proof of customer approval. File line numbers refer to the redacted view. Latest and earliest matching lines are sampled; file order is not authority. Check conflicting records.']
|
|
2647
|
-
if (records.length) sections.push('ON RECORD (
|
|
2653
|
+
const sections = ['RECEIPTS (dated evidence): a cited record is not proof of customer approval. File line numbers refer to the redacted view. Latest and earliest matching lines are sampled; file order is not authority. Check conflicting records.']
|
|
2654
|
+
if (records.length) sections.push('ON RECORD (source-backed; check stated dates and status):\n' + select(records))
|
|
2655
|
+
if (!records.length && claims.length) sections.push('No source-backed record matched. The notes below do not establish agreement; verify with the relevant decision owner when known.')
|
|
2648
2656
|
if (claims.length) sections.push('CLAIMS & working notes (verify source and approval before citing):\n' + select(claims))
|
|
2649
2657
|
if (!records.length && !claims.length) sections.push(`no record of "${term}" - a gap in the record, not proof of absence`)
|
|
2650
2658
|
process.stdout.write(maskedSections(sections))
|
package/bin/lib/provenance.js
CHANGED
|
@@ -29,11 +29,12 @@ function datedDecisions(value) {
|
|
|
29
29
|
for (let i = 0; i < lines.length; i++) {
|
|
30
30
|
const bullet = lines[i].match(/^\s*[-*]\s*\[(\d{4}-\d{2}-\d{2})\]\s*(.+)/)
|
|
31
31
|
const heading = lines[i].match(/^(#{2,3})\s+\[?(\d{4}-\d{2}-\d{2})(?:\]|\s+-)?\s+(.+)/)
|
|
32
|
+
const named = lines[i].match(/^(#{2,3})\s+((?:Decision:\s*.+|Scope change))\s+-\s+(\d{4}-\d{2}-\d{2})\s*$/i)
|
|
32
33
|
if (bullet) entries.push({ date: bullet[1], text: lines[i].trim(), line: i + 1 })
|
|
33
|
-
else if (heading) {
|
|
34
|
+
else if (heading || named) {
|
|
34
35
|
let end = i + 1
|
|
35
36
|
while (end < lines.length && !/^#{1,3}\s/.test(lines[end]) && !/^\s*[-*]\s*\[\d{4}-\d{2}-\d{2}\]/.test(lines[end])) end++
|
|
36
|
-
entries.push({ date: heading[2], text: lines.slice(i, end).join('\n').trim(), line: i + 1 })
|
|
37
|
+
entries.push({ date: heading ? heading[2] : named[3], text: lines.slice(i, end).join('\n').trim(), line: i + 1 })
|
|
37
38
|
i = end - 1
|
|
38
39
|
}
|
|
39
40
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fdeops",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.13",
|
|
4
4
|
"description": "Forward deployed engineering skills for AI coding agents. Use focused task skills or @fde for discovery, implementation, verification and handoff, with local engagement records.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fdeops": "bin/install.js",
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "fdeops",
|
|
4
|
-
"version": "5.1.
|
|
4
|
+
"version": "5.1.13",
|
|
5
5
|
"description": "Forward deployed engineering skills for AI coding agents. Use focused task skills or @fde for discovery, implementation, verification and handoff, with local engagement records.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Subash Natarajan",
|
package/skills/fde/SKILL.md
CHANGED
|
@@ -50,7 +50,7 @@ Scale this to the work: routine fixes reuse agreed scope, signer and acceptance
|
|
|
50
50
|
| day-1 look at the repo | `fde scan` |
|
|
51
51
|
| debrief / pasted notes for a bound record | `fde debrief --smart` → agent reconciliation → one plain-English review → Save this update? → `--apply`. `--smart` is a gate, not a brain. `references/debrief.md` |
|
|
52
52
|
| prep me for … | `fde prep "<label>"` |
|
|
53
|
-
|
|
|
53
|
+
| did we agree / who decided / why did we | `fde receipts <term>` |
|
|
54
54
|
| sponsor update / defend the number | `fde defend` |
|
|
55
55
|
| successor / rotation / portable handoff | `fde handoff` (stdout; `--out new-file.md` only after export requested) |
|
|
56
56
|
| they went quiet | Review evidence with `references/rescue.md`; confirm a signal change before `fde log contact "…" --signal amber\|green\|red` |
|
|
@@ -151,7 +151,7 @@ Work names (engage, diagnose, align, deliver, realize, transfer) are the same ma
|
|
|
151
151
|
| Engagement ending, team needs to operate without you, write the runbook | runbook | `references/runbook.md` |
|
|
152
152
|
| Something worked well and will apply to future engagements, encode the pattern | feedback | `references/encode-pattern.md` |
|
|
153
153
|
| "Red-team this," "stress-test my plan," poke holes, challenge the plan, what am I missing | red-team | `references/red-team.md` |
|
|
154
|
-
| "
|
|
154
|
+
| "Did we agree to X?", "Who decided?", "Why did we choose X?", scope dispute | - | run `fde receipts <term>`; return the decision, person, date, source and later changes; use targeted recall for missing context |
|
|
155
155
|
|
|
156
156
|
**Overlays - activate alongside any skill on signal, don't wait to be told:**
|
|
157
157
|
|
|
@@ -177,3 +177,9 @@ Ready to build: check that the supplied facts establish the outcome, constraints
|
|
|
177
177
|
### Keep open work visible
|
|
178
178
|
|
|
179
179
|
For confirmed follow-ups, maintain `## Commitments` and `## Open questions` in the existing `context.md`. Use unchecked bullets for unresolved items and check them only after confirmed resolution. A commitment says who owes what to whom; include a source and `due: YYYY-MM-DD` or `review: YYYY-MM-DD` only when agreed. Preserve unresolved `unknown - ask:` questions here when they affect the next decision. Do not infer a promise from a suggestion. `resume` and `prep` surface these entries with sources; dates prompt a status check, not an invented escalation. For a meeting, select relevant entries and use targeted recall for supporting evidence. Users describe the follow-up naturally; maintain the record for them.
|
|
180
|
+
|
|
181
|
+
### Answer from the record
|
|
182
|
+
|
|
183
|
+
A receipt is dated evidence for a claim, not automatic customer approval. For agreement questions, distinguish proposed work, an agreed decision, a later withdrawal and acceptance of a delivered result. Attribute the decision and rationale only when recorded; the person who wrote a note is not necessarily the decision-maker. If the search finds no agreement evidence, say “I found no recorded agreement in the material checked,” show relevant claims as claims, and name who can clarify only when their authority is known. Do not invent a rationale or treat absence as “never agreed.”
|
|
184
|
+
|
|
185
|
+
For meeting preparation, use `fde prep` as the starting packet, then select unresolved questions and commitments relevant to the meeting's purpose and participants. Include the recorded owner, due/review date and source where available; mark missing fields unknown. Retrieve supporting or conflicting context with targeted recall. Do not turn every old unknown into an agenda item, infer an overdue date, or hide a material blocker merely because its wording does not match the meeting label.
|