phasegate 0.160.3 → 0.160.4

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 CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.160.4] - 2026-05-14
11
+
12
+ ### Fixed
13
+
14
+ - **WI-184 — skill catalog CLI** — fixes `phasegate skills list` so guidance-category skills no longer crash an undefined accumulator, shares the `SKILL.md` catalog path with `skills info`, and covers empty skill catalogs.
15
+
10
16
  ## [0.160.3] - 2026-05-14
11
17
 
12
18
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.160.3",
3
+ "version": "0.160.4",
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",
@@ -6,6 +6,7 @@
6
6
  * @work-item-id WI-171 / WI-172 / WI-173
7
7
  * @work-item-id WI-175
8
8
  * @work-item-id WI-176
9
+ * @work-item-id WI-184
9
10
  *
10
11
  * Phasegate CLI エントリポイント。
11
12
  * 各Unitの Composition Root からハンドラーを取得し、コマンドに応じてディスパッチする。
@@ -64,7 +65,9 @@ import {
64
65
  deploySkills,
65
66
  getCategoryForSkill,
66
67
  getHarnessVersion,
68
+ getSkillMarkdownPath,
67
69
  initHarnessConfig,
70
+ listAvailableSkillNames,
68
71
  } from "./setup/skill-deployer.js";
69
72
  import { createSkillQualityHandlers } from "./skill-quality/composition-root.js";
70
73
  import { createTraceabilityModelModule } from "./traceability-model/composition-root.js";
@@ -2753,25 +2756,16 @@ Examples:
2753
2756
  // ── skills ──
2754
2757
  case "skills": {
2755
2758
  const subCommand = args[1];
2756
- const skillsRoot = join(harnessRoot, "skills");
2757
2759
 
2758
2760
  if (subCommand === "list") {
2759
- const { promises: fs } = await import("node:fs");
2760
- const entries = await fs.readdir(skillsRoot, { withFileTypes: true });
2761
- const skills: string[] = [];
2762
- for (const entry of entries) {
2763
- if (entry.isDirectory()) {
2764
- try {
2765
- await fs.access(join(skillsRoot, entry.name, "SKILL.md"));
2766
- skills.push(entry.name);
2767
- } catch {
2768
- // skip directories without SKILL.md
2769
- }
2770
- }
2771
- }
2772
- skills.sort();
2773
-
2774
- const grouped: Record<string, string[]> = { core: [], aidlc: [], utility: [], unknown: [] };
2761
+ const skills = await listAvailableSkillNames(harnessRoot);
2762
+ const grouped: Record<"core" | "aidlc" | "utility" | "guidance" | "unknown", string[]> = {
2763
+ core: [],
2764
+ aidlc: [],
2765
+ utility: [],
2766
+ guidance: [],
2767
+ unknown: [],
2768
+ };
2775
2769
  for (const name of skills) {
2776
2770
  const cat = getCategoryForSkill(name) ?? "unknown";
2777
2771
  grouped[cat].push(name);
@@ -2783,8 +2777,9 @@ Examples:
2783
2777
  core: "Core — Quality Defense",
2784
2778
  aidlc: "AIDLC — Development Workflow",
2785
2779
  utility: "Utility",
2780
+ guidance: "Guidance",
2786
2781
  };
2787
- for (const cat of ["core", "aidlc", "utility", "unknown"] as const) {
2782
+ for (const cat of ["core", "aidlc", "utility", "guidance", "unknown"] as const) {
2788
2783
  if (grouped[cat].length === 0) continue;
2789
2784
  const label = labels[cat] ?? "Other";
2790
2785
  console.log(` [${label}] (${grouped[cat].length})`);
@@ -2803,7 +2798,7 @@ Examples:
2803
2798
  process.exit(2);
2804
2799
  }
2805
2800
  const { promises: fs } = await import("node:fs");
2806
- const skillMdPath = join(skillsRoot, skillName, "SKILL.md");
2801
+ const skillMdPath = getSkillMarkdownPath(harnessRoot, skillName);
2807
2802
  try {
2808
2803
  const content = await fs.readFile(skillMdPath, "utf-8");
2809
2804
  console.log(content);
@@ -2,6 +2,7 @@
2
2
  // @layer infrastructure
3
3
  // @work-item-id WI-086 / WI-087
4
4
  // @work-item-id WI-127
5
+ // @work-item-id WI-184
5
6
  // Note: import.meta.url を使わず、呼び出し元 (main.ts) がパスを解決して渡す設計。
6
7
 
7
8
  import { promises as fs } from "node:fs";
@@ -78,6 +79,33 @@ export function getCategoryForSkill(skillName: string): SkillCategory | null {
78
79
  return null;
79
80
  }
80
81
 
82
+ export function getSkillMarkdownPath(harnessRoot: string, skillName: string): string {
83
+ return join(harnessRoot, SKILLS_SOURCE_DIR, skillName, "SKILL.md");
84
+ }
85
+
86
+ export async function listAvailableSkillNames(harnessRoot: string): Promise<string[]> {
87
+ const skillsRoot = join(harnessRoot, SKILLS_SOURCE_DIR);
88
+ let entries;
89
+ try {
90
+ entries = await fs.readdir(skillsRoot, { withFileTypes: true });
91
+ } catch {
92
+ return [];
93
+ }
94
+
95
+ const skills: string[] = [];
96
+ for (const entry of entries) {
97
+ if (!entry.isDirectory()) continue;
98
+ try {
99
+ await fs.access(getSkillMarkdownPath(harnessRoot, entry.name));
100
+ skills.push(entry.name);
101
+ } catch {
102
+ // Directories without SKILL.md are not catalog entries.
103
+ }
104
+ }
105
+
106
+ return skills.sort();
107
+ }
108
+
81
109
  export interface DeployResult {
82
110
  deployedSkills: string[];
83
111
  targetDir: string;