phasegate 0.136.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 (29) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.ja.md +5 -3
  3. package/README.md +5 -3
  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/package.json +1 -1
  8. package/scripts/harness/ci-governance/application/dto/check-agent-context-input.ts +8 -0
  9. package/scripts/harness/ci-governance/application/dto/check-agent-context-output.ts +17 -0
  10. package/scripts/harness/ci-governance/application/dto/refresh-agent-context-input.ts +8 -0
  11. package/scripts/harness/ci-governance/application/dto/refresh-agent-context-output.ts +15 -0
  12. package/scripts/harness/ci-governance/application/dto/refresh-claude-md-input.ts +8 -0
  13. package/scripts/harness/ci-governance/application/dto/refresh-claude-md-output.ts +13 -0
  14. package/scripts/harness/ci-governance/application/usecases/check-agent-context-usecase.ts +48 -0
  15. package/scripts/harness/ci-governance/application/usecases/refresh-agent-context-usecase.ts +31 -0
  16. package/scripts/harness/ci-governance/application/usecases/refresh-claude-md-usecase.ts +73 -0
  17. package/scripts/harness/ci-governance/composition-root.ts +22 -0
  18. package/scripts/harness/ci-governance/domain/aggregates/ci-template.ts +1 -1
  19. package/scripts/harness/ci-governance/domain/ports/agent-context-document-port.ts +17 -0
  20. package/scripts/harness/ci-governance/domain/services/claude-md-composer.ts +39 -0
  21. package/scripts/harness/ci-governance/domain/services/template-generator.ts +1 -0
  22. package/scripts/harness/ci-governance/domain/types/template-type.ts +2 -1
  23. package/scripts/harness/ci-governance/infrastructure/adapters/agent-context-file-adapter.ts +55 -0
  24. package/scripts/harness/ci-governance/infrastructure/adapters/yaml-template-renderer-adapter.ts +2 -0
  25. package/scripts/harness/ci-governance/presentation/handlers/check-agent-context-handler.ts +33 -0
  26. package/scripts/harness/ci-governance/presentation/handlers/refresh-agent-context-handler.ts +44 -0
  27. package/scripts/harness/ci-governance/presentation/handlers/refresh-claude-md-handler.ts +38 -0
  28. package/scripts/harness/main.ts +38 -1
  29. package/scripts/harness/setup/skill-deployer.ts +4 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.137.0] - 2026-05-09
11
+
12
+ ### Added
13
+
14
+ - **WI-032 — AGENTS.md / CLAUDE.md auto-refresh pipeline** — `ci:auto-refresh-agent-context --dry-run|--apply|--json` を追加し、lesson artifact 由来の AGENTS.md pointer 更新と CLAUDE.md 標準セクション更新を 1 コマンドで実行できるようにした。
15
+ - `refresh-claude-md --dry-run|--apply|--json` を追加し、CLAUDE.md の user-owned section を保持しながら bundled template から標準セクションを再生成。
16
+ - `p2:check-agent-context --threshold-days <n> --json` を追加し、AGENTS.md / CLAUDE.md の鮮度を検査可能にした。
17
+ - `docs/templates/ci/agent-context-refresh.yml` を追加し、`ci:generate-template --type agent-context-refresh --render` と `phasegate init --with-ci` で配布可能にした。
18
+ - PhaseGate 自身にも `.github/workflows/agent-context-refresh.yml` を追加し、週次 refresh PR を作成する構成にした。
19
+
10
20
  ## [0.136.0] - 2026-05-09
11
21
 
12
22
  ### Added
package/README.ja.md CHANGED
@@ -87,7 +87,7 @@ claude
87
87
  - `docs/principles/*.md` — アーキテクチャ哲学・テスト規約(immutable)
88
88
  - `docs/folder_management_rules.md` — ドキュメント配置ルール(**正本**)
89
89
  - `--with-husky` を付けると `.husky/pre-commit` ・ `.husky/commit-msg` も配置
90
- - `--with-ci` を付けると `.github/workflows/aidlc-gate.yml` ・ `.github/workflows/consistency-check.yml` も配置
90
+ - `--with-ci` を付けると `.github/workflows/aidlc-gate.yml` ・ `.github/workflows/consistency-check.yml` ・ `.github/workflows/agent-context-refresh.yml` も配置
91
91
 
92
92
  **`init` が生成しないもの**(後で skill が作る):
93
93
 
@@ -296,7 +296,10 @@ npx phasegate <command> [options]
296
296
  | `phasegate:detect-drift` | 設計-コード乖離レポート |
297
297
  | `migrate work-items --dry-run` / `--apply` | 既存リポジトリの旧 `ISSUE-XXX` / `H{NN}-{NN}` directory を WI 統一レイアウト(`_cross/{WI-XXX}/` / `{unit}/{WI-XXX}/`)へ移行。frontmatter(`type` / `severity` / `legacy_id` / `affects`)を自動注入。冪等。`--json` で CI/スクリプト連携可。詳細: [Work Item Migration](docs/guide/cli-reference.md#work-item-migration) |
298
298
  | `migrate --schema v3` | `phasegate.config.json` を v3 schema へ昇格(`architecture` キー追加) |
299
- | `ci:generate-template --type <aidlc-gate\|pre-commit\|consistency-check>` | CI/CD テンプレート生成(`--render` で bundled template を stdout 出力) |
299
+ | `ci:generate-template --type <aidlc-gate\|pre-commit\|consistency-check\|agent-context-refresh>` | CI/CD テンプレート生成(`--render` で bundled template を stdout 出力) |
300
+ | `ci:auto-refresh-agent-context --dry-run` / `--apply` | AGENTS.md pointer と CLAUDE.md 標準セクションを更新 |
301
+ | `refresh-claude-md --dry-run` / `--apply` | user section を保持して CLAUDE.md だけを更新 |
302
+ | `p2:check-agent-context` | AGENTS.md / CLAUDE.md の鮮度を検査 |
300
303
  | `list-errors --layer <L0-L4>` | エラー定義一覧 |
301
304
  | `hook <pre-tool-use\|post-tool-use\|stop>` | agent hook を起動(stdin から JSON) |
302
305
  | `pre-commit` | L2 pre-commit バリデータをステージファイルに適用 |
@@ -452,7 +455,6 @@ reports/
452
455
 
453
456
  | Work Item | 内容 |
454
457
  |---|---|
455
- | **[WI-032](docs/inception/_cross/WI-032/description.md)** | AGENTS.md / CLAUDE.md auto-refresh パイプライン |
456
458
  | **[WI-033](docs/inception/_cross/WI-033/description.md)** | `doc-freshness` / `pointer-validation` を L4 validator に昇格 |
457
459
 
458
460
  L3 Nyquist Validation の `requirement-test-matrix.json` 自動生成はまだ未自動化です。現時点では手動セットアップで利用できます。
package/README.md CHANGED
@@ -86,7 +86,7 @@ claude
86
86
  - `.claude/settings.json` and/or `.codex/hooks.json` hook configuration
87
87
  - `docs/principles/*.md` and `docs/folder_management_rules.md`
88
88
  - `.husky/pre-commit` and `.husky/commit-msg` when `--with-husky` is passed
89
- - `.github/workflows/aidlc-gate.yml` and `.github/workflows/consistency-check.yml` when `--with-ci` is passed
89
+ - `.github/workflows/aidlc-gate.yml`, `.github/workflows/consistency-check.yml`, and `.github/workflows/agent-context-refresh.yml` when `--with-ci` is passed
90
90
 
91
91
  `init` intentionally does **not** create `docs/inception/` work item directories or `docs/product/` design documents. Those are produced later by skills such as `/product-architect`, `/domain-designer`, and `/logical-designer`. That is the core contract: no design, no code.
92
92
 
@@ -468,7 +468,10 @@ npx phasegate <command> [options]
468
468
  | `lint` | Run L1 Biome AST checks |
469
469
  | `validate --layer <L2-L4\|all>` | Run validators for specified layer (`--layer L0` prints runtime hook guidance) |
470
470
  | `ci-check` | Full CI check (L2-L4) |
471
- | `ci:generate-template --type <aidlc-gate\|consistency-check\|pre-commit> --render` | Render the bundled CI/hook template to stdout |
471
+ | `ci:generate-template --type <aidlc-gate\|consistency-check\|pre-commit\|agent-context-refresh> --render` | Render the bundled CI/hook template to stdout |
472
+ | `ci:auto-refresh-agent-context --dry-run` / `--apply` | Refresh AGENTS.md pointers and CLAUDE.md standard sections |
473
+ | `refresh-claude-md --dry-run` / `--apply` | Refresh only CLAUDE.md while preserving the user-owned section |
474
+ | `p2:check-agent-context` | Check AGENTS.md / CLAUDE.md freshness |
472
475
  | `update-skills` | Update skills to latest version |
473
476
  | `phasegate:status` | Display overall harness health summary |
474
477
  | `phasegate:check-phase --unit <id>` | Check current phase for a Unit |
@@ -514,7 +517,6 @@ The main path is ready for project use, but a few documented behaviors still req
514
517
 
515
518
  | Work Item | Title | Why it matters |
516
519
  |---|---|---|
517
- | **[WI-032](docs/inception/_cross/WI-032/description.md)** | AGENTS.md / CLAUDE.md auto-refresh pipeline | `ci:migrate-agents-md` exists as a one-shot CLI but there is no scheduled job, and CLAUDE.md is fully hand-maintained. WI-032 adds an `auto-refresh-agent-context` workflow plus a template-driven CLAUDE.md regenerator that preserves user-owned sections. |
518
520
  | **[WI-033](docs/inception/_cross/WI-033/description.md)** | Promote `doc-freshness` / `pointer-validation` to L4 validators | Both capabilities exist as `p2:check-freshness` / `p2:validate-pointers` CLI commands but are not registered as L4 validators, so `validate --layer L4` skips them. WI-033 plumbs them through `validator-system` so they run via the standard L4 path and presets. |
519
521
 
520
522
  `requirement-test-matrix.json` auto-generation for L3 Nyquist Validation is not automated yet; see the L3 guide for the current manual setup.
@@ -321,6 +321,9 @@ ISSUE-005 P3-10 で明確化された境界:
321
321
  |---|---|---|
322
322
  | `ci:generate-template` | `--preset <id>` `--type <type>` `--render` `--json` | Generate CI/CD template |
323
323
  | `ci:migrate-agents-md` | `--dry-run` `--validate-only` `--json` | Migrate AGENTS.md to pointer format |
324
+ | `ci:auto-refresh-agent-context` | `--dry-run` `--apply` `--json` | Refresh AGENTS.md pointers and CLAUDE.md standard sections |
325
+ | `refresh-claude-md` | `--dry-run` `--apply` `--json` | Refresh CLAUDE.md while preserving the user-owned section |
326
+ | `p2:check-agent-context` | `--threshold-days <n>` `--json` | Check AGENTS.md / CLAUDE.md freshness |
324
327
  | `ci:check-repetition` | `--code <errorCode>` `--reset` `--json` | Detect repetitive errors |
325
328
 
326
329
  ---
@@ -0,0 +1,29 @@
1
+ # CLAUDE.md
2
+
3
+ ## 必読ドキュメント
4
+
5
+ - `docs/folder_management_rules.md`
6
+ - `docs/principles/architecture-philosophy.md`
7
+ - `docs/principles/testing-rules.md`
8
+
9
+ ## PhaseGate Commands
10
+
11
+ {{PHASEGATE_COMMANDS}}
12
+
13
+ ## Phase Presets
14
+
15
+ {{PHASEGATE_PRESETS}}
16
+
17
+ ## Skills
18
+
19
+ {{PHASEGATE_SKILLS}}
20
+
21
+ ## User Section
22
+
23
+ <!-- phasegate:user-section:start -->
24
+ {{PHASEGATE_USER_SECTION}}
25
+ <!-- phasegate:user-section:end -->
26
+
27
+ ## Agent Context Refresh
28
+
29
+ Run `phasegate ci:auto-refresh-agent-context --dry-run` to preview updates and `phasegate ci:auto-refresh-agent-context --apply` to write AGENTS.md / CLAUDE.md.
@@ -0,0 +1,59 @@
1
+ # Phasegate — Agent context refresh workflow
2
+ #
3
+ # 使い方:
4
+ # このファイルを .github/workflows/agent-context-refresh.yml にコピーして使用する。
5
+ #
6
+ # 実行タイミング: 毎週火曜 04:00 UTC(手動トリガーも可)
7
+ # 実行内容:
8
+ # 1. AGENTS.md pointer refresh
9
+ # 2. CLAUDE.md standard section refresh
10
+ # 3. 変更検出時: refresh branch を push して PR を作成
11
+
12
+ name: Agent Context Refresh
13
+
14
+ on:
15
+ schedule:
16
+ - cron: '0 4 * * 2'
17
+ workflow_dispatch:
18
+
19
+ jobs:
20
+ refresh-agent-context:
21
+ name: Refresh AGENTS.md and CLAUDE.md
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ contents: write
25
+ pull-requests: write
26
+
27
+ steps:
28
+ - name: Checkout
29
+ uses: actions/checkout@v4
30
+
31
+ - name: Setup Node.js
32
+ uses: actions/setup-node@v4
33
+ with:
34
+ node-version: '20'
35
+ cache: 'pnpm'
36
+
37
+ - name: Install pnpm
38
+ uses: pnpm/action-setup@v4
39
+ with:
40
+ version: 9
41
+
42
+ - name: Install dependencies
43
+ run: pnpm install --frozen-lockfile
44
+
45
+ - name: Refresh agent context
46
+ run: pnpm run harness ci:auto-refresh-agent-context --apply
47
+
48
+ - name: Create pull request
49
+ uses: peter-evans/create-pull-request@v6
50
+ with:
51
+ branch: phasegate/agent-context-refresh
52
+ title: 'chore: refresh agent context'
53
+ commit-message: 'chore: refresh agent context'
54
+ body: |
55
+ Automated AGENTS.md / CLAUDE.md refresh generated by PhaseGate.
56
+ labels: |
57
+ aidlc
58
+ agent-context
59
+ automated
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.136.0",
3
+ "version": "0.137.0",
4
4
  "packageManager": "pnpm@10.30.1",
5
5
  "description": "Phasegate — AI-agnostic quality defense toolkit. Enforces structural integrity between design intent and code.",
6
6
  "license": "MIT",
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @layer application
3
+ * @unit ci-governance
4
+ */
5
+
6
+ export interface CheckAgentContextInput {
7
+ readonly thresholdDays?: number;
8
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @layer application
3
+ * @unit ci-governance
4
+ */
5
+
6
+ export interface AgentContextFreshnessFinding {
7
+ readonly path: string;
8
+ readonly status: 'pass' | 'error';
9
+ readonly ageInDays: number | null;
10
+ readonly message: string;
11
+ }
12
+
13
+ export interface CheckAgentContextOutput {
14
+ readonly passed: boolean;
15
+ readonly thresholdDays: number;
16
+ readonly findings: readonly AgentContextFreshnessFinding[];
17
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @layer application
3
+ * @unit ci-governance
4
+ */
5
+
6
+ export interface RefreshAgentContextInput {
7
+ readonly dryRun: boolean;
8
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @layer application
3
+ * @unit ci-governance
4
+ */
5
+
6
+ import type { MigrateAgentsMdOutput } from './migrate-agents-md-output.js';
7
+ import type { RefreshClaudeMdOutput } from './refresh-claude-md-output.js';
8
+
9
+ export interface RefreshAgentContextOutput {
10
+ readonly success: boolean;
11
+ readonly applied: boolean;
12
+ readonly agentsMd: MigrateAgentsMdOutput;
13
+ readonly claudeMd: RefreshClaudeMdOutput;
14
+ readonly errors: Array<{ code: string; message: string }>;
15
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @layer application
3
+ * @unit ci-governance
4
+ */
5
+
6
+ export interface RefreshClaudeMdInput {
7
+ readonly dryRun: boolean;
8
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @layer application
3
+ * @unit ci-governance
4
+ */
5
+
6
+ export interface RefreshClaudeMdOutput {
7
+ readonly success: boolean;
8
+ readonly path: string;
9
+ readonly changed: boolean;
10
+ readonly applied: boolean;
11
+ readonly preview: string;
12
+ readonly errors: Array<{ code: string; message: string }>;
13
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @layer application
3
+ * @unit ci-governance
4
+ */
5
+
6
+ import type { AgentContextDocumentPort } from '../../domain/ports/agent-context-document-port.js';
7
+ import type { CheckAgentContextInput } from '../dto/check-agent-context-input.js';
8
+ import type { AgentContextFreshnessFinding, CheckAgentContextOutput } from '../dto/check-agent-context-output.js';
9
+
10
+ const AGENT_CONTEXT_PATHS = ['AGENTS.md', 'CLAUDE.md'] as const;
11
+ const DEFAULT_THRESHOLD_DAYS = 30;
12
+
13
+ export class CheckAgentContextUseCase {
14
+ constructor(private readonly documentPort: AgentContextDocumentPort) {}
15
+
16
+ async execute(input: CheckAgentContextInput): Promise<CheckAgentContextOutput> {
17
+ const thresholdDays = input.thresholdDays ?? DEFAULT_THRESHOLD_DAYS;
18
+ const findings: AgentContextFreshnessFinding[] = [];
19
+
20
+ for (const path of AGENT_CONTEXT_PATHS) {
21
+ const stat = await this.documentPort.statProjectFile(path);
22
+ if (!stat.exists) {
23
+ findings.push({
24
+ path,
25
+ status: 'error',
26
+ ageInDays: null,
27
+ message: `${path} does not exist`,
28
+ });
29
+ continue;
30
+ }
31
+ const stale = stat.ageInDays !== null && stat.ageInDays > thresholdDays;
32
+ findings.push({
33
+ path,
34
+ status: stale ? 'error' : 'pass',
35
+ ageInDays: stat.ageInDays,
36
+ message: stale
37
+ ? `${path} is older than ${thresholdDays} days`
38
+ : `${path} is fresh`,
39
+ });
40
+ }
41
+
42
+ return {
43
+ passed: findings.every((finding) => finding.status === 'pass'),
44
+ thresholdDays,
45
+ findings,
46
+ };
47
+ }
48
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @layer application
3
+ * @unit ci-governance
4
+ */
5
+
6
+ import type { MigrateAgentsMdUseCase } from './migrate-agents-md-usecase.js';
7
+ import type { RefreshClaudeMdUseCase } from './refresh-claude-md-usecase.js';
8
+ import type { RefreshAgentContextInput } from '../dto/refresh-agent-context-input.js';
9
+ import type { RefreshAgentContextOutput } from '../dto/refresh-agent-context-output.js';
10
+
11
+ export class RefreshAgentContextUseCase {
12
+ constructor(
13
+ private readonly migrateAgentsMdUseCase: MigrateAgentsMdUseCase,
14
+ private readonly refreshClaudeMdUseCase: RefreshClaudeMdUseCase,
15
+ ) {}
16
+
17
+ async execute(input: RefreshAgentContextInput): Promise<RefreshAgentContextOutput> {
18
+ const [agentsMd, claudeMd] = await Promise.all([
19
+ this.migrateAgentsMdUseCase.execute({ dryRun: input.dryRun }),
20
+ this.refreshClaudeMdUseCase.execute({ dryRun: input.dryRun }),
21
+ ]);
22
+ const errors = [...agentsMd.errors, ...claudeMd.errors];
23
+ return {
24
+ success: agentsMd.success && claudeMd.success,
25
+ applied: !input.dryRun && agentsMd.success && claudeMd.success,
26
+ agentsMd,
27
+ claudeMd,
28
+ errors,
29
+ };
30
+ }
31
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @layer application
3
+ * @unit ci-governance
4
+ */
5
+
6
+ import type { AgentContextDocumentPort } from '../../domain/ports/agent-context-document-port.js';
7
+ import type { ClaudeMdComposer } from '../../domain/services/claude-md-composer.js';
8
+ import type { RefreshClaudeMdInput } from '../dto/refresh-claude-md-input.js';
9
+ import type { RefreshClaudeMdOutput } from '../dto/refresh-claude-md-output.js';
10
+
11
+ const CLAUDE_MD_PATH = 'CLAUDE.md';
12
+ const CLAUDE_MD_TEMPLATE_PATH = 'docs/templates/agent-context/CLAUDE.md.template.md';
13
+
14
+ const PHASEGATE_COMMANDS = [
15
+ 'phasegate init --with-ci',
16
+ 'phasegate ci:auto-refresh-agent-context --dry-run',
17
+ 'phasegate ci:auto-refresh-agent-context --apply',
18
+ 'phasegate refresh-claude-md --apply',
19
+ 'phasegate p2:check-agent-context',
20
+ 'phasegate phasegate:check-ready',
21
+ ];
22
+
23
+ const PHASE_PRESETS = ['minimal', 'standard', 'full', 'custom'];
24
+
25
+ export class RefreshClaudeMdUseCase {
26
+ constructor(
27
+ private readonly documentPort: AgentContextDocumentPort,
28
+ private readonly composer: ClaudeMdComposer,
29
+ ) {}
30
+
31
+ async execute(input: RefreshClaudeMdInput): Promise<RefreshClaudeMdOutput> {
32
+ try {
33
+ const [template, existing, skills] = await Promise.all([
34
+ this.documentPort.readHarnessTemplate(CLAUDE_MD_TEMPLATE_PATH),
35
+ this.documentPort.readProjectFile(CLAUDE_MD_PATH),
36
+ this.documentPort.listHarnessSkillNames(),
37
+ ]);
38
+ const nextContent = this.composer.compose(template, existing, {
39
+ commands: PHASEGATE_COMMANDS,
40
+ skills,
41
+ presets: PHASE_PRESETS,
42
+ });
43
+ const changed = existing !== nextContent;
44
+
45
+ if (!input.dryRun && changed) {
46
+ await this.documentPort.writeProjectFile(CLAUDE_MD_PATH, nextContent);
47
+ }
48
+
49
+ return {
50
+ success: true,
51
+ path: CLAUDE_MD_PATH,
52
+ changed,
53
+ applied: !input.dryRun && changed,
54
+ preview: nextContent,
55
+ errors: [],
56
+ };
57
+ } catch (error) {
58
+ return {
59
+ success: false,
60
+ path: CLAUDE_MD_PATH,
61
+ changed: false,
62
+ applied: false,
63
+ preview: '',
64
+ errors: [
65
+ {
66
+ code: 'AGENT_CONTEXT_CLAUDE_REFRESH_FAILED',
67
+ message: error instanceof Error ? error.message : String(error),
68
+ },
69
+ ],
70
+ };
71
+ }
72
+ }
73
+ }
@@ -9,6 +9,7 @@ import { TemplateGenerator } from './domain/services/template-generator.js';
9
9
  import { RepetitionDetector } from './domain/services/repetition-detector.js';
10
10
  import { PointerValidator } from './domain/services/pointer-validator.js';
11
11
  import { LessonAggregator } from './domain/services/lesson-aggregator.js';
12
+ import { ClaudeMdComposer } from './domain/services/claude-md-composer.js';
12
13
 
13
14
  import { GenerateCiTemplateUseCase } from './application/usecases/generate-ci-template-usecase.js';
14
15
  import { RenderCiTemplateUseCase } from './application/usecases/render-ci-template-usecase.js';
@@ -16,6 +17,9 @@ import { RecordErrorOccurrenceUseCase } from './application/usecases/record-erro
16
17
  import { CheckEscalationUseCase } from './application/usecases/check-escalation-usecase.js';
17
18
  import { ResetRepetitionUseCase } from './application/usecases/reset-repetition-usecase.js';
18
19
  import { MigrateAgentsMdUseCase } from './application/usecases/migrate-agents-md-usecase.js';
20
+ import { RefreshAgentContextUseCase } from './application/usecases/refresh-agent-context-usecase.js';
21
+ import { RefreshClaudeMdUseCase } from './application/usecases/refresh-claude-md-usecase.js';
22
+ import { CheckAgentContextUseCase } from './application/usecases/check-agent-context-usecase.js';
19
23
  import { AggregateLessonsUseCase } from './application/usecases/aggregate-lessons-usecase.js';
20
24
  import { ValidatePointersUseCase } from './application/usecases/validate-pointers-usecase.js';
21
25
 
@@ -26,12 +30,16 @@ import { EscalationLogExecutorAdapter } from './infrastructure/adapters/escalati
26
30
  import { YamlTemplateRendererAdapter } from './infrastructure/adapters/yaml-template-renderer-adapter.js';
27
31
  import { FileSystemExistenceAdapter } from './infrastructure/adapters/file-system-existence-adapter.js';
28
32
  import { AgentsMdFileAdapter } from './infrastructure/adapters/agents-md-file-adapter.js';
33
+ import { AgentContextFileAdapter } from './infrastructure/adapters/agent-context-file-adapter.js';
29
34
  import { LessonArtifactFileReaderAdapter } from './infrastructure/adapters/lesson-artifact-file-reader-adapter.js';
30
35
  import { HarnessApiCommandExistenceAdapter } from './infrastructure/adapters/harness-api-command-existence-adapter.js';
31
36
  import { AdrFoundationExistenceAdapter } from './infrastructure/adapters/adr-foundation-existence-adapter.js';
32
37
 
33
38
  import { GenerateCiTemplateHandler } from './presentation/handlers/generate-ci-template-handler.js';
34
39
  import { MigrateAgentsMdHandler } from './presentation/handlers/migrate-agents-md-handler.js';
40
+ import { RefreshAgentContextHandler } from './presentation/handlers/refresh-agent-context-handler.js';
41
+ import { RefreshClaudeMdHandler } from './presentation/handlers/refresh-claude-md-handler.js';
42
+ import { CheckAgentContextHandler } from './presentation/handlers/check-agent-context-handler.js';
35
43
  import { CheckRepetitionHandler } from './presentation/handlers/check-repetition-handler.js';
36
44
  import { CreateBaselineHandler } from './presentation/handlers/create-baseline-handler.js';
37
45
 
@@ -48,6 +56,9 @@ import { ScaffoldDesignHandler } from './presentation/handlers/scaffold-design-h
48
56
  export interface CiGovernanceCompositionRoot {
49
57
  generateCiTemplateHandler: GenerateCiTemplateHandler;
50
58
  migrateAgentsMdHandler: MigrateAgentsMdHandler;
59
+ refreshAgentContextHandler: RefreshAgentContextHandler;
60
+ refreshClaudeMdHandler: RefreshClaudeMdHandler;
61
+ checkAgentContextHandler: CheckAgentContextHandler;
51
62
  checkRepetitionHandler: CheckRepetitionHandler;
52
63
  createBaselineHandler: CreateBaselineHandler;
53
64
  scaffoldDesignHandler: ScaffoldDesignHandler;
@@ -75,6 +86,7 @@ export function buildCiGovernance(
75
86
  const commandExistencePort = new HarnessApiCommandExistenceAdapter();
76
87
  const adrExistencePort = new AdrFoundationExistenceAdapter();
77
88
  const agentsMdPort = new AgentsMdFileAdapter(baseDir);
89
+ const agentContextDocumentPort = new AgentContextFileAdapter(baseDir, harnessRoot);
78
90
  const lessonArtifactReaderPort = new LessonArtifactFileReaderAdapter(baseDir);
79
91
 
80
92
  // Domain services
@@ -82,6 +94,7 @@ export function buildCiGovernance(
82
94
  const repetitionDetector = new RepetitionDetector(errorRepetitionRepository);
83
95
  const pointerValidator = new PointerValidator(commandExistencePort, fileExistencePort, adrExistencePort);
84
96
  const lessonAggregator = new LessonAggregator();
97
+ const claudeMdComposer = new ClaudeMdComposer();
85
98
 
86
99
  // Use cases
87
100
  const generateCiTemplateUseCase = new GenerateCiTemplateUseCase(templateGenerator);
@@ -95,6 +108,9 @@ export function buildCiGovernance(
95
108
  lessonAggregator,
96
109
  pointerValidator,
97
110
  );
111
+ const refreshClaudeMdUseCase = new RefreshClaudeMdUseCase(agentContextDocumentPort, claudeMdComposer);
112
+ const refreshAgentContextUseCase = new RefreshAgentContextUseCase(migrateAgentsMdUseCase, refreshClaudeMdUseCase);
113
+ const checkAgentContextUseCase = new CheckAgentContextUseCase(agentContextDocumentPort);
98
114
  const aggregateLessonsUseCase = new AggregateLessonsUseCase(lessonArtifactReaderPort, lessonAggregator);
99
115
  const validatePointersUseCase = new ValidatePointersUseCase(agentsMdPort, pointerValidator);
100
116
 
@@ -122,6 +138,9 @@ export function buildCiGovernance(
122
138
  renderCiTemplateUseCase,
123
139
  );
124
140
  const migrateAgentsMdHandler = new MigrateAgentsMdHandler(migrateAgentsMdUseCase);
141
+ const refreshAgentContextHandler = new RefreshAgentContextHandler(refreshAgentContextUseCase);
142
+ const refreshClaudeMdHandler = new RefreshClaudeMdHandler(refreshClaudeMdUseCase);
143
+ const checkAgentContextHandler = new CheckAgentContextHandler(checkAgentContextUseCase);
125
144
  const checkRepetitionHandler = new CheckRepetitionHandler(checkEscalationUseCase);
126
145
  const createBaselineHandler = new CreateBaselineHandler(createBaselineUseCase);
127
146
  const scaffoldDesignHandler = new ScaffoldDesignHandler(scaffoldDesignUseCase);
@@ -129,6 +148,9 @@ export function buildCiGovernance(
129
148
  return {
130
149
  generateCiTemplateHandler,
131
150
  migrateAgentsMdHandler,
151
+ refreshAgentContextHandler,
152
+ refreshClaudeMdHandler,
153
+ checkAgentContextHandler,
132
154
  checkRepetitionHandler,
133
155
  createBaselineHandler,
134
156
  scaffoldDesignHandler,
@@ -29,7 +29,7 @@ export class CiTemplate {
29
29
  if (!isTemplateType(templateType)) {
30
30
  throw new CiGovernanceDomainError(
31
31
  'CI_TEMPLATE_INVALID_TYPE',
32
- `INV-1: templateType must be one of 'aidlc-gate', 'consistency-check', 'pre-commit'. Got: ${templateType}`,
32
+ `INV-1: templateType must be one of 'aidlc-gate', 'consistency-check', 'pre-commit', 'agent-context-refresh'. Got: ${templateType}`,
33
33
  );
34
34
  }
35
35
  if (!presetRef || presetRef.trim() === '') {
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit ci-governance
4
+ */
5
+
6
+ export interface AgentContextDocumentStat {
7
+ readonly exists: boolean;
8
+ readonly ageInDays: number | null;
9
+ }
10
+
11
+ export interface AgentContextDocumentPort {
12
+ readProjectFile(relativePath: string): Promise<string | null>;
13
+ writeProjectFile(relativePath: string, content: string): Promise<void>;
14
+ readHarnessTemplate(relativePath: string): Promise<string>;
15
+ statProjectFile(relativePath: string): Promise<AgentContextDocumentStat>;
16
+ listHarnessSkillNames(): Promise<string[]>;
17
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit ci-governance
4
+ */
5
+
6
+ export interface ClaudeMdTemplateValues {
7
+ readonly commands: readonly string[];
8
+ readonly skills: readonly string[];
9
+ readonly presets: readonly string[];
10
+ }
11
+
12
+ const USER_SECTION_START = '<!-- phasegate:user-section:start -->';
13
+ const USER_SECTION_END = '<!-- phasegate:user-section:end -->';
14
+ const DEFAULT_USER_SECTION = 'プロジェクト固有の指示をここに記載してください。';
15
+
16
+ export class ClaudeMdComposer {
17
+ compose(template: string, existing: string | null, values: ClaudeMdTemplateValues): string {
18
+ const userSection = this.extractUserSection(existing) ?? DEFAULT_USER_SECTION;
19
+ return template
20
+ .replace('{{PHASEGATE_COMMANDS}}', this.toList(values.commands))
21
+ .replace('{{PHASEGATE_SKILLS}}', this.toList(values.skills))
22
+ .replace('{{PHASEGATE_PRESETS}}', this.toList(values.presets))
23
+ .replace('{{PHASEGATE_USER_SECTION}}', userSection)
24
+ .replace(/\n{3,}/g, '\n\n')
25
+ .trimEnd() + '\n';
26
+ }
27
+
28
+ private extractUserSection(existing: string | null): string | null {
29
+ if (existing === null) return null;
30
+ const start = existing.indexOf(USER_SECTION_START);
31
+ const end = existing.indexOf(USER_SECTION_END);
32
+ if (start === -1 || end === -1 || end < start) return existing.trim();
33
+ return existing.slice(start + USER_SECTION_START.length, end).trim();
34
+ }
35
+
36
+ private toList(values: readonly string[]): string {
37
+ return values.map((value) => `- \`${value}\``).join('\n');
38
+ }
39
+ }
@@ -17,6 +17,7 @@ const TRIGGER_CONDITION_MAP: Record<TemplateType, TriggerCondition> = {
17
17
  'aidlc-gate': 'pull_request',
18
18
  'consistency-check': 'schedule',
19
19
  'pre-commit': 'pre-commit',
20
+ 'agent-context-refresh': 'schedule',
20
21
  };
21
22
 
22
23
  export class TemplateGenerator {
@@ -1,12 +1,13 @@
1
1
  // @unit ci-governance
2
2
  // @layer domain
3
3
 
4
- export type TemplateType = 'aidlc-gate' | 'consistency-check' | 'pre-commit';
4
+ export type TemplateType = 'aidlc-gate' | 'consistency-check' | 'pre-commit' | 'agent-context-refresh';
5
5
 
6
6
  export const TEMPLATE_TYPES: readonly TemplateType[] = [
7
7
  'aidlc-gate',
8
8
  'consistency-check',
9
9
  'pre-commit',
10
+ 'agent-context-refresh',
10
11
  ] as const;
11
12
 
12
13
  export function isTemplateType(value: unknown): value is TemplateType {
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @layer infrastructure
3
+ * @unit ci-governance
4
+ */
5
+
6
+ import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
7
+ import { dirname, join } from 'node:path';
8
+ import type { AgentContextDocumentPort, AgentContextDocumentStat } from '../../domain/ports/agent-context-document-port.js';
9
+
10
+ export class AgentContextFileAdapter implements AgentContextDocumentPort {
11
+ constructor(
12
+ private readonly projectRoot: string,
13
+ private readonly harnessRoot: string,
14
+ ) {}
15
+
16
+ async readProjectFile(relativePath: string): Promise<string | null> {
17
+ try {
18
+ return await readFile(join(this.projectRoot, relativePath), 'utf-8');
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ async writeProjectFile(relativePath: string, content: string): Promise<void> {
25
+ const targetPath = join(this.projectRoot, relativePath);
26
+ await mkdir(dirname(targetPath), { recursive: true });
27
+ await writeFile(targetPath, content, 'utf-8');
28
+ }
29
+
30
+ async readHarnessTemplate(relativePath: string): Promise<string> {
31
+ return await readFile(join(this.harnessRoot, relativePath), 'utf-8');
32
+ }
33
+
34
+ async statProjectFile(relativePath: string): Promise<AgentContextDocumentStat> {
35
+ try {
36
+ const fileStat = await stat(join(this.projectRoot, relativePath));
37
+ const ageInDays = Math.floor((Date.now() - fileStat.mtime.getTime()) / 86_400_000);
38
+ return { exists: true, ageInDays };
39
+ } catch {
40
+ return { exists: false, ageInDays: null };
41
+ }
42
+ }
43
+
44
+ async listHarnessSkillNames(): Promise<string[]> {
45
+ try {
46
+ const entries = await readdir(join(this.harnessRoot, 'skills'), { withFileTypes: true });
47
+ return entries
48
+ .filter((entry) => entry.isDirectory())
49
+ .map((entry) => entry.name)
50
+ .sort();
51
+ } catch {
52
+ return [];
53
+ }
54
+ }
55
+ }
@@ -14,12 +14,14 @@ const OUTPUT_PATH_MAP: Record<string, string> = {
14
14
  'aidlc-gate': '.github/workflows/aidlc-gate.yml',
15
15
  'consistency-check': '.github/workflows/consistency-check.yml',
16
16
  'pre-commit': '.husky/pre-commit',
17
+ 'agent-context-refresh': '.github/workflows/agent-context-refresh.yml',
17
18
  };
18
19
 
19
20
  const TEMPLATE_PATH_MAP: Record<string, string> = {
20
21
  'aidlc-gate': 'docs/templates/ci/aidlc-gate.yml',
21
22
  'consistency-check': 'docs/templates/ci/consistency-check.yml',
22
23
  'pre-commit': 'docs/templates/hooks/pre-commit',
24
+ 'agent-context-refresh': 'docs/templates/ci/agent-context-refresh.yml',
23
25
  };
24
26
 
25
27
  export class YamlTemplateRendererAdapter implements TemplateRendererPort {
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @layer presentation
3
+ * @unit ci-governance
4
+ */
5
+
6
+ import type { CheckAgentContextUseCase } from '../../application/usecases/check-agent-context-usecase.js';
7
+
8
+ export interface CheckAgentContextHandlerArgs {
9
+ thresholdDays?: number;
10
+ format?: 'human' | 'json';
11
+ }
12
+
13
+ export class CheckAgentContextHandler {
14
+ constructor(private readonly useCase: CheckAgentContextUseCase) {}
15
+
16
+ async handle(args: CheckAgentContextHandlerArgs): Promise<{ exitCode: number; output: string }> {
17
+ const result = await this.useCase.execute({ thresholdDays: args.thresholdDays });
18
+
19
+ if (args.format === 'json') {
20
+ return { exitCode: result.passed ? 0 : 1, output: JSON.stringify(result, null, 2) };
21
+ }
22
+
23
+ const lines = [
24
+ `Agent context freshness: ${result.passed ? 'PASS' : 'FAIL'}`,
25
+ `Threshold: ${result.thresholdDays} days`,
26
+ ...result.findings.map((finding) => {
27
+ const age = finding.ageInDays === null ? 'missing' : `${finding.ageInDays} days`;
28
+ return `- ${finding.path}: ${finding.status} (${age}) ${finding.message}`;
29
+ }),
30
+ ];
31
+ return { exitCode: result.passed ? 0 : 1, output: lines.join('\n') };
32
+ }
33
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @layer presentation
3
+ * @unit ci-governance
4
+ */
5
+
6
+ import type { RefreshAgentContextUseCase } from '../../application/usecases/refresh-agent-context-usecase.js';
7
+
8
+ export interface RefreshAgentContextHandlerArgs {
9
+ dryRun?: boolean;
10
+ apply?: boolean;
11
+ format?: 'human' | 'json';
12
+ }
13
+
14
+ export class RefreshAgentContextHandler {
15
+ constructor(private readonly useCase: RefreshAgentContextUseCase) {}
16
+
17
+ async handle(args: RefreshAgentContextHandlerArgs): Promise<{ exitCode: number; output: string }> {
18
+ const dryRun = args.apply === true ? false : (args.dryRun ?? true);
19
+ const result = await this.useCase.execute({ dryRun });
20
+
21
+ if (args.format === 'json') {
22
+ return { exitCode: result.success ? 0 : 1, output: JSON.stringify(result, null, 2) };
23
+ }
24
+
25
+ if (!result.success) {
26
+ return {
27
+ exitCode: 1,
28
+ output: ['Agent context refresh failed', ...result.errors.map((error) => `[${error.code}] ${error.message}`)].join('\n'),
29
+ };
30
+ }
31
+
32
+ const mode = dryRun ? 'dry-run' : 'apply';
33
+ const preview = dryRun ? `\n\nCLAUDE.md preview:\n${result.claudeMd.preview}` : '';
34
+ return {
35
+ exitCode: 0,
36
+ output: [
37
+ `Agent context refresh ${mode} complete`,
38
+ `AGENTS.md added pointers: ${result.agentsMd.addedPointers}`,
39
+ `CLAUDE.md changed: ${result.claudeMd.changed}`,
40
+ `Applied: ${result.applied}`,
41
+ ].join('\n') + preview,
42
+ };
43
+ }
44
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @layer presentation
3
+ * @unit ci-governance
4
+ */
5
+
6
+ import type { RefreshClaudeMdUseCase } from '../../application/usecases/refresh-claude-md-usecase.js';
7
+
8
+ export interface RefreshClaudeMdHandlerArgs {
9
+ dryRun?: boolean;
10
+ apply?: boolean;
11
+ format?: 'human' | 'json';
12
+ }
13
+
14
+ export class RefreshClaudeMdHandler {
15
+ constructor(private readonly useCase: RefreshClaudeMdUseCase) {}
16
+
17
+ async handle(args: RefreshClaudeMdHandlerArgs): Promise<{ exitCode: number; output: string }> {
18
+ const dryRun = args.apply === true ? false : (args.dryRun ?? true);
19
+ const result = await this.useCase.execute({ dryRun });
20
+
21
+ if (args.format === 'json') {
22
+ return { exitCode: result.success ? 0 : 1, output: JSON.stringify(result, null, 2) };
23
+ }
24
+
25
+ if (!result.success) {
26
+ return {
27
+ exitCode: 1,
28
+ output: ['CLAUDE.md refresh failed', ...result.errors.map((error) => `[${error.code}] ${error.message}`)].join('\n'),
29
+ };
30
+ }
31
+
32
+ const mode = dryRun ? 'dry-run' : 'apply';
33
+ return {
34
+ exitCode: 0,
35
+ output: `CLAUDE.md refresh ${mode}: changed=${result.changed}, applied=${result.applied}`,
36
+ };
37
+ }
38
+ }
@@ -123,8 +123,11 @@ Commands:
123
123
  phasegate:complete-check Complete L2-L4 check (--json)
124
124
  phasegate:impact-analysis Impact analysis for story (<storyId>, --json)
125
125
 
126
- 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)
127
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)
128
131
  ci:check-repetition Check error repetition (--code <errorCode>, --reset, --json)
129
132
  baseline Create retrofit baseline snapshot (--dry-run, --force, --paths <glob,glob,...>, --json)
130
133
  scaffold-design Scaffold a design doc (--unit <id>, --phase <logical|domain|uiux|unit-test|it-test>, --force, --json)
@@ -346,6 +349,7 @@ Options:
346
349
  aidlc-gate — AIDLC phase gate checks
347
350
  consistency-check — Doc/code consistency checks
348
351
  pre-commit — Pre-commit hook template
352
+ agent-context-refresh — AGENTS.md / CLAUDE.md refresh workflow
349
353
  --render Render the template to stdout
350
354
  --json Output in JSON format
351
355
 
@@ -1233,6 +1237,39 @@ Examples:
1233
1237
  break;
1234
1238
  }
1235
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
+
1236
1273
  case "ci:check-repetition": {
1237
1274
  const mod = buildCiGovernance(rootDir, harnessRoot);
1238
1275
  const errorCode = parseFlag(args, "--code") ?? "";
@@ -555,6 +555,10 @@ export async function deployCiWorkflows(harnessRoot: string, projectRoot: string
555
555
  relativeSource: join("docs", "templates", "ci", "consistency-check.yml"),
556
556
  relativeTarget: join(".github", "workflows", "consistency-check.yml"),
557
557
  },
558
+ {
559
+ relativeSource: join("docs", "templates", "ci", "agent-context-refresh.yml"),
560
+ relativeTarget: join(".github", "workflows", "agent-context-refresh.yml"),
561
+ },
558
562
  ];
559
563
 
560
564
  await fs.mkdir(join(projectRoot, ".github", "workflows"), { recursive: true });