@iq-context/core 0.1.0 → 0.3.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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/dist/capture.js +2 -1
  3. package/dist/capture.js.map +1 -1
  4. package/dist/config.d.ts +1 -0
  5. package/dist/config.js +10 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/discovery.d.ts +9 -0
  8. package/dist/discovery.js +25 -0
  9. package/dist/discovery.js.map +1 -1
  10. package/dist/doctor.js +140 -112
  11. package/dist/doctor.js.map +1 -1
  12. package/dist/find.js +2 -1
  13. package/dist/find.js.map +1 -1
  14. package/dist/git.d.ts +1 -0
  15. package/dist/git.js +15 -0
  16. package/dist/git.js.map +1 -0
  17. package/dist/go.js +6 -1
  18. package/dist/go.js.map +1 -1
  19. package/dist/headline.d.ts +13 -0
  20. package/dist/headline.js +32 -0
  21. package/dist/headline.js.map +1 -0
  22. package/dist/index.d.ts +14 -3
  23. package/dist/index.js +12 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/init.js +93 -51
  26. package/dist/init.js.map +1 -1
  27. package/dist/invariantSnapshot.d.ts +2 -0
  28. package/dist/invariantSnapshot.js +219 -0
  29. package/dist/invariantSnapshot.js.map +1 -0
  30. package/dist/invariantTypes.d.ts +100 -0
  31. package/dist/invariantTypes.js +4 -0
  32. package/dist/invariantTypes.js.map +1 -0
  33. package/dist/invariants.d.ts +13 -0
  34. package/dist/invariants.js +415 -0
  35. package/dist/invariants.js.map +1 -0
  36. package/dist/listMutation.d.ts +4 -0
  37. package/dist/listMutation.js +37 -0
  38. package/dist/listMutation.js.map +1 -0
  39. package/dist/obligations.d.ts +3 -0
  40. package/dist/obligations.js +84 -0
  41. package/dist/obligations.js.map +1 -0
  42. package/dist/persistence.js +7 -4
  43. package/dist/persistence.js.map +1 -1
  44. package/dist/save.js +34 -34
  45. package/dist/save.js.map +1 -1
  46. package/dist/staleness.d.ts +2 -2
  47. package/dist/staleness.js +12 -89
  48. package/dist/staleness.js.map +1 -1
  49. package/dist/state.d.ts +6 -1
  50. package/dist/state.js +63 -23
  51. package/dist/state.js.map +1 -1
  52. package/dist/stateSchemas.d.ts +16 -0
  53. package/dist/stateSchemas.js +212 -0
  54. package/dist/stateSchemas.js.map +1 -0
  55. package/dist/status.js +1 -1
  56. package/dist/status.js.map +1 -1
  57. package/dist/trackedPath.d.ts +3 -0
  58. package/dist/trackedPath.js +27 -0
  59. package/dist/trackedPath.js.map +1 -0
  60. package/dist/types.d.ts +33 -18
  61. package/dist/workstream.js +3 -8
  62. package/dist/workstream.js.map +1 -1
  63. package/dist/workstreamId.d.ts +2 -0
  64. package/dist/workstreamId.js +67 -0
  65. package/dist/workstreamId.js.map +1 -0
  66. package/dist/wrap.js +100 -46
  67. package/dist/wrap.js.map +1 -1
  68. package/package.json +12 -2
@@ -0,0 +1,219 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { parseProjectConfigFile } from "./config.js";
4
+ import { getProjectPaths } from "./state.js";
5
+ import { parseCaptureEntry, parseFocusState, parseProjectState, parseResumeState, parseWorkstreamState, } from "./stateSchemas.js";
6
+ import { resolveWorkstreamPath, validateWorkstreamId } from "./workstreamId.js";
7
+ function ok(value) {
8
+ return { loaded: true, value };
9
+ }
10
+ function failed(error) {
11
+ return { loaded: false, error: error instanceof Error ? error.message : String(error) };
12
+ }
13
+ function messageOf(error) {
14
+ return error instanceof Error ? error.message : String(error);
15
+ }
16
+ function loadParsedJson(filePath, parse) {
17
+ try {
18
+ if (!fs.existsSync(filePath)) {
19
+ return { loaded: false, error: `Missing required state file: ${filePath}` };
20
+ }
21
+ const raw = JSON.parse(fs.readFileSync(filePath, "utf8"));
22
+ return ok(parse(raw, filePath));
23
+ }
24
+ catch (error) {
25
+ return failed(error);
26
+ }
27
+ }
28
+ /**
29
+ * Preserves the id validation `loadProjectState` performs, so a traversal
30
+ * attempt stays a load failure instead of degrading to a referential violation.
31
+ */
32
+ function loadProjectStateResult(projectStatePath) {
33
+ const result = loadParsedJson(projectStatePath, parseProjectState);
34
+ if (!result.loaded) {
35
+ return result;
36
+ }
37
+ try {
38
+ for (const workstreamId of result.value.workstreams) {
39
+ validateWorkstreamId(workstreamId, `persisted workstream id in ${projectStatePath}`);
40
+ }
41
+ }
42
+ catch (error) {
43
+ return failed(error);
44
+ }
45
+ return result;
46
+ }
47
+ /** Symmetric with loadProjectStateResult: loadFocusState validates this id too. */
48
+ function loadFocusStateResult(focusStatePath) {
49
+ const result = loadParsedJson(focusStatePath, parseFocusState);
50
+ if (!result.loaded) {
51
+ return result;
52
+ }
53
+ try {
54
+ validateWorkstreamId(result.value.current_workstream_id, `current_workstream_id in ${focusStatePath}`);
55
+ }
56
+ catch (error) {
57
+ return failed(error);
58
+ }
59
+ return result;
60
+ }
61
+ function loadConfig(configPath) {
62
+ try {
63
+ return ok(parseProjectConfigFile(configPath));
64
+ }
65
+ catch (error) {
66
+ return failed(error);
67
+ }
68
+ }
69
+ function loadPointer(pointerPath) {
70
+ try {
71
+ if (!fs.existsSync(pointerPath)) {
72
+ return ok(null);
73
+ }
74
+ const value = fs.readFileSync(pointerPath, "utf8").trim();
75
+ if (value.length === 0) {
76
+ return ok(null);
77
+ }
78
+ return ok(validateWorkstreamId(value, `workstream pointer in ${pointerPath}`));
79
+ }
80
+ catch (error) {
81
+ return failed(error);
82
+ }
83
+ }
84
+ /**
85
+ * A failure here means the listing is unknown, which is deliberately distinct
86
+ * from a successfully read empty directory. Missing and unreadable are both
87
+ * failures because neither yields a trustworthy list; doctor reports the
88
+ * directory's absence separately.
89
+ */
90
+ function enumerateWorkstreamDirs(workstreamsDir) {
91
+ let entries;
92
+ try {
93
+ if (!fs.existsSync(workstreamsDir)) {
94
+ return { loaded: false, error: `Missing workstreams directory: ${workstreamsDir}` };
95
+ }
96
+ if (!fs.statSync(workstreamsDir).isDirectory()) {
97
+ return { loaded: false, error: `Not a directory: ${workstreamsDir}` };
98
+ }
99
+ entries = fs.readdirSync(workstreamsDir, { withFileTypes: true });
100
+ }
101
+ catch (error) {
102
+ return failed(error);
103
+ }
104
+ const valid = [];
105
+ const invalid = [];
106
+ for (const entry of entries) {
107
+ if (!entry.isDirectory()) {
108
+ continue;
109
+ }
110
+ // Hidden entries are never workstreams; an interrupted atomic write can
111
+ // leave scratch entries here, and residue must not read as a bad name.
112
+ if (entry.name.startsWith(".")) {
113
+ continue;
114
+ }
115
+ try {
116
+ validateWorkstreamId(entry.name, "persisted workstream directory name");
117
+ valid.push(entry.name);
118
+ }
119
+ catch {
120
+ invalid.push(entry.name);
121
+ }
122
+ }
123
+ return ok({ valid: valid.sort(), invalid: invalid.sort() });
124
+ }
125
+ function loadCaptureLog(capturePath) {
126
+ const results = [];
127
+ let content;
128
+ try {
129
+ if (!fs.existsSync(capturePath)) {
130
+ return results;
131
+ }
132
+ content = fs.readFileSync(capturePath, "utf8");
133
+ }
134
+ catch (error) {
135
+ return [{ loaded: false, kind: "syntax", error: messageOf(error) }];
136
+ }
137
+ for (const [index, line] of content.split(/\r?\n/).entries()) {
138
+ if (line.trim().length === 0) {
139
+ continue;
140
+ }
141
+ const source = `${capturePath}:${index + 1}`;
142
+ let raw;
143
+ try {
144
+ raw = JSON.parse(line);
145
+ }
146
+ catch (error) {
147
+ results.push({ loaded: false, kind: "syntax", error: `${source}: ${messageOf(error)}` });
148
+ continue;
149
+ }
150
+ try {
151
+ results.push({ loaded: true, value: parseCaptureEntry(raw, source) });
152
+ }
153
+ catch (error) {
154
+ results.push({ loaded: false, kind: "schema", error: messageOf(error) });
155
+ }
156
+ }
157
+ return results;
158
+ }
159
+ /**
160
+ * Every in-workstream read goes through resolveWorkstreamPath inside a try, so
161
+ * a symlinked file inside a real workstream directory becomes a load failure
162
+ * rather than a read outside .iq-context.
163
+ */
164
+ function loadWorkstreamFile(projectRoot, workstreamId, fileName, parse) {
165
+ try {
166
+ return loadParsedJson(resolveWorkstreamPath(projectRoot, workstreamId, fileName), parse);
167
+ }
168
+ catch (error) {
169
+ return failed(error);
170
+ }
171
+ }
172
+ function loadOptionalJson(projectRoot, workstreamId, fileName) {
173
+ try {
174
+ const filePath = resolveWorkstreamPath(projectRoot, workstreamId, fileName);
175
+ if (!fs.existsSync(filePath)) {
176
+ return ok(null);
177
+ }
178
+ return ok(JSON.parse(fs.readFileSync(filePath, "utf8")));
179
+ }
180
+ catch (error) {
181
+ return failed(error);
182
+ }
183
+ }
184
+ function loadWorkstreamCaptures(projectRoot, workstreamId) {
185
+ let capturePath;
186
+ try {
187
+ capturePath = resolveWorkstreamPath(projectRoot, workstreamId, "captures.jsonl");
188
+ }
189
+ catch (error) {
190
+ // Containment, not syntax: grading this as a syntax failure would misreport
191
+ // why the file was rejected.
192
+ return [{ loaded: false, kind: "containment", error: messageOf(error) }];
193
+ }
194
+ return loadCaptureLog(capturePath);
195
+ }
196
+ export function loadInvariantSnapshot(projectRoot) {
197
+ const paths = getProjectPaths(projectRoot);
198
+ const workstreamDirs = enumerateWorkstreamDirs(paths.workstreamsDir);
199
+ const workstreams = workstreamDirs.loaded
200
+ ? workstreamDirs.value.valid.map((id) => ({
201
+ id,
202
+ state: loadWorkstreamFile(projectRoot, id, "state.json", parseWorkstreamState),
203
+ resume: loadWorkstreamFile(projectRoot, id, "resume-state.json", parseResumeState),
204
+ hostBindings: loadOptionalJson(projectRoot, id, "host-bindings.json"),
205
+ captures: loadWorkstreamCaptures(projectRoot, id),
206
+ }))
207
+ : [];
208
+ return {
209
+ projectRoot,
210
+ project: loadProjectStateResult(paths.projectStatePath),
211
+ focus: loadFocusStateResult(paths.focusStatePath),
212
+ config: loadConfig(paths.configPath),
213
+ pointer: loadPointer(paths.currentWorkstreamPath),
214
+ workstreamDirs,
215
+ workstreams,
216
+ projectCaptures: loadCaptureLog(path.join(projectRoot, ".iq-context", "captures.jsonl")),
217
+ };
218
+ }
219
+ //# sourceMappingURL=invariantSnapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariantSnapshot.js","sourceRoot":"","sources":["../src/invariantSnapshot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ import type { ProjectConfig } from "./config.js";
2
+ import type { CaptureEntry, FocusState, ProjectState, ResumeState, WorkstreamState } from "./types.js";
3
+ export type LoadResult<T> = {
4
+ loaded: true;
5
+ value: T;
6
+ } | {
7
+ loaded: false;
8
+ error: string;
9
+ };
10
+ export interface HostBindings {
11
+ bindings: unknown[];
12
+ }
13
+ export declare const HOST_BINDINGS_SCHEMA_FIELDS: readonly ["bindings"];
14
+ /**
15
+ * `containment` marks a path that escaped `.iq-context`, which is neither a
16
+ * JSON syntax problem nor a schema problem and must not be graded as either.
17
+ */
18
+ export type CaptureLoadFailureKind = "syntax" | "schema" | "containment";
19
+ export type CaptureLoadResult = {
20
+ loaded: true;
21
+ value: CaptureEntry;
22
+ } | {
23
+ loaded: false;
24
+ error: string;
25
+ kind: CaptureLoadFailureKind;
26
+ };
27
+ export interface WorkstreamDirs {
28
+ valid: string[];
29
+ invalid: string[];
30
+ }
31
+ export interface WorkstreamSnapshot {
32
+ id: string;
33
+ state: LoadResult<WorkstreamState>;
34
+ resume: LoadResult<ResumeState>;
35
+ hostBindings: LoadResult<unknown>;
36
+ captures: CaptureLoadResult[];
37
+ }
38
+ export interface InvariantSnapshot {
39
+ projectRoot: string;
40
+ project: LoadResult<ProjectState>;
41
+ focus: LoadResult<FocusState>;
42
+ config: LoadResult<ProjectConfig>;
43
+ pointer: LoadResult<string | null>;
44
+ /** Failed enumeration must never masquerade as "no workstreams exist". */
45
+ workstreamDirs: LoadResult<WorkstreamDirs>;
46
+ /** Always empty when `workstreamDirs` failed to load. */
47
+ workstreams: WorkstreamSnapshot[];
48
+ projectCaptures: CaptureLoadResult[];
49
+ }
50
+ export type InvariantSeverity = "error" | "warning";
51
+ export type InvariantScope = {
52
+ kind: "project" | "focus" | "config";
53
+ } | {
54
+ kind: "workstream";
55
+ workstreamId: string;
56
+ };
57
+ export interface InvariantViolation {
58
+ invariantId: string;
59
+ severity: InvariantSeverity;
60
+ scope: InvariantScope;
61
+ message: string;
62
+ }
63
+ /**
64
+ * Four arms, so illegal states are unrepresentable: a "pass" cannot carry
65
+ * violations and a "skipped" cannot omit its reason.
66
+ */
67
+ export type InvariantOutcome = {
68
+ status: "pass";
69
+ } | {
70
+ status: "skipped";
71
+ reason: string;
72
+ } | {
73
+ status: "partial";
74
+ skippedScopes: string[];
75
+ } | {
76
+ status: "violated";
77
+ violations: InvariantViolation[];
78
+ skippedScopes: string[];
79
+ };
80
+ /** Snapshot slots an invariant can declare a dependency on. */
81
+ export type InvariantRequirement = "project" | "focus" | "config" | "pointer" | "workstreamDirs";
82
+ /**
83
+ * `requires` is declarative so the evaluator can skip an invariant before it
84
+ * runs. Guarding inside each `evaluate` was tried and repeatedly forgotten,
85
+ * which turned unevaluable state into false passes; declaring the dependency
86
+ * makes that class of bug unrepresentable.
87
+ *
88
+ * No `severity` field: severity is carried per violation, so a nominal
89
+ * invariant-level severity could silently disagree with what it reports.
90
+ */
91
+ export interface Invariant {
92
+ id: string;
93
+ description: string;
94
+ requires: InvariantRequirement[];
95
+ evaluate(snapshot: InvariantSnapshot): InvariantOutcome;
96
+ }
97
+ export interface InvariantEvaluation {
98
+ invariant: Invariant;
99
+ outcome: InvariantOutcome;
100
+ }
@@ -0,0 +1,4 @@
1
+ export const HOST_BINDINGS_SCHEMA_FIELDS = [
2
+ "bindings",
3
+ ];
4
+ //# sourceMappingURL=invariantTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariantTypes.js","sourceRoot":"","sources":["../src/invariantTypes.ts"],"names":[],"mappings":"AAiBA,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,UAAU;CACwC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Invariant, InvariantEvaluation, InvariantOutcome, InvariantSnapshot, InvariantViolation } from "./invariantTypes.js";
2
+ export declare function pass(): InvariantOutcome;
3
+ export declare function skipped(reason: string): InvariantOutcome;
4
+ /**
5
+ * Collapses a per-scope evaluation into an outcome.
6
+ *
7
+ * Precedence: a real contradiction outranks incompleteness, so violations win
8
+ * and carry the unevaluated scopes along for reporting. With no violations and
9
+ * at least one unevaluated scope the result is "partial", never "pass".
10
+ */
11
+ export declare function resolveOutcome(violations: InvariantViolation[], skippedScopes?: string[]): InvariantOutcome;
12
+ export declare const INVARIANTS: Invariant[];
13
+ export declare function evaluateInvariants(snapshot: InvariantSnapshot): InvariantEvaluation[];