halfcycle 0.4.5 → 0.4.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"open-phase.d.ts","sourceRoot":"","sources":["../src/open-phase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAY3B;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,qDAAqD;IACrD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;;OAIG;IACH,YAAY,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACvC,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,yEAAyE;QACzE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,eAAe,GAAG,UAAU,CAAC,GAAG,MAAM,CAUtG;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,MAAM,EAAE,MAAM;aAEd,OAAO,EAAE,MAAM,GAAG,IAAI;aACtB,aAAa,EAAE,MAAM,GAAG,IAAI;gBAH5B,MAAM,EAAE,MAAM,EAC9B,OAAO,EAAE,MAAM,EACC,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,aAAa,GAAE,MAAM,GAAG,IAAW;CAKtD;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,kFAItB,CAAC;AAEX,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,IAAI,CAAC,EAAE,MAAM,GACZ,cAAc,CA4BhB;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,IAAI,CAAC,EAAE,MAAM,GACZ,iBAAiB,GAAG,IAAI,CAmB1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,SAAS,CAC7B,UAAU,EAAE,cAAc,EAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,KAAK,EAAE,kBAAkB,EACzB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,CAAC,CA0EtB"}
1
+ {"version":3,"file":"open-phase.d.ts","sourceRoot":"","sources":["../src/open-phase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAY3B;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,qDAAqD;IACrD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;;OAIG;IACH,YAAY,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACvC,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,yEAAyE;QACzE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF;;;;;OAKG;IACH,aAAa,EAAE,YAAY,GAAG,IAAI,CAAC;CACpC;AAcD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,eAAe,GAAG,UAAU,CAAC,GAAG,MAAM,CAUtG;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,MAAM,EAAE,MAAM;aAEd,OAAO,EAAE,MAAM,GAAG,IAAI;aACtB,aAAa,EAAE,MAAM,GAAG,IAAI;gBAH5B,MAAM,EAAE,MAAM,EAC9B,OAAO,EAAE,MAAM,EACC,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,aAAa,GAAE,MAAM,GAAG,IAAW;CAKtD;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,kFAItB,CAAC;AAEX,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,IAAI,CAAC,EAAE,MAAM,GACZ,cAAc,CA4BhB;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,IAAI,CAAC,EAAE,MAAM,GACZ,iBAAiB,GAAG,IAAI,CAmB1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,SAAS,CAC7B,UAAU,EAAE,cAAc,EAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,KAAK,EAAE,kBAAkB,EACzB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,CAAC,CA6EtB"}
package/dist/plan.d.ts ADDED
@@ -0,0 +1,200 @@
1
+ /**
2
+ * The client half of `npx halfcycle@latest plan <phase> [--show]` (System view · E2,
3
+ * feature `phase-plan-and-scope-approval`, AC2): check the project's description and the
4
+ * phase's plan file on the machine, report the description, then report the plan bound
5
+ * to that description version; with `--show`, list every part to the person and record
6
+ * on this machine which phase, number and plan version were shown.
7
+ *
8
+ * THE ORDER IS THE SPEC'S. (1) the description stage, `checkAndReportDescription`
9
+ * (`./system-report.ts`), never a copy of it; (2) the plan is read and checked in that
10
+ * stage's `beforeSend`, which runs after the description passed and before the
11
+ * credential is read or anything sent, so a bad plan sends nothing, the description
12
+ * included; (3) the plan that was checked is the plan sent, never a second read of the
13
+ * file; (4) with `--show`, the list is printed and the shown record written.
14
+ *
15
+ * ONE HOME FOR EVERY RULE THIS VERB APPLIES (INV-003). The plan's shape and its check
16
+ * against a description are `@halfcycle/events`' `checkPhasePlan`; the route's request
17
+ * and answer are its `phasePlanRequestSchema` and `phasePlanResultSchema`. The plan
18
+ * file's name is `renderPhaseSegment`'s (INV-023), and "is the file's phase the phase
19
+ * asked for" compares the two segments it renders, because that function is where a
20
+ * number and its decimal string become one phase. The shown record's format is
21
+ * `@halfcycle/core`'s `serialiseShownPlans` and `parseShownPlans`; this module keeps
22
+ * only the I/O. Whether a typed number reaches this phase is `resolvePhaseReference`
23
+ * (`@halfcycle/core`). The two approval-line swaps take their sentences and the floor
24
+ * from `client-version.ts` (`@halfcycle/events`); the switched-off swap is decided by
25
+ * the server's `typedApprovalOffered`, never by the client's copy of the switch, and
26
+ * delivery's `clientOffersChatApproval` is never called here. "Is this a service
27
+ * without the route" is `isMissingRoute`.
28
+ *
29
+ * THE CHANGE WORDS are the system picture's `PART_STATES` labels for `padd`, `pchg`
30
+ * and `pdel`, repeated in `PLAN_CHANGE_LABELS` because this package may not import the
31
+ * picture package (INV-001). `test/plan-verb.test.ts` reads that package's source and
32
+ * holds the two equal.
33
+ *
34
+ * WHAT LEAVES THE MACHINE. The checked description (through the shared stage), then
35
+ * `{ descriptionVersion, plan }`. Nothing is written into the person's tree; the shown
36
+ * record is machine state under the home folder (INV-019).
37
+ *
38
+ * INV-001: Node builtins, the global `fetch`, `@halfcycle/events` and `@halfcycle/core`
39
+ * only. SHIPS TO CLIENT DISK inside the CLI: every string here is a sentence a person
40
+ * reads, so none names an internal register, route or setting; no comment sits beside
41
+ * an object member.
42
+ */
43
+ import { type PhasePlan, type PhasePlanChange, type PhasePlanProblem, type PlannedScopeState, type SystemDescription } from '@halfcycle/events';
44
+ import { type ShownPlanEntry } from '@halfcycle/core';
45
+ import type { RepoCredential } from './open-phase.js';
46
+ import { type DescriptionVerb, type SystemDeps, type SystemRun } from './system-report.js';
47
+ /** The usage line, printed for no phase, an extra argument or an unknown flag. */
48
+ export declare const PLAN_USAGE = "npx halfcycle@latest plan <phase> [--show] [--repo <root>]";
49
+ /** The folder the plan file sits in, relative to the repository root. */
50
+ export declare const PLAN_DIR_REL = "docs/phases";
51
+ /**
52
+ * The plan file for `phase`, relative to the repository root:
53
+ * `docs/phases/<segment>.plan.json`, the segment being `renderPhaseSegment`'s and no
54
+ * other. Throws on a phase that is not a valid identity, as that function does.
55
+ */
56
+ export declare function planFileRel(phase: string | number): string;
57
+ /**
58
+ * The words for each change, as the system picture labels them (`PART_STATES`' `padd`,
59
+ * `pchg` and `pdel`). A test holds them equal to that package's source.
60
+ */
61
+ export declare const PLAN_CHANGE_LABELS: Readonly<Record<PhasePlanChange, string>>;
62
+ /**
63
+ * What the arguments after `plan` came to. Exactly one positional (the phase), at most
64
+ * one `--show` and at most one `--repo <root>`; anything else, no phase included, is
65
+ * usage.
66
+ */
67
+ export type PlanArgs = {
68
+ ok: true;
69
+ phase: string;
70
+ show: boolean;
71
+ repoRoot: string | undefined;
72
+ } | {
73
+ ok: false;
74
+ };
75
+ export declare function parsePlanArgs(argv: readonly string[]): PlanArgs;
76
+ /** One line per plan problem, each naming where it is. */
77
+ export declare function planProblemLines(raw: unknown, fileRel: string, problems: readonly PhasePlanProblem[]): string;
78
+ /** What reading and checking the plan file came to: the checked plan, or the line to print. */
79
+ export type PlanFileCheck = {
80
+ ok: true;
81
+ plan: PhasePlan;
82
+ } | {
83
+ ok: false;
84
+ line: string;
85
+ };
86
+ /**
87
+ * Read `docs/phases/<segment>.plan.json` under `repoRoot` and check it against the
88
+ * checked `description`. Never throws. `phase` must already be a valid identity.
89
+ *
90
+ * In order: a missing file names the path and the remedy; a file that cannot be read,
91
+ * or is not JSON, says so (the parser's own sentence for the second); a plan naming a
92
+ * valid phase other than `phase` is refused naming both, before its other problems,
93
+ * because it is the wrong file; otherwise every problem `checkPhasePlan` finds. Each
94
+ * line says nothing was sent, because nothing is.
95
+ */
96
+ export declare function readAndCheckPlan(repoRoot: string, phase: string, description: SystemDescription, verb: DescriptionVerb): PlanFileCheck;
97
+ /**
98
+ * What came of reporting the plan. `recorded`: the service's answer, as its schema
99
+ * parsed it. `refused`: the service's own problems. `no-route`: this service cannot
100
+ * take a plan yet. `unavailable`: it could not be reported, and `why` says so.
101
+ */
102
+ export type PlanOutcome = {
103
+ kind: 'recorded';
104
+ version: number;
105
+ phaseNumber: number;
106
+ state: PlannedScopeState;
107
+ changedParts: readonly string[];
108
+ typedApprovalOffered: boolean;
109
+ } | {
110
+ kind: 'refused';
111
+ problems: PhasePlanProblem[];
112
+ } | {
113
+ kind: 'no-route';
114
+ } | {
115
+ kind: 'unavailable';
116
+ why: string;
117
+ };
118
+ /** The address the plan is reported to: the engagement and the phase are each one path segment, encoded once. */
119
+ export declare function planReportUrl(credential: RepoCredential, phase: string | number): string;
120
+ /**
121
+ * Report a checked plan bound to `descriptionVersion`. Never throws.
122
+ *
123
+ * One response-handling rule, the description report's: a body that parses under
124
+ * `phasePlanResultSchema` is authoritative whatever its status; otherwise the
125
+ * service's missing-route answer means it predates plans; anything else is reported
126
+ * by its status and the service's own sentence.
127
+ */
128
+ export declare function reportPhasePlan(credential: RepoCredential, plan: PhasePlan, descriptionVersion: number): Promise<PlanOutcome>;
129
+ /** What writing the shown record came to: on success, every entry the record now holds. */
130
+ export type ShownWrite = {
131
+ ok: true;
132
+ entries: readonly ShownPlanEntry[];
133
+ } | {
134
+ ok: false;
135
+ };
136
+ /**
137
+ * Record on this machine that `entry` was shown: `~/.halfcycle/engagements/<id>/`
138
+ * `shown-plans.json`, the directory composed by `engagementStateDir` (which checks the
139
+ * id) and the filename `SHOWN_PLANS_FILENAME`. Never throws.
140
+ *
141
+ * The record keeps the latest entry per phase, so the entry for this phase is
142
+ * replaced. An entry holding this number under another phase is dropped too: the
143
+ * service gives each phase one number and never changes it, so such an entry is
144
+ * stale, and `serialiseShownPlans` refuses two entries with one number, which would
145
+ * otherwise fail every later write. A missing or malformed record reads as nothing
146
+ * shown (`parseShownPlans`).
147
+ *
148
+ * Written to a temporary file in the same folder, mode `0600` set on it before the
149
+ * rename (a rename carries the source's bits), then renamed into place, so a reader
150
+ * sees the old record or the new one and never a part of one. `chmod` is skipped on
151
+ * Windows, where the bits mean nothing.
152
+ */
153
+ export declare function writeShownPlan(engagementId: string, entry: ShownPlanEntry, home?: string): ShownWrite;
154
+ /**
155
+ * The installed Halfcycle's version, as the project's `.halfcycle/bundle.json` holds
156
+ * it, or `undefined` when there is no pin, it cannot be read, or it holds no string
157
+ * version. The floor treats `undefined` as older than every release.
158
+ */
159
+ export declare function readPinnedClientVersion(repoRoot: string): string | undefined;
160
+ /**
161
+ * The line for a plan the service refused, as `plan` prints it, prefixed by the verb
162
+ * that is running. `draw phase` prints a refusal through this too, so the two verbs
163
+ * word it one way.
164
+ */
165
+ export declare function planRefusedLines(verb: DescriptionVerb, phase: string, plan: PhasePlan, problems: readonly PhasePlanProblem[]): string;
166
+ /**
167
+ * The line for a phase name `isValidPhaseIdentity` refuses, prefixed by the verb that
168
+ * is running. Nothing has been sent when it is printed.
169
+ */
170
+ export declare function notAPhaseNameLine(verb: DescriptionVerb, phase: string): string;
171
+ /** The pieces `runPlan` reaches outside itself through; replaced in tests. */
172
+ export interface PlanDeps extends SystemDeps {
173
+ readonly reportPlan: (credential: RepoCredential, plan: PhasePlan, descriptionVersion: number) => Promise<PlanOutcome>;
174
+ readonly pinnedClientVersion: (repoRoot: string) => string | undefined;
175
+ readonly recordShown: (engagementId: string, entry: ShownPlanEntry) => ShownWrite;
176
+ readonly now: () => Date;
177
+ }
178
+ /** The sentence for the phase's state after the plan was recorded. */
179
+ export declare function planStateSentence(state: PlannedScopeState): string;
180
+ /**
181
+ * The line saying how the owner approves, with its two swaps (AC2). The server's
182
+ * `typedApprovalOffered: false` gives the switched-off sentence, whatever the client;
183
+ * otherwise a pinned version below the floor (none included) gives the update
184
+ * sentence; otherwise the typing offer.
185
+ *
186
+ * The offer names the phase, and its number too when typing the number would reach
187
+ * this phase among the phases shown on this machine (`resolvePhaseReference`): a
188
+ * phase whose name is that number would otherwise take it.
189
+ */
190
+ export declare function approvalLine(phase: string, number: number, typedApprovalOffered: boolean, pinnedVersion: string | undefined, shown: readonly ShownPlanEntry[]): string;
191
+ /** One line per change, in plan order: the part's name, what changes, the plan's line, and the changed mark. */
192
+ export declare function changeLines(plan: PhasePlan, description: SystemDescription, changed: readonly string[]): string;
193
+ /**
194
+ * The whole verb, in the order the module header gives. Exit 0 only when the plan is
195
+ * recorded and, with `--show`, the shown record is written. When that write fails the
196
+ * list is still printed, but not the approval line: an approval typed for a list this
197
+ * machine did not record could not bind to it, so the verb does not invite one.
198
+ */
199
+ export declare function runPlan(repoRoot: string, phase: string, show: boolean, deps: PlanDeps): Promise<SystemRun>;
200
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * The client half of `npx halfcycle@latest system` (System view · E1, feature
3
+ * `system-description`): read the project's `docs/system.json`, check it with the one
4
+ * shared check, warn about owned patterns that match no tracked file, and report it
5
+ * to the Halfcycle service, which answers with the version it holds.
6
+ *
7
+ * ONE HOME FOR EVERY RULE THIS VERB APPLIES (INV-003). The shape, the cross-field
8
+ * rules and the ownership grammar are `@halfcycle/events`' `checkSystemDescription`
9
+ * and `ownerOf` — this module calls them and restates none of them. The unowned-
10
+ * pattern warning asks `ownerOf` of a one-part, one-pattern description per pattern
11
+ * rather than matching paths itself, so "does this pattern own this file" has exactly
12
+ * one implementation, the one E3's counting uses. The answer's shape is the events
13
+ * package's `systemReportResultSchema`. "Is this a service without the route" is
14
+ * `isMissingRoute` (`./missing-route.ts`), shared with `build-record` and `draw`.
15
+ * `draw` and `plan` take this module's first stage, `checkAndReportDescription`,
16
+ * whole: the verbs read, check and report the description by one path, not copies.
17
+ *
18
+ * ONE RESPONSE-HANDLING RULE, the Build Record path's. A body that parses under
19
+ * `systemReportResultSchema` is authoritative, whatever its status. Otherwise the
20
+ * service's missing-route answer to a POST means it predates the route; anything
21
+ * else is reported by its status and the service's own sentence.
22
+ *
23
+ * WHAT LEAVES THE MACHINE. Only the description, and only after it passed the check.
24
+ * The tracked-file list read for the warning is a local read: nothing it finds is
25
+ * sent, and it sets nothing (INV-021). Nothing here writes into the person's tree.
26
+ *
27
+ * INV-001: Node builtins, the global `fetch`, `@halfcycle/events` and
28
+ * `@halfcycle/core` (through the shared credential and command helpers) only.
29
+ * SHIPS TO CLIENT DISK inside the CLI: every string here is a sentence a person
30
+ * reads, so none names an internal register, route or setting; no comment sits
31
+ * beside an object member.
32
+ */
33
+ import { type SystemDescription, type SystemDescriptionProblem } from '@halfcycle/events';
34
+ import type { RepoCredential } from './open-phase.js';
35
+ /** Where the description lives, relative to the repository root. */
36
+ export declare const SYSTEM_DESCRIPTION_REL = "docs/system.json";
37
+ /**
38
+ * What reading `docs/system.json` came to. `missing`: there is no such file.
39
+ * `unreadable`: something is there and could not be read as a file. `not-json`: it
40
+ * was read and does not parse; `error` is the parser's own sentence. `read`: the
41
+ * parsed value, not yet checked.
42
+ */
43
+ export type DescriptionFile = {
44
+ kind: 'missing';
45
+ } | {
46
+ kind: 'unreadable';
47
+ } | {
48
+ kind: 'not-json';
49
+ error: string;
50
+ } | {
51
+ kind: 'read';
52
+ value: unknown;
53
+ };
54
+ /**
55
+ * Read and parse the description under `repoRoot`. Never throws. A missing file is
56
+ * told apart from an unreadable one because the remedies differ: the first is
57
+ * written by the method's architecture step, the second is the person's to fix.
58
+ */
59
+ export declare function readDescriptionFile(repoRoot: string): DescriptionFile;
60
+ /**
61
+ * A problem's place in words a person can find in their file: the dotted path the
62
+ * check reports (`parts.3.name`), then the part or connection it is in, named by its
63
+ * id or its ends when the raw value carries them, by its position otherwise.
64
+ */
65
+ export declare function problemPlace(raw: unknown, path: string): string;
66
+ /** One line per problem, each naming where it is, for the terminal. */
67
+ export declare function problemLines(raw: unknown, problems: readonly SystemDescriptionProblem[]): string;
68
+ /** An owned pattern that matches no file git tracks, and the part that lists it. */
69
+ export interface UnownedPattern {
70
+ readonly partId: string;
71
+ readonly pattern: string;
72
+ }
73
+ /** How the tracked-file list is obtained; replaced in tests. `null` when there is none. */
74
+ export type ListTrackedFiles = (repoRoot: string) => string[] | null;
75
+ /**
76
+ * The files git tracks under `repoRoot`, as `git ls-files -z` prints them (relative,
77
+ * `/`-separated, unquoted), or `null` when there is no git, `repoRoot` is not inside
78
+ * a repository, or the list could not be read. The repository-locating variables are
79
+ * removed from git's environment so the folder named is the one read.
80
+ */
81
+ export declare const listTrackedFiles: ListTrackedFiles;
82
+ /**
83
+ * The owned patterns that match no tracked file — how a description drifts from the
84
+ * code. A warning, never a refusal. `null` when the tracked-file list is unavailable:
85
+ * the warning is then skipped, not guessed.
86
+ *
87
+ * Each pattern is asked of `ownerOf` on its own (a description holding just that part
88
+ * with just that pattern), so a pattern another part outranks on every file still
89
+ * counts as matching: the warning is about patterns that name nothing, not about
90
+ * ranking.
91
+ */
92
+ export declare function unownedPatterns(description: SystemDescription, tracked: readonly string[] | null): UnownedPattern[] | null;
93
+ /**
94
+ * The warning lines for unowned patterns, one per pattern, each prefixed by the verb
95
+ * that is running (`draw`, `plan` or `system`), as every other line it prints is.
96
+ */
97
+ export declare function unownedPatternLines(unowned: readonly UnownedPattern[], verb: DescriptionVerb): string;
98
+ /**
99
+ * What came of reporting the description. `recorded`: the service holds it as
100
+ * `version` (a description it already had returns that version). `refused`: the
101
+ * service's own problems. `no-route`: this service cannot take a description yet.
102
+ * `unavailable`: it could not be reported, and `why` says so.
103
+ */
104
+ export type ReportOutcome = {
105
+ kind: 'recorded';
106
+ version: number;
107
+ reportedAt: string;
108
+ } | {
109
+ kind: 'refused';
110
+ problems: SystemDescriptionProblem[];
111
+ } | {
112
+ kind: 'no-route';
113
+ } | {
114
+ kind: 'unavailable';
115
+ why: string;
116
+ };
117
+ /** The address the description is reported to: the engagement is one path segment, encoded once. */
118
+ export declare function systemReportUrl(credential: RepoCredential): string;
119
+ /**
120
+ * Report a checked description. Never throws: the network, the service and an answer
121
+ * this CLI cannot read are each an outcome the caller prints.
122
+ */
123
+ export declare function reportSystemDescription(credential: RepoCredential, description: SystemDescription): Promise<ReportOutcome>;
124
+ /** What `system` prints and the exit code it ends with. */
125
+ export interface SystemRun {
126
+ readonly stdout: string;
127
+ readonly stderr: string;
128
+ readonly exitCode: 0 | 1;
129
+ }
130
+ /** The pieces `runSystem` reaches outside itself through; replaced in tests. */
131
+ export interface SystemDeps {
132
+ readonly readCredential: (repoRoot: string) => RepoCredential;
133
+ readonly listTracked: ListTrackedFiles;
134
+ readonly report: (credential: RepoCredential, description: SystemDescription) => Promise<ReportOutcome>;
135
+ }
136
+ /**
137
+ * What the shared first stage came to. `stopped`: the verb ends here and `stderr` is
138
+ * the whole of what it prints. `no-route`: the service cannot take a description yet;
139
+ * the caller words that for its own verb. `recorded`: the service holds the checked
140
+ * description as `version`, and `credential` is the one it was reported with.
141
+ * `warnings` is the unowned-pattern lines, printed whatever the caller does next.
142
+ */
143
+ export type DescriptionStage = {
144
+ kind: 'stopped';
145
+ stderr: string;
146
+ } | {
147
+ kind: 'no-route';
148
+ warnings: string;
149
+ } | {
150
+ kind: 'recorded';
151
+ version: number;
152
+ credential: RepoCredential;
153
+ warnings: string;
154
+ };
155
+ /** The verbs that report the description first. Names the prefix of every line. */
156
+ export type DescriptionVerb = 'system' | 'draw' | 'plan';
157
+ /**
158
+ * A verb's own local refusal, asked with the description that just passed the check:
159
+ * the line to print, or `null` to go on. `plan` checks its plan file against that
160
+ * description here; `draw` ignores the argument.
161
+ */
162
+ export type BeforeSend = (description: SystemDescription) => string | null;
163
+ /**
164
+ * READ, CHECK, WARN, REPORT — the one path `system`, `draw` and `plan` take before
165
+ * anything else (feature `diagram-files-for-docs`, step 1 and 2 of the command). The
166
+ * order is the spec's: nothing is sent unless the file reads, parses and passes every
167
+ * rule, and the credential is only looked for once there is something to send.
168
+ *
169
+ * `beforeSend` is the caller's own local refusal, asked after the description passed
170
+ * and before the credential is read or anything sent: `draw` refuses there when a file
171
+ * it does not own is in the way, and `plan` when its plan file is missing, unreadable or
172
+ * fails `checkPhasePlan` against the checked description it is handed, so a refusal it
173
+ * can make on the machine never costs a request (the description included). It returns
174
+ * the line to print, or `null` to go on.
175
+ */
176
+ export declare function checkAndReportDescription(repoRoot: string, deps: SystemDeps, verb: DescriptionVerb, beforeSend?: BeforeSend): Promise<DescriptionStage>;
177
+ /** The whole `system` verb: the shared stage, then its version printed. */
178
+ export declare function runSystem(repoRoot: string, deps: SystemDeps): Promise<SystemRun>;
179
+ //# sourceMappingURL=system-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-report.d.ts","sourceRoot":"","sources":["../src/system-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC9B,MAAM,mBAAmB,CAAC;AAI3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,oEAAoE;AACpE,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAErC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAarE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAoB/D;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,wBAAwB,EAAE,GAAG,MAAM,CAEhG;AAED,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,2FAA2F;AAC3F,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,EAAE,GAAG,IAAI,CAAC;AAErE;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,gBAY9B,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,iBAAiB,EAC9B,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAChC,cAAc,EAAE,GAAG,IAAI,CAczB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAQrG;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,wBAAwB,EAAE,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,oGAAoG;AACpG,wBAAgB,eAAe,CAAC,UAAU,EAAE,cAAc,GAAG,MAAM,CAElE;AAWD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,cAAc,EAC1B,WAAW,EAAE,iBAAiB,GAC7B,OAAO,CAAC,aAAa,CAAC,CAoDxB;AAED,2DAA2D;AAC3D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;CAC1B;AAED,gFAAgF;AAChF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,CAAC;IAC9D,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CACzG;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,cAAc,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAExF,mFAAmF;AACnF,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,WAAW,EAAE,iBAAiB,KAAK,MAAM,GAAG,IAAI,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,eAAe,EACrB,UAAU,GAAE,UAAuB,GAClC,OAAO,CAAC,gBAAgB,CAAC,CAyE3B;AAED,2EAA2E;AAC3E,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAmBtF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "halfcycle",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "Your coding agents are the engineers. Halfcycle is the rest of the team: product owner, architect, planner and independent reviewer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",