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.
Files changed (82) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +152 -0
  4. package/assets/claude/commands/product-spec-align.md +37 -0
  5. package/assets/claude/commands/product-spec-domain.md +32 -0
  6. package/assets/claude/commands/product-spec-faq.md +34 -0
  7. package/assets/claude/commands/product-spec-press.md +31 -0
  8. package/assets/codex/commands/product-spec-align.md +25 -0
  9. package/assets/codex/commands/product-spec-domain.md +19 -0
  10. package/assets/codex/commands/product-spec-faq.md +22 -0
  11. package/assets/codex/commands/product-spec-press.md +19 -0
  12. package/assets/product/templates/domain-template.md +52 -0
  13. package/assets/product/templates/faq-template.md +38 -0
  14. package/assets/product/templates/press-template.md +26 -0
  15. package/assets/product/templates/requirements-template.md +34 -0
  16. package/dist/adapters/claude.d.ts +2 -0
  17. package/dist/adapters/claude.js +26 -0
  18. package/dist/adapters/claude.js.map +1 -0
  19. package/dist/adapters/codex.d.ts +2 -0
  20. package/dist/adapters/codex.js +26 -0
  21. package/dist/adapters/codex.js.map +1 -0
  22. package/dist/adapters/types.d.ts +12 -0
  23. package/dist/adapters/types.js +2 -0
  24. package/dist/adapters/types.js.map +1 -0
  25. package/dist/cli/commands/add.d.ts +6 -0
  26. package/dist/cli/commands/add.js +19 -0
  27. package/dist/cli/commands/add.js.map +1 -0
  28. package/dist/cli/commands/check.d.ts +4 -0
  29. package/dist/cli/commands/check.js +17 -0
  30. package/dist/cli/commands/check.js.map +1 -0
  31. package/dist/cli/commands/doctor.d.ts +4 -0
  32. package/dist/cli/commands/doctor.js +17 -0
  33. package/dist/cli/commands/doctor.js.map +1 -0
  34. package/dist/cli/commands/help.d.ts +2 -0
  35. package/dist/cli/commands/help.js +17 -0
  36. package/dist/cli/commands/help.js.map +1 -0
  37. package/dist/cli/commands/remove.d.ts +4 -0
  38. package/dist/cli/commands/remove.js +17 -0
  39. package/dist/cli/commands/remove.js.map +1 -0
  40. package/dist/cli/commands/targets.d.ts +2 -0
  41. package/dist/cli/commands/targets.js +9 -0
  42. package/dist/cli/commands/targets.js.map +1 -0
  43. package/dist/cli/commands/version.d.ts +2 -0
  44. package/dist/cli/commands/version.js +9 -0
  45. package/dist/cli/commands/version.js.map +1 -0
  46. package/dist/cli/main.d.ts +2 -0
  47. package/dist/cli/main.js +33 -0
  48. package/dist/cli/main.js.map +1 -0
  49. package/dist/cli/output/reporter.d.ts +6 -0
  50. package/dist/cli/output/reporter.js +38 -0
  51. package/dist/cli/output/reporter.js.map +1 -0
  52. package/dist/core/assets/registry.d.ts +11 -0
  53. package/dist/core/assets/registry.js +34 -0
  54. package/dist/core/assets/registry.js.map +1 -0
  55. package/dist/core/fs/project.d.ts +13 -0
  56. package/dist/core/fs/project.js +67 -0
  57. package/dist/core/fs/project.js.map +1 -0
  58. package/dist/core/orchestration/add.d.ts +8 -0
  59. package/dist/core/orchestration/add.js +63 -0
  60. package/dist/core/orchestration/add.js.map +1 -0
  61. package/dist/core/orchestration/check.d.ts +9 -0
  62. package/dist/core/orchestration/check.js +80 -0
  63. package/dist/core/orchestration/check.js.map +1 -0
  64. package/dist/core/orchestration/doctor.d.ts +9 -0
  65. package/dist/core/orchestration/doctor.js +18 -0
  66. package/dist/core/orchestration/doctor.js.map +1 -0
  67. package/dist/core/orchestration/remove.d.ts +6 -0
  68. package/dist/core/orchestration/remove.js +60 -0
  69. package/dist/core/orchestration/remove.js.map +1 -0
  70. package/dist/core/orchestration/shared-assets.d.ts +8 -0
  71. package/dist/core/orchestration/shared-assets.js +38 -0
  72. package/dist/core/orchestration/shared-assets.js.map +1 -0
  73. package/dist/core/orchestration/targets.d.ts +5 -0
  74. package/dist/core/orchestration/targets.js +19 -0
  75. package/dist/core/orchestration/targets.js.map +1 -0
  76. package/dist/core/state/manifest.d.ts +13 -0
  77. package/dist/core/state/manifest.js +91 -0
  78. package/dist/core/state/manifest.js.map +1 -0
  79. package/dist/types/index.d.ts +53 -0
  80. package/dist/types/index.js +2 -0
  81. package/dist/types/index.js.map +1 -0
  82. package/package.json +45 -0
@@ -0,0 +1,12 @@
1
+ import type { AssistantTarget, AssetRecord, HealthIssue } from "../types/index.js";
2
+ export interface AdapterHealthContext {
3
+ rootDir: string;
4
+ targetAssets: AssetRecord[];
5
+ }
6
+ export interface AssistantAdapter {
7
+ key: AssistantTarget;
8
+ displayName: string;
9
+ commandDir: string;
10
+ resolveCommandTarget(fileName: string): string;
11
+ describeHealthIssues(context: AdapterHealthContext): Promise<HealthIssue[]>;
12
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerAddCommand(program: Command, context: {
3
+ rootDir: string;
4
+ packageRoot: string;
5
+ version: string;
6
+ }): void;
@@ -0,0 +1,19 @@
1
+ import { runAdd } from "../../core/orchestration/add.js";
2
+ import { formatOperationSummary } from "../output/reporter.js";
3
+ import { parseRequestedTarget } from "./targets.js";
4
+ export function registerAddCommand(program, context) {
5
+ program
6
+ .command("add")
7
+ .argument("<target>", "claude, codex, or both")
8
+ .description("Add product-spec-managed integration files to the current project")
9
+ .action(async (target) => {
10
+ const summary = await runAdd({
11
+ rootDir: context.rootDir,
12
+ packageRoot: context.packageRoot,
13
+ requestedTarget: parseRequestedTarget(target),
14
+ productSpecVersion: context.version
15
+ });
16
+ process.stdout.write(formatOperationSummary("add", summary));
17
+ });
18
+ }
19
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,UAAU,kBAAkB,CAAC,OAAgB,EAAE,OAAkE;IACrH,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAC;SAC9C,WAAW,CAAC,mEAAmE,CAAC;SAChF,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC;YAC3B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,eAAe,EAAE,oBAAoB,CAAC,MAAM,CAAC;YAC7C,kBAAkB,EAAE,OAAO,CAAC,OAAO;SACpC,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerCheckCommand(program: Command, context: {
3
+ rootDir: string;
4
+ }): void;
@@ -0,0 +1,17 @@
1
+ import { runCheck } from "../../core/orchestration/check.js";
2
+ import { formatCheckResult } from "../output/reporter.js";
3
+ import { parseRequestedTarget } from "./targets.js";
4
+ export function registerCheckCommand(program, context) {
5
+ program
6
+ .command("check")
7
+ .argument("[target]", "claude, codex, or both", "both")
8
+ .description("Validate integration health for one or all supported assistants")
9
+ .action(async (target) => {
10
+ const result = await runCheck({
11
+ rootDir: context.rootDir,
12
+ requestedTarget: parseRequestedTarget(target, "both")
13
+ });
14
+ process.stdout.write(formatCheckResult(result));
15
+ });
16
+ }
17
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,UAAU,oBAAoB,CAAC,OAAgB,EAAE,OAA4B;IACjF,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,QAAQ,CAAC,UAAU,EAAE,wBAAwB,EAAE,MAAM,CAAC;SACtD,WAAW,CAAC,iEAAiE,CAAC;SAC9E,MAAM,CAAC,KAAK,EAAE,MAA0B,EAAE,EAAE;QAC3C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;YAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,eAAe,EAAE,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC;SACtD,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerDoctorCommand(program: Command, context: {
3
+ rootDir: string;
4
+ }): void;
@@ -0,0 +1,17 @@
1
+ import { runDoctor } from "../../core/orchestration/doctor.js";
2
+ import { formatDoctorResult } from "../output/reporter.js";
3
+ import { parseRequestedTarget } from "./targets.js";
4
+ export function registerDoctorCommand(program, context) {
5
+ program
6
+ .command("doctor")
7
+ .argument("[target]", "claude, codex, or both", "both")
8
+ .description("Show detailed diagnostics and repair guidance")
9
+ .action(async (target) => {
10
+ const result = await runDoctor({
11
+ rootDir: context.rootDir,
12
+ requestedTarget: parseRequestedTarget(target, "both")
13
+ });
14
+ process.stdout.write(formatDoctorResult(result));
15
+ });
16
+ }
17
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,UAAU,qBAAqB,CAAC,OAAgB,EAAE,OAA4B;IAClF,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,QAAQ,CAAC,UAAU,EAAE,wBAAwB,EAAE,MAAM,CAAC;SACtD,WAAW,CAAC,+CAA+C,CAAC;SAC5D,MAAM,CAAC,KAAK,EAAE,MAA0B,EAAE,EAAE;QAC3C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC;YAC7B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,eAAe,EAAE,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC;SACtD,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function configureHelp(program: Command): void;
@@ -0,0 +1,17 @@
1
+ export function configureHelp(program) {
2
+ program.addHelpText("after", `
3
+ Examples:
4
+ product-spec add claude
5
+ product-spec add both
6
+ product-spec remove codex
7
+ product-spec check both
8
+ product-spec doctor claude
9
+ `);
10
+ program
11
+ .command("help")
12
+ .description("Show help for product-spec")
13
+ .action(() => {
14
+ program.outputHelp();
15
+ });
16
+ }
17
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/cli/commands/help.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,OAAO,CAAC,WAAW,CACjB,OAAO,EACP;;;;;;;CAOH,CACE,CAAC;IAEF,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,4BAA4B,CAAC;SACzC,MAAM,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,UAAU,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerRemoveCommand(program: Command, context: {
3
+ rootDir: string;
4
+ }): void;
@@ -0,0 +1,17 @@
1
+ import { runRemove } from "../../core/orchestration/remove.js";
2
+ import { formatOperationSummary } from "../output/reporter.js";
3
+ import { parseRequestedTarget } from "./targets.js";
4
+ export function registerRemoveCommand(program, context) {
5
+ program
6
+ .command("remove")
7
+ .argument("<target>", "claude, codex, or both")
8
+ .description("Remove product-spec-managed integration files from the current project")
9
+ .action(async (target) => {
10
+ const summary = await runRemove({
11
+ rootDir: context.rootDir,
12
+ requestedTarget: parseRequestedTarget(target)
13
+ });
14
+ process.stdout.write(formatOperationSummary("remove", summary));
15
+ });
16
+ }
17
+ //# sourceMappingURL=remove.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove.js","sourceRoot":"","sources":["../../../src/cli/commands/remove.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,UAAU,qBAAqB,CAAC,OAAgB,EAAE,OAA4B;IAClF,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAC;SAC9C,WAAW,CAAC,wEAAwE,CAAC;SACrF,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC;YAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,eAAe,EAAE,oBAAoB,CAAC,MAAM,CAAC;SAC9C,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { RequestedTarget } from "../../types/index.js";
2
+ export declare function parseRequestedTarget(value: string | undefined, fallback?: RequestedTarget): RequestedTarget;
@@ -0,0 +1,9 @@
1
+ import { InvalidArgumentError } from "commander";
2
+ export function parseRequestedTarget(value, fallback = "both") {
3
+ const resolved = value ?? fallback;
4
+ if (resolved === "claude" || resolved === "codex" || resolved === "both") {
5
+ return resolved;
6
+ }
7
+ throw new InvalidArgumentError(`Unknown target "${resolved}". Use claude, codex, or both.`);
8
+ }
9
+ //# sourceMappingURL=targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.js","sourceRoot":"","sources":["../../../src/cli/commands/targets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGjD,MAAM,UAAU,oBAAoB,CAAC,KAAyB,EAAE,WAA4B,MAAM;IAChG,MAAM,QAAQ,GAAG,KAAK,IAAI,QAAQ,CAAC;IAEnC,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACzE,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,oBAAoB,CAAC,mBAAmB,QAAQ,gCAAgC,CAAC,CAAC;AAC9F,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerVersionCommand(program: Command, version: string): void;
@@ -0,0 +1,9 @@
1
+ export function registerVersionCommand(program, version) {
2
+ program
3
+ .command("version")
4
+ .description("Print the installed product-spec CLI version")
5
+ .action(() => {
6
+ process.stdout.write(`${version}\n`);
7
+ });
8
+ }
9
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/cli/commands/version.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,sBAAsB,CAAC,OAAgB,EAAE,OAAe;IACtE,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,8CAA8C,CAAC;SAC3D,MAAM,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import packageJson from "../../package.json" with { type: "json" };
4
+ import { resolveProjectRoot, getPackageRoot } from "../core/fs/project.js";
5
+ import { registerAddCommand } from "./commands/add.js";
6
+ import { registerRemoveCommand } from "./commands/remove.js";
7
+ import { registerCheckCommand } from "./commands/check.js";
8
+ import { registerDoctorCommand } from "./commands/doctor.js";
9
+ import { registerVersionCommand } from "./commands/version.js";
10
+ import { configureHelp } from "./commands/help.js";
11
+ async function main() {
12
+ const rootDir = resolveProjectRoot(process.cwd());
13
+ const packageRoot = getPackageRoot();
14
+ const program = new Command();
15
+ program
16
+ .name("product-spec")
17
+ .description("Manage product-spec project integrations for Claude Code and Codex")
18
+ .version(packageJson.version, "-V, --version", "Print the product-spec CLI version")
19
+ .showHelpAfterError();
20
+ registerAddCommand(program, { rootDir, packageRoot, version: packageJson.version });
21
+ registerRemoveCommand(program, { rootDir });
22
+ registerCheckCommand(program, { rootDir });
23
+ registerDoctorCommand(program, { rootDir });
24
+ registerVersionCommand(program, packageJson.version);
25
+ configureHelp(program);
26
+ await program.parseAsync(process.argv);
27
+ }
28
+ main().catch((error) => {
29
+ const message = error instanceof Error ? error.message : String(error);
30
+ process.stderr.write(`${message}\n`);
31
+ process.exitCode = 1;
32
+ });
33
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAC1D,OAAO,WAAW,MAAM,oBAAoB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEnE,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC9B,OAAO;SACJ,IAAI,CAAC,cAAc,CAAC;SACpB,WAAW,CAAC,oEAAoE,CAAC;SACjF,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,oCAAoC,CAAC;SACnF,kBAAkB,EAAE,CAAC;IAExB,kBAAkB,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACpF,qBAAqB,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5C,oBAAoB,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3C,qBAAqB,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5C,sBAAsB,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACrD,aAAa,CAAC,OAAO,CAAC,CAAC;IAEvB,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { CheckResult } from "../../core/orchestration/check.js";
2
+ import type { DoctorResult } from "../../core/orchestration/doctor.js";
3
+ import type { OperationSummary } from "../../types/index.js";
4
+ export declare function formatOperationSummary(action: "add" | "remove", summary: OperationSummary): string;
5
+ export declare function formatCheckResult(result: CheckResult): string;
6
+ export declare function formatDoctorResult(result: DoctorResult): string;
@@ -0,0 +1,38 @@
1
+ export function formatOperationSummary(action, summary) {
2
+ const lines = [
3
+ `product-spec ${action} ${summary.requestedTarget}`,
4
+ `Changed targets: ${summary.changedTargets.length > 0 ? summary.changedTargets.join(", ") : "none"}`,
5
+ `Skipped targets: ${summary.skippedTargets.length > 0 ? summary.skippedTargets.join(", ") : "none"}`
6
+ ];
7
+ if (summary.files.length > 0) {
8
+ lines.push("Files:");
9
+ for (const file of summary.files) {
10
+ lines.push(`- ${file.action}: ${file.path}`);
11
+ }
12
+ }
13
+ if (summary.notes.length > 0) {
14
+ lines.push("Notes:");
15
+ for (const note of summary.notes) {
16
+ lines.push(`- ${note}`);
17
+ }
18
+ }
19
+ return `${lines.join("\n")}\n`;
20
+ }
21
+ export function formatCheckResult(result) {
22
+ const lines = [];
23
+ for (const report of result.reports) {
24
+ lines.push(`${report.target}: ${report.status}`);
25
+ lines.push(`- manifest aligned: ${report.manifestAligned ? "yes" : "no"}`);
26
+ if (report.issues.length > 0) {
27
+ for (const issue of report.issues) {
28
+ lines.push(`- ${issue.severity}: ${issue.message}`);
29
+ }
30
+ }
31
+ lines.push(`- next step: ${report.recommendedAction}`);
32
+ }
33
+ return `${lines.join("\n")}\n`;
34
+ }
35
+ export function formatDoctorResult(result) {
36
+ return `${result.details.join("\n\n")}\n`;
37
+ }
38
+ //# sourceMappingURL=reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../../src/cli/output/reporter.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,sBAAsB,CAAC,MAAwB,EAAE,OAAyB;IACxF,MAAM,KAAK,GAAG;QACZ,gBAAgB,MAAM,IAAI,OAAO,CAAC,eAAe,EAAE;QACnD,oBAAoB,OAAO,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;QACpG,oBAAoB,OAAO,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;KACrG,CAAC;IAEF,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC,uBAAuB,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3E,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAoB;IACrD,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { AssetCategory, AssistantTarget } from "../../types/index.js";
2
+ export interface AssetDefinition {
3
+ id: string;
4
+ category: AssetCategory;
5
+ target: AssistantTarget | "shared";
6
+ sourcePath: string;
7
+ targetPath: string;
8
+ }
9
+ export declare const assistantAssetRegistry: Record<AssistantTarget, AssetDefinition[]>;
10
+ export declare const sharedAssetRegistry: AssetDefinition[];
11
+ export declare function getTargetAssetDefinitions(target: AssistantTarget): AssetDefinition[];
@@ -0,0 +1,34 @@
1
+ const assistantCommands = ["product-spec-domain.md", "product-spec-press.md", "product-spec-faq.md", "product-spec-align.md"];
2
+ const sharedTemplates = [
3
+ "domain-template.md",
4
+ "press-template.md",
5
+ "faq-template.md",
6
+ "requirements-template.md"
7
+ ];
8
+ function buildAssistantAssets(target, commandDir) {
9
+ return assistantCommands.map((fileName) => ({
10
+ id: `${target}/${fileName.replace(/\.md$/, "")}`,
11
+ category: "assistant-command",
12
+ target,
13
+ sourcePath: `assets/${target}/commands/${fileName}`,
14
+ targetPath: `${commandDir}/${fileName}`
15
+ }));
16
+ }
17
+ function buildSharedAssets() {
18
+ return sharedTemplates.map((fileName) => ({
19
+ id: `shared/${fileName.replace(/\.md$/, "")}`,
20
+ category: "product-template",
21
+ target: "shared",
22
+ sourcePath: `assets/product/templates/${fileName}`,
23
+ targetPath: `.product/templates/${fileName}`
24
+ }));
25
+ }
26
+ export const assistantAssetRegistry = {
27
+ claude: buildAssistantAssets("claude", ".claude/commands"),
28
+ codex: buildAssistantAssets("codex", ".Codex/commands")
29
+ };
30
+ export const sharedAssetRegistry = buildSharedAssets();
31
+ export function getTargetAssetDefinitions(target) {
32
+ return assistantAssetRegistry[target];
33
+ }
34
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/core/assets/registry.ts"],"names":[],"mappings":"AAUA,MAAM,iBAAiB,GAAG,CAAC,wBAAwB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,uBAAuB,CAAC,CAAC;AAC9H,MAAM,eAAe,GAAG;IACtB,oBAAoB;IACpB,mBAAmB;IACnB,iBAAiB;IACjB,0BAA0B;CAC3B,CAAC;AAEF,SAAS,oBAAoB,CAAC,MAAuB,EAAE,UAAkB;IACvE,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC1C,EAAE,EAAE,GAAG,MAAM,IAAI,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE;QAChD,QAAQ,EAAE,mBAAmB;QAC7B,MAAM;QACN,UAAU,EAAE,UAAU,MAAM,aAAa,QAAQ,EAAE;QACnD,UAAU,EAAE,GAAG,UAAU,IAAI,QAAQ,EAAE;KACxC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACxC,EAAE,EAAE,UAAU,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE;QAC7C,QAAQ,EAAE,kBAAkB;QAC5B,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE,4BAA4B,QAAQ,EAAE;QAClD,UAAU,EAAE,sBAAsB,QAAQ,EAAE;KAC7C,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAA+C;IAChF,MAAM,EAAE,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IAC1D,KAAK,EAAE,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,CAAC;CACxD,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,iBAAiB,EAAE,CAAC;AAEvD,MAAM,UAAU,yBAAyB,CAAC,MAAuB;IAC/D,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare function getPackageRoot(): string;
2
+ export declare function resolveProjectRoot(cwd: string): string;
3
+ export declare function pathExists(targetPath: string): Promise<boolean>;
4
+ export declare function ensureDirectory(targetPath: string): Promise<void>;
5
+ export declare function readText(targetPath: string): Promise<string>;
6
+ export declare function writeText(targetPath: string, contents: string): Promise<void>;
7
+ export declare function writeJsonAtomic(targetPath: string, value: unknown): Promise<void>;
8
+ export declare function removeIfExists(targetPath: string): Promise<boolean>;
9
+ export declare function hashFile(targetPath: string): Promise<string>;
10
+ export declare function relativeFromRoot(rootDir: string, targetPath: string): string;
11
+ export declare function joinProjectPath(rootDir: string, relativePath: string): string;
12
+ export declare function assertInsideRoot(rootDir: string, candidatePath: string): void;
13
+ export declare function removeEmptyDirectoryIfExists(targetPath: string): Promise<void>;
@@ -0,0 +1,67 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ export function getPackageRoot() {
6
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
7
+ }
8
+ export function resolveProjectRoot(cwd) {
9
+ return path.resolve(cwd);
10
+ }
11
+ export async function pathExists(targetPath) {
12
+ try {
13
+ await stat(targetPath);
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ export async function ensureDirectory(targetPath) {
21
+ await mkdir(targetPath, { recursive: true });
22
+ }
23
+ export async function readText(targetPath) {
24
+ return readFile(targetPath, "utf8");
25
+ }
26
+ export async function writeText(targetPath, contents) {
27
+ await ensureDirectory(path.dirname(targetPath));
28
+ await writeFile(targetPath, contents, "utf8");
29
+ }
30
+ export async function writeJsonAtomic(targetPath, value) {
31
+ const tempPath = `${targetPath}.tmp`;
32
+ await ensureDirectory(path.dirname(targetPath));
33
+ await writeFile(tempPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
34
+ await rename(tempPath, targetPath);
35
+ }
36
+ export async function removeIfExists(targetPath) {
37
+ if (!(await pathExists(targetPath))) {
38
+ return false;
39
+ }
40
+ await rm(targetPath, { force: true, recursive: false });
41
+ return true;
42
+ }
43
+ export async function hashFile(targetPath) {
44
+ const contents = await readFile(targetPath);
45
+ return createHash("sha256").update(contents).digest("hex");
46
+ }
47
+ export function relativeFromRoot(rootDir, targetPath) {
48
+ return path.relative(rootDir, targetPath).split(path.sep).join("/");
49
+ }
50
+ export function joinProjectPath(rootDir, relativePath) {
51
+ return path.join(rootDir, ...relativePath.split("/"));
52
+ }
53
+ export function assertInsideRoot(rootDir, candidatePath) {
54
+ const relative = path.relative(rootDir, candidatePath);
55
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
56
+ throw new Error(`Refusing to operate outside project root: ${candidatePath}`);
57
+ }
58
+ }
59
+ export async function removeEmptyDirectoryIfExists(targetPath) {
60
+ try {
61
+ await rm(targetPath, { recursive: false });
62
+ }
63
+ catch {
64
+ // Ignore non-empty or missing directories.
65
+ }
66
+ }
67
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../../src/core/fs/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { OperationSummary, RequestedTarget } from "../../types/index.js";
2
+ export interface AddOptions {
3
+ rootDir: string;
4
+ packageRoot: string;
5
+ requestedTarget: RequestedTarget;
6
+ productSpecVersion: string;
7
+ }
8
+ export declare function runAdd(options: AddOptions): Promise<OperationSummary>;
@@ -0,0 +1,63 @@
1
+ import path from "node:path";
2
+ import { getTargetAssetDefinitions } from "../assets/registry.js";
3
+ import { assertInsideRoot, ensureDirectory, hashFile, joinProjectPath, pathExists, readText, writeText } from "../fs/project.js";
4
+ import { createEmptyManifest, loadManifest, saveManifest, setSharedAssets, upsertTargetState } from "../state/manifest.js";
5
+ import { resolveTargets } from "./targets.js";
6
+ import { installSharedAssets } from "./shared-assets.js";
7
+ function buildTargetState(target, assets) {
8
+ return {
9
+ target,
10
+ installed: true,
11
+ assets,
12
+ lastOperation: "add"
13
+ };
14
+ }
15
+ export async function runAdd(options) {
16
+ const selectedTargets = resolveTargets(options.requestedTarget);
17
+ let manifest = (await loadManifest(options.rootDir)) ?? createEmptyManifest(options.rootDir, options.productSpecVersion);
18
+ manifest.productSpecVersion = options.productSpecVersion;
19
+ const files = [];
20
+ const changedTargets = [];
21
+ const skippedTargets = [];
22
+ for (const target of selectedTargets) {
23
+ const assetDefinitions = getTargetAssetDefinitions(target);
24
+ const managedAssets = [];
25
+ for (const asset of assetDefinitions) {
26
+ const sourcePath = joinProjectPath(options.packageRoot, asset.sourcePath);
27
+ const targetPath = joinProjectPath(options.rootDir, asset.targetPath);
28
+ assertInsideRoot(options.rootDir, targetPath);
29
+ await ensureDirectory(path.dirname(targetPath));
30
+ const existed = await pathExists(targetPath);
31
+ await writeText(targetPath, await readText(sourcePath));
32
+ managedAssets.push({
33
+ ...asset,
34
+ checksum: await hashFile(targetPath),
35
+ managed: true
36
+ });
37
+ files.push({
38
+ path: asset.targetPath,
39
+ action: existed ? "updated" : "created"
40
+ });
41
+ }
42
+ const alreadyManaged = manifest.targets.some((entry) => entry.target === target && entry.installed);
43
+ manifest = upsertTargetState(manifest, buildTargetState(target, managedAssets));
44
+ if (alreadyManaged) {
45
+ skippedTargets.push(target);
46
+ }
47
+ else {
48
+ changedTargets.push(target);
49
+ }
50
+ }
51
+ const sharedInstall = await installSharedAssets(options.rootDir, options.packageRoot);
52
+ manifest = setSharedAssets(manifest, sharedInstall.sharedAssets);
53
+ files.push(...sharedInstall.files);
54
+ await saveManifest(options.rootDir, manifest);
55
+ return {
56
+ requestedTarget: options.requestedTarget,
57
+ changedTargets,
58
+ skippedTargets,
59
+ files,
60
+ notes: ["Run `product-spec check` to validate the installed integrations."]
61
+ };
62
+ }
63
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../../src/core/orchestration/add.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { HealthReport, RequestedTarget } from "../../types/index.js";
2
+ export interface CheckOptions {
3
+ rootDir: string;
4
+ requestedTarget: RequestedTarget;
5
+ }
6
+ export interface CheckResult {
7
+ reports: HealthReport[];
8
+ }
9
+ export declare function runCheck(options: CheckOptions): Promise<CheckResult>;
@@ -0,0 +1,80 @@
1
+ import { getTargetAssetDefinitions, sharedAssetRegistry } from "../assets/registry.js";
2
+ import { joinProjectPath, pathExists } from "../fs/project.js";
3
+ import { loadManifest } from "../state/manifest.js";
4
+ import { getAdapter, resolveTargets } from "./targets.js";
5
+ function classifyStatus(commandFilesPresent, expectedCommandFiles, manifestInstalled, issues) {
6
+ if (commandFilesPresent === 0 && !manifestInstalled) {
7
+ return "missing";
8
+ }
9
+ if (issues.length > 0) {
10
+ if (commandFilesPresent > 0 && commandFilesPresent < expectedCommandFiles) {
11
+ return "partial";
12
+ }
13
+ return "unhealthy";
14
+ }
15
+ return "healthy";
16
+ }
17
+ export async function runCheck(options) {
18
+ const manifest = await loadManifest(options.rootDir);
19
+ const selectedTargets = resolveTargets(options.requestedTarget);
20
+ const reports = [];
21
+ for (const target of selectedTargets) {
22
+ const adapter = getAdapter(target);
23
+ const targetAssets = getTargetAssetDefinitions(target);
24
+ const targetState = manifest?.targets.find((entry) => entry.target === target);
25
+ const issues = [];
26
+ let presentCount = 0;
27
+ for (const asset of targetAssets) {
28
+ const absolutePath = joinProjectPath(options.rootDir, asset.targetPath);
29
+ if (await pathExists(absolutePath)) {
30
+ presentCount += 1;
31
+ }
32
+ else if (targetState?.installed) {
33
+ issues.push({
34
+ code: "TARGET_ASSET_MISSING",
35
+ severity: "error",
36
+ message: `Expected managed asset is missing: ${asset.targetPath}`,
37
+ path: asset.targetPath
38
+ });
39
+ }
40
+ }
41
+ for (const asset of sharedAssetRegistry) {
42
+ const absolutePath = joinProjectPath(options.rootDir, asset.targetPath);
43
+ if (!(await pathExists(absolutePath))) {
44
+ issues.push({
45
+ code: "SHARED_ASSET_MISSING",
46
+ severity: "warning",
47
+ message: `Shared template is missing: ${asset.targetPath}`,
48
+ path: asset.targetPath
49
+ });
50
+ }
51
+ }
52
+ if (!manifest && presentCount > 0) {
53
+ issues.push({
54
+ code: "MANIFEST_MISSING",
55
+ severity: "warning",
56
+ message: "Managed files exist but .product-spec/manifest.json is missing."
57
+ });
58
+ }
59
+ issues.push(...(await adapter.describeHealthIssues({
60
+ rootDir: options.rootDir,
61
+ targetAssets: (targetState?.assets ?? []).filter((asset) => asset.target === target)
62
+ })));
63
+ const manifestAligned = Boolean(targetState?.installed) || presentCount === 0;
64
+ const status = classifyStatus(presentCount, targetAssets.length, Boolean(targetState?.installed), issues);
65
+ const recommendedAction = status === "healthy"
66
+ ? "No action needed."
67
+ : status === "missing"
68
+ ? `Run \`product-spec add ${target}\` to install this integration.`
69
+ : `Run \`product-spec add ${target}\` to refresh managed files, then \`product-spec check ${target}\` again.`;
70
+ reports.push({
71
+ target,
72
+ status,
73
+ issues,
74
+ recommendedAction,
75
+ manifestAligned
76
+ });
77
+ }
78
+ return { reports };
79
+ }
80
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/core/orchestration/check.ts"],"names":[],"mappings":"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"}