heximon 0.0.1 → 0.1.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.
@@ -0,0 +1,405 @@
1
+ import { CommandDef } from "citty";
2
+ //#region src/prompter.d.ts
3
+ /** Thrown when the operator cancels an interactive prompt (Ctrl-C). */
4
+ declare class PromptCancelledError extends Error {
5
+ constructor();
6
+ }
7
+ /**
8
+ * The interactive-input surface, abstracted as a port so the CLI's {@link CredentialResolver} stays testable
9
+ * (a test injects a stub). It is a **CLI concept** — interactive prompting is how a human operator supplies a
10
+ * missing credential. The deploy library (`@heximon/deploy`) knows nothing of it: it consumes the resolved
11
+ * credential map this resolver produces. A non-interactive caller (a dashboard) has no prompter at all.
12
+ */
13
+ interface Prompter {
14
+ /** Prompt for a secret value (input is masked). Rejects with {@link PromptCancelledError} on cancel. */
15
+ secret(message: string): Promise<string>;
16
+ /** Prompt for a non-secret value (input is visible). Rejects with {@link PromptCancelledError} on cancel. */
17
+ text(message: string): Promise<string>;
18
+ }
19
+ //#endregion
20
+ //#region src/clack-prompter.d.ts
21
+ /**
22
+ * The {@link Prompter} port backed by `@clack/prompts` — the one interactive surface the CLI exposes. A cancelled
23
+ * prompt (clack's sentinel symbol) is normalized to a thrown {@link PromptCancelledError} so the credential
24
+ * resolver handles one cancel failure mode.
25
+ */
26
+ declare class ClackPrompter implements Prompter {
27
+ /** @throws PromptCancelledError when cancelled. */
28
+ secret(message: string): Promise<string>;
29
+ /** @throws PromptCancelledError when cancelled. */
30
+ text(message: string): Promise<string>;
31
+ }
32
+ //#endregion
33
+ //#region src/cli-environment.d.ts
34
+ /** Detects how the CLI is being run, so the deploy credential ladder knows whether interactive prompting is safe. */
35
+ declare class CliEnvironment {
36
+ /**
37
+ * Whether this is an interactive session a prompt may block on: a real TTY on stdin AND not a CI runner. In CI
38
+ * (or a piped/non-TTY invocation) the deploy credential ladder must fail fast on a missing value rather than
39
+ * hang on a prompt no one can answer.
40
+ */
41
+ static isInteractive(): boolean;
42
+ }
43
+ //#endregion
44
+ //#region src/project-scaffolder.d.ts
45
+ /**
46
+ * A template fetch that failed — the giget ref could not be downloaded. Distinct from every local failure so a
47
+ * caller may retry another ref without also retrying (or cleaning up after) a validation or manifest error.
48
+ */
49
+ declare class TemplateDownloadError extends Error {
50
+ /** The giget source string that could not be fetched. */
51
+ readonly gigetSource: string;
52
+ constructor(gigetSource: string, cause: unknown);
53
+ }
54
+ /** The template axes `heximon create` offers: the app shape and the request-body validator. */
55
+ interface TemplateSelection {
56
+ /** The base template: the smallest real app, or the Drizzle/libsql CRUD app. */
57
+ readonly template: "minimal" | "database";
58
+ /** The validator variant; `"none"` selects the base template with hand-checked bodies. */
59
+ readonly validator: "zod" | "valibot" | "none";
60
+ }
61
+ /** Where a template comes from: the published starters repo (via giget) or a local directory. */
62
+ interface TemplateSource {
63
+ /** A giget source string (e.g. `gh:heximon/starters/minimal#main`) — mutually exclusive with `localDirectory`. */
64
+ readonly gigetSource?: string;
65
+ /** A local starters directory to copy the template from (maintainer/dogfood path; no network). */
66
+ readonly localDirectory?: string;
67
+ }
68
+ /** The inputs {@link ProjectScaffolder.scaffold} materializes a project from. */
69
+ interface ScaffoldRequest extends TemplateSelection {
70
+ /** The new project's directory name AND `package.json` name. */
71
+ readonly projectName: string;
72
+ /** The directory the project directory is created under. */
73
+ readonly parentDirectory: string;
74
+ /** Where the template content comes from. */
75
+ readonly source: TemplateSource;
76
+ /**
77
+ * The version stamped onto every `@heximon/*` dependency range (`^<version>`), normally the CLI's own
78
+ * lockstep version. `"workspace"` stamps `workspace:*` instead (in-monorepo dogfooding).
79
+ */
80
+ readonly heximonVersion: string;
81
+ }
82
+ /** A target directory {@link ProjectScaffolder.prepareTarget} accepted. */
83
+ interface PreparedTarget {
84
+ /** The absolute project directory — always a direct child of the parent directory. */
85
+ readonly projectDirectory: string;
86
+ /**
87
+ * Whether this call created the directory. Only a directory this run created may be removed again; a
88
+ * pre-existing one belongs to the user, so a failed scaffold clears its own residue at most.
89
+ */
90
+ readonly createdDirectory: boolean;
91
+ }
92
+ /** What {@link ProjectScaffolder.scaffold} produced. */
93
+ interface ScaffoldResult {
94
+ /** The absolute path of the created project directory. */
95
+ readonly projectDirectory: string;
96
+ /** The resolved template directory name (e.g. `"minimal-zod"`). */
97
+ readonly templateName: string;
98
+ }
99
+ /**
100
+ * Materializes a new Heximon project from a starters template: resolves the template directory name from
101
+ * the {@link TemplateSelection}, fetches (giget) or copies (local) it, then stamps the project name and
102
+ * the `@heximon/*` dependency ranges onto the fetched `package.json`. Pure filesystem work — prompting,
103
+ * git, and dependency installation live with the CLI command.
104
+ */
105
+ declare class ProjectScaffolder {
106
+ /** A project/directory name: lowercase npm-safe segments (no scopes — the directory is the name). */
107
+ private static readonly projectNamePattern;
108
+ /**
109
+ * Resolve the concrete template directory name for a selection — the validator variant is a `-zod` /
110
+ * `-valibot` suffix; `"none"` selects the base directory.
111
+ *
112
+ * @param selection - The template + validator axes.
113
+ * @returns The starters directory name (e.g. `"database-valibot"`).
114
+ */
115
+ static templateNameOf(selection: TemplateSelection): string;
116
+ /**
117
+ * Validate a project name for use as both the directory and the `package.json` name.
118
+ *
119
+ * @param name - The candidate name.
120
+ * @returns `undefined` when valid, else a human-readable problem statement.
121
+ */
122
+ static validateProjectName(name: string): string | undefined;
123
+ /**
124
+ * Materialize the project: fetch/copy the template into `<parentDirectory>/<projectName>` and stamp the
125
+ * manifest. A target directory that already holds content is refused, never overwritten.
126
+ *
127
+ * @param request - What to scaffold, from where, and with which stamped versions.
128
+ * @returns The created project directory + the resolved template name.
129
+ */
130
+ scaffold(request: ScaffoldRequest): Promise<ScaffoldResult>;
131
+ /**
132
+ * Accept a target directory: the name must be valid, the resolved path must stay a direct child of the
133
+ * parent, and the directory must be absent or empty. A directory holding content is refused outright —
134
+ * `create` never overwrites.
135
+ *
136
+ * @param parentDirectory - The directory the project directory is created under.
137
+ * @param projectName - The candidate project (directory + package) name.
138
+ * @returns The accepted target, and whether this call created the directory.
139
+ */
140
+ static prepareTarget(parentDirectory: string, projectName: string): Promise<PreparedTarget>;
141
+ /**
142
+ * Discard what a failed scaffold left in a prepared target: the directory itself when this run created it,
143
+ * otherwise only the entries — a pre-existing directory was empty when accepted, so nothing else is ours.
144
+ */
145
+ static discardPreparedTarget(target: PreparedTarget): Promise<void>;
146
+ /** Fetch a giget template, tagging a fetch failure so a caller can tell it from a local error. */
147
+ private static fetchTemplate;
148
+ /**
149
+ * Rewrite the scaffolded `package.json`: the project name, and every `@heximon/*` range to the CLI's
150
+ * lockstep version (`^<version>`) or `workspace:*`.
151
+ */
152
+ private stampManifest;
153
+ /**
154
+ * Write the `.npmrc` scope line that routes `@heximon/*` installs to GitHub Packages — skipped when the
155
+ * template already ships its own `.npmrc` (never overwritten), and in workspace mode (`heximonVersion ===
156
+ * "workspace"`), where the `@heximon/*` ranges are workspace links and need no registry at all.
157
+ */
158
+ private static writeNpmrc;
159
+ /** Whether a filesystem path exists, of any kind. */
160
+ private static pathExists;
161
+ /** A directory's entries, or `undefined` when the directory does not exist. */
162
+ private static readEntries;
163
+ }
164
+ //#endregion
165
+ //#region src/create-command.d.ts
166
+ /** What {@link CreateDefaults.scaffoldFromStarters} materializes a project from. */
167
+ interface StartersScaffoldRequest {
168
+ /** The template + validator axes. */
169
+ readonly selection: TemplateSelection;
170
+ /** The new project's directory (and package) name. */
171
+ readonly projectName: string;
172
+ /** The directory the project directory is created under. */
173
+ readonly parentDirectory: string;
174
+ /** The version stamped onto every `@heximon/*` range. */
175
+ readonly heximonVersion: string;
176
+ /** The starters directory name the giget source addresses. */
177
+ readonly templateName: string;
178
+ }
179
+ /** The `heximon create` verb: scaffold a new Heximon app from the starters templates. */
180
+ declare const createCommand: CommandDef;
181
+ /**
182
+ * The `create` command's defaults + environment glue: package-manager detection, the CLI's own lockstep
183
+ * version, the starters giget refs (version branch → main fallback), git init, and the install spawn.
184
+ */
185
+ declare class CreateDefaults {
186
+ /** The default project (directory + package) name when none is given. */
187
+ static readonly projectName: string;
188
+ /** The GitHub repository the published templates are fetched from. */
189
+ static readonly startersRepository: string;
190
+ /**
191
+ * The CLI's own version — the framework version, since `heximon` releases in the lockstep group. It is
192
+ * what `@heximon/*` ranges are stamped to.
193
+ *
194
+ * @returns The version from this package's own manifest.
195
+ */
196
+ static cliVersion(): string;
197
+ /**
198
+ * Detect the invoking package manager from `npm_config_user_agent` (the create-vite trick).
199
+ *
200
+ * @param override - An explicit `--pm` value, which wins when given.
201
+ * @returns One of npm/pnpm/yarn/bun.
202
+ */
203
+ static resolvePackageManager(override: string | undefined): string;
204
+ /**
205
+ * Resolve the template + validator axes from flags, prompts, or defaults.
206
+ *
207
+ * @param template - The `--template` flag value, if any.
208
+ * @param validator - The `--validator` flag value, if any.
209
+ * @param options - Whether prompting is allowed.
210
+ * @returns The resolved selection, or `undefined` when the user cancelled a prompt.
211
+ */
212
+ static resolveSelection(template: string | undefined, validator: string | undefined, options: {
213
+ readonly interactive: boolean;
214
+ }): Promise<TemplateSelection | undefined>;
215
+ /**
216
+ * Scaffold from the published starters repository: try the CLI's `v<major.minor>` branch first (the
217
+ * templates matching this CLI's framework minor), falling back to `main`.
218
+ *
219
+ * The target is accepted before the first attempt, so a refusal (bad name, escaping path, existing
220
+ * content) never reaches the retry — the cleanup below discards only what this call itself created.
221
+ */
222
+ static scaffoldFromStarters(scaffolder: ProjectScaffolder, request: StartersScaffoldRequest): Promise<ScaffoldResult>;
223
+ /**
224
+ * `git init` the project directory, unless it already sits inside a work tree (or git is unavailable).
225
+ *
226
+ * @returns Whether a repository was initialized.
227
+ */
228
+ static initializeGit(projectDirectory: string): Promise<boolean>;
229
+ /** Run `<pm> install` in the project directory with inherited stdio (the user sees the real output). */
230
+ static runInstall(packageManager: string, projectDirectory: string): Promise<void>;
231
+ /** Run one starters attempt against a single giget ref. */
232
+ private static attemptScaffold;
233
+ /** Narrow a raw flag value to one of the allowed literals, or `undefined`. */
234
+ private static narrow;
235
+ /** Spawn a child process and resolve its exit code (non-zero-safe; spawn errors resolve to 1). */
236
+ private static run;
237
+ }
238
+ //#endregion
239
+ //#region src/registry-access.d.ts
240
+ /** The result of one spawned command, captured rather than inherited. */
241
+ interface ProcessRunResult {
242
+ /** The process exit code (`null` becomes `1`). */
243
+ readonly exitCode: number;
244
+ /** Captured stdout. */
245
+ readonly stdout: string;
246
+ /** Captured stderr. */
247
+ readonly stderr: string;
248
+ }
249
+ /** Spawns a command and resolves its captured result. Injected so a test never spawns a real process. */
250
+ type ProcessRunner = (command: string, commandArguments: readonly string[]) => Promise<ProcessRunResult>;
251
+ /** The verdict {@link RegistryAccess.probe} classifies an `npm view` attempt into. */
252
+ declare const RegistryAccessStatus: {
253
+ /** The probe package resolved — the registry is reachable and authorized. */
254
+ readonly Ok: "ok";
255
+ /** The registry rejected the request for lacking (or holding invalid) credentials. */
256
+ readonly Unauthorized: "unauthorized";
257
+ /** The registry could not be reached at all (DNS, network, timeout, an unrecognized failure). */
258
+ readonly Unreachable: "unreachable";
259
+ /** The registry was reached and authorized the request, but the probe package is not there. */
260
+ readonly NotFound: "not-found";
261
+ };
262
+ /** One of {@link RegistryAccessStatus}. */
263
+ type RegistryAccessStatus = (typeof RegistryAccessStatus)[keyof typeof RegistryAccessStatus];
264
+ /** What {@link RegistryAccess.probe} reports. */
265
+ interface RegistryAccessReport {
266
+ /** The classified verdict. */
267
+ readonly status: RegistryAccessStatus;
268
+ /** The raw detail (stderr/stdout, or the spawn error message) behind the verdict, for diagnostics. */
269
+ readonly detail?: string;
270
+ }
271
+ /**
272
+ * Probes whether `@heximon/*` packages are readable from GitHub Packages, the private registry the
273
+ * framework publishes to (the `heximon` CLI itself stays on public npm). A scaffolded project needs a
274
+ * `.npmrc` scope line plus a token in the user's `~/.npmrc`; this probe reuses npm's own config
275
+ * resolution — the same files npm itself reads — so the verdict matches what an install would do.
276
+ */
277
+ declare class RegistryAccess {
278
+ /** The private registry `@heximon/*` packages publish to. */
279
+ static readonly gitHubPackagesRegistry: string;
280
+ /** The package probed to decide reachability — small, always published, always current. */
281
+ static readonly probePackage: string;
282
+ /** The scope line a scaffolded project's `.npmrc` carries, routing `@heximon/*` to GitHub Packages. */
283
+ static readonly npmrcScopeLine: string;
284
+ private readonly run;
285
+ /**
286
+ * @param run - The process runner to probe with. Defaults to spawning `npm` for real; a test injects a
287
+ * stub so the probe never touches the network.
288
+ */
289
+ constructor(run?: ProcessRunner);
290
+ /**
291
+ * Runs `npm view @heximon/runtime version` against GitHub Packages and classifies the result.
292
+ *
293
+ * @returns The classified verdict, with the raw output kept as `detail` for diagnostics.
294
+ */
295
+ probe(): Promise<RegistryAccessReport>;
296
+ /**
297
+ * The fix instructions printed when {@link probe} does not report {@link RegistryAccessStatus.Ok}.
298
+ *
299
+ * @param status - The non-Ok verdict, when known — shapes the wording between an auth fix and a network hint.
300
+ */
301
+ static fixInstructions(status?: RegistryAccessStatus): string;
302
+ /** The default {@link ProcessRunner}: spawns `npm` for real, capturing stdout/stderr. */
303
+ private static spawnProcess;
304
+ }
305
+ //#endregion
306
+ //#region src/skill-installer.d.ts
307
+ /** One installed skill: its name and how many files were written. */
308
+ interface InstalledSkill {
309
+ readonly name: string;
310
+ readonly fileCount: number;
311
+ }
312
+ /**
313
+ * Installs the published Heximon agent skill into a scaffolded project from the docs site's RFC 8615
314
+ * well-known endpoint — `/.well-known/skills/index.json` plus `/.well-known/skills/<name>/<file>`, the
315
+ * same layout `npx skills add https://heximon.dev` consumes (Docus serves the docs repo's `skills/`
316
+ * directory there natively). Best-effort by contract: the caller treats a throw as a skippable step with
317
+ * a printed hint, never a scaffold failure.
318
+ */
319
+ declare class SkillInstaller {
320
+ private readonly sourceUrl;
321
+ /** The published docs site serving the skill. */
322
+ static readonly defaultSourceUrl = "https://heximon.dev";
323
+ /** Where skills land inside the project — the universal agent layout; `.claude/skills` symlinks here. */
324
+ private static readonly targetDirectory;
325
+ /** Per-agent skills directories, each created as a relative symlink onto {@link targetDirectory}. */
326
+ private static readonly agentSymlinks;
327
+ /** Per-request timeout — scaffolding must never hang on an unreachable docs deploy. */
328
+ private static readonly fetchTimeoutMs;
329
+ constructor(sourceUrl?: string);
330
+ /**
331
+ * Install every skill the source publishes into `projectDirectory`.
332
+ *
333
+ * @returns The installed skills, in index order.
334
+ * @throws {Error} When the index is unreachable/malformed or any listed file fails to download.
335
+ */
336
+ install(projectDirectory: string): Promise<readonly InstalledSkill[]>;
337
+ /**
338
+ * Point each per-agent skills directory (`.claude/skills`) at the universal `.agents/skills` copy via a
339
+ * relative symlink, so every agent convention reads the one install. Best-effort: an existing entry is
340
+ * left untouched, and a filesystem that cannot symlink (e.g. Windows without developer mode) is not an
341
+ * install failure — the universal copy stands on its own.
342
+ */
343
+ private static linkAgentDirectories;
344
+ /** Fetch and validate the discovery index, keeping only well-shaped entries with safe names. */
345
+ private static fetchIndex;
346
+ /** A relative file path with no traversal, absolute prefix, or backslash trickery. */
347
+ private static isSafeRelativePath;
348
+ private static fetchJson;
349
+ private static fetchText;
350
+ }
351
+ //#endregion
352
+ //#region src/skill-sources.d.ts
353
+ /**
354
+ * The project's recorded agent-skill sources, persisted as `heximon.skills` in its `package.json`.
355
+ *
356
+ * The manifest rather than the config sidecar, for one reason: `heximon create` records the default source
357
+ * before an app exists to compile, and a tool reads the list without evaluating any TypeScript. Every entry
358
+ * is a base URL {@link SkillInstaller} can install from — the same value `npx skills add <url>` takes.
359
+ */
360
+ declare abstract class SkillSources {
361
+ /** The `package.json` member holding the framework's own manifest block. */
362
+ private static readonly manifestKey;
363
+ /** The member of that block holding the source list. */
364
+ private static readonly listKey;
365
+ /**
366
+ * The sources recorded for a project, in the order they were added.
367
+ *
368
+ * @param projectDirectory - The project root holding `package.json`.
369
+ * @returns The recorded base URLs; empty when the manifest records none (or has no readable list).
370
+ * @throws {Error} When `package.json` is missing or is not readable JSON — a project whose manifest cannot
371
+ * be read is not one to silently report as having no skills.
372
+ */
373
+ static read(projectDirectory: string): Promise<readonly string[]>;
374
+ /**
375
+ * Record one source, leaving every other manifest member untouched.
376
+ *
377
+ * @param projectDirectory - The project root holding `package.json`.
378
+ * @param sourceUrl - The base URL to record, normalized by {@link normalize} first.
379
+ * @returns `true` when the source was added, `false` when it was already recorded — so recording twice is
380
+ * a no-op the caller can report as such rather than a duplicated entry.
381
+ */
382
+ static record(projectDirectory: string, sourceUrl: string): Promise<boolean>;
383
+ /**
384
+ * The canonical form of a source URL: trailing slashes removed, so `https://heximon.dev/` and
385
+ * `https://heximon.dev` are one recorded entry rather than two installs of the same site.
386
+ */
387
+ static normalize(sourceUrl: string): string;
388
+ /** Whether a string is an absolute `http`/`https` URL — the only form the installer can fetch from. */
389
+ static isInstallable(sourceUrl: string): boolean;
390
+ /** The project manifest as a mutable record. */
391
+ private static readManifest;
392
+ }
393
+ //#endregion
394
+ //#region src/skills-command.d.ts
395
+ /**
396
+ * The `heximon skills` command — install the published agent skills into a project and keep the list of
397
+ * sources in `package.json` (`heximon.skills`), so a second machine or a fresh clone reproduces the same
398
+ * install with one verb instead of a remembered URL.
399
+ *
400
+ * `heximon create` already installs the default source and records it, so an existing project's first use of
401
+ * this command is usually `sync` after a clone, or `add` for a second, private skill site.
402
+ */
403
+ declare const skillsCommand: CommandDef;
404
+ //#endregion
405
+ export { ClackPrompter, CliEnvironment, CreateDefaults, type InstalledSkill, type PreparedTarget, type ProcessRunResult, type ProcessRunner, ProjectScaffolder, PromptCancelledError, type Prompter, RegistryAccess, type RegistryAccessReport, RegistryAccessStatus, type ScaffoldRequest, type ScaffoldResult, SkillInstaller, SkillSources, type StartersScaffoldRequest, TemplateDownloadError, type TemplateSelection, type TemplateSource, createCommand, skillsCommand };
package/dist/index.mjs CHANGED
@@ -1,6 +1,2 @@
1
- // heximon — placeholder package.
2
- // This name is reserved for future development. There is no functionality yet.
3
-
4
- console.warn("heximon is a placeholder package and has no functionality yet.");
5
-
6
- export default {};
1
+ import { a as SkillInstaller, c as RegistryAccess, d as ClackPrompter, f as PromptCancelledError, i as SkillSources, l as RegistryAccessStatus, n as CreateDefaults, o as ProjectScaffolder, r as createCommand, s as TemplateDownloadError, t as skillsCommand, u as CliEnvironment } from "./skills-command-W72zUEYD.mjs";
2
+ export { ClackPrompter, CliEnvironment, CreateDefaults, ProjectScaffolder, PromptCancelledError, RegistryAccess, RegistryAccessStatus, SkillInstaller, SkillSources, TemplateDownloadError, createCommand, skillsCommand };