@hitch42/applet 0.1.1-beta.20 → 0.1.1-beta.21

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 (94) hide show
  1. package/dist/_internal/client.d.ts +0 -2
  2. package/dist/_internal/client.js +3 -5
  3. package/dist/_internal/log-context.d.ts +0 -2
  4. package/dist/_internal/log-context.js +1 -2
  5. package/dist/_internal/server.d.ts +2 -10
  6. package/dist/_internal/server.js +9 -14
  7. package/dist/applet.js +64 -23
  8. package/dist/auth-DuMRqSGe.js +49 -0
  9. package/dist/{batch-_ZjAg-Kk.js → batch-CiLZ177G.js} +3 -4
  10. package/dist/build-C7hi2yg4.js +1 -0
  11. package/dist/{build-D47vK5aa.js → build-FAQSHJgm.js} +4 -3
  12. package/dist/{ci-BTFTCBgy.js → ci-oWlUJKm9.js} +5 -6
  13. package/dist/client/hitch/client.d.ts +1 -1
  14. package/dist/client/hitch/client.js +1 -2
  15. package/dist/client/hitch/phone.d.ts +1 -1
  16. package/dist/client/hitch/phone.js +1 -2
  17. package/dist/client/hitch.d.ts +2 -2
  18. package/dist/client/hitch.js +2 -2
  19. package/dist/client/react/markdown-editor.d.ts +1 -1
  20. package/dist/client/react/markdown-editor.js +1 -2
  21. package/dist/client/react/markdown.d.ts +1 -1
  22. package/dist/client/react/markdown.js +1 -2
  23. package/dist/client/react.d.ts +2 -2
  24. package/dist/client/react.js +2 -2
  25. package/dist/client.d.ts +2 -4
  26. package/dist/client.js +3 -5
  27. package/dist/{contract-CJv-n1l3.d.ts → contract-CUc0B2rl.d.ts} +1 -3
  28. package/dist/{contract-BeKz1Qcs.js → contract-D9LCNGjD.js} +1 -2
  29. package/dist/{create-C2UrZYSK.d.ts → create-BJqLT8Uq.d.ts} +0 -4
  30. package/dist/{deploy-BbqyK5Ik.js → deploy-BM55ZoSp.js} +160 -152
  31. package/dist/{device-BgIfytaR.js → device-CZWWckCq.js} +54 -34
  32. package/dist/{email-protocol-B9u_dh2p.d.ts → email-protocol-CEqzASSx.d.ts} +0 -6
  33. package/dist/{generate-BLD0DoCk.js → generate-48pqFh5M.js} +31 -37
  34. package/dist/generate-BYK4aNud.js +1 -0
  35. package/dist/generated-agents-BUnJ0FQA.js +215 -0
  36. package/dist/git-credential-CU7-Erzs.js +24 -0
  37. package/dist/git-deploy-C12nMNB6.js +290 -0
  38. package/dist/git-deploy-CNz6mxiF.js +1 -0
  39. package/dist/git-migration-CIq-sN61.js +629 -0
  40. package/dist/git-safety-Ck3X7jvA.js +13 -0
  41. package/dist/{headers-CeRX-Xji.js → headers-BcEMIVCo.js} +1 -2
  42. package/dist/{hitch-client-DtyTZzP5.d.ts → hitch-client-D33JOEOi.d.ts} +5 -7
  43. package/dist/{hitch-client-Cw-WZ_8v.js → hitch-client-Dph_kzDd.js} +5 -7
  44. package/dist/{hitch-file-url-BPKj9zVG.js → hitch-file-url--AMjDOzP.js} +1 -2
  45. package/dist/index.d.ts +8 -15
  46. package/dist/index.js +32 -37
  47. package/dist/{init-BKUBf6i9.js → init-Bb6WT8UB.js} +70 -18
  48. package/dist/{lazy-client-CwIzYAoE.js → lazy-client--QCL0wiu.js} +1 -2
  49. package/dist/{load-config-DGALUExv.js → load-config-Cffvj-aM.js} +24 -26
  50. package/dist/{log-context-DWznGjfz.js → log-context-BepwdjrR.js} +1 -2
  51. package/dist/login-BWZKo7Ui.js +21 -0
  52. package/dist/{logs-Cv5z1e2w.js → logs-glCVHI-8.js} +6 -6
  53. package/dist/{manifest-BBHD_EY9.d.ts → manifest-CpghlaJc.d.ts} +6 -7
  54. package/dist/manifest-patterns-BfxqImlp.js +4 -0
  55. package/dist/{markdown-CwlqzBtP.js → markdown-BA_XzFxq.js} +2 -3
  56. package/dist/{markdown-DSHOQIOb.d.ts → markdown-BXt65dDo.d.ts} +1 -3
  57. package/dist/{markdown-editor-BmEaBzd7.d.ts → markdown-editor-CB_nWLzj.d.ts} +1 -5
  58. package/dist/{markdown-editor-C5mLHGqu.js → markdown-editor-CaUuphfc.js} +3 -5
  59. package/dist/parent-D52Kxp8U.js +1 -0
  60. package/dist/parent-Zx8Eg1Ki.js +75 -0
  61. package/dist/{project-CrxdZR0B.js → project-DZGk765E.js} +63 -54
  62. package/dist/{prompt-ChS-grYO.js → prompt-CW98R-Un.js} +1 -2
  63. package/dist/{query-cache-CXmAyLv3.js → query-cache-DQJy45RT.js} +1 -2
  64. package/dist/{query-utils-zz9pVjgp.d.ts → query-utils-okxIoKM3.d.ts} +0 -2
  65. package/dist/{schedules-Dajgni_z.js → schedules-CSUb84e2.js} +5 -5
  66. package/dist/server.d.ts +5 -13
  67. package/dist/server.js +17 -20
  68. package/dist/source-hash-BbvtRaus.js +107 -0
  69. package/dist/store-E5NhITHl.js +448 -0
  70. package/dist/{upload-D_kkmne_.d.ts → upload-DXZyjZSS.d.ts} +6 -10
  71. package/dist/{upload-DRbVhuDm.js → upload-DtMavo8Q.js} +4 -6
  72. package/dist/{use-phone-ug4B5gQp.js → use-phone-CB_idmrz.js} +9 -13
  73. package/dist/{use-phone-M7c_HsB7.d.ts → use-phone-DBAkasPq.d.ts} +0 -4
  74. package/dist/{variables-B-CLu8cY.js → variables-CgZstC7I.js} +7 -8
  75. package/dist/vite.d.ts +1 -3
  76. package/dist/vite.js +41 -50
  77. package/dist/{workflow-DurM6Fwx.d.ts → workflow-BnBrJIzu.d.ts} +0 -2
  78. package/dist/{workflow-engine-BOTzURKM.js → workflow-engine-CyyaVENo.js} +2 -4
  79. package/dist/{workflow-CTo2kZF1.js → workflow-l2SlVePB.js} +2 -4
  80. package/dist/workspace-CtIKSgMD.js +76 -0
  81. package/package.json +6 -5
  82. package/scaffold/.agents/skills/hitch-applets/SKILL.md +27 -9
  83. package/scaffold/.agents/skills/hitch-applets/references/files.md +2 -2
  84. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +8 -6
  85. package/scaffold/.agents/skills/hitch-applets/references/performance.md +2 -2
  86. package/scaffold/.agents/skills/hitch-applets/references/records.md +8 -8
  87. package/scaffold/.agents/skills/hitch-cli/SKILL.md +52 -43
  88. package/scaffold/AGENTS.md +6 -0
  89. package/dist/auth-D3CrCtZq.js +0 -21
  90. package/dist/build-JtbY8q2p.js +0 -2
  91. package/dist/generate-COCRy1Bs.js +0 -2
  92. package/dist/login-CceGvlMi.js +0 -14
  93. package/dist/store-B8N_8JIN.js +0 -196
  94. package/dist/workspace-8yqXsvce.js +0 -31
@@ -1,6 +1,5 @@
1
1
  import { ContractProcedure, Meta, Schema } from "@orpc/contract";
2
2
  import { z } from "zod";
3
- //#region src/hitch/email.d.ts
4
3
  type EmailTags = Record<string, string>;
5
4
  type CreateEmailPixelInput = {
6
5
  tags?: EmailTags;
@@ -175,8 +174,6 @@ type HitchEmailAttachmentOutput = {
175
174
  contentType: string;
176
175
  sizeBytes: number;
177
176
  };
178
- //#endregion
179
- //#region src/server/telephony-protocol.d.ts
180
177
  /** Handle events at least once and without order. Use eventId for idempotency.
181
178
  * Read the message with hitch.telephony.messages.get({ id: messageId }). */
182
179
  type TelephonyMessageReceivedEvent = {
@@ -186,8 +183,6 @@ type TelephonyMessageReceivedEvent = {
186
183
  numberId: string;
187
184
  occurredAt: string;
188
185
  };
189
- //#endregion
190
- //#region src/server/email-protocol.d.ts
191
186
  /** Handle events at least once and without order. Use eventId for idempotency.
192
187
  * Read the message with hitch.email.messages.get({ id: messageId }). */
193
188
  type EmailMessageReceivedEvent = {
@@ -198,5 +193,4 @@ type EmailMessageReceivedEvent = {
198
193
  threadId: string;
199
194
  occurredAt: string;
200
195
  };
201
- //#endregion
202
196
  export { EmailTracker as a, HitchEmailMessage as c, ListEmailTrackersInput as d, ListEmailTrackersOutput as f, emailContract as g, SendHitchEmailInput as h, EmailContract as i, HitchEmailThread as l, ListHitchEmailThreadsInput as m, TelephonyMessageReceivedEvent as n, HitchEmailAddress as o, ListHitchEmailMessagesInput as p, CreateEmailPixelInput as r, HitchEmailAttachment as s, EmailMessageReceivedEvent as t, HitchMailbox as u };
@@ -1,19 +1,20 @@
1
- import { a as organizationInstanceUrl, c as resolveInstanceUrl } from "./store-B8N_8JIN.js";
2
- import { t as fetchWithOrgAuth } from "./auth-D3CrCtZq.js";
3
- import { t as loadAppletConfig } from "./load-config-DGALUExv.js";
4
- import { a as userAgent, r as prepareAppletProject } from "./project-CrxdZR0B.js";
5
- import { i as loadGitHubActionsBuildInputs, n as githubActionsContext, r as hitchResponseError } from "./ci-BTFTCBgy.js";
1
+ import { l as organizationInstanceUrl, m as resolveInstanceUrl } from "./store-E5NhITHl.js";
2
+ import { n as findAppletRoot } from "./workspace-CtIKSgMD.js";
3
+ import { n as fetchWithOrgAuth } from "./auth-DuMRqSGe.js";
4
+ import { t as loadAppletConfig } from "./load-config-Cffvj-aM.js";
5
+ import { i as prepareAppletProject, o as userAgent } from "./project-DZGk765E.js";
6
+ import { i as loadGitHubActionsBuildInputs, n as githubActionsContext, r as hitchResponseError } from "./ci-oWlUJKm9.js";
6
7
  import { createRequire } from "node:module";
7
8
  import { isContractProcedure, minifyContractRouter } from "@orpc/contract";
8
9
  import { randomUUID } from "node:crypto";
10
+ import { pathToFileURL } from "node:url";
9
11
  import { existsSync } from "node:fs";
10
12
  import { access, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
11
13
  import { dirname, resolve } from "node:path";
12
- import { pathToFileURL } from "node:url";
13
14
  import { execFile } from "node:child_process";
14
15
  import { promisify } from "node:util";
15
16
  import { createClient } from "@hey-api/openapi-ts";
16
- //#region src/generated.ts
17
+ // src/generated.ts
17
18
  const GENERATED_HEADER = "// Generated by @hitch42/applet — do not edit";
18
19
  async function writeGenerated(path, source) {
19
20
  await mkdir(dirname(path), { recursive: true });
@@ -57,8 +58,7 @@ import { createWorkerEntry } from "@hitch42/applet/_internal/server";
57
58
  export default createWorkerEntry(server, { code: ${JSON.stringify(appletCode)} });
58
59
  `);
59
60
  }
60
- //#endregion
61
- //#region src/typegen/create-schema.ts
61
+ // src/typegen/create-schema.ts
62
62
  function isObject$3(value) {
63
63
  return value !== null && typeof value === "object" && !Array.isArray(value);
64
64
  }
@@ -71,22 +71,21 @@ function singleCreateSchema(value) {
71
71
  const [single, array] = value.oneOf;
72
72
  return isComponentRef(single) && isObject$3(array) && array.type === "array" && isComponentRef(array.items) && array.items.$ref === single.$ref ? single : void 0;
73
73
  }
74
- //#endregion
75
- //#region src/typegen/operations.ts
76
- const RECORD_ROUTE = /^\/v1\/([^/]+)(?:\/(\{id\}(?:\/history)?|query|update|aggregate))?$/;
74
+ // src/typegen/operations.ts
75
+ const RECORD_ROUTE = /^\/v1\/([^/]+)(?:\/(\{record\}(?:\/history)?|query|bulk-update|aggregate))?$/;
77
76
  const COMPONENT_REF_PREFIX$1 = "#/components/schemas/";
78
- const BATCH_PATH = "/v1/batch";
79
- const BATCH_ITEM_PATH = "paths./v1/batch.post.requestBody.content.application/json.schema.properties.requests.items";
77
+ const BATCH_PATH = "/v1/batch/run";
78
+ const BATCH_ITEM_PATH = "paths./v1/batch/run.post.requestBody.content.application/json.schema.properties.requests.items";
80
79
  const BATCH_ID_PATH = /^\^\/v1\/([^/]+)\/\[0-9a-fA-F-\]\{36\}\$$/;
81
80
  const VERBS = {
82
81
  "get:": "list",
83
82
  "post:": "create",
84
- "get:{id}": "get",
85
- "get:{id}/history": "history",
86
- "patch:{id}": "update",
87
- "delete:{id}": "delete",
83
+ "get:{record}": "get",
84
+ "get:{record}/history": "history",
85
+ "patch:{record}": "update",
86
+ "delete:{record}": "delete",
88
87
  "post:query": "query",
89
- "post:update": "updateWhere",
88
+ "post:bulk-update": "updateWhere",
90
89
  "post:aggregate": "aggregate"
91
90
  };
92
91
  const COMPONENT_RESPONSE_STATUS = {
@@ -103,7 +102,7 @@ function isObject$2(value) {
103
102
  function recordOperation(path, method) {
104
103
  const match = RECORD_ROUTE.exec(path);
105
104
  const objectCode = match?.[1];
106
- if (objectCode === void 0 || objectCode === "batch") return void 0;
105
+ if (objectCode === void 0 || objectCode === "objects") return void 0;
107
106
  const key = `${method.toLowerCase()}:${match?.at(2) ?? ""}`;
108
107
  if (!Object.hasOwn(VERBS, key)) return void 0;
109
108
  return {
@@ -113,7 +112,7 @@ function recordOperation(path, method) {
113
112
  }
114
113
  function recordObjectCode(path) {
115
114
  const match = RECORD_ROUTE.exec(path);
116
- return match?.[1] === "batch" ? void 0 : match?.[1];
115
+ return match?.[1] === "objects" ? void 0 : match?.[1];
117
116
  }
118
117
  function withRecordOperationId(path, method, operation) {
119
118
  const record = recordOperation(path, method);
@@ -130,13 +129,13 @@ function batchEntry(entry, index) {
130
129
  const enumPath = Array.isArray(path?.enum) && path.enum.length === 1 ? path.enum[0] : void 0;
131
130
  if (Array.isArray(method) && method.length !== 1 || Array.isArray(path?.enum) && path.enum.length !== 1) throw new Error(`${BATCH_ITEM_PATH}.oneOf[${index}] expected a create, update, delete, or updateWhere batch entry, found ${describeValue$1(entry)}`);
132
131
  if (methodValue === "POST" && typeof enumPath === "string") {
133
- const updateWhere = /^\/v1\/([^/]+)\/update$/.exec(enumPath);
132
+ const updateWhere = /^\/v1\/([^/]+)\/bulk-update$/.exec(enumPath);
134
133
  if (updateWhere?.[1] !== void 0) return {
135
134
  objectCode: updateWhere[1],
136
135
  verb: "updateWhere"
137
136
  };
138
137
  const create = /^\/v1\/([^/]+)$/.exec(enumPath);
139
- if (create?.[1] !== void 0 && create[1] !== "batch") return {
138
+ if (create?.[1] !== void 0) return {
140
139
  objectCode: create[1],
141
140
  verb: "create"
142
141
  };
@@ -225,7 +224,7 @@ function prepareRecordSpec(spec, manifest) {
225
224
  }));
226
225
  manifest?.objects.forEach(({ code, optionalAttributes }) => {
227
226
  if (optionalAttributes === void 0 || optionalAttributes.length === 0) return;
228
- const responsePath = `/v1/${code}/{id}`;
227
+ const responsePath = `/v1/${code}/{record}`;
229
228
  const response = prepared.paths[responsePath]?.get?.responses;
230
229
  const schema = isObject$2(response) && isObject$2(response["200"]) ? response["200"].content : void 0;
231
230
  const mediaType = isObject$2(schema) && isObject$2(schema["application/json"]) ? schema["application/json"] : void 0;
@@ -263,8 +262,7 @@ function describeValue$1(value, expectedRef = false) {
263
262
  if (typeof value === "object") return expectedRef ? "an object without a valid $ref" : "an object";
264
263
  return `${typeof value} ${JSON.stringify(value)}`;
265
264
  }
266
- //#endregion
267
- //#region src/typegen/catalog.ts
265
+ // src/typegen/catalog.ts
268
266
  const COMPONENT_REF_PREFIX = "#/components/schemas/";
269
267
  const RESERVED_ATTRIBUTE_CODES = /* @__PURE__ */ new Set([
270
268
  "id",
@@ -310,8 +308,7 @@ function catalogFromSpec(spec) {
310
308
  return [[operation.objectCode, Object.keys(schema.properties).filter((code) => !RESERVED_ATTRIBUTE_CODES.has(code))]];
311
309
  }));
312
310
  }
313
- //#endregion
314
- //#region src/typegen/guard.ts
311
+ // src/typegen/guard.ts
315
312
  function isObject(value) {
316
313
  return value !== null && typeof value === "object" && !Array.isArray(value);
317
314
  }
@@ -326,8 +323,7 @@ function guardManifestForSpec(spec, manifest) {
326
323
  const missing = manifest.objects.find(({ code }) => !available.includes(code));
327
324
  if (missing !== void 0) throw new Error(`object "${missing.code}" not found in org spec (available: ${available.join(", ")})`);
328
325
  }
329
- //#endregion
330
- //#region src/typegen/register.ts
326
+ // src/typegen/register.ts
331
327
  function registerFile(catalog, options, publicApplet) {
332
328
  const catalogType = catalog === void 0 ? "type Catalog = Record<string, string>;" : `type Catalog = {
333
329
  ${Object.entries(catalog).map(([objectCode, attributeCodes]) => ` ${JSON.stringify(objectCode)}: ${attributeCodes.map((code) => JSON.stringify(code)).join(" | ") || "never"};`).join("\n")}
@@ -391,8 +387,7 @@ async function writeRegisterDeclaration(outDir, catalog, publicApplet = false) {
391
387
  hasServer: existsSync(resolve(outDir, "../src/server/main.ts"))
392
388
  }, publicApplet));
393
389
  }
394
- //#endregion
395
- //#region src/typegen/generate.ts
390
+ // src/typegen/generate.ts
396
391
  const execFileAsync = promisify(execFile);
397
392
  const require = createRequire(import.meta.url);
398
393
  function routesModuleSource(routes) {
@@ -515,7 +510,7 @@ async function writeContractFiles(outDir, manifest, spec) {
515
510
  await generateContract(preparedSpec, outDir);
516
511
  await writeManifestGen(outDir, manifest);
517
512
  await writeRegisterDeclaration(outDir, catalog, manifest.public === true);
518
- const hasBatch = Object.hasOwn(preparedSpec.paths, "/v1/batch");
513
+ const hasBatch = Object.hasOwn(preparedSpec.paths, "/v1/batch/run");
519
514
  await writeFile(resolve(outDir, "index.ts"), `// Generated by applet generate — do not edit.
520
515
  import { emailContract, filesContract as files, meContract as me, pruneContract, registerHitchContract, teamsContract as teams, telephonyContract as telephony } from "@hitch42/applet";
521
516
  import manifest from "./manifest.gen.ts";
@@ -545,11 +540,11 @@ async function syncContract(outDir, manifest) {
545
540
  }
546
541
  async function runGenerate() {
547
542
  await prepareAppletProject();
548
- const root = process.cwd();
549
- const outDir = resolve("./.hitch");
543
+ const root = findAppletRoot(process.cwd()) ?? process.cwd();
544
+ const outDir = resolve(root, ".hitch");
550
545
  const manifest = await loadAppletConfig(process.cwd());
551
546
  const { spec, theme: themeStylesheet } = await loadBuildInputs(manifest);
552
- const stagedDir = await mkdtemp(resolve("./.hitch.tmp-"));
547
+ const stagedDir = await mkdtemp(resolve(root, ".hitch.tmp-"));
553
548
  const backupDir = `${stagedDir}.backup`;
554
549
  let staged = true;
555
550
  let backedUp = false;
@@ -595,5 +590,4 @@ async function runGenerate() {
595
590
  if (backedUp && !await fileExists(outDir)) await rename(backupDir, outDir);
596
591
  }
597
592
  }
598
- //#endregion
599
593
  export { writeRegisterDeclaration as a, writeAppletRoot as c, writeServerEntry as d, syncContract as i, writeClientEntry as l, routesModuleSource as n, prepareRecordSpec as o, runGenerate as r, recordOperation as s, generateContract as t, writeManifestGen as u };
@@ -0,0 +1 @@
1
+ export { r as runGenerate } from "./generate-48pqFh5M.js";
@@ -0,0 +1,215 @@
1
+ import { r as findParentRoot } from "./workspace-CtIKSgMD.js";
2
+ import { createHash, randomUUID } from "node:crypto";
3
+ import { fileURLToPath } from "node:url";
4
+ import { existsSync } from "node:fs";
5
+ import fs$1 from "node:fs/promises";
6
+ import { dirname, relative, resolve } from "node:path";
7
+ // src/atomic-file.ts
8
+ async function writeChangedFile(path, contents) {
9
+ const expected = Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
10
+ if ((await fs$1.readFile(path).catch((error) => {
11
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
12
+ throw error;
13
+ }))?.equals(expected) && !(await fs$1.lstat(path)).isSymbolicLink()) return;
14
+ const temporary = `${path}.${randomUUID()}.tmp`;
15
+ try {
16
+ await fs$1.writeFile(temporary, expected);
17
+ await fs$1.rename(temporary, path);
18
+ } finally {
19
+ await fs$1.rm(temporary, { force: true });
20
+ }
21
+ }
22
+ // src/generated-agents.ts
23
+ const GENERATED_SKILLS = [
24
+ "hitch-applets",
25
+ "hitch-cli",
26
+ "hitch-data",
27
+ "hitch-theory",
28
+ "react-best-practices",
29
+ "tanstack-query",
30
+ "tanstack-router",
31
+ "web-design-guidelines"
32
+ ];
33
+ const PARENT_GUIDE = `# Hitch applet parent\n\nEach child folder is one applet repo. Run applet commands in a child for that applet.\nRun applet deploy or applet bootstrap in this parent for all children. Commit changes in each child separately.\n\n`;
34
+ function agentFileAction(input) {
35
+ if (input.child) {
36
+ if (input.kind !== "missing" && !input.dangling && !input.generated) return "edited";
37
+ return input.kind === "link" && input.correctLink && !input.dangling ? "keep" : "link";
38
+ }
39
+ return "write";
40
+ }
41
+ async function state(path) {
42
+ try {
43
+ return (await fs$1.lstat(path)).isSymbolicLink() ? "link" : "file";
44
+ } catch (error) {
45
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return "missing";
46
+ throw error;
47
+ }
48
+ }
49
+ async function files(root) {
50
+ const entries = await fs$1.readdir(root, { withFileTypes: true });
51
+ const contents = await Promise.all(entries.map(async (entry) => {
52
+ if (entry.isDirectory()) return [...await files(resolve(root, entry.name))].map(([path, bytes]) => [`${entry.name}/${path}`, bytes]);
53
+ if (entry.isSymbolicLink()) return [[entry.name, Buffer.from(`symlink:${await fs$1.readlink(resolve(root, entry.name))}`)]];
54
+ return [[entry.name, await fs$1.readFile(resolve(root, entry.name))]];
55
+ }));
56
+ return new Map(contents.flat());
57
+ }
58
+ async function replaceLink(path, target, directory = false) {
59
+ const kind = await state(path);
60
+ if (kind === "link" && await fs$1.readlink(path) === target) return;
61
+ await fs$1.mkdir(dirname(path), { recursive: true });
62
+ const temporary = `${path}.${randomUUID()}.tmp`;
63
+ try {
64
+ await fs$1.symlink(target, temporary, directory ? "dir" : "file");
65
+ if (kind === "file" && directory) await fs$1.rm(path, {
66
+ recursive: true,
67
+ force: true
68
+ });
69
+ await fs$1.rename(temporary, path);
70
+ } finally {
71
+ await fs$1.rm(temporary, { force: true });
72
+ }
73
+ }
74
+ async function generatedAgentContents() {
75
+ const agents = await fs$1.readFile(new URL("../scaffold/AGENTS.md", import.meta.url));
76
+ const result = /* @__PURE__ */ new Map([["AGENTS.md", agents], ["CLAUDE.md", agents]]);
77
+ await Promise.all(GENERATED_SKILLS.map(async (skill) => {
78
+ (await files(fileURLToPath(new URL(`../scaffold/.agents/skills/${skill}`, import.meta.url)))).forEach((bytes, name) => {
79
+ result.set(`.agents/skills/${skill}/${name}`, bytes);
80
+ result.set(`.claude/skills/${skill}/${name}`, bytes);
81
+ });
82
+ }));
83
+ return result;
84
+ }
85
+ const editedPaths = /* @__PURE__ */ new Set();
86
+ async function writeGeneratedAgents(root) {
87
+ const parent = findParentRoot(dirname(root));
88
+ const home = parent ?? root;
89
+ const parentGuide = parent !== void 0 || existsSync(resolve(root, ".hitchroot"));
90
+ const agents = await fs$1.readFile(new URL("../scaffold/AGENTS.md", import.meta.url), "utf8");
91
+ const guide = `${parentGuide ? PARENT_GUIDE : ""}${agents}`;
92
+ await fs$1.mkdir(home, { recursive: true });
93
+ const inventoryPath = resolve(home, ".agents/.hitch-generated.json");
94
+ const previous = JSON.parse(await fs$1.readFile(inventoryPath, "utf8").catch((error) => {
95
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return "{}";
96
+ throw error;
97
+ }));
98
+ const digest = (bytes) => createHash("sha256").update(bytes).digest("hex");
99
+ const generated = await Promise.all(GENERATED_SKILLS.map(async (skill) => ({
100
+ skill,
101
+ contents: await files(fileURLToPath(new URL(`../scaffold/.agents/skills/${skill}`, import.meta.url)))
102
+ })));
103
+ const editedLinks = /* @__PURE__ */ new Set();
104
+ await Promise.all(generated.map(async ({ skill, contents }) => {
105
+ const path = resolve(home, ".agents/skills", skill);
106
+ if (await state(path) !== "link") return;
107
+ const current = await files(path).catch((error) => {
108
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
109
+ throw error;
110
+ });
111
+ if (current !== void 0 && (current.size !== contents.size || [...contents].some(([name, bytes]) => !current.get(name)?.equals(bytes)))) {
112
+ if (!editedPaths.has(path)) console.warn(`${path} was edited; Hitch left it in place.`);
113
+ editedPaths.add(path);
114
+ editedLinks.add(skill);
115
+ return;
116
+ }
117
+ await fs$1.unlink(path).catch((error) => {
118
+ if (!(error instanceof Error && "code" in error && ["ENOENT", "EISDIR"].includes(String(error.code)))) throw error;
119
+ });
120
+ }));
121
+ const expected = /* @__PURE__ */ new Map([["AGENTS.md", Buffer.from(guide)]]);
122
+ await Promise.all(generated.map(async ({ skill, contents }) => {
123
+ const realClaude = await state(resolve(home, ".claude/skills", skill)) === "file";
124
+ contents.forEach((bytes, name) => {
125
+ if (!editedLinks.has(skill)) expected.set(`.agents/skills/${skill}/${name}`, bytes);
126
+ if (realClaude) expected.set(`.claude/skills/${skill}/${name}`, bytes);
127
+ });
128
+ }));
129
+ const inventory = {};
130
+ await Promise.all([...expected].map(async ([name, bytes]) => {
131
+ const path = resolve(home, name);
132
+ const current = await state(path) === "missing" ? void 0 : await fs$1.readFile(path).catch((error) => {
133
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
134
+ throw error;
135
+ });
136
+ if (!(current === void 0 || current.equals(bytes) || digest(current) === previous[name] || name === "AGENTS.md" && current.toString() === agents)) {
137
+ if (!editedPaths.has(path)) console.warn(`${path} was edited; Hitch left it in place.`);
138
+ editedPaths.add(path);
139
+ return;
140
+ }
141
+ await fs$1.mkdir(dirname(path), { recursive: true });
142
+ await writeChangedFile(path, bytes);
143
+ inventory[name] = digest(bytes);
144
+ }));
145
+ await Promise.all(Object.entries(previous).filter(([name]) => !expected.has(name)).map(async ([name, hash]) => {
146
+ if ([...editedLinks].some((skill) => name.startsWith(`.agents/skills/${skill}/`))) return;
147
+ if (![".agents", ".claude"].some((directory) => GENERATED_SKILLS.some((skill) => name.startsWith(`${directory}/skills/${skill}/`)))) return;
148
+ const path = resolve(home, name);
149
+ if (await state(path) === "file" && digest(await fs$1.readFile(path)) === hash) await fs$1.rm(path, { force: true });
150
+ }));
151
+ const claude = resolve(home, "CLAUDE.md");
152
+ const claudeContents = await state(claude) === "missing" ? void 0 : await fs$1.readFile(claude, "utf8").catch((error) => {
153
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
154
+ throw error;
155
+ });
156
+ if (claudeContents === void 0 || claudeContents === agents || claudeContents === guide) await replaceLink(claude, "AGENTS.md");
157
+ else if (!editedPaths.has(claude)) {
158
+ console.warn(`${claude} was edited; Hitch left it in place.`);
159
+ editedPaths.add(claude);
160
+ }
161
+ await Promise.all(generated.map(async ({ skill, contents }) => {
162
+ const path = resolve(home, ".claude/skills", skill);
163
+ const kind = await state(path);
164
+ const target = `../../.agents/skills/${skill}`;
165
+ if (kind === "file") return;
166
+ if (kind === "link" && await fs$1.readlink(path) !== target) {
167
+ const current = await files(path).catch((error) => {
168
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
169
+ throw error;
170
+ });
171
+ if (current !== void 0 && (current.size !== contents.size || [...contents].some(([name, bytes]) => !current.get(name)?.equals(bytes)))) {
172
+ if (!editedPaths.has(path)) console.warn(`${path} was edited; Hitch left it in place.`);
173
+ editedPaths.add(path);
174
+ return;
175
+ }
176
+ }
177
+ await replaceLink(path, target, true);
178
+ }));
179
+ await fs$1.mkdir(dirname(inventoryPath), { recursive: true });
180
+ await writeChangedFile(inventoryPath, `${JSON.stringify(Object.fromEntries(Object.entries(inventory).sort(([a], [b]) => a.localeCompare(b))), null, 2)}\n`);
181
+ if (parent === void 0) return;
182
+ const paths = [
183
+ "AGENTS.md",
184
+ "CLAUDE.md",
185
+ ...GENERATED_SKILLS.flatMap((skill) => [`.agents/skills/${skill}`, `.claude/skills/${skill}`])
186
+ ];
187
+ for (const name of paths) {
188
+ const path = resolve(root, name);
189
+ const kind = await state(path);
190
+ const target = relative(dirname(path), resolve(home, name));
191
+ let equal = true;
192
+ if (kind === "file" || kind === "link" && existsSync(path)) {
193
+ if (name.endsWith(".md")) {
194
+ const current = await fs$1.readFile(path, "utf8");
195
+ equal = current === agents || current === guide;
196
+ } else {
197
+ const current = await files(path);
198
+ const expected = generated.find(({ skill }) => name.endsWith(`/${skill}`)).contents;
199
+ equal = current.size === expected.size && [...expected].every(([file, bytes]) => current.get(file)?.equals(bytes));
200
+ }
201
+ }
202
+ const action = agentFileAction({
203
+ child: true,
204
+ kind,
205
+ generated: equal,
206
+ dangling: kind === "link" && !existsSync(path),
207
+ correctLink: kind === "link" && await fs$1.readlink(path) === target
208
+ });
209
+ if (action === "edited") {
210
+ if (!editedPaths.has(path)) console.warn(`${path} was edited; Hitch left it in place.`);
211
+ editedPaths.add(path);
212
+ } else if (action === "link") await replaceLink(path, target, !name.endsWith(".md"));
213
+ }
214
+ }
215
+ export { generatedAgentContents as n, writeGeneratedAgents as r, GENERATED_SKILLS as t };
@@ -0,0 +1,24 @@
1
+ import { l as organizationInstanceUrl, p as resolveAuthForOrg, r as LoginRequiredError, t as AuthenticationError } from "./store-E5NhITHl.js";
2
+ // src/git-credential.ts
3
+ function parseGitCredentialHost(input, instanceUrl) {
4
+ const host = input.split(/\r?\n/).find((line) => line.startsWith("host="))?.slice(5);
5
+ if (host === void 0) throw new AuthenticationError("Git did not provide a host.");
6
+ const instance = new URL(instanceUrl);
7
+ const labels = instance.hostname.split(".");
8
+ const hostLabels = host.split(":")[0].split(".");
9
+ const orgCode = hostLabels[0];
10
+ if (labels[0] !== "app" || hostLabels.slice(1).join(".") !== labels.slice(1).join(".") || host.includes(":") && host.slice(host.lastIndexOf(":")) !== `:${instance.port}`) throw new AuthenticationError("Git requested credentials for a different Hitch host.");
11
+ if (new URL(organizationInstanceUrl(instanceUrl, orgCode)).host !== host) throw new AuthenticationError("Git requested credentials for a different Hitch host.");
12
+ return orgCode;
13
+ }
14
+ async function runGitCredential(action, instanceUrl) {
15
+ if (action !== "get" && action !== "store" && action !== "erase") throw new Error("Use applet git-credential <get|store|erase>.");
16
+ if (action !== "get") return;
17
+ let input = "";
18
+ for await (const chunk of process.stdin) input += String(chunk);
19
+ const orgCode = parseGitCredentialHost(input, instanceUrl);
20
+ const auth = await resolveAuthForOrg(instanceUrl, orgCode);
21
+ if (auth === void 0) throw new LoginRequiredError();
22
+ process.stdout.write(`username=hitch\npassword=${auth.accessToken}\n\n`);
23
+ }
24
+ export { runGitCredential };