pi-gsd 2.0.1 → 2.0.3

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 (66) hide show
  1. package/dist/pi-gsd-hooks.js +1533 -0
  2. package/dist/pi-gsd-tools.js +53 -52
  3. package/package.json +3 -5
  4. package/.gsd/extensions/pi-gsd-hooks.ts +0 -973
  5. package/src/cli.ts +0 -644
  6. package/src/commands/base.ts +0 -67
  7. package/src/commands/commit.ts +0 -22
  8. package/src/commands/config.ts +0 -71
  9. package/src/commands/frontmatter.ts +0 -51
  10. package/src/commands/index.ts +0 -76
  11. package/src/commands/init.ts +0 -43
  12. package/src/commands/milestone.ts +0 -37
  13. package/src/commands/phase.ts +0 -92
  14. package/src/commands/progress.ts +0 -71
  15. package/src/commands/roadmap.ts +0 -40
  16. package/src/commands/scaffold.ts +0 -19
  17. package/src/commands/state.ts +0 -102
  18. package/src/commands/template.ts +0 -52
  19. package/src/commands/verify.ts +0 -70
  20. package/src/commands/workstream.ts +0 -98
  21. package/src/commands/wxp.ts +0 -65
  22. package/src/lib/commands.ts +0 -1040
  23. package/src/lib/config.ts +0 -385
  24. package/src/lib/core.ts +0 -1167
  25. package/src/lib/frontmatter.ts +0 -462
  26. package/src/lib/init.ts +0 -517
  27. package/src/lib/milestone.ts +0 -290
  28. package/src/lib/model-profiles.ts +0 -272
  29. package/src/lib/phase.ts +0 -1012
  30. package/src/lib/profile-output.ts +0 -237
  31. package/src/lib/profile-pipeline.ts +0 -556
  32. package/src/lib/roadmap.ts +0 -378
  33. package/src/lib/schemas.ts +0 -290
  34. package/src/lib/security.ts +0 -176
  35. package/src/lib/state.ts +0 -1175
  36. package/src/lib/template.ts +0 -246
  37. package/src/lib/uat.ts +0 -289
  38. package/src/lib/verify.ts +0 -879
  39. package/src/lib/workstream.ts +0 -524
  40. package/src/output.ts +0 -45
  41. package/src/schemas/pi-gsd-settings.schema.json +0 -80
  42. package/src/schemas/wxp.xsd +0 -619
  43. package/src/schemas/wxp.zod.ts +0 -318
  44. package/src/wxp/__tests__/arguments.test.ts +0 -86
  45. package/src/wxp/__tests__/conditions.test.ts +0 -106
  46. package/src/wxp/__tests__/executor.test.ts +0 -95
  47. package/src/wxp/__tests__/helpers.ts +0 -26
  48. package/src/wxp/__tests__/integration.test.ts +0 -166
  49. package/src/wxp/__tests__/new-features.test.ts +0 -222
  50. package/src/wxp/__tests__/parser.test.ts +0 -159
  51. package/src/wxp/__tests__/paste.test.ts +0 -66
  52. package/src/wxp/__tests__/schema.test.ts +0 -120
  53. package/src/wxp/__tests__/security.test.ts +0 -87
  54. package/src/wxp/__tests__/shell.test.ts +0 -85
  55. package/src/wxp/__tests__/string-ops.test.ts +0 -25
  56. package/src/wxp/__tests__/variables.test.ts +0 -65
  57. package/src/wxp/arguments.ts +0 -89
  58. package/src/wxp/conditions.ts +0 -78
  59. package/src/wxp/executor.ts +0 -191
  60. package/src/wxp/index.ts +0 -191
  61. package/src/wxp/parser.ts +0 -198
  62. package/src/wxp/paste.ts +0 -51
  63. package/src/wxp/security.ts +0 -102
  64. package/src/wxp/shell.ts +0 -81
  65. package/src/wxp/string-ops.ts +0 -44
  66. 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
- }