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:
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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()}`,
|
||||||
|
|||||||
@@ -587,16 +587,46 @@ 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)
|
|
||||||
) {
|
|
||||||
// Create a clean copy without internal properties using destructuring
|
|
||||||
const { _rawTaggedData, tag, ...cleanedData } = data;
|
const { _rawTaggedData, tag, ...cleanedData } = data;
|
||||||
cleanData = cleanedData;
|
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');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log('error', `Error writing JSON file ${filepath}:`, error.message);
|
log('error', `Error writing JSON file ${filepath}:`, error.message);
|
||||||
|
|||||||
Reference in New Issue
Block a user