)]}'
{
  "log": [
    {
      "commit": "dc6d8f6f5a59956fb65dd84b065c16a4290dcbee",
      "tree": "6d5086d7146fe6017e6218c4a48ed1b91072ad49",
      "parents": [
        "5337c2bc7ab93112e7edd4c772d98987ba596e49"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 20:45:13 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 20:45:13 2026 +0400"
      },
      "message": "lexer: recognize identifiers, comma, and equals tokens\n\n- Add identifier scanning: starts with letter/underscore, continues with\n  letters/digits/underscores. Produces Ident tokens.\n- Add comma and equals to single-char token switch.\n- Add isLetter() helper.\n- Add 9 new tests covering: single ident, multi-char ident, ident with\n  digits, ident with underscore, comma, equals, function definition\n  syntax, function call with args, multi-param func def, func call in\n  expression.\n"
    },
    {
      "commit": "5337c2bc7ab93112e7edd4c772d98987ba596e49",
      "tree": "6e65b6d1f29c19b04742b9f035c5f65776fbfb4b",
      "parents": [
        "c3ec07d0e6f21f3a78888b5128c1f331c5533a99"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 20:37:00 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 20:37:00 2026 +0400"
      },
      "message": "ast: add Ident, FuncCall nodes and Statement interface (ExprStmt, FuncDef)\n\n- Ident{Name}: variable reference node (sealed, implements Node)\n- FuncCall{Name, Args}: function call node (sealed, implements Node)\n- Statement interface: sealed, for top-level REPL input\n- ExprStmt{Expr}: wraps expression as statement\n- FuncDef{Name, Params, Body}: function definition statement\n"
    },
    {
      "commit": "c3ec07d0e6f21f3a78888b5128c1f331c5533a99",
      "tree": "29f8832a448de80f90d91119573f67c12803fc56",
      "parents": [
        "e0e426038e9bd248ae465ecff118856a2389e9a6"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 20:35:06 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 20:35:06 2026 +0400"
      },
      "message": "token: add Ident, Comma, Equals token types\n"
    },
    {
      "commit": "e0e426038e9bd248ae465ecff118856a2389e9a6",
      "tree": "a0133c5fa62310fca5d43d32ea0f358925fa83aa",
      "parents": [
        "a22d352f4d4092b28a5f52460dceab42c8a3cac7"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:49:23 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:49:23 2026 +0400"
      },
      "message": "Session: 01\n"
    },
    {
      "commit": "a22d352f4d4092b28a5f52460dceab42c8a3cac7",
      "tree": "ce02c95d9780417dce425d17d1bf1c92307831f6",
      "parents": [
        "afb61d0a77879e655dead47c86d8de089c4627dd"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:36:42 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:36:42 2026 +0400"
      },
      "message": "Add README and finalize project\n"
    },
    {
      "commit": "afb61d0a77879e655dead47c86d8de089c4627dd",
      "tree": "db771a361d1294939cb5ce3d2e9c69f1a8e6926c",
      "parents": [
        "ca85f4ed5ffad72ce7f004cdf895bf62fe8a10a8"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:34:37 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:34:37 2026 +0400"
      },
      "message": "Add end-to-end integration tests\n\n36 tests covering the full lexer → parser → evaluator pipeline:\n- Basic arithmetic (single numbers, +, -, *, /)\n- Operator precedence (* / before + -)\n- Left associativity for subtraction and division\n- Parentheses (simple, nested, deeply nested, both sides, complex)\n- Complex multi-operator expressions\n- Long chained expressions\n- Floating point (arithmetic, division, precision)\n- Whitespace variations (none, extra, tabs)\n- Error cases (div by zero, invalid chars, mismatched parens,\n  empty parens, trailing/leading/consecutive operators, empty input,\n  consecutive numbers)\n"
    },
    {
      "commit": "ca85f4ed5ffad72ce7f004cdf895bf62fe8a10a8",
      "tree": "599950c4078ab9d2489fd9b06248505cebd1c410",
      "parents": [
        "cd59d229846e38345ceeaf71d4c8130981793969"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:31:47 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:31:47 2026 +0400"
      },
      "message": "Step 8: Wire REPL to main.go entry point\n"
    },
    {
      "commit": "cd59d229846e38345ceeaf71d4c8130981793969",
      "tree": "cd584fc2b97c9fee26cf6551902e50f75c80b9b0",
      "parents": [
        "586f28e03a608b86df0c07b6662009f352409fc3"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:28:26 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:28:26 2026 +0400"
      },
      "message": "Implement REPL package with Run(io.Reader, io.Writer)\n\n- Read-eval-print loop: reads lines, tokenizes, parses, evaluates, prints result\n- Prints prompt \u0027\u003e\u003e \u0027 before each input\n- Error handling: prints \u0027error: \u003cmsg\u003e\u0027 and continues loop\n- Skips empty lines\n- Formats results with %g (clean integers, no trailing zeros)\n- 10 unit tests covering: single/multiple expressions, error recovery,\n  division by zero, empty lines, prompt display, float results,\n  invalid chars, empty input, integer formatting\n"
    },
    {
      "commit": "586f28e03a608b86df0c07b6662009f352409fc3",
      "tree": "d15fcbe5f806449924e0ccb1c9953fec8ee9287f",
      "parents": [
        "b05c53fb01342873787b545359f443b5dbf2be7f"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:14:46 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:14:46 2026 +0400"
      },
      "message": "Step 6: Implement evaluator with TDD\n\n- Eval(ast.Node) (float64, error) - recursive AST walker\n- Handles all 4 operators: +, -, *, /\n- Division by zero returns error\n- Error propagation from sub-expressions\n- 10 unit tests covering: single number, all operators, nested/deep\n  expressions, division by zero (direct and in sub-expr), floats\n"
    },
    {
      "commit": "b05c53fb01342873787b545359f443b5dbf2be7f",
      "tree": "fb628f9aa5e539abc5bd2e64240bb0211707be0b",
      "parents": [
        "ad74392541b9a72b521e56673cfdd013532fbd4a"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:13:36 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:13:36 2026 +0400"
      },
      "message": "parser: implement recursive-descent parser with tests\n\n- Parse([]token.Token) (ast.Node, error) converts tokens to AST\n- Grammar: expr → term ((+|-) term)*, term → factor ((*|/) factor)*, factor → NUMBER | \u0027(\u0027 expr \u0027)\u0027\n- Correct operator precedence (* / before + -)\n- Left-associative operators\n- Error handling: empty input, missing/unexpected parens, trailing tokens, consecutive operators\n- 19 unit tests covering success and error cases\n"
    },
    {
      "commit": "ad74392541b9a72b521e56673cfdd013532fbd4a",
      "tree": "ee34b43689463e1ed0bc533aff7ca4ce6b2c0045",
      "parents": [
        "cdbb189191d122228d802b9964101a751dd30931"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:11:41 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:11:41 2026 +0400"
      },
      "message": "Add AST package: Node interface, NumberLit, BinaryExpr\n\n- Sealed Node interface with unexported marker method\n- NumberLit holds float64 value\n- BinaryExpr holds Op (token.Type), Left and Right children\n- Tests verify interface satisfaction, data integrity, and nesting\n"
    },
    {
      "commit": "cdbb189191d122228d802b9964101a751dd30931",
      "tree": "c1e1a9b48d7f8405e0fcb0a15e3285cf7b38e404",
      "parents": [
        "d04f362cd432ad643a69ca89f4aba57987b87170"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:10:35 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:10:35 2026 +0400"
      },
      "message": "Add lexer: Tokenize function with full test coverage\n\nImplements lexer.Tokenize(input string) ([]token.Token, error)\n- Skips whitespace\n- Parses integer and decimal numbers (including leading dot e.g. .5)\n- Handles all operators: + - * /\n- Handles parentheses: ( )\n- Appends EOF token\n- Returns error on invalid characters with position info\n- 12 unit tests covering: empty, whitespace-only, integers, decimals,\n  leading-dot numbers, operators, parens, full expressions, no-space\n  expressions, invalid chars, multiple decimals (1.2.3)\n"
    },
    {
      "commit": "d04f362cd432ad643a69ca89f4aba57987b87170",
      "tree": "34b53f6a319a30f893ce39fbe958e273cdb92a9a",
      "parents": [
        "719e823878d91176baf06c646ddec638530c10b8"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:09:06 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:09:06 2026 +0400"
      },
      "message": "token: add Type enum, Token struct, and String() method\n"
    },
    {
      "commit": "719e823878d91176baf06c646ddec638530c10b8",
      "tree": "4bec55aaaba03d29081524704af760d3ef370f9d",
      "parents": [],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:07:50 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:07:50 2026 +0400"
      },
      "message": "Step 1: project skeleton - go mod, directory structure, placeholder main.go\n"
    }
  ]
}
