taphound 0.2.0-dev.1

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 (176) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +200 -0
  3. package/README.zh-CN.md +200 -0
  4. package/assets/brand/taphound-mark.svg +9 -0
  5. package/assets/skills/taphound-ai-journey/GUIDE.md +862 -0
  6. package/assets/skills/taphound-ai-journey/SKILL.md +377 -0
  7. package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +526 -0
  8. package/assets/skills/taphound-ai-journey/prompts/check-completion.md +52 -0
  9. package/assets/skills/taphound-ai-journey/prompts/generate-step.md +109 -0
  10. package/assets/skills/taphound-ai-journey/schemas/observe-output.json +42 -0
  11. package/assets/skills/taphound-ai-journey/schemas/project-context.json +81 -0
  12. package/assets/skills/taphound-ai-journey/schemas/proposed-step-envelope.json +267 -0
  13. package/assets/skills/taphound-ai-journey/templates/project-context.example.json +49 -0
  14. package/dist/adapters/adb/adb-adapter.d.ts +23 -0
  15. package/dist/adapters/adb/adb-adapter.js +218 -0
  16. package/dist/adapters/android-cli/android-cli-adapter.d.ts +11 -0
  17. package/dist/adapters/android-cli/android-cli-adapter.js +64 -0
  18. package/dist/adapters/android-cli/layout-parser.d.ts +3 -0
  19. package/dist/adapters/android-cli/layout-parser.js +174 -0
  20. package/dist/adapters/clock/system-clock.d.ts +5 -0
  21. package/dist/adapters/clock/system-clock.js +13 -0
  22. package/dist/adapters/filesystem/artifact-store.d.ts +4 -0
  23. package/dist/adapters/filesystem/artifact-store.js +73 -0
  24. package/dist/adapters/filesystem/generation-meta-writer.d.ts +16 -0
  25. package/dist/adapters/filesystem/generation-meta-writer.js +34 -0
  26. package/dist/adapters/filesystem/generation-session-store.d.ts +68 -0
  27. package/dist/adapters/filesystem/generation-session-store.js +1603 -0
  28. package/dist/adapters/filesystem/journey-writer.d.ts +16 -0
  29. package/dist/adapters/filesystem/journey-writer.js +34 -0
  30. package/dist/adapters/filesystem/project-bound-file.d.ts +6 -0
  31. package/dist/adapters/filesystem/project-bound-file.js +240 -0
  32. package/dist/adapters/filesystem/project-file-inspector.d.ts +8 -0
  33. package/dist/adapters/filesystem/project-file-inspector.js +198 -0
  34. package/dist/adapters/filesystem/skill-installer.d.ts +10 -0
  35. package/dist/adapters/filesystem/skill-installer.js +39 -0
  36. package/dist/adapters/process/node-process-runner.d.ts +8 -0
  37. package/dist/adapters/process/node-process-runner.js +155 -0
  38. package/dist/adapters/prompt/inquirer-generation-prompt.d.ts +55 -0
  39. package/dist/adapters/prompt/inquirer-generation-prompt.js +213 -0
  40. package/dist/adapters/prompt/inquirer-init-prompt.d.ts +32 -0
  41. package/dist/adapters/prompt/inquirer-init-prompt.js +55 -0
  42. package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +47 -0
  43. package/dist/adapters/prompt/inquirer-recorder-prompt.js +157 -0
  44. package/dist/application/assertion/expectation-evaluator.d.ts +61 -0
  45. package/dist/application/assertion/expectation-evaluator.js +207 -0
  46. package/dist/application/collector/logcat-collector.d.ts +39 -0
  47. package/dist/application/collector/logcat-collector.js +107 -0
  48. package/dist/application/context/context-validator.d.ts +27 -0
  49. package/dist/application/context/context-validator.js +120 -0
  50. package/dist/application/doctor/doctor-service.d.ts +35 -0
  51. package/dist/application/doctor/doctor-service.js +178 -0
  52. package/dist/application/generation/focused-input.d.ts +2 -0
  53. package/dist/application/generation/focused-input.js +9 -0
  54. package/dist/application/generation/generation-confirmation-service.d.ts +76 -0
  55. package/dist/application/generation/generation-confirmation-service.js +383 -0
  56. package/dist/application/generation/generation-finalizer.d.ts +67 -0
  57. package/dist/application/generation/generation-finalizer.js +646 -0
  58. package/dist/application/generation/generation-publisher.d.ts +46 -0
  59. package/dist/application/generation/generation-publisher.js +178 -0
  60. package/dist/application/generation/generation-starter.d.ts +31 -0
  61. package/dist/application/generation/generation-starter.js +102 -0
  62. package/dist/application/generation/generation-step-executor.d.ts +52 -0
  63. package/dist/application/generation/generation-step-executor.js +668 -0
  64. package/dist/application/generation/proposed-step-validator.d.ts +11 -0
  65. package/dist/application/generation/proposed-step-validator.js +88 -0
  66. package/dist/application/generation/risk-evaluator.d.ts +9 -0
  67. package/dist/application/generation/risk-evaluator.js +14 -0
  68. package/dist/application/generation/runtime-observer.d.ts +39 -0
  69. package/dist/application/generation/runtime-observer.js +157 -0
  70. package/dist/application/init/init-service.d.ts +17 -0
  71. package/dist/application/init/init-service.js +44 -0
  72. package/dist/application/interaction/action-executor.d.ts +25 -0
  73. package/dist/application/interaction/action-executor.js +103 -0
  74. package/dist/application/interaction/fallback-resolver.d.ts +24 -0
  75. package/dist/application/interaction/fallback-resolver.js +48 -0
  76. package/dist/application/interaction/scroll-to-executor.d.ts +40 -0
  77. package/dist/application/interaction/scroll-to-executor.js +191 -0
  78. package/dist/application/locator/locator-resolver.d.ts +20 -0
  79. package/dist/application/locator/locator-resolver.js +89 -0
  80. package/dist/application/project/project-describer.d.ts +14 -0
  81. package/dist/application/project/project-describer.js +8 -0
  82. package/dist/application/recorder/locator-selector.d.ts +10 -0
  83. package/dist/application/recorder/locator-selector.js +80 -0
  84. package/dist/application/recorder/recorder-service.d.ts +41 -0
  85. package/dist/application/recorder/recorder-service.js +331 -0
  86. package/dist/application/report/report-writer.d.ts +10 -0
  87. package/dist/application/report/report-writer.js +39 -0
  88. package/dist/application/runtime/activity-waiter.d.ts +33 -0
  89. package/dist/application/runtime/activity-waiter.js +73 -0
  90. package/dist/application/runtime/launch-failure.d.ts +2 -0
  91. package/dist/application/runtime/launch-failure.js +20 -0
  92. package/dist/application/runtime/process-waiter.d.ts +27 -0
  93. package/dist/application/runtime/process-waiter.js +73 -0
  94. package/dist/application/runtime/step-runner.d.ts +50 -0
  95. package/dist/application/runtime/step-runner.js +429 -0
  96. package/dist/application/runtime/verify-runtime.d.ts +44 -0
  97. package/dist/application/runtime/verify-runtime.js +350 -0
  98. package/dist/application/wait/idle-waiter.d.ts +30 -0
  99. package/dist/application/wait/idle-waiter.js +90 -0
  100. package/dist/cli/commands/context.d.ts +3 -0
  101. package/dist/cli/commands/context.js +94 -0
  102. package/dist/cli/commands/doctor.d.ts +3 -0
  103. package/dist/cli/commands/doctor.js +51 -0
  104. package/dist/cli/commands/generation.d.ts +3 -0
  105. package/dist/cli/commands/generation.js +518 -0
  106. package/dist/cli/commands/init.d.ts +3 -0
  107. package/dist/cli/commands/init.js +104 -0
  108. package/dist/cli/commands/project.d.ts +3 -0
  109. package/dist/cli/commands/project.js +58 -0
  110. package/dist/cli/commands/record.d.ts +3 -0
  111. package/dist/cli/commands/record.js +88 -0
  112. package/dist/cli/commands/verify.d.ts +3 -0
  113. package/dist/cli/commands/verify.js +106 -0
  114. package/dist/cli/dependencies.d.ts +66 -0
  115. package/dist/cli/dependencies.js +204 -0
  116. package/dist/cli/main.d.ts +8 -0
  117. package/dist/cli/main.js +73 -0
  118. package/dist/cli/output.d.ts +16 -0
  119. package/dist/cli/output.js +22 -0
  120. package/dist/cli/program.d.ts +3 -0
  121. package/dist/cli/program.js +43 -0
  122. package/dist/domain/activity.d.ts +7 -0
  123. package/dist/domain/activity.js +65 -0
  124. package/dist/domain/app-process.d.ts +7 -0
  125. package/dist/domain/app-process.js +10 -0
  126. package/dist/domain/config.d.ts +15 -0
  127. package/dist/domain/config.js +17 -0
  128. package/dist/domain/failure.d.ts +4 -0
  129. package/dist/domain/failure.js +43 -0
  130. package/dist/domain/generation.d.ts +912 -0
  131. package/dist/domain/generation.js +342 -0
  132. package/dist/domain/init.d.ts +42 -0
  133. package/dist/domain/init.js +114 -0
  134. package/dist/domain/journey.d.ts +673 -0
  135. package/dist/domain/journey.js +109 -0
  136. package/dist/domain/layout.d.ts +35 -0
  137. package/dist/domain/layout.js +33 -0
  138. package/dist/domain/locator.d.ts +8 -0
  139. package/dist/domain/locator.js +5 -0
  140. package/dist/domain/project-context.d.ts +101 -0
  141. package/dist/domain/project-context.js +64 -0
  142. package/dist/domain/proposed-step.d.ts +341 -0
  143. package/dist/domain/proposed-step.js +87 -0
  144. package/dist/domain/report.d.ts +349 -0
  145. package/dist/domain/report.js +135 -0
  146. package/dist/domain/runtime-snapshot.d.ts +16 -0
  147. package/dist/domain/runtime-snapshot.js +46 -0
  148. package/dist/ports/adb.d.ts +42 -0
  149. package/dist/ports/adb.js +1 -0
  150. package/dist/ports/android-cli.d.ts +21 -0
  151. package/dist/ports/android-cli.js +1 -0
  152. package/dist/ports/artifact-store.d.ts +12 -0
  153. package/dist/ports/artifact-store.js +1 -0
  154. package/dist/ports/clock.d.ts +4 -0
  155. package/dist/ports/clock.js +1 -0
  156. package/dist/ports/generation-meta-writer.d.ts +10 -0
  157. package/dist/ports/generation-meta-writer.js +1 -0
  158. package/dist/ports/generation-prompt.d.ts +19 -0
  159. package/dist/ports/generation-prompt.js +6 -0
  160. package/dist/ports/generation-session-store.d.ts +32 -0
  161. package/dist/ports/generation-session-store.js +26 -0
  162. package/dist/ports/init-prompt.d.ts +8 -0
  163. package/dist/ports/init-prompt.js +6 -0
  164. package/dist/ports/journey-writer.d.ts +10 -0
  165. package/dist/ports/journey-writer.js +1 -0
  166. package/dist/ports/process-runner.d.ts +32 -0
  167. package/dist/ports/process-runner.js +1 -0
  168. package/dist/ports/project-bound-file.d.ts +8 -0
  169. package/dist/ports/project-bound-file.js +1 -0
  170. package/dist/ports/project-file-inspector.d.ts +33 -0
  171. package/dist/ports/project-file-inspector.js +1 -0
  172. package/dist/ports/recorder-prompt.d.ts +29 -0
  173. package/dist/ports/recorder-prompt.js +1 -0
  174. package/dist/ports/skill-installer.d.ts +8 -0
  175. package/dist/ports/skill-installer.js +1 -0
  176. package/package.json +64 -0
@@ -0,0 +1,342 @@
1
+ import { createHash } from "node:crypto";
2
+ import { z } from "zod";
3
+ import { JourneyStepSchema } from "./journey.js";
4
+ import { ProjectRelativePathSchema } from "./project-context.js";
5
+ import { InteractionPolicySchema } from "./project-context.js";
6
+ import { ProposedStepSchema } from "./proposed-step.js";
7
+ import { RuntimeSnapshotSchema } from "./runtime-snapshot.js";
8
+ export const GENERATION_ERROR_CODES = [
9
+ "CONFIG_INVALID",
10
+ "CONTEXT_INVALID",
11
+ "CONTEXT_STALE",
12
+ "SNAPSHOT_STALE",
13
+ "PACKAGE_ESCAPE",
14
+ "APP_CRASHED",
15
+ "ACTION_UNSUPPORTED",
16
+ "RISK_CONFIRMATION_REQUIRED",
17
+ "ACTION_FORBIDDEN",
18
+ "EXPECT_UNSUPPORTED",
19
+ "RECOVERY_REQUIRED",
20
+ "VERIFICATION_FAILED",
21
+ "PUBLICATION_FAILED",
22
+ "EXPORT_FAILED",
23
+ "FINALIZATION_IN_PROGRESS"
24
+ ];
25
+ export const GenerationErrorCodeSchema = z.enum(GENERATION_ERROR_CODES);
26
+ const Sha256Schema = z.string().regex(/^[a-f\d]{64}$/);
27
+ export const GenerationSessionIdSchema = z.string().regex(/^[A-Za-z\d](?:[A-Za-z\d._-]*[A-Za-z\d])?$/, "Generation session id must be a safe directory name");
28
+ const LiteralSchema = z.string().refine((value) => !value.includes("${") && !value.includes("}"), "Generation variable bindings must be literal");
29
+ export const GenerationVariablesSchema = z.strictObject({
30
+ runId: LiteralSchema.regex(/^[A-Za-z\d](?:[A-Za-z\d._-]*[A-Za-z\d])?$/),
31
+ timestamp: LiteralSchema.pipe(z.iso.datetime()),
32
+ randomHex: LiteralSchema.regex(/^[a-f\d]+$/)
33
+ });
34
+ const NonnegativeSafeIntegerSchema = z.number()
35
+ .int()
36
+ .nonnegative()
37
+ .max(Number.MAX_SAFE_INTEGER);
38
+ export const GenerationInFlightSchema = z.strictObject({
39
+ stepIndex: z.number().int().nonnegative(),
40
+ snapshotHash: Sha256Schema,
41
+ proposalHash: Sha256Schema,
42
+ attemptId: GenerationSessionIdSchema
43
+ });
44
+ export const PendingConfirmationSchema = z.strictObject({
45
+ challengeId: GenerationSessionIdSchema,
46
+ stepIndex: z.number().int().nonnegative(),
47
+ proposalHash: Sha256Schema,
48
+ snapshotHash: Sha256Schema,
49
+ evidenceHash: Sha256Schema,
50
+ actionSummary: z.string().trim().min(1),
51
+ expiresAt: z.iso.datetime(),
52
+ status: z.enum(["pending", "approved"])
53
+ });
54
+ const GenerationFailureSchema = z.strictObject({
55
+ code: GenerationErrorCodeSchema,
56
+ message: z.string().trim().min(1)
57
+ });
58
+ const VerificationSchema = z.discriminatedUnion("status", [
59
+ z.strictObject({ status: z.literal("notRun") }),
60
+ z.strictObject({
61
+ status: z.literal("running"),
62
+ attemptId: GenerationSessionIdSchema
63
+ }),
64
+ z.strictObject({
65
+ status: z.literal("passed"),
66
+ attemptId: GenerationSessionIdSchema,
67
+ reportPath: z.string().min(1),
68
+ reportSha256: Sha256Schema,
69
+ runId: z.string().min(1)
70
+ }),
71
+ z.strictObject({
72
+ status: z.literal("failed"),
73
+ failure: GenerationFailureSchema
74
+ })
75
+ ]);
76
+ const PublicationSchema = z.discriminatedUnion("status", [
77
+ z.strictObject({ status: z.literal("notRun") }),
78
+ z.strictObject({
79
+ status: z.literal("published"),
80
+ journeyPath: ProjectRelativePathSchema
81
+ }),
82
+ z.strictObject({
83
+ status: z.literal("failed"),
84
+ failure: GenerationFailureSchema
85
+ })
86
+ ]);
87
+ export const GenerationSessionSchema = z.strictObject({
88
+ version: z.literal(1),
89
+ id: GenerationSessionIdSchema,
90
+ revision: NonnegativeSafeIntegerSchema,
91
+ state: z.enum(["active", "recoveryRequired"]),
92
+ bindings: z.strictObject({
93
+ projectHash: Sha256Schema,
94
+ configHash: Sha256Schema,
95
+ contextHash: Sha256Schema,
96
+ snapshotHash: Sha256Schema.nullable()
97
+ }),
98
+ target: z.strictObject({
99
+ packageName: z.string().regex(/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/),
100
+ deviceSerial: z.string().trim().min(1),
101
+ resetStrategy: z.literal("processOnly"),
102
+ interactionPolicy: InteractionPolicySchema
103
+ }),
104
+ variables: GenerationVariablesSchema,
105
+ candidateSteps: z.array(JourneyStepSchema),
106
+ candidateSources: z.array(z.enum(["planner", "manualOverride"])),
107
+ inFlight: GenerationInFlightSchema.nullable(),
108
+ pendingConfirmation: PendingConfirmationSchema.nullable(),
109
+ verification: VerificationSchema,
110
+ publication: PublicationSchema
111
+ }).superRefine((session, context) => {
112
+ if (session.candidateSources.length !== session.candidateSteps.length) {
113
+ context.addIssue({
114
+ code: "custom",
115
+ path: ["candidateSources"],
116
+ message: "Candidate provenance must align exactly with candidate steps"
117
+ });
118
+ }
119
+ if (session.state === "recoveryRequired" && session.inFlight === null) {
120
+ context.addIssue({
121
+ code: "custom",
122
+ path: ["state"],
123
+ message: "recoveryRequired state must preserve inFlight evidence"
124
+ });
125
+ }
126
+ if (session.inFlight !== null && session.pendingConfirmation !== null) {
127
+ context.addIssue({
128
+ code: "custom",
129
+ message: "inFlight and pendingConfirmation cannot both be active"
130
+ });
131
+ }
132
+ for (const [field, state] of [
133
+ ["inFlight", session.inFlight],
134
+ ["pendingConfirmation", session.pendingConfirmation]
135
+ ]) {
136
+ if (state !== null && state.stepIndex !== session.candidateSteps.length) {
137
+ context.addIssue({
138
+ code: "custom",
139
+ path: [field, "stepIndex"],
140
+ message: "State stepIndex must equal the next candidate index"
141
+ });
142
+ }
143
+ }
144
+ if (session.verification.status !== "notRun"
145
+ && (session.inFlight !== null || session.pendingConfirmation !== null)) {
146
+ context.addIssue({
147
+ code: "custom",
148
+ path: ["verification"],
149
+ message: "Verification cannot run while candidate step state is active"
150
+ });
151
+ }
152
+ if (session.publication.status === "published"
153
+ && session.verification.status !== "passed") {
154
+ context.addIssue({
155
+ code: "custom",
156
+ path: ["publication"],
157
+ message: "Publication requires passed verification"
158
+ });
159
+ }
160
+ if (session.publication.status === "failed"
161
+ && session.verification.status !== "passed") {
162
+ context.addIssue({
163
+ code: "custom",
164
+ path: ["publication"],
165
+ message: "Publication can only start after passed verification"
166
+ });
167
+ }
168
+ });
169
+ export const GenerationConfirmationEvidenceSchema = z.strictObject({
170
+ version: z.literal(1),
171
+ proposal: ProposedStepSchema,
172
+ snapshot: RuntimeSnapshotSchema,
173
+ source: z.enum(["planner", "manualOverride"])
174
+ });
175
+ function canonicalizeConfirmationEvidence(value) {
176
+ if (Array.isArray(value)) {
177
+ return value.map(canonicalizeConfirmationEvidence);
178
+ }
179
+ if (value !== null && typeof value === "object") {
180
+ return Object.fromEntries(Object.entries(value)
181
+ .filter(([, item]) => item !== undefined)
182
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
183
+ .map(([key, item]) => [
184
+ key,
185
+ canonicalizeConfirmationEvidence(item)
186
+ ]));
187
+ }
188
+ return value;
189
+ }
190
+ export function hashGenerationConfirmationEvidence(value) {
191
+ const evidence = GenerationConfirmationEvidenceSchema.parse(value);
192
+ return createHash("sha256")
193
+ .update(JSON.stringify(canonicalizeConfirmationEvidence(evidence)))
194
+ .digest("hex");
195
+ }
196
+ const BundleRelativePathSchema = z.string().min(1).superRefine((path, context) => {
197
+ if (path.includes("\\")
198
+ || path.includes("\0")
199
+ || path.startsWith("/")
200
+ || /^[A-Za-z]:/.test(path)
201
+ || path.split("/").some((segment) => segment.length === 0 || segment === "." || segment === "..")) {
202
+ context.addIssue({
203
+ code: "custom",
204
+ message: "Path must be a safe bundle-relative path"
205
+ });
206
+ }
207
+ });
208
+ export const GenerationMetaSchema = z.strictObject({
209
+ version: z.literal(1),
210
+ status: z.literal("verified"),
211
+ generationId: GenerationSessionIdSchema,
212
+ journeyPath: ProjectRelativePathSchema,
213
+ bindings: z.strictObject({
214
+ projectHash: Sha256Schema,
215
+ configHash: Sha256Schema,
216
+ contextHash: Sha256Schema
217
+ }),
218
+ verification: z.strictObject({
219
+ reportPath: BundleRelativePathSchema,
220
+ reportSha256: Sha256Schema,
221
+ runId: z.string().min(1),
222
+ runs: z.literal(1)
223
+ }),
224
+ manualOverrideStepIndexes: z.array(z.number().int().nonnegative())
225
+ });
226
+ export const GenerationReportSchema = z.strictObject({
227
+ version: z.literal(1),
228
+ generationId: GenerationSessionIdSchema,
229
+ status: z.literal("verified"),
230
+ steps: z.array(z.strictObject({
231
+ index: z.number().int().nonnegative(),
232
+ source: z.enum(["planner", "manualOverride"])
233
+ })).min(1)
234
+ }).superRefine((report, context) => {
235
+ for (const [index, step] of report.steps.entries()) {
236
+ if (step.index !== index) {
237
+ context.addIssue({
238
+ code: "custom",
239
+ path: ["steps", index, "index"],
240
+ message: "Generation report step indexes must be contiguous"
241
+ });
242
+ }
243
+ }
244
+ });
245
+ export const GenerationBundleManifestEntrySchema = z.strictObject({
246
+ path: BundleRelativePathSchema,
247
+ bytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
248
+ sha256: Sha256Schema
249
+ });
250
+ export const GenerationBundleManifestSchema = z.strictObject({
251
+ version: z.literal(1),
252
+ generationId: GenerationSessionIdSchema,
253
+ files: z.array(GenerationBundleManifestEntrySchema).min(1)
254
+ }).superRefine((manifest, context) => {
255
+ const paths = new Set();
256
+ for (const [index, file] of manifest.files.entries()) {
257
+ if (file.path === "manifest.json") {
258
+ context.addIssue({
259
+ code: "custom",
260
+ path: ["files", index, "path"],
261
+ message: "Manifest cannot include itself"
262
+ });
263
+ }
264
+ if (paths.has(file.path)) {
265
+ context.addIssue({
266
+ code: "custom",
267
+ path: ["files", index, "path"],
268
+ message: "Manifest file paths must be unique"
269
+ });
270
+ }
271
+ paths.add(file.path);
272
+ }
273
+ });
274
+ export function generationCoreIdentity(session) {
275
+ return {
276
+ id: session.id,
277
+ bindings: {
278
+ projectHash: session.bindings.projectHash,
279
+ configHash: session.bindings.configHash,
280
+ contextHash: session.bindings.contextHash
281
+ },
282
+ target: session.target,
283
+ variables: session.variables
284
+ };
285
+ }
286
+ export function bindGenerationVariables(input) {
287
+ return GenerationVariablesSchema.parse(input);
288
+ }
289
+ function expandString(value, variables) {
290
+ let expanded = "";
291
+ let index = 0;
292
+ let literalBraceDepth = 0;
293
+ while (index < value.length) {
294
+ if (value.startsWith("${", index)) {
295
+ const closingIndex = value.indexOf("}", index + 2);
296
+ if (closingIndex === -1) {
297
+ throw new Error("Incomplete generation template marker");
298
+ }
299
+ const name = value.slice(index + 2, closingIndex);
300
+ if (name.length === 0 || name.includes("{") || name.includes("${")) {
301
+ throw new Error("Malformed or nested generation template marker");
302
+ }
303
+ if (name !== "runId" && name !== "timestamp" && name !== "randomHex") {
304
+ throw new Error(`Unsupported generation variable: ${name}`);
305
+ }
306
+ expanded += variables[name];
307
+ index = closingIndex + 1;
308
+ if (value.charAt(index) === "}" && literalBraceDepth === 0) {
309
+ throw new Error("Duplicated generation template marker closing");
310
+ }
311
+ continue;
312
+ }
313
+ const character = value.charAt(index);
314
+ if (character === "{") {
315
+ literalBraceDepth += 1;
316
+ }
317
+ else if (character === "}" && literalBraceDepth > 0) {
318
+ literalBraceDepth -= 1;
319
+ }
320
+ expanded += character;
321
+ index += 1;
322
+ }
323
+ return expanded;
324
+ }
325
+ function expandValue(value, variables) {
326
+ if (typeof value === "string") {
327
+ return expandString(value, variables);
328
+ }
329
+ if (Array.isArray(value)) {
330
+ return value.map((item) => expandValue(item, variables));
331
+ }
332
+ if (value !== null && typeof value === "object") {
333
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, expandValue(item, variables)]));
334
+ }
335
+ return value;
336
+ }
337
+ export function expandGenerationVariables(value, variables) {
338
+ return expandValue(value, bindGenerationVariables(variables));
339
+ }
340
+ export function expandProposedStepVariables(step, variables) {
341
+ return ProposedStepSchema.parse(expandGenerationVariables(step, variables));
342
+ }
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ export declare const SKILL_DIRECTORY_NAME = "taphound-ai-journey";
3
+ export declare const AGENT_IDS: readonly ["claude", "codex", "cursor", "droid", "other"];
4
+ export type AgentId = (typeof AGENT_IDS)[number];
5
+ export declare const AgentIdSchema: z.ZodEnum<{
6
+ other: "other";
7
+ claude: "claude";
8
+ codex: "codex";
9
+ cursor: "cursor";
10
+ droid: "droid";
11
+ }>;
12
+ export interface AgentDefinition {
13
+ readonly id: AgentId;
14
+ readonly label: string;
15
+ readonly projectPath: string;
16
+ readonly globalPath: string;
17
+ }
18
+ export declare const AGENT_REGISTRY: readonly AgentDefinition[];
19
+ export declare function findAgentDefinition(id: AgentId): AgentDefinition;
20
+ export declare function parseAgentIds(raw: string): AgentId[];
21
+ export declare class UnknownAgentError extends Error {
22
+ readonly agentId: string;
23
+ constructor(agentId: string);
24
+ }
25
+ export declare class NoAgentsSelectedError extends Error {
26
+ constructor();
27
+ }
28
+ export interface TargetPath {
29
+ readonly absolutePath: string;
30
+ readonly relativePath: string;
31
+ readonly agents: AgentId[];
32
+ readonly skipped: boolean;
33
+ }
34
+ export declare function resolveTargetPaths(agents: readonly AgentId[], global: boolean, cwd: string, homedir: string): TargetPath[];
35
+ export declare const InitResultSchema: z.ZodObject<{
36
+ status: z.ZodLiteral<"installed">;
37
+ exitCode: z.ZodLiteral<0>;
38
+ agents: z.ZodArray<z.ZodString>;
39
+ paths: z.ZodArray<z.ZodString>;
40
+ skipped: z.ZodOptional<z.ZodArray<z.ZodString>>;
41
+ }, z.core.$strict>;
42
+ export type InitResult = z.infer<typeof InitResultSchema>;
@@ -0,0 +1,114 @@
1
+ import { resolve } from "node:path";
2
+ import { z } from "zod";
3
+ export const SKILL_DIRECTORY_NAME = "taphound-ai-journey";
4
+ export const AGENT_IDS = [
5
+ "claude",
6
+ "codex",
7
+ "cursor",
8
+ "droid",
9
+ "other"
10
+ ];
11
+ export const AgentIdSchema = z.enum(AGENT_IDS);
12
+ export const AGENT_REGISTRY = [
13
+ {
14
+ id: "claude",
15
+ label: "Claude Code",
16
+ projectPath: ".claude/skills",
17
+ globalPath: ".claude/skills"
18
+ },
19
+ {
20
+ id: "codex",
21
+ label: "Codex",
22
+ projectPath: ".agents/skills",
23
+ globalPath: ".agents/skills"
24
+ },
25
+ {
26
+ id: "cursor",
27
+ label: "Cursor",
28
+ projectPath: ".cursor/skills",
29
+ globalPath: ".cursor/skills"
30
+ },
31
+ {
32
+ id: "droid",
33
+ label: "Droid",
34
+ projectPath: ".factory/skills",
35
+ globalPath: ".factory/skills"
36
+ },
37
+ {
38
+ id: "other",
39
+ label: "Other (standard .agents)",
40
+ projectPath: ".agents/skills",
41
+ globalPath: ".agents/skills"
42
+ }
43
+ ];
44
+ export function findAgentDefinition(id) {
45
+ const definition = AGENT_REGISTRY.find((agent) => agent.id === id);
46
+ if (definition === undefined) {
47
+ throw new Error(`Unknown agent: ${id}`);
48
+ }
49
+ return definition;
50
+ }
51
+ export function parseAgentIds(raw) {
52
+ const ids = raw
53
+ .split(",")
54
+ .map((part) => part.trim())
55
+ .filter((part) => part.length > 0);
56
+ const result = [];
57
+ for (const id of ids) {
58
+ const parsed = AgentIdSchema.safeParse(id);
59
+ if (!parsed.success) {
60
+ throw new UnknownAgentError(id);
61
+ }
62
+ result.push(parsed.data);
63
+ }
64
+ return result;
65
+ }
66
+ export class UnknownAgentError extends Error {
67
+ agentId;
68
+ constructor(agentId) {
69
+ super(`Unknown agent: ${agentId}`);
70
+ this.agentId = agentId;
71
+ this.name = "UnknownAgentError";
72
+ }
73
+ }
74
+ export class NoAgentsSelectedError extends Error {
75
+ constructor() {
76
+ super("At least one agent must be selected");
77
+ this.name = "NoAgentsSelectedError";
78
+ }
79
+ }
80
+ export function resolveTargetPaths(agents, global, cwd, homedir) {
81
+ if (agents.length === 0) {
82
+ throw new NoAgentsSelectedError();
83
+ }
84
+ const map = new Map();
85
+ for (const id of agents) {
86
+ const definition = findAgentDefinition(id);
87
+ const baseDir = global ? definition.globalPath : definition.projectPath;
88
+ const relativePath = `${baseDir}/${SKILL_DIRECTORY_NAME}`;
89
+ const absolutePath = resolve(global ? homedir : cwd, relativePath);
90
+ const existing = map.get(absolutePath);
91
+ if (existing === undefined) {
92
+ map.set(absolutePath, {
93
+ relativePath,
94
+ agents: [id]
95
+ });
96
+ }
97
+ else {
98
+ existing.agents.push(id);
99
+ }
100
+ }
101
+ return [...map.entries()].map(([absolutePath, entry]) => ({
102
+ absolutePath,
103
+ relativePath: entry.relativePath,
104
+ agents: entry.agents,
105
+ skipped: false
106
+ }));
107
+ }
108
+ export const InitResultSchema = z.strictObject({
109
+ status: z.literal("installed"),
110
+ exitCode: z.literal(0),
111
+ agents: z.array(z.string()),
112
+ paths: z.array(z.string()),
113
+ skipped: z.array(z.string()).optional()
114
+ });