Skip to content

Add headless pp-cli for AI-agent access - #6262

Open
idodoron11 wants to merge 133 commits into
portfolio-performance:masterfrom
idodoron11:feat/cli-support
Open

idodoron11 wants to merge 133 commits into
portfolio-performance:masterfrom
idodoron11:feat/cli-support

Conversation

@idodoron11

Copy link
Copy Markdown

Summary

Agent -> pp-cli -> Portfolio Performance core -> closed client file
         read: master data, transaction records, holdings, prices,
               performance, taxonomies, allocation
         write: apply -> validate -> backup -> atomic save
                restore -> recover from backup
         discover: describe -> commands, schemas, error codes

Distribution: macOS arm64 + Windows x64 bundles, installers,
uninstallers and pp-cli agent skill

Adds a headless Java 21 CLI that reads calculated figures and safely applies validated changes to client files, preserving supported formats and refusing unsupported file versions.

Evidence

  • Before: No headless shell interface for querying calculated figures or safely changing a closed client file.
  • After: CLI test suite: 439 tests passed; 0 failures, 0 errors.
    mvn -f portfolio-app/pom.xml verify -Plocal-dev -pl :portfolio-target-definition,:name.abuchen.portfolio.pdfbox1,:name.abuchen.portfolio.pdfbox3,:name.abuchen.portfolio,:name.abuchen.portfolio.cli,:name.abuchen.portfolio.junit,:name.abuchen.portfolio.cli.tests -am

Merge Danger

Door: Two-way

The CLI is additive; installations have uninstall scripts, and client-file writes create backups with a restore command.

Blast Radius: Moderate

The write surface can change client files, and installers modify per-user PATH or shell configuration. The desktop workflow remains unchanged.

idodoron11 and others added 30 commits October 24, 2026 18:05
…including feasibility of loading client files and prior art analysis; update architecture considerations for CLI design.
… structure, validation rules, and backup behavior
…ersion checks, and password management for CLI
- Implement walking skeleton for `pp-cli` with `info` command to return client file details.
- Create `describe` command to list all declared commands and their schemas.
- Enable reading of encrypted client files and support for all seven formats.
- Add master-data list commands with pagination for instruments, investment accounts, and cash accounts.
- Implement reference resolver for price history retrieval by UUID, ISIN, ticker, or name.
- Create `transactions` command to return joined transaction records with filtering options.
- Implement `holdings` command to report ownership and value at a specific date.
- Add `performance` command for client performance metrics with period and cost method options.
- Extend `performance` command to support account and instrument scopes.
- Implement `taxonomies` and `allocation` commands for classification and value reporting.
- Create `apply` command for adding and updating instruments with safe-write sequence.
- Harden safe-write sequence for `apply` and `restore` commands.
- Enable writing back in all formats and migrating older files.
- Implement account operations for cash and investment accounts.
- Add transaction types for cash-account transactions.
- Implement transaction operations for adding, updating, and deleting records.
- Create `taxonomy.assign` command for instrument classification.
- Implement `restore` command to roll back to backups.
- Package Windows and macOS bundles with installation scripts and documentation.
- Add `--format table` option for human-readable output in CLI commands.
New additive CLI module and test bundle in the reactor. A single in-process
entry point parses declared commands, loads client files (xml, xml-id-references,
xml-zip, binary), applies a version guard on every load and returns the
{data, meta, warnings} envelope or a problem+json error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Declares response schemas and write operations alongside commands, renders
them in describe with known limits, the pinned tag and maxSupportedVersion,
and adds a consistency test that validates real responses against the
published schemas. The CLI tests bundle becomes a fragment of the CLI bundle
so tests can reach the declarations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Passwords come from PP_PASSWORD or one line on stdin, never a flag.
The CLI decrypts the file header itself to report password-required,
incorrect-password, file-version-unsupported and the normalized format
without depending on core's localized messages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Record the answers in the spec, tickets 05/15/22/25, ADR 0004 (list output deviations from upstream portfolio-performance#5870) and the glossary (Master data, Reference cash account).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds the instruments, investment-accounts and cash-accounts commands with
shared --limit/--offset paging, stable name-then-UUID ordering, typed
instrument attributes, and a shared meta helper. OptionSpec now carries
an optional type, default and range that describe publishes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Write quote, shares and amount attributes as plain decimals instead of
scientific notation, reject "+5" and non-ASCII digits as paging values,
and use glossary vocabulary in the published schema descriptions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
idodoron11 and others added 30 commits October 26, 2026 00:16
The readers that cash events, trades and transfers share (accounts,
instrument, date and time, currency, amounts, units, gross amount and
the refusal of members a kind doesn't take) move from
TransactionOperations into TransactionInput. TransactionOperations
keeps the schema, the dispatch by type, the cash-event rules and the
commit that all kinds share.

Codes, fields and messages of every failure are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
transaction.update and transaction.delete found their record among all
records of the evolving model, so they could change one that an earlier
operation of the same batch had created. The spec allows no in-batch
references (story 79: every reference is a UUID that existed before the
batch), and every other write already refused them.

Transaction records are now an EntityKind like the others: ApplyBatch
remembers the transactions that were in the file, and a record exists
if its primary side does. The CLI can't reach the case today, because a
new record's UUID is only known from the response.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-up: a transfer read its input through three readers over
the same operation. Also drops a stray blank line and rewraps the
TransactionOperations javadoc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both directions now share one type list, in the published order, and
one unit catalog with a read and a write description per member.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each member of a transaction record is declared once in TransactionShape,
with whether a record shows it, whether an add takes it, and a read and
a write description. The write help now lists members in read order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The overlay keeps the rendered members that TransactionShape marks as
input instead of removing a hand-kept list of derived ones, and drops an
empty list of units for every record rather than only for transfers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each member is a named constant, declared with what a record shows and
what an add takes, so a derived or never-shown member carries no null
description and declares itself on a schema. render() and the update
overlay use the members' names instead of literals, an add and an
update each have their own declaring method, and the investment-account
types sit next to the type list.

The overlay and the round-trip test share inputOf, which keeps the
members an add takes, from a set, and drops an empty units list only.
The test names its investment accounts without "depot".

The published schemas are byte-for-byte unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… own

The catalog speaks of a member as output (what transactions shows) and
as input (what an add takes): Output/Input with their presences,
outputType/inputType and inputAs replace Shown/Taken, Read/Write,
schemaOf/inputSchemaOf and takenAs. The unit members all carry the UNIT_
prefix, and both unit schemas are fields, UNIT_OUTPUT and UNIT_INPUT. A
UUID member and its name member are one NamedUuid that puts both, so
render passes one constant per account.

The round-trip test lists the members an add takes itself instead of
using inputOf, so it no longer checks the catalog with the catalog. A
second case updates every type of record with an empty patch and expects
it unchanged, which is what covers inputOf in the update overlay. The
comment on dropping an empty units list says why.

The published schemas are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ReportingCurrency owns what every converting command wired itself: the
--currency option and its validation, the base-currency default, figures
with their rates and the zero rule, totals, the converted member and its
schema, the exchangeRates meta member and the rate warnings. ExchangeRates
stays the rate source underneath. Holdings drops its own Figure and
Valuation; its output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Allocation checks its rates with allRated, performance gets its strict
converter and its day-counting warnings from the facet. Output unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
transactions --currency is a reporting currency without a default. The
command still finds the record's own forex or transfer rate; the facet
tries the identity, that rate, then the rate series. Output unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
parseCurrency, converted, convertedSchema, metaSchema and WARNINGS now
live in ReportingCurrency; ExchangeRates keeps the rates, the cache and
the warning specs it emits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Name the record's own rate after the reporting currency, not a target,
as the glossary asks, and say why the own-rate figure takes a code and
an amount rather than a Money.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test read every dotted name in the skill as an operation, so the
holdings member `converted.amount` in insights.md failed it. A dotted
name now passes if it is an operation or a member path in some
command's data or meta schema; a misspelling of either still fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
holdings and allocation each kept their own map to warn once per
instrument, in the order they met instruments. In allocation that is
core's position order, so the order of the warnings changed from run
to run.

StalePrices now does both for them: one warning per instrument, sorted
by name, then UUID. holdings --by-account changes too: an instrument
from a later account no longer comes after one it sorts before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Commands read an option as a raw string by name and then parsed it
themselves, e.g. DATE.parseDate(context.option(DATE.name())), and
Sorting looked its option up by the literal "sort". CommandContext now
has text, date, integer and choice accessors that take the OptionSpec
and check its declared type, plus given for a presence check that must
not reject a bad value (--period with --from stays conflicting-options).
option(String) is gone. No output changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…text

Each command resolved its reference options by hand, as
References.instrument(client, X.name(), context.text(X).get()), and
transactions kept a private Resolver to do it for three options.
ReferenceOption now ties an option to the kind of entity it names, and
context.reference(option, client) returns the entity, or empty when the
option is absent. References keeps the matching rules behind one
resolve per kind; its four per-kind methods are gone, and ReferencesTest
calls resolve directly. Help and responses are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ErrorSpec named the commands that can return reference-not-found,
reference-ambiguous and option-required, and nothing checked those
lists, so a new reference or required option left its codes out of the
command's help. The reference codes now apply to any command that
declares a reference option, and option-required to any command that
declares a required option. performance is still named: it raises
option-required when --with-reference-cash-account comes without
--investment-account, a rule no declaration states. The codes from
other rules between options keep their lists. Help is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lines

flag() now rejects an option that is not a switch, like the other
accessors. The type-mismatch test also covers reading a reference option
as text and resolving a reference option declared with another type.
Lines this branch pushed past 120 characters are wrapped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI package had grown to 78 classes in one flat folder. This is the
first of several moves into feature packages. ClientLoader,
LoadedClient, EncryptedHeader, FileFormat, FileVersionProbe, SafeWrite,
WriteEnvironment and ConsistencyCheck now live in cli.file, with their
tests and the historic test files next to them.

The declarations used across the new package boundary are now public.
Anything used only inside its own package stays package-private. The
bundle still exports only the root package, so none of this is visible
outside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ReportingCurrency, ExchangeRates, EcbCache, StrictConverter, Prices and
StalePrices now live in cli.currency, with ReportingCurrencyTest and
StalePricesTest. As in the cli.file move, only the declarations used
across the package boundary are now public.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ormance

PerformanceCommand, PerformanceByInstrument, PerformancePeriod,
PerformanceScope and CostMethod now live in cli.performance, with
PerformanceCommandTest and PerformanceScopesTest. Only the declarations
used across the package boundary are now public.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApplyCommand, RestoreCommand, ApplyBatch, the operation framework
(OperationContext, OperationInput, OperationSpec, EntityKind) and the
per-entity operations, with TransactionInput, TransactionUpdates and
OversellGuard, now live in cli.apply. Their tests moved with them. Only
the declarations used across the package boundary are now public. The
declarations that grew past 120 characters are wrapped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
info, cash-accounts, investment-accounts, instruments, prices,
holdings, allocation, taxonomies and transactions now live in cli.query.
So do the types they are built on: AttributeCodec, TaxonomyTree,
TransactionRecord, TransactionShape and BuiltRecord. Their tests moved
with them. Only the declarations used across the package boundary are
now public. The declarations that grew past 120 characters are wrapped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CommandSpec, CommandContext, the option and reference types, responses,
warnings, schemas, table output, paging, sorting and the master-data
helpers now live in cli.command, with their tests. The root package
keeps only the entry point and what spans every command: PpCli,
ArgumentParser, Commands, HelpText, ErrorSpec, QuietStatusFilter and
CoreVersion. The shared test fixtures stay there too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The replacing context counts its own failures, so it never saw a
type-immutable or kind-immutable failure and still built the
replacement. The batch was refused, but later operations of the same
batch validated against the changed record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Added ticket 01: Define the interface for the Transaction record module, addressing ownership of transaction aspects and interface clarity.
- Added ticket 02: Establish the facet interface for commands, focusing on options declaration and error-code scoping.
- Added ticket 03: Evaluate the consolidation of Master data kinds into single modules, assessing interface and dependencies.
- Added ticket 04: Investigate the necessity of an amounts & rates module, including rounding consistency and conversion rules.
- Added ticket 05: Define the valuation module for Reporting currency, ensuring correct conversion and error handling.
- Added ticket 06: Clarify the declaration of write operation failure codes for better error management and documentation.
- Added ticket 07: Outline the refactoring spec and implementation ticket strategy for seamless integration and testing.
- Created a map for the deepening effort, detailing scope, decisions, and outstanding specifications.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant