@treeseed/sdk 0.4.12 → 0.5.0

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 (98) hide show
  1. package/dist/control-plane-client.d.ts +60 -1
  2. package/dist/control-plane-client.js +59 -0
  3. package/dist/control-plane.d.ts +1 -1
  4. package/dist/control-plane.js +11 -4
  5. package/dist/d1-store.d.ts +58 -0
  6. package/dist/d1-store.js +64 -0
  7. package/dist/dispatch.js +6 -0
  8. package/dist/graph/schema.js +4 -0
  9. package/dist/index.d.ts +5 -1
  10. package/dist/index.js +32 -0
  11. package/dist/knowledge-coop.d.ts +223 -0
  12. package/dist/knowledge-coop.js +82 -0
  13. package/dist/model-registry.js +79 -0
  14. package/dist/operations/providers/default.js +128 -7
  15. package/dist/operations/services/config-runtime.d.ts +102 -24
  16. package/dist/operations/services/config-runtime.js +896 -160
  17. package/dist/operations/services/deploy.d.ts +223 -15
  18. package/dist/operations/services/deploy.js +626 -55
  19. package/dist/operations/services/git-workflow.d.ts +47 -3
  20. package/dist/operations/services/git-workflow.js +125 -19
  21. package/dist/operations/services/github-automation.d.ts +85 -0
  22. package/dist/operations/services/github-automation.js +220 -1
  23. package/dist/operations/services/key-agent.d.ts +118 -0
  24. package/dist/operations/services/key-agent.js +476 -0
  25. package/dist/operations/services/knowledge-coop-launch.d.ts +90 -0
  26. package/dist/operations/services/knowledge-coop-launch.js +753 -0
  27. package/dist/operations/services/knowledge-coop-packaging.d.ts +59 -0
  28. package/dist/operations/services/knowledge-coop-packaging.js +234 -0
  29. package/dist/operations/services/local-dev.d.ts +0 -1
  30. package/dist/operations/services/local-dev.js +1 -14
  31. package/dist/operations/services/project-platform.d.ts +42 -182
  32. package/dist/operations/services/project-platform.js +162 -59
  33. package/dist/operations/services/railway-deploy.d.ts +1 -0
  34. package/dist/operations/services/railway-deploy.js +31 -13
  35. package/dist/operations/services/runtime-tools.d.ts +52 -5
  36. package/dist/operations/services/runtime-tools.js +186 -26
  37. package/dist/operations/services/watch-dev.js +2 -4
  38. package/dist/operations/services/workspace-preflight.d.ts +4 -4
  39. package/dist/operations/services/workspace-preflight.js +22 -20
  40. package/dist/operations/services/workspace-save.d.ts +10 -1
  41. package/dist/operations/services/workspace-save.js +54 -3
  42. package/dist/operations/services/workspace-tools.d.ts +1 -0
  43. package/dist/operations/services/workspace-tools.js +20 -5
  44. package/dist/operations-registry.js +15 -8
  45. package/dist/operations-types.d.ts +2 -2
  46. package/dist/platform/contracts.d.ts +39 -3
  47. package/dist/platform/deploy-config.d.ts +12 -1
  48. package/dist/platform/deploy-config.js +214 -15
  49. package/dist/platform/deploy-runtime.d.ts +1 -0
  50. package/dist/platform/deploy-runtime.js +10 -2
  51. package/dist/platform/env.yaml +93 -61
  52. package/dist/platform/environment.d.ts +13 -2
  53. package/dist/platform/environment.js +90 -20
  54. package/dist/platform/plugins/constants.d.ts +1 -0
  55. package/dist/platform/plugins/constants.js +7 -6
  56. package/dist/platform/tenant/runtime-config.js +8 -1
  57. package/dist/platform/tenant-config.js +4 -0
  58. package/dist/platform/utils/site-config-schema.js +18 -0
  59. package/dist/plugin-default.js +2 -2
  60. package/dist/scripts/key-agent.js +165 -0
  61. package/dist/scripts/tenant-build.js +4 -1
  62. package/dist/scripts/tenant-check.js +4 -1
  63. package/dist/scripts/tenant-deploy.js +43 -4
  64. package/dist/scripts/tenant-dev.js +0 -1
  65. package/dist/scripts/workspace-start-warning.js +2 -2
  66. package/dist/sdk-types.d.ts +2 -2
  67. package/dist/sdk-types.js +2 -0
  68. package/dist/sdk.d.ts +13 -0
  69. package/dist/sdk.js +40 -0
  70. package/dist/stores/knowledge-coop-store.d.ts +56 -0
  71. package/dist/stores/knowledge-coop-store.js +482 -0
  72. package/dist/treeseed/template-catalog/templates/starter-basic/template/package.json +6 -2
  73. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/api/server.js +4 -0
  74. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/config.yaml +25 -0
  75. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/decisions/adopt-initial-proposal-loop.mdx +22 -0
  76. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/people/starter-steward.mdx +11 -0
  77. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/proposals/establish-initial-proposal-loop.mdx +17 -0
  78. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/manifest.yaml +17 -10
  79. package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +69 -7
  80. package/dist/treeseed/template-catalog/templates/starter-basic/template.config.json +1 -0
  81. package/dist/workflow/operations.d.ts +592 -243
  82. package/dist/workflow/operations.js +1908 -219
  83. package/dist/workflow/runs.d.ts +90 -0
  84. package/dist/workflow/runs.js +242 -0
  85. package/dist/workflow/session.d.ts +31 -0
  86. package/dist/workflow/session.js +97 -0
  87. package/dist/workflow-state.d.ts +88 -2
  88. package/dist/workflow-state.js +288 -26
  89. package/dist/workflow-support.d.ts +1 -1
  90. package/dist/workflow-support.js +32 -2
  91. package/dist/workflow.d.ts +93 -3
  92. package/dist/workflow.js +12 -0
  93. package/package.json +1 -1
  94. package/templates/github/deploy.workflow.yml +11 -1
  95. package/dist/scripts/sync-dev-vars.js +0 -6
  96. package/dist/scripts/workspace-close.js +0 -24
  97. package/dist/scripts/workspace-release.js +0 -42
  98. package/dist/scripts/workspace-start.js +0 -71
@@ -1,24 +1,55 @@
1
1
  export declare const STAGING_BRANCH = "staging";
2
2
  export declare const PRODUCTION_BRANCH = "main";
3
+ export declare function headCommit(repoDir: any, ref?: string): string;
3
4
  export declare function gitWorkflowRoot(cwd?: any): string;
4
5
  export declare function assertCleanWorktree(cwd?: any): string;
6
+ export declare function assertCleanWorktrees(repoDirs: any): any;
5
7
  export declare function branchExists(repoDir: any, branchName: any): boolean;
6
8
  export declare function remoteBranchExists(repoDir: any, branchName: any): boolean;
7
9
  export declare function fetchOrigin(repoDir: any): void;
8
10
  export declare function ensureLocalBranchTracking(repoDir: any, branchName: any): void;
9
11
  export declare function checkoutBranch(repoDir: any, branchName: any): void;
12
+ export declare function checkoutTaskBranchFromStaging(cwd: any, branchName: any, { createIfMissing, pushIfCreated }?: {
13
+ createIfMissing?: boolean | undefined;
14
+ pushIfCreated?: boolean | undefined;
15
+ }): {
16
+ repoDir: string;
17
+ branchName: any;
18
+ baseBranch: string;
19
+ created: boolean;
20
+ resumed: boolean;
21
+ remoteBranch: boolean;
22
+ };
10
23
  export declare function syncBranchWithOrigin(repoDir: any, branchName: any): void;
11
24
  export declare function createFeatureBranchFromStaging(cwd: any, branchName: any): {
12
25
  repoDir: string;
13
- baseBranch: string;
14
26
  branchName: any;
27
+ baseBranch: string;
28
+ created: boolean;
29
+ resumed: boolean;
30
+ remoteBranch: boolean;
15
31
  };
16
32
  export declare function pushBranch(repoDir: any, branchName: any, { setUpstream }?: {
17
33
  setUpstream?: boolean | undefined;
18
34
  }): void;
19
35
  export declare function deleteLocalBranch(repoDir: any, branchName: any): void;
20
36
  export declare function deleteRemoteBranch(repoDir: any, branchName: any): boolean;
21
- export declare function mergeCurrentBranchIntoStaging(cwd: any, featureBranch: any): string;
37
+ export declare function mergeCurrentBranchIntoStaging(cwd: any, featureBranch: any): {
38
+ repoDir: string;
39
+ targetBranch: string;
40
+ committed: boolean;
41
+ commitSha: string;
42
+ pushed: boolean;
43
+ };
44
+ export declare function squashMergeBranchIntoStaging(cwd: any, featureBranch: any, message: any, { pushTarget }?: {
45
+ pushTarget?: boolean | undefined;
46
+ }): {
47
+ repoDir: string;
48
+ targetBranch: string;
49
+ committed: boolean;
50
+ commitSha: string;
51
+ pushed: boolean;
52
+ };
22
53
  export declare function currentManagedBranch(cwd?: any): string;
23
54
  export declare function isTaskBranch(branchName: any): boolean;
24
55
  export declare function assertFeatureBranch(cwd?: any): string;
@@ -46,4 +77,17 @@ export declare function waitForStagingAutomation(repoDir: any): {
46
77
  reason?: undefined;
47
78
  };
48
79
  export declare function prepareReleaseBranches(cwd?: any): string;
49
- export declare function mergeStagingIntoMain(cwd?: any): string;
80
+ export declare function mergeStagingIntoMain(cwd?: any): {
81
+ repoDir: string;
82
+ targetBranch: any;
83
+ commitSha: string;
84
+ pushed: boolean;
85
+ };
86
+ export declare function mergeBranchIntoTarget(cwd?: any, { sourceBranch, targetBranch, message, pushTarget }?: {
87
+ pushTarget?: boolean | undefined;
88
+ }): {
89
+ repoDir: string;
90
+ targetBranch: any;
91
+ commitSha: string;
92
+ pushed: boolean;
93
+ };
@@ -6,6 +6,17 @@ const RESERVED_BRANCHES = /* @__PURE__ */ new Set([STAGING_BRANCH, PRODUCTION_BR
6
6
  function runGit(args, { cwd, capture = false } = {}) {
7
7
  return run("git", args, { cwd, capture });
8
8
  }
9
+ function repoHasStagedChanges(repoDir) {
10
+ try {
11
+ runGit(["diff", "--cached", "--quiet"], { cwd: repoDir });
12
+ return false;
13
+ } catch {
14
+ return true;
15
+ }
16
+ }
17
+ function headCommit(repoDir, ref = "HEAD") {
18
+ return runGit(["rev-parse", ref], { cwd: repoDir, capture: true }).trim();
19
+ }
9
20
  function gitWorkflowRoot(cwd = workspaceRoot()) {
10
21
  return repoRoot(cwd);
11
22
  }
@@ -16,6 +27,12 @@ function assertCleanWorktree(cwd = workspaceRoot()) {
16
27
  }
17
28
  return root;
18
29
  }
30
+ function assertCleanWorktrees(repoDirs) {
31
+ for (const repoDir of repoDirs) {
32
+ assertCleanWorktree(repoDir);
33
+ }
34
+ return repoDirs;
35
+ }
19
36
  function branchExists(repoDir, branchName) {
20
37
  try {
21
38
  runGit(["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`], { cwd: repoDir });
@@ -48,6 +65,63 @@ function ensureLocalBranchTracking(repoDir, branchName) {
48
65
  function checkoutBranch(repoDir, branchName) {
49
66
  runGit(["checkout", branchName], { cwd: repoDir });
50
67
  }
68
+ function checkoutTaskBranchFromStaging(cwd, branchName, { createIfMissing = true, pushIfCreated = false } = {}) {
69
+ const repoDir = assertCleanWorktree(cwd);
70
+ fetchOrigin(repoDir);
71
+ syncBranchWithOrigin(repoDir, STAGING_BRANCH);
72
+ if (currentBranch(repoDir) === branchName) {
73
+ return {
74
+ repoDir,
75
+ branchName,
76
+ baseBranch: STAGING_BRANCH,
77
+ created: false,
78
+ resumed: true,
79
+ remoteBranch: remoteBranchExists(repoDir, branchName)
80
+ };
81
+ }
82
+ if (branchExists(repoDir, branchName)) {
83
+ checkoutBranch(repoDir, branchName);
84
+ if (remoteBranchExists(repoDir, branchName)) {
85
+ runGit(["pull", "--rebase", "origin", branchName], { cwd: repoDir });
86
+ }
87
+ return {
88
+ repoDir,
89
+ branchName,
90
+ baseBranch: STAGING_BRANCH,
91
+ created: false,
92
+ resumed: true,
93
+ remoteBranch: remoteBranchExists(repoDir, branchName)
94
+ };
95
+ }
96
+ if (remoteBranchExists(repoDir, branchName)) {
97
+ runGit(["checkout", "-b", branchName, `origin/${branchName}`], { cwd: repoDir });
98
+ runGit(["pull", "--rebase", "origin", branchName], { cwd: repoDir });
99
+ return {
100
+ repoDir,
101
+ branchName,
102
+ baseBranch: STAGING_BRANCH,
103
+ created: false,
104
+ resumed: true,
105
+ remoteBranch: true
106
+ };
107
+ }
108
+ if (!createIfMissing) {
109
+ throw new Error(`Branch "${branchName}" does not exist locally or on origin.`);
110
+ }
111
+ checkoutBranch(repoDir, STAGING_BRANCH);
112
+ runGit(["checkout", "-b", branchName], { cwd: repoDir });
113
+ if (pushIfCreated) {
114
+ pushBranch(repoDir, branchName, { setUpstream: true });
115
+ }
116
+ return {
117
+ repoDir,
118
+ branchName,
119
+ baseBranch: STAGING_BRANCH,
120
+ created: true,
121
+ resumed: false,
122
+ remoteBranch: pushIfCreated
123
+ };
124
+ }
51
125
  function syncBranchWithOrigin(repoDir, branchName) {
52
126
  fetchOrigin(repoDir);
53
127
  if (!branchExists(repoDir, branchName) && remoteBranchExists(repoDir, branchName)) {
@@ -60,18 +134,14 @@ function syncBranchWithOrigin(repoDir, branchName) {
60
134
  }
61
135
  }
62
136
  function createFeatureBranchFromStaging(cwd, branchName) {
63
- const repoDir = assertCleanWorktree(cwd);
64
- fetchOrigin(repoDir);
65
- if (branchExists(repoDir, branchName) || remoteBranchExists(repoDir, branchName)) {
137
+ const result = checkoutTaskBranchFromStaging(cwd, branchName, {
138
+ createIfMissing: true,
139
+ pushIfCreated: false
140
+ });
141
+ if (!result.created) {
66
142
  throw new Error(`Branch "${branchName}" already exists locally or on origin.`);
67
143
  }
68
- syncBranchWithOrigin(repoDir, STAGING_BRANCH);
69
- runGit(["checkout", "-b", branchName], { cwd: repoDir });
70
- return {
71
- repoDir,
72
- baseBranch: STAGING_BRANCH,
73
- branchName
74
- };
144
+ return result;
75
145
  }
76
146
  function pushBranch(repoDir, branchName, { setUpstream = false } = {}) {
77
147
  const args = setUpstream ? ["push", "-u", "origin", branchName] : ["push", "origin", branchName];
@@ -91,12 +161,28 @@ function deleteRemoteBranch(repoDir, branchName) {
91
161
  return true;
92
162
  }
93
163
  function mergeCurrentBranchIntoStaging(cwd, featureBranch) {
164
+ return squashMergeBranchIntoStaging(cwd, featureBranch, `stage: ${featureBranch}`);
165
+ }
166
+ function squashMergeBranchIntoStaging(cwd, featureBranch, message, { pushTarget = true } = {}) {
94
167
  const repoDir = assertCleanWorktree(cwd);
95
168
  fetchOrigin(repoDir);
96
169
  syncBranchWithOrigin(repoDir, STAGING_BRANCH);
97
- runGit(["merge", "--no-ff", featureBranch, "-m", `merge: ${featureBranch} -> ${STAGING_BRANCH}`], { cwd: repoDir });
98
- pushBranch(repoDir, STAGING_BRANCH);
99
- return repoDir;
170
+ runGit(["merge", "--squash", featureBranch], { cwd: repoDir });
171
+ let committed = false;
172
+ if (repoHasStagedChanges(repoDir)) {
173
+ runGit(["commit", "-m", message], { cwd: repoDir });
174
+ committed = true;
175
+ }
176
+ if (pushTarget) {
177
+ pushBranch(repoDir, STAGING_BRANCH);
178
+ }
179
+ return {
180
+ repoDir,
181
+ targetBranch: STAGING_BRANCH,
182
+ committed,
183
+ commitSha: headCommit(repoDir),
184
+ pushed: pushTarget
185
+ };
100
186
  }
101
187
  function currentManagedBranch(cwd = workspaceRoot()) {
102
188
  return currentBranch(gitWorkflowRoot(cwd));
@@ -180,23 +266,40 @@ function prepareReleaseBranches(cwd = workspaceRoot()) {
180
266
  return repoDir;
181
267
  }
182
268
  function mergeStagingIntoMain(cwd = workspaceRoot()) {
269
+ return mergeBranchIntoTarget(cwd, {
270
+ sourceBranch: STAGING_BRANCH,
271
+ targetBranch: PRODUCTION_BRANCH,
272
+ message: `release: ${STAGING_BRANCH} -> ${PRODUCTION_BRANCH}`,
273
+ pushTarget: true
274
+ });
275
+ }
276
+ function mergeBranchIntoTarget(cwd = workspaceRoot(), { sourceBranch, targetBranch, message, pushTarget = true } = {}) {
183
277
  const repoDir = prepareReleaseBranches(cwd);
184
- checkoutBranch(repoDir, PRODUCTION_BRANCH);
185
- if (remoteBranchExists(repoDir, PRODUCTION_BRANCH)) {
186
- runGit(["pull", "--rebase", "origin", PRODUCTION_BRANCH], { cwd: repoDir });
278
+ checkoutBranch(repoDir, targetBranch);
279
+ if (remoteBranchExists(repoDir, targetBranch)) {
280
+ runGit(["pull", "--rebase", "origin", targetBranch], { cwd: repoDir });
187
281
  }
188
- runGit(["merge", "--no-ff", STAGING_BRANCH, "-m", `release: ${STAGING_BRANCH} -> ${PRODUCTION_BRANCH}`], { cwd: repoDir });
282
+ runGit(["merge", "--no-ff", sourceBranch, "-m", message], { cwd: repoDir });
189
283
  pushBranch(repoDir, STAGING_BRANCH);
190
- pushBranch(repoDir, PRODUCTION_BRANCH);
191
- return repoDir;
284
+ if (pushTarget) {
285
+ pushBranch(repoDir, targetBranch);
286
+ }
287
+ return {
288
+ repoDir,
289
+ targetBranch,
290
+ commitSha: headCommit(repoDir),
291
+ pushed: pushTarget
292
+ };
192
293
  }
193
294
  export {
194
295
  PRODUCTION_BRANCH,
195
296
  STAGING_BRANCH,
196
297
  assertCleanWorktree,
298
+ assertCleanWorktrees,
197
299
  assertFeatureBranch,
198
300
  branchExists,
199
301
  checkoutBranch,
302
+ checkoutTaskBranchFromStaging,
200
303
  createDeprecatedTaskTag,
201
304
  createFeatureBranchFromStaging,
202
305
  currentManagedBranch,
@@ -205,13 +308,16 @@ export {
205
308
  ensureLocalBranchTracking,
206
309
  fetchOrigin,
207
310
  gitWorkflowRoot,
311
+ headCommit,
208
312
  isTaskBranch,
209
313
  listTaskBranches,
314
+ mergeBranchIntoTarget,
210
315
  mergeCurrentBranchIntoStaging,
211
316
  mergeStagingIntoMain,
212
317
  prepareReleaseBranches,
213
318
  pushBranch,
214
319
  remoteBranchExists,
320
+ squashMergeBranchIntoStaging,
215
321
  syncBranchWithOrigin,
216
322
  taskTagSlug,
217
323
  waitForStagingAutomation
@@ -1,7 +1,67 @@
1
+ export interface GitHubRepositoryProvisionInput {
2
+ owner: string;
3
+ name: string;
4
+ description?: string | null;
5
+ visibility?: 'private' | 'public' | 'internal';
6
+ homepageUrl?: string | null;
7
+ topics?: string[];
8
+ }
9
+ export interface GitHubProvisionedRepository {
10
+ slug: string;
11
+ owner: string;
12
+ name: string;
13
+ url: string;
14
+ sshUrl: string;
15
+ httpsUrl: string;
16
+ visibility: 'private' | 'public' | 'internal';
17
+ defaultBranch: string;
18
+ }
1
19
  export declare function getGitHubAutomationMode(): "stub" | "real";
2
20
  export declare function parseGitHubRepositoryFromRemote(remoteUrl: any): string | null;
3
21
  export declare function resolveGitHubRepositorySlug(tenantRoot: any): string;
4
22
  export declare function maybeResolveGitHubRepositorySlug(tenantRoot: any): string | null;
23
+ export declare function resolveDefaultGitHubOwner(): string;
24
+ export declare function createGitHubRepository(input: any): {
25
+ visibility: any;
26
+ defaultBranch: string;
27
+ mode: string;
28
+ slug: string;
29
+ owner: string;
30
+ name: string;
31
+ sshUrl: string;
32
+ httpsUrl: string;
33
+ url: string;
34
+ } | {
35
+ owner: string;
36
+ name: string;
37
+ url: string;
38
+ visibility: string;
39
+ defaultBranch: string;
40
+ slug: string;
41
+ sshUrl: string;
42
+ httpsUrl: string;
43
+ };
44
+ export declare function initializeGitHubRepositoryWorkingTree(cwd: any, repository: any, { defaultBranch, createStaging, commitMessage, remoteName, push, }?: {
45
+ defaultBranch?: string | undefined;
46
+ createStaging?: boolean | undefined;
47
+ commitMessage?: string | undefined;
48
+ remoteName?: string | undefined;
49
+ push?: boolean | undefined;
50
+ }): {
51
+ repository: any;
52
+ remoteName: string;
53
+ defaultBranch: string;
54
+ stagingBranch: string | null;
55
+ pushed: boolean;
56
+ mode: string;
57
+ } | {
58
+ repository: any;
59
+ remoteName: string;
60
+ defaultBranch: string;
61
+ stagingBranch: string | null;
62
+ pushed: boolean;
63
+ mode?: undefined;
64
+ };
5
65
  export declare function resolveGitRepositoryRoot(tenantRoot: any): any;
6
66
  export declare function requiredGitHubEnvironment(tenantRoot: any, { scope, purpose }?: {
7
67
  scope?: string | undefined;
@@ -190,3 +250,28 @@ export declare function ensureGitHubDeployAutomation(tenantRoot: any, { dryRun }
190
250
  mode?: undefined;
191
251
  };
192
252
  };
253
+ export declare function waitForGitHubWorkflowCompletion(tenantRoot: any, { repository, workflow, headSha, branch, timeoutSeconds, pollSeconds, }?: {
254
+ workflow?: string | undefined;
255
+ timeoutSeconds?: number | undefined;
256
+ pollSeconds?: number | undefined;
257
+ }): {
258
+ status: string;
259
+ reason: string;
260
+ repository: any;
261
+ workflow: string;
262
+ headSha: any;
263
+ branch: any;
264
+ runId?: undefined;
265
+ conclusion?: undefined;
266
+ url?: undefined;
267
+ } | {
268
+ status: string;
269
+ repository: any;
270
+ workflow: string;
271
+ runId: any;
272
+ headSha: any;
273
+ conclusion: any;
274
+ url: any;
275
+ reason?: undefined;
276
+ branch?: undefined;
277
+ };
@@ -7,6 +7,9 @@ function envOrNull(key) {
7
7
  const value = process.env[key];
8
8
  return typeof value === "string" && value.length > 0 ? value : null;
9
9
  }
10
+ function slugifySegment(value, fallback = "project") {
11
+ return String(value ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, 96) || fallback;
12
+ }
10
13
  function getGitHubAutomationMode() {
11
14
  return process.env.TREESEED_GITHUB_AUTOMATION_MODE === "stub" ? "stub" : "real";
12
15
  }
@@ -53,6 +56,36 @@ function runGh(args, { cwd, allowFailure = false, capture = true, input } = {})
53
56
  }
54
57
  return result;
55
58
  }
59
+ function sleepSeconds(seconds) {
60
+ if (!Number.isFinite(seconds) || seconds <= 0) {
61
+ return;
62
+ }
63
+ spawnSync("sleep", [String(seconds)], {
64
+ stdio: "ignore"
65
+ });
66
+ }
67
+ function resolveGitHubRemoteUrls(owner, name) {
68
+ const normalizedOwner = slugifySegment(owner, "owner");
69
+ const normalizedName = slugifySegment(name, "repo");
70
+ return {
71
+ slug: `${normalizedOwner}/${normalizedName}`,
72
+ owner: normalizedOwner,
73
+ name: normalizedName,
74
+ sshUrl: `git@github.com:${normalizedOwner}/${normalizedName}.git`,
75
+ httpsUrl: `https://github.com/${normalizedOwner}/${normalizedName}.git`,
76
+ url: `https://github.com/${normalizedOwner}/${normalizedName}`
77
+ };
78
+ }
79
+ function ensureGitIdentity(cwd) {
80
+ const currentName = runGit(["config", "--get", "user.name"], { cwd, allowFailure: true }).stdout?.trim() ?? "";
81
+ const currentEmail = runGit(["config", "--get", "user.email"], { cwd, allowFailure: true }).stdout?.trim() ?? "";
82
+ if (!currentName) {
83
+ runGit(["config", "user.name", envOrNull("TREESEED_GITHUB_COMMITTER_NAME") ?? "Treeseed Launch"], { cwd });
84
+ }
85
+ if (!currentEmail) {
86
+ runGit(["config", "user.email", envOrNull("TREESEED_GITHUB_COMMITTER_EMAIL") ?? "launch@knowledge.coop"], { cwd });
87
+ }
88
+ }
56
89
  function resolveGitHubRepositorySlug(tenantRoot) {
57
90
  const remoteResult = runGit(["remote", "get-url", "origin"], { cwd: tenantRoot });
58
91
  const remoteUrl = remoteResult.stdout?.trim() ?? "";
@@ -69,6 +102,116 @@ function maybeResolveGitHubRepositorySlug(tenantRoot) {
69
102
  return null;
70
103
  }
71
104
  }
105
+ function resolveDefaultGitHubOwner() {
106
+ const explicit = envOrNull("TREESEED_KNOWLEDGE_COOP_GITHUB_OWNER");
107
+ if (explicit) {
108
+ return explicit;
109
+ }
110
+ try {
111
+ const repository = maybeResolveGitHubRepositorySlug(process.cwd());
112
+ if (repository?.includes("/")) {
113
+ return repository.split("/")[0];
114
+ }
115
+ } catch {
116
+ }
117
+ return "treeseed-ai";
118
+ }
119
+ function createGitHubRepository(input) {
120
+ const visibility = input.visibility ?? "private";
121
+ const remotes = resolveGitHubRemoteUrls(input.owner, input.name);
122
+ if (isGitHubAutomationStubbed()) {
123
+ return {
124
+ ...remotes,
125
+ visibility,
126
+ defaultBranch: "main",
127
+ mode: "stub"
128
+ };
129
+ }
130
+ const existing = runGh(["repo", "view", remotes.slug, "--json", "name,owner,url,isPrivate,defaultBranchRef,visibility"], {
131
+ allowFailure: true
132
+ });
133
+ if (existing.status !== 0) {
134
+ const args = ["repo", "create", remotes.slug, "--disable-wiki", "--confirm"];
135
+ if (visibility === "public") {
136
+ args.push("--public");
137
+ } else if (visibility === "internal") {
138
+ args.push("--internal");
139
+ } else {
140
+ args.push("--private");
141
+ }
142
+ if (input.description) {
143
+ args.push("--description", input.description);
144
+ }
145
+ if (input.homepageUrl) {
146
+ args.push("--homepage", input.homepageUrl);
147
+ }
148
+ runGh(args, { capture: false });
149
+ }
150
+ if (Array.isArray(input.topics) && input.topics.length > 0) {
151
+ runGh(
152
+ ["repo", "edit", remotes.slug, ...input.topics.flatMap((topic) => ["--add-topic", slugifySegment(topic, "treeseed")])],
153
+ { capture: false }
154
+ );
155
+ }
156
+ const viewed = runGh(["repo", "view", remotes.slug, "--json", "name,owner,url,isPrivate,defaultBranchRef,visibility"], {});
157
+ const payload = JSON.parse(viewed.stdout || "{}");
158
+ return {
159
+ ...remotes,
160
+ owner: String(payload.owner?.login ?? remotes.owner),
161
+ name: String(payload.name ?? remotes.name),
162
+ url: String(payload.url ?? remotes.url),
163
+ visibility: String(payload.visibility ?? (payload.isPrivate === true ? "private" : visibility)),
164
+ defaultBranch: String(payload.defaultBranchRef?.name ?? "main")
165
+ };
166
+ }
167
+ function initializeGitHubRepositoryWorkingTree(cwd, repository, {
168
+ defaultBranch = "main",
169
+ createStaging = true,
170
+ commitMessage = "Initialize Knowledge Coop hub",
171
+ remoteName = "origin",
172
+ push = true
173
+ } = {}) {
174
+ if (isGitHubAutomationStubbed()) {
175
+ return {
176
+ repository,
177
+ remoteName,
178
+ defaultBranch,
179
+ stagingBranch: createStaging ? "staging" : null,
180
+ pushed: false,
181
+ mode: "stub"
182
+ };
183
+ }
184
+ runGit(["init", "-b", defaultBranch], { cwd, allowFailure: true });
185
+ ensureGitIdentity(cwd);
186
+ const currentRemote = runGit(["remote", "get-url", remoteName], { cwd, allowFailure: true }).stdout?.trim() ?? "";
187
+ if (!currentRemote) {
188
+ runGit(["remote", "add", remoteName, repository.sshUrl], { cwd });
189
+ } else if (currentRemote !== repository.sshUrl && currentRemote !== repository.httpsUrl) {
190
+ runGit(["remote", "set-url", remoteName, repository.sshUrl], { cwd });
191
+ }
192
+ runGit(["add", "-A"], { cwd });
193
+ const hasChanges = runGit(["status", "--porcelain"], { cwd }).stdout?.trim().length > 0;
194
+ if (hasChanges) {
195
+ runGit(["commit", "-m", commitMessage], { cwd });
196
+ }
197
+ if (push) {
198
+ runGit(["push", "-u", remoteName, defaultBranch], { cwd, capture: false });
199
+ }
200
+ if (createStaging) {
201
+ runGit(["checkout", "-B", "staging"], { cwd });
202
+ if (push) {
203
+ runGit(["push", "-u", remoteName, "staging"], { cwd, capture: false });
204
+ }
205
+ runGit(["checkout", defaultBranch], { cwd });
206
+ }
207
+ return {
208
+ repository,
209
+ remoteName,
210
+ defaultBranch,
211
+ stagingBranch: createStaging ? "staging" : null,
212
+ pushed: push
213
+ };
214
+ }
72
215
  function resolveGitRepositoryRoot(tenantRoot) {
73
216
  const result = runGit(["rev-parse", "--show-toplevel"], { cwd: tenantRoot, allowFailure: true });
74
217
  return result.status === 0 ? result.stdout.trim() : tenantRoot;
@@ -291,7 +434,80 @@ function ensureGitHubDeployAutomation(tenantRoot, { dryRun = false } = {}) {
291
434
  environment
292
435
  };
293
436
  }
437
+ function waitForGitHubWorkflowCompletion(tenantRoot, {
438
+ repository,
439
+ workflow = "publish.yml",
440
+ headSha,
441
+ branch,
442
+ timeoutSeconds = 600,
443
+ pollSeconds = 5
444
+ } = {}) {
445
+ if (isGitHubAutomationStubbed()) {
446
+ return {
447
+ status: "skipped",
448
+ reason: "stubbed",
449
+ repository: repository ?? maybeResolveGitHubRepositorySlug(tenantRoot),
450
+ workflow,
451
+ headSha: headSha ?? null,
452
+ branch: branch ?? null
453
+ };
454
+ }
455
+ const repo = repository ?? resolveGitHubRepositorySlug(tenantRoot);
456
+ const startedAt = Date.now();
457
+ while (Date.now() - startedAt < timeoutSeconds * 1e3) {
458
+ const result = runGh([
459
+ "run",
460
+ "list",
461
+ "--repo",
462
+ repo,
463
+ "--workflow",
464
+ workflow,
465
+ "--limit",
466
+ "20",
467
+ "--json",
468
+ "databaseId,headSha,headBranch,status,conclusion,event,displayTitle,url"
469
+ ], { cwd: tenantRoot });
470
+ const runs = JSON.parse(result.stdout || "[]");
471
+ const match = runs.find((run) => {
472
+ if (headSha && run?.headSha !== headSha) {
473
+ return false;
474
+ }
475
+ if (branch && run?.headBranch !== branch) {
476
+ return false;
477
+ }
478
+ return true;
479
+ });
480
+ if (match?.databaseId) {
481
+ runGh(["run", "watch", String(match.databaseId), "--repo", repo, "--exit-status"], {
482
+ cwd: tenantRoot,
483
+ capture: false
484
+ });
485
+ const finalResult = runGh([
486
+ "run",
487
+ "view",
488
+ String(match.databaseId),
489
+ "--repo",
490
+ repo,
491
+ "--json",
492
+ "status,conclusion,url,workflowName,headSha"
493
+ ], { cwd: tenantRoot });
494
+ const finalRun = JSON.parse(finalResult.stdout || "{}");
495
+ return {
496
+ status: "completed",
497
+ repository: repo,
498
+ workflow,
499
+ runId: match.databaseId,
500
+ headSha: finalRun.headSha ?? match.headSha ?? null,
501
+ conclusion: finalRun.conclusion ?? match.conclusion ?? null,
502
+ url: finalRun.url ?? match.url ?? null
503
+ };
504
+ }
505
+ sleepSeconds(pollSeconds);
506
+ }
507
+ throw new Error(`Timed out waiting for GitHub workflow ${workflow} in ${repo}.`);
508
+ }
294
509
  export {
510
+ createGitHubRepository,
295
511
  ensureDeployWorkflow,
296
512
  ensureGitHubDeployAutomation,
297
513
  ensureGitHubEnvironment,
@@ -300,6 +516,7 @@ export {
300
516
  ensureStandardizedGitHubWorkflows,
301
517
  formatMissingSecretsReport,
302
518
  getGitHubAutomationMode,
519
+ initializeGitHubRepositoryWorkingTree,
303
520
  listGitHubSecretNames,
304
521
  listGitHubVariableNames,
305
522
  maybeResolveGitHubRepositorySlug,
@@ -308,6 +525,8 @@ export {
308
525
  renderHostedProjectWorkflow,
309
526
  requiredGitHubEnvironment,
310
527
  requiredGitHubSecrets,
528
+ resolveDefaultGitHubOwner,
311
529
  resolveGitHubRepositorySlug,
312
- resolveGitRepositoryRoot
530
+ resolveGitRepositoryRoot,
531
+ waitForGitHubWorkflowCompletion
313
532
  };