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": []
}
],
"description": "Tasks live here by default",
"created": "2025-06-12T21:50:50.489Z",
"taskCount": 101
"metadata": {
"created": "2025-06-13T02:26:02.431Z",
"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}`);
}
// Use raw tagged data for tag operations
const rawData = data._rawTaggedData || data;
// Use raw tagged data for tag operations - ensure we get the actual tagged structure
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
if (rawData[tagName]) {
@@ -106,6 +130,7 @@ async function createTag(
tasks: [...sourceTasks], // Create a copy of the tasks array
metadata: {
created: new Date().toISOString(),
updated: new Date().toISOString(),
description:
description || `Tag created on ${new Date().toLocaleDateString()}`
}
@@ -227,8 +252,32 @@ async function deleteTag(
throw new Error(`Could not read tasks file at ${tasksPath}`);
}
// Use raw tagged data for tag operations
const rawData = data._rawTaggedData || data;
// Use raw tagged data for tag operations - ensure we get the actual tagged structure
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
if (!rawData[tagName]) {
@@ -979,6 +1028,7 @@ async function copyTag(
tasks: JSON.parse(JSON.stringify(sourceTasks)), // Deep copy tasks
metadata: {
created: new Date().toISOString(),
updated: new Date().toISOString(),
description:
description ||
`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
let cleanData = data;
if (
data &&
typeof data === 'object' &&
(data._rawTaggedData !== undefined || data.tag !== undefined)
) {
// Create a clean copy without internal properties using destructuring
const { _rawTaggedData, tag, ...cleanedData } = data;
cleanData = cleanedData;
if (data && typeof data === 'object') {
// First, filter out top-level internal properties
if (data._rawTaggedData !== undefined || data.tag !== undefined) {
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');