Package not found. Please check the package name and try again.

@harness-engineering/cli 1.8.2 → 1.10.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 (92) hide show
  1. package/dist/agents/skills/claude-code/cleanup-dead-code/SKILL.md +3 -3
  2. package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +20 -3
  3. package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +55 -5
  4. package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +36 -15
  5. package/dist/agents/skills/claude-code/harness-codebase-cleanup/SKILL.md +1 -1
  6. package/dist/agents/skills/claude-code/harness-execution/SKILL.md +70 -13
  7. package/dist/agents/skills/claude-code/harness-planning/SKILL.md +41 -3
  8. package/dist/agents/skills/claude-code/harness-pre-commit-review/SKILL.md +28 -3
  9. package/dist/agents/skills/claude-code/harness-release-readiness/SKILL.md +14 -2
  10. package/dist/agents/skills/claude-code/harness-verification/SKILL.md +18 -2
  11. package/dist/agents/skills/gemini-cli/cleanup-dead-code/SKILL.md +3 -3
  12. package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +20 -3
  13. package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +55 -5
  14. package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +36 -15
  15. package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/SKILL.md +1 -1
  16. package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +70 -13
  17. package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +41 -3
  18. package/dist/agents/skills/gemini-cli/harness-pre-commit-review/SKILL.md +28 -3
  19. package/dist/agents/skills/gemini-cli/harness-release-readiness/SKILL.md +14 -2
  20. package/dist/agents/skills/gemini-cli/harness-verification/SKILL.md +18 -2
  21. package/dist/agents-md-EMRFLNBC.js +8 -0
  22. package/dist/architecture-5JNN5L3M.js +13 -0
  23. package/dist/bin/harness-mcp.d.ts +1 -0
  24. package/dist/bin/harness-mcp.js +28 -0
  25. package/dist/bin/harness.js +42 -8
  26. package/dist/check-phase-gate-WOKIYGAM.js +12 -0
  27. package/dist/chunk-46YA6FI3.js +293 -0
  28. package/dist/chunk-4PFMY3H7.js +248 -0
  29. package/dist/{chunk-LB4GRDDV.js → chunk-72GHBOL2.js} +1 -1
  30. package/dist/chunk-7X7ZAYMY.js +373 -0
  31. package/dist/chunk-B7HFEHWP.js +35 -0
  32. package/dist/chunk-BM3PWGXQ.js +14 -0
  33. package/dist/chunk-C2ERUR3L.js +255 -0
  34. package/dist/chunk-CWZ4Y2PO.js +189 -0
  35. package/dist/{chunk-ULSRSP53.js → chunk-ECUJQS3B.js} +11 -112
  36. package/dist/chunk-EOLRW32Q.js +72 -0
  37. package/dist/chunk-F3YDAJFQ.js +125 -0
  38. package/dist/chunk-F4PTVZWA.js +116 -0
  39. package/dist/chunk-FPIPT36X.js +187 -0
  40. package/dist/chunk-FX7SQHGD.js +103 -0
  41. package/dist/chunk-HIOXKZYF.js +15 -0
  42. package/dist/chunk-IDZNPTYD.js +16 -0
  43. package/dist/chunk-JSTQ3AWB.js +31 -0
  44. package/dist/chunk-K6XAPGML.js +27 -0
  45. package/dist/chunk-KET4QQZB.js +8 -0
  46. package/dist/chunk-LXU5M77O.js +4028 -0
  47. package/dist/chunk-MDUK2J2O.js +67 -0
  48. package/dist/chunk-MHBMTPW7.js +29 -0
  49. package/dist/chunk-MO4YQOMB.js +85 -0
  50. package/dist/chunk-NKDM3FMH.js +52 -0
  51. package/dist/{chunk-SAB3VXOW.js → chunk-NX6DSZSM.js} +144 -111
  52. package/dist/chunk-OPXH4CQN.js +62 -0
  53. package/dist/{chunk-Y7U5AYAL.js → chunk-PAHHT2IK.js} +471 -2719
  54. package/dist/chunk-PMTFPOCT.js +122 -0
  55. package/dist/chunk-PSXF277V.js +89 -0
  56. package/dist/chunk-Q6AB7W5Z.js +135 -0
  57. package/dist/chunk-QPEH2QPG.js +347 -0
  58. package/dist/chunk-TEFCFC4H.js +15 -0
  59. package/dist/chunk-TRAPF4IX.js +185 -0
  60. package/dist/chunk-VUCPTQ6G.js +67 -0
  61. package/dist/chunk-W6Y7ZW3Y.js +13 -0
  62. package/dist/chunk-ZOAWBDWU.js +72 -0
  63. package/dist/ci-workflow-ZBBUNTHQ.js +8 -0
  64. package/dist/constants-5JGUXPEK.js +6 -0
  65. package/dist/create-skill-LUWO46WF.js +11 -0
  66. package/dist/dist-D4RYGUZE.js +14 -0
  67. package/dist/{dist-K6KTTN3I.js → dist-I7DB5VKB.js} +237 -0
  68. package/dist/dist-L7LAAQAS.js +18 -0
  69. package/dist/{dist-ZODQVGC4.js → dist-PBTNVK6K.js} +8 -6
  70. package/dist/docs-PTJGD6XI.js +12 -0
  71. package/dist/engine-SCMZ3G3E.js +8 -0
  72. package/dist/entropy-YIUBGKY7.js +12 -0
  73. package/dist/feedback-WEVQSLAA.js +18 -0
  74. package/dist/generate-agent-definitions-BU5LOJTI.js +15 -0
  75. package/dist/glob-helper-5OHBUQAI.js +52 -0
  76. package/dist/graph-loader-RLO3KRIX.js +8 -0
  77. package/dist/index.d.ts +11 -1
  78. package/dist/index.js +84 -33
  79. package/dist/loader-6S6PVGSF.js +10 -0
  80. package/dist/mcp-BNLBTCXZ.js +34 -0
  81. package/dist/performance-5TVW6SA6.js +24 -0
  82. package/dist/review-pipeline-4JTQAWKW.js +9 -0
  83. package/dist/runner-VMYLHWOC.js +6 -0
  84. package/dist/runtime-PXIM7UV6.js +9 -0
  85. package/dist/security-URYTKLGK.js +9 -0
  86. package/dist/skill-executor-KVS47DAU.js +8 -0
  87. package/dist/validate-KSDUUK2M.js +12 -0
  88. package/dist/validate-cross-check-WZAX357V.js +8 -0
  89. package/dist/version-KFFPOQAX.js +6 -0
  90. package/package.json +7 -5
  91. package/dist/create-skill-UZOHMXRU.js +0 -8
  92. package/dist/validate-cross-check-DLNK423G.js +0 -7
@@ -1,9 +1,80 @@
1
+ import {
2
+ generateCIWorkflow
3
+ } from "./chunk-VUCPTQ6G.js";
4
+ import {
5
+ OutputFormatter,
6
+ OutputMode,
7
+ createCheckPhaseGateCommand,
8
+ findFiles,
9
+ resolveConfig
10
+ } from "./chunk-7X7ZAYMY.js";
11
+ import {
12
+ createGenerateAgentDefinitionsCommand,
13
+ generateAgentDefinitions
14
+ } from "./chunk-46YA6FI3.js";
15
+ import {
16
+ listPersonas,
17
+ loadPersona
18
+ } from "./chunk-Q6AB7W5Z.js";
19
+ import {
20
+ runPersona
21
+ } from "./chunk-TRAPF4IX.js";
22
+ import {
23
+ executeSkill
24
+ } from "./chunk-F3YDAJFQ.js";
25
+ import {
26
+ ALLOWED_PERSONA_COMMANDS
27
+ } from "./chunk-TEFCFC4H.js";
28
+ import {
29
+ createCreateSkillCommand
30
+ } from "./chunk-ECUJQS3B.js";
31
+ import {
32
+ logger
33
+ } from "./chunk-HIOXKZYF.js";
34
+ import {
35
+ generate,
36
+ validate
37
+ } from "./chunk-QPEH2QPG.js";
38
+ import {
39
+ generateRuntime
40
+ } from "./chunk-JSTQ3AWB.js";
41
+ import {
42
+ toKebabCase
43
+ } from "./chunk-KET4QQZB.js";
44
+ import {
45
+ generateAgentsMd
46
+ } from "./chunk-NKDM3FMH.js";
47
+ import {
48
+ createGenerateSlashCommandsCommand,
49
+ generateSlashCommands,
50
+ handleOrphanDeletion
51
+ } from "./chunk-LXU5M77O.js";
52
+ import {
53
+ VALID_PLATFORMS
54
+ } from "./chunk-ZOAWBDWU.js";
55
+ import {
56
+ resolvePersonasDir,
57
+ resolveSkillsDir,
58
+ resolveTemplatesDir
59
+ } from "./chunk-EOLRW32Q.js";
60
+ import {
61
+ CLIError,
62
+ ExitCode,
63
+ handleError
64
+ } from "./chunk-B7HFEHWP.js";
65
+ import {
66
+ SkillMetadataSchema
67
+ } from "./chunk-MDUK2J2O.js";
68
+ import {
69
+ CLI_VERSION
70
+ } from "./chunk-BM3PWGXQ.js";
71
+ import {
72
+ TemplateEngine
73
+ } from "./chunk-C2ERUR3L.js";
1
74
  import {
2
75
  BaselineManager,
3
76
  CriticalPathResolver,
4
77
  EntropyAnalyzer,
5
- Err,
6
- Ok,
7
78
  SecurityScanner,
8
79
  TypeScriptParser,
9
80
  appendLearning,
@@ -31,277 +102,25 @@ import {
31
102
  validateAgentsMap,
32
103
  validateDependencies,
33
104
  validateKnowledgeMap
34
- } from "./chunk-SAB3VXOW.js";
105
+ } from "./chunk-NX6DSZSM.js";
35
106
  import {
36
- CLIError,
37
- ExitCode,
38
- SkillMetadataSchema,
39
- createCreateSkillCommand,
40
- handleError,
41
- logger
42
- } from "./chunk-ULSRSP53.js";
107
+ Err,
108
+ Ok
109
+ } from "./chunk-MHBMTPW7.js";
43
110
 
44
111
  // src/index.ts
45
- import { Command as Command43 } from "commander";
46
-
47
- // src/version.ts
48
- import { createRequire } from "module";
49
- var require_ = createRequire(import.meta.url);
50
- var resolved;
51
- try {
52
- resolved = require_("../package.json").version ?? "0.0.0";
53
- } catch {
54
- resolved = "0.0.0";
55
- }
56
- var CLI_VERSION = resolved;
112
+ import { Command as Command41 } from "commander";
57
113
 
58
114
  // src/commands/validate.ts
59
115
  import { Command } from "commander";
60
- import * as path2 from "path";
61
-
62
- // src/config/loader.ts
63
- import * as fs from "fs";
64
116
  import * as path from "path";
65
-
66
- // src/config/schema.ts
67
- import { z } from "zod";
68
- var LayerSchema = z.object({
69
- name: z.string(),
70
- pattern: z.string(),
71
- allowedDependencies: z.array(z.string())
72
- });
73
- var ForbiddenImportSchema = z.object({
74
- from: z.string(),
75
- disallow: z.array(z.string()),
76
- message: z.string().optional()
77
- });
78
- var BoundaryConfigSchema = z.object({
79
- requireSchema: z.array(z.string())
80
- });
81
- var AgentConfigSchema = z.object({
82
- executor: z.enum(["subprocess", "cloud", "noop"]).default("subprocess"),
83
- timeout: z.number().default(3e5),
84
- skills: z.array(z.string()).optional()
85
- });
86
- var EntropyConfigSchema = z.object({
87
- excludePatterns: z.array(z.string()).default(["**/node_modules/**", "**/*.test.ts"]),
88
- autoFix: z.boolean().default(false)
89
- });
90
- var PhaseGateMappingSchema = z.object({
91
- implPattern: z.string(),
92
- specPattern: z.string()
93
- });
94
- var PhaseGatesConfigSchema = z.object({
95
- enabled: z.boolean().default(false),
96
- severity: z.enum(["error", "warning"]).default("error"),
97
- mappings: z.array(PhaseGateMappingSchema).default([{ implPattern: "src/**/*.ts", specPattern: "docs/changes/{feature}/proposal.md" }])
98
- });
99
- var SecurityConfigSchema = z.object({
100
- enabled: z.boolean().default(true),
101
- strict: z.boolean().default(false),
102
- rules: z.record(z.string(), z.enum(["off", "error", "warning", "info"])).optional(),
103
- exclude: z.array(z.string()).optional()
104
- }).passthrough();
105
- var PerformanceConfigSchema = z.object({
106
- complexity: z.record(z.unknown()).optional(),
107
- coupling: z.record(z.unknown()).optional(),
108
- sizeBudget: z.record(z.unknown()).optional()
109
- }).passthrough();
110
- var DesignConfigSchema = z.object({
111
- strictness: z.enum(["strict", "standard", "permissive"]).default("standard"),
112
- platforms: z.array(z.enum(["web", "mobile"])).default([]),
113
- tokenPath: z.string().optional(),
114
- aestheticIntent: z.string().optional()
115
- });
116
- var I18nCoverageConfigSchema = z.object({
117
- minimumPercent: z.number().min(0).max(100).default(100),
118
- requirePlurals: z.boolean().default(true),
119
- detectUntranslated: z.boolean().default(true)
120
- });
121
- var I18nMcpConfigSchema = z.object({
122
- server: z.string(),
123
- projectId: z.string().optional()
124
- });
125
- var I18nConfigSchema = z.object({
126
- enabled: z.boolean().default(false),
127
- strictness: z.enum(["strict", "standard", "permissive"]).default("standard"),
128
- sourceLocale: z.string().default("en"),
129
- targetLocales: z.array(z.string()).default([]),
130
- framework: z.enum([
131
- "auto",
132
- "i18next",
133
- "react-intl",
134
- "vue-i18n",
135
- "flutter-intl",
136
- "apple",
137
- "android",
138
- "custom"
139
- ]).default("auto"),
140
- format: z.string().default("json"),
141
- messageFormat: z.enum(["icu", "i18next", "custom"]).default("icu"),
142
- keyConvention: z.enum(["dot-notation", "snake_case", "camelCase", "custom"]).default("dot-notation"),
143
- translationPaths: z.record(z.string(), z.string()).optional(),
144
- platforms: z.array(z.enum(["web", "mobile", "backend"])).default([]),
145
- industry: z.string().optional(),
146
- coverage: I18nCoverageConfigSchema.optional(),
147
- pseudoLocale: z.string().optional(),
148
- mcp: I18nMcpConfigSchema.optional()
149
- });
150
- var ModelTierConfigSchema = z.object({
151
- fast: z.string().optional(),
152
- standard: z.string().optional(),
153
- strong: z.string().optional()
154
- });
155
- var ReviewConfigSchema = z.object({
156
- model_tiers: ModelTierConfigSchema.optional()
157
- });
158
- var HarnessConfigSchema = z.object({
159
- version: z.literal(1),
160
- name: z.string().optional(),
161
- rootDir: z.string().default("."),
162
- layers: z.array(LayerSchema).optional(),
163
- forbiddenImports: z.array(ForbiddenImportSchema).optional(),
164
- boundaries: BoundaryConfigSchema.optional(),
165
- agentsMapPath: z.string().default("./AGENTS.md"),
166
- docsDir: z.string().default("./docs"),
167
- agent: AgentConfigSchema.optional(),
168
- entropy: EntropyConfigSchema.optional(),
169
- security: SecurityConfigSchema.optional(),
170
- performance: PerformanceConfigSchema.optional(),
171
- template: z.object({
172
- level: z.enum(["basic", "intermediate", "advanced"]),
173
- framework: z.string().optional(),
174
- version: z.number()
175
- }).optional(),
176
- phaseGates: PhaseGatesConfigSchema.optional(),
177
- design: DesignConfigSchema.optional(),
178
- i18n: I18nConfigSchema.optional(),
179
- review: ReviewConfigSchema.optional(),
180
- updateCheckInterval: z.number().int().min(0).optional()
181
- });
182
-
183
- // src/config/loader.ts
184
- var CONFIG_FILENAMES = ["harness.config.json"];
185
- function findConfigFile(startDir = process.cwd()) {
186
- let currentDir = path.resolve(startDir);
187
- const root = path.parse(currentDir).root;
188
- while (currentDir !== root) {
189
- for (const filename of CONFIG_FILENAMES) {
190
- const configPath = path.join(currentDir, filename);
191
- if (fs.existsSync(configPath)) {
192
- return Ok(configPath);
193
- }
194
- }
195
- currentDir = path.dirname(currentDir);
196
- }
197
- return Err(
198
- new CLIError('No harness.config.json found. Run "harness init" to create one.', ExitCode.ERROR)
199
- );
200
- }
201
- function loadConfig(configPath) {
202
- if (!fs.existsSync(configPath)) {
203
- return Err(new CLIError(`Config file not found: ${configPath}`, ExitCode.ERROR));
204
- }
205
- let rawConfig;
206
- try {
207
- const content = fs.readFileSync(configPath, "utf-8");
208
- rawConfig = JSON.parse(content);
209
- } catch (error) {
210
- return Err(
211
- new CLIError(
212
- `Failed to parse config: ${error instanceof Error ? error.message : "Unknown error"}`,
213
- ExitCode.ERROR
214
- )
215
- );
216
- }
217
- const parsed = HarnessConfigSchema.safeParse(rawConfig);
218
- if (!parsed.success) {
219
- const issues = parsed.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
220
- return Err(new CLIError(`Invalid config:
221
- ${issues}`, ExitCode.ERROR));
222
- }
223
- return Ok(parsed.data);
224
- }
225
- function resolveConfig(configPath) {
226
- if (configPath) {
227
- return loadConfig(configPath);
228
- }
229
- const findResult = findConfigFile();
230
- if (!findResult.ok) {
231
- return findResult;
232
- }
233
- return loadConfig(findResult.value);
234
- }
235
-
236
- // src/output/formatter.ts
237
- import chalk from "chalk";
238
- var OutputMode = {
239
- JSON: "json",
240
- TEXT: "text",
241
- QUIET: "quiet",
242
- VERBOSE: "verbose"
243
- };
244
- var OutputFormatter = class {
245
- constructor(mode = OutputMode.TEXT) {
246
- this.mode = mode;
247
- }
248
- /**
249
- * Format raw data (for JSON mode)
250
- */
251
- format(data) {
252
- if (this.mode === OutputMode.JSON) {
253
- return JSON.stringify(data, null, 2);
254
- }
255
- return String(data);
256
- }
257
- /**
258
- * Format validation result
259
- */
260
- formatValidation(result) {
261
- if (this.mode === OutputMode.JSON) {
262
- return JSON.stringify(result, null, 2);
263
- }
264
- if (this.mode === OutputMode.QUIET) {
265
- if (result.valid) return "";
266
- return result.issues.map((i) => `${i.file ?? ""}: ${i.message}`).join("\n");
267
- }
268
- const lines = [];
269
- if (result.valid) {
270
- lines.push(chalk.green("v validation passed"));
271
- } else {
272
- lines.push(chalk.red(`x Validation failed (${result.issues.length} issues)`));
273
- lines.push("");
274
- for (const issue of result.issues) {
275
- const location = issue.file ? issue.line ? `${issue.file}:${issue.line}` : issue.file : "unknown";
276
- lines.push(` ${chalk.yellow("*")} ${chalk.dim(location)}`);
277
- lines.push(` ${issue.message}`);
278
- if (issue.suggestion && this.mode === OutputMode.VERBOSE) {
279
- lines.push(` ${chalk.dim("->")} ${issue.suggestion}`);
280
- }
281
- }
282
- }
283
- return lines.join("\n");
284
- }
285
- /**
286
- * Format a summary line
287
- */
288
- formatSummary(label, value, success) {
289
- if (this.mode === OutputMode.JSON || this.mode === OutputMode.QUIET) {
290
- return "";
291
- }
292
- const icon = success ? chalk.green("v") : chalk.red("x");
293
- return `${icon} ${label}: ${value}`;
294
- }
295
- };
296
-
297
- // src/commands/validate.ts
298
117
  async function runValidate(options) {
299
118
  const configResult = resolveConfig(options.configPath);
300
119
  if (!configResult.ok) {
301
120
  return configResult;
302
121
  }
303
122
  const config = configResult.value;
304
- const cwd = options.cwd ?? (options.configPath ? path2.dirname(path2.resolve(options.configPath)) : process.cwd());
123
+ const cwd = options.cwd ?? (options.configPath ? path.dirname(path.resolve(options.configPath)) : process.cwd());
305
124
  const result = {
306
125
  valid: true,
307
126
  checks: {
@@ -311,7 +130,7 @@ async function runValidate(options) {
311
130
  },
312
131
  issues: []
313
132
  };
314
- const agentsMapPath = path2.resolve(cwd, config.agentsMapPath);
133
+ const agentsMapPath = path.resolve(cwd, config.agentsMapPath);
315
134
  const agentsResult = await validateAgentsMap(agentsMapPath);
316
135
  if (agentsResult.ok) {
317
136
  result.checks.agentsMap = true;
@@ -369,10 +188,10 @@ function createValidateCommand() {
369
188
  process.exit(result.error.exitCode);
370
189
  }
371
190
  if (opts.crossCheck) {
372
- const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-DLNK423G.js");
191
+ const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-WZAX357V.js");
373
192
  const cwd = process.cwd();
374
- const specsDir = path2.join(cwd, "docs", "specs");
375
- const plansDir = path2.join(cwd, "docs", "plans");
193
+ const specsDir = path.join(cwd, "docs", "specs");
194
+ const plansDir = path.join(cwd, "docs", "plans");
376
195
  const crossResult = await runCrossCheck2({ specsDir, plansDir, projectPath: cwd });
377
196
  if (crossResult.ok && crossResult.value.warnings > 0) {
378
197
  console.log("\nCross-artifact validation:");
@@ -396,15 +215,7 @@ function createValidateCommand() {
396
215
 
397
216
  // src/commands/check-deps.ts
398
217
  import { Command as Command2 } from "commander";
399
- import * as path3 from "path";
400
-
401
- // src/utils/files.ts
402
- import { glob } from "glob";
403
- async function findFiles(pattern, cwd = process.cwd()) {
404
- return glob(pattern, { cwd, absolute: true });
405
- }
406
-
407
- // src/commands/check-deps.ts
218
+ import * as path2 from "path";
408
219
  async function runCheckDeps(options) {
409
220
  const cwd = options.cwd ?? process.cwd();
410
221
  const configResult = resolveConfig(options.configPath);
@@ -420,7 +231,7 @@ async function runCheckDeps(options) {
420
231
  if (!config.layers || config.layers.length === 0) {
421
232
  return Ok(result);
422
233
  }
423
- const rootDir = path3.resolve(cwd, config.rootDir);
234
+ const rootDir = path2.resolve(cwd, config.rootDir);
424
235
  const parser = new TypeScriptParser();
425
236
  const layers = config.layers.map((l) => defineLayer(l.name, [l.pattern], l.allowedDependencies));
426
237
  const layerConfig = {
@@ -501,11 +312,11 @@ function createCheckDepsCommand() {
501
312
 
502
313
  // src/commands/check-perf.ts
503
314
  import { Command as Command3 } from "commander";
504
- import * as path4 from "path";
315
+ import * as path3 from "path";
505
316
  async function runCheckPerf(cwd, options) {
506
317
  const runAll = !options.structural && !options.size && !options.coupling;
507
318
  const analyzer = new EntropyAnalyzer({
508
- rootDir: path4.resolve(cwd),
319
+ rootDir: path3.resolve(cwd),
509
320
  analyze: {
510
321
  complexity: runAll || !!options.structural,
511
322
  coupling: runAll || !!options.coupling,
@@ -623,7 +434,7 @@ function createCheckPerfCommand() {
623
434
 
624
435
  // src/commands/check-security.ts
625
436
  import { Command as Command4 } from "commander";
626
- import * as path5 from "path";
437
+ import * as path4 from "path";
627
438
  import { execSync } from "child_process";
628
439
  var SEVERITY_RANK = {
629
440
  error: 3,
@@ -636,19 +447,19 @@ function getChangedFiles(cwd) {
636
447
  cwd,
637
448
  encoding: "utf-8"
638
449
  });
639
- return output.trim().split("\n").filter((f) => f.length > 0).map((f) => path5.resolve(cwd, f));
450
+ return output.trim().split("\n").filter((f) => f.length > 0).map((f) => path4.resolve(cwd, f));
640
451
  } catch {
641
452
  return [];
642
453
  }
643
454
  }
644
455
  async function runCheckSecurity(cwd, options) {
645
- const projectRoot = path5.resolve(cwd);
456
+ const projectRoot = path4.resolve(cwd);
646
457
  let configData = {};
647
458
  try {
648
- const fs25 = await import("fs");
649
- const configPath = path5.join(projectRoot, "harness.config.json");
650
- if (fs25.existsSync(configPath)) {
651
- const raw = fs25.readFileSync(configPath, "utf-8");
459
+ const fs11 = await import("fs");
460
+ const configPath = path4.join(projectRoot, "harness.config.json");
461
+ if (fs11.existsSync(configPath)) {
462
+ const raw = fs11.readFileSync(configPath, "utf-8");
652
463
  const parsed = JSON.parse(raw);
653
464
  configData = parsed.security ?? {};
654
465
  }
@@ -661,7 +472,7 @@ async function runCheckSecurity(cwd, options) {
661
472
  if (options.changedOnly) {
662
473
  filesToScan = getChangedFiles(projectRoot);
663
474
  } else {
664
- const { glob: glob2 } = await import("glob");
475
+ const { glob } = await import("glob");
665
476
  const pattern = "**/*.{ts,tsx,js,jsx,go,py,java,rb}";
666
477
  const ignore = securityConfig.exclude ?? [
667
478
  "**/node_modules/**",
@@ -669,7 +480,7 @@ async function runCheckSecurity(cwd, options) {
669
480
  "**/*.test.ts",
670
481
  "**/fixtures/**"
671
482
  ];
672
- filesToScan = await glob2(pattern, { cwd: projectRoot, absolute: true, ignore });
483
+ filesToScan = await glob(pattern, { cwd: projectRoot, absolute: true, ignore });
673
484
  }
674
485
  const result = await scanner.scanFiles(filesToScan);
675
486
  const threshold = options.severity ?? "warning";
@@ -729,15 +540,15 @@ function createCheckSecurityCommand() {
729
540
 
730
541
  // src/commands/perf.ts
731
542
  import { Command as Command5 } from "commander";
732
- import * as path6 from "path";
543
+ import * as path5 from "path";
733
544
  function createPerfCommand() {
734
545
  const perf = new Command5("perf").description("Performance benchmark and baseline management");
735
- perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
546
+ perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob, _opts, cmd) => {
736
547
  const globalOpts = cmd.optsWithGlobals();
737
548
  const cwd = process.cwd();
738
- const { BenchmarkRunner } = await import("./dist-ZODQVGC4.js");
549
+ const { BenchmarkRunner } = await import("./dist-PBTNVK6K.js");
739
550
  const runner = new BenchmarkRunner();
740
- const benchFiles = runner.discover(cwd, glob2);
551
+ const benchFiles = runner.discover(cwd, glob);
741
552
  if (benchFiles.length === 0) {
742
553
  if (globalOpts.json) {
743
554
  console.log(JSON.stringify({ benchFiles: [], message: "No .bench.ts files found" }));
@@ -755,7 +566,7 @@ function createPerfCommand() {
755
566
  }
756
567
  logger.info("Running benchmarks...");
757
568
  }
758
- const result = await runner.run(glob2 ? { cwd, glob: glob2 } : { cwd });
569
+ const result = await runner.run(glob ? { cwd, glob } : { cwd });
759
570
  if (globalOpts.json) {
760
571
  console.log(JSON.stringify({ results: result.results, success: result.success }));
761
572
  } else {
@@ -804,7 +615,7 @@ Results (${result.results.length} benchmarks):`);
804
615
  baselines.command("update").description("Update baselines from latest benchmark run").action(async (_opts, cmd) => {
805
616
  const globalOpts = cmd.optsWithGlobals();
806
617
  const cwd = process.cwd();
807
- const { BenchmarkRunner } = await import("./dist-ZODQVGC4.js");
618
+ const { BenchmarkRunner } = await import("./dist-PBTNVK6K.js");
808
619
  const runner = new BenchmarkRunner();
809
620
  const manager = new BaselineManager(cwd);
810
621
  logger.info("Running benchmarks to update baselines...");
@@ -832,9 +643,9 @@ Results (${result.results.length} benchmarks):`);
832
643
  perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
833
644
  const globalOpts = cmd.optsWithGlobals();
834
645
  const cwd = process.cwd();
835
- const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-ZODQVGC4.js");
646
+ const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-PBTNVK6K.js");
836
647
  const analyzer = new EntropyAnalyzer2({
837
- rootDir: path6.resolve(cwd),
648
+ rootDir: path5.resolve(cwd),
838
649
  analyze: { complexity: true, coupling: true }
839
650
  });
840
651
  const result = await analyzer.analyze();
@@ -892,7 +703,7 @@ Results (${result.results.length} benchmarks):`);
892
703
 
893
704
  // src/commands/check-docs.ts
894
705
  import { Command as Command6 } from "commander";
895
- import * as path7 from "path";
706
+ import * as path6 from "path";
896
707
  async function runCheckDocs(options) {
897
708
  const cwd = options.cwd ?? process.cwd();
898
709
  const minCoverage = options.minCoverage ?? 80;
@@ -901,8 +712,8 @@ async function runCheckDocs(options) {
901
712
  return configResult;
902
713
  }
903
714
  const config = configResult.value;
904
- const docsDir = path7.resolve(cwd, config.docsDir);
905
- const sourceDir = path7.resolve(cwd, config.rootDir);
715
+ const docsDir = path6.resolve(cwd, config.docsDir);
716
+ const sourceDir = path6.resolve(cwd, config.rootDir);
906
717
  const coverageResult = await checkDocCoverage("project", {
907
718
  docsDir,
908
719
  sourceDir,
@@ -987,341 +798,34 @@ function createCheckDocsCommand() {
987
798
 
988
799
  // src/commands/init.ts
989
800
  import { Command as Command8 } from "commander";
990
- import chalk3 from "chalk";
991
- import * as fs5 from "fs";
992
- import * as path11 from "path";
993
-
994
- // src/templates/engine.ts
801
+ import chalk2 from "chalk";
995
802
  import * as fs2 from "fs";
996
803
  import * as path8 from "path";
997
- import Handlebars from "handlebars";
998
-
999
- // src/templates/schema.ts
1000
- import { z as z2 } from "zod";
1001
- var MergeStrategySchema = z2.object({
1002
- json: z2.enum(["deep-merge", "overlay-wins"]).default("deep-merge"),
1003
- files: z2.literal("overlay-wins").default("overlay-wins")
1004
- });
1005
- var TemplateMetadataSchema = z2.object({
1006
- name: z2.string(),
1007
- description: z2.string(),
1008
- level: z2.enum(["basic", "intermediate", "advanced"]).optional(),
1009
- framework: z2.string().optional(),
1010
- extends: z2.string().optional(),
1011
- mergeStrategy: MergeStrategySchema.default({}),
1012
- version: z2.literal(1)
1013
- });
1014
-
1015
- // src/templates/merger.ts
1016
- function isPlainObject(val) {
1017
- return typeof val === "object" && val !== null && !Array.isArray(val);
1018
- }
1019
- function deepMergeJson(base, overlay) {
1020
- const result = { ...base };
1021
- for (const key of Object.keys(overlay)) {
1022
- if (isPlainObject(result[key]) && isPlainObject(overlay[key])) {
1023
- result[key] = deepMergeJson(
1024
- result[key],
1025
- overlay[key]
1026
- );
1027
- } else {
1028
- result[key] = overlay[key];
1029
- }
1030
- }
1031
- return result;
1032
- }
1033
- var CONCAT_KEYS = /* @__PURE__ */ new Set(["dependencies", "devDependencies", "peerDependencies"]);
1034
- function mergePackageJson(base, overlay) {
1035
- const result = { ...base };
1036
- for (const key of Object.keys(overlay)) {
1037
- if (CONCAT_KEYS.has(key) && isPlainObject(result[key]) && isPlainObject(overlay[key])) {
1038
- result[key] = {
1039
- ...result[key],
1040
- ...overlay[key]
1041
- };
1042
- } else if (isPlainObject(result[key]) && isPlainObject(overlay[key])) {
1043
- result[key] = deepMergeJson(
1044
- result[key],
1045
- overlay[key]
1046
- );
1047
- } else {
1048
- result[key] = overlay[key];
1049
- }
1050
- }
1051
- return result;
1052
- }
1053
-
1054
- // src/templates/engine.ts
1055
- var TemplateEngine = class {
1056
- constructor(templatesDir) {
1057
- this.templatesDir = templatesDir;
1058
- }
1059
- listTemplates() {
1060
- try {
1061
- const entries = fs2.readdirSync(this.templatesDir, { withFileTypes: true });
1062
- const templates = [];
1063
- for (const entry of entries) {
1064
- if (!entry.isDirectory()) continue;
1065
- const metaPath = path8.join(this.templatesDir, entry.name, "template.json");
1066
- if (!fs2.existsSync(metaPath)) continue;
1067
- const raw = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
1068
- const parsed = TemplateMetadataSchema.safeParse(raw);
1069
- if (parsed.success) templates.push(parsed.data);
1070
- }
1071
- return Ok(templates);
1072
- } catch (error) {
1073
- return Err(
1074
- new Error(
1075
- `Failed to list templates: ${error instanceof Error ? error.message : String(error)}`
1076
- )
1077
- );
1078
- }
1079
- }
1080
- resolveTemplate(level, framework) {
1081
- const levelDir = this.findTemplateDir(level, "level");
1082
- if (!levelDir) return Err(new Error(`Template not found for level: ${level}`));
1083
- const metaPath = path8.join(levelDir, "template.json");
1084
- const metaRaw = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
1085
- const metaResult = TemplateMetadataSchema.safeParse(metaRaw);
1086
- if (!metaResult.success)
1087
- return Err(new Error(`Invalid template.json in ${level}: ${metaResult.error.message}`));
1088
- const metadata = metaResult.data;
1089
- let files = [];
1090
- if (metadata.extends) {
1091
- const baseDir = path8.join(this.templatesDir, metadata.extends);
1092
- if (fs2.existsSync(baseDir)) files = this.collectFiles(baseDir, metadata.extends);
1093
- }
1094
- const levelFiles = this.collectFiles(levelDir, level);
1095
- files = this.mergeFileLists(files, levelFiles);
1096
- let overlayMetadata;
1097
- if (framework) {
1098
- const frameworkDir = this.findTemplateDir(framework, "framework");
1099
- if (!frameworkDir) return Err(new Error(`Framework template not found: ${framework}`));
1100
- const fMetaPath = path8.join(frameworkDir, "template.json");
1101
- const fMetaRaw = JSON.parse(fs2.readFileSync(fMetaPath, "utf-8"));
1102
- const fMetaResult = TemplateMetadataSchema.safeParse(fMetaRaw);
1103
- if (fMetaResult.success) overlayMetadata = fMetaResult.data;
1104
- const frameworkFiles = this.collectFiles(frameworkDir, framework);
1105
- files = this.mergeFileLists(files, frameworkFiles);
1106
- }
1107
- files = files.filter((f) => f.relativePath !== "template.json");
1108
- const resolved2 = { metadata, files };
1109
- if (overlayMetadata !== void 0) resolved2.overlayMetadata = overlayMetadata;
1110
- return Ok(resolved2);
1111
- }
1112
- render(template, context) {
1113
- const rendered = [];
1114
- const jsonBuffers = /* @__PURE__ */ new Map();
1115
- for (const file of template.files) {
1116
- const outputPath = file.relativePath.replace(/\.hbs$/, "");
1117
- if (file.isHandlebars) {
1118
- try {
1119
- const raw = fs2.readFileSync(file.absolutePath, "utf-8");
1120
- const compiled = Handlebars.compile(raw, { strict: true });
1121
- const content = compiled(context);
1122
- if (outputPath.endsWith(".json") && file.relativePath.endsWith(".json.hbs")) {
1123
- if (!jsonBuffers.has(outputPath)) jsonBuffers.set(outputPath, []);
1124
- jsonBuffers.get(outputPath).push(JSON.parse(content));
1125
- } else {
1126
- rendered.push({ relativePath: outputPath, content });
1127
- }
1128
- } catch (error) {
1129
- const msg = error instanceof Error ? error.message : String(error);
1130
- return Err(
1131
- new Error(
1132
- `Template render failed in ${file.sourceTemplate}/${file.relativePath}: ${msg}`
1133
- )
1134
- );
1135
- }
1136
- } else {
1137
- try {
1138
- const content = fs2.readFileSync(file.absolutePath, "utf-8");
1139
- rendered.push({ relativePath: file.relativePath, content });
1140
- } catch (error) {
1141
- const msg = error instanceof Error ? error.message : String(error);
1142
- return Err(
1143
- new Error(
1144
- `Template render failed in ${file.sourceTemplate}/${file.relativePath}: ${msg}`
1145
- )
1146
- );
1147
- }
1148
- }
1149
- }
1150
- try {
1151
- for (const [outputPath, jsons] of jsonBuffers) {
1152
- let merged = {};
1153
- for (const json of jsons) {
1154
- merged = outputPath === "package.json" ? mergePackageJson(merged, json) : deepMergeJson(merged, json);
1155
- }
1156
- rendered.push({ relativePath: outputPath, content: JSON.stringify(merged, null, 2) });
1157
- }
1158
- } catch (error) {
1159
- const msg = error instanceof Error ? error.message : String(error);
1160
- return Err(new Error(`JSON merge failed: ${msg}`));
1161
- }
1162
- return Ok({ files: rendered });
1163
- }
1164
- write(files, targetDir, options) {
1165
- try {
1166
- const written = [];
1167
- for (const file of files.files) {
1168
- const targetPath = path8.join(targetDir, file.relativePath);
1169
- const dir = path8.dirname(targetPath);
1170
- if (!options.overwrite && fs2.existsSync(targetPath)) continue;
1171
- fs2.mkdirSync(dir, { recursive: true });
1172
- fs2.writeFileSync(targetPath, file.content);
1173
- written.push(file.relativePath);
1174
- }
1175
- return Ok(written);
1176
- } catch (error) {
1177
- return Err(
1178
- new Error(
1179
- `Failed to write files: ${error instanceof Error ? error.message : String(error)}`
1180
- )
1181
- );
1182
- }
1183
- }
1184
- findTemplateDir(name, type) {
1185
- const entries = fs2.readdirSync(this.templatesDir, { withFileTypes: true });
1186
- for (const entry of entries) {
1187
- if (!entry.isDirectory()) continue;
1188
- const metaPath = path8.join(this.templatesDir, entry.name, "template.json");
1189
- if (!fs2.existsSync(metaPath)) continue;
1190
- const raw = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
1191
- const parsed = TemplateMetadataSchema.safeParse(raw);
1192
- if (!parsed.success) continue;
1193
- if (type === "level" && parsed.data.level === name)
1194
- return path8.join(this.templatesDir, entry.name);
1195
- if (type === "framework" && parsed.data.framework === name)
1196
- return path8.join(this.templatesDir, entry.name);
1197
- if (parsed.data.name === name) return path8.join(this.templatesDir, entry.name);
1198
- }
1199
- return null;
1200
- }
1201
- collectFiles(dir, sourceName) {
1202
- const files = [];
1203
- const walk = (currentDir) => {
1204
- const entries = fs2.readdirSync(currentDir, { withFileTypes: true });
1205
- for (const entry of entries) {
1206
- const fullPath = path8.join(currentDir, entry.name);
1207
- if (entry.isDirectory()) {
1208
- walk(fullPath);
1209
- } else {
1210
- files.push({
1211
- relativePath: path8.relative(dir, fullPath).replace(/\\/g, "/"),
1212
- absolutePath: fullPath,
1213
- isHandlebars: entry.name.endsWith(".hbs"),
1214
- sourceTemplate: sourceName
1215
- });
1216
- }
1217
- }
1218
- };
1219
- walk(dir);
1220
- return files;
1221
- }
1222
- mergeFileLists(base, overlay) {
1223
- const map = /* @__PURE__ */ new Map();
1224
- for (const file of base) map.set(file.relativePath, file);
1225
- for (const file of overlay) {
1226
- if (file.relativePath.endsWith(".json.hbs")) {
1227
- const baseKey = base.find((f) => f.relativePath === file.relativePath);
1228
- if (baseKey) {
1229
- map.set(`__overlay__${file.relativePath}`, file);
1230
- } else {
1231
- map.set(file.relativePath, file);
1232
- }
1233
- } else {
1234
- map.set(file.relativePath, file);
1235
- }
1236
- }
1237
- return Array.from(map.values());
1238
- }
1239
- };
1240
-
1241
- // src/utils/paths.ts
1242
- import * as fs3 from "fs";
1243
- import * as path9 from "path";
1244
- import { fileURLToPath } from "url";
1245
- var __filename = fileURLToPath(import.meta.url);
1246
- var __dirname = path9.dirname(__filename);
1247
- function findUpDir(targetName, marker, maxLevels = 8) {
1248
- let dir = __dirname;
1249
- for (let i = 0; i < maxLevels; i++) {
1250
- const candidate = path9.join(dir, targetName);
1251
- if (fs3.existsSync(candidate) && fs3.statSync(candidate).isDirectory()) {
1252
- if (fs3.existsSync(path9.join(candidate, marker))) {
1253
- return candidate;
1254
- }
1255
- }
1256
- dir = path9.dirname(dir);
1257
- }
1258
- return null;
1259
- }
1260
- function resolveTemplatesDir() {
1261
- return findUpDir("templates", "base") ?? path9.join(__dirname, "templates");
1262
- }
1263
- function resolvePersonasDir() {
1264
- const agentsDir = findUpDir("agents", "personas");
1265
- if (agentsDir) {
1266
- return path9.join(agentsDir, "personas");
1267
- }
1268
- return path9.join(__dirname, "agents", "personas");
1269
- }
1270
- function resolveSkillsDir() {
1271
- const agentsDir = findUpDir("agents", "skills");
1272
- if (agentsDir) {
1273
- return path9.join(agentsDir, "skills", "claude-code");
1274
- }
1275
- return path9.join(__dirname, "agents", "skills", "claude-code");
1276
- }
1277
- function resolveProjectSkillsDir(cwd) {
1278
- let dir = cwd ?? process.cwd();
1279
- for (let i = 0; i < 8; i++) {
1280
- const candidate = path9.join(dir, "agents", "skills", "claude-code");
1281
- if (fs3.existsSync(candidate) && fs3.statSync(candidate).isDirectory()) {
1282
- const agentsDir = path9.join(dir, "agents");
1283
- if (fs3.existsSync(path9.join(agentsDir, "skills"))) {
1284
- return candidate;
1285
- }
1286
- }
1287
- const parent = path9.dirname(dir);
1288
- if (parent === dir) break;
1289
- dir = parent;
1290
- }
1291
- return null;
1292
- }
1293
- function resolveGlobalSkillsDir() {
1294
- const agentsDir = findUpDir("agents", "skills");
1295
- if (agentsDir) {
1296
- return path9.join(agentsDir, "skills", "claude-code");
1297
- }
1298
- return path9.join(__dirname, "agents", "skills", "claude-code");
1299
- }
1300
804
 
1301
805
  // src/commands/setup-mcp.ts
1302
806
  import { Command as Command7 } from "commander";
1303
- import * as fs4 from "fs";
1304
- import * as path10 from "path";
807
+ import * as fs from "fs";
808
+ import * as path7 from "path";
1305
809
  import * as os from "os";
1306
- import chalk2 from "chalk";
810
+ import chalk from "chalk";
1307
811
  var HARNESS_MCP_ENTRY = {
1308
812
  command: "harness-mcp"
1309
813
  };
1310
814
  function readJsonFile(filePath) {
1311
- if (!fs4.existsSync(filePath)) return null;
815
+ if (!fs.existsSync(filePath)) return null;
1312
816
  try {
1313
- return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
817
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
1314
818
  } catch {
1315
- fs4.copyFileSync(filePath, filePath + ".bak");
819
+ fs.copyFileSync(filePath, filePath + ".bak");
1316
820
  return null;
1317
821
  }
1318
822
  }
1319
823
  function writeJsonFile(filePath, data) {
1320
- const dir = path10.dirname(filePath);
1321
- if (!fs4.existsSync(dir)) {
1322
- fs4.mkdirSync(dir, { recursive: true });
824
+ const dir = path7.dirname(filePath);
825
+ if (!fs.existsSync(dir)) {
826
+ fs.mkdirSync(dir, { recursive: true });
1323
827
  }
1324
- fs4.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
828
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
1325
829
  }
1326
830
  function configureMcpServer(configPath) {
1327
831
  const config = readJsonFile(configPath) ?? {};
@@ -1336,7 +840,7 @@ function configureMcpServer(configPath) {
1336
840
  return true;
1337
841
  }
1338
842
  function addGeminiTrustedFolder(cwd) {
1339
- const trustedPath = path10.join(os.homedir(), ".gemini", "trustedFolders.json");
843
+ const trustedPath = path7.join(os.homedir(), ".gemini", "trustedFolders.json");
1340
844
  const folders = readJsonFile(trustedPath) ?? {};
1341
845
  if (folders[cwd] === "TRUST_FOLDER") {
1342
846
  return false;
@@ -1350,7 +854,7 @@ function setupMcp(cwd, client) {
1350
854
  const skipped = [];
1351
855
  let trustedFolder = false;
1352
856
  if (client === "all" || client === "claude") {
1353
- const configPath = path10.join(cwd, ".mcp.json");
857
+ const configPath = path7.join(cwd, ".mcp.json");
1354
858
  if (configureMcpServer(configPath)) {
1355
859
  configured.push("Claude Code");
1356
860
  } else {
@@ -1358,7 +862,7 @@ function setupMcp(cwd, client) {
1358
862
  }
1359
863
  }
1360
864
  if (client === "all" || client === "gemini") {
1361
- const configPath = path10.join(cwd, ".gemini", "settings.json");
865
+ const configPath = path7.join(cwd, ".gemini", "settings.json");
1362
866
  if (configureMcpServer(configPath)) {
1363
867
  configured.push("Gemini CLI");
1364
868
  } else {
@@ -1379,7 +883,7 @@ function createSetupMcpCommand() {
1379
883
  logger.success("MCP server configured!");
1380
884
  console.log("");
1381
885
  for (const name of configured) {
1382
- console.log(` ${chalk2.green("+")} ${name}`);
886
+ console.log(` ${chalk.green("+")} ${name}`);
1383
887
  }
1384
888
  }
1385
889
  if (trustedFolder) {
@@ -1390,11 +894,11 @@ function createSetupMcpCommand() {
1390
894
  console.log("");
1391
895
  logger.info("Already configured:");
1392
896
  for (const name of skipped) {
1393
- console.log(` ${chalk2.dim("-")} ${name}`);
897
+ console.log(` ${chalk.dim("-")} ${name}`);
1394
898
  }
1395
899
  }
1396
900
  console.log("");
1397
- console.log(chalk2.bold("The harness MCP server provides:"));
901
+ console.log(chalk.bold("The harness MCP server provides:"));
1398
902
  console.log(
1399
903
  " - 31 tools for validation, entropy detection, skill execution, graph querying, and more"
1400
904
  );
@@ -1402,7 +906,7 @@ function createSetupMcpCommand() {
1402
906
  " - 8 resources for project context, skills, rules, learnings, state, and graph data"
1403
907
  );
1404
908
  console.log("");
1405
- console.log(`Run ${chalk2.cyan("harness skill list")} to see available skills.`);
909
+ console.log(`Run ${chalk.cyan("harness skill list")} to see available skills.`);
1406
910
  console.log("");
1407
911
  }
1408
912
  process.exit(ExitCode.SUCCESS);
@@ -1412,11 +916,11 @@ function createSetupMcpCommand() {
1412
916
  // src/commands/init.ts
1413
917
  async function runInit(options) {
1414
918
  const cwd = options.cwd ?? process.cwd();
1415
- const name = options.name ?? path11.basename(cwd);
919
+ const name = options.name ?? path8.basename(cwd);
1416
920
  const level = options.level ?? "basic";
1417
921
  const force = options.force ?? false;
1418
- const configPath = path11.join(cwd, "harness.config.json");
1419
- if (!force && fs5.existsSync(configPath)) {
922
+ const configPath = path8.join(cwd, "harness.config.json");
923
+ if (!force && fs2.existsSync(configPath)) {
1420
924
  return Err(
1421
925
  new CLIError("Project already initialized. Use --force to overwrite.", ExitCode.ERROR)
1422
926
  );
@@ -1462,20 +966,20 @@ function createInitCommand() {
1462
966
  console.log("");
1463
967
  logger.info("Created files:");
1464
968
  for (const file of result.value.filesCreated) {
1465
- console.log(` ${chalk3.green("+")} ${file}`);
969
+ console.log(` ${chalk2.green("+")} ${file}`);
1466
970
  }
1467
971
  if (mcpResult.configured.length > 0) {
1468
972
  console.log("");
1469
973
  logger.info("MCP server configured for:");
1470
974
  for (const name of mcpResult.configured) {
1471
- console.log(` ${chalk3.green("+")} ${name}`);
975
+ console.log(` ${chalk2.green("+")} ${name}`);
1472
976
  }
1473
977
  }
1474
978
  console.log("");
1475
- console.log(chalk3.bold("Next steps:"));
1476
- console.log(` 1. Review ${chalk3.cyan("harness.config.json")}`);
1477
- console.log(` 2. Update ${chalk3.cyan("AGENTS.md")} with your project context`);
1478
- console.log(` 3. Run ${chalk3.cyan("harness validate")} to check your setup`);
979
+ console.log(chalk2.bold("Next steps:"));
980
+ console.log(` 1. Review ${chalk2.cyan("harness.config.json")}`);
981
+ console.log(` 2. Update ${chalk2.cyan("AGENTS.md")} with your project context`);
982
+ console.log(` 3. Run ${chalk2.cyan("harness validate")} to check your setup`);
1479
983
  console.log("");
1480
984
  }
1481
985
  process.exit(ExitCode.SUCCESS);
@@ -1485,7 +989,7 @@ function createInitCommand() {
1485
989
 
1486
990
  // src/commands/cleanup.ts
1487
991
  import { Command as Command9 } from "commander";
1488
- import * as path12 from "path";
992
+ import * as path9 from "path";
1489
993
  async function runCleanup(options) {
1490
994
  const cwd = options.cwd ?? process.cwd();
1491
995
  const type = options.type ?? "all";
@@ -1500,11 +1004,11 @@ async function runCleanup(options) {
1500
1004
  patternViolations: [],
1501
1005
  totalIssues: 0
1502
1006
  };
1503
- const rootDir = path12.resolve(cwd, config.rootDir);
1504
- const docsDir = path12.resolve(cwd, config.docsDir);
1007
+ const rootDir = path9.resolve(cwd, config.rootDir);
1008
+ const docsDir = path9.resolve(cwd, config.docsDir);
1505
1009
  const entropyConfig = {
1506
1010
  rootDir,
1507
- entryPoints: [path12.join(rootDir, "src/index.ts")],
1011
+ entryPoints: [path9.join(rootDir, "src/index.ts")],
1508
1012
  docPaths: [docsDir],
1509
1013
  analyze: {
1510
1014
  drift: type === "all" || type === "drift",
@@ -1605,7 +1109,7 @@ function createCleanupCommand() {
1605
1109
 
1606
1110
  // src/commands/fix-drift.ts
1607
1111
  import { Command as Command10 } from "commander";
1608
- import * as path13 from "path";
1112
+ import * as path10 from "path";
1609
1113
  async function runFixDrift(options) {
1610
1114
  const cwd = options.cwd ?? process.cwd();
1611
1115
  const dryRun = options.dryRun !== false;
@@ -1614,11 +1118,11 @@ async function runFixDrift(options) {
1614
1118
  return Err(configResult.error);
1615
1119
  }
1616
1120
  const config = configResult.value;
1617
- const rootDir = path13.resolve(cwd, config.rootDir);
1618
- const docsDir = path13.resolve(cwd, config.docsDir);
1121
+ const rootDir = path10.resolve(cwd, config.rootDir);
1122
+ const docsDir = path10.resolve(cwd, config.docsDir);
1619
1123
  const entropyConfig = {
1620
1124
  rootDir,
1621
- entryPoints: [path13.join(rootDir, "src/index.ts")],
1125
+ entryPoints: [path10.join(rootDir, "src/index.ts")],
1622
1126
  docPaths: [docsDir],
1623
1127
  analyze: {
1624
1128
  drift: true,
@@ -1769,477 +1273,41 @@ import { Command as Command13 } from "commander";
1769
1273
 
1770
1274
  // src/commands/agent/run.ts
1771
1275
  import { Command as Command11 } from "commander";
1772
- import * as path17 from "path";
1276
+ import * as path11 from "path";
1773
1277
  import * as childProcess from "child_process";
1774
-
1775
- // src/persona/loader.ts
1776
- import * as fs6 from "fs";
1777
- import * as path14 from "path";
1778
- import YAML from "yaml";
1779
-
1780
- // src/persona/schema.ts
1781
- import { z as z3 } from "zod";
1782
- var TriggerContextSchema = z3.enum(["always", "on_pr", "on_commit", "on_review", "scheduled", "manual", "on_plan_approved"]).default("always");
1783
- var CommandStepSchema = z3.object({
1784
- command: z3.string(),
1785
- when: TriggerContextSchema
1786
- });
1787
- var SkillStepSchema = z3.object({
1788
- skill: z3.string(),
1789
- when: TriggerContextSchema,
1790
- output: z3.enum(["inline", "artifact", "auto"]).default("auto")
1791
- });
1792
- var StepSchema = z3.union([CommandStepSchema, SkillStepSchema]);
1793
- var PersonaTriggerSchema = z3.discriminatedUnion("event", [
1794
- z3.object({
1795
- event: z3.literal("on_pr"),
1796
- conditions: z3.object({
1797
- paths: z3.array(z3.string()).optional(),
1798
- min_files: z3.number().optional()
1799
- }).optional()
1800
- }),
1801
- z3.object({
1802
- event: z3.literal("on_commit"),
1803
- conditions: z3.object({ branches: z3.array(z3.string()).optional() }).optional()
1804
- }),
1805
- z3.object({
1806
- event: z3.literal("scheduled"),
1807
- cron: z3.string()
1808
- }),
1809
- z3.object({
1810
- event: z3.literal("manual")
1811
- })
1812
- ]);
1813
- var PersonaConfigSchema = z3.object({
1814
- severity: z3.enum(["error", "warning"]).default("error"),
1815
- autoFix: z3.boolean().default(false),
1816
- timeout: z3.number().default(3e5)
1817
- });
1818
- var PersonaOutputsSchema = z3.object({
1819
- "agents-md": z3.boolean().default(true),
1820
- "ci-workflow": z3.boolean().default(true),
1821
- "runtime-config": z3.boolean().default(true)
1822
- });
1823
- var PersonaSchemaV1 = z3.object({
1824
- version: z3.literal(1),
1825
- name: z3.string(),
1826
- description: z3.string(),
1827
- role: z3.string(),
1828
- skills: z3.array(z3.string()),
1829
- commands: z3.array(z3.string()),
1830
- triggers: z3.array(PersonaTriggerSchema),
1831
- config: PersonaConfigSchema.default({}),
1832
- outputs: PersonaOutputsSchema.default({})
1833
- });
1834
- var PersonaSchemaV2 = z3.object({
1835
- version: z3.literal(2),
1836
- name: z3.string(),
1837
- description: z3.string(),
1838
- role: z3.string(),
1839
- skills: z3.array(z3.string()),
1840
- steps: z3.array(StepSchema),
1841
- triggers: z3.array(PersonaTriggerSchema),
1842
- config: PersonaConfigSchema.default({}),
1843
- outputs: PersonaOutputsSchema.default({})
1844
- });
1845
- var PersonaSchema = z3.union([PersonaSchemaV1, PersonaSchemaV2]);
1846
-
1847
- // src/persona/loader.ts
1848
- function normalizePersona(raw) {
1849
- if (raw.version === 1 && Array.isArray(raw.commands)) {
1850
- const { commands, ...rest } = raw;
1851
- return {
1852
- ...rest,
1853
- steps: commands.map((cmd) => ({
1854
- command: cmd,
1855
- when: "always"
1856
- }))
1857
- };
1858
- }
1859
- return raw;
1860
- }
1861
- function loadPersona(filePath) {
1862
- try {
1863
- if (!fs6.existsSync(filePath)) {
1864
- return Err(new Error(`Persona file not found: ${filePath}`));
1865
- }
1866
- const raw = fs6.readFileSync(filePath, "utf-8");
1867
- const parsed = YAML.parse(raw);
1868
- const result = PersonaSchema.safeParse(parsed);
1869
- if (!result.success) {
1870
- return Err(new Error(`Invalid persona ${filePath}: ${result.error.message}`));
1871
- }
1872
- return Ok(normalizePersona(result.data));
1873
- } catch (error) {
1874
- return Err(
1875
- new Error(`Failed to load persona: ${error instanceof Error ? error.message : String(error)}`)
1876
- );
1278
+ async function runAgentTask(task, options) {
1279
+ const configResult = resolveConfig(options.configPath);
1280
+ if (!configResult.ok) {
1281
+ return configResult;
1877
1282
  }
1878
- }
1879
- function listPersonas(dir) {
1880
- try {
1881
- if (!fs6.existsSync(dir)) return Ok([]);
1882
- const entries = fs6.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
1883
- const personas = [];
1884
- for (const entry of entries) {
1885
- const filePath = path14.join(dir, entry);
1886
- const result = loadPersona(filePath);
1887
- if (result.ok) {
1888
- personas.push({ name: result.value.name, description: result.value.description, filePath });
1889
- }
1890
- }
1891
- return Ok(personas);
1892
- } catch (error) {
1283
+ const agentTypeMap = {
1284
+ review: "architecture-enforcer",
1285
+ "doc-review": "documentation-maintainer",
1286
+ "test-review": "test-reviewer"
1287
+ };
1288
+ const agentType = agentTypeMap[task];
1289
+ if (!agentType) {
1893
1290
  return Err(
1894
- new Error(
1895
- `Failed to list personas: ${error instanceof Error ? error.message : String(error)}`
1291
+ new CLIError(
1292
+ `Unknown task: ${task}. Available: ${Object.keys(agentTypeMap).join(", ")}`,
1293
+ ExitCode.ERROR
1896
1294
  )
1897
1295
  );
1898
1296
  }
1899
- }
1900
-
1901
- // src/persona/trigger-detector.ts
1902
- import * as fs7 from "fs";
1903
- import * as path15 from "path";
1904
- function detectTrigger(projectPath) {
1905
- const handoffPath = path15.join(projectPath, ".harness", "handoff.json");
1906
- if (!fs7.existsSync(handoffPath)) {
1907
- return { trigger: "manual" };
1908
- }
1909
- try {
1910
- const raw = fs7.readFileSync(handoffPath, "utf-8");
1911
- const handoff = JSON.parse(raw);
1912
- if (handoff.fromSkill === "harness-planning" && Array.isArray(handoff.pending) && handoff.pending.length > 0) {
1913
- return {
1914
- trigger: "on_plan_approved",
1915
- handoff: {
1916
- fromSkill: handoff.fromSkill,
1917
- summary: handoff.summary ?? "",
1918
- pending: handoff.pending,
1919
- planPath: handoff.planPath
1920
- }
1921
- };
1922
- }
1923
- return { trigger: "manual" };
1924
- } catch {
1925
- return { trigger: "manual" };
1926
- }
1927
- }
1928
-
1929
- // src/persona/runner.ts
1930
- var TIMEOUT_ERROR_MESSAGE = "__PERSONA_RUNNER_TIMEOUT__";
1931
- function stepName(step) {
1932
- return "command" in step ? step.command : step.skill;
1933
- }
1934
- function stepType(step) {
1935
- return "command" in step ? "command" : "skill";
1936
- }
1937
- function matchesTrigger(step, trigger) {
1938
- const when = step.when ?? "always";
1939
- return when === "always" || when === trigger;
1940
- }
1941
- function skipRemaining(activeSteps, fromIndex, report) {
1942
- for (let j = fromIndex; j < activeSteps.length; j++) {
1943
- const remaining = activeSteps[j];
1944
- report.steps.push({
1945
- name: stepName(remaining),
1946
- type: stepType(remaining),
1947
- status: "skipped",
1948
- durationMs: 0
1949
- });
1950
- }
1951
- }
1952
- async function runPersona(persona, context) {
1953
- const startTime = Date.now();
1954
- const timeout = persona.config.timeout;
1955
- const report = {
1956
- persona: persona.name.toLowerCase().replace(/\s+/g, "-"),
1957
- status: "pass",
1958
- steps: [],
1959
- totalDurationMs: 0
1960
- };
1961
- let resolvedTrigger;
1962
- let handoff = context.handoff;
1963
- if (context.trigger === "auto") {
1964
- const detection = detectTrigger(context.projectPath);
1965
- resolvedTrigger = detection.trigger;
1966
- handoff = detection.handoff ?? handoff;
1967
- } else {
1968
- resolvedTrigger = context.trigger;
1969
- }
1970
- const activeSteps = persona.steps.filter((s) => matchesTrigger(s, resolvedTrigger));
1971
- for (let i = 0; i < activeSteps.length; i++) {
1972
- const step = activeSteps[i];
1973
- if (Date.now() - startTime >= timeout) {
1974
- skipRemaining(activeSteps, i, report);
1975
- report.status = "partial";
1976
- break;
1977
- }
1978
- const stepStart = Date.now();
1979
- const remainingTime = timeout - (Date.now() - startTime);
1980
- if ("command" in step) {
1981
- const result = await Promise.race([
1982
- context.commandExecutor(step.command),
1983
- new Promise(
1984
- (resolve24) => setTimeout(
1985
- () => resolve24({
1986
- ok: false,
1987
- error: new Error(TIMEOUT_ERROR_MESSAGE)
1988
- }),
1989
- remainingTime
1990
- )
1991
- )
1992
- ]);
1993
- const durationMs = Date.now() - stepStart;
1994
- if (result.ok) {
1995
- report.steps.push({
1996
- name: step.command,
1997
- type: "command",
1998
- status: "pass",
1999
- result: result.value,
2000
- durationMs
2001
- });
2002
- } else if (result.error.message === TIMEOUT_ERROR_MESSAGE) {
2003
- report.steps.push({
2004
- name: step.command,
2005
- type: "command",
2006
- status: "skipped",
2007
- error: "timed out",
2008
- durationMs
2009
- });
2010
- report.status = "partial";
2011
- skipRemaining(activeSteps, i + 1, report);
2012
- break;
2013
- } else {
2014
- report.steps.push({
2015
- name: step.command,
2016
- type: "command",
2017
- status: "fail",
2018
- error: result.error.message,
2019
- durationMs
2020
- });
2021
- report.status = "fail";
2022
- skipRemaining(activeSteps, i + 1, report);
2023
- break;
2024
- }
2025
- } else {
2026
- const skillContext = {
2027
- trigger: resolvedTrigger,
2028
- projectPath: context.projectPath,
2029
- outputMode: step.output ?? "auto",
2030
- ...handoff ? { handoff } : {}
2031
- };
2032
- const SKILL_TIMEOUT_RESULT = {
2033
- status: "fail",
2034
- output: "timed out",
2035
- durationMs: 0
2036
- };
2037
- const result = await Promise.race([
2038
- context.skillExecutor(step.skill, skillContext),
2039
- new Promise(
2040
- (resolve24) => setTimeout(() => resolve24(SKILL_TIMEOUT_RESULT), remainingTime)
2041
- )
2042
- ]);
2043
- const durationMs = Date.now() - stepStart;
2044
- if (result === SKILL_TIMEOUT_RESULT) {
2045
- report.steps.push({
2046
- name: step.skill,
2047
- type: "skill",
2048
- status: "skipped",
2049
- error: "timed out",
2050
- durationMs
2051
- });
2052
- report.status = "partial";
2053
- skipRemaining(activeSteps, i + 1, report);
2054
- break;
2055
- } else if (result.status === "pass") {
2056
- report.steps.push({
2057
- name: step.skill,
2058
- type: "skill",
2059
- status: "pass",
2060
- result: result.output,
2061
- ...result.artifactPath ? { artifactPath: result.artifactPath } : {},
2062
- durationMs
2063
- });
2064
- } else {
2065
- report.steps.push({
2066
- name: step.skill,
2067
- type: "skill",
2068
- status: "fail",
2069
- error: result.output,
2070
- durationMs
2071
- });
2072
- report.status = "fail";
2073
- skipRemaining(activeSteps, i + 1, report);
2074
- break;
2075
- }
2076
- }
2077
- }
2078
- report.totalDurationMs = Date.now() - startTime;
2079
- return report;
2080
- }
2081
-
2082
- // src/persona/skill-executor.ts
2083
- import * as fs8 from "fs";
2084
- import * as path16 from "path";
2085
- import { parse as parse2 } from "yaml";
2086
- function resolveOutputMode(mode, trigger) {
2087
- if (mode !== "auto") return mode;
2088
- return trigger === "manual" ? "inline" : "artifact";
2089
- }
2090
- function buildArtifactPath(projectPath, headSha) {
2091
- const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2092
- const sha = headSha?.slice(0, 7) ?? "unknown";
2093
- return path16.join(projectPath, ".harness", "reviews", `${date}-${sha}.md`);
2094
- }
2095
- function buildArtifactContent(skillName, trigger, headSha) {
2096
- const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2097
- return [
2098
- "---",
2099
- `skill: ${skillName}`,
2100
- `trigger: ${trigger}`,
2101
- `sha: ${headSha?.slice(0, 7) ?? "unknown"}`,
2102
- `date: ${date}`,
2103
- `assessment: pending`,
2104
- "---",
2105
- "",
2106
- `# Review by ${skillName}`,
2107
- "",
2108
- "## Strengths",
2109
- "",
2110
- "- (review pending)",
2111
- "",
2112
- "## Issues",
2113
- "",
2114
- "### Critical",
2115
- "",
2116
- "- None identified",
2117
- "",
2118
- "### Important",
2119
- "",
2120
- "- None identified",
2121
- "",
2122
- "### Suggestions",
2123
- "",
2124
- "- None identified",
2125
- "",
2126
- "## Assessment",
2127
- "",
2128
- "Pending \u2014 skill execution scaffolded.",
2129
- "",
2130
- "## Harness Checks",
2131
- "",
2132
- "- (run harness validate, check-deps, check-docs to populate)",
2133
- ""
2134
- ].join("\n");
2135
- }
2136
- async function executeSkill(skillName, context) {
2137
- const startTime = Date.now();
2138
- const skillsDir = resolveSkillsDir();
2139
- const skillDir = path16.join(skillsDir, skillName);
2140
- if (!fs8.existsSync(skillDir)) {
2141
- return {
2142
- status: "fail",
2143
- output: `Skill not found: ${skillName}`,
2144
- durationMs: Date.now() - startTime
2145
- };
2146
- }
2147
- const yamlPath = path16.join(skillDir, "skill.yaml");
2148
- if (!fs8.existsSync(yamlPath)) {
2149
- return {
2150
- status: "fail",
2151
- output: `skill.yaml not found for ${skillName}`,
2152
- durationMs: Date.now() - startTime
2153
- };
2154
- }
2155
- const raw = fs8.readFileSync(yamlPath, "utf-8");
2156
- const parsed = parse2(raw);
2157
- const metadataResult = SkillMetadataSchema.safeParse(parsed);
2158
- if (!metadataResult.success) {
2159
- return {
2160
- status: "fail",
2161
- output: `Invalid skill metadata: ${metadataResult.error.message}`,
2162
- durationMs: Date.now() - startTime
2163
- };
2164
- }
2165
- const skillMdPath = path16.join(skillDir, "SKILL.md");
2166
- if (!fs8.existsSync(skillMdPath)) {
2167
- return {
2168
- status: "fail",
2169
- output: `SKILL.md not found for ${skillName}`,
2170
- durationMs: Date.now() - startTime
2171
- };
2172
- }
2173
- const skillContent = fs8.readFileSync(skillMdPath, "utf-8");
2174
- const metadata = metadataResult.data;
2175
- const resolvedMode = resolveOutputMode(context.outputMode, context.trigger);
2176
- const output = `Skill ${metadata.name} (${metadata.type}) loaded.
2177
- Cognitive mode: ${metadata.cognitive_mode ?? "default"}
2178
- Content length: ${skillContent.length} chars
2179
- Trigger: ${context.trigger}
2180
- `;
2181
- let artifactPath;
2182
- if (resolvedMode === "artifact") {
2183
- artifactPath = buildArtifactPath(context.projectPath, context.headSha);
2184
- const artifactContent = buildArtifactContent(skillName, context.trigger, context.headSha);
2185
- const dir = path16.dirname(artifactPath);
2186
- fs8.mkdirSync(dir, { recursive: true });
2187
- fs8.writeFileSync(artifactPath, artifactContent, "utf-8");
2188
- }
2189
- return {
2190
- status: "pass",
2191
- output,
2192
- ...artifactPath ? { artifactPath } : {},
2193
- durationMs: Date.now() - startTime
2194
- };
2195
- }
2196
-
2197
- // src/persona/constants.ts
2198
- var ALLOWED_PERSONA_COMMANDS = /* @__PURE__ */ new Set([
2199
- "validate",
2200
- "check-deps",
2201
- "check-docs",
2202
- "check-perf",
2203
- "check-security",
2204
- "cleanup",
2205
- "fix-drift",
2206
- "add"
2207
- ]);
2208
-
2209
- // src/commands/agent/run.ts
2210
- async function runAgentTask(task, options) {
2211
- const configResult = resolveConfig(options.configPath);
2212
- if (!configResult.ok) {
2213
- return configResult;
2214
- }
2215
- const agentTypeMap = {
2216
- review: "architecture-enforcer",
2217
- "doc-review": "documentation-maintainer",
2218
- "test-review": "test-reviewer"
2219
- };
2220
- const agentType = agentTypeMap[task];
2221
- if (!agentType) {
2222
- return Err(
2223
- new CLIError(
2224
- `Unknown task: ${task}. Available: ${Object.keys(agentTypeMap).join(", ")}`,
2225
- ExitCode.ERROR
2226
- )
2227
- );
2228
- }
2229
- const config = configResult.value;
2230
- const timeout = options.timeout ?? config.agent?.timeout ?? 3e5;
2231
- const reviewResult = await requestPeerReview(
2232
- agentType,
2233
- {
2234
- files: [],
2235
- diff: "",
2236
- commitMessage: task,
2237
- metadata: { task, timeout }
2238
- },
2239
- { timeout }
2240
- );
2241
- if (!reviewResult.ok) {
2242
- return Err(new CLIError(`Agent task failed: ${reviewResult.error.message}`, ExitCode.ERROR));
1297
+ const config = configResult.value;
1298
+ const timeout = options.timeout ?? config.agent?.timeout ?? 3e5;
1299
+ const reviewResult = await requestPeerReview(
1300
+ agentType,
1301
+ {
1302
+ files: [],
1303
+ diff: "",
1304
+ commitMessage: task,
1305
+ metadata: { task, timeout }
1306
+ },
1307
+ { timeout }
1308
+ );
1309
+ if (!reviewResult.ok) {
1310
+ return Err(new CLIError(`Agent task failed: ${reviewResult.error.message}`, ExitCode.ERROR));
2243
1311
  }
2244
1312
  const review = reviewResult.value;
2245
1313
  return Ok({
@@ -2263,7 +1331,7 @@ function createRunCommand() {
2263
1331
  const globalOpts = cmd.optsWithGlobals();
2264
1332
  if (opts.persona) {
2265
1333
  const personasDir = resolvePersonasDir();
2266
- const filePath = path17.join(personasDir, `${opts.persona}.yaml`);
1334
+ const filePath = path11.join(personasDir, `${opts.persona}.yaml`);
2267
1335
  const personaResult = loadPersona(filePath);
2268
1336
  if (!personaResult.ok) {
2269
1337
  logger.error(personaResult.error.message);
@@ -2444,8 +1512,8 @@ function createAgentCommand() {
2444
1512
 
2445
1513
  // src/commands/add.ts
2446
1514
  import { Command as Command14 } from "commander";
2447
- import * as fs9 from "fs";
2448
- import * as path18 from "path";
1515
+ import * as fs3 from "fs";
1516
+ import * as path12 from "path";
2449
1517
  var LAYER_INDEX_TEMPLATE = (name) => `// ${name} layer
2450
1518
  // Add your ${name} exports here
2451
1519
 
@@ -2489,61 +1557,61 @@ async function runAdd(componentType, name, options) {
2489
1557
  try {
2490
1558
  switch (componentType) {
2491
1559
  case "layer": {
2492
- const layerDir = path18.join(cwd, "src", name);
2493
- if (!fs9.existsSync(layerDir)) {
2494
- fs9.mkdirSync(layerDir, { recursive: true });
1560
+ const layerDir = path12.join(cwd, "src", name);
1561
+ if (!fs3.existsSync(layerDir)) {
1562
+ fs3.mkdirSync(layerDir, { recursive: true });
2495
1563
  created.push(`src/${name}/`);
2496
1564
  }
2497
- const indexPath = path18.join(layerDir, "index.ts");
2498
- if (!fs9.existsSync(indexPath)) {
2499
- fs9.writeFileSync(indexPath, LAYER_INDEX_TEMPLATE(name));
1565
+ const indexPath = path12.join(layerDir, "index.ts");
1566
+ if (!fs3.existsSync(indexPath)) {
1567
+ fs3.writeFileSync(indexPath, LAYER_INDEX_TEMPLATE(name));
2500
1568
  created.push(`src/${name}/index.ts`);
2501
1569
  }
2502
1570
  break;
2503
1571
  }
2504
1572
  case "module": {
2505
- const modulePath = path18.join(cwd, "src", `${name}.ts`);
2506
- if (fs9.existsSync(modulePath)) {
1573
+ const modulePath = path12.join(cwd, "src", `${name}.ts`);
1574
+ if (fs3.existsSync(modulePath)) {
2507
1575
  return Err(new CLIError(`Module ${name} already exists`, ExitCode.ERROR));
2508
1576
  }
2509
- fs9.writeFileSync(modulePath, MODULE_TEMPLATE(name));
1577
+ fs3.writeFileSync(modulePath, MODULE_TEMPLATE(name));
2510
1578
  created.push(`src/${name}.ts`);
2511
1579
  break;
2512
1580
  }
2513
1581
  case "doc": {
2514
- const docsDir = path18.join(cwd, "docs");
2515
- if (!fs9.existsSync(docsDir)) {
2516
- fs9.mkdirSync(docsDir, { recursive: true });
1582
+ const docsDir = path12.join(cwd, "docs");
1583
+ if (!fs3.existsSync(docsDir)) {
1584
+ fs3.mkdirSync(docsDir, { recursive: true });
2517
1585
  }
2518
- const docPath = path18.join(docsDir, `${name}.md`);
2519
- if (fs9.existsSync(docPath)) {
1586
+ const docPath = path12.join(docsDir, `${name}.md`);
1587
+ if (fs3.existsSync(docPath)) {
2520
1588
  return Err(new CLIError(`Doc ${name} already exists`, ExitCode.ERROR));
2521
1589
  }
2522
- fs9.writeFileSync(docPath, DOC_TEMPLATE(name));
1590
+ fs3.writeFileSync(docPath, DOC_TEMPLATE(name));
2523
1591
  created.push(`docs/${name}.md`);
2524
1592
  break;
2525
1593
  }
2526
1594
  case "skill": {
2527
- const { generateSkillFiles: generateSkillFiles2 } = await import("./create-skill-UZOHMXRU.js");
1595
+ const { generateSkillFiles: generateSkillFiles2 } = await import("./create-skill-LUWO46WF.js");
2528
1596
  generateSkillFiles2({
2529
1597
  name,
2530
1598
  description: `${name} skill`,
2531
- outputDir: path18.join(cwd, "agents", "skills", "claude-code")
1599
+ outputDir: path12.join(cwd, "agents", "skills", "claude-code")
2532
1600
  });
2533
1601
  created.push(`agents/skills/claude-code/${name}/skill.yaml`);
2534
1602
  created.push(`agents/skills/claude-code/${name}/SKILL.md`);
2535
1603
  break;
2536
1604
  }
2537
1605
  case "persona": {
2538
- const personasDir = path18.join(cwd, "agents", "personas");
2539
- if (!fs9.existsSync(personasDir)) {
2540
- fs9.mkdirSync(personasDir, { recursive: true });
1606
+ const personasDir = path12.join(cwd, "agents", "personas");
1607
+ if (!fs3.existsSync(personasDir)) {
1608
+ fs3.mkdirSync(personasDir, { recursive: true });
2541
1609
  }
2542
- const personaPath = path18.join(personasDir, `${name}.yaml`);
2543
- if (fs9.existsSync(personaPath)) {
1610
+ const personaPath = path12.join(personasDir, `${name}.yaml`);
1611
+ if (fs3.existsSync(personaPath)) {
2544
1612
  return Err(new CLIError(`Persona ${name} already exists`, ExitCode.ERROR));
2545
1613
  }
2546
- fs9.writeFileSync(
1614
+ fs3.writeFileSync(
2547
1615
  personaPath,
2548
1616
  `name: ${name}
2549
1617
  description: ${name} persona
@@ -2601,346 +1669,6 @@ import { Command as Command17 } from "commander";
2601
1669
 
2602
1670
  // src/commands/linter/generate.ts
2603
1671
  import { Command as Command15 } from "commander";
2604
-
2605
- // ../linter-gen/dist/generator/orchestrator.js
2606
- import * as fs12 from "fs/promises";
2607
- import * as path21 from "path";
2608
-
2609
- // ../linter-gen/dist/parser/config-parser.js
2610
- import * as fs10 from "fs/promises";
2611
- import * as yaml from "yaml";
2612
-
2613
- // ../linter-gen/dist/schema/linter-config.js
2614
- import { z as z4 } from "zod";
2615
- var RuleConfigSchema = z4.object({
2616
- /** Rule name in kebab-case (e.g., 'no-ui-in-services') */
2617
- name: z4.string().regex(/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/, "Rule name must be kebab-case"),
2618
- /** Rule type - determines which template to use */
2619
- type: z4.string().min(1),
2620
- /** ESLint severity level */
2621
- severity: z4.enum(["error", "warn", "off"]).default("error"),
2622
- /** Template-specific configuration */
2623
- config: z4.record(z4.unknown())
2624
- });
2625
- var LinterConfigSchema = z4.object({
2626
- /** Config version - currently only 1 is supported */
2627
- version: z4.literal(1),
2628
- /** Output directory for generated rules */
2629
- output: z4.string().min(1),
2630
- /** Optional explicit template path mappings (type → path) */
2631
- templates: z4.record(z4.string()).optional(),
2632
- /** Rules to generate */
2633
- rules: z4.array(RuleConfigSchema).min(1, "At least one rule is required")
2634
- });
2635
-
2636
- // ../linter-gen/dist/parser/config-parser.js
2637
- var ParseError = class extends Error {
2638
- code;
2639
- cause;
2640
- constructor(message, code, cause) {
2641
- super(message);
2642
- this.code = code;
2643
- this.cause = cause;
2644
- this.name = "ParseError";
2645
- }
2646
- };
2647
- async function parseConfig(configPath) {
2648
- let content;
2649
- try {
2650
- content = await fs10.readFile(configPath, "utf-8");
2651
- } catch (err) {
2652
- if (err.code === "ENOENT") {
2653
- return {
2654
- success: false,
2655
- error: new ParseError(`Config file not found: ${configPath}`, "FILE_NOT_FOUND", err)
2656
- };
2657
- }
2658
- return {
2659
- success: false,
2660
- error: new ParseError(`Failed to read config file: ${configPath}`, "FILE_READ_ERROR", err)
2661
- };
2662
- }
2663
- let parsed;
2664
- try {
2665
- parsed = yaml.parse(content);
2666
- } catch (err) {
2667
- return {
2668
- success: false,
2669
- error: new ParseError(`Invalid YAML syntax in ${configPath}: ${err.message}`, "YAML_PARSE_ERROR", err)
2670
- };
2671
- }
2672
- const result = LinterConfigSchema.safeParse(parsed);
2673
- if (!result.success) {
2674
- const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
2675
- return {
2676
- success: false,
2677
- error: new ParseError(`Invalid config: ${issues}`, "VALIDATION_ERROR", result.error)
2678
- };
2679
- }
2680
- return {
2681
- success: true,
2682
- data: result.data,
2683
- configPath
2684
- };
2685
- }
2686
-
2687
- // ../linter-gen/dist/engine/template-loader.js
2688
- import * as fs11 from "fs/promises";
2689
- import * as path19 from "path";
2690
- import { fileURLToPath as fileURLToPath2 } from "url";
2691
- var __filename2 = fileURLToPath2(import.meta.url);
2692
- var __dirname2 = path19.dirname(__filename2);
2693
- var TemplateLoadError = class extends Error {
2694
- code;
2695
- cause;
2696
- constructor(message, code, cause) {
2697
- super(message);
2698
- this.code = code;
2699
- this.cause = cause;
2700
- this.name = "TemplateLoadError";
2701
- }
2702
- };
2703
- var BUILTIN_TEMPLATES = ["import-restriction", "boundary-validation", "dependency-graph"];
2704
- async function fileExists(filePath) {
2705
- try {
2706
- await fs11.access(filePath);
2707
- return true;
2708
- } catch {
2709
- return false;
2710
- }
2711
- }
2712
- async function loadTemplateFile(filePath, type) {
2713
- try {
2714
- const content = await fs11.readFile(filePath, "utf-8");
2715
- return {
2716
- success: true,
2717
- source: { type, path: filePath, content }
2718
- };
2719
- } catch (err) {
2720
- return {
2721
- success: false,
2722
- error: new TemplateLoadError(`Failed to read template: ${filePath}`, "TEMPLATE_READ_ERROR", err)
2723
- };
2724
- }
2725
- }
2726
- async function loadTemplate(ruleType, templatesConfig, configDir) {
2727
- if (templatesConfig?.[ruleType]) {
2728
- const explicitPath = path19.resolve(configDir, templatesConfig[ruleType]);
2729
- if (await fileExists(explicitPath)) {
2730
- return loadTemplateFile(explicitPath, "explicit");
2731
- }
2732
- return {
2733
- success: false,
2734
- error: new TemplateLoadError(`Explicit template not found: ${explicitPath}`, "TEMPLATE_NOT_FOUND")
2735
- };
2736
- }
2737
- const conventionPath = path19.join(configDir, "templates", `${ruleType}.ts.hbs`);
2738
- if (await fileExists(conventionPath)) {
2739
- return loadTemplateFile(conventionPath, "convention");
2740
- }
2741
- if (BUILTIN_TEMPLATES.includes(ruleType)) {
2742
- const builtinPath = path19.join(__dirname2, "..", "templates", `${ruleType}.ts.hbs`);
2743
- if (await fileExists(builtinPath)) {
2744
- return loadTemplateFile(builtinPath, "builtin");
2745
- }
2746
- }
2747
- return {
2748
- success: false,
2749
- error: new TemplateLoadError(`Template not found for type '${ruleType}'. Checked: explicit config, ./templates/${ruleType}.ts.hbs, built-in templates.`, "TEMPLATE_NOT_FOUND")
2750
- };
2751
- }
2752
-
2753
- // ../linter-gen/dist/generator/rule-generator.js
2754
- import * as path20 from "path";
2755
-
2756
- // ../linter-gen/dist/engine/context-builder.js
2757
- var GENERATOR_VERSION = "0.1.0";
2758
- function toCamelCase(str) {
2759
- return str.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase());
2760
- }
2761
- function toPascalCase(str) {
2762
- const camel = toCamelCase(str);
2763
- return camel.charAt(0).toUpperCase() + camel.slice(1);
2764
- }
2765
- function buildRuleContext(rule, configPath) {
2766
- return {
2767
- name: rule.name,
2768
- nameCamel: toCamelCase(rule.name),
2769
- namePascal: toPascalCase(rule.name),
2770
- severity: rule.severity,
2771
- config: rule.config,
2772
- meta: {
2773
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2774
- generatorVersion: GENERATOR_VERSION,
2775
- configPath
2776
- }
2777
- };
2778
- }
2779
-
2780
- // ../linter-gen/dist/engine/template-renderer.js
2781
- import Handlebars2 from "handlebars";
2782
- var TemplateError = class extends Error {
2783
- cause;
2784
- constructor(message, cause) {
2785
- super(message);
2786
- this.cause = cause;
2787
- this.name = "TemplateError";
2788
- }
2789
- };
2790
- function toCamelCase2(str) {
2791
- return str.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase());
2792
- }
2793
- function toPascalCase2(str) {
2794
- const camel = toCamelCase2(str);
2795
- return camel.charAt(0).toUpperCase() + camel.slice(1);
2796
- }
2797
- Handlebars2.registerHelper("json", (obj) => JSON.stringify(obj));
2798
- Handlebars2.registerHelper("jsonPretty", (obj) => JSON.stringify(obj, null, 2));
2799
- Handlebars2.registerHelper("camelCase", (str) => toCamelCase2(str));
2800
- Handlebars2.registerHelper("pascalCase", (str) => toPascalCase2(str));
2801
- function renderTemplate(templateSource, context) {
2802
- try {
2803
- const compiled = Handlebars2.compile(templateSource, { strict: true });
2804
- const output = compiled(context);
2805
- return { success: true, output };
2806
- } catch (err) {
2807
- return {
2808
- success: false,
2809
- error: new TemplateError(`Template rendering failed: ${err.message}`, err)
2810
- };
2811
- }
2812
- }
2813
-
2814
- // ../linter-gen/dist/generator/rule-generator.js
2815
- function generateRule(rule, template, outputDir, configPath) {
2816
- const context = buildRuleContext(rule, configPath);
2817
- const renderResult = renderTemplate(template.content, context);
2818
- if (!renderResult.success) {
2819
- return {
2820
- success: false,
2821
- error: renderResult.error,
2822
- ruleName: rule.name
2823
- };
2824
- }
2825
- const outputPath = path20.join(outputDir, `${rule.name}.ts`);
2826
- return {
2827
- success: true,
2828
- rule: {
2829
- name: rule.name,
2830
- outputPath,
2831
- content: renderResult.output
2832
- }
2833
- };
2834
- }
2835
-
2836
- // ../linter-gen/dist/generator/index-generator.js
2837
- function toCamelCase3(str) {
2838
- return str.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase());
2839
- }
2840
- function generateIndex(ruleNames) {
2841
- const imports = ruleNames.map((name) => {
2842
- const camel = toCamelCase3(name);
2843
- return `import ${camel} from './${name}';`;
2844
- }).join("\n");
2845
- const rulesObject = ruleNames.map((name) => {
2846
- const camel = toCamelCase3(name);
2847
- return ` '${name}': ${camel},`;
2848
- }).join("\n");
2849
- const namedExports = ruleNames.map(toCamelCase3).join(", ");
2850
- return `// Generated by @harness-engineering/linter-gen
2851
- // Do not edit manually - regenerate from harness-linter.yml
2852
-
2853
- ${imports}
2854
-
2855
- export const rules = {
2856
- ${rulesObject}
2857
- };
2858
-
2859
- export { ${namedExports} };
2860
- `;
2861
- }
2862
-
2863
- // ../linter-gen/dist/generator/orchestrator.js
2864
- async function validate(options) {
2865
- const parseResult = await parseConfig(options.configPath);
2866
- if (!parseResult.success) {
2867
- return { success: false, error: parseResult.error };
2868
- }
2869
- return { success: true, ruleCount: parseResult.data.rules.length };
2870
- }
2871
- async function generate(options) {
2872
- const errors = [];
2873
- const parseResult = await parseConfig(options.configPath);
2874
- if (!parseResult.success) {
2875
- return { success: false, errors: [{ type: "parse", error: parseResult.error }] };
2876
- }
2877
- const config = parseResult.data;
2878
- const configDir = path21.dirname(path21.resolve(options.configPath));
2879
- const outputDir = options.outputDir ? path21.resolve(options.outputDir) : path21.resolve(configDir, config.output);
2880
- if (options.clean && !options.dryRun) {
2881
- try {
2882
- await fs12.rm(outputDir, { recursive: true, force: true });
2883
- } catch {
2884
- }
2885
- }
2886
- if (!options.dryRun) {
2887
- await fs12.mkdir(outputDir, { recursive: true });
2888
- }
2889
- const generatedRules = [];
2890
- for (const rule of config.rules) {
2891
- const templateResult = await loadTemplate(rule.type, config.templates, configDir);
2892
- if (!templateResult.success) {
2893
- errors.push({
2894
- type: "template",
2895
- error: templateResult.error,
2896
- ruleName: rule.name
2897
- });
2898
- continue;
2899
- }
2900
- const ruleResult = generateRule(rule, templateResult.source, outputDir, options.configPath);
2901
- if (!ruleResult.success) {
2902
- errors.push({
2903
- type: "render",
2904
- error: ruleResult.error,
2905
- ruleName: ruleResult.ruleName
2906
- });
2907
- continue;
2908
- }
2909
- if (!options.dryRun) {
2910
- try {
2911
- await fs12.writeFile(ruleResult.rule.outputPath, ruleResult.rule.content, "utf-8");
2912
- } catch (err) {
2913
- errors.push({
2914
- type: "write",
2915
- error: err,
2916
- path: ruleResult.rule.outputPath
2917
- });
2918
- continue;
2919
- }
2920
- }
2921
- generatedRules.push(rule.name);
2922
- }
2923
- if (generatedRules.length > 0 && !options.dryRun) {
2924
- const indexContent = generateIndex(generatedRules);
2925
- const indexPath = path21.join(outputDir, "index.ts");
2926
- try {
2927
- await fs12.writeFile(indexPath, indexContent, "utf-8");
2928
- } catch (err) {
2929
- errors.push({ type: "write", error: err, path: indexPath });
2930
- }
2931
- }
2932
- if (errors.length > 0) {
2933
- return { success: false, errors };
2934
- }
2935
- return {
2936
- success: true,
2937
- rulesGenerated: generatedRules,
2938
- outputDir,
2939
- dryRun: options.dryRun ?? false
2940
- };
2941
- }
2942
-
2943
- // src/commands/linter/generate.ts
2944
1672
  function createGenerateCommand() {
2945
1673
  return new Command15("generate").description("Generate ESLint rules from harness-linter.yml").option("-c, --config <path>", "Path to harness-linter.yml", "./harness-linter.yml").option("-o, --output <dir>", "Override output directory").option("--clean", "Remove existing files before generating").option("--dry-run", "Preview without writing files").option("--json", "Output as JSON").option("--verbose", "Show detailed output").action(async (options) => {
2946
1674
  try {
@@ -3069,176 +1797,46 @@ function createListCommand() {
3069
1797
 
3070
1798
  // src/commands/persona/generate.ts
3071
1799
  import { Command as Command19 } from "commander";
3072
- import * as fs13 from "fs";
3073
- import * as path22 from "path";
3074
-
3075
- // src/utils/string.ts
3076
- function toKebabCase(name) {
3077
- return name.toLowerCase().replace(/\s+/g, "-");
3078
- }
3079
-
3080
- // src/persona/generators/runtime.ts
3081
- function generateRuntime(persona) {
3082
- try {
3083
- const config = {
3084
- name: toKebabCase(persona.name),
3085
- skills: persona.skills,
3086
- steps: persona.steps,
3087
- timeout: persona.config.timeout,
3088
- severity: persona.config.severity
3089
- };
3090
- return Ok(JSON.stringify(config, null, 2));
3091
- } catch (error) {
3092
- return Err(
3093
- new Error(
3094
- `Failed to generate runtime config: ${error instanceof Error ? error.message : String(error)}`
3095
- )
3096
- );
3097
- }
3098
- }
3099
-
3100
- // src/persona/generators/agents-md.ts
3101
- function formatTrigger(trigger) {
3102
- switch (trigger.event) {
3103
- case "on_pr": {
3104
- const paths = trigger.conditions?.paths?.join(", ") ?? "all files";
3105
- return `On PR (${paths})`;
1800
+ import * as fs4 from "fs";
1801
+ import * as path13 from "path";
1802
+ function createGenerateCommand2() {
1803
+ return new Command19("generate").description("Generate artifacts from a persona config").argument("<name>", "Persona name (e.g., architecture-enforcer)").option("--output-dir <dir>", "Output directory", ".").option("--only <type>", "Generate only: ci, agents-md, runtime").action(async (name, opts, cmd) => {
1804
+ const globalOpts = cmd.optsWithGlobals();
1805
+ const personasDir = resolvePersonasDir();
1806
+ const filePath = path13.join(personasDir, `${name}.yaml`);
1807
+ const personaResult = loadPersona(filePath);
1808
+ if (!personaResult.ok) {
1809
+ logger.error(personaResult.error.message);
1810
+ process.exit(ExitCode.ERROR);
3106
1811
  }
3107
- case "on_commit": {
3108
- const branches = trigger.conditions?.branches?.join(", ") ?? "all branches";
3109
- return `On commit (${branches})`;
3110
- }
3111
- case "scheduled":
3112
- return `Scheduled (cron: ${trigger.cron})`;
3113
- case "manual":
3114
- return "Manual";
3115
- }
3116
- }
3117
- function generateAgentsMd(persona) {
3118
- try {
3119
- const triggers = persona.triggers.map(formatTrigger).join(", ");
3120
- const skills = persona.skills.join(", ");
3121
- const commands = persona.steps.filter((s) => "command" in s).map((s) => `\`harness ${s.command}\``).join(", ");
3122
- const stepSkills = persona.steps.filter((s) => "skill" in s).map((s) => `\`harness skill run ${s.skill}\``).join(", ");
3123
- const allCommands = [commands, stepSkills].filter(Boolean).join(", ");
3124
- const fragment = `## ${persona.name} Agent
3125
-
3126
- **Role:** ${persona.role}
3127
-
3128
- **Triggers:** ${triggers}
3129
-
3130
- **Skills:** ${skills}
3131
-
3132
- **When this agent flags an issue:** Fix violations before merging. Run ${allCommands} locally to validate.
3133
- `;
3134
- return Ok(fragment);
3135
- } catch (error) {
3136
- return Err(
3137
- new Error(
3138
- `Failed to generate AGENTS.md fragment: ${error instanceof Error ? error.message : String(error)}`
3139
- )
3140
- );
3141
- }
3142
- }
3143
-
3144
- // src/persona/generators/ci-workflow.ts
3145
- import YAML2 from "yaml";
3146
- function buildGitHubTriggers(triggers) {
3147
- const on = {};
3148
- for (const trigger of triggers) {
3149
- switch (trigger.event) {
3150
- case "on_pr": {
3151
- const prConfig = {};
3152
- if (trigger.conditions?.paths) prConfig.paths = trigger.conditions.paths;
3153
- on.pull_request = prConfig;
3154
- break;
3155
- }
3156
- case "on_commit": {
3157
- const pushConfig = {};
3158
- if (trigger.conditions?.branches) pushConfig.branches = trigger.conditions.branches;
3159
- on.push = pushConfig;
3160
- break;
3161
- }
3162
- case "scheduled":
3163
- on.schedule = [{ cron: trigger.cron }];
3164
- break;
3165
- }
3166
- }
3167
- return on;
3168
- }
3169
- function generateCIWorkflow(persona, platform) {
3170
- try {
3171
- if (platform === "gitlab") return Err(new Error("GitLab CI generation is not yet supported"));
3172
- const severity = persona.config.severity;
3173
- const steps = [
3174
- { uses: "actions/checkout@v4" },
3175
- { uses: "actions/setup-node@v4", with: { "node-version": "20" } },
3176
- { uses: "pnpm/action-setup@v4", with: { run_install: "frozen" } }
3177
- ];
3178
- const commandSteps = persona.steps.filter((s) => "command" in s);
3179
- for (const step of commandSteps) {
3180
- const severityFlag = severity ? ` --severity ${severity}` : "";
3181
- steps.push({ run: `npx harness ${step.command}${severityFlag}` });
3182
- }
3183
- const workflow = {
3184
- name: persona.name,
3185
- on: buildGitHubTriggers(persona.triggers),
3186
- jobs: {
3187
- enforce: {
3188
- "runs-on": "ubuntu-latest",
3189
- steps
3190
- }
3191
- }
3192
- };
3193
- return Ok(YAML2.stringify(workflow, { lineWidth: 0 }));
3194
- } catch (error) {
3195
- return Err(
3196
- new Error(
3197
- `Failed to generate CI workflow: ${error instanceof Error ? error.message : String(error)}`
3198
- )
3199
- );
3200
- }
3201
- }
3202
-
3203
- // src/commands/persona/generate.ts
3204
- function createGenerateCommand2() {
3205
- return new Command19("generate").description("Generate artifacts from a persona config").argument("<name>", "Persona name (e.g., architecture-enforcer)").option("--output-dir <dir>", "Output directory", ".").option("--only <type>", "Generate only: ci, agents-md, runtime").action(async (name, opts, cmd) => {
3206
- const globalOpts = cmd.optsWithGlobals();
3207
- const personasDir = resolvePersonasDir();
3208
- const filePath = path22.join(personasDir, `${name}.yaml`);
3209
- const personaResult = loadPersona(filePath);
3210
- if (!personaResult.ok) {
3211
- logger.error(personaResult.error.message);
3212
- process.exit(ExitCode.ERROR);
3213
- }
3214
- const persona = personaResult.value;
3215
- const outputDir = path22.resolve(opts.outputDir);
3216
- const slug = toKebabCase(persona.name);
3217
- const only = opts.only;
3218
- const generated = [];
3219
- if (!only || only === "runtime") {
3220
- const result = generateRuntime(persona);
3221
- if (result.ok) {
3222
- const outPath = path22.join(outputDir, `${slug}.runtime.json`);
3223
- fs13.mkdirSync(path22.dirname(outPath), { recursive: true });
3224
- fs13.writeFileSync(outPath, result.value);
3225
- generated.push(outPath);
3226
- }
1812
+ const persona = personaResult.value;
1813
+ const outputDir = path13.resolve(opts.outputDir);
1814
+ const slug = toKebabCase(persona.name);
1815
+ const only = opts.only;
1816
+ const generated = [];
1817
+ if (!only || only === "runtime") {
1818
+ const result = generateRuntime(persona);
1819
+ if (result.ok) {
1820
+ const outPath = path13.join(outputDir, `${slug}.runtime.json`);
1821
+ fs4.mkdirSync(path13.dirname(outPath), { recursive: true });
1822
+ fs4.writeFileSync(outPath, result.value);
1823
+ generated.push(outPath);
1824
+ }
3227
1825
  }
3228
1826
  if (!only || only === "agents-md") {
3229
1827
  const result = generateAgentsMd(persona);
3230
1828
  if (result.ok) {
3231
- const outPath = path22.join(outputDir, `${slug}.agents.md`);
3232
- fs13.writeFileSync(outPath, result.value);
1829
+ const outPath = path13.join(outputDir, `${slug}.agents.md`);
1830
+ fs4.writeFileSync(outPath, result.value);
3233
1831
  generated.push(outPath);
3234
1832
  }
3235
1833
  }
3236
1834
  if (!only || only === "ci") {
3237
1835
  const result = generateCIWorkflow(persona, "github");
3238
1836
  if (result.ok) {
3239
- const outPath = path22.join(outputDir, ".github", "workflows", `${slug}.yml`);
3240
- fs13.mkdirSync(path22.dirname(outPath), { recursive: true });
3241
- fs13.writeFileSync(outPath, result.value);
1837
+ const outPath = path13.join(outputDir, ".github", "workflows", `${slug}.yml`);
1838
+ fs4.mkdirSync(path13.dirname(outPath), { recursive: true });
1839
+ fs4.writeFileSync(outPath, result.value);
3242
1840
  generated.push(outPath);
3243
1841
  }
3244
1842
  }
@@ -3263,26 +1861,26 @@ import { Command as Command25 } from "commander";
3263
1861
 
3264
1862
  // src/commands/skill/list.ts
3265
1863
  import { Command as Command21 } from "commander";
3266
- import * as fs14 from "fs";
3267
- import * as path23 from "path";
3268
- import { parse as parse4 } from "yaml";
1864
+ import * as fs5 from "fs";
1865
+ import * as path14 from "path";
1866
+ import { parse } from "yaml";
3269
1867
  function createListCommand2() {
3270
1868
  return new Command21("list").description("List available skills").action(async (_opts, cmd) => {
3271
1869
  const globalOpts = cmd.optsWithGlobals();
3272
1870
  const skillsDir = resolveSkillsDir();
3273
- if (!fs14.existsSync(skillsDir)) {
1871
+ if (!fs5.existsSync(skillsDir)) {
3274
1872
  logger.info("No skills directory found.");
3275
1873
  process.exit(ExitCode.SUCCESS);
3276
1874
  return;
3277
1875
  }
3278
- const entries = fs14.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1876
+ const entries = fs5.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
3279
1877
  const skills = [];
3280
1878
  for (const name of entries) {
3281
- const yamlPath = path23.join(skillsDir, name, "skill.yaml");
3282
- if (!fs14.existsSync(yamlPath)) continue;
1879
+ const yamlPath = path14.join(skillsDir, name, "skill.yaml");
1880
+ if (!fs5.existsSync(yamlPath)) continue;
3283
1881
  try {
3284
- const raw = fs14.readFileSync(yamlPath, "utf-8");
3285
- const parsed = parse4(raw);
1882
+ const raw = fs5.readFileSync(yamlPath, "utf-8");
1883
+ const parsed = parse(raw);
3286
1884
  const result = SkillMetadataSchema.safeParse(parsed);
3287
1885
  if (result.success) {
3288
1886
  skills.push(result.data);
@@ -3312,9 +1910,9 @@ function createListCommand2() {
3312
1910
 
3313
1911
  // src/commands/skill/run.ts
3314
1912
  import { Command as Command22 } from "commander";
3315
- import * as fs15 from "fs";
3316
- import * as path24 from "path";
3317
- import { parse as parse5 } from "yaml";
1913
+ import * as fs6 from "fs";
1914
+ import * as path15 from "path";
1915
+ import { parse as parse2 } from "yaml";
3318
1916
 
3319
1917
  // src/skill/complexity.ts
3320
1918
  import { execFileSync as execFileSync2 } from "child_process";
@@ -3403,18 +2001,18 @@ ${options.priorState}`);
3403
2001
  function createRunCommand2() {
3404
2002
  return new Command22("run").description("Run a skill (outputs SKILL.md content with context preamble)").argument("<name>", "Skill name (e.g., harness-tdd)").option("--path <path>", "Project root path for context injection").option("--complexity <level>", "Complexity: auto, light, full", "auto").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").action(async (name, opts, _cmd) => {
3405
2003
  const skillsDir = resolveSkillsDir();
3406
- const skillDir = path24.join(skillsDir, name);
3407
- if (!fs15.existsSync(skillDir)) {
2004
+ const skillDir = path15.join(skillsDir, name);
2005
+ if (!fs6.existsSync(skillDir)) {
3408
2006
  logger.error(`Skill not found: ${name}`);
3409
2007
  process.exit(ExitCode.ERROR);
3410
2008
  return;
3411
2009
  }
3412
- const yamlPath = path24.join(skillDir, "skill.yaml");
2010
+ const yamlPath = path15.join(skillDir, "skill.yaml");
3413
2011
  let metadata = null;
3414
- if (fs15.existsSync(yamlPath)) {
2012
+ if (fs6.existsSync(yamlPath)) {
3415
2013
  try {
3416
- const raw = fs15.readFileSync(yamlPath, "utf-8");
3417
- const parsed = parse5(raw);
2014
+ const raw = fs6.readFileSync(yamlPath, "utf-8");
2015
+ const parsed = parse2(raw);
3418
2016
  const result = SkillMetadataSchema.safeParse(parsed);
3419
2017
  if (result.success) metadata = result.data;
3420
2018
  } catch {
@@ -3424,17 +2022,17 @@ function createRunCommand2() {
3424
2022
  if (metadata?.phases && metadata.phases.length > 0) {
3425
2023
  const requested = opts.complexity ?? "auto";
3426
2024
  if (requested === "auto") {
3427
- const projectPath2 = opts.path ? path24.resolve(opts.path) : process.cwd();
2025
+ const projectPath2 = opts.path ? path15.resolve(opts.path) : process.cwd();
3428
2026
  complexity = detectComplexity(projectPath2);
3429
2027
  } else {
3430
2028
  complexity = requested;
3431
2029
  }
3432
2030
  }
3433
2031
  let principles;
3434
- const projectPath = opts.path ? path24.resolve(opts.path) : process.cwd();
3435
- const principlesPath = path24.join(projectPath, "docs", "principles.md");
3436
- if (fs15.existsSync(principlesPath)) {
3437
- principles = fs15.readFileSync(principlesPath, "utf-8");
2032
+ const projectPath = opts.path ? path15.resolve(opts.path) : process.cwd();
2033
+ const principlesPath = path15.join(projectPath, "docs", "principles.md");
2034
+ if (fs6.existsSync(principlesPath)) {
2035
+ principles = fs6.readFileSync(principlesPath, "utf-8");
3438
2036
  }
3439
2037
  let priorState;
3440
2038
  let stateWarning;
@@ -3449,16 +2047,16 @@ function createRunCommand2() {
3449
2047
  }
3450
2048
  if (metadata?.state.persistent && metadata.state.files.length > 0) {
3451
2049
  for (const stateFilePath of metadata.state.files) {
3452
- const fullPath = path24.join(projectPath, stateFilePath);
3453
- if (fs15.existsSync(fullPath)) {
3454
- const stat = fs15.statSync(fullPath);
2050
+ const fullPath = path15.join(projectPath, stateFilePath);
2051
+ if (fs6.existsSync(fullPath)) {
2052
+ const stat = fs6.statSync(fullPath);
3455
2053
  if (stat.isDirectory()) {
3456
- const files = fs15.readdirSync(fullPath).map((f) => ({ name: f, mtime: fs15.statSync(path24.join(fullPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
2054
+ const files = fs6.readdirSync(fullPath).map((f) => ({ name: f, mtime: fs6.statSync(path15.join(fullPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
3457
2055
  if (files.length > 0) {
3458
- priorState = fs15.readFileSync(path24.join(fullPath, files[0].name), "utf-8");
2056
+ priorState = fs6.readFileSync(path15.join(fullPath, files[0].name), "utf-8");
3459
2057
  }
3460
2058
  } else {
3461
- priorState = fs15.readFileSync(fullPath, "utf-8");
2059
+ priorState = fs6.readFileSync(fullPath, "utf-8");
3462
2060
  }
3463
2061
  break;
3464
2062
  }
@@ -3477,17 +2075,17 @@ function createRunCommand2() {
3477
2075
  ...stateWarning !== void 0 && { stateWarning },
3478
2076
  party: opts.party
3479
2077
  });
3480
- const skillMdPath = path24.join(skillDir, "SKILL.md");
3481
- if (!fs15.existsSync(skillMdPath)) {
2078
+ const skillMdPath = path15.join(skillDir, "SKILL.md");
2079
+ if (!fs6.existsSync(skillMdPath)) {
3482
2080
  logger.error(`SKILL.md not found for skill: ${name}`);
3483
2081
  process.exit(ExitCode.ERROR);
3484
2082
  return;
3485
2083
  }
3486
- let content = fs15.readFileSync(skillMdPath, "utf-8");
2084
+ let content = fs6.readFileSync(skillMdPath, "utf-8");
3487
2085
  if (metadata?.state.persistent && opts.path) {
3488
- const stateFile = path24.join(projectPath, ".harness", "state.json");
3489
- if (fs15.existsSync(stateFile)) {
3490
- const stateContent = fs15.readFileSync(stateFile, "utf-8");
2086
+ const stateFile = path15.join(projectPath, ".harness", "state.json");
2087
+ if (fs6.existsSync(stateFile)) {
2088
+ const stateContent = fs6.readFileSync(stateFile, "utf-8");
3491
2089
  content += `
3492
2090
 
3493
2091
  ---
@@ -3505,9 +2103,9 @@ ${stateContent}
3505
2103
 
3506
2104
  // src/commands/skill/validate.ts
3507
2105
  import { Command as Command23 } from "commander";
3508
- import * as fs16 from "fs";
3509
- import * as path25 from "path";
3510
- import { parse as parse6 } from "yaml";
2106
+ import * as fs7 from "fs";
2107
+ import * as path16 from "path";
2108
+ import { parse as parse3 } from "yaml";
3511
2109
  var REQUIRED_SECTIONS = [
3512
2110
  "## When to Use",
3513
2111
  "## Process",
@@ -3519,32 +2117,32 @@ function createValidateCommand3() {
3519
2117
  return new Command23("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
3520
2118
  const globalOpts = cmd.optsWithGlobals();
3521
2119
  const skillsDir = resolveSkillsDir();
3522
- if (!fs16.existsSync(skillsDir)) {
2120
+ if (!fs7.existsSync(skillsDir)) {
3523
2121
  logger.info("No skills directory found.");
3524
2122
  process.exit(ExitCode.SUCCESS);
3525
2123
  return;
3526
2124
  }
3527
- const entries = fs16.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
2125
+ const entries = fs7.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
3528
2126
  const errors = [];
3529
2127
  let validated = 0;
3530
2128
  for (const name of entries) {
3531
- const skillDir = path25.join(skillsDir, name);
3532
- const yamlPath = path25.join(skillDir, "skill.yaml");
3533
- const skillMdPath = path25.join(skillDir, "SKILL.md");
3534
- if (!fs16.existsSync(yamlPath)) {
2129
+ const skillDir = path16.join(skillsDir, name);
2130
+ const yamlPath = path16.join(skillDir, "skill.yaml");
2131
+ const skillMdPath = path16.join(skillDir, "SKILL.md");
2132
+ if (!fs7.existsSync(yamlPath)) {
3535
2133
  errors.push(`${name}: missing skill.yaml`);
3536
2134
  continue;
3537
2135
  }
3538
2136
  try {
3539
- const raw = fs16.readFileSync(yamlPath, "utf-8");
3540
- const parsed = parse6(raw);
2137
+ const raw = fs7.readFileSync(yamlPath, "utf-8");
2138
+ const parsed = parse3(raw);
3541
2139
  const result = SkillMetadataSchema.safeParse(parsed);
3542
2140
  if (!result.success) {
3543
2141
  errors.push(`${name}/skill.yaml: ${result.error.message}`);
3544
2142
  continue;
3545
2143
  }
3546
- if (fs16.existsSync(skillMdPath)) {
3547
- const mdContent = fs16.readFileSync(skillMdPath, "utf-8");
2144
+ if (fs7.existsSync(skillMdPath)) {
2145
+ const mdContent = fs7.readFileSync(skillMdPath, "utf-8");
3548
2146
  for (const section of REQUIRED_SECTIONS) {
3549
2147
  if (!mdContent.includes(section)) {
3550
2148
  errors.push(`${name}/SKILL.md: missing section "${section}"`);
@@ -3586,28 +2184,28 @@ function createValidateCommand3() {
3586
2184
 
3587
2185
  // src/commands/skill/info.ts
3588
2186
  import { Command as Command24 } from "commander";
3589
- import * as fs17 from "fs";
3590
- import * as path26 from "path";
3591
- import { parse as parse7 } from "yaml";
2187
+ import * as fs8 from "fs";
2188
+ import * as path17 from "path";
2189
+ import { parse as parse4 } from "yaml";
3592
2190
  function createInfoCommand() {
3593
2191
  return new Command24("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
3594
2192
  const globalOpts = cmd.optsWithGlobals();
3595
2193
  const skillsDir = resolveSkillsDir();
3596
- const skillDir = path26.join(skillsDir, name);
3597
- if (!fs17.existsSync(skillDir)) {
2194
+ const skillDir = path17.join(skillsDir, name);
2195
+ if (!fs8.existsSync(skillDir)) {
3598
2196
  logger.error(`Skill not found: ${name}`);
3599
2197
  process.exit(ExitCode.ERROR);
3600
2198
  return;
3601
2199
  }
3602
- const yamlPath = path26.join(skillDir, "skill.yaml");
3603
- if (!fs17.existsSync(yamlPath)) {
2200
+ const yamlPath = path17.join(skillDir, "skill.yaml");
2201
+ if (!fs8.existsSync(yamlPath)) {
3604
2202
  logger.error(`skill.yaml not found for skill: ${name}`);
3605
2203
  process.exit(ExitCode.ERROR);
3606
2204
  return;
3607
2205
  }
3608
2206
  try {
3609
- const raw = fs17.readFileSync(yamlPath, "utf-8");
3610
- const parsed = parse7(raw);
2207
+ const raw = fs8.readFileSync(yamlPath, "utf-8");
2208
+ const parsed = parse4(raw);
3611
2209
  const result = SkillMetadataSchema.safeParse(parsed);
3612
2210
  if (!result.success) {
3613
2211
  logger.error(`Invalid skill.yaml: ${result.error.message}`);
@@ -3660,11 +2258,11 @@ import { Command as Command30 } from "commander";
3660
2258
 
3661
2259
  // src/commands/state/show.ts
3662
2260
  import { Command as Command26 } from "commander";
3663
- import * as path27 from "path";
2261
+ import * as path18 from "path";
3664
2262
  function createShowCommand() {
3665
2263
  return new Command26("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
3666
2264
  const globalOpts = cmd.optsWithGlobals();
3667
- const projectPath = path27.resolve(opts.path);
2265
+ const projectPath = path18.resolve(opts.path);
3668
2266
  const result = await loadState(projectPath, opts.stream);
3669
2267
  if (!result.ok) {
3670
2268
  logger.error(result.error.message);
@@ -3705,12 +2303,12 @@ Decisions: ${state.decisions.length}`);
3705
2303
 
3706
2304
  // src/commands/state/reset.ts
3707
2305
  import { Command as Command27 } from "commander";
3708
- import * as fs18 from "fs";
3709
- import * as path28 from "path";
2306
+ import * as fs9 from "fs";
2307
+ import * as path19 from "path";
3710
2308
  import * as readline from "readline";
3711
2309
  function createResetCommand() {
3712
2310
  return new Command27("reset").description("Reset project state (deletes .harness/state.json)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts, _cmd) => {
3713
- const projectPath = path28.resolve(opts.path);
2311
+ const projectPath = path19.resolve(opts.path);
3714
2312
  let statePath;
3715
2313
  if (opts.stream) {
3716
2314
  const streamResult = await resolveStreamPath(projectPath, { stream: opts.stream });
@@ -3719,708 +2317,137 @@ function createResetCommand() {
3719
2317
  process.exit(ExitCode.ERROR);
3720
2318
  return;
3721
2319
  }
3722
- statePath = path28.join(streamResult.value, "state.json");
2320
+ statePath = path19.join(streamResult.value, "state.json");
3723
2321
  } else {
3724
- statePath = path28.join(projectPath, ".harness", "state.json");
3725
- }
3726
- if (!fs18.existsSync(statePath)) {
3727
- logger.info("No state file found. Nothing to reset.");
3728
- process.exit(ExitCode.SUCCESS);
3729
- return;
3730
- }
3731
- if (!opts.yes) {
3732
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3733
- const answer = await new Promise((resolve24) => {
3734
- rl.question("Reset project state? This cannot be undone. [y/N] ", resolve24);
3735
- });
3736
- rl.close();
3737
- if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
3738
- logger.info("Reset cancelled.");
3739
- process.exit(ExitCode.SUCCESS);
3740
- return;
3741
- }
3742
- }
3743
- try {
3744
- fs18.unlinkSync(statePath);
3745
- logger.success("Project state reset.");
3746
- } catch (e) {
3747
- logger.error(`Failed to reset state: ${e instanceof Error ? e.message : String(e)}`);
3748
- process.exit(ExitCode.ERROR);
3749
- return;
3750
- }
3751
- process.exit(ExitCode.SUCCESS);
3752
- });
3753
- }
3754
-
3755
- // src/commands/state/learn.ts
3756
- import { Command as Command28 } from "commander";
3757
- import * as path29 from "path";
3758
- function createLearnCommand() {
3759
- return new Command28("learn").description("Append a learning to .harness/learnings.md").argument("<message>", "The learning to record").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (message, opts, _cmd) => {
3760
- const projectPath = path29.resolve(opts.path);
3761
- const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
3762
- if (!result.ok) {
3763
- logger.error(result.error.message);
3764
- process.exit(ExitCode.ERROR);
3765
- return;
3766
- }
3767
- logger.success(`Learning recorded.`);
3768
- process.exit(ExitCode.SUCCESS);
3769
- });
3770
- }
3771
-
3772
- // src/commands/state/streams.ts
3773
- import { Command as Command29 } from "commander";
3774
- import * as path30 from "path";
3775
- function createStreamsCommand() {
3776
- const command = new Command29("streams").description("Manage state streams");
3777
- command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => {
3778
- const globalOpts = cmd.optsWithGlobals();
3779
- const projectPath = path30.resolve(opts.path);
3780
- const indexResult = await loadStreamIndex(projectPath);
3781
- const result = await listStreams(projectPath);
3782
- if (!result.ok) {
3783
- logger.error(result.error.message);
3784
- process.exit(ExitCode.ERROR);
3785
- return;
3786
- }
3787
- const active = indexResult.ok ? indexResult.value.activeStream : null;
3788
- if (globalOpts.json) {
3789
- logger.raw({ activeStream: active, streams: result.value });
3790
- } else {
3791
- if (result.value.length === 0) {
3792
- console.log("No streams found.");
3793
- }
3794
- for (const s of result.value) {
3795
- const marker = s.name === active ? " (active)" : "";
3796
- const branch = s.branch ? ` [${s.branch}]` : "";
3797
- console.log(` ${s.name}${marker}${branch} \u2014 last active: ${s.lastActiveAt}`);
3798
- }
3799
- }
3800
- process.exit(ExitCode.SUCCESS);
3801
- });
3802
- command.command("create <name>").description("Create a new stream").option("--path <path>", "Project root path", ".").option("--branch <branch>", "Associate with a git branch").action(async (name, opts) => {
3803
- const projectPath = path30.resolve(opts.path);
3804
- const result = await createStream(projectPath, name, opts.branch);
3805
- if (!result.ok) {
3806
- logger.error(result.error.message);
3807
- process.exit(ExitCode.ERROR);
3808
- return;
3809
- }
3810
- logger.success(`Stream '${name}' created.`);
3811
- process.exit(ExitCode.SUCCESS);
3812
- });
3813
- command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => {
3814
- const projectPath = path30.resolve(opts.path);
3815
- const result = await archiveStream(projectPath, name);
3816
- if (!result.ok) {
3817
- logger.error(result.error.message);
3818
- process.exit(ExitCode.ERROR);
3819
- return;
3820
- }
3821
- logger.success(`Stream '${name}' archived.`);
3822
- process.exit(ExitCode.SUCCESS);
3823
- });
3824
- command.command("activate <name>").description("Set the active stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => {
3825
- const projectPath = path30.resolve(opts.path);
3826
- const result = await setActiveStream(projectPath, name);
3827
- if (!result.ok) {
3828
- logger.error(result.error.message);
3829
- process.exit(ExitCode.ERROR);
3830
- return;
3831
- }
3832
- logger.success(`Active stream set to '${name}'.`);
3833
- process.exit(ExitCode.SUCCESS);
3834
- });
3835
- return command;
3836
- }
3837
-
3838
- // src/commands/state/index.ts
3839
- function createStateCommand() {
3840
- const command = new Command30("state").description("Project state management commands");
3841
- command.addCommand(createShowCommand());
3842
- command.addCommand(createResetCommand());
3843
- command.addCommand(createLearnCommand());
3844
- command.addCommand(createStreamsCommand());
3845
- return command;
3846
- }
3847
-
3848
- // src/commands/check-phase-gate.ts
3849
- import { Command as Command31 } from "commander";
3850
- import * as path31 from "path";
3851
- import * as fs19 from "fs";
3852
- function resolveSpecPath(implFile, implPattern, specPattern, cwd) {
3853
- const relImpl = path31.relative(cwd, implFile).replace(/\\/g, "/");
3854
- const implBase = (implPattern.split("*")[0] ?? "").replace(/\/+$/, "");
3855
- const afterBase = relImpl.startsWith(implBase + "/") ? relImpl.slice(implBase.length + 1) : relImpl;
3856
- const segments = afterBase.split("/");
3857
- const firstSegment = segments[0] ?? "";
3858
- const feature = segments.length > 1 ? firstSegment : path31.basename(firstSegment, path31.extname(firstSegment));
3859
- const specRelative = specPattern.replace("{feature}", feature);
3860
- return path31.resolve(cwd, specRelative);
3861
- }
3862
- async function runCheckPhaseGate(options) {
3863
- const configResult = resolveConfig(options.configPath);
3864
- if (!configResult.ok) {
3865
- return configResult;
3866
- }
3867
- const config = configResult.value;
3868
- const cwd = options.cwd ?? (options.configPath ? path31.dirname(path31.resolve(options.configPath)) : process.cwd());
3869
- if (!config.phaseGates?.enabled) {
3870
- return Ok({
3871
- pass: true,
3872
- skipped: true,
3873
- missingSpecs: [],
3874
- checkedFiles: 0
3875
- });
3876
- }
3877
- const phaseGates = config.phaseGates;
3878
- const missingSpecs = [];
3879
- let checkedFiles = 0;
3880
- for (const mapping of phaseGates.mappings) {
3881
- const implFiles = await findFiles(mapping.implPattern, cwd);
3882
- for (const implFile of implFiles) {
3883
- checkedFiles++;
3884
- const expectedSpec = resolveSpecPath(implFile, mapping.implPattern, mapping.specPattern, cwd);
3885
- if (!fs19.existsSync(expectedSpec)) {
3886
- missingSpecs.push({
3887
- implFile: path31.relative(cwd, implFile).replace(/\\/g, "/"),
3888
- expectedSpec: path31.relative(cwd, expectedSpec).replace(/\\/g, "/")
3889
- });
3890
- }
3891
- }
3892
- }
3893
- const pass = missingSpecs.length === 0;
3894
- return Ok({
3895
- pass,
3896
- skipped: false,
3897
- severity: phaseGates.severity,
3898
- missingSpecs,
3899
- checkedFiles
3900
- });
3901
- }
3902
- function createCheckPhaseGateCommand() {
3903
- const command = new Command31("check-phase-gate").description("Verify that implementation files have matching spec documents").action(async (_opts, cmd) => {
3904
- const globalOpts = cmd.optsWithGlobals();
3905
- const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
3906
- const formatter = new OutputFormatter(mode);
3907
- const result = await runCheckPhaseGate({
3908
- configPath: globalOpts.config,
3909
- json: globalOpts.json,
3910
- verbose: globalOpts.verbose,
3911
- quiet: globalOpts.quiet
3912
- });
3913
- if (!result.ok) {
3914
- if (mode === OutputMode.JSON) {
3915
- console.log(JSON.stringify({ error: result.error.message }));
3916
- } else {
3917
- logger.error(result.error.message);
3918
- }
3919
- process.exit(result.error.exitCode);
3920
- }
3921
- const value = result.value;
3922
- if (value.skipped) {
3923
- if (mode === OutputMode.JSON) {
3924
- console.log(formatter.format(value));
3925
- } else if (mode !== OutputMode.QUIET) {
3926
- logger.dim("Phase gates not enabled, skipping.");
3927
- }
3928
- process.exit(ExitCode.SUCCESS);
3929
- }
3930
- const output = formatter.formatValidation({
3931
- valid: value.pass,
3932
- issues: value.missingSpecs.map((m) => ({
3933
- file: m.implFile,
3934
- message: `Missing spec: ${m.expectedSpec}`
3935
- }))
3936
- });
3937
- if (output) {
3938
- console.log(output);
3939
- }
3940
- const summary = formatter.formatSummary(
3941
- "Phase gate check",
3942
- `${value.checkedFiles} files checked, ${value.missingSpecs.length} missing specs`,
3943
- value.pass
3944
- );
3945
- if (summary) {
3946
- console.log(summary);
3947
- }
3948
- if (!value.pass && value.severity === "error") {
3949
- process.exit(ExitCode.VALIDATION_FAILED);
3950
- }
3951
- process.exit(ExitCode.SUCCESS);
3952
- });
3953
- return command;
3954
- }
3955
-
3956
- // src/commands/generate-slash-commands.ts
3957
- import { Command as Command32 } from "commander";
3958
- import fs22 from "fs";
3959
- import path34 from "path";
3960
- import os2 from "os";
3961
- import readline2 from "readline";
3962
-
3963
- // src/slash-commands/normalize.ts
3964
- import fs20 from "fs";
3965
- import path32 from "path";
3966
- import { parse as parse8 } from "yaml";
3967
-
3968
- // src/slash-commands/normalize-name.ts
3969
- function normalizeName(skillName) {
3970
- let name = skillName;
3971
- if (name.startsWith("harness-")) {
3972
- name = name.slice("harness-".length);
3973
- }
3974
- name = name.replace(/-harness-/g, "-");
3975
- if (name.endsWith("-harness")) {
3976
- name = name.slice(0, -"-harness".length);
3977
- }
3978
- return name;
3979
- }
3980
-
3981
- // src/slash-commands/normalize.ts
3982
- function normalizeSkills(skillSources, platforms) {
3983
- const specs = [];
3984
- const nameMap = /* @__PURE__ */ new Map();
3985
- for (const { dir: skillsDir, source } of skillSources) {
3986
- if (!fs20.existsSync(skillsDir)) continue;
3987
- const entries = fs20.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
3988
- for (const entry of entries) {
3989
- const yamlPath = path32.join(skillsDir, entry.name, "skill.yaml");
3990
- if (!fs20.existsSync(yamlPath)) continue;
3991
- let raw;
3992
- try {
3993
- raw = fs20.readFileSync(yamlPath, "utf-8");
3994
- } catch {
3995
- continue;
3996
- }
3997
- const parsed = parse8(raw);
3998
- const result = SkillMetadataSchema.safeParse(parsed);
3999
- if (!result.success) {
4000
- console.warn(`Skipping ${entry.name}: invalid skill.yaml`);
4001
- continue;
4002
- }
4003
- const meta = result.data;
4004
- const matchesPlatform = platforms.some((p) => meta.platforms.includes(p));
4005
- if (!matchesPlatform) continue;
4006
- const normalized = normalizeName(meta.name);
4007
- const existing = nameMap.get(normalized);
4008
- if (existing) {
4009
- if (existing.source === source) {
4010
- throw new Error(
4011
- `Name collision: skills "${existing.skillName}" and "${meta.name}" both normalize to "${normalized}"`
4012
- );
4013
- }
4014
- continue;
4015
- }
4016
- nameMap.set(normalized, { skillName: meta.name, source });
4017
- const skillMdPath = path32.join(skillsDir, entry.name, "SKILL.md");
4018
- const skillMdContent = fs20.existsSync(skillMdPath) ? fs20.readFileSync(skillMdPath, "utf-8") : "";
4019
- const skillMdRelative = path32.relative(
4020
- process.cwd(),
4021
- path32.join(skillsDir, entry.name, "SKILL.md")
4022
- );
4023
- const skillYamlRelative = path32.relative(
4024
- process.cwd(),
4025
- path32.join(skillsDir, entry.name, "skill.yaml")
4026
- );
4027
- const args = (meta.cli?.args ?? []).map((a) => ({
4028
- name: a.name,
4029
- description: a.description ?? "",
4030
- required: a.required ?? false
4031
- }));
4032
- const tools = [...meta.tools ?? []];
4033
- if (!tools.includes("Read")) {
4034
- tools.push("Read");
4035
- }
4036
- const contextLines = [];
4037
- if (meta.cognitive_mode) {
4038
- contextLines.push(`Cognitive mode: ${meta.cognitive_mode}`);
4039
- }
4040
- if (meta.type) {
4041
- contextLines.push(`Type: ${meta.type}`);
4042
- }
4043
- if (meta.state?.persistent) {
4044
- const files = meta.state.files?.join(", ") ?? "";
4045
- contextLines.push(`State: persistent${files ? ` (files: ${files})` : ""}`);
4046
- }
4047
- const objectiveLines = [meta.description];
4048
- if (meta.phases && meta.phases.length > 0) {
4049
- objectiveLines.push("");
4050
- objectiveLines.push("Phases:");
4051
- for (const phase of meta.phases) {
4052
- const req = phase.required !== false ? "" : " (optional)";
4053
- objectiveLines.push(`- ${phase.name}: ${phase.description}${req}`);
4054
- }
4055
- }
4056
- const executionContextLines = [];
4057
- if (skillMdContent) {
4058
- executionContextLines.push(`@${skillMdRelative}`);
4059
- executionContextLines.push(`@${skillYamlRelative}`);
4060
- }
4061
- const processLines = [];
4062
- if (meta.mcp?.tool) {
4063
- processLines.push(
4064
- `1. Try: invoke mcp__harness__${meta.mcp.tool} with skill: "${meta.name}"`
4065
- );
4066
- processLines.push(`2. If MCP unavailable: read SKILL.md and follow its workflow directly`);
4067
- processLines.push(`3. Pass through any arguments provided by the user`);
4068
- } else {
4069
- processLines.push(`1. Read SKILL.md and follow its workflow directly`);
4070
- processLines.push(`2. Pass through any arguments provided by the user`);
4071
- }
4072
- specs.push({
4073
- name: normalized,
4074
- namespace: "harness",
4075
- fullName: `harness:${normalized}`,
4076
- description: meta.description,
4077
- version: meta.version,
4078
- ...meta.cognitive_mode ? { cognitiveMode: meta.cognitive_mode } : {},
4079
- tools,
4080
- args,
4081
- skillYamlName: meta.name,
4082
- sourceDir: entry.name,
4083
- skillsBaseDir: skillsDir,
4084
- source,
4085
- prompt: {
4086
- context: contextLines.join("\n"),
4087
- objective: objectiveLines.join("\n"),
4088
- executionContext: executionContextLines.join("\n"),
4089
- process: processLines.join("\n")
4090
- }
4091
- });
4092
- }
4093
- }
4094
- return specs;
4095
- }
4096
-
4097
- // src/slash-commands/types.ts
4098
- var VALID_PLATFORMS = ["claude-code", "gemini-cli"];
4099
- var GENERATED_HEADER_CLAUDE = "<!-- Generated by harness generate-slash-commands. Do not edit. -->";
4100
- var GENERATED_HEADER_GEMINI = "# Generated by harness generate-slash-commands. Do not edit.";
4101
-
4102
- // src/slash-commands/argument-hint.ts
4103
- function buildArgumentHint(args) {
4104
- return args.map((arg) => arg.required ? `--${arg.name} <${arg.name}>` : `[--${arg.name} <${arg.name}>]`).join(" ");
4105
- }
4106
-
4107
- // src/slash-commands/render-claude-code.ts
4108
- function renderClaudeCode(spec) {
4109
- const lines = ["---"];
4110
- lines.push(`name: ${spec.fullName}`);
4111
- lines.push(`description: ${spec.description}`);
4112
- const hint = buildArgumentHint(spec.args);
4113
- if (hint) {
4114
- lines.push(`argument-hint: "${hint}"`);
4115
- }
4116
- if (spec.tools.length > 0) {
4117
- lines.push("allowed-tools:");
4118
- for (const tool of spec.tools) {
4119
- lines.push(` - ${tool}`);
4120
- }
4121
- }
4122
- lines.push("---");
4123
- lines.push("");
4124
- lines.push(GENERATED_HEADER_CLAUDE);
4125
- lines.push("");
4126
- lines.push("<context>");
4127
- lines.push(spec.prompt.context);
4128
- lines.push("</context>");
4129
- lines.push("");
4130
- lines.push("<objective>");
4131
- lines.push(spec.prompt.objective);
4132
- lines.push("</objective>");
4133
- lines.push("");
4134
- if (spec.prompt.executionContext) {
4135
- lines.push("<execution_context>");
4136
- lines.push(spec.prompt.executionContext);
4137
- lines.push("</execution_context>");
4138
- lines.push("");
4139
- }
4140
- lines.push("<process>");
4141
- lines.push(spec.prompt.process);
4142
- lines.push("</process>");
4143
- lines.push("");
4144
- return lines.join("\n");
4145
- }
4146
-
4147
- // src/slash-commands/render-gemini.ts
4148
- function escapeTomlLiteral(content) {
4149
- return content.replace(/'''/g, "''\\'''");
4150
- }
4151
- function renderGemini(spec, skillMdContent, skillYamlContent) {
4152
- const lines = [GENERATED_HEADER_GEMINI];
4153
- const safeDesc = spec.description.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
4154
- lines.push(`description = "${safeDesc}"`);
4155
- lines.push("prompt = '''");
4156
- lines.push("<context>");
4157
- lines.push(spec.prompt.context);
4158
- lines.push("</context>");
4159
- lines.push("");
4160
- lines.push("<objective>");
4161
- lines.push(spec.prompt.objective);
4162
- lines.push("</objective>");
4163
- lines.push("");
4164
- if (skillMdContent || skillYamlContent) {
4165
- lines.push("<execution_context>");
4166
- if (skillMdContent) {
4167
- const mdPath = spec.prompt.executionContext.split("\n")[0]?.replace(/^@/, "") ?? "";
4168
- lines.push(`--- SKILL.md (${mdPath}) ---`);
4169
- lines.push(escapeTomlLiteral(skillMdContent));
4170
- lines.push("");
4171
- }
4172
- if (skillYamlContent) {
4173
- const refs = spec.prompt.executionContext.split("\n");
4174
- const yamlPath = (refs[1] ?? refs[0] ?? "").replace(/^@/, "");
4175
- lines.push(`--- skill.yaml (${yamlPath}) ---`);
4176
- lines.push(escapeTomlLiteral(skillYamlContent));
4177
- }
4178
- lines.push("</execution_context>");
4179
- lines.push("");
4180
- }
4181
- const geminiProcess = spec.prompt.process.replace(
4182
- "read SKILL.md and follow its workflow directly",
4183
- "follow the SKILL.md workflow provided above directly"
4184
- );
4185
- lines.push("<process>");
4186
- lines.push(geminiProcess);
4187
- lines.push("</process>");
4188
- lines.push("'''");
4189
- lines.push("");
4190
- return lines.join("\n");
4191
- }
4192
-
4193
- // src/slash-commands/sync.ts
4194
- import fs21 from "fs";
4195
- import path33 from "path";
4196
-
4197
- // src/agent-definitions/constants.ts
4198
- var GENERATED_HEADER_AGENT = "<!-- Generated by harness generate-agent-definitions. Do not edit. -->";
4199
-
4200
- // src/slash-commands/sync.ts
4201
- function computeSyncPlan(outputDir, rendered) {
4202
- const added = [];
4203
- const updated = [];
4204
- const removed = [];
4205
- const unchanged = [];
4206
- for (const [filename, content] of rendered) {
4207
- const filePath = path33.join(outputDir, filename);
4208
- if (!fs21.existsSync(filePath)) {
4209
- added.push(filename);
4210
- } else {
4211
- const existing = fs21.readFileSync(filePath, "utf-8");
4212
- if (existing === content) {
4213
- unchanged.push(filename);
4214
- } else {
4215
- updated.push(filename);
4216
- }
4217
- }
4218
- }
4219
- if (fs21.existsSync(outputDir)) {
4220
- const existing = fs21.readdirSync(outputDir).filter((f) => {
4221
- const stat = fs21.statSync(path33.join(outputDir, f));
4222
- return stat.isFile();
4223
- });
4224
- for (const filename of existing) {
4225
- if (rendered.has(filename)) continue;
4226
- const content = fs21.readFileSync(path33.join(outputDir, filename), "utf-8");
4227
- if (content.includes(GENERATED_HEADER_CLAUDE) || content.includes(GENERATED_HEADER_GEMINI) || content.includes(GENERATED_HEADER_AGENT)) {
4228
- removed.push(filename);
4229
- }
2322
+ statePath = path19.join(projectPath, ".harness", "state.json");
4230
2323
  }
4231
- }
4232
- return { added, updated, removed, unchanged };
4233
- }
4234
- function applySyncPlan(outputDir, rendered, plan, deleteOrphans) {
4235
- fs21.mkdirSync(outputDir, { recursive: true });
4236
- for (const filename of [...plan.added, ...plan.updated]) {
4237
- const content = rendered.get(filename);
4238
- if (content !== void 0) {
4239
- fs21.writeFileSync(path33.join(outputDir, filename), content);
2324
+ if (!fs9.existsSync(statePath)) {
2325
+ logger.info("No state file found. Nothing to reset.");
2326
+ process.exit(ExitCode.SUCCESS);
2327
+ return;
4240
2328
  }
4241
- }
4242
- if (deleteOrphans) {
4243
- for (const filename of plan.removed) {
4244
- const filePath = path33.join(outputDir, filename);
4245
- if (fs21.existsSync(filePath)) {
4246
- fs21.unlinkSync(filePath);
2329
+ if (!opts.yes) {
2330
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2331
+ const answer = await new Promise((resolve20) => {
2332
+ rl.question("Reset project state? This cannot be undone. [y/N] ", resolve20);
2333
+ });
2334
+ rl.close();
2335
+ if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
2336
+ logger.info("Reset cancelled.");
2337
+ process.exit(ExitCode.SUCCESS);
2338
+ return;
4247
2339
  }
4248
2340
  }
4249
- }
2341
+ try {
2342
+ fs9.unlinkSync(statePath);
2343
+ logger.success("Project state reset.");
2344
+ } catch (e) {
2345
+ logger.error(`Failed to reset state: ${e instanceof Error ? e.message : String(e)}`);
2346
+ process.exit(ExitCode.ERROR);
2347
+ return;
2348
+ }
2349
+ process.exit(ExitCode.SUCCESS);
2350
+ });
4250
2351
  }
4251
2352
 
4252
- // src/commands/generate-slash-commands.ts
4253
- function resolveOutputDir(platform, opts) {
4254
- if (opts.output) {
4255
- return path34.join(opts.output, "harness");
4256
- }
4257
- if (opts.global) {
4258
- const home = os2.homedir();
4259
- return platform === "claude-code" ? path34.join(home, ".claude", "commands", "harness") : path34.join(home, ".gemini", "commands", "harness");
4260
- }
4261
- return platform === "claude-code" ? path34.join("agents", "commands", "claude-code", "harness") : path34.join("agents", "commands", "gemini-cli", "harness");
4262
- }
4263
- function fileExtension(platform) {
4264
- return platform === "claude-code" ? ".md" : ".toml";
4265
- }
4266
- async function confirmDeletion(files) {
4267
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
4268
- return new Promise((resolve24) => {
4269
- rl.question(`
4270
- Remove ${files.length} orphaned command(s)? (y/N) `, (answer) => {
4271
- rl.close();
4272
- resolve24(answer.toLowerCase() === "y");
4273
- });
2353
+ // src/commands/state/learn.ts
2354
+ import { Command as Command28 } from "commander";
2355
+ import * as path20 from "path";
2356
+ function createLearnCommand() {
2357
+ return new Command28("learn").description("Append a learning to .harness/learnings.md").argument("<message>", "The learning to record").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (message, opts, _cmd) => {
2358
+ const projectPath = path20.resolve(opts.path);
2359
+ const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
2360
+ if (!result.ok) {
2361
+ logger.error(result.error.message);
2362
+ process.exit(ExitCode.ERROR);
2363
+ return;
2364
+ }
2365
+ logger.success(`Learning recorded.`);
2366
+ process.exit(ExitCode.SUCCESS);
4274
2367
  });
4275
2368
  }
4276
- function generateSlashCommands(opts) {
4277
- const skillSources = [];
4278
- if (opts.skillsDir) {
4279
- skillSources.push({ dir: opts.skillsDir, source: "project" });
4280
- } else {
4281
- const projectDir = resolveProjectSkillsDir();
4282
- if (projectDir) {
4283
- skillSources.push({ dir: projectDir, source: "project" });
2369
+
2370
+ // src/commands/state/streams.ts
2371
+ import { Command as Command29 } from "commander";
2372
+ import * as path21 from "path";
2373
+ function createStreamsCommand() {
2374
+ const command = new Command29("streams").description("Manage state streams");
2375
+ command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => {
2376
+ const globalOpts = cmd.optsWithGlobals();
2377
+ const projectPath = path21.resolve(opts.path);
2378
+ const indexResult = await loadStreamIndex(projectPath);
2379
+ const result = await listStreams(projectPath);
2380
+ if (!result.ok) {
2381
+ logger.error(result.error.message);
2382
+ process.exit(ExitCode.ERROR);
2383
+ return;
4284
2384
  }
4285
- if (opts.includeGlobal || skillSources.length === 0) {
4286
- const globalDir = resolveGlobalSkillsDir();
4287
- if (!projectDir || path34.resolve(globalDir) !== path34.resolve(projectDir)) {
4288
- skillSources.push({ dir: globalDir, source: "global" });
2385
+ const active = indexResult.ok ? indexResult.value.activeStream : null;
2386
+ if (globalOpts.json) {
2387
+ logger.raw({ activeStream: active, streams: result.value });
2388
+ } else {
2389
+ if (result.value.length === 0) {
2390
+ console.log("No streams found.");
4289
2391
  }
4290
- }
4291
- }
4292
- const specs = normalizeSkills(skillSources, opts.platforms);
4293
- const results = [];
4294
- for (const platform of opts.platforms) {
4295
- const outputDir = resolveOutputDir(platform, opts);
4296
- const ext = fileExtension(platform);
4297
- const useAbsolutePaths = opts.global;
4298
- const rendered = /* @__PURE__ */ new Map();
4299
- for (const spec of specs) {
4300
- const filename = `${spec.name}${ext}`;
4301
- if (platform === "claude-code") {
4302
- const renderSpec = useAbsolutePaths ? {
4303
- ...spec,
4304
- prompt: {
4305
- ...spec.prompt,
4306
- executionContext: spec.prompt.executionContext.split("\n").map((line) => {
4307
- if (line.startsWith("@")) {
4308
- const relPath = line.slice(1);
4309
- return `@${path34.resolve(relPath)}`;
4310
- }
4311
- return line;
4312
- }).join("\n")
4313
- }
4314
- } : spec;
4315
- rendered.set(filename, renderClaudeCode(renderSpec));
4316
- } else {
4317
- const mdPath = path34.join(spec.skillsBaseDir, spec.sourceDir, "SKILL.md");
4318
- const yamlPath = path34.join(spec.skillsBaseDir, spec.sourceDir, "skill.yaml");
4319
- const mdContent = fs22.existsSync(mdPath) ? fs22.readFileSync(mdPath, "utf-8") : "";
4320
- const yamlContent = fs22.existsSync(yamlPath) ? fs22.readFileSync(yamlPath, "utf-8") : "";
4321
- rendered.set(filename, renderGemini(spec, mdContent, yamlContent));
2392
+ for (const s of result.value) {
2393
+ const marker = s.name === active ? " (active)" : "";
2394
+ const branch = s.branch ? ` [${s.branch}]` : "";
2395
+ console.log(` ${s.name}${marker}${branch} \u2014 last active: ${s.lastActiveAt}`);
4322
2396
  }
4323
2397
  }
4324
- const plan = computeSyncPlan(outputDir, rendered);
4325
- if (!opts.dryRun) {
4326
- applySyncPlan(outputDir, rendered, plan, false);
4327
- }
4328
- results.push({
4329
- platform,
4330
- added: plan.added,
4331
- updated: plan.updated,
4332
- removed: plan.removed,
4333
- unchanged: plan.unchanged,
4334
- outputDir
4335
- });
4336
- }
4337
- return results;
4338
- }
4339
- async function handleOrphanDeletion(results, opts) {
4340
- if (opts.dryRun) return;
4341
- for (const result of results) {
4342
- if (result.removed.length === 0) continue;
4343
- const shouldDelete = opts.yes || await confirmDeletion(result.removed);
4344
- if (shouldDelete) {
4345
- for (const filename of result.removed) {
4346
- const filePath = path34.join(result.outputDir, filename);
4347
- if (fs22.existsSync(filePath)) {
4348
- fs22.unlinkSync(filePath);
4349
- }
4350
- }
2398
+ process.exit(ExitCode.SUCCESS);
2399
+ });
2400
+ command.command("create <name>").description("Create a new stream").option("--path <path>", "Project root path", ".").option("--branch <branch>", "Associate with a git branch").action(async (name, opts) => {
2401
+ const projectPath = path21.resolve(opts.path);
2402
+ const result = await createStream(projectPath, name, opts.branch);
2403
+ if (!result.ok) {
2404
+ logger.error(result.error.message);
2405
+ process.exit(ExitCode.ERROR);
2406
+ return;
4351
2407
  }
4352
- }
4353
- }
4354
- function createGenerateSlashCommandsCommand() {
4355
- return new Command32("generate-slash-commands").description(
4356
- "Generate native slash commands for Claude Code and Gemini CLI from skill metadata"
4357
- ).option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global config directories", false).option("--include-global", "Include built-in global skills alongside project skills", false).option("--output <dir>", "Custom output directory").option("--skills-dir <path>", "Skills directory to scan").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
4358
- const globalOpts = cmd.optsWithGlobals();
4359
- const platforms = opts.platforms.split(",").map((p) => p.trim());
4360
- for (const p of platforms) {
4361
- if (!VALID_PLATFORMS.includes(p)) {
4362
- throw new CLIError(
4363
- `Invalid platform "${p}". Valid platforms: ${VALID_PLATFORMS.join(", ")}`,
4364
- ExitCode.VALIDATION_FAILED
4365
- );
4366
- }
2408
+ logger.success(`Stream '${name}' created.`);
2409
+ process.exit(ExitCode.SUCCESS);
2410
+ });
2411
+ command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => {
2412
+ const projectPath = path21.resolve(opts.path);
2413
+ const result = await archiveStream(projectPath, name);
2414
+ if (!result.ok) {
2415
+ logger.error(result.error.message);
2416
+ process.exit(ExitCode.ERROR);
2417
+ return;
4367
2418
  }
4368
- const generateOpts = {
4369
- platforms,
4370
- global: opts.global,
4371
- includeGlobal: opts.includeGlobal,
4372
- output: opts.output,
4373
- skillsDir: opts.skillsDir ?? "",
4374
- dryRun: opts.dryRun,
4375
- yes: opts.yes
4376
- };
4377
- try {
4378
- const results = generateSlashCommands(generateOpts);
4379
- if (globalOpts.json) {
4380
- console.log(JSON.stringify(results, null, 2));
4381
- return;
4382
- }
4383
- const totalCommands = results.reduce(
4384
- (sum, r) => sum + r.added.length + r.updated.length + r.unchanged.length,
4385
- 0
4386
- );
4387
- if (totalCommands === 0) {
4388
- console.log(
4389
- "\nNo skills found. Use --include-global to include built-in skills, or create a skill with: harness create-skill"
4390
- );
4391
- return;
4392
- }
4393
- for (const result of results) {
4394
- console.log(`
4395
- ${result.platform} \u2192 ${result.outputDir}`);
4396
- if (result.added.length > 0) {
4397
- console.log(` + ${result.added.length} new: ${result.added.join(", ")}`);
4398
- }
4399
- if (result.updated.length > 0) {
4400
- console.log(` ~ ${result.updated.length} updated: ${result.updated.join(", ")}`);
4401
- }
4402
- if (result.removed.length > 0) {
4403
- console.log(` - ${result.removed.length} removed: ${result.removed.join(", ")}`);
4404
- }
4405
- if (result.unchanged.length > 0) {
4406
- console.log(` = ${result.unchanged.length} unchanged`);
4407
- }
4408
- if (opts.dryRun) {
4409
- console.log(" (dry run \u2014 no files written)");
4410
- }
4411
- }
4412
- await handleOrphanDeletion(results, { yes: opts.yes, dryRun: opts.dryRun });
4413
- } catch (error) {
4414
- handleError(error);
2419
+ logger.success(`Stream '${name}' archived.`);
2420
+ process.exit(ExitCode.SUCCESS);
2421
+ });
2422
+ command.command("activate <name>").description("Set the active stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => {
2423
+ const projectPath = path21.resolve(opts.path);
2424
+ const result = await setActiveStream(projectPath, name);
2425
+ if (!result.ok) {
2426
+ logger.error(result.error.message);
2427
+ process.exit(ExitCode.ERROR);
2428
+ return;
4415
2429
  }
2430
+ logger.success(`Active stream set to '${name}'.`);
2431
+ process.exit(ExitCode.SUCCESS);
4416
2432
  });
2433
+ return command;
2434
+ }
2435
+
2436
+ // src/commands/state/index.ts
2437
+ function createStateCommand() {
2438
+ const command = new Command30("state").description("Project state management commands");
2439
+ command.addCommand(createShowCommand());
2440
+ command.addCommand(createResetCommand());
2441
+ command.addCommand(createLearnCommand());
2442
+ command.addCommand(createStreamsCommand());
2443
+ return command;
4417
2444
  }
4418
2445
 
4419
2446
  // src/commands/ci/index.ts
4420
- import { Command as Command35 } from "commander";
2447
+ import { Command as Command33 } from "commander";
4421
2448
 
4422
2449
  // src/commands/ci/check.ts
4423
- import { Command as Command33 } from "commander";
2450
+ import { Command as Command31 } from "commander";
4424
2451
  var VALID_CHECKS = ["validate", "deps", "docs", "entropy", "phase-gate"];
4425
2452
  async function runCICheck(options) {
4426
2453
  const configResult = resolveConfig(options.configPath);
@@ -4451,7 +2478,7 @@ function parseFailOn(failOn) {
4451
2478
  return "error";
4452
2479
  }
4453
2480
  function createCheckCommand() {
4454
- return new Command33("check").description("Run all harness checks for CI (validate, deps, docs, entropy, phase-gate)").option("--skip <checks>", "Comma-separated checks to skip (e.g., entropy,docs)").option("--fail-on <severity>", "Fail on severity level: error (default) or warning", "error").action(async (opts, cmd) => {
2481
+ return new Command31("check").description("Run all harness checks for CI (validate, deps, docs, entropy, phase-gate)").option("--skip <checks>", "Comma-separated checks to skip (e.g., entropy,docs)").option("--fail-on <severity>", "Fail on severity level: error (default) or warning", "error").action(async (opts, cmd) => {
4455
2482
  const globalOpts = cmd.optsWithGlobals();
4456
2483
  const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
4457
2484
  const skip = parseSkip(opts.skip);
@@ -4495,9 +2522,9 @@ function createCheckCommand() {
4495
2522
  }
4496
2523
 
4497
2524
  // src/commands/ci/init.ts
4498
- import { Command as Command34 } from "commander";
4499
- import * as fs23 from "fs";
4500
- import * as path35 from "path";
2525
+ import { Command as Command32 } from "commander";
2526
+ import * as fs10 from "fs";
2527
+ import * as path22 from "path";
4501
2528
  var ALL_CHECKS = ["validate", "deps", "docs", "entropy", "phase-gate"];
4502
2529
  function buildSkipFlag(checks) {
4503
2530
  if (!checks) return "";
@@ -4589,12 +2616,12 @@ function generateCIConfig(options) {
4589
2616
  });
4590
2617
  }
4591
2618
  function detectPlatform() {
4592
- if (fs23.existsSync(".github")) return "github";
4593
- if (fs23.existsSync(".gitlab-ci.yml")) return "gitlab";
2619
+ if (fs10.existsSync(".github")) return "github";
2620
+ if (fs10.existsSync(".gitlab-ci.yml")) return "gitlab";
4594
2621
  return null;
4595
2622
  }
4596
2623
  function createInitCommand2() {
4597
- return new Command34("init").description("Generate CI configuration for harness checks").option("--platform <platform>", "CI platform: github, gitlab, or generic").option("--checks <list>", "Comma-separated list of checks to include").action(async (opts, cmd) => {
2624
+ return new Command32("init").description("Generate CI configuration for harness checks").option("--platform <platform>", "CI platform: github, gitlab, or generic").option("--checks <list>", "Comma-separated list of checks to include").action(async (opts, cmd) => {
4598
2625
  const globalOpts = cmd.optsWithGlobals();
4599
2626
  const platform = opts.platform ?? detectPlatform() ?? "generic";
4600
2627
  const checks = opts.checks ? opts.checks.split(",").map((s) => s.trim()) : void 0;
@@ -4606,12 +2633,12 @@ function createInitCommand2() {
4606
2633
  process.exit(result.error.exitCode);
4607
2634
  }
4608
2635
  const { filename, content } = result.value;
4609
- const targetPath = path35.resolve(filename);
4610
- const dir = path35.dirname(targetPath);
4611
- fs23.mkdirSync(dir, { recursive: true });
4612
- fs23.writeFileSync(targetPath, content);
2636
+ const targetPath = path22.resolve(filename);
2637
+ const dir = path22.dirname(targetPath);
2638
+ fs10.mkdirSync(dir, { recursive: true });
2639
+ fs10.writeFileSync(targetPath, content);
4613
2640
  if (platform === "generic" && process.platform !== "win32") {
4614
- fs23.chmodSync(targetPath, "755");
2641
+ fs10.chmodSync(targetPath, "755");
4615
2642
  }
4616
2643
  if (globalOpts.json) {
4617
2644
  console.log(JSON.stringify({ file: filename, platform }));
@@ -4624,18 +2651,18 @@ function createInitCommand2() {
4624
2651
 
4625
2652
  // src/commands/ci/index.ts
4626
2653
  function createCICommand() {
4627
- const command = new Command35("ci").description("CI/CD integration commands");
2654
+ const command = new Command33("ci").description("CI/CD integration commands");
4628
2655
  command.addCommand(createCheckCommand());
4629
2656
  command.addCommand(createInitCommand2());
4630
2657
  return command;
4631
2658
  }
4632
2659
 
4633
2660
  // src/commands/update.ts
4634
- import { Command as Command36 } from "commander";
2661
+ import { Command as Command34 } from "commander";
4635
2662
  import { execFileSync as execFileSync3 } from "child_process";
4636
2663
  import { realpathSync } from "fs";
4637
- import readline3 from "readline";
4638
- import chalk4 from "chalk";
2664
+ import readline2 from "readline";
2665
+ import chalk3 from "chalk";
4639
2666
  function detectPackageManager() {
4640
2667
  try {
4641
2668
  const argv1 = process.argv[1];
@@ -4687,19 +2714,19 @@ function getInstalledPackages(pm) {
4687
2714
  }
4688
2715
  }
4689
2716
  function prompt(question) {
4690
- const rl = readline3.createInterface({
2717
+ const rl = readline2.createInterface({
4691
2718
  input: process.stdin,
4692
2719
  output: process.stdout
4693
2720
  });
4694
- return new Promise((resolve24) => {
2721
+ return new Promise((resolve20) => {
4695
2722
  rl.question(question, (answer) => {
4696
2723
  rl.close();
4697
- resolve24(answer.trim().toLowerCase());
2724
+ resolve20(answer.trim().toLowerCase());
4698
2725
  });
4699
2726
  });
4700
2727
  }
4701
2728
  function createUpdateCommand() {
4702
- return new Command36("update").description("Update all @harness-engineering packages to the latest version").option("--version <semver>", "Pin @harness-engineering/cli to a specific version").action(async (opts, cmd) => {
2729
+ return new Command34("update").description("Update all @harness-engineering packages to the latest version").option("--version <semver>", "Pin @harness-engineering/cli to a specific version").action(async (opts, cmd) => {
4703
2730
  const globalOpts = cmd.optsWithGlobals();
4704
2731
  const pm = detectPackageManager();
4705
2732
  if (globalOpts.verbose) {
@@ -4721,8 +2748,8 @@ function createUpdateCommand() {
4721
2748
  }
4722
2749
  if (currentVersion) {
4723
2750
  console.log("");
4724
- logger.info(`Current CLI version: ${chalk4.dim(`v${currentVersion}`)}`);
4725
- logger.info(`Latest CLI version: ${chalk4.green(`v${latestCliVersion}`)}`);
2751
+ logger.info(`Current CLI version: ${chalk3.dim(`v${currentVersion}`)}`);
2752
+ logger.info(`Latest CLI version: ${chalk3.green(`v${latestCliVersion}`)}`);
4726
2753
  console.log("");
4727
2754
  }
4728
2755
  }
@@ -4748,7 +2775,7 @@ function createUpdateCommand() {
4748
2775
  } catch {
4749
2776
  console.log("");
4750
2777
  logger.error("Update failed. You can try manually:");
4751
- console.log(` ${chalk4.cyan(installCmd)}`);
2778
+ console.log(` ${chalk3.cyan(installCmd)}`);
4752
2779
  process.exit(ExitCode.ERROR);
4753
2780
  }
4754
2781
  console.log("");
@@ -4762,277 +2789,17 @@ function createUpdateCommand() {
4762
2789
  });
4763
2790
  } catch {
4764
2791
  logger.warn("Generation failed. Run manually:");
4765
- console.log(` ${chalk4.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
2792
+ console.log(` ${chalk3.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
4766
2793
  }
4767
2794
  }
4768
2795
  process.exit(ExitCode.SUCCESS);
4769
2796
  });
4770
2797
  }
4771
2798
 
4772
- // src/commands/generate-agent-definitions.ts
4773
- import { Command as Command37 } from "commander";
4774
- import * as fs24 from "fs";
4775
- import * as path36 from "path";
4776
- import * as os3 from "os";
4777
-
4778
- // src/agent-definitions/generator.ts
4779
- var AGENT_DESCRIPTIONS = {
4780
- "code-reviewer": "Perform code review and address review findings using harness methodology. Use when reviewing code, fixing review findings, responding to review feedback, or when a code review has produced issues that need to be addressed.",
4781
- "task-executor": "Execute implementation plans task-by-task with state tracking, TDD, and verification. Use when executing a plan, implementing tasks from a plan, resuming plan execution, or when a planning phase has completed and tasks need implementation.",
4782
- "parallel-coordinator": "Dispatch independent tasks across isolated agents for parallel execution. Use when multiple independent tasks need to run concurrently, splitting work across agents, or coordinating parallel implementation.",
4783
- "architecture-enforcer": "Validate architectural constraints and dependency rules. Use when checking layer boundaries, detecting circular dependencies, or verifying import direction compliance.",
4784
- "documentation-maintainer": "Keep documentation in sync with source code. Use when detecting documentation drift, validating doc coverage, or aligning docs with code changes.",
4785
- "entropy-cleaner": "Detect and fix codebase entropy including drift, dead code, and pattern violations. Use when running cleanup, detecting dead code, or fixing pattern violations.",
4786
- planner: "Create detailed implementation plans from specs with task breakdown, dependency ordering, and checkpoint placement. Use when planning a phase, breaking a spec into tasks, or creating an execution plan.",
4787
- verifier: "Verify implementation completeness against spec and plan at three tiers (EXISTS, SUBSTANTIVE, WIRED). Use when checking if built code matches what was planned, validating phase completion, or auditing implementation quality."
4788
- };
4789
- var DEFAULT_TOOLS = ["Bash", "Read", "Write", "Edit", "Glob", "Grep"];
4790
- var GEMINI_TOOL_MAP = {
4791
- Bash: "run_shell_command",
4792
- Read: "read_file",
4793
- Write: "write_file",
4794
- Edit: "replace",
4795
- Glob: "glob",
4796
- Grep: "search_file_content"
4797
- };
4798
- function generateAgentDefinition(persona, skillContents) {
4799
- const kebabName = toKebabCase(persona.name);
4800
- const name = `harness-${kebabName}`;
4801
- const description = AGENT_DESCRIPTIONS[kebabName] ?? persona.description;
4802
- const methodologyParts = [];
4803
- for (const skillName of persona.skills) {
4804
- const content = skillContents.get(skillName);
4805
- if (content) {
4806
- methodologyParts.push(content);
4807
- }
4808
- }
4809
- return {
4810
- name,
4811
- description,
4812
- tools: [...DEFAULT_TOOLS],
4813
- role: persona.role,
4814
- skills: persona.skills,
4815
- steps: persona.steps,
4816
- methodology: methodologyParts.join("\n\n---\n\n")
4817
- };
4818
- }
4819
-
4820
- // src/agent-definitions/render-claude-code.ts
4821
- function formatStep(step, index) {
4822
- if ("command" in step && step.command) {
4823
- const cmd = step.command;
4824
- const when = step.when ?? "always";
4825
- return `${index + 1}. Run \`harness ${cmd}\` (${when})`;
4826
- }
4827
- if ("skill" in step && step.skill) {
4828
- const skill = step.skill;
4829
- const when = step.when ?? "always";
4830
- return `${index + 1}. Execute ${skill} skill (${when})`;
4831
- }
4832
- return `${index + 1}. Unknown step`;
4833
- }
4834
- function renderClaudeCodeAgent(def) {
4835
- const lines = ["---"];
4836
- lines.push(`name: ${def.name}`);
4837
- lines.push(`description: >`);
4838
- lines.push(` ${def.description}`);
4839
- lines.push(`tools: ${def.tools.join(", ")}`);
4840
- lines.push("---");
4841
- lines.push("");
4842
- lines.push(GENERATED_HEADER_AGENT);
4843
- lines.push("");
4844
- lines.push("## Role");
4845
- lines.push("");
4846
- lines.push(def.role);
4847
- lines.push("");
4848
- lines.push("## Skills");
4849
- lines.push("");
4850
- for (const skill of def.skills) {
4851
- lines.push(`- ${skill}`);
4852
- }
4853
- lines.push("");
4854
- lines.push("## Steps");
4855
- lines.push("");
4856
- def.steps.forEach((step, i) => {
4857
- lines.push(formatStep(step, i));
4858
- });
4859
- lines.push("");
4860
- if (def.methodology) {
4861
- lines.push("## Methodology");
4862
- lines.push("");
4863
- lines.push(def.methodology);
4864
- lines.push("");
4865
- }
4866
- return lines.join("\n");
4867
- }
4868
-
4869
- // src/agent-definitions/render-gemini-cli.ts
4870
- function toGeminiToolName(tool) {
4871
- return GEMINI_TOOL_MAP[tool] ?? tool;
4872
- }
4873
- function formatStep2(step, index) {
4874
- if ("command" in step && step.command) {
4875
- const cmd = step.command;
4876
- const when = step.when ?? "always";
4877
- return `${index + 1}. Run \`harness ${cmd}\` (${when})`;
4878
- }
4879
- if ("skill" in step && step.skill) {
4880
- const skill = step.skill;
4881
- const when = step.when ?? "always";
4882
- return `${index + 1}. Execute ${skill} skill (${when})`;
4883
- }
4884
- return `${index + 1}. Unknown step`;
4885
- }
4886
- function renderGeminiAgent(def) {
4887
- const lines = ["---"];
4888
- lines.push(`name: ${def.name}`);
4889
- lines.push(`description: >`);
4890
- lines.push(` ${def.description}`);
4891
- if (def.tools.length > 0) {
4892
- lines.push("tools:");
4893
- for (const tool of def.tools) {
4894
- lines.push(` - ${toGeminiToolName(tool)}`);
4895
- }
4896
- }
4897
- lines.push("---");
4898
- lines.push("");
4899
- lines.push(GENERATED_HEADER_AGENT);
4900
- lines.push("");
4901
- lines.push("## Role");
4902
- lines.push("");
4903
- lines.push(def.role);
4904
- lines.push("");
4905
- lines.push("## Skills");
4906
- lines.push("");
4907
- for (const skill of def.skills) {
4908
- lines.push(`- ${skill}`);
4909
- }
4910
- lines.push("");
4911
- lines.push("## Steps");
4912
- lines.push("");
4913
- def.steps.forEach((step, i) => {
4914
- lines.push(formatStep2(step, i));
4915
- });
4916
- lines.push("");
4917
- if (def.methodology) {
4918
- lines.push("## Methodology");
4919
- lines.push("");
4920
- lines.push(def.methodology);
4921
- lines.push("");
4922
- }
4923
- return lines.join("\n");
4924
- }
4925
-
4926
- // src/commands/generate-agent-definitions.ts
4927
- function resolveOutputDir2(platform, opts) {
4928
- if (opts.output) {
4929
- return platform === "claude-code" ? path36.join(opts.output, "claude-code") : path36.join(opts.output, "gemini-cli");
4930
- }
4931
- if (opts.global) {
4932
- const home = os3.homedir();
4933
- return platform === "claude-code" ? path36.join(home, ".claude", "agents") : path36.join(home, ".gemini", "agents");
4934
- }
4935
- return platform === "claude-code" ? path36.join("agents", "agents", "claude-code") : path36.join("agents", "agents", "gemini-cli");
4936
- }
4937
- function loadSkillContent(skillName) {
4938
- const skillsDir = resolveSkillsDir();
4939
- const skillMdPath = path36.join(skillsDir, skillName, "SKILL.md");
4940
- if (!fs24.existsSync(skillMdPath)) return null;
4941
- return fs24.readFileSync(skillMdPath, "utf-8");
4942
- }
4943
- function getRenderer(platform) {
4944
- return platform === "claude-code" ? renderClaudeCodeAgent : renderGeminiAgent;
4945
- }
4946
- function generateAgentDefinitions(opts) {
4947
- const personasDir = resolvePersonasDir();
4948
- const personaList = listPersonas(personasDir);
4949
- if (!personaList.ok) return [];
4950
- const personas = personaList.value.map((meta) => loadPersona(meta.filePath)).filter((r) => r.ok).map((r) => r.value);
4951
- const allSkillNames = new Set(personas.flatMap((p) => p.skills));
4952
- const skillContents = /* @__PURE__ */ new Map();
4953
- for (const skillName of allSkillNames) {
4954
- const content = loadSkillContent(skillName);
4955
- if (content) {
4956
- skillContents.set(skillName, content);
4957
- }
4958
- }
4959
- const definitions = personas.map((p) => generateAgentDefinition(p, skillContents));
4960
- const results = [];
4961
- for (const platform of opts.platforms) {
4962
- const outputDir = resolveOutputDir2(platform, opts);
4963
- const renderer = getRenderer(platform);
4964
- const rendered = /* @__PURE__ */ new Map();
4965
- for (const def of definitions) {
4966
- const filename = `${def.name}.md`;
4967
- rendered.set(filename, renderer(def));
4968
- }
4969
- const plan = computeSyncPlan(outputDir, rendered);
4970
- if (!opts.dryRun) {
4971
- applySyncPlan(outputDir, rendered, plan, false);
4972
- }
4973
- results.push({
4974
- platform,
4975
- added: plan.added,
4976
- updated: plan.updated,
4977
- removed: plan.removed,
4978
- unchanged: plan.unchanged,
4979
- outputDir
4980
- });
4981
- }
4982
- return results;
4983
- }
4984
- function createGenerateAgentDefinitionsCommand() {
4985
- return new Command37("generate-agent-definitions").description("Generate agent definition files from personas for Claude Code and Gemini CLI").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global agent directories", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).action(async (opts, cmd) => {
4986
- const globalOpts = cmd.optsWithGlobals();
4987
- const platforms = opts.platforms.split(",").map((p) => p.trim());
4988
- for (const p of platforms) {
4989
- if (!VALID_PLATFORMS.includes(p)) {
4990
- throw new CLIError(
4991
- `Invalid platform "${p}". Valid platforms: ${VALID_PLATFORMS.join(", ")}`,
4992
- ExitCode.VALIDATION_FAILED
4993
- );
4994
- }
4995
- }
4996
- try {
4997
- const results = generateAgentDefinitions({
4998
- platforms,
4999
- global: opts.global,
5000
- output: opts.output,
5001
- dryRun: opts.dryRun
5002
- });
5003
- if (globalOpts.json) {
5004
- console.log(JSON.stringify(results, null, 2));
5005
- return;
5006
- }
5007
- for (const result of results) {
5008
- console.log(`
5009
- ${result.platform} \u2192 ${result.outputDir}`);
5010
- if (result.added.length > 0) {
5011
- console.log(` + ${result.added.length} new: ${result.added.join(", ")}`);
5012
- }
5013
- if (result.updated.length > 0) {
5014
- console.log(` ~ ${result.updated.length} updated: ${result.updated.join(", ")}`);
5015
- }
5016
- if (result.removed.length > 0) {
5017
- console.log(` - ${result.removed.length} removed: ${result.removed.join(", ")}`);
5018
- }
5019
- if (result.unchanged.length > 0) {
5020
- console.log(` = ${result.unchanged.length} unchanged`);
5021
- }
5022
- if (opts.dryRun) {
5023
- console.log(" (dry run \u2014 no files written)");
5024
- }
5025
- }
5026
- } catch (error) {
5027
- handleError(error);
5028
- }
5029
- });
5030
- }
5031
-
5032
2799
  // src/commands/generate.ts
5033
- import { Command as Command38 } from "commander";
2800
+ import { Command as Command35 } from "commander";
5034
2801
  function createGenerateCommand3() {
5035
- return new Command38("generate").description("Generate all platform integrations (slash commands + agent definitions)").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global directories", false).option("--include-global", "Include built-in global skills", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
2802
+ return new Command35("generate").description("Generate all platform integrations (slash commands + agent definitions)").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global directories", false).option("--include-global", "Include built-in global skills", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
5036
2803
  const globalOpts = cmd.optsWithGlobals();
5037
2804
  const platforms = opts.platforms.split(",").map((p) => p.trim());
5038
2805
  for (const p of platforms) {
@@ -5091,10 +2858,10 @@ function createGenerateCommand3() {
5091
2858
  }
5092
2859
 
5093
2860
  // src/commands/graph/scan.ts
5094
- import { Command as Command39 } from "commander";
5095
- import * as path37 from "path";
2861
+ import { Command as Command36 } from "commander";
2862
+ import * as path23 from "path";
5096
2863
  async function runScan(projectPath) {
5097
- const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-K6KTTN3I.js");
2864
+ const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-I7DB5VKB.js");
5098
2865
  const store = new GraphStore();
5099
2866
  const start = Date.now();
5100
2867
  await new CodeIngestor(store).ingest(projectPath);
@@ -5105,13 +2872,13 @@ async function runScan(projectPath) {
5105
2872
  await new GitIngestor(store).ingest(projectPath);
5106
2873
  } catch {
5107
2874
  }
5108
- const graphDir = path37.join(projectPath, ".harness", "graph");
2875
+ const graphDir = path23.join(projectPath, ".harness", "graph");
5109
2876
  await store.save(graphDir);
5110
2877
  return { nodeCount: store.nodeCount, edgeCount: store.edgeCount, durationMs: Date.now() - start };
5111
2878
  }
5112
2879
  function createScanCommand() {
5113
- return new Command39("scan").description("Scan project and build knowledge graph").argument("[path]", "Project root path", ".").action(async (inputPath, _opts, cmd) => {
5114
- const projectPath = path37.resolve(inputPath);
2880
+ return new Command36("scan").description("Scan project and build knowledge graph").argument("[path]", "Project root path", ".").action(async (inputPath, _opts, cmd) => {
2881
+ const projectPath = path23.resolve(inputPath);
5115
2882
  const globalOpts = cmd.optsWithGlobals();
5116
2883
  try {
5117
2884
  const result = await runScan(projectPath);
@@ -5130,13 +2897,13 @@ function createScanCommand() {
5130
2897
  }
5131
2898
 
5132
2899
  // src/commands/graph/ingest.ts
5133
- import { Command as Command40 } from "commander";
5134
- import * as path38 from "path";
2900
+ import { Command as Command37 } from "commander";
2901
+ import * as path24 from "path";
5135
2902
  async function loadConnectorConfig(projectPath, source) {
5136
2903
  try {
5137
- const fs25 = await import("fs/promises");
5138
- const configPath = path38.join(projectPath, "harness.config.json");
5139
- const config = JSON.parse(await fs25.readFile(configPath, "utf-8"));
2904
+ const fs11 = await import("fs/promises");
2905
+ const configPath = path24.join(projectPath, "harness.config.json");
2906
+ const config = JSON.parse(await fs11.readFile(configPath, "utf-8"));
5140
2907
  const connector = config.graph?.connectors?.find(
5141
2908
  (c) => c.source === source
5142
2909
  );
@@ -5175,8 +2942,8 @@ async function runIngest(projectPath, source, opts) {
5175
2942
  SyncManager,
5176
2943
  JiraConnector,
5177
2944
  SlackConnector
5178
- } = await import("./dist-K6KTTN3I.js");
5179
- const graphDir = path38.join(projectPath, ".harness", "graph");
2945
+ } = await import("./dist-I7DB5VKB.js");
2946
+ const graphDir = path24.join(projectPath, ".harness", "graph");
5180
2947
  const store = new GraphStore();
5181
2948
  await store.load(graphDir);
5182
2949
  if (opts?.all) {
@@ -5237,13 +3004,13 @@ async function runIngest(projectPath, source, opts) {
5237
3004
  return result;
5238
3005
  }
5239
3006
  function createIngestCommand() {
5240
- return new Command40("ingest").description("Ingest data into the knowledge graph").option("--source <name>", "Source to ingest (code, knowledge, git, jira, slack)").option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(async (opts, cmd) => {
3007
+ return new Command37("ingest").description("Ingest data into the knowledge graph").option("--source <name>", "Source to ingest (code, knowledge, git, jira, slack)").option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(async (opts, cmd) => {
5241
3008
  if (!opts.source && !opts.all) {
5242
3009
  console.error("Error: --source or --all is required");
5243
3010
  process.exit(1);
5244
3011
  }
5245
3012
  const globalOpts = cmd.optsWithGlobals();
5246
- const projectPath = path38.resolve(globalOpts.config ? path38.dirname(globalOpts.config) : ".");
3013
+ const projectPath = path24.resolve(globalOpts.config ? path24.dirname(globalOpts.config) : ".");
5247
3014
  try {
5248
3015
  const result = await runIngest(projectPath, opts.source ?? "", {
5249
3016
  full: opts.full,
@@ -5265,12 +3032,12 @@ function createIngestCommand() {
5265
3032
  }
5266
3033
 
5267
3034
  // src/commands/graph/query.ts
5268
- import { Command as Command41 } from "commander";
5269
- import * as path39 from "path";
3035
+ import { Command as Command38 } from "commander";
3036
+ import * as path25 from "path";
5270
3037
  async function runQuery(projectPath, rootNodeId, opts) {
5271
- const { GraphStore, ContextQL } = await import("./dist-K6KTTN3I.js");
3038
+ const { GraphStore, ContextQL } = await import("./dist-I7DB5VKB.js");
5272
3039
  const store = new GraphStore();
5273
- const graphDir = path39.join(projectPath, ".harness", "graph");
3040
+ const graphDir = path25.join(projectPath, ".harness", "graph");
5274
3041
  const loaded = await store.load(graphDir);
5275
3042
  if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
5276
3043
  const params = {
@@ -5284,9 +3051,9 @@ async function runQuery(projectPath, rootNodeId, opts) {
5284
3051
  return cql.execute(params);
5285
3052
  }
5286
3053
  function createQueryCommand() {
5287
- return new Command41("query").description("Query the knowledge graph").argument("<rootNodeId>", "Starting node ID").option("--depth <n>", "Max traversal depth", "3").option("--types <types>", "Comma-separated node types to include").option("--edges <edges>", "Comma-separated edge types to include").option("--bidirectional", "Traverse both directions").action(async (rootNodeId, opts, cmd) => {
3054
+ return new Command38("query").description("Query the knowledge graph").argument("<rootNodeId>", "Starting node ID").option("--depth <n>", "Max traversal depth", "3").option("--types <types>", "Comma-separated node types to include").option("--edges <edges>", "Comma-separated edge types to include").option("--bidirectional", "Traverse both directions").action(async (rootNodeId, opts, cmd) => {
5288
3055
  const globalOpts = cmd.optsWithGlobals();
5289
- const projectPath = path39.resolve(globalOpts.config ? path39.dirname(globalOpts.config) : ".");
3056
+ const projectPath = path25.resolve(globalOpts.config ? path25.dirname(globalOpts.config) : ".");
5290
3057
  try {
5291
3058
  const result = await runQuery(projectPath, rootNodeId, {
5292
3059
  depth: parseInt(opts.depth),
@@ -5312,21 +3079,21 @@ function createQueryCommand() {
5312
3079
  }
5313
3080
 
5314
3081
  // src/commands/graph/index.ts
5315
- import { Command as Command42 } from "commander";
3082
+ import { Command as Command39 } from "commander";
5316
3083
 
5317
3084
  // src/commands/graph/status.ts
5318
- import * as path40 from "path";
3085
+ import * as path26 from "path";
5319
3086
  async function runGraphStatus(projectPath) {
5320
- const { GraphStore } = await import("./dist-K6KTTN3I.js");
5321
- const graphDir = path40.join(projectPath, ".harness", "graph");
3087
+ const { GraphStore } = await import("./dist-I7DB5VKB.js");
3088
+ const graphDir = path26.join(projectPath, ".harness", "graph");
5322
3089
  const store = new GraphStore();
5323
3090
  const loaded = await store.load(graphDir);
5324
3091
  if (!loaded) return { status: "no_graph", message: "No graph found. Run `harness scan` first." };
5325
- const fs25 = await import("fs/promises");
5326
- const metaPath = path40.join(graphDir, "metadata.json");
3092
+ const fs11 = await import("fs/promises");
3093
+ const metaPath = path26.join(graphDir, "metadata.json");
5327
3094
  let lastScan = "unknown";
5328
3095
  try {
5329
- const meta = JSON.parse(await fs25.readFile(metaPath, "utf-8"));
3096
+ const meta = JSON.parse(await fs11.readFile(metaPath, "utf-8"));
5330
3097
  lastScan = meta.lastScanTimestamp;
5331
3098
  } catch {
5332
3099
  }
@@ -5337,8 +3104,8 @@ async function runGraphStatus(projectPath) {
5337
3104
  }
5338
3105
  let connectorSyncStatus = {};
5339
3106
  try {
5340
- const syncMetaPath = path40.join(graphDir, "sync-metadata.json");
5341
- const syncMeta = JSON.parse(await fs25.readFile(syncMetaPath, "utf-8"));
3107
+ const syncMetaPath = path26.join(graphDir, "sync-metadata.json");
3108
+ const syncMeta = JSON.parse(await fs11.readFile(syncMetaPath, "utf-8"));
5342
3109
  for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
5343
3110
  connectorSyncStatus[name] = data.lastSyncTimestamp;
5344
3111
  }
@@ -5355,10 +3122,10 @@ async function runGraphStatus(projectPath) {
5355
3122
  }
5356
3123
 
5357
3124
  // src/commands/graph/export.ts
5358
- import * as path41 from "path";
3125
+ import * as path27 from "path";
5359
3126
  async function runGraphExport(projectPath, format) {
5360
- const { GraphStore } = await import("./dist-K6KTTN3I.js");
5361
- const graphDir = path41.join(projectPath, ".harness", "graph");
3127
+ const { GraphStore } = await import("./dist-I7DB5VKB.js");
3128
+ const graphDir = path27.join(projectPath, ".harness", "graph");
5362
3129
  const store = new GraphStore();
5363
3130
  const loaded = await store.load(graphDir);
5364
3131
  if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
@@ -5387,13 +3154,13 @@ async function runGraphExport(projectPath, format) {
5387
3154
  }
5388
3155
 
5389
3156
  // src/commands/graph/index.ts
5390
- import * as path42 from "path";
3157
+ import * as path28 from "path";
5391
3158
  function createGraphCommand() {
5392
- const graph = new Command42("graph").description("Knowledge graph management");
3159
+ const graph = new Command39("graph").description("Knowledge graph management");
5393
3160
  graph.command("status").description("Show graph statistics").action(async (_opts, cmd) => {
5394
3161
  try {
5395
3162
  const globalOpts = cmd.optsWithGlobals();
5396
- const projectPath = path42.resolve(globalOpts.config ? path42.dirname(globalOpts.config) : ".");
3163
+ const projectPath = path28.resolve(globalOpts.config ? path28.dirname(globalOpts.config) : ".");
5397
3164
  const result = await runGraphStatus(projectPath);
5398
3165
  if (globalOpts.json) {
5399
3166
  console.log(JSON.stringify(result, null, 2));
@@ -5420,7 +3187,7 @@ function createGraphCommand() {
5420
3187
  });
5421
3188
  graph.command("export").description("Export graph").requiredOption("--format <format>", "Output format (json, mermaid)").action(async (opts, cmd) => {
5422
3189
  const globalOpts = cmd.optsWithGlobals();
5423
- const projectPath = path42.resolve(globalOpts.config ? path42.dirname(globalOpts.config) : ".");
3190
+ const projectPath = path28.resolve(globalOpts.config ? path28.dirname(globalOpts.config) : ".");
5424
3191
  try {
5425
3192
  const output = await runGraphExport(projectPath, opts.format);
5426
3193
  console.log(output);
@@ -5432,9 +3199,18 @@ function createGraphCommand() {
5432
3199
  return graph;
5433
3200
  }
5434
3201
 
3202
+ // src/commands/mcp.ts
3203
+ import { Command as Command40 } from "commander";
3204
+ function createMcpCommand() {
3205
+ return new Command40("mcp").description("Start the MCP (Model Context Protocol) server on stdio").action(async () => {
3206
+ const { startServer: startServer2 } = await import("./mcp-BNLBTCXZ.js");
3207
+ await startServer2();
3208
+ });
3209
+ }
3210
+
5435
3211
  // src/index.ts
5436
3212
  function createProgram() {
5437
- const program = new Command43();
3213
+ const program = new Command41();
5438
3214
  program.name("harness").description("CLI for Harness Engineering toolkit").version(CLI_VERSION).option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--verbose", "Verbose output").option("--quiet", "Minimal output");
5439
3215
  program.addCommand(createValidateCommand());
5440
3216
  program.addCommand(createCheckDepsCommand());
@@ -5463,36 +3239,12 @@ function createProgram() {
5463
3239
  program.addCommand(createIngestCommand());
5464
3240
  program.addCommand(createQueryCommand());
5465
3241
  program.addCommand(createGraphCommand());
3242
+ program.addCommand(createMcpCommand());
5466
3243
  return program;
5467
3244
  }
5468
3245
 
5469
3246
  export {
5470
- CLI_VERSION,
5471
- findConfigFile,
5472
- loadConfig,
5473
- resolveConfig,
5474
- OutputMode,
5475
- OutputFormatter,
5476
- TemplateEngine,
5477
- loadPersona,
5478
- listPersonas,
5479
- detectTrigger,
5480
- runPersona,
5481
- executeSkill,
5482
- ALLOWED_PERSONA_COMMANDS,
5483
- generateRuntime,
5484
- generateAgentsMd,
5485
- generateCIWorkflow,
5486
3247
  buildPreamble,
5487
- runCheckPhaseGate,
5488
- generateSlashCommands,
5489
- AGENT_DESCRIPTIONS,
5490
- DEFAULT_TOOLS,
5491
- GEMINI_TOOL_MAP,
5492
- generateAgentDefinition,
5493
- renderClaudeCodeAgent,
5494
- renderGeminiAgent,
5495
- generateAgentDefinitions,
5496
3248
  runScan,
5497
3249
  runIngest,
5498
3250
  runQuery,