@codyswann/lisa 1.1.0 → 1.3.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.
@@ -8,18 +8,35 @@ allowed-tools: Read, Bash, TaskCreate, TaskUpdate, TaskList
8
8
 
9
9
  Load tasks from `projects/$ARGUMENTS/tasks/` into the current Claude Code session.
10
10
 
11
+ Tasks are stored in session subdirectories to preserve history across `/clear` commands:
12
+ ```
13
+ projects/$ARGUMENTS/tasks/
14
+ ├── {session-1-uuid}/
15
+ │ ├── 1.json
16
+ │ └── 2.json
17
+ └── {session-2-uuid}/
18
+ ├── 1.json
19
+ └── 2.json
20
+ ```
21
+
11
22
  ## Process
12
23
 
13
24
  ### Step 1: Validate Project
14
25
 
15
- Check if the project exists:
26
+ Check if the project exists and has task files:
16
27
 
17
28
  ```bash
18
- ls projects/$ARGUMENTS/tasks/*.json 2>/dev/null
29
+ find projects/$ARGUMENTS/tasks -name "*.json" 2>/dev/null | head -5
19
30
  ```
20
31
 
21
32
  If no task files exist, report: "No tasks found in projects/$ARGUMENTS/tasks/"
22
33
 
34
+ List available sessions:
35
+
36
+ ```bash
37
+ ls -dt projects/$ARGUMENTS/tasks/*/ 2>/dev/null | head -10
38
+ ```
39
+
23
40
  ### Step 2: Set Active Project
24
41
 
25
42
  Create the active project marker:
@@ -32,7 +49,13 @@ This ensures any new tasks created will sync back to this project.
32
49
 
33
50
  ### Step 3: Load Tasks
34
51
 
35
- For each JSON file in `projects/$ARGUMENTS/tasks/`:
52
+ Find and load all task JSON files from ALL session directories:
53
+
54
+ ```bash
55
+ find projects/$ARGUMENTS/tasks -name "*.json" -type f
56
+ ```
57
+
58
+ For each JSON file found:
36
59
 
37
60
  1. Read the task JSON file
38
61
  2. Use TaskCreate to recreate the task with:
@@ -48,6 +71,7 @@ After loading all tasks, report:
48
71
 
49
72
  ```
50
73
  Loaded X tasks from projects/$ARGUMENTS/tasks/
74
+ - Sessions found: N
51
75
  - Pending: Y
52
76
  - Completed: Z
53
77
 
@@ -59,5 +83,6 @@ New tasks will automatically sync to this project.
59
83
 
60
84
  - Tasks are recreated with new IDs in the current session
61
85
  - The original task IDs from the project are not preserved
86
+ - Tasks from ALL sessions are loaded (full history)
62
87
  - Task dependencies (blocks/blockedBy) are NOT currently preserved across load/sync cycles
63
88
  - Use TaskList to see the loaded tasks
@@ -6,7 +6,10 @@ allowed-tools: Read, Write, Bash, TaskList, TaskGet
6
6
 
7
7
  # Sync Tasks to Project
8
8
 
9
- Sync all tasks from the current session to `projects/$ARGUMENTS/tasks/`.
9
+ Sync all tasks from the current session to `projects/$ARGUMENTS/tasks/{session-id}/`.
10
+
11
+ This command is for manual syncing when you started work without setting an active project.
12
+ Once synced, the automatic hook will handle future task updates.
10
13
 
11
14
  ## Process
12
15
 
@@ -26,7 +29,23 @@ If yes, create the project structure:
26
29
  mkdir -p projects/$ARGUMENTS/tasks
27
30
  ```
28
31
 
29
- ### Step 2: Set Active Project
32
+ ### Step 2: Determine Session ID
33
+
34
+ Find the current session by looking for the most recently modified task directory:
35
+
36
+ ```bash
37
+ ls -dt ~/.claude/tasks/*/ 2>/dev/null | head -1 | xargs basename
38
+ ```
39
+
40
+ If no session found, use a timestamp-based identifier:
41
+
42
+ ```bash
43
+ echo "manual-$(date +%Y%m%d-%H%M%S)"
44
+ ```
45
+
46
+ Store the session ID for use in subsequent steps.
47
+
48
+ ### Step 3: Set Active Project
30
49
 
31
50
  Create/update the active project marker:
32
51
 
@@ -34,16 +53,22 @@ Create/update the active project marker:
34
53
  echo "$ARGUMENTS" > .claude-active-project
35
54
  ```
36
55
 
37
- ### Step 3: Get Current Tasks
56
+ ### Step 4: Get Current Tasks
38
57
 
39
58
  Use TaskList to get all tasks in the current session.
40
59
 
41
- ### Step 4: Sync Each Task
60
+ ### Step 5: Sync Each Task
42
61
 
43
62
  For each task from TaskList:
44
63
 
45
64
  1. Use TaskGet to get full task details
46
- 2. Create a JSON file with the task data:
65
+ 2. Create the session directory:
66
+
67
+ ```bash
68
+ mkdir -p projects/$ARGUMENTS/tasks/{session-id}
69
+ ```
70
+
71
+ 3. Create a JSON file with the task data:
47
72
 
48
73
  ```json
49
74
  {
@@ -57,18 +82,18 @@ For each task from TaskList:
57
82
  }
58
83
  ```
59
84
 
60
- 3. Write to `projects/$ARGUMENTS/tasks/<id>.json`
85
+ 4. Write to `projects/$ARGUMENTS/tasks/{session-id}/<id>.json`
61
86
 
62
- ### Step 5: Stage for Git
87
+ ### Step 6: Stage for Git
63
88
 
64
89
  ```bash
65
- git add projects/$ARGUMENTS/tasks/*.json
90
+ git add projects/$ARGUMENTS/tasks/{session-id}/*.json
66
91
  ```
67
92
 
68
- ### Step 6: Report
93
+ ### Step 7: Report
69
94
 
70
95
  ```
71
- Synced X tasks to projects/$ARGUMENTS/tasks/
96
+ Synced X tasks to projects/$ARGUMENTS/tasks/{session-id}/
72
97
  - Pending: Y
73
98
  - In Progress: Z
74
99
  - Completed: W
@@ -80,5 +105,5 @@ Files staged for commit. Run /git:commit when ready.
80
105
 
81
106
  - This command manually syncs all current tasks to a project
82
107
  - Use this when you started work without a project context
83
- - After syncing, the active project is set so future tasks auto-sync
84
- - Existing task files in the project directory will be overwritten
108
+ - After syncing, the active project is set so future tasks auto-sync via the hook
109
+ - Each sync creates a new session directory to preserve history
@@ -4,7 +4,10 @@
4
4
  #
5
5
  # This hook is triggered on PostToolUse for TaskCreate and TaskUpdate.
6
6
  # It reads the task metadata to determine the project and syncs
7
- # task JSON files to ./projects/{project}/tasks/
7
+ # task JSON files to ./projects/{project}/tasks/{session-id}/
8
+ #
9
+ # This session-based structure preserves task history across /clear commands,
10
+ # preventing overwrites when new sessions create tasks with the same IDs.
8
11
  #
9
12
  # Input (via stdin): JSON with tool_name, tool_input, tool_output
10
13
  #
@@ -82,8 +85,14 @@ if [[ -z "$TASK_FILE" || ! -f "$TASK_FILE" ]]; then
82
85
  exit 0
83
86
  fi
84
87
 
85
- # Ensure project tasks directory exists
86
- PROJECT_TASKS_DIR="./projects/${PROJECT}/tasks"
88
+ # Require session ID for proper history tracking
89
+ if [[ -z "$SESSION_ID" ]]; then
90
+ echo "Warning: No session_id available, skipping sync" >&2
91
+ exit 0
92
+ fi
93
+
94
+ # Ensure project tasks directory exists (includes session ID for history preservation)
95
+ PROJECT_TASKS_DIR="./projects/${PROJECT}/tasks/${SESSION_ID}"
87
96
  mkdir -p "$PROJECT_TASKS_DIR"
88
97
 
89
98
  # Copy task file to project directory
@@ -1,9 +1,9 @@
1
1
  import type { FileOperationResult } from "../core/config.js";
2
2
  import type { ICopyStrategy, StrategyContext } from "./strategy.interface.js";
3
3
  /**
4
- * Merge strategy: Deep merge JSON files (project values take precedence)
5
- * - Lisa values serve as defaults
6
- * - Project values take precedence on conflicts
4
+ * Merge strategy: Deep merge JSON files (Lisa values take precedence)
5
+ * - Project values serve as defaults
6
+ * - Lisa values take precedence on conflicts
7
7
  * - Handle nested paths (.claude/settings.json)
8
8
  */
9
9
  export declare class MergeStrategy implements ICopyStrategy {
@@ -4,9 +4,9 @@ import { ensureParentDir } from "../utils/file-operations.js";
4
4
  import { readJson, writeJson, deepMerge } from "../utils/json-utils.js";
5
5
  import { JsonMergeError } from "../errors/index.js";
6
6
  /**
7
- * Merge strategy: Deep merge JSON files (project values take precedence)
8
- * - Lisa values serve as defaults
9
- * - Project values take precedence on conflicts
7
+ * Merge strategy: Deep merge JSON files (Lisa values take precedence)
8
+ * - Project values serve as defaults
9
+ * - Lisa values take precedence on conflicts
10
10
  * - Handle nested paths (.claude/settings.json)
11
11
  */
12
12
  export class MergeStrategy {
@@ -38,8 +38,8 @@ export class MergeStrategy {
38
38
  const destJson = await readJson(destPath).catch(() => {
39
39
  throw new JsonMergeError(relativePath, `Failed to parse destination: ${destPath}`);
40
40
  });
41
- // Deep merge: Lisa (source) provides defaults, project (dest) values win
42
- const merged = deepMerge(sourceJson, destJson);
41
+ // Deep merge: Lisa (source) takes precedence, project (dest) provides defaults
42
+ const merged = deepMerge(destJson, sourceJson);
43
43
  // Normalize for comparison (parse and re-stringify both)
44
44
  const normalizedDest = JSON.stringify(destJson, null, 2);
45
45
  const normalizedMerged = JSON.stringify(merged, null, 2);
@@ -1 +1 @@
1
- {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/strategies/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,OAAgB,CAAC;IAEjC;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,CACT,UAAkB,EAClB,QAAgB,EAChB,YAAoB,EACpB,OAAwB;QAExB,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;QACnD,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAElD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,4CAA4C;YAC5C,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACnB,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;gBAChC,MAAM,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;gBACrC,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACjE,CAAC;QAED,gCAAgC;QAChC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAS,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAC/D,MAAM,IAAI,cAAc,CACtB,YAAY,EACZ,2BAA2B,UAAU,EAAE,CACxC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAS,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAC3D,MAAM,IAAI,cAAc,CACtB,YAAY,EACZ,gCAAgC,QAAQ,EAAE,CAC3C,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,yEAAyE;QACzE,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAE/C,yDAAyD;QACzD,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzD,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAEzD,IAAI,cAAc,KAAK,gBAAgB,EAAE,CAAC;YACxC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACnB,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAClE,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC3B,MAAM,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAClC,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACjE,CAAC;CACF"}
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/strategies/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,OAAgB,CAAC;IAEjC;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,CACT,UAAkB,EAClB,QAAgB,EAChB,YAAoB,EACpB,OAAwB;QAExB,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;QACnD,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAElD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,4CAA4C;YAC5C,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACnB,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;gBAChC,MAAM,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;gBACrC,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACjE,CAAC;QAED,gCAAgC;QAChC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAS,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAC/D,MAAM,IAAI,cAAc,CACtB,YAAY,EACZ,2BAA2B,UAAU,EAAE,CACxC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAS,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAC3D,MAAM,IAAI,cAAc,CACtB,YAAY,EACZ,gCAAgC,QAAQ,EAAE,CAC3C,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,+EAA+E;QAC/E,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAE/C,yDAAyD;QACzD,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzD,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAEzD,IAAI,cAAc,KAAK,gBAAgB,EAAE,CAAC;YACxC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACnB,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAClE,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC3B,MAAM,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAClC,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACjE,CAAC;CACF"}
@@ -24,11 +24,11 @@ export declare function writeJson(filePath: string, data: unknown, spaces?: numb
24
24
  */
25
25
  export declare function isValidJson(filePath: string): Promise<boolean>;
26
26
  /**
27
- * Deep merge two objects (base values serve as defaults, override values take precedence)
27
+ * Deep merge two objects (Lisa values take precedence on conflicts)
28
28
  * Uses lodash.merge for deep merging
29
- * @param base Base object providing defaults
30
- * @param override Override object with precedence
31
- * @returns Merged object
29
+ * @param base Base object (project values)
30
+ * @param override Override object with precedence (Lisa values)
31
+ * @returns Merged object with Lisa values winning conflicts
32
32
  */
33
33
  export declare function deepMerge<T extends object>(base: T, override: T): T;
34
34
  //# sourceMappingURL=json-utils.d.ts.map
@@ -56,11 +56,11 @@ export async function isValidJson(filePath) {
56
56
  }
57
57
  }
58
58
  /**
59
- * Deep merge two objects (base values serve as defaults, override values take precedence)
59
+ * Deep merge two objects (Lisa values take precedence on conflicts)
60
60
  * Uses lodash.merge for deep merging
61
- * @param base Base object providing defaults
62
- * @param override Override object with precedence
63
- * @returns Merged object
61
+ * @param base Base object (project values)
62
+ * @param override Override object with precedence (Lisa values)
63
+ * @returns Merged object with Lisa values winning conflicts
64
64
  */
65
65
  export function deepMerge(base, override) {
66
66
  // Create a new object to avoid mutating inputs
package/package.json CHANGED
@@ -79,7 +79,7 @@
79
79
  "@ast-grep/cli"
80
80
  ],
81
81
  "name": "@codyswann/lisa",
82
- "version": "1.1.0",
82
+ "version": "1.3.0",
83
83
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
84
84
  "main": "dist/index.js",
85
85
  "bin": {