carrick 0.3.53

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 (114) hide show
  1. package/LICENSE.md +99 -0
  2. package/README.md +79 -0
  3. package/bin/carrick.mjs +168 -0
  4. package/dist/channel.d.ts +16 -0
  5. package/dist/channel.js +37 -0
  6. package/dist/channel.js.map +1 -0
  7. package/dist/cli.d.ts +29 -0
  8. package/dist/cli.js +60 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/contract.d.ts +154 -0
  11. package/dist/contract.js +141 -0
  12. package/dist/contract.js.map +1 -0
  13. package/dist/diagnostics.d.ts +54 -0
  14. package/dist/diagnostics.js +199 -0
  15. package/dist/diagnostics.js.map +1 -0
  16. package/dist/hook/post-edit.d.ts +13 -0
  17. package/dist/hook/post-edit.js +76 -0
  18. package/dist/hook/post-edit.js.map +1 -0
  19. package/dist/hook/session-start.d.ts +2 -0
  20. package/dist/hook/session-start.js +44 -0
  21. package/dist/hook/session-start.js.map +1 -0
  22. package/dist/init/identity.d.ts +20 -0
  23. package/dist/init/identity.js +60 -0
  24. package/dist/init/identity.js.map +1 -0
  25. package/dist/init/repos.d.ts +19 -0
  26. package/dist/init/repos.js +46 -0
  27. package/dist/init/repos.js.map +1 -0
  28. package/dist/init/run.d.ts +11 -0
  29. package/dist/init/run.js +228 -0
  30. package/dist/init/run.js.map +1 -0
  31. package/dist/init/settings.d.ts +50 -0
  32. package/dist/init/settings.js +138 -0
  33. package/dist/init/settings.js.map +1 -0
  34. package/dist/log.d.ts +2 -0
  35. package/dist/log.js +35 -0
  36. package/dist/log.js.map +1 -0
  37. package/dist/native.d.ts +48 -0
  38. package/dist/native.js +127 -0
  39. package/dist/native.js.map +1 -0
  40. package/dist/render.d.ts +65 -0
  41. package/dist/render.js +334 -0
  42. package/dist/render.js.map +1 -0
  43. package/dist/root.d.ts +30 -0
  44. package/dist/root.js +82 -0
  45. package/dist/root.js.map +1 -0
  46. package/dist/server.d.ts +2 -0
  47. package/dist/server.js +255 -0
  48. package/dist/server.js.map +1 -0
  49. package/dist/templates.d.ts +18 -0
  50. package/dist/templates.js +66 -0
  51. package/dist/templates.js.map +1 -0
  52. package/package.json +72 -0
  53. package/plugin/.claude-plugin/plugin.json +6 -0
  54. package/plugin/.lsp.json +14 -0
  55. package/plugin/hooks/hooks.json +27 -0
  56. package/sidecar/dist/src/bundler.d.ts +141 -0
  57. package/sidecar/dist/src/bundler.js +680 -0
  58. package/sidecar/dist/src/capture/anchors.d.ts +61 -0
  59. package/sidecar/dist/src/capture/anchors.js +1132 -0
  60. package/sidecar/dist/src/capture/api.d.ts +378 -0
  61. package/sidecar/dist/src/capture/api.js +10 -0
  62. package/sidecar/dist/src/capture/augmentations.d.ts +20 -0
  63. package/sidecar/dist/src/capture/augmentations.js +60 -0
  64. package/sidecar/dist/src/capture/check-classify.d.ts +58 -0
  65. package/sidecar/dist/src/capture/check-classify.js +189 -0
  66. package/sidecar/dist/src/capture/check-deep.d.ts +32 -0
  67. package/sidecar/dist/src/capture/check-deep.js +91 -0
  68. package/sidecar/dist/src/capture/check-poison.d.ts +40 -0
  69. package/sidecar/dist/src/capture/check-poison.js +155 -0
  70. package/sidecar/dist/src/capture/check-probe.d.ts +70 -0
  71. package/sidecar/dist/src/capture/check-probe.js +147 -0
  72. package/sidecar/dist/src/capture/check-scrub.d.ts +31 -0
  73. package/sidecar/dist/src/capture/check-scrub.js +71 -0
  74. package/sidecar/dist/src/capture/check-workspace.d.ts +52 -0
  75. package/sidecar/dist/src/capture/check-workspace.js +218 -0
  76. package/sidecar/dist/src/capture/check.d.ts +39 -0
  77. package/sidecar/dist/src/capture/check.js +450 -0
  78. package/sidecar/dist/src/capture/deep-walk.d.ts +62 -0
  79. package/sidecar/dist/src/capture/deep-walk.js +243 -0
  80. package/sidecar/dist/src/capture/index.d.ts +36 -0
  81. package/sidecar/dist/src/capture/index.js +477 -0
  82. package/sidecar/dist/src/capture/lockfile.d.ts +48 -0
  83. package/sidecar/dist/src/capture/lockfile.js +490 -0
  84. package/sidecar/dist/src/capture/machinery.d.ts +59 -0
  85. package/sidecar/dist/src/capture/machinery.js +160 -0
  86. package/sidecar/dist/src/capture/node-builder.d.ts +37 -0
  87. package/sidecar/dist/src/capture/node-builder.js +123 -0
  88. package/sidecar/dist/src/capture/paths-rewrite.d.ts +34 -0
  89. package/sidecar/dist/src/capture/paths-rewrite.js +100 -0
  90. package/sidecar/dist/src/capture/self-check.d.ts +38 -0
  91. package/sidecar/dist/src/capture/self-check.js +317 -0
  92. package/sidecar/dist/src/capture/specifiers.d.ts +39 -0
  93. package/sidecar/dist/src/capture/specifiers.js +56 -0
  94. package/sidecar/dist/src/definition-resolver.d.ts +57 -0
  95. package/sidecar/dist/src/definition-resolver.js +153 -0
  96. package/sidecar/dist/src/index.d.ts +14 -0
  97. package/sidecar/dist/src/index.js +564 -0
  98. package/sidecar/dist/src/monorepo-builder.d.ts +129 -0
  99. package/sidecar/dist/src/monorepo-builder.js +584 -0
  100. package/sidecar/dist/src/project-loader.d.ts +130 -0
  101. package/sidecar/dist/src/project-loader.js +399 -0
  102. package/sidecar/dist/src/type-inferrer.d.ts +940 -0
  103. package/sidecar/dist/src/type-inferrer.js +3540 -0
  104. package/sidecar/dist/src/type-structural-expander.d.ts +61 -0
  105. package/sidecar/dist/src/type-structural-expander.js +283 -0
  106. package/sidecar/dist/src/type-text-canonicalizer.d.ts +40 -0
  107. package/sidecar/dist/src/type-text-canonicalizer.js +297 -0
  108. package/sidecar/dist/src/types.d.ts +669 -0
  109. package/sidecar/dist/src/types.js +5 -0
  110. package/sidecar/dist/src/validators.d.ts +2214 -0
  111. package/sidecar/dist/src/validators.js +336 -0
  112. package/sidecar/package.json +6 -0
  113. package/templates/carrick.json +7 -0
  114. package/templates/carrick.yml +45 -0
package/LICENSE.md ADDED
@@ -0,0 +1,99 @@
1
+ Elastic License 2.0
2
+
3
+ URL: https://www.elastic.co/licensing/elastic-license
4
+
5
+ Copyright (c) 2026 Far Harbour B.V.
6
+
7
+ Carrick is licensed under the Elastic License 2.0.
8
+
9
+ ## Acceptance
10
+
11
+ By using the software, you agree to all of the terms and conditions below.
12
+
13
+ ## Copyright License
14
+
15
+ The licensor grants you a non-exclusive, royalty-free, worldwide,
16
+ non-sublicensable, non-transferable license to use, copy, distribute, make
17
+ available, and prepare derivative works of the software, in each case
18
+ subject to the limitations and conditions below.
19
+
20
+ ## Limitations
21
+
22
+ You may not provide the software to third parties as a hosted or managed
23
+ service, where the service provides users with access to any substantial
24
+ set of the features or functionality of the software.
25
+
26
+ You may not move, change, disable, or circumvent the license key
27
+ functionality in the software, and you may not remove or obscure any
28
+ functionality in the software that is protected by the license key.
29
+
30
+ You may not alter, remove, or obscure any licensing, copyright, or other
31
+ notices of the licensor in the software. Any use of the licensor's
32
+ trademarks is subject to applicable law.
33
+
34
+ ## Patents
35
+
36
+ The licensor grants you a license, under any patent claims the licensor
37
+ can license, or becomes able to license, to make, have made, use, sell,
38
+ offer for sale, import and have imported the software, in each case
39
+ subject to the limitations and conditions in this license. This license
40
+ does not cover any patent claims that you cause to be infringed by
41
+ modifications or additions to the software. If you or your company make
42
+ any written claim that the software infringes or contributes to
43
+ infringement of any patent, your patent license for the software granted
44
+ under these terms ends immediately. If your company makes such a claim,
45
+ your patent license ends immediately for work on behalf of your company.
46
+
47
+ ## Notices
48
+
49
+ You must ensure that anyone who gets a copy of any part of the software
50
+ from you also gets a copy of these terms.
51
+
52
+ If you modify the software, you must include in any modified copies of
53
+ the software prominent notices stating that you have modified the
54
+ software.
55
+
56
+ ## No Other Rights
57
+
58
+ These terms do not imply any licenses other than those expressly granted
59
+ in these terms.
60
+
61
+ ## Termination
62
+
63
+ If you use the software in violation of these terms, such use is not
64
+ licensed, and your licenses will automatically terminate. If the licensor
65
+ provides you with a notice of your violation, and you cease all violation
66
+ of this license no later than 30 days after you receive that notice, your
67
+ licenses will be reinstated retroactively. However, if you violate these
68
+ terms after such reinstatement, any additional violation of these terms
69
+ will cause your licenses to terminate automatically and permanently.
70
+
71
+ ## No Liability
72
+
73
+ As far as the law allows, the software comes as is, without any warranty
74
+ or condition, and the licensor will not be liable to you for any damages
75
+ arising out of these terms or the use or nature of the software, under
76
+ any kind of legal claim.
77
+
78
+ ## Definitions
79
+
80
+ The licensor is the entity offering these terms, and the software is the
81
+ software the licensor makes available under these terms, including any
82
+ portion of it.
83
+
84
+ you refers to the individual or entity agreeing to these terms.
85
+
86
+ your company is any legal entity, sole proprietorship, or other kind of
87
+ organization that you work for, plus all organizations that have control
88
+ over, are under the control of, or are under common control with that
89
+ organization. control means ownership of substantially all the assets of
90
+ an entity, or the power to direct its management and policies by vote,
91
+ contract, or otherwise. Control can be direct or indirect.
92
+
93
+ your licenses are all the licenses granted to you for the software under
94
+ these terms.
95
+
96
+ use means anything you do with the software requiring one of your
97
+ licenses.
98
+
99
+ trademark means trademarks, service marks, and similar rights.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # carrick
2
+
3
+ Carrick indexes the TypeScript repos sitting beside each other on your disk and
4
+ answers, about the file you are editing right now: which routes and calls are in
5
+ it, who is on the other side of them in the other repos, and whether the
6
+ contract between them still holds. It answers in your editor's Problems panel
7
+ and in your coding agent's context, without either of them asking.
8
+
9
+ ```
10
+ npm install -g carrick
11
+ cd ~/code # the folder that holds your repos
12
+ carrick init
13
+ ```
14
+
15
+ `carrick init` needs a GitHub identity — `gh auth login`, or a `GITHUB_TOKEN` in
16
+ the environment — and tells you so before it writes anything.
17
+
18
+ ## What it installs
19
+
20
+ One package. The scanner is a Rust binary that arrives as
21
+ `@carrick-tools/cli-<platform>-<arch>`, an optional dependency npm resolves by
22
+ `os` and `cpu`; the type sidecar, the language server and the hook scripts come
23
+ with this package. There is no postinstall step and nothing is downloaded after
24
+ the install, so `--ignore-scripts` works.
25
+
26
+ Node 24 or newer, because the sidecar that resolves your request and response
27
+ types runs on it.
28
+
29
+ ## The commands
30
+
31
+ | Command | What it does |
32
+ |---|---|
33
+ | `carrick init` | The repo list, the first index, the agent hooks, and the lines it cannot run for you |
34
+ | `carrick index` | Scan every repo in `carrick-workspace.json` and write `.carrick/` |
35
+ | `carrick refresh [--service X]` | Re-scan one repo, or all of them, and re-join |
36
+ | `carrick check <file>` | What the index knows about that file, verdicts included |
37
+ | `carrick touch <file>` | The same, without the verdicts |
38
+ | `carrick status` | What the index holds, and how far each repo has moved since |
39
+ | `carrick lsp --stdio` | The language server, for an editor or any LSP client |
40
+ | `carrick hook post-edit` | Claude Code PostToolUse hook, reading the tool payload on stdin |
41
+ | `carrick hook session-start` | Claude Code SessionStart hook |
42
+ | `carrick templates workflow` | Print the CI workflow to add to a repo |
43
+ | `carrick <path>` | The full scan, which is what the GitHub Action runs |
44
+
45
+ `check`, `touch` and `status` read `.carrick/` and write nothing. Add `--json`
46
+ for the machine-readable shape, pinned in
47
+ [`docs/local-mode-output.md`](https://github.com/carrick-tools/carrick/blob/main/docs/local-mode-output.md).
48
+
49
+ ## Where the answers land
50
+
51
+ - **Claude Code**: the hooks `carrick init` writes deliver on the edit itself.
52
+ `claude --plugin-dir <carrick checkout>/plugin` adds the language server too.
53
+ - **VS Code, Cursor, Windsurf**: the `carrick-tools.carrick` extension is a
54
+ client on `carrick lsp --stdio`. Editor-hosted agents read the Problems panel
55
+ after their own edits.
56
+ - **Neovim, Helix, Zed, JetBrains**: any LSP client, on the same command.
57
+ - **Anything else**: `carrick check <file>` on demand, and the pull request
58
+ check in CI.
59
+
60
+ ## What a local index holds
61
+
62
+ Deterministic rows: file-based and descriptor routes, class-controller routes,
63
+ imported-member calls, GraphQL schema and document rows, socket and pub/sub
64
+ operations, and the types on both sides of them. A route registered on a typed
65
+ receiver, and a call whose URL is assembled at the call site, are classified in
66
+ the hosted index and are absent here — every answer says how many of those it
67
+ counted and did not classify, so a thin answer never reads as an empty one.
68
+
69
+ The hosted index covers every repo in your organisation on its main branch,
70
+ including the ones you do not have checked out, and answers over MCP:
71
+
72
+ ```
73
+ claude mcp add --scope user --transport http carrick https://api.carrick.tools/mcp
74
+ ```
75
+
76
+ ## Licence
77
+
78
+ Elastic License 2.0. See
79
+ [LICENSE.md](https://github.com/carrick-tools/carrick/blob/main/LICENSE.md).
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ // One command for every host.
3
+ //
4
+ // `carrick index|touch|check|refresh|status|<path>` is the Rust scanner, run
5
+ // from the platform package npm installed. `carrick lsp`, `carrick hook` and
6
+ // `carrick init` are this package's own TypeScript, run by Node's type
7
+ // stripping — which is why the floor is Node 24 and why the check for it is
8
+ // the first thing in this file, before any `.ts` is imported. On an older Node
9
+ // the import itself is a syntax error, and a syntax error is not an answer to
10
+ // "which Node do I need".
11
+ //
12
+ // What it imports is `dist/`, not `src/`: Node refuses to strip types for any
13
+ // file under node_modules, so the published package carries the emit and the
14
+ // checkout builds it (`npm run build`, and `prepack` before a publish).
15
+ //
16
+ // Nothing here re-enters `carrick` on PATH: the binary is resolved to an
17
+ // absolute path (src/native.ts). A shim that shelled out to its own name would
18
+ // loop forever the moment the platform package went missing.
19
+
20
+ import { spawn } from "node:child_process";
21
+
22
+ const NODE_FLOOR = 24;
23
+
24
+ function nodeMajor() {
25
+ const [major] = process.versions.node.split(".");
26
+ return Number.parseInt(major ?? "0", 10);
27
+ }
28
+
29
+ if (nodeMajor() < NODE_FLOOR) {
30
+ process.stderr.write(
31
+ `carrick needs Node ${NODE_FLOOR} or newer; this is Node ${process.versions.node}.\n` +
32
+ `The type sidecar that resolves your request and response types runs on it, and this CLI is TypeScript run directly.\n` +
33
+ `Install Node ${NODE_FLOOR}+ (https://nodejs.org) and run carrick again.\n`,
34
+ );
35
+ process.exit(1);
36
+ }
37
+
38
+ const HOOKS = {
39
+ "post-edit": "../dist/hook/post-edit.js",
40
+ "session-start": "../dist/hook/session-start.js",
41
+ };
42
+
43
+ const argv = process.argv.slice(2);
44
+ const [command, ...rest] = argv;
45
+
46
+ /** Run the scanner binary, streaming its output and answering with its code. */
47
+ async function runNative(args) {
48
+ const { resolveNativeBinary, nativeEnv } = await import("../dist/native.js");
49
+ const lookup = resolveNativeBinary();
50
+ if (!lookup.binary) {
51
+ process.stderr.write(`carrick: ${lookup.problem}\n`);
52
+ process.exit(1);
53
+ }
54
+ const child = spawn(lookup.binary, args, {
55
+ stdio: "inherit",
56
+ env: nativeEnv(),
57
+ });
58
+ child.on("error", (error) => {
59
+ process.stderr.write(`carrick: could not run ${lookup.binary}: ${error.message}\n`);
60
+ process.exit(1);
61
+ });
62
+ child.on("exit", (code, signal) => {
63
+ // A signalled child is reported the way a shell reports it, so a Ctrl-C
64
+ // through this shim looks like a Ctrl-C of the scanner.
65
+ if (signal) {
66
+ process.kill(process.pid, signal);
67
+ return;
68
+ }
69
+ process.exit(code ?? 1);
70
+ });
71
+ }
72
+
73
+ /**
74
+ * Point this package's own commands at the binary directly, rather than at
75
+ * whatever `carrick` PATH holds. The hook and the server run on every edit;
76
+ * one process, not two, and no dependence on the install being global.
77
+ */
78
+ async function pointAtNativeBinary() {
79
+ const { resolveNativeBinary, resolveSidecarDir } = await import("../dist/native.js");
80
+ if (!process.env["CARRICK_BIN"]) {
81
+ const lookup = resolveNativeBinary();
82
+ if (lookup.binary) process.env["CARRICK_BIN"] = lookup.binary;
83
+ }
84
+ if (!process.env["CARRICK_SIDECAR_DIR"]) {
85
+ const sidecar = resolveSidecarDir();
86
+ if (sidecar) process.env["CARRICK_SIDECAR_DIR"] = sidecar;
87
+ }
88
+ }
89
+
90
+ async function printVersion() {
91
+ const { readFile } = await import("node:fs/promises");
92
+ const manifest = new URL("../package.json", import.meta.url);
93
+ const { version } = JSON.parse(await readFile(manifest, "utf8"));
94
+ process.stdout.write(`${version}\n`);
95
+ }
96
+
97
+ function extraHelp() {
98
+ return [
99
+ "",
100
+ "This package also carries the commands that put those answers where you work:",
101
+ "",
102
+ " carrick init set up this folder: the repos to index, the first",
103
+ " index, and the editor and agent wiring",
104
+ " carrick lsp --stdio the language server, for an editor or an agent",
105
+ " that speaks LSP",
106
+ " carrick hook post-edit Claude Code PostToolUse hook (reads the tool",
107
+ " payload on stdin)",
108
+ " carrick hook session-start Claude Code SessionStart hook",
109
+ " carrick templates workflow print the CI workflow to add to a repo",
110
+ "",
111
+ ].join("\n");
112
+ }
113
+
114
+ switch (command) {
115
+ case "lsp": {
116
+ await pointAtNativeBinary();
117
+ await import("../dist/server.js");
118
+ break;
119
+ }
120
+ case "hook": {
121
+ const [name] = rest;
122
+ const entry = name ? HOOKS[name] : undefined;
123
+ if (!entry) {
124
+ process.stderr.write(
125
+ `carrick hook needs one of: ${Object.keys(HOOKS).join(", ")}\n`,
126
+ );
127
+ process.exit(2);
128
+ }
129
+ await pointAtNativeBinary();
130
+ await import(entry);
131
+ break;
132
+ }
133
+ case "init": {
134
+ const { init } = await import("../dist/init/run.js");
135
+ process.exit(await init(rest));
136
+ }
137
+ case "templates": {
138
+ const { templates } = await import("../dist/init/run.js");
139
+ process.exit(templates(rest));
140
+ }
141
+ case "--version":
142
+ case "-V": {
143
+ await printVersion();
144
+ break;
145
+ }
146
+ case "--help":
147
+ case "-h":
148
+ case undefined: {
149
+ // The scanner owns its own help, including the environment variables, so
150
+ // print that and add what this package puts on top of it.
151
+ const { resolveNativeBinary, nativeEnv } = await import("../dist/native.js");
152
+ const lookup = resolveNativeBinary();
153
+ if (lookup.binary) {
154
+ await new Promise((resolve) => {
155
+ spawn(lookup.binary, ["--help"], { stdio: "inherit", env: nativeEnv() }).on(
156
+ "exit",
157
+ resolve,
158
+ );
159
+ });
160
+ } else {
161
+ process.stderr.write(`carrick: ${lookup.problem}\n`);
162
+ }
163
+ process.stderr.write(extraHelp());
164
+ process.exit(0);
165
+ }
166
+ default:
167
+ await runNative(argv);
168
+ }
@@ -0,0 +1,16 @@
1
+ export type Channel = "hook" | "lsp" | "off";
2
+ export type ChannelInput = {
3
+ env?: NodeJS.ProcessEnv;
4
+ /** True when a PostToolUse hook is registered alongside this process. */
5
+ hooksInstalled: boolean;
6
+ };
7
+ export type ChannelChoice = {
8
+ channel: Channel;
9
+ /** How it was decided, for the log line. */
10
+ source: "env" | "default";
11
+ /** Set when CARRICK_CHANNEL held a value we do not know. */
12
+ ignoredValue: string | null;
13
+ };
14
+ export declare function resolveChannel(input: ChannelInput): ChannelChoice;
15
+ /** Whether this process is the one that delivers. */
16
+ export declare function ownsDelivery(mine: Exclude<Channel, "off">, choice: ChannelChoice): boolean;
@@ -0,0 +1,37 @@
1
+ // One channel per install.
2
+ //
3
+ // The hook and the language server carry the same verdicts. Delivering both in
4
+ // one session means the model reads every finding twice and a measurement of
5
+ // either channel measures the pair, so exactly one of them speaks.
6
+ //
7
+ // The decision is config, not sniffing. The Claude Code plugin registers the
8
+ // hook and the server together, so its `.lsp.json` passes `--hooks-installed`
9
+ // to the server: the manifest that knows the hook exists is the one that says
10
+ // so. An editor or a generic LSP client starts the server without that flag and
11
+ // has no hook at all, so the server publishes. `CARRICK_CHANNEL` overrides both
12
+ // and is how a smoke run pins one arm.
13
+ const KNOWN = ["hook", "lsp", "off"];
14
+ export function resolveChannel(input) {
15
+ const raw = (input.env ?? process.env)["CARRICK_CHANNEL"];
16
+ if (raw) {
17
+ const value = raw.trim().toLowerCase();
18
+ if (KNOWN.includes(value)) {
19
+ return { channel: value, source: "env", ignoredValue: null };
20
+ }
21
+ return {
22
+ channel: input.hooksInstalled ? "hook" : "lsp",
23
+ source: "default",
24
+ ignoredValue: raw,
25
+ };
26
+ }
27
+ return {
28
+ channel: input.hooksInstalled ? "hook" : "lsp",
29
+ source: "default",
30
+ ignoredValue: null,
31
+ };
32
+ }
33
+ /** Whether this process is the one that delivers. */
34
+ export function ownsDelivery(mine, choice) {
35
+ return choice.channel === mine;
36
+ }
37
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,mEAAmE;AACnE,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,8EAA8E;AAC9E,gFAAgF;AAChF,gFAAgF;AAChF,uCAAuC;AAkBvC,MAAM,KAAK,GAAc,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAEhD,MAAM,UAAU,cAAc,CAAC,KAAmB;IAChD,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC1D,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACvC,IAAK,KAAkB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,OAAO,EAAE,OAAO,EAAE,KAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QAC1E,CAAC;QACD,OAAO;YACL,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;YAC9C,MAAM,EAAE,SAAS;YACjB,YAAY,EAAE,GAAG;SAClB,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;QAC9C,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,IAAI;KACnB,CAAC;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,YAAY,CAAC,IAA6B,EAAE,MAAqB;IAC/E,OAAO,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;AACjC,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { type CheckResult, type StatusResult } from "./contract.ts";
2
+ /** Binary to run. `carrick` on PATH unless CARRICK_BIN names another. */
3
+ export declare function binary(env?: NodeJS.ProcessEnv): string;
4
+ /** Time limit for one CLI call. The budget for the hook is 300 ms of our own. */
5
+ export declare function timeoutMs(env?: NodeJS.ProcessEnv): number;
6
+ export type RunOutcome<T = CheckResult> = {
7
+ result: T | null;
8
+ /** Why there is no result, for the log. Null when there is one. */
9
+ failure: string | null;
10
+ ms: number;
11
+ };
12
+ export type RunOptions = {
13
+ cwd: string;
14
+ env?: NodeJS.ProcessEnv;
15
+ /** Injectable for tests; defaults to the real `carrick` binary. */
16
+ bin?: string;
17
+ };
18
+ /** `carrick check <file> --json`, run from the workspace root. */
19
+ export declare function check(file: string, options: RunOptions): Promise<RunOutcome<CheckResult>>;
20
+ /**
21
+ * `carrick status --json`: the workspace, with no file in the question.
22
+ *
23
+ * `check` and `touch` each take exactly one file, so this is the command a
24
+ * surface opening a session asks. `--workspace` is passed only when the caller
25
+ * names one; without it the CLI finds `.carrick/` from the working directory.
26
+ */
27
+ export declare function status(options: RunOptions & {
28
+ workspace?: string | null;
29
+ }): Promise<RunOutcome<StatusResult>>;
package/dist/cli.js ADDED
@@ -0,0 +1,60 @@
1
+ // Running the `carrick` binary, read-only, with a hard time limit.
2
+ //
3
+ // Both channels go through here. Nothing in this file may throw at its caller:
4
+ // a missing binary, a crash, a timeout and unparseable stdout all read as "no
5
+ // answer", because neither an edit nor a session start may fail on Carrick.
6
+ import { execFile } from "node:child_process";
7
+ import { parseCheckResult, parseStatusResult, } from "./contract.js";
8
+ /** Binary to run. `carrick` on PATH unless CARRICK_BIN names another. */
9
+ export function binary(env = process.env) {
10
+ return env["CARRICK_BIN"] || "carrick";
11
+ }
12
+ /** Time limit for one CLI call. The budget for the hook is 300 ms of our own. */
13
+ export function timeoutMs(env = process.env) {
14
+ const raw = env["CARRICK_TIMEOUT_MS"];
15
+ const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
16
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 5000;
17
+ }
18
+ async function run(args, options, parse) {
19
+ const env = options.env ?? process.env;
20
+ const bin = options.bin ?? binary(env);
21
+ const started = Date.now();
22
+ return await new Promise((resolve) => {
23
+ execFile(bin, args, {
24
+ cwd: options.cwd,
25
+ env,
26
+ timeout: timeoutMs(env),
27
+ maxBuffer: 32 * 1024 * 1024,
28
+ encoding: "utf8",
29
+ }, (error, stdout) => {
30
+ const ms = Date.now() - started;
31
+ const parsed = parse(stdout ?? "");
32
+ if (parsed) {
33
+ resolve({ result: parsed, failure: null, ms });
34
+ return;
35
+ }
36
+ const failure = error
37
+ ? `${bin} ${args.join(" ")} failed: ${error.message}`
38
+ : `${bin} ${args.join(" ")} printed no payload this reader knows`;
39
+ resolve({ result: null, failure, ms });
40
+ });
41
+ });
42
+ }
43
+ /** `carrick check <file> --json`, run from the workspace root. */
44
+ export async function check(file, options) {
45
+ return await run(["check", file, "--json"], options, parseCheckResult);
46
+ }
47
+ /**
48
+ * `carrick status --json`: the workspace, with no file in the question.
49
+ *
50
+ * `check` and `touch` each take exactly one file, so this is the command a
51
+ * surface opening a session asks. `--workspace` is passed only when the caller
52
+ * names one; without it the CLI finds `.carrick/` from the working directory.
53
+ */
54
+ export async function status(options) {
55
+ const args = options.workspace
56
+ ? ["status", "--workspace", options.workspace, "--json"]
57
+ : ["status", "--json"];
58
+ return await run(args, options, parseStatusResult);
59
+ }
60
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}