@ddtcorex/dsh-maestro-memory 1.0.1

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +21 -0
  3. package/README.md +448 -0
  4. package/cordis.patch.yml +13 -0
  5. package/lib/client.js +756 -0
  6. package/lib/index.d.ts +10 -0
  7. package/lib/index.d.ts.map +1 -0
  8. package/lib/index.js +627 -0
  9. package/lib/index.js.map +1 -0
  10. package/lib/memory/batch.d.ts +44 -0
  11. package/lib/memory/batch.d.ts.map +1 -0
  12. package/lib/memory/batch.js +75 -0
  13. package/lib/memory/batch.js.map +1 -0
  14. package/lib/memory/feedback.d.ts +18 -0
  15. package/lib/memory/feedback.d.ts.map +1 -0
  16. package/lib/memory/feedback.js +29 -0
  17. package/lib/memory/feedback.js.map +1 -0
  18. package/lib/memory/store.d.ts +154 -0
  19. package/lib/memory/store.d.ts.map +1 -0
  20. package/lib/memory/store.js +606 -0
  21. package/lib/memory/store.js.map +1 -0
  22. package/lib/migration/cli.d.ts +18 -0
  23. package/lib/migration/cli.d.ts.map +1 -0
  24. package/lib/migration/cli.js +143 -0
  25. package/lib/migration/cli.js.map +1 -0
  26. package/lib/migration/fixture.d.ts +45 -0
  27. package/lib/migration/fixture.d.ts.map +1 -0
  28. package/lib/migration/fixture.js +177 -0
  29. package/lib/migration/fixture.js.map +1 -0
  30. package/lib/migration/service.d.ts +75 -0
  31. package/lib/migration/service.d.ts.map +1 -0
  32. package/lib/migration/service.js +713 -0
  33. package/lib/migration/service.js.map +1 -0
  34. package/lib/prompt/snapshot.d.ts +26 -0
  35. package/lib/prompt/snapshot.d.ts.map +1 -0
  36. package/lib/prompt/snapshot.js +74 -0
  37. package/lib/prompt/snapshot.js.map +1 -0
  38. package/lib/review/queue.d.ts +72 -0
  39. package/lib/review/queue.d.ts.map +1 -0
  40. package/lib/review/queue.js +306 -0
  41. package/lib/review/queue.js.map +1 -0
  42. package/lib/skills-browser.d.ts +67 -0
  43. package/lib/skills-browser.d.ts.map +1 -0
  44. package/lib/skills-browser.js +185 -0
  45. package/lib/skills-browser.js.map +1 -0
  46. package/lib/storage/atomic-store.d.ts +211 -0
  47. package/lib/storage/atomic-store.d.ts.map +1 -0
  48. package/lib/storage/atomic-store.js +582 -0
  49. package/lib/storage/atomic-store.js.map +1 -0
  50. package/lib/storage/layout.d.ts +81 -0
  51. package/lib/storage/layout.d.ts.map +1 -0
  52. package/lib/storage/layout.js +162 -0
  53. package/lib/storage/layout.js.map +1 -0
  54. package/lib/storage/legacy-format.d.ts +104 -0
  55. package/lib/storage/legacy-format.d.ts.map +1 -0
  56. package/lib/storage/legacy-format.js +262 -0
  57. package/lib/storage/legacy-format.js.map +1 -0
  58. package/lib/sync/config.d.ts +22 -0
  59. package/lib/sync/config.d.ts.map +1 -0
  60. package/lib/sync/config.js +57 -0
  61. package/lib/sync/config.js.map +1 -0
  62. package/lib/sync/git.d.ts +97 -0
  63. package/lib/sync/git.d.ts.map +1 -0
  64. package/lib/sync/git.js +282 -0
  65. package/lib/sync/git.js.map +1 -0
  66. package/lib/sync/index.d.ts +6 -0
  67. package/lib/sync/index.d.ts.map +1 -0
  68. package/lib/sync/index.js +6 -0
  69. package/lib/sync/index.js.map +1 -0
  70. package/lib/sync/layout.d.ts +15 -0
  71. package/lib/sync/layout.d.ts.map +1 -0
  72. package/lib/sync/layout.js +33 -0
  73. package/lib/sync/layout.js.map +1 -0
  74. package/lib/sync/merge.d.ts +42 -0
  75. package/lib/sync/merge.d.ts.map +1 -0
  76. package/lib/sync/merge.js +159 -0
  77. package/lib/sync/merge.js.map +1 -0
  78. package/lib/sync/service.d.ts +70 -0
  79. package/lib/sync/service.d.ts.map +1 -0
  80. package/lib/sync/service.js +508 -0
  81. package/lib/sync/service.js.map +1 -0
  82. package/lib/todo/store.d.ts +142 -0
  83. package/lib/todo/store.d.ts.map +1 -0
  84. package/lib/todo/store.js +452 -0
  85. package/lib/todo/store.js.map +1 -0
  86. package/lib/types/client/index.d.ts +7 -0
  87. package/lib/types/client/index.d.ts.map +1 -0
  88. package/package.json +63 -0
  89. package/src/client/index.tsx +994 -0
  90. package/src/host/index.ts +591 -0
  91. package/src/host/memory/batch.ts +107 -0
  92. package/src/host/memory/feedback.ts +36 -0
  93. package/src/host/memory/store.ts +650 -0
  94. package/src/host/migration/cli.ts +139 -0
  95. package/src/host/migration/fixture.ts +218 -0
  96. package/src/host/migration/service.ts +786 -0
  97. package/src/host/prompt/snapshot.ts +95 -0
  98. package/src/host/review/queue.ts +325 -0
  99. package/src/host/skills-browser.ts +191 -0
  100. package/src/host/storage/atomic-store.ts +605 -0
  101. package/src/host/storage/layout.ts +191 -0
  102. package/src/host/storage/legacy-format.ts +308 -0
  103. package/src/host/sync/config.ts +67 -0
  104. package/src/host/sync/git.ts +280 -0
  105. package/src/host/sync/index.ts +5 -0
  106. package/src/host/sync/layout.ts +39 -0
  107. package/src/host/sync/merge.ts +188 -0
  108. package/src/host/sync/service.ts +494 -0
  109. package/src/host/todo/store.ts +480 -0
@@ -0,0 +1,107 @@
1
+ import type { MaestroMemoryStore, MemoryTarget } from './store.ts'
2
+ import { buildFeedbackLine, type FeedbackSentiment } from './feedback.ts'
3
+
4
+ /** Targets accepted by MaestroMemoryStore ('global' is a normalized alias of 'memory'). */
5
+ const VALID_TARGETS = new Set<string>(['memory', 'global', 'user', 'project', 'key', 'daily'])
6
+
7
+ export interface BatchEntryInput {
8
+ target: MemoryTarget | string
9
+ content: string
10
+ /** Working directory for project/key tracks. */
11
+ cwd?: string
12
+ /** Explicit day for daily track (YYYY-MM-DD). */
13
+ date?: string
14
+ /** Branch scope csv for key add (e.g. 'main,dev'). */
15
+ branches?: string
16
+ /** One-line summary for key add (progressive disclosure). */
17
+ summary?: string
18
+ /** When set, appends the end-of-turn `[Feedback]` line to the stored content. */
19
+ sentiment?: FeedbackSentiment
20
+ category?: string
21
+ quote?: string
22
+ note?: string
23
+ }
24
+
25
+ export type BatchResult =
26
+ | { ok: true; ids: (string | undefined)[] }
27
+ | { ok: false; index: number; error: string }
28
+
29
+ /**
30
+ * Add several entries sequentially through {@link MaestroMemoryStore.add}.
31
+ *
32
+ * Atomicity contract: storage-level writes stay per-file atomic (existing
33
+ * appendEntryAtomicSync), and the batch wraps them with rollback-on-failure —
34
+ * every entry successfully created earlier in this same call is removed again
35
+ * before reporting the failure index. Removal targets the generated `[id:]`
36
+ * token when the track issues ids (key), otherwise the trimmed content itself;
37
+ * remove()'s unique-match guard turns any ambiguity into a reported rollback
38
+ * failure instead of deleting a wrong entry. Entries detected as duplicates
39
+ * are left untouched (they pre-date this call).
40
+ *
41
+ * Unknown targets are rejected up-front per entry so a typo never writes a
42
+ * stray file mid-batch.
43
+ */
44
+ export function applyBatch(
45
+ store: MaestroMemoryStore,
46
+ entries: BatchEntryInput[],
47
+ ): BatchResult {
48
+ const added: { target: MemoryTarget; token: string; cwd?: string; date?: string }[] = []
49
+ const ids: (string | undefined)[] = []
50
+
51
+ for (let i = 0; i < entries.length; i++) {
52
+ const e = entries[i]
53
+ const target = String(e?.target ?? '')
54
+ if (!VALID_TARGETS.has(target)) {
55
+ return fail(store, added, i, `unknown target '${target}'`)
56
+ }
57
+ let content = e.content ?? ''
58
+ if (e.sentiment !== undefined) {
59
+ try {
60
+ content = `${content.trimEnd()} ${buildFeedbackLine({
61
+ sentiment: e.sentiment,
62
+ category: e.category,
63
+ quote: e.quote,
64
+ note: e.note,
65
+ })}`
66
+ } catch (err: any) {
67
+ return fail(store, added, i, err?.message ?? String(err))
68
+ }
69
+ }
70
+ const res = store.add(target as MemoryTarget, content, e.cwd, {
71
+ branches: e.branches,
72
+ summary: e.summary,
73
+ date: e.date,
74
+ })
75
+ if (!res.ok) {
76
+ return fail(store, added, i, res.error)
77
+ }
78
+ ids.push(res.id)
79
+ // Duplicates carry no id and did not modify storage — nothing to roll back.
80
+ if (!res.duplicate) {
81
+ added.push({
82
+ target: target as MemoryTarget,
83
+ // key entries carry a generated id token; other tracks are stored
84
+ // verbatim, so the trimmed stored content is the precise removal token.
85
+ token: res.id !== undefined ? `[id:${res.id}]` : content.trim(),
86
+ cwd: e.cwd,
87
+ date: e.date,
88
+ })
89
+ }
90
+ }
91
+ return { ok: true, ids }
92
+ }
93
+
94
+ function fail(
95
+ store: MaestroMemoryStore,
96
+ added: { target: MemoryTarget; token: string; cwd?: string; date?: string }[],
97
+ index: number,
98
+ error: string,
99
+ ): BatchResult {
100
+ const rollbackFailures: string[] = []
101
+ for (const a of added) {
102
+ const rm = store.remove(a.target, a.token, a.cwd, { date: a.date })
103
+ if (!rm.ok) rollbackFailures.push(`${a.target}:${a.token.slice(0, 24)}`)
104
+ }
105
+ const suffix = rollbackFailures.length ? ` (rollback failed for ${rollbackFailures.join(',')})` : ''
106
+ return { ok: false, index, error: `${error}${suffix}` }
107
+ }
@@ -0,0 +1,36 @@
1
+ /** Sentiments accepted for `[Feedback]` lines — anything else is rejected, never stored. */
2
+ export const FEEDBACK_SENTIMENTS = ['positive', 'negative', 'neutral'] as const
3
+
4
+ export type FeedbackSentiment = typeof FEEDBACK_SENTIMENTS[number]
5
+
6
+ export interface FeedbackInput {
7
+ sentiment: FeedbackSentiment
8
+ category?: string
9
+ quote?: string
10
+ note?: string
11
+ }
12
+
13
+ function escapeValue(value: string): string {
14
+ return String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"')
15
+ }
16
+
17
+ /**
18
+ * Format the end-of-turn feedback contract as one trailing line:
19
+ * `[Feedback] sentiment=positive; category="…"; quote="…"; note="…"`.
20
+ *
21
+ * Absent optional fields are omitted; unknown sentiments throw so callers can
22
+ * fail atomically instead of persisting an invalid marker.
23
+ */
24
+ export function buildFeedbackLine(feedback: FeedbackInput): string {
25
+ if (!feedback || feedback.sentiment === undefined || feedback.sentiment === null) {
26
+ throw new Error('feedback sentiment is required')
27
+ }
28
+ if (!FEEDBACK_SENTIMENTS.includes(feedback.sentiment)) {
29
+ throw new Error(`unknown feedback sentiment '${String(feedback.sentiment)}'`)
30
+ }
31
+ const parts: string[] = [`sentiment=${feedback.sentiment}`]
32
+ if (feedback.category !== undefined) parts.push(`category="${escapeValue(feedback.category)}"`)
33
+ if (feedback.quote !== undefined) parts.push(`quote="${escapeValue(feedback.quote)}"`)
34
+ if (feedback.note !== undefined) parts.push(`note="${escapeValue(feedback.note)}"`)
35
+ return `[Feedback] ${parts.join('; ')}`
36
+ }