@harness-lab/cli 0.4.0 → 0.4.2

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifestVersion": 1,
3
3
  "bundleName": "harness-lab-workshop",
4
- "bundleVersion": "0.4.0",
4
+ "bundleVersion": "0.4.2",
5
5
  "contentHash": "99a5b8475b6a4c11bc0652784d160a3a9d8fa3cfe38595c21bbe71d48159b3a4",
6
6
  "files": [
7
7
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harness-lab/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Participant-facing Harness Lab CLI for facilitator auth and workshop operations",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/run-cli.js CHANGED
@@ -305,18 +305,23 @@ async function handleSkillInstall(io, ui, deps, flags) {
305
305
  ui.status("ok", "Installed the Harness Lab workshop skill bundle.");
306
306
  }
307
307
  ui.keyValue("Target", result.targetRoot);
308
- ui.keyValue("Location", result.installPath);
309
- ui.keyValue("Discovery", ".agents/skills");
308
+ ui.keyValue("Codex/pi", result.installPath);
309
+ if (result.claudeCodePath) {
310
+ ui.keyValue("Claude Code", result.claudeCodePath);
311
+ }
310
312
  ui.keyValue("Bundle source", result.sourceMode === "packaged_bundle" ? "packaged portable bundle" : "source checkout fallback");
311
313
  ui.blank();
312
314
  ui.section("Next steps");
313
- ui.numberedList([
314
- "Open Codex or pi in the target repo.",
315
- "Start with the workshop command menu.",
315
+ const steps = [
316
+ "Open your coding agent in the target repo.",
316
317
  "Codex: `$workshop commands`.",
317
318
  "pi: `/skill:workshop`, then ask for the workshop commands.",
318
- "Next: `$workshop reference`, `$workshop brief`, and `$workshop resources`.",
319
- ]);
319
+ ];
320
+ if (result.claudeCodePath) {
321
+ steps.push("Claude Code: the `workshop` skill is available as a slash command.");
322
+ }
323
+ steps.push("Next: `$workshop reference`, `$workshop brief`, and `$workshop resources`.");
324
+ ui.numberedList(steps);
320
325
  return 0;
321
326
  } catch (error) {
322
327
  if (error instanceof SkillInstallError) {
@@ -879,11 +884,19 @@ async function handleWorkshopShowInstance(io, ui, env, positionals, flags, deps)
879
884
 
880
885
  try {
881
886
  const client = createHarnessClient({ fetchFn: deps.fetchFn, session });
882
- const result = await client.getWorkshopInstance(instanceId);
887
+ const [result, agenda] = await Promise.all([
888
+ client.getWorkshopInstance(instanceId),
889
+ client.getWorkshopAgenda(instanceId).catch(() => null),
890
+ ]);
883
891
  ui.json("Workshop Instance", {
884
892
  ok: true,
885
893
  ...summarizeWorkshopInstance(result.instance),
886
894
  instance: result.instance,
895
+ contentSummary: agenda ? {
896
+ phases: Array.isArray(agenda.items) ? agenda.items.length : 0,
897
+ scenes: Array.isArray(agenda.items) ? agenda.items.reduce((sum, item) => sum + (item.presenterScenes?.length ?? 0), 0) : 0,
898
+ currentPhase: agenda.phase ?? null,
899
+ } : null,
887
900
  });
888
901
  return 0;
889
902
  } catch (error) {
@@ -1118,6 +1131,10 @@ async function handleWorkshopResetInstance(io, ui, env, positionals, flags, deps
1118
1131
  readStringFlag(flags, "template-id", "template"),
1119
1132
  );
1120
1133
  ui.json("Workshop Reset Instance", result);
1134
+ if (result.contentSummary) {
1135
+ const s = result.contentSummary;
1136
+ ui.status("ok", `Reset ${instanceId}: ${s.phases} phases, ${s.scenes} scenes, ${s.briefs} briefs, ${s.challenges} challenges`);
1137
+ }
1121
1138
  return 0;
1122
1139
  } catch (error) {
1123
1140
  if (error instanceof HarnessApiError) {
@@ -1338,7 +1355,7 @@ export async function runCli(argv, io, deps = {}) {
1338
1355
 
1339
1356
  if (scope === "skill") {
1340
1357
  ui.heading("Harness CLI — Skill");
1341
- ui.paragraph("Install the workshop skill into your project repo.");
1358
+ ui.paragraph("Install the workshop skill into your project repo for Codex, pi, and Claude Code.");
1342
1359
  ui.blank();
1343
1360
  ui.section("Commands");
1344
1361
  ui.commandList([
@@ -1347,11 +1364,10 @@ export async function runCli(argv, io, deps = {}) {
1347
1364
  ui.blank();
1348
1365
  ui.section("After install");
1349
1366
  ui.commandList([
1350
- "Open Codex or pi in the same repo",
1351
- "Run: $workshop commands",
1352
- "Run: $workshop reference",
1353
- "Run: $workshop brief",
1354
- "Run: $workshop help",
1367
+ "Open your coding agent (Codex, pi, or Claude Code) in the same repo",
1368
+ "Codex: $workshop commands",
1369
+ "pi: /skill:workshop",
1370
+ "Claude Code: the workshop skill is available as a slash command",
1355
1371
  ]);
1356
1372
  ui.blank();
1357
1373
  ui.paragraph("The skill is the participant's primary workshop interface.");
@@ -6,6 +6,7 @@ import {
6
6
  createWorkshopBundleManifestFromSource,
7
7
  createWorkshopBundleFromSource,
8
8
  getInstalledSkillPath,
9
+ getClaudeCodeSkillPath,
9
10
  getPackagedWorkshopBundlePath,
10
11
  getRepoWorkshopSourceRoot,
11
12
  pathExists,
@@ -189,12 +190,15 @@ export async function installWorkshopSkill(startDir, options = {}) {
189
190
  if (existingInstall && options.force !== true) {
190
191
  const installedManifest = await createWorkshopBundleManifestFromDirectory(installPath);
191
192
  if (installedManifest.contentHash === sourceManifest.contentHash) {
193
+ // Agents bundle is current but Claude Code target may be missing
194
+ const claudeInstalled = await installClaudeCodeSkill(installPath, targetRoot);
192
195
  return {
193
196
  installPath,
194
197
  skillName: WORKSHOP_SKILL_NAME,
195
198
  mode: "already_current",
196
199
  sourceMode: resolvedBundle.mode,
197
200
  targetRoot,
201
+ claudeCodePath: claudeInstalled,
198
202
  };
199
203
  }
200
204
 
@@ -234,11 +238,36 @@ async function installFreshBundle(resolvedBundle, installPath, targetRoot) {
234
238
  );
235
239
  await installFromResolvedBundle(resolvedBundle, installPath);
236
240
 
241
+ const claudeInstalled = await installClaudeCodeSkill(installPath, targetRoot);
242
+
237
243
  return {
238
244
  installPath,
239
245
  skillName: WORKSHOP_SKILL_NAME,
240
246
  mode: "installed",
241
247
  sourceMode: resolvedBundle.mode,
242
248
  targetRoot,
249
+ claudeCodePath: claudeInstalled,
243
250
  };
244
251
  }
252
+
253
+ async function installClaudeCodeSkill(agentsInstallPath, targetRoot) {
254
+ const claudePath = getClaudeCodeSkillPath(targetRoot);
255
+ try {
256
+ await fs.mkdir(claudePath, { recursive: true });
257
+ // Copy the participant SKILL.md as the Claude Code entry point
258
+ await fs.copyFile(
259
+ path.join(agentsInstallPath, "SKILL.md"),
260
+ path.join(claudePath, "SKILL.md"),
261
+ );
262
+ // Copy the workshop-skill support files that the skill references
263
+ const workshopSkillSource = path.join(agentsInstallPath, "workshop-skill");
264
+ const workshopSkillTarget = path.join(claudePath, "workshop-skill");
265
+ if (await pathExists(workshopSkillSource)) {
266
+ await fs.cp(workshopSkillSource, workshopSkillTarget, { recursive: true });
267
+ }
268
+ return claudePath;
269
+ } catch {
270
+ // Claude Code install is best-effort — don't fail the main install
271
+ return null;
272
+ }
273
+ }
@@ -66,6 +66,10 @@ export function getInstalledSkillPath(targetRoot) {
66
66
  return path.join(targetRoot, ".agents", "skills", WORKSHOP_SKILL_NAME);
67
67
  }
68
68
 
69
+ export function getClaudeCodeSkillPath(targetRoot) {
70
+ return path.join(targetRoot, ".claude", "skills", "workshop");
71
+ }
72
+
69
73
  export function getBundleManifestPath(bundleRoot) {
70
74
  return path.join(bundleRoot, WORKSHOP_BUNDLE_MANIFEST);
71
75
  }