@harness-lab/cli 0.2.1 → 0.2.3

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 (62) hide show
  1. package/README.md +15 -5
  2. package/assets/workshop-bundle/SKILL.md +306 -0
  3. package/assets/workshop-bundle/bundle-manifest.json +232 -0
  4. package/assets/workshop-bundle/content/challenge-cards/.gitkeep +0 -0
  5. package/assets/workshop-bundle/content/challenge-cards/deck.md +38 -0
  6. package/assets/workshop-bundle/content/challenge-cards/locales/en/deck.md +38 -0
  7. package/assets/workshop-bundle/content/challenge-cards/print-spec.md +28 -0
  8. package/assets/workshop-bundle/content/czech-editorial-review-checklist.md +88 -0
  9. package/assets/workshop-bundle/content/facilitation/.gitkeep +0 -0
  10. package/assets/workshop-bundle/content/facilitation/codex-setup-verification.md +54 -0
  11. package/assets/workshop-bundle/content/facilitation/master-guide.md +131 -0
  12. package/assets/workshop-bundle/content/project-briefs/.gitkeep +0 -0
  13. package/assets/workshop-bundle/content/project-briefs/code-review-helper.md +31 -0
  14. package/assets/workshop-bundle/content/project-briefs/devtoolbox-cli.md +31 -0
  15. package/assets/workshop-bundle/content/project-briefs/doc-generator.md +31 -0
  16. package/assets/workshop-bundle/content/project-briefs/locales/en/code-review-helper.md +31 -0
  17. package/assets/workshop-bundle/content/project-briefs/locales/en/devtoolbox-cli.md +31 -0
  18. package/assets/workshop-bundle/content/project-briefs/locales/en/doc-generator.md +31 -0
  19. package/assets/workshop-bundle/content/project-briefs/locales/en/metrics-dashboard.md +31 -0
  20. package/assets/workshop-bundle/content/project-briefs/locales/en/standup-bot.md +31 -0
  21. package/assets/workshop-bundle/content/project-briefs/metrics-dashboard.md +31 -0
  22. package/assets/workshop-bundle/content/project-briefs/standup-bot.md +31 -0
  23. package/assets/workshop-bundle/content/style-examples.md +127 -0
  24. package/assets/workshop-bundle/content/style-guide.md +108 -0
  25. package/assets/workshop-bundle/content/talks/.gitkeep +0 -0
  26. package/assets/workshop-bundle/content/talks/codex-demo-script.md +43 -0
  27. package/assets/workshop-bundle/content/talks/context-is-king.md +42 -0
  28. package/assets/workshop-bundle/docs/harness-cli-foundation.md +143 -0
  29. package/assets/workshop-bundle/docs/learner-reference-gallery.md +82 -0
  30. package/assets/workshop-bundle/docs/learner-resource-kit.md +126 -0
  31. package/assets/workshop-bundle/docs/locales/en/learner-reference-gallery.md +82 -0
  32. package/assets/workshop-bundle/docs/locales/en/learner-resource-kit.md +126 -0
  33. package/assets/workshop-bundle/docs/workshop-event-context-contract.md +123 -0
  34. package/assets/workshop-bundle/materials/locales/en/participant-resource-kit.md +72 -0
  35. package/assets/workshop-bundle/materials/participant-resource-kit.md +72 -0
  36. package/assets/workshop-bundle/workshop-blueprint/README.md +55 -0
  37. package/assets/workshop-bundle/workshop-blueprint/agenda.json +70 -0
  38. package/assets/workshop-bundle/workshop-blueprint/control-surfaces.md +101 -0
  39. package/assets/workshop-bundle/workshop-blueprint/day-structure.md +129 -0
  40. package/assets/workshop-bundle/workshop-blueprint/edit-boundaries.md +64 -0
  41. package/assets/workshop-bundle/workshop-blueprint/operator-guide.md +74 -0
  42. package/assets/workshop-bundle/workshop-blueprint/teaching-spine.md +134 -0
  43. package/assets/workshop-bundle/workshop-skill/.gitkeep +0 -0
  44. package/assets/workshop-bundle/workshop-skill/analyze-checklist.md +21 -0
  45. package/assets/workshop-bundle/workshop-skill/closing-skill.md +30 -0
  46. package/assets/workshop-bundle/workshop-skill/commands.md +44 -0
  47. package/assets/workshop-bundle/workshop-skill/facilitator.md +426 -0
  48. package/assets/workshop-bundle/workshop-skill/follow-up-package.md +35 -0
  49. package/assets/workshop-bundle/workshop-skill/install.md +58 -0
  50. package/assets/workshop-bundle/workshop-skill/locales/en/commands.md +44 -0
  51. package/assets/workshop-bundle/workshop-skill/locales/en/follow-up-package.md +35 -0
  52. package/assets/workshop-bundle/workshop-skill/locales/en/recap.md +22 -0
  53. package/assets/workshop-bundle/workshop-skill/locales/en/reference.md +80 -0
  54. package/assets/workshop-bundle/workshop-skill/locales/en/setup.md +84 -0
  55. package/assets/workshop-bundle/workshop-skill/recap.md +22 -0
  56. package/assets/workshop-bundle/workshop-skill/reference.md +80 -0
  57. package/assets/workshop-bundle/workshop-skill/setup.md +84 -0
  58. package/assets/workshop-bundle/workshop-skill/template-agents.md +35 -0
  59. package/package.json +8 -3
  60. package/src/run-cli.js +16 -9
  61. package/src/skill-install.js +98 -57
  62. package/src/workshop-bundle.js +236 -0
@@ -0,0 +1,236 @@
1
+ import fs from "node:fs/promises";
2
+ import crypto from "node:crypto";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ export const WORKSHOP_SKILL_NAME = "harness-lab-workshop";
7
+ export const WORKSHOP_BUNDLE_MANIFEST = "bundle-manifest.json";
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ const packageRoot = path.resolve(__dirname, "..");
12
+
13
+ const DIRECTORY_COPIES = [
14
+ ["workshop-skill", "workshop-skill"],
15
+ ["content", "content"],
16
+ ["workshop-blueprint", "workshop-blueprint"],
17
+ ];
18
+
19
+ const FILE_COPIES = [
20
+ ["workshop-skill/SKILL.md", "SKILL.md"],
21
+ ["docs/workshop-event-context-contract.md", "docs/workshop-event-context-contract.md"],
22
+ ["docs/harness-cli-foundation.md", "docs/harness-cli-foundation.md"],
23
+ ["docs/learner-resource-kit.md", "docs/learner-resource-kit.md"],
24
+ ["docs/locales/en/learner-resource-kit.md", "docs/locales/en/learner-resource-kit.md"],
25
+ ["docs/learner-reference-gallery.md", "docs/learner-reference-gallery.md"],
26
+ ["docs/locales/en/learner-reference-gallery.md", "docs/locales/en/learner-reference-gallery.md"],
27
+ ["materials/participant-resource-kit.md", "materials/participant-resource-kit.md"],
28
+ ["materials/locales/en/participant-resource-kit.md", "materials/locales/en/participant-resource-kit.md"],
29
+ ];
30
+
31
+ export function getPackageRoot() {
32
+ return packageRoot;
33
+ }
34
+
35
+ export function getPackagedWorkshopBundlePath() {
36
+ return path.join(packageRoot, "assets", "workshop-bundle");
37
+ }
38
+
39
+ export function getRepoWorkshopSourceRoot() {
40
+ return path.resolve(packageRoot, "..");
41
+ }
42
+
43
+ export function getRepoBundledWorkshopSkillPath() {
44
+ return path.join(getRepoWorkshopSourceRoot(), ".agents", "skills", WORKSHOP_SKILL_NAME);
45
+ }
46
+
47
+ export function getInstalledSkillPath(targetRoot) {
48
+ return path.join(targetRoot, ".agents", "skills", WORKSHOP_SKILL_NAME);
49
+ }
50
+
51
+ export function getBundleManifestPath(bundleRoot) {
52
+ return path.join(bundleRoot, WORKSHOP_BUNDLE_MANIFEST);
53
+ }
54
+
55
+ export async function pathExists(targetPath) {
56
+ try {
57
+ await fs.access(targetPath);
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ async function copyDirectoryTree(sourceRoot, targetRoot) {
65
+ for (const [sourceRelativePath, targetRelativePath] of DIRECTORY_COPIES) {
66
+ await fs.cp(path.join(sourceRoot, sourceRelativePath), path.join(targetRoot, targetRelativePath), { recursive: true });
67
+ }
68
+ }
69
+
70
+ async function copyBundleFiles(sourceRoot, targetRoot) {
71
+ for (const [sourceRelativePath, targetRelativePath] of FILE_COPIES) {
72
+ const targetPath = path.join(targetRoot, targetRelativePath);
73
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
74
+ await fs.copyFile(path.join(sourceRoot, sourceRelativePath), targetPath);
75
+ }
76
+ }
77
+
78
+ function normalizePathForManifest(targetPath) {
79
+ return targetPath.split(path.sep).join("/");
80
+ }
81
+
82
+ async function listFilesRecursive(rootPath) {
83
+ const entries = [];
84
+
85
+ async function walk(currentPath, relativePrefix = "") {
86
+ const dirEntries = await fs.readdir(currentPath, { withFileTypes: true });
87
+ dirEntries.sort((left, right) => left.name.localeCompare(right.name));
88
+
89
+ for (const entry of dirEntries) {
90
+ const entryRelativePath = relativePrefix ? path.join(relativePrefix, entry.name) : entry.name;
91
+ const entryPath = path.join(currentPath, entry.name);
92
+
93
+ if (entry.isDirectory()) {
94
+ await walk(entryPath, entryRelativePath);
95
+ continue;
96
+ }
97
+
98
+ if (entry.isFile()) {
99
+ entries.push({
100
+ absolutePath: entryPath,
101
+ relativePath: normalizePathForManifest(entryRelativePath),
102
+ });
103
+ }
104
+ }
105
+ }
106
+
107
+ await walk(rootPath);
108
+ return entries;
109
+ }
110
+
111
+ async function readPackageVersion() {
112
+ const packageJson = JSON.parse(await fs.readFile(path.join(packageRoot, "package.json"), "utf8"));
113
+ return String(packageJson.version);
114
+ }
115
+
116
+ async function createManifestFromEntries(entries) {
117
+ const bundleVersion = await readPackageVersion();
118
+ const files = [];
119
+
120
+ for (const entry of entries) {
121
+ const contents = await fs.readFile(entry.absolutePath);
122
+ const sha256 = crypto.createHash("sha256").update(contents).digest("hex");
123
+ files.push({
124
+ path: entry.relativePath,
125
+ sha256,
126
+ });
127
+ }
128
+
129
+ files.sort((left, right) => left.path.localeCompare(right.path));
130
+
131
+ const contentHash = crypto
132
+ .createHash("sha256")
133
+ .update(
134
+ files
135
+ .map((file) => `${file.path}:${file.sha256}`)
136
+ .join("\n"),
137
+ )
138
+ .digest("hex");
139
+
140
+ return {
141
+ manifestVersion: 1,
142
+ bundleName: WORKSHOP_SKILL_NAME,
143
+ bundleVersion,
144
+ contentHash,
145
+ files,
146
+ };
147
+ }
148
+
149
+ export async function createWorkshopBundleManifestFromDirectory(bundleRoot) {
150
+ const files = await listFilesRecursive(bundleRoot);
151
+ return createManifestFromEntries(
152
+ files.filter((file) => file.relativePath !== WORKSHOP_BUNDLE_MANIFEST),
153
+ );
154
+ }
155
+
156
+ export async function createWorkshopBundleManifestFromSource(sourceRoot) {
157
+ const entries = [];
158
+
159
+ for (const [sourceRelativePath, targetRelativePath] of DIRECTORY_COPIES) {
160
+ const sourceDirectory = path.join(sourceRoot, sourceRelativePath);
161
+ const sourceFiles = await listFilesRecursive(sourceDirectory);
162
+
163
+ for (const file of sourceFiles) {
164
+ const targetRelative = normalizePathForManifest(path.join(targetRelativePath, file.relativePath));
165
+ if (targetRelative === "workshop-skill/SKILL.md") {
166
+ continue;
167
+ }
168
+
169
+ entries.push({
170
+ absolutePath: file.absolutePath,
171
+ relativePath: targetRelative,
172
+ });
173
+ }
174
+ }
175
+
176
+ for (const [sourceRelativePath, targetRelativePath] of FILE_COPIES) {
177
+ entries.push({
178
+ absolutePath: path.join(sourceRoot, sourceRelativePath),
179
+ relativePath: normalizePathForManifest(targetRelativePath),
180
+ });
181
+ }
182
+
183
+ return createManifestFromEntries(entries);
184
+ }
185
+
186
+ export async function readWorkshopBundleManifest(bundleRoot) {
187
+ try {
188
+ const manifest = JSON.parse(await fs.readFile(getBundleManifestPath(bundleRoot), "utf8"));
189
+ if (!manifest || typeof manifest.contentHash !== "string") {
190
+ return null;
191
+ }
192
+ return manifest;
193
+ } catch {
194
+ return null;
195
+ }
196
+ }
197
+
198
+ async function writeWorkshopBundleManifest(bundleRoot, manifest) {
199
+ await fs.writeFile(
200
+ getBundleManifestPath(bundleRoot),
201
+ `${JSON.stringify(manifest, null, 2)}\n`,
202
+ "utf8",
203
+ );
204
+ }
205
+
206
+ export async function createWorkshopBundleFromSource(sourceRoot, targetRoot, options = {}) {
207
+ if (options.clean === true) {
208
+ await fs.rm(targetRoot, { recursive: true, force: true });
209
+ }
210
+ await fs.mkdir(targetRoot, { recursive: true });
211
+ await copyDirectoryTree(sourceRoot, targetRoot);
212
+ await fs.rm(path.join(targetRoot, "workshop-skill", "SKILL.md"), { force: true });
213
+ await copyBundleFiles(sourceRoot, targetRoot);
214
+ const manifest = await createWorkshopBundleManifestFromSource(sourceRoot);
215
+ await writeWorkshopBundleManifest(targetRoot, manifest);
216
+ }
217
+
218
+ export async function syncPackagedWorkshopBundle() {
219
+ const sourceRoot = getRepoWorkshopSourceRoot();
220
+ const bundleRoot = getPackagedWorkshopBundlePath();
221
+ await createWorkshopBundleFromSource(sourceRoot, bundleRoot, { clean: true });
222
+ return {
223
+ sourceRoot,
224
+ bundleRoot,
225
+ };
226
+ }
227
+
228
+ export async function syncRepoBundledWorkshopSkill() {
229
+ const sourceRoot = getRepoWorkshopSourceRoot();
230
+ const bundleRoot = getRepoBundledWorkshopSkillPath();
231
+ await createWorkshopBundleFromSource(sourceRoot, bundleRoot, { clean: true });
232
+ return {
233
+ sourceRoot,
234
+ bundleRoot,
235
+ };
236
+ }