product-spec 0.2.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 +49 -0
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/assets/claude/commands/product-spec-align.md +37 -0
- package/assets/claude/commands/product-spec-domain.md +32 -0
- package/assets/claude/commands/product-spec-faq.md +34 -0
- package/assets/claude/commands/product-spec-press.md +31 -0
- package/assets/codex/commands/product-spec-align.md +25 -0
- package/assets/codex/commands/product-spec-domain.md +19 -0
- package/assets/codex/commands/product-spec-faq.md +22 -0
- package/assets/codex/commands/product-spec-press.md +19 -0
- package/assets/product/templates/domain-template.md +52 -0
- package/assets/product/templates/faq-template.md +38 -0
- package/assets/product/templates/press-template.md +26 -0
- package/assets/product/templates/requirements-template.md +34 -0
- package/dist/adapters/claude.d.ts +2 -0
- package/dist/adapters/claude.js +26 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex.d.ts +2 -0
- package/dist/adapters/codex.js +26 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/types.d.ts +12 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli/commands/add.d.ts +6 -0
- package/dist/cli/commands/add.js +19 -0
- package/dist/cli/commands/add.js.map +1 -0
- package/dist/cli/commands/check.d.ts +4 -0
- package/dist/cli/commands/check.js +17 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +4 -0
- package/dist/cli/commands/doctor.js +17 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/help.d.ts +2 -0
- package/dist/cli/commands/help.js +17 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/remove.d.ts +4 -0
- package/dist/cli/commands/remove.js +17 -0
- package/dist/cli/commands/remove.js.map +1 -0
- package/dist/cli/commands/targets.d.ts +2 -0
- package/dist/cli/commands/targets.js +9 -0
- package/dist/cli/commands/targets.js.map +1 -0
- package/dist/cli/commands/version.d.ts +2 -0
- package/dist/cli/commands/version.js +9 -0
- package/dist/cli/commands/version.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +33 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output/reporter.d.ts +6 -0
- package/dist/cli/output/reporter.js +38 -0
- package/dist/cli/output/reporter.js.map +1 -0
- package/dist/core/assets/registry.d.ts +11 -0
- package/dist/core/assets/registry.js +34 -0
- package/dist/core/assets/registry.js.map +1 -0
- package/dist/core/fs/project.d.ts +13 -0
- package/dist/core/fs/project.js +67 -0
- package/dist/core/fs/project.js.map +1 -0
- package/dist/core/orchestration/add.d.ts +8 -0
- package/dist/core/orchestration/add.js +63 -0
- package/dist/core/orchestration/add.js.map +1 -0
- package/dist/core/orchestration/check.d.ts +9 -0
- package/dist/core/orchestration/check.js +80 -0
- package/dist/core/orchestration/check.js.map +1 -0
- package/dist/core/orchestration/doctor.d.ts +9 -0
- package/dist/core/orchestration/doctor.js +18 -0
- package/dist/core/orchestration/doctor.js.map +1 -0
- package/dist/core/orchestration/remove.d.ts +6 -0
- package/dist/core/orchestration/remove.js +60 -0
- package/dist/core/orchestration/remove.js.map +1 -0
- package/dist/core/orchestration/shared-assets.d.ts +8 -0
- package/dist/core/orchestration/shared-assets.js +38 -0
- package/dist/core/orchestration/shared-assets.js.map +1 -0
- package/dist/core/orchestration/targets.d.ts +5 -0
- package/dist/core/orchestration/targets.js +19 -0
- package/dist/core/orchestration/targets.js.map +1 -0
- package/dist/core/state/manifest.d.ts +13 -0
- package/dist/core/state/manifest.js +91 -0
- package/dist/core/state/manifest.js.map +1 -0
- package/dist/types/index.d.ts +53 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RequestedTarget } from "../../types/index.js";
|
|
2
|
+
export interface DoctorOptions {
|
|
3
|
+
rootDir: string;
|
|
4
|
+
requestedTarget: RequestedTarget;
|
|
5
|
+
}
|
|
6
|
+
export interface DoctorResult {
|
|
7
|
+
details: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function runDoctor(options: DoctorOptions): Promise<DoctorResult>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { runCheck } from "./check.js";
|
|
2
|
+
export async function runDoctor(options) {
|
|
3
|
+
const checkResult = await runCheck(options);
|
|
4
|
+
const details = checkResult.reports.map((report) => {
|
|
5
|
+
const issueList = report.issues.length === 0
|
|
6
|
+
? "No issues detected."
|
|
7
|
+
: report.issues.map((issue) => `- [${issue.severity}] ${issue.message}`).join("\n");
|
|
8
|
+
return [
|
|
9
|
+
`## ${report.target}`,
|
|
10
|
+
`Status: ${report.status}`,
|
|
11
|
+
`Manifest aligned: ${report.manifestAligned ? "yes" : "no"}`,
|
|
12
|
+
issueList,
|
|
13
|
+
`Recommended action: ${report.recommendedAction}`
|
|
14
|
+
].join("\n");
|
|
15
|
+
});
|
|
16
|
+
return { details };
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/core/orchestration/doctor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAWtC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAsB;IACpD,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACjD,MAAM,SAAS,GACb,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YACxB,CAAC,CAAC,qBAAqB;YACvB,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAExF,OAAO;YACL,MAAM,MAAM,CAAC,MAAM,EAAE;YACrB,WAAW,MAAM,CAAC,MAAM,EAAE;YAC1B,qBAAqB,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;YAC5D,SAAS;YACT,uBAAuB,MAAM,CAAC,iBAAiB,EAAE;SAClD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { joinProjectPath, removeEmptyDirectoryIfExists, removeIfExists } from "../fs/project.js";
|
|
3
|
+
import { loadManifest, manifestHasInstalledTargets, manifestPath, removeManifest, saveManifest } from "../state/manifest.js";
|
|
4
|
+
import { resolveTargets } from "./targets.js";
|
|
5
|
+
import { removeSharedAssets } from "./shared-assets.js";
|
|
6
|
+
export async function runRemove(options) {
|
|
7
|
+
const selectedTargets = resolveTargets(options.requestedTarget);
|
|
8
|
+
const manifest = await loadManifest(options.rootDir);
|
|
9
|
+
const files = [];
|
|
10
|
+
const changedTargets = [];
|
|
11
|
+
const skippedTargets = [];
|
|
12
|
+
if (!manifest) {
|
|
13
|
+
return {
|
|
14
|
+
requestedTarget: options.requestedTarget,
|
|
15
|
+
changedTargets,
|
|
16
|
+
skippedTargets: selectedTargets,
|
|
17
|
+
files,
|
|
18
|
+
notes: ["No manifest found. Nothing to remove."]
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
for (const target of selectedTargets) {
|
|
22
|
+
const existing = manifest.targets.find((entry) => entry.target === target);
|
|
23
|
+
if (!existing) {
|
|
24
|
+
skippedTargets.push(target);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
changedTargets.push(target);
|
|
28
|
+
for (const asset of existing.assets) {
|
|
29
|
+
const targetPath = joinProjectPath(options.rootDir, asset.targetPath);
|
|
30
|
+
const removed = await removeIfExists(targetPath);
|
|
31
|
+
files.push({
|
|
32
|
+
path: asset.targetPath,
|
|
33
|
+
action: removed ? "removed" : "skipped"
|
|
34
|
+
});
|
|
35
|
+
await removeEmptyDirectoryIfExists(path.dirname(targetPath));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
manifest.targets = manifest.targets.filter((entry) => !selectedTargets.includes(entry.target));
|
|
39
|
+
const remainingTargets = manifest.targets.filter((entry) => entry.installed);
|
|
40
|
+
if (remainingTargets.length === 0) {
|
|
41
|
+
files.push(...(await removeSharedAssets(options.rootDir, manifest)));
|
|
42
|
+
manifest.sharedAssets = [];
|
|
43
|
+
}
|
|
44
|
+
if (manifestHasInstalledTargets(manifest) || manifest.sharedAssets.length > 0) {
|
|
45
|
+
manifest.updatedAt = new Date().toISOString();
|
|
46
|
+
await saveManifest(options.rootDir, manifest);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
await removeManifest(options.rootDir);
|
|
50
|
+
await removeEmptyDirectoryIfExists(path.dirname(manifestPath(options.rootDir)));
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
requestedTarget: options.requestedTarget,
|
|
54
|
+
changedTargets,
|
|
55
|
+
skippedTargets: Array.from(new Set(skippedTargets.filter((target) => !changedTargets.includes(target)))),
|
|
56
|
+
files,
|
|
57
|
+
notes: []
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=remove.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove.js","sourceRoot":"","sources":["../../../src/core/orchestration/remove.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AssetRecord, ManagedManifest, OperationFileResult } from "../../types/index.js";
|
|
2
|
+
interface SharedAssetInstallResult {
|
|
3
|
+
sharedAssets: AssetRecord[];
|
|
4
|
+
files: OperationFileResult[];
|
|
5
|
+
}
|
|
6
|
+
export declare function installSharedAssets(rootDir: string, packageRoot: string): Promise<SharedAssetInstallResult>;
|
|
7
|
+
export declare function removeSharedAssets(rootDir: string, manifest: ManagedManifest): Promise<OperationFileResult[]>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { sharedAssetRegistry } from "../assets/registry.js";
|
|
2
|
+
import { assertInsideRoot, ensureDirectory, hashFile, joinProjectPath, pathExists, readText, removeIfExists, writeText } from "../fs/project.js";
|
|
3
|
+
export async function installSharedAssets(rootDir, packageRoot) {
|
|
4
|
+
const sharedAssets = [];
|
|
5
|
+
const files = [];
|
|
6
|
+
for (const asset of sharedAssetRegistry) {
|
|
7
|
+
const sourcePath = joinProjectPath(packageRoot, asset.sourcePath);
|
|
8
|
+
const targetPath = joinProjectPath(rootDir, asset.targetPath);
|
|
9
|
+
assertInsideRoot(rootDir, targetPath);
|
|
10
|
+
const existed = await pathExists(targetPath);
|
|
11
|
+
await ensureDirectory(joinProjectPath(rootDir, ".product/templates"));
|
|
12
|
+
await writeText(targetPath, await readText(sourcePath));
|
|
13
|
+
sharedAssets.push({
|
|
14
|
+
...asset,
|
|
15
|
+
checksum: await hashFile(targetPath),
|
|
16
|
+
managed: true
|
|
17
|
+
});
|
|
18
|
+
files.push({
|
|
19
|
+
path: asset.targetPath,
|
|
20
|
+
action: existed ? "updated" : "created"
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return { sharedAssets, files };
|
|
24
|
+
}
|
|
25
|
+
export async function removeSharedAssets(rootDir, manifest) {
|
|
26
|
+
const files = [];
|
|
27
|
+
for (const asset of manifest.sharedAssets) {
|
|
28
|
+
const targetPath = joinProjectPath(rootDir, asset.targetPath);
|
|
29
|
+
assertInsideRoot(rootDir, targetPath);
|
|
30
|
+
const removed = await removeIfExists(targetPath);
|
|
31
|
+
files.push({
|
|
32
|
+
path: asset.targetPath,
|
|
33
|
+
action: removed ? "removed" : "skipped"
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return files;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=shared-assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-assets.js","sourceRoot":"","sources":["../../../src/core/orchestration/shared-assets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,QAAQ,EACR,eAAe,EACf,UAAU,EACV,QAAQ,EACR,cAAc,EACd,SAAS,EACV,MAAM,kBAAkB,CAAC;AAO1B,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAe,EACf,WAAmB;IAEnB,MAAM,YAAY,GAAkB,EAAE,CAAC;IACvC,MAAM,KAAK,GAA0B,EAAE,CAAC;IAExC,KAAK,MAAM,KAAK,IAAI,mBAAmB,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,eAAe,CAAC,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAC9D,gBAAgB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAEtC,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,eAAe,CAAC,eAAe,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC;QACtE,MAAM,SAAS,CAAC,UAAU,EAAE,MAAM,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QAExD,YAAY,CAAC,IAAI,CAAC;YAChB,GAAG,KAAK;YACR,QAAQ,EAAE,MAAM,QAAQ,CAAC,UAAU,CAAC;YACpC,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,KAAK,CAAC,UAAU;YACtB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;SACxC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAe,EACf,QAAyB;IAEzB,MAAM,KAAK,GAA0B,EAAE,CAAC;IAExC,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QAC1C,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAC9D,gBAAgB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,KAAK,CAAC,UAAU;YACtB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;SACxC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AssistantAdapter } from "../../adapters/types.js";
|
|
2
|
+
import type { AssistantTarget, RequestedTarget } from "../../types/index.js";
|
|
3
|
+
export declare function resolveTargets(requestedTarget: RequestedTarget): AssistantTarget[];
|
|
4
|
+
export declare function getAdapter(target: AssistantTarget): AssistantAdapter;
|
|
5
|
+
export declare function getAllAdapters(): AssistantAdapter[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { claudeAdapter } from "../../adapters/claude.js";
|
|
2
|
+
import { codexAdapter } from "../../adapters/codex.js";
|
|
3
|
+
const adapters = {
|
|
4
|
+
claude: claudeAdapter,
|
|
5
|
+
codex: codexAdapter
|
|
6
|
+
};
|
|
7
|
+
export function resolveTargets(requestedTarget) {
|
|
8
|
+
if (requestedTarget === "both") {
|
|
9
|
+
return ["claude", "codex"];
|
|
10
|
+
}
|
|
11
|
+
return [requestedTarget];
|
|
12
|
+
}
|
|
13
|
+
export function getAdapter(target) {
|
|
14
|
+
return adapters[target];
|
|
15
|
+
}
|
|
16
|
+
export function getAllAdapters() {
|
|
17
|
+
return [claudeAdapter, codexAdapter];
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=targets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.js","sourceRoot":"","sources":["../../../src/core/orchestration/targets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAIvD,MAAM,QAAQ,GAA8C;IAC1D,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE,YAAY;CACpB,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,eAAgC;IAC7D,IAAI,eAAe,KAAK,MAAM,EAAE,CAAC;QAC/B,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,eAAe,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAuB;IAChD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AssetRecord, AssistantTarget, ManagedManifest, ManagedTargetState } from "../../types/index.js";
|
|
2
|
+
export declare const MANIFEST_VERSION = "1";
|
|
3
|
+
export declare const MANIFEST_RELATIVE_PATH = ".product-spec/manifest.json";
|
|
4
|
+
export declare function manifestPath(rootDir: string): string;
|
|
5
|
+
export declare function loadManifest(rootDir: string): Promise<ManagedManifest | null>;
|
|
6
|
+
export declare function saveManifest(rootDir: string, manifest: ManagedManifest): Promise<void>;
|
|
7
|
+
export declare function removeManifest(rootDir: string): Promise<void>;
|
|
8
|
+
export declare function createEmptyManifest(rootDir: string, productSpecVersion: string): ManagedManifest;
|
|
9
|
+
export declare function getTargetState(manifest: ManagedManifest, target: AssistantTarget): ManagedTargetState | undefined;
|
|
10
|
+
export declare function upsertTargetState(manifest: ManagedManifest, state: ManagedTargetState): ManagedManifest;
|
|
11
|
+
export declare function removeTargetState(manifest: ManagedManifest, target: AssistantTarget): ManagedManifest;
|
|
12
|
+
export declare function setSharedAssets(manifest: ManagedManifest, assets: AssetRecord[]): ManagedManifest;
|
|
13
|
+
export declare function manifestHasInstalledTargets(manifest: ManagedManifest): boolean;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { pathExists, readText, writeJsonAtomic } from "../fs/project.js";
|
|
4
|
+
export const MANIFEST_VERSION = "1";
|
|
5
|
+
export const MANIFEST_RELATIVE_PATH = ".product-spec/manifest.json";
|
|
6
|
+
const assetRecordSchema = z.object({
|
|
7
|
+
id: z.string(),
|
|
8
|
+
category: z.enum(["assistant-command", "product-template"]),
|
|
9
|
+
sourcePath: z.string(),
|
|
10
|
+
targetPath: z.string(),
|
|
11
|
+
target: z.enum(["claude", "codex", "shared"]),
|
|
12
|
+
checksum: z.string(),
|
|
13
|
+
managed: z.boolean()
|
|
14
|
+
});
|
|
15
|
+
const managedTargetStateSchema = z.object({
|
|
16
|
+
target: z.enum(["claude", "codex"]),
|
|
17
|
+
installed: z.boolean(),
|
|
18
|
+
assets: z.array(assetRecordSchema),
|
|
19
|
+
lastOperation: z.enum(["add", "remove", "check", "doctor"]),
|
|
20
|
+
lastHealthyAt: z.string().optional()
|
|
21
|
+
});
|
|
22
|
+
const managedManifestSchema = z.object({
|
|
23
|
+
manifestVersion: z.string(),
|
|
24
|
+
productSpecVersion: z.string(),
|
|
25
|
+
projectRoot: z.string(),
|
|
26
|
+
targets: z.array(managedTargetStateSchema),
|
|
27
|
+
sharedAssets: z.array(assetRecordSchema),
|
|
28
|
+
updatedAt: z.string()
|
|
29
|
+
});
|
|
30
|
+
export function manifestPath(rootDir) {
|
|
31
|
+
return path.join(rootDir, MANIFEST_RELATIVE_PATH);
|
|
32
|
+
}
|
|
33
|
+
export async function loadManifest(rootDir) {
|
|
34
|
+
const targetPath = manifestPath(rootDir);
|
|
35
|
+
if (!(await pathExists(targetPath))) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const parsed = JSON.parse(await readText(targetPath));
|
|
39
|
+
return managedManifestSchema.parse(parsed);
|
|
40
|
+
}
|
|
41
|
+
export async function saveManifest(rootDir, manifest) {
|
|
42
|
+
await writeJsonAtomic(manifestPath(rootDir), manifest);
|
|
43
|
+
}
|
|
44
|
+
export async function removeManifest(rootDir) {
|
|
45
|
+
const targetPath = manifestPath(rootDir);
|
|
46
|
+
if (await pathExists(targetPath)) {
|
|
47
|
+
const { removeIfExists } = await import("../fs/project.js");
|
|
48
|
+
await removeIfExists(targetPath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function createEmptyManifest(rootDir, productSpecVersion) {
|
|
52
|
+
return {
|
|
53
|
+
manifestVersion: MANIFEST_VERSION,
|
|
54
|
+
productSpecVersion,
|
|
55
|
+
projectRoot: ".",
|
|
56
|
+
targets: [],
|
|
57
|
+
sharedAssets: [],
|
|
58
|
+
updatedAt: new Date().toISOString()
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function getTargetState(manifest, target) {
|
|
62
|
+
return manifest.targets.find((entry) => entry.target === target);
|
|
63
|
+
}
|
|
64
|
+
export function upsertTargetState(manifest, state) {
|
|
65
|
+
const nextTargets = manifest.targets.filter((entry) => entry.target !== state.target);
|
|
66
|
+
nextTargets.push(state);
|
|
67
|
+
return {
|
|
68
|
+
...manifest,
|
|
69
|
+
targets: nextTargets.sort((left, right) => left.target.localeCompare(right.target)),
|
|
70
|
+
updatedAt: new Date().toISOString()
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function removeTargetState(manifest, target) {
|
|
74
|
+
return {
|
|
75
|
+
...manifest,
|
|
76
|
+
targets: manifest.targets.filter((entry) => entry.target !== target),
|
|
77
|
+
updatedAt: new Date().toISOString(),
|
|
78
|
+
sharedAssets: manifest.sharedAssets
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function setSharedAssets(manifest, assets) {
|
|
82
|
+
return {
|
|
83
|
+
...manifest,
|
|
84
|
+
sharedAssets: assets,
|
|
85
|
+
updatedAt: new Date().toISOString()
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export function manifestHasInstalledTargets(manifest) {
|
|
89
|
+
return manifest.targets.some((target) => target.installed);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/core/state/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type AssistantTarget = "claude" | "codex";
|
|
2
|
+
export type RequestedTarget = AssistantTarget | "both";
|
|
3
|
+
export type AssetCategory = "assistant-command" | "product-template";
|
|
4
|
+
export type HealthStatus = "healthy" | "missing" | "partial" | "unhealthy";
|
|
5
|
+
export type LastOperation = "add" | "remove" | "check" | "doctor";
|
|
6
|
+
export interface AssetRecord {
|
|
7
|
+
id: string;
|
|
8
|
+
category: AssetCategory;
|
|
9
|
+
sourcePath: string;
|
|
10
|
+
targetPath: string;
|
|
11
|
+
target: AssistantTarget | "shared";
|
|
12
|
+
checksum: string;
|
|
13
|
+
managed: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface ManagedTargetState {
|
|
16
|
+
target: AssistantTarget;
|
|
17
|
+
installed: boolean;
|
|
18
|
+
assets: AssetRecord[];
|
|
19
|
+
lastOperation: LastOperation;
|
|
20
|
+
lastHealthyAt?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ManagedManifest {
|
|
23
|
+
manifestVersion: string;
|
|
24
|
+
productSpecVersion: string;
|
|
25
|
+
projectRoot: string;
|
|
26
|
+
targets: ManagedTargetState[];
|
|
27
|
+
sharedAssets: AssetRecord[];
|
|
28
|
+
updatedAt: string;
|
|
29
|
+
}
|
|
30
|
+
export interface HealthIssue {
|
|
31
|
+
code: string;
|
|
32
|
+
severity: "info" | "warning" | "error";
|
|
33
|
+
message: string;
|
|
34
|
+
path?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface HealthReport {
|
|
37
|
+
target: AssistantTarget | "all";
|
|
38
|
+
status: HealthStatus;
|
|
39
|
+
issues: HealthIssue[];
|
|
40
|
+
recommendedAction: string;
|
|
41
|
+
manifestAligned: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface OperationFileResult {
|
|
44
|
+
path: string;
|
|
45
|
+
action: "created" | "updated" | "removed" | "skipped";
|
|
46
|
+
}
|
|
47
|
+
export interface OperationSummary {
|
|
48
|
+
requestedTarget: RequestedTarget;
|
|
49
|
+
changedTargets: AssistantTarget[];
|
|
50
|
+
skippedTargets: AssistantTarget[];
|
|
51
|
+
files: OperationFileResult[];
|
|
52
|
+
notes: string[];
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "product-spec",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "product-spec CLI for installing product-management command assets into Claude Code and Codex projects",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"product-spec": "dist/cli/main.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"assets",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"CHANGELOG.md"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22.0.0"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json",
|
|
22
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
23
|
+
"audit:names": "node scripts/audit-current-names.mjs",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"prepare": "npm run build",
|
|
26
|
+
"cli": "tsx src/cli/main.ts"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"cli",
|
|
30
|
+
"codex",
|
|
31
|
+
"claude-code",
|
|
32
|
+
"product-management",
|
|
33
|
+
"spec-kit"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"commander": "^14.0.0",
|
|
37
|
+
"zod": "^4.1.5"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^24.6.0",
|
|
41
|
+
"tsx": "^4.20.5",
|
|
42
|
+
"typescript": "^5.9.2",
|
|
43
|
+
"vitest": "^3.2.4"
|
|
44
|
+
}
|
|
45
|
+
}
|