personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Convert entity commands to use --facet option with SOL_FACET resolution

All entity commands (detect, attach, update, aka, observations, observe)
now accept facet as a --facet/-f option instead of a required positional
argument. When SOL_FACET is set in the environment, agents can omit it
entirely, matching the pattern established for --day across other call
modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+94 -51
+25 -11
apps/entities/call.py
··· 22 22 load_observations, 23 23 ) 24 24 from think.entities.saving import save_entities 25 - from think.utils import now_ms 25 + from think.utils import now_ms, resolve_sol_day, resolve_sol_facet 26 26 27 27 app = typer.Typer(help="Entity management.") 28 28 ··· 58 58 ), 59 59 ) -> None: 60 60 """List entities for a facet.""" 61 - from think.utils import resolve_sol_facet 62 - 63 61 facet = resolve_sol_facet(facet) 64 62 entities = load_entities(facet, day) 65 63 if not entities: ··· 74 72 75 73 @app.command("detect") 76 74 def detect_entity( 77 - facet: str = typer.Argument(help="Facet name."), 78 75 type_: str = typer.Argument(metavar="TYPE", help="Entity type."), 79 76 entity: str = typer.Argument(help="Entity name or identifier."), 80 77 description: str = typer.Argument(help="Description."), 78 + facet: str | None = typer.Option( 79 + None, "--facet", "-f", help="Facet name (or set SOL_FACET)." 80 + ), 81 81 day: str | None = typer.Option( 82 82 None, "--day", "-d", help="Day (YYYYMMDD, or set SOL_DAY)." 83 83 ), 84 84 ) -> None: 85 85 """Record a detected entity for a day in a facet.""" 86 - from think.utils import resolve_sol_day 87 - 86 + facet = resolve_sol_facet(facet) 88 87 day = resolve_sol_day(day) 89 88 if not is_valid_entity_type(type_): 90 89 typer.echo(f"Error: Invalid entity type '{type_}'.", err=True) ··· 116 115 117 116 @app.command("attach") 118 117 def attach_entity( 119 - facet: str = typer.Argument(help="Facet name."), 120 118 type_: str = typer.Argument(metavar="TYPE", help="Entity type."), 121 119 entity: str = typer.Argument(help="Entity name."), 122 120 description: str = typer.Argument(help="Description."), 121 + facet: str | None = typer.Option( 122 + None, "--facet", "-f", help="Facet name (or set SOL_FACET)." 123 + ), 123 124 ) -> None: 124 125 """Attach an entity permanently to a facet.""" 126 + facet = resolve_sol_facet(facet) 125 127 if not is_valid_entity_type(type_): 126 128 typer.echo(f"Error: Invalid entity type '{type_}'.", err=True) 127 129 raise typer.Exit(1) ··· 167 169 168 170 @app.command("update") 169 171 def update_entity( 170 - facet: str = typer.Argument(help="Facet name."), 171 172 entity: str = typer.Argument(help="Entity name or identifier."), 172 173 description: str = typer.Argument(help="New description."), 174 + facet: str | None = typer.Option( 175 + None, "--facet", "-f", help="Facet name (or set SOL_FACET)." 176 + ), 173 177 day: str | None = typer.Option( 174 178 None, "--day", "-d", help="Day for detected entities." 175 179 ), 176 180 ) -> None: 177 181 """Update an entity description.""" 182 + facet = resolve_sol_facet(facet) 178 183 if day is None: 179 184 resolved = _resolve_or_exit(facet, entity) 180 185 resolved_name = resolved.get("name", entity) ··· 216 221 217 222 @app.command("aka") 218 223 def add_aka( 219 - facet: str = typer.Argument(help="Facet name."), 220 224 entity: str = typer.Argument(help="Entity name or identifier."), 221 225 aka_value: str = typer.Argument(metavar="AKA", help="Alias to add."), 226 + facet: str | None = typer.Option( 227 + None, "--facet", "-f", help="Facet name (or set SOL_FACET)." 228 + ), 222 229 ) -> None: 223 230 """Add an alias to an attached entity.""" 231 + facet = resolve_sol_facet(facet) 224 232 resolved = _resolve_or_exit(facet, entity) 225 233 resolved_name = resolved.get("name", "") 226 234 ··· 265 273 266 274 @app.command("observations") 267 275 def list_observations( 268 - facet: str = typer.Argument(help="Facet name."), 269 276 entity: str = typer.Argument(help="Entity name or identifier."), 277 + facet: str | None = typer.Option( 278 + None, "--facet", "-f", help="Facet name (or set SOL_FACET)." 279 + ), 270 280 ) -> None: 271 281 """List observations for an attached entity.""" 282 + facet = resolve_sol_facet(facet) 272 283 resolved = _resolve_or_exit(facet, entity) 273 284 resolved_name = resolved.get("name", "") 274 285 obs = load_observations(facet, resolved_name) ··· 284 295 285 296 @app.command("observe") 286 297 def observe_entity( 287 - facet: str = typer.Argument(help="Facet name."), 288 298 entity: str = typer.Argument(help="Entity name or identifier."), 289 299 content: str = typer.Argument(help="Observation content."), 300 + facet: str | None = typer.Option( 301 + None, "--facet", "-f", help="Facet name (or set SOL_FACET)." 302 + ), 290 303 source_day: str | None = typer.Option(None, "--source-day", help="Day (YYYYMMDD)."), 291 304 ) -> None: 292 305 """Add an observation to an attached entity.""" 306 + facet = resolve_sol_facet(facet) 293 307 resolved = _resolve_or_exit(facet, entity) 294 308 resolved_name = resolved.get("name", "") 295 309 obs = load_observations(facet, resolved_name)
+20 -20
apps/entities/muse/entities/SKILL.md
··· 7 7 8 8 Use these commands to maintain facet-scoped entity memory from the terminal. 9 9 10 - **Environment defaults**: When `SOL_FACET` is set, commands that take a FACET argument will use it automatically. Same for `SOL_DAY` where DAY is accepted. 10 + **Environment defaults**: When `SOL_FACET` is set, all commands use it automatically. Same for `SOL_DAY` where DAY is accepted. 11 11 12 12 Common pattern: 13 13 ··· 50 50 ## detect 51 51 52 52 ```bash 53 - sol call entities detect FACET TYPE ENTITY DESCRIPTION [-d DAY] 53 + sol call entities detect TYPE ENTITY DESCRIPTION [-f FACET] [-d DAY] 54 54 ``` 55 55 56 56 Record a detected entity for a day. 57 57 58 - - `FACET`: required facet name (positional argument). 59 58 - `TYPE`: entity type (alphanumeric + spaces, minimum 3 chars). 60 59 - `ENTITY`: entity id, full name, or alias. 61 60 - `DESCRIPTION`: day-scoped description. 61 + - `-f, --facet`: facet name (default: `SOL_FACET` env). 62 62 - `-d, --day`: day in `YYYYMMDD` (default: `SOL_DAY` env). 63 63 64 64 Behavior notes: ··· 70 70 Example: 71 71 72 72 ```bash 73 - sol call entities detect work "Person" "Alicia Chen" "Led architecture review" -d 20260115 73 + sol call entities detect "Person" "Alicia Chen" "Led architecture review" -f work -d 20260115 74 74 ``` 75 75 76 76 ## attach 77 77 78 78 ```bash 79 - sol call entities attach FACET TYPE ENTITY DESCRIPTION 79 + sol call entities attach TYPE ENTITY DESCRIPTION [-f FACET] 80 80 ``` 81 81 82 82 Attach an entity permanently to a facet. 83 83 84 - - `FACET`: required facet name. 85 84 - `TYPE`: required type. 86 85 - `ENTITY`: id, name, or alias reference. 87 86 - `DESCRIPTION`: persistent description. 87 + - `-f, --facet`: facet name (default: `SOL_FACET` env). 88 88 89 89 Behavior notes: 90 90 ··· 95 95 Example: 96 96 97 97 ```bash 98 - sol call entities attach work "Company" "Acme Corp" "Primary platform vendor" 98 + sol call entities attach "Company" "Acme Corp" "Primary platform vendor" -f work 99 99 ``` 100 100 101 101 ## update 102 102 103 103 ```bash 104 - sol call entities update FACET ENTITY DESCRIPTION [-d DAY] 104 + sol call entities update ENTITY DESCRIPTION [-f FACET] [-d DAY] 105 105 ``` 106 106 107 107 Update entity description. 108 108 109 - - `FACET`: required facet name. 110 109 - `ENTITY`: entity id, name, or alias for attached entities; exact name for day-scoped detected updates. 111 110 - `DESCRIPTION`: new description. 111 + - `-f, --facet`: facet name (default: `SOL_FACET` env). 112 112 - `-d, --day`: optional day (`YYYYMMDD`) to update a detected entity. 113 113 114 114 Behavior notes: ··· 119 119 Examples: 120 120 121 121 ```bash 122 - sol call entities update work "acme_corp" "Primary vendor for identity services" 123 - sol call entities update work "Alicia Chen" "Discussed migration plan" -d 20260115 122 + sol call entities update "acme_corp" "Primary vendor for identity services" -f work 123 + sol call entities update "Alicia Chen" "Discussed migration plan" -f work -d 20260115 124 124 ``` 125 125 126 126 ## aka 127 127 128 128 ```bash 129 - sol call entities aka FACET ENTITY AKA 129 + sol call entities aka ENTITY AKA [-f FACET] 130 130 ``` 131 131 132 132 Add an alias to an attached entity. 133 133 134 - - `FACET`: required facet name. 135 134 - `ENTITY`: entity id, name, or alias reference. 136 135 - `AKA`: alias to add. 136 + - `-f, --facet`: facet name (default: `SOL_FACET` env). 137 137 138 138 Behavior notes: 139 139 ··· 144 144 Example: 145 145 146 146 ```bash 147 - sol call entities aka work "Federal Aviation Administration" "FAA" 147 + sol call entities aka "Federal Aviation Administration" "FAA" -f work 148 148 ``` 149 149 150 150 ## observations 151 151 152 152 ```bash 153 - sol call entities observations FACET ENTITY 153 + sol call entities observations ENTITY [-f FACET] 154 154 ``` 155 155 156 156 List durable observations for an attached entity. 157 157 158 - - `FACET`: required facet name. 159 158 - `ENTITY`: entity id, name, or alias. 159 + - `-f, --facet`: facet name (default: `SOL_FACET` env). 160 160 161 161 Output is numbered for quick review. 162 162 163 163 Example: 164 164 165 165 ```bash 166 - sol call entities observations work "Alicia Chen" 166 + sol call entities observations "Alicia Chen" -f work 167 167 ``` 168 168 169 169 ## observe 170 170 171 171 ```bash 172 - sol call entities observe FACET ENTITY CONTENT [--source-day DAY] 172 + sol call entities observe ENTITY CONTENT [-f FACET] [--source-day DAY] 173 173 ``` 174 174 175 175 Add a durable observation to an attached entity. 176 176 177 - - `FACET`: required facet name. 178 177 - `ENTITY`: entity id, name, or alias. 179 178 - `CONTENT`: observation text. 179 + - `-f, --facet`: facet name (default: `SOL_FACET` env). 180 180 - `--source-day`: optional day (`YYYYMMDD`) when this was observed. 181 181 182 182 Behavior notes: ··· 200 200 Example: 201 201 202 202 ```bash 203 - sol call entities observe work "Alicia Chen" "Prefers design docs before implementation" --source-day 20260115 203 + sol call entities observe "Alicia Chen" "Prefers design docs before implementation" -f work --source-day 20260115 204 204 ```
+49 -20
apps/entities/tests/test_call.py
··· 81 81 [ 82 82 "entities", 83 83 "detect", 84 - "personal", 85 84 "Person", 86 85 "Alice", 87 86 "Met at conference", 87 + "--facet", 88 + "personal", 88 89 "--day", 89 90 "20240101", 90 91 ], ··· 106 107 [ 107 108 "entities", 108 109 "detect", 109 - "personal", 110 110 "Person", 111 111 "Alice", 112 112 "Second", 113 + "--facet", 114 + "personal", 113 115 "--day", 114 116 "20240101", 115 117 ], ··· 126 128 [ 127 129 "entities", 128 130 "detect", 129 - "personal", 130 131 "AB", 131 132 "Alice", 132 133 "Met at conference", 134 + "--facet", 135 + "personal", 133 136 "--day", 134 137 "20240101", 135 138 ], ··· 145 148 146 149 result = runner.invoke( 147 150 call_app, 148 - ["entities", "attach", "personal", "Person", "Alice Johnson", "Friend"], 151 + [ 152 + "entities", 153 + "attach", 154 + "Person", 155 + "Alice Johnson", 156 + "Friend", 157 + "-f", 158 + "personal", 159 + ], 149 160 ) 150 161 151 162 assert result.exit_code == 0 ··· 166 177 167 178 result = runner.invoke( 168 179 call_app, 169 - ["entities", "attach", "personal", "Person", "Alice Johnson", "Friend"], 180 + [ 181 + "entities", 182 + "attach", 183 + "Person", 184 + "Alice Johnson", 185 + "Friend", 186 + "-f", 187 + "personal", 188 + ], 170 189 ) 171 190 172 191 assert result.exit_code == 0 ··· 177 196 178 197 result = runner.invoke( 179 198 call_app, 180 - ["entities", "attach", "personal", "AB", "Alice Johnson", "Friend"], 199 + ["entities", "attach", "AB", "Alice Johnson", "Friend", "-f", "personal"], 181 200 ) 182 201 183 202 assert result.exit_code == 1 ··· 200 219 201 220 result = runner.invoke( 202 221 call_app, 203 - ["entities", "update", "personal", "Alice Johnson", "New description"], 222 + [ 223 + "entities", 224 + "update", 225 + "Alice Johnson", 226 + "New description", 227 + "-f", 228 + "personal", 229 + ], 204 230 ) 205 231 verify = runner.invoke(call_app, ["entities", "list", "personal"]) 206 232 ··· 221 247 [ 222 248 "entities", 223 249 "update", 224 - "personal", 225 250 "Alice", 226 251 "New desc", 252 + "-f", 253 + "personal", 227 254 "--day", 228 255 "20240101", 229 256 ], ··· 237 264 238 265 result = runner.invoke( 239 266 call_app, 240 - ["entities", "update", "personal", "Missing", "New description"], 267 + ["entities", "update", "Missing", "New description", "-f", "personal"], 241 268 ) 242 269 243 270 assert result.exit_code == 1 ··· 260 287 261 288 result = runner.invoke( 262 289 call_app, 263 - ["entities", "aka", "personal", "Alice Johnson", "Ali"], 290 + ["entities", "aka", "Alice Johnson", "Ali", "-f", "personal"], 264 291 ) 265 292 266 293 assert result.exit_code == 0 ··· 282 309 283 310 result = runner.invoke( 284 311 call_app, 285 - ["entities", "aka", "personal", "Alice Johnson", "Ali"], 312 + ["entities", "aka", "Alice Johnson", "Ali", "-f", "personal"], 286 313 ) 287 314 288 315 assert result.exit_code == 0 ··· 303 330 304 331 result = runner.invoke( 305 332 call_app, 306 - ["entities", "aka", "personal", "Alice Johnson", "Alice"], 333 + ["entities", "aka", "Alice Johnson", "Alice", "-f", "personal"], 307 334 ) 308 335 309 336 assert result.exit_code == 0 ··· 326 353 327 354 result = runner.invoke( 328 355 call_app, 329 - ["entities", "observations", "personal", "Alice Johnson"], 356 + ["entities", "observations", "Alice Johnson", "-f", "personal"], 330 357 ) 331 358 332 359 assert result.exit_code == 0 ··· 349 376 350 377 result = runner.invoke( 351 378 call_app, 352 - ["entities", "observations", "personal", "Alice Johnson"], 379 + ["entities", "observations", "Alice Johnson", "-f", "personal"], 353 380 ) 354 381 355 382 assert result.exit_code == 0 ··· 373 400 374 401 result = runner.invoke( 375 402 call_app, 376 - ["entities", "observe", "personal", "Alice Johnson", "Likes coffee"], 403 + ["entities", "observe", "Alice Johnson", "Likes coffee", "-f", "personal"], 377 404 ) 378 405 379 406 assert result.exit_code == 0 ··· 384 411 385 412 result = runner.invoke( 386 413 call_app, 387 - ["entities", "observe", "personal", "Missing", "Likes coffee"], 414 + ["entities", "observe", "Missing", "Likes coffee", "-f", "personal"], 388 415 ) 389 416 390 417 assert result.exit_code == 1 ··· 412 439 assert result.exit_code == 0 413 440 assert "Alice" in result.output 414 441 415 - def test_detect_from_sol_day(self, entity_env, monkeypatch): 416 - """detect with SOL_DAY env instead of --day option works.""" 442 + def test_detect_from_sol_day_and_facet(self, entity_env, monkeypatch): 443 + """detect with SOL_DAY + SOL_FACET env works.""" 417 444 entity_env() 418 445 monkeypatch.setenv("SOL_DAY", "20240101") 446 + monkeypatch.setenv("SOL_FACET", "personal") 419 447 result = runner.invoke( 420 448 call_app, 421 - ["entities", "detect", "personal", "Person", "Bob", "Met at party"], 449 + ["entities", "detect", "Person", "Bob", "Met at party"], 422 450 ) 423 451 assert result.exit_code == 0 424 452 assert "detected" in result.output ··· 432 460 [ 433 461 "entities", 434 462 "detect", 435 - "personal", 436 463 "Person", 437 464 "Charlie", 438 465 "Met at office", 466 + "-f", 467 + "personal", 439 468 "--day", 440 469 "20240101", 441 470 ],