@lusipad/pmspec 1.0.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 (60) hide show
  1. package/README.md +306 -0
  2. package/README.zh.md +304 -0
  3. package/bin/pmspec.js +5 -0
  4. package/dist/cli/index.d.ts +3 -0
  5. package/dist/cli/index.js +39 -0
  6. package/dist/commands/analyze.d.ts +4 -0
  7. package/dist/commands/analyze.js +240 -0
  8. package/dist/commands/breakdown.d.ts +4 -0
  9. package/dist/commands/breakdown.js +194 -0
  10. package/dist/commands/create.d.ts +4 -0
  11. package/dist/commands/create.js +529 -0
  12. package/dist/commands/history.d.ts +4 -0
  13. package/dist/commands/history.js +213 -0
  14. package/dist/commands/import.d.ts +4 -0
  15. package/dist/commands/import.js +196 -0
  16. package/dist/commands/index-legacy.d.ts +4 -0
  17. package/dist/commands/index-legacy.js +27 -0
  18. package/dist/commands/init.d.ts +3 -0
  19. package/dist/commands/init.js +60 -0
  20. package/dist/commands/list.d.ts +3 -0
  21. package/dist/commands/list.js +127 -0
  22. package/dist/commands/search.d.ts +7 -0
  23. package/dist/commands/search.js +183 -0
  24. package/dist/commands/serve.d.ts +3 -0
  25. package/dist/commands/serve.js +68 -0
  26. package/dist/commands/show.d.ts +3 -0
  27. package/dist/commands/show.js +152 -0
  28. package/dist/commands/simple.d.ts +7 -0
  29. package/dist/commands/simple.js +360 -0
  30. package/dist/commands/update.d.ts +4 -0
  31. package/dist/commands/update.js +247 -0
  32. package/dist/commands/validate.d.ts +3 -0
  33. package/dist/commands/validate.js +74 -0
  34. package/dist/core/changelog-service.d.ts +88 -0
  35. package/dist/core/changelog-service.js +208 -0
  36. package/dist/core/changelog.d.ts +113 -0
  37. package/dist/core/changelog.js +147 -0
  38. package/dist/core/importers.d.ts +343 -0
  39. package/dist/core/importers.js +715 -0
  40. package/dist/core/parser.d.ts +50 -0
  41. package/dist/core/parser.js +246 -0
  42. package/dist/core/project.d.ts +155 -0
  43. package/dist/core/project.js +138 -0
  44. package/dist/core/search.d.ts +119 -0
  45. package/dist/core/search.js +299 -0
  46. package/dist/core/simple-model.d.ts +54 -0
  47. package/dist/core/simple-model.js +20 -0
  48. package/dist/core/team.d.ts +41 -0
  49. package/dist/core/team.js +57 -0
  50. package/dist/core/workload.d.ts +49 -0
  51. package/dist/core/workload.js +116 -0
  52. package/dist/index.d.ts +15 -0
  53. package/dist/index.js +11 -0
  54. package/dist/utils/csv-handler.d.ts +15 -0
  55. package/dist/utils/csv-handler.js +224 -0
  56. package/dist/utils/markdown.d.ts +43 -0
  57. package/dist/utils/markdown.js +202 -0
  58. package/dist/utils/validation.d.ts +35 -0
  59. package/dist/utils/validation.js +178 -0
  60. package/package.json +71 -0
@@ -0,0 +1,88 @@
1
+ import { ChangelogEntry, ChangelogFile, ChangelogQueryOptions, ChangelogEntityType } from './changelog.js';
2
+ /**
3
+ * Service for managing changelog operations
4
+ */
5
+ export declare class ChangelogService {
6
+ private filePath;
7
+ constructor(basePath?: string);
8
+ /**
9
+ * Initialize the changelog file if it doesn't exist
10
+ */
11
+ initialize(): Promise<void>;
12
+ /**
13
+ * Read the changelog file
14
+ */
15
+ read(): Promise<ChangelogFile>;
16
+ /**
17
+ * Write the changelog file
18
+ */
19
+ write(data: ChangelogFile): Promise<void>;
20
+ /**
21
+ * Add entries to the changelog
22
+ */
23
+ addEntries(entries: ChangelogEntry[]): Promise<void>;
24
+ /**
25
+ * Record a create action
26
+ */
27
+ recordCreate(entityType: ChangelogEntityType, entityId: string, user?: string): Promise<ChangelogEntry>;
28
+ /**
29
+ * Record a delete action
30
+ */
31
+ recordDelete(entityType: ChangelogEntityType, entityId: string, user?: string): Promise<ChangelogEntry>;
32
+ /**
33
+ * Record an update action for a single field
34
+ */
35
+ recordUpdate(entityType: ChangelogEntityType, entityId: string, field: string, oldValue: unknown, newValue: unknown, user?: string): Promise<ChangelogEntry | null>;
36
+ /**
37
+ * Record multiple field updates at once
38
+ */
39
+ recordUpdates(entityType: ChangelogEntityType, entityId: string, changes: Record<string, {
40
+ oldValue: unknown;
41
+ newValue: unknown;
42
+ }>, user?: string): Promise<ChangelogEntry[]>;
43
+ /**
44
+ * Query changelog entries
45
+ */
46
+ query(options?: ChangelogQueryOptions): Promise<ChangelogEntry[]>;
47
+ /**
48
+ * Get history for a specific entity
49
+ */
50
+ getEntityHistory(entityId: string): Promise<ChangelogEntry[]>;
51
+ /**
52
+ * Get all entries (with optional pagination)
53
+ */
54
+ getAll(limit?: number, offset?: number): Promise<{
55
+ entries: ChangelogEntry[];
56
+ total: number;
57
+ }>;
58
+ /**
59
+ * Get entries since a specific date
60
+ */
61
+ getSince(since: string): Promise<ChangelogEntry[]>;
62
+ /**
63
+ * Get changelog summary statistics
64
+ */
65
+ getStats(): Promise<{
66
+ totalEntries: number;
67
+ byEntityType: Record<string, number>;
68
+ byAction: Record<string, number>;
69
+ recentActivity: {
70
+ last24h: number;
71
+ last7d: number;
72
+ last30d: number;
73
+ };
74
+ }>;
75
+ /**
76
+ * Clear all changelog entries (use with caution)
77
+ */
78
+ clear(): Promise<void>;
79
+ }
80
+ /**
81
+ * Get the default changelog service instance
82
+ */
83
+ export declare function getChangelogService(): ChangelogService;
84
+ /**
85
+ * Reset the default service (mainly for testing)
86
+ */
87
+ export declare function resetChangelogService(): void;
88
+ //# sourceMappingURL=changelog-service.d.ts.map
@@ -0,0 +1,208 @@
1
+ import { readFile, writeFile, mkdir } from 'fs/promises';
2
+ import { join, dirname } from 'path';
3
+ import { ChangelogFileSchema, createChangelogEntry, createUpdateEntries, filterChangelogEntries, } from './changelog.js';
4
+ const CHANGELOG_FILE = 'pmspace/changelog.json';
5
+ /**
6
+ * Service for managing changelog operations
7
+ */
8
+ export class ChangelogService {
9
+ filePath;
10
+ constructor(basePath = process.cwd()) {
11
+ this.filePath = join(basePath, CHANGELOG_FILE);
12
+ }
13
+ /**
14
+ * Initialize the changelog file if it doesn't exist
15
+ */
16
+ async initialize() {
17
+ try {
18
+ await readFile(this.filePath, 'utf-8');
19
+ }
20
+ catch (error) {
21
+ if (error.code === 'ENOENT') {
22
+ // Create directory and file
23
+ await mkdir(dirname(this.filePath), { recursive: true });
24
+ const initialData = {
25
+ version: '1.0',
26
+ lastUpdated: new Date().toISOString(),
27
+ entries: [],
28
+ };
29
+ await writeFile(this.filePath, JSON.stringify(initialData, null, 2));
30
+ }
31
+ else {
32
+ throw error;
33
+ }
34
+ }
35
+ }
36
+ /**
37
+ * Read the changelog file
38
+ */
39
+ async read() {
40
+ try {
41
+ const content = await readFile(this.filePath, 'utf-8');
42
+ const data = JSON.parse(content);
43
+ return ChangelogFileSchema.parse(data);
44
+ }
45
+ catch (error) {
46
+ if (error.code === 'ENOENT') {
47
+ // Return empty changelog if file doesn't exist
48
+ return {
49
+ version: '1.0',
50
+ lastUpdated: new Date().toISOString(),
51
+ entries: [],
52
+ };
53
+ }
54
+ throw error;
55
+ }
56
+ }
57
+ /**
58
+ * Write the changelog file
59
+ */
60
+ async write(data) {
61
+ await mkdir(dirname(this.filePath), { recursive: true });
62
+ data.lastUpdated = new Date().toISOString();
63
+ await writeFile(this.filePath, JSON.stringify(data, null, 2));
64
+ }
65
+ /**
66
+ * Add entries to the changelog
67
+ */
68
+ async addEntries(entries) {
69
+ if (entries.length === 0)
70
+ return;
71
+ const data = await this.read();
72
+ data.entries.push(...entries);
73
+ await this.write(data);
74
+ }
75
+ /**
76
+ * Record a create action
77
+ */
78
+ async recordCreate(entityType, entityId, user) {
79
+ const entry = createChangelogEntry(entityType, entityId, 'create', { user });
80
+ await this.addEntries([entry]);
81
+ return entry;
82
+ }
83
+ /**
84
+ * Record a delete action
85
+ */
86
+ async recordDelete(entityType, entityId, user) {
87
+ const entry = createChangelogEntry(entityType, entityId, 'delete', { user });
88
+ await this.addEntries([entry]);
89
+ return entry;
90
+ }
91
+ /**
92
+ * Record an update action for a single field
93
+ */
94
+ async recordUpdate(entityType, entityId, field, oldValue, newValue, user) {
95
+ // Don't record if value didn't change
96
+ if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
97
+ return null;
98
+ }
99
+ const entry = createChangelogEntry(entityType, entityId, 'update', {
100
+ field,
101
+ oldValue,
102
+ newValue,
103
+ user,
104
+ });
105
+ await this.addEntries([entry]);
106
+ return entry;
107
+ }
108
+ /**
109
+ * Record multiple field updates at once
110
+ */
111
+ async recordUpdates(entityType, entityId, changes, user) {
112
+ const entries = createUpdateEntries(entityType, entityId, changes, user);
113
+ await this.addEntries(entries);
114
+ return entries;
115
+ }
116
+ /**
117
+ * Query changelog entries
118
+ */
119
+ async query(options = {}) {
120
+ const data = await this.read();
121
+ return filterChangelogEntries(data.entries, options);
122
+ }
123
+ /**
124
+ * Get history for a specific entity
125
+ */
126
+ async getEntityHistory(entityId) {
127
+ return this.query({ entityId });
128
+ }
129
+ /**
130
+ * Get all entries (with optional pagination)
131
+ */
132
+ async getAll(limit, offset) {
133
+ const data = await this.read();
134
+ const total = data.entries.length;
135
+ const entries = filterChangelogEntries(data.entries, { limit, offset });
136
+ return { entries, total };
137
+ }
138
+ /**
139
+ * Get entries since a specific date
140
+ */
141
+ async getSince(since) {
142
+ return this.query({ since });
143
+ }
144
+ /**
145
+ * Get changelog summary statistics
146
+ */
147
+ async getStats() {
148
+ const data = await this.read();
149
+ const entries = data.entries;
150
+ const now = new Date();
151
+ const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
152
+ const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
153
+ const oneMonthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
154
+ const byEntityType = {};
155
+ const byAction = {};
156
+ let last24h = 0;
157
+ let last7d = 0;
158
+ let last30d = 0;
159
+ for (const entry of entries) {
160
+ // Count by entity type
161
+ byEntityType[entry.entityType] = (byEntityType[entry.entityType] || 0) + 1;
162
+ // Count by action
163
+ byAction[entry.action] = (byAction[entry.action] || 0) + 1;
164
+ // Count recent activity
165
+ const entryDate = new Date(entry.timestamp);
166
+ if (entryDate >= oneDayAgo)
167
+ last24h++;
168
+ if (entryDate >= oneWeekAgo)
169
+ last7d++;
170
+ if (entryDate >= oneMonthAgo)
171
+ last30d++;
172
+ }
173
+ return {
174
+ totalEntries: entries.length,
175
+ byEntityType,
176
+ byAction,
177
+ recentActivity: { last24h, last7d, last30d },
178
+ };
179
+ }
180
+ /**
181
+ * Clear all changelog entries (use with caution)
182
+ */
183
+ async clear() {
184
+ await this.write({
185
+ version: '1.0',
186
+ lastUpdated: new Date().toISOString(),
187
+ entries: [],
188
+ });
189
+ }
190
+ }
191
+ // Singleton instance for convenience
192
+ let defaultService = null;
193
+ /**
194
+ * Get the default changelog service instance
195
+ */
196
+ export function getChangelogService() {
197
+ if (!defaultService) {
198
+ defaultService = new ChangelogService();
199
+ }
200
+ return defaultService;
201
+ }
202
+ /**
203
+ * Reset the default service (mainly for testing)
204
+ */
205
+ export function resetChangelogService() {
206
+ defaultService = null;
207
+ }
208
+ //# sourceMappingURL=changelog-service.js.map
@@ -0,0 +1,113 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Entity types that can be tracked in the changelog
4
+ */
5
+ export declare const ChangelogEntityType: z.ZodEnum<{
6
+ epic: "epic";
7
+ feature: "feature";
8
+ milestone: "milestone";
9
+ story: "story";
10
+ }>;
11
+ export type ChangelogEntityType = z.infer<typeof ChangelogEntityType>;
12
+ /**
13
+ * Action types for changelog entries
14
+ */
15
+ export declare const ChangelogAction: z.ZodEnum<{
16
+ create: "create";
17
+ update: "update";
18
+ delete: "delete";
19
+ }>;
20
+ export type ChangelogAction = z.infer<typeof ChangelogAction>;
21
+ /**
22
+ * Schema for a single changelog entry
23
+ */
24
+ export declare const ChangelogEntrySchema: z.ZodObject<{
25
+ id: z.ZodString;
26
+ timestamp: z.ZodString;
27
+ entityType: z.ZodEnum<{
28
+ epic: "epic";
29
+ feature: "feature";
30
+ milestone: "milestone";
31
+ story: "story";
32
+ }>;
33
+ entityId: z.ZodString;
34
+ action: z.ZodEnum<{
35
+ create: "create";
36
+ update: "update";
37
+ delete: "delete";
38
+ }>;
39
+ field: z.ZodOptional<z.ZodString>;
40
+ oldValue: z.ZodOptional<z.ZodUnknown>;
41
+ newValue: z.ZodOptional<z.ZodUnknown>;
42
+ user: z.ZodOptional<z.ZodString>;
43
+ }, z.core.$strip>;
44
+ export type ChangelogEntry = z.infer<typeof ChangelogEntrySchema>;
45
+ /**
46
+ * Schema for the changelog file structure
47
+ */
48
+ export declare const ChangelogFileSchema: z.ZodObject<{
49
+ version: z.ZodDefault<z.ZodString>;
50
+ lastUpdated: z.ZodOptional<z.ZodString>;
51
+ entries: z.ZodArray<z.ZodObject<{
52
+ id: z.ZodString;
53
+ timestamp: z.ZodString;
54
+ entityType: z.ZodEnum<{
55
+ epic: "epic";
56
+ feature: "feature";
57
+ milestone: "milestone";
58
+ story: "story";
59
+ }>;
60
+ entityId: z.ZodString;
61
+ action: z.ZodEnum<{
62
+ create: "create";
63
+ update: "update";
64
+ delete: "delete";
65
+ }>;
66
+ field: z.ZodOptional<z.ZodString>;
67
+ oldValue: z.ZodOptional<z.ZodUnknown>;
68
+ newValue: z.ZodOptional<z.ZodUnknown>;
69
+ user: z.ZodOptional<z.ZodString>;
70
+ }, z.core.$strip>>;
71
+ }, z.core.$strip>;
72
+ export type ChangelogFile = z.infer<typeof ChangelogFileSchema>;
73
+ /**
74
+ * Options for querying changelog entries
75
+ */
76
+ export interface ChangelogQueryOptions {
77
+ entityId?: string;
78
+ entityType?: ChangelogEntityType;
79
+ action?: ChangelogAction;
80
+ since?: string;
81
+ until?: string;
82
+ limit?: number;
83
+ offset?: number;
84
+ }
85
+ /**
86
+ * Generate a unique ID for a changelog entry
87
+ */
88
+ export declare function generateChangelogId(): string;
89
+ /**
90
+ * Create a new changelog entry
91
+ */
92
+ export declare function createChangelogEntry(entityType: ChangelogEntityType, entityId: string, action: ChangelogAction, options?: {
93
+ field?: string;
94
+ oldValue?: unknown;
95
+ newValue?: unknown;
96
+ user?: string;
97
+ }): ChangelogEntry;
98
+ /**
99
+ * Create multiple changelog entries for a batch of field updates
100
+ */
101
+ export declare function createUpdateEntries(entityType: ChangelogEntityType, entityId: string, changes: Record<string, {
102
+ oldValue: unknown;
103
+ newValue: unknown;
104
+ }>, user?: string): ChangelogEntry[];
105
+ /**
106
+ * Filter changelog entries based on query options
107
+ */
108
+ export declare function filterChangelogEntries(entries: ChangelogEntry[], options: ChangelogQueryOptions): ChangelogEntry[];
109
+ /**
110
+ * Format a changelog entry for display
111
+ */
112
+ export declare function formatChangelogEntry(entry: ChangelogEntry): string;
113
+ //# sourceMappingURL=changelog.d.ts.map
@@ -0,0 +1,147 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Entity types that can be tracked in the changelog
4
+ */
5
+ export const ChangelogEntityType = z.enum(['epic', 'feature', 'milestone', 'story']);
6
+ /**
7
+ * Action types for changelog entries
8
+ */
9
+ export const ChangelogAction = z.enum(['create', 'update', 'delete']);
10
+ /**
11
+ * Schema for a single changelog entry
12
+ */
13
+ export const ChangelogEntrySchema = z.object({
14
+ id: z.string(),
15
+ timestamp: z.string(), // ISO datetime
16
+ entityType: ChangelogEntityType,
17
+ entityId: z.string(),
18
+ action: ChangelogAction,
19
+ field: z.string().optional(),
20
+ oldValue: z.unknown().optional(),
21
+ newValue: z.unknown().optional(),
22
+ user: z.string().optional(),
23
+ });
24
+ /**
25
+ * Schema for the changelog file structure
26
+ */
27
+ export const ChangelogFileSchema = z.object({
28
+ version: z.string().default('1.0'),
29
+ lastUpdated: z.string().optional(),
30
+ entries: z.array(ChangelogEntrySchema),
31
+ });
32
+ /**
33
+ * Generate a unique ID for a changelog entry
34
+ */
35
+ export function generateChangelogId() {
36
+ const timestamp = Date.now().toString(36);
37
+ const random = Math.random().toString(36).substring(2, 8);
38
+ return `CHG-${timestamp}-${random}`;
39
+ }
40
+ /**
41
+ * Create a new changelog entry
42
+ */
43
+ export function createChangelogEntry(entityType, entityId, action, options) {
44
+ return ChangelogEntrySchema.parse({
45
+ id: generateChangelogId(),
46
+ timestamp: new Date().toISOString(),
47
+ entityType,
48
+ entityId,
49
+ action,
50
+ field: options?.field,
51
+ oldValue: options?.oldValue,
52
+ newValue: options?.newValue,
53
+ user: options?.user,
54
+ });
55
+ }
56
+ /**
57
+ * Create multiple changelog entries for a batch of field updates
58
+ */
59
+ export function createUpdateEntries(entityType, entityId, changes, user) {
60
+ const entries = [];
61
+ const timestamp = new Date().toISOString();
62
+ for (const [field, { oldValue, newValue }] of Object.entries(changes)) {
63
+ // Only create entry if value actually changed
64
+ if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
65
+ entries.push(ChangelogEntrySchema.parse({
66
+ id: generateChangelogId(),
67
+ timestamp,
68
+ entityType,
69
+ entityId,
70
+ action: 'update',
71
+ field,
72
+ oldValue,
73
+ newValue,
74
+ user,
75
+ }));
76
+ }
77
+ }
78
+ return entries;
79
+ }
80
+ /**
81
+ * Filter changelog entries based on query options
82
+ */
83
+ export function filterChangelogEntries(entries, options) {
84
+ let filtered = [...entries];
85
+ if (options.entityId) {
86
+ filtered = filtered.filter(e => e.entityId === options.entityId);
87
+ }
88
+ if (options.entityType) {
89
+ filtered = filtered.filter(e => e.entityType === options.entityType);
90
+ }
91
+ if (options.action) {
92
+ filtered = filtered.filter(e => e.action === options.action);
93
+ }
94
+ if (options.since) {
95
+ const sinceDate = new Date(options.since);
96
+ filtered = filtered.filter(e => new Date(e.timestamp) >= sinceDate);
97
+ }
98
+ if (options.until) {
99
+ const untilDate = new Date(options.until);
100
+ filtered = filtered.filter(e => new Date(e.timestamp) <= untilDate);
101
+ }
102
+ // Sort by timestamp descending (newest first)
103
+ filtered.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
104
+ // Apply pagination
105
+ if (options.offset) {
106
+ filtered = filtered.slice(options.offset);
107
+ }
108
+ if (options.limit) {
109
+ filtered = filtered.slice(0, options.limit);
110
+ }
111
+ return filtered;
112
+ }
113
+ /**
114
+ * Format a changelog entry for display
115
+ */
116
+ export function formatChangelogEntry(entry) {
117
+ const timestamp = new Date(entry.timestamp).toLocaleString();
118
+ const user = entry.user ? ` by ${entry.user}` : '';
119
+ switch (entry.action) {
120
+ case 'create':
121
+ return `[${timestamp}] Created ${entry.entityType} ${entry.entityId}${user}`;
122
+ case 'delete':
123
+ return `[${timestamp}] Deleted ${entry.entityType} ${entry.entityId}${user}`;
124
+ case 'update':
125
+ if (entry.field) {
126
+ const oldVal = formatValue(entry.oldValue);
127
+ const newVal = formatValue(entry.newValue);
128
+ return `[${timestamp}] Updated ${entry.entityType} ${entry.entityId} - ${entry.field}: ${oldVal} → ${newVal}${user}`;
129
+ }
130
+ return `[${timestamp}] Updated ${entry.entityType} ${entry.entityId}${user}`;
131
+ default:
132
+ return `[${timestamp}] ${entry.action} ${entry.entityType} ${entry.entityId}${user}`;
133
+ }
134
+ }
135
+ /**
136
+ * Format a value for display
137
+ */
138
+ function formatValue(value) {
139
+ if (value === undefined || value === null) {
140
+ return '(empty)';
141
+ }
142
+ if (typeof value === 'object') {
143
+ return JSON.stringify(value);
144
+ }
145
+ return String(value);
146
+ }
147
+ //# sourceMappingURL=changelog.js.map