pi-gsd 2.0.1 → 2.0.2

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 (65) hide show
  1. package/dist/pi-gsd-hooks.js +1532 -0
  2. package/package.json +3 -5
  3. package/.gsd/extensions/pi-gsd-hooks.ts +0 -973
  4. package/src/cli.ts +0 -644
  5. package/src/commands/base.ts +0 -67
  6. package/src/commands/commit.ts +0 -22
  7. package/src/commands/config.ts +0 -71
  8. package/src/commands/frontmatter.ts +0 -51
  9. package/src/commands/index.ts +0 -76
  10. package/src/commands/init.ts +0 -43
  11. package/src/commands/milestone.ts +0 -37
  12. package/src/commands/phase.ts +0 -92
  13. package/src/commands/progress.ts +0 -71
  14. package/src/commands/roadmap.ts +0 -40
  15. package/src/commands/scaffold.ts +0 -19
  16. package/src/commands/state.ts +0 -102
  17. package/src/commands/template.ts +0 -52
  18. package/src/commands/verify.ts +0 -70
  19. package/src/commands/workstream.ts +0 -98
  20. package/src/commands/wxp.ts +0 -65
  21. package/src/lib/commands.ts +0 -1040
  22. package/src/lib/config.ts +0 -385
  23. package/src/lib/core.ts +0 -1167
  24. package/src/lib/frontmatter.ts +0 -462
  25. package/src/lib/init.ts +0 -517
  26. package/src/lib/milestone.ts +0 -290
  27. package/src/lib/model-profiles.ts +0 -272
  28. package/src/lib/phase.ts +0 -1012
  29. package/src/lib/profile-output.ts +0 -237
  30. package/src/lib/profile-pipeline.ts +0 -556
  31. package/src/lib/roadmap.ts +0 -378
  32. package/src/lib/schemas.ts +0 -290
  33. package/src/lib/security.ts +0 -176
  34. package/src/lib/state.ts +0 -1175
  35. package/src/lib/template.ts +0 -246
  36. package/src/lib/uat.ts +0 -289
  37. package/src/lib/verify.ts +0 -879
  38. package/src/lib/workstream.ts +0 -524
  39. package/src/output.ts +0 -45
  40. package/src/schemas/pi-gsd-settings.schema.json +0 -80
  41. package/src/schemas/wxp.xsd +0 -619
  42. package/src/schemas/wxp.zod.ts +0 -318
  43. package/src/wxp/__tests__/arguments.test.ts +0 -86
  44. package/src/wxp/__tests__/conditions.test.ts +0 -106
  45. package/src/wxp/__tests__/executor.test.ts +0 -95
  46. package/src/wxp/__tests__/helpers.ts +0 -26
  47. package/src/wxp/__tests__/integration.test.ts +0 -166
  48. package/src/wxp/__tests__/new-features.test.ts +0 -222
  49. package/src/wxp/__tests__/parser.test.ts +0 -159
  50. package/src/wxp/__tests__/paste.test.ts +0 -66
  51. package/src/wxp/__tests__/schema.test.ts +0 -120
  52. package/src/wxp/__tests__/security.test.ts +0 -87
  53. package/src/wxp/__tests__/shell.test.ts +0 -85
  54. package/src/wxp/__tests__/string-ops.test.ts +0 -25
  55. package/src/wxp/__tests__/variables.test.ts +0 -65
  56. package/src/wxp/arguments.ts +0 -89
  57. package/src/wxp/conditions.ts +0 -78
  58. package/src/wxp/executor.ts +0 -191
  59. package/src/wxp/index.ts +0 -191
  60. package/src/wxp/parser.ts +0 -198
  61. package/src/wxp/paste.ts +0 -51
  62. package/src/wxp/security.ts +0 -102
  63. package/src/wxp/shell.ts +0 -81
  64. package/src/wxp/string-ops.ts +0 -44
  65. package/src/wxp/variables.ts +0 -109
@@ -1,176 +0,0 @@
1
- /**
2
- * security.ts - Prompt injection scanning and path/field validation.
3
- *
4
- * Ported from lib/security.cjs. Provides sanitization for agent-generated content
5
- * written to .planning/ files.
6
- */
7
-
8
- import path from "path";
9
-
10
- // ─── Types ────────────────────────────────────────────────────────────────────
11
-
12
- export interface SafeJsonResult<T> {
13
- ok: true;
14
- value: T;
15
- }
16
- export interface SafeJsonError {
17
- ok: false;
18
- error: string;
19
- }
20
- export type SafeJsonParseResult<T> = SafeJsonResult<T> | SafeJsonError;
21
-
22
- export interface PathValidationResult {
23
- safe: boolean;
24
- resolved: string;
25
- error?: string;
26
- }
27
-
28
- export interface FieldValidationResult {
29
- valid: boolean;
30
- error?: string;
31
- }
32
-
33
- // ─── Injection patterns ───────────────────────────────────────────────────────
34
-
35
- const INJECTION_PATTERNS: RegExp[] = [
36
- /ignore\s+(?:all\s+)?previous\s+instructions?/i,
37
- /forget\s+(?:all\s+)?previous/i,
38
- /\bsystem\s+prompt\b/i,
39
- /\bdisregard\s+(?:all\s+)?previous\b/i,
40
- /\byou\s+are\s+now\b/i,
41
- /\bact\s+as\s+(?:a\s+)?(?:new\s+)?(?:AI|assistant|gpt|claude|llm)\b/i,
42
- /\bpretend\s+(?:you\s+are|to\s+be)\b/i,
43
- /\boverride\s+(?:all\s+)?(?:previous\s+)?(?:instructions?|directives?|rules?|constraints?)\b/i,
44
- /\bdo\s+not\s+follow\s+(?:the\s+)?(?:previous\s+)?instructions?\b/i,
45
- /\bnew\s+instructions?:\s/i,
46
- /\bignore\s+(?:all\s+)?(?:previous\s+)?(?:context|history|instructions?)\b/i,
47
- /\bdeveloper\s+mode\b/i,
48
- /\bDAN\s+mode\b/i,
49
- ];
50
-
51
- const MAX_SAFE_LENGTH = 10000;
52
-
53
- /**
54
- * Scan content for prompt-injection patterns.
55
- * Returns an array of matched patterns (empty = safe).
56
- */
57
- export function scanForInjection(content: string): string[] {
58
- const matches: string[] = [];
59
- for (const pattern of INJECTION_PATTERNS) {
60
- if (pattern.test(content)) {
61
- matches.push(pattern.source);
62
- }
63
- }
64
- return matches;
65
- }
66
-
67
- /**
68
- * Sanitize a string for safe use in agent prompts.
69
- * Strips invisible characters and known injection markers.
70
- */
71
- export function sanitizeForPrompt(text: string): string {
72
- if (!text) return text;
73
- // Remove null bytes and other invisible control characters
74
- // eslint-disable-next-line no-control-regex
75
- let sanitized = text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
76
- // Truncate to safe length
77
- if (sanitized.length > MAX_SAFE_LENGTH) {
78
- sanitized = sanitized.slice(0, MAX_SAFE_LENGTH) + "... [truncated]";
79
- }
80
- return sanitized;
81
- }
82
-
83
- /**
84
- * Safely parse JSON with a human-readable error on failure.
85
- */
86
- export function safeJsonParse<T = unknown>(
87
- raw: string,
88
- opts: { label?: string } = {},
89
- ): SafeJsonParseResult<T> {
90
- try {
91
- return { ok: true, value: JSON.parse(raw) as T };
92
- } catch (err) {
93
- const label = opts.label ?? "JSON";
94
- return { ok: false, error: `Invalid ${label}: ${(err as Error).message}` };
95
- }
96
- }
97
-
98
- /**
99
- * Validate a file path is safe (no null bytes, no path traversal to sensitive dirs).
100
- */
101
- export function validatePath(
102
- filePath: string,
103
- cwd: string,
104
- opts: { allowAbsolute?: boolean } = {},
105
- ): PathValidationResult {
106
- if (filePath.includes("\0")) {
107
- return {
108
- safe: false,
109
- resolved: filePath,
110
- error: "Path contains null bytes",
111
- };
112
- }
113
-
114
- const resolved = path.isAbsolute(filePath)
115
- ? filePath
116
- : path.resolve(cwd, filePath);
117
-
118
- // Prevent traversal above home directory
119
- const home = process.env["HOME"] ?? "/";
120
- if (!resolved.startsWith(home) && !resolved.startsWith(cwd)) {
121
- return { safe: false, resolved, error: "Path traversal rejected" };
122
- }
123
-
124
- if (!opts.allowAbsolute && path.isAbsolute(filePath)) {
125
- return { safe: false, resolved, error: "Absolute paths not allowed" };
126
- }
127
-
128
- return { safe: true, resolved };
129
- }
130
-
131
- /**
132
- * Validate a file path and throw on traversal attempt.
133
- */
134
- export function requireSafePath(
135
- filePath: string,
136
- baseDir: string,
137
- label: string,
138
- opts: { allowAbsolute?: boolean } = {},
139
- ): string {
140
- const result = validatePath(filePath, baseDir, opts);
141
- if (!result.safe)
142
- throw new Error(`${label || "Path"} validation failed: ${result.error}`);
143
- return result.resolved;
144
- }
145
-
146
- /**
147
- * Sanitize text for display back to the user.
148
- */
149
- export function sanitizeForDisplay(text: string): string {
150
- if (!text || typeof text !== "string") return text;
151
- let s = text.replace(/[\u200B-\u200F\u2028-\u202F\uFEFF\u00AD]/g, "");
152
- s = s.replace(
153
- /<(\/?)(?:system|assistant|human)>/gi,
154
- (_, sl) => `\uFF1C${sl || ""}system-text\uFF1E`,
155
- );
156
- s = s.replace(/\[(SYSTEM|INST)\]/gi, "[$1-TEXT]");
157
- s = s.replace(/<<\s*SYS\s*>>/gi, "\u00ABSYS-TEXT\u00BB");
158
- return s;
159
- }
160
-
161
- /**
162
- * Validate a STATE.md field name to prevent regex injection via crafted field names.
163
- * Field names must be human-readable: letters, spaces, hyphens, underscores only.
164
- */
165
- export function validateFieldName(fieldName: string): FieldValidationResult {
166
- if (!fieldName || typeof fieldName !== "string") {
167
- return { valid: false, error: "Field name must be a non-empty string" };
168
- }
169
- if (!/^[a-zA-Z][a-zA-Z0-9 _-]*$/.test(fieldName)) {
170
- return {
171
- valid: false,
172
- error: `Field name "${fieldName}" contains invalid characters. Only letters, digits, spaces, hyphens, and underscores are allowed.`,
173
- };
174
- }
175
- return { valid: true };
176
- }