phasegate 0.136.0 → 0.138.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.
- package/CHANGELOG.md +18 -0
- package/README.ja.md +5 -3
- package/README.md +5 -3
- package/docs/guide/cli-reference.md +3 -0
- package/docs/templates/agent-context/CLAUDE.md.template.md +29 -0
- package/docs/templates/ci/agent-context-refresh.yml +59 -0
- package/package.json +1 -1
- package/scripts/harness/ci-governance/application/dto/check-agent-context-input.ts +8 -0
- package/scripts/harness/ci-governance/application/dto/check-agent-context-output.ts +17 -0
- package/scripts/harness/ci-governance/application/dto/refresh-agent-context-input.ts +8 -0
- package/scripts/harness/ci-governance/application/dto/refresh-agent-context-output.ts +15 -0
- package/scripts/harness/ci-governance/application/dto/refresh-claude-md-input.ts +8 -0
- package/scripts/harness/ci-governance/application/dto/refresh-claude-md-output.ts +13 -0
- package/scripts/harness/ci-governance/application/usecases/check-agent-context-usecase.ts +48 -0
- package/scripts/harness/ci-governance/application/usecases/refresh-agent-context-usecase.ts +31 -0
- package/scripts/harness/ci-governance/application/usecases/refresh-claude-md-usecase.ts +73 -0
- package/scripts/harness/ci-governance/composition-root.ts +22 -0
- package/scripts/harness/ci-governance/domain/aggregates/ci-template.ts +1 -1
- package/scripts/harness/ci-governance/domain/ports/agent-context-document-port.ts +17 -0
- package/scripts/harness/ci-governance/domain/services/claude-md-composer.ts +39 -0
- package/scripts/harness/ci-governance/domain/services/template-generator.ts +1 -0
- package/scripts/harness/ci-governance/domain/types/template-type.ts +2 -1
- package/scripts/harness/ci-governance/infrastructure/adapters/agent-context-file-adapter.ts +55 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/yaml-template-renderer-adapter.ts +2 -0
- package/scripts/harness/ci-governance/presentation/handlers/check-agent-context-handler.ts +33 -0
- package/scripts/harness/ci-governance/presentation/handlers/refresh-agent-context-handler.ts +44 -0
- package/scripts/harness/ci-governance/presentation/handlers/refresh-claude-md-handler.ts +38 -0
- package/scripts/harness/config-foundation/domain/harness-config.ts +4 -0
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +9 -0
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +9 -0
- package/scripts/harness/main.ts +38 -1
- package/scripts/harness/setup/skill-deployer.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.138.0] - 2026-05-09
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **WI-032 post-publish dogfood** — `phasegate init --with-ci` が生成する `phasegate.config.json` の `ci.enabled` を config schema v2/v3 で許可した。
|
|
15
|
+
- v0.137.0 published package の dogfood で、init 後の `ci:generate-template` / `ci:auto-refresh-agent-context` が `additionalProperties: /ci is not allowed` で失敗することを確認。
|
|
16
|
+
- `ci.enabled` は WI-031 以降の公開設定として生成済みのため、schema 側を追従させる。
|
|
17
|
+
|
|
18
|
+
## [0.137.0] - 2026-05-09
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **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 コマンドで実行できるようにした。
|
|
23
|
+
- `refresh-claude-md --dry-run|--apply|--json` を追加し、CLAUDE.md の user-owned section を保持しながら bundled template から標準セクションを再生成。
|
|
24
|
+
- `p2:check-agent-context --threshold-days <n> --json` を追加し、AGENTS.md / CLAUDE.md の鮮度を検査可能にした。
|
|
25
|
+
- `docs/templates/ci/agent-context-refresh.yml` を追加し、`ci:generate-template --type agent-context-refresh --render` と `phasegate init --with-ci` で配布可能にした。
|
|
26
|
+
- PhaseGate 自身にも `.github/workflows/agent-context-refresh.yml` を追加し、週次 refresh PR を作成する構成にした。
|
|
27
|
+
|
|
10
28
|
## [0.136.0] - 2026-05-09
|
|
11
29
|
|
|
12
30
|
### 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
|
|
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
|
@@ -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,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,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
|
+
}
|
|
@@ -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
|
+
}
|
package/scripts/harness/ci-governance/infrastructure/adapters/yaml-template-renderer-adapter.ts
CHANGED
|
@@ -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
|
+
}
|
|
@@ -50,6 +50,7 @@ export interface HarnessConfigSourceDocument {
|
|
|
50
50
|
harnesses: Partial<HarnessConfigResolvedDocument['harnesses']>;
|
|
51
51
|
paths: HarnessConfigResolvedDocument['paths'];
|
|
52
52
|
reporting: HarnessConfigResolvedDocument['reporting'];
|
|
53
|
+
ci?: HarnessConfigResolvedDocument['ci'];
|
|
53
54
|
validate?: HarnessConfigResolvedDocument['validate'];
|
|
54
55
|
architecture?: ArchitectureConfigSource;
|
|
55
56
|
}
|
|
@@ -110,6 +111,9 @@ export interface HarnessConfigResolvedDocument {
|
|
|
110
111
|
format: string;
|
|
111
112
|
outputDir: string;
|
|
112
113
|
};
|
|
114
|
+
ci?: {
|
|
115
|
+
enabled: boolean;
|
|
116
|
+
};
|
|
113
117
|
validate: {
|
|
114
118
|
failOnWarning: boolean;
|
|
115
119
|
};
|
package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json
CHANGED
|
@@ -439,6 +439,15 @@
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
441
|
},
|
|
442
|
+
"ci": {
|
|
443
|
+
"type": "object",
|
|
444
|
+
"additionalProperties": false,
|
|
445
|
+
"properties": {
|
|
446
|
+
"enabled": {
|
|
447
|
+
"type": "boolean"
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
},
|
|
442
451
|
"validate": {
|
|
443
452
|
"type": "object",
|
|
444
453
|
"additionalProperties": false,
|
package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json
CHANGED
|
@@ -439,6 +439,15 @@
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
441
|
},
|
|
442
|
+
"ci": {
|
|
443
|
+
"type": "object",
|
|
444
|
+
"additionalProperties": false,
|
|
445
|
+
"properties": {
|
|
446
|
+
"enabled": {
|
|
447
|
+
"type": "boolean"
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
},
|
|
442
451
|
"validate": {
|
|
443
452
|
"type": "object",
|
|
444
453
|
"additionalProperties": false,
|
package/scripts/harness/main.ts
CHANGED
|
@@ -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 });
|