dsh-cc-loader 0.1.2 → 0.2.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/package.json CHANGED
@@ -1,30 +1,30 @@
1
- {
2
- "name": "dsh-cc-loader",
3
- "version": "0.1.2",
4
- "description": "Shared parse layer for the dsh-cc ecosystem: parses Claude Code .claude/ (project + global) into a standalone .dsh intermediate representation (IR), classifying every component DIRECT/ADAPTED/UNSUPPORTED/BLOCKED and filtering unsupported ones out.",
5
- "type": "module",
6
- "main": "src/index.js",
7
- "exports": {
8
- ".": "./src/index.js"
9
- },
10
- "files": [
11
- "src",
12
- "README.md"
13
- ],
14
- "keywords": [
15
- "dsh",
16
- "deepseek-harness",
17
- "claude-code",
18
- "loader",
19
- "parser"
20
- ],
21
- "license": "MIT",
22
- "dependencies": {
23
- "yaml": "^2.0.0"
24
- },
25
- "engines": {
26
- "node": ">=20"
27
- },
28
- "repository": "git+https://github.com/Bcy2020/dsh-cc-ecosystem.git",
29
- "homepage": "https://github.com/Bcy2020/dsh-cc-ecosystem"
30
- }
1
+ {
2
+ "name": "dsh-cc-loader",
3
+ "version": "0.2.0",
4
+ "description": "Shared parse layer for the dsh-cc ecosystem: parses Claude Code .claude/ (project + global) into a standalone .dsh intermediate representation (IR), classifying every component DIRECT/ADAPTED/UNSUPPORTED/BLOCKED and filtering unsupported ones out.",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "README.md"
13
+ ],
14
+ "keywords": [
15
+ "dsh",
16
+ "deepseek-harness",
17
+ "claude-code",
18
+ "loader",
19
+ "parser"
20
+ ],
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "yaml": "^2.0.0"
24
+ },
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "repository": "git+https://github.com/Bcy2020/dsh-cc-ecosystem.git",
29
+ "homepage": "https://github.com/Bcy2020/dsh-cc-ecosystem"
30
+ }
package/src/index.js CHANGED
@@ -39,3 +39,6 @@ export {
39
39
  evaluateCall, parseRulesFor, splitSubcommands, removedToolNames,
40
40
  classifyComponents, STATUS, matchesIfRule,
41
41
  } from './classify.js'
42
+ // Session event-log readers. `Session.events` was removed in DSH 0.1.2-alpha.4
43
+ // and is gone in 0.1.5; every plugin read of the log goes through these.
44
+ export { sessionEvents, sessionEventAt, sessionLastEvent } from './session-compat.js'
@@ -0,0 +1,112 @@
1
+ // session-compat.js — the single compatibility boundary for reading a DSH
2
+ // session's event log.
3
+ //
4
+ // WHY THIS EXISTS
5
+ // ---------------
6
+ // `Session.events` was REMOVED in DSH 0.1.2-alpha.4 and is gone in 0.1.5. It
7
+ // was the full event-log array (both spread and numeric indexing were used on
8
+ // it). Hosts at 0.1.5 expose a different, narrower surface:
9
+ //
10
+ // session.seq -> SessionLogOffset (== log length)
11
+ // session.eventAt(seq) -> SessionEvent | undefined
12
+ // session.snapshotEvents(from?, to?) -> readonly SessionEvent[] (frozen)
13
+ // session.ownEvents() -> readonly SessionEvent[] (post-fork)
14
+ //
15
+ // Reading `session.events` on 0.1.5 does not throw a nice error: it is
16
+ // `undefined`, so a spread raises `agent.session.events is not iterable` and a
17
+ // numeric index raises `Cannot read properties of undefined (reading 'NN')`.
18
+ // Both were live crashes. Every plugin read of the log MUST go through this
19
+ // module so the legacy fallback exists in exactly one place instead of being
20
+ // re-guessed at each call site.
21
+ //
22
+ // SUPPORT POLICY
23
+ // --------------
24
+ // The new API is ALWAYS preferred; the legacy `session.events` property is
25
+ // read only when the new methods are absent, i.e. only on pre-0.1.2-alpha.4
26
+ // hosts. Supporting two shapes is intentional and cheap: `typeof fn ===
27
+ // 'function'` is the whole branch.
28
+ //
29
+ // Both functions are total: a missing, partial, or malformed session object
30
+ // yields `undefined` rather than throwing, so a plugin degrades to "no log
31
+ // visible" instead of crashing the host at an event boundary.
32
+
33
+ /**
34
+ * Read a session's full event log.
35
+ *
36
+ * Prefers `session.snapshotEvents()` (DSH >= 0.1.2-alpha.4). Falls back to the
37
+ * legacy `session.events` array on older hosts. The full log is returned —
38
+ * including any fork-inherited prefix — because callers index it with absolute
39
+ * sequence numbers taken from `session.surface.nodes`, and because the legacy
40
+ * `events` property was itself the full log.
41
+ *
42
+ * @param {unknown} session - the agent's session (`agent.session`), or anything else.
43
+ * @returns {readonly unknown[]|undefined} the event log, or `undefined` when
44
+ * the session exposes neither reader.
45
+ */
46
+ export function sessionEvents(session) {
47
+ if (session === null || typeof session !== 'object') return undefined
48
+ const snapshot = session.snapshotEvents
49
+ if (typeof snapshot === 'function') {
50
+ const events = snapshot.call(session)
51
+ if (Array.isArray(events)) return events
52
+ }
53
+ // A host exposing only the point reader (`eventAt` + `seq`) still has a
54
+ // readable log: materialize it by walking the sequence range. Real 0.1.5
55
+ // sessions have `snapshotEvents` too; this keeps the boundary total.
56
+ const at = session.eventAt
57
+ const end = session.seq
58
+ if (typeof at === 'function' && typeof end === 'number' && Number.isInteger(end) && end >= 0) {
59
+ const events = []
60
+ for (let seq = 0; seq < end; seq += 1) {
61
+ const event = at.call(session, seq)
62
+ if (event === undefined) break
63
+ events.push(event)
64
+ }
65
+ return events
66
+ }
67
+ const legacy = session.events
68
+ return Array.isArray(legacy) ? legacy : undefined
69
+ }
70
+
71
+ /**
72
+ * Read one session event by its absolute sequence number.
73
+ *
74
+ * Prefers `session.eventAt(seq)` (DSH >= 0.1.2-alpha.4). Falls back to numeric
75
+ * indexing into the legacy `session.events` array on older hosts. This is the
76
+ * cheaper reader when only one event is needed: `eventAt` is a direct array
77
+ * lookup, whereas `snapshotEvents()` materializes (and on a full read freezes)
78
+ * the whole log.
79
+ *
80
+ * @param {unknown} session - the agent's session (`agent.session`), or anything else.
81
+ * @param {number} seq - the absolute event sequence number.
82
+ * @returns {unknown} the event, or `undefined` when absent/unreadable.
83
+ */
84
+ export function sessionEventAt(session, seq) {
85
+ if (session === null || typeof session !== 'object') return undefined
86
+ if (typeof seq !== 'number' || !Number.isInteger(seq) || seq < 0) return undefined
87
+ const at = session.eventAt
88
+ if (typeof at === 'function') return at.call(session, seq)
89
+ const legacy = session.events
90
+ return Array.isArray(legacy) ? legacy[seq] : undefined
91
+ }
92
+
93
+ /**
94
+ * The newest event matching a predicate, or `undefined`.
95
+ *
96
+ * A convenience over {@link sessionEvents} for the common "findLast" pattern
97
+ * (`lastTurn`, `lastAssistantMessage`): it scans the log newest-first, so it
98
+ * never builds a reversed copy of a potentially long log.
99
+ *
100
+ * @param {unknown} session - the agent's session (`agent.session`).
101
+ * @param {(event: any) => boolean} predicate - match test for one event.
102
+ * @returns {any} the newest matching event, or `undefined`.
103
+ */
104
+ export function sessionLastEvent(session, predicate) {
105
+ const events = sessionEvents(session)
106
+ if (events === undefined || typeof predicate !== 'function') return undefined
107
+ for (let index = events.length - 1; index >= 0; index -= 1) {
108
+ const event = events[index]
109
+ if (predicate(event)) return event
110
+ }
111
+ return undefined
112
+ }