phasegate 0.135.0 → 0.137.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 (36) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.ja.md +7 -5
  3. package/README.md +7 -4
  4. package/docs/guide/cli-reference.md +3 -0
  5. package/docs/templates/agent-context/CLAUDE.md.template.md +29 -0
  6. package/docs/templates/ci/agent-context-refresh.yml +59 -0
  7. package/docs/templates/ci/aidlc-gate.yml +97 -0
  8. package/docs/templates/ci/consistency-check.yml +117 -0
  9. package/docs/templates/hooks/commit-msg +13 -0
  10. package/docs/templates/hooks/pre-commit +62 -0
  11. package/package.json +2 -1
  12. package/scripts/harness/ci-governance/application/dto/check-agent-context-input.ts +8 -0
  13. package/scripts/harness/ci-governance/application/dto/check-agent-context-output.ts +17 -0
  14. package/scripts/harness/ci-governance/application/dto/refresh-agent-context-input.ts +8 -0
  15. package/scripts/harness/ci-governance/application/dto/refresh-agent-context-output.ts +15 -0
  16. package/scripts/harness/ci-governance/application/dto/refresh-claude-md-input.ts +8 -0
  17. package/scripts/harness/ci-governance/application/dto/refresh-claude-md-output.ts +13 -0
  18. package/scripts/harness/ci-governance/application/usecases/check-agent-context-usecase.ts +48 -0
  19. package/scripts/harness/ci-governance/application/usecases/refresh-agent-context-usecase.ts +31 -0
  20. package/scripts/harness/ci-governance/application/usecases/refresh-claude-md-usecase.ts +73 -0
  21. package/scripts/harness/ci-governance/composition-root.ts +23 -1
  22. package/scripts/harness/ci-governance/domain/aggregates/ci-template.ts +1 -1
  23. package/scripts/harness/ci-governance/domain/ports/agent-context-document-port.ts +17 -0
  24. package/scripts/harness/ci-governance/domain/services/claude-md-composer.ts +39 -0
  25. package/scripts/harness/ci-governance/domain/services/template-generator.ts +1 -0
  26. package/scripts/harness/ci-governance/domain/types/template-type.ts +2 -1
  27. package/scripts/harness/ci-governance/infrastructure/adapters/agent-context-file-adapter.ts +55 -0
  28. package/scripts/harness/ci-governance/infrastructure/adapters/yaml-template-renderer-adapter.ts +16 -31
  29. package/scripts/harness/ci-governance/presentation/handlers/check-agent-context-handler.ts +33 -0
  30. package/scripts/harness/ci-governance/presentation/handlers/generate-ci-template-handler.ts +19 -1
  31. package/scripts/harness/ci-governance/presentation/handlers/refresh-agent-context-handler.ts +44 -0
  32. package/scripts/harness/ci-governance/presentation/handlers/refresh-claude-md-handler.ts +38 -0
  33. package/scripts/harness/main.ts +57 -8
  34. package/scripts/harness/setup/skill-deployer.ts +52 -2
  35. package/scripts/harness/traceability-model/domain/value-objects/work-item-frontmatter.ts +3 -1
  36. package/scripts/harness/validator-system/infrastructure/adapters/phase-dependency-phase-gate-policy-adapter.ts +4 -0
@@ -41,6 +41,7 @@ import type { SkillSet } from "./setup/skill-deployer.js";
41
41
  import {
42
42
  deployAgentSkillLinks,
43
43
  deployCodexHooks,
44
+ deployCiWorkflows,
44
45
  deployDesignDocs,
45
46
  deployHookScripts,
46
47
  deployHuskyCommitMsgHook,
@@ -89,7 +90,7 @@ Usage: phasegate <command> [options]
89
90
  Setup:
90
91
  init Initialize project: deploy skills + design docs + phasegate.config.json
91
92
  (--name <project-name>, --preset <full|standard|minimal|custom>,
92
- --skills <core|all>, --agent <claude|codex|both>, --with-husky, --yes)
93
+ --skills <core|all>, --agent <claude|codex|both>, --with-husky, --with-ci, --yes)
93
94
  update-skills Re-deploy skills from current harness version
94
95
 
95
96
  Commands:
@@ -122,8 +123,11 @@ Commands:
122
123
  phasegate:complete-check Complete L2-L4 check (--json)
123
124
  phasegate:impact-analysis Impact analysis for story (<storyId>, --json)
124
125
 
125
- ci:generate-template Generate CI template (--preset <id>, --type <aidlc-gate|consistency-check|pre-commit>, --render, --json)
126
+ ci:generate-template Generate CI template (--preset <id>, --type <aidlc-gate|consistency-check|pre-commit|agent-context-refresh>, --render, --json)
126
127
  ci:migrate-agents-md Migrate AGENTS.md (--dry-run, --validate-only, --json)
128
+ ci:auto-refresh-agent-context Refresh AGENTS.md / CLAUDE.md (--dry-run, --apply, --json)
129
+ refresh-claude-md Refresh CLAUDE.md standard sections (--dry-run, --apply, --json)
130
+ p2:check-agent-context Check AGENTS.md / CLAUDE.md freshness (--threshold-days <n>, --json)
127
131
  ci:check-repetition Check error repetition (--code <errorCode>, --reset, --json)
128
132
  baseline Create retrofit baseline snapshot (--dry-run, --force, --paths <glob,glob,...>, --json)
129
133
  scaffold-design Scaffold a design doc (--unit <id>, --phase <logical|domain|uiux|unit-test|it-test>, --force, --json)
@@ -251,6 +255,7 @@ Options:
251
255
  --skills <core|all> Skill set to deploy (default: "all")
252
256
  --agent <claude|codex|both> Agent integration target (default: "claude")
253
257
  --with-husky Install Husky pre-commit hooks
258
+ --with-ci Install GitHub Actions workflows
254
259
  --yes Skip confirmation prompts
255
260
  --help, -h Show this help`,
256
261
  "update-skills": `Usage: phasegate update-skills [options]
@@ -344,6 +349,7 @@ Options:
344
349
  aidlc-gate — AIDLC phase gate checks
345
350
  consistency-check — Doc/code consistency checks
346
351
  pre-commit — Pre-commit hook template
352
+ agent-context-refresh — AGENTS.md / CLAUDE.md refresh workflow
347
353
  --render Render the template to stdout
348
354
  --json Output in JSON format
349
355
 
@@ -636,7 +642,7 @@ async function main(): Promise<void> {
636
642
  switch (command) {
637
643
  // ── harness setup ──
638
644
  case "init": {
639
- const KNOWN_INIT_FLAGS = ["--name", "--preset", "--skills", "--agent", "--with-husky", "--yes"];
645
+ const KNOWN_INIT_FLAGS = ["--name", "--preset", "--skills", "--agent", "--with-husky", "--with-ci", "--yes"];
640
646
  const flagError = validateKnownFlags(args, KNOWN_INIT_FLAGS);
641
647
  if (flagError) {
642
648
  console.error(flagError);
@@ -674,7 +680,8 @@ async function main(): Promise<void> {
674
680
  claude: deployClaude,
675
681
  codex: deployCodex,
676
682
  });
677
- const configResult = await initHarnessConfig(rootDir, projectName, phasePreset);
683
+ const withCi = hasFlag(args, "--with-ci");
684
+ const configResult = await initHarnessConfig(rootDir, projectName, phasePreset, { ciEnabled: withCi });
678
685
  const hooksResult = deployClaude
679
686
  ? await deployHookScripts(harnessRoot, rootDir)
680
687
  : {
@@ -689,6 +696,7 @@ async function main(): Promise<void> {
689
696
  const withHusky = hasFlag(args, "--with-husky");
690
697
  const huskyResult = withHusky ? await deployHuskyHook(harnessRoot, rootDir) : null;
691
698
  const huskyCommitMsgResult = withHusky ? await deployHuskyCommitMsgHook(harnessRoot, rootDir) : null;
699
+ const ciWorkflowResult = withCi ? await deployCiWorkflows(harnessRoot, rootDir) : null;
692
700
  console.log(
693
701
  `✓ Skills deployed to ${result.targetDir} (${result.deployedSkills.length} skills, set: ${skillSet})`,
694
702
  );
@@ -753,6 +761,14 @@ async function main(): Promise<void> {
753
761
  console.log(` .husky/commit-msg already exists, skipped`);
754
762
  }
755
763
  }
764
+ if (ciWorkflowResult !== null) {
765
+ if (ciWorkflowResult.copiedFiles.length > 0) {
766
+ console.log(`✓ CI workflows deployed (${ciWorkflowResult.copiedFiles.length} files)`);
767
+ }
768
+ for (const skipped of ciWorkflowResult.skippedFiles) {
769
+ console.log(` ${skipped} already exists, skipped`);
770
+ }
771
+ }
756
772
  console.log(`✓ Harness v${result.version} initialized (agent: ${agent})`);
757
773
  console.log("");
758
774
  console.log("Next steps:");
@@ -1199,7 +1215,7 @@ Examples:
1199
1215
  phasegate ci:generate-template --preset strict --type pre-commit --render`);
1200
1216
  process.exit(0);
1201
1217
  }
1202
- const mod = buildCiGovernance(rootDir);
1218
+ const mod = buildCiGovernance(rootDir, harnessRoot);
1203
1219
  const presetId = parseFlag(args, "--preset") ?? "default";
1204
1220
  const templateType = parseFlag(args, "--type") ?? "aidlc-gate";
1205
1221
  const render = hasFlag(args, "--render");
@@ -1211,7 +1227,7 @@ Examples:
1211
1227
  }
1212
1228
 
1213
1229
  case "ci:migrate-agents-md": {
1214
- const mod = buildCiGovernance(rootDir);
1230
+ const mod = buildCiGovernance(rootDir, harnessRoot);
1215
1231
  const dryRun = hasFlag(args, "--dry-run");
1216
1232
  const validateOnly = hasFlag(args, "--validate-only");
1217
1233
  const format = json ? "json" : "human";
@@ -1221,8 +1237,41 @@ Examples:
1221
1237
  break;
1222
1238
  }
1223
1239
 
1240
+ case "ci:auto-refresh-agent-context": {
1241
+ const mod = buildCiGovernance(rootDir, harnessRoot);
1242
+ const dryRun = hasFlag(args, "--dry-run");
1243
+ const apply = hasFlag(args, "--apply");
1244
+ const format = json ? "json" : "human";
1245
+ const result = await mod.refreshAgentContextHandler.handle({ dryRun, apply, format });
1246
+ console.log(result.output);
1247
+ process.exit(result.exitCode);
1248
+ break;
1249
+ }
1250
+
1251
+ case "refresh-claude-md": {
1252
+ const mod = buildCiGovernance(rootDir, harnessRoot);
1253
+ const dryRun = hasFlag(args, "--dry-run");
1254
+ const apply = hasFlag(args, "--apply");
1255
+ const format = json ? "json" : "human";
1256
+ const result = await mod.refreshClaudeMdHandler.handle({ dryRun, apply, format });
1257
+ console.log(result.output);
1258
+ process.exit(result.exitCode);
1259
+ break;
1260
+ }
1261
+
1262
+ case "p2:check-agent-context": {
1263
+ const mod = buildCiGovernance(rootDir, harnessRoot);
1264
+ const thresholdRaw = parseFlag(args, "--threshold-days");
1265
+ const thresholdDays = thresholdRaw === undefined ? undefined : Number(thresholdRaw);
1266
+ const format = json ? "json" : "human";
1267
+ const result = await mod.checkAgentContextHandler.handle({ thresholdDays, format });
1268
+ console.log(result.output);
1269
+ process.exit(result.exitCode);
1270
+ break;
1271
+ }
1272
+
1224
1273
  case "ci:check-repetition": {
1225
- const mod = buildCiGovernance(rootDir);
1274
+ const mod = buildCiGovernance(rootDir, harnessRoot);
1226
1275
  const errorCode = parseFlag(args, "--code") ?? "";
1227
1276
  const reset = hasFlag(args, "--reset");
1228
1277
  const format = json ? "json" : "human";
@@ -1233,7 +1282,7 @@ Examples:
1233
1282
  }
1234
1283
 
1235
1284
  case "baseline": {
1236
- const mod = buildCiGovernance(rootDir);
1285
+ const mod = buildCiGovernance(rootDir, harnessRoot);
1237
1286
  const dryRun = hasFlag(args, "--dry-run");
1238
1287
  const force = hasFlag(args, "--force");
1239
1288
  const pathsFlag = parseFlag(args, "--paths");
@@ -420,10 +420,15 @@ export async function deployHookScripts(harnessRoot: string, projectRoot: string
420
420
  };
421
421
  }
422
422
 
423
+ export interface InitHarnessConfigOptions {
424
+ ciEnabled?: boolean;
425
+ }
426
+
423
427
  export async function initHarnessConfig(
424
428
  projectRoot: string,
425
429
  projectName: string,
426
430
  phasePreset?: "full" | "standard" | "minimal" | "custom",
431
+ options: InitHarnessConfigOptions = {},
427
432
  ): Promise<{ created: boolean; path: string }> {
428
433
  const configPath = join(projectRoot, HARNESS_CONFIG_FILE);
429
434
  try {
@@ -461,6 +466,7 @@ export async function initHarnessConfig(
461
466
  format: "json",
462
467
  outputDir: "reports",
463
468
  },
469
+ ...(options.ciEnabled ? { ci: { enabled: true } } : {}),
464
470
  };
465
471
 
466
472
  await fs.writeFile(configPath, JSON.stringify(template, null, 2) + "\n", "utf-8");
@@ -532,6 +538,50 @@ export interface DeployHuskyHookResult {
532
538
  path: string;
533
539
  }
534
540
 
541
+ export interface DeployCiWorkflowsResult {
542
+ copiedFiles: string[];
543
+ skippedFiles: string[];
544
+ }
545
+
546
+ export async function deployCiWorkflows(harnessRoot: string, projectRoot: string): Promise<DeployCiWorkflowsResult> {
547
+ const copiedFiles: string[] = [];
548
+ const skippedFiles: string[] = [];
549
+ const workflows = [
550
+ {
551
+ relativeSource: join("docs", "templates", "ci", "aidlc-gate.yml"),
552
+ relativeTarget: join(".github", "workflows", "aidlc-gate.yml"),
553
+ },
554
+ {
555
+ relativeSource: join("docs", "templates", "ci", "consistency-check.yml"),
556
+ relativeTarget: join(".github", "workflows", "consistency-check.yml"),
557
+ },
558
+ {
559
+ relativeSource: join("docs", "templates", "ci", "agent-context-refresh.yml"),
560
+ relativeTarget: join(".github", "workflows", "agent-context-refresh.yml"),
561
+ },
562
+ ];
563
+
564
+ await fs.mkdir(join(projectRoot, ".github", "workflows"), { recursive: true });
565
+
566
+ for (const workflow of workflows) {
567
+ const sourcePath = join(harnessRoot, workflow.relativeSource);
568
+ const targetPath = join(projectRoot, workflow.relativeTarget);
569
+
570
+ try {
571
+ await fs.access(targetPath);
572
+ skippedFiles.push(workflow.relativeTarget);
573
+ continue;
574
+ } catch {
575
+ // ファイルが存在しない場合のみコピーする
576
+ }
577
+
578
+ await fs.copyFile(sourcePath, targetPath);
579
+ copiedFiles.push(workflow.relativeTarget);
580
+ }
581
+
582
+ return { copiedFiles, skippedFiles };
583
+ }
584
+
535
585
  export async function deployHuskyHook(harnessRoot: string, projectRoot: string): Promise<DeployHuskyHookResult> {
536
586
  const targetPath = join(projectRoot, ".husky", "pre-commit");
537
587
 
@@ -540,7 +590,7 @@ export async function deployHuskyHook(harnessRoot: string, projectRoot: string):
540
590
  return { created: false, path: targetPath };
541
591
  } catch {}
542
592
 
543
- const sourcePath = join(harnessRoot, "templates", ".husky", "pre-commit");
593
+ const sourcePath = join(harnessRoot, "docs", "templates", "hooks", "pre-commit");
544
594
  await fs.mkdir(join(projectRoot, ".husky"), { recursive: true });
545
595
  await fs.copyFile(sourcePath, targetPath);
546
596
  await fs.chmod(targetPath, 0o755);
@@ -559,7 +609,7 @@ export async function deployHuskyCommitMsgHook(
559
609
  return { created: false, path: targetPath };
560
610
  } catch {}
561
611
 
562
- const sourcePath = join(harnessRoot, "templates", ".husky", "commit-msg");
612
+ const sourcePath = join(harnessRoot, "docs", "templates", "hooks", "commit-msg");
563
613
  await fs.mkdir(join(projectRoot, ".husky"), { recursive: true });
564
614
  await fs.copyFile(sourcePath, targetPath);
565
615
  await fs.chmod(targetPath, 0o755);
@@ -13,7 +13,8 @@ export type WorkItemStatus =
13
13
  | 'drafted'
14
14
  | 'reflected'
15
15
  | 'implemented'
16
- | 'tested';
16
+ | 'tested'
17
+ | 'completed';
17
18
 
18
19
  export interface WorkItemFrontmatter {
19
20
  readonly id: string;
@@ -54,4 +55,5 @@ export const WORK_ITEM_STATUSES: ReadonlySet<WorkItemStatus> = new Set([
54
55
  'reflected',
55
56
  'implemented',
56
57
  'tested',
58
+ 'completed',
57
59
  ]);
@@ -12,6 +12,10 @@ export class PhaseDependencyPhaseGatePolicyAdapter implements PhaseGatePolicyPor
12
12
  satisfied: boolean;
13
13
  violations: readonly HarnessErrorLike[];
14
14
  }> {
15
+ if (context.unitName.trim().length === 0) {
16
+ return { satisfied: true, violations: [] };
17
+ }
18
+
15
19
  try {
16
20
  // WI-085: paths config を phase-dependency-model に流入させる
17
21
  const { createConfigFoundationModule } = await import('../../../config-foundation/composition-root.js');