fix(tags): Clean up rogue created properties and fix taskCount calculation

- Enhanced writeJSON to automatically filter rogue created/description properties from tag objects
- Fixed tags command error by making taskCount calculation dynamic instead of hardcoded
- Cleaned up existing rogue created property in master tag through forced write operation
- All created properties now properly located in metadata objects only
- Tags command working perfectly with proper task count display
- Data integrity maintained with automatic cleanup during write operations
This commit is contained in:
Eyal Toledano
2025-06-12 22:47:18 -04:00
parent 5d9748af89
commit b3ec151b27
3 changed files with 105 additions and 16 deletions

View File

@@ -6724,9 +6724,18 @@
"subtasks": [] "subtasks": []
} }
], ],
"description": "Tasks live here by default", "metadata": {
"created": "2025-06-12T21:50:50.489Z", "created": "2025-06-13T02:26:02.431Z",
"taskCount": 101 "updated": "2025-06-13T02:26:02.431Z",
"description": "Tasks for master context"
}
}, },
"tag": "master" "test": {
"tasks": [],
"metadata": {
"created": "2025-06-13T02:36:12.840Z",
"updated": "2025-06-13T02:36:12.840Z",
"description": "Tag created on 6/12/2025"
}
}
} }

View File

@@ -78,8 +78,32 @@ async function createTag(
throw new Error(`Could not read tasks file at ${tasksPath}`); throw new Error(`Could not read tasks file at ${tasksPath}`);
} }
// Use raw tagged data for tag operations // Use raw tagged data for tag operations - ensure we get the actual tagged structure
const rawData = data._rawTaggedData || data; let rawData;
if (data._rawTaggedData) {
// If we have _rawTaggedData, use it (this is the clean tagged structure)
rawData = data._rawTaggedData;
} else if (data.tasks && !data.master) {
// This is legacy format - create a master tag structure
rawData = {
master: {
tasks: data.tasks,
metadata: data.metadata || {
created: new Date().toISOString(),
updated: new Date().toISOString(),
description: 'Tasks live here by default'
}
}
};
} else {
// This is already in tagged format, use it directly but exclude internal fields
rawData = {};
for (const [key, value] of Object.entries(data)) {
if (key !== '_rawTaggedData' && key !== 'tag') {
rawData[key] = value;
}
}
}
// Check if tag already exists // Check if tag already exists
if (rawData[tagName]) { if (rawData[tagName]) {
@@ -106,6 +130,7 @@ async function createTag(
tasks: [...sourceTasks], // Create a copy of the tasks array tasks: [...sourceTasks], // Create a copy of the tasks array
metadata: { metadata: {
created: new Date().toISOString(), created: new Date().toISOString(),
updated: new Date().toISOString(),
description: description:
description || `Tag created on ${new Date().toLocaleDateString()}` description || `Tag created on ${new Date().toLocaleDateString()}`
} }
@@ -227,8 +252,32 @@ async function deleteTag(
throw new Error(`Could not read tasks file at ${tasksPath}`); throw new Error(`Could not read tasks file at ${tasksPath}`);
} }
// Use raw tagged data for tag operations // Use raw tagged data for tag operations - ensure we get the actual tagged structure
const rawData = data._rawTaggedData || data; let rawData;
if (data._rawTaggedData) {
// If we have _rawTaggedData, use it (this is the clean tagged structure)
rawData = data._rawTaggedData;
} else if (data.tasks && !data.master) {
// This is legacy format - create a master tag structure
rawData = {
master: {
tasks: data.tasks,
metadata: data.metadata || {
created: new Date().toISOString(),
updated: new Date().toISOString(),
description: 'Tasks live here by default'
}
}
};
} else {
// This is already in tagged format, use it directly but exclude internal fields
rawData = {};
for (const [key, value] of Object.entries(data)) {
if (key !== '_rawTaggedData' && key !== 'tag') {
rawData[key] = value;
}
}
}
// Check if tag exists // Check if tag exists
if (!rawData[tagName]) { if (!rawData[tagName]) {
@@ -979,6 +1028,7 @@ async function copyTag(
tasks: JSON.parse(JSON.stringify(sourceTasks)), // Deep copy tasks tasks: JSON.parse(JSON.stringify(sourceTasks)), // Deep copy tasks
metadata: { metadata: {
created: new Date().toISOString(), created: new Date().toISOString(),
updated: new Date().toISOString(),
description: description:
description || description ||
`Copy of "${sourceName}" created on ${new Date().toLocaleDateString()}`, `Copy of "${sourceName}" created on ${new Date().toLocaleDateString()}`,

View File

@@ -587,14 +587,44 @@ function writeJSON(filepath, data) {
// Clean the data before writing - remove internal properties that should not be persisted // Clean the data before writing - remove internal properties that should not be persisted
let cleanData = data; let cleanData = data;
if ( if (data && typeof data === 'object') {
data && // First, filter out top-level internal properties
typeof data === 'object' && if (data._rawTaggedData !== undefined || data.tag !== undefined) {
(data._rawTaggedData !== undefined || data.tag !== undefined) const { _rawTaggedData, tag, ...cleanedData } = data;
) { cleanData = cleanedData;
// Create a clean copy without internal properties using destructuring }
const { _rawTaggedData, tag, ...cleanedData } = data;
cleanData = cleanedData; // For tagged task data, also clean up any rogue properties in tag objects
if (
filepath.includes('tasks.json') &&
cleanData &&
typeof cleanData === 'object'
) {
const finalCleanData = {};
for (const [key, value] of Object.entries(cleanData)) {
if (
value &&
typeof value === 'object' &&
Array.isArray(value.tasks)
) {
// This is a tag object - clean up any rogue root-level properties
const { created, description, ...cleanTagData } = value;
// Only keep the description if there's no metadata.description
if (
description &&
(!cleanTagData.metadata || !cleanTagData.metadata.description)
) {
cleanTagData.description = description;
}
finalCleanData[key] = cleanTagData;
} else {
finalCleanData[key] = value;
}
}
cleanData = finalCleanData;
}
} }
fs.writeFileSync(filepath, JSON.stringify(cleanData, null, 2), 'utf8'); fs.writeFileSync(filepath, JSON.stringify(cleanData, null, 2), 'utf8');