create-thally-docs 0.9.0 → 0.10.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.
package/dist/release.d.ts CHANGED
@@ -11,6 +11,22 @@ interface ScaffoldSourceRelease {
11
11
  commitSha: string;
12
12
  treeSha: string;
13
13
  archiveUrl: string;
14
+ manifestPath: 'starter-release.json';
15
+ manifestSha256: string;
16
+ }
17
+ interface ScaffoldOwnershipContract {
18
+ frameworkSyncEligible: readonly string[];
19
+ userOwnedNeverOverwrite: readonly string[];
20
+ manualReview: readonly string[];
21
+ }
22
+ interface StarterReleaseManifest {
23
+ schemaVersion: 1;
24
+ starterVersion: number;
25
+ repository: string;
26
+ defaultBranch: string;
27
+ runtime: Pick<ScaffoldRuntimeRelease, 'repository' | 'commitSha' | 'treeSha'>;
28
+ packages: Record<string, string>;
29
+ ownership: ScaffoldOwnershipContract;
14
30
  }
15
31
  interface ScaffoldRuntimeRelease {
16
32
  repository: string;
@@ -29,30 +45,22 @@ interface ScaffoldRelease {
29
45
  /**
30
46
  * Current stable scaffold release.
31
47
  *
32
- * The source commit is the refreshed public docs template and the runtime
33
- * commit is the matching engine change. `create-thally-docs`, the CLI, MCP,
34
- * and Thally Cloud all import this exact record rather than following a moving
35
- * branch independently.
48
+ * The source commit is the complete, standalone `thallylabs/starter` tree and
49
+ * the runtime commit is the matching engine change. `create-thally-docs`, the
50
+ * CLI, MCP, and Thally Cloud all import this exact record rather than following
51
+ * a moving branch independently. Promotion replaces the placeholders in the
52
+ * adjacent JSON record, which is also consumed by Cloud build parity checks.
53
+ */
54
+ declare const STABLE_SCAFFOLD_RELEASE: ScaffoldRelease;
55
+ /**
56
+ * Immutable releases this toolchain can use as a three-way update base.
57
+ *
58
+ * Promotion prepends the new stable record but retains earlier records. That
59
+ * lets a project created directly from GitHub carry only the exact manifest
60
+ * bytes and still resolve its original starter without mutable branch state.
36
61
  */
37
- declare const STABLE_SCAFFOLD_RELEASE: {
38
- readonly schemaVersion: 1;
39
- readonly id: "2026-08-04.b0094de4.e36d2bcf";
40
- readonly source: {
41
- readonly repository: "thallylabs/docs";
42
- readonly commitSha: "b0094de4fea84567eb12c39c6783fdae6820bb98";
43
- readonly treeSha: "ffd0a6fda07341ddd1ba164cb40acef796f89e2d";
44
- readonly archiveUrl: "https://codeload.github.com/thallylabs/docs/tar.gz/b0094de4fea84567eb12c39c6783fdae6820bb98";
45
- };
46
- readonly runtime: {
47
- readonly repository: "thallylabs/thally";
48
- readonly commitSha: "e36d2bcff38f7638a77369e12773a7cab4d5d9ce";
49
- readonly treeSha: "1c8b0358d78d8caa14ed039bff6ab47c98b685d8";
50
- readonly contentSource: "assets";
51
- readonly identityContractVersion: 1;
52
- };
53
- readonly starterVersion: 1;
54
- };
62
+ declare const SUPPORTED_SCAFFOLD_RELEASES: readonly ScaffoldRelease[];
55
63
  /** True only when an unknown record is the currently supported release. */
56
64
  declare function isStableScaffoldRelease(value: unknown): value is ScaffoldRelease;
57
65
 
58
- export { STABLE_SCAFFOLD_RELEASE, type ScaffoldRelease, type ScaffoldRuntimeRelease, type ScaffoldSourceRelease, isStableScaffoldRelease };
66
+ export { STABLE_SCAFFOLD_RELEASE, SUPPORTED_SCAFFOLD_RELEASES, type ScaffoldOwnershipContract, type ScaffoldRelease, type ScaffoldRuntimeRelease, type ScaffoldSourceRelease, type StarterReleaseManifest, isStableScaffoldRelease };
package/dist/release.js CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  STABLE_SCAFFOLD_RELEASE,
4
+ SUPPORTED_SCAFFOLD_RELEASES,
4
5
  isStableScaffoldRelease
5
- } from "./chunk-PJW4JJIT.js";
6
+ } from "./chunk-RLGB6QF3.js";
6
7
  export {
7
8
  STABLE_SCAFFOLD_RELEASE,
9
+ SUPPORTED_SCAFFOLD_RELEASES,
8
10
  isStableScaffoldRelease
9
11
  };
@@ -1,16 +1,31 @@
1
1
  export { STABLE_SCAFFOLD_RELEASE } from './release.js';
2
2
 
3
3
  /**
4
- * The live Thally documentation site is also the canonical standalone site
5
- * source. Keeping one source means a runtime or UI improvement shipped to the
6
- * docs is available to every subsequent scaffold without maintaining a second
7
- * template repository.
4
+ * Immutable starter archive download and extraction.
5
+ *
6
+ * The dedicated starter repository is already a customer-ready project. Every
7
+ * safe filesystem entry is extracted; there is no exclusion list or second
8
+ * manifest that can silently produce a different scaffold.
8
9
  */
9
- declare const TEMPLATE_REPOSITORY: "thallylabs/docs";
10
- declare const TEMPLATE_COMMIT_SHA: "b0094de4fea84567eb12c39c6783fdae6820bb98";
11
- declare const EXCLUDE_PATHS: string[];
12
- /** True if a tarball entry should land in the scaffold (see EXCLUDE_PATHS). */
13
- declare function shouldInclude(path: string): boolean;
10
+
11
+ /** Repository whose complete tree becomes every newly scaffolded site. */
12
+ declare const STARTER_REPOSITORY: string;
13
+ /** Exact starter commit consumed by every creation surface. */
14
+ declare const STARTER_COMMIT_SHA: string;
15
+ /** GitHub codeload's single top-level directory for the pinned archive. */
16
+ declare const STARTER_ARCHIVE_ROOT: string;
17
+ interface StarterArchiveEntryMetadata {
18
+ type: string;
19
+ size: number;
20
+ }
21
+ /**
22
+ * Validate one codeload entry before `strip: 1` can write it to disk.
23
+ *
24
+ * Git cannot store devices or FIFOs, and starter sites do not need links. A
25
+ * release containing one is rejected rather than partially extracted because
26
+ * the entire repository tree is the immutable contract.
27
+ */
28
+ declare function validateStarterArchiveEntry(archivePath: string, entry: StarterArchiveEntryMetadata, archiveRoot?: string): void;
14
29
 
15
30
  interface ScaffoldOptions {
16
31
  projectDir: string;
@@ -35,4 +50,4 @@ interface ScaffoldResult {
35
50
  }
36
51
  declare function scaffold(options: ScaffoldOptions): Promise<ScaffoldResult>;
37
52
 
38
- export { EXCLUDE_PATHS, type ScaffoldOptions, type ScaffoldResult, TEMPLATE_COMMIT_SHA, TEMPLATE_REPOSITORY, scaffold, shouldInclude };
53
+ export { STARTER_ARCHIVE_ROOT, STARTER_COMMIT_SHA, STARTER_REPOSITORY, type ScaffoldOptions, type ScaffoldResult, scaffold, validateStarterArchiveEntry };
package/dist/scaffold.js CHANGED
@@ -1,20 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- EXCLUDE_PATHS,
4
- TEMPLATE_COMMIT_SHA,
5
- TEMPLATE_REPOSITORY,
6
- scaffold,
7
- shouldInclude
8
- } from "./chunk-JNDRCCJV.js";
9
- import "./chunk-BW7J7DJV.js";
3
+ scaffold
4
+ } from "./chunk-2N74PW2Y.js";
5
+ import "./chunk-ORUAMPNF.js";
6
+ import {
7
+ STARTER_ARCHIVE_ROOT,
8
+ STARTER_COMMIT_SHA,
9
+ STARTER_REPOSITORY,
10
+ validateStarterArchiveEntry
11
+ } from "./chunk-TMXBINSI.js";
12
+ import "./chunk-IJXZ4YRL.js";
10
13
  import {
11
14
  STABLE_SCAFFOLD_RELEASE
12
- } from "./chunk-PJW4JJIT.js";
15
+ } from "./chunk-RLGB6QF3.js";
13
16
  export {
14
- EXCLUDE_PATHS,
15
17
  STABLE_SCAFFOLD_RELEASE,
16
- TEMPLATE_COMMIT_SHA,
17
- TEMPLATE_REPOSITORY,
18
+ STARTER_ARCHIVE_ROOT,
19
+ STARTER_COMMIT_SHA,
20
+ STARTER_REPOSITORY,
18
21
  scaffold,
19
- shouldInclude
22
+ validateStarterArchiveEntry
20
23
  };
@@ -0,0 +1,68 @@
1
+ import { ScaffoldOwnershipContract, ScaffoldRelease, StarterReleaseManifest } from './release.js';
2
+
3
+ /**
4
+ * Ownership-aware, three-way planning for future starter runtime updates.
5
+ *
6
+ * The ownership contract comes from the pinned starter archive's
7
+ * `starter-release.json`. Automatic updates touch only framework-eligible files
8
+ * that still match the recorded old starter; owner and manual-review paths are
9
+ * never included in an apply plan.
10
+ */
11
+
12
+ type StarterPathOwnership = 'protected' | 'manual' | 'syncable' | 'unmanaged';
13
+ interface StarterRuntimeSyncPlan {
14
+ copyPaths: string[];
15
+ deletePaths: string[];
16
+ conflictPaths: string[];
17
+ manualReviewPaths: string[];
18
+ preservedPaths: string[];
19
+ unchangedPaths: string[];
20
+ targetPreconditions: Record<string, StarterTargetPrecondition>;
21
+ }
22
+ interface StarterTargetPrecondition {
23
+ kind: 'missing' | 'file';
24
+ sha256?: string;
25
+ }
26
+ interface StarterApplyProvenance {
27
+ sourcePath: string;
28
+ targetPath: string;
29
+ expectedSha256: string;
30
+ }
31
+ interface ApplyStarterRuntimeSyncOptions {
32
+ /** Explicit acknowledgement that the caller reviewed this exact plan. */
33
+ confirmed: boolean;
34
+ /** Optional immutable provenance file, atomically replaced after mutations. */
35
+ provenance?: StarterApplyProvenance;
36
+ /** Integration hook; an exception aborts and rolls back the transaction. */
37
+ onMutationApplied?: (path: string, index: number) => void;
38
+ }
39
+ /** Parse and validate the ownership section in `starter-release.json`. */
40
+ declare function parseStarterOwnershipContract(value: unknown): ScaffoldOwnershipContract;
41
+ /**
42
+ * Combine the previous and next release policies without weakening ownership.
43
+ *
44
+ * A later starter may broaden framework ownership, but it cannot retroactively
45
+ * make a path safe to overwrite when either release marked that path as owner
46
+ * controlled or manual-review. Classification precedence enforces that union.
47
+ */
48
+ declare function mergeStarterOwnershipContracts(previous: ScaffoldOwnershipContract, next: ScaffoldOwnershipContract): ScaffoldOwnershipContract;
49
+ /** Stable SHA-256 of the exact manifest bytes promoted with a release. */
50
+ declare function starterManifestSha256(source: string): string;
51
+ /**
52
+ * Parse the archive-owned release manifest and bind it to its promoted source,
53
+ * runtime identity, and SHA-256. Branch names never participate in updates.
54
+ */
55
+ declare function parseStarterReleaseManifest(source: string, release?: ScaffoldRelease): StarterReleaseManifest;
56
+ /** Read and validate the canonical ownership manifest from an extracted archive. */
57
+ declare function readStarterReleaseManifest(starterDir: string, release?: ScaffoldRelease): StarterReleaseManifest;
58
+ /** Protected > manual-review > syncable > unmanaged. */
59
+ declare function classifyStarterPath(path: string, contract: ScaffoldOwnershipContract): StarterPathOwnership;
60
+ /**
61
+ * Build a safe three-way update plan from the recorded old starter, the new
62
+ * starter, and the customer's current tree.
63
+ */
64
+ declare function planStarterRuntimeSync(oldStarterDir: string, newStarterDir: string, targetDir: string, ownership: ScaffoldOwnershipContract): StarterRuntimeSyncPlan;
65
+ /** Apply an explicitly confirmed, conflict-free three-way plan. */
66
+ declare function applyStarterRuntimeSyncPlan(newStarterDir: string, targetDir: string, plan: StarterRuntimeSyncPlan, ownership: ScaffoldOwnershipContract, options: ApplyStarterRuntimeSyncOptions): void;
67
+
68
+ export { type ApplyStarterRuntimeSyncOptions, type StarterApplyProvenance, type StarterPathOwnership, type StarterRuntimeSyncPlan, type StarterTargetPrecondition, applyStarterRuntimeSyncPlan, classifyStarterPath, mergeStarterOwnershipContracts, parseStarterOwnershipContract, parseStarterReleaseManifest, planStarterRuntimeSync, readStarterReleaseManifest, starterManifestSha256 };
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ applyStarterRuntimeSyncPlan,
4
+ classifyStarterPath,
5
+ mergeStarterOwnershipContracts,
6
+ parseStarterOwnershipContract,
7
+ parseStarterReleaseManifest,
8
+ planStarterRuntimeSync,
9
+ readStarterReleaseManifest,
10
+ starterManifestSha256
11
+ } from "./chunk-IJXZ4YRL.js";
12
+ import "./chunk-RLGB6QF3.js";
13
+ export {
14
+ applyStarterRuntimeSyncPlan,
15
+ classifyStarterPath,
16
+ mergeStarterOwnershipContracts,
17
+ parseStarterOwnershipContract,
18
+ parseStarterReleaseManifest,
19
+ planStarterRuntimeSync,
20
+ readStarterReleaseManifest,
21
+ starterManifestSha256
22
+ };
@@ -0,0 +1,43 @@
1
+ import { ScaffoldRelease } from './release.js';
2
+ import { StarterRuntimeSyncPlan } from './starter-sync.js';
3
+
4
+ /**
5
+ * User-reachable orchestration for immutable starter release updates.
6
+ *
7
+ * Projects carry the exact bytes of `starter-release.json`. The manifest hash
8
+ * resolves the old release from the toolchain's retained immutable catalog;
9
+ * neither the old nor new side follows a branch. Updates are dry-run by
10
+ * default and advance provenance only after a confirmed, conflict-free apply.
11
+ */
12
+
13
+ interface StarterUpdateDownload {
14
+ (targetDir: string, release: ScaffoldRelease): Promise<void>;
15
+ }
16
+ interface UpdateStarterProjectOptions {
17
+ targetDir: string;
18
+ /** Applying is opt-in. Omitted/false always produces a dry-run plan. */
19
+ apply?: boolean;
20
+ /** Separate acknowledgement for programmatic callers. */
21
+ confirmed?: boolean;
22
+ targetRelease?: ScaffoldRelease;
23
+ releases?: readonly ScaffoldRelease[];
24
+ download?: StarterUpdateDownload;
25
+ }
26
+ interface StarterUpdateResult {
27
+ previousRelease: ScaffoldRelease;
28
+ targetRelease: ScaffoldRelease;
29
+ plan: StarterRuntimeSyncPlan;
30
+ isUpToDate: boolean;
31
+ applied: boolean;
32
+ }
33
+ /** Resolve exact downstream provenance without consulting a moving branch. */
34
+ declare function resolveStarterReleaseFromManifest(source: string, releases?: readonly ScaffoldRelease[]): ScaffoldRelease;
35
+ /**
36
+ * Plan or explicitly apply an immutable three-way starter update.
37
+ *
38
+ * Direct GitHub-template copies need no generated state beyond the canonical
39
+ * manifest: its pinned hash selects the old archive from the retained catalog.
40
+ */
41
+ declare function updateStarterProject(options: UpdateStarterProjectOptions): Promise<StarterUpdateResult>;
42
+
43
+ export { type StarterUpdateDownload, type StarterUpdateResult, type UpdateStarterProjectOptions, resolveStarterReleaseFromManifest, updateStarterProject };
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ downloadStarter
4
+ } from "./chunk-TMXBINSI.js";
5
+ import {
6
+ applyStarterRuntimeSyncPlan,
7
+ mergeStarterOwnershipContracts,
8
+ parseStarterReleaseManifest,
9
+ planStarterRuntimeSync,
10
+ readStarterReleaseManifest,
11
+ starterManifestSha256
12
+ } from "./chunk-IJXZ4YRL.js";
13
+ import {
14
+ STABLE_SCAFFOLD_RELEASE,
15
+ SUPPORTED_SCAFFOLD_RELEASES
16
+ } from "./chunk-RLGB6QF3.js";
17
+
18
+ // src/starter-update.ts
19
+ import {
20
+ existsSync,
21
+ mkdirSync,
22
+ mkdtempSync,
23
+ readFileSync,
24
+ rmSync
25
+ } from "fs";
26
+ import { tmpdir } from "os";
27
+ import { join } from "path";
28
+ var EMPTY_PLAN = {
29
+ copyPaths: [],
30
+ deletePaths: [],
31
+ conflictPaths: [],
32
+ manualReviewPaths: [],
33
+ preservedPaths: [],
34
+ unchangedPaths: [],
35
+ targetPreconditions: {}
36
+ };
37
+ function readDownstreamManifest(targetDir, manifestPath) {
38
+ const path = join(targetDir, manifestPath);
39
+ if (!existsSync(path)) {
40
+ throw new Error(
41
+ `This project has no ${manifestPath}; update it from a versioned Thally starter first.`
42
+ );
43
+ }
44
+ return readFileSync(path, "utf8");
45
+ }
46
+ function resolveStarterReleaseFromManifest(source, releases = SUPPORTED_SCAFFOLD_RELEASES) {
47
+ const hash = starterManifestSha256(source);
48
+ const matching = releases.filter(
49
+ (release) => release.source.manifestSha256 === hash
50
+ );
51
+ if (matching.length !== 1) {
52
+ throw new Error(
53
+ "The project starter manifest is edited, unknown, or ambiguously registered; refusing to update."
54
+ );
55
+ }
56
+ parseStarterReleaseManifest(source, matching[0]);
57
+ return matching[0];
58
+ }
59
+ function assertForwardRelease(previous, target) {
60
+ if (previous.source.repository !== target.source.repository || previous.source.manifestPath !== target.source.manifestPath) {
61
+ throw new Error("Starter updates cannot cross repository or manifest contracts.");
62
+ }
63
+ if (previous.source.commitSha !== target.source.commitSha && previous.starterVersion >= target.starterVersion) {
64
+ throw new Error("The installed CLI does not contain a newer starter release.");
65
+ }
66
+ }
67
+ async function updateStarterProject(options) {
68
+ const targetRelease = options.targetRelease ?? STABLE_SCAFFOLD_RELEASE;
69
+ const releases = options.releases ?? SUPPORTED_SCAFFOLD_RELEASES;
70
+ const manifestPath = targetRelease.source.manifestPath;
71
+ const downstreamManifest = readDownstreamManifest(options.targetDir, manifestPath);
72
+ const previousRelease = resolveStarterReleaseFromManifest(
73
+ downstreamManifest,
74
+ releases
75
+ );
76
+ assertForwardRelease(previousRelease, targetRelease);
77
+ if (previousRelease.source.commitSha === targetRelease.source.commitSha) {
78
+ return {
79
+ previousRelease,
80
+ targetRelease,
81
+ plan: EMPTY_PLAN,
82
+ isUpToDate: true,
83
+ applied: false
84
+ };
85
+ }
86
+ if (options.apply && !options.confirmed) {
87
+ throw new Error("Pass explicit confirmation before applying a starter update.");
88
+ }
89
+ const workspace = mkdtempSync(join(tmpdir(), "thally-starter-update-"));
90
+ const oldStarterDir = join(workspace, "old");
91
+ const newStarterDir = join(workspace, "new");
92
+ mkdirSync(oldStarterDir);
93
+ mkdirSync(newStarterDir);
94
+ const download = options.download ?? (async (targetDir, release) => {
95
+ await downloadStarter(targetDir, void 0, release, { announce: false });
96
+ });
97
+ try {
98
+ await download(oldStarterDir, previousRelease);
99
+ await download(newStarterDir, targetRelease);
100
+ const oldManifest = readStarterReleaseManifest(oldStarterDir, previousRelease);
101
+ const newManifest = readStarterReleaseManifest(newStarterDir, targetRelease);
102
+ const ownership = mergeStarterOwnershipContracts(
103
+ oldManifest.ownership,
104
+ newManifest.ownership
105
+ );
106
+ const plan = planStarterRuntimeSync(
107
+ oldStarterDir,
108
+ newStarterDir,
109
+ options.targetDir,
110
+ ownership
111
+ );
112
+ if (!options.apply) {
113
+ return {
114
+ previousRelease,
115
+ targetRelease,
116
+ plan,
117
+ isUpToDate: false,
118
+ applied: false
119
+ };
120
+ }
121
+ if (plan.conflictPaths.length > 0) {
122
+ throw new Error("Resolve starter synchronization conflicts before applying.");
123
+ }
124
+ if (readDownstreamManifest(options.targetDir, manifestPath) !== downstreamManifest) {
125
+ throw new Error("The project starter manifest changed during update planning.");
126
+ }
127
+ applyStarterRuntimeSyncPlan(
128
+ newStarterDir,
129
+ options.targetDir,
130
+ plan,
131
+ ownership,
132
+ {
133
+ confirmed: true,
134
+ provenance: {
135
+ sourcePath: join(newStarterDir, manifestPath),
136
+ targetPath: join(options.targetDir, manifestPath),
137
+ expectedSha256: previousRelease.source.manifestSha256
138
+ }
139
+ }
140
+ );
141
+ return {
142
+ previousRelease,
143
+ targetRelease,
144
+ plan,
145
+ isUpToDate: false,
146
+ applied: true
147
+ };
148
+ } finally {
149
+ rmSync(workspace, { recursive: true, force: true });
150
+ }
151
+ }
152
+ export {
153
+ resolveStarterReleaseFromManifest,
154
+ updateStarterProject
155
+ };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "create-thally-docs",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "description": "Scaffold the first documentation surface in a Thally product-knowledge pipeline.",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=18"
8
8
  },
9
9
  "bin": {
10
- "create-thally-docs": "./dist/index.js"
10
+ "create-thally-docs": "dist/index.js"
11
11
  },
12
12
  "exports": {
13
13
  ".": "./dist/index.js",
@@ -20,6 +20,14 @@
20
20
  "types": "./dist/release.d.ts",
21
21
  "import": "./dist/release.js"
22
22
  },
23
+ "./starter-sync": {
24
+ "types": "./dist/starter-sync.d.ts",
25
+ "import": "./dist/starter-sync.js"
26
+ },
27
+ "./starter-update": {
28
+ "types": "./dist/starter-update.d.ts",
29
+ "import": "./dist/starter-update.js"
30
+ },
23
31
  "./migrate": "./dist/migrate/index.js",
24
32
  "./package.json": "./package.json"
25
33
  },