@inharness-ai/claude4spec 1.0.2 → 1.0.4

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 (97) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +1 -0
  3. package/dist/client/assets/{arc-D8brDDag.js → arc-grMo2vzL.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-Bmh1uTQZ.js → architectureDiagram-3BPJPVTR-Fp_VHONq.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-55jaVWwd.js → blockDiagram-GPEHLZMM-CIwVG0sx.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-Dxm0tLsZ.js → c4Diagram-AAUBKEIU-tysuJyz0.js} +1 -1
  7. package/dist/client/assets/channel-B30S19wt.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-vDJ1slcJ.js → chunk-2J33WTMH-DZFNNwID.js} +1 -1
  9. package/dist/client/assets/{chunk-4BX2VUAB-DP6CC-Qn.js → chunk-4BX2VUAB-CsnJG7Pv.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-SgOSJYkL.js → chunk-55IACEB6-MhREYXzo.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-BtRfbjBO.js → chunk-727SXJPM-BjvyGtK3.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-yIRaTgFg.js → chunk-AQP2D5EJ-zfcfKNbO.js} +1 -1
  13. package/dist/client/assets/{chunk-FMBD7UC4-B_VD4t8N.js → chunk-FMBD7UC4-Ckx-Hc8U.js} +1 -1
  14. package/dist/client/assets/{chunk-ND2GUHAM-BvJdtX82.js → chunk-ND2GUHAM-DSIVVmYM.js} +1 -1
  15. package/dist/client/assets/{chunk-QZHKN3VN-BLNWKymD.js → chunk-QZHKN3VN-EcIM6ZdS.js} +1 -1
  16. package/dist/client/assets/classDiagram-4FO5ZUOK-Bm4SZLsb.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Bm4SZLsb.js +1 -0
  18. package/dist/client/assets/{cose-bilkent-S5V4N54A-BQyA-r-y.js → cose-bilkent-S5V4N54A-DC8DVe0z.js} +1 -1
  19. package/dist/client/assets/{dagre-BM42HDAG-CAh0mTzA.js → dagre-BM42HDAG-BQjcdXnw.js} +1 -1
  20. package/dist/client/assets/{diagram-2AECGRRQ-C83TGK4o.js → diagram-2AECGRRQ-BUPeBMwt.js} +1 -1
  21. package/dist/client/assets/{diagram-5GNKFQAL-2L6uowvf.js → diagram-5GNKFQAL-I_Q-Pnp6.js} +1 -1
  22. package/dist/client/assets/{diagram-KO2AKTUF-Cj5n4jdq.js → diagram-KO2AKTUF-B8WbbWJu.js} +1 -1
  23. package/dist/client/assets/{diagram-LMA3HP47-Bp5SDMjg.js → diagram-LMA3HP47-CsACxgW7.js} +1 -1
  24. package/dist/client/assets/{diagram-OG6HWLK6-cFsDQ8Qk.js → diagram-OG6HWLK6-lPuJxQED.js} +1 -1
  25. package/dist/client/assets/{erDiagram-TEJ5UH35-D8G5xpg3.js → erDiagram-TEJ5UH35-cU5ZGREz.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-I6XJVG4X-CuNudlCD.js → flowDiagram-I6XJVG4X-Dr-vZTBV.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-6RSMTGT7-CtFlsB15.js → ganttDiagram-6RSMTGT7-CXiWZ0R8.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-h66Tso4O.js → gitGraphDiagram-PVQCEYII-PDvLBbvz.js} +1 -1
  29. package/dist/client/assets/{index-aPVup1Re.js → index-BQltseJ-.js} +1 -1
  30. package/dist/client/assets/{index-aJE-l7h7.js → index-RMlMPKY7.js} +1 -1
  31. package/dist/client/assets/{index-D5ljIkCJ.js → index-YFPhJxE5.js} +184 -174
  32. package/dist/client/assets/{index-CZOpb3O7.css → index-zpHL2aYW.css} +1 -1
  33. package/dist/client/assets/{infoDiagram-5YYISTIA-CO_BLfuW.js → infoDiagram-5YYISTIA-Re9fOUPS.js} +1 -1
  34. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-D4rSRnQA.js → ishikawaDiagram-YF4QCWOH-BTazB_7p.js} +1 -1
  35. package/dist/client/assets/{journeyDiagram-JHISSGLW-GyHI6dB8.js → journeyDiagram-JHISSGLW-pGUoqxHl.js} +1 -1
  36. package/dist/client/assets/{kanban-definition-UN3LZRKU-CrH33WLe.js → kanban-definition-UN3LZRKU-C2RzCJrM.js} +1 -1
  37. package/dist/client/assets/{linear-Dh1OyvSm.js → linear-Be3WPYKf.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-C45sMDhS.js → mermaid.core-GdPXct3q.js} +4 -4
  39. package/dist/client/assets/{mindmap-definition-RKZ34NQL-Bgwb0vwA.js → mindmap-definition-RKZ34NQL-DvktVTUU.js} +1 -1
  40. package/dist/client/assets/{pieDiagram-4H26LBE5-l9cCpVfG.js → pieDiagram-4H26LBE5-DvC_m4m5.js} +1 -1
  41. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BCPSUbC3.js → quadrantDiagram-W4KKPZXB-SWtidcZL.js} +1 -1
  42. package/dist/client/assets/{requirementDiagram-4Y6WPE33-TEGOe-vV.js → requirementDiagram-4Y6WPE33-CQEHDx1W.js} +1 -1
  43. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-C69pv_7D.js → sankeyDiagram-5OEKKPKP-C0PTA3gP.js} +1 -1
  44. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-arinzeCk.js → sequenceDiagram-3UESZ5HK-D-x7SXoo.js} +1 -1
  45. package/dist/client/assets/{stateDiagram-AJRCARHV-C2YENpmw.js → stateDiagram-AJRCARHV-BhcGVfs1.js} +1 -1
  46. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-2vcyLY1V.js +1 -0
  47. package/dist/client/assets/{timeline-definition-PNZ67QCA-h5qtI99f.js → timeline-definition-PNZ67QCA-JYTlKPB-.js} +1 -1
  48. package/dist/client/assets/{vennDiagram-CIIHVFJN-CnuK75Ut.js → vennDiagram-CIIHVFJN-KjNL1Qpz.js} +1 -1
  49. package/dist/client/assets/{wardley-L42UT6IY-B4bQ0Xyy.js → wardley-L42UT6IY-DcljmmEY.js} +1 -1
  50. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-apV8olQc.js → wardleyDiagram-YWT4CUSO-Pbq6ALgg.js} +1 -1
  51. package/dist/client/assets/{xychartDiagram-2RQKCTM6-OTP6zeEy.js → xychartDiagram-2RQKCTM6-G_r_2Cvk.js} +1 -1
  52. package/dist/client/index.html +2 -2
  53. package/dist/server/config.d.ts +6 -0
  54. package/dist/server/config.js +6 -0
  55. package/dist/server/config.js.map +1 -1
  56. package/dist/server/db/migrate.js +28 -11
  57. package/dist/server/db/migrate.js.map +1 -1
  58. package/dist/server/db/migrations/028_page_version_kind_patch.sql +43 -0
  59. package/dist/server/db/migrations/029_chat_thread_patch.sql +64 -0
  60. package/dist/server/index.js +73 -4
  61. package/dist/server/index.js.map +1 -1
  62. package/dist/server/routes/chat.d.ts +2 -0
  63. package/dist/server/routes/chat.js +14 -0
  64. package/dist/server/routes/chat.js.map +1 -1
  65. package/dist/server/routes/errors.js +4 -0
  66. package/dist/server/routes/errors.js.map +1 -1
  67. package/dist/server/routes/patches.d.ts +8 -0
  68. package/dist/server/routes/patches.js +106 -0
  69. package/dist/server/routes/patches.js.map +1 -0
  70. package/dist/server/services/brief.js +0 -1
  71. package/dist/server/services/brief.js.map +1 -1
  72. package/dist/server/services/chat-context.d.ts +3 -1
  73. package/dist/server/services/chat-context.js +32 -1
  74. package/dist/server/services/chat-context.js.map +1 -1
  75. package/dist/server/services/chat.d.ts +5 -2
  76. package/dist/server/services/chat.js +35 -7
  77. package/dist/server/services/chat.js.map +1 -1
  78. package/dist/server/services/page-version.d.ts +11 -4
  79. package/dist/server/services/page-version.js +19 -11
  80. package/dist/server/services/page-version.js.map +1 -1
  81. package/dist/server/services/pages-frontmatter-indexer.d.ts +4 -1
  82. package/dist/server/services/pages-frontmatter-indexer.js +19 -9
  83. package/dist/server/services/pages-frontmatter-indexer.js.map +1 -1
  84. package/dist/server/services/patch.d.ts +66 -0
  85. package/dist/server/services/patch.js +196 -0
  86. package/dist/server/services/patch.js.map +1 -0
  87. package/dist/shared/entities.d.ts +62 -2
  88. package/dist/shared/entities.js +11 -0
  89. package/dist/shared/entities.js.map +1 -1
  90. package/dist/shared/types.d.ts +5 -2
  91. package/docs/screenshots/hero-dark.png +0 -0
  92. package/docs/screenshots/hero.png +0 -0
  93. package/package.json +3 -2
  94. package/dist/client/assets/channel-ArVcv4Yl.js +0 -1
  95. package/dist/client/assets/classDiagram-4FO5ZUOK-B3nmPR7w.js +0 -1
  96. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B3nmPR7w.js +0 -1
  97. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CS6ocDH6.js +0 -1
@@ -0,0 +1,196 @@
1
+ /**
2
+ * M23 PatchService — thin wrapper over the third `PagesService` instance
3
+ * mounted on `patchesDir`. Patches are markdown files with mandatory YAML
4
+ * frontmatter (`type: patch`, `brief`, `patch_kind`, `created_at`,
5
+ * `created_by`, `status`). They are authored by coding agents in *other*
6
+ * terminals during brief implementation — claude4spec only reads them, lets
7
+ * the spec author flip `status`, and spins up a chat thread to fold the
8
+ * patch's findings back into the spec.
9
+ *
10
+ * Design notes (parallel to BriefService):
11
+ * - **Zero new tables**. Listing comes from PagesFrontmatterIndexer.
12
+ * - **Optimistic concurrency** by sha256 hash of full content.
13
+ * - **Immutable frontmatter** keys protected (PATCH_IMMUTABLE_FRONTMATTER_KEYS);
14
+ * only `status` is mutable from the claude4spec side.
15
+ * - A patch links to a brief via the `brief` frontmatter field, or — when
16
+ * absent — by filename prefix. Unresolvable ⇒ orphan (`briefPath: null`).
17
+ */
18
+ import crypto from 'node:crypto';
19
+ import fs from 'node:fs/promises';
20
+ import path from 'node:path';
21
+ import matter from 'gray-matter';
22
+ import { PATCH_IMMUTABLE_FRONTMATTER_KEYS } from '../../shared/entities.js';
23
+ import { DomainError } from './tags.js';
24
+ import { ConflictError } from './brief.js';
25
+ const VALID_PATCH_KINDS = new Set([
26
+ 'drift',
27
+ 'missing',
28
+ 'incorrect',
29
+ 'clarification',
30
+ ]);
31
+ export class PatchService {
32
+ deps;
33
+ constructor(deps) {
34
+ this.deps = deps;
35
+ }
36
+ // ─── Reads ───────────────────────────────────────────────────────────────
37
+ async getPatch(relPath) {
38
+ if (!(await this.deps.patchesPages.exists(relPath))) {
39
+ throw new DomainError('NOT_FOUND', `patch '${relPath}' not found`);
40
+ }
41
+ const abs = this.absPath(relPath);
42
+ const content = await fs.readFile(abs, 'utf-8');
43
+ const parsed = matter(content);
44
+ const frontmatter = (parsed.data ?? {});
45
+ if (frontmatter.type !== 'patch') {
46
+ throw new DomainError('PATCH_INVALID_FRONTMATTER', `file '${relPath}' is not a patch (frontmatter.type=${JSON.stringify(frontmatter.type)})`);
47
+ }
48
+ return {
49
+ path: relPath,
50
+ title: extractTitle(parsed.content, frontmatter, relPath),
51
+ frontmatter,
52
+ body: parsed.content,
53
+ content,
54
+ hash: hashContent(content),
55
+ };
56
+ }
57
+ listPatches(opts = {}) {
58
+ const briefPaths = this.knownBriefPaths();
59
+ const records = this.deps.frontmatterIndexer.findByFrontmatterType('patch', {
60
+ rootDir: 'patches',
61
+ });
62
+ const out = [];
63
+ for (const rec of records) {
64
+ const fm = rec.frontmatter;
65
+ const status = fm.status === 'completed' ? 'completed' : 'awaiting';
66
+ const briefPath = this.resolveBriefPath(rec.path, fm, briefPaths);
67
+ if (opts.brief !== undefined && opts.brief !== briefPath)
68
+ continue;
69
+ if (opts.status !== undefined && opts.status !== status)
70
+ continue;
71
+ const lastVersion = this.deps.pageVersions.getLatestForPath(rec.path, undefined, 'patch');
72
+ const createdAt = toIso(fm.created_at);
73
+ out.push({
74
+ path: rec.path,
75
+ title: extractTitleFromFrontmatter(fm, rec.path),
76
+ briefPath,
77
+ patchKind: normalizeKind(fm.patch_kind),
78
+ status,
79
+ createdAt,
80
+ createdBy: String(fm.created_by ?? ''),
81
+ lastModified: lastVersion?.createdAt ?? createdAt,
82
+ threadCount: this.deps.chatService.threadCountForPatch(rec.path),
83
+ });
84
+ }
85
+ return out;
86
+ }
87
+ listThreadsForPatch(relPath) {
88
+ return this.deps.chatService.listThreadsForPatch(relPath).map((t) => ({
89
+ id: t.id,
90
+ title: t.title,
91
+ updatedAt: t.updatedAt,
92
+ messageCount: t.messageCount,
93
+ }));
94
+ }
95
+ // ─── Mutations ──────────────────────────────────────────────────────────
96
+ async updateContent(opts) {
97
+ const current = await this.getPatch(opts.path);
98
+ if (typeof opts.expectedHash === 'string' && opts.expectedHash !== current.hash) {
99
+ throw new ConflictError('PATCH_CONFLICT', 'patch changed since last read', current.hash);
100
+ }
101
+ const incoming = matter(opts.content);
102
+ const incomingFm = (incoming.data ?? {});
103
+ const violated = PATCH_IMMUTABLE_FRONTMATTER_KEYS.filter((k) => JSON.stringify(incomingFm[k]) !== JSON.stringify(current.frontmatter[k]));
104
+ if (violated.length > 0) {
105
+ throw new DomainError('PATCH_FRONTMATTER_IMMUTABLE', `cannot mutate immutable frontmatter keys: ${violated.join(', ')}`);
106
+ }
107
+ const abs = this.absPath(opts.path);
108
+ this.deps.patchesWatcher.suppress(opts.path);
109
+ await fs.writeFile(abs, opts.content, 'utf-8');
110
+ await this.deps.pageVersions.recordVersion(opts.path, 'update', 'user', undefined, this.deps.patchesSerializer, 'patch');
111
+ await this.deps.frontmatterIndexer.indexPage('patches', opts.path);
112
+ return this.getPatch(opts.path);
113
+ }
114
+ async updateFrontmatter(opts) {
115
+ const current = await this.getPatch(opts.path);
116
+ const next = { ...current.frontmatter, status: opts.status };
117
+ const newContent = matter.stringify(current.body, next);
118
+ const abs = this.absPath(opts.path);
119
+ this.deps.patchesWatcher.suppress(opts.path);
120
+ await fs.writeFile(abs, newContent, 'utf-8');
121
+ await this.deps.pageVersions.recordVersion(opts.path, 'update', 'user', undefined, this.deps.patchesSerializer, 'patch', `set status=${opts.status}`);
122
+ await this.deps.frontmatterIndexer.indexPage('patches', opts.path);
123
+ return this.getPatch(opts.path);
124
+ }
125
+ async createThreadForPatch(relPath, name) {
126
+ if (!(await this.deps.patchesPages.exists(relPath))) {
127
+ throw new DomainError('NOT_FOUND', `patch '${relPath}' not found`);
128
+ }
129
+ const thread = this.deps.chatService.createThread(name ?? `Patch: ${relPath}`, {
130
+ contextType: 'patch',
131
+ patchPath: relPath,
132
+ });
133
+ return { threadId: thread.id };
134
+ }
135
+ // ─── Internals ──────────────────────────────────────────────────────────
136
+ absPath(relPath) {
137
+ return path.join(this.deps.patchesPages.root, relPath);
138
+ }
139
+ knownBriefPaths() {
140
+ return this.deps.frontmatterIndexer
141
+ .findByFrontmatterType('brief', { rootDir: 'briefs' })
142
+ .map((r) => r.path);
143
+ }
144
+ /**
145
+ * Resolve the brief a patch belongs to: the `brief` frontmatter field if it
146
+ * names an existing brief, else the brief whose filename stem is the longest
147
+ * prefix of the patch filename stem, else `null` (orphan).
148
+ */
149
+ resolveBriefPath(patchPath, fm, briefPaths) {
150
+ if (typeof fm.brief === 'string' && fm.brief.length > 0) {
151
+ const declared = fm.brief;
152
+ if (briefPaths.includes(declared))
153
+ return declared;
154
+ }
155
+ const patchStem = stem(patchPath);
156
+ let best = null;
157
+ for (const bp of briefPaths) {
158
+ const briefStem = stem(bp);
159
+ if (patchStem === briefStem || patchStem.startsWith(briefStem + '-')) {
160
+ if (best === null || stem(bp).length > stem(best).length)
161
+ best = bp;
162
+ }
163
+ }
164
+ return best;
165
+ }
166
+ }
167
+ /**
168
+ * YAML auto-parses ISO timestamps into JS `Date` objects — normalize back to
169
+ * an ISO 8601 string for DTO fields (`PatchListItem.createdAt`).
170
+ */
171
+ function toIso(v) {
172
+ if (v instanceof Date)
173
+ return v.toISOString();
174
+ return v == null ? '' : String(v);
175
+ }
176
+ function stem(p) {
177
+ return path.basename(p).replace(/\.md$/i, '');
178
+ }
179
+ function normalizeKind(raw) {
180
+ return VALID_PATCH_KINDS.has(String(raw)) ? raw : 'clarification';
181
+ }
182
+ function extractTitle(body, fm, relPath) {
183
+ const h1 = body.match(/^#\s+(.+)$/m);
184
+ if (h1 && h1[1])
185
+ return h1[1].trim();
186
+ return extractTitleFromFrontmatter(fm, relPath);
187
+ }
188
+ function extractTitleFromFrontmatter(fm, relPath) {
189
+ if (typeof fm.title === 'string' && fm.title.length > 0)
190
+ return fm.title;
191
+ return stem(relPath);
192
+ }
193
+ function hashContent(content) {
194
+ return crypto.createHash('sha256').update(content, 'utf-8').digest('hex');
195
+ }
196
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../../../src/server/services/patch.ts"],"names":[],"mappings":"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"}
@@ -446,8 +446,11 @@ export type ChatRole = 'user' | 'assistant' | 'tool_use' | 'tool_result' | 'user
446
446
  * M21 generic context discriminator. 'chat' = default (full toolset, overlay UI).
447
447
  * 'brief' = brief editorial thread (whitelisted tools, brief-detail chrome,
448
448
  * brief_path points to FS file under briefsDir).
449
+ * M23 'patch' = patch resolution thread — applies a patch's findings to the
450
+ * spec. Full spec-editing toolset; patch_path points to FS file under
451
+ * patchesDir; the patch content is injected into the system prompt.
449
452
  */
450
- export type ChatContextType = 'chat' | 'brief';
453
+ export type ChatContextType = 'chat' | 'brief' | 'patch';
451
454
  export interface ChatThread {
452
455
  id: string;
453
456
  title: string | null;
@@ -461,6 +464,8 @@ export interface ChatThread {
461
464
  hasSystemPrompt: boolean;
462
465
  contextType: ChatContextType;
463
466
  briefPath: string | null;
467
+ /** M23: FS path (relative to patchesDir) — set iff contextType='patch'. */
468
+ patchPath: string | null;
464
469
  createdAt: string;
465
470
  updatedAt: string;
466
471
  }
@@ -587,7 +592,6 @@ export interface BriefListItem {
587
592
  implemented: boolean;
588
593
  generatedAt: string;
589
594
  lastModifiedAt: string | null;
590
- threadCount: number;
591
595
  }
592
596
  export interface BriefCreateRequest {
593
597
  /** `null` = initial brief (no previous release to compare against). */
@@ -620,3 +624,59 @@ export interface BriefThreadSummary {
620
624
  updatedAt: string;
621
625
  messageCount: number;
622
626
  }
627
+ /** Why a coding agent filed the patch (frontmatter `patch_kind`). */
628
+ export type PatchKind = 'drift' | 'missing' | 'incorrect' | 'clarification';
629
+ /** Resolution state — `awaiting` until the spec author resolves the patch. */
630
+ export type PatchStatus = 'awaiting' | 'completed';
631
+ /**
632
+ * Reserved frontmatter keys — set by the terminal agent that authored the
633
+ * patch, immutable from the claude4spec side. Only `status` is mutable.
634
+ */
635
+ export declare const PATCH_IMMUTABLE_FRONTMATTER_KEYS: readonly ["type", "brief", "patch_kind", "created_at", "created_by"];
636
+ export interface PatchFrontmatter {
637
+ type: 'patch';
638
+ /** Path of the associated brief (relative to briefsDir). Absent ⇒ resolve by filename prefix. */
639
+ brief?: string;
640
+ patch_kind: PatchKind;
641
+ created_at: string;
642
+ created_by: string;
643
+ /** Absent is treated as `'awaiting'`. */
644
+ status?: PatchStatus;
645
+ [key: string]: unknown;
646
+ }
647
+ /** Response of `GET /api/patches/:path` and the result of PUT/PATCH writes. */
648
+ export interface PatchResponse {
649
+ /** Path relative to patchesDir. */
650
+ path: string;
651
+ title: string;
652
+ frontmatter: PatchFrontmatter;
653
+ body: string;
654
+ /** Full file content (frontmatter + body, byte-faithful). */
655
+ content: string;
656
+ /** sha256 hex of `content` — used for optimistic concurrency. */
657
+ hash: string;
658
+ }
659
+ export interface PatchListItem {
660
+ path: string;
661
+ title: string;
662
+ /** `null` = orphan (no resolvable brief). */
663
+ briefPath: string | null;
664
+ patchKind: PatchKind;
665
+ status: PatchStatus;
666
+ createdAt: string;
667
+ createdBy: string;
668
+ /** `created_at` of the latest page_version row with kind='patch'. */
669
+ lastModified: string;
670
+ /** Count of chat threads with context_type='patch' pointing at this patch. */
671
+ threadCount: number;
672
+ }
673
+ export interface PatchContentUpdateRequest {
674
+ content: string;
675
+ expectedHash: string;
676
+ }
677
+ export interface PatchFrontmatterUpdateRequest {
678
+ status: PatchStatus;
679
+ }
680
+ export interface PatchThreadCreateRequest {
681
+ name?: string;
682
+ }
@@ -6,4 +6,15 @@ export const BRIEF_IMMUTABLE_FRONTMATTER_KEYS = [
6
6
  'generated_at',
7
7
  'generator_version',
8
8
  ];
9
+ /**
10
+ * Reserved frontmatter keys — set by the terminal agent that authored the
11
+ * patch, immutable from the claude4spec side. Only `status` is mutable.
12
+ */
13
+ export const PATCH_IMMUTABLE_FRONTMATTER_KEYS = [
14
+ 'type',
15
+ 'brief',
16
+ 'patch_kind',
17
+ 'created_at',
18
+ 'created_by',
19
+ ];
9
20
  //# sourceMappingURL=entities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/shared/entities.ts"],"names":[],"mappings":"AA6oBA,sEAAsE;AACtE,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,MAAM;IACN,cAAc;IACd,YAAY;IACZ,cAAc;IACd,mBAAmB;CACX,CAAC"}
1
+ {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/shared/entities.ts"],"names":[],"mappings":"AAkpBA,sEAAsE;AACtE,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,MAAM;IACN,cAAc;IACd,YAAY;IACZ,cAAc;IACd,mBAAmB;CACX,CAAC;AAiFX;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,MAAM;IACN,OAAO;IACP,YAAY;IACZ,YAAY;IACZ,YAAY;CACJ,CAAC"}
@@ -67,12 +67,15 @@ export type WsEvent = {
67
67
  } | {
68
68
  kind: 'briefs:changed';
69
69
  path?: string;
70
+ } | {
71
+ kind: 'patches:changed';
72
+ path?: string;
70
73
  } | {
71
74
  kind: 'hello';
72
75
  ts: number;
73
76
  };
74
- /** M02 multidir: discriminator dla source-of-truth (pagesDir vs briefsDir). */
75
- export type PagesRootDir = 'pages' | 'briefs';
77
+ /** M02 multidir: discriminator dla source-of-truth (pagesDir / briefsDir / patchesDir). */
78
+ export type PagesRootDir = 'pages' | 'briefs' | 'patches';
76
79
  export interface TodoHit {
77
80
  pagePath: string;
78
81
  line: number;
Binary file
Binary file
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@inharness-ai/claude4spec",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Plan the whole system before your agent writes a line of code. Local-first planning layer with structured entities and typed relations.",
5
5
  "license": "MIT",
6
6
  "author": "Michael Tomala <mi.tomala@gmail.com>",
7
- "homepage": "https://github.com/InHarness/claude4spec#readme",
7
+ "homepage": "https://claude4spec.inharness.ai",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/InHarness/claude4spec.git"
@@ -27,6 +27,7 @@
27
27
  "dist",
28
28
  ".claude/skills/c4s-spec-reader",
29
29
  ".claude/skills/c4s-brief-implementer",
30
+ "docs/screenshots",
30
31
  "README.md",
31
32
  "LICENSE",
32
33
  "CHANGELOG.md"
@@ -1 +0,0 @@
1
- import{ai as o,aj as n}from"./mermaid.core-C45sMDhS.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-727SXJPM-BtRfbjBO.js";import{_ as i}from"./mermaid.core-C45sMDhS.js";import"./chunk-FMBD7UC4-B_VD4t8N.js";import"./chunk-ND2GUHAM-BvJdtX82.js";import"./chunk-55IACEB6-SgOSJYkL.js";import"./chunk-2J33WTMH-vDJ1slcJ.js";import"./index-D5ljIkCJ.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-727SXJPM-BtRfbjBO.js";import{_ as i}from"./mermaid.core-C45sMDhS.js";import"./chunk-FMBD7UC4-B_VD4t8N.js";import"./chunk-ND2GUHAM-BvJdtX82.js";import"./chunk-55IACEB6-SgOSJYkL.js";import"./chunk-2J33WTMH-vDJ1slcJ.js";import"./index-D5ljIkCJ.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{s as e,b as r,a,S as s}from"./chunk-AQP2D5EJ-yIRaTgFg.js";import{_ as i}from"./mermaid.core-C45sMDhS.js";import"./chunk-55IACEB6-SgOSJYkL.js";import"./chunk-2J33WTMH-vDJ1slcJ.js";import"./index-D5ljIkCJ.js";var p={parser:a,get db(){return new s(2)},renderer:r,styles:e,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{p as diagram};